diff options
author | Nathan Whitaker <17734409+nathanwhit@users.noreply.github.com> | 2024-08-21 15:23:32 -0700 |
---|---|---|
committer | GitHub <noreply@github.com> | 2024-08-21 15:23:32 -0700 |
commit | 48da3c17ea905f50b82948e6f94795e1589f852e (patch) | |
tree | e65d5da97ef0cd4b9f67972b8ba647420d39952e | |
parent | 9aaad3064a412b24e88e308750e038d4e1df6f3c (diff) |
fix(add): Handle packages without root exports (#25102)
Fixes #24607.
This PR makes the logic that caches top level dependencies (things
present in import map) smarter, so we handle JSR dependencies without
root exports.
-rw-r--r-- | cli/module_loader.rs | 49 | ||||
-rw-r--r-- | cli/tools/installer.rs | 2 | ||||
-rw-r--r-- | cli/tools/registry/mod.rs | 2 | ||||
-rw-r--r-- | cli/tools/registry/pm.rs | 25 | ||||
-rw-r--r-- | cli/tools/registry/pm/cache_deps.rs | 115 | ||||
-rw-r--r-- | tests/registry/jsr/@std/testing/1.0.0/bdd.ts | 2 | ||||
-rw-r--r-- | tests/registry/jsr/@std/testing/1.0.0/types.ts | 1 | ||||
-rw-r--r-- | tests/registry/jsr/@std/testing/1.0.0_meta.json | 6 | ||||
-rw-r--r-- | tests/registry/jsr/@std/testing/meta.json | 8 | ||||
-rw-r--r-- | tests/specs/add/no_root_export/__test__.jsonc | 9 | ||||
-rw-r--r-- | tests/specs/add/no_root_export/add.out | 5 | ||||
-rw-r--r-- | tests/specs/add/no_root_export/deno.json | 0 | ||||
-rw-r--r-- | tests/specs/add/no_root_export/main.ts | 3 | ||||
-rw-r--r-- | tests/specs/install/future_install_local_deno/deno.json | 4 | ||||
-rw-r--r-- | tests/specs/install/future_install_local_deno/deno.lock.out | 12 | ||||
-rw-r--r-- | tests/specs/install/future_install_local_deno/install.out | 4 | ||||
-rw-r--r-- | tests/specs/remove/basic/add.out | 6 |
17 files changed, 189 insertions, 64 deletions
diff --git a/cli/module_loader.rs b/cli/module_loader.rs index 9f5208936..3208d1365 100644 --- a/cli/module_loader.rs +++ b/cli/module_loader.rs @@ -18,7 +18,6 @@ use crate::cache::CodeCache; use crate::cache::FastInsecureHasher; use crate::cache::ParsedSourceCache; use crate::emit::Emitter; -use crate::factory::CliFactory; use crate::graph_container::MainModuleGraphContainer; use crate::graph_container::ModuleGraphContainer; use crate::graph_container::ModuleGraphUpdatePermit; @@ -70,54 +69,6 @@ use deno_runtime::deno_permissions::PermissionsContainer; use deno_semver::npm::NpmPackageReqReference; use node_resolver::NodeResolutionMode; -pub async fn load_top_level_deps(factory: &CliFactory) -> Result<(), AnyError> { - let npm_resolver = factory.npm_resolver().await?; - let cli_options = factory.cli_options()?; - if let Some(npm_resolver) = npm_resolver.as_managed() { - if !npm_resolver.ensure_top_level_package_json_install().await? { - if let Some(lockfile) = cli_options.maybe_lockfile() { - lockfile.error_if_changed()?; - } - - npm_resolver.cache_packages().await?; - } - } - // cache as many entries in the import map as we can - let resolver = factory.workspace_resolver().await?; - if let Some(import_map) = resolver.maybe_import_map() { - let roots = import_map - .imports() - .entries() - .filter_map(|entry| { - if entry.key.ends_with('/') { - None - } else { - entry.value.cloned() - } - }) - .collect::<Vec<_>>(); - let mut graph_permit = factory - .main_module_graph_container() - .await? - .acquire_update_permit() - .await; - let graph = graph_permit.graph_mut(); - factory - .module_load_preparer() - .await? - .prepare_module_load( - graph, - &roots, - false, - factory.cli_options()?.ts_type_lib_window(), - deno_runtime::deno_permissions::PermissionsContainer::allow_all(), - ) - .await?; - } - - Ok(()) -} - pub struct ModuleLoadPreparer { options: Arc<CliOptions>, lockfile: Option<Arc<CliLockfile>>, diff --git a/cli/tools/installer.rs b/cli/tools/installer.rs index 456e5c1a6..1809e1f16 100644 --- a/cli/tools/installer.rs +++ b/cli/tools/installer.rs @@ -275,7 +275,7 @@ async fn install_local( } let factory = CliFactory::from_flags(flags); - crate::module_loader::load_top_level_deps(&factory).await?; + crate::tools::registry::cache_top_level_deps(&factory, None).await?; if let Some(lockfile) = factory.cli_options()?.maybe_lockfile() { lockfile.write_if_changed()?; diff --git a/cli/tools/registry/mod.rs b/cli/tools/registry/mod.rs index d6e06fb58..ee3204dc7 100644 --- a/cli/tools/registry/mod.rs +++ b/cli/tools/registry/mod.rs @@ -52,6 +52,7 @@ use crate::util::display::human_size; mod api; mod auth; + mod diagnostics; mod graph; mod paths; @@ -64,6 +65,7 @@ mod unfurl; use auth::get_auth_method; use auth::AuthMethod; pub use pm::add; +pub use pm::cache_top_level_deps; pub use pm::remove; pub use pm::AddCommandName; use publish_order::PublishOrderGraph; diff --git a/cli/tools/registry/pm.rs b/cli/tools/registry/pm.rs index 3cdef071f..87a5ea69a 100644 --- a/cli/tools/registry/pm.rs +++ b/cli/tools/registry/pm.rs @@ -1,5 +1,9 @@ // Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +mod cache_deps; + +pub use cache_deps::cache_top_level_deps; + use std::borrow::Cow; use std::path::Path; use std::path::PathBuf; @@ -236,13 +240,16 @@ pub async fn add( let package_futures = package_reqs .into_iter() - .map(move |package_req| { - find_package_and_select_version_for_req( - jsr_resolver.clone(), - npm_resolver.clone(), - package_req, - ) - .boxed_local() + .map({ + let jsr_resolver = jsr_resolver.clone(); + move |package_req| { + find_package_and_select_version_for_req( + jsr_resolver.clone(), + npm_resolver.clone(), + package_req, + ) + .boxed_local() + } }) .collect::<Vec<_>>(); @@ -350,7 +357,7 @@ pub async fn add( // make a new CliFactory to pick up the updated config file let cli_factory = CliFactory::from_flags(flags); // cache deps - crate::module_loader::load_top_level_deps(&cli_factory).await?; + cache_deps::cache_top_level_deps(&cli_factory, Some(jsr_resolver)).await?; Ok(()) } @@ -597,7 +604,7 @@ pub async fn remove( // Update deno.lock node_resolver::PackageJsonThreadLocalCache::clear(); let cli_factory = CliFactory::from_flags(flags); - crate::module_loader::load_top_level_deps(&cli_factory).await?; + cache_deps::cache_top_level_deps(&cli_factory, None).await?; } Ok(()) diff --git a/cli/tools/registry/pm/cache_deps.rs b/cli/tools/registry/pm/cache_deps.rs new file mode 100644 index 000000000..d292c32f5 --- /dev/null +++ b/cli/tools/registry/pm/cache_deps.rs @@ -0,0 +1,115 @@ +// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. + +use std::sync::Arc; + +use crate::factory::CliFactory; +use crate::graph_container::ModuleGraphContainer; +use crate::graph_container::ModuleGraphUpdatePermit; +use deno_core::error::AnyError; +use deno_core::futures::stream::FuturesUnordered; +use deno_core::futures::StreamExt; +use deno_semver::package::PackageReq; + +pub async fn cache_top_level_deps( + factory: &CliFactory, + jsr_resolver: Option<Arc<crate::jsr::JsrFetchResolver>>, +) -> Result<(), AnyError> { + let npm_resolver = factory.npm_resolver().await?; + let cli_options = factory.cli_options()?; + if let Some(npm_resolver) = npm_resolver.as_managed() { + if !npm_resolver.ensure_top_level_package_json_install().await? { + if let Some(lockfile) = cli_options.maybe_lockfile() { + lockfile.error_if_changed()?; + } + + npm_resolver.cache_packages().await?; + } + } + // cache as many entries in the import map as we can + let resolver = factory.workspace_resolver().await?; + if let Some(import_map) = resolver.maybe_import_map() { + let jsr_resolver = if let Some(resolver) = jsr_resolver { + resolver + } else { + Arc::new(crate::jsr::JsrFetchResolver::new( + factory.file_fetcher()?.clone(), + )) + }; + + let mut roots = Vec::new(); + + let mut info_futures = FuturesUnordered::new(); + + let mut seen_reqs = std::collections::HashSet::new(); + + for entry in import_map.imports().entries() { + let Some(specifier) = entry.value else { + continue; + }; + + match specifier.scheme() { + "jsr" => { + let specifier_str = specifier.as_str(); + let specifier_str = + specifier_str.strip_prefix("jsr:").unwrap_or(specifier_str); + if let Ok(req) = PackageReq::from_str(specifier_str) { + if !seen_reqs.insert(req.clone()) { + continue; + } + let jsr_resolver = jsr_resolver.clone(); + info_futures.push(async move { + if let Some(nv) = jsr_resolver.req_to_nv(&req).await { + if let Some(info) = jsr_resolver.package_version_info(&nv).await + { + return Some((specifier.clone(), info)); + } + } + None + }); + } + } + "npm" => roots.push(specifier.clone()), + _ => { + if entry.key.ends_with('/') && specifier.as_str().ends_with('/') { + continue; + } + roots.push(specifier.clone()); + } + } + } + + while let Some(info_future) = info_futures.next().await { + if let Some((specifier, info)) = info_future { + if info.export(".").is_some() { + roots.push(specifier.clone()); + continue; + } + let exports = info.exports(); + for (k, _) in exports { + if let Ok(spec) = specifier.join(k) { + roots.push(spec); + } + } + } + } + let mut graph_permit = factory + .main_module_graph_container() + .await? + .acquire_update_permit() + .await; + let graph = graph_permit.graph_mut(); + factory + .module_load_preparer() + .await? + .prepare_module_load( + graph, + &roots, + false, + deno_config::deno_json::TsTypeLib::DenoWorker, + deno_runtime::deno_permissions::PermissionsContainer::allow_all(), + ) + .await?; + } + + Ok(()) +} diff --git a/tests/registry/jsr/@std/testing/1.0.0/bdd.ts b/tests/registry/jsr/@std/testing/1.0.0/bdd.ts new file mode 100644 index 000000000..a665d0603 --- /dev/null +++ b/tests/registry/jsr/@std/testing/1.0.0/bdd.ts @@ -0,0 +1,2 @@ +export function it(_name: string, _fn: () => void) { +}
\ No newline at end of file diff --git a/tests/registry/jsr/@std/testing/1.0.0/types.ts b/tests/registry/jsr/@std/testing/1.0.0/types.ts new file mode 100644 index 000000000..feebd2603 --- /dev/null +++ b/tests/registry/jsr/@std/testing/1.0.0/types.ts @@ -0,0 +1 @@ +export type AssertType<A, B> = A extends B ? true : never;
\ No newline at end of file diff --git a/tests/registry/jsr/@std/testing/1.0.0_meta.json b/tests/registry/jsr/@std/testing/1.0.0_meta.json new file mode 100644 index 000000000..5c1bdd807 --- /dev/null +++ b/tests/registry/jsr/@std/testing/1.0.0_meta.json @@ -0,0 +1,6 @@ +{ + "exports": { + "./bdd": "./bdd.ts", + "./types": "./types.ts" + } +}
\ No newline at end of file diff --git a/tests/registry/jsr/@std/testing/meta.json b/tests/registry/jsr/@std/testing/meta.json new file mode 100644 index 000000000..d8037eabb --- /dev/null +++ b/tests/registry/jsr/@std/testing/meta.json @@ -0,0 +1,8 @@ +{ + "scope": "std", + "name": "path", + "latest": "1.0.0", + "versions": { + "1.0.0": {} + } +} diff --git a/tests/specs/add/no_root_export/__test__.jsonc b/tests/specs/add/no_root_export/__test__.jsonc new file mode 100644 index 000000000..2adfbd8de --- /dev/null +++ b/tests/specs/add/no_root_export/__test__.jsonc @@ -0,0 +1,9 @@ +{ + "tempDir": true, + "steps": [ + { + "args": "add @std/testing", + "output": "add.out" + } + ] +} diff --git a/tests/specs/add/no_root_export/add.out b/tests/specs/add/no_root_export/add.out new file mode 100644 index 000000000..4bd9da7be --- /dev/null +++ b/tests/specs/add/no_root_export/add.out @@ -0,0 +1,5 @@ +Add jsr:@std/testing@1.0.0 +[UNORDERED_START] +Download http://127.0.0.1:4250/@std/testing/1.0.0/bdd.ts +Download http://127.0.0.1:4250/@std/testing/1.0.0/types.ts +[UNORDERED_END] diff --git a/tests/specs/add/no_root_export/deno.json b/tests/specs/add/no_root_export/deno.json new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/tests/specs/add/no_root_export/deno.json diff --git a/tests/specs/add/no_root_export/main.ts b/tests/specs/add/no_root_export/main.ts new file mode 100644 index 000000000..0c0d4107f --- /dev/null +++ b/tests/specs/add/no_root_export/main.ts @@ -0,0 +1,3 @@ +import { it } from "@std/testing/bdd"; + +const _it = it; diff --git a/tests/specs/install/future_install_local_deno/deno.json b/tests/specs/install/future_install_local_deno/deno.json index dbcf1c220..9213ce834 100644 --- a/tests/specs/install/future_install_local_deno/deno.json +++ b/tests/specs/install/future_install_local_deno/deno.json @@ -3,6 +3,8 @@ "@std/fs/": "https://deno.land/std@0.224.0/fs/", "@denotest/esm-basic": "npm:@denotest/esm-basic@^1.0.0", "@denotest/add": "jsr:@denotest/add", - "test-http": "http://localhost:4545/v1/extensionless" + "test-http": "http://localhost:4545/v1/extensionless", + "@std/testing": "jsr:@std/testing", + "@std/testing/": "jsr:/@std/testing/" } } diff --git a/tests/specs/install/future_install_local_deno/deno.lock.out b/tests/specs/install/future_install_local_deno/deno.lock.out index 188de5de9..04bfc9b3a 100644 --- a/tests/specs/install/future_install_local_deno/deno.lock.out +++ b/tests/specs/install/future_install_local_deno/deno.lock.out @@ -2,11 +2,15 @@ "version": "4", "specifiers": { "jsr:@denotest/add": "jsr:@denotest/add@1.0.0", + "jsr:@std/testing": "jsr:@std/testing@1.0.0", "npm:@denotest/esm-basic@^1.0.0": "npm:@denotest/esm-basic@1.0.0" }, "jsr": { "@denotest/add@1.0.0": { "integrity": "[WILDCARD]" + }, + "@std/testing@1.0.0": { + "integrity": "[WILDCARD]" } }, "npm": { @@ -14,10 +18,16 @@ "integrity": "[WILDCARD]" } }, - "remote": [WILDCARD], + "remote": { + "http://localhost:4545/subdir/mod1.ts": "[WILDCARD]", + "http://localhost:4545/subdir/print_hello.ts": "[WILDCARD]", + "http://localhost:4545/subdir/subdir2/mod2.ts": "[WILDCARD]", + "http://localhost:4545/v1/extensionless": "[WILDCARD]" + }, "workspace": { "dependencies": [ "jsr:@denotest/add", + "jsr:@std/testing", "npm:@denotest/esm-basic@^1.0.0" ] } diff --git a/tests/specs/install/future_install_local_deno/install.out b/tests/specs/install/future_install_local_deno/install.out index eecba1299..15263a37b 100644 --- a/tests/specs/install/future_install_local_deno/install.out +++ b/tests/specs/install/future_install_local_deno/install.out @@ -1,4 +1,6 @@ [UNORDERED_START] +Download http://127.0.0.1:4250/@std/testing/meta.json +Download http://127.0.0.1:4250/@std/testing/1.0.0_meta.json Download http://localhost:4545/v1/extensionless Download http://localhost:4545/subdir/mod1.ts Download http://localhost:4545/subdir/subdir2/mod2.ts @@ -8,4 +10,6 @@ Download http://127.0.0.1:4250/@denotest/add/1.0.0_meta.json Download http://127.0.0.1:4250/@denotest/add/1.0.0/mod.ts Download http://localhost:4260/@denotest/esm-basic Download http://localhost:4260/@denotest/esm-basic/1.0.0.tgz +Download http://127.0.0.1:4250/@std/testing/1.0.0/bdd.ts +Download http://127.0.0.1:4250/@std/testing/1.0.0/types.ts [UNORDERED_END] diff --git a/tests/specs/remove/basic/add.out b/tests/specs/remove/basic/add.out index a93b0ab52..75848b7c6 100644 --- a/tests/specs/remove/basic/add.out +++ b/tests/specs/remove/basic/add.out @@ -1,9 +1,9 @@ Created deno.json configuration file. -Add jsr:@std/assert@1.0.0 +[UNORDERED_START] Add jsr:@std/http@1.0.0 +Add jsr:@std/assert@1.0.0 +[UNORDERED_END] [UNORDERED_START] -Download http://127.0.0.1:4250/@std/http/1.0.0_meta.json -Download http://127.0.0.1:4250/@std/assert/1.0.0_meta.json Download http://127.0.0.1:4250/@std/http/1.0.0/mod.ts Download http://127.0.0.1:4250/@std/assert/1.0.0/mod.ts Download http://127.0.0.1:4250/@std/assert/1.0.0/assert_equals.ts |