diff options
author | Casper Beyer <caspervonb@pm.me> | 2021-08-25 04:34:09 +0800 |
---|---|---|
committer | GitHub <noreply@github.com> | 2021-08-24 22:34:09 +0200 |
commit | a3fd4bb998d875e130ad0db40d9392192468bba1 (patch) | |
tree | 0588bb8c53281899e8c1be68d06bb41be33a26fd /cli/main.rs | |
parent | 85a56e7144cb2e213eb670f754027d19e31c315a (diff) |
fix(cli): dispatch unload event on watch drop (#11696)
Diffstat (limited to 'cli/main.rs')
-rw-r--r-- | cli/main.rs | 80 |
1 files changed, 66 insertions, 14 deletions
diff --git a/cli/main.rs b/cli/main.rs index 9749496c6..650ab62a2 100644 --- a/cli/main.rs +++ b/cli/main.rs @@ -892,29 +892,81 @@ async fn run_with_watch(flags: Flags, script: String) -> Result<(), AnyError> { }) }; + /// The FileWatcherModuleExecutor provides module execution with safe dispatching of life-cycle events by tracking the + /// state of any pending events and emitting accordingly on drop in the case of a future + /// cancellation. + struct FileWatcherModuleExecutor { + worker: MainWorker, + pending_unload: bool, + } + + impl FileWatcherModuleExecutor { + pub fn new(worker: MainWorker) -> FileWatcherModuleExecutor { + FileWatcherModuleExecutor { + worker, + pending_unload: false, + } + } + + /// Execute the given main module emitting load and unload events before and after execution + /// respectively. + pub async fn execute( + &mut self, + main_module: &ModuleSpecifier, + ) -> Result<(), AnyError> { + self.worker.execute_module(main_module).await?; + self.worker.execute_script( + &located_script_name!(), + "window.dispatchEvent(new Event('load'))", + )?; + self.pending_unload = true; + + let result = self.worker.run_event_loop(false).await; + self.pending_unload = false; + + if let Err(err) = result { + return Err(err); + } + + self.worker.execute_script( + &located_script_name!(), + "window.dispatchEvent(new Event('unload'))", + )?; + + Ok(()) + } + } + + impl Drop for FileWatcherModuleExecutor { + fn drop(&mut self) { + if self.pending_unload { + self + .worker + .execute_script( + &located_script_name!(), + "window.dispatchEvent(new Event('unload'))", + ) + .unwrap(); + } + } + } + let operation = |(program_state, main_module): (Arc<ProgramState>, ModuleSpecifier)| { let flags = flags.clone(); let permissions = Permissions::from_options(&flags.into()); async move { - let main_module = main_module.clone(); - let mut worker = create_main_worker( + // We make use an module executor guard to ensure that unload is always fired when an + // operation is called. + let mut executor = FileWatcherModuleExecutor::new(create_main_worker( &program_state, main_module.clone(), permissions, None, - ); - debug!("main_module {}", main_module); - worker.execute_module(&main_module).await?; - worker.execute_script( - &located_script_name!(), - "window.dispatchEvent(new Event('load'))", - )?; - worker.run_event_loop(false).await?; - worker.execute_script( - &located_script_name!(), - "window.dispatchEvent(new Event('unload'))", - )?; + )); + + executor.execute(&main_module).await?; + Ok(()) } }; |