summaryrefslogtreecommitdiff
path: root/cli/resolver.rs
diff options
context:
space:
mode:
authorDavid Sherret <dsherret@users.noreply.github.com>2024-11-13 10:10:09 -0500
committerGitHub <noreply@github.com>2024-11-13 15:10:09 +0000
commitf091d1ad69b4e5217ae3272b641171781a372c4f (patch)
tree4ef4f90ec8a6b5c977efb187449f8c59c45de5e1 /cli/resolver.rs
parent6a4c6d83bacf5f03628a494778a30bce970f7cbc (diff)
feat(node): stabilize detecting if CJS via `"type": "commonjs"` in a package.json (#26439)
This will respect `"type": "commonjs"` in a package.json to determine if `.js`/`.jsx`/`.ts`/.tsx` files are CJS or ESM. If the file is found to be ESM it will be loaded as ESM though.
Diffstat (limited to 'cli/resolver.rs')
-rw-r--r--cli/resolver.rs271
1 files changed, 167 insertions, 104 deletions
diff --git a/cli/resolver.rs b/cli/resolver.rs
index 710b97509..786e5d0db 100644
--- a/cli/resolver.rs
+++ b/cli/resolver.rs
@@ -4,7 +4,6 @@ use async_trait::async_trait;
use dashmap::DashMap;
use dashmap::DashSet;
use deno_ast::MediaType;
-use deno_ast::ModuleKind;
use deno_config::workspace::MappedResolution;
use deno_config::workspace::MappedResolutionDiagnostic;
use deno_config::workspace::MappedResolutionError;
@@ -17,9 +16,7 @@ use deno_core::ModuleSourceCode;
use deno_core::ModuleSpecifier;
use deno_graph::source::ResolutionMode;
use deno_graph::source::ResolveError;
-use deno_graph::source::Resolver;
use deno_graph::source::UnknownBuiltInNodeModuleError;
-use deno_graph::source::DEFAULT_JSX_IMPORT_SOURCE_MODULE;
use deno_graph::NpmLoadError;
use deno_graph::NpmResolvePkgReqsResult;
use deno_npm::resolution::NpmResolutionError;
@@ -52,7 +49,6 @@ use std::path::PathBuf;
use std::sync::Arc;
use thiserror::Error;
-use crate::args::JsxImportSourceConfig;
use crate::args::DENO_DISABLE_PEDANTIC_NODE_WARNINGS;
use crate::node::CliNodeCodeTranslator;
use crate::npm::CliNpmResolver;
@@ -108,7 +104,6 @@ impl deno_resolver::fs::DenoResolverFs for CliDenoResolverFs {
#[derive(Debug)]
pub struct CliNodeResolver {
- cjs_tracker: Arc<CjsTracker>,
fs: Arc<dyn deno_fs::FileSystem>,
in_npm_pkg_checker: Arc<dyn InNpmPackageChecker>,
node_resolver: Arc<NodeResolver>,
@@ -117,14 +112,12 @@ pub struct CliNodeResolver {
impl CliNodeResolver {
pub fn new(
- cjs_tracker: Arc<CjsTracker>,
fs: Arc<dyn deno_fs::FileSystem>,
in_npm_pkg_checker: Arc<dyn InNpmPackageChecker>,
node_resolver: Arc<NodeResolver>,
npm_resolver: Arc<dyn CliNpmResolver>,
) -> Self {
Self {
- cjs_tracker,
fs,
in_npm_pkg_checker,
node_resolver,
@@ -140,9 +133,11 @@ impl CliNodeResolver {
&self,
specifier: &str,
referrer: &ModuleSpecifier,
+ referrer_kind: NodeModuleKind,
mode: NodeResolutionMode,
) -> Result<Option<NodeResolution>, AnyError> {
- let resolution_result = self.resolve(specifier, referrer, mode);
+ let resolution_result =
+ self.resolve(specifier, referrer, referrer_kind, mode);
match resolution_result {
Ok(res) => Ok(Some(res)),
Err(err) => {
@@ -213,35 +208,26 @@ impl CliNodeResolver {
&self,
specifier: &str,
referrer: &ModuleSpecifier,
+ referrer_kind: NodeModuleKind,
mode: NodeResolutionMode,
) -> Result<NodeResolution, NodeResolveError> {
- let referrer_kind = if self
- .cjs_tracker
- .is_maybe_cjs(referrer, MediaType::from_specifier(referrer))
- .map_err(|err| NodeResolveErrorKind::PackageResolve(err.into()))?
- {
- NodeModuleKind::Cjs
- } else {
- NodeModuleKind::Esm
- };
-
- let res =
- self
- .node_resolver
- .resolve(specifier, referrer, referrer_kind, mode)?;
- Ok(res)
+ self
+ .node_resolver
+ .resolve(specifier, referrer, referrer_kind, mode)
}
pub fn resolve_req_reference(
&self,
req_ref: &NpmPackageReqReference,
referrer: &ModuleSpecifier,
+ referrer_kind: NodeModuleKind,
mode: NodeResolutionMode,
) -> Result<ModuleSpecifier, AnyError> {
self.resolve_req_with_sub_path(
req_ref.req(),
req_ref.sub_path(),
referrer,
+ referrer_kind,
mode,
)
}
@@ -251,6 +237,7 @@ impl CliNodeResolver {
req: &PackageReq,
sub_path: Option<&str>,
referrer: &ModuleSpecifier,
+ referrer_kind: NodeModuleKind,
mode: NodeResolutionMode,
) -> Result<ModuleSpecifier, AnyError> {
let package_folder = self
@@ -260,6 +247,7 @@ impl CliNodeResolver {
&package_folder,
sub_path,
Some(referrer),
+ referrer_kind,
mode,
);
match resolution_result {
@@ -284,12 +272,14 @@ impl CliNodeResolver {
package_folder: &Path,
sub_path: Option<&str>,
maybe_referrer: Option<&ModuleSpecifier>,
+ referrer_kind: NodeModuleKind,
mode: NodeResolutionMode,
) -> Result<ModuleSpecifier, PackageSubpathResolveError> {
self.node_resolver.resolve_package_subpath_from_deno_module(
package_folder,
sub_path,
maybe_referrer,
+ referrer_kind,
mode,
)
}
@@ -419,10 +409,6 @@ impl NpmModuleLoader {
}
}
-pub struct CjsTrackerOptions {
- pub unstable_detect_cjs: bool,
-}
-
/// Keeps track of what module specifiers were resolved as CJS.
///
/// Modules that are `.js` or `.ts` are only known to be CJS or
@@ -430,22 +416,22 @@ pub struct CjsTrackerOptions {
/// will be "maybe CJS" until they're loaded.
#[derive(Debug)]
pub struct CjsTracker {
- in_npm_pkg_checker: Arc<dyn InNpmPackageChecker>,
- pkg_json_resolver: Arc<PackageJsonResolver>,
- unstable_detect_cjs: bool,
- known: DashMap<ModuleSpecifier, ModuleKind>,
+ is_cjs_resolver: IsCjsResolver,
+ known: DashMap<ModuleSpecifier, NodeModuleKind>,
}
impl CjsTracker {
pub fn new(
in_npm_pkg_checker: Arc<dyn InNpmPackageChecker>,
pkg_json_resolver: Arc<PackageJsonResolver>,
- options: CjsTrackerOptions,
+ options: IsCjsResolverOptions,
) -> Self {
Self {
- in_npm_pkg_checker,
- pkg_json_resolver,
- unstable_detect_cjs: options.unstable_detect_cjs,
+ is_cjs_resolver: IsCjsResolver::new(
+ in_npm_pkg_checker,
+ pkg_json_resolver,
+ options,
+ ),
known: Default::default(),
}
}
@@ -485,47 +471,135 @@ impl CjsTracker {
.get_known_kind_with_is_script(specifier, media_type, is_script)
{
Some(kind) => kind,
- None => self.check_based_on_pkg_json(specifier)?,
+ None => self.is_cjs_resolver.check_based_on_pkg_json(specifier)?,
};
- Ok(kind.is_cjs())
+ Ok(kind == NodeModuleKind::Cjs)
}
pub fn get_known_kind(
&self,
specifier: &ModuleSpecifier,
media_type: MediaType,
- ) -> Option<ModuleKind> {
+ ) -> Option<NodeModuleKind> {
self.get_known_kind_with_is_script(specifier, media_type, None)
}
+ pub fn get_referrer_kind(
+ &self,
+ specifier: &ModuleSpecifier,
+ ) -> NodeModuleKind {
+ if specifier.scheme() != "file" {
+ return NodeModuleKind::Esm;
+ }
+ self
+ .get_known_kind(specifier, MediaType::from_specifier(specifier))
+ .unwrap_or(NodeModuleKind::Esm)
+ }
+
+ fn get_known_kind_with_is_script(
+ &self,
+ specifier: &ModuleSpecifier,
+ media_type: MediaType,
+ is_script: Option<bool>,
+ ) -> Option<NodeModuleKind> {
+ self.is_cjs_resolver.get_known_kind_with_is_script(
+ specifier,
+ media_type,
+ is_script,
+ &self.known,
+ )
+ }
+}
+
+#[derive(Debug)]
+pub struct IsCjsResolverOptions {
+ pub detect_cjs: bool,
+ pub is_node_main: bool,
+}
+
+#[derive(Debug)]
+pub struct IsCjsResolver {
+ in_npm_pkg_checker: Arc<dyn InNpmPackageChecker>,
+ pkg_json_resolver: Arc<PackageJsonResolver>,
+ options: IsCjsResolverOptions,
+}
+
+impl IsCjsResolver {
+ pub fn new(
+ in_npm_pkg_checker: Arc<dyn InNpmPackageChecker>,
+ pkg_json_resolver: Arc<PackageJsonResolver>,
+ options: IsCjsResolverOptions,
+ ) -> Self {
+ Self {
+ in_npm_pkg_checker,
+ pkg_json_resolver,
+ options,
+ }
+ }
+
+ pub fn get_lsp_referrer_kind(
+ &self,
+ specifier: &ModuleSpecifier,
+ is_script: Option<bool>,
+ ) -> NodeModuleKind {
+ if specifier.scheme() != "file" {
+ return NodeModuleKind::Esm;
+ }
+ match MediaType::from_specifier(specifier) {
+ MediaType::Mts | MediaType::Mjs | MediaType::Dmts => NodeModuleKind::Esm,
+ MediaType::Cjs | MediaType::Cts | MediaType::Dcts => NodeModuleKind::Cjs,
+ MediaType::Dts => {
+ // dts files are always determined based on the package.json because
+ // they contain imports/exports even when considered CJS
+ self.check_based_on_pkg_json(specifier).unwrap_or(NodeModuleKind::Esm)
+ }
+ MediaType::Wasm |
+ MediaType::Json => NodeModuleKind::Esm,
+ MediaType::JavaScript
+ | MediaType::Jsx
+ | MediaType::TypeScript
+ | MediaType::Tsx
+ // treat these as unknown
+ | MediaType::Css
+ | MediaType::SourceMap
+ | MediaType::Unknown => {
+ match is_script {
+ Some(true) => self.check_based_on_pkg_json(specifier).unwrap_or(NodeModuleKind::Esm),
+ Some(false) | None => NodeModuleKind::Esm,
+ }
+ }
+ }
+ }
+
fn get_known_kind_with_is_script(
&self,
specifier: &ModuleSpecifier,
media_type: MediaType,
is_script: Option<bool>,
- ) -> Option<ModuleKind> {
+ known_cache: &DashMap<ModuleSpecifier, NodeModuleKind>,
+ ) -> Option<NodeModuleKind> {
if specifier.scheme() != "file" {
- return Some(ModuleKind::Esm);
+ return Some(NodeModuleKind::Esm);
}
match media_type {
- MediaType::Mts | MediaType::Mjs | MediaType::Dmts => Some(ModuleKind::Esm),
- MediaType::Cjs | MediaType::Cts | MediaType::Dcts => Some(ModuleKind::Cjs),
+ MediaType::Mts | MediaType::Mjs | MediaType::Dmts => Some(NodeModuleKind::Esm),
+ MediaType::Cjs | MediaType::Cts | MediaType::Dcts => Some(NodeModuleKind::Cjs),
MediaType::Dts => {
// dts files are always determined based on the package.json because
// they contain imports/exports even when considered CJS
- if let Some(value) = self.known.get(specifier).map(|v| *v) {
+ if let Some(value) = known_cache.get(specifier).map(|v| *v) {
Some(value)
} else {
let value = self.check_based_on_pkg_json(specifier).ok();
if let Some(value) = value {
- self.known.insert(specifier.clone(), value);
+ known_cache.insert(specifier.clone(), value);
}
- Some(value.unwrap_or(ModuleKind::Esm))
+ Some(value.unwrap_or(NodeModuleKind::Esm))
}
}
MediaType::Wasm |
- MediaType::Json => Some(ModuleKind::Esm),
+ MediaType::Json => Some(NodeModuleKind::Esm),
MediaType::JavaScript
| MediaType::Jsx
| MediaType::TypeScript
@@ -534,18 +608,18 @@ impl CjsTracker {
| MediaType::Css
| MediaType::SourceMap
| MediaType::Unknown => {
- if let Some(value) = self.known.get(specifier).map(|v| *v) {
- if value.is_cjs() && is_script == Some(false) {
+ if let Some(value) = known_cache.get(specifier).map(|v| *v) {
+ if value == NodeModuleKind::Cjs && is_script == Some(false) {
// we now know this is actually esm
- self.known.insert(specifier.clone(), ModuleKind::Esm);
- Some(ModuleKind::Esm)
+ known_cache.insert(specifier.clone(), NodeModuleKind::Esm);
+ Some(NodeModuleKind::Esm)
} else {
Some(value)
}
} else if is_script == Some(false) {
// we know this is esm
- self.known.insert(specifier.clone(), ModuleKind::Esm);
- Some(ModuleKind::Esm)
+ known_cache.insert(specifier.clone(), NodeModuleKind::Esm);
+ Some(NodeModuleKind::Esm)
} else {
None
}
@@ -556,27 +630,38 @@ impl CjsTracker {
fn check_based_on_pkg_json(
&self,
specifier: &ModuleSpecifier,
- ) -> Result<ModuleKind, ClosestPkgJsonError> {
+ ) -> Result<NodeModuleKind, ClosestPkgJsonError> {
if self.in_npm_pkg_checker.in_npm_package(specifier) {
if let Some(pkg_json) =
self.pkg_json_resolver.get_closest_package_json(specifier)?
{
let is_file_location_cjs = pkg_json.typ != "module";
- Ok(ModuleKind::from_is_cjs(is_file_location_cjs))
+ Ok(if is_file_location_cjs {
+ NodeModuleKind::Cjs
+ } else {
+ NodeModuleKind::Esm
+ })
} else {
- Ok(ModuleKind::Cjs)
+ Ok(NodeModuleKind::Cjs)
}
- } else if self.unstable_detect_cjs {
+ } else if self.options.detect_cjs || self.options.is_node_main {
if let Some(pkg_json) =
self.pkg_json_resolver.get_closest_package_json(specifier)?
{
- let is_cjs_type = pkg_json.typ == "commonjs";
- Ok(ModuleKind::from_is_cjs(is_cjs_type))
+ let is_cjs_type = pkg_json.typ == "commonjs"
+ || self.options.is_node_main && pkg_json.typ == "none";
+ Ok(if is_cjs_type {
+ NodeModuleKind::Cjs
+ } else {
+ NodeModuleKind::Esm
+ })
+ } else if self.options.is_node_main {
+ Ok(NodeModuleKind::Cjs)
} else {
- Ok(ModuleKind::Esm)
+ Ok(NodeModuleKind::Esm)
}
} else {
- Ok(ModuleKind::Esm)
+ Ok(NodeModuleKind::Esm)
}
}
}
@@ -587,48 +672,33 @@ pub type CliSloppyImportsResolver =
/// A resolver that takes care of resolution, taking into account loaded
/// import map, JSX settings.
#[derive(Debug)]
-pub struct CliGraphResolver {
+pub struct CliResolver {
node_resolver: Option<Arc<CliNodeResolver>>,
npm_resolver: Option<Arc<dyn CliNpmResolver>>,
sloppy_imports_resolver: Option<Arc<CliSloppyImportsResolver>>,
workspace_resolver: Arc<WorkspaceResolver>,
- maybe_default_jsx_import_source: Option<String>,
- maybe_default_jsx_import_source_types: Option<String>,
- maybe_jsx_import_source_module: Option<String>,
maybe_vendor_specifier: Option<ModuleSpecifier>,
found_package_json_dep_flag: AtomicFlag,
bare_node_builtins_enabled: bool,
warned_pkgs: DashSet<PackageReq>,
}
-pub struct CliGraphResolverOptions<'a> {
+pub struct CliResolverOptions<'a> {
pub node_resolver: Option<Arc<CliNodeResolver>>,
pub npm_resolver: Option<Arc<dyn CliNpmResolver>>,
pub sloppy_imports_resolver: Option<Arc<CliSloppyImportsResolver>>,
pub workspace_resolver: Arc<WorkspaceResolver>,
pub bare_node_builtins_enabled: bool,
- pub maybe_jsx_import_source_config: Option<JsxImportSourceConfig>,
pub maybe_vendor_dir: Option<&'a PathBuf>,
}
-impl CliGraphResolver {
- pub fn new(options: CliGraphResolverOptions) -> Self {
+impl CliResolver {
+ pub fn new(options: CliResolverOptions) -> Self {
Self {
node_resolver: options.node_resolver,
npm_resolver: options.npm_resolver,
sloppy_imports_resolver: options.sloppy_imports_resolver,
workspace_resolver: options.workspace_resolver,
- maybe_default_jsx_import_source: options
- .maybe_jsx_import_source_config
- .as_ref()
- .and_then(|c| c.default_specifier.clone()),
- maybe_default_jsx_import_source_types: options
- .maybe_jsx_import_source_config
- .as_ref()
- .and_then(|c| c.default_types_specifier.clone()),
- maybe_jsx_import_source_module: options
- .maybe_jsx_import_source_config
- .map(|c| c.module),
maybe_vendor_specifier: options
.maybe_vendor_dir
.and_then(|v| ModuleSpecifier::from_directory_path(v).ok()),
@@ -638,10 +708,6 @@ impl CliGraphResolver {
}
}
- pub fn as_graph_resolver(&self) -> &dyn Resolver {
- self
- }
-
pub fn create_graph_npm_resolver(&self) -> WorkerCliNpmGraphResolver {
WorkerCliNpmGraphResolver {
npm_resolver: self.npm_resolver.as_ref(),
@@ -649,28 +715,12 @@ impl CliGraphResolver {
bare_node_builtins_enabled: self.bare_node_builtins_enabled,
}
}
-}
-
-impl Resolver for CliGraphResolver {
- fn default_jsx_import_source(&self) -> Option<String> {
- self.maybe_default_jsx_import_source.clone()
- }
-
- fn default_jsx_import_source_types(&self) -> Option<String> {
- self.maybe_default_jsx_import_source_types.clone()
- }
- fn jsx_import_source_module(&self) -> &str {
- self
- .maybe_jsx_import_source_module
- .as_deref()
- .unwrap_or(DEFAULT_JSX_IMPORT_SOURCE_MODULE)
- }
-
- fn resolve(
+ pub fn resolve(
&self,
raw_specifier: &str,
referrer_range: &deno_graph::Range,
+ referrer_kind: NodeModuleKind,
mode: ResolutionMode,
) -> Result<ModuleSpecifier, ResolveError> {
fn to_node_mode(mode: ResolutionMode) -> NodeResolutionMode {
@@ -686,7 +736,7 @@ impl Resolver for CliGraphResolver {
if let Some(node_resolver) = self.node_resolver.as_ref() {
if referrer.scheme() == "file" && node_resolver.in_npm_package(referrer) {
return node_resolver
- .resolve(raw_specifier, referrer, to_node_mode(mode))
+ .resolve(raw_specifier, referrer, referrer_kind, to_node_mode(mode))
.map(|res| res.into_url())
.map_err(|e| ResolveError::Other(e.into()));
}
@@ -759,6 +809,7 @@ impl Resolver for CliGraphResolver {
pkg_json.dir_path(),
sub_path.as_deref(),
Some(referrer),
+ referrer_kind,
to_node_mode(mode),
)
.map_err(|e| ResolveError::Other(e.into())),
@@ -800,6 +851,7 @@ impl Resolver for CliGraphResolver {
pkg_folder,
sub_path.as_deref(),
Some(referrer),
+ referrer_kind,
to_node_mode(mode),
)
.map_err(|e| ResolveError::Other(e.into()))
@@ -847,6 +899,7 @@ impl Resolver for CliGraphResolver {
pkg_folder,
npm_req_ref.sub_path(),
Some(referrer),
+ referrer_kind,
to_node_mode(mode),
)
.map_err(|e| ResolveError::Other(e.into()));
@@ -855,7 +908,12 @@ impl Resolver for CliGraphResolver {
// do npm resolution for byonm
if is_byonm {
return node_resolver
- .resolve_req_reference(&npm_req_ref, referrer, to_node_mode(mode))
+ .resolve_req_reference(
+ &npm_req_ref,
+ referrer,
+ referrer_kind,
+ to_node_mode(mode),
+ )
.map_err(|err| err.into());
}
}
@@ -869,7 +927,12 @@ impl Resolver for CliGraphResolver {
// If byonm, check if the bare specifier resolves to an npm package
if is_byonm && referrer.scheme() == "file" {
let maybe_resolution = node_resolver
- .resolve_if_for_npm_pkg(raw_specifier, referrer, to_node_mode(mode))
+ .resolve_if_for_npm_pkg(
+ raw_specifier,
+ referrer,
+ referrer_kind,
+ to_node_mode(mode),
+ )
.map_err(ResolveError::Other)?;
if let Some(res) = maybe_resolution {
match res {