Skip to content

Commit 7b992e5

Browse files
authored
feat: show missing deps after template bootstrap (#367)
1 parent 7bfdcaa commit 7b992e5

13 files changed

Lines changed: 220 additions & 45 deletions

File tree

.changes/deps.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
---
2+
"create-tauri-app": "patch"
3+
"create-tauri-app-js": "patch"
4+
---
5+
6+
Show a table of missing dependencies with installation instructions.

packages/cli/fragments/fragment-leptos/%(mobile)%Trunk.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,3 +8,4 @@ ignore = ["./src-tauri"]
88
address = "0.0.0.0"
99
port = 1420
1010
open = false
11+
ws_protocol = "ws"

packages/cli/fragments/fragment-sycamore/%(mobile)%Trunk.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,3 +8,4 @@ ignore = ["./src-tauri"]
88
address = "0.0.0.0"
99
port = 1420
1010
open = false
11+
ws_protocol = "ws"

packages/cli/fragments/fragment-yew/%(mobile)%Trunk.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,3 +8,4 @@ ignore = ["./src-tauri"]
88
address = "0.0.0.0"
99
port = 1420
1010
open = false
11+
ws_protocol = "ws"

packages/cli/node/create-tauri-app.js

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,10 +13,12 @@ const binStem = path.parse(bin).name.toLowerCase();
1313
// We want to make a helpful binary name for the underlying CLI helper, if we
1414
// can successfully detect what command likely started the execution.
1515
let binName;
16-
16+
if (bin === "@tauri-apps/cli") {
17+
binName = "@tauri-apps/cli";
18+
}
1719
// Even if started by a package manager, the binary will be NodeJS.
1820
// Some distribution still use "nodejs" as the binary name.
19-
if (binStem.match(/(nodejs|node)([1-9]*)*$/g)) {
21+
if (binStem.match(/(nodejs|node)-*([1-9]*)*$/g)) {
2022
const managerStem = process.env.npm_execpath
2123
? path.parse(process.env.npm_execpath).name.toLowerCase()
2224
: null;

packages/cli/src/cli.rs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -62,17 +62,17 @@ pub fn parse(argv: Vec<OsString>, bin_name: Option<String>) -> anyhow::Result<Ar
6262
desc = env!("CARGO_PKG_DESCRIPTION"),
6363
managers = PackageManager::ALL
6464
.iter()
65-
.map(|e| format!("{}{}{}", GREEN, e, RESET))
65+
.map(|e| format!("{GREEN}{e}{RESET}"))
6666
.collect::<Vec<_>>()
6767
.join(", "),
6868
fragments = Template::ALL
6969
.iter()
70-
.map(|e| format!("{}{}{}", GREEN, e, RESET))
70+
.map(|e| format!("{GREEN}{e}{RESET}"))
7171
.collect::<Vec<_>>()
7272
.join(", "),
7373
);
7474

75-
println!("{}", help);
75+
println!("{help}");
7676
std::process::exit(0);
7777
}
7878
if pargs.contains(["-v", "--version"]) {

packages/cli/src/colors.rs

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,9 @@
22
// SPDX-License-Identifier: Apache-2.0
33
// SPDX-License-Identifier: MIT
44

5+
#![allow(unused)]
6+
7+
pub const BLACK: &str = "\x1b[30m";
58
pub const RED: &str = "\x1b[31m";
69
pub const GREEN: &str = "\x1b[32m";
710
pub const YELLOW: &str = "\x1b[33m";
@@ -12,3 +15,17 @@ pub const BOLD: &str = "\x1b[1m";
1215
pub const ITALIC: &str = "\x1b[3m";
1316
pub const DIM: &str = "\x1b[2m";
1417
pub const DIMRESET: &str = "\x1b[22m";
18+
19+
pub fn remove_colors(s: &str) -> String {
20+
s.replace(BLACK, "")
21+
.replace(RED, "")
22+
.replace(GREEN, "")
23+
.replace(YELLOW, "")
24+
.replace(BLUE, "")
25+
.replace(WHITE, "")
26+
.replace(RESET, "")
27+
.replace(BOLD, "")
28+
.replace(ITALIC, "")
29+
.replace(DIM, "")
30+
.replace(DIMRESET, "")
31+
}

packages/cli/src/deps.rs

Lines changed: 154 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,154 @@
1+
use template::Template;
2+
3+
use crate::colors::*;
4+
use crate::internal::template;
5+
use crate::package_manager::PackageManager;
6+
use std::process::Command;
7+
8+
fn is_rustc_installed() -> bool {
9+
Command::new("rustc").arg("-V").output().is_ok()
10+
}
11+
fn is_cargo_installed() -> bool {
12+
Command::new("cargo").arg("-V").output().is_ok()
13+
}
14+
fn is_node_installed() -> bool {
15+
Command::new("node").arg("-v").output().is_ok()
16+
}
17+
18+
fn is_trunk_installed() -> bool {
19+
Command::new("trunk").arg("-V").output().is_ok()
20+
}
21+
fn is_tauri_cli_installed() -> bool {
22+
Command::new("cargo")
23+
.arg("tauri")
24+
.arg("-V")
25+
.output()
26+
.map(|o| {
27+
let s = String::from_utf8_lossy(&o.stderr);
28+
!s.starts_with("error:")
29+
})
30+
.unwrap_or(false)
31+
}
32+
fn is_wasm32_installed() -> bool {
33+
Command::new("rustup")
34+
.args(["target", "list", "--installed"])
35+
.output()
36+
.map(|o| {
37+
let s = String::from_utf8_lossy(&o.stdout);
38+
s.contains("wasm32-unknown-unknown")
39+
})
40+
.unwrap_or(false)
41+
}
42+
43+
pub fn print_missing_deps(pkg_manager: PackageManager, template: Template, alpha: bool) {
44+
let rustc_installed = is_rustc_installed();
45+
let cargo_installed = is_cargo_installed();
46+
let deps: &[(&str, String, &dyn Fn() -> bool, bool)] = &[
47+
(
48+
"Rust",
49+
format!("Visit {BLUE}{BOLD}https://www.rust-lang.org/learn/get-started#installing-rust{RESET}"),
50+
&|| rustc_installed && cargo_installed,
51+
rustc_installed || cargo_installed,
52+
),
53+
(
54+
"rustc",
55+
format!("Visit {BLUE}{BOLD}https://www.rust-lang.org/learn/get-started#installing-rust{RESET} to install Rust"),
56+
&|| rustc_installed,
57+
!rustc_installed && !cargo_installed,
58+
),
59+
(
60+
"Cargo",
61+
format!("Visit {BLUE}{BOLD}https://www.rust-lang.org/learn/get-started#installing-rust{RESET} to install Rust"),
62+
&|| cargo_installed,
63+
!rustc_installed && !cargo_installed,
64+
),
65+
(
66+
"Tauri CLI",
67+
if alpha {
68+
format!("Run `{BLUE}{BOLD}cargo install tauri-cli --version 2.0.0-alpha.2{RESET}`")
69+
} else {
70+
format!("Run `{BLUE}{BOLD}cargo install tauri-cli{RESET}`")
71+
},
72+
&is_tauri_cli_installed,
73+
pkg_manager.is_node() || !template.needs_tauri_cli(),
74+
),
75+
(
76+
"Trunk",
77+
if alpha {
78+
format!("Run `{BLUE}{BOLD}cargo install trunk --git https://github.com/amrbashir/trunk{RESET}`")
79+
} else {
80+
format!("Visit {BLUE}{BOLD}https://trunkrs.dev/#install{RESET}")
81+
},
82+
&is_trunk_installed,
83+
pkg_manager.is_node() || !template.needs_trunk(),
84+
),
85+
(
86+
"wasm32 target",
87+
format!("Run `{BLUE}{BOLD}rustup target add wasm32-unknown-unknown{RESET}`"),
88+
&is_wasm32_installed,
89+
pkg_manager.is_node() || !template.needs_wasm32_target(),
90+
),
91+
(
92+
"Node.js",
93+
format!("Visit {BLUE}{BOLD}https://nodejs.org/en/{RESET}"),
94+
&is_node_installed,
95+
!pkg_manager.is_node(),
96+
),
97+
];
98+
99+
let missing_deps: Vec<(String, String)> = deps
100+
.iter()
101+
.filter(|(_, _, exists, skip)| !skip && !exists())
102+
.map(|(s, d, _, _)| (s.to_string(), d.clone()))
103+
.collect();
104+
105+
let (largest_first_cell, largest_second_cell) =
106+
missing_deps
107+
.iter()
108+
.fold((0, 0), |(mut prev_f, mut prev_s), (f, s)| {
109+
let f_len = f.len();
110+
if f_len > prev_f {
111+
prev_f = f_len;
112+
}
113+
114+
let s_len = remove_colors(s).len();
115+
if s_len > prev_s {
116+
prev_s = s_len;
117+
}
118+
119+
(prev_f, prev_s)
120+
});
121+
122+
if !missing_deps.is_empty() {
123+
println!("\n\nYour system is {YELLOW}missing dependencies{RESET} (or they do not exist in {YELLOW}$PATH{RESET}):");
124+
for (index, (name, instruction)) in missing_deps.iter().enumerate() {
125+
if index == 0 {
126+
println!(
127+
"╭{}┬{}╮",
128+
"─".repeat(largest_first_cell + 2),
129+
"─".repeat(largest_second_cell + 2)
130+
);
131+
} else {
132+
println!(
133+
"├{}┼{}┤",
134+
"─".repeat(largest_first_cell + 2),
135+
"─".repeat(largest_second_cell + 2)
136+
);
137+
}
138+
println!(
139+
"│ {YELLOW}{name}{RESET}{} │ {instruction}{} │",
140+
" ".repeat(largest_first_cell - name.len()),
141+
" ".repeat(largest_second_cell - remove_colors(instruction).len()),
142+
);
143+
}
144+
println!(
145+
"╰{}┴{}╯",
146+
"─".repeat(largest_first_cell + 2),
147+
"─".repeat(largest_second_cell + 2),
148+
);
149+
println!();
150+
println!("Make sure you have installed the prerequisites for your OS: {BLUE}{BOLD}https://tauri.app/v1/guides/getting-started/prerequisites{RESET}, then run:");
151+
} else {
152+
println!(" To get started run:")
153+
}
154+
}

packages/cli/src/lib.rs

Lines changed: 11 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -5,11 +5,15 @@
55
use dialoguer::{Confirm, Input, Select};
66
use std::{ffi::OsString, fs, process::exit};
77

8-
use crate::{category::Category, colors::*, package_manager::PackageManager, theme::ColorfulTheme};
8+
use crate::{
9+
category::Category, colors::*, deps::print_missing_deps, package_manager::PackageManager,
10+
theme::ColorfulTheme,
11+
};
912

1013
mod category;
1114
mod cli;
1215
mod colors;
16+
mod deps;
1317
mod manifest;
1418
mod package_manager;
1519
mod template;
@@ -257,7 +261,7 @@ where
257261
eprintln!(
258262
"{BOLD}{RED}error{RESET}: the {GREEN}{}{RESET} template is not suppported for the {GREEN}{pkg_manager}{RESET} package manager\n possible templates for {GREEN}{pkg_manager}{RESET} are: [{}]",
259263
template,
260-
templates.iter().map(|e|format!("{GREEN}{}{RESET}", e, GREEN = GREEN, RESET = RESET)).collect::<Vec<_>>().join(", ")
264+
templates.iter().map(|e|format!("{GREEN}{e}{RESET}")).collect::<Vec<_>>().join(", ")
261265
);
262266
exit(1);
263267
}
@@ -276,27 +280,22 @@ where
276280
template.render(&target_dir, pkg_manager, &package_name, alpha, mobile)?;
277281

278282
// Print post-render instructions
283+
279284
println!();
280-
println!(
281-
"{ITALIC}{DIM}Please follow{DIMRESET} {BLUE}https://tauri.app/v1/guides/getting-started/prerequisites{WHITE} {DIM}to install the needed prerequisites, if you haven't already.{DIMRESET}{RESET}",
282-
);
283-
if let Some(info) = template.post_init_info(pkg_manager, alpha) {
284-
println!("{}", info);
285-
}
286-
println!();
287-
println!("Done, now run:");
285+
print!("Template created!");
286+
print_missing_deps(pkg_manager, template, alpha);
288287
if target_dir != cwd {
289288
println!(
290289
" cd {}",
291290
if project_name.contains(' ') {
292-
format!("\"{}\"", project_name)
291+
format!("\"{project_name}\"")
293292
} else {
294293
project_name
295294
}
296295
);
297296
}
298297
if let Some(cmd) = pkg_manager.install_cmd() {
299-
println!(" {}", cmd);
298+
println!(" {cmd}");
300299
}
301300
if !mobile {
302301
println!(" {} tauri dev", pkg_manager.run_cmd());

packages/cli/src/package_manager.rs

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -91,6 +91,13 @@ impl PackageManager {
9191
PackageManager::Npm => "npm run",
9292
}
9393
}
94+
95+
pub const fn is_node(&self) -> bool {
96+
matches!(
97+
self,
98+
PackageManager::Pnpm | PackageManager::Yarn | PackageManager::Npm,
99+
)
100+
}
94101
}
95102

96103
impl Display for PackageManager {

0 commit comments

Comments
 (0)