diff --git a/.changes/cli-js-kill-dev-fn.md b/.changes/cli-js-kill-dev-fn.md new file mode 100644 index 000000000..a62400b42 --- /dev/null +++ b/.changes/cli-js-kill-dev-fn.md @@ -0,0 +1,5 @@ +--- +"cli.js": "minor" +--- + +Add `killDevApp` API to kill the dev app process manually. diff --git a/.changes/cli-rs-kill-dev-fn.md b/.changes/cli-rs-kill-dev-fn.md new file mode 100644 index 000000000..aa889b376 --- /dev/null +++ b/.changes/cli-rs-kill-dev-fn.md @@ -0,0 +1,5 @@ +--- +"cli.rs": "minor" +--- + +Add `tauri_cli::kill_dev_app` API to kill the dev app process manually. diff --git a/tooling/cli/node/index.d.ts b/tooling/cli/node/index.d.ts index b562ef74a..7c7c2afec 100644 --- a/tooling/cli/node/index.d.ts +++ b/tooling/cli/node/index.d.ts @@ -4,4 +4,5 @@ /* auto-generated by NAPI-RS */ export function run(args: Array, binName: string | undefined | null, callback: (...args: any[]) => any): void +export function killDevApp(callback: (...args: any[]) => any): void export function logError(error: string): void diff --git a/tooling/cli/node/index.js b/tooling/cli/node/index.js index 4bf34ea76..8617a06c9 100644 --- a/tooling/cli/node/index.js +++ b/tooling/cli/node/index.js @@ -252,7 +252,8 @@ if (!nativeBinding) { throw new Error(`Failed to load native binding`) } -const { run, logError } = nativeBinding +const { run, killDevApp, logError } = nativeBinding module.exports.run = run +module.exports.killDevApp = killDevApp module.exports.logError = logError diff --git a/tooling/cli/node/main.d.ts b/tooling/cli/node/main.d.ts index a89dae573..3963fefba 100644 --- a/tooling/cli/node/main.d.ts +++ b/tooling/cli/node/main.d.ts @@ -6,3 +6,4 @@ /* eslint-disable */ export function run(args: Array, binName: string | undefined | null): Promise +export function killDevApp(): Promise diff --git a/tooling/cli/node/main.js b/tooling/cli/node/main.js index b3156ed09..b541f2639 100644 --- a/tooling/cli/node/main.js +++ b/tooling/cli/node/main.js @@ -2,7 +2,7 @@ // SPDX-License-Identifier: Apache-2.0 // SPDX-License-Identifier: MIT -const { run, logError } = require('./index') +const { run, killDevApp, logError } = require('./index') module.exports.run = (args, binName) => { return new Promise((resolve, reject) => { @@ -16,4 +16,16 @@ module.exports.run = (args, binName) => { }) } +module.exports.killDevApp = () => { + return new Promise((resolve, reject) => { + killDevApp(res => { + if (res instanceof Error) { + reject(res) + } else { + resolve(res) + } + }) + }) +} + module.exports.logError = logError diff --git a/tooling/cli/node/src/lib.rs b/tooling/cli/node/src/lib.rs index 213dd84c4..aa6ae13ae 100644 --- a/tooling/cli/node/src/lib.rs +++ b/tooling/cli/node/src/lib.rs @@ -25,6 +25,23 @@ pub fn run(args: Vec, bin_name: Option, callback: JsFunction) -> Ok(()) } +#[napi_derive::napi] +pub fn kill_dev_app(callback: JsFunction) -> Result<()> { + let function: ThreadsafeFunction = callback + .create_threadsafe_function(0, |ctx| ctx.env.get_boolean(ctx.value).map(|v| vec![v]))?; + + // we need to run in a separate thread so Node.js (e.g. vue-cli-plugin-tauri) consumers + // can do work while `tauri dev` is running. + std::thread::spawn(move || match tauri_cli::kill_dev_app() { + Ok(_) => function.call(Ok(true), ThreadsafeFunctionCallMode::Blocking), + Err(e) => function.call( + Err(Error::new(Status::GenericFailure, format!("{:#}", e))), + ThreadsafeFunctionCallMode::Blocking, + ), + }); + Ok(()) +} + #[napi_derive::napi] pub fn log_error(error: String) { log::error!("{}", error); diff --git a/tooling/cli/src/dev.rs b/tooling/cli/src/dev.rs index b0bae7157..52c0c567c 100644 --- a/tooling/cli/src/dev.rs +++ b/tooling/cli/src/dev.rs @@ -8,7 +8,7 @@ use crate::{ command_env, config::{get as get_config, reload as reload_config, AppUrl, BeforeDevCommand, WindowUrl}, }, - interface::{AppInterface, ExitReason, Interface}, + interface::{rust::DevChild, AppInterface, ExitReason, Interface}, CommandExt, Result, }; use clap::{ArgAction, Parser}; @@ -27,6 +27,7 @@ use std::{ }, }; +pub(crate) static DEV_CHILD: OnceCell>> = OnceCell::new(); static BEFORE_DEV: OnceCell>> = OnceCell::new(); static KILL_BEFORE_DEV_FLAG: OnceCell = OnceCell::new(); diff --git a/tooling/cli/src/interface/rust.rs b/tooling/cli/src/interface/rust.rs index d2fb9474f..30945d05f 100644 --- a/tooling/cli/src/interface/rust.rs +++ b/tooling/cli/src/interface/rust.rs @@ -91,7 +91,7 @@ pub struct DevChild { } impl DevChild { - fn kill(&self) -> std::io::Result<()> { + pub fn kill(&self) -> std::io::Result<()> { if let Some(child) = &*self.app_child.lock().unwrap() { child.kill()?; } else { @@ -194,11 +194,13 @@ impl Interface for Rust { if options.no_watch { let (tx, rx) = sync_channel(1); - self.run_dev(options, move |status, reason| { + let child = self.run_dev(options, move |status, reason| { tx.send(()).unwrap(); on_exit_(status, reason) })?; + let _ = crate::dev::DEV_CHILD.set(Arc::new(Mutex::new(child))); + rx.recv().unwrap(); Ok(()) } else { @@ -206,6 +208,10 @@ impl Interface for Rust { on_exit_(status, reason) })?; + let child = Arc::new(Mutex::new(child)); + let child_ = Arc::clone(&child); + let _ = crate::dev::DEV_CHILD.set(child_); + self.run_dev_watcher(child, options, on_exit) } } @@ -392,11 +398,10 @@ impl Rust { fn run_dev_watcher( &mut self, - child: DevChild, + process: Arc>, options: Options, on_exit: Arc, ) -> crate::Result<()> { - let process = Arc::new(Mutex::new(child)); let (tx, rx) = sync_channel(1); let app_path = app_dir(); let tauri_path = tauri_dir(); diff --git a/tooling/cli/src/lib.rs b/tooling/cli/src/lib.rs index de520f85d..4ad3a3e17 100644 --- a/tooling/cli/src/lib.rs +++ b/tooling/cli/src/lib.rs @@ -176,6 +176,14 @@ where Ok(()) } +pub fn kill_dev_app() -> Result<()> { + dev::DEV_CHILD + .get() + .map(|child| child.lock().unwrap().kill()) + .unwrap_or(Ok(())) + .map_err(Into::into) +} + /// This maps the occurrence of `--verbose` flags to the correct log level fn verbosity_level(num: u8) -> Level { match num {