Fix plugin dev rebuild signaling and reload toast timing

This commit is contained in:
Gregory Schier
2026-03-02 07:02:28 -08:00
parent 876b7ef454
commit fbf0473b20
3 changed files with 49 additions and 7 deletions

View File

@@ -4,8 +4,8 @@ use crate::utils::http;
use keyring::Entry; use keyring::Entry;
use rand::Rng; use rand::Rng;
use rolldown::{ use rolldown::{
Bundler, BundlerOptions, ExperimentalOptions, InputItem, LogLevel, OutputFormat, Platform, BundleEvent, Bundler, BundlerOptions, ExperimentalOptions, InputItem, LogLevel, OutputFormat,
WatchOption, Watcher, Platform, WatchOption, Watcher, WatcherEvent,
}; };
use serde::Deserialize; use serde::Deserialize;
use std::collections::HashSet; use std::collections::HashSet;
@@ -114,12 +114,53 @@ async fn dev(args: PluginPathArg) -> CommandResult {
ensure_plugin_build_inputs(&plugin_dir)?; ensure_plugin_build_inputs(&plugin_dir)?;
ui::info(&format!("Watching plugin {}...", plugin_dir.display())); ui::info(&format!("Watching plugin {}...", plugin_dir.display()));
ui::info("Press Ctrl-C to stop");
let bundler = Bundler::new(bundler_options(&plugin_dir, true)) let bundler = Bundler::new(bundler_options(&plugin_dir, true))
.map_err(|err| format!("Failed to initialize Rolldown watcher: {err}"))?; .map_err(|err| format!("Failed to initialize Rolldown watcher: {err}"))?;
let watcher = Watcher::new(vec![Arc::new(Mutex::new(bundler))], None) let watcher = Watcher::new(vec![Arc::new(Mutex::new(bundler))], None)
.map_err(|err| format!("Failed to start Rolldown watcher: {err}"))?; .map_err(|err| format!("Failed to start Rolldown watcher: {err}"))?;
let emitter = watcher.emitter();
let watch_root = plugin_dir.clone();
let _event_logger = tokio::spawn(async move {
loop {
let event = {
let rx = emitter.rx.lock().await;
rx.recv()
};
let Ok(event) = event else {
break;
};
match event {
WatcherEvent::Change(change) => {
let changed_path = Path::new(change.path.as_str());
let display_path = changed_path
.strip_prefix(&watch_root)
.map(|p| p.display().to_string())
.unwrap_or_else(|_| {
changed_path
.file_name()
.map(|name| name.to_string_lossy().into_owned())
.unwrap_or_else(|| "unknown".to_string())
});
ui::info(&format!("Rebuilding plugin {display_path}"));
}
WatcherEvent::Event(BundleEvent::BundleEnd(_)) => {}
WatcherEvent::Event(BundleEvent::Error(event)) => {
if event.error.diagnostics.is_empty() {
ui::error("Plugin build failed");
} else {
for diagnostic in event.error.diagnostics {
ui::error(&diagnostic.to_string());
}
}
}
WatcherEvent::Close => break,
_ => {}
}
}
});
watcher.start().await; watcher.start().await;
Ok(()) Ok(())

View File

@@ -118,7 +118,7 @@ async fn handle_host_plugin_request<R: Runtime>(
&InternalEventPayload::ShowToastRequest(ShowToastRequest { &InternalEventPayload::ShowToastRequest(ShowToastRequest {
message: format!("Reloaded plugin {}@{}", info.name, info.version), message: format!("Reloaded plugin {}@{}", info.name, info.version),
icon: Some(Icon::Info), icon: Some(Icon::Info),
timeout: Some(3000), timeout: Some(5000),
..Default::default() ..Default::default()
}), }),
None, None,

View File

@@ -76,10 +76,10 @@ export class PluginInstance {
this.#mod = {}; this.#mod = {};
const fileChangeCallback = async () => { const fileChangeCallback = async () => {
await this.#mod?.dispose?.();
this.#importModule();
const ctx = this.#newCtx(workerData.context); const ctx = this.#newCtx(workerData.context);
try { try {
await this.#mod?.dispose?.();
this.#importModule();
await this.#mod?.init?.(ctx); await this.#mod?.init?.(ctx);
this.#sendPayload( this.#sendPayload(
workerData.context, workerData.context,
@@ -90,7 +90,7 @@ export class PluginInstance {
null, null,
); );
} catch (err: unknown) { } catch (err: unknown) {
ctx.toast.show({ await ctx.toast.show({
message: `Failed to initialize plugin ${this.#workerData.bootRequest.dir.split('/').pop()}: ${err}`, message: `Failed to initialize plugin ${this.#workerData.bootRequest.dir.split('/').pop()}: ${err}`,
color: 'notice', color: 'notice',
icon: 'alert_triangle', icon: 'alert_triangle',
@@ -1003,6 +1003,7 @@ function watchFile(filepath: string, cb: () => void) {
const stat = statSync(filepath, { throwIfNoEntry: false }); const stat = statSync(filepath, { throwIfNoEntry: false });
if (stat == null || stat.mtimeMs !== watchedFiles[filepath]?.mtimeMs) { if (stat == null || stat.mtimeMs !== watchedFiles[filepath]?.mtimeMs) {
watchedFiles[filepath] = stat ?? null; watchedFiles[filepath] = stat ?? null;
console.log('[plugin-runtime] watchFile triggered', filepath);
cb(); cb();
} }
}); });