From 8d5c2313495014a6af842e21da802e01e11b8e08 Mon Sep 17 00:00:00 2001 From: Luca Casonato Date: Tue, 27 Feb 2024 16:13:16 +0100 Subject: [PATCH] feat(publish): support sloppy imports and bare node built-ins (#22588) --- cli/factory.rs | 2 +- cli/tools/registry/diagnostics.rs | 32 +- cli/tools/registry/mod.rs | 23 +- cli/tools/registry/tar.rs | 8 +- .../registry/unfurl.rs} | 316 ++++++++++++------ cli/util/mod.rs | 1 - tests/integration/publish_tests.rs | 16 + tests/testdata/publish/bare_node_builtins.out | 11 + .../publish/bare_node_builtins/deno.json | 7 + .../publish/bare_node_builtins/mod.ts | 5 + tests/testdata/publish/sloppy_imports.out | 12 + .../publish/sloppy_imports/b/index.ts | 1 + .../testdata/publish/sloppy_imports/deno.json | 7 + tests/testdata/publish/sloppy_imports/mod.ts | 1 + .../publish/unanalyzable_dynamic_import.out | 4 +- tests/testdata/unfurl/b.ts | 0 tests/testdata/unfurl/baz/index.js | 0 17 files changed, 316 insertions(+), 130 deletions(-) rename cli/{util/import_map.rs => tools/registry/unfurl.rs} (54%) create mode 100644 tests/testdata/publish/bare_node_builtins.out create mode 100644 tests/testdata/publish/bare_node_builtins/deno.json create mode 100644 tests/testdata/publish/bare_node_builtins/mod.ts create mode 100644 tests/testdata/publish/sloppy_imports.out create mode 100644 tests/testdata/publish/sloppy_imports/b/index.ts create mode 100644 tests/testdata/publish/sloppy_imports/deno.json create mode 100644 tests/testdata/publish/sloppy_imports/mod.ts create mode 100644 tests/testdata/unfurl/b.ts create mode 100644 tests/testdata/unfurl/baz/index.js diff --git a/cli/factory.rs b/cli/factory.rs index 7302c03129..bee3ea5f6c 100644 --- a/cli/factory.rs +++ b/cli/factory.rs @@ -44,10 +44,10 @@ use crate::resolver::SloppyImportsResolver; use crate::standalone::DenoCompileBinaryWriter; use crate::tools::check::TypeChecker; use crate::tools::coverage::CoverageCollector; +use crate::tools::registry::deno_json_deps; use crate::tools::run::hmr::HmrRunner; use crate::util::file_watcher::WatcherCommunicator; use crate::util::fs::canonicalize_path_maybe_not_exists; -use crate::util::import_map::deno_json_deps; use crate::util::progress_bar::ProgressBar; use crate::util::progress_bar::ProgressBarStyle; use crate::worker::CliMainWorkerFactory; diff --git a/cli/tools/registry/diagnostics.rs b/cli/tools/registry/diagnostics.rs index b605c293b0..78cc6f5552 100644 --- a/cli/tools/registry/diagnostics.rs +++ b/cli/tools/registry/diagnostics.rs @@ -20,7 +20,7 @@ use deno_core::error::AnyError; use deno_graph::FastCheckDiagnostic; use lsp_types::Url; -use crate::util::import_map::ImportMapUnfurlDiagnostic; +use super::unfurl::SpecifierUnfurlerDiagnostic; #[derive(Clone, Default)] pub struct PublishDiagnosticsCollector { @@ -74,7 +74,7 @@ impl PublishDiagnosticsCollector { pub enum PublishDiagnostic { FastCheck(FastCheckDiagnostic), - ImportMapUnfurl(ImportMapUnfurlDiagnostic), + SpecifierUnfurl(SpecifierUnfurlerDiagnostic), InvalidPath { path: PathBuf, message: String, @@ -102,7 +102,7 @@ impl Diagnostic for PublishDiagnostic { .. }) => DiagnosticLevel::Warning, FastCheck(_) => DiagnosticLevel::Error, - ImportMapUnfurl(_) => DiagnosticLevel::Warning, + SpecifierUnfurl(_) => DiagnosticLevel::Warning, InvalidPath { .. } => DiagnosticLevel::Error, DuplicatePath { .. } => DiagnosticLevel::Error, UnsupportedFileType { .. } => DiagnosticLevel::Warning, @@ -114,7 +114,7 @@ impl Diagnostic for PublishDiagnostic { use PublishDiagnostic::*; match &self { FastCheck(diagnostic) => diagnostic.code(), - ImportMapUnfurl(diagnostic) => Cow::Borrowed(diagnostic.code()), + SpecifierUnfurl(diagnostic) => Cow::Borrowed(diagnostic.code()), InvalidPath { .. } => Cow::Borrowed("invalid-path"), DuplicatePath { .. } => Cow::Borrowed("case-insensitive-duplicate-path"), UnsupportedFileType { .. } => Cow::Borrowed("unsupported-file-type"), @@ -126,7 +126,7 @@ impl Diagnostic for PublishDiagnostic { use PublishDiagnostic::*; match &self { FastCheck(diagnostic) => diagnostic.message(), - ImportMapUnfurl(diagnostic) => Cow::Borrowed(diagnostic.message()), + SpecifierUnfurl(diagnostic) => Cow::Borrowed(diagnostic.message()), InvalidPath { message, .. } => Cow::Borrowed(message.as_str()), DuplicatePath { .. } => { Cow::Borrowed("package path is a case insensitive duplicate of another path in the package") @@ -142,8 +142,8 @@ impl Diagnostic for PublishDiagnostic { use PublishDiagnostic::*; match &self { FastCheck(diagnostic) => diagnostic.location(), - ImportMapUnfurl(diagnostic) => match diagnostic { - ImportMapUnfurlDiagnostic::UnanalyzableDynamicImport { + SpecifierUnfurl(diagnostic) => match diagnostic { + SpecifierUnfurlerDiagnostic::UnanalyzableDynamicImport { specifier, text_info, range, @@ -180,8 +180,8 @@ impl Diagnostic for PublishDiagnostic { fn snippet(&self) -> Option> { match &self { PublishDiagnostic::FastCheck(diagnostic) => diagnostic.snippet(), - PublishDiagnostic::ImportMapUnfurl(diagnostic) => match diagnostic { - ImportMapUnfurlDiagnostic::UnanalyzableDynamicImport { + PublishDiagnostic::SpecifierUnfurl(diagnostic) => match diagnostic { + SpecifierUnfurlerDiagnostic::UnanalyzableDynamicImport { text_info, range, .. @@ -227,7 +227,7 @@ impl Diagnostic for PublishDiagnostic { fn hint(&self) -> Option> { match &self { PublishDiagnostic::FastCheck(diagnostic) => diagnostic.hint(), - PublishDiagnostic::ImportMapUnfurl(_) => None, + PublishDiagnostic::SpecifierUnfurl(_) => None, PublishDiagnostic::InvalidPath { .. } => Some( Cow::Borrowed("rename or remove the file, or add it to 'publish.exclude' in the config file"), ), @@ -250,11 +250,11 @@ impl Diagnostic for PublishDiagnostic { PublishDiagnostic::FastCheck(diagnostic) => { diagnostic.info() } - PublishDiagnostic::ImportMapUnfurl(diagnostic) => match diagnostic { - ImportMapUnfurlDiagnostic::UnanalyzableDynamicImport { .. } => Cow::Borrowed(&[ - Cow::Borrowed("after publishing this package, imports from the local import map do not work"), + PublishDiagnostic::SpecifierUnfurl(diagnostic) => match diagnostic { + SpecifierUnfurlerDiagnostic::UnanalyzableDynamicImport { .. } => Cow::Borrowed(&[ + Cow::Borrowed("after publishing this package, imports from the local import map / package.json do not work"), Cow::Borrowed("dynamic imports that can not be analyzed at publish time will not be rewritten automatically"), - Cow::Borrowed("make sure the dynamic import is resolvable at runtime without an import map") + Cow::Borrowed("make sure the dynamic import is resolvable at runtime without an import map / package.json") ]), }, PublishDiagnostic::InvalidPath { .. } => Cow::Borrowed(&[ @@ -278,8 +278,8 @@ impl Diagnostic for PublishDiagnostic { fn docs_url(&self) -> Option> { match &self { PublishDiagnostic::FastCheck(diagnostic) => diagnostic.docs_url(), - PublishDiagnostic::ImportMapUnfurl(diagnostic) => match diagnostic { - ImportMapUnfurlDiagnostic::UnanalyzableDynamicImport { .. } => None, + PublishDiagnostic::SpecifierUnfurl(diagnostic) => match diagnostic { + SpecifierUnfurlerDiagnostic::UnanalyzableDynamicImport { .. } => None, }, PublishDiagnostic::InvalidPath { .. } => { Some(Cow::Borrowed("https://jsr.io/go/invalid-path")) diff --git a/cli/tools/registry/mod.rs b/cli/tools/registry/mod.rs index 52d303ae1d..eadd0e44d9 100644 --- a/cli/tools/registry/mod.rs +++ b/cli/tools/registry/mod.rs @@ -35,13 +35,13 @@ use crate::factory::CliFactory; use crate::graph_util::ModuleGraphCreator; use crate::http_util::HttpClient; use crate::resolver::MappedSpecifierResolver; +use crate::resolver::SloppyImportsResolver; use crate::tools::check::CheckOptions; use crate::tools::lint::no_slow_types; use crate::tools::registry::diagnostics::PublishDiagnostic; use crate::tools::registry::diagnostics::PublishDiagnosticsCollector; use crate::tools::registry::graph::collect_invalid_external_imports; use crate::util::display::human_size; -use crate::util::import_map::ImportMapUnfurler; mod api; mod auth; @@ -50,10 +50,13 @@ mod graph; mod paths; mod publish_order; mod tar; +mod unfurl; use auth::get_auth_method; use auth::AuthMethod; use publish_order::PublishOrderGraph; +pub use unfurl::deno_json_deps; +use unfurl::SpecifierUnfurler; use super::check::TypeChecker; @@ -81,12 +84,15 @@ impl PreparedPublishPackage { static SUGGESTED_ENTRYPOINTS: [&str; 4] = ["mod.ts", "mod.js", "index.ts", "index.js"]; +#[allow(clippy::too_many_arguments)] async fn prepare_publish( package_name: &str, deno_json: &ConfigFile, source_cache: Arc, graph: Arc, mapped_resolver: Arc, + sloppy_imports_resolver: Option, + bare_node_builtins: bool, diagnostics_collector: &PublishDiagnosticsCollector, ) -> Result, AnyError> { let config_path = deno_json.specifier.to_file_path().unwrap(); @@ -132,7 +138,11 @@ async fn prepare_publish( let diagnostics_collector = diagnostics_collector.clone(); let tarball = deno_core::unsync::spawn_blocking(move || { - let unfurler = ImportMapUnfurler::new(&mapped_resolver); + let unfurler = SpecifierUnfurler::new( + &mapped_resolver, + sloppy_imports_resolver.as_ref(), + bare_node_builtins, + ); tar::create_gzipped_tarball( &dir_path, LazyGraphSourceParser::new(&source_cache, &graph), @@ -661,7 +671,9 @@ async fn prepare_packages_for_publishing( let module_graph_creator = cli_factory.module_graph_creator().await?.as_ref(); let source_cache = cli_factory.parsed_source_cache(); let type_checker = cli_factory.type_checker().await?; + let fs = cli_factory.fs(); let cli_options = cli_factory.cli_options(); + let bare_node_builtins = cli_options.unstable_bare_node_builtins(); if members.len() > 1 { println!("Publishing a workspace..."); @@ -686,6 +698,11 @@ async fn prepare_packages_for_publishing( .into_iter() .map(|member| { let mapped_resolver = mapped_resolver.clone(); + let sloppy_imports_resolver = if cli_options.unstable_sloppy_imports() { + Some(SloppyImportsResolver::new(fs.clone())) + } else { + None + }; let graph = graph.clone(); async move { let package = prepare_publish( @@ -694,6 +711,8 @@ async fn prepare_packages_for_publishing( source_cache.clone(), graph, mapped_resolver, + sloppy_imports_resolver, + bare_node_builtins, diagnostics_collector, ) .await diff --git a/cli/tools/registry/tar.rs b/cli/tools/registry/tar.rs index 66d15b5a65..a8519fe026 100644 --- a/cli/tools/registry/tar.rs +++ b/cli/tools/registry/tar.rs @@ -18,10 +18,10 @@ use tar::Header; use crate::cache::LazyGraphSourceParser; use crate::tools::registry::paths::PackagePath; -use crate::util::import_map::ImportMapUnfurler; use super::diagnostics::PublishDiagnostic; use super::diagnostics::PublishDiagnosticsCollector; +use super::unfurl::SpecifierUnfurler; #[derive(Debug, Clone, PartialEq)] pub struct PublishableTarballFile { @@ -40,7 +40,7 @@ pub fn create_gzipped_tarball( dir: &Path, source_parser: LazyGraphSourceParser, diagnostics_collector: &PublishDiagnosticsCollector, - unfurler: &ImportMapUnfurler, + unfurler: &SpecifierUnfurler, file_patterns: Option, ) -> Result { let mut tar = TarGzArchive::new(); @@ -192,7 +192,7 @@ pub fn create_gzipped_tarball( fn resolve_content_maybe_unfurling( path: &Path, specifier: &Url, - unfurler: &ImportMapUnfurler, + unfurler: &SpecifierUnfurler, source_parser: LazyGraphSourceParser, diagnostics_collector: &PublishDiagnosticsCollector, ) -> Result, AnyError> { @@ -241,7 +241,7 @@ fn resolve_content_maybe_unfurling( log::debug!("Unfurling {}", specifier); let mut reporter = |diagnostic| { - diagnostics_collector.push(PublishDiagnostic::ImportMapUnfurl(diagnostic)); + diagnostics_collector.push(PublishDiagnostic::SpecifierUnfurl(diagnostic)); }; let content = unfurler.unfurl(specifier, &parsed_source, &mut reporter); Ok(content.into_bytes()) diff --git a/cli/util/import_map.rs b/cli/tools/registry/unfurl.rs similarity index 54% rename from cli/util/import_map.rs rename to cli/tools/registry/unfurl.rs index a075443bad..abea2bd0c3 100644 --- a/cli/util/import_map.rs +++ b/cli/tools/registry/unfurl.rs @@ -11,11 +11,13 @@ use deno_graph::DefaultModuleAnalyzer; use deno_graph::DependencyDescriptor; use deno_graph::DynamicTemplatePart; use deno_graph::TypeScriptReference; +use deno_runtime::deno_node::is_builtin_node_module; use deno_semver::jsr::JsrDepPackageReq; use deno_semver::jsr::JsrPackageReqReference; use deno_semver::npm::NpmPackageReqReference; use crate::resolver::MappedSpecifierResolver; +use crate::resolver::SloppyImportsResolver; pub fn deno_json_deps( config: &deno_config::ConfigFile, @@ -61,7 +63,7 @@ fn values_to_set<'a>( } #[derive(Debug, Clone)] -pub enum ImportMapUnfurlDiagnostic { +pub enum SpecifierUnfurlerDiagnostic { UnanalyzableDynamicImport { specifier: ModuleSpecifier, text_info: SourceTextInfo, @@ -69,7 +71,7 @@ pub enum ImportMapUnfurlDiagnostic { }, } -impl ImportMapUnfurlDiagnostic { +impl SpecifierUnfurlerDiagnostic { pub fn code(&self) -> &'static str { match self { Self::UnanalyzableDynamicImport { .. } => "unanalyzable-dynamic-import", @@ -85,20 +87,153 @@ impl ImportMapUnfurlDiagnostic { } } -pub struct ImportMapUnfurler<'a> { - import_map: &'a MappedSpecifierResolver, +pub struct SpecifierUnfurler<'a> { + mapped_resolver: &'a MappedSpecifierResolver, + sloppy_imports_resolver: Option<&'a SloppyImportsResolver>, + bare_node_builtins: bool, } -impl<'a> ImportMapUnfurler<'a> { - pub fn new(import_map: &'a MappedSpecifierResolver) -> Self { - Self { import_map } +impl<'a> SpecifierUnfurler<'a> { + pub fn new( + mapped_resolver: &'a MappedSpecifierResolver, + sloppy_imports_resolver: Option<&'a SloppyImportsResolver>, + bare_node_builtins: bool, + ) -> Self { + Self { + mapped_resolver, + sloppy_imports_resolver, + bare_node_builtins, + } + } + + fn unfurl_specifier( + &self, + referrer: &ModuleSpecifier, + specifier: &str, + ) -> Option { + let resolved = + if let Ok(resolved) = self.mapped_resolver.resolve(specifier, referrer) { + resolved.into_specifier() + } else { + None + }; + let resolved = match resolved { + Some(resolved) => resolved, + None if self.bare_node_builtins && is_builtin_node_module(specifier) => { + format!("node:{specifier}").parse().unwrap() + } + None => ModuleSpecifier::options() + .base_url(Some(referrer)) + .parse(specifier) + .ok()?, + }; + // TODO(lucacasonato): this requires integration in deno_graph first + // let resolved = if let Ok(specifier) = + // NpmPackageReqReference::from_specifier(&resolved) + // { + // if let Some(scope_name) = specifier.req().name.strip_prefix("@jsr/") { + // let (scope, name) = scope_name.split_once("__")?; + // let new_specifier = JsrPackageReqReference::new(PackageReqReference { + // req: PackageReq { + // name: format!("@{scope}/{name}"), + // version_req: specifier.req().version_req.clone(), + // }, + // sub_path: specifier.sub_path().map(ToOwned::to_owned), + // }) + // .to_string(); + // ModuleSpecifier::parse(&new_specifier).unwrap() + // } else { + // resolved + // } + // } else { + // resolved + // }; + let resolved = + if let Some(sloppy_imports_resolver) = self.sloppy_imports_resolver { + sloppy_imports_resolver + .resolve(&resolved) + .as_specifier() + .clone() + } else { + resolved + }; + relative_url(&resolved, referrer, specifier) + } + + /// Attempts to unfurl the dynamic dependency returning `true` on success + /// or `false` when the import was not analyzable. + fn try_unfurl_dynamic_dep( + &self, + module_url: &lsp_types::Url, + parsed_source: &ParsedSource, + dep: &deno_graph::DynamicDependencyDescriptor, + text_changes: &mut Vec, + ) -> bool { + match &dep.argument { + deno_graph::DynamicArgument::String(specifier) => { + let range = to_range(parsed_source, &dep.argument_range); + let maybe_relative_index = + parsed_source.text_info().text_str()[range.start..].find(specifier); + let Some(relative_index) = maybe_relative_index else { + return false; + }; + let unfurled = self.unfurl_specifier(module_url, specifier); + let Some(unfurled) = unfurled else { + return false; + }; + let start = range.start + relative_index; + text_changes.push(deno_ast::TextChange { + range: start..start + specifier.len(), + new_text: unfurled, + }); + true + } + deno_graph::DynamicArgument::Template(parts) => match parts.first() { + Some(DynamicTemplatePart::String { value: specifier }) => { + // relative doesn't need to be modified + let is_relative = + specifier.starts_with("./") || specifier.starts_with("../"); + if is_relative { + return true; + } + if !specifier.ends_with('/') { + return false; + } + let unfurled = self.unfurl_specifier(module_url, specifier); + let Some(unfurled) = unfurled else { + return false; + }; + let range = to_range(parsed_source, &dep.argument_range); + let maybe_relative_index = + parsed_source.text_info().text_str()[range.start..].find(specifier); + let Some(relative_index) = maybe_relative_index else { + return false; + }; + let start = range.start + relative_index; + text_changes.push(deno_ast::TextChange { + range: start..start + specifier.len(), + new_text: unfurled, + }); + true + } + Some(DynamicTemplatePart::Expr) => { + false // failed analyzing + } + None => { + true // ignore + } + }, + deno_graph::DynamicArgument::Expr => { + false // failed analyzing + } + } } pub fn unfurl( &self, url: &ModuleSpecifier, parsed_source: &ParsedSource, - diagnostic_reporter: &mut dyn FnMut(ImportMapUnfurlDiagnostic), + diagnostic_reporter: &mut dyn FnMut(SpecifierUnfurlerDiagnostic), ) -> String { let mut text_changes = Vec::new(); let module_info = DefaultModuleAnalyzer::module_info(parsed_source); @@ -106,14 +241,11 @@ impl<'a> ImportMapUnfurler<'a> { |specifier: &str, range: &deno_graph::PositionRange, text_changes: &mut Vec| { - let resolved = self.import_map.resolve(specifier, url); - if let Ok(resolved) = resolved { - if let Some(resolved) = resolved.into_specifier() { - text_changes.push(deno_ast::TextChange { - range: to_range(parsed_source, range), - new_text: make_relative_to(url, &resolved), - }); - } + if let Some(unfurled) = self.unfurl_specifier(url, specifier) { + text_changes.push(deno_ast::TextChange { + range: to_range(parsed_source, range), + new_text: unfurled, + }); } }; for dep in &module_info.dependencies { @@ -126,8 +258,7 @@ impl<'a> ImportMapUnfurler<'a> { ); } DependencyDescriptor::Dynamic(dep) => { - let success = try_unfurl_dynamic_dep( - self.import_map, + let success = self.try_unfurl_dynamic_dep( url, parsed_source, dep, @@ -144,7 +275,7 @@ impl<'a> ImportMapUnfurler<'a> { .line_start(dep.argument_range.end.line) + dep.argument_range.end.character; diagnostic_reporter( - ImportMapUnfurlDiagnostic::UnanalyzableDynamicImport { + SpecifierUnfurlerDiagnostic::UnanalyzableDynamicImport { specifier: url.to_owned(), range: SourceRange::new(start_pos, end_pos), text_info: parsed_source.text_info().clone(), @@ -188,85 +319,20 @@ impl<'a> ImportMapUnfurler<'a> { } } -fn make_relative_to(from: &ModuleSpecifier, to: &ModuleSpecifier) -> String { - if to.scheme() == "file" { - format!("./{}", from.make_relative(to).unwrap()) +fn relative_url( + resolved: &ModuleSpecifier, + referrer: &ModuleSpecifier, + specifier: &str, +) -> Option { + let new_specifier = if resolved.scheme() == "file" { + format!("./{}", referrer.make_relative(resolved).unwrap()) } else { - to.to_string() - } -} - -/// Attempts to unfurl the dynamic dependency returning `true` on success -/// or `false` when the import was not analyzable. -fn try_unfurl_dynamic_dep( - mapped_resolver: &MappedSpecifierResolver, - module_url: &lsp_types::Url, - parsed_source: &ParsedSource, - dep: &deno_graph::DynamicDependencyDescriptor, - text_changes: &mut Vec, -) -> bool { - match &dep.argument { - deno_graph::DynamicArgument::String(value) => { - let range = to_range(parsed_source, &dep.argument_range); - let maybe_relative_index = - parsed_source.text_info().text_str()[range.start..].find(value); - let Some(relative_index) = maybe_relative_index else { - return false; - }; - let resolved = mapped_resolver.resolve(value, module_url); - let Ok(resolved) = resolved else { - return false; - }; - let Some(resolved) = resolved.into_specifier() else { - return false; - }; - let start = range.start + relative_index; - text_changes.push(deno_ast::TextChange { - range: start..start + value.len(), - new_text: make_relative_to(module_url, &resolved), - }); - true - } - deno_graph::DynamicArgument::Template(parts) => match parts.first() { - Some(DynamicTemplatePart::String { value }) => { - // relative doesn't need to be modified - let is_relative = value.starts_with("./") || value.starts_with("../"); - if is_relative { - return true; - } - if !value.ends_with('/') { - return false; - } - let Ok(resolved) = mapped_resolver.resolve(value, module_url) else { - return false; - }; - let Some(resolved) = resolved.into_specifier() else { - return false; - }; - let range = to_range(parsed_source, &dep.argument_range); - let maybe_relative_index = - parsed_source.text_info().text_str()[range.start..].find(value); - let Some(relative_index) = maybe_relative_index else { - return false; - }; - let start = range.start + relative_index; - text_changes.push(deno_ast::TextChange { - range: start..start + value.len(), - new_text: make_relative_to(module_url, &resolved), - }); - true - } - Some(DynamicTemplatePart::Expr) => { - false // failed analyzing - } - None => { - true // ignore - } - }, - deno_graph::DynamicArgument::Expr => { - false // failed analyzing - } + resolved.to_string() + }; + if new_specifier == specifier { + return None; } + Some(new_specifier) } fn to_range( @@ -290,6 +356,7 @@ fn to_range( mod tests { use std::sync::Arc; + use crate::args::package_json::get_local_package_json_version_reqs; use crate::args::PackageJsonDepsProvider; use super::*; @@ -297,8 +364,12 @@ mod tests { use deno_ast::ModuleSpecifier; use deno_core::serde_json::json; use deno_core::url::Url; + use deno_runtime::deno_fs::RealFs; + use deno_runtime::deno_node::PackageJson; use import_map::ImportMapWithDiagnostics; + use indexmap::IndexMap; use pretty_assertions::assert_eq; + use test_util::testdata_path; fn parse_ast(specifier: &Url, source_code: &str) -> ParsedSource { let media_type = MediaType::from_specifier(specifier); @@ -315,22 +386,38 @@ mod tests { #[test] fn test_unfurling() { + let cwd = testdata_path().join("unfurl").to_path_buf(); + let deno_json_url = - ModuleSpecifier::parse("file:///dev/deno.json").unwrap(); + ModuleSpecifier::from_file_path(cwd.join("deno.json")).unwrap(); let value = json!({ "imports": { "express": "npm:express@5", "lib/": "./lib/", - "fizz": "./fizz/mod.ts" + "fizz": "./fizz/mod.ts", + "@std/fs": "npm:@jsr/std__fs@1", } }); let ImportMapWithDiagnostics { import_map, .. } = import_map::parse_from_value(deno_json_url, value).unwrap(); - let mapped_resolved = MappedSpecifierResolver::new( + let mut package_json = PackageJson::empty(cwd.join("package.json")); + package_json.dependencies = + Some(IndexMap::from([("chalk".to_string(), "5".to_string())])); + let mapped_resolver = MappedSpecifierResolver::new( Some(Arc::new(import_map)), - Arc::new(PackageJsonDepsProvider::new(None)), + Arc::new(PackageJsonDepsProvider::new(Some( + get_local_package_json_version_reqs(&package_json), + ))), + ); + + let fs = Arc::new(RealFs); + let sloppy_imports_resolver = SloppyImportsResolver::new(fs); + + let unfurler = SpecifierUnfurler::new( + &mapped_resolver, + Some(&sloppy_imports_resolver), + true, ); - let unfurler = ImportMapUnfurler::new(&mapped_resolved); // Unfurling TS file should apply changes. { @@ -338,6 +425,16 @@ mod tests { import foo from "lib/foo.ts"; import bar from "lib/bar.ts"; import fizz from "fizz"; +import chalk from "chalk"; +import baz from "./baz"; +import b from "./b.js"; +import b2 from "./b"; +import url from "url"; +// TODO: unfurl these to jsr +// import "npm:@jsr/std__fs@1/file"; +// import "npm:@jsr/std__fs@1"; +// import "npm:@jsr/std__fs"; +// import "@std/fs"; const test1 = await import("lib/foo.ts"); const test2 = await import(`lib/foo.ts`); @@ -347,7 +444,8 @@ const test4 = await import(`./lib/${expr}`); const test5 = await import(`lib${expr}`); const test6 = await import(`${expr}`); "#; - let specifier = ModuleSpecifier::parse("file:///dev/mod.ts").unwrap(); + let specifier = + ModuleSpecifier::from_file_path(cwd.join("mod.ts")).unwrap(); let source = parse_ast(&specifier, source_code); let mut d = Vec::new(); let mut reporter = |diagnostic| d.push(diagnostic); @@ -356,7 +454,7 @@ const test6 = await import(`${expr}`); assert!( matches!( d[0], - ImportMapUnfurlDiagnostic::UnanalyzableDynamicImport { .. } + SpecifierUnfurlerDiagnostic::UnanalyzableDynamicImport { .. } ), "{:?}", d[0] @@ -364,7 +462,7 @@ const test6 = await import(`${expr}`); assert!( matches!( d[1], - ImportMapUnfurlDiagnostic::UnanalyzableDynamicImport { .. } + SpecifierUnfurlerDiagnostic::UnanalyzableDynamicImport { .. } ), "{:?}", d[1] @@ -373,6 +471,16 @@ const test6 = await import(`${expr}`); import foo from "./lib/foo.ts"; import bar from "./lib/bar.ts"; import fizz from "./fizz/mod.ts"; +import chalk from "npm:chalk@5"; +import baz from "./baz/index.js"; +import b from "./b.ts"; +import b2 from "./b.ts"; +import url from "node:url"; +// TODO: unfurl these to jsr +// import "npm:@jsr/std__fs@1/file"; +// import "npm:@jsr/std__fs@1"; +// import "npm:@jsr/std__fs"; +// import "@std/fs"; const test1 = await import("./lib/foo.ts"); const test2 = await import(`./lib/foo.ts`); diff --git a/cli/util/mod.rs b/cli/util/mod.rs index a3f4a5aa42..a6f72bc048 100644 --- a/cli/util/mod.rs +++ b/cli/util/mod.rs @@ -8,7 +8,6 @@ pub mod display; pub mod draw_thread; pub mod file_watcher; pub mod fs; -pub mod import_map; pub mod logger; pub mod path; pub mod progress_bar; diff --git a/tests/integration/publish_tests.rs b/tests/integration/publish_tests.rs index d2ea279065..ae7a332c41 100644 --- a/tests/integration/publish_tests.rs +++ b/tests/integration/publish_tests.rs @@ -224,6 +224,22 @@ itest!(config_flag { http_server: true, }); +itest!(bare_node_builtins { + args: "publish --token 'sadfasdf' --dry-run --unstable-bare-node-builtins", + output: "publish/bare_node_builtins.out", + cwd: Some("publish/bare_node_builtins"), + envs: env_vars_for_jsr_npm_tests(), + http_server: true, +}); + +itest!(sloppy_imports { + args: "publish --token 'sadfasdf' --dry-run --unstable-sloppy-imports", + output: "publish/sloppy_imports.out", + cwd: Some("publish/sloppy_imports"), + envs: env_vars_for_jsr_tests(), + http_server: true, +}); + itest!(jsr_jsonc { args: "publish --token 'sadfasdf'", cwd: Some("publish/jsr_jsonc"), diff --git a/tests/testdata/publish/bare_node_builtins.out b/tests/testdata/publish/bare_node_builtins.out new file mode 100644 index 0000000000..99d7e4cf94 --- /dev/null +++ b/tests/testdata/publish/bare_node_builtins.out @@ -0,0 +1,11 @@ +Warning: Resolving "url" as "node:url" at file:///[WILDCARD]/publish/bare_node_builtins/mod.ts:1:22. If you want to use a built-in Node module, add a "node:" prefix. +Warning: Resolving "url" as "node:url" at file:///[WILDCARD]/publish/bare_node_builtins/mod.ts:1:22. If you want to use a built-in Node module, add a "node:" prefix. +Download http://localhost:4545/npm/registry/@types/node +Download http://localhost:4545/npm/registry/@types/node/node-18.8.2.tgz +Check file:///[WILDCARD]/publish/bare_node_builtins/mod.ts +Checking for slow types in the public API... +Check file:///[WILDCARD]/publish/bare_node_builtins/mod.ts +Simulating publish of @foo/bar@1.0.0 with files: + file:///[WILDCARD]/publish/bare_node_builtins/deno.json (87B) + file:///[WILDCARD]/publish/bare_node_builtins/mod.ts (121B) +Warning Aborting due to --dry-run diff --git a/tests/testdata/publish/bare_node_builtins/deno.json b/tests/testdata/publish/bare_node_builtins/deno.json new file mode 100644 index 0000000000..213a7cec63 --- /dev/null +++ b/tests/testdata/publish/bare_node_builtins/deno.json @@ -0,0 +1,7 @@ +{ + "name": "@foo/bar", + "version": "1.0.0", + "exports": { + ".": "./mod.ts" + } +} diff --git a/tests/testdata/publish/bare_node_builtins/mod.ts b/tests/testdata/publish/bare_node_builtins/mod.ts new file mode 100644 index 0000000000..04374d8b78 --- /dev/null +++ b/tests/testdata/publish/bare_node_builtins/mod.ts @@ -0,0 +1,5 @@ +import * as url from "url"; + +export function foobar(): { href: string } { + return url.pathToFileURL("/foo/bar"); +} diff --git a/tests/testdata/publish/sloppy_imports.out b/tests/testdata/publish/sloppy_imports.out new file mode 100644 index 0000000000..a3af865756 --- /dev/null +++ b/tests/testdata/publish/sloppy_imports.out @@ -0,0 +1,12 @@ +Warning Sloppy module resolution (hint: specify path to index.ts file in directory instead) + at file:///[WILDCARD]/publish/sloppy_imports/mod.ts:1:20 +Warning Sloppy module resolution (hint: specify path to index.ts file in directory instead) + at file:///[WILDCARD]/publish/sloppy_imports/mod.ts:1:20 +Check file:///[WILDCARD]/publish/sloppy_imports/mod.ts +Checking for slow types in the public API... +Check file:///[WILDCARD]/publish/sloppy_imports/mod.ts +Simulating publish of @foo/bar@1.0.0 with files: + file:///[WILDCARD]/publish/sloppy_imports/b/index.ts (27B) + file:///[WILDCARD]/publish/sloppy_imports/deno.json (87B) + file:///[WILDCARD]/publish/sloppy_imports/mod.ts (35B) +Warning Aborting due to --dry-run diff --git a/tests/testdata/publish/sloppy_imports/b/index.ts b/tests/testdata/publish/sloppy_imports/b/index.ts new file mode 100644 index 0000000000..1392bf6ba4 --- /dev/null +++ b/tests/testdata/publish/sloppy_imports/b/index.ts @@ -0,0 +1 @@ +export const PI = Math.PI; diff --git a/tests/testdata/publish/sloppy_imports/deno.json b/tests/testdata/publish/sloppy_imports/deno.json new file mode 100644 index 0000000000..213a7cec63 --- /dev/null +++ b/tests/testdata/publish/sloppy_imports/deno.json @@ -0,0 +1,7 @@ +{ + "name": "@foo/bar", + "version": "1.0.0", + "exports": { + ".": "./mod.ts" + } +} diff --git a/tests/testdata/publish/sloppy_imports/mod.ts b/tests/testdata/publish/sloppy_imports/mod.ts new file mode 100644 index 0000000000..f5084bb3be --- /dev/null +++ b/tests/testdata/publish/sloppy_imports/mod.ts @@ -0,0 +1 @@ +export { PI } from "./b"; diff --git a/tests/testdata/publish/unanalyzable_dynamic_import.out b/tests/testdata/publish/unanalyzable_dynamic_import.out index da6a6f9021..7f3ca55554 100644 --- a/tests/testdata/publish/unanalyzable_dynamic_import.out +++ b/tests/testdata/publish/unanalyzable_dynamic_import.out @@ -7,9 +7,9 @@ warning[unanalyzable-dynamic-import]: unable to analyze dynamic import 2 | await import("asd " + asd); | ^^^^^^^^^^^^ the unanalyzable dynamic import - info: after publishing this package, imports from the local import map do not work + info: after publishing this package, imports from the local import map / package.json do not work info: dynamic imports that can not be analyzed at publish time will not be rewritten automatically - info: make sure the dynamic import is resolvable at runtime without an import map + info: make sure the dynamic import is resolvable at runtime without an import map / package.json Publishing @foo/bar@1.0.0 ... Successfully published @foo/bar@1.0.0 diff --git a/tests/testdata/unfurl/b.ts b/tests/testdata/unfurl/b.ts new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/testdata/unfurl/baz/index.js b/tests/testdata/unfurl/baz/index.js new file mode 100644 index 0000000000..e69de29bb2