瀏覽代碼

refactor(cli): rewrite init command in Rust (#1382)

Co-authored-by: nothingismagick <denjell@mailscript.com>
Lucas Fernandes Nogueira 4 年之前
父節點
當前提交
f72b93b676
共有 40 個文件被更改,包括 372 次插入540 次删除
  1. 6 0
      .changes/refactor-init.md
  2. 2 2
      api/rollup.config.js
  3. 11 3
      api/src/window.ts
  4. 82 0
      cli/core/Cargo.lock
  5. 3 0
      cli/core/Cargo.toml
  6. 4 2
      cli/core/src/cli.yml
  7. 118 21
      cli/core/src/init.rs
  8. 53 21
      cli/core/src/main.rs
  9. 0 0
      cli/core/templates/src-tauri/.gitignore
  10. 2 2
      cli/core/templates/src-tauri/Cargo.toml
  11. 0 0
      cli/core/templates/src-tauri/icons/128x128.png
  12. 0 0
      cli/core/templates/src-tauri/icons/128x128@2x.png
  13. 0 0
      cli/core/templates/src-tauri/icons/32x32.png
  14. 0 0
      cli/core/templates/src-tauri/icons/Square107x107Logo.png
  15. 0 0
      cli/core/templates/src-tauri/icons/Square142x142Logo.png
  16. 0 0
      cli/core/templates/src-tauri/icons/Square150x150Logo.png
  17. 0 0
      cli/core/templates/src-tauri/icons/Square284x284Logo.png
  18. 0 0
      cli/core/templates/src-tauri/icons/Square30x30Logo.png
  19. 0 0
      cli/core/templates/src-tauri/icons/Square310x310Logo.png
  20. 0 0
      cli/core/templates/src-tauri/icons/Square44x44Logo.png
  21. 0 0
      cli/core/templates/src-tauri/icons/Square71x71Logo.png
  22. 0 0
      cli/core/templates/src-tauri/icons/Square89x89Logo.png
  23. 0 0
      cli/core/templates/src-tauri/icons/StoreLogo.png
  24. 0 0
      cli/core/templates/src-tauri/icons/icon.icns
  25. 0 0
      cli/core/templates/src-tauri/icons/icon.ico
  26. 0 0
      cli/core/templates/src-tauri/icons/icon.png
  27. 0 0
      cli/core/templates/src-tauri/rustfmt.toml
  28. 0 0
      cli/core/templates/src-tauri/src/build.rs
  29. 0 0
      cli/core/templates/src-tauri/src/main.rs
  30. 57 0
      cli/core/templates/src-tauri/tauri.conf.json
  31. 0 157
      cli/tauri.js/bin/tauri-init.js
  32. 11 8
      cli/tauri.js/bin/tauri.js
  33. 17 9
      cli/tauri.js/src/api/cli.ts
  34. 0 43
      cli/tauri.js/src/api/init.ts
  35. 0 58
      cli/tauri.js/src/template/defaultConfig.ts
  36. 0 138
      cli/tauri.js/src/template/index.ts
  37. 0 69
      cli/tauri.js/templates/updater.rs
  38. 6 6
      cli/tauri.js/test/jest/__tests__/template.spec.js
  39. 0 1
      cli/tauri.js/webpack.config.js
  40. 0 0
      tauri/scripts/bundle.js

+ 6 - 0
.changes/refactor-init.md

@@ -0,0 +1,6 @@
+---
+"tauri-cli": minor
+"tauri.js": minor
+---
+
+The `init` command was rewritten in Rust.

+ 2 - 2
api/rollup.config.js

@@ -46,7 +46,7 @@ export default [
       resolve({
         // pass custom options to the resolve plugin
         customResolveOptions: {
-          moduleDirectory: 'node_modules'
+          moduleDirectories: ['node_modules']
         }
       }),
       typescript({
@@ -91,7 +91,7 @@ export default [
       resolve({
         // pass custom options to the resolve plugin
         customResolveOptions: {
-          moduleDirectory: 'node_modules'
+          moduleDirectories: ['node_modules']
         }
       })
     ],

+ 11 - 3
api/src/window.ts

@@ -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)
   }
@@ -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)
   }

+ 82 - 0
cli/core/Cargo.lock

@@ -252,6 +252,21 @@ dependencies = [
  "unreachable",
 ]
 
+[[package]]
+name = "console"
+version = "0.14.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3993e6445baa160675931ec041a5e03ca84b9c6e32a056150d3aa2bdda0a1f45"
+dependencies = [
+ "encode_unicode",
+ "lazy_static",
+ "libc",
+ "regex",
+ "terminal_size",
+ "unicode-width",
+ "winapi 0.3.9",
+]
+
 [[package]]
 name = "core-foundation"
 version = "0.9.1"
