1
0
Fork 0
mirror of https://github.com/denoland/deno.git synced 2025-01-03 12:58:54 -05:00

fix(cli): make dynamic import errors catchable (#8750)

Fixes #6259
This commit is contained in:
Kitson Kelly 2020-12-15 16:52:55 +11:00 committed by GitHub
parent b6d5ae1ecd
commit 63a821b78b
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
16 changed files with 446 additions and 269 deletions

View file

@ -46,7 +46,7 @@ impl DiskCache {
}) })
} }
pub fn get_cache_filename(&self, url: &Url) -> PathBuf { fn get_cache_filename(&self, url: &Url) -> Option<PathBuf> {
let mut out = PathBuf::new(); let mut out = PathBuf::new();
let scheme = url.scheme(); let scheme = url.scheme();
@ -105,31 +105,25 @@ impl DiskCache {
out = out.join(remaining_components); out = out.join(remaining_components);
} }
scheme => { _ => return None,
unimplemented!(
"Don't know how to create cache name for scheme: {}\n Url: {}",
scheme,
url
);
}
}; };
out Some(out)
} }
pub fn get_cache_filename_with_extension( pub fn get_cache_filename_with_extension(
&self, &self,
url: &Url, url: &Url,
extension: &str, extension: &str,
) -> PathBuf { ) -> Option<PathBuf> {
let base = self.get_cache_filename(url); let base = self.get_cache_filename(url)?;
match base.extension() { match base.extension() {
None => base.with_extension(extension), None => Some(base.with_extension(extension)),
Some(ext) => { Some(ext) => {
let original_extension = OsStr::to_str(ext).unwrap(); let original_extension = OsStr::to_str(ext).unwrap();
let final_extension = format!("{}.{}", original_extension, extension); let final_extension = format!("{}.{}", original_extension, extension);
base.with_extension(final_extension) Some(base.with_extension(final_extension))
} }
} }
} }
@ -234,7 +228,7 @@ mod tests {
for test_case in &test_cases { for test_case in &test_cases {
let cache_filename = let cache_filename =
cache.get_cache_filename(&Url::parse(test_case.0).unwrap()); cache.get_cache_filename(&Url::parse(test_case.0).unwrap());
assert_eq!(cache_filename, PathBuf::from(test_case.1)); assert_eq!(cache_filename, Some(PathBuf::from(test_case.1)));
} }
} }
@ -280,7 +274,7 @@ mod tests {
&Url::parse(test_case.0).unwrap(), &Url::parse(test_case.0).unwrap(),
test_case.1 test_case.1
), ),
PathBuf::from(test_case.2) Some(PathBuf::from(test_case.2))
) )
} }
} }

View file

