Skip to content

Commit

Permalink
refactor(cli): rewrite init command in Rust (#1382)
Browse files Browse the repository at this point in the history
Co-authored-by: nothingismagick <denjell@mailscript.com>
  • Loading branch information
lucasfernog and nothingismagick authored Mar 24, 2021
1 parent b0d0b28 commit f72b93b
Show file tree
Hide file tree
Showing 40 changed files with 373 additions and 541 deletions.
6 changes: 6 additions & 0 deletions .changes/refactor-init.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
---
"tauri-cli": minor
"tauri.js": minor
---

The `init` command was rewritten in Rust.
4 changes: 2 additions & 2 deletions api/rollup.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ export default [
resolve({
// pass custom options to the resolve plugin
customResolveOptions: {
moduleDirectory: 'node_modules'
moduleDirectories: ['node_modules']
}
}),
typescript({
Expand Down Expand Up @@ -91,7 +91,7 @@ export default [
resolve({
// pass custom options to the resolve plugin
customResolveOptions: {
moduleDirectory: 'node_modules'
moduleDirectories: ['node_modules']
}
})
],
Expand Down
14 changes: 11 additions & 3 deletions api/src/window.ts
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,11 @@ class WebviewWindowHandle {
handler: EventCallback<T>
): Promise<UnlistenFn> {
if (this._handleTauriEvent(event, handler)) {
return Promise.resolve(() => {})
return Promise.resolve(() => {
// eslint-disable-next-line security/detect-object-injection
const listeners = this.listeners[event]
listeners.splice(listeners.indexOf(handler), 1)
})
}
return listen(event, handler)
}
Expand All @@ -57,9 +61,13 @@ class WebviewWindowHandle {
* @param event the event name
* @param handler the event handler callback
*/
async once<T>(event: string, handler: EventCallback<T>): Promise<void> {
async once<T>(event: string, handler: EventCallback<T>): Promise<UnlistenFn> {
if (this._handleTauriEvent(event, handler)) {
return Promise.resolve()
return Promise.resolve(() => {
// eslint-disable-next-line security/detect-object-injection
const listeners = this.listeners[event]
listeners.splice(listeners.indexOf(handler), 1)
})
}
return once(event, handler)
}
Expand Down
82 changes: 82 additions & 0 deletions cli/core/Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 3 additions & 0 deletions cli/core/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,9 @@ json-patch = "0.2"
schemars = "0.8"
toml = "0.5.8"
valico = "3.6"
handlebars = "3.5"
include_dir = "0.6"
dialoguer = "0.8"

[build-dependencies]
schemars = "0.8"
Expand Down
6 changes: 4 additions & 2 deletions cli/core/src/cli.yml
Original file line number Diff line number Diff line change
Expand Up @@ -45,11 +45,13 @@ subcommands:
- init:
about: Initializes a Tauri project
args:
- ci:
long: ci
about: Skip prompting for values
- force:
short: f
long: force
about: Force init to overwrite [conf|template|all]
takes_value: true
about: Force init to overwrite the src-tauri folder
- log:
short: l
long: log
Expand Down
139 changes: 118 additions & 21 deletions cli/core/src/init.rs
Original file line number Diff line number Diff line change
@@ -1,27 +1,29 @@
#![allow(dead_code)]
use std::{
collections::BTreeMap,
fs::{create_dir_all, remove_dir_all, File},
io::Write,
path::{Path, PathBuf},
};

use std::{convert::TryFrom, path::PathBuf};
use crate::helpers::Logger;
use handlebars::{to_json, Handlebars};
use include_dir::{include_dir, Dir};
use serde::Deserialize;

pub enum ForceType {
All,
Config,
Template,
const TEMPLATE_DIR: Dir = include_dir!("./templates");

#[derive(Deserialize)]
struct ManifestPackage {
version: String,
}

impl TryFrom<&str> for ForceType {
type Error = anyhow::Error;
fn try_from(value: &str) -> Result<Self, Self::Error> {
match value.to_lowercase().as_str() {
"all" => Ok(Self::All),
"conf" => Ok(Self::Config),
"template" => Ok(Self::Template),
_ => Err(anyhow::anyhow!("Invalid `force` value.")),
}
}
#[derive(Deserialize)]
struct Manifest {
package: ManifestPackage,
}

pub struct Init {
force: Option<ForceType>,
force: bool,
directory: PathBuf,
tauri_path: Option<PathBuf>,
app_name: Option<String>,
Expand All @@ -33,7 +35,7 @@ pub struct Init {
impl Default for Init {
fn default() -> Self {
Self {
force: None,
force: false,
directory: std::env::current_dir().expect("failed to read cwd"),
tauri_path: None,
app_name: None,
Expand All @@ -49,8 +51,8 @@ impl Init {
Default::default()
}

pub fn force(mut self, force: ForceType) -> Self {
self.force = Some(force);
pub fn force(mut self) -> Self {
self.force = true;
self
}

Expand Down Expand Up @@ -85,6 +87,101 @@ impl Init {
}

pub fn run(self) -> crate::Result<()> {
unimplemented!()
let logger = Logger::new("tauri:init");
let template_target_path = self.directory.join("src-tauri");
if template_target_path.exists() && !self.force {
logger.warn(format!(
"Tauri dir ({:?}) not empty. Run `init --force template` to overwrite.",
template_target_path
));
} else {
let (tauri_dep, tauri_build_dep) = if let Some(tauri_path) = self.tauri_path {
(
format!(
"{{ path = {:?} }}",
resolve_tauri_path(&tauri_path, "tauri")
),
format!(
"{{ path = {:?} }}",
resolve_tauri_path(&tauri_path, "core/tauri-build")
),
)
} else {
let tauri_manifest: Manifest =
toml::from_str(include_str!("../../../tauri/Cargo.toml")).unwrap();
let tauri_build_manifest: Manifest =
toml::from_str(include_str!("../../../core/tauri-build/Cargo.toml")).unwrap();
(
format!(r#"{{ version = "{}" }}"#, tauri_manifest.package.version),
format!(
r#"{{ version = "{}" }}"#,
tauri_build_manifest.package.version
),
)
};

let _ = remove_dir_all(&template_target_path);
let handlebars = Handlebars::new();

let mut data = BTreeMap::new();
data.insert("tauri_dep", to_json(tauri_dep));
data.insert("tauri_build_dep", to_json(tauri_build_dep));
data.insert(
"dist_dir",
to_json(self.dist_dir.unwrap_or_else(|| "../dist".to_string())),
);
data.insert(
"dev_path",
to_json(
self
.dev_path
.unwrap_or_else(|| "http://localhost:4000".to_string()),
),
);
data.insert(
"app_name",
to_json(self.app_name.unwrap_or_else(|| "Tauri App".to_string())),
);
data.insert(
"window_title",
to_json(self.window_title.unwrap_or_else(|| "Tauri".to_string())),
);

render_template(&handlebars, &data, &TEMPLATE_DIR, &self.directory)?;
}

Ok(())
}
}

fn render_template<P: AsRef<Path>>(
handlebars: &Handlebars,
data: &BTreeMap<&str, serde_json::Value>,
dir: &Dir,
out_dir: P,
) -> crate::Result<()> {
create_dir_all(out_dir.as_ref().join(dir.path()))?;
for file in dir.files() {
let mut output_file = File::create(out_dir.as_ref().join(file.path()))?;
if let Some(utf8) = file.contents_utf8() {
handlebars
.render_template_to_write(utf8, &data, &mut output_file)
.expect("Failed to render template");
} else {
output_file.write_all(file.contents())?;
}
}
for dir in dir.dirs() {
render_template(handlebars, data, dir, out_dir.as_ref())?;
}
Ok(())
}

fn resolve_tauri_path<P: AsRef<Path>>(path: P, crate_name: &str) -> PathBuf {
let path = path.as_ref();
if path.is_absolute() {
path.join(crate_name)
} else {
PathBuf::from("..").join(path).join(crate_name)
}
}
Loading

0 comments on commit f72b93b

Please sign in to comment.