@@ -373,6 +388,18 @@ dependencies = [
  "byteorder",
 ]
 
+[[package]]
+name = "dialoguer"
+version = "0.8.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c9dd058f8b65922819fabb4a41e7d1964e56344042c26efbccd465202c23fa0c"
+dependencies = [
+ "console",
+ "lazy_static",
+ "tempfile",
+ "zeroize",
+]
+
 [[package]]
 name = "digest"
 version = "0.8.1"
@@ -424,6 +451,12 @@ version = "1.6.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "e78d4f1cc4ae33bbfc157ed5d5a5ef3bc29227303d595861deb238fcec4e9457"
 
+[[package]]
+name = "encode_unicode"
+version = "0.3.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a357d28ed41a50f9c765dbfe56cbc04a64e53e5fc58ba79fbc34c10ef3df831f"
+
 [[package]]
 name = "fake-simd"
 version = "0.1.2"
@@ -678,6 +711,30 @@ dependencies = [
  "tiff",
 ]
 
+[[package]]
+name = "include_dir"
+version = "0.6.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "23d58bdeb22b1c4691106c084b1063781904c35d0f22eda2a283598968eac61a"
+dependencies = [
+ "glob",
+ "include_dir_impl",
+ "proc-macro-hack",
+]
+
+[[package]]
+name = "include_dir_impl"
+version = "0.6.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "327869970574819d24d1dca25c891856144d29159ab797fa9dc725c5c3f57215"
+dependencies = [
+ "anyhow",
+ "proc-macro-hack",
+ "proc-macro2",
+ "quote",
+ "syn",
+]
+
 [[package]]
 name = "indexmap"
 version = "1.6.1"
@@ -1208,6 +1265,12 @@ dependencies = [
  "version_check",
 ]
 
+[[package]]
+name = "proc-macro-hack"
+version = "0.5.19"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "dbf0c48bc1d91375ae5c3cd81e3722dff1abcf81a30960240640d223f59fe0e5"
+
 [[package]]
 name = "proc-macro2"
 version = "1.0.24"
@@ -1677,7 +1740,10 @@ dependencies = [
  "anyhow",
  "clap",
  "colored",
+ "dialoguer",
+ "handlebars",
  "heck",
+ "include_dir",
  "json-patch",
  "notify",
  "once_cell",
@@ -1716,6 +1782,16 @@ dependencies = [
  "winapi-util",
 ]
 
+[[package]]
+name = "terminal_size"
+version = "0.1.16"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "86ca8ced750734db02076f44132d802af0b33b09942331f4459dde8636fd2406"
+dependencies = [
+ "libc",
+ "winapi 0.3.9",
+]
+
 [[package]]
 name = "textwrap"
 version = "0.12.1"
@@ -2069,6 +2145,12 @@ dependencies = [
  "linked-hash-map",
 ]
 
+[[package]]
+name = "zeroize"
+version = "1.2.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "81a974bcdd357f0dca4d41677db03436324d45a4c9ed2d0b873a5a360ce41c36"
+
 [[package]]
 name = "zip"
 version = "0.5.11"

+ 3 - 0
cli/core/Cargo.toml

@@ -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"

+ 4 - 2
cli/core/src/cli.yml

@@ -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

+ 118 - 21
cli/core/src/init.rs

@@ -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>,
@@ -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,
@@ -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
   }
 
@@ -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)
   }
 }

+ 53 - 21
cli/core/src/main.rs

@@ -1,6 +1,6 @@
 pub use anyhow::Result;
 use clap::{crate_version, load_yaml, App, AppSettings, ArgMatches};
-use std::convert::TryInto;
+use dialoguer::Input;
 
 mod build;
 mod dev;
@@ -10,18 +10,34 @@ mod init;
 
 pub use helpers::Logger;
 
