Skip to content

Commit f72b93b

Browse files
refactor(cli): rewrite init command in Rust (#1382)
Co-authored-by: nothingismagick <denjell@mailscript.com>
1 parent b0d0b28 commit f72b93b

40 files changed

+373
-541
lines changed

.changes/refactor-init.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
---
2+
"tauri-cli": minor
3+
"tauri.js": minor
4+
---
5+
6+
The `init` command was rewritten in Rust.

api/rollup.config.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,7 @@ export default [
4646
resolve({
4747
// pass custom options to the resolve plugin
4848
customResolveOptions: {
49-
moduleDirectory: 'node_modules'
49+
moduleDirectories: ['node_modules']
5050
}
5151
}),
5252
typescript({
@@ -91,7 +91,7 @@ export default [
9191
resolve({
9292
// pass custom options to the resolve plugin
9393
customResolveOptions: {
94-
moduleDirectory: 'node_modules'
94+
moduleDirectories: ['node_modules']
9595
}
9696
})
9797
],

api/src/window.ts

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,11 @@ class WebviewWindowHandle {
4646
handler: EventCallback<T>
4747
): Promise<UnlistenFn> {
4848
if (this._handleTauriEvent(event, handler)) {
49-
return Promise.resolve(() => {})
49+
return Promise.resolve(() => {
50+
// eslint-disable-next-line security/detect-object-injection
51+
const listeners = this.listeners[event]
52+
listeners.splice(listeners.indexOf(handler), 1)
53+
})
5054
}
5155
return listen(event, handler)
5256
}
@@ -57,9 +61,13 @@ class WebviewWindowHandle {
5761
* @param event the event name
5862
* @param handler the event handler callback
5963
*/
60-
async once<T>(event: string, handler: EventCallback<T>): Promise<void> {
64+
async once<T>(event: string, handler: EventCallback<T>): Promise<UnlistenFn> {
6165
if (this._handleTauriEvent(event, handler)) {
62-
return Promise.resolve()
66+
return Promise.resolve(() => {
67+
// eslint-disable-next-line security/detect-object-injection
68+
const listeners = this.listeners[event]
69+
listeners.splice(listeners.indexOf(handler), 1)
70+
})
6371
}
6472
return once(event, handler)
6573
}

cli/core/Cargo.lock

Lines changed: 82 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

cli/core/Cargo.toml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,9 @@ json-patch = "0.2"
2626
schemars = "0.8"
2727
toml = "0.5.8"
2828
valico = "3.6"
29+
handlebars = "3.5"
30+
include_dir = "0.6"
31+
dialoguer = "0.8"
2932

3033
[build-dependencies]
3134
schemars = "0.8"

cli/core/src/cli.yml

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -45,11 +45,13 @@ subcommands:
4545
- init:
4646
about: Initializes a Tauri project
4747
args:
48+
- ci:
49+
long: ci
50+
about: Skip prompting for values
4851
- force:
4952
short: f
5053
long: force
51-
about: Force init to overwrite [conf|template|all]
52-
takes_value: true
54+
about: Force init to overwrite the src-tauri folder
5355
- log:
5456
short: l
5557
long: log

cli/core/src/init.rs

Lines changed: 118 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -1,27 +1,29 @@
1-
#![allow(dead_code)]
1+
use std::{
2+
collections::BTreeMap,
3+
fs::{create_dir_all, remove_dir_all, File},
4+
io::Write,
5+
path::{Path, PathBuf},
6+
};
27

3-
use std::{convert::TryFrom, path::PathBuf};
8+
use crate::helpers::Logger;
9+
use handlebars::{to_json, Handlebars};
10+
use include_dir::{include_dir, Dir};
11+
use serde::Deserialize;
412

5-
pub enum ForceType {
6-
All,
7-
Config,
8-
Template,
13+
const TEMPLATE_DIR: Dir = include_dir!("./templates");
14+
15+
#[derive(Deserialize)]
16+
struct ManifestPackage {
17+
version: String,
918
}
1019

11-
impl TryFrom<&str> for ForceType {
12-
type Error = anyhow::Error;
13-
fn try_from(value: &str) -> Result<Self, Self::Error> {
14-
match value.to_lowercase().as_str() {
15-
"all" => Ok(Self::All),
16-
"conf" => Ok(Self::Config),
17-
"template" => Ok(Self::Template),
18-
_ => Err(anyhow::anyhow!("Invalid `force` value.")),
19-
}
20-
}
20+
#[derive(Deserialize)]
21+
struct Manifest {
22+
package: ManifestPackage,
2123
}
2224

2325
pub struct Init {
24-
force: Option<ForceType>,
26+
force: bool,
2527
directory: PathBuf,
2628
tauri_path: Option<PathBuf>,
2729
app_name: Option<String>,
@@ -33,7 +35,7 @@ pub struct Init {
3335
impl Default for Init {
3436
fn default() -> Self {
3537
Self {
36-
force: None,
38+
force: false,
3739
directory: std::env::current_dir().expect("failed to read cwd"),
3840
tauri_path: None,
3941
app_name: None,
@@ -49,8 +51,8 @@ impl Init {
4951
Default::default()
5052
}
5153

52-
pub fn force(mut self, force: ForceType) -> Self {
53-
self.force = Some(force);
54+
pub fn force(mut self) -> Self {
55+
self.force = true;
5456
self
5557
}
5658

@@ -85,6 +87,101 @@ impl Init {
8587
}
8688

8789
pub fn run(self) -> crate::Result<()> {
88-
unimplemented!()
90+
let logger = Logger::new("tauri:init");
91+
let template_target_path = self.directory.join("src-tauri");
92+
if template_target_path.exists() && !self.force {
93+
logger.warn(format!(
94+
"Tauri dir ({:?}) not empty. Run `init --force template` to overwrite.",
95+
template_target_path
96+
));
97+
} else {
98+
let (tauri_dep, tauri_build_dep) = if let Some(tauri_path) = self.tauri_path {
99+
(
100+
format!(
101+
"{{ path = {:?} }}",
102+
resolve_tauri_path(&tauri_path, "tauri")
103+
),
104+
format!(
105+
"{{ path = {:?} }}",
106+
resolve_tauri_path(&tauri_path, "core/tauri-build")
107+
),
108+
)
109+
} else {
110+
let tauri_manifest: Manifest =
111+
toml::from_str(include_str!("../../../tauri/Cargo.toml")).unwrap();
112+
let tauri_build_manifest: Manifest =
113+
toml::from_str(include_str!("../../../core/tauri-build/Cargo.toml")).unwrap();
114+
(
115+
format!(r#"{{ version = "{}" }}"#, tauri_manifest.package.version),
116+
format!(
117+
r#"{{ version = "{}" }}"#,
118+
tauri_build_manifest.package.version
119+
),
120+
)
121+
};
122+
123+
let _ = remove_dir_all(&template_target_path);
124+
let handlebars = Handlebars::new();
125+
126+
let mut data = BTreeMap::new();
127+
data.insert("tauri_dep", to_json(tauri_dep));
128+
data.insert("tauri_build_dep", to_json(tauri_build_dep));
129+
data.insert(
130+
"dist_dir",
131+
to_json(self.dist_dir.unwrap_or_else(|| "../dist".to_string())),
132+
);
133+
data.insert(
134+
"dev_path",
135+
to_json(
136+
self
137+
.dev_path
138+
.unwrap_or_else(|| "http://localhost:4000".to_string()),
139+
),
140+
);
141+
data.insert(
142+
"app_name",
143+
to_json(self.app_name.unwrap_or_else(|| "Tauri App".to_string())),
144+
);
145+
data.insert(
146+
"window_title",
147+
to_json(self.window_title.unwrap_or_else(|| "Tauri".to_string())),
148+
);
149+
150+
render_template(&handlebars, &data, &TEMPLATE_DIR, &self.directory)?;
151+
}
152+
153+
Ok(())
154+
}
155+
}
156+
157+
fn render_template<P: AsRef<Path>>(
158+
handlebars: &Handlebars,
159+
data: &BTreeMap<&str, serde_json::Value>,
160+
dir: &Dir,
161+
out_dir: P,
162+
) -> crate::Result<()> {
163+
create_dir_all(out_dir.as_ref().join(dir.path()))?;
164+
for file in dir.files() {
165+
let mut output_file = File::create(out_dir.as_ref().join(file.path()))?;
166+
if let Some(utf8) = file.contents_utf8() {
167+
handlebars
168+
.render_template_to_write(utf8, &data, &mut output_file)
169+
.expect("Failed to render template");
170+
} else {
171+
output_file.write_all(file.contents())?;
172+
}
173+
}
174+
for dir in dir.dirs() {
175+
render_template(handlebars, data, dir, out_dir.as_ref())?;
176+
}
177+
Ok(())
178+
}
179+
180+
fn resolve_tauri_path<P: AsRef<Path>>(path: P, crate_name: &str) -> PathBuf {
181+
let path = path.as_ref();
182+
if path.is_absolute() {
183+
path.join(crate_name)
184+
} else {
185+
PathBuf::from("..").join(path).join(crate_name)
89186
}
90187
}

0 commit comments

Comments
 (0)