mirror of
https://github.com/denoland/deno.git
synced 2024-11-21 15:04:11 -05:00
fix(cli): Create child node_modules for conflicting dependency versions, respect aliases in package.json (#24609)
Fixes #24419.
This commit is contained in:
parent
6421dc33ed
commit
c9da27e147
16 changed files with 190 additions and 14 deletions
|
@ -7,9 +7,9 @@ mod bin_entries;
|
||||||
use std::borrow::Cow;
|
use std::borrow::Cow;
|
||||||
use std::cell::RefCell;
|
use std::cell::RefCell;
|
||||||
use std::cmp::Ordering;
|
use std::cmp::Ordering;
|
||||||
|
use std::collections::hash_map::Entry;
|
||||||
use std::collections::BTreeMap;
|
use std::collections::BTreeMap;
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
use std::collections::HashSet;
|
|
||||||
use std::fs;
|
use std::fs;
|
||||||
use std::path::Path;
|
use std::path::Path;
|
||||||
use std::path::PathBuf;
|
use std::path::PathBuf;
|
||||||
|
@ -609,17 +609,82 @@ async fn sync_resolution_with_fs(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 4. Create all the top level packages in the node_modules folder, which are symlinks.
|
let mut found_names: HashMap<&String, &PackageNv> = HashMap::new();
|
||||||
//
|
|
||||||
// Symlink node_modules/<package_name> to
|
// 4. Create symlinks for package json dependencies
|
||||||
// node_modules/.deno/<package_id>/node_modules/<package_name>
|
{
|
||||||
let mut found_names = HashSet::new();
|
for remote in pkg_json_deps_provider.remote_pkgs() {
|
||||||
let mut ids = snapshot.top_level_packages().collect::<Vec<_>>();
|
let Some(remote_id) = snapshot
|
||||||
|
.resolve_best_package_id(&remote.req.name, &remote.req.version_req)
|
||||||
|
else {
|
||||||
|
continue; // skip, package not found
|
||||||
|
};
|
||||||
|
let remote_pkg = snapshot.package_from_id(&remote_id).unwrap();
|
||||||
|
let alias_clashes = remote.req.name != remote.alias
|
||||||
|
&& newest_packages_by_name.contains_key(&remote.alias);
|
||||||
|
let install_in_child = {
|
||||||
|
// we'll install in the child if the alias is taken by another package, or
|
||||||
|
// if there's already a package with the same name but different version
|
||||||
|
// linked into the root
|
||||||
|
match found_names.entry(&remote.alias) {
|
||||||
|
Entry::Occupied(nv) => {
|
||||||
|
alias_clashes
|
||||||
|
|| remote.req.name != nv.get().name // alias to a different package (in case of duplicate aliases)
|
||||||
|
|| !remote.req.version_req.matches(&nv.get().version) // incompatible version
|
||||||
|
}
|
||||||
|
Entry::Vacant(entry) => {
|
||||||
|
entry.insert(&remote_pkg.id.nv);
|
||||||
|
alias_clashes
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
let target_folder_name = get_package_folder_id_folder_name(
|
||||||
|
&remote_pkg.get_package_cache_folder_id(),
|
||||||
|
);
|
||||||
|
let local_registry_package_path = join_package_name(
|
||||||
|
&deno_local_registry_dir
|
||||||
|
.join(&target_folder_name)
|
||||||
|
.join("node_modules"),
|
||||||
|
&remote_pkg.id.nv.name,
|
||||||
|
);
|
||||||
|
if install_in_child {
|
||||||
|
// symlink the dep into the package's child node_modules folder
|
||||||
|
let dest_path =
|
||||||
|
remote.base_dir.join("node_modules").join(&remote.alias);
|
||||||
|
|
||||||
|
symlink_package_dir(&local_registry_package_path, &dest_path)?;
|
||||||
|
} else {
|
||||||
|
// symlink the package into `node_modules/<alias>`
|
||||||
|
if setup_cache
|
||||||
|
.insert_root_symlink(&remote_pkg.id.nv.name, &target_folder_name)
|
||||||
|
{
|
||||||
|
symlink_package_dir(
|
||||||
|
&local_registry_package_path,
|
||||||
|
&join_package_name(root_node_modules_dir_path, &remote.alias),
|
||||||
|
)?;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 5. Create symlinks for the remaining top level packages in the node_modules folder.
|
||||||
|
// (These may be present if they are not in the package.json dependencies, such as )
|
||||||
|
// Symlink node_modules/.deno/<package_id>/node_modules/<package_name> to
|
||||||
|
// node_modules/<package_name>
|
||||||
|
let mut ids = snapshot
|
||||||
|
.top_level_packages()
|
||||||
|
.filter(|f| !found_names.contains_key(&f.nv.name))
|
||||||
|
.collect::<Vec<_>>();
|
||||||
ids.sort_by(|a, b| b.cmp(a)); // create determinism and only include the latest version
|
ids.sort_by(|a, b| b.cmp(a)); // create determinism and only include the latest version
|
||||||
for id in ids {
|
for id in ids {
|
||||||
if !found_names.insert(&id.nv.name) {
|
match found_names.entry(&id.nv.name) {
|
||||||
|
Entry::Occupied(_) => {
|
||||||
continue; // skip, already handled
|
continue; // skip, already handled
|
||||||
}
|
}
|
||||||
|
Entry::Vacant(entry) => {
|
||||||
|
entry.insert(&id.nv);
|
||||||
|
}
|
||||||
|
}
|
||||||
let package = snapshot.package_from_id(id).unwrap();
|
let package = snapshot.package_from_id(id).unwrap();
|
||||||
let target_folder_name =
|
let target_folder_name =
|
||||||
get_package_folder_id_folder_name(&package.get_package_cache_folder_id());
|
get_package_folder_id_folder_name(&package.get_package_cache_folder_id());
|
||||||
|
@ -638,12 +703,17 @@ async fn sync_resolution_with_fs(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 5. Create a node_modules/.deno/node_modules/<package-name> directory with
|
// 6. Create a node_modules/.deno/node_modules/<package-name> directory with
|
||||||
// the remaining packages
|
// the remaining packages
|
||||||
for package in newest_packages_by_name.values() {
|
for package in newest_packages_by_name.values() {
|
||||||
if !found_names.insert(&package.id.nv.name) {
|
match found_names.entry(&package.id.nv.name) {
|
||||||
|
Entry::Occupied(_) => {
|
||||||
continue; // skip, already handled
|
continue; // skip, already handled
|
||||||
}
|
}
|
||||||
|
Entry::Vacant(entry) => {
|
||||||
|
entry.insert(&package.id.nv);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
let target_folder_name =
|
let target_folder_name =
|
||||||
get_package_folder_id_folder_name(&package.get_package_cache_folder_id());
|
get_package_folder_id_folder_name(&package.get_package_cache_folder_id());
|
||||||
|
@ -663,13 +733,13 @@ async fn sync_resolution_with_fs(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 6. Set up `node_modules/.bin` entries for packages that need it.
|
// 7. Set up `node_modules/.bin` entries for packages that need it.
|
||||||
{
|
{
|
||||||
let bin_entries = std::mem::take(&mut *bin_entries.borrow_mut());
|
let bin_entries = std::mem::take(&mut *bin_entries.borrow_mut());
|
||||||
bin_entries.finish(snapshot, &bin_node_modules_dir_path)?;
|
bin_entries.finish(snapshot, &bin_node_modules_dir_path)?;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 7. Create symlinks for the workspace packages
|
// 8. Create symlinks for the workspace packages
|
||||||
{
|
{
|
||||||
// todo(#24419): this is not exactly correct because it should
|
// todo(#24419): this is not exactly correct because it should
|
||||||
// install correctly for a workspace (potentially in sub directories),
|
// install correctly for a workspace (potentially in sub directories),
|
||||||
|
|
1
tests/registry/npm/@denotest/add/0.5.0/index.d.ts
vendored
Normal file
1
tests/registry/npm/@denotest/add/0.5.0/index.d.ts
vendored
Normal file
|
@ -0,0 +1 @@
|
||||||
|
export function sum(a: number, b: number): number;
|
1
tests/registry/npm/@denotest/add/0.5.0/index.js
Normal file
1
tests/registry/npm/@denotest/add/0.5.0/index.js
Normal file
|
@ -0,0 +1 @@
|
||||||
|
module.exports.sum = (a, b) => a + b;
|
4
tests/registry/npm/@denotest/add/0.5.0/package.json
Normal file
4
tests/registry/npm/@denotest/add/0.5.0/package.json
Normal file
|
@ -0,0 +1,4 @@
|
||||||
|
{
|
||||||
|
"name": "@denotest/add",
|
||||||
|
"version": "0.5.0"
|
||||||
|
}
|
24
tests/specs/npm/workspace_conflicting_dep/__test__.jsonc
Normal file
24
tests/specs/npm/workspace_conflicting_dep/__test__.jsonc
Normal file
|
@ -0,0 +1,24 @@
|
||||||
|
{
|
||||||
|
"tempDir": true,
|
||||||
|
"tests": {
|
||||||
|
"conflicting_deps": {
|
||||||
|
"envs": {
|
||||||
|
"DENO_FUTURE": "1"
|
||||||
|
},
|
||||||
|
"steps": [
|
||||||
|
{
|
||||||
|
"args": "install",
|
||||||
|
"output": "[WILDCARD]"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"args": "run ./a/index.js",
|
||||||
|
"output": "1 + 2 = 3\n"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"args": "run ./b/index.js",
|
||||||
|
"output": "1 + 2 = 3\n"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
3
tests/specs/npm/workspace_conflicting_dep/a/index.js
Normal file
3
tests/specs/npm/workspace_conflicting_dep/a/index.js
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
import { sum } from "@denotest/add";
|
||||||
|
|
||||||
|
console.log(`1 + 2 = ${sum(1, 2)}`);
|
7
tests/specs/npm/workspace_conflicting_dep/a/package.json
Normal file
7
tests/specs/npm/workspace_conflicting_dep/a/package.json
Normal file
|
@ -0,0 +1,7 @@
|
||||||
|
{
|
||||||
|
"name": "@denotest/a",
|
||||||
|
"version": "1.0.0",
|
||||||
|
"dependencies": {
|
||||||
|
"@denotest/add": "0.5.0"
|
||||||
|
}
|
||||||
|
}
|
3
tests/specs/npm/workspace_conflicting_dep/b/index.js
Normal file
3
tests/specs/npm/workspace_conflicting_dep/b/index.js
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
import { add } from "@denotest/add";
|
||||||
|
|
||||||
|
console.log(`1 + 2 = ${add(1, 2)}`);
|
7
tests/specs/npm/workspace_conflicting_dep/b/package.json
Normal file
7
tests/specs/npm/workspace_conflicting_dep/b/package.json
Normal file
|
@ -0,0 +1,7 @@
|
||||||
|
{
|
||||||
|
"name": "@denotest/b",
|
||||||
|
"version": "1.0.0",
|
||||||
|
"dependencies": {
|
||||||
|
"@denotest/add": "1.0.0"
|
||||||
|
}
|
||||||
|
}
|
6
tests/specs/npm/workspace_conflicting_dep/package.json
Normal file
6
tests/specs/npm/workspace_conflicting_dep/package.json
Normal file
|
@ -0,0 +1,6 @@
|
||||||
|
{
|
||||||
|
"workspaces": [
|
||||||
|
"./a",
|
||||||
|
"./b"
|
||||||
|
]
|
||||||
|
}
|
24
tests/specs/npm/workspace_dep_aliases/__test__.jsonc
Normal file
24
tests/specs/npm/workspace_dep_aliases/__test__.jsonc
Normal file
|
@ -0,0 +1,24 @@
|
||||||
|
{
|
||||||
|
"tempDir": true,
|
||||||
|
"tests": {
|
||||||
|
"conflicting_deps": {
|
||||||
|
"envs": {
|
||||||
|
"DENO_FUTURE": "1"
|
||||||
|
},
|
||||||
|
"steps": [
|
||||||
|
{
|
||||||
|
"args": "install",
|
||||||
|
"output": "[WILDCARD]"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"args": "run ./a/index.js",
|
||||||
|
"output": "1 + 2 = 3\n"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"args": "run ./b/index.js",
|
||||||
|
"output": "1 + 2 = 3\n"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
3
tests/specs/npm/workspace_dep_aliases/a/index.js
Normal file
3
tests/specs/npm/workspace_dep_aliases/a/index.js
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
import { sum } from "sumPkg";
|
||||||
|
|
||||||
|
console.log(`1 + 2 = ${sum(1, 2)}`);
|
7
tests/specs/npm/workspace_dep_aliases/a/package.json
Normal file
7
tests/specs/npm/workspace_dep_aliases/a/package.json
Normal file
|
@ -0,0 +1,7 @@
|
||||||
|
{
|
||||||
|
"name": "@denotest/a",
|
||||||
|
"version": "1.0.0",
|
||||||
|
"dependencies": {
|
||||||
|
"sumPkg": "npm:@denotest/add@0.5.0"
|
||||||
|
}
|
||||||
|
}
|
3
tests/specs/npm/workspace_dep_aliases/b/index.js
Normal file
3
tests/specs/npm/workspace_dep_aliases/b/index.js
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
import { add } from "addPkg";
|
||||||
|
|
||||||
|
console.log(`1 + 2 = ${add(1, 2)}`);
|
7
tests/specs/npm/workspace_dep_aliases/b/package.json
Normal file
7
tests/specs/npm/workspace_dep_aliases/b/package.json
Normal file
|
@ -0,0 +1,7 @@
|
||||||
|
{
|
||||||
|
"name": "@denotest/b",
|
||||||
|
"version": "1.0.0",
|
||||||
|
"dependencies": {
|
||||||
|
"addPkg": "npm:@denotest/add@1.0.0"
|
||||||
|
}
|
||||||
|
}
|
6
tests/specs/npm/workspace_dep_aliases/package.json
Normal file
6
tests/specs/npm/workspace_dep_aliases/package.json
Normal file
|
@ -0,0 +1,6 @@
|
||||||
|
{
|
||||||
|
"workspaces": [
|
||||||
|
"./a",
|
||||||
|
"./b"
|
||||||
|
]
|
||||||
|
}
|
Loading…
Reference in a new issue