+macro_rules! value_or_prompt {
+  ($init_runner: ident, $setter_fn: ident, $value: ident, $ci: ident, $prompt_message: expr) => {{
+    let mut init_runner = $init_runner;
+    if let Some(value) = $value {
+      init_runner = init_runner.$setter_fn(value);
+    } else if !$ci {
+      let input = Input::<String>::new()
+        .with_prompt($prompt_message)
+        .interact_text()?;
+      init_runner = init_runner.$setter_fn(input);
+    }
+    init_runner
+  }};
+}
+
 fn init_command(matches: &ArgMatches) -> Result<()> {
-  let force = matches.value_of("force");
+  let force = matches.is_present("force");
   let directory = matches.value_of("directory");
-  let tauri_path = matches.value_of("tauri_path");
-  let app_name = matches.value_of("app_name");
-  let window_title = matches.value_of("window_title");
-  let dist_dir = matches.value_of("dist_dir");
-  let dev_path = matches.value_of("dev_path");
+  let tauri_path = matches.value_of("tauri-path");
+  let app_name = matches.value_of("app-name");
+  let window_title = matches.value_of("window-title");
+  let dist_dir = matches.value_of("dist-dir");
+  let dev_path = matches.value_of("dev-path");
+  let ci = matches.is_present("ci") || std::env::var("CI").is_ok();
 
   let mut init_runner = init::Init::new();
-  if let Some(force) = force {
-    init_runner = init_runner.force(force.try_into()?);
+  if force {
+    init_runner = init_runner.force();
   }
   if let Some(directory) = directory {
     init_runner = init_runner.directory(directory);
@@ -29,18 +45,34 @@ fn init_command(matches: &ArgMatches) -> Result<()> {
   if let Some(tauri_path) = tauri_path {
     init_runner = init_runner.tauri_path(tauri_path);
   }
-  if let Some(app_name) = app_name {
-    init_runner = init_runner.app_name(app_name);
-  }
-  if let Some(window_title) = window_title {
-    init_runner = init_runner.window_title(window_title);
-  }
-  if let Some(dist_dir) = dist_dir {
-    init_runner = init_runner.dist_dir(dist_dir);
-  }
-  if let Some(dev_path) = dev_path {
-    init_runner = init_runner.directory(dev_path);
-  }
+  init_runner = value_or_prompt!(
+    init_runner,
+    app_name,
+    app_name,
+    ci,
+    "What is your app name?"
+  );
+  init_runner = value_or_prompt!(
+    init_runner,
+    window_title,
+    window_title,
+    ci,
+    "What should the window title be?"
+  );
+  init_runner = value_or_prompt!(
+    init_runner,
+    dist_dir,
+    dist_dir,
+    ci,
+    r#"Where are your web assets (HTML/CSS/JS) located, relative to the "<current dir>/src-tauri" folder that will be created?"#
+  );
+  init_runner = value_or_prompt!(
+    init_runner,
+    dev_path,
+    dev_path,
+    ci,
+    "What is the url of your dev server?"
+  );
 
   init_runner.run()
 }

+ 0 - 0
cli/tauri.js/templates/src-tauri/_gitignore → cli/core/templates/src-tauri/.gitignore


+ 2 - 2
cli/tauri.js/templates/src-tauri/Cargo.toml → cli/core/templates/src-tauri/Cargo.toml

@@ -12,12 +12,12 @@ build = "src/build.rs"
 # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
 
 [build-dependencies]
-tauri-build = <%= tauriBuildDep %>
+tauri-build = {{{  tauri_build_dep  }}}
 
 [dependencies]
 serde_json = "1.0"
 serde = { version = "1.0", features = ["derive"] }
-tauri = <%= tauriDep %>
+tauri = {{{  tauri_dep  }}}
 
 [features]
 default = [ "custom-protocol" ]

+ 0 - 0
cli/tauri.js/templates/src-tauri/icons/128x128.png → cli/core/templates/src-tauri/icons/128x128.png


+ 0 - 0
cli/tauri.js/templates/src-tauri/icons/128x128@2x.png → cli/core/templates/src-tauri/icons/128x128@2x.png


+ 0 - 0
cli/tauri.js/templates/src-tauri/icons/32x32.png → cli/core/templates/src-tauri/icons/32x32.png


+ 0 - 0
cli/tauri.js/templates/src-tauri/icons/Square107x107Logo.png → cli/core/templates/src-tauri/icons/Square107x107Logo.png


+ 0 - 0
cli/tauri.js/templates/src-tauri/icons/Square142x142Logo.png → cli/core/templates/src-tauri/icons/Square142x142Logo.png


+ 0 - 0
cli/tauri.js/templates/src-tauri/icons/Square150x150Logo.png → cli/core/templates/src-tauri/icons/Square150x150Logo.png


+ 0 - 0
cli/tauri.js/templates/src-tauri/icons/Square284x284Logo.png → cli/core/templates/src-tauri/icons/Square284x284Logo.png


+ 0 - 0
cli/tauri.js/templates/src-tauri/icons/Square30x30Logo.png → cli/core/templates/src-tauri/icons/Square30x30Logo.png


+ 0 - 0
cli/tauri.js/templates/src-tauri/icons/Square310x310Logo.png → cli/core/templates/src-tauri/icons/Square310x310Logo.png


+ 0 - 0
cli/tauri.js/templates/src-tauri/icons/Square44x44Logo.png → cli/core/templates/src-tauri/icons/Square44x44Logo.png


+ 0 - 0
cli/tauri.js/templates/src-tauri/icons/Square71x71Logo.png → cli/core/templates/src-tauri/icons/Square71x71Logo.png


+ 0 - 0
cli/tauri.js/templates/src-tauri/icons/Square89x89Logo.png → cli/core/templates/src-tauri/icons/Square89x89Logo.png


+ 0 - 0
cli/tauri.js/templates/src-tauri/icons/StoreLogo.png → cli/core/templates/src-tauri/icons/StoreLogo.png


+ 0 - 0
cli/tauri.js/templates/src-tauri/icons/icon.icns → cli/core/templates/src-tauri/icons/icon.icns


+ 0 - 0
cli/tauri.js/templates/src-tauri/icons/icon.ico → cli/core/templates/src-tauri/icons/icon.ico


+ 0 - 0
cli/tauri.js/templates/src-tauri/icons/icon.png → cli/core/templates/src-tauri/icons/icon.png


+ 0 - 0
cli/tauri.js/templates/src-tauri/rustfmt.toml → cli/core/templates/src-tauri/rustfmt.toml


+ 0 - 0
cli/tauri.js/templates/src-tauri/src/build.rs → cli/core/templates/src-tauri/src/build.rs


+ 0 - 0
cli/tauri.js/templates/src-tauri/src/main.rs → cli/core/templates/src-tauri/src/main.rs


+ 57 - 0
cli/core/templates/src-tauri/tauri.conf.json

@@ -0,0 +1,57 @@
+{
+  "package": {
+    "productName": "{{ app_name }}",
+    "version": "0.1.0"
+  },
+  "build": {
+    "distDir": "{{ dist_dir }}",
+    "devPath": "{{ dev_path }}",
+    "beforeDevCommand": "",
+    "beforeBuildCommand": ""
+  },
+  "tauri": {
+    "bundle": {
+      "active": true,
+      "targets": "all",
+      "identifier": "com.tauri.dev",
+      "icon": [
+        "icons/32x32.png",
+        "icons/128x128.png",
+        "icons/128x128@2x.png",
+        "icons/icon.icns",
+        "icons/icon.ico"
+      ],
+      "resources": [],
+      "externalBin": [],
+      "copyright": "",
+      "category": "DeveloperTool",
+      "shortDescription": "",
+      "longDescription": "",
+      "deb": {
+        "depends": [],
+        "useBootstrapper": false
+      },
+      "osx": {
+        "frameworks": [],
+        "minimumSystemVersion": "",
+        "useBootstrapper": false,
+        "exceptionDomain": ""
+      }
+    },
+    "allowlist": {
+      "all": true
+    },
+    "windows": [
+      {
+        "title": "{{ window_title }}",
+        "width": 800,
+        "height": 600,
+        "resizable": true,
+        "fullscreen": false
+      }
+    ],
+    "security": {
+      "csp": "default-src blob: data: filesystem: ws: http: https: 'unsafe-eval' 'unsafe-inline'"
+    }
+  }
+}

+ 0 - 157
cli/tauri.js/bin/tauri-init.js

@@ -1,157 +0,0 @@
-const parseArgs = require('minimist')
-const inquirer = require('inquirer')
-const { resolve } = require('path')
-const { merge } = require('lodash')
-
-/**
- * init creates the src-tauri folder
- *
- * @type {object}
- * @property {boolean} h
- * @property {boolean} help
- * @property {string|boolean} f
- * @property {string|boolean} force
- * @property {boolean} l
- * @property {boolean} log
- * @property {boolean} d
- * @property {boolean} directory
- */
-function main(cliArgs) {
-  const argv = parseArgs(cliArgs, {
-    alias: {
-      h: 'help',
-      f: 'force',
-      l: 'log',
-      A: 'app-name',
-      W: 'window-title',
-      D: 'dist-dir',
-      P: 'dev-path'
-    },
-    boolean: ['h', 'l', 'ci']
-  })
-
-  if (argv.help) {
-    printUsage()
-    process.exit(0)
-  }
-
-  if (argv.ci) {
-    runInit(argv)
-  } else {
-    getOptionsInteractive(argv).then((responses) => runInit(argv, responses))
-  }
-}
-
-function printUsage() {
-  console.log(`
-  Description
-    Inits the Tauri template. If Tauri cannot find the tauri.conf.json
-    it will create one.
-  Usage
-    $ tauri init
-  Options
-    --help, -h           Displays this message
-    --app-name, -a       Name of your Tauri application
-    --window-title, -w   Window title of your Tauri application
-    --dist-dir, -d       Web assets location, relative to <project-dir>/src-tauri
-    --dev-path, -p       Url of your dev server
-    --ci                 Skip prompts
-    --force, -f          Force init to overwrite [conf|template|all]
-    --log, -l            Logging [boolean]
-    --tauri-path, -t     Path of the Tauri project to use (relative to the cwd)
-    --directory, -D      Set target directory for init
-    `)
-}
-
-const getOptionsInteractive = (argv) => {
-  let defaultAppName = argv.a
-  if (!defaultAppName) {
-    try {
-      const packageJson = JSON.parse(
-        readFileSync(resolve(process.cwd(), 'package.json')).toString()
-      )
-      defaultAppName = packageJson.displayName || packageJson.name
-    } catch {}
-  }
-
-  return inquirer
-    .prompt([
-      {
-        type: 'input',
-        name: 'appName',
-        message: 'What is your app name?',
-        default: defaultAppName,
-        when: !argv.a
-      },
-      {
-        type: 'input',
-        name: 'windowTitle',
-        message: 'What should the window title be?',
-        default: 'Tauri App',
-        when: () => !argv.w
-      },
-      {
-        type: 'input',
-        name: 'build.devPath',
-        message: 'What is the url of your dev server?',
-        default: 'http://localhost:4000',
-        when: () => !argv.p
-      },
-      {
-        type: 'input',
-        name: 'build.distDir',
-        message:
-          'Where are your web assets (HTML/CSS/JS) located, relative to the "<current dir>/src-tauri" folder that will be created?',
-        default: '../dist',
-        when: () => !argv.d
-      }
-    ])
-    .then((answers) => answers)
-    .catch((error) => {
-      if (error.isTtyError) {
-        // Prompt couldn't be rendered in the current environment
-        console.log(
-          'It appears your terminal does not support interactive prompts. Using default values.'
-        )
-        runInit()
-      } else {
-        // Something else when wrong
-        console.error('An unknown error occurred:', error)
-      }
-    })
-}
-
-async function runInit(argv, config = {}) {
-  const { appName, windowTitle, ...configOptions } = config
-  const init = require('../dist/api/init')
-
-  let buildConfig = {
-    distDir: argv.d,
-    devPath: argv.p
-  }
-
-  const directory = argv.D || process.cwd()
-
-  init({
-    directory,
-    force: argv.f || null,
-    logging: argv.l || null,
-    tauriPath: argv.t || null,
-    appName: appName || argv.a || null,
-    customConfig: merge(configOptions, {
-      build: buildConfig,
-      tauri: {
-        windows: [
-          {
-            title: windowTitle || argv.w
-          }
-        ]
-      }
-    })
-  })
-
-  const { installDependencies } = require('../dist/api/dependency-manager')
-  await installDependencies()
-}
-
-module.exports = main

+ 11 - 8
cli/tauri.js/bin/tauri.js

@@ -1,7 +1,7 @@
 #!/usr/bin/env node
 
-const cmds = ['init', 'help', 'icon', 'info', 'deps']
-const rustCliCmds = ['dev', 'build']
+const cmds = ['help', 'icon', 'info', 'deps']
+const rustCliCmds = ['dev', 'build', 'init']
 
 const cmd = process.argv[2]
 
@@ -22,7 +22,14 @@ const tauri = function (command) {
     if (process.argv && !process.env.test) {
       process.argv.splice(0, 3)
     }
-    runOnRustCli(command, process.argv || [])
+    runOnRustCli(command, process.argv || []).promise.then(() => {
+      if (command === 'init') {
+        const {
+          installDependencies
+        } = require('../dist/api/dependency-manager')
+        return installDependencies()
+      }
+    })
     return
   }
 
@@ -57,11 +64,7 @@ const tauri = function (command) {
     }
     console.log(`[tauri]: running ${command}`)
     // eslint-disable-next-line security/detect-non-literal-require
-    if (['init'].includes(command)) {
-      require(`./tauri-${command}`)(process.argv.slice(2))
-    } else {
-      require(`./tauri-${command}`)
-    }
+    require(`./tauri-${command}`)
   } else {
     console.log(`Invalid command ${command}. Use one of ${cmds.join(', ')}.`)
   }

+ 17 - 9
cli/tauri.js/src/api/cli.ts

@@ -4,16 +4,25 @@ interface Args {
   [key: string]: string | Object
 }
 
-function runCliCommand(
-  command: string,
-  args: Args
-): { pid: number; promise: Promise<void> } {
+interface Cmd {
+  pid: number
+  promise: Promise<void>
+}
+
+function toKebabCase(value: string): string {
+  return value
+    .replace(/([a-z])([A-Z])/g, '$1-$2')
+    .replace(/\s+/g, '-')
+    .toLowerCase()
+}
+
+function runCliCommand(command: string, args: Args): Cmd {
   const argsArray = []
   for (const [argName, argValue] of Object.entries(args)) {
     if (argValue === false) {
       continue
     }
-    argsArray.push(`--${argName}`)
+    argsArray.push(`--${toKebabCase(argName)}`)
     if (argValue === true) {
       continue
     }
@@ -24,7 +33,6 @@ function runCliCommand(
   return runOnRustCli(command, argsArray)
 }
 
-export const dev = (args: Args): { pid: number; promise: Promise<void> } =>
-  runCliCommand('dev', args)
-export const build = (args: Args): { pid: number; promise: Promise<void> } =>
-  runCliCommand('build', args)
+export const init = (args: Args): Cmd => runCliCommand('init', args)
+export const dev = (args: Args): Cmd => runCliCommand('dev', args)
+export const build = (args: Args): Cmd => runCliCommand('build', args)

+ 0 - 43
cli/tauri.js/src/api/init.ts

@@ -1,43 +0,0 @@
-import { inject } from '../template'
-import { resolve } from 'path'
-import toml, { JsonMap } from '@tauri-apps/toml'
-import { readFileSync, writeFileSync } from 'fs'
-import { kebabCase } from 'lodash'
-import { CargoManifest } from 'src/types/cargo'
-
-module.exports = (args: {
-  directory: string
-  force: false | 'conf' | 'template' | 'all'
-  logging: boolean
-  tauriPath?: string
-  customConfig?: Object
-  appName?: string
-}): boolean => {
-  const injectResult = inject(
-    args.directory,
-    'all',
-    {
-      force: args.force,
-      logging: args.logging,
-      tauriPath: args.tauriPath
-    },
-    args.customConfig
-  )
-  if (args.appName) {
-    const manifestPath = resolve(args.directory, 'src-tauri/Cargo.toml')
-    const cargoManifest = (toml.parse(
-      readFileSync(manifestPath).toString()
-    ) as unknown) as CargoManifest
-    const binName = kebabCase(args.appName)
-    cargoManifest.package.name = binName
-    cargoManifest.package['default-run'] = binName
-    if (cargoManifest.bin?.length) {
-      cargoManifest.bin[0].name = binName
-    }
-    writeFileSync(
-      manifestPath,
-      toml.stringify((cargoManifest as unknown) as JsonMap)
-    )
-  }
-  return injectResult
-}

+ 0 - 58
cli/tauri.js/src/template/defaultConfig.ts

@@ -1,58 +0,0 @@
-export default {
-  package: {
-    productName: 'app',
-    version: '0.1.0'
-  },
-  build: {
-    distDir: '../dist',
-    devPath: 'http://localhost:4000',
-    beforeDevCommand: '',
-    beforeBuildCommand: ''
-  },
-  tauri: {
-    bundle: {
-      active: true,
-      targets: 'all', // or an array of targets
-      identifier: 'com.tauri.dev',
-      icon: [
-        'icons/32x32.png',
-        'icons/128x128.png',
-        'icons/128x128@2x.png',
-        'icons/icon.icns',
-        'icons/icon.ico'
-      ],
-      resources: [],
-      externalBin: [],
-      copyright: '',
-      category: 'DeveloperTool',
-      shortDescription: '',
-      longDescription: '',
-      deb: {
-        depends: [],
-        useBootstrapper: false
-      },
-      osx: {
-        frameworks: [],
-        minimumSystemVersion: '',
-        useBootstrapper: false,
-        exceptionDomain: ''
-      }
-    },
-    allowlist: {
-      all: true
-    },
-    windows: [
-      {
-        title: 'Tauri App',
-        width: 800,
-        height: 600,
-        resizable: true,
-        fullscreen: false
-      }
-    ],
-    security: {
-      csp:
-        "default-src blob: data: filesystem: ws: http: https: 'unsafe-eval' 'unsafe-inline'"
-    }
-  }
-}

+ 0 - 138
cli/tauri.js/src/template/index.ts

@@ -1,138 +0,0 @@
-import { CargoManifest } from './../types/cargo'
-import { existsSync, removeSync, writeFileSync } from 'fs-extra'
-import { join, normalize, resolve, isAbsolute } from 'path'
-import { merge } from 'webpack-merge'
-import copyTemplates from '../helpers/copy-templates'
-import logger from '../helpers/logger'
-import defaultConfig from './defaultConfig'
-import chalk from 'chalk'
-
-const log = logger('app:tauri')
-const warn = logger('app:tauri (template)', chalk.red)
-
-interface InjectOptions {
-  force: false | InjectionType
-  logging: boolean
-  tauriPath?: string
-}
-type InjectionType = 'conf' | 'template' | 'all'
-
-interface UnknownObject {
-  [index: string]: any
-}
-
-const injectConfFile = (
-  injectPath: string,
-  { force, logging }: InjectOptions,
-  customConfig: Object = {}
-): boolean | undefined => {
-  const path = join(injectPath, 'tauri.conf.json')
-  if (existsSync(path) && force !== 'conf' && force !== 'all') {
-    warn(`tauri.conf.json found in ${path}
-  Run \`tauri init --force conf\` to overwrite.`)
-    if (!force) return false
-  } else {
-    removeSync(path)
-    Object.keys(defaultConfig).forEach((key) => {
-      // Options marked `null` should be removed
-      /* eslint-disable security/detect-object-injection */
-      if ((customConfig as UnknownObject)[key] === null) {
-        // eslint-disable-next-line @typescript-eslint/no-dynamic-delete
-        delete (defaultConfig as UnknownObject)[key]
-        // eslint-disable-next-line @typescript-eslint/no-dynamic-delete
-        delete (customConfig as UnknownObject)[key]
-      }
-      /* eslint-enable security/detect-object-injection */
-    })
-    // Window config should be merged
-    // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access
-    if ((customConfig as UnknownObject).tauri?.windows[0]) {
-      // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access, @typescript-eslint/no-unsafe-assignment
-      ;(customConfig as UnknownObject).tauri.windows[0] = {
-        ...defaultConfig.tauri.windows[0],
-        // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access
-        ...(customConfig as UnknownObject).tauri.windows[0]
-      }
-      // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access
-      delete (defaultConfig as UnknownObject).tauri.windows
-    }
-    const finalConf = merge(
-      defaultConfig as any,
-      customConfig as any
-    ) as UnknownObject
-
-    writeFileSync(path, JSON.stringify(finalConf, undefined, 2))
-    if (logging) log('Successfully wrote tauri.conf.json')
-  }
-}
-
-const injectTemplate = (
-  injectPath: string,
-  { force, logging, tauriPath }: InjectOptions
-): boolean | undefined => {
-  const dir = normalize(join(injectPath, 'src-tauri'))
-  if (existsSync(dir) && force !== 'template' && force !== 'all') {
-    warn(`Tauri dir (${dir}) not empty.
-Run \`tauri init --force template\` to overwrite.`)
-    if (!force) return false
-  }
-
-  const resolveTauriPath = (tauriPath: string, crate: string): string => {
-    const resolvedPath = isAbsolute(tauriPath)
-      ? join(tauriPath, crate) // we received a full path as argument
-      : join('..', tauriPath, crate) // we received a relative path
-    return resolvedPath.replace(/\\/g, '/')
-  }
-
-  const resolveCurrentTauriVersion = (manifest: CargoManifest): string => {
-    const version = manifest.package.version
-    return version.substring(0, version.lastIndexOf('.'))
-  }
-  // eslint-disable-next-line @typescript-eslint/no-var-requires
-  const tauriManifest = require('../../../../tauri/Cargo.toml') as CargoManifest
-  // eslint-disable-next-line @typescript-eslint/no-var-requires
-  const tauriBuildManifest = require('../../../../core/tauri-build/Cargo.toml') as CargoManifest
-
-  const tauriDep = tauriPath
-    ? `{ path = "${resolveTauriPath(tauriPath, 'tauri')}" }`
-    : `{ version = "${resolveCurrentTauriVersion(tauriManifest)}" }`
-  const tauriBuildDep = tauriPath
-    ? `{ path = "${resolveTauriPath(tauriPath, 'core/tauri-build')}" }`
-    : `{ version = "${resolveCurrentTauriVersion(tauriBuildManifest)}" }`
-
-  removeSync(dir)
-  copyTemplates({
-    source: resolve(__dirname, '../../templates/src-tauri'),
-    scope: {
-      tauriDep,
-      tauriBuildDep
-    },
-    target: dir
-  })
-  if (logging) log('Successfully wrote src-tauri')
-}
-
-const inject = (
-  injectPath: string,
-  type: InjectionType,
-  { force = false, logging = false, tauriPath }: InjectOptions,
-  customConfig?: Object
-): boolean => {
-  if (typeof type !== 'string' || typeof injectPath !== 'string') {
-    warn('- internal error. Required params missing.')
-    return false
-  }
-  if (type === 'template' || type === 'all') {
-    injectTemplate(injectPath, { force, logging, tauriPath })
-  }
-  if (type === 'conf' || type === 'all') {
-    injectConfFile(
-      join(injectPath, 'src-tauri'),
-      { force, logging },
-      customConfig
-    )
-  }
-  return true
-}
-
-export { inject }

+ 0 - 69
cli/tauri.js/templates/updater.rs

@@ -1,69 +0,0 @@
-use crate::tauri::process::{ProcessExt, Signal, SystemExt};
-
-fn update() -> Result<(), String> {
-  let target = tauri::platform::target_triple().map_err(|_| "Could not determine target")?;
-  let github_release = tauri::updater::github::get_latest_release("jaemk", "self_update")
-    .map_err(|_| "Could not fetch latest release")?;
-  match github_release.asset_for(&target) {
-    Some(github_release_asset) => {
-      let release = tauri::updater::Release {
-        version: github_release.tag.trim_start_matches('v').to_string(),
-        download_url: github_release_asset.download_url,
-        asset_name: github_release_asset.name,
-      };
-
-      let status = tauri::updater::Update::configure()
-        .unwrap()
-        .release(release)
-        .bin_path_in_archive("github")
-        .bin_name("app")
-        .bin_install_path(&tauri::command::command_path("app".to_string()).unwrap())
-        .show_download_progress(true)
-        .current_version(env!("CARGO_PKG_VERSION"))
-        .build()
-        .unwrap()
-        .update()
-        .unwrap();
-
-      println!("found release: {}", status.version());
-
-      /*let tmp_dir = tauri::dir::with_temp_dir(|dir| {
-          let file_path = dir.path().join("my-temporary-note.pdf");
-          let mut tmp_archive = std::fs::File::create(file_path).unwrap();
-          tauri::http::download(&"https://www.w3.org/WAI/ER/tests/xhtml/testfiles/resources/pdf/dummy.pdf".to_string(), &mut tmp_archive, true).unwrap();
-      });*/
-
-      Ok(())
-    }
-    None => Err(format!("Could not find release for target {}", target)),
-  }
-}
-
-fn restart_app(app_command: String) -> Result<(), String> {
-  let mut system = tauri::process::System::new();
-  let parent_process = tauri::process::get_parent_process(&mut system)
-    .map_err(|_| "Could not determine parent process")?;
-  if parent_process.name() == "app" {
-    parent_process.kill(Signal::Kill);
-    std::thread::sleep(std::time::Duration::from_secs(1));
-    std::process::Command::new(app_command)
-      .spawn()
-      .map_err(|_| "Could not start app")?;
-  }
-  Ok(())
-}
-
-fn run_updater() -> Result<(), String> {
-  let app_command = tauri::command::relative_command("app".to_string())
-    .map_err(|_| "Could not determine app path")?;
-  update()?;
-  restart_app(app_command)?;
-  Ok(())
-}
-
-fn main() {
-  match run_updater() {
-    Ok(_) => {}
-    Err(err) => panic!(err),
-  };
-}

+ 6 - 6
cli/tauri.js/test/jest/__tests__/template.spec.js

@@ -12,12 +12,13 @@ describe('[CLI] tauri.js template', () => {
 
     process.chdir(fixturePath)
 
-    const init = require('api/init')
-    init({
+    const { init, build } = require('dist/api/cli')
+    await init({
       directory: process.cwd(),
-      force: 'all',
-      tauriPath: resolve(__dirname, '../../../../..')
-    })
+      force: true,
+      tauriPath: resolve(__dirname, '../../../../..'),
+      ci: true
+    }).promise
 
     process.chdir(tauriFixturePath)
 
@@ -25,7 +26,6 @@ describe('[CLI] tauri.js template', () => {
     const manifestFile = readFileSync(manifestPath).toString()
     writeFileSync(manifestPath, `workspace = { }\n\n${manifestFile}`)
 
-    const { build } = require('dist/api/cli')
     await build({
       config: {
         tauri: {

+ 0 - 1
cli/tauri.js/webpack.config.js

@@ -4,7 +4,6 @@ const nodeExternals = require('webpack-node-externals')
 module.exports = {
   entry: {
     'api/cli': './src/api/cli.ts',
-    'api/init': './src/api/init.ts',
     'api/tauricon': './src/api/tauricon.ts',
     'api/info': './src/api/info.ts',
     'api/dependency-manager': './src/api/dependency-manager/index.ts',

File diff suppressed because it is too large
+ 0 - 0
tauri/scripts/bundle.js


Some files were not shown because too many files changed in this diff