@ -28,6 +28,8 @@ use crate::tsc_config::TsConfig;
use crate::version; use crate::version;
use crate::AnyError; use crate::AnyError;
use deno_core::error::anyhow;
use deno_core::error::custom_error;
use deno_core::error::Context; use deno_core::error::Context;
use deno_core::futures::stream::FuturesUnordered; use deno_core::futures::stream::FuturesUnordered;
use deno_core::futures::stream::StreamExt; use deno_core::futures::stream::StreamExt;
@ -38,6 +40,7 @@ use deno_core::serde::Serializer;
use deno_core::serde_json::json; use deno_core::serde_json::json;
use deno_core::serde_json::Value; use deno_core::serde_json::Value;
use deno_core::ModuleResolutionError; use deno_core::ModuleResolutionError;
use deno_core::ModuleSource;
use deno_core::ModuleSpecifier; use deno_core::ModuleSpecifier;
use regex::Regex; use regex::Regex;
use std::cell::RefCell; use std::cell::RefCell;
@ -482,6 +485,10 @@ impl Module {
Ok(specifier) Ok(specifier)
} }
pub fn set_emit(&mut self, code: String, maybe_map: Option<String>) {
self.maybe_emit = Some(Emit::Cli((code, maybe_map)));
}
/// Calculate the hashed version of the module and update the `maybe_version`. /// Calculate the hashed version of the module and update the `maybe_version`.
pub fn set_version(&mut self, config: &[u8]) { pub fn set_version(&mut self, config: &[u8]) {
self.maybe_version = self.maybe_version =
@ -523,6 +530,9 @@ pub struct ResultInfo {
/// A structure which provides diagnostic information (usually from `tsc`) /// A structure which provides diagnostic information (usually from `tsc`)
/// about the code in the module graph. /// about the code in the module graph.
pub diagnostics: Diagnostics, pub diagnostics: Diagnostics,
/// A map of specifiers to the result of their resolution in the module graph.
pub loadable_modules:
HashMap<ModuleSpecifier, Result<ModuleSource, AnyError>>,
/// Optionally ignored compiler options that represent any options that were /// Optionally ignored compiler options that represent any options that were
/// ignored if there was a user provided configuration. /// ignored if there was a user provided configuration.
pub maybe_ignored_options: Option<IgnoredCompilerOptions>, pub maybe_ignored_options: Option<IgnoredCompilerOptions>,
@ -637,6 +647,18 @@ pub struct TranspileOptions {
pub reload: bool, pub reload: bool,
} }
#[derive(Debug, Clone)]
enum ModuleSlot {
/// The module fetch resulted in a non-recoverable error.
Err(Rc<AnyError>),
/// The the fetch resulted in a module.
Module(Box<Module>),
/// Used to denote a module that isn't part of the graph.
None,
/// The fetch of the module is pending.
Pending,
}
/// A dependency graph of modules, were the modules that have been inserted via /// A dependency graph of modules, were the modules that have been inserted via
/// the builder will be loaded into the graph. Also provides an interface to /// the builder will be loaded into the graph. Also provides an interface to
/// be able to manipulate and handle the graph. /// be able to manipulate and handle the graph.
@ -649,7 +671,7 @@ pub struct Graph {
/// invoked. /// invoked.
maybe_tsbuildinfo: Option<String>, maybe_tsbuildinfo: Option<String>,
/// The modules that are part of the graph. /// The modules that are part of the graph.
modules: HashMap<ModuleSpecifier, Module>, modules: HashMap<ModuleSpecifier, ModuleSlot>,
/// A map of redirects, where a module specifier is redirected to another /// A map of redirects, where a module specifier is redirected to another
/// module specifier by the handler. All modules references should be /// module specifier by the handler. All modules references should be
/// resolved internally via this, before attempting to access the module via /// resolved internally via this, before attempting to access the module via
@ -667,6 +689,44 @@ pub struct Graph {
maybe_lockfile: Option<Arc<Mutex<Lockfile>>>, maybe_lockfile: Option<Arc<Mutex<Lockfile>>>,
} }
/// Convert a specifier and a module slot in a result to the module source which
/// is needed by Deno core for loading the module.
fn to_module_result(
(specifier, module_slot): (&ModuleSpecifier, &ModuleSlot),
) -> (ModuleSpecifier, Result<ModuleSource, AnyError>) {
match module_slot {
ModuleSlot::Err(err) => (specifier.clone(), Err(anyhow!(err.to_string()))),
ModuleSlot::Module(module) => (
specifier.clone(),
if let Some(emit) = &module.maybe_emit {
match emit {
Emit::Cli((code, _)) => Ok(ModuleSource {
code: code.clone(),
module_url_found: module.specifier.to_string(),
module_url_specified: specifier.to_string(),
}),
}
} else {
match module.media_type {
MediaType::JavaScript | MediaType::Unknown => Ok(ModuleSource {
code: module.source.clone(),
module_url_found: module.specifier.to_string(),
module_url_specified: specifier.to_string(),
}),
_ => Err(custom_error(
"NotFound",
format!("Compiled module not found \"{}\"", specifier),
)),
}
},
),
_ => (
specifier.clone(),
Err(anyhow!("Module \"{}\" unavailable.", specifier)),
),
}
}
impl Graph { impl Graph {
/// Create a new instance of a graph, ready to have modules loaded it. /// Create a new instance of a graph, ready to have modules loaded it.
/// ///
@ -767,6 +827,7 @@ impl Graph {
debug!("graph does not need to be checked or emitted."); debug!("graph does not need to be checked or emitted.");
return Ok(ResultInfo { return Ok(ResultInfo {
maybe_ignored_options, maybe_ignored_options,
loadable_modules: self.get_loadable_modules(),
..Default::default() ..Default::default()
}); });
} }
@ -834,9 +895,10 @@ impl Graph {
} }
let config = config.as_bytes(); let config = config.as_bytes();
for (specifier, code) in codes.iter() { for (specifier, code) in codes.iter() {
if let Some(module) = graph.get_module_mut(specifier) { if let ModuleSlot::Module(module) =
module.maybe_emit = graph.get_module_mut(specifier).unwrap()
Some(Emit::Cli((code.clone(), maps.get(specifier).cloned()))); {
module.set_emit(code.clone(), maps.get(specifier).cloned());
module.set_version(&config); module.set_version(&config);
module.is_dirty = true; module.is_dirty = true;
} else { } else {
@ -849,16 +911,12 @@ impl Graph {
Ok(ResultInfo { Ok(ResultInfo {
diagnostics: response.diagnostics, diagnostics: response.diagnostics,
loadable_modules: graph.get_loadable_modules(),
maybe_ignored_options, maybe_ignored_options,
stats: response.stats, stats: response.stats,
}) })
} }
fn contains_module(&self, specifier: &ModuleSpecifier) -> bool {
let s = self.resolve_specifier(specifier);
self.modules.contains_key(s)
}
/// Emit the module graph in a specific format. This is specifically designed /// Emit the module graph in a specific format. This is specifically designed
/// to be an "all-in-one" API for access by the runtime, allowing both /// to be an "all-in-one" API for access by the runtime, allowing both
/// emitting single modules as well as bundles, using Deno module resolution /// emitting single modules as well as bundles, using Deno module resolution
@ -921,13 +979,13 @@ impl Graph {
)?; )?;
let mut emitted_files = HashMap::new(); let mut emitted_files = HashMap::new();
let graph = graph.borrow();
match options.bundle_type { match options.bundle_type {
BundleType::Esm => { BundleType::Esm => {
assert!( assert!(
response.emitted_files.is_empty(), response.emitted_files.is_empty(),
"No files should have been emitted from tsc." "No files should have been emitted from tsc."
); );
let graph = graph.borrow();
assert_eq!( assert_eq!(
graph.roots.len(), graph.roots.len(),
1, 1,
@ -966,6 +1024,7 @@ impl Graph {
emitted_files, emitted_files,
ResultInfo { ResultInfo {
diagnostics: response.diagnostics, diagnostics: response.diagnostics,
loadable_modules: graph.get_loadable_modules(),
maybe_ignored_options, maybe_ignored_options,
stats: response.stats, stats: response.stats,
}, },
@ -1023,15 +1082,17 @@ impl Graph {
/// any build info if present. /// any build info if present.
fn flush(&mut self) -> Result<(), AnyError> { fn flush(&mut self) -> Result<(), AnyError> {
let mut handler = self.handler.borrow_mut(); let mut handler = self.handler.borrow_mut();
for (_, module) in self.modules.iter_mut() { for (_, module_slot) in self.modules.iter_mut() {
if module.is_dirty { if let ModuleSlot::Module(module) = module_slot {
if let Some(emit) = &module.maybe_emit { if module.is_dirty {
handler.set_cache(&module.specifier, emit)?; if let Some(emit) = &module.maybe_emit {
handler.set_cache(&module.specifier, emit)?;
}
if let Some(version) = &module.maybe_version {
handler.set_version(&module.specifier, version.clone())?;
}
module.is_dirty = false;
} }
if let Some(version) = &module.maybe_version {
handler.set_version(&module.specifier, version.clone())?;
}
module.is_dirty = false;
} }
} }
for root_specifier in self.roots.iter() { for root_specifier in self.roots.iter() {
@ -1050,7 +1111,12 @@ impl Graph {
totals: &mut HashMap<ModuleSpecifier, usize>, totals: &mut HashMap<ModuleSpecifier, usize>,
) -> ModuleInfo { ) -> ModuleInfo {
let not_seen = seen.insert(specifier.clone()); let not_seen = seen.insert(specifier.clone());
let module = self.get_module(specifier).unwrap(); let module = if let ModuleSlot::Module(module) = self.get_module(specifier)
{
module
} else {
unreachable!();
};
let mut deps = Vec::new(); let mut deps = Vec::new();
let mut total_size = None; let mut total_size = None;
@ -1097,50 +1163,79 @@ impl Graph {
let map = self let map = self
.modules .modules
.iter() .iter()
.map(|(specifier, module)| { .filter_map(|(specifier, module_slot)| {
let mut deps = BTreeSet::new(); if let ModuleSlot::Module(module) = module_slot {
for (_, dep) in module.dependencies.iter() { let mut deps = BTreeSet::new();
if let Some(code_dep) = &dep.maybe_code { for (_, dep) in module.dependencies.iter() {
deps.insert(code_dep.clone()); if let Some(code_dep) = &dep.maybe_code {
deps.insert(code_dep.clone());
}
if let Some(type_dep) = &dep.maybe_type {
deps.insert(type_dep.clone());
}
} }
if let Some(type_dep) = &dep.maybe_type { if let Some((_, types_dep)) = &module.maybe_types {
deps.insert(type_dep.clone()); deps.insert(types_dep.clone());
} }
let item = ModuleInfoMapItem {
deps: deps.into_iter().collect(),
size: module.size(),
};
Some((specifier.clone(), item))
} else {
None
} }
if let Some((_, types_dep)) = &module.maybe_types {
deps.insert(types_dep.clone());
}
let item = ModuleInfoMapItem {
deps: deps.into_iter().collect(),
size: module.size(),
};
(specifier.clone(), item)
}) })
.collect(); .collect();
ModuleInfoMap::new(map) ModuleInfoMap::new(map)
} }
/// Retrieve a map that contains a representation of each module in the graph
/// which can be used to provide code to a module loader without holding all
/// the state to be able to operate on the graph.
pub fn get_loadable_modules(
&self,
) -> HashMap<ModuleSpecifier, Result<ModuleSource, AnyError>> {
let mut loadable_modules: HashMap<
ModuleSpecifier,
Result<ModuleSource, AnyError>,
> = self.modules.iter().map(to_module_result).collect();
for (specifier, _) in self.redirects.iter() {
if let Some(module_slot) =
self.modules.get(self.resolve_specifier(specifier))
{
let (_, result) = to_module_result((specifier, module_slot));
loadable_modules.insert(specifier.clone(), result);
}
}
loadable_modules
}
pub fn get_media_type( pub fn get_media_type(
&self, &self,
specifier: &ModuleSpecifier, specifier: &ModuleSpecifier,
) -> Option<MediaType> { ) -> Option<MediaType> {
if let Some(module) = self.get_module(specifier) { if let ModuleSlot::Module(module) = self.get_module(specifier) {
Some(module.media_type) Some(module.media_type)
} else { } else {
None None
} }
} }
fn get_module(&self, specifier: &ModuleSpecifier) -> Option<&Module> { fn get_module(&self, specifier: &ModuleSpecifier) -> &ModuleSlot {
let s = self.resolve_specifier(specifier); let s = self.resolve_specifier(specifier);
self.modules.get(s) if let Some(module_slot) = self.modules.get(s) {
module_slot
} else {
&ModuleSlot::None
}
} }
fn get_module_mut( fn get_module_mut(
&mut self, &mut self,
specifier: &ModuleSpecifier, specifier: &ModuleSpecifier,
) -> Option<&mut Module> { ) -> Option<&mut ModuleSlot> {
// this is duplicated code because `.resolve_specifier` requires an // this is duplicated code because `.resolve_specifier` requires an
// immutable borrow, but if `.resolve_specifier` is mut, then everything // immutable borrow, but if `.resolve_specifier` is mut, then everything
// that calls it is is mut // that calls it is is mut
@ -1174,12 +1269,14 @@ impl Graph {
// files will not get emitted. To counter act that behavior, we will // files will not get emitted. To counter act that behavior, we will
// include all modules that are emittable. // include all modules that are emittable.
let mut specifiers = HashSet::<&ModuleSpecifier>::new(); let mut specifiers = HashSet::<&ModuleSpecifier>::new();
for (_, module) in self.modules.iter() { for (_, module_slot) in self.modules.iter() {
if module.media_type == MediaType::JSX if let ModuleSlot::Module(module) = module_slot {
|| module.media_type == MediaType::TypeScript if module.media_type == MediaType::JSX
|| module.media_type == MediaType::TSX || module.media_type == MediaType::TypeScript
{ || module.media_type == MediaType::TSX
specifiers.insert(&module.specifier); {
specifiers.insert(&module.specifier);
}
} }
} }
// We should include all the original roots as well. // We should include all the original roots as well.
@ -1196,7 +1293,12 @@ impl Graph {
// if the root module has a types specifier, we should be sending that // if the root module has a types specifier, we should be sending that
// to tsc instead of the original specifier // to tsc instead of the original specifier
let specifier = self.resolve_specifier(ms); let specifier = self.resolve_specifier(ms);
let module = self.get_module(specifier).unwrap(); let module =
if let ModuleSlot::Module(module) = self.get_module(specifier) {
module
} else {
panic!("missing module");
};
let specifier = if let Some((_, types_specifier)) = &module.maybe_types let specifier = if let Some((_, types_specifier)) = &module.maybe_types
{ {
self.resolve_specifier(types_specifier) self.resolve_specifier(types_specifier)
@ -1216,7 +1318,7 @@ impl Graph {
/// Get the source for a given module specifier. If the module is not part /// Get the source for a given module specifier. If the module is not part
/// of the graph, the result will be `None`. /// of the graph, the result will be `None`.
pub fn get_source(&self, specifier: &ModuleSpecifier) -> Option<String> { pub fn get_source(&self, specifier: &ModuleSpecifier) -> Option<String> {
if let Some(module) = self.get_module(specifier) { if let ModuleSlot::Module(module) = self.get_module(specifier) {
Some(module.source.clone()) Some(module.source.clone())
} else { } else {
None None
@ -1232,7 +1334,11 @@ impl Graph {
} }
let module = self.roots[0].clone(); let module = self.roots[0].clone();
let m = self.get_module(&module).unwrap(); let m = if let ModuleSlot::Module(module) = self.get_module(&module) {
module
} else {
return Err(GraphError::MissingSpecifier(module.clone()).into());
};
let mut seen = HashSet::new(); let mut seen = HashSet::new();
let mut totals = HashMap::new(); let mut totals = HashMap::new();
@ -1247,9 +1353,19 @@ impl Graph {
(None, None) (None, None)
}; };
let dep_count = self
.modules
.iter()
.filter_map(|(_, m)| match m {
ModuleSlot::Module(_) => Some(1),
_ => None,
})
.count()
- 1;
Ok(ModuleGraphInfo { Ok(ModuleGraphInfo {
compiled, compiled,
dep_count: self.modules.len() - 1, dep_count,
file_type: m.media_type, file_type: m.media_type,
files, files,
info, info,
@ -1267,15 +1383,19 @@ impl Graph {
let check_js = config.get_check_js(); let check_js = config.get_check_js();
let config = config.as_bytes(); let config = config.as_bytes();
self.modules.iter().all(|(_, m)| { self.modules.iter().all(|(_, m)| {
let needs_emit = match m.media_type { if let ModuleSlot::Module(m) = m {
MediaType::TypeScript | MediaType::TSX | MediaType::JSX => true, let needs_emit = match m.media_type {
MediaType::JavaScript => check_js, MediaType::TypeScript | MediaType::TSX | MediaType::JSX => true,
_ => false, MediaType::JavaScript => check_js,
}; _ => false,
if needs_emit { };
m.is_emit_valid(&config) if needs_emit {
m.is_emit_valid(&config)
} else {
true
}
} else { } else {
true false
} }
}) })
} }
@ -1286,15 +1406,17 @@ impl Graph {
pub fn lock(&self) { pub fn lock(&self) {
if let Some(lf) = self.maybe_lockfile.as_ref() { if let Some(lf) = self.maybe_lockfile.as_ref() {
let mut lockfile = lf.lock().unwrap(); let mut lockfile = lf.lock().unwrap();
for (ms, module) in self.modules.iter() { for (ms, module_slot) in self.modules.iter() {
let specifier = module.specifier.to_string(); if let ModuleSlot::Module(module) = module_slot {
let valid = lockfile.check_or_insert(&specifier, &module.source); let specifier = module.specifier.to_string();
if !valid { let valid = lockfile.check_or_insert(&specifier, &module.source);
eprintln!( if !valid {
"{}", eprintln!(
GraphError::InvalidSource(ms.clone(), lockfile.filename.clone()) "{}",
); GraphError::InvalidSource(ms.clone(), lockfile.filename.clone())
std::process::exit(10); );
std::process::exit(10);
}
} }
} }
} }
@ -1305,9 +1427,12 @@ impl Graph {
/// checked to determine if it is valid. /// checked to determine if it is valid.
fn needs_emit(&self, config: &TsConfig) -> bool { fn needs_emit(&self, config: &TsConfig) -> bool {
let check_js = config.get_check_js(); let check_js = config.get_check_js();
self.modules.iter().any(|(_, m)| match m.media_type { self.modules.iter().any(|(_, m)| match m {
MediaType::TypeScript | MediaType::TSX | MediaType::JSX => true, ModuleSlot::Module(m) => match m.media_type {
MediaType::JavaScript => check_js, MediaType::TypeScript | MediaType::TSX | MediaType::JSX => true,
MediaType::JavaScript => check_js,
_ => false,
},
_ => false, _ => false,
}) })
} }
@ -1332,10 +1457,11 @@ impl Graph {
referrer: &ModuleSpecifier, referrer: &ModuleSpecifier,
prefer_types: bool, prefer_types: bool,
) -> Result<ModuleSpecifier, AnyError> { ) -> Result<ModuleSpecifier, AnyError> {
if !self.contains_module(referrer) { let module = if let ModuleSlot::Module(module) = self.get_module(referrer) {
return Err(GraphError::MissingSpecifier(referrer.to_owned()).into()); module
} } else {
let module = self.get_module(referrer).unwrap(); return Err(GraphError::MissingSpecifier(referrer.clone()).into());
};
if !module.dependencies.contains_key(specifier) { if !module.dependencies.contains_key(specifier) {
return Err( return Err(
GraphError::MissingDependency( GraphError::MissingDependency(
@ -1363,7 +1489,11 @@ impl Graph {
.into(), .into(),
); );
}; };
if !self.contains_module(&resolved_specifier) { let dep_module = if let ModuleSlot::Module(dep_module) =
self.get_module(&resolved_specifier)
{
dep_module
} else {
return Err( return Err(
GraphError::MissingDependency( GraphError::MissingDependency(
referrer.to_owned(), referrer.to_owned(),
@ -1371,8 +1501,7 @@ impl Graph {
) )
.into(), .into(),
); );
} };
let dep_module = self.get_module(&resolved_specifier).unwrap();
// In the case that there is a X-TypeScript-Types or a triple-slash types, // In the case that there is a X-TypeScript-Types or a triple-slash types,
// then the `maybe_types` specifier will be populated and we should use that // then the `maybe_types` specifier will be populated and we should use that
// instead. // instead.
@ -1424,7 +1553,7 @@ impl Graph {
pub fn transpile( pub fn transpile(
&mut self, &mut self,
options: TranspileOptions, options: TranspileOptions,
) -> Result<(Stats, Option<IgnoredCompilerOptions>), AnyError> { ) -> Result<ResultInfo, AnyError> {
let start = Instant::now(); let start = Instant::now();
let mut ts_config = TsConfig::new(json!({ let mut ts_config = TsConfig::new(json!({
@ -1443,37 +1572,39 @@ impl Graph {
let mut emit_count: u128 = 0; let mut emit_count: u128 = 0;
let config = ts_config.as_bytes(); let config = ts_config.as_bytes();
for (_, module) in self.modules.iter_mut() { for (_, module_slot) in self.modules.iter_mut() {
// TODO(kitsonk) a lot of this logic should be refactored into `Module` as if let ModuleSlot::Module(module) = module_slot {
// we start to support other methods on the graph. Especially managing // TODO(kitsonk) a lot of this logic should be refactored into `Module` as
// the dirty state is something the module itself should "own". // we start to support other methods on the graph. Especially managing
// the dirty state is something the module itself should "own".
// if the module is a Dts file we should skip it // if the module is a Dts file we should skip it
if module.media_type == MediaType::Dts { if module.media_type == MediaType::Dts {
continue; continue;
}
// if we don't have check_js enabled, we won't touch non TypeScript or JSX
// modules
if !(emit_options.check_js
|| module.media_type == MediaType::JSX
|| module.media_type == MediaType::TSX
|| module.media_type == MediaType::TypeScript)
{
continue;
}
// skip modules that already have a valid emit
if !options.reload && module.is_emit_valid(&config) {
continue;
}
if module.maybe_parsed_module.is_none() {
module.parse()?;
}
let parsed_module = module.maybe_parsed_module.clone().unwrap();
let emit = parsed_module.transpile(&emit_options)?;
emit_count += 1;
module.maybe_emit = Some(Emit::Cli(emit));
module.set_version(&config);
module.is_dirty = true;
} }
// if we don't have check_js enabled, we won't touch non TypeScript or JSX
// modules
if !(emit_options.check_js
|| module.media_type == MediaType::JSX
|| module.media_type == MediaType::TSX
|| module.media_type == MediaType::TypeScript)
{
continue;
}
// skip modules that already have a valid emit
if !options.reload && module.is_emit_valid(&config) {
continue;
}
if module.maybe_parsed_module.is_none() {
module.parse()?;
}
let parsed_module = module.maybe_parsed_module.clone().unwrap();
let emit = parsed_module.transpile(&emit_options)?;
emit_count += 1;
module.maybe_emit = Some(Emit::Cli(emit));
module.set_version(&config);
module.is_dirty = true;
} }
self.flush()?; self.flush()?;
@ -1483,7 +1614,12 @@ impl Graph {
("Total time".to_string(), start.elapsed().as_millis()), ("Total time".to_string(), start.elapsed().as_millis()),
]); ]);
Ok((stats, maybe_ignored_options)) Ok(ResultInfo {
diagnostics: Default::default(),
loadable_modules: self.get_loadable_modules(),
maybe_ignored_options,
stats,
})
} }
} }
@ -1510,7 +1646,6 @@ impl swc_bundler::Resolve for Graph {
/// A structure for building a dependency graph of modules. /// A structure for building a dependency graph of modules.
pub struct GraphBuilder { pub struct GraphBuilder {
fetched: HashSet<ModuleSpecifier>,
graph: Graph, graph: Graph,
maybe_import_map: Option<Rc<RefCell<ImportMap>>>, maybe_import_map: Option<Rc<RefCell<ImportMap>>>,
pending: FuturesUnordered<FetchFuture>, pending: FuturesUnordered<FetchFuture>,
@ -1529,7 +1664,6 @@ impl GraphBuilder {
}; };
GraphBuilder { GraphBuilder {
graph: Graph::new(handler, maybe_lockfile), graph: Graph::new(handler, maybe_lockfile),
fetched: HashSet::new(),
maybe_import_map: internal_import_map, maybe_import_map: internal_import_map,
pending: FuturesUnordered::new(), pending: FuturesUnordered::new(),
} }
@ -1543,12 +1677,22 @@ impl GraphBuilder {
specifier: &ModuleSpecifier, specifier: &ModuleSpecifier,
is_dynamic: bool, is_dynamic: bool,
) -> Result<(), AnyError> { ) -> Result<(), AnyError> {
self.fetch(specifier, &None, is_dynamic)?; self.fetch(specifier, &None, is_dynamic);
loop { loop {
let cached_module = self.pending.next().await.unwrap()?; match self.pending.next().await {
let is_root = &cached_module.specifier == specifier; Some(Err((specifier, err))) => {
self.visit(cached_module, is_root)?; self
.graph
.modules
.insert(specifier, ModuleSlot::Err(Rc::new(err)));
}
Some(Ok(cached_module)) => {
let is_root = &cached_module.specifier == specifier;
self.visit(cached_module, is_root)?;
}
_ => {}
}
if self.pending.is_empty() { if self.pending.is_empty() {
break; break;
} }
@ -1573,20 +1717,19 @@ impl GraphBuilder {
specifier: &ModuleSpecifier, specifier: &ModuleSpecifier,
maybe_referrer: &Option<Location>, maybe_referrer: &Option<Location>,
is_dynamic: bool, is_dynamic: bool,
) -> Result<(), AnyError> { ) {
if self.fetched.contains(&specifier) { if !self.graph.modules.contains_key(&specifier) {
return Ok(()); self
.graph
.modules
.insert(specifier.clone(), ModuleSlot::Pending);
let future = self.graph.handler.borrow_mut().fetch(
specifier.clone(),
maybe_referrer.clone(),
is_dynamic,
);
self.pending.push(future);
} }
self.fetched.insert(specifier.clone());
let future = self.graph.handler.borrow_mut().fetch(
specifier.clone(),
maybe_referrer.clone(),
is_dynamic,
);
self.pending.push(future);
Ok(())
} }
/// Visit a module that has been fetched, hydrating the module, analyzing its /// Visit a module that has been fetched, hydrating the module, analyzing its
@ -1632,14 +1775,14 @@ impl GraphBuilder {
for (_, dep) in module.dependencies.iter() { for (_, dep) in module.dependencies.iter() {
let maybe_referrer = Some(dep.location.clone()); let maybe_referrer = Some(dep.location.clone());
if let Some(specifier) = dep.maybe_code.as_ref() { if let Some(specifier) = dep.maybe_code.as_ref() {
self.fetch(specifier, &maybe_referrer, dep.is_dynamic)?; self.fetch(specifier, &maybe_referrer, dep.is_dynamic);
} }
if let Some(specifier) = dep.maybe_type.as_ref() { if let Some(specifier) = dep.maybe_type.as_ref() {
self.fetch(specifier, &maybe_referrer, dep.is_dynamic)?; self.fetch(specifier, &maybe_referrer, dep.is_dynamic);
} }
} }
if let Some((_, specifier)) = module.maybe_types.as_ref() { if let Some((_, specifier)) = module.maybe_types.as_ref() {
self.fetch(specifier, &None, false)?; self.fetch(specifier, &None, false);
} }
if specifier != requested_specifier { if specifier != requested_specifier {
self self
@ -1647,7 +1790,10 @@ impl GraphBuilder {
.redirects .redirects
.insert(requested_specifier, specifier.clone()); .insert(requested_specifier, specifier.clone());
} }
self.graph.modules.insert(specifier, module); self
.graph
.modules
.insert(specifier, ModuleSlot::Module(Box::new(module)));
Ok(()) Ok(())
} }
@ -1702,7 +1848,7 @@ pub mod tests {
fn get_cache( fn get_cache(
&self, &self,
specifier: ModuleSpecifier, specifier: ModuleSpecifier,
) -> Result<CachedModule, AnyError> { ) -> Result<CachedModule, (ModuleSpecifier, AnyError)> {
let specifier_text = specifier let specifier_text = specifier
.to_string() .to_string()
.replace(":///", "_") .replace(":///", "_")
@ -1710,7 +1856,8 @@ pub mod tests {
.replace("/", "-"); .replace("/", "-");
let source_path = self.fixtures.join(specifier_text); let source_path = self.fixtures.join(specifier_text);
let media_type = MediaType::from(&source_path); let media_type = MediaType::from(&source_path);
let source = fs::read_to_string(&source_path)?; let source = fs::read_to_string(&source_path)
.map_err(|err| (specifier.clone(), err.into()))?;
let is_remote = specifier.as_url().scheme() != "file"; let is_remote = specifier.as_url().scheme() != "file";
Ok(CachedModule { Ok(CachedModule {
@ -2280,10 +2427,9 @@ pub mod tests {
ModuleSpecifier::resolve_url_or_path("file:///tests/main.ts") ModuleSpecifier::resolve_url_or_path("file:///tests/main.ts")
.expect("could not resolve module"); .expect("could not resolve module");
let (mut graph, handler) = setup(specifier).await; let (mut graph, handler) = setup(specifier).await;
let (stats, maybe_ignored_options) = let result_info = graph.transpile(TranspileOptions::default()).unwrap();
graph.transpile(TranspileOptions::default()).unwrap(); assert_eq!(result_info.stats.0.len(), 3);
assert_eq!(stats.0.len(), 3); assert_eq!(result_info.maybe_ignored_options, None);
assert_eq!(maybe_ignored_options, None);
let h = handler.borrow(); let h = handler.borrow();
assert_eq!(h.cache_calls.len(), 2); assert_eq!(h.cache_calls.len(), 2);
match &h.cache_calls[0].1 { match &h.cache_calls[0].1 {
@ -2334,7 +2480,7 @@ pub mod tests {
ModuleSpecifier::resolve_url_or_path("https://deno.land/x/transpile.tsx") ModuleSpecifier::resolve_url_or_path("https://deno.land/x/transpile.tsx")
.expect("could not resolve module"); .expect("could not resolve module");
let (mut graph, handler) = setup(specifier).await; let (mut graph, handler) = setup(specifier).await;
let (_, maybe_ignored_options) = graph let result_info = graph
.transpile(TranspileOptions { .transpile(TranspileOptions {
debug: false, debug: false,
maybe_config_path: Some("tests/module_graph/tsconfig.json".to_string()), maybe_config_path: Some("tests/module_graph/tsconfig.json".to_string()),
@ -2342,7 +2488,7 @@ pub mod tests {
}) })
.unwrap(); .unwrap();
assert_eq!( assert_eq!(
maybe_ignored_options.unwrap().items, result_info.maybe_ignored_options.unwrap().items,
vec!["target".to_string()], vec!["target".to_string()],
"the 'target' options should have been ignored" "the 'target' options should have been ignored"
); );

View file

@ -94,26 +94,14 @@ impl ModuleLoader for CliModuleLoader {
maybe_referrer: Option<ModuleSpecifier>, maybe_referrer: Option<ModuleSpecifier>,
_is_dynamic: bool, _is_dynamic: bool,
) -> Pin<Box<deno_core::ModuleSourceFuture>> { ) -> Pin<Box<deno_core::ModuleSourceFuture>> {
let module_specifier = module_specifier.to_owned(); let module_specifier = module_specifier.clone();
let module_url_specified = module_specifier.to_string();
let program_state = self.program_state.clone(); let program_state = self.program_state.clone();
// NOTE: this block is async only because of `deno_core` // NOTE: this block is async only because of `deno_core`
// interface requirements; module was already loaded // interface requirements; module was already loaded
// when constructing module graph during call to `prepare_load`. // when constructing module graph during call to `prepare_load`.
let fut = async move { async move { program_state.load(module_specifier, maybe_referrer) }
let compiled_module = program_state .boxed_local()
.fetch_compiled_module(module_specifier, maybe_referrer)?;
Ok(deno_core::ModuleSource {
// Real module name, might be different from initial specifier
// due to redirections.
code: compiled_module.code,
module_url_specified,
module_url_found: compiled_module.name,
})
};
fut.boxed_local()
} }
fn prepare_load( fn prepare_load(

View file

@ -8,7 +8,6 @@ use crate::http_cache;
use crate::http_util; use crate::http_util;
use crate::import_map::ImportMap; use crate::import_map::ImportMap;
use crate::lockfile::Lockfile; use crate::lockfile::Lockfile;
use crate::media_type::MediaType;
use crate::module_graph::CheckOptions; use crate::module_graph::CheckOptions;
use crate::module_graph::GraphBuilder; use crate::module_graph::GraphBuilder;
use crate::module_graph::TranspileOptions; use crate::module_graph::TranspileOptions;
@ -18,11 +17,14 @@ use crate::specifier_handler::FetchHandler;
use deno_runtime::inspector::InspectorServer; use deno_runtime::inspector::InspectorServer;
use deno_runtime::permissions::Permissions; use deno_runtime::permissions::Permissions;
use deno_core::error::generic_error; use deno_core::error::anyhow;
use deno_core::error::get_custom_error_class;
use deno_core::error::AnyError; use deno_core::error::AnyError;
use deno_core::url::Url; use deno_core::url::Url;
use deno_core::ModuleSource;
use deno_core::ModuleSpecifier; use deno_core::ModuleSpecifier;
use std::cell::RefCell; use std::cell::RefCell;
use std::collections::HashMap;
use std::env; use std::env;
use std::rc::Rc; use std::rc::Rc;
use std::sync::Arc; use std::sync::Arc;
@ -36,12 +38,6 @@ pub fn exit_unstable(api_name: &str) {
std::process::exit(70); std::process::exit(70);
} }
// TODO(@kitsonk) probably can refactor this better with the graph.
pub struct CompiledModule {
pub code: String,
pub name: String,
}
/// This structure represents state of single "deno" program. /// This structure represents state of single "deno" program.
/// ///
/// It is shared by all created workers (thus V8 isolates). /// It is shared by all created workers (thus V8 isolates).
@ -50,6 +46,8 @@ pub struct ProgramState {
pub flags: flags::Flags, pub flags: flags::Flags,
pub dir: deno_dir::DenoDir, pub dir: deno_dir::DenoDir,
pub file_fetcher: FileFetcher, pub file_fetcher: FileFetcher,
pub modules:
Arc<Mutex<HashMap<ModuleSpecifier, Result<ModuleSource, AnyError>>>>,
pub lockfile: Option<Arc<Mutex<Lockfile>>>, pub lockfile: Option<Arc<Mutex<Lockfile>>>,
pub maybe_import_map: Option<ImportMap>, pub maybe_import_map: Option<ImportMap>,
pub maybe_inspector_server: Option<Arc<InspectorServer>>, pub maybe_inspector_server: Option<Arc<InspectorServer>>,
@ -111,6 +109,7 @@ impl ProgramState {
dir, dir,
flags, flags,
file_fetcher, file_fetcher,
modules: Default::default(),
lockfile, lockfile,
maybe_import_map, maybe_import_map,
maybe_inspector_server, maybe_inspector_server,
@ -146,17 +145,17 @@ impl ProgramState {
let debug = self.flags.log_level == Some(log::Level::Debug); let debug = self.flags.log_level == Some(log::Level::Debug);
let maybe_config_path = self.flags.config_path.clone(); let maybe_config_path = self.flags.config_path.clone();
if self.flags.no_check { let result_modules = if self.flags.no_check {
let (stats, maybe_ignored_options) = let result_info = graph.transpile(TranspileOptions {
graph.transpile(TranspileOptions { debug,
debug, maybe_config_path,
maybe_config_path, reload: self.flags.reload,
reload: self.flags.reload, })?;
})?; debug!("{}", result_info.stats);
debug!("{}", stats); if let Some(ignored_options) = result_info.maybe_ignored_options {
if let Some(ignored_options) = maybe_ignored_options { warn!("{}", ignored_options);
eprintln!("{}", ignored_options);
} }
result_info.loadable_modules
} else { } else {
let result_info = graph.check(CheckOptions { let result_info = graph.check(CheckOptions {
debug, debug,
@ -171,10 +170,14 @@ impl ProgramState {
eprintln!("{}", ignored_options); eprintln!("{}", ignored_options);
} }
if !result_info.diagnostics.is_empty() { if !result_info.diagnostics.is_empty() {
return Err(generic_error(result_info.diagnostics.to_string())); return Err(anyhow!(result_info.diagnostics));
} }
result_info.loadable_modules
}; };
let mut loadable_modules = self.modules.lock().unwrap();
loadable_modules.extend(result_modules);
if let Some(ref lockfile) = self.lockfile { if let Some(ref lockfile) = self.lockfile {
let g = lockfile.lock().unwrap(); let g = lockfile.lock().unwrap();
g.write()?; g.write()?;
@ -183,56 +186,55 @@ impl ProgramState {
Ok(()) Ok(())
} }
pub fn fetch_compiled_module( pub fn load(
&self, &self,
module_specifier: ModuleSpecifier, specifier: ModuleSpecifier,
maybe_referrer: Option<ModuleSpecifier>, maybe_referrer: Option<ModuleSpecifier>,
) -> Result<CompiledModule, AnyError> { ) -> Result<ModuleSource, AnyError> {
// TODO(@kitsonk) this really needs to be avoided and refactored out, as we let modules = self.modules.lock().unwrap();
// really should just be getting this from the module graph. modules
let out = self .get(&specifier)
.file_fetcher .map(|r| match r {
.get_source(&module_specifier) Ok(module_source) => Ok(module_source.clone()),
.expect("Cached source file doesn't exist"); Err(err) => {
// TODO(@kitsonk) this feels a bit hacky but it works, without
let specifier = out.specifier.clone(); // introducing another enum to have to try to deal with.
let compiled_module = if let Some((code, _)) = if get_custom_error_class(err) == Some("NotFound") {
self.get_emit(&specifier.as_url()) let message = if let Some(referrer) = &maybe_referrer {
{ format!("{}\n From: {}\n If the source module contains only types, use `import type` and `export type` to import it instead.", err, referrer)
CompiledModule { } else {
code: String::from_utf8(code).unwrap(), format!("{}\n If the source module contains only types, use `import type` and `export type` to import it instead.", err)
name: specifier.as_url().to_string(), };
} warn!("{}: {}", crate::colors::yellow("warning"), message);
// We expect a compiled source for any non-JavaScript files, except for Ok(ModuleSource {
// local files that have an unknown media type and no referrer (root modules code: "".to_string(),
// that do not have an extension.) module_url_found: specifier.to_string(),
} else if out.media_type != MediaType::JavaScript module_url_specified: specifier.to_string(),
&& !(out.media_type == MediaType::Unknown })
&& maybe_referrer.is_none() } else {
&& specifier.as_url().scheme() == "file") // anyhow errors don't support cloning, so we have to manage this
{ // ourselves
let message = if let Some(referrer) = maybe_referrer { Err(anyhow!(err.to_string()))
format!("Compiled module not found \"{}\"\n From: {}\n If the source module contains only types, use `import type` and `export type` to import it instead.", module_specifier, referrer) }
} else { },
format!("Compiled module not found \"{}\"\n If the source module contains only types, use `import type` and `export type` to import it instead.", module_specifier) })
}; .unwrap_or_else(|| {
info!("{}: {}", crate::colors::yellow("warning"), message); if let Some(referrer) = maybe_referrer {
CompiledModule { Err(anyhow!(
code: "".to_string(), "Module \"{}\" is missing from the graph.\n From: {}",
name: specifier.as_url().to_string(), specifier,
} referrer
} else { ))
CompiledModule { } else {
code: out.source, Err(anyhow!(
name: specifier.as_url().to_string(), "Module \"{}\" is missing from the graph.",
} specifier
}; ))
}
Ok(compiled_module) })
} }
// TODO(@kitsonk) this should be a straight forward API on file_fetcher or // TODO(@kitsonk) this should be refactored to get it from the module graph
// whatever future refactors do...
fn get_emit(&self, url: &Url) -> Option<(Vec<u8>, Option<Vec<u8>>)> { fn get_emit(&self, url: &Url) -> Option<(Vec<u8>, Option<Vec<u8>>)> {
match url.scheme() { match url.scheme() {
// we should only be looking for emits for schemes that denote external // we should only be looking for emits for schemes that denote external
@ -245,11 +247,11 @@ impl ProgramState {
let emit_path = self let emit_path = self
.dir .dir
.gen_cache .gen_cache
.get_cache_filename_with_extension(&url, "js"); .get_cache_filename_with_extension(&url, "js")?;
let emit_map_path = self let emit_map_path = self
.dir .dir
.gen_cache .gen_cache
.get_cache_filename_with_extension(&url, "js.map"); .get_cache_filename_with_extension(&url, "js.map")?;
if let Ok(code) = self.dir.gen_cache.get(&emit_path) { if let Ok(code) = self.dir.gen_cache.get(&emit_path) {
let maybe_map = if let Ok(map) = self.dir.gen_cache.get(&emit_map_path) { let maybe_map = if let Ok(map) = self.dir.gen_cache.get(&emit_map_path) {
Some(map) Some(map)

View file

@ -25,8 +25,12 @@ use std::pin::Pin;
use std::sync::Arc; use std::sync::Arc;
pub type DependencyMap = HashMap<String, Dependency>; pub type DependencyMap = HashMap<String, Dependency>;
pub type FetchFuture = pub type FetchFuture = Pin<
Pin<Box<(dyn Future<Output = Result<CachedModule, AnyError>> + 'static)>>; Box<
(dyn Future<Output = Result<CachedModule, (ModuleSpecifier, AnyError)>>
+ 'static),
>,
>;
/// A group of errors that represent errors that can occur with an /// A group of errors that represent errors that can occur with an
/// an implementation of `SpecifierHandler`. /// an implementation of `SpecifierHandler`.
@ -287,23 +291,30 @@ impl SpecifierHandler for FetchHandler {
// they cannot actually get to the source code that is quoted, as // they cannot actually get to the source code that is quoted, as
// it only exists in the runtime memory of Deno. // it only exists in the runtime memory of Deno.
if !location.filename.contains("$deno$") { if !location.filename.contains("$deno$") {
HandlerError::FetchErrorWithLocation(err.to_string(), location) (
.into() requested_specifier.clone(),
HandlerError::FetchErrorWithLocation(err.to_string(), location)
.into(),
)
} else { } else {
err (requested_specifier.clone(), err)
} }
} else { } else {
err (requested_specifier.clone(), err)
} }
})?; })?;
let url = source_file.specifier.as_url(); let url = source_file.specifier.as_url();
let is_remote = url.scheme() != "file"; let is_remote = url.scheme() != "file";
let filename = disk_cache.get_cache_filename_with_extension(url, "meta"); let filename = disk_cache.get_cache_filename_with_extension(url, "meta");
let maybe_version = if let Ok(bytes) = disk_cache.get(&filename) { let maybe_version = if let Some(filename) = filename {
if let Ok(compiled_file_metadata) = if let Ok(bytes) = disk_cache.get(&filename) {
CompiledFileMetadata::from_bytes(&bytes) if let Ok(compiled_file_metadata) =
{ CompiledFileMetadata::from_bytes(&bytes)
Some(compiled_file_metadata.version_hash) {
Some(compiled_file_metadata.version_hash)
} else {
None
}
} else { } else {
None None
} }
@ -314,19 +325,26 @@ impl SpecifierHandler for FetchHandler {
let mut maybe_map_path = None; let mut maybe_map_path = None;
let map_path = let map_path =
disk_cache.get_cache_filename_with_extension(&url, "js.map"); disk_cache.get_cache_filename_with_extension(&url, "js.map");
let maybe_map = if let Ok(map) = disk_cache.get(&map_path) { let maybe_map = if let Some(map_path) = map_path {
maybe_map_path = Some(disk_cache.location.join(map_path)); if let Ok(map) = disk_cache.get(&map_path) {
Some(String::from_utf8(map)?) maybe_map_path = Some(disk_cache.location.join(map_path));
Some(String::from_utf8(map).unwrap())
} else {
None
}
} else { } else {
None None
}; };
let mut maybe_emit = None; let mut maybe_emit = None;
let mut maybe_emit_path = None; let mut maybe_emit_path = None;
let emit_path = disk_cache.get_cache_filename_with_extension(&url, "js"); let emit_path = disk_cache.get_cache_filename_with_extension(&url, "js");
if let Ok(code) = disk_cache.get(&emit_path) { if let Some(emit_path) = emit_path {
maybe_emit = Some(Emit::Cli((String::from_utf8(code)?, maybe_map))); if let Ok(code) = disk_cache.get(&emit_path) {
maybe_emit_path = maybe_emit =
Some((disk_cache.location.join(emit_path), maybe_map_path)); Some(Emit::Cli((String::from_utf8(code).unwrap(), maybe_map)));
maybe_emit_path =
Some((disk_cache.location.join(emit_path), maybe_map_path));
}
}; };
Ok(CachedModule { Ok(CachedModule {
@ -353,8 +371,12 @@ impl SpecifierHandler for FetchHandler {
let filename = self let filename = self
.disk_cache .disk_cache
.get_cache_filename_with_extension(specifier.as_url(), "buildinfo"); .get_cache_filename_with_extension(specifier.as_url(), "buildinfo");
if let Ok(tsbuildinfo) = self.disk_cache.get(&filename) { if let Some(filename) = filename {
Ok(Some(String::from_utf8(tsbuildinfo)?)) if let Ok(tsbuildinfo) = self.disk_cache.get(&filename) {
Ok(Some(String::from_utf8(tsbuildinfo)?))
} else {
Ok(None)
}
} else { } else {
Ok(None) Ok(None)
} }
@ -367,7 +389,8 @@ impl SpecifierHandler for FetchHandler {
) -> Result<(), AnyError> { ) -> Result<(), AnyError> {
let filename = self let filename = self
.disk_cache .disk_cache
.get_cache_filename_with_extension(specifier.as_url(), "buildinfo"); .get_cache_filename_with_extension(specifier.as_url(), "buildinfo")
.unwrap();
debug!("set_tsbuildinfo - filename {:?}", filename); debug!("set_tsbuildinfo - filename {:?}", filename);
self self
.disk_cache .disk_cache
@ -383,14 +406,17 @@ impl SpecifierHandler for FetchHandler {
match emit { match emit {
Emit::Cli((code, maybe_map)) => { Emit::Cli((code, maybe_map)) => {
let url = specifier.as_url(); let url = specifier.as_url();
let filename = let filename = self
self.disk_cache.get_cache_filename_with_extension(url, "js"); .disk_cache
.get_cache_filename_with_extension(url, "js")
.unwrap();
self.disk_cache.set(&filename, code.as_bytes())?; self.disk_cache.set(&filename, code.as_bytes())?;
if let Some(map) = maybe_map { if let Some(map) = maybe_map {
let filename = self let filename = self
.disk_cache .disk_cache
.get_cache_filename_with_extension(url, "js.map"); .get_cache_filename_with_extension(url, "js.map")
.unwrap();
self.disk_cache.set(&filename, map.as_bytes())?; self.disk_cache.set(&filename, map.as_bytes())?;
} }
} }
@ -425,7 +451,8 @@ impl SpecifierHandler for FetchHandler {
let compiled_file_metadata = CompiledFileMetadata { version_hash }; let compiled_file_metadata = CompiledFileMetadata { version_hash };
let filename = self let filename = self
.disk_cache .disk_cache
.get_cache_filename_with_extension(specifier.as_url(), "meta"); .get_cache_filename_with_extension(specifier.as_url(), "meta")
.unwrap();
self self
.disk_cache .disk_cache
@ -475,9 +502,12 @@ impl SpecifierHandler for MemoryHandler {
..Default::default() ..Default::default()
}) })
} else { } else {
Err(custom_error( Err((
"NotFound", specifier.clone(),
format!("Unable to find specifier in sources: {}", specifier), custom_error(
"NotFound",
format!("Unable to find specifier in sources: {}", specifier),
),
)) ))
}; };

View file

@ -0,0 +1,2 @@
import "./bad.mjs";
export default () => "error";

View file

@ -0,0 +1,2 @@
await import("./bad2.mjs");
export default () => "error";

View file

@ -1,2 +1,4 @@
// eslint-disable-next-line // eslint-disable-next-line
import * as badModule from "./bad-module.ts"; import * as badModule from "./bad-module.ts";
console.log(badModule);

View file

@ -1,2 +1 @@
error: Cannot resolve module "[WILDCARD]/bad-module.ts" from "[WILDCARD]/error_005_missing_dynamic_import.ts". error: Uncaught (in promise) TypeError: Cannot resolve module "[WILDCARD]/cli/tests/bad-module.ts".
at file:///[WILDCARD]/error_005_missing_dynamic_import.ts:3:26

View file

@ -1,2 +1 @@
error: network access to "http://localhost:4545/cli/tests/subdir/mod4.js", run again with the --allow-net flag error: Uncaught (in promise) TypeError: network access to "http://localhost:4545/cli/tests/subdir/mod4.js", run again with the --allow-net flag
at file:///[WILDCARD]cli/tests/error_015_dynamic_import_permissions.js:[WILDCARD]

View file

@ -0,0 +1,7 @@
import("./dynamic_import/b.js").catch(() => {
console.log("caught import error from b.js");
});
import("./dynamic_import/c.js").catch(() => {
console.log("caught import error from c.js");
});

View file

@ -0,0 +1,2 @@
caught import error from [WILDCARD].js
caught import error from [WILDCARD].js

View file

@ -3227,6 +3227,11 @@ itest!(tsx_imports {
output: "tsx_imports.ts.out", output: "tsx_imports.ts.out",
}); });
itest!(fix_dynamic_import_errors {
args: "run --reload fix_dynamic_import_errors.js",
output: "fix_dynamic_import_errors.js.out",
});
itest!(fix_emittable_skipped { itest!(fix_emittable_skipped {
args: "run --reload fix_emittable_skipped.js", args: "run --reload fix_emittable_skipped.js",
output: "fix_emittable_skipped.ts.out", output: "fix_emittable_skipped.ts.out",

View file

@ -2,5 +2,4 @@ Download http://localhost:4548/cli/tests/subdir/redirects/a.ts
Download http://localhost:4546/cli/tests/subdir/redirects/a.ts Download http://localhost:4546/cli/tests/subdir/redirects/a.ts
Download http://localhost:4545/cli/tests/subdir/redirects/a.ts Download http://localhost:4545/cli/tests/subdir/redirects/a.ts
Download http://localhost:4545/cli/tests/subdir/redirects/b.ts Download http://localhost:4545/cli/tests/subdir/redirects/b.ts
Download http://localhost:4545/cli/tests/subdir/redirects/a.ts
Check http://localhost:4548/cli/tests/subdir/redirects/a.ts Check http://localhost:4548/cli/tests/subdir/redirects/a.ts

View file

@ -1,4 +1,4 @@
error: Unsupported scheme "xxx" for module "xxx:". Supported schemes: [ error: Uncaught (in promise) TypeError: Unsupported scheme "xxx" for module "xxx:". Supported schemes: [
"http", "http",
"https", "https",
"file", "file",

View file

@ -43,7 +43,7 @@ pub type ModuleLoadId = i32;
// that happened; not only first and final target. It would simplify a lot // that happened; not only first and final target. It would simplify a lot
// of things throughout the codebase otherwise we may end up requesting // of things throughout the codebase otherwise we may end up requesting
// intermediate redirects from file loader. // intermediate redirects from file loader.
#[derive(Debug, Eq, PartialEq)] #[derive(Debug, Clone, Eq, PartialEq)]
pub struct ModuleSource { pub struct ModuleSource {
pub code: String, pub code: String,
pub module_url_specified: String, pub module_url_specified: String,