Skip to content

Commit 21a971c

Browse files
authored
feat(cli.rs): infer devPath/distDir/appName from package.json (#1930)
1 parent 3c21ddc commit 21a971c

File tree

7 files changed

+179
-41
lines changed

7 files changed

+179
-41
lines changed

.changes/cli-init-infer-prompts.md

+6
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
---
2+
"cli.rs": patch
3+
---
4+
5+
Infer `app name` and `window title` from `package.json > productName` or `package.json > name`.
6+
Infer `distDir` and `devPath` by reading the package.json and trying to determine the UI framework (Vue.js, Angular, React, Svelte and some UI frameworks).
+111
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,111 @@
1+
// Copyright 2019-2021 Tauri Programme within The Commons Conservancy
2+
// SPDX-License-Identifier: Apache-2.0
3+
// SPDX-License-Identifier: MIT
4+
5+
use std::fmt;
6+
7+
#[derive(Debug, Clone)]
8+
pub enum Framework {
9+
Svelte,
10+
Angular,
11+
React,
12+
Nextjs,
13+
14+
Gatsby,
15+
Nuxt,
16+
Quasar,
17+
VueCli,
18+
Vue,
19+
}
20+
21+
impl Framework {
22+
pub fn dev_path(&self) -> String {
23+
match self {
24+
Self::Svelte => "http://localhost:5000",
25+
Self::Angular => "http://localhost:4200",
26+
Self::React => "http://localhost:3000",
27+
Self::Nextjs => "http://localhost:3000",
28+
Self::Gatsby => "http://localhost:8000",
29+
Self::Nuxt => "http://localhost:3000",
30+
Self::Quasar => "http://localhost:8080",
31+
Self::VueCli => "http://localhost:8080",
32+
Self::Vue => "http://localhost:8080",
33+
}
34+
.into()
35+
}
36+
37+
pub fn dist_dir(&self) -> String {
38+
match self {
39+
Self::Svelte => "../public",
40+
Self::Angular => "../dist",
41+
Self::React => "../build",
42+
Self::Nextjs => "../out",
43+
Self::Gatsby => "../public",
44+
Self::Nuxt => "../dist",
45+
Self::Quasar => "../dist/spa",
46+
Self::VueCli => "../dist",
47+
Self::Vue => "../dist",
48+
}
49+
.into()
50+
}
51+
}
52+
53+
impl fmt::Display for Framework {
54+
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
55+
match self {
56+
Self::Svelte => write!(f, "Svelte"),
57+
Self::Angular => write!(f, "Angular"),
58+
Self::React => write!(f, "React"),
59+
Self::Nextjs => write!(f, "React (Next.js)"),
60+
Self::Gatsby => write!(f, "React (Gatsby)"),
61+
Self::Nuxt => write!(f, "Vue.js (Nuxt)"),
62+
Self::Quasar => write!(f, "Vue.js (Quasar)"),
63+
Self::VueCli => write!(f, "Vue.js (Vue CLI)"),
64+
Self::Vue => write!(f, "Vue.js"),
65+
}
66+
}
67+
}
68+
69+
#[derive(Debug, Clone)]
70+
pub enum Bundler {
71+
Webpack,
72+
Rollup,
73+
}
74+
75+
impl fmt::Display for Bundler {
76+
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
77+
match self {
78+
Self::Webpack => write!(f, "Webpack"),
79+
Self::Rollup => write!(f, "Rollup"),
80+
}
81+
}
82+
}
83+
84+
pub fn infer_from_package_json(package_json: &str) -> (Option<Framework>, Option<Bundler>) {
85+
let framework_map = [
86+
("svelte", Framework::Svelte, None),
87+
("@angular", Framework::Angular, Some(Bundler::Webpack)),
88+
(r#""next""#, Framework::Nextjs, Some(Bundler::Webpack)),
89+
("gatsby", Framework::Gatsby, Some(Bundler::Webpack)),
90+
("react", Framework::React, None),
91+
("nuxt", Framework::Nuxt, Some(Bundler::Webpack)),
92+
("quasar", Framework::Quasar, Some(Bundler::Webpack)),
93+
("@vue/cli", Framework::VueCli, Some(Bundler::Webpack)),
94+
("vue", Framework::Vue, None),
95+
];
96+
let bundler_map = [("webpack", Bundler::Webpack), ("rollup", Bundler::Rollup)];
97+
98+
let (framework, framework_bundler) = framework_map
99+
.iter()
100+
.find(|(keyword, _, _)| package_json.contains(keyword))
101+
.map(|(_, framework, bundler)| (Some(framework.clone()), bundler.clone()))
102+
.unwrap_or((None, None));
103+
104+
let bundler = bundler_map
105+
.iter()
106+
.find(|(keyword, _)| package_json.contains(keyword))
107+
.map(|(_, bundler)| bundler.clone())
108+
.or(framework_bundler);
109+
110+
(framework, bundler)
111+
}

tooling/cli.rs/src/helpers/logger.rs

+1
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ impl<'a> Logger<'a> {
2929
);
3030
}
3131

