summaryrefslogtreecommitdiff
path: root/cli/node.rs
diff options
context:
space:
mode:
authorDavid Sherret <dsherret@users.noreply.github.com>2024-11-01 12:27:00 -0400
committerGitHub <noreply@github.com>2024-11-01 12:27:00 -0400
commit826e42a5b5880c974ae019a7a21aade6a718062c (patch)
treea46502ecc3c73e4f7fc3a4517d83c7b2f3d0c0d3 /cli/node.rs
parent4774eab64d5176e997b6431f03f075782321b3d9 (diff)
fix: improved support for cjs and cts modules (#26558)
* cts support * better cjs/cts type checking * deno compile cjs/cts support * More efficient detect cjs (going towards stabilization) * Determination of whether .js, .ts, .jsx, or .tsx is cjs or esm is only done after loading * Support `import x = require(...);` Co-authored-by: Bartek IwaƄczuk <biwanczuk@gmail.com>
Diffstat (limited to 'cli/node.rs')
-rw-r--r--cli/node.rs136
1 files changed, 68 insertions, 68 deletions
diff --git a/cli/node.rs b/cli/node.rs
index 733d5f871..1d410a726 100644
--- a/cli/node.rs
+++ b/cli/node.rs
@@ -1,11 +1,14 @@
// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license.
+use std::borrow::Cow;
use std::sync::Arc;
use deno_ast::MediaType;
use deno_ast::ModuleSpecifier;
use deno_core::error::AnyError;
use deno_graph::ParsedSourceStore;
+use deno_path_util::url_from_file_path;
+use deno_path_util::url_to_file_path;
use deno_runtime::deno_fs;
use deno_runtime::deno_node::DenoFsNodeResolverEnv;
use node_resolver::analyze::CjsAnalysis as ExtNodeCjsAnalysis;
@@ -18,8 +21,8 @@ use serde::Serialize;
use crate::cache::CacheDBHash;
use crate::cache::NodeAnalysisCache;
use crate::cache::ParsedSourceCache;
-use crate::resolver::CliNodeResolver;
-use crate::util::fs::canonicalize_path_maybe_not_exists;
+use crate::resolver::CjsTracker;
+use crate::util::fs::canonicalize_path_maybe_not_exists_with_fs;
pub type CliNodeCodeTranslator =
NodeCodeTranslator<CliCjsCodeAnalyzer, DenoFsNodeResolverEnv>;
@@ -32,14 +35,14 @@ pub type CliNodeCodeTranslator =
/// because the node_modules folder might not exist at that time.
pub fn resolve_specifier_into_node_modules(
specifier: &ModuleSpecifier,
+ fs: &dyn deno_fs::FileSystem,
) -> ModuleSpecifier {
- specifier
- .to_file_path()
+ url_to_file_path(specifier)
.ok()
// this path might not exist at the time the graph is being created
// because the node_modules folder might not yet exist
- .and_then(|path| canonicalize_path_maybe_not_exists(&path).ok())
- .and_then(|path| ModuleSpecifier::from_file_path(path).ok())
+ .and_then(|path| canonicalize_path_maybe_not_exists_with_fs(&path, fs).ok())
+ .and_then(|path| url_from_file_path(&path).ok())
.unwrap_or_else(|| specifier.clone())
}
@@ -56,23 +59,29 @@ pub enum CliCjsAnalysis {
pub struct CliCjsCodeAnalyzer {
cache: NodeAnalysisCache,
+ cjs_tracker: Arc<CjsTracker>,
fs: deno_fs::FileSystemRc,
- node_resolver: Arc<CliNodeResolver>,
parsed_source_cache: Option<Arc<ParsedSourceCache>>,
+ // todo(dsherret): hack, remove in https://github.com/denoland/deno/pull/26439
+ // For example, this does not properly handle if cjs analysis was already done
+ // and has been cached.
+ is_npm_main: bool,
}
impl CliCjsCodeAnalyzer {
pub fn new(
cache: NodeAnalysisCache,
+ cjs_tracker: Arc<CjsTracker>,
fs: deno_fs::FileSystemRc,
- node_resolver: Arc<CliNodeResolver>,
parsed_source_cache: Option<Arc<ParsedSourceCache>>,
+ is_npm_main: bool,
) -> Self {
Self {
cache,
+ cjs_tracker,
fs,
- node_resolver,
parsed_source_cache,
+ is_npm_main,
}
}
@@ -88,7 +97,7 @@ impl CliCjsCodeAnalyzer {
return Ok(analysis);
}
- let mut media_type = MediaType::from_specifier(specifier);
+ let media_type = MediaType::from_specifier(specifier);
if media_type == MediaType::Json {
return Ok(CliCjsAnalysis::Cjs {
exports: vec![],
@@ -96,62 +105,53 @@ impl CliCjsCodeAnalyzer {
});
}
- if media_type == MediaType::JavaScript {
- if let Some(package_json) =
- self.node_resolver.get_closest_package_json(specifier)?
- {
- match package_json.typ.as_str() {
- "commonjs" => {
- media_type = MediaType::Cjs;
- }
- "module" => {
- media_type = MediaType::Mjs;
- }
- _ => {}
- }
- }
- }
-
- let maybe_parsed_source = self
- .parsed_source_cache
- .as_ref()
- .and_then(|c| c.remove_parsed_source(specifier));
-
- let analysis = deno_core::unsync::spawn_blocking({
- let specifier = specifier.clone();
- let source: Arc<str> = source.into();
- move || -> Result<_, deno_ast::ParseDiagnostic> {
- let parsed_source =
- maybe_parsed_source.map(Ok).unwrap_or_else(|| {
- deno_ast::parse_program(deno_ast::ParseParams {
- specifier,
- text: source,
- media_type,
- capture_tokens: true,
- scope_analysis: false,
- maybe_syntax: None,
+ let cjs_tracker = self.cjs_tracker.clone();
+ let is_npm_main = self.is_npm_main;
+ let is_maybe_cjs =
+ cjs_tracker.is_maybe_cjs(specifier, media_type)? || is_npm_main;
+ let analysis = if is_maybe_cjs {
+ let maybe_parsed_source = self
+ .parsed_source_cache
+ .as_ref()
+ .and_then(|c| c.remove_parsed_source(specifier));
+
+ deno_core::unsync::spawn_blocking({
+ let specifier = specifier.clone();
+ let source: Arc<str> = source.into();
+ move || -> Result<_, AnyError> {
+ let parsed_source =
+ maybe_parsed_source.map(Ok).unwrap_or_else(|| {
+ deno_ast::parse_program(deno_ast::ParseParams {
+ specifier,
+ text: source,
+ media_type,
+ capture_tokens: true,
+ scope_analysis: false,
+ maybe_syntax: None,
+ })
+ })?;
+ let is_script = parsed_source.compute_is_script();
+ let is_cjs = cjs_tracker.is_cjs_with_known_is_script(
+ parsed_source.specifier(),
+ media_type,
+ is_script,
+ )? || is_script && is_npm_main;
+ if is_cjs {
+ let analysis = parsed_source.analyze_cjs();
+ Ok(CliCjsAnalysis::Cjs {
+ exports: analysis.exports,
+ reexports: analysis.reexports,
})
- })?;
- if parsed_source.is_script() {
- let analysis = parsed_source.analyze_cjs();
- Ok(CliCjsAnalysis::Cjs {
- exports: analysis.exports,
- reexports: analysis.reexports,
- })
- } else if media_type == MediaType::Cjs {
- // FIXME: `deno_ast` should internally handle MediaType::Cjs implying that
- // the result must never be Esm
- Ok(CliCjsAnalysis::Cjs {
- exports: vec![],
- reexports: vec![],
- })
- } else {
- Ok(CliCjsAnalysis::Esm)
+ } else {
+ Ok(CliCjsAnalysis::Esm)
+ }
}
- }
- })
- .await
- .unwrap()?;
+ })
+ .await
+ .unwrap()?
+ } else {
+ CliCjsAnalysis::Esm
+ };
self
.cache
@@ -163,11 +163,11 @@ impl CliCjsCodeAnalyzer {
#[async_trait::async_trait(?Send)]
impl CjsCodeAnalyzer for CliCjsCodeAnalyzer {
- async fn analyze_cjs(
+ async fn analyze_cjs<'a>(
&self,
specifier: &ModuleSpecifier,
- source: Option<String>,
- ) -> Result<ExtNodeCjsAnalysis, AnyError> {
+ source: Option<Cow<'a, str>>,
+ ) -> Result<ExtNodeCjsAnalysis<'a>, AnyError> {
let source = match source {
Some(source) => source,
None => {
@@ -175,7 +175,7 @@ impl CjsCodeAnalyzer for CliCjsCodeAnalyzer {
if let Ok(source_from_file) =
self.fs.read_text_file_lossy_async(path, None).await
{
- source_from_file
+ Cow::Owned(source_from_file)
} else {
return Ok(ExtNodeCjsAnalysis::Cjs(CjsAnalysisExports {
exports: vec![],