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.
This commit is contained in:
David Sherret 2024-11-13 10:10:09 -05:00 committed by GitHub
parent 6a4c6d83ba
commit f091d1ad69
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
119 changed files with 1244 additions and 616 deletions

View file

@ -7,6 +7,7 @@ mod import_map;
mod lockfile;
mod package_json;
use deno_ast::MediaType;
use deno_ast::SourceMapOption;
use deno_config::deno_json::NodeModulesDirMode;
use deno_config::workspace::CreateResolverOptions;
@ -34,7 +35,6 @@ use import_map::resolve_import_map_value_from_specifier;
pub use deno_config::deno_json::BenchConfig;
pub use deno_config::deno_json::ConfigFile;
pub use deno_config::deno_json::FmtOptionsConfig;
pub use deno_config::deno_json::JsxImportSourceConfig;
pub use deno_config::deno_json::LintRulesConfig;
pub use deno_config::deno_json::ProseWrap;
pub use deno_config::deno_json::TsConfig;
@ -1155,21 +1155,34 @@ impl CliOptions {
self
.main_module_cell
.get_or_init(|| {
let main_module = match &self.flags.subcommand {
Ok(match &self.flags.subcommand {
DenoSubcommand::Compile(compile_flags) => {
resolve_url_or_path(&compile_flags.source_file, self.initial_cwd())?
}
DenoSubcommand::Eval(_) => {
resolve_url_or_path("./$deno$eval.ts", self.initial_cwd())?
resolve_url_or_path("./$deno$eval.mts", self.initial_cwd())?
}
DenoSubcommand::Repl(_) => {
resolve_url_or_path("./$deno$repl.ts", self.initial_cwd())?
resolve_url_or_path("./$deno$repl.mts", self.initial_cwd())?
}
DenoSubcommand::Run(run_flags) => {
if run_flags.is_stdin() {
resolve_url_or_path("./$deno$stdin.ts", self.initial_cwd())?
resolve_url_or_path("./$deno$stdin.mts", self.initial_cwd())?
} else {
resolve_url_or_path(&run_flags.script, self.initial_cwd())?
let url =
resolve_url_or_path(&run_flags.script, self.initial_cwd())?;
if self.is_node_main()
&& url.scheme() == "file"
&& MediaType::from_specifier(&url) == MediaType::Unknown
{
try_resolve_node_binary_main_entrypoint(
&run_flags.script,
self.initial_cwd(),
)?
.unwrap_or(url)
} else {
url
}
}
}
DenoSubcommand::Serve(run_flags) => {
@ -1178,9 +1191,7 @@ impl CliOptions {
_ => {
bail!("No main module.")
}
};
Ok(main_module)
})
})
.as_ref()
.map_err(|err| deno_core::anyhow::anyhow!("{}", err))
@ -1229,7 +1240,7 @@ impl CliOptions {
// This is triggered via a secret environment variable which is used
// for functionality like child_process.fork. Users should NOT depend
// on this functionality.
pub fn is_npm_main(&self) -> bool {
pub fn is_node_main(&self) -> bool {
NPM_PROCESS_STATE.is_some()
}
@ -1607,9 +1618,11 @@ impl CliOptions {
|| self.workspace().has_unstable("bare-node-builtins")
}
pub fn unstable_detect_cjs(&self) -> bool {
self.flags.unstable_config.detect_cjs
|| self.workspace().has_unstable("detect-cjs")
pub fn detect_cjs(&self) -> bool {
// only enabled when there's a package.json in order to not have a
// perf penalty for non-npm Deno projects of searching for the closest
// package.json beside each module
self.workspace().package_jsons().next().is_some() || self.is_node_main()
}
fn byonm_enabled(&self) -> bool {
@ -1673,7 +1686,6 @@ impl CliOptions {
"byonm",
"bare-node-builtins",
"fmt-component",
"detect-cjs",
])
.collect();
@ -1811,6 +1823,36 @@ fn resolve_node_modules_folder(
Ok(Some(canonicalize_path_maybe_not_exists(&path)?))
}
fn try_resolve_node_binary_main_entrypoint(
specifier: &str,
initial_cwd: &Path,
) -> Result<Option<Url>, AnyError> {
// node allows running files at paths without a `.js` extension
// or at directories with an index.js file
let path = deno_core::normalize_path(initial_cwd.join(specifier));
if path.is_dir() {
let index_file = path.join("index.js");
Ok(if index_file.is_file() {
Some(deno_path_util::url_from_file_path(&index_file)?)
} else {
None
})
} else {
let path = path.with_extension(
path
.extension()
.and_then(|s| s.to_str())
.map(|s| format!("{}.js", s))
.unwrap_or("js".to_string()),
);
if path.is_file() {
Ok(Some(deno_path_util::url_from_file_path(&path)?))
} else {
Ok(None)
}
}
}
fn resolve_import_map_specifier(
maybe_import_map_path: Option<&str>,
maybe_config_file: Option<&ConfigFile>,