32+
#[allow(dead_code)]
3233
pub fn error(&self, message: impl AsRef<str>) {
3334
println!(
3435
"{} {}",

tooling/cli.rs/src/helpers/mod.rs

+1
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44

55
pub mod app_paths;
66
pub mod config;
7+
pub mod framework;
78
mod logger;
89
pub mod manifest;
910
pub mod updater_signature;

tooling/cli.rs/src/info.rs

+8-29
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
use crate::helpers::{
66
app_paths::{app_dir, tauri_dir},
77
config::get as get_config,
8+
framework::infer_from_package_json as infer_framework,
89
};
910
use serde::Deserialize;
1011

@@ -552,38 +553,16 @@ impl Info {
552553
.display();
553554
}
554555
if let Ok(package_json) = read_to_string(app_dir.join("package.json")) {
555-
let framework_map = [
556-
("svelte", "Svelte", None),
557-
("@angular", "Angular", Some("Webpack")),
558-
(r#""next""#, "React (Next.js)", Some("Webpack")),
559-
("gatsby", "React (Gatsby)", Some("Webpack")),
560-
("react", "React", None),
561-
("nuxt", "Vue.js (Nuxt)", Some("Webpack")),
562-
("quasar", "Vue.js (Quasar)", Some("Webpack")),
563-
("@vue/cli", "Vue.js (Vue CLI)", Some("Webpack")),
564-
("vue", "Vue.js", None),
565-
];
566-
let bundler_map = [("webpack", "Webpack"), ("rollup", "Rollup")];
567-
568-
let (framework, framework_bundler) = framework_map
569-
.iter()
570-
.find(|(keyword, _, _)| package_json.contains(keyword))
571-
.map(|(_, framework, bundler)| {
572-
(Some(framework.to_string()), bundler.map(|b| b.to_string()))
573-
})
574-
.unwrap_or((None, None));
575-
576-
let bundler = bundler_map
577-
.iter()
578-
.find(|(keyword, _)| package_json.contains(keyword))
579-
.map(|(_, bundler)| bundler.to_string())
580-
.or(framework_bundler);
581-
556+
let (framework, bundler) = infer_framework(&package_json);
582557
if let Some(framework) = framework {
583-
InfoBlock::new(" framework").value(framework).display();
558+
InfoBlock::new(" framework")
559+
.value(framework.to_string())
560+
.display();
584561
}
585562
if let Some(bundler) = bundler {
586-
InfoBlock::new(" bundler").value(bundler).display();
563+
InfoBlock::new(" bundler")
564+
.value(bundler.to_string())
565+
.display();
587566
}
588567
} else {
589568
println!("package.json not found");

tooling/cli.rs/src/init.rs

+2-1
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44

55
use std::{
66
collections::BTreeMap,
7+
env::current_dir,
78
fs::{create_dir_all, remove_dir_all, File},
89
io::Write,
910
path::{Path, PathBuf},
@@ -38,7 +39,7 @@ impl Default for Init {
3839
fn default() -> Self {
3940
Self {
4041
force: false,
41-
directory: std::env::current_dir().expect("failed to read cwd"),
42+
directory: current_dir().expect("failed to read cwd"),
4243
tauri_path: None,
4344
app_name: None,
4445
window_title: None,

tooling/cli.rs/src/main.rs

+50-11
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
pub use anyhow::Result;
66
use clap::{crate_version, load_yaml, App, AppSettings, ArgMatches};
77
use dialoguer::Input;
8+
use serde::Deserialize;
89

910
mod build;
1011
mod dev;
@@ -20,17 +21,34 @@ mod console;
2021
#[allow(dead_code)]
2122
mod dialoguer;
2223

23-
pub use helpers::Logger;
24+
use helpers::framework::{infer_from_package_json as infer_framework, Framework};
25+
26+
use std::{env::current_dir, fs::read_to_string, path::PathBuf};
27+
28+
#[derive(Deserialize)]
29+
struct PackageJson {
30+
name: Option<String>,
31+
product_name: Option<String>,
32+
}
33+
34+
#[derive(Default)]
35+
struct InitDefaults {
36+
app_name: Option<String>,
37+
framework: Option<Framework>,
38+
}
2439

2540
macro_rules! value_or_prompt {
26-
($init_runner: ident, $setter_fn: ident, $value: ident, $ci: ident, $prompt_message: expr) => {{
41+
($init_runner: ident, $setter_fn: ident, $value: ident, $ci: ident, $prompt_message: expr, $prompt_default: expr) => {{
2742
let mut init_runner = $init_runner;
2843
if let Some(value) = $value {
2944
init_runner = init_runner.$setter_fn(value);
3045
} else if !$ci {
31-
let input = Input::<String>::new()
32-
.with_prompt($prompt_message)
33-
.interact_text()?;
46+
let mut builder = Input::<String>::new();
47+
builder.with_prompt($prompt_message);
48+
if let Some(default) = $prompt_default {
49+
builder.default(default);
50+
}
51+
let input = builder.interact_text()?;
3452
init_runner = init_runner.$setter_fn(input);
3553
}
3654
init_runner
@@ -51,39 +69,60 @@ fn init_command(matches: &ArgMatches) -> Result<()> {
5169
if force {
5270
init_runner = init_runner.force();
5371
}
54-
if let Some(directory) = directory {
72+
let base_directory = if let Some(directory) = directory {
5573
init_runner = init_runner.directory(directory);
56-
}
74+
PathBuf::from(directory)
75+
} else {
76+
current_dir().expect("failed to read cwd")
77+
};
5778
if let Some(tauri_path) = tauri_path {
5879
init_runner = init_runner.tauri_path(tauri_path);
5980
}
81+
82+
let package_json_path = base_directory.join("package.json");
83+
let init_defaults = if package_json_path.exists() {
84+
let package_json_text = read_to_string(package_json_path)?;
85+
let package_json: PackageJson = serde_json::from_str(&package_json_text)?;
86+
let (framework, _) = infer_framework(&package_json_text);
87+
InitDefaults {
88+
app_name: package_json.product_name.or(package_json.name),
89+
framework,
90+
}
91+
} else {
92+
Default::default()
93+
};
94+
6095
init_runner = value_or_prompt!(
6196
init_runner,
6297
app_name,
6398
app_name,
6499
ci,
65-
"What is your app name?"
100+
"What is your app name?",
101+
init_defaults.app_name.clone()
66102
);
67103
init_runner = value_or_prompt!(
68104
init_runner,
69105
window_title,
70106
window_title,
71107
ci,
72-
"What should the window title be?"
108+
"What should the window title be?",
109+
init_defaults.app_name.clone()
73110
);
74111
init_runner = value_or_prompt!(
75112
init_runner,
76113
dist_dir,
77114
dist_dir,
78115
ci,
79-
r#"Where are your web assets (HTML/CSS/JS) located, relative to the "<current dir>/src-tauri" folder that will be created?"#
116+
r#"Where are your web assets (HTML/CSS/JS) located, relative to the "<current dir>/src-tauri" folder that will be created?"#,
117+
init_defaults.framework.as_ref().map(|f| f.dist_dir())
80118
);
81119
init_runner = value_or_prompt!(
82120
init_runner,
83121
dev_path,
84122
dev_path,
85123
ci,
86-
"What is the url of your dev server?"
124+
"What is the url of your dev server?",
125+
init_defaults.framework.map(|f| f.dev_path())
87126
);
88127

89128
init_runner.run()

0 commit comments

Comments
 (0)