// Copyright 2018-2025 the Deno authors. MIT license. use std::borrow::Cow; use std::sync::Arc; use deno_core::url::Url; use deno_error::JsErrorBox; use deno_lib::loader::NpmModuleLoader; use deno_media_type::MediaType; use deno_resolver::npm::DenoInNpmPackageChecker; use deno_resolver::npm::NpmReqResolver; use deno_runtime::deno_fs; use deno_runtime::deno_node::RealIsBuiltInNodeModuleChecker; use node_resolver::analyze::CjsAnalysis; use node_resolver::analyze::CjsAnalysisExports; use node_resolver::analyze::NodeCodeTranslator; use crate::file_system::DenoRtSys; pub type DenoRtCjsTracker = deno_resolver::cjs::CjsTracker; pub type DenoRtNpmResolver = deno_resolver::npm::NpmResolver; pub type DenoRtNpmModuleLoader = NpmModuleLoader< CjsCodeAnalyzer, DenoInNpmPackageChecker, RealIsBuiltInNodeModuleChecker, DenoRtNpmResolver, DenoRtSys, >; pub type DenoRtNodeCodeTranslator = NodeCodeTranslator< CjsCodeAnalyzer, DenoInNpmPackageChecker, RealIsBuiltInNodeModuleChecker, DenoRtNpmResolver, DenoRtSys, >; pub type DenoRtNodeResolver = deno_runtime::deno_node::NodeResolver< DenoInNpmPackageChecker, DenoRtNpmResolver, DenoRtSys, >; pub type DenoRtNpmReqResolver = NpmReqResolver< DenoInNpmPackageChecker, RealIsBuiltInNodeModuleChecker, DenoRtNpmResolver, DenoRtSys, >; pub struct CjsCodeAnalyzer { cjs_tracker: Arc, fs: deno_fs::FileSystemRc, } impl CjsCodeAnalyzer { pub fn new( cjs_tracker: Arc, fs: deno_fs::FileSystemRc, ) -> Self { Self { cjs_tracker, fs } } async fn inner_cjs_analysis<'a>( &self, specifier: &Url, source: Cow<'a, str>, ) -> Result, JsErrorBox> { let media_type = MediaType::from_specifier(specifier); if media_type == MediaType::Json { return Ok(CjsAnalysis::Cjs(CjsAnalysisExports { exports: vec![], reexports: vec![], })); } let cjs_tracker = self.cjs_tracker.clone(); let is_maybe_cjs = cjs_tracker .is_maybe_cjs(specifier, media_type) .map_err(JsErrorBox::from_err)?; let analysis = if is_maybe_cjs { let maybe_cjs = deno_core::unsync::spawn_blocking({ let specifier = specifier.clone(); let source: Arc = source.to_string().into(); move || -> Result<_, JsErrorBox> { let parsed_source = deno_ast::parse_program(deno_ast::ParseParams { specifier, text: source.clone(), media_type, capture_tokens: true, scope_analysis: false, maybe_syntax: None, }) .map_err(JsErrorBox::from_err)?; 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, ) .map_err(JsErrorBox::from_err)?; if is_cjs { let analysis = parsed_source.analyze_cjs(); Ok(Some(CjsAnalysisExports { exports: analysis.exports, reexports: analysis.reexports, })) } else { Ok(None) } } }) .await .unwrap()?; match maybe_cjs { Some(cjs) => CjsAnalysis::Cjs(cjs), None => CjsAnalysis::Esm(source), } } else { CjsAnalysis::Esm(source) }; Ok(analysis) } } #[async_trait::async_trait(?Send)] impl node_resolver::analyze::CjsCodeAnalyzer for CjsCodeAnalyzer { async fn analyze_cjs<'a>( &self, specifier: &Url, source: Option>, ) -> Result, JsErrorBox> { let source = match source { Some(source) => source, None => { if let Ok(path) = specifier.to_file_path() { if let Ok(source_from_file) = self.fs.read_text_file_lossy_async(path, None).await { source_from_file } else { return Ok(CjsAnalysis::Cjs(CjsAnalysisExports { exports: vec![], reexports: vec![], })); } } else { return Ok(CjsAnalysis::Cjs(CjsAnalysisExports { exports: vec![], reexports: vec![], })); } } }; self.inner_cjs_analysis(specifier, source).await } }