fix(cli): dispatch unload event on watch drop (#11696)

This commit is contained in:
Casper Beyer 2021-08-25 04:34:09 +08:00 committed by GitHub
parent 85a56e7144
commit a3fd4bb998
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
2 changed files with 141 additions and 14 deletions

View file

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