mirror of
https://github.com/denoland/deno.git
synced 2024-12-21 23:04:45 -05:00
feat(unstable): --unstable-detect-cjs
for respecting explicit "type": "commonjs"
(#26149)
When using the `--unstable-detect-cjs` flag or adding `"unstable": ["detect-cjs"]` to a deno.json, it will make a JS file CJS if the closest package.json contains `"type": "commonjs"` and the file is not an ESM module (no TLA, no `import.meta`, no `import`/`export`).
This commit is contained in:
parent
ee7d450143
commit
1a0cb5b531
47 changed files with 520 additions and 155 deletions
|
@ -575,7 +575,8 @@ fn parse_packages_allowed_scripts(s: &str) -> Result<String, AnyError> {
|
|||
pub struct UnstableConfig {
|
||||
// TODO(bartlomieju): remove in Deno 2.5
|
||||
pub legacy_flag_enabled: bool, // --unstable
|
||||
pub bare_node_builtins: bool, // --unstable-bare-node-builts
|
||||
pub bare_node_builtins: bool,
|
||||
pub detect_cjs: bool,
|
||||
pub sloppy_imports: bool,
|
||||
pub features: Vec<String>, // --unstabe-kv --unstable-cron
|
||||
}
|
||||
|
@ -1528,7 +1529,7 @@ pub fn clap_root() -> Command {
|
|||
);
|
||||
|
||||
run_args(Command::new("deno"), true)
|
||||
.args(unstable_args(UnstableArgsConfig::ResolutionAndRuntime))
|
||||
.with_unstable_args(UnstableArgsConfig::ResolutionAndRuntime)
|
||||
.next_line_help(false)
|
||||
.bin_name("deno")
|
||||
.styles(
|
||||
|
@ -1630,7 +1631,7 @@ fn command(
|
|||
) -> Command {
|
||||
Command::new(name)
|
||||
.about(about)
|
||||
.args(unstable_args(unstable_args_config))
|
||||
.with_unstable_args(unstable_args_config)
|
||||
}
|
||||
|
||||
fn help_subcommand(app: &Command) -> Command {
|
||||
|
@ -4142,23 +4143,29 @@ enum UnstableArgsConfig {
|
|||
ResolutionAndRuntime,
|
||||
}
|
||||
|
||||
struct UnstableArgsIter {
|
||||
idx: usize,
|
||||
cfg: UnstableArgsConfig,
|
||||
trait CommandExt {
|
||||
fn with_unstable_args(self, cfg: UnstableArgsConfig) -> Self;
|
||||
}
|
||||
|
||||
impl Iterator for UnstableArgsIter {
|
||||
type Item = Arg;
|
||||
impl CommandExt for Command {
|
||||
fn with_unstable_args(self, cfg: UnstableArgsConfig) -> Self {
|
||||
let mut next_display_order = {
|
||||
let mut value = 1000;
|
||||
move || {
|
||||
value += 1;
|
||||
value
|
||||
}
|
||||
};
|
||||
|
||||
fn next(&mut self) -> Option<Self::Item> {
|
||||
let arg = if self.idx == 0 {
|
||||
let mut cmd = self.arg(
|
||||
Arg::new("unstable")
|
||||
.long("unstable")
|
||||
.help(cstr!("Enable all unstable features and APIs. Instead of using this flag, consider enabling individual unstable features
|
||||
.long("unstable")
|
||||
.help(cstr!("Enable all unstable features and APIs. Instead of using this flag, consider enabling individual unstable features
|
||||
<p(245)>To view the list of individual unstable feature flags, run this command again with --help=unstable</>"))
|
||||
.action(ArgAction::SetTrue)
|
||||
.hide(matches!(self.cfg, UnstableArgsConfig::None))
|
||||
} else if self.idx == 1 {
|
||||
.action(ArgAction::SetTrue)
|
||||
.hide(matches!(cfg, UnstableArgsConfig::None))
|
||||
.display_order(next_display_order())
|
||||
).arg(
|
||||
Arg::new("unstable-bare-node-builtins")
|
||||
.long("unstable-bare-node-builtins")
|
||||
.help("Enable unstable bare node builtins feature")
|
||||
|
@ -4166,20 +4173,36 @@ impl Iterator for UnstableArgsIter {
|
|||
.value_parser(FalseyValueParser::new())
|
||||
.action(ArgAction::SetTrue)
|
||||
.hide(true)
|
||||
.long_help(match self.cfg {
|
||||
.long_help(match cfg {
|
||||
UnstableArgsConfig::None => None,
|
||||
UnstableArgsConfig::ResolutionOnly
|
||||
| UnstableArgsConfig::ResolutionAndRuntime => Some("true"),
|
||||
})
|
||||
.help_heading(UNSTABLE_HEADING)
|
||||
} else if self.idx == 2 {
|
||||
.display_order(next_display_order()),
|
||||
).arg(
|
||||
Arg::new("unstable-detect-cjs")
|
||||
.long("unstable-detect-cjs")
|
||||
.help("Reads the package.json type field in a project to treat .js files as .cjs")
|
||||
.value_parser(FalseyValueParser::new())
|
||||
.action(ArgAction::SetTrue)
|
||||
.hide(true)
|
||||
.long_help(match cfg {
|
||||
UnstableArgsConfig::None => None,
|
||||
UnstableArgsConfig::ResolutionOnly
|
||||
| UnstableArgsConfig::ResolutionAndRuntime => Some("true"),
|
||||
})
|
||||
.help_heading(UNSTABLE_HEADING)
|
||||
.display_order(next_display_order())
|
||||
).arg(
|
||||
Arg::new("unstable-byonm")
|
||||
.long("unstable-byonm")
|
||||
.value_parser(FalseyValueParser::new())
|
||||
.action(ArgAction::SetTrue)
|
||||
.hide(true)
|
||||
.help_heading(UNSTABLE_HEADING)
|
||||
} else if self.idx == 3 {
|
||||
.display_order(next_display_order()),
|
||||
).arg(
|
||||
Arg::new("unstable-sloppy-imports")
|
||||
.long("unstable-sloppy-imports")
|
||||
.help("Enable unstable resolving of specifiers by extension probing, .js to .ts, and directory probing")
|
||||
|
@ -4187,40 +4210,39 @@ impl Iterator for UnstableArgsIter {
|
|||
.value_parser(FalseyValueParser::new())
|
||||
.action(ArgAction::SetTrue)
|
||||
.hide(true)
|
||||
.long_help(match self.cfg {
|
||||
.long_help(match cfg {
|
||||
UnstableArgsConfig::None => None,
|
||||
UnstableArgsConfig::ResolutionOnly | UnstableArgsConfig::ResolutionAndRuntime => Some("true")
|
||||
})
|
||||
.help_heading(UNSTABLE_HEADING)
|
||||
} else if self.idx > 3 {
|
||||
let granular_flag = crate::UNSTABLE_GRANULAR_FLAGS.get(self.idx - 4)?;
|
||||
Arg::new(format!("unstable-{}", granular_flag.name))
|
||||
.long(format!("unstable-{}", granular_flag.name))
|
||||
.help(granular_flag.help_text)
|
||||
.action(ArgAction::SetTrue)
|
||||
.hide(true)
|
||||
.help_heading(UNSTABLE_HEADING)
|
||||
// we don't render long help, so using it here as a sort of metadata
|
||||
.long_help(if granular_flag.show_in_help {
|
||||
match self.cfg {
|
||||
UnstableArgsConfig::None | UnstableArgsConfig::ResolutionOnly => {
|
||||
None
|
||||
}
|
||||
UnstableArgsConfig::ResolutionAndRuntime => Some("true"),
|
||||
}
|
||||
} else {
|
||||
None
|
||||
})
|
||||
} else {
|
||||
return None;
|
||||
};
|
||||
self.idx += 1;
|
||||
Some(arg.display_order(self.idx + 1000))
|
||||
}
|
||||
}
|
||||
.display_order(next_display_order())
|
||||
);
|
||||
|
||||
fn unstable_args(cfg: UnstableArgsConfig) -> impl IntoIterator<Item = Arg> {
|
||||
UnstableArgsIter { idx: 0, cfg }
|
||||
for granular_flag in crate::UNSTABLE_GRANULAR_FLAGS.iter() {
|
||||
cmd = cmd.arg(
|
||||
Arg::new(format!("unstable-{}", granular_flag.name))
|
||||
.long(format!("unstable-{}", granular_flag.name))
|
||||
.help(granular_flag.help_text)
|
||||
.action(ArgAction::SetTrue)
|
||||
.hide(true)
|
||||
.help_heading(UNSTABLE_HEADING)
|
||||
// we don't render long help, so using it here as a sort of metadata
|
||||
.long_help(if granular_flag.show_in_help {
|
||||
match cfg {
|
||||
UnstableArgsConfig::None | UnstableArgsConfig::ResolutionOnly => {
|
||||
None
|
||||
}
|
||||
UnstableArgsConfig::ResolutionAndRuntime => Some("true"),
|
||||
}
|
||||
} else {
|
||||
None
|
||||
})
|
||||
.display_order(next_display_order()),
|
||||
);
|
||||
}
|
||||
|
||||
cmd
|
||||
}
|
||||
}
|
||||
|
||||
fn allow_scripts_arg_parse(
|
||||
|
@ -5678,6 +5700,7 @@ fn unstable_args_parse(
|
|||
|
||||
flags.unstable_config.bare_node_builtins =
|
||||
matches.get_flag("unstable-bare-node-builtins");
|
||||
flags.unstable_config.detect_cjs = matches.get_flag("unstable-detect-cjs");
|
||||
flags.unstable_config.sloppy_imports =
|
||||
matches.get_flag("unstable-sloppy-imports");
|
||||
|
||||
|
|
|
@ -1576,6 +1576,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")
|
||||
}
|
||||
|
||||
fn byonm_enabled(&self) -> bool {
|
||||
// check if enabled via unstable
|
||||
self.node_modules_dir().ok().flatten() == Some(NodeModulesDirMode::Manual)
|
||||
|
@ -1620,21 +1625,17 @@ impl CliOptions {
|
|||
});
|
||||
|
||||
if !from_config_file.is_empty() {
|
||||
// collect unstable granular flags
|
||||
let mut all_valid_unstable_flags: Vec<&str> =
|
||||
crate::UNSTABLE_GRANULAR_FLAGS
|
||||
.iter()
|
||||
.map(|granular_flag| granular_flag.name)
|
||||
.collect();
|
||||
|
||||
let mut another_unstable_flags = Vec::from([
|
||||
"sloppy-imports",
|
||||
"byonm",
|
||||
"bare-node-builtins",
|
||||
"fmt-component",
|
||||
]);
|
||||
// add more unstable flags to the same vector holding granular flags
|
||||
all_valid_unstable_flags.append(&mut another_unstable_flags);
|
||||
let all_valid_unstable_flags: Vec<&str> = crate::UNSTABLE_GRANULAR_FLAGS
|
||||
.iter()
|
||||
.map(|granular_flag| granular_flag.name)
|
||||
.chain([
|
||||
"sloppy-imports",
|
||||
"byonm",
|
||||
"bare-node-builtins",
|
||||
"fmt-component",
|
||||
"detect-cjs",
|
||||
])
|
||||
.collect();
|
||||
|
||||
// check and warn if the unstable flag of config file isn't supported, by
|
||||
// iterating through the vector holding the unstable flags
|
||||
|
|
57
cli/cache/mod.rs
vendored
57
cli/cache/mod.rs
vendored
|
@ -9,10 +9,13 @@ use crate::file_fetcher::FetchPermissionsOptionRef;
|
|||
use crate::file_fetcher::FileFetcher;
|
||||
use crate::file_fetcher::FileOrRedirect;
|
||||
use crate::npm::CliNpmResolver;
|
||||
use crate::resolver::CliNodeResolver;
|
||||
use crate::util::fs::atomic_write_file_with_retries;
|
||||
use crate::util::fs::atomic_write_file_with_retries_and_fs;
|
||||
use crate::util::fs::AtomicWriteFileFsAdapter;
|
||||
use crate::util::path::specifier_has_extension;
|
||||
use crate::util::text_encoding::arc_str_to_bytes;
|
||||
use crate::util::text_encoding::from_utf8_lossy_owned;
|
||||
|
||||
use deno_ast::MediaType;
|
||||
use deno_core::futures;
|
||||
|
@ -57,6 +60,7 @@ pub use fast_check::FastCheckCache;
|
|||
pub use incremental::IncrementalCache;
|
||||
pub use module_info::ModuleInfoCache;
|
||||
pub use node::NodeAnalysisCache;
|
||||
pub use parsed_source::EsmOrCjsChecker;
|
||||
pub use parsed_source::LazyGraphSourceParser;
|
||||
pub use parsed_source::ParsedSourceCache;
|
||||
|
||||
|
@ -177,37 +181,46 @@ pub struct FetchCacherOptions {
|
|||
pub permissions: PermissionsContainer,
|
||||
/// If we're publishing for `deno publish`.
|
||||
pub is_deno_publish: bool,
|
||||
pub unstable_detect_cjs: bool,
|
||||
}
|
||||
|
||||
/// A "wrapper" for the FileFetcher and DiskCache for the Deno CLI that provides
|
||||
/// a concise interface to the DENO_DIR when building module graphs.
|
||||
pub struct FetchCacher {
|
||||
file_fetcher: Arc<FileFetcher>,
|
||||
pub file_header_overrides: HashMap<ModuleSpecifier, HashMap<String, String>>,
|
||||
esm_or_cjs_checker: Arc<EsmOrCjsChecker>,
|
||||
file_fetcher: Arc<FileFetcher>,
|
||||
global_http_cache: Arc<GlobalHttpCache>,
|
||||
node_resolver: Arc<CliNodeResolver>,
|
||||
npm_resolver: Arc<dyn CliNpmResolver>,
|
||||
module_info_cache: Arc<ModuleInfoCache>,
|
||||
permissions: PermissionsContainer,
|
||||
cache_info_enabled: bool,
|
||||
is_deno_publish: bool,
|
||||
unstable_detect_cjs: bool,
|
||||
cache_info_enabled: bool,
|
||||
}
|
||||
|
||||
impl FetchCacher {
|
||||
pub fn new(
|
||||
esm_or_cjs_checker: Arc<EsmOrCjsChecker>,
|
||||
file_fetcher: Arc<FileFetcher>,
|
||||
global_http_cache: Arc<GlobalHttpCache>,
|
||||
node_resolver: Arc<CliNodeResolver>,
|
||||
npm_resolver: Arc<dyn CliNpmResolver>,
|
||||
module_info_cache: Arc<ModuleInfoCache>,
|
||||
options: FetchCacherOptions,
|
||||
) -> Self {
|
||||
Self {
|
||||
file_fetcher,
|
||||
esm_or_cjs_checker,
|
||||
global_http_cache,
|
||||
node_resolver,
|
||||
npm_resolver,
|
||||
module_info_cache,
|
||||
file_header_overrides: options.file_header_overrides,
|
||||
permissions: options.permissions,
|
||||
is_deno_publish: options.is_deno_publish,
|
||||
unstable_detect_cjs: options.unstable_detect_cjs,
|
||||
cache_info_enabled: false,
|
||||
}
|
||||
}
|
||||
|
@ -282,6 +295,46 @@ impl Loader for FetchCacher {
|
|||
},
|
||||
))));
|
||||
}
|
||||
|
||||
if self.unstable_detect_cjs && specifier_has_extension(specifier, "js") {
|
||||
if let Ok(Some(pkg_json)) =
|
||||
self.node_resolver.get_closest_package_json(specifier)
|
||||
{
|
||||
if pkg_json.typ == "commonjs" {
|
||||
if let Ok(path) = specifier.to_file_path() {
|
||||
if let Ok(bytes) = std::fs::read(&path) {
|
||||
let text: Arc<str> = from_utf8_lossy_owned(bytes).into();
|
||||
let is_es_module = match self.esm_or_cjs_checker.is_esm(
|
||||
specifier,
|
||||
text.clone(),
|
||||
MediaType::JavaScript,
|
||||
) {
|
||||
Ok(value) => value,
|
||||
Err(err) => {
|
||||
return Box::pin(futures::future::ready(Err(err.into())));
|
||||
}
|
||||
};
|
||||
if !is_es_module {
|
||||
self.node_resolver.mark_cjs_resolution(specifier.clone());
|
||||
return Box::pin(futures::future::ready(Ok(Some(
|
||||
LoadResponse::External {
|
||||
specifier: specifier.clone(),
|
||||
},
|
||||
))));
|
||||
} else {
|
||||
return Box::pin(futures::future::ready(Ok(Some(
|
||||
LoadResponse::Module {
|
||||
specifier: specifier.clone(),
|
||||
content: arc_str_to_bytes(text),
|
||||
maybe_headers: None,
|
||||
},
|
||||
))));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if self.is_deno_publish
|
||||
|
|
40
cli/cache/parsed_source.rs
vendored
40
cli/cache/parsed_source.rs
vendored
|
@ -5,6 +5,7 @@ use std::sync::Arc;
|
|||
|
||||
use deno_ast::MediaType;
|
||||
use deno_ast::ModuleSpecifier;
|
||||
use deno_ast::ParseDiagnostic;
|
||||
use deno_ast::ParsedSource;
|
||||
use deno_core::parking_lot::Mutex;
|
||||
use deno_graph::CapturingModuleParser;
|
||||
|
@ -149,3 +150,42 @@ impl deno_graph::ParsedSourceStore for ParsedSourceCache {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub struct EsmOrCjsChecker {
|
||||
parsed_source_cache: Arc<ParsedSourceCache>,
|
||||
}
|
||||
|
||||
impl EsmOrCjsChecker {
|
||||
pub fn new(parsed_source_cache: Arc<ParsedSourceCache>) -> Self {
|
||||
Self {
|
||||
parsed_source_cache,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn is_esm(
|
||||
&self,
|
||||
specifier: &ModuleSpecifier,
|
||||
source: Arc<str>,
|
||||
media_type: MediaType,
|
||||
) -> Result<bool, ParseDiagnostic> {
|
||||
// todo(dsherret): add a file cache here to avoid parsing with swc on each run
|
||||
let source = match self.parsed_source_cache.get_parsed_source(specifier) {
|
||||
Some(source) => source.clone(),
|
||||
None => {
|
||||
let source = deno_ast::parse_program(deno_ast::ParseParams {
|
||||
specifier: specifier.clone(),
|
||||
text: source,
|
||||
media_type,
|
||||
capture_tokens: true, // capture because it's used for cjs export analysis
|
||||
scope_analysis: false,
|
||||
maybe_syntax: None,
|
||||
})?;
|
||||
self
|
||||
.parsed_source_cache
|
||||
.set_parsed_source(specifier.clone(), source.clone());
|
||||
source
|
||||
}
|
||||
};
|
||||
Ok(source.is_module())
|
||||
}
|
||||
}
|
||||
|
|
|
@ -14,6 +14,7 @@ use crate::cache::CodeCache;
|
|||
use crate::cache::DenoDir;
|
||||
use crate::cache::DenoDirProvider;
|
||||
use crate::cache::EmitCache;
|
||||
use crate::cache::EsmOrCjsChecker;
|
||||
use crate::cache::GlobalHttpCache;
|
||||
use crate::cache::HttpCache;
|
||||
use crate::cache::LocalHttpCache;
|
||||
|
@ -171,6 +172,7 @@ struct CliFactoryServices {
|
|||
http_client_provider: Deferred<Arc<HttpClientProvider>>,
|
||||
emit_cache: Deferred<Arc<EmitCache>>,
|
||||
emitter: Deferred<Arc<Emitter>>,
|
||||
esm_or_cjs_checker: Deferred<Arc<EsmOrCjsChecker>>,
|
||||
fs: Deferred<Arc<dyn deno_fs::FileSystem>>,
|
||||
main_graph_container: Deferred<Arc<MainModuleGraphContainer>>,
|
||||
maybe_inspector_server: Deferred<Option<Arc<InspectorServer>>>,
|
||||
|
@ -298,6 +300,12 @@ impl CliFactory {
|
|||
.get_or_init(|| ProgressBar::new(ProgressBarStyle::TextOnly))
|
||||
}
|
||||
|
||||
pub fn esm_or_cjs_checker(&self) -> &Arc<EsmOrCjsChecker> {
|
||||
self.services.esm_or_cjs_checker.get_or_init(|| {
|
||||
Arc::new(EsmOrCjsChecker::new(self.parsed_source_cache().clone()))
|
||||
})
|
||||
}
|
||||
|
||||
pub fn global_http_cache(&self) -> Result<&Arc<GlobalHttpCache>, AnyError> {
|
||||
self.services.global_http_cache.get_or_try_init(|| {
|
||||
Ok(Arc::new(GlobalHttpCache::new(
|
||||
|
@ -579,6 +587,7 @@ impl CliFactory {
|
|||
node_analysis_cache,
|
||||
self.fs().clone(),
|
||||
node_resolver,
|
||||
Some(self.parsed_source_cache().clone()),
|
||||
);
|
||||
|
||||
Ok(Arc::new(NodeCodeTranslator::new(
|
||||
|
@ -619,8 +628,10 @@ impl CliFactory {
|
|||
Ok(Arc::new(ModuleGraphBuilder::new(
|
||||
cli_options.clone(),
|
||||
self.caches()?.clone(),
|
||||
self.esm_or_cjs_checker().clone(),
|
||||
self.fs().clone(),
|
||||
self.resolver().await?.clone(),
|
||||
self.cli_node_resolver().await?.clone(),
|
||||
self.npm_resolver().await?.clone(),
|
||||
self.module_info_cache()?.clone(),
|
||||
self.parsed_source_cache().clone(),
|
||||
|
@ -792,6 +803,7 @@ impl CliFactory {
|
|||
|
||||
Ok(CliMainWorkerFactory::new(
|
||||
self.blob_store().clone(),
|
||||
self.cjs_resolutions().clone(),
|
||||
if cli_options.code_cache_enabled() {
|
||||
Some(self.code_cache()?.clone())
|
||||
} else {
|
||||
|
@ -896,6 +908,7 @@ impl CliFactory {
|
|||
node_ipc: cli_options.node_ipc_fd(),
|
||||
serve_port: cli_options.serve_port(),
|
||||
serve_host: cli_options.serve_host(),
|
||||
unstable_detect_cjs: cli_options.unstable_detect_cjs(),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
|
|
@ -6,6 +6,7 @@ use crate::args::CliLockfile;
|
|||
use crate::args::CliOptions;
|
||||
use crate::args::DENO_DISABLE_PEDANTIC_NODE_WARNINGS;
|
||||
use crate::cache;
|
||||
use crate::cache::EsmOrCjsChecker;
|
||||
use crate::cache::GlobalHttpCache;
|
||||
use crate::cache::ModuleInfoCache;
|
||||
use crate::cache::ParsedSourceCache;
|
||||
|
@ -14,6 +15,7 @@ use crate::errors::get_error_class_name;
|
|||
use crate::file_fetcher::FileFetcher;
|
||||
use crate::npm::CliNpmResolver;
|
||||
use crate::resolver::CliGraphResolver;
|
||||
use crate::resolver::CliNodeResolver;
|
||||
use crate::resolver::CliSloppyImportsResolver;
|
||||
use crate::resolver::SloppyImportsCachedFs;
|
||||
use crate::tools::check;
|
||||
|
@ -379,8 +381,10 @@ pub struct BuildFastCheckGraphOptions<'a> {
|
|||
pub struct ModuleGraphBuilder {
|
||||
options: Arc<CliOptions>,
|
||||
caches: Arc<cache::Caches>,
|
||||
esm_or_cjs_checker: Arc<EsmOrCjsChecker>,
|
||||
fs: Arc<dyn FileSystem>,
|
||||
resolver: Arc<CliGraphResolver>,
|
||||
node_resolver: Arc<CliNodeResolver>,
|
||||
npm_resolver: Arc<dyn CliNpmResolver>,
|
||||
module_info_cache: Arc<ModuleInfoCache>,
|
||||
parsed_source_cache: Arc<ParsedSourceCache>,
|
||||
|
@ -396,8 +400,10 @@ impl ModuleGraphBuilder {
|
|||
pub fn new(
|
||||
options: Arc<CliOptions>,
|
||||
caches: Arc<cache::Caches>,
|
||||
esm_or_cjs_checker: Arc<EsmOrCjsChecker>,
|
||||
fs: Arc<dyn FileSystem>,
|
||||
resolver: Arc<CliGraphResolver>,
|
||||
node_resolver: Arc<CliNodeResolver>,
|
||||
npm_resolver: Arc<dyn CliNpmResolver>,
|
||||
module_info_cache: Arc<ModuleInfoCache>,
|
||||
parsed_source_cache: Arc<ParsedSourceCache>,
|
||||
|
@ -410,8 +416,10 @@ impl ModuleGraphBuilder {
|
|||
Self {
|
||||
options,
|
||||
caches,
|
||||
esm_or_cjs_checker,
|
||||
fs,
|
||||
resolver,
|
||||
node_resolver,
|
||||
npm_resolver,
|
||||
module_info_cache,
|
||||
parsed_source_cache,
|
||||
|
@ -691,8 +699,10 @@ impl ModuleGraphBuilder {
|
|||
permissions: PermissionsContainer,
|
||||
) -> cache::FetchCacher {
|
||||
cache::FetchCacher::new(
|
||||
self.esm_or_cjs_checker.clone(),
|
||||
self.file_fetcher.clone(),
|
||||
self.global_http_cache.clone(),
|
||||
self.node_resolver.clone(),
|
||||
self.npm_resolver.clone(),
|
||||
self.module_info_cache.clone(),
|
||||
cache::FetchCacherOptions {
|
||||
|
@ -702,6 +712,7 @@ impl ModuleGraphBuilder {
|
|||
self.options.sub_command(),
|
||||
crate::args::DenoSubcommand::Publish { .. }
|
||||
),
|
||||
unstable_detect_cjs: self.options.unstable_detect_cjs(),
|
||||
},
|
||||
)
|
||||
}
|
||||
|
|
|
@ -331,15 +331,23 @@ impl<TGraphContainer: ModuleGraphContainer>
|
|||
maybe_referrer: Option<&ModuleSpecifier>,
|
||||
requested_module_type: RequestedModuleType,
|
||||
) -> Result<ModuleSource, AnyError> {
|
||||
let code_source = if let Some(result) = self
|
||||
.shared
|
||||
.npm_module_loader
|
||||
.load_if_in_npm_package(specifier, maybe_referrer)
|
||||
.await
|
||||
{
|
||||
result?
|
||||
} else {
|
||||
self.load_prepared_module(specifier, maybe_referrer).await?
|
||||
let code_source = match self.load_prepared_module(specifier).await? {
|
||||
Some(code_source) => code_source,
|
||||
None => {
|
||||
if self.shared.npm_module_loader.if_in_npm_package(specifier) {
|
||||
self
|
||||
.shared
|
||||
.npm_module_loader
|
||||
.load(specifier, maybe_referrer)
|
||||
.await?
|
||||
} else {
|
||||
let mut msg = format!("Loading unprepared module: {specifier}");
|
||||
if let Some(referrer) = maybe_referrer {
|
||||
msg = format!("{}, imported from: {}", msg, referrer.as_str());
|
||||
}
|
||||
return Err(anyhow!(msg));
|
||||
}
|
||||
}
|
||||
};
|
||||
let code = if self.shared.is_inspecting {
|
||||
// we need the code with the source map in order for
|
||||
|
@ -514,17 +522,12 @@ impl<TGraphContainer: ModuleGraphContainer>
|
|||
async fn load_prepared_module(
|
||||
&self,
|
||||
specifier: &ModuleSpecifier,
|
||||
maybe_referrer: Option<&ModuleSpecifier>,
|
||||
) -> Result<ModuleCodeStringSource, AnyError> {
|
||||
) -> Result<Option<ModuleCodeStringSource>, AnyError> {
|
||||
// Note: keep this in sync with the sync version below
|
||||
let graph = self.graph_container.graph();
|
||||
match self.load_prepared_module_or_defer_emit(
|
||||
&graph,
|
||||
specifier,
|
||||
maybe_referrer,
|
||||
) {
|
||||
Ok(CodeOrDeferredEmit::Code(code_source)) => Ok(code_source),
|
||||
Ok(CodeOrDeferredEmit::DeferredEmit {
|
||||
match self.load_prepared_module_or_defer_emit(&graph, specifier)? {
|
||||
Some(CodeOrDeferredEmit::Code(code_source)) => Ok(Some(code_source)),
|
||||
Some(CodeOrDeferredEmit::DeferredEmit {
|
||||
specifier,
|
||||
media_type,
|
||||
source,
|
||||
|
@ -537,30 +540,25 @@ impl<TGraphContainer: ModuleGraphContainer>
|
|||
// at this point, we no longer need the parsed source in memory, so free it
|
||||
self.parsed_source_cache.free(specifier);
|
||||
|
||||
Ok(ModuleCodeStringSource {
|
||||
Ok(Some(ModuleCodeStringSource {
|
||||
code: ModuleSourceCode::Bytes(transpile_result),
|
||||
found_url: specifier.clone(),
|
||||
media_type,
|
||||
})
|
||||
}))
|
||||
}
|
||||
Err(err) => Err(err),
|
||||
None => Ok(None),
|
||||
}
|
||||
}
|
||||
|
||||
fn load_prepared_module_sync(
|
||||
&self,
|
||||
specifier: &ModuleSpecifier,
|
||||
maybe_referrer: Option<&ModuleSpecifier>,
|
||||
) -> Result<ModuleCodeStringSource, AnyError> {
|
||||
) -> Result<Option<ModuleCodeStringSource>, AnyError> {
|
||||
// Note: keep this in sync with the async version above
|
||||
let graph = self.graph_container.graph();
|
||||
match self.load_prepared_module_or_defer_emit(
|
||||
&graph,
|
||||
specifier,
|
||||
maybe_referrer,
|
||||
) {
|
||||
Ok(CodeOrDeferredEmit::Code(code_source)) => Ok(code_source),
|
||||
Ok(CodeOrDeferredEmit::DeferredEmit {
|
||||
match self.load_prepared_module_or_defer_emit(&graph, specifier)? {
|
||||
Some(CodeOrDeferredEmit::Code(code_source)) => Ok(Some(code_source)),
|
||||
Some(CodeOrDeferredEmit::DeferredEmit {
|
||||
specifier,
|
||||
media_type,
|
||||
source,
|
||||
|
@ -572,13 +570,13 @@ impl<TGraphContainer: ModuleGraphContainer>
|
|||
// at this point, we no longer need the parsed source in memory, so free it
|
||||
self.parsed_source_cache.free(specifier);
|
||||
|
||||
Ok(ModuleCodeStringSource {
|
||||
Ok(Some(ModuleCodeStringSource {
|
||||
code: ModuleSourceCode::Bytes(transpile_result),
|
||||
found_url: specifier.clone(),
|
||||
media_type,
|
||||
})
|
||||
}))
|
||||
}
|
||||
Err(err) => Err(err),
|
||||
None => Ok(None),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -586,8 +584,7 @@ impl<TGraphContainer: ModuleGraphContainer>
|
|||
&self,
|
||||
graph: &'graph ModuleGraph,
|
||||
specifier: &ModuleSpecifier,
|
||||
maybe_referrer: Option<&ModuleSpecifier>,
|
||||
) -> Result<CodeOrDeferredEmit<'graph>, AnyError> {
|
||||
) -> Result<Option<CodeOrDeferredEmit<'graph>>, AnyError> {
|
||||
if specifier.scheme() == "node" {
|
||||
// Node built-in modules should be handled internally.
|
||||
unreachable!("Deno bug. {} was misconfigured internally.", specifier);
|
||||
|
@ -599,11 +596,11 @@ impl<TGraphContainer: ModuleGraphContainer>
|
|||
media_type,
|
||||
specifier,
|
||||
..
|
||||
})) => Ok(CodeOrDeferredEmit::Code(ModuleCodeStringSource {
|
||||
})) => Ok(Some(CodeOrDeferredEmit::Code(ModuleCodeStringSource {
|
||||
code: ModuleSourceCode::String(source.clone().into()),
|
||||
found_url: specifier.clone(),
|
||||
media_type: *media_type,
|
||||
})),
|
||||
}))),
|
||||
Some(deno_graph::Module::Js(JsModule {
|
||||
source,
|
||||
media_type,
|
||||
|
@ -624,11 +621,11 @@ impl<TGraphContainer: ModuleGraphContainer>
|
|||
| MediaType::Cts
|
||||
| MediaType::Jsx
|
||||
| MediaType::Tsx => {
|
||||
return Ok(CodeOrDeferredEmit::DeferredEmit {
|
||||
return Ok(Some(CodeOrDeferredEmit::DeferredEmit {
|
||||
specifier,
|
||||
media_type: *media_type,
|
||||
source,
|
||||
});
|
||||
}));
|
||||
}
|
||||
MediaType::TsBuildInfo | MediaType::Wasm | MediaType::SourceMap => {
|
||||
panic!("Unexpected media type {media_type} for {specifier}")
|
||||
|
@ -638,24 +635,18 @@ impl<TGraphContainer: ModuleGraphContainer>
|
|||
// at this point, we no longer need the parsed source in memory, so free it
|
||||
self.parsed_source_cache.free(specifier);
|
||||
|
||||
Ok(CodeOrDeferredEmit::Code(ModuleCodeStringSource {
|
||||
Ok(Some(CodeOrDeferredEmit::Code(ModuleCodeStringSource {
|
||||
code: ModuleSourceCode::String(code),
|
||||
found_url: specifier.clone(),
|
||||
media_type: *media_type,
|
||||
}))
|
||||
})))
|
||||
}
|
||||
Some(
|
||||
deno_graph::Module::External(_)
|
||||
| deno_graph::Module::Node(_)
|
||||
| deno_graph::Module::Npm(_),
|
||||
)
|
||||
| None => {
|
||||
let mut msg = format!("Loading unprepared module: {specifier}");
|
||||
if let Some(referrer) = maybe_referrer {
|
||||
msg = format!("{}, imported from: {}", msg, referrer.as_str());
|
||||
}
|
||||
Err(anyhow!(msg))
|
||||
}
|
||||
| None => Ok(None),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -828,7 +819,7 @@ impl<TGraphContainer: ModuleGraphContainer> ModuleLoader
|
|||
"wasm" | "file" | "http" | "https" | "data" | "blob" => (),
|
||||
_ => return None,
|
||||
}
|
||||
let source = self.0.load_prepared_module_sync(&specifier, None).ok()?;
|
||||
let source = self.0.load_prepared_module_sync(&specifier).ok()??;
|
||||
source_map_from_code(source.code.as_bytes())
|
||||
}
|
||||
|
||||
|
|
29
cli/node.rs
29
cli/node.rs
|
@ -5,6 +5,7 @@ use std::sync::Arc;
|
|||
use deno_ast::MediaType;
|
||||
use deno_ast::ModuleSpecifier;
|
||||
use deno_core::error::AnyError;
|
||||
use deno_graph::ParsedSourceStore;
|
||||
use deno_runtime::deno_fs;
|
||||
use deno_runtime::deno_node::DenoFsNodeResolverEnv;
|
||||
use node_resolver::analyze::CjsAnalysis as ExtNodeCjsAnalysis;
|
||||
|
@ -16,6 +17,7 @@ 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;
|
||||
|
||||
|
@ -56,6 +58,7 @@ pub struct CliCjsCodeAnalyzer {
|
|||
cache: NodeAnalysisCache,
|
||||
fs: deno_fs::FileSystemRc,
|
||||
node_resolver: Arc<CliNodeResolver>,
|
||||
parsed_source_cache: Option<Arc<ParsedSourceCache>>,
|
||||
}
|
||||
|
||||
impl CliCjsCodeAnalyzer {
|
||||
|
@ -63,11 +66,13 @@ impl CliCjsCodeAnalyzer {
|
|||
cache: NodeAnalysisCache,
|
||||
fs: deno_fs::FileSystemRc,
|
||||
node_resolver: Arc<CliNodeResolver>,
|
||||
parsed_source_cache: Option<Arc<ParsedSourceCache>>,
|
||||
) -> Self {
|
||||
Self {
|
||||
cache,
|
||||
fs,
|
||||
node_resolver,
|
||||
parsed_source_cache,
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -107,18 +112,26 @@ impl CliCjsCodeAnalyzer {
|
|||
}
|
||||
}
|
||||
|
||||
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 = deno_ast::parse_program(deno_ast::ParseParams {
|
||||
specifier,
|
||||
text: source,
|
||||
media_type,
|
||||
capture_tokens: true,
|
||||
scope_analysis: false,
|
||||
maybe_syntax: None,
|
||||
})?;
|
||||
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,
|
||||
})
|
||||
})?;
|
||||
if parsed_source.is_script() {
|
||||
let analysis = parsed_source.analyze_cjs();
|
||||
Ok(CliCjsAnalysis::Cjs {
|
||||
|
|
|
@ -43,7 +43,6 @@ use node_resolver::NodeModuleKind;
|
|||
use node_resolver::NodeResolution;
|
||||
use node_resolver::NodeResolutionMode;
|
||||
use node_resolver::PackageJson;
|
||||
use std::borrow::Cow;
|
||||
use std::path::Path;
|
||||
use std::path::PathBuf;
|
||||
use std::sync::Arc;
|
||||
|
@ -53,7 +52,9 @@ use crate::args::DENO_DISABLE_PEDANTIC_NODE_WARNINGS;
|
|||
use crate::node::CliNodeCodeTranslator;
|
||||
use crate::npm::CliNpmResolver;
|
||||
use crate::npm::InnerCliNpmResolverRef;
|
||||
use crate::util::path::specifier_has_extension;
|
||||
use crate::util::sync::AtomicFlag;
|
||||
use crate::util::text_encoding::from_utf8_lossy_owned;
|
||||
|
||||
pub struct ModuleCodeStringSource {
|
||||
pub code: ModuleSourceCode,
|
||||
|
@ -215,7 +216,7 @@ impl CliNodeResolver {
|
|||
referrer: &ModuleSpecifier,
|
||||
mode: NodeResolutionMode,
|
||||
) -> Result<NodeResolution, NodeResolveError> {
|
||||
let referrer_kind = if self.cjs_resolutions.contains(referrer) {
|
||||
let referrer_kind = if self.cjs_resolutions.is_known_cjs(referrer) {
|
||||
NodeModuleKind::Cjs
|
||||
} else {
|
||||
NodeModuleKind::Esm
|
||||
|
@ -310,9 +311,7 @@ impl CliNodeResolver {
|
|||
if self.in_npm_package(&specifier) {
|
||||
let resolution =
|
||||
self.node_resolver.url_to_node_resolution(specifier)?;
|
||||
if let NodeResolution::CommonJs(specifier) = &resolution {
|
||||
self.cjs_resolutions.insert(specifier.clone());
|
||||
}
|
||||
let resolution = self.handle_node_resolution(resolution);
|
||||
return Ok(Some(resolution.into_url()));
|
||||
}
|
||||
}
|
||||
|
@ -333,12 +332,17 @@ impl CliNodeResolver {
|
|||
) -> NodeResolution {
|
||||
if let NodeResolution::CommonJs(specifier) = &resolution {
|
||||
// remember that this was a common js resolution
|
||||
self.cjs_resolutions.insert(specifier.clone());
|
||||
self.mark_cjs_resolution(specifier.clone());
|
||||
}
|
||||
resolution
|
||||
}
|
||||
|
||||
pub fn mark_cjs_resolution(&self, specifier: ModuleSpecifier) {
|
||||
self.cjs_resolutions.insert(specifier);
|
||||
}
|
||||
}
|
||||
|
||||
// todo(dsherret): move to module_loader.rs
|
||||
#[derive(Clone)]
|
||||
pub struct NpmModuleLoader {
|
||||
cjs_resolutions: Arc<CjsResolutionStore>,
|
||||
|
@ -362,18 +366,9 @@ impl NpmModuleLoader {
|
|||
}
|
||||
}
|
||||
|
||||
pub async fn load_if_in_npm_package(
|
||||
&self,
|
||||
specifier: &ModuleSpecifier,
|
||||
maybe_referrer: Option<&ModuleSpecifier>,
|
||||
) -> Option<Result<ModuleCodeStringSource, AnyError>> {
|
||||
if self.node_resolver.in_npm_package(specifier)
|
||||
|| (specifier.scheme() == "file" && specifier.path().ends_with(".cjs"))
|
||||
{
|
||||
Some(self.load(specifier, maybe_referrer).await)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
pub fn if_in_npm_package(&self, specifier: &ModuleSpecifier) -> bool {
|
||||
self.node_resolver.in_npm_package(specifier)
|
||||
|| self.cjs_resolutions.is_known_cjs(specifier)
|
||||
}
|
||||
|
||||
pub async fn load(
|
||||
|
@ -418,16 +413,9 @@ impl NpmModuleLoader {
|
|||
}
|
||||
})?;
|
||||
|
||||
let code = if self.cjs_resolutions.contains(specifier)
|
||||
|| (specifier.scheme() == "file" && specifier.path().ends_with(".cjs"))
|
||||
{
|
||||
let code = if self.cjs_resolutions.is_known_cjs(specifier) {
|
||||
// translate cjs to esm if it's cjs and inject node globals
|
||||
let code = match String::from_utf8_lossy(&code) {
|
||||
Cow::Owned(code) => code,
|
||||
// SAFETY: `String::from_utf8_lossy` guarantees that the result is valid
|
||||
// UTF-8 if `Cow::Borrowed` is returned.
|
||||
Cow::Borrowed(_) => unsafe { String::from_utf8_unchecked(code) },
|
||||
};
|
||||
let code = from_utf8_lossy_owned(code);
|
||||
ModuleSourceCode::String(
|
||||
self
|
||||
.node_code_translator
|
||||
|
@ -452,8 +440,12 @@ impl NpmModuleLoader {
|
|||
pub struct CjsResolutionStore(DashSet<ModuleSpecifier>);
|
||||
|
||||
impl CjsResolutionStore {
|
||||
pub fn contains(&self, specifier: &ModuleSpecifier) -> bool {
|
||||
self.0.contains(specifier)
|
||||
pub fn is_known_cjs(&self, specifier: &ModuleSpecifier) -> bool {
|
||||
if specifier.scheme() != "file" {
|
||||
return false;
|
||||
}
|
||||
|
||||
specifier_has_extension(specifier, "cjs") || self.0.contains(specifier)
|
||||
}
|
||||
|
||||
pub fn insert(&self, specifier: ModuleSpecifier) {
|
||||
|
|
|
@ -528,6 +528,7 @@
|
|||
"bare-node-builtins",
|
||||
"byonm",
|
||||
"cron",
|
||||
"detect-cjs",
|
||||
"ffi",
|
||||
"fs",
|
||||
"http",
|
||||
|
|
|
@ -622,6 +622,7 @@ impl<'a> DenoCompileBinaryWriter<'a> {
|
|||
unstable_config: UnstableConfig {
|
||||
legacy_flag_enabled: false,
|
||||
bare_node_builtins: cli_options.unstable_bare_node_builtins(),
|
||||
detect_cjs: cli_options.unstable_detect_cjs(),
|
||||
sloppy_imports: cli_options.unstable_sloppy_imports(),
|
||||
features: cli_options.unstable_features(),
|
||||
},
|
||||
|
|
|
@ -586,6 +586,7 @@ pub async fn run(
|
|||
node_analysis_cache,
|
||||
fs.clone(),
|
||||
cli_node_resolver.clone(),
|
||||
None,
|
||||
);
|
||||
let node_code_translator = Arc::new(NodeCodeTranslator::new(
|
||||
cjs_esm_code_analyzer,
|
||||
|
@ -651,7 +652,7 @@ pub async fn run(
|
|||
workspace_resolver,
|
||||
node_resolver: cli_node_resolver.clone(),
|
||||
npm_module_loader: Arc::new(NpmModuleLoader::new(
|
||||
cjs_resolutions,
|
||||
cjs_resolutions.clone(),
|
||||
node_code_translator,
|
||||
fs.clone(),
|
||||
cli_node_resolver,
|
||||
|
@ -696,6 +697,7 @@ pub async fn run(
|
|||
});
|
||||
let worker_factory = CliMainWorkerFactory::new(
|
||||
Arc::new(BlobStore::default()),
|
||||
cjs_resolutions,
|
||||
// Code cache is not supported for standalone binary yet.
|
||||
None,
|
||||
feature_checker,
|
||||
|
@ -738,6 +740,7 @@ pub async fn run(
|
|||
node_ipc: None,
|
||||
serve_port: None,
|
||||
serve_host: None,
|
||||
unstable_detect_cjs: metadata.unstable_config.detect_cjs,
|
||||
},
|
||||
);
|
||||
|
||||
|
|
|
@ -54,6 +54,16 @@ pub async fn compile(
|
|||
);
|
||||
}
|
||||
|
||||
if cli_options.unstable_detect_cjs() {
|
||||
log::warn!(
|
||||
concat!(
|
||||
"{} --unstable-detect-cjs is not properly supported in deno compile. ",
|
||||
"The compiled executable may encounter runtime errors.",
|
||||
),
|
||||
crate::colors::yellow("Warning"),
|
||||
);
|
||||
}
|
||||
|
||||
let output_path = resolve_compile_executable_output_path(
|
||||
http_client,
|
||||
&compile_flags,
|
||||
|
|
|
@ -1,6 +1,8 @@
|
|||
// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license.
|
||||
|
||||
use std::borrow::Cow;
|
||||
use std::ops::Range;
|
||||
use std::sync::Arc;
|
||||
|
||||
use base64::prelude::BASE64_STANDARD;
|
||||
use base64::Engine;
|
||||
|
@ -9,6 +11,15 @@ use deno_core::ModuleSourceCode;
|
|||
static SOURCE_MAP_PREFIX: &[u8] =
|
||||
b"//# sourceMappingURL=data:application/json;base64,";
|
||||
|
||||
pub fn from_utf8_lossy_owned(bytes: Vec<u8>) -> String {
|
||||
match String::from_utf8_lossy(&bytes) {
|
||||
Cow::Owned(code) => code,
|
||||
// SAFETY: `String::from_utf8_lossy` guarantees that the result is valid
|
||||
// UTF-8 if `Cow::Borrowed` is returned.
|
||||
Cow::Borrowed(_) => unsafe { String::from_utf8_unchecked(bytes) },
|
||||
}
|
||||
}
|
||||
|
||||
pub fn source_map_from_code(code: &[u8]) -> Option<Vec<u8>> {
|
||||
let range = find_source_map_range(code)?;
|
||||
let source_map_range = &code[range];
|
||||
|
@ -85,6 +96,13 @@ fn find_source_map_range(code: &[u8]) -> Option<Range<usize>> {
|
|||
}
|
||||
}
|
||||
|
||||
/// Converts an `Arc<str>` to an `Arc<[u8]>`.
|
||||
pub fn arc_str_to_bytes(arc_str: Arc<str>) -> Arc<[u8]> {
|
||||
let raw = Arc::into_raw(arc_str);
|
||||
// SAFETY: This is safe because they have the same memory layout.
|
||||
unsafe { Arc::from_raw(raw as *const [u8]) }
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use std::sync::Arc;
|
||||
|
|
|
@ -51,9 +51,11 @@ use crate::args::DenoSubcommand;
|
|||
use crate::args::StorageKeyResolver;
|
||||
use crate::errors;
|
||||
use crate::npm::CliNpmResolver;
|
||||
use crate::resolver::CjsResolutionStore;
|
||||
use crate::util::checksum;
|
||||
use crate::util::file_watcher::WatcherCommunicator;
|
||||
use crate::util::file_watcher::WatcherRestartMode;
|
||||
use crate::util::path::specifier_has_extension;
|
||||
use crate::version;
|
||||
|
||||
pub struct ModuleLoaderAndSourceMapGetter {
|
||||
|
@ -120,11 +122,13 @@ pub struct CliMainWorkerOptions {
|
|||
pub node_ipc: Option<i64>,
|
||||
pub serve_port: Option<u16>,
|
||||
pub serve_host: Option<String>,
|
||||
pub unstable_detect_cjs: bool,
|
||||
}
|
||||
|
||||
struct SharedWorkerState {
|
||||
blob_store: Arc<BlobStore>,
|
||||
broadcast_channel: InMemoryBroadcastChannel,
|
||||
cjs_resolution_store: Arc<CjsResolutionStore>,
|
||||
code_cache: Option<Arc<dyn code_cache::CodeCache>>,
|
||||
compiled_wasm_module_store: CompiledWasmModuleStore,
|
||||
feature_checker: Arc<FeatureChecker>,
|
||||
|
@ -422,6 +426,7 @@ impl CliMainWorkerFactory {
|
|||
#[allow(clippy::too_many_arguments)]
|
||||
pub fn new(
|
||||
blob_store: Arc<BlobStore>,
|
||||
cjs_resolution_store: Arc<CjsResolutionStore>,
|
||||
code_cache: Option<Arc<dyn code_cache::CodeCache>>,
|
||||
feature_checker: Arc<FeatureChecker>,
|
||||
fs: Arc<dyn deno_fs::FileSystem>,
|
||||
|
@ -441,6 +446,7 @@ impl CliMainWorkerFactory {
|
|||
shared: Arc::new(SharedWorkerState {
|
||||
blob_store,
|
||||
broadcast_channel: Default::default(),
|
||||
cjs_resolution_store,
|
||||
code_cache,
|
||||
compiled_wasm_module_store: Default::default(),
|
||||
feature_checker,
|
||||
|
@ -486,6 +492,9 @@ impl CliMainWorkerFactory {
|
|||
stdio: deno_runtime::deno_io::Stdio,
|
||||
) -> Result<CliMainWorker, AnyError> {
|
||||
let shared = &self.shared;
|
||||
let ModuleLoaderAndSourceMapGetter { module_loader } = shared
|
||||
.module_loader_factory
|
||||
.create_for_main(permissions.clone());
|
||||
let (main_module, is_main_cjs) = if let Ok(package_ref) =
|
||||
NpmPackageReqReference::from_specifier(&main_module)
|
||||
{
|
||||
|
@ -526,13 +535,28 @@ impl CliMainWorkerFactory {
|
|||
let is_main_cjs = matches!(node_resolution, NodeResolution::CommonJs(_));
|
||||
(node_resolution.into_url(), is_main_cjs)
|
||||
} else {
|
||||
let is_cjs = main_module.path().ends_with(".cjs");
|
||||
let is_maybe_cjs_js_ext = self.shared.options.unstable_detect_cjs
|
||||
&& specifier_has_extension(&main_module, "js")
|
||||
&& self
|
||||
.shared
|
||||
.node_resolver
|
||||
.get_closest_package_json(&main_module)
|
||||
.ok()
|
||||
.flatten()
|
||||
.map(|pkg_json| pkg_json.typ == "commonjs")
|
||||
.unwrap_or(false);
|
||||
let is_cjs = if is_maybe_cjs_js_ext {
|
||||
// fill the cjs resolution store by preparing the module load
|
||||
module_loader
|
||||
.prepare_load(&main_module, None, false)
|
||||
.await?;
|
||||
self.shared.cjs_resolution_store.is_known_cjs(&main_module)
|
||||
} else {
|
||||
specifier_has_extension(&main_module, "cjs")
|
||||
};
|
||||
(main_module, is_cjs)
|
||||
};
|
||||
|
||||
let ModuleLoaderAndSourceMapGetter { module_loader } = shared
|
||||
.module_loader_factory
|
||||
.create_for_main(permissions.clone());
|
||||
let maybe_inspector_server = shared.maybe_inspector_server.clone();
|
||||
|
||||
let create_web_worker_cb =
|
||||
|
|
24
tests/specs/compile/detect_cjs/__test__.jsonc
Normal file
24
tests/specs/compile/detect_cjs/__test__.jsonc
Normal file
|
@ -0,0 +1,24 @@
|
|||
{
|
||||
"tempDir": true,
|
||||
"steps": [{
|
||||
"if": "unix",
|
||||
"args": "compile --allow-read --output main main.js",
|
||||
"output": "compile.out"
|
||||
}, {
|
||||
"if": "unix",
|
||||
"commandName": "./main",
|
||||
"args": [],
|
||||
"output": "output.out",
|
||||
"exitCode": 1
|
||||
}, {
|
||||
"if": "windows",
|
||||
"args": "compile --allow-read --output main.exe main.js",
|
||||
"output": "compile.out"
|
||||
}, {
|
||||
"if": "windows",
|
||||
"commandName": "./main.exe",
|
||||
"args": [],
|
||||
"output": "output.out",
|
||||
"exitCode": 1
|
||||
}]
|
||||
}
|
3
tests/specs/compile/detect_cjs/add.js
Normal file
3
tests/specs/compile/detect_cjs/add.js
Normal file
|
@ -0,0 +1,3 @@
|
|||
module.exports.add = function (a, b) {
|
||||
return a + b;
|
||||
};
|
3
tests/specs/compile/detect_cjs/compile.out
Normal file
3
tests/specs/compile/detect_cjs/compile.out
Normal file
|
@ -0,0 +1,3 @@
|
|||
Warning --unstable-detect-cjs is not properly supported in deno compile. The compiled executable may encounter runtime errors.
|
||||
Check file:///[WILDLINE]/main.js
|
||||
Compile file:///[WILDLINE]
|
5
tests/specs/compile/detect_cjs/deno.json
Normal file
5
tests/specs/compile/detect_cjs/deno.json
Normal file
|
@ -0,0 +1,5 @@
|
|||
{
|
||||
"unstable": [
|
||||
"detect-cjs"
|
||||
]
|
||||
}
|
3
tests/specs/compile/detect_cjs/main.js
Normal file
3
tests/specs/compile/detect_cjs/main.js
Normal file
|
@ -0,0 +1,3 @@
|
|||
import { add } from "./add.js";
|
||||
|
||||
console.log(add(1, 2));
|
1
tests/specs/compile/detect_cjs/output.out
Normal file
1
tests/specs/compile/detect_cjs/output.out
Normal file
|
@ -0,0 +1 @@
|
|||
error: Module not found: file:///[WILDLINE]/add.js
|
3
tests/specs/compile/detect_cjs/package.json
Normal file
3
tests/specs/compile/detect_cjs/package.json
Normal file
|
@ -0,0 +1,3 @@
|
|||
{
|
||||
"type": "commonjs"
|
||||
}
|
34
tests/specs/run/package_json_type/commonjs/__test__.jsonc
Normal file
34
tests/specs/run/package_json_type/commonjs/__test__.jsonc
Normal file
|
@ -0,0 +1,34 @@
|
|||
{
|
||||
"tests": {
|
||||
"main_cjs": {
|
||||
"args": "run --allow-read=. main_cjs.js",
|
||||
"output": "3\n"
|
||||
},
|
||||
"main_esm": {
|
||||
"args": "run --allow-read=. main_esm.js",
|
||||
"output": "3\n"
|
||||
},
|
||||
"main_mix": {
|
||||
"args": "run --allow-read=. main_mix.js",
|
||||
"output": "main_mix.out",
|
||||
"exitCode": 1
|
||||
},
|
||||
"import_import_meta": {
|
||||
"args": "run import_import_meta.js",
|
||||
"output": "[WILDLINE]/import_meta.js\n"
|
||||
},
|
||||
"main_import_meta": {
|
||||
"args": "run main_esm_import_meta.js",
|
||||
"output": "main_esm_import_meta.out",
|
||||
"exitCode": 1
|
||||
},
|
||||
"not_import_meta": {
|
||||
"args": "run --allow-read=. not_import_meta.js",
|
||||
"output": "3\n"
|
||||
},
|
||||
"tla": {
|
||||
"args": "run --allow-read=. tla.js",
|
||||
"output": "loaded\n"
|
||||
}
|
||||
}
|
||||
}
|
3
tests/specs/run/package_json_type/commonjs/add.js
Normal file
3
tests/specs/run/package_json_type/commonjs/add.js
Normal file
|
@ -0,0 +1,3 @@
|
|||
module.exports.add = function (a, b) {
|
||||
return a + b;
|
||||
};
|
5
tests/specs/run/package_json_type/commonjs/deno.jsonc
Normal file
5
tests/specs/run/package_json_type/commonjs/deno.jsonc
Normal file
|
@ -0,0 +1,5 @@
|
|||
{
|
||||
"unstable": [
|
||||
"detect-cjs"
|
||||
]
|
||||
}
|
|
@ -0,0 +1,3 @@
|
|||
import { value } from "./import_meta.js";
|
||||
|
||||
console.log(value);
|
|
@ -0,0 +1 @@
|
|||
export const value = import.meta.url;
|
2
tests/specs/run/package_json_type/commonjs/main_cjs.js
Normal file
2
tests/specs/run/package_json_type/commonjs/main_cjs.js
Normal file
|
@ -0,0 +1,2 @@
|
|||
const { add } = require("./add");
|
||||
console.log(add(1, 2));
|
3
tests/specs/run/package_json_type/commonjs/main_esm.js
Normal file
3
tests/specs/run/package_json_type/commonjs/main_esm.js
Normal file
|
@ -0,0 +1,3 @@
|
|||
import { add } from "./add.js";
|
||||
|
||||
console.log(add(1, 2));
|
|
@ -0,0 +1,2 @@
|
|||
console.log(import.meta.url);
|
||||
console.log(require("./add"));
|
|
@ -0,0 +1,2 @@
|
|||
[WILDLINE]main_esm_import_meta.js
|
||||
error: Uncaught (in promise) ReferenceError: require is not defined[WILDCARD]
|
6
tests/specs/run/package_json_type/commonjs/main_mix.js
Normal file
6
tests/specs/run/package_json_type/commonjs/main_mix.js
Normal file
|
@ -0,0 +1,6 @@
|
|||
import { add } from "./add.js";
|
||||
|
||||
console.log(add(1, 2));
|
||||
|
||||
// will error
|
||||
console.log(require("./add").add(1, 2));
|
5
tests/specs/run/package_json_type/commonjs/main_mix.out
Normal file
5
tests/specs/run/package_json_type/commonjs/main_mix.out
Normal file
|
@ -0,0 +1,5 @@
|
|||
3
|
||||
error: Uncaught (in promise) ReferenceError: require is not defined
|
||||
console.log(require("./add").add(1, 2));
|
||||
^
|
||||
at file:///[WILDLINE]main_mix.js:[WILDLINE]
|
|
@ -0,0 +1,8 @@
|
|||
try {
|
||||
console.log(test.import.meta.url);
|
||||
} catch {
|
||||
// ignore
|
||||
}
|
||||
|
||||
// should work because this is not an ESM file
|
||||
console.log(require("./add").add(1, 2));
|
3
tests/specs/run/package_json_type/commonjs/package.json
Normal file
3
tests/specs/run/package_json_type/commonjs/package.json
Normal file
|
@ -0,0 +1,3 @@
|
|||
{
|
||||
"type": "commonjs"
|
||||
}
|
2
tests/specs/run/package_json_type/commonjs/tla.js
Normal file
2
tests/specs/run/package_json_type/commonjs/tla.js
Normal file
|
@ -0,0 +1,2 @@
|
|||
await new Promise((r) => r());
|
||||
console.log("loaded");
|
18
tests/specs/run/package_json_type/none/__test__.jsonc
Normal file
18
tests/specs/run/package_json_type/none/__test__.jsonc
Normal file
|
@ -0,0 +1,18 @@
|
|||
{
|
||||
"tests": {
|
||||
"main_cjs": {
|
||||
"args": "run --allow-read=. main_cjs.js",
|
||||
"output": "main_cjs.out",
|
||||
"exitCode": 1
|
||||
},
|
||||
"main_esm": {
|
||||
"args": "run --allow-read=. main_esm.js",
|
||||
"output": "main_esm.out",
|
||||
"exitCode": 1
|
||||
},
|
||||
"sub_folder_cjs": {
|
||||
"args": "run --allow-read=. sub_folder_cjs.js",
|
||||
"output": "3\n"
|
||||
}
|
||||
}
|
||||
}
|
3
tests/specs/run/package_json_type/none/add.js
Normal file
3
tests/specs/run/package_json_type/none/add.js
Normal file
|
@ -0,0 +1,3 @@
|
|||
module.exports.add = function (a, b) {
|
||||
return a + b;
|
||||
};
|
3
tests/specs/run/package_json_type/none/commonjs/add.js
Normal file
3
tests/specs/run/package_json_type/none/commonjs/add.js
Normal file
|
@ -0,0 +1,3 @@
|
|||
module.exports.add = function (a, b) {
|
||||
return a + b;
|
||||
};
|
|
@ -0,0 +1,3 @@
|
|||
{
|
||||
"type": "commonjs"
|
||||
}
|
5
tests/specs/run/package_json_type/none/deno.jsonc
Normal file
5
tests/specs/run/package_json_type/none/deno.jsonc
Normal file
|
@ -0,0 +1,5 @@
|
|||
{
|
||||
"unstable": [
|
||||
"detect-cjs"
|
||||
]
|
||||
}
|
2
tests/specs/run/package_json_type/none/main_cjs.js
Normal file
2
tests/specs/run/package_json_type/none/main_cjs.js
Normal file
|
@ -0,0 +1,2 @@
|
|||
const { add } = require("./add");
|
||||
console.log(add(1, 2));
|
4
tests/specs/run/package_json_type/none/main_cjs.out
Normal file
4
tests/specs/run/package_json_type/none/main_cjs.out
Normal file
|
@ -0,0 +1,4 @@
|
|||
error: Uncaught (in promise) ReferenceError: require is not defined
|
||||
const { add } = require("./add");
|
||||
^
|
||||
at file:///[WILDLINE]
|
3
tests/specs/run/package_json_type/none/main_esm.js
Normal file
3
tests/specs/run/package_json_type/none/main_esm.js
Normal file
|
@ -0,0 +1,3 @@
|
|||
import { add } from "./add.js";
|
||||
|
||||
console.log(add(1, 2));
|
4
tests/specs/run/package_json_type/none/main_esm.out
Normal file
4
tests/specs/run/package_json_type/none/main_esm.out
Normal file
|
@ -0,0 +1,4 @@
|
|||
error: Uncaught SyntaxError: The requested module './add.js' does not provide an export named 'add'
|
||||
import { add } from "./add.js";
|
||||
^
|
||||
at <anonymous> (file:///[WILDLINE])
|
2
tests/specs/run/package_json_type/none/package.json
Normal file
2
tests/specs/run/package_json_type/none/package.json
Normal file
|
@ -0,0 +1,2 @@
|
|||
{
|
||||
}
|
3
tests/specs/run/package_json_type/none/sub_folder_cjs.js
Normal file
3
tests/specs/run/package_json_type/none/sub_folder_cjs.js
Normal file
|
@ -0,0 +1,3 @@
|
|||
import { add } from "./commonjs/add.js";
|
||||
|
||||
console.log(add(1, 2));
|
Loading…
Reference in a new issue