diff options
author | David Sherret <dsherret@users.noreply.github.com> | 2022-07-19 11:58:18 -0400 |
---|---|---|
committer | GitHub <noreply@github.com> | 2022-07-19 11:58:18 -0400 |
commit | 0ab262b901348e9251262a02bef17d14ed13b997 (patch) | |
tree | fc5a6e3926ea7480714cbc844098eca6c43c1ab5 /cli/module_loader.rs | |
parent | e99d64acedb6e111d33f53599da494865978f1aa (diff) |
feat: emit files on demand and fix racy emit (#15220)
Diffstat (limited to 'cli/module_loader.rs')
-rw-r--r-- | cli/module_loader.rs | 154 |
1 files changed, 145 insertions, 9 deletions
diff --git a/cli/module_loader.rs b/cli/module_loader.rs index cf94a4767..8da205907 100644 --- a/cli/module_loader.rs +++ b/cli/module_loader.rs @@ -1,20 +1,36 @@ // Copyright 2018-2022 the Deno authors. All rights reserved. MIT license. +use crate::emit::emit_parsed_source; use crate::emit::TsTypeLib; +use crate::graph_util::ModuleEntry; use crate::proc_state::ProcState; +use crate::text_encoding::code_without_source_map; +use crate::text_encoding::source_map_from_code; +use deno_ast::MediaType; +use deno_core::anyhow::anyhow; use deno_core::error::AnyError; use deno_core::futures::future::FutureExt; use deno_core::futures::Future; +use deno_core::resolve_url; use deno_core::ModuleLoader; +use deno_core::ModuleSource; use deno_core::ModuleSpecifier; +use deno_core::ModuleType; use deno_core::OpState; +use deno_core::SourceMapGetter; use deno_runtime::permissions::Permissions; use std::cell::RefCell; use std::pin::Pin; use std::rc::Rc; use std::str; +struct ModuleCodeSource { + pub code: String, + pub found_url: ModuleSpecifier, + pub media_type: MediaType, +} + pub struct CliModuleLoader { pub lib: TsTypeLib, /// The initial set of permissions used to resolve the static imports in the @@ -40,6 +56,65 @@ impl CliModuleLoader { ps, }) } + + fn load_prepared_module( + &self, + specifier: &ModuleSpecifier, + ) -> Result<ModuleCodeSource, AnyError> { + let graph_data = self.ps.graph_data.read(); + let found_url = graph_data.follow_redirect(specifier); + match graph_data.get(&found_url) { + Some(ModuleEntry::Module { + code, + media_type, + maybe_parsed_source, + .. + }) => { + let code = match media_type { + MediaType::JavaScript + | MediaType::Unknown + | MediaType::Cjs + | MediaType::Mjs + | MediaType::Json => { + if let Some(source) = graph_data.get_cjs_esm_translation(specifier) + { + source.to_owned() + } else { + code.to_string() + } + } + MediaType::Dts | MediaType::Dcts | MediaType::Dmts => "".to_string(), + MediaType::TypeScript + | MediaType::Mts + | MediaType::Cts + | MediaType::Jsx + | MediaType::Tsx => { + // get emit text + let parsed_source = maybe_parsed_source.as_ref().unwrap(); // should always be set + emit_parsed_source( + &self.ps.emit_cache, + &found_url, + parsed_source, + &self.ps.emit_options, + self.ps.emit_options_hash, + )? + } + MediaType::TsBuildInfo | MediaType::Wasm | MediaType::SourceMap => { + panic!("Unexpected media type {} for {}", media_type, found_url) + } + }; + Ok(ModuleCodeSource { + code, + found_url, + media_type: *media_type, + }) + } + _ => Err(anyhow!( + "Loading unprepared module: {}", + specifier.to_string() + )), + } + } } impl ModuleLoader for CliModuleLoader { @@ -54,18 +129,35 @@ impl ModuleLoader for CliModuleLoader { fn load( &self, - module_specifier: &ModuleSpecifier, - maybe_referrer: Option<ModuleSpecifier>, - is_dynamic: bool, + specifier: &ModuleSpecifier, + _maybe_referrer: Option<ModuleSpecifier>, + _is_dynamic: bool, ) -> Pin<Box<deno_core::ModuleSourceFuture>> { - let module_specifier = module_specifier.clone(); - let ps = self.ps.clone(); - // NOTE: this block is async only because of `deno_core` interface // requirements; module was already loaded when constructing module graph - // during call to `prepare_load`. - async move { ps.load(module_specifier, maybe_referrer, is_dynamic) } - .boxed_local() + // during call to `prepare_load` so we can load it synchronously. + let result = self.load_prepared_module(specifier).map(|code_source| { + let code = if self.ps.options.is_inspecting() { + // we need the code with the source map in order for + // it to work with --inspect or --inspect-brk + code_source.code + } else { + // reduce memory and throw away the source map + // because we don't need it + code_without_source_map(code_source.code) + }; + ModuleSource { + code: code.into_bytes().into_boxed_slice(), + module_url_specified: specifier.to_string(), + module_url_found: code_source.found_url.to_string(), + module_type: match code_source.media_type { + MediaType::Json => ModuleType::Json, + _ => ModuleType::JavaScript, + }, + } + }); + + Box::pin(deno_core::futures::future::ready(result)) } fn prepare_load( @@ -103,3 +195,47 @@ impl ModuleLoader for CliModuleLoader { .boxed_local() } } + +impl SourceMapGetter for CliModuleLoader { + fn get_source_map(&self, file_name: &str) -> Option<Vec<u8>> { + if let Ok(specifier) = resolve_url(file_name) { + match specifier.scheme() { + // we should only be looking for emits for schemes that denote external + // modules, which the disk_cache supports + "wasm" | "file" | "http" | "https" | "data" | "blob" => (), + _ => return None, + } + if let Ok(source) = self.load_prepared_module(&specifier) { + source_map_from_code(&source.code) + } else { + None + } + } else { + None + } + } + + fn get_source_line( + &self, + file_name: &str, + line_number: usize, + ) -> Option<String> { + let graph_data = self.ps.graph_data.read(); + let specifier = graph_data.follow_redirect(&resolve_url(file_name).ok()?); + let code = match graph_data.get(&specifier) { + Some(ModuleEntry::Module { code, .. }) => code, + _ => return None, + }; + // Do NOT use .lines(): it skips the terminating empty line. + // (due to internally using_terminator() instead of .split()) + let lines: Vec<&str> = code.split('\n').collect(); + if line_number >= lines.len() { + Some(format!( + "{} Couldn't format source line: Line {} is out of bounds (source may have changed at runtime)", + crate::colors::yellow("Warning"), line_number + 1, + )) + } else { + Some(lines[line_number].to_string()) + } + } +} |