diff options
author | David Sherret <dsherret@users.noreply.github.com> | 2023-07-14 18:10:42 -0400 |
---|---|---|
committer | GitHub <noreply@github.com> | 2023-07-14 18:10:42 -0400 |
commit | 306b51d7728a6e28461171d3332a0b3aa8545a6e (patch) | |
tree | 6a5b78c9fcd219ca9c236e5e22cdaba907158146 /cli/tools/vendor/build.rs | |
parent | b83dac3b14340d452ae9a83e5f4da0104407a220 (diff) |
fix(vendor): do not panic vendoring with jsxImportSource and no jsx files (#19837)
Closes #19833
Diffstat (limited to 'cli/tools/vendor/build.rs')
-rw-r--r-- | cli/tools/vendor/build.rs | 138 |
1 files changed, 121 insertions, 17 deletions
diff --git a/cli/tools/vendor/build.rs b/cli/tools/vendor/build.rs index 5cd50836f..3e5f13d81 100644 --- a/cli/tools/vendor/build.rs +++ b/cli/tools/vendor/build.rs @@ -9,6 +9,7 @@ use deno_ast::ModuleSpecifier; use deno_core::anyhow::bail; use deno_core::anyhow::Context; use deno_core::error::AnyError; +use deno_core::futures::future::LocalBoxFuture; use deno_core::parking_lot::Mutex; use deno_graph::EsmModule; use deno_graph::Module; @@ -21,6 +22,7 @@ use crate::args::Lockfile; use crate::cache::ParsedSourceCache; use crate::graph_util; use crate::graph_util::graph_lock_or_exit; +use crate::tools::vendor::import_map::BuildImportMapInput; use super::analyze::has_default_export; use super::import_map::build_import_map; @@ -57,16 +59,47 @@ impl VendorEnvironment for RealVendorEnvironment { } } +type BuildGraphFuture = LocalBoxFuture<'static, Result<ModuleGraph, AnyError>>; + +pub struct BuildInput< + 'a, + TBuildGraphFn: FnOnce(Vec<ModuleSpecifier>) -> BuildGraphFuture, + TEnvironment: VendorEnvironment, +> { + pub entry_points: Vec<ModuleSpecifier>, + pub build_graph: TBuildGraphFn, + pub parsed_source_cache: &'a ParsedSourceCache, + pub output_dir: &'a Path, + pub maybe_original_import_map: Option<&'a ImportMap>, + pub maybe_lockfile: Option<Arc<Mutex<Lockfile>>>, + pub maybe_jsx_import_source: Option<&'a JsxImportSourceConfig>, + pub resolver: &'a dyn deno_graph::source::Resolver, + pub environment: &'a TEnvironment, +} + +pub struct BuildOutput { + pub vendored_count: usize, + pub graph: ModuleGraph, +} + /// Vendors remote modules and returns how many were vendored. -pub fn build( - graph: ModuleGraph, - parsed_source_cache: &ParsedSourceCache, - output_dir: &Path, - original_import_map: Option<&ImportMap>, - maybe_lockfile: Option<Arc<Mutex<Lockfile>>>, - jsx_import_source: Option<&JsxImportSourceConfig>, - environment: &impl VendorEnvironment, -) -> Result<usize, AnyError> { +pub async fn build< + TBuildGraphFn: FnOnce(Vec<ModuleSpecifier>) -> BuildGraphFuture, + TEnvironment: VendorEnvironment, +>( + input: BuildInput<'_, TBuildGraphFn, TEnvironment>, +) -> Result<BuildOutput, AnyError> { + let BuildInput { + mut entry_points, + build_graph, + parsed_source_cache, + output_dir, + maybe_original_import_map: original_import_map, + maybe_lockfile, + maybe_jsx_import_source: jsx_import_source, + resolver, + environment, + } = input; assert!(output_dir.is_absolute()); let output_dir_specifier = ModuleSpecifier::from_directory_path(output_dir).unwrap(); @@ -75,6 +108,19 @@ pub fn build( validate_original_import_map(original_im, &output_dir_specifier)?; } + // add the jsx import source to the entry points to ensure it is always vendored + if let Some(jsx_import_source) = jsx_import_source { + if let Some(specifier_text) = jsx_import_source.maybe_specifier_text() { + if let Ok(specifier) = + resolver.resolve(&specifier_text, &jsx_import_source.base_url) + { + entry_points.push(specifier); + } + } + } + + let graph = build_graph(entry_points).await?; + // check the lockfile if let Some(lockfile) = maybe_lockfile { graph_lock_or_exit(&graph, &mut lockfile.lock()); @@ -130,19 +176,23 @@ pub fn build( // create the import map if necessary if !remote_modules.is_empty() { let import_map_path = output_dir.join("import_map.json"); - let import_map_text = build_import_map( - &output_dir_specifier, - &graph, - &all_modules, - &mappings, + let import_map_text = build_import_map(BuildImportMapInput { + base_dir: &output_dir_specifier, + graph: &graph, + modules: &all_modules, + mappings: &mappings, original_import_map, jsx_import_source, + resolver, parsed_source_cache, - )?; + })?; environment.write_file(&import_map_path, &import_map_text)?; } - Ok(remote_modules.len()) + Ok(BuildOutput { + vendored_count: remote_modules.len(), + graph, + }) } fn validate_original_import_map( @@ -1152,12 +1202,13 @@ mod test { } #[tokio::test] - async fn existing_import_map_jsx_import_source() { + async fn existing_import_map_jsx_import_source_jsx_files() { let mut builder = VendorTestBuilder::default(); builder.add_entry_point("/mod.tsx"); builder.set_jsx_import_source_config(JsxImportSourceConfig { default_specifier: Some("preact".to_string()), module: "jsx-runtime".to_string(), + base_url: builder.resolve_to_url("/deno.json"), }); let mut original_import_map = builder.new_import_map("/import_map.json"); let imports = original_import_map.imports_mut(); @@ -1200,6 +1251,59 @@ mod test { } #[tokio::test] + async fn existing_import_map_jsx_import_source_no_jsx_files() { + let mut builder = VendorTestBuilder::default(); + builder.add_entry_point("/mod.ts"); + builder.set_jsx_import_source_config(JsxImportSourceConfig { + default_specifier: Some("preact".to_string()), + module: "jsx-runtime".to_string(), + base_url: builder.resolve_to_url("/deno.json"), + }); + let mut original_import_map = builder.new_import_map("/import_map.json"); + let imports = original_import_map.imports_mut(); + imports + .append( + "preact/".to_string(), + "https://localhost/preact/".to_string(), + ) + .unwrap(); + let output = builder + .with_loader(|loader| { + loader.add("/mod.ts", "import 'https://localhost/mod.ts';"); + loader.add("https://localhost/mod.ts", "console.log(1)"); + loader.add_with_headers( + "https://localhost/preact/jsx-runtime", + "export function stuff() {}", + &[("content-type", "application/typescript")], + ); + }) + .set_original_import_map(original_import_map) + .build() + .await + .unwrap(); + + assert_eq!( + output.import_map, + Some(json!({ + "imports": { + "https://localhost/": "./localhost/", + "preact/jsx-runtime": "./localhost/preact/jsx-runtime.ts" + }, + })) + ); + assert_eq!( + output.files, + to_file_vec(&[ + ("/vendor/localhost/mod.ts", "console.log(1)"), + ( + "/vendor/localhost/preact/jsx-runtime.ts", + "export function stuff() {}" + ), + ]), + ); + } + + #[tokio::test] async fn vendor_file_fails_loading_dynamic_import() { let mut builder = VendorTestBuilder::with_default_setup(); let err = builder |