mirror of
https://github.com/denoland/deno.git
synced 2024-12-24 08:09:08 -05:00
fix(npm): prefer importing esm from esm (#15676)
This commit is contained in:
parent
54be07d05e
commit
5f251b283b
14 changed files with 331 additions and 209 deletions
1
Cargo.lock
generated
1
Cargo.lock
generated
|
@ -1141,6 +1141,7 @@ name = "deno_node"
|
||||||
version = "0.3.0"
|
version = "0.3.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"deno_core",
|
"deno_core",
|
||||||
|
"path-clean",
|
||||||
"regex",
|
"regex",
|
||||||
"serde",
|
"serde",
|
||||||
]
|
]
|
||||||
|
|
|
@ -17,11 +17,13 @@ use deno_core::serde_json::Value;
|
||||||
use deno_core::url::Url;
|
use deno_core::url::Url;
|
||||||
use deno_core::JsRuntime;
|
use deno_core::JsRuntime;
|
||||||
use deno_graph::source::ResolveResponse;
|
use deno_graph::source::ResolveResponse;
|
||||||
|
use deno_runtime::deno_node::get_closest_package_json;
|
||||||
use deno_runtime::deno_node::legacy_main_resolve;
|
use deno_runtime::deno_node::legacy_main_resolve;
|
||||||
use deno_runtime::deno_node::package_exports_resolve;
|
use deno_runtime::deno_node::package_exports_resolve;
|
||||||
use deno_runtime::deno_node::package_imports_resolve;
|
use deno_runtime::deno_node::package_imports_resolve;
|
||||||
use deno_runtime::deno_node::package_resolve;
|
use deno_runtime::deno_node::package_resolve;
|
||||||
use deno_runtime::deno_node::DenoDirNpmResolver;
|
use deno_runtime::deno_node::DenoDirNpmResolver;
|
||||||
|
use deno_runtime::deno_node::NodeModuleKind;
|
||||||
use deno_runtime::deno_node::PackageJson;
|
use deno_runtime::deno_node::PackageJson;
|
||||||
use deno_runtime::deno_node::DEFAULT_CONDITIONS;
|
use deno_runtime::deno_node::DEFAULT_CONDITIONS;
|
||||||
use once_cell::sync::Lazy;
|
use once_cell::sync::Lazy;
|
||||||
|
@ -342,6 +344,8 @@ pub fn node_resolve(
|
||||||
referrer: &ModuleSpecifier,
|
referrer: &ModuleSpecifier,
|
||||||
npm_resolver: &dyn DenoDirNpmResolver,
|
npm_resolver: &dyn DenoDirNpmResolver,
|
||||||
) -> Result<Option<ResolveResponse>, AnyError> {
|
) -> Result<Option<ResolveResponse>, AnyError> {
|
||||||
|
// Note: if we are here, then the referrer is an esm module
|
||||||
|
|
||||||
// TODO(bartlomieju): skipped "policy" part as we don't plan to support it
|
// TODO(bartlomieju): skipped "policy" part as we don't plan to support it
|
||||||
|
|
||||||
// NOTE(bartlomieju): this will force `ProcState` to use Node.js polyfill for
|
// NOTE(bartlomieju): this will force `ProcState` to use Node.js polyfill for
|
||||||
|
@ -385,7 +389,7 @@ pub fn node_resolve(
|
||||||
return Err(errors::err_unsupported_esm_url_scheme(&url));
|
return Err(errors::err_unsupported_esm_url_scheme(&url));
|
||||||
}
|
}
|
||||||
|
|
||||||
// todo(THIS PR): I think this is handled upstream so can be removed?
|
// todo(dsherret): this seems wrong
|
||||||
if referrer.scheme() == "data" {
|
if referrer.scheme() == "data" {
|
||||||
let url = referrer.join(specifier).map_err(AnyError::from)?;
|
let url = referrer.join(specifier).map_err(AnyError::from)?;
|
||||||
return Ok(Some(ResolveResponse::Specifier(url)));
|
return Ok(Some(ResolveResponse::Specifier(url)));
|
||||||
|
@ -412,7 +416,7 @@ pub fn node_resolve_npm_reference(
|
||||||
let package_folder = npm_resolver
|
let package_folder = npm_resolver
|
||||||
.resolve_package_from_deno_module(&reference.req)?
|
.resolve_package_from_deno_module(&reference.req)?
|
||||||
.folder_path;
|
.folder_path;
|
||||||
let maybe_url = package_config_resolve(
|
let resolved_path = package_config_resolve(
|
||||||
&reference
|
&reference
|
||||||
.sub_path
|
.sub_path
|
||||||
.as_ref()
|
.as_ref()
|
||||||
|
@ -420,16 +424,13 @@ pub fn node_resolve_npm_reference(
|
||||||
.unwrap_or_else(|| ".".to_string()),
|
.unwrap_or_else(|| ".".to_string()),
|
||||||
&package_folder,
|
&package_folder,
|
||||||
npm_resolver,
|
npm_resolver,
|
||||||
|
NodeModuleKind::Esm,
|
||||||
)
|
)
|
||||||
.map(Some)
|
|
||||||
.with_context(|| {
|
.with_context(|| {
|
||||||
format!("Error resolving package config for '{}'.", reference)
|
format!("Error resolving package config for '{}'.", reference)
|
||||||
})?;
|
})?;
|
||||||
let url = match maybe_url {
|
|
||||||
Some(url) => url,
|
|
||||||
None => return Ok(None),
|
|
||||||
};
|
|
||||||
|
|
||||||
|
let url = ModuleSpecifier::from_file_path(resolved_path).unwrap();
|
||||||
let resolve_response = url_to_resolve_response(url, npm_resolver)?;
|
let resolve_response = url_to_resolve_response(url, npm_resolver)?;
|
||||||
// TODO(bartlomieju): skipped checking errors for commonJS resolution and
|
// TODO(bartlomieju): skipped checking errors for commonJS resolution and
|
||||||
// "preserveSymlinksMain"/"preserveSymlinks" options.
|
// "preserveSymlinksMain"/"preserveSymlinks" options.
|
||||||
|
@ -521,33 +522,30 @@ fn package_config_resolve(
|
||||||
package_subpath: &str,
|
package_subpath: &str,
|
||||||
package_dir: &Path,
|
package_dir: &Path,
|
||||||
npm_resolver: &dyn DenoDirNpmResolver,
|
npm_resolver: &dyn DenoDirNpmResolver,
|
||||||
) -> Result<ModuleSpecifier, AnyError> {
|
referrer_kind: NodeModuleKind,
|
||||||
|
) -> Result<PathBuf, AnyError> {
|
||||||
let package_json_path = package_dir.join("package.json");
|
let package_json_path = package_dir.join("package.json");
|
||||||
// todo(dsherret): remove base from this code
|
let referrer =
|
||||||
let base =
|
|
||||||
ModuleSpecifier::from_directory_path(package_json_path.parent().unwrap())
|
ModuleSpecifier::from_directory_path(package_json_path.parent().unwrap())
|
||||||
.unwrap();
|
.unwrap();
|
||||||
let package_config =
|
let package_config =
|
||||||
PackageJson::load(npm_resolver, package_json_path.clone())?;
|
PackageJson::load(npm_resolver, package_json_path.clone())?;
|
||||||
let package_json_url =
|
|
||||||
ModuleSpecifier::from_file_path(&package_json_path).unwrap();
|
|
||||||
if let Some(exports) = &package_config.exports {
|
if let Some(exports) = &package_config.exports {
|
||||||
return package_exports_resolve(
|
return package_exports_resolve(
|
||||||
package_json_url,
|
&package_json_path,
|
||||||
package_subpath.to_string(),
|
package_subpath.to_string(),
|
||||||
exports,
|
exports,
|
||||||
&base,
|
&referrer,
|
||||||
|
referrer_kind,
|
||||||
DEFAULT_CONDITIONS,
|
DEFAULT_CONDITIONS,
|
||||||
npm_resolver,
|
npm_resolver,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
if package_subpath == "." {
|
if package_subpath == "." {
|
||||||
return legacy_main_resolve(&package_json_url, &package_config, &base);
|
return legacy_main_resolve(&package_config, referrer_kind);
|
||||||
}
|
}
|
||||||
|
|
||||||
package_json_url
|
Ok(package_dir.join(package_subpath))
|
||||||
.join(package_subpath)
|
|
||||||
.map_err(AnyError::from)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn url_to_resolve_response(
|
fn url_to_resolve_response(
|
||||||
|
@ -570,37 +568,6 @@ fn url_to_resolve_response(
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_closest_package_json(
|
|
||||||
url: &ModuleSpecifier,
|
|
||||||
npm_resolver: &dyn DenoDirNpmResolver,
|
|
||||||
) -> Result<PackageJson, AnyError> {
|
|
||||||
let package_json_path = get_closest_package_json_path(url, npm_resolver)?;
|
|
||||||
PackageJson::load(npm_resolver, package_json_path)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn get_closest_package_json_path(
|
|
||||||
url: &ModuleSpecifier,
|
|
||||||
npm_resolver: &dyn DenoDirNpmResolver,
|
|
||||||
) -> Result<PathBuf, AnyError> {
|
|
||||||
let file_path = url.to_file_path().unwrap();
|
|
||||||
let mut current_dir = file_path.parent().unwrap();
|
|
||||||
let package_json_path = current_dir.join("package.json");
|
|
||||||
if package_json_path.exists() {
|
|
||||||
return Ok(package_json_path);
|
|
||||||
}
|
|
||||||
let root_folder = npm_resolver
|
|
||||||
.resolve_package_folder_from_path(&url.to_file_path().unwrap())?;
|
|
||||||
while current_dir.starts_with(&root_folder) {
|
|
||||||
current_dir = current_dir.parent().unwrap();
|
|
||||||
let package_json_path = current_dir.join("./package.json");
|
|
||||||
if package_json_path.exists() {
|
|
||||||
return Ok(package_json_path);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
bail!("did not find package.json in {}", root_folder.display())
|
|
||||||
}
|
|
||||||
|
|
||||||
fn finalize_resolution(
|
fn finalize_resolution(
|
||||||
resolved: ModuleSpecifier,
|
resolved: ModuleSpecifier,
|
||||||
base: &ModuleSpecifier,
|
base: &ModuleSpecifier,
|
||||||
|
@ -667,25 +634,34 @@ fn module_resolve(
|
||||||
conditions: &[&str],
|
conditions: &[&str],
|
||||||
npm_resolver: &dyn DenoDirNpmResolver,
|
npm_resolver: &dyn DenoDirNpmResolver,
|
||||||
) -> Result<Option<ModuleSpecifier>, AnyError> {
|
) -> Result<Option<ModuleSpecifier>, AnyError> {
|
||||||
|
// note: if we're here, the referrer is an esm module
|
||||||
let url = if should_be_treated_as_relative_or_absolute_path(specifier) {
|
let url = if should_be_treated_as_relative_or_absolute_path(specifier) {
|
||||||
let resolved_specifier = referrer.join(specifier)?;
|
let resolved_specifier = referrer.join(specifier)?;
|
||||||
Some(resolved_specifier)
|
Some(resolved_specifier)
|
||||||
} else if specifier.starts_with('#') {
|
} else if specifier.starts_with('#') {
|
||||||
Some(package_imports_resolve(
|
Some(
|
||||||
specifier,
|
package_imports_resolve(
|
||||||
referrer,
|
specifier,
|
||||||
conditions,
|
referrer,
|
||||||
npm_resolver,
|
NodeModuleKind::Esm,
|
||||||
)?)
|
conditions,
|
||||||
|
npm_resolver,
|
||||||
|
)
|
||||||
|
.map(|p| ModuleSpecifier::from_file_path(p).unwrap())?,
|
||||||
|
)
|
||||||
} else if let Ok(resolved) = Url::parse(specifier) {
|
} else if let Ok(resolved) = Url::parse(specifier) {
|
||||||
Some(resolved)
|
Some(resolved)
|
||||||
} else {
|
} else {
|
||||||
Some(package_resolve(
|
Some(
|
||||||
specifier,
|
package_resolve(
|
||||||
referrer,
|
specifier,
|
||||||
conditions,
|
referrer,
|
||||||
npm_resolver,
|
NodeModuleKind::Esm,
|
||||||
)?)
|
conditions,
|
||||||
|
npm_resolver,
|
||||||
|
)
|
||||||
|
.map(|p| ModuleSpecifier::from_file_path(p).unwrap())?,
|
||||||
|
)
|
||||||
};
|
};
|
||||||
Ok(match url {
|
Ok(match url {
|
||||||
Some(url) => Some(finalize_resolution(url, referrer)?),
|
Some(url) => Some(finalize_resolution(url, referrer)?),
|
||||||
|
|
|
@ -75,6 +75,13 @@ itest!(conditional_exports {
|
||||||
http_server: true,
|
http_server: true,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
itest!(dual_cjs_esm {
|
||||||
|
args: "run --unstable -A --quiet npm/dual_cjs_esm/main.ts",
|
||||||
|
output: "npm/dual_cjs_esm/main.out",
|
||||||
|
envs: env_vars(),
|
||||||
|
http_server: true,
|
||||||
|
});
|
||||||
|
|
||||||
itest!(dynamic_import {
|
itest!(dynamic_import {
|
||||||
args: "run --allow-read --allow-env --unstable npm/dynamic_import/main.ts",
|
args: "run --allow-read --allow-env --unstable npm/dynamic_import/main.ts",
|
||||||
output: "npm/dynamic_import/main.out",
|
output: "npm/dynamic_import/main.out",
|
||||||
|
|
1
cli/tests/testdata/npm/dual_cjs_esm/main.out
vendored
Normal file
1
cli/tests/testdata/npm/dual_cjs_esm/main.out
vendored
Normal file
|
@ -0,0 +1 @@
|
||||||
|
esm
|
3
cli/tests/testdata/npm/dual_cjs_esm/main.ts
vendored
Normal file
3
cli/tests/testdata/npm/dual_cjs_esm/main.ts
vendored
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
import { getKind } from "npm:@denotest/dual-cjs-esm";
|
||||||
|
|
||||||
|
console.log(getKind());
|
3
cli/tests/testdata/npm/registry/@denotest/dual-cjs-esm/1.0.0/index.cjs
vendored
Normal file
3
cli/tests/testdata/npm/registry/@denotest/dual-cjs-esm/1.0.0/index.cjs
vendored
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
exports.getKind = function() {
|
||||||
|
return "cjs";
|
||||||
|
};
|
3
cli/tests/testdata/npm/registry/@denotest/dual-cjs-esm/1.0.0/index.mjs
vendored
Normal file
3
cli/tests/testdata/npm/registry/@denotest/dual-cjs-esm/1.0.0/index.mjs
vendored
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
export function getKind() {
|
||||||
|
return "esm";
|
||||||
|
}
|
7
cli/tests/testdata/npm/registry/@denotest/dual-cjs-esm/1.0.0/package.json
vendored
Normal file
7
cli/tests/testdata/npm/registry/@denotest/dual-cjs-esm/1.0.0/package.json
vendored
Normal file
|
@ -0,0 +1,7 @@
|
||||||
|
{
|
||||||
|
"name": "@denotest/dual-cjs-esm",
|
||||||
|
"version": "1.0.0",
|
||||||
|
"type": "module",
|
||||||
|
"main": "./index.cjs",
|
||||||
|
"module": "./index.mjs"
|
||||||
|
}
|
|
@ -390,8 +390,8 @@
|
||||||
return false;
|
return false;
|
||||||
};
|
};
|
||||||
|
|
||||||
Module._nodeModulePaths = function (from) {
|
Module._nodeModulePaths = function (fromPath) {
|
||||||
return ops.op_require_node_module_paths(from);
|
return ops.op_require_node_module_paths(fromPath);
|
||||||
};
|
};
|
||||||
|
|
||||||
Module._resolveLookupPaths = function (request, parent) {
|
Module._resolveLookupPaths = function (request, parent) {
|
||||||
|
@ -728,7 +728,7 @@
|
||||||
const content = ops.op_require_read_file(filename);
|
const content = ops.op_require_read_file(filename);
|
||||||
|
|
||||||
if (StringPrototypeEndsWith(filename, ".js")) {
|
if (StringPrototypeEndsWith(filename, ".js")) {
|
||||||
const pkg = core.ops.op_require_read_package_scope(filename);
|
const pkg = core.ops.op_require_read_closest_package_json(filename);
|
||||||
if (pkg && pkg.exists && pkg.typ == "module") {
|
if (pkg && pkg.exists && pkg.typ == "module") {
|
||||||
let message = `Trying to import ESM module: ${filename}`;
|
let message = `Trying to import ESM module: ${filename}`;
|
||||||
|
|
||||||
|
|
|
@ -15,5 +15,6 @@ path = "lib.rs"
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
deno_core = { version = "0.148.0", path = "../../core" }
|
deno_core = { version = "0.148.0", path = "../../core" }
|
||||||
|
path-clean = "=0.1.0"
|
||||||
regex = "1"
|
regex = "1"
|
||||||
serde = "1.0.136"
|
serde = "1.0.136"
|
||||||
|
|
|
@ -56,7 +56,7 @@ pub fn err_invalid_package_target(
|
||||||
key: String,
|
key: String,
|
||||||
target: String,
|
target: String,
|
||||||
is_import: bool,
|
is_import: bool,
|
||||||
maybe_base: Option<String>,
|
maybe_referrer: Option<String>,
|
||||||
) -> AnyError {
|
) -> AnyError {
|
||||||
let rel_error = !is_import && !target.is_empty() && !target.starts_with("./");
|
let rel_error = !is_import && !target.is_empty() && !target.starts_with("./");
|
||||||
let mut msg = "[ERR_INVALID_PACKAGE_TARGET]".to_string();
|
let mut msg = "[ERR_INVALID_PACKAGE_TARGET]".to_string();
|
||||||
|
@ -69,7 +69,7 @@ pub fn err_invalid_package_target(
|
||||||
msg = format!("{} Invalid \"{}\" target {} defined for '{}' in the package config {}package.json", msg, ie, target, key, pkg_path)
|
msg = format!("{} Invalid \"{}\" target {} defined for '{}' in the package config {}package.json", msg, ie, target, key, pkg_path)
|
||||||
};
|
};
|
||||||
|
|
||||||
if let Some(base) = maybe_base {
|
if let Some(base) = maybe_referrer {
|
||||||
msg = format!("{} imported from {}", msg, base);
|
msg = format!("{} imported from {}", msg, base);
|
||||||
};
|
};
|
||||||
if rel_error {
|
if rel_error {
|
||||||
|
@ -82,7 +82,7 @@ pub fn err_invalid_package_target(
|
||||||
pub fn err_package_path_not_exported(
|
pub fn err_package_path_not_exported(
|
||||||
pkg_path: String,
|
pkg_path: String,
|
||||||
subpath: String,
|
subpath: String,
|
||||||
maybe_base: Option<String>,
|
maybe_referrer: Option<String>,
|
||||||
) -> AnyError {
|
) -> AnyError {
|
||||||
let mut msg = "[ERR_PACKAGE_PATH_NOT_EXPORTED]".to_string();
|
let mut msg = "[ERR_PACKAGE_PATH_NOT_EXPORTED]".to_string();
|
||||||
|
|
||||||
|
@ -95,8 +95,8 @@ pub fn err_package_path_not_exported(
|
||||||
msg = format!("{} Package subpath \'{}\' is not defined by \"exports\" in {}package.json", msg, subpath, pkg_path);
|
msg = format!("{} Package subpath \'{}\' is not defined by \"exports\" in {}package.json", msg, subpath, pkg_path);
|
||||||
};
|
};
|
||||||
|
|
||||||
if let Some(base) = maybe_base {
|
if let Some(referrer) = maybe_referrer {
|
||||||
msg = format!("{} imported from {}", msg, base);
|
msg = format!("{} imported from {}", msg, referrer);
|
||||||
}
|
}
|
||||||
|
|
||||||
generic_error(msg)
|
generic_error(msg)
|
||||||
|
|
|
@ -12,11 +12,13 @@ use std::path::PathBuf;
|
||||||
use std::rc::Rc;
|
use std::rc::Rc;
|
||||||
|
|
||||||
pub use package_json::PackageJson;
|
pub use package_json::PackageJson;
|
||||||
|
pub use resolution::get_closest_package_json;
|
||||||
pub use resolution::get_package_scope_config;
|
pub use resolution::get_package_scope_config;
|
||||||
pub use resolution::legacy_main_resolve;
|
pub use resolution::legacy_main_resolve;
|
||||||
pub use resolution::package_exports_resolve;
|
pub use resolution::package_exports_resolve;
|
||||||
pub use resolution::package_imports_resolve;
|
pub use resolution::package_imports_resolve;
|
||||||
pub use resolution::package_resolve;
|
pub use resolution::package_resolve;
|
||||||
|
pub use resolution::NodeModuleKind;
|
||||||
pub use resolution::DEFAULT_CONDITIONS;
|
pub use resolution::DEFAULT_CONDITIONS;
|
||||||
|
|
||||||
pub trait NodePermissions {
|
pub trait NodePermissions {
|
||||||
|
@ -77,6 +79,7 @@ pub fn init<P: NodePermissions + 'static>(
|
||||||
op_require_read_file::decl::<P>(),
|
op_require_read_file::decl::<P>(),
|
||||||
op_require_as_file_path::decl(),
|
op_require_as_file_path::decl(),
|
||||||
op_require_resolve_exports::decl(),
|
op_require_resolve_exports::decl(),
|
||||||
|
op_require_read_closest_package_json::decl::<P>(),
|
||||||
op_require_read_package_scope::decl(),
|
op_require_read_package_scope::decl(),
|
||||||
op_require_package_imports_resolve::decl::<P>(),
|
op_require_package_imports_resolve::decl::<P>(),
|
||||||
])
|
])
|
||||||
|
@ -485,17 +488,18 @@ fn op_require_try_self(
|
||||||
return Ok(None);
|
return Ok(None);
|
||||||
}
|
}
|
||||||
|
|
||||||
let base = deno_core::url::Url::from_file_path(PathBuf::from("/")).unwrap();
|
let referrer = deno_core::url::Url::from_file_path(&pkg.path).unwrap();
|
||||||
if let Some(exports) = &pkg.exports {
|
if let Some(exports) = &pkg.exports {
|
||||||
resolution::package_exports_resolve(
|
resolution::package_exports_resolve(
|
||||||
deno_core::url::Url::from_file_path(&pkg.path).unwrap(),
|
&pkg.path,
|
||||||
expansion,
|
expansion,
|
||||||
exports,
|
exports,
|
||||||
&base,
|
&referrer,
|
||||||
|
NodeModuleKind::Cjs,
|
||||||
resolution::REQUIRE_CONDITIONS,
|
resolution::REQUIRE_CONDITIONS,
|
||||||
&*resolver,
|
&*resolver,
|
||||||
)
|
)
|
||||||
.map(|r| Some(r.as_str().to_string()))
|
.map(|r| Some(r.to_string_lossy().to_string()))
|
||||||
} else {
|
} else {
|
||||||
Ok(None)
|
Ok(None)
|
||||||
}
|
}
|
||||||
|
@ -550,21 +554,42 @@ fn op_require_resolve_exports(
|
||||||
)?;
|
)?;
|
||||||
|
|
||||||
if let Some(exports) = &pkg.exports {
|
if let Some(exports) = &pkg.exports {
|
||||||
let base = Url::from_file_path(parent_path).unwrap();
|
let referrer = Url::from_file_path(parent_path).unwrap();
|
||||||
resolution::package_exports_resolve(
|
resolution::package_exports_resolve(
|
||||||
deno_core::url::Url::from_directory_path(pkg_path).unwrap(),
|
&pkg.path,
|
||||||
format!(".{}", expansion),
|
format!(".{}", expansion),
|
||||||
exports,
|
exports,
|
||||||
&base,
|
&referrer,
|
||||||
|
NodeModuleKind::Cjs,
|
||||||
resolution::REQUIRE_CONDITIONS,
|
resolution::REQUIRE_CONDITIONS,
|
||||||
&*resolver,
|
&*resolver,
|
||||||
)
|
)
|
||||||
.map(|r| Some(r.to_file_path().unwrap().to_string_lossy().to_string()))
|
.map(|r| Some(r.to_string_lossy().to_string()))
|
||||||
} else {
|
} else {
|
||||||
Ok(None)
|
Ok(None)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[op]
|
||||||
|
fn op_require_read_closest_package_json<P>(
|
||||||
|
state: &mut OpState,
|
||||||
|
filename: String,
|
||||||
|
) -> Result<PackageJson, AnyError>
|
||||||
|
where
|
||||||
|
P: NodePermissions + 'static,
|
||||||
|
{
|
||||||
|
check_unstable(state);
|
||||||
|
ensure_read_permission::<P>(
|
||||||
|
state,
|
||||||
|
PathBuf::from(&filename).parent().unwrap(),
|
||||||
|
)?;
|
||||||
|
let resolver = state.borrow::<Rc<dyn DenoDirNpmResolver>>().clone();
|
||||||
|
resolution::get_closest_package_json(
|
||||||
|
&Url::from_file_path(filename).unwrap(),
|
||||||
|
&*resolver,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
#[op]
|
#[op]
|
||||||
fn op_require_read_package_scope(
|
fn op_require_read_package_scope(
|
||||||
state: &mut OpState,
|
state: &mut OpState,
|
||||||
|
@ -600,10 +625,11 @@ where
|
||||||
let r = resolution::package_imports_resolve(
|
let r = resolution::package_imports_resolve(
|
||||||
&request,
|
&request,
|
||||||
&referrer,
|
&referrer,
|
||||||
|
NodeModuleKind::Cjs,
|
||||||
resolution::REQUIRE_CONDITIONS,
|
resolution::REQUIRE_CONDITIONS,
|
||||||
&*resolver,
|
&*resolver,
|
||||||
)
|
)
|
||||||
.map(|r| Some(r.as_str().to_string()));
|
.map(|r| Some(Url::from_file_path(r).unwrap().to_string()));
|
||||||
state.put(resolver);
|
state.put(resolver);
|
||||||
r
|
r
|
||||||
} else {
|
} else {
|
||||||
|
|
|
@ -19,6 +19,7 @@ pub struct PackageJson {
|
||||||
pub imports: Option<Map<String, Value>>,
|
pub imports: Option<Map<String, Value>>,
|
||||||
pub bin: Option<Value>,
|
pub bin: Option<Value>,
|
||||||
pub main: Option<String>,
|
pub main: Option<String>,
|
||||||
|
pub module: Option<String>,
|
||||||
pub name: Option<String>,
|
pub name: Option<String>,
|
||||||
pub path: PathBuf,
|
pub path: PathBuf,
|
||||||
pub typ: String,
|
pub typ: String,
|
||||||
|
@ -33,6 +34,7 @@ impl PackageJson {
|
||||||
imports: None,
|
imports: None,
|
||||||
bin: None,
|
bin: None,
|
||||||
main: None,
|
main: None,
|
||||||
|
module: None,
|
||||||
name: None,
|
name: None,
|
||||||
path,
|
path,
|
||||||
typ: "none".to_string(),
|
typ: "none".to_string(),
|
||||||
|
@ -66,6 +68,7 @@ impl PackageJson {
|
||||||
|
|
||||||
let imports_val = package_json.get("imports");
|
let imports_val = package_json.get("imports");
|
||||||
let main_val = package_json.get("main");
|
let main_val = package_json.get("main");
|
||||||
|
let module_val = package_json.get("module");
|
||||||
let name_val = package_json.get("name");
|
let name_val = package_json.get("name");
|
||||||
let type_val = package_json.get("type");
|
let type_val = package_json.get("type");
|
||||||
let bin = package_json.get("bin").map(ToOwned::to_owned);
|
let bin = package_json.get("bin").map(ToOwned::to_owned);
|
||||||
|
@ -79,21 +82,12 @@ impl PackageJson {
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
let imports = if let Some(imp) = imports_val {
|
let imports = imports_val
|
||||||
imp.as_object().map(|imp| imp.to_owned())
|
.and_then(|imp| imp.as_object())
|
||||||
} else {
|
.map(|imp| imp.to_owned());
|
||||||
None
|
let main = main_val.and_then(|s| s.as_str()).map(|s| s.to_string());
|
||||||
};
|
let name = name_val.and_then(|s| s.as_str()).map(|s| s.to_string());
|
||||||
let main = if let Some(m) = main_val {
|
let module = module_val.and_then(|s| s.as_str()).map(|s| s.to_string());
|
||||||
m.as_str().map(|m| m.to_string())
|
|
||||||
} else {
|
|
||||||
None
|
|
||||||
};
|
|
||||||
let name = if let Some(n) = name_val {
|
|
||||||
n.as_str().map(|n| n.to_string())
|
|
||||||
} else {
|
|
||||||
None
|
|
||||||
};
|
|
||||||
|
|
||||||
// Ignore unknown types for forwards compatibility
|
// Ignore unknown types for forwards compatibility
|
||||||
let typ = if let Some(t) = type_val {
|
let typ = if let Some(t) = type_val {
|
||||||
|
@ -121,6 +115,7 @@ impl PackageJson {
|
||||||
path,
|
path,
|
||||||
main,
|
main,
|
||||||
name,
|
name,
|
||||||
|
module,
|
||||||
typ,
|
typ,
|
||||||
types,
|
types,
|
||||||
exports,
|
exports,
|
||||||
|
|
|
@ -1,13 +1,16 @@
|
||||||
// Copyright 2018-2022 the Deno authors. All rights reserved. MIT license.
|
// Copyright 2018-2022 the Deno authors. All rights reserved. MIT license.
|
||||||
|
|
||||||
|
use std::path::Path;
|
||||||
use std::path::PathBuf;
|
use std::path::PathBuf;
|
||||||
|
|
||||||
|
use deno_core::anyhow::bail;
|
||||||
use deno_core::error::generic_error;
|
use deno_core::error::generic_error;
|
||||||
use deno_core::error::AnyError;
|
use deno_core::error::AnyError;
|
||||||
use deno_core::serde_json::Map;
|
use deno_core::serde_json::Map;
|
||||||
use deno_core::serde_json::Value;
|
use deno_core::serde_json::Value;
|
||||||
use deno_core::url::Url;
|
use deno_core::url::Url;
|
||||||
use deno_core::ModuleSpecifier;
|
use deno_core::ModuleSpecifier;
|
||||||
|
use path_clean::PathClean;
|
||||||
use regex::Regex;
|
use regex::Regex;
|
||||||
|
|
||||||
use crate::errors;
|
use crate::errors;
|
||||||
|
@ -17,25 +20,29 @@ use crate::DenoDirNpmResolver;
|
||||||
pub static DEFAULT_CONDITIONS: &[&str] = &["deno", "node", "import"];
|
pub static DEFAULT_CONDITIONS: &[&str] = &["deno", "node", "import"];
|
||||||
pub static REQUIRE_CONDITIONS: &[&str] = &["require", "node"];
|
pub static REQUIRE_CONDITIONS: &[&str] = &["require", "node"];
|
||||||
|
|
||||||
fn to_file_path(url: &ModuleSpecifier) -> PathBuf {
|
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
|
||||||
url
|
pub enum NodeModuleKind {
|
||||||
.to_file_path()
|
Esm,
|
||||||
.unwrap_or_else(|_| panic!("Provided URL was not file:// URL: {}", url))
|
Cjs,
|
||||||
}
|
}
|
||||||
|
|
||||||
fn to_file_path_string(url: &ModuleSpecifier) -> String {
|
fn to_specifier_display_string(url: &ModuleSpecifier) -> String {
|
||||||
to_file_path(url).display().to_string()
|
if let Ok(path) = url.to_file_path() {
|
||||||
|
path.display().to_string()
|
||||||
|
} else {
|
||||||
|
url.to_string()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn throw_import_not_defined(
|
fn throw_import_not_defined(
|
||||||
specifier: &str,
|
specifier: &str,
|
||||||
package_json_url: Option<ModuleSpecifier>,
|
package_json_path: Option<&Path>,
|
||||||
base: &ModuleSpecifier,
|
base: &ModuleSpecifier,
|
||||||
) -> AnyError {
|
) -> AnyError {
|
||||||
errors::err_package_import_not_defined(
|
errors::err_package_import_not_defined(
|
||||||
specifier,
|
specifier,
|
||||||
package_json_url.map(|u| to_file_path_string(&u.join(".").unwrap())),
|
package_json_path.map(|p| p.parent().unwrap().display().to_string()),
|
||||||
&to_file_path_string(base),
|
&to_specifier_display_string(base),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -84,30 +91,32 @@ fn pattern_key_compare(a: &str, b: &str) -> i32 {
|
||||||
pub fn package_imports_resolve(
|
pub fn package_imports_resolve(
|
||||||
name: &str,
|
name: &str,
|
||||||
referrer: &ModuleSpecifier,
|
referrer: &ModuleSpecifier,
|
||||||
|
referrer_kind: NodeModuleKind,
|
||||||
conditions: &[&str],
|
conditions: &[&str],
|
||||||
npm_resolver: &dyn DenoDirNpmResolver,
|
npm_resolver: &dyn DenoDirNpmResolver,
|
||||||
) -> Result<ModuleSpecifier, AnyError> {
|
) -> Result<PathBuf, AnyError> {
|
||||||
if name == "#" || name.starts_with("#/") || name.ends_with('/') {
|
if name == "#" || name.starts_with("#/") || name.ends_with('/') {
|
||||||
let reason = "is not a valid internal imports specifier name";
|
let reason = "is not a valid internal imports specifier name";
|
||||||
return Err(errors::err_invalid_module_specifier(
|
return Err(errors::err_invalid_module_specifier(
|
||||||
name,
|
name,
|
||||||
reason,
|
reason,
|
||||||
Some(to_file_path_string(referrer)),
|
Some(to_specifier_display_string(referrer)),
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
let package_config = get_package_scope_config(referrer, npm_resolver)?;
|
let package_config = get_package_scope_config(referrer, npm_resolver)?;
|
||||||
let mut package_json_url = None;
|
let mut package_json_path = None;
|
||||||
if package_config.exists {
|
if package_config.exists {
|
||||||
package_json_url = Some(Url::from_file_path(package_config.path).unwrap());
|
package_json_path = Some(package_config.path.clone());
|
||||||
if let Some(imports) = &package_config.imports {
|
if let Some(imports) = &package_config.imports {
|
||||||
if imports.contains_key(name) && !name.contains('*') {
|
if imports.contains_key(name) && !name.contains('*') {
|
||||||
let maybe_resolved = resolve_package_target(
|
let maybe_resolved = resolve_package_target(
|
||||||
package_json_url.clone().unwrap(),
|
package_json_path.as_ref().unwrap(),
|
||||||
imports.get(name).unwrap().to_owned(),
|
imports.get(name).unwrap().to_owned(),
|
||||||
"".to_string(),
|
"".to_string(),
|
||||||
name.to_string(),
|
name.to_string(),
|
||||||
referrer,
|
referrer,
|
||||||
|
referrer_kind,
|
||||||
false,
|
false,
|
||||||
true,
|
true,
|
||||||
conditions,
|
conditions,
|
||||||
|
@ -143,11 +152,12 @@ pub fn package_imports_resolve(
|
||||||
if !best_match.is_empty() {
|
if !best_match.is_empty() {
|
||||||
let target = imports.get(best_match).unwrap().to_owned();
|
let target = imports.get(best_match).unwrap().to_owned();
|
||||||
let maybe_resolved = resolve_package_target(
|
let maybe_resolved = resolve_package_target(
|
||||||
package_json_url.clone().unwrap(),
|
package_json_path.as_ref().unwrap(),
|
||||||
target,
|
target,
|
||||||
best_match_subpath.unwrap(),
|
best_match_subpath.unwrap(),
|
||||||
best_match.to_string(),
|
best_match.to_string(),
|
||||||
referrer,
|
referrer,
|
||||||
|
referrer_kind,
|
||||||
true,
|
true,
|
||||||
true,
|
true,
|
||||||
conditions,
|
conditions,
|
||||||
|
@ -161,41 +171,45 @@ pub fn package_imports_resolve(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Err(throw_import_not_defined(name, package_json_url, referrer))
|
Err(throw_import_not_defined(
|
||||||
|
name,
|
||||||
|
package_json_path.as_deref(),
|
||||||
|
referrer,
|
||||||
|
))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn throw_invalid_package_target(
|
fn throw_invalid_package_target(
|
||||||
subpath: String,
|
subpath: String,
|
||||||
target: String,
|
target: String,
|
||||||
package_json_url: &ModuleSpecifier,
|
package_json_path: &Path,
|
||||||
internal: bool,
|
internal: bool,
|
||||||
base: &ModuleSpecifier,
|
referrer: &ModuleSpecifier,
|
||||||
) -> AnyError {
|
) -> AnyError {
|
||||||
errors::err_invalid_package_target(
|
errors::err_invalid_package_target(
|
||||||
to_file_path_string(&package_json_url.join(".").unwrap()),
|
package_json_path.parent().unwrap().display().to_string(),
|
||||||
subpath,
|
subpath,
|
||||||
target,
|
target,
|
||||||
internal,
|
internal,
|
||||||
Some(base.as_str().to_string()),
|
Some(referrer.as_str().to_string()),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn throw_invalid_subpath(
|
fn throw_invalid_subpath(
|
||||||
subpath: String,
|
subpath: String,
|
||||||
package_json_url: &ModuleSpecifier,
|
package_json_path: &Path,
|
||||||
internal: bool,
|
internal: bool,
|
||||||
base: &ModuleSpecifier,
|
referrer: &ModuleSpecifier,
|
||||||
) -> AnyError {
|
) -> AnyError {
|
||||||
let ie = if internal { "imports" } else { "exports" };
|
let ie = if internal { "imports" } else { "exports" };
|
||||||
let reason = format!(
|
let reason = format!(
|
||||||
"request is not a valid subpath for the \"{}\" resolution of {}",
|
"request is not a valid subpath for the \"{}\" resolution of {}",
|
||||||
ie,
|
ie,
|
||||||
to_file_path_string(package_json_url)
|
package_json_path.display(),
|
||||||
);
|
);
|
||||||
errors::err_invalid_module_specifier(
|
errors::err_invalid_module_specifier(
|
||||||
&subpath,
|
&subpath,
|
||||||
&reason,
|
&reason,
|
||||||
Some(to_file_path_string(base)),
|
Some(to_specifier_display_string(referrer)),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -204,20 +218,21 @@ fn resolve_package_target_string(
|
||||||
target: String,
|
target: String,
|
||||||
subpath: String,
|
subpath: String,
|
||||||
match_: String,
|
match_: String,
|
||||||
package_json_url: ModuleSpecifier,
|
package_json_path: &Path,
|
||||||
base: &ModuleSpecifier,
|
referrer: &ModuleSpecifier,
|
||||||
|
referrer_kind: NodeModuleKind,
|
||||||
pattern: bool,
|
pattern: bool,
|
||||||
internal: bool,
|
internal: bool,
|
||||||
conditions: &[&str],
|
conditions: &[&str],
|
||||||
npm_resolver: &dyn DenoDirNpmResolver,
|
npm_resolver: &dyn DenoDirNpmResolver,
|
||||||
) -> Result<ModuleSpecifier, AnyError> {
|
) -> Result<PathBuf, AnyError> {
|
||||||
if !subpath.is_empty() && !pattern && !target.ends_with('/') {
|
if !subpath.is_empty() && !pattern && !target.ends_with('/') {
|
||||||
return Err(throw_invalid_package_target(
|
return Err(throw_invalid_package_target(
|
||||||
match_,
|
match_,
|
||||||
target,
|
target,
|
||||||
&package_json_url,
|
package_json_path,
|
||||||
internal,
|
internal,
|
||||||
base,
|
referrer,
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
let invalid_segment_re =
|
let invalid_segment_re =
|
||||||
|
@ -234,9 +249,12 @@ fn resolve_package_target_string(
|
||||||
} else {
|
} else {
|
||||||
format!("{}{}", target, subpath)
|
format!("{}{}", target, subpath)
|
||||||
};
|
};
|
||||||
|
let package_json_url =
|
||||||
|
ModuleSpecifier::from_file_path(package_json_path).unwrap();
|
||||||
return package_resolve(
|
return package_resolve(
|
||||||
&export_target,
|
&export_target,
|
||||||
&package_json_url,
|
&package_json_url,
|
||||||
|
referrer_kind,
|
||||||
conditions,
|
conditions,
|
||||||
npm_resolver,
|
npm_resolver,
|
||||||
);
|
);
|
||||||
|
@ -245,35 +263,33 @@ fn resolve_package_target_string(
|
||||||
return Err(throw_invalid_package_target(
|
return Err(throw_invalid_package_target(
|
||||||
match_,
|
match_,
|
||||||
target,
|
target,
|
||||||
&package_json_url,
|
package_json_path,
|
||||||
internal,
|
internal,
|
||||||
base,
|
referrer,
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
if invalid_segment_re.is_match(&target[2..]) {
|
if invalid_segment_re.is_match(&target[2..]) {
|
||||||
return Err(throw_invalid_package_target(
|
return Err(throw_invalid_package_target(
|
||||||
match_,
|
match_,
|
||||||
target,
|
target,
|
||||||
&package_json_url,
|
package_json_path,
|
||||||
internal,
|
internal,
|
||||||
base,
|
referrer,
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
let resolved = package_json_url.join(&target)?;
|
let package_path = package_json_path.parent().unwrap();
|
||||||
let resolved_path = resolved.path();
|
let resolved_path = package_path.join(&target).clean();
|
||||||
let package_url = package_json_url.join(".").unwrap();
|
|
||||||
let package_path = package_url.path();
|
|
||||||
if !resolved_path.starts_with(package_path) {
|
if !resolved_path.starts_with(package_path) {
|
||||||
return Err(throw_invalid_package_target(
|
return Err(throw_invalid_package_target(
|
||||||
match_,
|
match_,
|
||||||
target,
|
target,
|
||||||
&package_json_url,
|
package_json_path,
|
||||||
internal,
|
internal,
|
||||||
base,
|
referrer,
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
if subpath.is_empty() {
|
if subpath.is_empty() {
|
||||||
return Ok(resolved);
|
return Ok(resolved_path);
|
||||||
}
|
}
|
||||||
if invalid_segment_re.is_match(&subpath) {
|
if invalid_segment_re.is_match(&subpath) {
|
||||||
let request = if pattern {
|
let request = if pattern {
|
||||||
|
@ -283,39 +299,43 @@ fn resolve_package_target_string(
|
||||||
};
|
};
|
||||||
return Err(throw_invalid_subpath(
|
return Err(throw_invalid_subpath(
|
||||||
request,
|
request,
|
||||||
&package_json_url,
|
package_json_path,
|
||||||
internal,
|
internal,
|
||||||
base,
|
referrer,
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
if pattern {
|
if pattern {
|
||||||
|
let resolved_path_str = resolved_path.to_string_lossy();
|
||||||
let replaced = pattern_re
|
let replaced = pattern_re
|
||||||
.replace(resolved.as_str(), |_caps: ®ex::Captures| subpath.clone());
|
.replace(&resolved_path_str, |_caps: ®ex::Captures| {
|
||||||
let url = Url::parse(&replaced)?;
|
subpath.clone()
|
||||||
return Ok(url);
|
});
|
||||||
|
return Ok(PathBuf::from(replaced.to_string()));
|
||||||
}
|
}
|
||||||
Ok(resolved.join(&subpath)?)
|
Ok(resolved_path.join(&subpath).clean())
|
||||||
}
|
}
|
||||||
|
|
||||||
#[allow(clippy::too_many_arguments)]
|
#[allow(clippy::too_many_arguments)]
|
||||||
fn resolve_package_target(
|
fn resolve_package_target(
|
||||||
package_json_url: ModuleSpecifier,
|
package_json_path: &Path,
|
||||||
target: Value,
|
target: Value,
|
||||||
subpath: String,
|
subpath: String,
|
||||||
package_subpath: String,
|
package_subpath: String,
|
||||||
base: &ModuleSpecifier,
|
referrer: &ModuleSpecifier,
|
||||||
|
referrer_kind: NodeModuleKind,
|
||||||
pattern: bool,
|
pattern: bool,
|
||||||
internal: bool,
|
internal: bool,
|
||||||
conditions: &[&str],
|
conditions: &[&str],
|
||||||
npm_resolver: &dyn DenoDirNpmResolver,
|
npm_resolver: &dyn DenoDirNpmResolver,
|
||||||
) -> Result<Option<ModuleSpecifier>, AnyError> {
|
) -> Result<Option<PathBuf>, AnyError> {
|
||||||
if let Some(target) = target.as_str() {
|
if let Some(target) = target.as_str() {
|
||||||
return Ok(Some(resolve_package_target_string(
|
return Ok(Some(resolve_package_target_string(
|
||||||
target.to_string(),
|
target.to_string(),
|
||||||
subpath,
|
subpath,
|
||||||
package_subpath,
|
package_subpath,
|
||||||
package_json_url,
|
package_json_path,
|
||||||
base,
|
referrer,
|
||||||
|
referrer_kind,
|
||||||
pattern,
|
pattern,
|
||||||
internal,
|
internal,
|
||||||
conditions,
|
conditions,
|
||||||
|
@ -329,11 +349,12 @@ fn resolve_package_target(
|
||||||
let mut last_error = None;
|
let mut last_error = None;
|
||||||
for target_item in target_arr {
|
for target_item in target_arr {
|
||||||
let resolved_result = resolve_package_target(
|
let resolved_result = resolve_package_target(
|
||||||
package_json_url.clone(),
|
package_json_path,
|
||||||
target_item.to_owned(),
|
target_item.to_owned(),
|
||||||
subpath.clone(),
|
subpath.clone(),
|
||||||
package_subpath.clone(),
|
package_subpath.clone(),
|
||||||
base,
|
referrer,
|
||||||
|
referrer_kind,
|
||||||
pattern,
|
pattern,
|
||||||
internal,
|
internal,
|
||||||
conditions,
|
conditions,
|
||||||
|
@ -371,11 +392,12 @@ fn resolve_package_target(
|
||||||
if key == "default" || conditions.contains(&key.as_str()) {
|
if key == "default" || conditions.contains(&key.as_str()) {
|
||||||
let condition_target = target_obj.get(key).unwrap().to_owned();
|
let condition_target = target_obj.get(key).unwrap().to_owned();
|
||||||
let resolved = resolve_package_target(
|
let resolved = resolve_package_target(
|
||||||
package_json_url.clone(),
|
package_json_path,
|
||||||
condition_target,
|
condition_target,
|
||||||
subpath.clone(),
|
subpath.clone(),
|
||||||
package_subpath.clone(),
|
package_subpath.clone(),
|
||||||
base,
|
referrer,
|
||||||
|
referrer_kind,
|
||||||
pattern,
|
pattern,
|
||||||
internal,
|
internal,
|
||||||
conditions,
|
conditions,
|
||||||
|
@ -394,43 +416,45 @@ fn resolve_package_target(
|
||||||
Err(throw_invalid_package_target(
|
Err(throw_invalid_package_target(
|
||||||
package_subpath,
|
package_subpath,
|
||||||
target.to_string(),
|
target.to_string(),
|
||||||
&package_json_url,
|
package_json_path,
|
||||||
internal,
|
internal,
|
||||||
base,
|
referrer,
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn throw_exports_not_found(
|
fn throw_exports_not_found(
|
||||||
subpath: String,
|
subpath: String,
|
||||||
package_json_url: &ModuleSpecifier,
|
package_json_path: &Path,
|
||||||
base: &ModuleSpecifier,
|
referrer: &ModuleSpecifier,
|
||||||
) -> AnyError {
|
) -> AnyError {
|
||||||
errors::err_package_path_not_exported(
|
errors::err_package_path_not_exported(
|
||||||
to_file_path_string(&package_json_url.join(".").unwrap()),
|
package_json_path.parent().unwrap().display().to_string(),
|
||||||
subpath,
|
subpath,
|
||||||
Some(to_file_path_string(base)),
|
Some(to_specifier_display_string(referrer)),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn package_exports_resolve(
|
pub fn package_exports_resolve(
|
||||||
package_json_url: ModuleSpecifier,
|
package_json_path: &Path,
|
||||||
package_subpath: String,
|
package_subpath: String,
|
||||||
package_exports: &Map<String, Value>,
|
package_exports: &Map<String, Value>,
|
||||||
base: &ModuleSpecifier,
|
referrer: &ModuleSpecifier,
|
||||||
|
referrer_kind: NodeModuleKind,
|
||||||
conditions: &[&str],
|
conditions: &[&str],
|
||||||
npm_resolver: &dyn DenoDirNpmResolver,
|
npm_resolver: &dyn DenoDirNpmResolver,
|
||||||
) -> Result<ModuleSpecifier, AnyError> {
|
) -> Result<PathBuf, AnyError> {
|
||||||
if package_exports.contains_key(&package_subpath)
|
if package_exports.contains_key(&package_subpath)
|
||||||
&& package_subpath.find('*').is_none()
|
&& package_subpath.find('*').is_none()
|
||||||
&& !package_subpath.ends_with('/')
|
&& !package_subpath.ends_with('/')
|
||||||
{
|
{
|
||||||
let target = package_exports.get(&package_subpath).unwrap().to_owned();
|
let target = package_exports.get(&package_subpath).unwrap().to_owned();
|
||||||
let resolved = resolve_package_target(
|
let resolved = resolve_package_target(
|
||||||
package_json_url.clone(),
|
package_json_path,
|
||||||
target,
|
target,
|
||||||
"".to_string(),
|
"".to_string(),
|
||||||
package_subpath.to_string(),
|
package_subpath.to_string(),
|
||||||
base,
|
referrer,
|
||||||
|
referrer_kind,
|
||||||
false,
|
false,
|
||||||
false,
|
false,
|
||||||
conditions,
|
conditions,
|
||||||
|
@ -439,8 +463,8 @@ pub fn package_exports_resolve(
|
||||||
if resolved.is_none() {
|
if resolved.is_none() {
|
||||||
return Err(throw_exports_not_found(
|
return Err(throw_exports_not_found(
|
||||||
package_subpath,
|
package_subpath,
|
||||||
&package_json_url,
|
package_json_path,
|
||||||
base,
|
referrer,
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
return Ok(resolved.unwrap());
|
return Ok(resolved.unwrap());
|
||||||
|
@ -483,11 +507,12 @@ pub fn package_exports_resolve(
|
||||||
if !best_match.is_empty() {
|
if !best_match.is_empty() {
|
||||||
let target = package_exports.get(best_match).unwrap().to_owned();
|
let target = package_exports.get(best_match).unwrap().to_owned();
|
||||||
let maybe_resolved = resolve_package_target(
|
let maybe_resolved = resolve_package_target(
|
||||||
package_json_url.clone(),
|
package_json_path,
|
||||||
target,
|
target,
|
||||||
best_match_subpath.unwrap(),
|
best_match_subpath.unwrap(),
|
||||||
best_match.to_string(),
|
best_match.to_string(),
|
||||||
base,
|
referrer,
|
||||||
|
referrer_kind,
|
||||||
true,
|
true,
|
||||||
false,
|
false,
|
||||||
conditions,
|
conditions,
|
||||||
|
@ -498,22 +523,22 @@ pub fn package_exports_resolve(
|
||||||
} else {
|
} else {
|
||||||
return Err(throw_exports_not_found(
|
return Err(throw_exports_not_found(
|
||||||
package_subpath,
|
package_subpath,
|
||||||
&package_json_url,
|
package_json_path,
|
||||||
base,
|
referrer,
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Err(throw_exports_not_found(
|
Err(throw_exports_not_found(
|
||||||
package_subpath,
|
package_subpath,
|
||||||
&package_json_url,
|
package_json_path,
|
||||||
base,
|
referrer,
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn parse_package_name(
|
fn parse_package_name(
|
||||||
specifier: &str,
|
specifier: &str,
|
||||||
base: &ModuleSpecifier,
|
referrer: &ModuleSpecifier,
|
||||||
) -> Result<(String, String, bool), AnyError> {
|
) -> Result<(String, String, bool), AnyError> {
|
||||||
let mut separator_index = specifier.find('/');
|
let mut separator_index = specifier.find('/');
|
||||||
let mut valid_package_name = true;
|
let mut valid_package_name = true;
|
||||||
|
@ -547,7 +572,7 @@ fn parse_package_name(
|
||||||
return Err(errors::err_invalid_module_specifier(
|
return Err(errors::err_invalid_module_specifier(
|
||||||
specifier,
|
specifier,
|
||||||
"is not a valid package name",
|
"is not a valid package name",
|
||||||
Some(to_file_path_string(base)),
|
Some(to_specifier_display_string(referrer)),
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -563,27 +588,28 @@ fn parse_package_name(
|
||||||
pub fn package_resolve(
|
pub fn package_resolve(
|
||||||
specifier: &str,
|
specifier: &str,
|
||||||
referrer: &ModuleSpecifier,
|
referrer: &ModuleSpecifier,
|
||||||
|
referrer_kind: NodeModuleKind,
|
||||||
conditions: &[&str],
|
conditions: &[&str],
|
||||||
npm_resolver: &dyn DenoDirNpmResolver,
|
npm_resolver: &dyn DenoDirNpmResolver,
|
||||||
) -> Result<ModuleSpecifier, AnyError> {
|
) -> Result<PathBuf, AnyError> {
|
||||||
let (package_name, package_subpath, _is_scoped) =
|
let (package_name, package_subpath, _is_scoped) =
|
||||||
parse_package_name(specifier, referrer)?;
|
parse_package_name(specifier, referrer)?;
|
||||||
|
|
||||||
// ResolveSelf
|
// ResolveSelf
|
||||||
let package_config = get_package_scope_config(referrer, npm_resolver)?;
|
let package_config = get_package_scope_config(referrer, npm_resolver)?;
|
||||||
if package_config.exists {
|
if package_config.exists
|
||||||
let package_json_url = Url::from_file_path(&package_config.path).unwrap();
|
&& package_config.name.as_ref() == Some(&package_name)
|
||||||
if package_config.name.as_ref() == Some(&package_name) {
|
{
|
||||||
if let Some(exports) = &package_config.exports {
|
if let Some(exports) = &package_config.exports {
|
||||||
return package_exports_resolve(
|
return package_exports_resolve(
|
||||||
package_json_url,
|
&package_config.path,
|
||||||
package_subpath,
|
package_subpath,
|
||||||
exports,
|
exports,
|
||||||
referrer,
|
referrer,
|
||||||
conditions,
|
referrer_kind,
|
||||||
npm_resolver,
|
conditions,
|
||||||
);
|
npm_resolver,
|
||||||
}
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -592,8 +618,6 @@ pub fn package_resolve(
|
||||||
&referrer.to_file_path().unwrap(),
|
&referrer.to_file_path().unwrap(),
|
||||||
)?;
|
)?;
|
||||||
let package_json_path = package_dir_path.join("package.json");
|
let package_json_path = package_dir_path.join("package.json");
|
||||||
let package_json_url =
|
|
||||||
ModuleSpecifier::from_file_path(&package_json_path).unwrap();
|
|
||||||
|
|
||||||
// todo: error with this instead when can't find package
|
// todo: error with this instead when can't find package
|
||||||
// Err(errors::err_module_not_found(
|
// Err(errors::err_module_not_found(
|
||||||
|
@ -612,21 +636,20 @@ pub fn package_resolve(
|
||||||
let package_json = PackageJson::load(npm_resolver, package_json_path)?;
|
let package_json = PackageJson::load(npm_resolver, package_json_path)?;
|
||||||
if let Some(exports) = &package_json.exports {
|
if let Some(exports) = &package_json.exports {
|
||||||
return package_exports_resolve(
|
return package_exports_resolve(
|
||||||
package_json_url,
|
&package_json.path,
|
||||||
package_subpath,
|
package_subpath,
|
||||||
exports,
|
exports,
|
||||||
referrer,
|
referrer,
|
||||||
|
referrer_kind,
|
||||||
conditions,
|
conditions,
|
||||||
npm_resolver,
|
npm_resolver,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
if package_subpath == "." {
|
if package_subpath == "." {
|
||||||
return legacy_main_resolve(&package_json_url, &package_json, referrer);
|
return legacy_main_resolve(&package_json, referrer_kind);
|
||||||
}
|
}
|
||||||
|
|
||||||
package_json_url
|
Ok(package_json.path.parent().unwrap().join(&package_subpath))
|
||||||
.join(&package_subpath)
|
|
||||||
.map_err(AnyError::from)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_package_scope_config(
|
pub fn get_package_scope_config(
|
||||||
|
@ -635,12 +658,43 @@ pub fn get_package_scope_config(
|
||||||
) -> Result<PackageJson, AnyError> {
|
) -> Result<PackageJson, AnyError> {
|
||||||
let root_folder = npm_resolver
|
let root_folder = npm_resolver
|
||||||
.resolve_package_folder_from_path(&referrer.to_file_path().unwrap())?;
|
.resolve_package_folder_from_path(&referrer.to_file_path().unwrap())?;
|
||||||
let package_json_path = root_folder.join("./package.json");
|
let package_json_path = root_folder.join("package.json");
|
||||||
PackageJson::load(npm_resolver, package_json_path)
|
PackageJson::load(npm_resolver, package_json_path)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn file_exists(path_url: &ModuleSpecifier) -> bool {
|
pub fn get_closest_package_json(
|
||||||
if let Ok(stats) = std::fs::metadata(to_file_path(path_url)) {
|
url: &ModuleSpecifier,
|
||||||
|
npm_resolver: &dyn DenoDirNpmResolver,
|
||||||
|
) -> Result<PackageJson, AnyError> {
|
||||||
|
let package_json_path = get_closest_package_json_path(url, npm_resolver)?;
|
||||||
|
PackageJson::load(npm_resolver, package_json_path)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_closest_package_json_path(
|
||||||
|
url: &ModuleSpecifier,
|
||||||
|
npm_resolver: &dyn DenoDirNpmResolver,
|
||||||
|
) -> Result<PathBuf, AnyError> {
|
||||||
|
let file_path = url.to_file_path().unwrap();
|
||||||
|
let mut current_dir = file_path.parent().unwrap();
|
||||||
|
let package_json_path = current_dir.join("package.json");
|
||||||
|
if package_json_path.exists() {
|
||||||
|
return Ok(package_json_path);
|
||||||
|
}
|
||||||
|
let root_pkg_folder = npm_resolver
|
||||||
|
.resolve_package_folder_from_path(&url.to_file_path().unwrap())?;
|
||||||
|
while current_dir.starts_with(&root_pkg_folder) {
|
||||||
|
current_dir = current_dir.parent().unwrap();
|
||||||
|
let package_json_path = current_dir.join("package.json");
|
||||||
|
if package_json_path.exists() {
|
||||||
|
return Ok(package_json_path);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bail!("did not find package.json in {}", root_pkg_folder.display())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn file_exists(path: &Path) -> bool {
|
||||||
|
if let Ok(stats) = std::fs::metadata(path) {
|
||||||
stats.is_file()
|
stats.is_file()
|
||||||
} else {
|
} else {
|
||||||
false
|
false
|
||||||
|
@ -648,28 +702,56 @@ fn file_exists(path_url: &ModuleSpecifier) -> bool {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn legacy_main_resolve(
|
pub fn legacy_main_resolve(
|
||||||
package_json_url: &ModuleSpecifier,
|
|
||||||
package_json: &PackageJson,
|
package_json: &PackageJson,
|
||||||
_base: &ModuleSpecifier,
|
referrer_kind: NodeModuleKind,
|
||||||
) -> Result<ModuleSpecifier, AnyError> {
|
) -> Result<PathBuf, AnyError> {
|
||||||
|
let maybe_main =
|
||||||
|
if referrer_kind == NodeModuleKind::Esm && package_json.typ == "module" {
|
||||||
|
&package_json.module
|
||||||
|
} else {
|
||||||
|
&package_json.main
|
||||||
|
};
|
||||||
let mut guess;
|
let mut guess;
|
||||||
|
|
||||||
if let Some(main) = &package_json.main {
|
if let Some(main) = maybe_main {
|
||||||
guess = package_json_url.join(&format!("./{}", main))?;
|
guess = package_json.path.parent().unwrap().join(main).clean();
|
||||||
if file_exists(&guess) {
|
if file_exists(&guess) {
|
||||||
return Ok(guess);
|
return Ok(guess);
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut found = false;
|
let mut found = false;
|
||||||
for ext in [
|
// todo(dsherret): investigate exactly how node handles this
|
||||||
".js",
|
let endings = match referrer_kind {
|
||||||
".json",
|
NodeModuleKind::Cjs => vec![
|
||||||
".node",
|
".js",
|
||||||
"/index.js",
|
".cjs",
|
||||||
"/index.json",
|
".json",
|
||||||
"/index.node",
|
".node",
|
||||||
] {
|
"/index.js",
|
||||||
guess = package_json_url.join(&format!("./{}{}", main, ext))?;
|
"/index.cjs",
|
||||||
|
"/index.json",
|
||||||
|
"/index.node",
|
||||||
|
],
|
||||||
|
NodeModuleKind::Esm => vec![
|
||||||
|
".js",
|
||||||
|
".mjs",
|
||||||
|
".json",
|
||||||
|
".node",
|
||||||
|
"/index.js",
|
||||||
|
"/index.mjs",
|
||||||
|
".cjs",
|
||||||
|
"/index.cjs",
|
||||||
|
"/index.json",
|
||||||
|
"/index.node",
|
||||||
|
],
|
||||||
|
};
|
||||||
|
for ending in endings {
|
||||||
|
guess = package_json
|
||||||
|
.path
|
||||||
|
.parent()
|
||||||
|
.unwrap()
|
||||||
|
.join(&format!("{}{}", main, ending))
|
||||||
|
.clean();
|
||||||
if file_exists(&guess) {
|
if file_exists(&guess) {
|
||||||
found = true;
|
found = true;
|
||||||
break;
|
break;
|
||||||
|
@ -682,8 +764,25 @@ pub fn legacy_main_resolve(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
for p in ["./index.js", "./index.json", "./index.node"] {
|
let index_file_names = match referrer_kind {
|
||||||
guess = package_json_url.join(p)?;
|
NodeModuleKind::Cjs => {
|
||||||
|
vec!["index.js", "index.cjs", "index.json", "index.node"]
|
||||||
|
}
|
||||||
|
NodeModuleKind::Esm => vec![
|
||||||
|
"index.js",
|
||||||
|
"index.mjs",
|
||||||
|
"index.cjs",
|
||||||
|
"index.json",
|
||||||
|
"index.node",
|
||||||
|
],
|
||||||
|
};
|
||||||
|
for index_file_name in index_file_names {
|
||||||
|
guess = package_json
|
||||||
|
.path
|
||||||
|
.parent()
|
||||||
|
.unwrap()
|
||||||
|
.join(index_file_name)
|
||||||
|
.clean();
|
||||||
if file_exists(&guess) {
|
if file_exists(&guess) {
|
||||||
// TODO(bartlomieju): emitLegacyIndexDeprecation()
|
// TODO(bartlomieju): emitLegacyIndexDeprecation()
|
||||||
return Ok(guess);
|
return Ok(guess);
|
||||||
|
|
Loading…
Reference in a new issue