summaryrefslogtreecommitdiff
path: root/cli
diff options
context:
space:
mode:
authorDavid Sherret <dsherret@users.noreply.github.com>2023-11-04 12:41:51 -0400
committerGitHub <noreply@github.com>2023-11-04 16:41:51 +0000
commite4c947dd2b63726ecc9b4303e03921b6839adade (patch)
tree9619bdb6649d1f460fb02ab8e448c27c95dbfa74 /cli
parent0b75a7169b2e123cac04e7ffcaf16a28eb356fd0 (diff)
fix(node): use closest package.json to resolve package.json imports (#21075)
Diffstat (limited to 'cli')
-rw-r--r--cli/lsp/analysis.rs25
-rw-r--r--cli/lsp/language_server.rs1
-rw-r--r--cli/npm/byonm.rs44
-rw-r--r--cli/npm/managed/mod.rs18
-rw-r--r--cli/tests/integration/npm_tests.rs25
-rw-r--r--cli/tests/testdata/npm/imports_package_json/import_not_defined.js3
-rw-r--r--cli/tests/testdata/npm/imports_package_json/import_not_defined.out6
-rw-r--r--cli/tests/testdata/npm/imports_package_json/main.js4
-rw-r--r--cli/tests/testdata/npm/imports_package_json/main.out4
-rw-r--r--cli/tests/testdata/npm/imports_package_json/package.json6
-rw-r--r--cli/tests/testdata/npm/imports_package_json/sub_path_import_not_defined.js3
-rw-r--r--cli/tests/testdata/npm/imports_package_json/sub_path_import_not_defined.out6
-rw-r--r--cli/tests/testdata/npm/registry/@denotest/imports-package-json/1.0.0/hi.js1
-rw-r--r--cli/tests/testdata/npm/registry/@denotest/imports-package-json/1.0.0/import_not_defined.js3
-rw-r--r--cli/tests/testdata/npm/registry/@denotest/imports-package-json/1.0.0/main.js7
-rw-r--r--cli/tests/testdata/npm/registry/@denotest/imports-package-json/1.0.0/package.json16
-rw-r--r--cli/tests/testdata/npm/registry/@denotest/imports-package-json/1.0.0/sub_path/bye.js1
-rw-r--r--cli/tests/testdata/npm/registry/@denotest/imports-package-json/1.0.0/sub_path/import_not_defined.js4
-rw-r--r--cli/tests/testdata/npm/registry/@denotest/imports-package-json/1.0.0/sub_path/main.js3
-rw-r--r--cli/tests/testdata/npm/registry/@denotest/imports-package-json/1.0.0/sub_path/package.json6
20 files changed, 115 insertions, 71 deletions
diff --git a/cli/lsp/analysis.rs b/cli/lsp/analysis.rs
index f8ace060a..e62294012 100644
--- a/cli/lsp/analysis.rs
+++ b/cli/lsp/analysis.rs
@@ -8,6 +8,7 @@ use super::tsc;
use crate::npm::CliNpmResolver;
use crate::tools::lint::create_linter;
+use crate::util::path::specifier_to_file_path;
use deno_ast::SourceRange;
use deno_ast::SourceRangedForSpanned;
@@ -20,9 +21,10 @@ use deno_core::serde_json;
use deno_core::serde_json::json;
use deno_core::ModuleSpecifier;
use deno_lint::rules::LintRule;
+use deno_runtime::deno_node::NodeResolver;
use deno_runtime::deno_node::NpmResolver;
-use deno_runtime::deno_node::PackageJson;
use deno_runtime::deno_node::PathClean;
+use deno_runtime::permissions::PermissionsContainer;
use deno_semver::package::PackageReq;
use import_map::ImportMap;
use once_cell::sync::Lazy;
@@ -161,6 +163,7 @@ fn code_as_string(code: &Option<lsp::NumberOrString>) -> String {
pub struct TsResponseImportMapper<'a> {
documents: &'a Documents,
maybe_import_map: Option<&'a ImportMap>,
+ node_resolver: Option<&'a NodeResolver>,
npm_resolver: Option<&'a dyn CliNpmResolver>,
}
@@ -168,11 +171,13 @@ impl<'a> TsResponseImportMapper<'a> {
pub fn new(
documents: &'a Documents,
maybe_import_map: Option<&'a ImportMap>,
+ node_resolver: Option<&'a NodeResolver>,
npm_resolver: Option<&'a dyn CliNpmResolver>,
) -> Self {
Self {
documents,
maybe_import_map,
+ node_resolver,
npm_resolver,
}
}
@@ -249,18 +254,14 @@ impl<'a> TsResponseImportMapper<'a> {
&self,
specifier: &ModuleSpecifier,
) -> Option<String> {
- let specifier_path = specifier.to_file_path().ok()?;
- let root_folder = self
- .npm_resolver
- .as_ref()
- .and_then(|r| r.resolve_package_folder_from_path(specifier).ok())
+ let node_resolver = self.node_resolver?;
+ let package_json = node_resolver
+ .get_closest_package_json(specifier, &PermissionsContainer::allow_all())
+ .ok()
.flatten()?;
- let package_json_path = root_folder.join("package.json");
- let package_json_text = std::fs::read_to_string(&package_json_path).ok()?;
- let package_json =
- PackageJson::load_from_string(package_json_path, package_json_text)
- .ok()?;
+ let root_folder = package_json.path.parent()?;
+ let specifier_path = specifier_to_file_path(specifier).ok()?;
let mut search_paths = vec![specifier_path.clone()];
// TypeScript will provide a .js extension for quick fixes, so do
// a search for the .d.ts file instead
@@ -271,7 +272,7 @@ impl<'a> TsResponseImportMapper<'a> {
for search_path in search_paths {
if let Some(exports) = &package_json.exports {
if let Some(result) = try_reverse_map_package_json_exports(
- &root_folder,
+ root_folder,
&search_path,
exports,
) {
diff --git a/cli/lsp/language_server.rs b/cli/lsp/language_server.rs
index 8b275a650..435daee90 100644
--- a/cli/lsp/language_server.rs
+++ b/cli/lsp/language_server.rs
@@ -2127,6 +2127,7 @@ impl Inner {
TsResponseImportMapper::new(
&self.documents,
self.maybe_import_map.as_deref(),
+ self.npm.node_resolver.as_deref(),
self.npm.resolver.as_deref(),
)
}
diff --git a/cli/npm/byonm.rs b/cli/npm/byonm.rs
index 6ddac42e4..da36e3b53 100644
--- a/cli/npm/byonm.rs
+++ b/cli/npm/byonm.rs
@@ -91,16 +91,16 @@ impl NpmResolver for ByonmCliNpmResolver {
fn inner(
fs: &dyn FileSystem,
name: &str,
- package_root_path: &Path,
referrer: &ModuleSpecifier,
mode: NodeResolutionMode,
) -> Result<PathBuf, AnyError> {
+ let referrer_file = specifier_to_file_path(referrer)?;
let types_pkg_name = if mode.is_types() && !name.starts_with("@types/") {
Some(types_package_name(name))
} else {
None
};
- let mut current_folder = package_root_path;
+ let mut current_folder = referrer_file.parent().unwrap();
loop {
let node_modules_folder = if current_folder.ends_with("node_modules") {
Cow::Borrowed(current_folder)
@@ -135,48 +135,10 @@ impl NpmResolver for ByonmCliNpmResolver {
);
}
- let package_root_path =
- self.resolve_package_folder_from_path(referrer)?.unwrap(); // todo(byonm): don't unwrap
- let path = inner(&*self.fs, name, &package_root_path, referrer, mode)?;
+ let path = inner(&*self.fs, name, referrer, mode)?;
Ok(self.fs.realpath_sync(&path)?)
}
- fn resolve_package_folder_from_path(
- &self,
- specifier: &deno_core::ModuleSpecifier,
- ) -> Result<Option<PathBuf>, AnyError> {
- let path = specifier.to_file_path().unwrap(); // todo(byonm): don't unwrap
- let path = self.fs.realpath_sync(&path)?;
- if self.in_npm_package(specifier) {
- let mut path = path.as_path();
- while let Some(parent) = path.parent() {
- if parent
- .file_name()
- .and_then(|f| f.to_str())
- .map(|s| s.to_ascii_lowercase())
- .as_deref()
- == Some("node_modules")
- {
- return Ok(Some(path.to_path_buf()));
- } else {
- path = parent;
- }
- }
- } else {
- // find the folder with a package.json
- // todo(dsherret): not exactly correct, but good enough for a first pass
- let mut path = path.as_path();
- while let Some(parent) = path.parent() {
- if self.fs.exists_sync(&parent.join("package.json")) {
- return Ok(Some(parent.to_path_buf()));
- } else {
- path = parent;
- }
- }
- }
- Ok(None)
- }
-
fn in_npm_package(&self, specifier: &ModuleSpecifier) -> bool {
specifier.scheme() == "file"
&& specifier
diff --git a/cli/npm/managed/mod.rs b/cli/npm/managed/mod.rs
index 68b5c2134..d59b81912 100644
--- a/cli/npm/managed/mod.rs
+++ b/cli/npm/managed/mod.rs
@@ -505,24 +505,6 @@ impl NpmResolver for ManagedCliNpmResolver {
Ok(path)
}
- fn resolve_package_folder_from_path(
- &self,
- specifier: &ModuleSpecifier,
- ) -> Result<Option<PathBuf>, AnyError> {
- let Some(path) = self
- .fs_resolver
- .resolve_package_folder_from_specifier(specifier)?
- else {
- return Ok(None);
- };
- log::debug!(
- "Resolved package folder of {} to {}",
- specifier,
- path.display()
- );
- Ok(Some(path))
- }
-
fn in_npm_package(&self, specifier: &ModuleSpecifier) -> bool {
let root_dir_url = self.fs_resolver.root_dir_url();
debug_assert!(root_dir_url.as_str().ends_with('/'));
diff --git a/cli/tests/integration/npm_tests.rs b/cli/tests/integration/npm_tests.rs
index 5104036e3..927e83475 100644
--- a/cli/tests/integration/npm_tests.rs
+++ b/cli/tests/integration/npm_tests.rs
@@ -2505,3 +2505,28 @@ console.log(add(1, 2));
.run();
output.assert_matches_text("Check file:///[WILDCARD]/project-b/main.ts\n");
}
+
+itest!(imports_package_json {
+ args: "run --node-modules-dir=false npm/imports_package_json/main.js",
+ output: "npm/imports_package_json/main.out",
+ envs: env_vars_for_npm_tests(),
+ http_server: true,
+});
+
+itest!(imports_package_json_import_not_defined {
+ args:
+ "run --node-modules-dir=false npm/imports_package_json/import_not_defined.js",
+ output: "npm/imports_package_json/import_not_defined.out",
+ envs: env_vars_for_npm_tests(),
+ exit_code: 1,
+ http_server: true,
+});
+
+itest!(imports_package_json_sub_path_import_not_defined {
+ args:
+ "run --node-modules-dir=false npm/imports_package_json/sub_path_import_not_defined.js",
+ output: "npm/imports_package_json/sub_path_import_not_defined.out",
+ envs: env_vars_for_npm_tests(),
+ exit_code: 1,
+ http_server: true,
+});
diff --git a/cli/tests/testdata/npm/imports_package_json/import_not_defined.js b/cli/tests/testdata/npm/imports_package_json/import_not_defined.js
new file mode 100644
index 000000000..dc4d2df16
--- /dev/null
+++ b/cli/tests/testdata/npm/imports_package_json/import_not_defined.js
@@ -0,0 +1,3 @@
+import data from "@denotest/imports-package-json/import-not-defined";
+
+console.log(data);
diff --git a/cli/tests/testdata/npm/imports_package_json/import_not_defined.out b/cli/tests/testdata/npm/imports_package_json/import_not_defined.out
new file mode 100644
index 000000000..3580d9007
--- /dev/null
+++ b/cli/tests/testdata/npm/imports_package_json/import_not_defined.out
@@ -0,0 +1,6 @@
+Download http://localhost:4545/npm/registry/@denotest/imports-package-json
+Download http://localhost:4545/npm/registry/@denotest/imports-package-json/1.0.0.tgz
+error: Could not resolve '#not-defined' from 'file:///[WILDCARD]/@denotest/imports-package-json/1.0.0/import_not_defined.js'.
+
+Caused by:
+ [ERR_PACKAGE_IMPORT_NOT_DEFINED] Package import specifier "#not-defined" is not defined in package [WILDCARD]package.json imported from [WILDCARD]import_not_defined.js
diff --git a/cli/tests/testdata/npm/imports_package_json/main.js b/cli/tests/testdata/npm/imports_package_json/main.js
new file mode 100644
index 000000000..cd7bbf8e9
--- /dev/null
+++ b/cli/tests/testdata/npm/imports_package_json/main.js
@@ -0,0 +1,4 @@
+import data from "@denotest/imports-package-json";
+
+console.log(data.hi);
+console.log(data.bye);
diff --git a/cli/tests/testdata/npm/imports_package_json/main.out b/cli/tests/testdata/npm/imports_package_json/main.out
new file mode 100644
index 000000000..f006f18d5
--- /dev/null
+++ b/cli/tests/testdata/npm/imports_package_json/main.out
@@ -0,0 +1,4 @@
+Download http://localhost:4545/npm/registry/@denotest/imports-package-json
+Download http://localhost:4545/npm/registry/@denotest/imports-package-json/1.0.0.tgz
+hi
+bye
diff --git a/cli/tests/testdata/npm/imports_package_json/package.json b/cli/tests/testdata/npm/imports_package_json/package.json
new file mode 100644
index 000000000..cb6a08d1a
--- /dev/null
+++ b/cli/tests/testdata/npm/imports_package_json/package.json
@@ -0,0 +1,6 @@
+{
+ "name": "my-test",
+ "dependencies": {
+ "@denotest/imports-package-json": "1.0.0"
+ }
+}
diff --git a/cli/tests/testdata/npm/imports_package_json/sub_path_import_not_defined.js b/cli/tests/testdata/npm/imports_package_json/sub_path_import_not_defined.js
new file mode 100644
index 000000000..f1097aa06
--- /dev/null
+++ b/cli/tests/testdata/npm/imports_package_json/sub_path_import_not_defined.js
@@ -0,0 +1,3 @@
+import data from "@denotest/imports-package-json/sub-path-import-not-defined";
+
+console.log(data);
diff --git a/cli/tests/testdata/npm/imports_package_json/sub_path_import_not_defined.out b/cli/tests/testdata/npm/imports_package_json/sub_path_import_not_defined.out
new file mode 100644
index 000000000..04a21c99e
--- /dev/null
+++ b/cli/tests/testdata/npm/imports_package_json/sub_path_import_not_defined.out
@@ -0,0 +1,6 @@
+Download http://localhost:4545/npm/registry/@denotest/imports-package-json
+Download http://localhost:4545/npm/registry/@denotest/imports-package-json/1.0.0.tgz
+error: Could not resolve '#hi' from 'file:///[WILDCARD]/@denotest/imports-package-json/1.0.0/sub_path/import_not_defined.js'.
+
+Caused by:
+ [ERR_PACKAGE_IMPORT_NOT_DEFINED] Package import specifier "#hi" is not defined in package [WILDCARD]sub_path[WILDCARD]package.json imported from [WILDCARD]import_not_defined.js
diff --git a/cli/tests/testdata/npm/registry/@denotest/imports-package-json/1.0.0/hi.js b/cli/tests/testdata/npm/registry/@denotest/imports-package-json/1.0.0/hi.js
new file mode 100644
index 000000000..407090812
--- /dev/null
+++ b/cli/tests/testdata/npm/registry/@denotest/imports-package-json/1.0.0/hi.js
@@ -0,0 +1 @@
+export default "hi";
diff --git a/cli/tests/testdata/npm/registry/@denotest/imports-package-json/1.0.0/import_not_defined.js b/cli/tests/testdata/npm/registry/@denotest/imports-package-json/1.0.0/import_not_defined.js
new file mode 100644
index 000000000..07864fd2c
--- /dev/null
+++ b/cli/tests/testdata/npm/registry/@denotest/imports-package-json/1.0.0/import_not_defined.js
@@ -0,0 +1,3 @@
+import notDefined from "#not-defined";
+
+export default notDefined;
diff --git a/cli/tests/testdata/npm/registry/@denotest/imports-package-json/1.0.0/main.js b/cli/tests/testdata/npm/registry/@denotest/imports-package-json/1.0.0/main.js
new file mode 100644
index 000000000..46625479e
--- /dev/null
+++ b/cli/tests/testdata/npm/registry/@denotest/imports-package-json/1.0.0/main.js
@@ -0,0 +1,7 @@
+import hi from "#hi";
+import bye from "./sub_path/main.js";
+
+export default {
+ hi,
+ bye,
+};
diff --git a/cli/tests/testdata/npm/registry/@denotest/imports-package-json/1.0.0/package.json b/cli/tests/testdata/npm/registry/@denotest/imports-package-json/1.0.0/package.json
new file mode 100644
index 000000000..a4fbc8889
--- /dev/null
+++ b/cli/tests/testdata/npm/registry/@denotest/imports-package-json/1.0.0/package.json
@@ -0,0 +1,16 @@
+{
+ "name": "imports-package-json",
+ "type": "module",
+ "version": "1.0.0",
+ "description": "",
+ "license": "ISC",
+ "author": "",
+ "exports": {
+ ".": "./main.js",
+ "./import-not-defined": "./import_not_defined.js",
+ "./sub-path-import-not-defined": "./sub_path/import_not_defined.js"
+ },
+ "imports": {
+ "#hi": "./hi.js"
+ }
+}
diff --git a/cli/tests/testdata/npm/registry/@denotest/imports-package-json/1.0.0/sub_path/bye.js b/cli/tests/testdata/npm/registry/@denotest/imports-package-json/1.0.0/sub_path/bye.js
new file mode 100644
index 000000000..6fc719e48
--- /dev/null
+++ b/cli/tests/testdata/npm/registry/@denotest/imports-package-json/1.0.0/sub_path/bye.js
@@ -0,0 +1 @@
+export default "bye";
diff --git a/cli/tests/testdata/npm/registry/@denotest/imports-package-json/1.0.0/sub_path/import_not_defined.js b/cli/tests/testdata/npm/registry/@denotest/imports-package-json/1.0.0/sub_path/import_not_defined.js
new file mode 100644
index 000000000..ffaa2b1ad
--- /dev/null
+++ b/cli/tests/testdata/npm/registry/@denotest/imports-package-json/1.0.0/sub_path/import_not_defined.js
@@ -0,0 +1,4 @@
+// this won't be defined in the closest package.json and will fail
+import hi from "#hi";
+
+export default hi;
diff --git a/cli/tests/testdata/npm/registry/@denotest/imports-package-json/1.0.0/sub_path/main.js b/cli/tests/testdata/npm/registry/@denotest/imports-package-json/1.0.0/sub_path/main.js
new file mode 100644
index 000000000..260ca79ae
--- /dev/null
+++ b/cli/tests/testdata/npm/registry/@denotest/imports-package-json/1.0.0/sub_path/main.js
@@ -0,0 +1,3 @@
+import bye from "#bye";
+
+export default bye;
diff --git a/cli/tests/testdata/npm/registry/@denotest/imports-package-json/1.0.0/sub_path/package.json b/cli/tests/testdata/npm/registry/@denotest/imports-package-json/1.0.0/sub_path/package.json
new file mode 100644
index 000000000..3f2c2bbd8
--- /dev/null
+++ b/cli/tests/testdata/npm/registry/@denotest/imports-package-json/1.0.0/sub_path/package.json
@@ -0,0 +1,6 @@
+{
+ "type": "module",
+ "imports": {
+ "#bye": "./bye.js"
+ }
+}