1
0
Fork 0
mirror of https://github.com/denoland/deno.git synced 2024-12-23 07:44:48 -05:00

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.
This commit is contained in:
Nathan Whitaker 2024-08-21 15:23:32 -07:00 committed by GitHub
parent 9aaad3064a
commit 48da3c17ea
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
17 changed files with 190 additions and 65 deletions

View file

@ -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>>,

View file

@ -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()?;

View file

@ -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;

View file

@ -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(())

View file

@ -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(())
}

View file

@ -0,0 +1,2 @@
export function it(_name: string, _fn: () => void) {
}

View file

@ -0,0 +1 @@
export type AssertType<A, B> = A extends B ? true : never;

View file

@ -0,0 +1,6 @@
{
"exports": {
"./bdd": "./bdd.ts",
"./types": "./types.ts"
}
}

View file

@ -0,0 +1,8 @@
{
"scope": "std",
"name": "path",
"latest": "1.0.0",
"versions": {
"1.0.0": {}
}
}

View file

@ -0,0 +1,9 @@
{
"tempDir": true,
"steps": [
{
"args": "add @std/testing",
"output": "add.out"
}
]
}

View file

@ -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]

View file

View file

@ -0,0 +1,3 @@
import { it } from "@std/testing/bdd";
const _it = it;

View file

@ -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/"
}
}

View file

@ -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"
]
}

View file

@ -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]

View file

@ -1,9 +1,9 @@
Created deno.json configuration file.
Add jsr:@std/assert@1.0.0
Add jsr:@std/http@1.0.0
[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
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/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