1
0
Fork 0
mirror of https://github.com/denoland/deno.git synced 2024-11-24 15:19:26 -05:00

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>
This commit is contained in:
David Sherret 2024-11-01 12:27:00 -04:00 committed by GitHub
parent 4774eab64d
commit 826e42a5b5
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
143 changed files with 2418 additions and 1527 deletions

28
Cargo.lock generated
View file

@ -1279,9 +1279,9 @@ dependencies = [
[[package]] [[package]]
name = "deno_ast" name = "deno_ast"
version = "0.42.2" version = "0.43.3"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b2b9d03b1bbeeecdac54367f075d572131736d06c5be3bc49037855bc5ab1bbb" checksum = "48d00b724e06d2081a141ec1155756a0b465d413d8e2a7515221f61d482eb2ee"
dependencies = [ dependencies = [
"base64 0.21.7", "base64 0.21.7",
"deno_media_type", "deno_media_type",
@ -1356,9 +1356,9 @@ dependencies = [
[[package]] [[package]]
name = "deno_cache_dir" name = "deno_cache_dir"
version = "0.13.1" version = "0.13.2"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "693ca429aebf945de5fef30df232044f9f80be4cc5a5e7c8d767226c43880f5a" checksum = "08c1f52170cd7715f8006da54cde1444863a0d6fbd9c11d037a737db2dec8e22"
dependencies = [ dependencies = [
"base32", "base32",
"deno_media_type", "deno_media_type",
@ -1506,9 +1506,9 @@ dependencies = [
[[package]] [[package]]
name = "deno_doc" name = "deno_doc"
version = "0.154.0" version = "0.156.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "17e204e45b0d79750880114e37b34abe19ad0710d8435a8da8f23a528fe98de4" checksum = "2585b98d6ad76dae30bf2d7b6d71b8363cae041158b8780d14a2f4fe17590a61"
dependencies = [ dependencies = [
"anyhow", "anyhow",
"cfg-if", "cfg-if",
@ -1606,9 +1606,9 @@ dependencies = [
[[package]] [[package]]
name = "deno_graph" name = "deno_graph"
version = "0.83.4" version = "0.84.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5bd20bc0780071989c622cbfd5d4fb2e4fd05a247ccd7f791f13c8d2c3792228" checksum = "cd4f4a14aa069087be41c2998077b0453f0191747898f96e6343f700abfc2c18"
dependencies = [ dependencies = [
"anyhow", "anyhow",
"async-trait", "async-trait",
@ -1726,9 +1726,9 @@ dependencies = [
[[package]] [[package]]
name = "deno_lint" name = "deno_lint"
version = "0.67.0" version = "0.68.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "871b60e32bfb6c110cbb9b0688dbf048f81e5d347fe4ce5a42239263de9dd938" checksum = "bb994e6d1b18223df0a756c7948143b35682941d615edffef60d5b38822f38ac"
dependencies = [ dependencies = [
"anyhow", "anyhow",
"deno_ast", "deno_ast",
@ -1756,9 +1756,9 @@ dependencies = [
[[package]] [[package]]
name = "deno_media_type" name = "deno_media_type"
version = "0.1.4" version = "0.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a8978229b82552bf8457a0125aa20863f023619cfc21ebb007b1e571d68fd85b" checksum = "7fcf552fbdedbe81c89705349d7d2485c7051382b000dfddbdbf7fc25931cf83"
dependencies = [ dependencies = [
"data-url", "data-url",
"serde", "serde",
@ -2608,9 +2608,9 @@ dependencies = [
[[package]] [[package]]
name = "dprint-plugin-typescript" name = "dprint-plugin-typescript"
version = "0.93.0" version = "0.93.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e9308d98b923b7c0335c2ee1560199e3f2321b1be82803107b4ba4ed5dac46cc" checksum = "5abfd78fe3cde4f5a6699d65f760c8d44da130cf446b6f80a7a9bc6580e156ab"
dependencies = [ dependencies = [
"anyhow", "anyhow",
"deno_ast", "deno_ast",

View file

@ -45,12 +45,12 @@ license = "MIT"
repository = "https://github.com/denoland/deno" repository = "https://github.com/denoland/deno"
[workspace.dependencies] [workspace.dependencies]
deno_ast = { version = "=0.42.2", features = ["transpiling"] } deno_ast = { version = "=0.43.3", features = ["transpiling"] }
deno_core = { version = "0.316.0" } deno_core = { version = "0.316.0" }
deno_bench_util = { version = "0.169.0", path = "./bench_util" } deno_bench_util = { version = "0.169.0", path = "./bench_util" }
deno_lockfile = "=0.23.1" deno_lockfile = "=0.23.1"
deno_media_type = { version = "0.1.4", features = ["module_specifier"] } deno_media_type = { version = "0.2.0", features = ["module_specifier"] }
deno_npm = "=0.25.4" deno_npm = "=0.25.4"
deno_path_util = "=0.2.1" deno_path_util = "=0.2.1"
deno_permissions = { version = "0.35.0", path = "./runtime/permissions" } deno_permissions = { version = "0.35.0", path = "./runtime/permissions" }
@ -111,7 +111,7 @@ console_static_text = "=0.8.1"
dashmap = "5.5.3" dashmap = "5.5.3"
data-encoding = "2.3.3" data-encoding = "2.3.3"
data-url = "=0.3.0" data-url = "=0.3.0"
deno_cache_dir = "=0.13.1" deno_cache_dir = "=0.13.2"
deno_package_json = { version = "0.1.2", default-features = false } deno_package_json = { version = "0.1.2", default-features = false }
dlopen2 = "0.6.1" dlopen2 = "0.6.1"
ecb = "=0.1.2" ecb = "=0.1.2"

View file

@ -72,9 +72,9 @@ deno_ast = { workspace = true, features = ["bundler", "cjs", "codegen", "proposa
deno_cache_dir = { workspace = true } deno_cache_dir = { workspace = true }
deno_config = { version = "=0.37.2", features = ["workspace", "sync"] } deno_config = { version = "=0.37.2", features = ["workspace", "sync"] }
deno_core = { workspace = true, features = ["include_js_files_for_snapshotting"] } deno_core = { workspace = true, features = ["include_js_files_for_snapshotting"] }
deno_doc = { version = "0.154.0", default-features = false, features = ["rust", "html", "syntect"] } deno_doc = { version = "0.156.0", default-features = false, features = ["rust", "html", "syntect"] }
deno_graph = { version = "=0.83.4" } deno_graph = { version = "=0.84.1" }
deno_lint = { version = "=0.67.0", features = ["docs"] } deno_lint = { version = "=0.68.0", features = ["docs"] }
deno_lockfile.workspace = true deno_lockfile.workspace = true
deno_npm.workspace = true deno_npm.workspace = true
deno_package_json.workspace = true deno_package_json.workspace = true
@ -107,7 +107,7 @@ dotenvy = "0.15.7"
dprint-plugin-json = "=0.19.4" dprint-plugin-json = "=0.19.4"
dprint-plugin-jupyter = "=0.1.5" dprint-plugin-jupyter = "=0.1.5"
dprint-plugin-markdown = "=0.17.8" dprint-plugin-markdown = "=0.17.8"
dprint-plugin-typescript = "=0.93.0" dprint-plugin-typescript = "=0.93.1"
env_logger = "=0.10.0" env_logger = "=0.10.0"
fancy-regex = "=0.10.0" fancy-regex = "=0.10.0"
faster-hex.workspace = true faster-hex.workspace = true

View file

@ -201,6 +201,8 @@ pub fn ts_config_to_transpile_and_emit_options(
precompile_jsx_dynamic_props: None, precompile_jsx_dynamic_props: None,
transform_jsx, transform_jsx,
var_decl_imports: false, var_decl_imports: false,
// todo(dsherret): support verbatim_module_syntax here properly
verbatim_module_syntax: false,
}, },
deno_ast::EmitOptions { deno_ast::EmitOptions {
inline_sources: options.inline_sources, inline_sources: options.inline_sources,
@ -1602,6 +1604,15 @@ impl CliOptions {
} }
pub fn use_byonm(&self) -> bool { pub fn use_byonm(&self) -> bool {
if matches!(
self.sub_command(),
DenoSubcommand::Install(_)
| DenoSubcommand::Add(_)
| DenoSubcommand::Remove(_)
) {
// For `deno install/add/remove` we want to force the managed resolver so it can set up `node_modules/` directory.
return false;
}
if self.node_modules_dir().ok().flatten().is_none() if self.node_modules_dir().ok().flatten().is_none()
&& self.maybe_node_modules_folder.is_some() && self.maybe_node_modules_folder.is_some()
&& self && self

View file

@ -57,7 +57,7 @@ impl rusqlite::types::FromSql for CacheDBHash {
} }
/// What should the cache should do on failure? /// What should the cache should do on failure?
#[derive(Default)] #[derive(Debug, Default)]
pub enum CacheFailure { pub enum CacheFailure {
/// Return errors if failure mode otherwise unspecified. /// Return errors if failure mode otherwise unspecified.
#[default] #[default]
@ -69,6 +69,7 @@ pub enum CacheFailure {
} }
/// Configuration SQL and other parameters for a [`CacheDB`]. /// Configuration SQL and other parameters for a [`CacheDB`].
#[derive(Debug)]
pub struct CacheDBConfiguration { pub struct CacheDBConfiguration {
/// SQL to run for a new database. /// SQL to run for a new database.
pub table_initializer: &'static str, pub table_initializer: &'static str,
@ -98,6 +99,7 @@ impl CacheDBConfiguration {
} }
} }
#[derive(Debug)]
enum ConnectionState { enum ConnectionState {
Connected(Connection), Connected(Connection),
Blackhole, Blackhole,
@ -106,7 +108,7 @@ enum ConnectionState {
/// A cache database that eagerly initializes itself off-thread, preventing initialization operations /// A cache database that eagerly initializes itself off-thread, preventing initialization operations
/// from blocking the main thread. /// from blocking the main thread.
#[derive(Clone)] #[derive(Debug, Clone)]
pub struct CacheDB { pub struct CacheDB {
// TODO(mmastrac): We can probably simplify our thread-safe implementation here // TODO(mmastrac): We can probably simplify our thread-safe implementation here
conn: Arc<Mutex<OnceCell<ConnectionState>>>, conn: Arc<Mutex<OnceCell<ConnectionState>>>,

2
cli/cache/emit.rs vendored
View file

@ -10,6 +10,7 @@ use deno_core::unsync::sync::AtomicFlag;
use super::DiskCache; use super::DiskCache;
/// The cache that stores previously emitted files. /// The cache that stores previously emitted files.
#[derive(Debug)]
pub struct EmitCache { pub struct EmitCache {
disk_cache: DiskCache, disk_cache: DiskCache,
emit_failed_flag: AtomicFlag, emit_failed_flag: AtomicFlag,
@ -91,6 +92,7 @@ impl EmitCache {
const LAST_LINE_PREFIX: &str = "\n// denoCacheMetadata="; const LAST_LINE_PREFIX: &str = "\n// denoCacheMetadata=";
#[derive(Debug)]
struct EmitFileSerializer { struct EmitFileSerializer {
cli_version: &'static str, cli_version: &'static str,
} }

89
cli/cache/mod.rs vendored
View file

@ -8,14 +8,9 @@ use crate::file_fetcher::FetchOptions;
use crate::file_fetcher::FetchPermissionsOptionRef; use crate::file_fetcher::FetchPermissionsOptionRef;
use crate::file_fetcher::FileFetcher; use crate::file_fetcher::FileFetcher;
use crate::file_fetcher::FileOrRedirect; 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;
use crate::util::fs::atomic_write_file_with_retries_and_fs; use crate::util::fs::atomic_write_file_with_retries_and_fs;
use crate::util::fs::AtomicWriteFileFsAdapter; 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_ast::MediaType;
use deno_core::futures; use deno_core::futures;
@ -25,7 +20,9 @@ use deno_graph::source::CacheInfo;
use deno_graph::source::LoadFuture; use deno_graph::source::LoadFuture;
use deno_graph::source::LoadResponse; use deno_graph::source::LoadResponse;
use deno_graph::source::Loader; use deno_graph::source::Loader;
use deno_runtime::deno_fs;
use deno_runtime::deno_permissions::PermissionsContainer; use deno_runtime::deno_permissions::PermissionsContainer;
use node_resolver::InNpmPackageChecker;
use std::collections::HashMap; use std::collections::HashMap;
use std::path::Path; use std::path::Path;
use std::path::PathBuf; use std::path::PathBuf;
@ -60,7 +57,6 @@ pub use fast_check::FastCheckCache;
pub use incremental::IncrementalCache; pub use incremental::IncrementalCache;
pub use module_info::ModuleInfoCache; pub use module_info::ModuleInfoCache;
pub use node::NodeAnalysisCache; pub use node::NodeAnalysisCache;
pub use parsed_source::EsmOrCjsChecker;
pub use parsed_source::LazyGraphSourceParser; pub use parsed_source::LazyGraphSourceParser;
pub use parsed_source::ParsedSourceCache; pub use parsed_source::ParsedSourceCache;
@ -181,46 +177,40 @@ pub struct FetchCacherOptions {
pub permissions: PermissionsContainer, pub permissions: PermissionsContainer,
/// If we're publishing for `deno publish`. /// If we're publishing for `deno publish`.
pub is_deno_publish: bool, pub is_deno_publish: bool,
pub unstable_detect_cjs: bool,
} }
/// A "wrapper" for the FileFetcher and DiskCache for the Deno CLI that provides /// A "wrapper" for the FileFetcher and DiskCache for the Deno CLI that provides
/// a concise interface to the DENO_DIR when building module graphs. /// a concise interface to the DENO_DIR when building module graphs.
pub struct FetchCacher { pub struct FetchCacher {
pub file_header_overrides: HashMap<ModuleSpecifier, HashMap<String, String>>, pub file_header_overrides: HashMap<ModuleSpecifier, HashMap<String, String>>,
esm_or_cjs_checker: Arc<EsmOrCjsChecker>,
file_fetcher: Arc<FileFetcher>, file_fetcher: Arc<FileFetcher>,
fs: Arc<dyn deno_fs::FileSystem>,
global_http_cache: Arc<GlobalHttpCache>, global_http_cache: Arc<GlobalHttpCache>,
node_resolver: Arc<CliNodeResolver>, in_npm_pkg_checker: Arc<dyn InNpmPackageChecker>,
npm_resolver: Arc<dyn CliNpmResolver>,
module_info_cache: Arc<ModuleInfoCache>, module_info_cache: Arc<ModuleInfoCache>,
permissions: PermissionsContainer, permissions: PermissionsContainer,
is_deno_publish: bool, is_deno_publish: bool,
unstable_detect_cjs: bool,
cache_info_enabled: bool, cache_info_enabled: bool,
} }
impl FetchCacher { impl FetchCacher {
pub fn new( pub fn new(
esm_or_cjs_checker: Arc<EsmOrCjsChecker>,
file_fetcher: Arc<FileFetcher>, file_fetcher: Arc<FileFetcher>,
fs: Arc<dyn deno_fs::FileSystem>,
global_http_cache: Arc<GlobalHttpCache>, global_http_cache: Arc<GlobalHttpCache>,
node_resolver: Arc<CliNodeResolver>, in_npm_pkg_checker: Arc<dyn InNpmPackageChecker>,
npm_resolver: Arc<dyn CliNpmResolver>,
module_info_cache: Arc<ModuleInfoCache>, module_info_cache: Arc<ModuleInfoCache>,
options: FetchCacherOptions, options: FetchCacherOptions,
) -> Self { ) -> Self {
Self { Self {
file_fetcher, file_fetcher,
esm_or_cjs_checker, fs,
global_http_cache, global_http_cache,
node_resolver, in_npm_pkg_checker,
npm_resolver,
module_info_cache, module_info_cache,
file_header_overrides: options.file_header_overrides, file_header_overrides: options.file_header_overrides,
permissions: options.permissions, permissions: options.permissions,
is_deno_publish: options.is_deno_publish, is_deno_publish: options.is_deno_publish,
unstable_detect_cjs: options.unstable_detect_cjs,
cache_info_enabled: false, cache_info_enabled: false,
} }
} }
@ -271,72 +261,25 @@ impl Loader for FetchCacher {
) -> LoadFuture { ) -> LoadFuture {
use deno_graph::source::CacheSetting as LoaderCacheSetting; use deno_graph::source::CacheSetting as LoaderCacheSetting;
if specifier.scheme() == "file" { if specifier.scheme() == "file"
if specifier.path().contains("/node_modules/") { && specifier.path().contains("/node_modules/")
{
// The specifier might be in a completely different symlinked tree than // The specifier might be in a completely different symlinked tree than
// what the node_modules url is in (ex. `/my-project-1/node_modules` // what the node_modules url is in (ex. `/my-project-1/node_modules`
// symlinked to `/my-project-2/node_modules`), so first we checked if the path // symlinked to `/my-project-2/node_modules`), so first we checked if the path
// is in a node_modules dir to avoid needlessly canonicalizing, then now compare // is in a node_modules dir to avoid needlessly canonicalizing, then now compare
// against the canonicalized specifier. // against the canonicalized specifier.
let specifier = let specifier = crate::node::resolve_specifier_into_node_modules(
crate::node::resolve_specifier_into_node_modules(specifier); specifier,
if self.npm_resolver.in_npm_package(&specifier) { self.fs.as_ref(),
);
if self.in_npm_pkg_checker.in_npm_package(&specifier) {
return Box::pin(futures::future::ready(Ok(Some( return Box::pin(futures::future::ready(Ok(Some(
LoadResponse::External { specifier }, LoadResponse::External { specifier },
)))); ))));
} }
} }
// make local CJS modules external to the graph
if specifier_has_extension(specifier, "cjs") {
return Box::pin(futures::future::ready(Ok(Some(
LoadResponse::External {
specifier: specifier.clone(),
},
))));
}
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 if self.is_deno_publish
&& matches!(specifier.scheme(), "http" | "https") && matches!(specifier.scheme(), "http" | "https")
&& !specifier.as_str().starts_with(jsr_url().as_str()) && !specifier.as_str().starts_with(jsr_url().as_str())

View file

@ -44,18 +44,32 @@ pub static MODULE_INFO_CACHE_DB: CacheDBConfiguration = CacheDBConfiguration {
/// A cache of `deno_graph::ModuleInfo` objects. Using this leads to a considerable /// A cache of `deno_graph::ModuleInfo` objects. Using this leads to a considerable
/// performance improvement because when it exists we can skip parsing a module for /// performance improvement because when it exists we can skip parsing a module for
/// deno_graph. /// deno_graph.
#[derive(Debug)]
pub struct ModuleInfoCache { pub struct ModuleInfoCache {
conn: CacheDB, conn: CacheDB,
parsed_source_cache: Arc<ParsedSourceCache>,
} }
impl ModuleInfoCache { impl ModuleInfoCache {
#[cfg(test)] #[cfg(test)]
pub fn new_in_memory(version: &'static str) -> Self { pub fn new_in_memory(
Self::new(CacheDB::in_memory(&MODULE_INFO_CACHE_DB, version)) version: &'static str,
parsed_source_cache: Arc<ParsedSourceCache>,
) -> Self {
Self::new(
CacheDB::in_memory(&MODULE_INFO_CACHE_DB, version),
parsed_source_cache,
)
} }
pub fn new(conn: CacheDB) -> Self { pub fn new(
Self { conn } conn: CacheDB,
parsed_source_cache: Arc<ParsedSourceCache>,
) -> Self {
Self {
conn,
parsed_source_cache,
}
} }
/// Useful for testing: re-create this cache DB with a different current version. /// Useful for testing: re-create this cache DB with a different current version.
@ -63,6 +77,7 @@ impl ModuleInfoCache {
pub(crate) fn recreate_with_version(self, version: &'static str) -> Self { pub(crate) fn recreate_with_version(self, version: &'static str) -> Self {
Self { Self {
conn: self.conn.recreate_with_version(version), conn: self.conn.recreate_with_version(version),
parsed_source_cache: self.parsed_source_cache,
} }
} }
@ -113,13 +128,10 @@ impl ModuleInfoCache {
Ok(()) Ok(())
} }
pub fn as_module_analyzer<'a>( pub fn as_module_analyzer(&self) -> ModuleInfoCacheModuleAnalyzer {
&'a self,
parsed_source_cache: &'a Arc<ParsedSourceCache>,
) -> ModuleInfoCacheModuleAnalyzer<'a> {
ModuleInfoCacheModuleAnalyzer { ModuleInfoCacheModuleAnalyzer {
module_info_cache: self, module_info_cache: self,
parsed_source_cache, parsed_source_cache: &self.parsed_source_cache,
} }
} }
} }
@ -129,6 +141,84 @@ pub struct ModuleInfoCacheModuleAnalyzer<'a> {
parsed_source_cache: &'a Arc<ParsedSourceCache>, parsed_source_cache: &'a Arc<ParsedSourceCache>,
} }
impl<'a> ModuleInfoCacheModuleAnalyzer<'a> {
fn load_cached_module_info(
&self,
specifier: &ModuleSpecifier,
media_type: MediaType,
source_hash: CacheDBHash,
) -> Option<ModuleInfo> {
match self.module_info_cache.get_module_info(
specifier,
media_type,
source_hash,
) {
Ok(Some(info)) => Some(info),
Ok(None) => None,
Err(err) => {
log::debug!(
"Error loading module cache info for {}. {:#}",
specifier,
err
);
None
}
}
}
fn save_module_info_to_cache(
&self,
specifier: &ModuleSpecifier,
media_type: MediaType,
source_hash: CacheDBHash,
module_info: &ModuleInfo,
) {
if let Err(err) = self.module_info_cache.set_module_info(
specifier,
media_type,
source_hash,
module_info,
) {
log::debug!(
"Error saving module cache info for {}. {:#}",
specifier,
err
);
}
}
pub fn analyze_sync(
&self,
specifier: &ModuleSpecifier,
media_type: MediaType,
source: &Arc<str>,
) -> Result<ModuleInfo, deno_ast::ParseDiagnostic> {
// attempt to load from the cache
let source_hash = CacheDBHash::from_source(source);
if let Some(info) =
self.load_cached_module_info(specifier, media_type, source_hash)
{
return Ok(info);
}
// otherwise, get the module info from the parsed source cache
let parser = self.parsed_source_cache.as_capturing_parser();
let analyzer = ParserModuleAnalyzer::new(&parser);
let module_info =
analyzer.analyze_sync(specifier, source.clone(), media_type)?;
// then attempt to cache it
self.save_module_info_to_cache(
specifier,
media_type,
source_hash,
&module_info,
);
Ok(module_info)
}
}
#[async_trait::async_trait(?Send)] #[async_trait::async_trait(?Send)]
impl<'a> deno_graph::ModuleAnalyzer for ModuleInfoCacheModuleAnalyzer<'a> { impl<'a> deno_graph::ModuleAnalyzer for ModuleInfoCacheModuleAnalyzer<'a> {
async fn analyze( async fn analyze(
@ -139,20 +229,10 @@ impl<'a> deno_graph::ModuleAnalyzer for ModuleInfoCacheModuleAnalyzer<'a> {
) -> Result<ModuleInfo, deno_ast::ParseDiagnostic> { ) -> Result<ModuleInfo, deno_ast::ParseDiagnostic> {
// attempt to load from the cache // attempt to load from the cache
let source_hash = CacheDBHash::from_source(&source); let source_hash = CacheDBHash::from_source(&source);
match self.module_info_cache.get_module_info( if let Some(info) =
specifier, self.load_cached_module_info(specifier, media_type, source_hash)
media_type, {
source_hash, return Ok(info);
) {
Ok(Some(info)) => return Ok(info),
Ok(None) => {}
Err(err) => {
log::debug!(
"Error loading module cache info for {}. {:#}",
specifier,
err
);
}
} }
// otherwise, get the module info from the parsed source cache // otherwise, get the module info from the parsed source cache
@ -169,18 +249,12 @@ impl<'a> deno_graph::ModuleAnalyzer for ModuleInfoCacheModuleAnalyzer<'a> {
.unwrap()?; .unwrap()?;
// then attempt to cache it // then attempt to cache it
if let Err(err) = self.module_info_cache.set_module_info( self.save_module_info_to_cache(
specifier, specifier,
media_type, media_type,
source_hash, source_hash,
&module_info, &module_info,
) {
log::debug!(
"Error saving module cache info for {}. {:#}",
specifier,
err
); );
}
Ok(module_info) Ok(module_info)
} }
@ -202,7 +276,7 @@ fn serialize_media_type(media_type: MediaType) -> i64 {
Tsx => 11, Tsx => 11,
Json => 12, Json => 12,
Wasm => 13, Wasm => 13,
TsBuildInfo => 14, Css => 14,
SourceMap => 15, SourceMap => 15,
Unknown => 16, Unknown => 16,
} }
@ -217,7 +291,7 @@ mod test {
#[test] #[test]
pub fn module_info_cache_general_use() { pub fn module_info_cache_general_use() {
let cache = ModuleInfoCache::new_in_memory("1.0.0"); let cache = ModuleInfoCache::new_in_memory("1.0.0", Default::default());
let specifier1 = let specifier1 =
ModuleSpecifier::parse("https://localhost/mod.ts").unwrap(); ModuleSpecifier::parse("https://localhost/mod.ts").unwrap();
let specifier2 = let specifier2 =

View file

@ -5,12 +5,11 @@ use std::sync::Arc;
use deno_ast::MediaType; use deno_ast::MediaType;
use deno_ast::ModuleSpecifier; use deno_ast::ModuleSpecifier;
use deno_ast::ParseDiagnostic;
use deno_ast::ParsedSource; use deno_ast::ParsedSource;
use deno_core::parking_lot::Mutex; use deno_core::parking_lot::Mutex;
use deno_graph::CapturingModuleParser; use deno_graph::CapturingEsParser;
use deno_graph::DefaultModuleParser; use deno_graph::DefaultEsParser;
use deno_graph::ModuleParser; use deno_graph::EsParser;
use deno_graph::ParseOptions; use deno_graph::ParseOptions;
use deno_graph::ParsedSourceStore; use deno_graph::ParsedSourceStore;
@ -47,7 +46,7 @@ impl<'a> LazyGraphSourceParser<'a> {
} }
} }
#[derive(Default)] #[derive(Debug, Default)]
pub struct ParsedSourceCache { pub struct ParsedSourceCache {
sources: Mutex<HashMap<ModuleSpecifier, ParsedSource>>, sources: Mutex<HashMap<ModuleSpecifier, ParsedSource>>,
} }
@ -58,12 +57,11 @@ impl ParsedSourceCache {
module: &deno_graph::JsModule, module: &deno_graph::JsModule,
) -> Result<ParsedSource, deno_ast::ParseDiagnostic> { ) -> Result<ParsedSource, deno_ast::ParseDiagnostic> {
let parser = self.as_capturing_parser(); let parser = self.as_capturing_parser();
// this will conditionally parse because it's using a CapturingModuleParser // this will conditionally parse because it's using a CapturingEsParser
parser.parse_module(ParseOptions { parser.parse_program(ParseOptions {
specifier: &module.specifier, specifier: &module.specifier,
source: module.source.clone(), source: module.source.clone(),
media_type: module.media_type, media_type: module.media_type,
// don't bother enabling because this method is currently only used for vendoring
scope_analysis: false, scope_analysis: false,
}) })
} }
@ -87,10 +85,9 @@ impl ParsedSourceCache {
specifier, specifier,
source, source,
media_type, media_type,
// don't bother enabling because this method is currently only used for emitting
scope_analysis: false, scope_analysis: false,
}; };
DefaultModuleParser.parse_module(options) DefaultEsParser.parse_program(options)
} }
/// Frees the parsed source from memory. /// Frees the parsed source from memory.
@ -100,8 +97,8 @@ impl ParsedSourceCache {
/// Creates a parser that will reuse a ParsedSource from the store /// Creates a parser that will reuse a ParsedSource from the store
/// if it exists, or else parse. /// if it exists, or else parse.
pub fn as_capturing_parser(&self) -> CapturingModuleParser { pub fn as_capturing_parser(&self) -> CapturingEsParser {
CapturingModuleParser::new(None, self) CapturingEsParser::new(None, self)
} }
} }
@ -150,42 +147,3 @@ 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())
}
}

View file

@ -3,11 +3,14 @@
use crate::cache::EmitCache; use crate::cache::EmitCache;
use crate::cache::FastInsecureHasher; use crate::cache::FastInsecureHasher;
use crate::cache::ParsedSourceCache; use crate::cache::ParsedSourceCache;
use crate::resolver::CjsTracker;
use deno_ast::ModuleKind;
use deno_ast::SourceMapOption; use deno_ast::SourceMapOption;
use deno_ast::SourceRange; use deno_ast::SourceRange;
use deno_ast::SourceRanged; use deno_ast::SourceRanged;
use deno_ast::SourceRangedForSpanned; use deno_ast::SourceRangedForSpanned;
use deno_ast::TranspileModuleOptions;
use deno_ast::TranspileResult; use deno_ast::TranspileResult;
use deno_core::error::AnyError; use deno_core::error::AnyError;
use deno_core::futures::stream::FuturesUnordered; use deno_core::futures::stream::FuturesUnordered;
@ -19,7 +22,9 @@ use deno_graph::Module;
use deno_graph::ModuleGraph; use deno_graph::ModuleGraph;
use std::sync::Arc; use std::sync::Arc;
#[derive(Debug)]
pub struct Emitter { pub struct Emitter {
cjs_tracker: Arc<CjsTracker>,
emit_cache: Arc<EmitCache>, emit_cache: Arc<EmitCache>,
parsed_source_cache: Arc<ParsedSourceCache>, parsed_source_cache: Arc<ParsedSourceCache>,
transpile_and_emit_options: transpile_and_emit_options:
@ -30,6 +35,7 @@ pub struct Emitter {
impl Emitter { impl Emitter {
pub fn new( pub fn new(
cjs_tracker: Arc<CjsTracker>,
emit_cache: Arc<EmitCache>, emit_cache: Arc<EmitCache>,
parsed_source_cache: Arc<ParsedSourceCache>, parsed_source_cache: Arc<ParsedSourceCache>,
transpile_options: deno_ast::TranspileOptions, transpile_options: deno_ast::TranspileOptions,
@ -42,6 +48,7 @@ impl Emitter {
hasher.finish() hasher.finish()
}; };
Self { Self {
cjs_tracker,
emit_cache, emit_cache,
parsed_source_cache, parsed_source_cache,
transpile_and_emit_options: Arc::new((transpile_options, emit_options)), transpile_and_emit_options: Arc::new((transpile_options, emit_options)),
@ -59,21 +66,19 @@ impl Emitter {
continue; continue;
}; };
// todo(https://github.com/denoland/deno_media_type/pull/12): use is_emittable() if module.media_type.is_emittable() {
let is_emittable = matches!(
module.media_type,
MediaType::TypeScript
| MediaType::Mts
| MediaType::Cts
| MediaType::Jsx
| MediaType::Tsx
);
if is_emittable {
futures.push( futures.push(
self self
.emit_parsed_source( .emit_parsed_source(
&module.specifier, &module.specifier,
module.media_type, module.media_type,
ModuleKind::from_is_cjs(
self.cjs_tracker.is_cjs_with_known_is_script(
&module.specifier,
module.media_type,
module.is_script,
)?,
),
&module.source, &module.source,
) )
.boxed_local(), .boxed_local(),
@ -92,9 +97,10 @@ impl Emitter {
pub fn maybe_cached_emit( pub fn maybe_cached_emit(
&self, &self,
specifier: &ModuleSpecifier, specifier: &ModuleSpecifier,
module_kind: deno_ast::ModuleKind,
source: &str, source: &str,
) -> Option<String> { ) -> Option<String> {
let source_hash = self.get_source_hash(source); let source_hash = self.get_source_hash(module_kind, source);
self.emit_cache.get_emit_code(specifier, source_hash) self.emit_cache.get_emit_code(specifier, source_hash)
} }
@ -102,11 +108,12 @@ impl Emitter {
&self, &self,
specifier: &ModuleSpecifier, specifier: &ModuleSpecifier,
media_type: MediaType, media_type: MediaType,
module_kind: deno_ast::ModuleKind,
source: &Arc<str>, source: &Arc<str>,
) -> Result<String, AnyError> { ) -> Result<String, AnyError> {
// Note: keep this in sync with the sync version below // Note: keep this in sync with the sync version below
let helper = EmitParsedSourceHelper(self); let helper = EmitParsedSourceHelper(self);
match helper.pre_emit_parsed_source(specifier, source) { match helper.pre_emit_parsed_source(specifier, module_kind, source) {
PreEmitResult::Cached(emitted_text) => Ok(emitted_text), PreEmitResult::Cached(emitted_text) => Ok(emitted_text),
PreEmitResult::NotCached { source_hash } => { PreEmitResult::NotCached { source_hash } => {
let parsed_source_cache = self.parsed_source_cache.clone(); let parsed_source_cache = self.parsed_source_cache.clone();
@ -119,8 +126,9 @@ impl Emitter {
EmitParsedSourceHelper::transpile( EmitParsedSourceHelper::transpile(
&parsed_source_cache, &parsed_source_cache,
&specifier, &specifier,
source.clone(),
media_type, media_type,
module_kind,
source.clone(),
&transpile_and_emit_options.0, &transpile_and_emit_options.0,
&transpile_and_emit_options.1, &transpile_and_emit_options.1,
) )
@ -142,18 +150,20 @@ impl Emitter {
&self, &self,
specifier: &ModuleSpecifier, specifier: &ModuleSpecifier,
media_type: MediaType, media_type: MediaType,
module_kind: deno_ast::ModuleKind,
source: &Arc<str>, source: &Arc<str>,
) -> Result<String, AnyError> { ) -> Result<String, AnyError> {
// Note: keep this in sync with the async version above // Note: keep this in sync with the async version above
let helper = EmitParsedSourceHelper(self); let helper = EmitParsedSourceHelper(self);
match helper.pre_emit_parsed_source(specifier, source) { match helper.pre_emit_parsed_source(specifier, module_kind, source) {
PreEmitResult::Cached(emitted_text) => Ok(emitted_text), PreEmitResult::Cached(emitted_text) => Ok(emitted_text),
PreEmitResult::NotCached { source_hash } => { PreEmitResult::NotCached { source_hash } => {
let transpiled_source = EmitParsedSourceHelper::transpile( let transpiled_source = EmitParsedSourceHelper::transpile(
&self.parsed_source_cache, &self.parsed_source_cache,
specifier, specifier,
source.clone(),
media_type, media_type,
module_kind,
source.clone(),
&self.transpile_and_emit_options.0, &self.transpile_and_emit_options.0,
&self.transpile_and_emit_options.1, &self.transpile_and_emit_options.1,
)?; )?;
@ -171,6 +181,7 @@ impl Emitter {
pub async fn load_and_emit_for_hmr( pub async fn load_and_emit_for_hmr(
&self, &self,
specifier: &ModuleSpecifier, specifier: &ModuleSpecifier,
module_kind: deno_ast::ModuleKind,
) -> Result<String, AnyError> { ) -> Result<String, AnyError> {
let media_type = MediaType::from_specifier(specifier); let media_type = MediaType::from_specifier(specifier);
let source_code = tokio::fs::read_to_string( let source_code = tokio::fs::read_to_string(
@ -193,9 +204,14 @@ impl Emitter {
let mut options = self.transpile_and_emit_options.1.clone(); let mut options = self.transpile_and_emit_options.1.clone();
options.source_map = SourceMapOption::None; options.source_map = SourceMapOption::None;
let transpiled_source = parsed_source let transpiled_source = parsed_source
.transpile(&self.transpile_and_emit_options.0, &options)? .transpile(
.into_source() &self.transpile_and_emit_options.0,
.into_string()?; &deno_ast::TranspileModuleOptions {
module_kind: Some(module_kind),
},
&options,
)?
.into_source();
Ok(transpiled_source.text) Ok(transpiled_source.text)
} }
MediaType::JavaScript MediaType::JavaScript
@ -206,7 +222,7 @@ impl Emitter {
| MediaType::Dcts | MediaType::Dcts
| MediaType::Json | MediaType::Json
| MediaType::Wasm | MediaType::Wasm
| MediaType::TsBuildInfo | MediaType::Css
| MediaType::SourceMap | MediaType::SourceMap
| MediaType::Unknown => { | MediaType::Unknown => {
// clear this specifier from the parsed source cache as it's now out of date // clear this specifier from the parsed source cache as it's now out of date
@ -219,10 +235,11 @@ impl Emitter {
/// A hashing function that takes the source code and uses the global emit /// A hashing function that takes the source code and uses the global emit
/// options then generates a string hash which can be stored to /// options then generates a string hash which can be stored to
/// determine if the cached emit is valid or not. /// determine if the cached emit is valid or not.
fn get_source_hash(&self, source_text: &str) -> u64 { fn get_source_hash(&self, module_kind: ModuleKind, source_text: &str) -> u64 {
FastInsecureHasher::new_without_deno_version() // stored in the transpile_and_emit_options_hash FastInsecureHasher::new_without_deno_version() // stored in the transpile_and_emit_options_hash
.write_str(source_text) .write_str(source_text)
.write_u64(self.transpile_and_emit_options_hash) .write_u64(self.transpile_and_emit_options_hash)
.write_hashable(module_kind)
.finish() .finish()
} }
} }
@ -239,9 +256,10 @@ impl<'a> EmitParsedSourceHelper<'a> {
pub fn pre_emit_parsed_source( pub fn pre_emit_parsed_source(
&self, &self,
specifier: &ModuleSpecifier, specifier: &ModuleSpecifier,
module_kind: deno_ast::ModuleKind,
source: &Arc<str>, source: &Arc<str>,
) -> PreEmitResult { ) -> PreEmitResult {
let source_hash = self.0.get_source_hash(source); let source_hash = self.0.get_source_hash(module_kind, source);
if let Some(emit_code) = if let Some(emit_code) =
self.0.emit_cache.get_emit_code(specifier, source_hash) self.0.emit_cache.get_emit_code(specifier, source_hash)
@ -255,8 +273,9 @@ impl<'a> EmitParsedSourceHelper<'a> {
pub fn transpile( pub fn transpile(
parsed_source_cache: &ParsedSourceCache, parsed_source_cache: &ParsedSourceCache,
specifier: &ModuleSpecifier, specifier: &ModuleSpecifier,
source: Arc<str>,
media_type: MediaType, media_type: MediaType,
module_kind: deno_ast::ModuleKind,
source: Arc<str>,
transpile_options: &deno_ast::TranspileOptions, transpile_options: &deno_ast::TranspileOptions,
emit_options: &deno_ast::EmitOptions, emit_options: &deno_ast::EmitOptions,
) -> Result<String, AnyError> { ) -> Result<String, AnyError> {
@ -265,8 +284,13 @@ impl<'a> EmitParsedSourceHelper<'a> {
let parsed_source = parsed_source_cache let parsed_source = parsed_source_cache
.remove_or_parse_module(specifier, source, media_type)?; .remove_or_parse_module(specifier, source, media_type)?;
ensure_no_import_assertion(&parsed_source)?; ensure_no_import_assertion(&parsed_source)?;
let transpile_result = let transpile_result = parsed_source.transpile(
parsed_source.transpile(transpile_options, emit_options)?; transpile_options,
&TranspileModuleOptions {
module_kind: Some(module_kind),
},
emit_options,
)?;
let transpiled_source = match transpile_result { let transpiled_source = match transpile_result {
TranspileResult::Owned(source) => source, TranspileResult::Owned(source) => source,
TranspileResult::Cloned(source) => { TranspileResult::Cloned(source) => {
@ -275,8 +299,7 @@ impl<'a> EmitParsedSourceHelper<'a> {
} }
}; };
debug_assert!(transpiled_source.source_map.is_none()); debug_assert!(transpiled_source.source_map.is_none());
let text = String::from_utf8(transpiled_source.source)?; Ok(transpiled_source.text)
Ok(text)
} }
pub fn post_emit_parsed_source( pub fn post_emit_parsed_source(
@ -321,7 +344,7 @@ fn ensure_no_import_assertion(
deno_core::anyhow::anyhow!("{}", msg) deno_core::anyhow::anyhow!("{}", msg)
} }
let Some(module) = parsed_source.program_ref().as_module() else { let deno_ast::ProgramRef::Module(module) = parsed_source.program_ref() else {
return Ok(()); return Ok(());
}; };

View file

@ -11,10 +11,10 @@ use crate::args::StorageKeyResolver;
use crate::args::TsConfigType; use crate::args::TsConfigType;
use crate::cache::Caches; use crate::cache::Caches;
use crate::cache::CodeCache; use crate::cache::CodeCache;
use crate::cache::DenoCacheEnvFsAdapter;
use crate::cache::DenoDir; use crate::cache::DenoDir;
use crate::cache::DenoDirProvider; use crate::cache::DenoDirProvider;
use crate::cache::EmitCache; use crate::cache::EmitCache;
use crate::cache::EsmOrCjsChecker;
use crate::cache::GlobalHttpCache; use crate::cache::GlobalHttpCache;
use crate::cache::HttpCache; use crate::cache::HttpCache;
use crate::cache::LocalHttpCache; use crate::cache::LocalHttpCache;
@ -33,12 +33,16 @@ use crate::module_loader::ModuleLoadPreparer;
use crate::node::CliCjsCodeAnalyzer; use crate::node::CliCjsCodeAnalyzer;
use crate::node::CliNodeCodeTranslator; use crate::node::CliNodeCodeTranslator;
use crate::npm::create_cli_npm_resolver; use crate::npm::create_cli_npm_resolver;
use crate::npm::create_in_npm_pkg_checker;
use crate::npm::CliByonmNpmResolverCreateOptions; use crate::npm::CliByonmNpmResolverCreateOptions;
use crate::npm::CliManagedInNpmPkgCheckerCreateOptions;
use crate::npm::CliManagedNpmResolverCreateOptions;
use crate::npm::CliNpmResolver; use crate::npm::CliNpmResolver;
use crate::npm::CliNpmResolverCreateOptions; use crate::npm::CliNpmResolverCreateOptions;
use crate::npm::CliNpmResolverManagedCreateOptions;
use crate::npm::CliNpmResolverManagedSnapshotOption; use crate::npm::CliNpmResolverManagedSnapshotOption;
use crate::resolver::CjsResolutionStore; use crate::npm::CreateInNpmPkgCheckerOptions;
use crate::resolver::CjsTracker;
use crate::resolver::CjsTrackerOptions;
use crate::resolver::CliDenoResolverFs; use crate::resolver::CliDenoResolverFs;
use crate::resolver::CliGraphResolver; use crate::resolver::CliGraphResolver;
use crate::resolver::CliGraphResolverOptions; use crate::resolver::CliGraphResolverOptions;
@ -51,6 +55,7 @@ use crate::tools::check::TypeChecker;
use crate::tools::coverage::CoverageCollector; use crate::tools::coverage::CoverageCollector;
use crate::tools::lint::LintRuleProvider; use crate::tools::lint::LintRuleProvider;
use crate::tools::run::hmr::HmrRunner; use crate::tools::run::hmr::HmrRunner;
use crate::tsc::TypeCheckingCjsTracker;
use crate::util::file_watcher::WatcherCommunicator; use crate::util::file_watcher::WatcherCommunicator;
use crate::util::fs::canonicalize_path_maybe_not_exists; use crate::util::fs::canonicalize_path_maybe_not_exists;
use crate::util::progress_bar::ProgressBar; use crate::util::progress_bar::ProgressBar;
@ -59,6 +64,7 @@ use crate::worker::CliMainWorkerFactory;
use crate::worker::CliMainWorkerOptions; use crate::worker::CliMainWorkerOptions;
use std::path::PathBuf; use std::path::PathBuf;
use deno_cache_dir::npm::NpmCacheDir;
use deno_config::workspace::PackageJsonDepResolution; use deno_config::workspace::PackageJsonDepResolution;
use deno_config::workspace::WorkspaceResolver; use deno_config::workspace::WorkspaceResolver;
use deno_core::error::AnyError; use deno_core::error::AnyError;
@ -68,6 +74,7 @@ use deno_core::FeatureChecker;
use deno_runtime::deno_fs; use deno_runtime::deno_fs;
use deno_runtime::deno_node::DenoFsNodeResolverEnv; use deno_runtime::deno_node::DenoFsNodeResolverEnv;
use deno_runtime::deno_node::NodeResolver; use deno_runtime::deno_node::NodeResolver;
use deno_runtime::deno_node::PackageJsonResolver;
use deno_runtime::deno_permissions::Permissions; use deno_runtime::deno_permissions::Permissions;
use deno_runtime::deno_permissions::PermissionsContainer; use deno_runtime::deno_permissions::PermissionsContainer;
use deno_runtime::deno_tls::rustls::RootCertStore; use deno_runtime::deno_tls::rustls::RootCertStore;
@ -77,6 +84,7 @@ use deno_runtime::inspector_server::InspectorServer;
use deno_runtime::permissions::RuntimePermissionDescriptorParser; use deno_runtime::permissions::RuntimePermissionDescriptorParser;
use log::warn; use log::warn;
use node_resolver::analyze::NodeCodeTranslator; use node_resolver::analyze::NodeCodeTranslator;
use node_resolver::InNpmPackageChecker;
use once_cell::sync::OnceCell; use once_cell::sync::OnceCell;
use std::future::Future; use std::future::Future;
use std::sync::Arc; use std::sync::Arc;
@ -164,39 +172,41 @@ impl<T> Deferred<T> {
#[derive(Default)] #[derive(Default)]
struct CliFactoryServices { struct CliFactoryServices {
cli_options: Deferred<Arc<CliOptions>>, blob_store: Deferred<Arc<BlobStore>>,
caches: Deferred<Arc<Caches>>, caches: Deferred<Arc<Caches>>,
cjs_tracker: Deferred<Arc<CjsTracker>>,
cli_node_resolver: Deferred<Arc<CliNodeResolver>>,
cli_options: Deferred<Arc<CliOptions>>,
code_cache: Deferred<Arc<CodeCache>>,
emit_cache: Deferred<Arc<EmitCache>>,
emitter: Deferred<Arc<Emitter>>,
feature_checker: Deferred<Arc<FeatureChecker>>,
file_fetcher: Deferred<Arc<FileFetcher>>, file_fetcher: Deferred<Arc<FileFetcher>>,
fs: Deferred<Arc<dyn deno_fs::FileSystem>>,
global_http_cache: Deferred<Arc<GlobalHttpCache>>, global_http_cache: Deferred<Arc<GlobalHttpCache>>,
http_cache: Deferred<Arc<dyn HttpCache>>, http_cache: Deferred<Arc<dyn HttpCache>>,
http_client_provider: Deferred<Arc<HttpClientProvider>>, http_client_provider: Deferred<Arc<HttpClientProvider>>,
emit_cache: Deferred<Arc<EmitCache>>, in_npm_pkg_checker: Deferred<Arc<dyn InNpmPackageChecker>>,
emitter: Deferred<Arc<Emitter>>,
esm_or_cjs_checker: Deferred<Arc<EsmOrCjsChecker>>,
fs: Deferred<Arc<dyn deno_fs::FileSystem>>,
main_graph_container: Deferred<Arc<MainModuleGraphContainer>>, main_graph_container: Deferred<Arc<MainModuleGraphContainer>>,
maybe_inspector_server: Deferred<Option<Arc<InspectorServer>>>,
root_cert_store_provider: Deferred<Arc<dyn RootCertStoreProvider>>,
blob_store: Deferred<Arc<BlobStore>>,
module_info_cache: Deferred<Arc<ModuleInfoCache>>,
parsed_source_cache: Deferred<Arc<ParsedSourceCache>>,
resolver: Deferred<Arc<CliGraphResolver>>,
maybe_file_watcher_reporter: Deferred<Option<FileWatcherReporter>>, maybe_file_watcher_reporter: Deferred<Option<FileWatcherReporter>>,
maybe_inspector_server: Deferred<Option<Arc<InspectorServer>>>,
module_graph_builder: Deferred<Arc<ModuleGraphBuilder>>, module_graph_builder: Deferred<Arc<ModuleGraphBuilder>>,
module_graph_creator: Deferred<Arc<ModuleGraphCreator>>, module_graph_creator: Deferred<Arc<ModuleGraphCreator>>,
module_info_cache: Deferred<Arc<ModuleInfoCache>>,
module_load_preparer: Deferred<Arc<ModuleLoadPreparer>>, module_load_preparer: Deferred<Arc<ModuleLoadPreparer>>,
node_code_translator: Deferred<Arc<CliNodeCodeTranslator>>, node_code_translator: Deferred<Arc<CliNodeCodeTranslator>>,
node_resolver: Deferred<Arc<NodeResolver>>, node_resolver: Deferred<Arc<NodeResolver>>,
npm_cache_dir: Deferred<Arc<NpmCacheDir>>,
npm_resolver: Deferred<Arc<dyn CliNpmResolver>>, npm_resolver: Deferred<Arc<dyn CliNpmResolver>>,
parsed_source_cache: Deferred<Arc<ParsedSourceCache>>,
permission_desc_parser: Deferred<Arc<RuntimePermissionDescriptorParser>>, permission_desc_parser: Deferred<Arc<RuntimePermissionDescriptorParser>>,
pkg_json_resolver: Deferred<Arc<PackageJsonResolver>>,
resolver: Deferred<Arc<CliGraphResolver>>,
root_cert_store_provider: Deferred<Arc<dyn RootCertStoreProvider>>,
root_permissions_container: Deferred<PermissionsContainer>, root_permissions_container: Deferred<PermissionsContainer>,
sloppy_imports_resolver: Deferred<Option<Arc<CliSloppyImportsResolver>>>, sloppy_imports_resolver: Deferred<Option<Arc<CliSloppyImportsResolver>>>,
text_only_progress_bar: Deferred<ProgressBar>, text_only_progress_bar: Deferred<ProgressBar>,
type_checker: Deferred<Arc<TypeChecker>>, type_checker: Deferred<Arc<TypeChecker>>,
cjs_resolutions: Deferred<Arc<CjsResolutionStore>>,
cli_node_resolver: Deferred<Arc<CliNodeResolver>>,
feature_checker: Deferred<Arc<FeatureChecker>>,
code_cache: Deferred<Arc<CodeCache>>,
workspace_resolver: Deferred<Arc<WorkspaceResolver>>, workspace_resolver: Deferred<Arc<WorkspaceResolver>>,
} }
@ -300,12 +310,6 @@ impl CliFactory {
.get_or_init(|| ProgressBar::new(ProgressBarStyle::TextOnly)) .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> { pub fn global_http_cache(&self) -> Result<&Arc<GlobalHttpCache>, AnyError> {
self.services.global_http_cache.get_or_try_init(|| { self.services.global_http_cache.get_or_try_init(|| {
Ok(Arc::new(GlobalHttpCache::new( Ok(Arc::new(GlobalHttpCache::new(
@ -359,32 +363,76 @@ impl CliFactory {
self.services.fs.get_or_init(|| Arc::new(deno_fs::RealFs)) self.services.fs.get_or_init(|| Arc::new(deno_fs::RealFs))
} }
pub fn in_npm_pkg_checker(
&self,
) -> Result<&Arc<dyn InNpmPackageChecker>, AnyError> {
self.services.in_npm_pkg_checker.get_or_try_init(|| {
let cli_options = self.cli_options()?;
let options = if cli_options.use_byonm() {
CreateInNpmPkgCheckerOptions::Byonm
} else {
CreateInNpmPkgCheckerOptions::Managed(
CliManagedInNpmPkgCheckerCreateOptions {
root_cache_dir_url: self.npm_cache_dir()?.root_dir_url(),
maybe_node_modules_path: cli_options
.node_modules_dir_path()
.map(|p| p.as_path()),
},
)
};
Ok(create_in_npm_pkg_checker(options))
})
}
pub fn npm_cache_dir(&self) -> Result<&Arc<NpmCacheDir>, AnyError> {
self.services.npm_cache_dir.get_or_try_init(|| {
let fs = self.fs();
let global_path = self.deno_dir()?.npm_folder_path();
let cli_options = self.cli_options()?;
Ok(Arc::new(NpmCacheDir::new(
&DenoCacheEnvFsAdapter(fs.as_ref()),
global_path,
cli_options.npmrc().get_all_known_registries_urls(),
)))
})
}
pub async fn npm_resolver( pub async fn npm_resolver(
&self, &self,
) -> Result<&Arc<dyn CliNpmResolver>, AnyError> { ) -> Result<&Arc<dyn CliNpmResolver>, AnyError> {
self self
.services .services
.npm_resolver .npm_resolver
.get_or_try_init_async(async { .get_or_try_init_async(
async {
let fs = self.fs(); let fs = self.fs();
let cli_options = self.cli_options()?; let cli_options = self.cli_options()?;
// For `deno install` we want to force the managed resolver so it can set up `node_modules/` directory. create_cli_npm_resolver(if cli_options.use_byonm() {
create_cli_npm_resolver(if cli_options.use_byonm() && !matches!(cli_options.sub_command(), DenoSubcommand::Install(_) | DenoSubcommand::Add(_) | DenoSubcommand::Remove(_)) { CliNpmResolverCreateOptions::Byonm(
CliNpmResolverCreateOptions::Byonm(CliByonmNpmResolverCreateOptions { CliByonmNpmResolverCreateOptions {
fs: CliDenoResolverFs(fs.clone()), fs: CliDenoResolverFs(fs.clone()),
root_node_modules_dir: Some(match cli_options.node_modules_dir_path() { pkg_json_resolver: self.pkg_json_resolver().clone(),
root_node_modules_dir: Some(
match cli_options.node_modules_dir_path() {
Some(node_modules_path) => node_modules_path.to_path_buf(), Some(node_modules_path) => node_modules_path.to_path_buf(),
// path needs to be canonicalized for node resolution // path needs to be canonicalized for node resolution
// (node_modules_dir_path above is already canonicalized) // (node_modules_dir_path above is already canonicalized)
None => canonicalize_path_maybe_not_exists(cli_options.initial_cwd())? None => canonicalize_path_maybe_not_exists(
cli_options.initial_cwd(),
)?
.join("node_modules"), .join("node_modules"),
}), },
}) ),
},
)
} else { } else {
CliNpmResolverCreateOptions::Managed(CliNpmResolverManagedCreateOptions { CliNpmResolverCreateOptions::Managed(
CliManagedNpmResolverCreateOptions {
snapshot: match cli_options.resolve_npm_resolution_snapshot()? { snapshot: match cli_options.resolve_npm_resolution_snapshot()? {
Some(snapshot) => { Some(snapshot) => {
CliNpmResolverManagedSnapshotOption::Specified(Some(snapshot)) CliNpmResolverManagedSnapshotOption::Specified(Some(
snapshot,
))
} }
None => match cli_options.maybe_lockfile() { None => match cli_options.maybe_lockfile() {
Some(lockfile) => { Some(lockfile) => {
@ -392,23 +440,35 @@ impl CliFactory {
lockfile.clone(), lockfile.clone(),
) )
} }
None => CliNpmResolverManagedSnapshotOption::Specified(None), None => {
CliNpmResolverManagedSnapshotOption::Specified(None)
}
}, },
}, },
maybe_lockfile: cli_options.maybe_lockfile().cloned(), maybe_lockfile: cli_options.maybe_lockfile().cloned(),
fs: fs.clone(), fs: fs.clone(),
http_client_provider: self.http_client_provider().clone(), http_client_provider: self.http_client_provider().clone(),
npm_global_cache_dir: self.deno_dir()?.npm_folder_path(), npm_cache_dir: self.npm_cache_dir()?.clone(),
cache_setting: cli_options.cache_setting(), cache_setting: cli_options.cache_setting(),
text_only_progress_bar: self.text_only_progress_bar().clone(), text_only_progress_bar: self.text_only_progress_bar().clone(),
maybe_node_modules_path: cli_options.node_modules_dir_path().cloned(), maybe_node_modules_path: cli_options
npm_install_deps_provider: Arc::new(NpmInstallDepsProvider::from_workspace(cli_options.workspace())), .node_modules_dir_path()
.cloned(),
npm_install_deps_provider: Arc::new(
NpmInstallDepsProvider::from_workspace(
cli_options.workspace(),
),
),
npm_system_info: cli_options.npm_system_info(), npm_system_info: cli_options.npm_system_info(),
npmrc: cli_options.npmrc().clone(), npmrc: cli_options.npmrc().clone(),
lifecycle_scripts: cli_options.lifecycle_scripts_config(), lifecycle_scripts: cli_options.lifecycle_scripts_config(),
},
)
}) })
}).await .await
}.boxed_local()) }
.boxed_local(),
)
.await .await
} }
@ -513,6 +573,7 @@ impl CliFactory {
self.services.module_info_cache.get_or_try_init(|| { self.services.module_info_cache.get_or_try_init(|| {
Ok(Arc::new(ModuleInfoCache::new( Ok(Arc::new(ModuleInfoCache::new(
self.caches()?.dep_analysis_db(), self.caches()?.dep_analysis_db(),
self.parsed_source_cache().clone(),
))) )))
}) })
} }
@ -541,6 +602,7 @@ impl CliFactory {
ts_config_result.ts_config, ts_config_result.ts_config,
)?; )?;
Ok(Arc::new(Emitter::new( Ok(Arc::new(Emitter::new(
self.cjs_tracker()?.clone(),
self.emit_cache()?.clone(), self.emit_cache()?.clone(),
self.parsed_source_cache().clone(), self.parsed_source_cache().clone(),
transpile_options, transpile_options,
@ -564,7 +626,9 @@ impl CliFactory {
async { async {
Ok(Arc::new(NodeResolver::new( Ok(Arc::new(NodeResolver::new(
DenoFsNodeResolverEnv::new(self.fs().clone()), DenoFsNodeResolverEnv::new(self.fs().clone()),
self.in_npm_pkg_checker()?.clone(),
self.npm_resolver().await?.clone().into_npm_resolver(), self.npm_resolver().await?.clone().into_npm_resolver(),
self.pkg_json_resolver().clone(),
))) )))
} }
.boxed_local(), .boxed_local(),
@ -582,24 +646,35 @@ impl CliFactory {
let caches = self.caches()?; let caches = self.caches()?;
let node_analysis_cache = let node_analysis_cache =
NodeAnalysisCache::new(caches.node_analysis_db()); NodeAnalysisCache::new(caches.node_analysis_db());
let node_resolver = self.cli_node_resolver().await?.clone(); let node_resolver = self.node_resolver().await?.clone();
let cjs_esm_analyzer = CliCjsCodeAnalyzer::new( let cjs_esm_analyzer = CliCjsCodeAnalyzer::new(
node_analysis_cache, node_analysis_cache,
self.cjs_tracker()?.clone(),
self.fs().clone(), self.fs().clone(),
node_resolver,
Some(self.parsed_source_cache().clone()), Some(self.parsed_source_cache().clone()),
self.cli_options()?.is_npm_main(),
); );
Ok(Arc::new(NodeCodeTranslator::new( Ok(Arc::new(NodeCodeTranslator::new(
cjs_esm_analyzer, cjs_esm_analyzer,
DenoFsNodeResolverEnv::new(self.fs().clone()), DenoFsNodeResolverEnv::new(self.fs().clone()),
self.node_resolver().await?.clone(), self.in_npm_pkg_checker()?.clone(),
node_resolver,
self.npm_resolver().await?.clone().into_npm_resolver(), self.npm_resolver().await?.clone().into_npm_resolver(),
self.pkg_json_resolver().clone(),
))) )))
}) })
.await .await
} }
pub fn pkg_json_resolver(&self) -> &Arc<PackageJsonResolver> {
self.services.pkg_json_resolver.get_or_init(|| {
Arc::new(PackageJsonResolver::new(DenoFsNodeResolverEnv::new(
self.fs().clone(),
)))
})
}
pub async fn type_checker(&self) -> Result<&Arc<TypeChecker>, AnyError> { pub async fn type_checker(&self) -> Result<&Arc<TypeChecker>, AnyError> {
self self
.services .services
@ -608,6 +683,10 @@ impl CliFactory {
let cli_options = self.cli_options()?; let cli_options = self.cli_options()?;
Ok(Arc::new(TypeChecker::new( Ok(Arc::new(TypeChecker::new(
self.caches()?.clone(), self.caches()?.clone(),
Arc::new(TypeCheckingCjsTracker::new(
self.cjs_tracker()?.clone(),
self.module_info_cache()?.clone(),
)),
cli_options.clone(), cli_options.clone(),
self.module_graph_builder().await?.clone(), self.module_graph_builder().await?.clone(),
self.node_resolver().await?.clone(), self.node_resolver().await?.clone(),
@ -626,19 +705,18 @@ impl CliFactory {
.get_or_try_init_async(async { .get_or_try_init_async(async {
let cli_options = self.cli_options()?; let cli_options = self.cli_options()?;
Ok(Arc::new(ModuleGraphBuilder::new( Ok(Arc::new(ModuleGraphBuilder::new(
cli_options.clone(),
self.caches()?.clone(), self.caches()?.clone(),
self.esm_or_cjs_checker().clone(), cli_options.clone(),
self.file_fetcher()?.clone(),
self.fs().clone(), self.fs().clone(),
self.resolver().await?.clone(), self.global_http_cache()?.clone(),
self.cli_node_resolver().await?.clone(), self.in_npm_pkg_checker()?.clone(),
self.npm_resolver().await?.clone(),
self.module_info_cache()?.clone(),
self.parsed_source_cache().clone(),
cli_options.maybe_lockfile().cloned(), cli_options.maybe_lockfile().cloned(),
self.maybe_file_watcher_reporter().clone(), self.maybe_file_watcher_reporter().clone(),
self.file_fetcher()?.clone(), self.module_info_cache()?.clone(),
self.global_http_cache()?.clone(), self.npm_resolver().await?.clone(),
self.parsed_source_cache().clone(),
self.resolver().await?.clone(),
self.root_permissions_container()?.clone(), self.root_permissions_container()?.clone(),
))) )))
}) })
@ -710,8 +788,17 @@ impl CliFactory {
.await .await
} }
pub fn cjs_resolutions(&self) -> &Arc<CjsResolutionStore> { pub fn cjs_tracker(&self) -> Result<&Arc<CjsTracker>, AnyError> {
self.services.cjs_resolutions.get_or_init(Default::default) self.services.cjs_tracker.get_or_try_init(|| {
let options = self.cli_options()?;
Ok(Arc::new(CjsTracker::new(
self.in_npm_pkg_checker()?.clone(),
self.pkg_json_resolver().clone(),
CjsTrackerOptions {
unstable_detect_cjs: options.unstable_detect_cjs(),
},
)))
})
} }
pub async fn cli_node_resolver( pub async fn cli_node_resolver(
@ -722,8 +809,9 @@ impl CliFactory {
.cli_node_resolver .cli_node_resolver
.get_or_try_init_async(async { .get_or_try_init_async(async {
Ok(Arc::new(CliNodeResolver::new( Ok(Arc::new(CliNodeResolver::new(
self.cjs_resolutions().clone(), self.cjs_tracker()?.clone(),
self.fs().clone(), self.fs().clone(),
self.in_npm_pkg_checker()?.clone(),
self.node_resolver().await?.clone(), self.node_resolver().await?.clone(),
self.npm_resolver().await?.clone(), self.npm_resolver().await?.clone(),
))) )))
@ -761,6 +849,7 @@ impl CliFactory {
) -> Result<DenoCompileBinaryWriter, AnyError> { ) -> Result<DenoCompileBinaryWriter, AnyError> {
let cli_options = self.cli_options()?; let cli_options = self.cli_options()?;
Ok(DenoCompileBinaryWriter::new( Ok(DenoCompileBinaryWriter::new(
self.cjs_tracker()?,
self.deno_dir()?, self.deno_dir()?,
self.emitter()?, self.emitter()?,
self.file_fetcher()?, self.file_fetcher()?,
@ -791,53 +880,60 @@ impl CliFactory {
&self, &self,
) -> Result<CliMainWorkerFactory, AnyError> { ) -> Result<CliMainWorkerFactory, AnyError> {
let cli_options = self.cli_options()?; let cli_options = self.cli_options()?;
let fs = self.fs();
let node_resolver = self.node_resolver().await?; let node_resolver = self.node_resolver().await?;
let npm_resolver = self.npm_resolver().await?; let npm_resolver = self.npm_resolver().await?;
let fs = self.fs();
let cli_node_resolver = self.cli_node_resolver().await?; let cli_node_resolver = self.cli_node_resolver().await?;
let cli_npm_resolver = self.npm_resolver().await?.clone(); let cli_npm_resolver = self.npm_resolver().await?.clone();
let in_npm_pkg_checker = self.in_npm_pkg_checker()?;
let maybe_file_watcher_communicator = if cli_options.has_hmr() { let maybe_file_watcher_communicator = if cli_options.has_hmr() {
Some(self.watcher_communicator.clone().unwrap()) Some(self.watcher_communicator.clone().unwrap())
} else { } else {
None None
}; };
let node_code_translator = self.node_code_translator().await?;
let cjs_tracker = self.cjs_tracker()?.clone();
let pkg_json_resolver = self.pkg_json_resolver().clone();
Ok(CliMainWorkerFactory::new( Ok(CliMainWorkerFactory::new(
self.blob_store().clone(), self.blob_store().clone(),
self.cjs_resolutions().clone(),
if cli_options.code_cache_enabled() { if cli_options.code_cache_enabled() {
Some(self.code_cache()?.clone()) Some(self.code_cache()?.clone())
} else { } else {
None None
}, },
self.feature_checker()?.clone(), self.feature_checker()?.clone(),
self.fs().clone(), fs.clone(),
maybe_file_watcher_communicator, maybe_file_watcher_communicator,
self.maybe_inspector_server()?.clone(), self.maybe_inspector_server()?.clone(),
cli_options.maybe_lockfile().cloned(), cli_options.maybe_lockfile().cloned(),
Box::new(CliModuleLoaderFactory::new( Box::new(CliModuleLoaderFactory::new(
cli_options, cli_options,
cjs_tracker,
if cli_options.code_cache_enabled() { if cli_options.code_cache_enabled() {
Some(self.code_cache()?.clone()) Some(self.code_cache()?.clone())
} else { } else {
None None
}, },
self.emitter()?.clone(), self.emitter()?.clone(),
fs.clone(),
in_npm_pkg_checker.clone(),
self.main_module_graph_container().await?.clone(), self.main_module_graph_container().await?.clone(),
self.module_load_preparer().await?.clone(), self.module_load_preparer().await?.clone(),
node_code_translator.clone(),
cli_node_resolver.clone(), cli_node_resolver.clone(),
cli_npm_resolver.clone(), cli_npm_resolver.clone(),
NpmModuleLoader::new( NpmModuleLoader::new(
self.cjs_resolutions().clone(), self.cjs_tracker()?.clone(),
self.node_code_translator().await?.clone(),
fs.clone(), fs.clone(),
cli_node_resolver.clone(), node_code_translator.clone(),
), ),
self.parsed_source_cache().clone(), self.parsed_source_cache().clone(),
self.resolver().await?.clone(), self.resolver().await?.clone(),
)), )),
node_resolver.clone(), node_resolver.clone(),
npm_resolver.clone(), npm_resolver.clone(),
pkg_json_resolver,
self.root_cert_store_provider().clone(), self.root_cert_store_provider().clone(),
self.root_permissions_container()?.clone(), self.root_permissions_container()?.clone(),
StorageKeyResolver::from_options(cli_options), StorageKeyResolver::from_options(cli_options),
@ -853,8 +949,10 @@ impl CliFactory {
let create_hmr_runner = if cli_options.has_hmr() { let create_hmr_runner = if cli_options.has_hmr() {
let watcher_communicator = self.watcher_communicator.clone().unwrap(); let watcher_communicator = self.watcher_communicator.clone().unwrap();
let emitter = self.emitter()?.clone(); let emitter = self.emitter()?.clone();
let cjs_tracker = self.cjs_tracker()?.clone();
let fn_: crate::worker::CreateHmrRunnerCb = Box::new(move |session| { let fn_: crate::worker::CreateHmrRunnerCb = Box::new(move |session| {
Box::new(HmrRunner::new( Box::new(HmrRunner::new(
cjs_tracker.clone(),
emitter.clone(), emitter.clone(),
session, session,
watcher_communicator.clone(), watcher_communicator.clone(),
@ -891,7 +989,6 @@ impl CliFactory {
inspect_wait: cli_options.inspect_wait().is_some(), inspect_wait: cli_options.inspect_wait().is_some(),
strace_ops: cli_options.strace_ops().clone(), strace_ops: cli_options.strace_ops().clone(),
is_inspecting: cli_options.is_inspecting(), is_inspecting: cli_options.is_inspecting(),
is_npm_main: cli_options.is_npm_main(),
location: cli_options.location_flag().clone(), location: cli_options.location_flag().clone(),
// if the user ran a binary command, we'll need to set process.argv[0] // if the user ran a binary command, we'll need to set process.argv[0]
// to be the name of the binary command instead of deno // to be the name of the binary command instead of deno
@ -909,7 +1006,6 @@ impl CliFactory {
node_ipc: cli_options.node_ipc_fd(), node_ipc: cli_options.node_ipc_fd(),
serve_port: cli_options.serve_port(), serve_port: cli_options.serve_port(),
serve_host: cli_options.serve_host(), serve_host: cli_options.serve_host(),
unstable_detect_cjs: cli_options.unstable_detect_cjs(),
}) })
} }
} }

View file

@ -6,7 +6,6 @@ use crate::args::CliLockfile;
use crate::args::CliOptions; use crate::args::CliOptions;
use crate::args::DENO_DISABLE_PEDANTIC_NODE_WARNINGS; use crate::args::DENO_DISABLE_PEDANTIC_NODE_WARNINGS;
use crate::cache; use crate::cache;
use crate::cache::EsmOrCjsChecker;
use crate::cache::GlobalHttpCache; use crate::cache::GlobalHttpCache;
use crate::cache::ModuleInfoCache; use crate::cache::ModuleInfoCache;
use crate::cache::ParsedSourceCache; use crate::cache::ParsedSourceCache;
@ -15,7 +14,6 @@ use crate::errors::get_error_class_name;
use crate::file_fetcher::FileFetcher; use crate::file_fetcher::FileFetcher;
use crate::npm::CliNpmResolver; use crate::npm::CliNpmResolver;
use crate::resolver::CliGraphResolver; use crate::resolver::CliGraphResolver;
use crate::resolver::CliNodeResolver;
use crate::resolver::CliSloppyImportsResolver; use crate::resolver::CliSloppyImportsResolver;
use crate::resolver::SloppyImportsCachedFs; use crate::resolver::SloppyImportsCachedFs;
use crate::tools::check; use crate::tools::check;
@ -50,6 +48,7 @@ use deno_runtime::deno_permissions::PermissionsContainer;
use deno_semver::jsr::JsrDepPackageReq; use deno_semver::jsr::JsrDepPackageReq;
use deno_semver::package::PackageNv; use deno_semver::package::PackageNv;
use import_map::ImportMapError; use import_map::ImportMapError;
use node_resolver::InNpmPackageChecker;
use std::collections::HashSet; use std::collections::HashSet;
use std::error::Error; use std::error::Error;
use std::ops::Deref; use std::ops::Deref;
@ -379,54 +378,51 @@ pub struct BuildFastCheckGraphOptions<'a> {
} }
pub struct ModuleGraphBuilder { pub struct ModuleGraphBuilder {
options: Arc<CliOptions>,
caches: Arc<cache::Caches>, caches: Arc<cache::Caches>,
esm_or_cjs_checker: Arc<EsmOrCjsChecker>, cli_options: Arc<CliOptions>,
file_fetcher: Arc<FileFetcher>,
fs: Arc<dyn FileSystem>, fs: Arc<dyn FileSystem>,
resolver: Arc<CliGraphResolver>, global_http_cache: Arc<GlobalHttpCache>,
node_resolver: Arc<CliNodeResolver>, in_npm_pkg_checker: Arc<dyn InNpmPackageChecker>,
npm_resolver: Arc<dyn CliNpmResolver>,
module_info_cache: Arc<ModuleInfoCache>,
parsed_source_cache: Arc<ParsedSourceCache>,
lockfile: Option<Arc<CliLockfile>>, lockfile: Option<Arc<CliLockfile>>,
maybe_file_watcher_reporter: Option<FileWatcherReporter>, maybe_file_watcher_reporter: Option<FileWatcherReporter>,
file_fetcher: Arc<FileFetcher>, module_info_cache: Arc<ModuleInfoCache>,
global_http_cache: Arc<GlobalHttpCache>, npm_resolver: Arc<dyn CliNpmResolver>,
parsed_source_cache: Arc<ParsedSourceCache>,
resolver: Arc<CliGraphResolver>,
root_permissions_container: PermissionsContainer, root_permissions_container: PermissionsContainer,
} }
impl ModuleGraphBuilder { impl ModuleGraphBuilder {
#[allow(clippy::too_many_arguments)] #[allow(clippy::too_many_arguments)]
pub fn new( pub fn new(
options: Arc<CliOptions>,
caches: Arc<cache::Caches>, caches: Arc<cache::Caches>,
esm_or_cjs_checker: Arc<EsmOrCjsChecker>, cli_options: Arc<CliOptions>,
file_fetcher: Arc<FileFetcher>,
fs: Arc<dyn FileSystem>, fs: Arc<dyn FileSystem>,
resolver: Arc<CliGraphResolver>, global_http_cache: Arc<GlobalHttpCache>,
node_resolver: Arc<CliNodeResolver>, in_npm_pkg_checker: Arc<dyn InNpmPackageChecker>,
npm_resolver: Arc<dyn CliNpmResolver>,
module_info_cache: Arc<ModuleInfoCache>,
parsed_source_cache: Arc<ParsedSourceCache>,
lockfile: Option<Arc<CliLockfile>>, lockfile: Option<Arc<CliLockfile>>,
maybe_file_watcher_reporter: Option<FileWatcherReporter>, maybe_file_watcher_reporter: Option<FileWatcherReporter>,
file_fetcher: Arc<FileFetcher>, module_info_cache: Arc<ModuleInfoCache>,
global_http_cache: Arc<GlobalHttpCache>, npm_resolver: Arc<dyn CliNpmResolver>,
parsed_source_cache: Arc<ParsedSourceCache>,
resolver: Arc<CliGraphResolver>,
root_permissions_container: PermissionsContainer, root_permissions_container: PermissionsContainer,
) -> Self { ) -> Self {
Self { Self {
options,
caches, caches,
esm_or_cjs_checker, cli_options,
file_fetcher,
fs, fs,
resolver, global_http_cache,
node_resolver, in_npm_pkg_checker,
npm_resolver,
module_info_cache,
parsed_source_cache,
lockfile, lockfile,
maybe_file_watcher_reporter, maybe_file_watcher_reporter,
file_fetcher, module_info_cache,
global_http_cache, npm_resolver,
parsed_source_cache,
resolver,
root_permissions_container, root_permissions_container,
} }
} }
@ -512,13 +508,11 @@ impl ModuleGraphBuilder {
} }
let maybe_imports = if options.graph_kind.include_types() { let maybe_imports = if options.graph_kind.include_types() {
self.options.to_compiler_option_types()? self.cli_options.to_compiler_option_types()?
} else { } else {
Vec::new() Vec::new()
}; };
let analyzer = self let analyzer = self.module_info_cache.as_module_analyzer();
.module_info_cache
.as_module_analyzer(&self.parsed_source_cache);
let mut loader = match options.loader { let mut loader = match options.loader {
Some(loader) => MutLoaderRef::Borrowed(loader), Some(loader) => MutLoaderRef::Borrowed(loader),
None => MutLoaderRef::Owned(self.create_graph_loader()), None => MutLoaderRef::Owned(self.create_graph_loader()),
@ -566,7 +560,7 @@ impl ModuleGraphBuilder {
// ensure an "npm install" is done if the user has explicitly // ensure an "npm install" is done if the user has explicitly
// opted into using a node_modules directory // opted into using a node_modules directory
if self if self
.options .cli_options
.node_modules_dir()? .node_modules_dir()?
.map(|m| m.uses_node_modules_dir()) .map(|m| m.uses_node_modules_dir())
.unwrap_or(false) .unwrap_or(false)
@ -677,10 +671,10 @@ impl ModuleGraphBuilder {
graph.build_fast_check_type_graph( graph.build_fast_check_type_graph(
deno_graph::BuildFastCheckTypeGraphOptions { deno_graph::BuildFastCheckTypeGraphOptions {
jsr_url_provider: &CliJsrUrlProvider, es_parser: Some(&parser),
fast_check_cache: fast_check_cache.as_ref().map(|c| c as _), fast_check_cache: fast_check_cache.as_ref().map(|c| c as _),
fast_check_dts: false, fast_check_dts: false,
module_parser: Some(&parser), jsr_url_provider: &CliJsrUrlProvider,
resolver: Some(graph_resolver), resolver: Some(graph_resolver),
npm_resolver: Some(&graph_npm_resolver), npm_resolver: Some(&graph_npm_resolver),
workspace_fast_check: options.workspace_fast_check, workspace_fast_check: options.workspace_fast_check,
@ -699,20 +693,18 @@ impl ModuleGraphBuilder {
permissions: PermissionsContainer, permissions: PermissionsContainer,
) -> cache::FetchCacher { ) -> cache::FetchCacher {
cache::FetchCacher::new( cache::FetchCacher::new(
self.esm_or_cjs_checker.clone(),
self.file_fetcher.clone(), self.file_fetcher.clone(),
self.fs.clone(),
self.global_http_cache.clone(), self.global_http_cache.clone(),
self.node_resolver.clone(), self.in_npm_pkg_checker.clone(),
self.npm_resolver.clone(),
self.module_info_cache.clone(), self.module_info_cache.clone(),
cache::FetchCacherOptions { cache::FetchCacherOptions {
file_header_overrides: self.options.resolve_file_header_overrides(), file_header_overrides: self.cli_options.resolve_file_header_overrides(),
permissions, permissions,
is_deno_publish: matches!( is_deno_publish: matches!(
self.options.sub_command(), self.cli_options.sub_command(),
crate::args::DenoSubcommand::Publish { .. } crate::args::DenoSubcommand::Publish { .. }
), ),
unstable_detect_cjs: self.options.unstable_detect_cjs(),
}, },
) )
} }
@ -737,12 +729,12 @@ impl ModuleGraphBuilder {
&self.fs, &self.fs,
roots, roots,
GraphValidOptions { GraphValidOptions {
kind: if self.options.type_check_mode().is_true() { kind: if self.cli_options.type_check_mode().is_true() {
GraphKind::All GraphKind::All
} else { } else {
GraphKind::CodeOnly GraphKind::CodeOnly
}, },
check_js: self.options.check_js(), check_js: self.cli_options.check_js(),
exit_integrity_errors: true, exit_integrity_errors: true,
}, },
) )

View file

@ -36,7 +36,6 @@ use deno_semver::package::PackageReq;
use deno_semver::package::PackageReqReference; use deno_semver::package::PackageReqReference;
use deno_semver::Version; use deno_semver::Version;
use import_map::ImportMap; use import_map::ImportMap;
use node_resolver::NpmResolver;
use once_cell::sync::Lazy; use once_cell::sync::Lazy;
use regex::Regex; use regex::Regex;
use std::borrow::Cow; use std::borrow::Cow;
@ -336,7 +335,12 @@ impl<'a> TsResponseImportMapper<'a> {
.resolver .resolver
.maybe_managed_npm_resolver(Some(&self.file_referrer)) .maybe_managed_npm_resolver(Some(&self.file_referrer))
{ {
if npm_resolver.in_npm_package(specifier) { let in_npm_pkg = self
.resolver
.maybe_node_resolver(Some(&self.file_referrer))
.map(|n| n.in_npm_package(specifier))
.unwrap_or(false);
if in_npm_pkg {
if let Ok(Some(pkg_id)) = if let Ok(Some(pkg_id)) =
npm_resolver.resolve_pkg_id_from_specifier(specifier) npm_resolver.resolve_pkg_id_from_specifier(specifier)
{ {
@ -1199,14 +1203,11 @@ impl CodeActionCollection {
}), }),
); );
match parsed_source.program_ref() { parsed_source
deno_ast::swc::ast::Program::Module(module) => module .program_ref()
.body .body()
.iter()
.find(|i| i.range().contains(&specifier_range)) .find(|i| i.range().contains(&specifier_range))
.map(|i| text_info.line_and_column_index(i.range().start)), .map(|i| text_info.line_and_column_index(i.range().start))
deno_ast::swc::ast::Program::Script(_) => None,
}
} }
async fn deno_types_for_npm_action( async fn deno_types_for_npm_action(

View file

@ -421,7 +421,7 @@ pub fn collect_test(
) -> Result<Vec<lsp::CodeLens>, AnyError> { ) -> Result<Vec<lsp::CodeLens>, AnyError> {
let mut collector = let mut collector =
DenoTestCollector::new(specifier.clone(), parsed_source.clone()); DenoTestCollector::new(specifier.clone(), parsed_source.clone());
parsed_source.module().visit_with(&mut collector); parsed_source.program().visit_with(&mut collector);
Ok(collector.take()) Ok(collector.take())
} }
@ -581,7 +581,7 @@ mod tests {
.unwrap(); .unwrap();
let mut collector = let mut collector =
DenoTestCollector::new(specifier, parsed_module.clone()); DenoTestCollector::new(specifier, parsed_module.clone());
parsed_module.module().visit_with(&mut collector); parsed_module.program().visit_with(&mut collector);
assert_eq!( assert_eq!(
collector.take(), collector.take(),
vec![ vec![

View file

@ -985,7 +985,7 @@ impl Config {
| MediaType::Tsx => Some(&workspace_settings.typescript), | MediaType::Tsx => Some(&workspace_settings.typescript),
MediaType::Json MediaType::Json
| MediaType::Wasm | MediaType::Wasm
| MediaType::TsBuildInfo | MediaType::Css
| MediaType::SourceMap | MediaType::SourceMap
| MediaType::Unknown => None, | MediaType::Unknown => None,
} }

View file

@ -272,7 +272,7 @@ fn get_maybe_test_module_fut(
parsed_source.specifier().clone(), parsed_source.specifier().clone(),
parsed_source.text_info_lazy().clone(), parsed_source.text_info_lazy().clone(),
); );
parsed_source.module().visit_with(&mut collector); parsed_source.program().visit_with(&mut collector);
Arc::new(collector.take()) Arc::new(collector.take())
}) })
.map(Result::ok) .map(Result::ok)
@ -332,12 +332,8 @@ impl Document {
.filter(|s| cache.is_valid_file_referrer(s)) .filter(|s| cache.is_valid_file_referrer(s))
.cloned() .cloned()
.or(file_referrer); .or(file_referrer);
let media_type = resolve_media_type( let media_type =
&specifier, resolve_media_type(&specifier, maybe_headers.as_ref(), maybe_language_id);
maybe_headers.as_ref(),
maybe_language_id,
&resolver,
);
let (maybe_parsed_source, maybe_module) = let (maybe_parsed_source, maybe_module) =
if media_type_is_diagnosable(media_type) { if media_type_is_diagnosable(media_type) {
parse_and_analyze_module( parse_and_analyze_module(
@ -399,7 +395,6 @@ impl Document {
&self.specifier, &self.specifier,
self.maybe_headers.as_ref(), self.maybe_headers.as_ref(),
self.maybe_language_id, self.maybe_language_id,
&resolver,
); );
let dependencies; let dependencies;
let maybe_types_dependency; let maybe_types_dependency;
@ -764,14 +759,7 @@ fn resolve_media_type(
specifier: &ModuleSpecifier, specifier: &ModuleSpecifier,
maybe_headers: Option<&HashMap<String, String>>, maybe_headers: Option<&HashMap<String, String>>,
maybe_language_id: Option<LanguageId>, maybe_language_id: Option<LanguageId>,
resolver: &LspResolver,
) -> MediaType { ) -> MediaType {
if resolver.in_node_modules(specifier) {
if let Some(media_type) = resolver.node_media_type(specifier) {
return media_type;
}
}
if let Some(language_id) = maybe_language_id { if let Some(language_id) = maybe_language_id {
return MediaType::from_specifier_and_content_type( return MediaType::from_specifier_and_content_type(
specifier, specifier,
@ -1561,7 +1549,7 @@ fn parse_source(
text: Arc<str>, text: Arc<str>,
media_type: MediaType, media_type: MediaType,
) -> ParsedSourceResult { ) -> ParsedSourceResult {
deno_ast::parse_module(deno_ast::ParseParams { deno_ast::parse_program(deno_ast::ParseParams {
specifier, specifier,
text, text,
media_type, media_type,

View file

@ -904,7 +904,7 @@ impl Inner {
| MediaType::Tsx => {} | MediaType::Tsx => {}
MediaType::Wasm MediaType::Wasm
| MediaType::SourceMap | MediaType::SourceMap
| MediaType::TsBuildInfo | MediaType::Css
| MediaType::Unknown => { | MediaType::Unknown => {
if path.extension().and_then(|s| s.to_str()) != Some("jsonc") { if path.extension().and_then(|s| s.to_str()) != Some("jsonc") {
continue; continue;

View file

@ -2,6 +2,8 @@
use dashmap::DashMap; use dashmap::DashMap;
use deno_ast::MediaType; use deno_ast::MediaType;
use deno_ast::ParsedSource;
use deno_cache_dir::npm::NpmCacheDir;
use deno_cache_dir::HttpCache; use deno_cache_dir::HttpCache;
use deno_config::workspace::PackageJsonDepResolution; use deno_config::workspace::PackageJsonDepResolution;
use deno_config::workspace::WorkspaceResolver; use deno_config::workspace::WorkspaceResolver;
@ -14,15 +16,15 @@ use deno_path_util::url_to_file_path;
use deno_runtime::deno_fs; use deno_runtime::deno_fs;
use deno_runtime::deno_node::NodeResolver; use deno_runtime::deno_node::NodeResolver;
use deno_runtime::deno_node::PackageJson; use deno_runtime::deno_node::PackageJson;
use deno_runtime::deno_node::PackageJsonResolver;
use deno_semver::jsr::JsrPackageReqReference; use deno_semver::jsr::JsrPackageReqReference;
use deno_semver::npm::NpmPackageReqReference; use deno_semver::npm::NpmPackageReqReference;
use deno_semver::package::PackageNv; use deno_semver::package::PackageNv;
use deno_semver::package::PackageReq; use deno_semver::package::PackageReq;
use indexmap::IndexMap; use indexmap::IndexMap;
use node_resolver::errors::ClosestPkgJsonError; use node_resolver::errors::ClosestPkgJsonError;
use node_resolver::NodeResolution; use node_resolver::InNpmPackageChecker;
use node_resolver::NodeResolutionMode; use node_resolver::NodeResolutionMode;
use node_resolver::NpmResolver;
use std::borrow::Cow; use std::borrow::Cow;
use std::collections::BTreeMap; use std::collections::BTreeMap;
use std::collections::BTreeSet; use std::collections::BTreeSet;
@ -36,6 +38,7 @@ use crate::args::create_default_npmrc;
use crate::args::CacheSetting; use crate::args::CacheSetting;
use crate::args::CliLockfile; use crate::args::CliLockfile;
use crate::args::NpmInstallDepsProvider; use crate::args::NpmInstallDepsProvider;
use crate::cache::DenoCacheEnvFsAdapter;
use crate::graph_util::CliJsrUrlProvider; use crate::graph_util::CliJsrUrlProvider;
use crate::http_util::HttpClientProvider; use crate::http_util::HttpClientProvider;
use crate::lsp::config::Config; use crate::lsp::config::Config;
@ -43,26 +46,32 @@ use crate::lsp::config::ConfigData;
use crate::lsp::logging::lsp_warn; use crate::lsp::logging::lsp_warn;
use crate::npm::create_cli_npm_resolver_for_lsp; use crate::npm::create_cli_npm_resolver_for_lsp;
use crate::npm::CliByonmNpmResolverCreateOptions; use crate::npm::CliByonmNpmResolverCreateOptions;
use crate::npm::CliManagedInNpmPkgCheckerCreateOptions;
use crate::npm::CliManagedNpmResolverCreateOptions;
use crate::npm::CliNpmResolver; use crate::npm::CliNpmResolver;
use crate::npm::CliNpmResolverCreateOptions; use crate::npm::CliNpmResolverCreateOptions;
use crate::npm::CliNpmResolverManagedCreateOptions;
use crate::npm::CliNpmResolverManagedSnapshotOption; use crate::npm::CliNpmResolverManagedSnapshotOption;
use crate::npm::CreateInNpmPkgCheckerOptions;
use crate::npm::ManagedCliNpmResolver; use crate::npm::ManagedCliNpmResolver;
use crate::resolver::CjsResolutionStore; use crate::resolver::CjsTracker;
use crate::resolver::CjsTrackerOptions;
use crate::resolver::CliDenoResolverFs; use crate::resolver::CliDenoResolverFs;
use crate::resolver::CliGraphResolver; use crate::resolver::CliGraphResolver;
use crate::resolver::CliGraphResolverOptions; use crate::resolver::CliGraphResolverOptions;
use crate::resolver::CliNodeResolver; use crate::resolver::CliNodeResolver;
use crate::resolver::WorkerCliNpmGraphResolver; use crate::resolver::WorkerCliNpmGraphResolver;
use crate::tsc::into_specifier_and_media_type;
use crate::util::progress_bar::ProgressBar; use crate::util::progress_bar::ProgressBar;
use crate::util::progress_bar::ProgressBarStyle; use crate::util::progress_bar::ProgressBarStyle;
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
struct LspScopeResolver { struct LspScopeResolver {
cjs_tracker: Option<Arc<LspCjsTracker>>,
graph_resolver: Arc<CliGraphResolver>, graph_resolver: Arc<CliGraphResolver>,
jsr_resolver: Option<Arc<JsrCacheResolver>>, jsr_resolver: Option<Arc<JsrCacheResolver>>,
npm_resolver: Option<Arc<dyn CliNpmResolver>>, npm_resolver: Option<Arc<dyn CliNpmResolver>>,
node_resolver: Option<Arc<CliNodeResolver>>, node_resolver: Option<Arc<CliNodeResolver>>,
pkg_json_resolver: Option<Arc<PackageJsonResolver>>,
redirect_resolver: Option<Arc<RedirectResolver>>, redirect_resolver: Option<Arc<RedirectResolver>>,
graph_imports: Arc<IndexMap<ModuleSpecifier, GraphImport>>, graph_imports: Arc<IndexMap<ModuleSpecifier, GraphImport>>,
config_data: Option<Arc<ConfigData>>, config_data: Option<Arc<ConfigData>>,
@ -71,10 +80,12 @@ struct LspScopeResolver {
impl Default for LspScopeResolver { impl Default for LspScopeResolver {
fn default() -> Self { fn default() -> Self {
Self { Self {
cjs_tracker: None,
graph_resolver: create_graph_resolver(None, None, None), graph_resolver: create_graph_resolver(None, None, None),
jsr_resolver: None, jsr_resolver: None,
npm_resolver: None, npm_resolver: None,
node_resolver: None, node_resolver: None,
pkg_json_resolver: None,
redirect_resolver: None, redirect_resolver: None,
graph_imports: Default::default(), graph_imports: Default::default(),
config_data: None, config_data: None,
@ -90,14 +101,35 @@ impl LspScopeResolver {
) -> Self { ) -> Self {
let mut npm_resolver = None; let mut npm_resolver = None;
let mut node_resolver = None; let mut node_resolver = None;
let mut lsp_cjs_tracker = None;
let fs = Arc::new(deno_fs::RealFs);
let pkg_json_resolver = Arc::new(PackageJsonResolver::new(
deno_runtime::deno_node::DenoFsNodeResolverEnv::new(fs.clone()),
));
if let Some(http_client) = http_client_provider { if let Some(http_client) = http_client_provider {
npm_resolver = create_npm_resolver( npm_resolver = create_npm_resolver(
config_data.map(|d| d.as_ref()), config_data.map(|d| d.as_ref()),
cache, cache,
http_client, http_client,
&pkg_json_resolver,
) )
.await; .await;
node_resolver = create_node_resolver(npm_resolver.as_ref()); if let Some(npm_resolver) = &npm_resolver {
let in_npm_pkg_checker = create_in_npm_pkg_checker(npm_resolver);
let cjs_tracker = create_cjs_tracker(
in_npm_pkg_checker.clone(),
pkg_json_resolver.clone(),
);
lsp_cjs_tracker =
Some(Arc::new(LspCjsTracker::new(cjs_tracker.clone())));
node_resolver = Some(create_node_resolver(
cjs_tracker,
fs.clone(),
in_npm_pkg_checker,
npm_resolver,
pkg_json_resolver.clone(),
));
}
} }
let graph_resolver = create_graph_resolver( let graph_resolver = create_graph_resolver(
config_data.map(|d| d.as_ref()), config_data.map(|d| d.as_ref()),
@ -134,10 +166,12 @@ impl LspScopeResolver {
}) })
.unwrap_or_default(); .unwrap_or_default();
Self { Self {
cjs_tracker: lsp_cjs_tracker,
graph_resolver, graph_resolver,
jsr_resolver, jsr_resolver,
npm_resolver, npm_resolver,
node_resolver, node_resolver,
pkg_json_resolver: Some(pkg_json_resolver),
redirect_resolver, redirect_resolver,
graph_imports, graph_imports,
config_data: config_data.cloned(), config_data: config_data.cloned(),
@ -147,18 +181,40 @@ impl LspScopeResolver {
fn snapshot(&self) -> Arc<Self> { fn snapshot(&self) -> Arc<Self> {
let npm_resolver = let npm_resolver =
self.npm_resolver.as_ref().map(|r| r.clone_snapshotted()); self.npm_resolver.as_ref().map(|r| r.clone_snapshotted());
let node_resolver = create_node_resolver(npm_resolver.as_ref()); let fs = Arc::new(deno_fs::RealFs);
let pkg_json_resolver = Arc::new(PackageJsonResolver::new(
deno_runtime::deno_node::DenoFsNodeResolverEnv::new(fs.clone()),
));
let mut node_resolver = None;
let mut lsp_cjs_tracker = None;
if let Some(npm_resolver) = &npm_resolver {
let in_npm_pkg_checker = create_in_npm_pkg_checker(npm_resolver);
let cjs_tracker = create_cjs_tracker(
in_npm_pkg_checker.clone(),
pkg_json_resolver.clone(),
);
lsp_cjs_tracker = Some(Arc::new(LspCjsTracker::new(cjs_tracker.clone())));
node_resolver = Some(create_node_resolver(
cjs_tracker,
fs,
in_npm_pkg_checker,
npm_resolver,
pkg_json_resolver.clone(),
));
}
let graph_resolver = create_graph_resolver( let graph_resolver = create_graph_resolver(
self.config_data.as_deref(), self.config_data.as_deref(),
npm_resolver.as_ref(), npm_resolver.as_ref(),
node_resolver.as_ref(), node_resolver.as_ref(),
); );
Arc::new(Self { Arc::new(Self {
cjs_tracker: lsp_cjs_tracker,
graph_resolver, graph_resolver,
jsr_resolver: self.jsr_resolver.clone(), jsr_resolver: self.jsr_resolver.clone(),
npm_resolver, npm_resolver,
node_resolver, node_resolver,
redirect_resolver: self.redirect_resolver.clone(), redirect_resolver: self.redirect_resolver.clone(),
pkg_json_resolver: Some(pkg_json_resolver),
graph_imports: self.graph_imports.clone(), graph_imports: self.graph_imports.clone(),
config_data: self.config_data.clone(), config_data: self.config_data.clone(),
}) })
@ -261,6 +317,22 @@ impl LspResolver {
resolver.graph_resolver.create_graph_npm_resolver() resolver.graph_resolver.create_graph_npm_resolver()
} }
pub fn maybe_cjs_tracker(
&self,
file_referrer: Option<&ModuleSpecifier>,
) -> Option<&Arc<LspCjsTracker>> {
let resolver = self.get_scope_resolver(file_referrer);
resolver.cjs_tracker.as_ref()
}
pub fn maybe_node_resolver(
&self,
file_referrer: Option<&ModuleSpecifier>,
) -> Option<&Arc<CliNodeResolver>> {
let resolver = self.get_scope_resolver(file_referrer);
resolver.node_resolver.as_ref()
}
pub fn maybe_managed_npm_resolver( pub fn maybe_managed_npm_resolver(
&self, &self,
file_referrer: Option<&ModuleSpecifier>, file_referrer: Option<&ModuleSpecifier>,
@ -328,7 +400,7 @@ impl LspResolver {
) -> Option<(ModuleSpecifier, MediaType)> { ) -> Option<(ModuleSpecifier, MediaType)> {
let resolver = self.get_scope_resolver(file_referrer); let resolver = self.get_scope_resolver(file_referrer);
let node_resolver = resolver.node_resolver.as_ref()?; let node_resolver = resolver.node_resolver.as_ref()?;
Some(NodeResolution::into_specifier_and_media_type(Some( Some(into_specifier_and_media_type(Some(
node_resolver node_resolver
.resolve_req_reference(req_ref, referrer, NodeResolutionMode::Types) .resolve_req_reference(req_ref, referrer, NodeResolutionMode::Types)
.ok()?, .ok()?,
@ -346,14 +418,10 @@ impl LspResolver {
.contains("/node_modules/") .contains("/node_modules/")
} }
let global_npm_resolver = self if let Some(node_resolver) =
.get_scope_resolver(Some(specifier)) &self.get_scope_resolver(Some(specifier)).node_resolver
.npm_resolver {
.as_ref() if node_resolver.in_npm_package(specifier) {
.and_then(|npm_resolver| npm_resolver.as_managed())
.filter(|r| r.root_node_modules_path().is_none());
if let Some(npm_resolver) = &global_npm_resolver {
if npm_resolver.in_npm_package(specifier) {
return true; return true;
} }
} }
@ -361,18 +429,6 @@ impl LspResolver {
has_node_modules_dir(specifier) has_node_modules_dir(specifier)
} }
pub fn node_media_type(
&self,
specifier: &ModuleSpecifier,
) -> Option<MediaType> {
let resolver = self.get_scope_resolver(Some(specifier));
let node_resolver = resolver.node_resolver.as_ref()?;
let resolution = node_resolver
.url_to_node_resolution(specifier.clone())
.ok()?;
Some(NodeResolution::into_specifier_and_media_type(Some(resolution)).1)
}
pub fn is_bare_package_json_dep( pub fn is_bare_package_json_dep(
&self, &self,
specifier_text: &str, specifier_text: &str,
@ -398,10 +454,10 @@ impl LspResolver {
referrer: &ModuleSpecifier, referrer: &ModuleSpecifier,
) -> Result<Option<Arc<PackageJson>>, ClosestPkgJsonError> { ) -> Result<Option<Arc<PackageJson>>, ClosestPkgJsonError> {
let resolver = self.get_scope_resolver(Some(referrer)); let resolver = self.get_scope_resolver(Some(referrer));
let Some(node_resolver) = resolver.node_resolver.as_ref() else { let Some(pkg_json_resolver) = resolver.pkg_json_resolver.as_ref() else {
return Ok(None); return Ok(None);
}; };
node_resolver.get_closest_package_json(referrer) pkg_json_resolver.get_closest_package_json(referrer)
} }
pub fn resolve_redirects( pub fn resolve_redirects(
@ -457,11 +513,13 @@ async fn create_npm_resolver(
config_data: Option<&ConfigData>, config_data: Option<&ConfigData>,
cache: &LspCache, cache: &LspCache,
http_client_provider: &Arc<HttpClientProvider>, http_client_provider: &Arc<HttpClientProvider>,
pkg_json_resolver: &Arc<PackageJsonResolver>,
) -> Option<Arc<dyn CliNpmResolver>> { ) -> Option<Arc<dyn CliNpmResolver>> {
let enable_byonm = config_data.map(|d| d.byonm).unwrap_or(false); let enable_byonm = config_data.map(|d| d.byonm).unwrap_or(false);
let options = if enable_byonm { let options = if enable_byonm {
CliNpmResolverCreateOptions::Byonm(CliByonmNpmResolverCreateOptions { CliNpmResolverCreateOptions::Byonm(CliByonmNpmResolverCreateOptions {
fs: CliDenoResolverFs(Arc::new(deno_fs::RealFs)), fs: CliDenoResolverFs(Arc::new(deno_fs::RealFs)),
pkg_json_resolver: pkg_json_resolver.clone(),
root_node_modules_dir: config_data.and_then(|config_data| { root_node_modules_dir: config_data.and_then(|config_data| {
config_data.node_modules_dir.clone().or_else(|| { config_data.node_modules_dir.clone().or_else(|| {
url_to_file_path(&config_data.scope) url_to_file_path(&config_data.scope)
@ -471,7 +529,15 @@ async fn create_npm_resolver(
}), }),
}) })
} else { } else {
CliNpmResolverCreateOptions::Managed(CliNpmResolverManagedCreateOptions { let npmrc = config_data
.and_then(|d| d.npmrc.clone())
.unwrap_or_else(create_default_npmrc);
let npm_cache_dir = Arc::new(NpmCacheDir::new(
&DenoCacheEnvFsAdapter(&deno_fs::RealFs),
cache.deno_dir().npm_folder_path(),
npmrc.get_all_known_registries_urls(),
));
CliNpmResolverCreateOptions::Managed(CliManagedNpmResolverCreateOptions {
http_client_provider: http_client_provider.clone(), http_client_provider: http_client_provider.clone(),
snapshot: match config_data.and_then(|d| d.lockfile.as_ref()) { snapshot: match config_data.and_then(|d| d.lockfile.as_ref()) {
Some(lockfile) => { Some(lockfile) => {
@ -485,7 +551,7 @@ async fn create_npm_resolver(
// updating it. Only the cache request should update the lockfile. // updating it. Only the cache request should update the lockfile.
maybe_lockfile: None, maybe_lockfile: None,
fs: Arc::new(deno_fs::RealFs), fs: Arc::new(deno_fs::RealFs),
npm_global_cache_dir: cache.deno_dir().npm_folder_path(), npm_cache_dir,
// Use an "only" cache setting in order to make the // Use an "only" cache setting in order to make the
// user do an explicit "cache" command and prevent // user do an explicit "cache" command and prevent
// the cache from being filled with lots of packages while // the cache from being filled with lots of packages while
@ -496,9 +562,7 @@ async fn create_npm_resolver(
.and_then(|d| d.node_modules_dir.clone()), .and_then(|d| d.node_modules_dir.clone()),
// only used for top level install, so we can ignore this // only used for top level install, so we can ignore this
npm_install_deps_provider: Arc::new(NpmInstallDepsProvider::empty()), npm_install_deps_provider: Arc::new(NpmInstallDepsProvider::empty()),
npmrc: config_data npmrc,
.and_then(|d| d.npmrc.clone())
.unwrap_or_else(create_default_npmrc),
npm_system_info: NpmSystemInfo::default(), npm_system_info: NpmSystemInfo::default(),
lifecycle_scripts: Default::default(), lifecycle_scripts: Default::default(),
}) })
@ -506,28 +570,59 @@ async fn create_npm_resolver(
Some(create_cli_npm_resolver_for_lsp(options).await) Some(create_cli_npm_resolver_for_lsp(options).await)
} }
fn create_cjs_tracker(
in_npm_pkg_checker: Arc<dyn InNpmPackageChecker>,
pkg_json_resolver: Arc<PackageJsonResolver>,
) -> Arc<CjsTracker> {
Arc::new(CjsTracker::new(
in_npm_pkg_checker,
pkg_json_resolver,
CjsTrackerOptions {
// todo(dsherret): support in the lsp by stabilizing the feature
// so that we don't have to pipe the config in here
unstable_detect_cjs: false,
},
))
}
fn create_in_npm_pkg_checker(
npm_resolver: &Arc<dyn CliNpmResolver>,
) -> Arc<dyn InNpmPackageChecker> {
crate::npm::create_in_npm_pkg_checker(match npm_resolver.as_inner() {
crate::npm::InnerCliNpmResolverRef::Byonm(_) => {
CreateInNpmPkgCheckerOptions::Byonm
}
crate::npm::InnerCliNpmResolverRef::Managed(m) => {
CreateInNpmPkgCheckerOptions::Managed(
CliManagedInNpmPkgCheckerCreateOptions {
root_cache_dir_url: m.global_cache_root_url(),
maybe_node_modules_path: m.maybe_node_modules_path(),
},
)
}
})
}
fn create_node_resolver( fn create_node_resolver(
npm_resolver: Option<&Arc<dyn CliNpmResolver>>, cjs_tracker: Arc<CjsTracker>,
) -> Option<Arc<CliNodeResolver>> { fs: Arc<dyn deno_fs::FileSystem>,
use once_cell::sync::Lazy; in_npm_pkg_checker: Arc<dyn InNpmPackageChecker>,
npm_resolver: &Arc<dyn CliNpmResolver>,
// it's not ideal to share this across all scopes and to pkg_json_resolver: Arc<PackageJsonResolver>,
// never clear it, but it's fine for the time being ) -> Arc<CliNodeResolver> {
static CJS_RESOLUTIONS: Lazy<Arc<CjsResolutionStore>> =
Lazy::new(Default::default);
let npm_resolver = npm_resolver?;
let fs = Arc::new(deno_fs::RealFs);
let node_resolver_inner = Arc::new(NodeResolver::new( let node_resolver_inner = Arc::new(NodeResolver::new(
deno_runtime::deno_node::DenoFsNodeResolverEnv::new(fs.clone()), deno_runtime::deno_node::DenoFsNodeResolverEnv::new(fs.clone()),
in_npm_pkg_checker.clone(),
npm_resolver.clone().into_npm_resolver(), npm_resolver.clone().into_npm_resolver(),
pkg_json_resolver.clone(),
)); ));
Some(Arc::new(CliNodeResolver::new( Arc::new(CliNodeResolver::new(
CJS_RESOLUTIONS.clone(), cjs_tracker.clone(),
fs, fs,
in_npm_pkg_checker,
node_resolver_inner, node_resolver_inner,
npm_resolver.clone(), npm_resolver.clone(),
))) ))
} }
fn create_graph_resolver( fn create_graph_resolver(
@ -702,6 +797,45 @@ impl RedirectResolver {
} }
} }
#[derive(Debug)]
pub struct LspCjsTracker {
cjs_tracker: Arc<CjsTracker>,
}
impl LspCjsTracker {
pub fn new(cjs_tracker: Arc<CjsTracker>) -> Self {
Self { cjs_tracker }
}
pub fn is_cjs(
&self,
specifier: &ModuleSpecifier,
media_type: MediaType,
maybe_parsed_source: Option<&ParsedSource>,
) -> bool {
if let Some(module_kind) =
self.cjs_tracker.get_known_kind(specifier, media_type)
{
module_kind.is_cjs()
} else {
let maybe_is_script = maybe_parsed_source.map(|p| p.compute_is_script());
maybe_is_script
.and_then(|is_script| {
self
.cjs_tracker
.is_cjs_with_known_is_script(specifier, media_type, is_script)
.ok()
})
.unwrap_or_else(|| {
self
.cjs_tracker
.is_maybe_cjs(specifier, media_type)
.unwrap_or(false)
})
}
}
}
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use super::*; use super::*;

View file

@ -650,7 +650,7 @@ pub mod tests {
.unwrap(); .unwrap();
let text_info = parsed_module.text_info_lazy().clone(); let text_info = parsed_module.text_info_lazy().clone();
let mut collector = TestCollector::new(specifier, text_info); let mut collector = TestCollector::new(specifier, text_info);
parsed_module.module().visit_with(&mut collector); parsed_module.program().visit_with(&mut collector);
collector.take() collector.take()
} }

View file

@ -4362,14 +4362,25 @@ fn op_load<'s>(
None None
} else { } else {
let asset_or_document = state.get_asset_or_document(&specifier); let asset_or_document = state.get_asset_or_document(&specifier);
asset_or_document.map(|doc| LoadResponse { asset_or_document.map(|doc| {
let maybe_cjs_tracker = state
.state_snapshot
.resolver
.maybe_cjs_tracker(Some(&specifier));
LoadResponse {
data: doc.text(), data: doc.text(),
script_kind: crate::tsc::as_ts_script_kind(doc.media_type()), script_kind: crate::tsc::as_ts_script_kind(doc.media_type()),
version: state.script_version(&specifier), version: state.script_version(&specifier),
is_cjs: matches!( is_cjs: maybe_cjs_tracker
.map(|t| {
t.is_cjs(
&specifier,
doc.media_type(), doc.media_type(),
MediaType::Cjs | MediaType::Cts | MediaType::Dcts doc.maybe_parsed_source().and_then(|p| p.as_ref().ok()),
), )
})
.unwrap_or(false),
}
}) })
}; };

View file

@ -135,7 +135,7 @@ async fn run_subcommand(flags: Arc<Flags>) -> Result<i32, AnyError> {
tools::compile::compile(flags, compile_flags).await tools::compile::compile(flags, compile_flags).await
}), }),
DenoSubcommand::Coverage(coverage_flags) => spawn_subcommand(async { DenoSubcommand::Coverage(coverage_flags) => spawn_subcommand(async {
tools::coverage::cover_files(flags, coverage_flags).await tools::coverage::cover_files(flags, coverage_flags)
}), }),
DenoSubcommand::Fmt(fmt_flags) => { DenoSubcommand::Fmt(fmt_flags) => {
spawn_subcommand( spawn_subcommand(

View file

@ -2,6 +2,7 @@
use std::borrow::Cow; use std::borrow::Cow;
use std::cell::RefCell; use std::cell::RefCell;
use std::path::Path;
use std::path::PathBuf; use std::path::PathBuf;
use std::pin::Pin; use std::pin::Pin;
use std::rc::Rc; use std::rc::Rc;
@ -23,19 +24,23 @@ use crate::graph_container::ModuleGraphUpdatePermit;
use crate::graph_util::CreateGraphOptions; use crate::graph_util::CreateGraphOptions;
use crate::graph_util::ModuleGraphBuilder; use crate::graph_util::ModuleGraphBuilder;
use crate::node; use crate::node;
use crate::node::CliNodeCodeTranslator;
use crate::npm::CliNpmResolver; use crate::npm::CliNpmResolver;
use crate::resolver::CjsTracker;
use crate::resolver::CliGraphResolver; use crate::resolver::CliGraphResolver;
use crate::resolver::CliNodeResolver; use crate::resolver::CliNodeResolver;
use crate::resolver::ModuleCodeStringSource; use crate::resolver::ModuleCodeStringSource;
use crate::resolver::NotSupportedKindInNpmError;
use crate::resolver::NpmModuleLoader; use crate::resolver::NpmModuleLoader;
use crate::tools::check; use crate::tools::check;
use crate::tools::check::TypeChecker; use crate::tools::check::TypeChecker;
use crate::util::progress_bar::ProgressBar; use crate::util::progress_bar::ProgressBar;
use crate::util::text_encoding::code_without_source_map; use crate::util::text_encoding::code_without_source_map;
use crate::util::text_encoding::source_map_from_code; use crate::util::text_encoding::source_map_from_code;
use crate::worker::ModuleLoaderAndSourceMapGetter; use crate::worker::CreateModuleLoaderResult;
use crate::worker::ModuleLoaderFactory; use crate::worker::ModuleLoaderFactory;
use deno_ast::MediaType; use deno_ast::MediaType;
use deno_ast::ModuleKind;
use deno_core::anyhow::anyhow; use deno_core::anyhow::anyhow;
use deno_core::anyhow::bail; use deno_core::anyhow::bail;
use deno_core::anyhow::Context; use deno_core::anyhow::Context;
@ -63,9 +68,12 @@ use deno_graph::Module;
use deno_graph::ModuleGraph; use deno_graph::ModuleGraph;
use deno_graph::Resolution; use deno_graph::Resolution;
use deno_runtime::code_cache; use deno_runtime::code_cache;
use deno_runtime::deno_fs::FileSystem;
use deno_runtime::deno_node::create_host_defined_options; use deno_runtime::deno_node::create_host_defined_options;
use deno_runtime::deno_node::NodeRequireLoader;
use deno_runtime::deno_permissions::PermissionsContainer; use deno_runtime::deno_permissions::PermissionsContainer;
use deno_semver::npm::NpmPackageReqReference; use deno_semver::npm::NpmPackageReqReference;
use node_resolver::InNpmPackageChecker;
use node_resolver::NodeResolutionMode; use node_resolver::NodeResolutionMode;
pub struct ModuleLoadPreparer { pub struct ModuleLoadPreparer {
@ -198,11 +206,16 @@ struct SharedCliModuleLoaderState {
lib_worker: TsTypeLib, lib_worker: TsTypeLib,
initial_cwd: PathBuf, initial_cwd: PathBuf,
is_inspecting: bool, is_inspecting: bool,
is_npm_main: bool,
is_repl: bool, is_repl: bool,
cjs_tracker: Arc<CjsTracker>,
code_cache: Option<Arc<CodeCache>>, code_cache: Option<Arc<CodeCache>>,
emitter: Arc<Emitter>, emitter: Arc<Emitter>,
fs: Arc<dyn FileSystem>,
in_npm_pkg_checker: Arc<dyn InNpmPackageChecker>,
main_module_graph_container: Arc<MainModuleGraphContainer>, main_module_graph_container: Arc<MainModuleGraphContainer>,
module_load_preparer: Arc<ModuleLoadPreparer>, module_load_preparer: Arc<ModuleLoadPreparer>,
node_code_translator: Arc<CliNodeCodeTranslator>,
node_resolver: Arc<CliNodeResolver>, node_resolver: Arc<CliNodeResolver>,
npm_resolver: Arc<dyn CliNpmResolver>, npm_resolver: Arc<dyn CliNpmResolver>,
npm_module_loader: NpmModuleLoader, npm_module_loader: NpmModuleLoader,
@ -218,10 +231,14 @@ impl CliModuleLoaderFactory {
#[allow(clippy::too_many_arguments)] #[allow(clippy::too_many_arguments)]
pub fn new( pub fn new(
options: &CliOptions, options: &CliOptions,
cjs_tracker: Arc<CjsTracker>,
code_cache: Option<Arc<CodeCache>>, code_cache: Option<Arc<CodeCache>>,
emitter: Arc<Emitter>, emitter: Arc<Emitter>,
fs: Arc<dyn FileSystem>,
in_npm_pkg_checker: Arc<dyn InNpmPackageChecker>,
main_module_graph_container: Arc<MainModuleGraphContainer>, main_module_graph_container: Arc<MainModuleGraphContainer>,
module_load_preparer: Arc<ModuleLoadPreparer>, module_load_preparer: Arc<ModuleLoadPreparer>,
node_code_translator: Arc<CliNodeCodeTranslator>,
node_resolver: Arc<CliNodeResolver>, node_resolver: Arc<CliNodeResolver>,
npm_resolver: Arc<dyn CliNpmResolver>, npm_resolver: Arc<dyn CliNpmResolver>,
npm_module_loader: NpmModuleLoader, npm_module_loader: NpmModuleLoader,
@ -235,14 +252,19 @@ impl CliModuleLoaderFactory {
lib_worker: options.ts_type_lib_worker(), lib_worker: options.ts_type_lib_worker(),
initial_cwd: options.initial_cwd().to_path_buf(), initial_cwd: options.initial_cwd().to_path_buf(),
is_inspecting: options.is_inspecting(), is_inspecting: options.is_inspecting(),
is_npm_main: options.is_npm_main(),
is_repl: matches!( is_repl: matches!(
options.sub_command(), options.sub_command(),
DenoSubcommand::Repl(_) | DenoSubcommand::Jupyter(_) DenoSubcommand::Repl(_) | DenoSubcommand::Jupyter(_)
), ),
cjs_tracker,
code_cache, code_cache,
emitter, emitter,
fs,
in_npm_pkg_checker,
main_module_graph_container, main_module_graph_container,
module_load_preparer, module_load_preparer,
node_code_translator,
node_resolver, node_resolver,
npm_resolver, npm_resolver,
npm_module_loader, npm_module_loader,
@ -259,19 +281,30 @@ impl CliModuleLoaderFactory {
is_worker: bool, is_worker: bool,
parent_permissions: PermissionsContainer, parent_permissions: PermissionsContainer,
permissions: PermissionsContainer, permissions: PermissionsContainer,
) -> ModuleLoaderAndSourceMapGetter { ) -> CreateModuleLoaderResult {
let loader = Rc::new(CliModuleLoader(Rc::new(CliModuleLoaderInner { let module_loader =
Rc::new(CliModuleLoader(Rc::new(CliModuleLoaderInner {
lib, lib,
is_worker, is_worker,
is_npm_main: self.shared.is_npm_main,
parent_permissions, parent_permissions,
permissions, permissions,
graph_container, graph_container: graph_container.clone(),
node_code_translator: self.shared.node_code_translator.clone(),
emitter: self.shared.emitter.clone(), emitter: self.shared.emitter.clone(),
parsed_source_cache: self.shared.parsed_source_cache.clone(), parsed_source_cache: self.shared.parsed_source_cache.clone(),
shared: self.shared.clone(), shared: self.shared.clone(),
}))); })));
ModuleLoaderAndSourceMapGetter { let node_require_loader = Rc::new(CliNodeRequireLoader::new(
module_loader: loader, self.shared.emitter.clone(),
self.shared.fs.clone(),
graph_container,
self.shared.in_npm_pkg_checker.clone(),
self.shared.npm_resolver.clone(),
));
CreateModuleLoaderResult {
module_loader,
node_require_loader,
} }
} }
} }
@ -280,7 +313,7 @@ impl ModuleLoaderFactory for CliModuleLoaderFactory {
fn create_for_main( fn create_for_main(
&self, &self,
root_permissions: PermissionsContainer, root_permissions: PermissionsContainer,
) -> ModuleLoaderAndSourceMapGetter { ) -> CreateModuleLoaderResult {
self.create_with_lib( self.create_with_lib(
(*self.shared.main_module_graph_container).clone(), (*self.shared.main_module_graph_container).clone(),
self.shared.lib_window, self.shared.lib_window,
@ -294,7 +327,7 @@ impl ModuleLoaderFactory for CliModuleLoaderFactory {
&self, &self,
parent_permissions: PermissionsContainer, parent_permissions: PermissionsContainer,
permissions: PermissionsContainer, permissions: PermissionsContainer,
) -> ModuleLoaderAndSourceMapGetter { ) -> CreateModuleLoaderResult {
self.create_with_lib( self.create_with_lib(
// create a fresh module graph for the worker // create a fresh module graph for the worker
WorkerModuleGraphContainer::new(Arc::new(ModuleGraph::new( WorkerModuleGraphContainer::new(Arc::new(ModuleGraph::new(
@ -310,6 +343,7 @@ impl ModuleLoaderFactory for CliModuleLoaderFactory {
struct CliModuleLoaderInner<TGraphContainer: ModuleGraphContainer> { struct CliModuleLoaderInner<TGraphContainer: ModuleGraphContainer> {
lib: TsTypeLib, lib: TsTypeLib,
is_npm_main: bool,
is_worker: bool, is_worker: bool,
/// The initial set of permissions used to resolve the static imports in the /// The initial set of permissions used to resolve the static imports in the
/// worker. These are "allow all" for main worker, and parent thread /// worker. These are "allow all" for main worker, and parent thread
@ -318,6 +352,7 @@ struct CliModuleLoaderInner<TGraphContainer: ModuleGraphContainer> {
permissions: PermissionsContainer, permissions: PermissionsContainer,
shared: Arc<SharedCliModuleLoaderState>, shared: Arc<SharedCliModuleLoaderState>,
emitter: Arc<Emitter>, emitter: Arc<Emitter>,
node_code_translator: Arc<CliNodeCodeTranslator>,
parsed_source_cache: Arc<ParsedSourceCache>, parsed_source_cache: Arc<ParsedSourceCache>,
graph_container: TGraphContainer, graph_container: TGraphContainer,
} }
@ -331,24 +366,7 @@ impl<TGraphContainer: ModuleGraphContainer>
maybe_referrer: Option<&ModuleSpecifier>, maybe_referrer: Option<&ModuleSpecifier>,
requested_module_type: RequestedModuleType, requested_module_type: RequestedModuleType,
) -> Result<ModuleSource, AnyError> { ) -> Result<ModuleSource, AnyError> {
let code_source = match self.load_prepared_module(specifier).await? { let code_source = self.load_code_source(specifier, maybe_referrer).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 { let code = if self.shared.is_inspecting {
// we need the code with the source map in order for // we need the code with the source map in order for
// it to work with --inspect or --inspect-brk // it to work with --inspect or --inspect-brk
@ -402,6 +420,29 @@ impl<TGraphContainer: ModuleGraphContainer>
)) ))
} }
async fn load_code_source(
&self,
specifier: &ModuleSpecifier,
maybe_referrer: Option<&ModuleSpecifier>,
) -> Result<ModuleCodeStringSource, AnyError> {
if let Some(code_source) = self.load_prepared_module(specifier).await? {
return Ok(code_source);
}
if self.shared.node_resolver.in_npm_package(specifier) {
return self
.shared
.npm_module_loader
.load(specifier, maybe_referrer)
.await;
}
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))
}
fn resolve_referrer( fn resolve_referrer(
&self, &self,
referrer: &str, referrer: &str,
@ -474,15 +515,11 @@ impl<TGraphContainer: ModuleGraphContainer>
if self.shared.is_repl { if self.shared.is_repl {
if let Ok(reference) = NpmPackageReqReference::from_specifier(&specifier) if let Ok(reference) = NpmPackageReqReference::from_specifier(&specifier)
{ {
return self return self.shared.node_resolver.resolve_req_reference(
.shared
.node_resolver
.resolve_req_reference(
&reference, &reference,
referrer, referrer,
NodeResolutionMode::Execution, NodeResolutionMode::Execution,
) );
.map(|res| res.into_url());
} }
} }
@ -506,13 +543,15 @@ impl<TGraphContainer: ModuleGraphContainer>
.with_context(|| { .with_context(|| {
format!("Could not resolve '{}'.", module.nv_reference) format!("Could not resolve '{}'.", module.nv_reference)
})? })?
.into_url()
} }
Some(Module::Node(module)) => module.specifier.clone(), Some(Module::Node(module)) => module.specifier.clone(),
Some(Module::Js(module)) => module.specifier.clone(), Some(Module::Js(module)) => module.specifier.clone(),
Some(Module::Json(module)) => module.specifier.clone(), Some(Module::Json(module)) => module.specifier.clone(),
Some(Module::External(module)) => { Some(Module::External(module)) => {
node::resolve_specifier_into_node_modules(&module.specifier) node::resolve_specifier_into_node_modules(
&module.specifier,
self.shared.fs.as_ref(),
)
} }
None => specifier.into_owned(), None => specifier.into_owned(),
}; };
@ -534,7 +573,7 @@ impl<TGraphContainer: ModuleGraphContainer>
}) => { }) => {
let transpile_result = self let transpile_result = self
.emitter .emitter
.emit_parsed_source(specifier, media_type, source) .emit_parsed_source(specifier, media_type, ModuleKind::Esm, source)
.await?; .await?;
// at this point, we no longer need the parsed source in memory, so free it // at this point, we no longer need the parsed source in memory, so free it
@ -547,11 +586,19 @@ impl<TGraphContainer: ModuleGraphContainer>
media_type, media_type,
})) }))
} }
Some(CodeOrDeferredEmit::Cjs {
specifier,
media_type,
source,
}) => self
.load_maybe_cjs(specifier, media_type, source)
.await
.map(Some),
None => Ok(None), None => Ok(None),
} }
} }
fn load_prepared_module_sync( fn load_prepared_module_for_source_map_sync(
&self, &self,
specifier: &ModuleSpecifier, specifier: &ModuleSpecifier,
) -> Result<Option<ModuleCodeStringSource>, AnyError> { ) -> Result<Option<ModuleCodeStringSource>, AnyError> {
@ -564,9 +611,12 @@ impl<TGraphContainer: ModuleGraphContainer>
media_type, media_type,
source, source,
}) => { }) => {
let transpile_result = self let transpile_result = self.emitter.emit_parsed_source_sync(
.emitter specifier,
.emit_parsed_source_sync(specifier, media_type, source)?; media_type,
ModuleKind::Esm,
source,
)?;
// at this point, we no longer need the parsed source in memory, so free it // at this point, we no longer need the parsed source in memory, so free it
self.parsed_source_cache.free(specifier); self.parsed_source_cache.free(specifier);
@ -578,6 +628,14 @@ impl<TGraphContainer: ModuleGraphContainer>
media_type, media_type,
})) }))
} }
Some(CodeOrDeferredEmit::Cjs { .. }) => {
self.parsed_source_cache.free(specifier);
// todo(dsherret): to make this work, we should probably just
// rely on the CJS export cache. At the moment this is hard because
// cjs export analysis is only async
Ok(None)
}
None => Ok(None), None => Ok(None),
} }
} }
@ -607,20 +665,40 @@ impl<TGraphContainer: ModuleGraphContainer>
source, source,
media_type, media_type,
specifier, specifier,
is_script,
.. ..
})) => { })) => {
// todo(dsherret): revert in https://github.com/denoland/deno/pull/26439
if self.is_npm_main && *is_script
|| self.shared.cjs_tracker.is_cjs_with_known_is_script(
specifier,
*media_type,
*is_script,
)?
{
return Ok(Some(CodeOrDeferredEmit::Cjs {
specifier,
media_type: *media_type,
source,
}));
}
let code: ModuleCodeString = match media_type { let code: ModuleCodeString = match media_type {
MediaType::JavaScript MediaType::JavaScript
| MediaType::Unknown | MediaType::Unknown
| MediaType::Cjs
| MediaType::Mjs | MediaType::Mjs
| MediaType::Json => source.clone().into(), | MediaType::Json => source.clone().into(),
MediaType::Dts | MediaType::Dcts | MediaType::Dmts => { MediaType::Dts | MediaType::Dcts | MediaType::Dmts => {
Default::default() Default::default()
} }
MediaType::Cjs | MediaType::Cts => {
return Ok(Some(CodeOrDeferredEmit::Cjs {
specifier,
media_type: *media_type,
source,
}));
}
MediaType::TypeScript MediaType::TypeScript
| MediaType::Mts | MediaType::Mts
| MediaType::Cts
| MediaType::Jsx | MediaType::Jsx
| MediaType::Tsx => { | MediaType::Tsx => {
return Ok(Some(CodeOrDeferredEmit::DeferredEmit { return Ok(Some(CodeOrDeferredEmit::DeferredEmit {
@ -629,7 +707,7 @@ impl<TGraphContainer: ModuleGraphContainer>
source, source,
})); }));
} }
MediaType::TsBuildInfo | MediaType::Wasm | MediaType::SourceMap => { MediaType::Css | MediaType::Wasm | MediaType::SourceMap => {
panic!("Unexpected media type {media_type} for {specifier}") panic!("Unexpected media type {media_type} for {specifier}")
} }
}; };
@ -651,6 +729,48 @@ impl<TGraphContainer: ModuleGraphContainer>
| None => Ok(None), | None => Ok(None),
} }
} }
async fn load_maybe_cjs(
&self,
specifier: &ModuleSpecifier,
media_type: MediaType,
original_source: &Arc<str>,
) -> Result<ModuleCodeStringSource, AnyError> {
let js_source = if media_type.is_emittable() {
Cow::Owned(
self
.emitter
.emit_parsed_source(
specifier,
media_type,
ModuleKind::Cjs,
original_source,
)
.await?,
)
} else {
Cow::Borrowed(original_source.as_ref())
};
let text = self
.node_code_translator
.translate_cjs_to_esm(specifier, Some(js_source))
.await?;
// at this point, we no longer need the parsed source in memory, so free it
self.parsed_source_cache.free(specifier);
Ok(ModuleCodeStringSource {
code: match text {
// perf: if the text is borrowed, that means it didn't make any changes
// to the original source, so we can just provide that instead of cloning
// the borrowed text
Cow::Borrowed(_) => {
ModuleSourceCode::String(original_source.clone().into())
}
Cow::Owned(text) => ModuleSourceCode::String(text.into()),
},
found_url: specifier.clone(),
media_type,
})
}
} }
enum CodeOrDeferredEmit<'a> { enum CodeOrDeferredEmit<'a> {
@ -660,6 +780,11 @@ enum CodeOrDeferredEmit<'a> {
media_type: MediaType, media_type: MediaType,
source: &'a Arc<str>, source: &'a Arc<str>,
}, },
Cjs {
specifier: &'a ModuleSpecifier,
media_type: MediaType,
source: &'a Arc<str>,
},
} }
// todo(dsherret): this double Rc boxing is not ideal // todo(dsherret): this double Rc boxing is not ideal
@ -821,7 +946,10 @@ impl<TGraphContainer: ModuleGraphContainer> ModuleLoader
"wasm" | "file" | "http" | "https" | "data" | "blob" => (), "wasm" | "file" | "http" | "https" | "data" | "blob" => (),
_ => return None, _ => return None,
} }
let source = self.0.load_prepared_module_sync(&specifier).ok()??; let source = self
.0
.load_prepared_module_for_source_map_sync(&specifier)
.ok()??;
source_map_from_code(source.code.as_bytes()) source_map_from_code(source.code.as_bytes())
} }
@ -900,3 +1028,79 @@ impl ModuleGraphUpdatePermit for WorkerModuleGraphUpdatePermit {
drop(self.permit); // explicit drop for clarity drop(self.permit); // explicit drop for clarity
} }
} }
#[derive(Debug)]
struct CliNodeRequireLoader<TGraphContainer: ModuleGraphContainer> {
emitter: Arc<Emitter>,
fs: Arc<dyn FileSystem>,
graph_container: TGraphContainer,
in_npm_pkg_checker: Arc<dyn InNpmPackageChecker>,
npm_resolver: Arc<dyn CliNpmResolver>,
}
impl<TGraphContainer: ModuleGraphContainer>
CliNodeRequireLoader<TGraphContainer>
{
pub fn new(
emitter: Arc<Emitter>,
fs: Arc<dyn FileSystem>,
graph_container: TGraphContainer,
in_npm_pkg_checker: Arc<dyn InNpmPackageChecker>,
npm_resolver: Arc<dyn CliNpmResolver>,
) -> Self {
Self {
emitter,
fs,
graph_container,
in_npm_pkg_checker,
npm_resolver,
}
}
}
impl<TGraphContainer: ModuleGraphContainer> NodeRequireLoader
for CliNodeRequireLoader<TGraphContainer>
{
fn ensure_read_permission<'a>(
&self,
permissions: &mut dyn deno_runtime::deno_node::NodePermissions,
path: &'a Path,
) -> Result<std::borrow::Cow<'a, Path>, AnyError> {
if let Ok(url) = deno_path_util::url_from_file_path(path) {
// allow reading if it's in the module graph
if self.graph_container.graph().get(&url).is_some() {
return Ok(std::borrow::Cow::Borrowed(path));
}
}
self.npm_resolver.ensure_read_permission(permissions, path)
}
fn load_text_file_lossy(&self, path: &Path) -> Result<String, AnyError> {
// todo(dsherret): use the preloaded module from the graph if available?
let media_type = MediaType::from_path(path);
let text = self.fs.read_text_file_lossy_sync(path, None)?;
if media_type.is_emittable() {
let specifier = deno_path_util::url_from_file_path(path)?;
if self.in_npm_pkg_checker.in_npm_package(&specifier) {
return Err(
NotSupportedKindInNpmError {
media_type,
specifier,
}
.into(),
);
}
self.emitter.emit_parsed_source_sync(
&specifier,
media_type,
// this is probably not super accurate due to require esm, but probably ok.
// If we find this causes a lot of churn in the emit cache then we should
// investigate how we can make this better
ModuleKind::Cjs,
&text.into(),
)
} else {
Ok(text)
}
}
}

View file

@ -1,11 +1,14 @@
// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. // Copyright 2018-2024 the Deno authors. All rights reserved. MIT license.
use std::borrow::Cow;
use std::sync::Arc; use std::sync::Arc;
use deno_ast::MediaType; use deno_ast::MediaType;
use deno_ast::ModuleSpecifier; use deno_ast::ModuleSpecifier;
use deno_core::error::AnyError; use deno_core::error::AnyError;
use deno_graph::ParsedSourceStore; 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_fs;
use deno_runtime::deno_node::DenoFsNodeResolverEnv; use deno_runtime::deno_node::DenoFsNodeResolverEnv;
use node_resolver::analyze::CjsAnalysis as ExtNodeCjsAnalysis; use node_resolver::analyze::CjsAnalysis as ExtNodeCjsAnalysis;
@ -18,8 +21,8 @@ use serde::Serialize;
use crate::cache::CacheDBHash; use crate::cache::CacheDBHash;
use crate::cache::NodeAnalysisCache; use crate::cache::NodeAnalysisCache;
use crate::cache::ParsedSourceCache; use crate::cache::ParsedSourceCache;
use crate::resolver::CliNodeResolver; use crate::resolver::CjsTracker;
use crate::util::fs::canonicalize_path_maybe_not_exists; use crate::util::fs::canonicalize_path_maybe_not_exists_with_fs;
pub type CliNodeCodeTranslator = pub type CliNodeCodeTranslator =
NodeCodeTranslator<CliCjsCodeAnalyzer, DenoFsNodeResolverEnv>; NodeCodeTranslator<CliCjsCodeAnalyzer, DenoFsNodeResolverEnv>;
@ -32,14 +35,14 @@ pub type CliNodeCodeTranslator =
/// because the node_modules folder might not exist at that time. /// because the node_modules folder might not exist at that time.
pub fn resolve_specifier_into_node_modules( pub fn resolve_specifier_into_node_modules(
specifier: &ModuleSpecifier, specifier: &ModuleSpecifier,
fs: &dyn deno_fs::FileSystem,
) -> ModuleSpecifier { ) -> ModuleSpecifier {
specifier url_to_file_path(specifier)
.to_file_path()
.ok() .ok()
// this path might not exist at the time the graph is being created // this path might not exist at the time the graph is being created
// because the node_modules folder might not yet exist // because the node_modules folder might not yet exist
.and_then(|path| canonicalize_path_maybe_not_exists(&path).ok()) .and_then(|path| canonicalize_path_maybe_not_exists_with_fs(&path, fs).ok())
.and_then(|path| ModuleSpecifier::from_file_path(path).ok()) .and_then(|path| url_from_file_path(&path).ok())
.unwrap_or_else(|| specifier.clone()) .unwrap_or_else(|| specifier.clone())
} }
@ -56,23 +59,29 @@ pub enum CliCjsAnalysis {
pub struct CliCjsCodeAnalyzer { pub struct CliCjsCodeAnalyzer {
cache: NodeAnalysisCache, cache: NodeAnalysisCache,
cjs_tracker: Arc<CjsTracker>,
fs: deno_fs::FileSystemRc, fs: deno_fs::FileSystemRc,
node_resolver: Arc<CliNodeResolver>,
parsed_source_cache: Option<Arc<ParsedSourceCache>>, 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 { impl CliCjsCodeAnalyzer {
pub fn new( pub fn new(
cache: NodeAnalysisCache, cache: NodeAnalysisCache,
cjs_tracker: Arc<CjsTracker>,
fs: deno_fs::FileSystemRc, fs: deno_fs::FileSystemRc,
node_resolver: Arc<CliNodeResolver>,
parsed_source_cache: Option<Arc<ParsedSourceCache>>, parsed_source_cache: Option<Arc<ParsedSourceCache>>,
is_npm_main: bool,
) -> Self { ) -> Self {
Self { Self {
cache, cache,
cjs_tracker,
fs, fs,
node_resolver,
parsed_source_cache, parsed_source_cache,
is_npm_main,
} }
} }
@ -88,7 +97,7 @@ impl CliCjsCodeAnalyzer {
return Ok(analysis); return Ok(analysis);
} }
let mut media_type = MediaType::from_specifier(specifier); let media_type = MediaType::from_specifier(specifier);
if media_type == MediaType::Json { if media_type == MediaType::Json {
return Ok(CliCjsAnalysis::Cjs { return Ok(CliCjsAnalysis::Cjs {
exports: vec![], exports: vec![],
@ -96,31 +105,20 @@ impl CliCjsCodeAnalyzer {
}); });
} }
if media_type == MediaType::JavaScript { let cjs_tracker = self.cjs_tracker.clone();
if let Some(package_json) = let is_npm_main = self.is_npm_main;
self.node_resolver.get_closest_package_json(specifier)? let is_maybe_cjs =
{ cjs_tracker.is_maybe_cjs(specifier, media_type)? || is_npm_main;
match package_json.typ.as_str() { let analysis = if is_maybe_cjs {
"commonjs" => {
media_type = MediaType::Cjs;
}
"module" => {
media_type = MediaType::Mjs;
}
_ => {}
}
}
}
let maybe_parsed_source = self let maybe_parsed_source = self
.parsed_source_cache .parsed_source_cache
.as_ref() .as_ref()
.and_then(|c| c.remove_parsed_source(specifier)); .and_then(|c| c.remove_parsed_source(specifier));
let analysis = deno_core::unsync::spawn_blocking({ deno_core::unsync::spawn_blocking({
let specifier = specifier.clone(); let specifier = specifier.clone();
let source: Arc<str> = source.into(); let source: Arc<str> = source.into();
move || -> Result<_, deno_ast::ParseDiagnostic> { move || -> Result<_, AnyError> {
let parsed_source = let parsed_source =
maybe_parsed_source.map(Ok).unwrap_or_else(|| { maybe_parsed_source.map(Ok).unwrap_or_else(|| {
deno_ast::parse_program(deno_ast::ParseParams { deno_ast::parse_program(deno_ast::ParseParams {
@ -132,26 +130,28 @@ impl CliCjsCodeAnalyzer {
maybe_syntax: None, maybe_syntax: None,
}) })
})?; })?;
if parsed_source.is_script() { 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(); let analysis = parsed_source.analyze_cjs();
Ok(CliCjsAnalysis::Cjs { Ok(CliCjsAnalysis::Cjs {
exports: analysis.exports, exports: analysis.exports,
reexports: analysis.reexports, 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 { } else {
Ok(CliCjsAnalysis::Esm) Ok(CliCjsAnalysis::Esm)
} }
} }
}) })
.await .await
.unwrap()?; .unwrap()?
} else {
CliCjsAnalysis::Esm
};
self self
.cache .cache
@ -163,11 +163,11 @@ impl CliCjsCodeAnalyzer {
#[async_trait::async_trait(?Send)] #[async_trait::async_trait(?Send)]
impl CjsCodeAnalyzer for CliCjsCodeAnalyzer { impl CjsCodeAnalyzer for CliCjsCodeAnalyzer {
async fn analyze_cjs( async fn analyze_cjs<'a>(
&self, &self,
specifier: &ModuleSpecifier, specifier: &ModuleSpecifier,
source: Option<String>, source: Option<Cow<'a, str>>,
) -> Result<ExtNodeCjsAnalysis, AnyError> { ) -> Result<ExtNodeCjsAnalysis<'a>, AnyError> {
let source = match source { let source = match source {
Some(source) => source, Some(source) => source,
None => { None => {
@ -175,7 +175,7 @@ impl CjsCodeAnalyzer for CliCjsCodeAnalyzer {
if let Ok(source_from_file) = if let Ok(source_from_file) =
self.fs.read_text_file_lossy_async(path, None).await self.fs.read_text_file_lossy_async(path, None).await
{ {
source_from_file Cow::Owned(source_from_file)
} else { } else {
return Ok(ExtNodeCjsAnalysis::Cjs(CjsAnalysisExports { return Ok(ExtNodeCjsAnalysis::Cjs(CjsAnalysisExports {
exports: vec![], exports: vec![],

View file

@ -10,8 +10,8 @@ use deno_core::serde_json;
use deno_core::url::Url; use deno_core::url::Url;
use deno_resolver::npm::ByonmNpmResolver; use deno_resolver::npm::ByonmNpmResolver;
use deno_resolver::npm::ByonmNpmResolverCreateOptions; use deno_resolver::npm::ByonmNpmResolverCreateOptions;
use deno_runtime::deno_node::DenoFsNodeResolverEnv;
use deno_runtime::deno_node::NodePermissions; use deno_runtime::deno_node::NodePermissions;
use deno_runtime::deno_node::NodeRequireResolver;
use deno_runtime::ops::process::NpmProcessStateProvider; use deno_runtime::ops::process::NpmProcessStateProvider;
use deno_semver::package::PackageReq; use deno_semver::package::PackageReq;
use node_resolver::NpmResolver; use node_resolver::NpmResolver;
@ -25,30 +25,14 @@ use super::InnerCliNpmResolverRef;
use super::ResolvePkgFolderFromDenoReqError; use super::ResolvePkgFolderFromDenoReqError;
pub type CliByonmNpmResolverCreateOptions = pub type CliByonmNpmResolverCreateOptions =
ByonmNpmResolverCreateOptions<CliDenoResolverFs>; ByonmNpmResolverCreateOptions<CliDenoResolverFs, DenoFsNodeResolverEnv>;
pub type CliByonmNpmResolver = ByonmNpmResolver<CliDenoResolverFs>; pub type CliByonmNpmResolver =
ByonmNpmResolver<CliDenoResolverFs, DenoFsNodeResolverEnv>;
// todo(dsherret): the services hanging off `CliNpmResolver` doesn't seem ideal. We should probably decouple. // todo(dsherret): the services hanging off `CliNpmResolver` doesn't seem ideal. We should probably decouple.
#[derive(Debug)] #[derive(Debug)]
struct CliByonmWrapper(Arc<CliByonmNpmResolver>); struct CliByonmWrapper(Arc<CliByonmNpmResolver>);
impl NodeRequireResolver for CliByonmWrapper {
fn ensure_read_permission<'a>(
&self,
permissions: &mut dyn NodePermissions,
path: &'a Path,
) -> Result<Cow<'a, Path>, AnyError> {
if !path
.components()
.any(|c| c.as_os_str().to_ascii_lowercase() == "node_modules")
{
permissions.check_read_path(path)
} else {
Ok(Cow::Borrowed(path))
}
}
}
impl NpmProcessStateProvider for CliByonmWrapper { impl NpmProcessStateProvider for CliByonmWrapper {
fn get_npm_process_state(&self) -> String { fn get_npm_process_state(&self) -> String {
serde_json::to_string(&NpmProcessState { serde_json::to_string(&NpmProcessState {
@ -67,10 +51,6 @@ impl CliNpmResolver for CliByonmNpmResolver {
self self
} }
fn into_require_resolver(self: Arc<Self>) -> Arc<dyn NodeRequireResolver> {
Arc::new(CliByonmWrapper(self))
}
fn into_process_state_provider( fn into_process_state_provider(
self: Arc<Self>, self: Arc<Self>,
) -> Arc<dyn NpmProcessStateProvider> { ) -> Arc<dyn NpmProcessStateProvider> {
@ -100,6 +80,21 @@ impl CliNpmResolver for CliByonmNpmResolver {
.map_err(ResolvePkgFolderFromDenoReqError::Byonm) .map_err(ResolvePkgFolderFromDenoReqError::Byonm)
} }
fn ensure_read_permission<'a>(
&self,
permissions: &mut dyn NodePermissions,
path: &'a Path,
) -> Result<Cow<'a, Path>, AnyError> {
if !path
.components()
.any(|c| c.as_os_str().to_ascii_lowercase() == "node_modules")
{
permissions.check_read_path(path)
} else {
Ok(Cow::Borrowed(path))
}
}
fn check_state_hash(&self) -> Option<u64> { fn check_state_hash(&self) -> Option<u64> {
// it is very difficult to determine the check state hash for byonm // it is very difficult to determine the check state hash for byonm
// so we just return None to signify check caching is not supported // so we just return None to signify check caching is not supported

View file

@ -36,7 +36,7 @@ pub use tarball::TarballCache;
/// Stores a single copy of npm packages in a cache. /// Stores a single copy of npm packages in a cache.
#[derive(Debug)] #[derive(Debug)]
pub struct NpmCache { pub struct NpmCache {
cache_dir: NpmCacheDir, cache_dir: Arc<NpmCacheDir>,
cache_setting: CacheSetting, cache_setting: CacheSetting,
npmrc: Arc<ResolvedNpmRc>, npmrc: Arc<ResolvedNpmRc>,
/// ensures a package is only downloaded once per run /// ensures a package is only downloaded once per run
@ -45,7 +45,7 @@ pub struct NpmCache {
impl NpmCache { impl NpmCache {
pub fn new( pub fn new(
cache_dir: NpmCacheDir, cache_dir: Arc<NpmCacheDir>,
cache_setting: CacheSetting, cache_setting: CacheSetting,
npmrc: Arc<ResolvedNpmRc>, npmrc: Arc<ResolvedNpmRc>,
) -> Self { ) -> Self {
@ -61,6 +61,10 @@ impl NpmCache {
&self.cache_setting &self.cache_setting
} }
pub fn root_dir_path(&self) -> &Path {
self.cache_dir.root_dir()
}
pub fn root_dir_url(&self) -> &Url { pub fn root_dir_url(&self) -> &Url {
self.cache_dir.root_dir_url() self.cache_dir.root_dir_url()
} }
@ -152,10 +156,6 @@ impl NpmCache {
self.cache_dir.package_name_folder(name, registry_url) self.cache_dir.package_name_folder(name, registry_url)
} }
pub fn root_folder(&self) -> PathBuf {
self.cache_dir.root_dir().to_owned()
}
pub fn resolve_package_folder_id_from_specifier( pub fn resolve_package_folder_id_from_specifier(
&self, &self,
specifier: &ModuleSpecifier, specifier: &ModuleSpecifier,

View file

@ -12,6 +12,7 @@ use deno_cache_dir::npm::NpmCacheDir;
use deno_core::anyhow::Context; use deno_core::anyhow::Context;
use deno_core::error::AnyError; use deno_core::error::AnyError;
use deno_core::serde_json; use deno_core::serde_json;
use deno_core::url::Url;
use deno_npm::npm_rc::ResolvedNpmRc; use deno_npm::npm_rc::ResolvedNpmRc;
use deno_npm::registry::NpmPackageInfo; use deno_npm::registry::NpmPackageInfo;
use deno_npm::registry::NpmRegistryApi; use deno_npm::registry::NpmRegistryApi;
@ -24,12 +25,12 @@ use deno_npm::NpmSystemInfo;
use deno_runtime::colors; use deno_runtime::colors;
use deno_runtime::deno_fs::FileSystem; use deno_runtime::deno_fs::FileSystem;
use deno_runtime::deno_node::NodePermissions; use deno_runtime::deno_node::NodePermissions;
use deno_runtime::deno_node::NodeRequireResolver;
use deno_runtime::ops::process::NpmProcessStateProvider; use deno_runtime::ops::process::NpmProcessStateProvider;
use deno_semver::package::PackageNv; use deno_semver::package::PackageNv;
use deno_semver::package::PackageReq; use deno_semver::package::PackageReq;
use node_resolver::errors::PackageFolderResolveError; use node_resolver::errors::PackageFolderResolveError;
use node_resolver::errors::PackageFolderResolveIoError; use node_resolver::errors::PackageFolderResolveIoError;
use node_resolver::InNpmPackageChecker;
use node_resolver::NpmResolver; use node_resolver::NpmResolver;
use resolution::AddPkgReqsResult; use resolution::AddPkgReqsResult;
@ -39,7 +40,6 @@ use crate::args::NpmInstallDepsProvider;
use crate::args::NpmProcessState; use crate::args::NpmProcessState;
use crate::args::NpmProcessStateKind; use crate::args::NpmProcessStateKind;
use crate::args::PackageJsonDepValueParseWithLocationError; use crate::args::PackageJsonDepValueParseWithLocationError;
use crate::cache::DenoCacheEnvFsAdapter;
use crate::cache::FastInsecureHasher; use crate::cache::FastInsecureHasher;
use crate::http_util::HttpClientProvider; use crate::http_util::HttpClientProvider;
use crate::util::fs::canonicalize_path_maybe_not_exists_with_fs; use crate::util::fs::canonicalize_path_maybe_not_exists_with_fs;
@ -66,12 +66,12 @@ pub enum CliNpmResolverManagedSnapshotOption {
Specified(Option<ValidSerializedNpmResolutionSnapshot>), Specified(Option<ValidSerializedNpmResolutionSnapshot>),
} }
pub struct CliNpmResolverManagedCreateOptions { pub struct CliManagedNpmResolverCreateOptions {
pub snapshot: CliNpmResolverManagedSnapshotOption, pub snapshot: CliNpmResolverManagedSnapshotOption,
pub maybe_lockfile: Option<Arc<CliLockfile>>, pub maybe_lockfile: Option<Arc<CliLockfile>>,
pub fs: Arc<dyn deno_runtime::deno_fs::FileSystem>, pub fs: Arc<dyn deno_runtime::deno_fs::FileSystem>,
pub http_client_provider: Arc<crate::http_util::HttpClientProvider>, pub http_client_provider: Arc<crate::http_util::HttpClientProvider>,
pub npm_global_cache_dir: PathBuf, pub npm_cache_dir: Arc<NpmCacheDir>,
pub cache_setting: crate::args::CacheSetting, pub cache_setting: crate::args::CacheSetting,
pub text_only_progress_bar: crate::util::progress_bar::ProgressBar, pub text_only_progress_bar: crate::util::progress_bar::ProgressBar,
pub maybe_node_modules_path: Option<PathBuf>, pub maybe_node_modules_path: Option<PathBuf>,
@ -82,7 +82,7 @@ pub struct CliNpmResolverManagedCreateOptions {
} }
pub async fn create_managed_npm_resolver_for_lsp( pub async fn create_managed_npm_resolver_for_lsp(
options: CliNpmResolverManagedCreateOptions, options: CliManagedNpmResolverCreateOptions,
) -> Arc<dyn CliNpmResolver> { ) -> Arc<dyn CliNpmResolver> {
let npm_cache = create_cache(&options); let npm_cache = create_cache(&options);
let npm_api = create_api(&options, npm_cache.clone()); let npm_api = create_api(&options, npm_cache.clone());
@ -115,7 +115,7 @@ pub async fn create_managed_npm_resolver_for_lsp(
} }
pub async fn create_managed_npm_resolver( pub async fn create_managed_npm_resolver(
options: CliNpmResolverManagedCreateOptions, options: CliManagedNpmResolverCreateOptions,
) -> Result<Arc<dyn CliNpmResolver>, AnyError> { ) -> Result<Arc<dyn CliNpmResolver>, AnyError> {
let npm_cache = create_cache(&options); let npm_cache = create_cache(&options);
let npm_api = create_api(&options, npm_cache.clone()); let npm_api = create_api(&options, npm_cache.clone());
@ -189,20 +189,16 @@ fn create_inner(
)) ))
} }
fn create_cache(options: &CliNpmResolverManagedCreateOptions) -> Arc<NpmCache> { fn create_cache(options: &CliManagedNpmResolverCreateOptions) -> Arc<NpmCache> {
Arc::new(NpmCache::new( Arc::new(NpmCache::new(
NpmCacheDir::new( options.npm_cache_dir.clone(),
&DenoCacheEnvFsAdapter(options.fs.as_ref()),
options.npm_global_cache_dir.clone(),
options.npmrc.get_all_known_registries_urls(),
),
options.cache_setting.clone(), options.cache_setting.clone(),
options.npmrc.clone(), options.npmrc.clone(),
)) ))
} }
fn create_api( fn create_api(
options: &CliNpmResolverManagedCreateOptions, options: &CliManagedNpmResolverCreateOptions,
npm_cache: Arc<NpmCache>, npm_cache: Arc<NpmCache>,
) -> Arc<CliNpmRegistryApi> { ) -> Arc<CliNpmRegistryApi> {
Arc::new(CliNpmRegistryApi::new( Arc::new(CliNpmRegistryApi::new(
@ -259,6 +255,35 @@ async fn snapshot_from_lockfile(
Ok(snapshot) Ok(snapshot)
} }
#[derive(Debug)]
struct ManagedInNpmPackageChecker {
root_dir: Url,
}
impl InNpmPackageChecker for ManagedInNpmPackageChecker {
fn in_npm_package(&self, specifier: &Url) -> bool {
specifier.as_ref().starts_with(self.root_dir.as_str())
}
}
pub struct CliManagedInNpmPkgCheckerCreateOptions<'a> {
pub root_cache_dir_url: &'a Url,
pub maybe_node_modules_path: Option<&'a Path>,
}
pub fn create_managed_in_npm_pkg_checker(
options: CliManagedInNpmPkgCheckerCreateOptions,
) -> Arc<dyn InNpmPackageChecker> {
let root_dir = match options.maybe_node_modules_path {
Some(node_modules_folder) => {
deno_path_util::url_from_directory_path(node_modules_folder).unwrap()
}
None => options.root_cache_dir_url.clone(),
};
debug_assert!(root_dir.as_str().ends_with('/'));
Arc::new(ManagedInNpmPackageChecker { root_dir })
}
/// An npm resolver where the resolution is managed by Deno rather than /// An npm resolver where the resolution is managed by Deno rather than
/// the user bringing their own node_modules (BYONM) on the file system. /// the user bringing their own node_modules (BYONM) on the file system.
pub struct ManagedCliNpmResolver { pub struct ManagedCliNpmResolver {
@ -555,8 +580,16 @@ impl ManagedCliNpmResolver {
.map_err(|err| err.into()) .map_err(|err| err.into())
} }
pub fn global_cache_root_folder(&self) -> PathBuf { pub fn maybe_node_modules_path(&self) -> Option<&Path> {
self.npm_cache.root_folder() self.fs_resolver.node_modules_path()
}
pub fn global_cache_root_path(&self) -> &Path {
self.npm_cache.root_dir_path()
}
pub fn global_cache_root_url(&self) -> &Url {
self.npm_cache.root_dir_url()
} }
} }
@ -591,22 +624,6 @@ impl NpmResolver for ManagedCliNpmResolver {
log::debug!("Resolved {} from {} to {}", name, referrer, path.display()); log::debug!("Resolved {} from {} to {}", name, referrer, path.display());
Ok(path) Ok(path)
} }
fn in_npm_package(&self, specifier: &ModuleSpecifier) -> bool {
let root_dir_url = self.fs_resolver.root_dir_url();
debug_assert!(root_dir_url.as_str().ends_with('/'));
specifier.as_ref().starts_with(root_dir_url.as_str())
}
}
impl NodeRequireResolver for ManagedCliNpmResolver {
fn ensure_read_permission<'a>(
&self,
permissions: &mut dyn NodePermissions,
path: &'a Path,
) -> Result<Cow<'a, Path>, AnyError> {
self.fs_resolver.ensure_read_permission(permissions, path)
}
} }
impl NpmProcessStateProvider for ManagedCliNpmResolver { impl NpmProcessStateProvider for ManagedCliNpmResolver {
@ -623,10 +640,6 @@ impl CliNpmResolver for ManagedCliNpmResolver {
self self
} }
fn into_require_resolver(self: Arc<Self>) -> Arc<dyn NodeRequireResolver> {
self
}
fn into_process_state_provider( fn into_process_state_provider(
self: Arc<Self>, self: Arc<Self>,
) -> Arc<dyn NpmProcessStateProvider> { ) -> Arc<dyn NpmProcessStateProvider> {
@ -687,6 +700,14 @@ impl CliNpmResolver for ManagedCliNpmResolver {
.map_err(ResolvePkgFolderFromDenoReqError::Managed) .map_err(ResolvePkgFolderFromDenoReqError::Managed)
} }
fn ensure_read_permission<'a>(
&self,
permissions: &mut dyn NodePermissions,
path: &'a Path,
) -> Result<Cow<'a, Path>, AnyError> {
self.fs_resolver.ensure_read_permission(permissions, path)
}
fn check_state_hash(&self) -> Option<u64> { fn check_state_hash(&self) -> Option<u64> {
// We could go further and check all the individual // We could go further and check all the individual
// npm packages, but that's probably overkill. // npm packages, but that's probably overkill.

View file

@ -17,7 +17,6 @@ use deno_core::anyhow::Context;
use deno_core::error::AnyError; use deno_core::error::AnyError;
use deno_core::futures; use deno_core::futures;
use deno_core::futures::StreamExt; use deno_core::futures::StreamExt;
use deno_core::url::Url;
use deno_npm::NpmPackageCacheFolderId; use deno_npm::NpmPackageCacheFolderId;
use deno_npm::NpmPackageId; use deno_npm::NpmPackageId;
use deno_npm::NpmResolutionPackage; use deno_npm::NpmResolutionPackage;
@ -30,9 +29,6 @@ use crate::npm::managed::cache::TarballCache;
/// Part of the resolution that interacts with the file system. /// Part of the resolution that interacts with the file system.
#[async_trait(?Send)] #[async_trait(?Send)]
pub trait NpmPackageFsResolver: Send + Sync { pub trait NpmPackageFsResolver: Send + Sync {
/// Specifier for the root directory.
fn root_dir_url(&self) -> &Url;
/// The local node_modules folder if it is applicable to the implementation. /// The local node_modules folder if it is applicable to the implementation.
fn node_modules_path(&self) -> Option<&Path>; fn node_modules_path(&self) -> Option<&Path>;

View file

@ -11,7 +11,6 @@ use crate::colors;
use async_trait::async_trait; use async_trait::async_trait;
use deno_ast::ModuleSpecifier; use deno_ast::ModuleSpecifier;
use deno_core::error::AnyError; use deno_core::error::AnyError;
use deno_core::url::Url;
use deno_npm::NpmPackageCacheFolderId; use deno_npm::NpmPackageCacheFolderId;
use deno_npm::NpmPackageId; use deno_npm::NpmPackageId;
use deno_npm::NpmResolutionPackage; use deno_npm::NpmResolutionPackage;
@ -56,7 +55,7 @@ impl GlobalNpmPackageResolver {
Self { Self {
registry_read_permission_checker: RegistryReadPermissionChecker::new( registry_read_permission_checker: RegistryReadPermissionChecker::new(
fs, fs,
cache.root_folder(), cache.root_dir_path().to_path_buf(),
), ),
cache, cache,
tarball_cache, tarball_cache,
@ -69,10 +68,6 @@ impl GlobalNpmPackageResolver {
#[async_trait(?Send)] #[async_trait(?Send)]
impl NpmPackageFsResolver for GlobalNpmPackageResolver { impl NpmPackageFsResolver for GlobalNpmPackageResolver {
fn root_dir_url(&self) -> &Url {
self.cache.root_dir_url()
}
fn node_modules_path(&self) -> Option<&Path> { fn node_modules_path(&self) -> Option<&Path> {
None None
} }

View file

@ -155,10 +155,6 @@ impl LocalNpmPackageResolver {
#[async_trait(?Send)] #[async_trait(?Send)]
impl NpmPackageFsResolver for LocalNpmPackageResolver { impl NpmPackageFsResolver for LocalNpmPackageResolver {
fn root_dir_url(&self) -> &Url {
&self.root_node_modules_url
}
fn node_modules_path(&self) -> Option<&Path> { fn node_modules_path(&self) -> Option<&Path> {
Some(self.root_node_modules_path.as_ref()) Some(self.root_node_modules_path.as_ref())
} }

View file

@ -4,6 +4,7 @@ mod byonm;
mod common; mod common;
mod managed; mod managed;
use std::borrow::Cow;
use std::path::Path; use std::path::Path;
use std::path::PathBuf; use std::path::PathBuf;
use std::sync::Arc; use std::sync::Arc;
@ -15,13 +16,16 @@ use deno_core::error::AnyError;
use deno_core::serde_json; use deno_core::serde_json;
use deno_npm::npm_rc::ResolvedNpmRc; use deno_npm::npm_rc::ResolvedNpmRc;
use deno_npm::registry::NpmPackageInfo; use deno_npm::registry::NpmPackageInfo;
use deno_resolver::npm::ByonmInNpmPackageChecker;
use deno_resolver::npm::ByonmNpmResolver; use deno_resolver::npm::ByonmNpmResolver;
use deno_resolver::npm::ByonmResolvePkgFolderFromDenoReqError; use deno_resolver::npm::ByonmResolvePkgFolderFromDenoReqError;
use deno_runtime::deno_node::NodeRequireResolver; use deno_runtime::deno_node::NodePermissions;
use deno_runtime::ops::process::NpmProcessStateProvider; use deno_runtime::ops::process::NpmProcessStateProvider;
use deno_semver::package::PackageNv; use deno_semver::package::PackageNv;
use deno_semver::package::PackageReq; use deno_semver::package::PackageReq;
use managed::cache::registry_info::get_package_url; use managed::cache::registry_info::get_package_url;
use managed::create_managed_in_npm_pkg_checker;
use node_resolver::InNpmPackageChecker;
use node_resolver::NpmResolver; use node_resolver::NpmResolver;
use thiserror::Error; use thiserror::Error;
@ -29,7 +33,8 @@ use crate::file_fetcher::FileFetcher;
pub use self::byonm::CliByonmNpmResolver; pub use self::byonm::CliByonmNpmResolver;
pub use self::byonm::CliByonmNpmResolverCreateOptions; pub use self::byonm::CliByonmNpmResolverCreateOptions;
pub use self::managed::CliNpmResolverManagedCreateOptions; pub use self::managed::CliManagedInNpmPkgCheckerCreateOptions;
pub use self::managed::CliManagedNpmResolverCreateOptions;
pub use self::managed::CliNpmResolverManagedSnapshotOption; pub use self::managed::CliNpmResolverManagedSnapshotOption;
pub use self::managed::ManagedCliNpmResolver; pub use self::managed::ManagedCliNpmResolver;
@ -42,7 +47,7 @@ pub enum ResolvePkgFolderFromDenoReqError {
} }
pub enum CliNpmResolverCreateOptions { pub enum CliNpmResolverCreateOptions {
Managed(CliNpmResolverManagedCreateOptions), Managed(CliManagedNpmResolverCreateOptions),
Byonm(CliByonmNpmResolverCreateOptions), Byonm(CliByonmNpmResolverCreateOptions),
} }
@ -68,6 +73,22 @@ pub async fn create_cli_npm_resolver(
} }
} }
pub enum CreateInNpmPkgCheckerOptions<'a> {
Managed(CliManagedInNpmPkgCheckerCreateOptions<'a>),
Byonm,
}
pub fn create_in_npm_pkg_checker(
options: CreateInNpmPkgCheckerOptions,
) -> Arc<dyn InNpmPackageChecker> {
match options {
CreateInNpmPkgCheckerOptions::Managed(options) => {
create_managed_in_npm_pkg_checker(options)
}
CreateInNpmPkgCheckerOptions::Byonm => Arc::new(ByonmInNpmPackageChecker),
}
}
pub enum InnerCliNpmResolverRef<'a> { pub enum InnerCliNpmResolverRef<'a> {
Managed(&'a ManagedCliNpmResolver), Managed(&'a ManagedCliNpmResolver),
#[allow(dead_code)] #[allow(dead_code)]
@ -76,7 +97,6 @@ pub enum InnerCliNpmResolverRef<'a> {
pub trait CliNpmResolver: NpmResolver { pub trait CliNpmResolver: NpmResolver {
fn into_npm_resolver(self: Arc<Self>) -> Arc<dyn NpmResolver>; fn into_npm_resolver(self: Arc<Self>) -> Arc<dyn NpmResolver>;
fn into_require_resolver(self: Arc<Self>) -> Arc<dyn NodeRequireResolver>;
fn into_process_state_provider( fn into_process_state_provider(
self: Arc<Self>, self: Arc<Self>,
) -> Arc<dyn NpmProcessStateProvider>; ) -> Arc<dyn NpmProcessStateProvider>;
@ -107,6 +127,12 @@ pub trait CliNpmResolver: NpmResolver {
referrer: &ModuleSpecifier, referrer: &ModuleSpecifier,
) -> Result<PathBuf, ResolvePkgFolderFromDenoReqError>; ) -> Result<PathBuf, ResolvePkgFolderFromDenoReqError>;
fn ensure_read_permission<'a>(
&self,
permissions: &mut dyn NodePermissions,
path: &'a Path,
) -> Result<Cow<'a, Path>, AnyError>;
/// Returns a hash returning the state of the npm resolver /// Returns a hash returning the state of the npm resolver
/// or `None` if the state currently can't be determined. /// or `None` if the state currently can't be determined.
fn check_state_hash(&self) -> Option<u64>; fn check_state_hash(&self) -> Option<u64>;

View file

@ -4,6 +4,7 @@ use async_trait::async_trait;
use dashmap::DashMap; use dashmap::DashMap;
use dashmap::DashSet; use dashmap::DashSet;
use deno_ast::MediaType; use deno_ast::MediaType;
use deno_ast::ModuleKind;
use deno_config::workspace::MappedResolution; use deno_config::workspace::MappedResolution;
use deno_config::workspace::MappedResolutionDiagnostic; use deno_config::workspace::MappedResolutionDiagnostic;
use deno_config::workspace::MappedResolutionError; use deno_config::workspace::MappedResolutionError;
@ -11,6 +12,7 @@ use deno_config::workspace::WorkspaceResolver;
use deno_core::anyhow::anyhow; use deno_core::anyhow::anyhow;
use deno_core::anyhow::Context; use deno_core::anyhow::Context;
use deno_core::error::AnyError; use deno_core::error::AnyError;
use deno_core::url::Url;
use deno_core::ModuleSourceCode; use deno_core::ModuleSourceCode;
use deno_core::ModuleSpecifier; use deno_core::ModuleSpecifier;
use deno_graph::source::ResolutionMode; use deno_graph::source::ResolutionMode;
@ -29,6 +31,7 @@ use deno_runtime::deno_fs;
use deno_runtime::deno_fs::FileSystem; use deno_runtime::deno_fs::FileSystem;
use deno_runtime::deno_node::is_builtin_node_module; use deno_runtime::deno_node::is_builtin_node_module;
use deno_runtime::deno_node::NodeResolver; use deno_runtime::deno_node::NodeResolver;
use deno_runtime::deno_node::PackageJsonResolver;
use deno_semver::npm::NpmPackageReqReference; use deno_semver::npm::NpmPackageReqReference;
use deno_semver::package::PackageReq; use deno_semver::package::PackageReq;
use node_resolver::errors::ClosestPkgJsonError; use node_resolver::errors::ClosestPkgJsonError;
@ -38,21 +41,22 @@ use node_resolver::errors::PackageFolderResolveErrorKind;
use node_resolver::errors::PackageFolderResolveIoError; use node_resolver::errors::PackageFolderResolveIoError;
use node_resolver::errors::PackageNotFoundError; use node_resolver::errors::PackageNotFoundError;
use node_resolver::errors::PackageResolveErrorKind; use node_resolver::errors::PackageResolveErrorKind;
use node_resolver::errors::UrlToNodeResolutionError; use node_resolver::errors::PackageSubpathResolveError;
use node_resolver::InNpmPackageChecker;
use node_resolver::NodeModuleKind; use node_resolver::NodeModuleKind;
use node_resolver::NodeResolution; use node_resolver::NodeResolution;
use node_resolver::NodeResolutionMode; use node_resolver::NodeResolutionMode;
use node_resolver::PackageJson; use std::borrow::Cow;
use std::path::Path; use std::path::Path;
use std::path::PathBuf; use std::path::PathBuf;
use std::sync::Arc; use std::sync::Arc;
use thiserror::Error;
use crate::args::JsxImportSourceConfig; use crate::args::JsxImportSourceConfig;
use crate::args::DENO_DISABLE_PEDANTIC_NODE_WARNINGS; use crate::args::DENO_DISABLE_PEDANTIC_NODE_WARNINGS;
use crate::node::CliNodeCodeTranslator; use crate::node::CliNodeCodeTranslator;
use crate::npm::CliNpmResolver; use crate::npm::CliNpmResolver;
use crate::npm::InnerCliNpmResolverRef; use crate::npm::InnerCliNpmResolverRef;
use crate::util::path::specifier_has_extension;
use crate::util::sync::AtomicFlag; use crate::util::sync::AtomicFlag;
use crate::util::text_encoding::from_utf8_lossy_owned; use crate::util::text_encoding::from_utf8_lossy_owned;
@ -104,36 +108,32 @@ impl deno_resolver::fs::DenoResolverFs for CliDenoResolverFs {
#[derive(Debug)] #[derive(Debug)]
pub struct CliNodeResolver { pub struct CliNodeResolver {
cjs_resolutions: Arc<CjsResolutionStore>, cjs_tracker: Arc<CjsTracker>,
fs: Arc<dyn deno_fs::FileSystem>, fs: Arc<dyn deno_fs::FileSystem>,
in_npm_pkg_checker: Arc<dyn InNpmPackageChecker>,
node_resolver: Arc<NodeResolver>, node_resolver: Arc<NodeResolver>,
npm_resolver: Arc<dyn CliNpmResolver>, npm_resolver: Arc<dyn CliNpmResolver>,
} }
impl CliNodeResolver { impl CliNodeResolver {
pub fn new( pub fn new(
cjs_resolutions: Arc<CjsResolutionStore>, cjs_tracker: Arc<CjsTracker>,
fs: Arc<dyn deno_fs::FileSystem>, fs: Arc<dyn deno_fs::FileSystem>,
in_npm_pkg_checker: Arc<dyn InNpmPackageChecker>,
node_resolver: Arc<NodeResolver>, node_resolver: Arc<NodeResolver>,
npm_resolver: Arc<dyn CliNpmResolver>, npm_resolver: Arc<dyn CliNpmResolver>,
) -> Self { ) -> Self {
Self { Self {
cjs_resolutions, cjs_tracker,
fs, fs,
in_npm_pkg_checker,
node_resolver, node_resolver,
npm_resolver, npm_resolver,
} }
} }
pub fn in_npm_package(&self, specifier: &ModuleSpecifier) -> bool { pub fn in_npm_package(&self, specifier: &ModuleSpecifier) -> bool {
self.npm_resolver.in_npm_package(specifier) self.in_npm_pkg_checker.in_npm_package(specifier)
}
pub fn get_closest_package_json(
&self,
referrer: &ModuleSpecifier,
) -> Result<Option<Arc<PackageJson>>, ClosestPkgJsonError> {
self.node_resolver.get_closest_package_json(referrer)
} }
pub fn resolve_if_for_npm_pkg( pub fn resolve_if_for_npm_pkg(
@ -153,8 +153,7 @@ impl CliNodeResolver {
| NodeResolveErrorKind::UnsupportedEsmUrlScheme(_) | NodeResolveErrorKind::UnsupportedEsmUrlScheme(_)
| NodeResolveErrorKind::DataUrlReferrer(_) | NodeResolveErrorKind::DataUrlReferrer(_)
| NodeResolveErrorKind::TypesNotFound(_) | NodeResolveErrorKind::TypesNotFound(_)
| NodeResolveErrorKind::FinalizeResolution(_) | NodeResolveErrorKind::FinalizeResolution(_) => Err(err.into()),
| NodeResolveErrorKind::UrlToNodeResolution(_) => Err(err.into()),
NodeResolveErrorKind::PackageResolve(err) => { NodeResolveErrorKind::PackageResolve(err) => {
let err = err.into_kind(); let err = err.into_kind();
match err { match err {
@ -216,7 +215,11 @@ impl CliNodeResolver {
referrer: &ModuleSpecifier, referrer: &ModuleSpecifier,
mode: NodeResolutionMode, mode: NodeResolutionMode,
) -> Result<NodeResolution, NodeResolveError> { ) -> Result<NodeResolution, NodeResolveError> {
let referrer_kind = if self.cjs_resolutions.is_known_cjs(referrer) { let referrer_kind = if self
.cjs_tracker
.is_maybe_cjs(referrer, MediaType::from_specifier(referrer))
.map_err(|err| NodeResolveErrorKind::PackageResolve(err.into()))?
{
NodeModuleKind::Cjs NodeModuleKind::Cjs
} else { } else {
NodeModuleKind::Esm NodeModuleKind::Esm
@ -226,7 +229,7 @@ impl CliNodeResolver {
self self
.node_resolver .node_resolver
.resolve(specifier, referrer, referrer_kind, mode)?; .resolve(specifier, referrer, referrer_kind, mode)?;
Ok(self.handle_node_resolution(res)) Ok(res)
} }
pub fn resolve_req_reference( pub fn resolve_req_reference(
@ -234,7 +237,7 @@ impl CliNodeResolver {
req_ref: &NpmPackageReqReference, req_ref: &NpmPackageReqReference,
referrer: &ModuleSpecifier, referrer: &ModuleSpecifier,
mode: NodeResolutionMode, mode: NodeResolutionMode,
) -> Result<NodeResolution, AnyError> { ) -> Result<ModuleSpecifier, AnyError> {
self.resolve_req_with_sub_path( self.resolve_req_with_sub_path(
req_ref.req(), req_ref.req(),
req_ref.sub_path(), req_ref.sub_path(),
@ -249,7 +252,7 @@ impl CliNodeResolver {
sub_path: Option<&str>, sub_path: Option<&str>,
referrer: &ModuleSpecifier, referrer: &ModuleSpecifier,
mode: NodeResolutionMode, mode: NodeResolutionMode,
) -> Result<NodeResolution, AnyError> { ) -> Result<ModuleSpecifier, AnyError> {
let package_folder = self let package_folder = self
.npm_resolver .npm_resolver
.resolve_pkg_folder_from_deno_module_req(req, referrer)?; .resolve_pkg_folder_from_deno_module_req(req, referrer)?;
@ -260,7 +263,7 @@ impl CliNodeResolver {
mode, mode,
); );
match resolution_result { match resolution_result {
Ok(resolution) => Ok(resolution), Ok(url) => Ok(url),
Err(err) => { Err(err) => {
if self.npm_resolver.as_byonm().is_some() { if self.npm_resolver.as_byonm().is_some() {
let package_json_path = package_folder.join("package.json"); let package_json_path = package_folder.join("package.json");
@ -271,7 +274,7 @@ impl CliNodeResolver {
)); ));
} }
} }
Err(err) Err(err.into())
} }
} }
} }
@ -282,16 +285,13 @@ impl CliNodeResolver {
sub_path: Option<&str>, sub_path: Option<&str>,
maybe_referrer: Option<&ModuleSpecifier>, maybe_referrer: Option<&ModuleSpecifier>,
mode: NodeResolutionMode, mode: NodeResolutionMode,
) -> Result<NodeResolution, AnyError> { ) -> Result<ModuleSpecifier, PackageSubpathResolveError> {
let res = self self.node_resolver.resolve_package_subpath_from_deno_module(
.node_resolver
.resolve_package_subpath_from_deno_module(
package_folder, package_folder,
sub_path, sub_path,
maybe_referrer, maybe_referrer,
mode, mode,
)?; )
Ok(self.handle_node_resolution(res))
} }
pub fn handle_if_in_node_modules( pub fn handle_if_in_node_modules(
@ -306,71 +306,45 @@ impl CliNodeResolver {
// so canoncalize then check if it's in the node_modules directory. // so canoncalize then check if it's in the node_modules directory.
// If so, check if we need to store this specifier as being a CJS // If so, check if we need to store this specifier as being a CJS
// resolution. // resolution.
let specifier = let specifier = crate::node::resolve_specifier_into_node_modules(
crate::node::resolve_specifier_into_node_modules(specifier); specifier,
if self.in_npm_package(&specifier) { self.fs.as_ref(),
let resolution = );
self.node_resolver.url_to_node_resolution(specifier)?; return Ok(Some(specifier));
let resolution = self.handle_node_resolution(resolution);
return Ok(Some(resolution.into_url()));
}
} }
Ok(None) Ok(None)
} }
pub fn url_to_node_resolution(
&self,
specifier: ModuleSpecifier,
) -> Result<NodeResolution, UrlToNodeResolutionError> {
self.node_resolver.url_to_node_resolution(specifier)
} }
fn handle_node_resolution( #[derive(Debug, Error)]
&self, #[error("{media_type} files are not supported in npm packages: {specifier}")]
resolution: NodeResolution, pub struct NotSupportedKindInNpmError {
) -> NodeResolution { pub media_type: MediaType,
if let NodeResolution::CommonJs(specifier) = &resolution { pub specifier: Url,
// remember that this was a common js resolution
self.mark_cjs_resolution(specifier.clone());
}
resolution
} }
pub fn mark_cjs_resolution(&self, specifier: ModuleSpecifier) { // todo(dsherret): move to module_loader.rs (it seems to be here due to use in standalone)
self.cjs_resolutions.insert(specifier);
}
}
// todo(dsherret): move to module_loader.rs
#[derive(Clone)] #[derive(Clone)]
pub struct NpmModuleLoader { pub struct NpmModuleLoader {
cjs_resolutions: Arc<CjsResolutionStore>, cjs_tracker: Arc<CjsTracker>,
node_code_translator: Arc<CliNodeCodeTranslator>,
fs: Arc<dyn deno_fs::FileSystem>, fs: Arc<dyn deno_fs::FileSystem>,
node_resolver: Arc<CliNodeResolver>, node_code_translator: Arc<CliNodeCodeTranslator>,
} }
impl NpmModuleLoader { impl NpmModuleLoader {
pub fn new( pub fn new(
cjs_resolutions: Arc<CjsResolutionStore>, cjs_tracker: Arc<CjsTracker>,
node_code_translator: Arc<CliNodeCodeTranslator>,
fs: Arc<dyn deno_fs::FileSystem>, fs: Arc<dyn deno_fs::FileSystem>,
node_resolver: Arc<CliNodeResolver>, node_code_translator: Arc<CliNodeCodeTranslator>,
) -> Self { ) -> Self {
Self { Self {
cjs_resolutions, cjs_tracker,
node_code_translator, node_code_translator,
fs, fs,
node_resolver,
} }
} }
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( pub async fn load(
&self, &self,
specifier: &ModuleSpecifier, specifier: &ModuleSpecifier,
@ -413,20 +387,30 @@ impl NpmModuleLoader {
} }
})?; })?;
let code = if self.cjs_resolutions.is_known_cjs(specifier) { let media_type = MediaType::from_specifier(specifier);
if media_type.is_emittable() {
return Err(AnyError::from(NotSupportedKindInNpmError {
media_type,
specifier: specifier.clone(),
}));
}
let code = if self.cjs_tracker.is_maybe_cjs(specifier, media_type)? {
// translate cjs to esm if it's cjs and inject node globals // translate cjs to esm if it's cjs and inject node globals
let code = from_utf8_lossy_owned(code); let code = from_utf8_lossy_owned(code);
ModuleSourceCode::String( ModuleSourceCode::String(
self self
.node_code_translator .node_code_translator
.translate_cjs_to_esm(specifier, Some(code)) .translate_cjs_to_esm(specifier, Some(Cow::Owned(code)))
.await? .await?
.into_owned()
.into(), .into(),
) )
} else { } else {
// esm and json code is untouched // esm and json code is untouched
ModuleSourceCode::Bytes(code.into_boxed_slice().into()) ModuleSourceCode::Bytes(code.into_boxed_slice().into())
}; };
Ok(ModuleCodeStringSource { Ok(ModuleCodeStringSource {
code, code,
found_url: specifier.clone(), found_url: specifier.clone(),
@ -435,21 +419,165 @@ impl NpmModuleLoader {
} }
} }
pub struct CjsTrackerOptions {
pub unstable_detect_cjs: bool,
}
/// Keeps track of what module specifiers were resolved as CJS. /// Keeps track of what module specifiers were resolved as CJS.
#[derive(Debug, Default)] ///
pub struct CjsResolutionStore(DashSet<ModuleSpecifier>); /// Modules that are `.js` or `.ts` are only known to be CJS or
/// ESM after they're loaded based on their contents. So these files
/// 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>,
}
impl CjsResolutionStore { impl CjsTracker {
pub fn is_known_cjs(&self, specifier: &ModuleSpecifier) -> bool { pub fn new(
in_npm_pkg_checker: Arc<dyn InNpmPackageChecker>,
pkg_json_resolver: Arc<PackageJsonResolver>,
options: CjsTrackerOptions,
) -> Self {
Self {
in_npm_pkg_checker,
pkg_json_resolver,
unstable_detect_cjs: options.unstable_detect_cjs,
known: Default::default(),
}
}
/// Checks whether the file might be treated as CJS, but it's not for sure
/// yet because the source hasn't been loaded to see whether it contains
/// imports or exports.
pub fn is_maybe_cjs(
&self,
specifier: &ModuleSpecifier,
media_type: MediaType,
) -> Result<bool, ClosestPkgJsonError> {
self.treat_as_cjs_with_is_script(specifier, media_type, None)
}
/// Gets whether the file is CJS. If true, this is for sure
/// cjs because `is_script` is provided.
///
/// `is_script` should be `true` when the contents of the file at the
/// provided specifier are known to be a script and not an ES module.
pub fn is_cjs_with_known_is_script(
&self,
specifier: &ModuleSpecifier,
media_type: MediaType,
is_script: bool,
) -> Result<bool, ClosestPkgJsonError> {
self.treat_as_cjs_with_is_script(specifier, media_type, Some(is_script))
}
fn treat_as_cjs_with_is_script(
&self,
specifier: &ModuleSpecifier,
media_type: MediaType,
is_script: Option<bool>,
) -> Result<bool, ClosestPkgJsonError> {
let kind = match self
.get_known_kind_with_is_script(specifier, media_type, is_script)
{
Some(kind) => kind,
None => self.check_based_on_pkg_json(specifier)?,
};
Ok(kind.is_cjs())
}
pub fn get_known_kind(
&self,
specifier: &ModuleSpecifier,
media_type: MediaType,
) -> Option<ModuleKind> {
self.get_known_kind_with_is_script(specifier, media_type, None)
}
fn get_known_kind_with_is_script(
&self,
specifier: &ModuleSpecifier,
media_type: MediaType,
is_script: Option<bool>,
) -> Option<ModuleKind> {
if specifier.scheme() != "file" { if specifier.scheme() != "file" {
return false; return Some(ModuleKind::Esm);
} }
specifier_has_extension(specifier, "cjs") || self.0.contains(specifier) match media_type {
MediaType::Mts | MediaType::Mjs | MediaType::Dmts => Some(ModuleKind::Esm),
MediaType::Cjs | MediaType::Cts | MediaType::Dcts => Some(ModuleKind::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) {
Some(value)
} else {
let value = self.check_based_on_pkg_json(specifier).ok();
if let Some(value) = value {
self.known.insert(specifier.clone(), value);
}
Some(value.unwrap_or(ModuleKind::Esm))
}
}
MediaType::Wasm |
MediaType::Json => Some(ModuleKind::Esm),
MediaType::JavaScript
| MediaType::Jsx
| MediaType::TypeScript
| MediaType::Tsx
// treat these as unknown
| 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) {
// we now know this is actually esm
self.known.insert(specifier.clone(), ModuleKind::Esm);
Some(ModuleKind::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)
} else {
None
}
}
}
} }
pub fn insert(&self, specifier: ModuleSpecifier) { fn check_based_on_pkg_json(
self.0.insert(specifier); &self,
specifier: &ModuleSpecifier,
) -> Result<ModuleKind, 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))
} else {
Ok(ModuleKind::Cjs)
}
} else if self.unstable_detect_cjs {
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))
} else {
Ok(ModuleKind::Esm)
}
} else {
Ok(ModuleKind::Esm)
}
} }
} }
@ -633,8 +761,7 @@ impl Resolver for CliGraphResolver {
Some(referrer), Some(referrer),
to_node_mode(mode), to_node_mode(mode),
) )
.map_err(ResolveError::Other) .map_err(|e| ResolveError::Other(e.into())),
.map(|res| res.into_url()),
MappedResolution::PackageJson { MappedResolution::PackageJson {
dep_result, dep_result,
alias, alias,
@ -665,7 +792,6 @@ impl Resolver for CliGraphResolver {
) )
.map_err(|e| ResolveError::Other(e.into())) .map_err(|e| ResolveError::Other(e.into()))
.and_then(|pkg_folder| { .and_then(|pkg_folder| {
Ok(
self self
.node_resolver .node_resolver
.as_ref() .as_ref()
@ -675,9 +801,8 @@ impl Resolver for CliGraphResolver {
sub_path.as_deref(), sub_path.as_deref(),
Some(referrer), Some(referrer),
to_node_mode(mode), to_node_mode(mode),
)?
.into_url(),
) )
.map_err(|e| ResolveError::Other(e.into()))
}), }),
}) })
} }
@ -717,23 +842,20 @@ impl Resolver for CliGraphResolver {
npm_req_ref.req(), npm_req_ref.req(),
) )
{ {
return Ok( return node_resolver
node_resolver
.resolve_package_sub_path_from_deno_module( .resolve_package_sub_path_from_deno_module(
pkg_folder, pkg_folder,
npm_req_ref.sub_path(), npm_req_ref.sub_path(),
Some(referrer), Some(referrer),
to_node_mode(mode), to_node_mode(mode),
)? )
.into_url(), .map_err(|e| ResolveError::Other(e.into()));
);
} }
// do npm resolution for byonm // do npm resolution for byonm
if is_byonm { if is_byonm {
return node_resolver return node_resolver
.resolve_req_reference(&npm_req_ref, referrer, to_node_mode(mode)) .resolve_req_reference(&npm_req_ref, referrer, to_node_mode(mode))
.map(|res| res.into_url())
.map_err(|err| err.into()); .map_err(|err| err.into());
} }
} }
@ -751,9 +873,7 @@ impl Resolver for CliGraphResolver {
.map_err(ResolveError::Other)?; .map_err(ResolveError::Other)?;
if let Some(res) = maybe_resolution { if let Some(res) = maybe_resolution {
match res { match res {
NodeResolution::Esm(url) | NodeResolution::CommonJs(url) => { NodeResolution::Module(url) => return Ok(url),
return Ok(url)
}
NodeResolution::BuiltIn(_) => { NodeResolution::BuiltIn(_) => {
// don't resolve bare specifiers for built-in modules via node resolution // don't resolve bare specifiers for built-in modules via node resolution
} }

View file

@ -21,6 +21,7 @@ use std::process::Command;
use std::sync::Arc; use std::sync::Arc;
use deno_ast::MediaType; use deno_ast::MediaType;
use deno_ast::ModuleKind;
use deno_ast::ModuleSpecifier; use deno_ast::ModuleSpecifier;
use deno_config::workspace::PackageJsonDepResolution; use deno_config::workspace::PackageJsonDepResolution;
use deno_config::workspace::ResolverWorkspaceJsrPackage; use deno_config::workspace::ResolverWorkspaceJsrPackage;
@ -67,6 +68,7 @@ use crate::file_fetcher::FileFetcher;
use crate::http_util::HttpClientProvider; use crate::http_util::HttpClientProvider;
use crate::npm::CliNpmResolver; use crate::npm::CliNpmResolver;
use crate::npm::InnerCliNpmResolverRef; use crate::npm::InnerCliNpmResolverRef;
use crate::resolver::CjsTracker;
use crate::shared::ReleaseChannel; use crate::shared::ReleaseChannel;
use crate::standalone::virtual_fs::VfsEntry; use crate::standalone::virtual_fs::VfsEntry;
use crate::util::archive; use crate::util::archive;
@ -257,6 +259,10 @@ impl StandaloneModules {
} }
} }
pub fn has_file(&self, path: &Path) -> bool {
self.vfs.file_entry(path).is_ok()
}
pub fn read<'a>( pub fn read<'a>(
&'a self, &'a self,
specifier: &'a ModuleSpecifier, specifier: &'a ModuleSpecifier,
@ -353,6 +359,7 @@ pub fn extract_standalone(
} }
pub struct DenoCompileBinaryWriter<'a> { pub struct DenoCompileBinaryWriter<'a> {
cjs_tracker: &'a CjsTracker,
deno_dir: &'a DenoDir, deno_dir: &'a DenoDir,
emitter: &'a Emitter, emitter: &'a Emitter,
file_fetcher: &'a FileFetcher, file_fetcher: &'a FileFetcher,
@ -365,6 +372,7 @@ pub struct DenoCompileBinaryWriter<'a> {
impl<'a> DenoCompileBinaryWriter<'a> { impl<'a> DenoCompileBinaryWriter<'a> {
#[allow(clippy::too_many_arguments)] #[allow(clippy::too_many_arguments)]
pub fn new( pub fn new(
cjs_tracker: &'a CjsTracker,
deno_dir: &'a DenoDir, deno_dir: &'a DenoDir,
emitter: &'a Emitter, emitter: &'a Emitter,
file_fetcher: &'a FileFetcher, file_fetcher: &'a FileFetcher,
@ -374,6 +382,7 @@ impl<'a> DenoCompileBinaryWriter<'a> {
npm_system_info: NpmSystemInfo, npm_system_info: NpmSystemInfo,
) -> Self { ) -> Self {
Self { Self {
cjs_tracker,
deno_dir, deno_dir,
emitter, emitter,
file_fetcher, file_fetcher,
@ -599,19 +608,21 @@ impl<'a> DenoCompileBinaryWriter<'a> {
} }
let (maybe_source, media_type) = match module { let (maybe_source, media_type) = match module {
deno_graph::Module::Js(m) => { deno_graph::Module::Js(m) => {
// todo(https://github.com/denoland/deno_media_type/pull/12): use is_emittable() let source = if m.media_type.is_emittable() {
let is_emittable = matches!( let is_cjs = self.cjs_tracker.is_cjs_with_known_is_script(
&m.specifier,
m.media_type, m.media_type,
MediaType::TypeScript m.is_script,
| MediaType::Mts )?;
| MediaType::Cts let module_kind = ModuleKind::from_is_cjs(is_cjs);
| MediaType::Jsx
| MediaType::Tsx
);
let source = if is_emittable {
let source = self let source = self
.emitter .emitter
.emit_parsed_source(&m.specifier, m.media_type, &m.source) .emit_parsed_source(
&m.specifier,
m.media_type,
module_kind,
&m.source,
)
.await?; .await?;
source.into_bytes() source.into_bytes()
} else { } else {
@ -745,8 +756,9 @@ impl<'a> DenoCompileBinaryWriter<'a> {
} else { } else {
// DO NOT include the user's registry url as it may contain credentials, // DO NOT include the user's registry url as it may contain credentials,
// but also don't make this dependent on the registry url // but also don't make this dependent on the registry url
let global_cache_root_path = npm_resolver.global_cache_root_folder(); let global_cache_root_path = npm_resolver.global_cache_root_path();
let mut builder = VfsBuilder::new(global_cache_root_path)?; let mut builder =
VfsBuilder::new(global_cache_root_path.to_path_buf())?;
let mut packages = let mut packages =
npm_resolver.all_system_packages(&self.npm_system_info); npm_resolver.all_system_packages(&self.npm_system_info);
packages.sort_by(|a, b| a.id.cmp(&b.id)); // determinism packages.sort_by(|a, b| a.id.cmp(&b.id)); // determinism

View file

@ -19,6 +19,7 @@ use deno_core::error::type_error;
use deno_core::error::AnyError; use deno_core::error::AnyError;
use deno_core::futures::FutureExt; use deno_core::futures::FutureExt;
use deno_core::v8_set_flags; use deno_core::v8_set_flags;
use deno_core::FastString;
use deno_core::FeatureChecker; use deno_core::FeatureChecker;
use deno_core::ModuleLoader; use deno_core::ModuleLoader;
use deno_core::ModuleSourceCode; use deno_core::ModuleSourceCode;
@ -30,7 +31,9 @@ use deno_npm::npm_rc::ResolvedNpmRc;
use deno_package_json::PackageJsonDepValue; use deno_package_json::PackageJsonDepValue;
use deno_runtime::deno_fs; use deno_runtime::deno_fs;
use deno_runtime::deno_node::create_host_defined_options; use deno_runtime::deno_node::create_host_defined_options;
use deno_runtime::deno_node::NodeRequireLoader;
use deno_runtime::deno_node::NodeResolver; use deno_runtime::deno_node::NodeResolver;
use deno_runtime::deno_node::PackageJsonResolver;
use deno_runtime::deno_permissions::Permissions; use deno_runtime::deno_permissions::Permissions;
use deno_runtime::deno_permissions::PermissionsContainer; use deno_runtime::deno_permissions::PermissionsContainer;
use deno_runtime::deno_tls::rustls::RootCertStore; use deno_runtime::deno_tls::rustls::RootCertStore;
@ -43,6 +46,7 @@ use deno_semver::npm::NpmPackageReqReference;
use import_map::parse_from_json; use import_map::parse_from_json;
use node_resolver::analyze::NodeCodeTranslator; use node_resolver::analyze::NodeCodeTranslator;
use node_resolver::NodeResolutionMode; use node_resolver::NodeResolutionMode;
use serialization::DenoCompileModuleSource;
use std::borrow::Cow; use std::borrow::Cow;
use std::rc::Rc; use std::rc::Rc;
use std::sync::Arc; use std::sync::Arc;
@ -61,12 +65,18 @@ use crate::cache::NodeAnalysisCache;
use crate::cache::RealDenoCacheEnv; use crate::cache::RealDenoCacheEnv;
use crate::http_util::HttpClientProvider; use crate::http_util::HttpClientProvider;
use crate::node::CliCjsCodeAnalyzer; use crate::node::CliCjsCodeAnalyzer;
use crate::node::CliNodeCodeTranslator;
use crate::npm::create_cli_npm_resolver; use crate::npm::create_cli_npm_resolver;
use crate::npm::create_in_npm_pkg_checker;
use crate::npm::CliByonmNpmResolverCreateOptions; use crate::npm::CliByonmNpmResolverCreateOptions;
use crate::npm::CliManagedInNpmPkgCheckerCreateOptions;
use crate::npm::CliManagedNpmResolverCreateOptions;
use crate::npm::CliNpmResolver;
use crate::npm::CliNpmResolverCreateOptions; use crate::npm::CliNpmResolverCreateOptions;
use crate::npm::CliNpmResolverManagedCreateOptions;
use crate::npm::CliNpmResolverManagedSnapshotOption; use crate::npm::CliNpmResolverManagedSnapshotOption;
use crate::resolver::CjsResolutionStore; use crate::npm::CreateInNpmPkgCheckerOptions;
use crate::resolver::CjsTracker;
use crate::resolver::CjsTrackerOptions;
use crate::resolver::CliDenoResolverFs; use crate::resolver::CliDenoResolverFs;
use crate::resolver::CliNodeResolver; use crate::resolver::CliNodeResolver;
use crate::resolver::NpmModuleLoader; use crate::resolver::NpmModuleLoader;
@ -75,7 +85,7 @@ use crate::util::progress_bar::ProgressBarStyle;
use crate::util::v8::construct_v8_flags; use crate::util::v8::construct_v8_flags;
use crate::worker::CliMainWorkerFactory; use crate::worker::CliMainWorkerFactory;
use crate::worker::CliMainWorkerOptions; use crate::worker::CliMainWorkerOptions;
use crate::worker::ModuleLoaderAndSourceMapGetter; use crate::worker::CreateModuleLoaderResult;
use crate::worker::ModuleLoaderFactory; use crate::worker::ModuleLoaderFactory;
pub mod binary; pub mod binary;
@ -91,10 +101,14 @@ use self::binary::Metadata;
use self::file_system::DenoCompileFileSystem; use self::file_system::DenoCompileFileSystem;
struct SharedModuleLoaderState { struct SharedModuleLoaderState {
cjs_tracker: Arc<CjsTracker>,
fs: Arc<dyn deno_fs::FileSystem>,
modules: StandaloneModules, modules: StandaloneModules,
workspace_resolver: WorkspaceResolver, node_code_translator: Arc<CliNodeCodeTranslator>,
node_resolver: Arc<CliNodeResolver>, node_resolver: Arc<CliNodeResolver>,
npm_module_loader: Arc<NpmModuleLoader>, npm_module_loader: Arc<NpmModuleLoader>,
npm_resolver: Arc<dyn CliNpmResolver>,
workspace_resolver: WorkspaceResolver,
} }
#[derive(Clone)] #[derive(Clone)]
@ -102,6 +116,12 @@ struct EmbeddedModuleLoader {
shared: Arc<SharedModuleLoaderState>, shared: Arc<SharedModuleLoaderState>,
} }
impl std::fmt::Debug for EmbeddedModuleLoader {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("EmbeddedModuleLoader").finish()
}
}
pub const MODULE_NOT_FOUND: &str = "Module not found"; pub const MODULE_NOT_FOUND: &str = "Module not found";
pub const UNSUPPORTED_SCHEME: &str = "Unsupported scheme"; pub const UNSUPPORTED_SCHEME: &str = "Unsupported scheme";
@ -159,8 +179,7 @@ impl ModuleLoader for EmbeddedModuleLoader {
sub_path.as_deref(), sub_path.as_deref(),
Some(&referrer), Some(&referrer),
NodeResolutionMode::Execution, NodeResolutionMode::Execution,
)? )?,
.into_url(),
), ),
Ok(MappedResolution::PackageJson { Ok(MappedResolution::PackageJson {
dep_result, dep_result,
@ -168,16 +187,14 @@ impl ModuleLoader for EmbeddedModuleLoader {
alias, alias,
.. ..
}) => match dep_result.as_ref().map_err(|e| AnyError::from(e.clone()))? { }) => match dep_result.as_ref().map_err(|e| AnyError::from(e.clone()))? {
PackageJsonDepValue::Req(req) => self PackageJsonDepValue::Req(req) => {
.shared self.shared.node_resolver.resolve_req_with_sub_path(
.node_resolver
.resolve_req_with_sub_path(
req, req,
sub_path.as_deref(), sub_path.as_deref(),
&referrer, &referrer,
NodeResolutionMode::Execution, NodeResolutionMode::Execution,
) )
.map(|res| res.into_url()), }
PackageJsonDepValue::Workspace(version_req) => { PackageJsonDepValue::Workspace(version_req) => {
let pkg_folder = self let pkg_folder = self
.shared .shared
@ -195,8 +212,7 @@ impl ModuleLoader for EmbeddedModuleLoader {
sub_path.as_deref(), sub_path.as_deref(),
Some(&referrer), Some(&referrer),
NodeResolutionMode::Execution, NodeResolutionMode::Execution,
)? )?,
.into_url(),
) )
} }
}, },
@ -205,15 +221,11 @@ impl ModuleLoader for EmbeddedModuleLoader {
if let Ok(reference) = if let Ok(reference) =
NpmPackageReqReference::from_specifier(&specifier) NpmPackageReqReference::from_specifier(&specifier)
{ {
return self return self.shared.node_resolver.resolve_req_reference(
.shared
.node_resolver
.resolve_req_reference(
&reference, &reference,
&referrer, &referrer,
NodeResolutionMode::Execution, NodeResolutionMode::Execution,
) );
.map(|res| res.into_url());
} }
if specifier.scheme() == "jsr" { if specifier.scheme() == "jsr" {
@ -317,8 +329,62 @@ impl ModuleLoader for EmbeddedModuleLoader {
match self.shared.modules.read(original_specifier) { match self.shared.modules.read(original_specifier) {
Ok(Some(module)) => { Ok(Some(module)) => {
let media_type = module.media_type;
let (module_specifier, module_type, module_source) = let (module_specifier, module_type, module_source) =
module.into_for_v8(); module.into_parts();
let is_maybe_cjs = match self
.shared
.cjs_tracker
.is_maybe_cjs(original_specifier, media_type)
{
Ok(is_maybe_cjs) => is_maybe_cjs,
Err(err) => {
return deno_core::ModuleLoadResponse::Sync(Err(type_error(
format!("{:?}", err),
)));
}
};
if is_maybe_cjs {
let original_specifier = original_specifier.clone();
let module_specifier = module_specifier.clone();
let shared = self.shared.clone();
deno_core::ModuleLoadResponse::Async(
async move {
let source = match module_source {
DenoCompileModuleSource::String(string) => {
Cow::Borrowed(string)
}
DenoCompileModuleSource::Bytes(module_code_bytes) => {
match module_code_bytes {
Cow::Owned(bytes) => Cow::Owned(
crate::util::text_encoding::from_utf8_lossy_owned(bytes),
),
Cow::Borrowed(bytes) => String::from_utf8_lossy(bytes),
}
}
};
let source = shared
.node_code_translator
.translate_cjs_to_esm(&module_specifier, Some(source))
.await?;
let module_source = match source {
Cow::Owned(source) => ModuleSourceCode::String(source.into()),
Cow::Borrowed(source) => {
ModuleSourceCode::String(FastString::from_static(source))
}
};
Ok(deno_core::ModuleSource::new_with_redirect(
module_type,
module_source,
&original_specifier,
&module_specifier,
None,
))
}
.boxed_local(),
)
} else {
let module_source = module_source.into_for_v8();
deno_core::ModuleLoadResponse::Sync(Ok( deno_core::ModuleLoadResponse::Sync(Ok(
deno_core::ModuleSource::new_with_redirect( deno_core::ModuleSource::new_with_redirect(
module_type, module_type,
@ -329,6 +395,7 @@ impl ModuleLoader for EmbeddedModuleLoader {
), ),
)) ))
} }
}
Ok(None) => deno_core::ModuleLoadResponse::Sync(Err(type_error( Ok(None) => deno_core::ModuleLoadResponse::Sync(Err(type_error(
format!("{MODULE_NOT_FOUND}: {}", original_specifier), format!("{MODULE_NOT_FOUND}: {}", original_specifier),
))), ))),
@ -339,32 +406,61 @@ impl ModuleLoader for EmbeddedModuleLoader {
} }
} }
impl NodeRequireLoader for EmbeddedModuleLoader {
fn ensure_read_permission<'a>(
&self,
permissions: &mut dyn deno_runtime::deno_node::NodePermissions,
path: &'a std::path::Path,
) -> Result<Cow<'a, std::path::Path>, AnyError> {
if self.shared.modules.has_file(path) {
// allow reading if the file is in the snapshot
return Ok(Cow::Borrowed(path));
}
self
.shared
.npm_resolver
.ensure_read_permission(permissions, path)
}
fn load_text_file_lossy(
&self,
path: &std::path::Path,
) -> Result<String, AnyError> {
Ok(self.shared.fs.read_text_file_lossy_sync(path, None)?)
}
}
struct StandaloneModuleLoaderFactory { struct StandaloneModuleLoaderFactory {
shared: Arc<SharedModuleLoaderState>, shared: Arc<SharedModuleLoaderState>,
} }
impl StandaloneModuleLoaderFactory {
pub fn create_result(&self) -> CreateModuleLoaderResult {
let loader = Rc::new(EmbeddedModuleLoader {
shared: self.shared.clone(),
});
CreateModuleLoaderResult {
module_loader: loader.clone(),
node_require_loader: loader,
}
}
}
impl ModuleLoaderFactory for StandaloneModuleLoaderFactory { impl ModuleLoaderFactory for StandaloneModuleLoaderFactory {
fn create_for_main( fn create_for_main(
&self, &self,
_root_permissions: PermissionsContainer, _root_permissions: PermissionsContainer,
) -> ModuleLoaderAndSourceMapGetter { ) -> CreateModuleLoaderResult {
ModuleLoaderAndSourceMapGetter { self.create_result()
module_loader: Rc::new(EmbeddedModuleLoader {
shared: self.shared.clone(),
}),
}
} }
fn create_for_worker( fn create_for_worker(
&self, &self,
_parent_permissions: PermissionsContainer, _parent_permissions: PermissionsContainer,
_permissions: PermissionsContainer, _permissions: PermissionsContainer,
) -> ModuleLoaderAndSourceMapGetter { ) -> CreateModuleLoaderResult {
ModuleLoaderAndSourceMapGetter { self.create_result()
module_loader: Rc::new(EmbeddedModuleLoader {
shared: self.shared.clone(),
}),
}
} }
} }
@ -410,20 +506,45 @@ pub async fn run(data: StandaloneData) -> Result<i32, AnyError> {
let main_module = root_dir_url.join(&metadata.entrypoint_key).unwrap(); let main_module = root_dir_url.join(&metadata.entrypoint_key).unwrap();
let npm_global_cache_dir = root_path.join(".deno_compile_node_modules"); let npm_global_cache_dir = root_path.join(".deno_compile_node_modules");
let cache_setting = CacheSetting::Only; let cache_setting = CacheSetting::Only;
let npm_resolver = match metadata.node_modules { let pkg_json_resolver = Arc::new(PackageJsonResolver::new(
deno_runtime::deno_node::DenoFsNodeResolverEnv::new(fs.clone()),
));
let (in_npm_pkg_checker, npm_resolver) = match metadata.node_modules {
Some(binary::NodeModules::Managed { node_modules_dir }) => { Some(binary::NodeModules::Managed { node_modules_dir }) => {
// create an npmrc that uses the fake npm_registry_url to resolve packages
let npmrc = Arc::new(ResolvedNpmRc {
default_config: deno_npm::npm_rc::RegistryConfigWithUrl {
registry_url: npm_registry_url.clone(),
config: Default::default(),
},
scopes: Default::default(),
registry_configs: Default::default(),
});
let npm_cache_dir = Arc::new(NpmCacheDir::new(
&DenoCacheEnvFsAdapter(fs.as_ref()),
npm_global_cache_dir,
npmrc.get_all_known_registries_urls(),
));
let snapshot = npm_snapshot.unwrap(); let snapshot = npm_snapshot.unwrap();
let maybe_node_modules_path = node_modules_dir let maybe_node_modules_path = node_modules_dir
.map(|node_modules_dir| root_path.join(node_modules_dir)); .map(|node_modules_dir| root_path.join(node_modules_dir));
let in_npm_pkg_checker =
create_in_npm_pkg_checker(CreateInNpmPkgCheckerOptions::Managed(
CliManagedInNpmPkgCheckerCreateOptions {
root_cache_dir_url: npm_cache_dir.root_dir_url(),
maybe_node_modules_path: maybe_node_modules_path.as_deref(),
},
));
let npm_resolver =
create_cli_npm_resolver(CliNpmResolverCreateOptions::Managed( create_cli_npm_resolver(CliNpmResolverCreateOptions::Managed(
CliNpmResolverManagedCreateOptions { CliManagedNpmResolverCreateOptions {
snapshot: CliNpmResolverManagedSnapshotOption::Specified(Some( snapshot: CliNpmResolverManagedSnapshotOption::Specified(Some(
snapshot, snapshot,
)), )),
maybe_lockfile: None, maybe_lockfile: None,
fs: fs.clone(), fs: fs.clone(),
http_client_provider: http_client_provider.clone(), http_client_provider: http_client_provider.clone(),
npm_global_cache_dir, npm_cache_dir,
cache_setting, cache_setting,
text_only_progress_bar: progress_bar, text_only_progress_bar: progress_bar,
maybe_node_modules_path, maybe_node_modules_path,
@ -432,41 +553,54 @@ pub async fn run(data: StandaloneData) -> Result<i32, AnyError> {
// this is only used for installing packages, which isn't necessary with deno compile // this is only used for installing packages, which isn't necessary with deno compile
NpmInstallDepsProvider::empty(), NpmInstallDepsProvider::empty(),
), ),
// create an npmrc that uses the fake npm_registry_url to resolve packages npmrc,
npmrc: Arc::new(ResolvedNpmRc {
default_config: deno_npm::npm_rc::RegistryConfigWithUrl {
registry_url: npm_registry_url.clone(),
config: Default::default(),
},
scopes: Default::default(),
registry_configs: Default::default(),
}),
lifecycle_scripts: Default::default(), lifecycle_scripts: Default::default(),
}, },
)) ))
.await? .await?;
(in_npm_pkg_checker, npm_resolver)
} }
Some(binary::NodeModules::Byonm { Some(binary::NodeModules::Byonm {
root_node_modules_dir, root_node_modules_dir,
}) => { }) => {
let root_node_modules_dir = let root_node_modules_dir =
root_node_modules_dir.map(|p| vfs.root().join(p)); root_node_modules_dir.map(|p| vfs.root().join(p));
create_cli_npm_resolver(CliNpmResolverCreateOptions::Byonm( let in_npm_pkg_checker =
CliByonmNpmResolverCreateOptions { create_in_npm_pkg_checker(CreateInNpmPkgCheckerOptions::Byonm);
let npm_resolver = create_cli_npm_resolver(
CliNpmResolverCreateOptions::Byonm(CliByonmNpmResolverCreateOptions {
fs: CliDenoResolverFs(fs.clone()), fs: CliDenoResolverFs(fs.clone()),
pkg_json_resolver: pkg_json_resolver.clone(),
root_node_modules_dir, root_node_modules_dir,
}, }),
)) )
.await? .await?;
(in_npm_pkg_checker, npm_resolver)
} }
None => { None => {
// Packages from different registries are already inlined in the binary,
// so no need to create actual `.npmrc` configuration.
let npmrc = create_default_npmrc();
let npm_cache_dir = Arc::new(NpmCacheDir::new(
&DenoCacheEnvFsAdapter(fs.as_ref()),
npm_global_cache_dir,
npmrc.get_all_known_registries_urls(),
));
let in_npm_pkg_checker =
create_in_npm_pkg_checker(CreateInNpmPkgCheckerOptions::Managed(
CliManagedInNpmPkgCheckerCreateOptions {
root_cache_dir_url: npm_cache_dir.root_dir_url(),
maybe_node_modules_path: None,
},
));
let npm_resolver =
create_cli_npm_resolver(CliNpmResolverCreateOptions::Managed( create_cli_npm_resolver(CliNpmResolverCreateOptions::Managed(
CliNpmResolverManagedCreateOptions { CliManagedNpmResolverCreateOptions {
snapshot: CliNpmResolverManagedSnapshotOption::Specified(None), snapshot: CliNpmResolverManagedSnapshotOption::Specified(None),
maybe_lockfile: None, maybe_lockfile: None,
fs: fs.clone(), fs: fs.clone(),
http_client_provider: http_client_provider.clone(), http_client_provider: http_client_provider.clone(),
npm_global_cache_dir, npm_cache_dir,
cache_setting, cache_setting,
text_only_progress_bar: progress_bar, text_only_progress_bar: progress_bar,
maybe_node_modules_path: None, maybe_node_modules_path: None,
@ -475,41 +609,52 @@ pub async fn run(data: StandaloneData) -> Result<i32, AnyError> {
// this is only used for installing packages, which isn't necessary with deno compile // this is only used for installing packages, which isn't necessary with deno compile
NpmInstallDepsProvider::empty(), NpmInstallDepsProvider::empty(),
), ),
// Packages from different registries are already inlined in the binary,
// so no need to create actual `.npmrc` configuration.
npmrc: create_default_npmrc(), npmrc: create_default_npmrc(),
lifecycle_scripts: Default::default(), lifecycle_scripts: Default::default(),
}, },
)) ))
.await? .await?;
(in_npm_pkg_checker, npm_resolver)
} }
}; };
let has_node_modules_dir = npm_resolver.root_node_modules_path().is_some(); let has_node_modules_dir = npm_resolver.root_node_modules_path().is_some();
let node_resolver = Arc::new(NodeResolver::new( let node_resolver = Arc::new(NodeResolver::new(
deno_runtime::deno_node::DenoFsNodeResolverEnv::new(fs.clone()), deno_runtime::deno_node::DenoFsNodeResolverEnv::new(fs.clone()),
in_npm_pkg_checker.clone(),
npm_resolver.clone().into_npm_resolver(), npm_resolver.clone().into_npm_resolver(),
pkg_json_resolver.clone(),
));
let cjs_tracker = Arc::new(CjsTracker::new(
in_npm_pkg_checker.clone(),
pkg_json_resolver.clone(),
CjsTrackerOptions {
unstable_detect_cjs: metadata.unstable_config.detect_cjs,
},
)); ));
let cjs_resolutions = Arc::new(CjsResolutionStore::default());
let cache_db = Caches::new(deno_dir_provider.clone()); let cache_db = Caches::new(deno_dir_provider.clone());
let node_analysis_cache = NodeAnalysisCache::new(cache_db.node_analysis_db()); let node_analysis_cache = NodeAnalysisCache::new(cache_db.node_analysis_db());
let cli_node_resolver = Arc::new(CliNodeResolver::new( let cli_node_resolver = Arc::new(CliNodeResolver::new(
cjs_resolutions.clone(), cjs_tracker.clone(),
fs.clone(), fs.clone(),
in_npm_pkg_checker.clone(),
node_resolver.clone(), node_resolver.clone(),
npm_resolver.clone(), npm_resolver.clone(),
)); ));
let cjs_esm_code_analyzer = CliCjsCodeAnalyzer::new( let cjs_esm_code_analyzer = CliCjsCodeAnalyzer::new(
node_analysis_cache, node_analysis_cache,
cjs_tracker.clone(),
fs.clone(), fs.clone(),
cli_node_resolver.clone(),
None, None,
false,
); );
let node_code_translator = Arc::new(NodeCodeTranslator::new( let node_code_translator = Arc::new(NodeCodeTranslator::new(
cjs_esm_code_analyzer, cjs_esm_code_analyzer,
deno_runtime::deno_node::DenoFsNodeResolverEnv::new(fs.clone()), deno_runtime::deno_node::DenoFsNodeResolverEnv::new(fs.clone()),
in_npm_pkg_checker,
node_resolver.clone(), node_resolver.clone(),
npm_resolver.clone().into_npm_resolver(), npm_resolver.clone().into_npm_resolver(),
pkg_json_resolver.clone(),
)); ));
let workspace_resolver = { let workspace_resolver = {
let import_map = match metadata.workspace_resolver.import_map { let import_map = match metadata.workspace_resolver.import_map {
@ -562,15 +707,18 @@ pub async fn run(data: StandaloneData) -> Result<i32, AnyError> {
}; };
let module_loader_factory = StandaloneModuleLoaderFactory { let module_loader_factory = StandaloneModuleLoaderFactory {
shared: Arc::new(SharedModuleLoaderState { shared: Arc::new(SharedModuleLoaderState {
cjs_tracker: cjs_tracker.clone(),
fs: fs.clone(),
modules, modules,
workspace_resolver, node_code_translator: node_code_translator.clone(),
node_resolver: cli_node_resolver.clone(), node_resolver: cli_node_resolver.clone(),
npm_module_loader: Arc::new(NpmModuleLoader::new( npm_module_loader: Arc::new(NpmModuleLoader::new(
cjs_resolutions.clone(), cjs_tracker.clone(),
node_code_translator,
fs.clone(), fs.clone(),
cli_node_resolver, node_code_translator,
)), )),
npm_resolver: npm_resolver.clone(),
workspace_resolver,
}), }),
}; };
@ -609,7 +757,6 @@ pub async fn run(data: StandaloneData) -> Result<i32, AnyError> {
}); });
let worker_factory = CliMainWorkerFactory::new( let worker_factory = CliMainWorkerFactory::new(
Arc::new(BlobStore::default()), Arc::new(BlobStore::default()),
cjs_resolutions,
// Code cache is not supported for standalone binary yet. // Code cache is not supported for standalone binary yet.
None, None,
feature_checker, feature_checker,
@ -620,6 +767,7 @@ pub async fn run(data: StandaloneData) -> Result<i32, AnyError> {
Box::new(module_loader_factory), Box::new(module_loader_factory),
node_resolver, node_resolver,
npm_resolver, npm_resolver,
pkg_json_resolver,
root_cert_store_provider, root_cert_store_provider,
permissions, permissions,
StorageKeyResolver::empty(), StorageKeyResolver::empty(),
@ -635,7 +783,6 @@ pub async fn run(data: StandaloneData) -> Result<i32, AnyError> {
inspect_wait: false, inspect_wait: false,
strace_ops: None, strace_ops: None,
is_inspecting: false, is_inspecting: false,
is_npm_main: main_module.scheme() == "npm",
skip_op_registration: true, skip_op_registration: true,
location: metadata.location, location: metadata.location,
argv0: NpmPackageReqReference::from_specifier(&main_module) argv0: NpmPackageReqReference::from_specifier(&main_module)
@ -652,7 +799,6 @@ pub async fn run(data: StandaloneData) -> Result<i32, AnyError> {
node_ipc: None, node_ipc: None,
serve_port: None, serve_port: None,
serve_host: None, serve_host: None,
unstable_detect_cjs: metadata.unstable_config.detect_cjs,
}, },
); );

View file

@ -214,14 +214,13 @@ impl RemoteModulesStoreBuilder {
} }
} }
pub struct DenoCompileModuleData<'a> { pub enum DenoCompileModuleSource {
pub specifier: &'a Url, String(&'static str),
pub media_type: MediaType, Bytes(Cow<'static, [u8]>),
pub data: Cow<'static, [u8]>,
} }
impl<'a> DenoCompileModuleData<'a> { impl DenoCompileModuleSource {
pub fn into_for_v8(self) -> (&'a Url, ModuleType, ModuleSourceCode) { pub fn into_for_v8(self) -> ModuleSourceCode {
fn into_bytes(data: Cow<'static, [u8]>) -> ModuleSourceCode { fn into_bytes(data: Cow<'static, [u8]>) -> ModuleSourceCode {
ModuleSourceCode::Bytes(match data { ModuleSourceCode::Bytes(match data {
Cow::Borrowed(d) => d.into(), Cow::Borrowed(d) => d.into(),
@ -229,16 +228,31 @@ impl<'a> DenoCompileModuleData<'a> {
}) })
} }
fn into_string_unsafe(data: Cow<'static, [u8]>) -> ModuleSourceCode { match self {
// todo(https://github.com/denoland/deno_core/pull/943): store whether // todo(https://github.com/denoland/deno_core/pull/943): store whether
// the string is ascii or not ahead of time so we can avoid the is_ascii() // the string is ascii or not ahead of time so we can avoid the is_ascii()
// check in FastString::from_static // check in FastString::from_static
Self::String(s) => ModuleSourceCode::String(FastString::from_static(s)),
Self::Bytes(b) => into_bytes(b),
}
}
}
pub struct DenoCompileModuleData<'a> {
pub specifier: &'a Url,
pub media_type: MediaType,
pub data: Cow<'static, [u8]>,
}
impl<'a> DenoCompileModuleData<'a> {
pub fn into_parts(self) -> (&'a Url, ModuleType, DenoCompileModuleSource) {
fn into_string_unsafe(data: Cow<'static, [u8]>) -> DenoCompileModuleSource {
match data { match data {
Cow::Borrowed(d) => ModuleSourceCode::String( Cow::Borrowed(d) => DenoCompileModuleSource::String(
// SAFETY: we know this is a valid utf8 string // SAFETY: we know this is a valid utf8 string
unsafe { FastString::from_static(std::str::from_utf8_unchecked(d)) }, unsafe { std::str::from_utf8_unchecked(d) },
), ),
Cow::Owned(d) => ModuleSourceCode::Bytes(d.into_boxed_slice().into()), Cow::Owned(d) => DenoCompileModuleSource::Bytes(Cow::Owned(d)),
} }
} }
@ -257,11 +271,14 @@ impl<'a> DenoCompileModuleData<'a> {
(ModuleType::JavaScript, into_string_unsafe(self.data)) (ModuleType::JavaScript, into_string_unsafe(self.data))
} }
MediaType::Json => (ModuleType::Json, into_string_unsafe(self.data)), MediaType::Json => (ModuleType::Json, into_string_unsafe(self.data)),
MediaType::Wasm => (ModuleType::Wasm, into_bytes(self.data)), MediaType::Wasm => {
// just assume javascript if we made it here (ModuleType::Wasm, DenoCompileModuleSource::Bytes(self.data))
MediaType::TsBuildInfo | MediaType::SourceMap | MediaType::Unknown => {
(ModuleType::JavaScript, into_bytes(self.data))
} }
// just assume javascript if we made it here
MediaType::Css | MediaType::SourceMap | MediaType::Unknown => (
ModuleType::JavaScript,
DenoCompileModuleSource::Bytes(self.data),
),
}; };
(self.specifier, media_type, source) (self.specifier, media_type, source)
} }
@ -551,7 +568,7 @@ fn serialize_media_type(media_type: MediaType) -> u8 {
MediaType::Tsx => 10, MediaType::Tsx => 10,
MediaType::Json => 11, MediaType::Json => 11,
MediaType::Wasm => 12, MediaType::Wasm => 12,
MediaType::TsBuildInfo => 13, MediaType::Css => 13,
MediaType::SourceMap => 14, MediaType::SourceMap => 14,
MediaType::Unknown => 15, MediaType::Unknown => 15,
} }
@ -572,7 +589,7 @@ fn deserialize_media_type(value: u8) -> Result<MediaType, AnyError> {
10 => Ok(MediaType::Tsx), 10 => Ok(MediaType::Tsx),
11 => Ok(MediaType::Json), 11 => Ok(MediaType::Json),
12 => Ok(MediaType::Wasm), 12 => Ok(MediaType::Wasm),
13 => Ok(MediaType::TsBuildInfo), 13 => Ok(MediaType::Css),
14 => Ok(MediaType::SourceMap), 14 => Ok(MediaType::SourceMap),
15 => Ok(MediaType::Unknown), 15 => Ok(MediaType::Unknown),
_ => bail!("Unknown media type value: {}", value), _ => bail!("Unknown media type value: {}", value),

View file

@ -193,7 +193,7 @@ async fn bench_specifier_inner(
.await?; .await?;
// We execute the main module as a side module so that import.meta.main is not set. // We execute the main module as a side module so that import.meta.main is not set.
worker.execute_side_module_possibly_with_npm().await?; worker.execute_side_module().await?;
let mut worker = worker.into_main_worker(); let mut worker = worker.into_main_worker();

View file

@ -32,6 +32,7 @@ use crate::graph_util::ModuleGraphBuilder;
use crate::npm::CliNpmResolver; use crate::npm::CliNpmResolver;
use crate::tsc; use crate::tsc;
use crate::tsc::Diagnostics; use crate::tsc::Diagnostics;
use crate::tsc::TypeCheckingCjsTracker;
use crate::util::extract; use crate::util::extract;
use crate::util::path::to_percent_decoded_str; use crate::util::path::to_percent_decoded_str;
@ -99,6 +100,7 @@ pub struct CheckOptions {
pub struct TypeChecker { pub struct TypeChecker {
caches: Arc<Caches>, caches: Arc<Caches>,
cjs_tracker: Arc<TypeCheckingCjsTracker>,
cli_options: Arc<CliOptions>, cli_options: Arc<CliOptions>,
module_graph_builder: Arc<ModuleGraphBuilder>, module_graph_builder: Arc<ModuleGraphBuilder>,
node_resolver: Arc<NodeResolver>, node_resolver: Arc<NodeResolver>,
@ -108,6 +110,7 @@ pub struct TypeChecker {
impl TypeChecker { impl TypeChecker {
pub fn new( pub fn new(
caches: Arc<Caches>, caches: Arc<Caches>,
cjs_tracker: Arc<TypeCheckingCjsTracker>,
cli_options: Arc<CliOptions>, cli_options: Arc<CliOptions>,
module_graph_builder: Arc<ModuleGraphBuilder>, module_graph_builder: Arc<ModuleGraphBuilder>,
node_resolver: Arc<NodeResolver>, node_resolver: Arc<NodeResolver>,
@ -115,6 +118,7 @@ impl TypeChecker {
) -> Self { ) -> Self {
Self { Self {
caches, caches,
cjs_tracker,
cli_options, cli_options,
module_graph_builder, module_graph_builder,
node_resolver, node_resolver,
@ -244,6 +248,7 @@ impl TypeChecker {
graph: graph.clone(), graph: graph.clone(),
hash_data, hash_data,
maybe_npm: Some(tsc::RequestNpmState { maybe_npm: Some(tsc::RequestNpmState {
cjs_tracker: self.cjs_tracker.clone(),
node_resolver: self.node_resolver.clone(), node_resolver: self.node_resolver.clone(),
npm_resolver: self.npm_resolver.clone(), npm_resolver: self.npm_resolver.clone(),
}), }),
@ -346,7 +351,7 @@ fn get_check_hash(
} }
} }
MediaType::Json MediaType::Json
| MediaType::TsBuildInfo | MediaType::Css
| MediaType::SourceMap | MediaType::SourceMap
| MediaType::Wasm | MediaType::Wasm
| MediaType::Unknown => continue, | MediaType::Unknown => continue,
@ -428,7 +433,7 @@ fn get_tsc_roots(
} }
MediaType::Json MediaType::Json
| MediaType::Wasm | MediaType::Wasm
| MediaType::TsBuildInfo | MediaType::Css
| MediaType::SourceMap | MediaType::SourceMap
| MediaType::Unknown => None, | MediaType::Unknown => None,
}, },
@ -536,7 +541,7 @@ fn has_ts_check(media_type: MediaType, file_text: &str) -> bool {
| MediaType::Tsx | MediaType::Tsx
| MediaType::Json | MediaType::Json
| MediaType::Wasm | MediaType::Wasm
| MediaType::TsBuildInfo | MediaType::Css
| MediaType::SourceMap | MediaType::SourceMap
| MediaType::Unknown => false, | MediaType::Unknown => false,
} }

View file

@ -53,16 +53,6 @@ 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( let output_path = resolve_compile_executable_output_path(
http_client, http_client,
&compile_flags, &compile_flags,

View file

@ -6,12 +6,12 @@ use crate::args::FileFlags;
use crate::args::Flags; use crate::args::Flags;
use crate::cdp; use crate::cdp;
use crate::factory::CliFactory; use crate::factory::CliFactory;
use crate::npm::CliNpmResolver;
use crate::tools::fmt::format_json; use crate::tools::fmt::format_json;
use crate::tools::test::is_supported_test_path; use crate::tools::test::is_supported_test_path;
use crate::util::text_encoding::source_map_from_code; use crate::util::text_encoding::source_map_from_code;
use deno_ast::MediaType; use deno_ast::MediaType;
use deno_ast::ModuleKind;
use deno_ast::ModuleSpecifier; use deno_ast::ModuleSpecifier;
use deno_config::glob::FileCollector; use deno_config::glob::FileCollector;
use deno_config::glob::FilePatterns; use deno_config::glob::FilePatterns;
@ -25,6 +25,7 @@ use deno_core::serde_json;
use deno_core::sourcemap::SourceMap; use deno_core::sourcemap::SourceMap;
use deno_core::url::Url; use deno_core::url::Url;
use deno_core::LocalInspectorSession; use deno_core::LocalInspectorSession;
use node_resolver::InNpmPackageChecker;
use regex::Regex; use regex::Regex;
use std::fs; use std::fs;
use std::fs::File; use std::fs::File;
@ -461,7 +462,7 @@ fn filter_coverages(
coverages: Vec<cdp::ScriptCoverage>, coverages: Vec<cdp::ScriptCoverage>,
include: Vec<String>, include: Vec<String>,
exclude: Vec<String>, exclude: Vec<String>,
npm_resolver: &dyn CliNpmResolver, in_npm_pkg_checker: &dyn InNpmPackageChecker,
) -> Vec<cdp::ScriptCoverage> { ) -> Vec<cdp::ScriptCoverage> {
let include: Vec<Regex> = let include: Vec<Regex> =
include.iter().map(|e| Regex::new(e).unwrap()).collect(); include.iter().map(|e| Regex::new(e).unwrap()).collect();
@ -485,7 +486,7 @@ fn filter_coverages(
|| doc_test_re.is_match(e.url.as_str()) || doc_test_re.is_match(e.url.as_str())
|| Url::parse(&e.url) || Url::parse(&e.url)
.ok() .ok()
.map(|url| npm_resolver.in_npm_package(&url)) .map(|url| in_npm_pkg_checker.in_npm_package(&url))
.unwrap_or(false); .unwrap_or(false);
let is_included = include.iter().any(|p| p.is_match(&e.url)); let is_included = include.iter().any(|p| p.is_match(&e.url));
@ -496,7 +497,7 @@ fn filter_coverages(
.collect::<Vec<cdp::ScriptCoverage>>() .collect::<Vec<cdp::ScriptCoverage>>()
} }
pub async fn cover_files( pub fn cover_files(
flags: Arc<Flags>, flags: Arc<Flags>,
coverage_flags: CoverageFlags, coverage_flags: CoverageFlags,
) -> Result<(), AnyError> { ) -> Result<(), AnyError> {
@ -506,9 +507,10 @@ pub async fn cover_files(
let factory = CliFactory::from_flags(flags); let factory = CliFactory::from_flags(flags);
let cli_options = factory.cli_options()?; let cli_options = factory.cli_options()?;
let npm_resolver = factory.npm_resolver().await?; let in_npm_pkg_checker = factory.in_npm_pkg_checker()?;
let file_fetcher = factory.file_fetcher()?; let file_fetcher = factory.file_fetcher()?;
let emitter = factory.emitter()?; let emitter = factory.emitter()?;
let cjs_tracker = factory.cjs_tracker()?;
assert!(!coverage_flags.files.include.is_empty()); assert!(!coverage_flags.files.include.is_empty());
@ -528,7 +530,7 @@ pub async fn cover_files(
script_coverages, script_coverages,
coverage_flags.include, coverage_flags.include,
coverage_flags.exclude, coverage_flags.exclude,
npm_resolver.as_ref(), in_npm_pkg_checker.as_ref(),
); );
if script_coverages.is_empty() { if script_coverages.is_empty() {
return Err(generic_error("No covered files included in the report")); return Err(generic_error("No covered files included in the report"));
@ -585,6 +587,8 @@ pub async fn cover_files(
let transpiled_code = match file.media_type { let transpiled_code = match file.media_type {
MediaType::JavaScript MediaType::JavaScript
| MediaType::Unknown | MediaType::Unknown
| MediaType::Css
| MediaType::Wasm
| MediaType::Cjs | MediaType::Cjs
| MediaType::Mjs | MediaType::Mjs
| MediaType::Json => None, | MediaType::Json => None,
@ -594,7 +598,10 @@ pub async fn cover_files(
| MediaType::Mts | MediaType::Mts
| MediaType::Cts | MediaType::Cts
| MediaType::Tsx => { | MediaType::Tsx => {
Some(match emitter.maybe_cached_emit(&file.specifier, &file.source) { let module_kind = ModuleKind::from_is_cjs(
cjs_tracker.is_maybe_cjs(&file.specifier, file.media_type)?,
);
Some(match emitter.maybe_cached_emit(&file.specifier, module_kind, &file.source) {
Some(code) => code, Some(code) => code,
None => { None => {
return Err(anyhow!( return Err(anyhow!(
@ -605,7 +612,7 @@ pub async fn cover_files(
} }
}) })
} }
MediaType::Wasm | MediaType::TsBuildInfo | MediaType::SourceMap => { MediaType::SourceMap => {
unreachable!() unreachable!()
} }
}; };

View file

@ -22,9 +22,9 @@ use deno_core::serde_json;
use deno_doc as doc; use deno_doc as doc;
use deno_doc::html::UrlResolveKind; use deno_doc::html::UrlResolveKind;
use deno_graph::source::NullFileSystem; use deno_graph::source::NullFileSystem;
use deno_graph::EsParser;
use deno_graph::GraphKind; use deno_graph::GraphKind;
use deno_graph::ModuleAnalyzer; use deno_graph::ModuleAnalyzer;
use deno_graph::ModuleParser;
use deno_graph::ModuleSpecifier; use deno_graph::ModuleSpecifier;
use doc::html::ShortPath; use doc::html::ShortPath;
use doc::DocDiagnostic; use doc::DocDiagnostic;
@ -37,7 +37,7 @@ const JSON_SCHEMA_VERSION: u8 = 1;
async fn generate_doc_nodes_for_builtin_types( async fn generate_doc_nodes_for_builtin_types(
doc_flags: DocFlags, doc_flags: DocFlags,
parser: &dyn ModuleParser, parser: &dyn EsParser,
analyzer: &dyn ModuleAnalyzer, analyzer: &dyn ModuleAnalyzer,
) -> Result<IndexMap<ModuleSpecifier, Vec<doc::DocNode>>, AnyError> { ) -> Result<IndexMap<ModuleSpecifier, Vec<doc::DocNode>>, AnyError> {
let source_file_specifier = let source_file_specifier =
@ -96,7 +96,7 @@ pub async fn doc(
let module_info_cache = factory.module_info_cache()?; let module_info_cache = factory.module_info_cache()?;
let parsed_source_cache = factory.parsed_source_cache(); let parsed_source_cache = factory.parsed_source_cache();
let capturing_parser = parsed_source_cache.as_capturing_parser(); let capturing_parser = parsed_source_cache.as_capturing_parser();
let analyzer = module_info_cache.as_module_analyzer(parsed_source_cache); let analyzer = module_info_cache.as_module_analyzer();
let doc_nodes_by_url = match doc_flags.source_files { let doc_nodes_by_url = match doc_flags.source_files {
DocSourceFileFlag::Builtin => { DocSourceFileFlag::Builtin => {

View file

@ -120,7 +120,7 @@ fn resolve_content_maybe_unfurling(
| MediaType::Unknown | MediaType::Unknown
| MediaType::Json | MediaType::Json
| MediaType::Wasm | MediaType::Wasm
| MediaType::TsBuildInfo => { | MediaType::Css => {
// not unfurlable data // not unfurlable data
return Ok(data); return Ok(data);
} }

View file

@ -25,6 +25,7 @@ use deno_ast::swc::visit::noop_visit_type;
use deno_ast::swc::visit::Visit; use deno_ast::swc::visit::Visit;
use deno_ast::swc::visit::VisitWith; use deno_ast::swc::visit::VisitWith;
use deno_ast::ImportsNotUsedAsValues; use deno_ast::ImportsNotUsedAsValues;
use deno_ast::ModuleKind;
use deno_ast::ModuleSpecifier; use deno_ast::ModuleSpecifier;
use deno_ast::ParseDiagnosticsError; use deno_ast::ParseDiagnosticsError;
use deno_ast::ParsedSource; use deno_ast::ParsedSource;
@ -641,6 +642,10 @@ impl ReplSession {
jsx_fragment_factory: self.jsx.frag_factory.clone(), jsx_fragment_factory: self.jsx.frag_factory.clone(),
jsx_import_source: self.jsx.import_source.clone(), jsx_import_source: self.jsx.import_source.clone(),
var_decl_imports: true, var_decl_imports: true,
verbatim_module_syntax: false,
},
&deno_ast::TranspileModuleOptions {
module_kind: Some(ModuleKind::Esm),
}, },
&deno_ast::EmitOptions { &deno_ast::EmitOptions {
source_map: deno_ast::SourceMapOption::None, source_map: deno_ast::SourceMapOption::None,
@ -651,7 +656,6 @@ impl ReplSession {
}, },
)? )?
.into_source() .into_source()
.into_string()?
.text; .text;
let value = self let value = self

View file

@ -1,9 +1,11 @@
// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. // Copyright 2018-2024 the Deno authors. All rights reserved. MIT license.
use crate::cdp; use std::collections::HashMap;
use crate::emit::Emitter; use std::path::PathBuf;
use crate::util::file_watcher::WatcherCommunicator; use std::sync::Arc;
use crate::util::file_watcher::WatcherRestartMode;
use deno_ast::MediaType;
use deno_ast::ModuleKind;
use deno_core::error::generic_error; use deno_core::error::generic_error;
use deno_core::error::AnyError; use deno_core::error::AnyError;
use deno_core::futures::StreamExt; use deno_core::futures::StreamExt;
@ -12,11 +14,14 @@ use deno_core::serde_json::{self};
use deno_core::url::Url; use deno_core::url::Url;
use deno_core::LocalInspectorSession; use deno_core::LocalInspectorSession;
use deno_terminal::colors; use deno_terminal::colors;
use std::collections::HashMap;
use std::path::PathBuf;
use std::sync::Arc;
use tokio::select; use tokio::select;
use crate::cdp;
use crate::emit::Emitter;
use crate::resolver::CjsTracker;
use crate::util::file_watcher::WatcherCommunicator;
use crate::util::file_watcher::WatcherRestartMode;
fn explain(status: &cdp::Status) -> &'static str { fn explain(status: &cdp::Status) -> &'static str {
match status { match status {
cdp::Status::Ok => "OK", cdp::Status::Ok => "OK",
@ -58,6 +63,7 @@ pub struct HmrRunner {
session: LocalInspectorSession, session: LocalInspectorSession,
watcher_communicator: Arc<WatcherCommunicator>, watcher_communicator: Arc<WatcherCommunicator>,
script_ids: HashMap<String, String>, script_ids: HashMap<String, String>,
cjs_tracker: Arc<CjsTracker>,
emitter: Arc<Emitter>, emitter: Arc<Emitter>,
} }
@ -139,7 +145,8 @@ impl crate::worker::HmrRunner for HmrRunner {
}; };
let source_code = self.emitter.load_and_emit_for_hmr( let source_code = self.emitter.load_and_emit_for_hmr(
&module_url &module_url,
ModuleKind::from_is_cjs(self.cjs_tracker.is_maybe_cjs(&module_url, MediaType::from_specifier(&module_url))?),
).await?; ).await?;
let mut tries = 1; let mut tries = 1;
@ -172,12 +179,14 @@ impl crate::worker::HmrRunner for HmrRunner {
impl HmrRunner { impl HmrRunner {
pub fn new( pub fn new(
cjs_tracker: Arc<CjsTracker>,
emitter: Arc<Emitter>, emitter: Arc<Emitter>,
session: LocalInspectorSession, session: LocalInspectorSession,
watcher_communicator: Arc<WatcherCommunicator>, watcher_communicator: Arc<WatcherCommunicator>,
) -> Self { ) -> Self {
Self { Self {
session, session,
cjs_tracker,
emitter, emitter,
watcher_communicator, watcher_communicator,
script_ids: HashMap::new(), script_ids: HashMap::new(),

View file

@ -631,7 +631,7 @@ async fn configure_main_worker(
"Deno[Deno.internal].core.setLeakTracingEnabled(true);", "Deno[Deno.internal].core.setLeakTracingEnabled(true);",
)?; )?;
} }
let res = worker.execute_side_module_possibly_with_npm().await; let res = worker.execute_side_module().await;
let mut worker = worker.into_main_worker(); let mut worker = worker.into_main_worker();
match res { match res {
Ok(()) => Ok(()), Ok(()) => Ok(()),

View file

@ -845,6 +845,8 @@ delete Object.prototype.__proto__;
jqueryMessage, jqueryMessage,
"Cannot_find_name_0_Do_you_need_to_install_type_definitions_for_jQuery_Try_npm_i_save_dev_types_Slash_2592": "Cannot_find_name_0_Do_you_need_to_install_type_definitions_for_jQuery_Try_npm_i_save_dev_types_Slash_2592":
jqueryMessage, jqueryMessage,
"Module_0_was_resolved_to_1_but_allowArbitraryExtensions_is_not_set_6263":
"Module '{0}' was resolved to '{1}', but importing these modules is not supported.",
}; };
})()); })());

View file

@ -323,7 +323,7 @@ impl Diagnostics {
// todo(dsherret): use a short lived cache to prevent parsing // todo(dsherret): use a short lived cache to prevent parsing
// source maps so often // source maps so often
if let Ok(source_map) = if let Ok(source_map) =
SourceMap::from_slice(&fast_check_module.source_map) SourceMap::from_slice(fast_check_module.source_map.as_bytes())
{ {
if let Some(start) = d.start.as_mut() { if let Some(start) = d.start.as_mut() {
let maybe_token = source_map let maybe_token = source_map

View file

@ -3,9 +3,11 @@
use crate::args::TsConfig; use crate::args::TsConfig;
use crate::args::TypeCheckMode; use crate::args::TypeCheckMode;
use crate::cache::FastInsecureHasher; use crate::cache::FastInsecureHasher;
use crate::cache::ModuleInfoCache;
use crate::node; use crate::node;
use crate::npm::CliNpmResolver; use crate::npm::CliNpmResolver;
use crate::npm::ResolvePkgFolderFromDenoReqError; use crate::npm::ResolvePkgFolderFromDenoReqError;
use crate::resolver::CjsTracker;
use crate::util::checksum; use crate::util::checksum;
use crate::util::path::mapped_specifier_for_tsc; use crate::util::path::mapped_specifier_for_tsc;
@ -32,13 +34,13 @@ use deno_graph::GraphKind;
use deno_graph::Module; use deno_graph::Module;
use deno_graph::ModuleGraph; use deno_graph::ModuleGraph;
use deno_graph::ResolutionResolved; use deno_graph::ResolutionResolved;
use deno_runtime::deno_fs;
use deno_runtime::deno_node::NodeResolver; use deno_runtime::deno_node::NodeResolver;
use deno_semver::npm::NpmPackageReqReference; use deno_semver::npm::NpmPackageReqReference;
use node_resolver::errors::NodeJsErrorCode; use node_resolver::errors::NodeJsErrorCode;
use node_resolver::errors::NodeJsErrorCoded; use node_resolver::errors::NodeJsErrorCoded;
use node_resolver::errors::ResolvePkgSubpathFromDenoModuleError; use node_resolver::errors::PackageSubpathResolveError;
use node_resolver::NodeModuleKind; use node_resolver::NodeModuleKind;
use node_resolver::NodeResolution;
use node_resolver::NodeResolutionMode; use node_resolver::NodeResolutionMode;
use once_cell::sync::Lazy; use once_cell::sync::Lazy;
use std::borrow::Cow; use std::borrow::Cow;
@ -302,8 +304,76 @@ pub struct EmittedFile {
pub media_type: MediaType, pub media_type: MediaType,
} }
pub fn into_specifier_and_media_type(
specifier: Option<ModuleSpecifier>,
) -> (ModuleSpecifier, MediaType) {
match specifier {
Some(specifier) => {
let media_type = MediaType::from_specifier(&specifier);
(specifier, media_type)
}
None => (
Url::parse("internal:///missing_dependency.d.ts").unwrap(),
MediaType::Dts,
),
}
}
#[derive(Debug)]
pub struct TypeCheckingCjsTracker {
cjs_tracker: Arc<CjsTracker>,
module_info_cache: Arc<ModuleInfoCache>,
}
impl TypeCheckingCjsTracker {
pub fn new(
cjs_tracker: Arc<CjsTracker>,
module_info_cache: Arc<ModuleInfoCache>,
) -> Self {
Self {
cjs_tracker,
module_info_cache,
}
}
pub fn is_cjs(
&self,
specifier: &ModuleSpecifier,
media_type: MediaType,
code: &Arc<str>,
) -> bool {
if let Some(module_kind) =
self.cjs_tracker.get_known_kind(specifier, media_type)
{
module_kind.is_cjs()
} else {
let maybe_is_script = self
.module_info_cache
.as_module_analyzer()
.analyze_sync(specifier, media_type, code)
.ok()
.map(|info| info.is_script);
maybe_is_script
.and_then(|is_script| {
self
.cjs_tracker
.is_cjs_with_known_is_script(specifier, media_type, is_script)
.ok()
})
.unwrap_or_else(|| {
self
.cjs_tracker
.is_maybe_cjs(specifier, media_type)
.unwrap_or(false)
})
}
}
}
#[derive(Debug)] #[derive(Debug)]
pub struct RequestNpmState { pub struct RequestNpmState {
pub cjs_tracker: Arc<TypeCheckingCjsTracker>,
pub node_resolver: Arc<NodeResolver>, pub node_resolver: Arc<NodeResolver>,
pub npm_resolver: Arc<dyn CliNpmResolver>, pub npm_resolver: Arc<dyn CliNpmResolver>,
} }
@ -456,7 +526,7 @@ pub fn as_ts_script_kind(media_type: MediaType) -> i32 {
MediaType::Tsx => 4, MediaType::Tsx => 4,
MediaType::Json => 6, MediaType::Json => 6,
MediaType::SourceMap MediaType::SourceMap
| MediaType::TsBuildInfo | MediaType::Css
| MediaType::Wasm | MediaType::Wasm
| MediaType::Unknown => 0, | MediaType::Unknown => 0,
} }
@ -489,25 +559,22 @@ fn op_load_inner(
) -> Result<Option<LoadResponse>, AnyError> { ) -> Result<Option<LoadResponse>, AnyError> {
fn load_from_node_modules( fn load_from_node_modules(
specifier: &ModuleSpecifier, specifier: &ModuleSpecifier,
node_resolver: Option<&NodeResolver>, npm_state: Option<&RequestNpmState>,
media_type: &mut MediaType, media_type: &mut MediaType,
is_cjs: &mut bool, is_cjs: &mut bool,
) -> Result<String, AnyError> { ) -> Result<String, AnyError> {
*media_type = MediaType::from_specifier(specifier); *media_type = MediaType::from_specifier(specifier);
*is_cjs = node_resolver
.map(|node_resolver| {
match node_resolver.url_to_node_resolution(specifier.clone()) {
Ok(NodeResolution::CommonJs(_)) => true,
Ok(NodeResolution::Esm(_))
| Ok(NodeResolution::BuiltIn(_))
| Err(_) => false,
}
})
.unwrap_or(false);
let file_path = specifier.to_file_path().unwrap(); let file_path = specifier.to_file_path().unwrap();
let code = std::fs::read_to_string(&file_path) let code = std::fs::read_to_string(&file_path)
.with_context(|| format!("Unable to load {}", file_path.display()))?; .with_context(|| format!("Unable to load {}", file_path.display()))?;
Ok(code) let code: Arc<str> = code.into();
*is_cjs = npm_state
.map(|npm_state| {
npm_state.cjs_tracker.is_cjs(specifier, *media_type, &code)
})
.unwrap_or(false);
// todo(dsherret): how to avoid cloning here?
Ok(code.to_string())
} }
let state = state.borrow_mut::<State>(); let state = state.borrow_mut::<State>();
@ -560,6 +627,9 @@ fn op_load_inner(
match module { match module {
Module::Js(module) => { Module::Js(module) => {
media_type = module.media_type; media_type = module.media_type;
if matches!(media_type, MediaType::Cjs | MediaType::Cts) {
is_cjs = true;
}
let source = module let source = module
.fast_check_module() .fast_check_module()
.map(|m| &*m.source) .map(|m| &*m.source)
@ -573,11 +643,13 @@ fn op_load_inner(
Module::Npm(_) | Module::Node(_) => None, Module::Npm(_) | Module::Node(_) => None,
Module::External(module) => { Module::External(module) => {
// means it's Deno code importing an npm module // means it's Deno code importing an npm module
let specifier = let specifier = node::resolve_specifier_into_node_modules(
node::resolve_specifier_into_node_modules(&module.specifier); &module.specifier,
&deno_fs::RealFs,
);
Some(Cow::Owned(load_from_node_modules( Some(Cow::Owned(load_from_node_modules(
&specifier, &specifier,
state.maybe_npm.as_ref().map(|n| n.node_resolver.as_ref()), state.maybe_npm.as_ref(),
&mut media_type, &mut media_type,
&mut is_cjs, &mut is_cjs,
)?)) )?))
@ -590,7 +662,7 @@ fn op_load_inner(
{ {
Some(Cow::Owned(load_from_node_modules( Some(Cow::Owned(load_from_node_modules(
specifier, specifier,
Some(npm.node_resolver.as_ref()), Some(npm),
&mut media_type, &mut media_type,
&mut is_cjs, &mut is_cjs,
)?)) )?))
@ -739,7 +811,13 @@ fn op_resolve_inner(
} }
} }
}; };
(specifier_str, media_type.as_ts_extension()) (
specifier_str,
match media_type {
MediaType::Css => ".js", // surface these as .js for typescript
media_type => media_type.as_ts_extension(),
},
)
} }
None => ( None => (
MISSING_DEPENDENCY_SPECIFIER.to_string(), MISSING_DEPENDENCY_SPECIFIER.to_string(),
@ -810,29 +888,27 @@ fn resolve_graph_specifier_types(
Some(referrer), Some(referrer),
NodeResolutionMode::Types, NodeResolutionMode::Types,
); );
let maybe_resolution = match res_result { let maybe_url = match res_result {
Ok(res) => Some(res), Ok(url) => Some(url),
Err(err) => match err.code() { Err(err) => match err.code() {
NodeJsErrorCode::ERR_TYPES_NOT_FOUND NodeJsErrorCode::ERR_TYPES_NOT_FOUND
| NodeJsErrorCode::ERR_MODULE_NOT_FOUND => None, | NodeJsErrorCode::ERR_MODULE_NOT_FOUND => None,
_ => return Err(err.into()), _ => return Err(err.into()),
}, },
}; };
Ok(Some(NodeResolution::into_specifier_and_media_type( Ok(Some(into_specifier_and_media_type(maybe_url)))
maybe_resolution,
)))
} else { } else {
Ok(None) Ok(None)
} }
} }
Some(Module::External(module)) => { Some(Module::External(module)) => {
// we currently only use "External" for when the module is in an npm package // we currently only use "External" for when the module is in an npm package
Ok(state.maybe_npm.as_ref().map(|npm| { Ok(state.maybe_npm.as_ref().map(|_| {
let specifier = let specifier = node::resolve_specifier_into_node_modules(
node::resolve_specifier_into_node_modules(&module.specifier); &module.specifier,
NodeResolution::into_specifier_and_media_type( &deno_fs::RealFs,
npm.node_resolver.url_to_node_resolution(specifier).ok(), );
) into_specifier_and_media_type(Some(specifier))
})) }))
} }
Some(Module::Node(_)) | None => Ok(None), Some(Module::Node(_)) | None => Ok(None),
@ -844,7 +920,7 @@ enum ResolveNonGraphSpecifierTypesError {
#[error(transparent)] #[error(transparent)]
ResolvePkgFolderFromDenoReq(#[from] ResolvePkgFolderFromDenoReqError), ResolvePkgFolderFromDenoReq(#[from] ResolvePkgFolderFromDenoReqError),
#[error(transparent)] #[error(transparent)]
ResolvePkgSubpathFromDenoModule(#[from] ResolvePkgSubpathFromDenoModuleError), PackageSubpathResolve(#[from] PackageSubpathResolveError),
} }
fn resolve_non_graph_specifier_types( fn resolve_non_graph_specifier_types(
@ -863,7 +939,7 @@ fn resolve_non_graph_specifier_types(
let node_resolver = &npm.node_resolver; let node_resolver = &npm.node_resolver;
if node_resolver.in_npm_package(referrer) { if node_resolver.in_npm_package(referrer) {
// we're in an npm package, so use node resolution // we're in an npm package, so use node resolution
Ok(Some(NodeResolution::into_specifier_and_media_type( Ok(Some(into_specifier_and_media_type(
node_resolver node_resolver
.resolve( .resolve(
raw_specifier, raw_specifier,
@ -871,7 +947,8 @@ fn resolve_non_graph_specifier_types(
referrer_kind, referrer_kind,
NodeResolutionMode::Types, NodeResolutionMode::Types,
) )
.ok(), .ok()
.map(|res| res.into_url()),
))) )))
} else if let Ok(npm_req_ref) = } else if let Ok(npm_req_ref) =
NpmPackageReqReference::from_str(raw_specifier) NpmPackageReqReference::from_str(raw_specifier)
@ -890,17 +967,15 @@ fn resolve_non_graph_specifier_types(
Some(referrer), Some(referrer),
NodeResolutionMode::Types, NodeResolutionMode::Types,
); );
let maybe_resolution = match res_result { let maybe_url = match res_result {
Ok(res) => Some(res), Ok(url) => Some(url),
Err(err) => match err.code() { Err(err) => match err.code() {
NodeJsErrorCode::ERR_TYPES_NOT_FOUND NodeJsErrorCode::ERR_TYPES_NOT_FOUND
| NodeJsErrorCode::ERR_MODULE_NOT_FOUND => None, | NodeJsErrorCode::ERR_MODULE_NOT_FOUND => None,
_ => return Err(err.into()), _ => return Err(err.into()),
}, },
}; };
Ok(Some(NodeResolution::into_specifier_and_media_type( Ok(Some(into_specifier_and_media_type(maybe_url)))
maybe_resolution,
)))
} else { } else {
Ok(None) Ok(None)
} }

View file

@ -64,7 +64,7 @@ fn extract_inner(
}) { }) {
Ok(parsed) => { Ok(parsed) => {
let mut c = ExportCollector::default(); let mut c = ExportCollector::default();
c.visit_program(parsed.program_ref()); c.visit_program(parsed.program().as_ref());
c c
} }
Err(_) => ExportCollector::default(), Err(_) => ExportCollector::default(),
@ -570,14 +570,14 @@ fn generate_pseudo_file(
})?; })?;
let top_level_atoms = swc_utils::collect_decls_with_ctxt::<Atom, _>( let top_level_atoms = swc_utils::collect_decls_with_ctxt::<Atom, _>(
parsed.program_ref(), &parsed.program_ref(),
parsed.top_level_context(), parsed.top_level_context(),
); );
let transformed = let transformed =
parsed parsed
.program_ref() .program_ref()
.clone() .to_owned()
.fold_with(&mut as_folder(Transform { .fold_with(&mut as_folder(Transform {
specifier: &file.specifier, specifier: &file.specifier,
base_file_specifier, base_file_specifier,
@ -1416,7 +1416,7 @@ console.log(Foo);
}) })
.unwrap(); .unwrap();
collector.visit_program(parsed.program_ref()); parsed.program_ref().visit_with(&mut collector);
collector collector
} }

View file

@ -65,6 +65,8 @@ pub fn init(maybe_level: Option<log::Level>) {
.filter_module("swc_ecma_parser", log::LevelFilter::Error) .filter_module("swc_ecma_parser", log::LevelFilter::Error)
// Suppress span lifecycle logs since they are too verbose // Suppress span lifecycle logs since they are too verbose
.filter_module("tracing::span", log::LevelFilter::Off) .filter_module("tracing::span", log::LevelFilter::Off)
// for deno_compile, this is too verbose
.filter_module("editpe", log::LevelFilter::Error)
.format(|buf, record| { .format(|buf, record| {
let mut target = record.target().to_string(); let mut target = record.target().to_string();
if let Some(line_no) = record.line() { if let Some(line_no) = record.line() {

View file

@ -42,21 +42,6 @@ pub fn get_extension(file_path: &Path) -> Option<String> {
.map(|e| e.to_lowercase()); .map(|e| e.to_lowercase());
} }
pub fn specifier_has_extension(
specifier: &ModuleSpecifier,
searching_ext: &str,
) -> bool {
let Some((_, ext)) = specifier.path().rsplit_once('.') else {
return false;
};
let searching_ext = searching_ext.strip_prefix('.').unwrap_or(searching_ext);
debug_assert!(!searching_ext.contains('.')); // exts like .d.ts are not implemented here
if ext.len() != searching_ext.len() {
return false;
}
ext.eq_ignore_ascii_case(searching_ext)
}
pub fn get_atomic_dir_path(file_path: &Path) -> PathBuf { pub fn get_atomic_dir_path(file_path: &Path) -> PathBuf {
let rand = gen_rand_path_component(); let rand = gen_rand_path_component();
let new_file_name = format!( let new_file_name = format!(
@ -350,18 +335,6 @@ mod test {
} }
} }
#[test]
fn test_specifier_has_extension() {
fn get(specifier: &str, ext: &str) -> bool {
specifier_has_extension(&ModuleSpecifier::parse(specifier).unwrap(), ext)
}
assert!(get("file:///a/b/c.ts", "ts"));
assert!(get("file:///a/b/c.ts", ".ts"));
assert!(!get("file:///a/b/c.ts", ".cts"));
assert!(get("file:///a/b/c.CtS", ".cts"));
}
#[test] #[test]
fn test_to_percent_decoded_str() { fn test_to_percent_decoded_str() {
let str = to_percent_decoded_str("%F0%9F%A6%95"); let str = to_percent_decoded_str("%F0%9F%A6%95");

View file

@ -97,6 +97,7 @@ fn find_source_map_range(code: &[u8]) -> Option<Range<usize>> {
} }
/// Converts an `Arc<str>` to an `Arc<[u8]>`. /// Converts an `Arc<str>` to an `Arc<[u8]>`.
#[allow(dead_code)]
pub fn arc_str_to_bytes(arc_str: Arc<str>) -> Arc<[u8]> { pub fn arc_str_to_bytes(arc_str: Arc<str>) -> Arc<[u8]> {
let raw = Arc::into_raw(arc_str); let raw = Arc::into_raw(arc_str);
// SAFETY: This is safe because they have the same memory layout. // SAFETY: This is safe because they have the same memory layout.

View file

@ -14,16 +14,17 @@ use deno_core::v8;
use deno_core::CompiledWasmModuleStore; use deno_core::CompiledWasmModuleStore;
use deno_core::Extension; use deno_core::Extension;
use deno_core::FeatureChecker; use deno_core::FeatureChecker;
use deno_core::ModuleId;
use deno_core::ModuleLoader; use deno_core::ModuleLoader;
use deno_core::PollEventLoopOptions; use deno_core::PollEventLoopOptions;
use deno_core::SharedArrayBufferStore; use deno_core::SharedArrayBufferStore;
use deno_runtime::code_cache; use deno_runtime::code_cache;
use deno_runtime::deno_broadcast_channel::InMemoryBroadcastChannel; use deno_runtime::deno_broadcast_channel::InMemoryBroadcastChannel;
use deno_runtime::deno_fs; use deno_runtime::deno_fs;
use deno_runtime::deno_node;
use deno_runtime::deno_node::NodeExtInitServices; use deno_runtime::deno_node::NodeExtInitServices;
use deno_runtime::deno_node::NodeRequireLoader;
use deno_runtime::deno_node::NodeRequireLoaderRc;
use deno_runtime::deno_node::NodeResolver; use deno_runtime::deno_node::NodeResolver;
use deno_runtime::deno_node::PackageJsonResolver;
use deno_runtime::deno_permissions::PermissionsContainer; use deno_runtime::deno_permissions::PermissionsContainer;
use deno_runtime::deno_tls::RootCertStoreProvider; use deno_runtime::deno_tls::RootCertStoreProvider;
use deno_runtime::deno_web::BlobStore; use deno_runtime::deno_web::BlobStore;
@ -42,7 +43,6 @@ use deno_runtime::WorkerExecutionMode;
use deno_runtime::WorkerLogLevel; use deno_runtime::WorkerLogLevel;
use deno_semver::npm::NpmPackageReqReference; use deno_semver::npm::NpmPackageReqReference;
use deno_terminal::colors; use deno_terminal::colors;
use node_resolver::NodeResolution;
use node_resolver::NodeResolutionMode; use node_resolver::NodeResolutionMode;
use tokio::select; use tokio::select;
@ -51,28 +51,27 @@ use crate::args::DenoSubcommand;
use crate::args::StorageKeyResolver; use crate::args::StorageKeyResolver;
use crate::errors; use crate::errors;
use crate::npm::CliNpmResolver; use crate::npm::CliNpmResolver;
use crate::resolver::CjsResolutionStore;
use crate::util::checksum; use crate::util::checksum;
use crate::util::file_watcher::WatcherCommunicator; use crate::util::file_watcher::WatcherCommunicator;
use crate::util::file_watcher::WatcherRestartMode; use crate::util::file_watcher::WatcherRestartMode;
use crate::util::path::specifier_has_extension;
use crate::version; use crate::version;
pub struct ModuleLoaderAndSourceMapGetter { pub struct CreateModuleLoaderResult {
pub module_loader: Rc<dyn ModuleLoader>, pub module_loader: Rc<dyn ModuleLoader>,
pub node_require_loader: Rc<dyn NodeRequireLoader>,
} }
pub trait ModuleLoaderFactory: Send + Sync { pub trait ModuleLoaderFactory: Send + Sync {
fn create_for_main( fn create_for_main(
&self, &self,
root_permissions: PermissionsContainer, root_permissions: PermissionsContainer,
) -> ModuleLoaderAndSourceMapGetter; ) -> CreateModuleLoaderResult;
fn create_for_worker( fn create_for_worker(
&self, &self,
parent_permissions: PermissionsContainer, parent_permissions: PermissionsContainer,
permissions: PermissionsContainer, permissions: PermissionsContainer,
) -> ModuleLoaderAndSourceMapGetter; ) -> CreateModuleLoaderResult;
} }
#[async_trait::async_trait(?Send)] #[async_trait::async_trait(?Send)]
@ -109,7 +108,6 @@ pub struct CliMainWorkerOptions {
pub inspect_wait: bool, pub inspect_wait: bool,
pub strace_ops: Option<Vec<String>>, pub strace_ops: Option<Vec<String>>,
pub is_inspecting: bool, pub is_inspecting: bool,
pub is_npm_main: bool,
pub location: Option<Url>, pub location: Option<Url>,
pub argv0: Option<String>, pub argv0: Option<String>,
pub node_debug: Option<String>, pub node_debug: Option<String>,
@ -122,13 +120,11 @@ pub struct CliMainWorkerOptions {
pub node_ipc: Option<i64>, pub node_ipc: Option<i64>,
pub serve_port: Option<u16>, pub serve_port: Option<u16>,
pub serve_host: Option<String>, pub serve_host: Option<String>,
pub unstable_detect_cjs: bool,
} }
struct SharedWorkerState { struct SharedWorkerState {
blob_store: Arc<BlobStore>, blob_store: Arc<BlobStore>,
broadcast_channel: InMemoryBroadcastChannel, broadcast_channel: InMemoryBroadcastChannel,
cjs_resolution_store: Arc<CjsResolutionStore>,
code_cache: Option<Arc<dyn code_cache::CodeCache>>, code_cache: Option<Arc<dyn code_cache::CodeCache>>,
compiled_wasm_module_store: CompiledWasmModuleStore, compiled_wasm_module_store: CompiledWasmModuleStore,
feature_checker: Arc<FeatureChecker>, feature_checker: Arc<FeatureChecker>,
@ -139,6 +135,7 @@ struct SharedWorkerState {
module_loader_factory: Box<dyn ModuleLoaderFactory>, module_loader_factory: Box<dyn ModuleLoaderFactory>,
node_resolver: Arc<NodeResolver>, node_resolver: Arc<NodeResolver>,
npm_resolver: Arc<dyn CliNpmResolver>, npm_resolver: Arc<dyn CliNpmResolver>,
pkg_json_resolver: Arc<PackageJsonResolver>,
root_cert_store_provider: Arc<dyn RootCertStoreProvider>, root_cert_store_provider: Arc<dyn RootCertStoreProvider>,
root_permissions: PermissionsContainer, root_permissions: PermissionsContainer,
shared_array_buffer_store: SharedArrayBufferStore, shared_array_buffer_store: SharedArrayBufferStore,
@ -148,11 +145,15 @@ struct SharedWorkerState {
} }
impl SharedWorkerState { impl SharedWorkerState {
pub fn create_node_init_services(&self) -> NodeExtInitServices { pub fn create_node_init_services(
&self,
node_require_loader: NodeRequireLoaderRc,
) -> NodeExtInitServices {
NodeExtInitServices { NodeExtInitServices {
node_require_resolver: self.npm_resolver.clone().into_require_resolver(), node_require_loader,
node_resolver: self.node_resolver.clone(), node_resolver: self.node_resolver.clone(),
npm_resolver: self.npm_resolver.clone().into_npm_resolver(), npm_resolver: self.npm_resolver.clone().into_npm_resolver(),
pkg_json_resolver: self.pkg_json_resolver.clone(),
} }
} }
@ -163,7 +164,6 @@ impl SharedWorkerState {
pub struct CliMainWorker { pub struct CliMainWorker {
main_module: ModuleSpecifier, main_module: ModuleSpecifier,
is_main_cjs: bool,
worker: MainWorker, worker: MainWorker,
shared: Arc<SharedWorkerState>, shared: Arc<SharedWorkerState>,
} }
@ -185,17 +185,7 @@ impl CliMainWorker {
log::debug!("main_module {}", self.main_module); log::debug!("main_module {}", self.main_module);
if self.is_main_cjs { self.execute_main_module().await?;
deno_node::load_cjs_module(
&mut self.worker.js_runtime,
&self.main_module.to_file_path().unwrap().to_string_lossy(),
true,
self.shared.options.inspect_brk,
)?;
} else {
self.execute_main_module_possibly_with_npm().await?;
}
self.worker.dispatch_load_event()?; self.worker.dispatch_load_event()?;
loop { loop {
@ -283,22 +273,7 @@ impl CliMainWorker {
/// Execute the given main module emitting load and unload events before and after execution /// Execute the given main module emitting load and unload events before and after execution
/// respectively. /// respectively.
pub async fn execute(&mut self) -> Result<(), AnyError> { pub async fn execute(&mut self) -> Result<(), AnyError> {
if self.inner.is_main_cjs { self.inner.execute_main_module().await?;
deno_node::load_cjs_module(
&mut self.inner.worker.js_runtime,
&self
.inner
.main_module
.to_file_path()
.unwrap()
.to_string_lossy(),
true,
self.inner.shared.options.inspect_brk,
)?;
} else {
self.inner.execute_main_module_possibly_with_npm().await?;
}
self.inner.worker.dispatch_load_event()?; self.inner.worker.dispatch_load_event()?;
self.pending_unload = true; self.pending_unload = true;
@ -339,24 +314,13 @@ impl CliMainWorker {
executor.execute().await executor.execute().await
} }
pub async fn execute_main_module_possibly_with_npm( pub async fn execute_main_module(&mut self) -> Result<(), AnyError> {
&mut self,
) -> Result<(), AnyError> {
let id = self.worker.preload_main_module(&self.main_module).await?; let id = self.worker.preload_main_module(&self.main_module).await?;
self.evaluate_module_possibly_with_npm(id).await self.worker.evaluate_module(id).await
} }
pub async fn execute_side_module_possibly_with_npm( pub async fn execute_side_module(&mut self) -> Result<(), AnyError> {
&mut self,
) -> Result<(), AnyError> {
let id = self.worker.preload_side_module(&self.main_module).await?; let id = self.worker.preload_side_module(&self.main_module).await?;
self.evaluate_module_possibly_with_npm(id).await
}
async fn evaluate_module_possibly_with_npm(
&mut self,
id: ModuleId,
) -> Result<(), AnyError> {
self.worker.evaluate_module(id).await self.worker.evaluate_module(id).await
} }
@ -426,7 +390,6 @@ impl CliMainWorkerFactory {
#[allow(clippy::too_many_arguments)] #[allow(clippy::too_many_arguments)]
pub fn new( pub fn new(
blob_store: Arc<BlobStore>, blob_store: Arc<BlobStore>,
cjs_resolution_store: Arc<CjsResolutionStore>,
code_cache: Option<Arc<dyn code_cache::CodeCache>>, code_cache: Option<Arc<dyn code_cache::CodeCache>>,
feature_checker: Arc<FeatureChecker>, feature_checker: Arc<FeatureChecker>,
fs: Arc<dyn deno_fs::FileSystem>, fs: Arc<dyn deno_fs::FileSystem>,
@ -436,6 +399,7 @@ impl CliMainWorkerFactory {
module_loader_factory: Box<dyn ModuleLoaderFactory>, module_loader_factory: Box<dyn ModuleLoaderFactory>,
node_resolver: Arc<NodeResolver>, node_resolver: Arc<NodeResolver>,
npm_resolver: Arc<dyn CliNpmResolver>, npm_resolver: Arc<dyn CliNpmResolver>,
pkg_json_resolver: Arc<PackageJsonResolver>,
root_cert_store_provider: Arc<dyn RootCertStoreProvider>, root_cert_store_provider: Arc<dyn RootCertStoreProvider>,
root_permissions: PermissionsContainer, root_permissions: PermissionsContainer,
storage_key_resolver: StorageKeyResolver, storage_key_resolver: StorageKeyResolver,
@ -446,7 +410,6 @@ impl CliMainWorkerFactory {
shared: Arc::new(SharedWorkerState { shared: Arc::new(SharedWorkerState {
blob_store, blob_store,
broadcast_channel: Default::default(), broadcast_channel: Default::default(),
cjs_resolution_store,
code_cache, code_cache,
compiled_wasm_module_store: Default::default(), compiled_wasm_module_store: Default::default(),
feature_checker, feature_checker,
@ -457,6 +420,7 @@ impl CliMainWorkerFactory {
module_loader_factory, module_loader_factory,
node_resolver, node_resolver,
npm_resolver, npm_resolver,
pkg_json_resolver,
root_cert_store_provider, root_cert_store_provider,
root_permissions, root_permissions,
shared_array_buffer_store: Default::default(), shared_array_buffer_store: Default::default(),
@ -492,10 +456,13 @@ impl CliMainWorkerFactory {
stdio: deno_runtime::deno_io::Stdio, stdio: deno_runtime::deno_io::Stdio,
) -> Result<CliMainWorker, AnyError> { ) -> Result<CliMainWorker, AnyError> {
let shared = &self.shared; let shared = &self.shared;
let ModuleLoaderAndSourceMapGetter { module_loader } = shared let CreateModuleLoaderResult {
module_loader,
node_require_loader,
} = shared
.module_loader_factory .module_loader_factory
.create_for_main(permissions.clone()); .create_for_main(permissions.clone());
let (main_module, is_main_cjs) = if let Ok(package_ref) = let main_module = if let Ok(package_ref) =
NpmPackageReqReference::from_specifier(&main_module) NpmPackageReqReference::from_specifier(&main_module)
{ {
if let Some(npm_resolver) = shared.npm_resolver.as_managed() { if let Some(npm_resolver) = shared.npm_resolver.as_managed() {
@ -515,9 +482,8 @@ impl CliMainWorkerFactory {
package_ref.req(), package_ref.req(),
&referrer, &referrer,
)?; )?;
let node_resolution = self let main_module = self
.resolve_binary_entrypoint(&package_folder, package_ref.sub_path())?; .resolve_binary_entrypoint(&package_folder, package_ref.sub_path())?;
let is_main_cjs = matches!(node_resolution, NodeResolution::CommonJs(_));
if let Some(lockfile) = &shared.maybe_lockfile { if let Some(lockfile) = &shared.maybe_lockfile {
// For npm binary commands, ensure that the lockfile gets updated // For npm binary commands, ensure that the lockfile gets updated
@ -526,36 +492,9 @@ impl CliMainWorkerFactory {
lockfile.write_if_changed()?; lockfile.write_if_changed()?;
} }
(node_resolution.into_url(), is_main_cjs) main_module
} else if shared.options.is_npm_main
|| shared.node_resolver.in_npm_package(&main_module)
{
let node_resolution =
shared.node_resolver.url_to_node_resolution(main_module)?;
let is_main_cjs = matches!(node_resolution, NodeResolution::CommonJs(_));
(node_resolution.into_url(), is_main_cjs)
} else { } else {
let is_maybe_cjs_js_ext = self.shared.options.unstable_detect_cjs main_module
&& 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 {
main_module.scheme() == "file"
&& specifier_has_extension(&main_module, "cjs")
};
(main_module, is_cjs)
}; };
let maybe_inspector_server = shared.maybe_inspector_server.clone(); let maybe_inspector_server = shared.maybe_inspector_server.clone();
@ -597,7 +536,9 @@ impl CliMainWorkerFactory {
root_cert_store_provider: Some(shared.root_cert_store_provider.clone()), root_cert_store_provider: Some(shared.root_cert_store_provider.clone()),
module_loader, module_loader,
fs: shared.fs.clone(), fs: shared.fs.clone(),
node_services: Some(shared.create_node_init_services()), node_services: Some(
shared.create_node_init_services(node_require_loader),
),
npm_process_state_provider: Some(shared.npm_process_state_provider()), npm_process_state_provider: Some(shared.npm_process_state_provider()),
blob_store: shared.blob_store.clone(), blob_store: shared.blob_store.clone(),
broadcast_channel: shared.broadcast_channel.clone(), broadcast_channel: shared.broadcast_channel.clone(),
@ -682,7 +623,6 @@ impl CliMainWorkerFactory {
Ok(CliMainWorker { Ok(CliMainWorker {
main_module, main_module,
is_main_cjs,
worker, worker,
shared: shared.clone(), shared: shared.clone(),
}) })
@ -692,19 +632,19 @@ impl CliMainWorkerFactory {
&self, &self,
package_folder: &Path, package_folder: &Path,
sub_path: Option<&str>, sub_path: Option<&str>,
) -> Result<NodeResolution, AnyError> { ) -> Result<ModuleSpecifier, AnyError> {
match self match self
.shared .shared
.node_resolver .node_resolver
.resolve_binary_export(package_folder, sub_path) .resolve_binary_export(package_folder, sub_path)
{ {
Ok(node_resolution) => Ok(node_resolution), Ok(specifier) => Ok(specifier),
Err(original_err) => { Err(original_err) => {
// if the binary entrypoint was not found, fallback to regular node resolution // if the binary entrypoint was not found, fallback to regular node resolution
let result = let result =
self.resolve_binary_entrypoint_fallback(package_folder, sub_path); self.resolve_binary_entrypoint_fallback(package_folder, sub_path);
match result { match result {
Ok(Some(resolution)) => Ok(resolution), Ok(Some(specifier)) => Ok(specifier),
Ok(None) => Err(original_err.into()), Ok(None) => Err(original_err.into()),
Err(fallback_err) => { Err(fallback_err) => {
bail!("{:#}\n\nFallback failed: {:#}", original_err, fallback_err) bail!("{:#}\n\nFallback failed: {:#}", original_err, fallback_err)
@ -719,7 +659,7 @@ impl CliMainWorkerFactory {
&self, &self,
package_folder: &Path, package_folder: &Path,
sub_path: Option<&str>, sub_path: Option<&str>,
) -> Result<Option<NodeResolution>, AnyError> { ) -> Result<Option<ModuleSpecifier>, AnyError> {
// only fallback if the user specified a sub path // only fallback if the user specified a sub path
if sub_path.is_none() { if sub_path.is_none() {
// it's confusing to users if the package doesn't have any binary // it's confusing to users if the package doesn't have any binary
@ -728,7 +668,7 @@ impl CliMainWorkerFactory {
return Ok(None); return Ok(None);
} }
let resolution = self let specifier = self
.shared .shared
.node_resolver .node_resolver
.resolve_package_subpath_from_deno_module( .resolve_package_subpath_from_deno_module(
@ -737,22 +677,17 @@ impl CliMainWorkerFactory {
/* referrer */ None, /* referrer */ None,
NodeResolutionMode::Execution, NodeResolutionMode::Execution,
)?; )?;
match &resolution {
NodeResolution::BuiltIn(_) => Ok(None),
NodeResolution::CommonJs(specifier) | NodeResolution::Esm(specifier) => {
if specifier if specifier
.to_file_path() .to_file_path()
.map(|p| p.exists()) .map(|p| p.exists())
.unwrap_or(false) .unwrap_or(false)
{ {
Ok(Some(resolution)) Ok(Some(specifier))
} else { } else {
bail!("Cannot find module '{}'", specifier) bail!("Cannot find module '{}'", specifier)
} }
} }
} }
}
}
fn create_web_worker_callback( fn create_web_worker_callback(
shared: Arc<SharedWorkerState>, shared: Arc<SharedWorkerState>,
@ -761,8 +696,10 @@ fn create_web_worker_callback(
Arc::new(move |args| { Arc::new(move |args| {
let maybe_inspector_server = shared.maybe_inspector_server.clone(); let maybe_inspector_server = shared.maybe_inspector_server.clone();
let ModuleLoaderAndSourceMapGetter { module_loader } = let CreateModuleLoaderResult {
shared.module_loader_factory.create_for_worker( module_loader,
node_require_loader,
} = shared.module_loader_factory.create_for_worker(
args.parent_permissions.clone(), args.parent_permissions.clone(),
args.permissions.clone(), args.permissions.clone(),
); );
@ -795,7 +732,9 @@ fn create_web_worker_callback(
root_cert_store_provider: Some(shared.root_cert_store_provider.clone()), root_cert_store_provider: Some(shared.root_cert_store_provider.clone()),
module_loader, module_loader,
fs: shared.fs.clone(), fs: shared.fs.clone(),
node_services: Some(shared.create_node_init_services()), node_services: Some(
shared.create_node_init_services(node_require_loader),
),
blob_store: shared.blob_store.clone(), blob_store: shared.blob_store.clone(),
broadcast_channel: shared.broadcast_channel.clone(), broadcast_channel: shared.broadcast_channel.clone(),
shared_array_buffer_store: Some(shared.shared_array_buffer_store.clone()), shared_array_buffer_store: Some(shared.shared_array_buffer_store.clone()),

View file

@ -9,15 +9,11 @@ use std::path::Path;
use std::path::PathBuf; use std::path::PathBuf;
use deno_core::error::AnyError; use deno_core::error::AnyError;
use deno_core::located_script_name;
use deno_core::op2; use deno_core::op2;
use deno_core::url::Url; use deno_core::url::Url;
#[allow(unused_imports)] #[allow(unused_imports)]
use deno_core::v8; use deno_core::v8;
use deno_core::v8::ExternalReference; use deno_core::v8::ExternalReference;
use deno_core::JsRuntime;
use deno_fs::sync::MaybeSend;
use deno_fs::sync::MaybeSync;
use node_resolver::NpmResolverRc; use node_resolver::NpmResolverRc;
use once_cell::sync::Lazy; use once_cell::sync::Lazy;
@ -125,16 +121,17 @@ impl NodePermissions for deno_permissions::PermissionsContainer {
} }
#[allow(clippy::disallowed_types)] #[allow(clippy::disallowed_types)]
pub type NodeRequireResolverRc = pub type NodeRequireLoaderRc = std::rc::Rc<dyn NodeRequireLoader>;
deno_fs::sync::MaybeArc<dyn NodeRequireResolver>;
pub trait NodeRequireResolver: std::fmt::Debug + MaybeSend + MaybeSync { pub trait NodeRequireLoader {
#[must_use = "the resolved return value to mitigate time-of-check to time-of-use issues"] #[must_use = "the resolved return value to mitigate time-of-check to time-of-use issues"]
fn ensure_read_permission<'a>( fn ensure_read_permission<'a>(
&self, &self,
permissions: &mut dyn NodePermissions, permissions: &mut dyn NodePermissions,
path: &'a Path, path: &'a Path,
) -> Result<Cow<'a, Path>, AnyError>; ) -> Result<Cow<'a, Path>, AnyError>;
fn load_text_file_lossy(&self, path: &Path) -> Result<String, AnyError>;
} }
pub static NODE_ENV_VAR_ALLOWLIST: Lazy<HashSet<String>> = Lazy::new(|| { pub static NODE_ENV_VAR_ALLOWLIST: Lazy<HashSet<String>> = Lazy::new(|| {
@ -152,10 +149,12 @@ fn op_node_build_os() -> String {
env!("TARGET").split('-').nth(2).unwrap().to_string() env!("TARGET").split('-').nth(2).unwrap().to_string()
} }
#[derive(Clone)]
pub struct NodeExtInitServices { pub struct NodeExtInitServices {
pub node_require_resolver: NodeRequireResolverRc, pub node_require_loader: NodeRequireLoaderRc,
pub node_resolver: NodeResolverRc, pub node_resolver: NodeResolverRc,
pub npm_resolver: NpmResolverRc, pub npm_resolver: NpmResolverRc,
pub pkg_json_resolver: PackageJsonResolverRc,
} }
deno_core::extension!(deno_node, deno_core::extension!(deno_node,
@ -639,9 +638,10 @@ deno_core::extension!(deno_node,
state.put(options.fs.clone()); state.put(options.fs.clone());
if let Some(init) = &options.maybe_init { if let Some(init) = &options.maybe_init {
state.put(init.node_require_resolver.clone()); state.put(init.node_require_loader.clone());
state.put(init.node_resolver.clone()); state.put(init.node_resolver.clone());
state.put(init.npm_resolver.clone()); state.put(init.npm_resolver.clone());
state.put(init.pkg_json_resolver.clone());
} }
}, },
global_template_middleware = global_template_middleware, global_template_middleware = global_template_middleware,
@ -761,33 +761,16 @@ deno_core::extension!(deno_node,
}, },
); );
pub fn load_cjs_module(
js_runtime: &mut JsRuntime,
module: &str,
main: bool,
inspect_brk: bool,
) -> Result<(), AnyError> {
fn escape_for_single_quote_string(text: &str) -> String {
text.replace('\\', r"\\").replace('\'', r"\'")
}
let source_code = format!(
r#"(function loadCjsModule(moduleName, isMain, inspectBrk) {{
Deno[Deno.internal].node.loadCjsModule(moduleName, isMain, inspectBrk);
}})('{module}', {main}, {inspect_brk});"#,
main = main,
module = escape_for_single_quote_string(module),
inspect_brk = inspect_brk,
);
js_runtime.execute_script(located_script_name!(), source_code)?;
Ok(())
}
pub type NodeResolver = node_resolver::NodeResolver<DenoFsNodeResolverEnv>; pub type NodeResolver = node_resolver::NodeResolver<DenoFsNodeResolverEnv>;
#[allow(clippy::disallowed_types)] #[allow(clippy::disallowed_types)]
pub type NodeResolverRc = pub type NodeResolverRc =
deno_fs::sync::MaybeArc<node_resolver::NodeResolver<DenoFsNodeResolverEnv>>; deno_fs::sync::MaybeArc<node_resolver::NodeResolver<DenoFsNodeResolverEnv>>;
pub type PackageJsonResolver =
node_resolver::PackageJsonResolver<DenoFsNodeResolverEnv>;
#[allow(clippy::disallowed_types)]
pub type PackageJsonResolverRc = deno_fs::sync::MaybeArc<
node_resolver::PackageJsonResolver<DenoFsNodeResolverEnv>,
>;
#[derive(Debug)] #[derive(Debug)]
pub struct DenoFsNodeResolverEnv { pub struct DenoFsNodeResolverEnv {

View file

@ -9,6 +9,7 @@ use deno_core::OpState;
use deno_fs::FileSystemRc; use deno_fs::FileSystemRc;
use deno_package_json::PackageJsonRc; use deno_package_json::PackageJsonRc;
use deno_path_util::normalize_path; use deno_path_util::normalize_path;
use deno_path_util::url_to_file_path;
use node_resolver::NodeModuleKind; use node_resolver::NodeModuleKind;
use node_resolver::NodeResolutionMode; use node_resolver::NodeResolutionMode;
use node_resolver::REQUIRE_CONDITIONS; use node_resolver::REQUIRE_CONDITIONS;
@ -19,9 +20,10 @@ use std::path::PathBuf;
use std::rc::Rc; use std::rc::Rc;
use crate::NodePermissions; use crate::NodePermissions;
use crate::NodeRequireResolverRc; use crate::NodeRequireLoaderRc;
use crate::NodeResolverRc; use crate::NodeResolverRc;
use crate::NpmResolverRc; use crate::NpmResolverRc;
use crate::PackageJsonResolverRc;
#[must_use = "the resolved return value to mitigate time-of-check to time-of-use issues"] #[must_use = "the resolved return value to mitigate time-of-check to time-of-use issues"]
fn ensure_read_permission<'a, P>( fn ensure_read_permission<'a, P>(
@ -31,9 +33,9 @@ fn ensure_read_permission<'a, P>(
where where
P: NodePermissions + 'static, P: NodePermissions + 'static,
{ {
let resolver = state.borrow::<NodeRequireResolverRc>().clone(); let loader = state.borrow::<NodeRequireLoaderRc>().clone();
let permissions = state.borrow_mut::<P>(); let permissions = state.borrow_mut::<P>();
resolver.ensure_read_permission(permissions, file_path) loader.ensure_read_permission(permissions, file_path)
} }
#[derive(Debug, thiserror::Error)] #[derive(Debug, thiserror::Error)]
@ -54,10 +56,14 @@ pub enum RequireError {
PackageImportsResolve( PackageImportsResolve(
#[from] node_resolver::errors::PackageImportsResolveError, #[from] node_resolver::errors::PackageImportsResolveError,
), ),
#[error("failed to convert '{0}' to file path")] #[error(transparent)]
FilePathConversion(Url), FilePathConversion(#[from] deno_path_util::UrlToFilePathError),
#[error(transparent)]
UrlConversion(#[from] deno_path_util::PathToUrlError),
#[error(transparent)] #[error(transparent)]
Fs(#[from] deno_io::fs::FsError), Fs(#[from] deno_io::fs::FsError),
#[error(transparent)]
ReadModule(deno_core::error::AnyError),
#[error("Unable to get CWD: {0}")] #[error("Unable to get CWD: {0}")]
UnableToGetCwd(deno_io::fs::FsError), UnableToGetCwd(deno_io::fs::FsError),
} }
@ -229,8 +235,11 @@ pub fn op_require_is_deno_dir_package(
state: &mut OpState, state: &mut OpState,
#[string] path: String, #[string] path: String,
) -> bool { ) -> bool {
let resolver = state.borrow::<NpmResolverRc>(); let resolver = state.borrow::<NodeResolverRc>();
resolver.in_npm_package_at_file_path(&PathBuf::from(path)) match deno_path_util::url_from_file_path(&PathBuf::from(path)) {
Ok(specifier) => resolver.in_npm_package(&specifier),
Err(_) => false,
}
} }
#[op2] #[op2]
@ -411,8 +420,8 @@ where
return Ok(None); return Ok(None);
} }
let node_resolver = state.borrow::<NodeResolverRc>(); let pkg_json_resolver = state.borrow::<PackageJsonResolverRc>();
let pkg = node_resolver let pkg = pkg_json_resolver
.get_closest_package_json_from_path(&PathBuf::from(parent_path.unwrap())) .get_closest_package_json_from_path(&PathBuf::from(parent_path.unwrap()))
.ok() .ok()
.flatten(); .flatten();
@ -441,6 +450,7 @@ where
let referrer = deno_core::url::Url::from_file_path(&pkg.path).unwrap(); let referrer = deno_core::url::Url::from_file_path(&pkg.path).unwrap();
if let Some(exports) = &pkg.exports { if let Some(exports) = &pkg.exports {
let node_resolver = state.borrow::<NodeResolverRc>();
let r = node_resolver.package_exports_resolve( let r = node_resolver.package_exports_resolve(
&pkg.path, &pkg.path,
&expansion, &expansion,
@ -470,10 +480,13 @@ where
P: NodePermissions + 'static, P: NodePermissions + 'static,
{ {
let file_path = PathBuf::from(file_path); let file_path = PathBuf::from(file_path);
// todo(dsherret): there's multiple borrows to NodeRequireLoaderRc here
let file_path = ensure_read_permission::<P>(state, &file_path) let file_path = ensure_read_permission::<P>(state, &file_path)
.map_err(RequireError::Permission)?; .map_err(RequireError::Permission)?;
let fs = state.borrow::<FileSystemRc>(); let loader = state.borrow::<NodeRequireLoaderRc>();
Ok(fs.read_text_file_lossy_sync(&file_path, None)?) loader
.load_text_file_lossy(&file_path)
.map_err(RequireError::ReadModule)
} }
#[op2] #[op2]
@ -503,11 +516,12 @@ where
P: NodePermissions + 'static, P: NodePermissions + 'static,
{ {
let fs = state.borrow::<FileSystemRc>(); let fs = state.borrow::<FileSystemRc>();
let npm_resolver = state.borrow::<NpmResolverRc>();
let node_resolver = state.borrow::<NodeResolverRc>(); let node_resolver = state.borrow::<NodeResolverRc>();
let pkg_json_resolver = state.borrow::<PackageJsonResolverRc>();
let modules_path = PathBuf::from(&modules_path_str); let modules_path = PathBuf::from(&modules_path_str);
let pkg_path = if npm_resolver.in_npm_package_at_file_path(&modules_path) let modules_specifier = deno_path_util::url_from_file_path(&modules_path)?;
let pkg_path = if node_resolver.in_npm_package(&modules_specifier)
&& !uses_local_node_modules_dir && !uses_local_node_modules_dir
{ {
modules_path modules_path
@ -521,7 +535,7 @@ where
} }
}; };
let Some(pkg) = let Some(pkg) =
node_resolver.load_package_json(&pkg_path.join("package.json"))? pkg_json_resolver.load_package_json(&pkg_path.join("package.json"))?
else { else {
return Ok(None); return Ok(None);
}; };
@ -561,8 +575,8 @@ where
{ {
let filename = PathBuf::from(filename); let filename = PathBuf::from(filename);
// permissions: allow reading the closest package.json files // permissions: allow reading the closest package.json files
let node_resolver = state.borrow::<NodeResolverRc>().clone(); let pkg_json_resolver = state.borrow::<PackageJsonResolverRc>();
node_resolver.get_closest_package_json_from_path(&filename) pkg_json_resolver.get_closest_package_json_from_path(&filename)
} }
#[op2] #[op2]
@ -574,13 +588,13 @@ pub fn op_require_read_package_scope<P>(
where where
P: NodePermissions + 'static, P: NodePermissions + 'static,
{ {
let node_resolver = state.borrow::<NodeResolverRc>().clone(); let pkg_json_resolver = state.borrow::<PackageJsonResolverRc>();
let package_json_path = PathBuf::from(package_json_path); let package_json_path = PathBuf::from(package_json_path);
if package_json_path.file_name() != Some("package.json".as_ref()) { if package_json_path.file_name() != Some("package.json".as_ref()) {
// permissions: do not allow reading a non-package.json file // permissions: do not allow reading a non-package.json file
return None; return None;
} }
node_resolver pkg_json_resolver
.load_package_json(&package_json_path) .load_package_json(&package_json_path)
.ok() .ok()
.flatten() .flatten()
@ -599,14 +613,15 @@ where
let referrer_path = PathBuf::from(&referrer_filename); let referrer_path = PathBuf::from(&referrer_filename);
let referrer_path = ensure_read_permission::<P>(state, &referrer_path) let referrer_path = ensure_read_permission::<P>(state, &referrer_path)
.map_err(RequireError::Permission)?; .map_err(RequireError::Permission)?;
let node_resolver = state.borrow::<NodeResolverRc>(); let pkg_json_resolver = state.borrow::<PackageJsonResolverRc>();
let Some(pkg) = let Some(pkg) =
node_resolver.get_closest_package_json_from_path(&referrer_path)? pkg_json_resolver.get_closest_package_json_from_path(&referrer_path)?
else { else {
return Ok(None); return Ok(None);
}; };
if pkg.imports.is_some() { if pkg.imports.is_some() {
let node_resolver = state.borrow::<NodeResolverRc>();
let referrer_url = Url::from_file_path(&referrer_filename).unwrap(); let referrer_url = Url::from_file_path(&referrer_filename).unwrap();
let url = node_resolver.package_imports_resolve( let url = node_resolver.package_imports_resolve(
&request, &request,
@ -637,13 +652,6 @@ fn url_to_file_path_string(url: &Url) -> Result<String, RequireError> {
Ok(file_path.to_string_lossy().into_owned()) Ok(file_path.to_string_lossy().into_owned())
} }
fn url_to_file_path(url: &Url) -> Result<PathBuf, RequireError> {
match url.to_file_path() {
Ok(file_path) => Ok(file_path),
Err(()) => Err(RequireError::FilePathConversion(url.clone())),
}
}
#[op2(fast)] #[op2(fast)]
pub fn op_require_can_parse_as_esm( pub fn op_require_can_parse_as_esm(
scope: &mut v8::HandleScope, scope: &mut v8::HandleScope,

View file

@ -4,14 +4,12 @@ use deno_core::op2;
use deno_core::url::Url; use deno_core::url::Url;
use deno_core::OpState; use deno_core::OpState;
use deno_fs::FileSystemRc; use deno_fs::FileSystemRc;
use node_resolver::NodeResolution;
use std::borrow::Cow; use std::borrow::Cow;
use std::path::Path; use std::path::Path;
use std::path::PathBuf; use std::path::PathBuf;
use crate::NodePermissions; use crate::NodePermissions;
use crate::NodeRequireResolverRc; use crate::NodeRequireLoaderRc;
use crate::NodeResolverRc;
#[must_use = "the resolved return value to mitigate time-of-check to time-of-use issues"] #[must_use = "the resolved return value to mitigate time-of-check to time-of-use issues"]
fn ensure_read_permission<'a, P>( fn ensure_read_permission<'a, P>(
@ -21,9 +19,9 @@ fn ensure_read_permission<'a, P>(
where where
P: NodePermissions + 'static, P: NodePermissions + 'static,
{ {
let resolver = state.borrow::<NodeRequireResolverRc>().clone(); let loader = state.borrow::<NodeRequireLoaderRc>().clone();
let permissions = state.borrow_mut::<P>(); let permissions = state.borrow_mut::<P>();
resolver.ensure_read_permission(permissions, file_path) loader.ensure_read_permission(permissions, file_path)
} }
#[derive(Debug, thiserror::Error)] #[derive(Debug, thiserror::Error)]
@ -42,14 +40,11 @@ pub enum WorkerThreadsFilenameError {
UrlToPath, UrlToPath,
#[error("File not found [{0:?}]")] #[error("File not found [{0:?}]")]
FileNotFound(PathBuf), FileNotFound(PathBuf),
#[error("Neither ESM nor CJS")]
NeitherEsmNorCjs,
#[error("{0}")]
UrlToNodeResolution(node_resolver::errors::UrlToNodeResolutionError),
#[error(transparent)] #[error(transparent)]
Fs(#[from] deno_io::fs::FsError), Fs(#[from] deno_io::fs::FsError),
} }
// todo(dsherret): we should remove this and do all this work inside op_create_worker
#[op2] #[op2]
#[string] #[string]
pub fn op_worker_threads_filename<P>( pub fn op_worker_threads_filename<P>(
@ -88,30 +83,5 @@ where
url_path.to_path_buf(), url_path.to_path_buf(),
)); ));
} }
let node_resolver = state.borrow::<NodeResolverRc>(); Ok(url.to_string())
match node_resolver
.url_to_node_resolution(url)
.map_err(WorkerThreadsFilenameError::UrlToNodeResolution)?
{
NodeResolution::Esm(u) => Ok(u.to_string()),
NodeResolution::CommonJs(u) => wrap_cjs(u),
NodeResolution::BuiltIn(_) => {
Err(WorkerThreadsFilenameError::NeitherEsmNorCjs)
}
}
}
///
/// Wrap a CJS file-URL and the required setup in a stringified `data:`-URL
///
fn wrap_cjs(url: Url) -> Result<String, WorkerThreadsFilenameError> {
let path = url
.to_file_path()
.map_err(|_| WorkerThreadsFilenameError::UrlToPath)?;
let filename = path.file_name().unwrap().to_string_lossy();
Ok(format!(
"data:text/javascript,import {{ createRequire }} from \"node:module\";\
const require = createRequire(\"{}\"); require(\"./{}\");",
url, filename,
))
} }

View file

@ -1071,13 +1071,35 @@ Module._extensions[".js"] = function (module, filename) {
} else if (pkg?.type === "commonjs") { } else if (pkg?.type === "commonjs") {
format = "commonjs"; format = "commonjs";
} }
} else if (StringPrototypeEndsWith(filename, ".cjs")) { }
module._compile(content, filename, format);
};
Module._extensions[".ts"] =
Module._extensions[".jsx"] =
Module._extensions[".tsx"] =
function (module, filename) {
const content = op_require_read_file(filename);
let format;
const pkg = op_require_read_closest_package_json(filename);
if (pkg?.type === "module") {
format = "module";
} else if (pkg?.type === "commonjs") {
format = "commonjs"; format = "commonjs";
} }
module._compile(content, filename, format); module._compile(content, filename, format);
}; };
Module._extensions[".cjs"] =
Module._extensions[".cts"] =
function (module, filename) {
const content = op_require_read_file(filename);
module._compile(content, filename, "commonjs");
};
function loadESMFromCJS(module, filename, code) { function loadESMFromCJS(module, filename, code) {
const namespace = op_import_sync( const namespace = op_import_sync(
url.pathToFileURL(filename).toString(), url.pathToFileURL(filename).toString(),
@ -1087,7 +1109,10 @@ function loadESMFromCJS(module, filename, code) {
module.exports = namespace; module.exports = namespace;
} }
Module._extensions[".mjs"] = function (module, filename) { Module._extensions[".mjs"] = Module._extensions[".mts"] = function (
module,
filename,
) {
loadESMFromCJS(module, filename); loadESMFromCJS(module, filename);
}; };

View file

@ -15,13 +15,3 @@ pub trait DenoResolverFs {
fn is_dir_sync(&self, path: &Path) -> bool; fn is_dir_sync(&self, path: &Path) -> bool;
fn read_dir_sync(&self, dir_path: &Path) -> std::io::Result<Vec<DirEntry>>; fn read_dir_sync(&self, dir_path: &Path) -> std::io::Result<Vec<DirEntry>>;
} }
pub(crate) struct DenoPkgJsonFsAdapter<'a, Fs: DenoResolverFs>(pub &'a Fs);
impl<'a, Fs: DenoResolverFs> deno_package_json::fs::DenoPkgJsonFs
for DenoPkgJsonFsAdapter<'a, Fs>
{
fn read_to_string_lossy(&self, path: &Path) -> std::io::Result<String> {
self.0.read_to_string_lossy(path)
}
}

View file

@ -10,16 +10,17 @@ use deno_package_json::PackageJsonDepValue;
use deno_path_util::url_to_file_path; use deno_path_util::url_to_file_path;
use deno_semver::package::PackageReq; use deno_semver::package::PackageReq;
use deno_semver::Version; use deno_semver::Version;
use node_resolver::env::NodeResolverEnv;
use node_resolver::errors::PackageFolderResolveError; use node_resolver::errors::PackageFolderResolveError;
use node_resolver::errors::PackageFolderResolveIoError; use node_resolver::errors::PackageFolderResolveIoError;
use node_resolver::errors::PackageJsonLoadError; use node_resolver::errors::PackageJsonLoadError;
use node_resolver::errors::PackageNotFoundError; use node_resolver::errors::PackageNotFoundError;
use node_resolver::load_pkg_json; use node_resolver::InNpmPackageChecker;
use node_resolver::NpmResolver; use node_resolver::NpmResolver;
use node_resolver::PackageJsonResolverRc;
use thiserror::Error; use thiserror::Error;
use url::Url; use url::Url;
use crate::fs::DenoPkgJsonFsAdapter;
use crate::fs::DenoResolverFs; use crate::fs::DenoResolverFs;
use super::local::normalize_pkg_name_for_node_modules_deno_folder; use super::local::normalize_pkg_name_for_node_modules_deno_folder;
@ -36,32 +37,41 @@ pub enum ByonmResolvePkgFolderFromDenoReqError {
Io(#[from] std::io::Error), Io(#[from] std::io::Error),
} }
pub struct ByonmNpmResolverCreateOptions<Fs: DenoResolverFs> { pub struct ByonmNpmResolverCreateOptions<
pub fs: Fs, Fs: DenoResolverFs,
TEnv: NodeResolverEnv,
> {
// todo(dsherret): investigate removing this // todo(dsherret): investigate removing this
pub root_node_modules_dir: Option<PathBuf>, pub root_node_modules_dir: Option<PathBuf>,
pub fs: Fs,
pub pkg_json_resolver: PackageJsonResolverRc<TEnv>,
} }
#[derive(Debug)] #[derive(Debug)]
pub struct ByonmNpmResolver<Fs: DenoResolverFs> { pub struct ByonmNpmResolver<Fs: DenoResolverFs, TEnv: NodeResolverEnv> {
fs: Fs, fs: Fs,
pkg_json_resolver: PackageJsonResolverRc<TEnv>,
root_node_modules_dir: Option<PathBuf>, root_node_modules_dir: Option<PathBuf>,
} }
impl<Fs: DenoResolverFs + Clone> Clone for ByonmNpmResolver<Fs> { impl<Fs: DenoResolverFs + Clone, TEnv: NodeResolverEnv> Clone
for ByonmNpmResolver<Fs, TEnv>
{
fn clone(&self) -> Self { fn clone(&self) -> Self {
Self { Self {
fs: self.fs.clone(), fs: self.fs.clone(),
pkg_json_resolver: self.pkg_json_resolver.clone(),
root_node_modules_dir: self.root_node_modules_dir.clone(), root_node_modules_dir: self.root_node_modules_dir.clone(),
} }
} }
} }
impl<Fs: DenoResolverFs> ByonmNpmResolver<Fs> { impl<Fs: DenoResolverFs, TEnv: NodeResolverEnv> ByonmNpmResolver<Fs, TEnv> {
pub fn new(options: ByonmNpmResolverCreateOptions<Fs>) -> Self { pub fn new(options: ByonmNpmResolverCreateOptions<Fs, TEnv>) -> Self {
Self { Self {
fs: options.fs,
root_node_modules_dir: options.root_node_modules_dir, root_node_modules_dir: options.root_node_modules_dir,
fs: options.fs,
pkg_json_resolver: options.pkg_json_resolver,
} }
} }
@ -73,7 +83,7 @@ impl<Fs: DenoResolverFs> ByonmNpmResolver<Fs> {
&self, &self,
path: &Path, path: &Path,
) -> Result<Option<Arc<PackageJson>>, PackageJsonLoadError> { ) -> Result<Option<Arc<PackageJson>>, PackageJsonLoadError> {
load_pkg_json(&DenoPkgJsonFsAdapter(&self.fs), path) self.pkg_json_resolver.load_package_json(path)
} }
/// Finds the ancestor package.json that contains the specified dependency. /// Finds the ancestor package.json that contains the specified dependency.
@ -290,8 +300,10 @@ impl<Fs: DenoResolverFs> ByonmNpmResolver<Fs> {
} }
} }
impl<Fs: DenoResolverFs + Send + Sync + std::fmt::Debug> NpmResolver impl<
for ByonmNpmResolver<Fs> Fs: DenoResolverFs + Send + Sync + std::fmt::Debug,
TEnv: NodeResolverEnv,
> NpmResolver for ByonmNpmResolver<Fs, TEnv>
{ {
fn resolve_package_folder_from_package( fn resolve_package_folder_from_package(
&self, &self,
@ -342,7 +354,12 @@ impl<Fs: DenoResolverFs + Send + Sync + std::fmt::Debug> NpmResolver
.into() .into()
}) })
} }
}
#[derive(Debug)]
pub struct ByonmInNpmPackageChecker;
impl InNpmPackageChecker for ByonmInNpmPackageChecker {
fn in_npm_package(&self, specifier: &Url) -> bool { fn in_npm_package(&self, specifier: &Url) -> bool {
specifier.scheme() == "file" specifier.scheme() == "file"
&& specifier && specifier

View file

@ -3,6 +3,7 @@
mod byonm; mod byonm;
mod local; mod local;
pub use byonm::ByonmInNpmPackageChecker;
pub use byonm::ByonmNpmResolver; pub use byonm::ByonmNpmResolver;
pub use byonm::ByonmNpmResolverCreateOptions; pub use byonm::ByonmNpmResolverCreateOptions;
pub use byonm::ByonmResolvePkgFolderFromDenoReqError; pub use byonm::ByonmResolvePkgFolderFromDenoReqError;

View file

@ -232,7 +232,7 @@ impl<Fs: SloppyImportResolverFs> SloppyImportsResolver<Fs> {
| MediaType::Tsx | MediaType::Tsx
| MediaType::Json | MediaType::Json
| MediaType::Wasm | MediaType::Wasm
| MediaType::TsBuildInfo | MediaType::Css
| MediaType::SourceMap => { | MediaType::SourceMap => {
return None; return None;
} }

View file

@ -19,18 +19,19 @@ use anyhow::Error as AnyError;
use url::Url; use url::Url;
use crate::env::NodeResolverEnv; use crate::env::NodeResolverEnv;
use crate::package_json::load_pkg_json; use crate::npm::InNpmPackageCheckerRc;
use crate::resolution::NodeResolverRc; use crate::resolution::NodeResolverRc;
use crate::NodeModuleKind; use crate::NodeModuleKind;
use crate::NodeResolutionMode; use crate::NodeResolutionMode;
use crate::NpmResolverRc; use crate::NpmResolverRc;
use crate::PackageJsonResolverRc;
use crate::PathClean; use crate::PathClean;
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
pub enum CjsAnalysis { pub enum CjsAnalysis<'a> {
/// File was found to be an ES module and the translator should /// File was found to be an ES module and the translator should
/// load the code as ESM. /// load the code as ESM.
Esm(String), Esm(Cow<'a, str>),
Cjs(CjsAnalysisExports), Cjs(CjsAnalysisExports),
} }
@ -50,11 +51,11 @@ pub trait CjsCodeAnalyzer {
/// already has it. If the source is needed by the implementation, /// already has it. If the source is needed by the implementation,
/// then it can use the provided source, or otherwise load it if /// then it can use the provided source, or otherwise load it if
/// necessary. /// necessary.
async fn analyze_cjs( async fn analyze_cjs<'a>(
&self, &self,
specifier: &Url, specifier: &Url,
maybe_source: Option<String>, maybe_source: Option<Cow<'a, str>>,
) -> Result<CjsAnalysis, AnyError>; ) -> Result<CjsAnalysis<'a>, AnyError>;
} }
pub struct NodeCodeTranslator< pub struct NodeCodeTranslator<
@ -63,8 +64,10 @@ pub struct NodeCodeTranslator<
> { > {
cjs_code_analyzer: TCjsCodeAnalyzer, cjs_code_analyzer: TCjsCodeAnalyzer,
env: TNodeResolverEnv, env: TNodeResolverEnv,
in_npm_pkg_checker: InNpmPackageCheckerRc,
node_resolver: NodeResolverRc<TNodeResolverEnv>, node_resolver: NodeResolverRc<TNodeResolverEnv>,
npm_resolver: NpmResolverRc, npm_resolver: NpmResolverRc,
pkg_json_resolver: PackageJsonResolverRc<TNodeResolverEnv>,
} }
impl<TCjsCodeAnalyzer: CjsCodeAnalyzer, TNodeResolverEnv: NodeResolverEnv> impl<TCjsCodeAnalyzer: CjsCodeAnalyzer, TNodeResolverEnv: NodeResolverEnv>
@ -73,14 +76,18 @@ impl<TCjsCodeAnalyzer: CjsCodeAnalyzer, TNodeResolverEnv: NodeResolverEnv>
pub fn new( pub fn new(
cjs_code_analyzer: TCjsCodeAnalyzer, cjs_code_analyzer: TCjsCodeAnalyzer,
env: TNodeResolverEnv, env: TNodeResolverEnv,
in_npm_pkg_checker: InNpmPackageCheckerRc,
node_resolver: NodeResolverRc<TNodeResolverEnv>, node_resolver: NodeResolverRc<TNodeResolverEnv>,
npm_resolver: NpmResolverRc, npm_resolver: NpmResolverRc,
pkg_json_resolver: PackageJsonResolverRc<TNodeResolverEnv>,
) -> Self { ) -> Self {
Self { Self {
cjs_code_analyzer, cjs_code_analyzer,
env, env,
in_npm_pkg_checker,
node_resolver, node_resolver,
npm_resolver, npm_resolver,
pkg_json_resolver,
} }
} }
@ -90,11 +97,11 @@ impl<TCjsCodeAnalyzer: CjsCodeAnalyzer, TNodeResolverEnv: NodeResolverEnv>
/// For all discovered reexports the analysis will be performed recursively. /// For all discovered reexports the analysis will be performed recursively.
/// ///
/// If successful a source code for equivalent ES module is returned. /// If successful a source code for equivalent ES module is returned.
pub async fn translate_cjs_to_esm( pub async fn translate_cjs_to_esm<'a>(
&self, &self,
entry_specifier: &Url, entry_specifier: &Url,
source: Option<String>, source: Option<Cow<'a, str>>,
) -> Result<String, AnyError> { ) -> Result<Cow<'a, str>, AnyError> {
let mut temp_var_count = 0; let mut temp_var_count = 0;
let analysis = self let analysis = self
@ -108,7 +115,7 @@ impl<TCjsCodeAnalyzer: CjsCodeAnalyzer, TNodeResolverEnv: NodeResolverEnv>
}; };
let mut source = vec![ let mut source = vec![
r#"import {createRequire as __internalCreateRequire} from "node:module"; r#"import {createRequire as __internalCreateRequire, Module as __internalModule } from "node:module";
const require = __internalCreateRequire(import.meta.url);"# const require = __internalCreateRequire(import.meta.url);"#
.to_string(), .to_string(),
]; ];
@ -135,7 +142,12 @@ impl<TCjsCodeAnalyzer: CjsCodeAnalyzer, TNodeResolverEnv: NodeResolverEnv>
} }
source.push(format!( source.push(format!(
"const mod = require(\"{}\");", r#"let mod;
if (import.meta.main) {{
mod = __internalModule._load("{0}", null, true)
}} else {{
mod = require("{0}");
}}"#,
url_to_file_path(entry_specifier) url_to_file_path(entry_specifier)
.unwrap() .unwrap()
.to_str() .to_str()
@ -159,7 +171,7 @@ impl<TCjsCodeAnalyzer: CjsCodeAnalyzer, TNodeResolverEnv: NodeResolverEnv>
source.push("export default mod;".to_string()); source.push("export default mod;".to_string());
let translated_source = source.join("\n"); let translated_source = source.join("\n");
Ok(translated_source) Ok(Cow::Owned(translated_source))
} }
async fn analyze_reexports<'a>( async fn analyze_reexports<'a>(
@ -174,7 +186,7 @@ impl<TCjsCodeAnalyzer: CjsCodeAnalyzer, TNodeResolverEnv: NodeResolverEnv>
struct Analysis { struct Analysis {
reexport_specifier: url::Url, reexport_specifier: url::Url,
referrer: url::Url, referrer: url::Url,
analysis: CjsAnalysis, analysis: CjsAnalysis<'static>,
} }
type AnalysisFuture<'a> = LocalBoxFuture<'a, Result<Analysis, AnyError>>; type AnalysisFuture<'a> = LocalBoxFuture<'a, Result<Analysis, AnyError>>;
@ -329,8 +341,9 @@ impl<TCjsCodeAnalyzer: CjsCodeAnalyzer, TNodeResolverEnv: NodeResolverEnv>
}?; }?;
let package_json_path = module_dir.join("package.json"); let package_json_path = module_dir.join("package.json");
let maybe_package_json = let maybe_package_json = self
load_pkg_json(self.env.pkg_json_fs(), &package_json_path)?; .pkg_json_resolver
.load_package_json(&package_json_path)?;
if let Some(package_json) = maybe_package_json { if let Some(package_json) = maybe_package_json {
if let Some(exports) = &package_json.exports { if let Some(exports) = &package_json.exports {
return Some( return Some(
@ -356,8 +369,9 @@ impl<TCjsCodeAnalyzer: CjsCodeAnalyzer, TNodeResolverEnv: NodeResolverEnv>
if self.env.is_dir_sync(&d) { if self.env.is_dir_sync(&d) {
// subdir might have a package.json that specifies the entrypoint // subdir might have a package.json that specifies the entrypoint
let package_json_path = d.join("package.json"); let package_json_path = d.join("package.json");
let maybe_package_json = let maybe_package_json = self
load_pkg_json(self.env.pkg_json_fs(), &package_json_path)?; .pkg_json_resolver
.load_package_json(&package_json_path)?;
if let Some(package_json) = maybe_package_json { if let Some(package_json) = maybe_package_json {
if let Some(main) = package_json.main(NodeModuleKind::Cjs) { if let Some(main) = package_json.main(NodeModuleKind::Cjs) {
return Ok(Some(url_from_file_path(&d.join(main).clean())?)); return Ok(Some(url_from_file_path(&d.join(main).clean())?));
@ -382,7 +396,7 @@ impl<TCjsCodeAnalyzer: CjsCodeAnalyzer, TNodeResolverEnv: NodeResolverEnv>
// as a fallback, attempt to resolve it via the ancestor directories // as a fallback, attempt to resolve it via the ancestor directories
let mut last = referrer_path.as_path(); let mut last = referrer_path.as_path();
while let Some(parent) = last.parent() { while let Some(parent) = last.parent() {
if !self.npm_resolver.in_npm_package_at_dir_path(parent) { if !self.in_npm_pkg_checker.in_npm_package_at_dir_path(parent) {
break; break;
} }
let path = if parent.ends_with("node_modules") { let path = if parent.ends_with("node_modules") {

View file

@ -81,29 +81,6 @@ pub trait NodeJsErrorCoded {
fn code(&self) -> NodeJsErrorCode; fn code(&self) -> NodeJsErrorCode;
} }
kinded_err!(
ResolvePkgSubpathFromDenoModuleError,
ResolvePkgSubpathFromDenoModuleErrorKind
);
impl NodeJsErrorCoded for ResolvePkgSubpathFromDenoModuleError {
fn code(&self) -> NodeJsErrorCode {
use ResolvePkgSubpathFromDenoModuleErrorKind::*;
match self.as_kind() {
PackageSubpathResolve(e) => e.code(),
UrlToNodeResolution(e) => e.code(),
}
}
}
#[derive(Debug, Error)]
pub enum ResolvePkgSubpathFromDenoModuleErrorKind {
#[error(transparent)]
PackageSubpathResolve(#[from] PackageSubpathResolveError),
#[error(transparent)]
UrlToNodeResolution(#[from] UrlToNodeResolutionError),
}
// todo(https://github.com/denoland/deno_core/issues/810): make this a TypeError // todo(https://github.com/denoland/deno_core/issues/810): make this a TypeError
#[derive(Debug, Clone, Error)] #[derive(Debug, Clone, Error)]
#[error( #[error(
@ -394,37 +371,6 @@ impl NodeJsErrorCoded for CanonicalizingPkgJsonDirError {
} }
} }
#[derive(Debug, Error)]
#[error("TypeScript files are not supported in npm packages: {specifier}")]
pub struct TypeScriptNotSupportedInNpmError {
pub specifier: Url,
}
impl NodeJsErrorCoded for TypeScriptNotSupportedInNpmError {
fn code(&self) -> NodeJsErrorCode {
NodeJsErrorCode::ERR_UNKNOWN_FILE_EXTENSION
}
}
kinded_err!(UrlToNodeResolutionError, UrlToNodeResolutionErrorKind);
impl NodeJsErrorCoded for UrlToNodeResolutionError {
fn code(&self) -> NodeJsErrorCode {
match self.as_kind() {
UrlToNodeResolutionErrorKind::TypeScriptNotSupported(e) => e.code(),
UrlToNodeResolutionErrorKind::ClosestPkgJson(e) => e.code(),
}
}
}
#[derive(Debug, Error)]
pub enum UrlToNodeResolutionErrorKind {
#[error(transparent)]
TypeScriptNotSupported(#[from] TypeScriptNotSupportedInNpmError),
#[error(transparent)]
ClosestPkgJson(#[from] ClosestPkgJsonError),
}
// todo(https://github.com/denoland/deno_core/issues/810): make this a TypeError // todo(https://github.com/denoland/deno_core/issues/810): make this a TypeError
#[derive(Debug, Error)] #[derive(Debug, Error)]
#[error( #[error(
@ -533,8 +479,6 @@ pub enum NodeResolveErrorKind {
TypesNotFound(#[from] TypesNotFoundError), TypesNotFound(#[from] TypesNotFoundError),
#[error(transparent)] #[error(transparent)]
FinalizeResolution(#[from] FinalizeResolutionError), FinalizeResolution(#[from] FinalizeResolutionError),
#[error(transparent)]
UrlToNodeResolution(#[from] UrlToNodeResolutionError),
} }
kinded_err!(FinalizeResolutionError, FinalizeResolutionErrorKind); kinded_err!(FinalizeResolutionError, FinalizeResolutionErrorKind);
@ -728,8 +672,6 @@ pub enum ResolvePkgJsonBinExportError {
MissingPkgJson { pkg_json_path: PathBuf }, MissingPkgJson { pkg_json_path: PathBuf },
#[error("Failed resolving binary export. {message}")] #[error("Failed resolving binary export. {message}")]
InvalidBinProperty { message: String }, InvalidBinProperty { message: String },
#[error(transparent)]
UrlToNodeResolution(#[from] UrlToNodeResolutionError),
} }
#[derive(Debug, Error)] #[derive(Debug, Error)]

View file

@ -13,9 +13,12 @@ mod resolution;
mod sync; mod sync;
pub use deno_package_json::PackageJson; pub use deno_package_json::PackageJson;
pub use npm::InNpmPackageChecker;
pub use npm::InNpmPackageCheckerRc;
pub use npm::NpmResolver; pub use npm::NpmResolver;
pub use npm::NpmResolverRc; pub use npm::NpmResolverRc;
pub use package_json::load_pkg_json; pub use package_json::PackageJsonResolver;
pub use package_json::PackageJsonResolverRc;
pub use package_json::PackageJsonThreadLocalCache; pub use package_json::PackageJsonThreadLocalCache;
pub use path::PathClean; pub use path::PathClean;
pub use resolution::parse_npm_pkg_name; pub use resolution::parse_npm_pkg_name;

View file

@ -22,7 +22,13 @@ pub trait NpmResolver: std::fmt::Debug + MaybeSend + MaybeSync {
specifier: &str, specifier: &str,
referrer: &Url, referrer: &Url,
) -> Result<PathBuf, errors::PackageFolderResolveError>; ) -> Result<PathBuf, errors::PackageFolderResolveError>;
}
#[allow(clippy::disallowed_types)]
pub type InNpmPackageCheckerRc = crate::sync::MaybeArc<dyn InNpmPackageChecker>;
/// Checks if a provided specifier is in an npm package.
pub trait InNpmPackageChecker: std::fmt::Debug + MaybeSend + MaybeSync {
fn in_npm_package(&self, specifier: &Url) -> bool; fn in_npm_package(&self, specifier: &Url) -> bool;
fn in_npm_package_at_dir_path(&self, path: &Path) -> bool { fn in_npm_package_at_dir_path(&self, path: &Path) -> bool {

View file

@ -2,15 +2,21 @@
use deno_package_json::PackageJson; use deno_package_json::PackageJson;
use deno_package_json::PackageJsonRc; use deno_package_json::PackageJsonRc;
use deno_path_util::strip_unc_prefix;
use std::cell::RefCell; use std::cell::RefCell;
use std::collections::HashMap; use std::collections::HashMap;
use std::io::ErrorKind; use std::io::ErrorKind;
use std::path::Path; use std::path::Path;
use std::path::PathBuf; use std::path::PathBuf;
use url::Url;
use crate::env::NodeResolverEnv;
use crate::errors::CanonicalizingPkgJsonDirError;
use crate::errors::ClosestPkgJsonError;
use crate::errors::PackageJsonLoadError; use crate::errors::PackageJsonLoadError;
// use a thread local cache so that workers have their own distinct cache // todo(dsherret): this isn't exactly correct and we should change it to instead
// be created per worker and passed down as a ctor arg to the pkg json resolver
thread_local! { thread_local! {
static CACHE: RefCell<HashMap<PathBuf, PackageJsonRc>> = RefCell::new(HashMap::new()); static CACHE: RefCell<HashMap<PathBuf, PackageJsonRc>> = RefCell::new(HashMap::new());
} }
@ -33,14 +39,83 @@ impl deno_package_json::PackageJsonCache for PackageJsonThreadLocalCache {
} }
} }
/// Helper to load a package.json file using the thread local cache #[allow(clippy::disallowed_types)]
/// in node_resolver. pub type PackageJsonResolverRc<TEnv> =
pub fn load_pkg_json( crate::sync::MaybeArc<PackageJsonResolver<TEnv>>;
fs: &dyn deno_package_json::fs::DenoPkgJsonFs,
#[derive(Debug)]
pub struct PackageJsonResolver<TEnv: NodeResolverEnv> {
env: TEnv,
}
impl<TEnv: NodeResolverEnv> PackageJsonResolver<TEnv> {
pub fn new(env: TEnv) -> Self {
Self { env }
}
pub fn get_closest_package_json(
&self,
url: &Url,
) -> Result<Option<PackageJsonRc>, ClosestPkgJsonError> {
let Ok(file_path) = deno_path_util::url_to_file_path(url) else {
return Ok(None);
};
self.get_closest_package_json_from_path(&file_path)
}
pub fn get_closest_package_json_from_path(
&self,
file_path: &Path,
) -> Result<Option<PackageJsonRc>, ClosestPkgJsonError> {
// we use this for deno compile using byonm because the script paths
// won't be in virtual file system, but the package.json paths will be
fn canonicalize_first_ancestor_exists<TEnv: NodeResolverEnv>(
dir_path: &Path,
env: &TEnv,
) -> Result<Option<PathBuf>, std::io::Error> {
for ancestor in dir_path.ancestors() {
match env.realpath_sync(ancestor) {
Ok(dir_path) => return Ok(Some(dir_path)),
Err(err) if err.kind() == std::io::ErrorKind::NotFound => {
// keep searching
}
Err(err) => return Err(err),
}
}
Ok(None)
}
let parent_dir = file_path.parent().unwrap();
let Some(start_dir) = canonicalize_first_ancestor_exists(
parent_dir, &self.env,
)
.map_err(|source| CanonicalizingPkgJsonDirError {
dir_path: parent_dir.to_path_buf(),
source,
})?
else {
return Ok(None);
};
let start_dir = strip_unc_prefix(start_dir);
for current_dir in start_dir.ancestors() {
let package_json_path = current_dir.join("package.json");
if let Some(pkg_json) = self.load_package_json(&package_json_path)? {
return Ok(Some(pkg_json));
}
}
Ok(None)
}
pub fn load_package_json(
&self,
path: &Path, path: &Path,
) -> Result<Option<PackageJsonRc>, PackageJsonLoadError> { ) -> Result<Option<PackageJsonRc>, PackageJsonLoadError> {
let result = let result = PackageJson::load_from_path(
PackageJson::load_from_path(path, fs, Some(&PackageJsonThreadLocalCache)); path,
self.env.pkg_json_fs(),
Some(&PackageJsonThreadLocalCache),
);
match result { match result {
Ok(pkg_json) => Ok(Some(pkg_json)), Ok(pkg_json) => Ok(Some(pkg_json)),
Err(deno_package_json::PackageJsonLoadError::Io { source, .. }) Err(deno_package_json::PackageJsonLoadError::Io { source, .. })
@ -51,3 +126,4 @@ pub fn load_pkg_json(
Err(err) => Err(PackageJsonLoadError(err)), Err(err) => Err(PackageJsonLoadError(err)),
} }
} }
}

View file

@ -6,9 +6,6 @@ use std::path::PathBuf;
use anyhow::bail; use anyhow::bail;
use anyhow::Error as AnyError; use anyhow::Error as AnyError;
use deno_media_type::MediaType;
use deno_package_json::PackageJsonRc;
use deno_path_util::strip_unc_prefix;
use deno_path_util::url_from_file_path; use deno_path_util::url_from_file_path;
use serde_json::Map; use serde_json::Map;
use serde_json::Value; use serde_json::Value;
@ -16,8 +13,6 @@ use url::Url;
use crate::env::NodeResolverEnv; use crate::env::NodeResolverEnv;
use crate::errors; use crate::errors;
use crate::errors::CanonicalizingPkgJsonDirError;
use crate::errors::ClosestPkgJsonError;
use crate::errors::DataUrlReferrerError; use crate::errors::DataUrlReferrerError;
use crate::errors::FinalizeResolutionError; use crate::errors::FinalizeResolutionError;
use crate::errors::InvalidModuleSpecifierError; use crate::errors::InvalidModuleSpecifierError;
@ -32,7 +27,6 @@ use crate::errors::PackageExportsResolveError;
use crate::errors::PackageImportNotDefinedError; use crate::errors::PackageImportNotDefinedError;
use crate::errors::PackageImportsResolveError; use crate::errors::PackageImportsResolveError;
use crate::errors::PackageImportsResolveErrorKind; use crate::errors::PackageImportsResolveErrorKind;
use crate::errors::PackageJsonLoadError;
use crate::errors::PackagePathNotExportedError; use crate::errors::PackagePathNotExportedError;
use crate::errors::PackageResolveError; use crate::errors::PackageResolveError;
use crate::errors::PackageSubpathResolveError; use crate::errors::PackageSubpathResolveError;
@ -42,14 +36,13 @@ use crate::errors::PackageTargetResolveError;
use crate::errors::PackageTargetResolveErrorKind; use crate::errors::PackageTargetResolveErrorKind;
use crate::errors::ResolveBinaryCommandsError; use crate::errors::ResolveBinaryCommandsError;
use crate::errors::ResolvePkgJsonBinExportError; use crate::errors::ResolvePkgJsonBinExportError;
use crate::errors::ResolvePkgSubpathFromDenoModuleError;
use crate::errors::TypeScriptNotSupportedInNpmError;
use crate::errors::TypesNotFoundError; use crate::errors::TypesNotFoundError;
use crate::errors::TypesNotFoundErrorData; use crate::errors::TypesNotFoundErrorData;
use crate::errors::UnsupportedDirImportError; use crate::errors::UnsupportedDirImportError;
use crate::errors::UnsupportedEsmUrlSchemeError; use crate::errors::UnsupportedEsmUrlSchemeError;
use crate::errors::UrlToNodeResolutionError; use crate::npm::InNpmPackageCheckerRc;
use crate::NpmResolverRc; use crate::NpmResolverRc;
use crate::PackageJsonResolverRc;
use crate::PathClean; use crate::PathClean;
use deno_package_json::PackageJson; use deno_package_json::PackageJson;
@ -73,16 +66,14 @@ impl NodeResolutionMode {
#[derive(Debug)] #[derive(Debug)]
pub enum NodeResolution { pub enum NodeResolution {
Esm(Url), Module(Url),
CommonJs(Url),
BuiltIn(String), BuiltIn(String),
} }
impl NodeResolution { impl NodeResolution {
pub fn into_url(self) -> Url { pub fn into_url(self) -> Url {
match self { match self {
Self::Esm(u) => u, Self::Module(u) => u,
Self::CommonJs(u) => u,
Self::BuiltIn(specifier) => { Self::BuiltIn(specifier) => {
if specifier.starts_with("node:") { if specifier.starts_with("node:") {
Url::parse(&specifier).unwrap() Url::parse(&specifier).unwrap()
@ -92,42 +83,6 @@ impl NodeResolution {
} }
} }
} }
pub fn into_specifier_and_media_type(
resolution: Option<Self>,
) -> (Url, MediaType) {
match resolution {
Some(NodeResolution::CommonJs(specifier)) => {
let media_type = MediaType::from_specifier(&specifier);
(
specifier,
match media_type {
MediaType::JavaScript | MediaType::Jsx => MediaType::Cjs,
MediaType::TypeScript | MediaType::Tsx => MediaType::Cts,
MediaType::Dts => MediaType::Dcts,
_ => media_type,
},
)
}
Some(NodeResolution::Esm(specifier)) => {
let media_type = MediaType::from_specifier(&specifier);
(
specifier,
match media_type {
MediaType::JavaScript | MediaType::Jsx => MediaType::Mjs,
MediaType::TypeScript | MediaType::Tsx => MediaType::Mts,
MediaType::Dts => MediaType::Dmts,
_ => media_type,
},
)
}
Some(resolution) => (resolution.into_url(), MediaType::Dts),
None => (
Url::parse("internal:///missing_dependency.d.ts").unwrap(),
MediaType::Dts,
),
}
}
} }
#[allow(clippy::disallowed_types)] #[allow(clippy::disallowed_types)]
@ -136,16 +91,28 @@ pub type NodeResolverRc<TEnv> = crate::sync::MaybeArc<NodeResolver<TEnv>>;
#[derive(Debug)] #[derive(Debug)]
pub struct NodeResolver<TEnv: NodeResolverEnv> { pub struct NodeResolver<TEnv: NodeResolverEnv> {
env: TEnv, env: TEnv,
in_npm_pkg_checker: InNpmPackageCheckerRc,
npm_resolver: NpmResolverRc, npm_resolver: NpmResolverRc,
pkg_json_resolver: PackageJsonResolverRc<TEnv>,
} }
impl<TEnv: NodeResolverEnv> NodeResolver<TEnv> { impl<TEnv: NodeResolverEnv> NodeResolver<TEnv> {
pub fn new(env: TEnv, npm_resolver: NpmResolverRc) -> Self { pub fn new(
Self { env, npm_resolver } env: TEnv,
in_npm_pkg_checker: InNpmPackageCheckerRc,
npm_resolver: NpmResolverRc,
pkg_json_resolver: PackageJsonResolverRc<TEnv>,
) -> Self {
Self {
env,
in_npm_pkg_checker,
npm_resolver,
pkg_json_resolver,
}
} }
pub fn in_npm_package(&self, specifier: &Url) -> bool { pub fn in_npm_package(&self, specifier: &Url) -> bool {
self.npm_resolver.in_npm_package(specifier) self.in_npm_pkg_checker.in_npm_package(specifier)
} }
/// This function is an implementation of `defaultResolve` in /// This function is an implementation of `defaultResolve` in
@ -166,7 +133,7 @@ impl<TEnv: NodeResolverEnv> NodeResolver<TEnv> {
if let Ok(url) = Url::parse(specifier) { if let Ok(url) = Url::parse(specifier) {
if url.scheme() == "data" { if url.scheme() == "data" {
return Ok(NodeResolution::Esm(url)); return Ok(NodeResolution::Module(url));
} }
if let Some(module_name) = if let Some(module_name) =
@ -191,7 +158,7 @@ impl<TEnv: NodeResolverEnv> NodeResolver<TEnv> {
let url = referrer let url = referrer
.join(specifier) .join(specifier)
.map_err(|source| DataUrlReferrerError { source })?; .map_err(|source| DataUrlReferrerError { source })?;
return Ok(NodeResolution::Esm(url)); return Ok(NodeResolution::Module(url));
} }
} }
@ -212,7 +179,7 @@ impl<TEnv: NodeResolverEnv> NodeResolver<TEnv> {
}; };
let url = self.finalize_resolution(url, Some(referrer))?; let url = self.finalize_resolution(url, Some(referrer))?;
let resolve_response = self.url_to_node_resolution(url)?; let resolve_response = NodeResolution::Module(url);
// TODO(bartlomieju): skipped checking errors for commonJS resolution and // TODO(bartlomieju): skipped checking errors for commonJS resolution and
// "preserveSymlinksMain"/"preserveSymlinks" options. // "preserveSymlinksMain"/"preserveSymlinks" options.
Ok(resolve_response) Ok(resolve_response)
@ -236,6 +203,7 @@ impl<TEnv: NodeResolverEnv> NodeResolver<TEnv> {
})?) })?)
} else if specifier.starts_with('#') { } else if specifier.starts_with('#') {
let pkg_config = self let pkg_config = self
.pkg_json_resolver
.get_closest_package_json(referrer) .get_closest_package_json(referrer)
.map_err(PackageImportsResolveErrorKind::ClosestPkgJson) .map_err(PackageImportsResolveErrorKind::ClosestPkgJson)
.map_err(|err| PackageImportsResolveError(Box::new(err)))?; .map_err(|err| PackageImportsResolveError(Box::new(err)))?;
@ -332,7 +300,7 @@ impl<TEnv: NodeResolverEnv> NodeResolver<TEnv> {
package_subpath: Option<&str>, package_subpath: Option<&str>,
maybe_referrer: Option<&Url>, maybe_referrer: Option<&Url>,
mode: NodeResolutionMode, mode: NodeResolutionMode,
) -> Result<NodeResolution, ResolvePkgSubpathFromDenoModuleError> { ) -> Result<Url, PackageSubpathResolveError> {
let node_module_kind = NodeModuleKind::Esm; let node_module_kind = NodeModuleKind::Esm;
let package_subpath = package_subpath let package_subpath = package_subpath
.map(|s| format!("./{s}")) .map(|s| format!("./{s}"))
@ -345,10 +313,9 @@ impl<TEnv: NodeResolverEnv> NodeResolver<TEnv> {
DEFAULT_CONDITIONS, DEFAULT_CONDITIONS,
mode, mode,
)?; )?;
let resolve_response = self.url_to_node_resolution(resolved_url)?;
// TODO(bartlomieju): skipped checking errors for commonJS resolution and // TODO(bartlomieju): skipped checking errors for commonJS resolution and
// "preserveSymlinksMain"/"preserveSymlinks" options. // "preserveSymlinksMain"/"preserveSymlinks" options.
Ok(resolve_response) Ok(resolved_url)
} }
pub fn resolve_binary_commands( pub fn resolve_binary_commands(
@ -356,7 +323,9 @@ impl<TEnv: NodeResolverEnv> NodeResolver<TEnv> {
package_folder: &Path, package_folder: &Path,
) -> Result<Vec<String>, ResolveBinaryCommandsError> { ) -> Result<Vec<String>, ResolveBinaryCommandsError> {
let pkg_json_path = package_folder.join("package.json"); let pkg_json_path = package_folder.join("package.json");
let Some(package_json) = self.load_package_json(&pkg_json_path)? else { let Some(package_json) =
self.pkg_json_resolver.load_package_json(&pkg_json_path)?
else {
return Ok(Vec::new()); return Ok(Vec::new());
}; };
@ -381,9 +350,11 @@ impl<TEnv: NodeResolverEnv> NodeResolver<TEnv> {
&self, &self,
package_folder: &Path, package_folder: &Path,
sub_path: Option<&str>, sub_path: Option<&str>,
) -> Result<NodeResolution, ResolvePkgJsonBinExportError> { ) -> Result<Url, ResolvePkgJsonBinExportError> {
let pkg_json_path = package_folder.join("package.json"); let pkg_json_path = package_folder.join("package.json");
let Some(package_json) = self.load_package_json(&pkg_json_path)? else { let Some(package_json) =
self.pkg_json_resolver.load_package_json(&pkg_json_path)?
else {
return Err(ResolvePkgJsonBinExportError::MissingPkgJson { return Err(ResolvePkgJsonBinExportError::MissingPkgJson {
pkg_json_path, pkg_json_path,
}); });
@ -396,37 +367,9 @@ impl<TEnv: NodeResolverEnv> NodeResolver<TEnv> {
})?; })?;
let url = url_from_file_path(&package_folder.join(bin_entry)).unwrap(); let url = url_from_file_path(&package_folder.join(bin_entry)).unwrap();
let resolve_response = self.url_to_node_resolution(url)?;
// TODO(bartlomieju): skipped checking errors for commonJS resolution and // TODO(bartlomieju): skipped checking errors for commonJS resolution and
// "preserveSymlinksMain"/"preserveSymlinks" options. // "preserveSymlinksMain"/"preserveSymlinks" options.
Ok(resolve_response) Ok(url)
}
pub fn url_to_node_resolution(
&self,
url: Url,
) -> Result<NodeResolution, UrlToNodeResolutionError> {
let url_str = url.as_str().to_lowercase();
if url_str.starts_with("http") || url_str.ends_with(".json") {
Ok(NodeResolution::Esm(url))
} else if url_str.ends_with(".js") || url_str.ends_with(".d.ts") {
let maybe_package_config = self.get_closest_package_json(&url)?;
match maybe_package_config {
Some(c) if c.typ == "module" => Ok(NodeResolution::Esm(url)),
Some(_) => Ok(NodeResolution::CommonJs(url)),
None => Ok(NodeResolution::Esm(url)),
}
} else if url_str.ends_with(".mjs") || url_str.ends_with(".d.mts") {
Ok(NodeResolution::Esm(url))
} else if url_str.ends_with(".ts") || url_str.ends_with(".mts") {
if self.in_npm_package(&url) {
Err(TypeScriptNotSupportedInNpmError { specifier: url }.into())
} else {
Ok(NodeResolution::Esm(url))
}
} else {
Ok(NodeResolution::CommonJs(url))
}
} }
/// Checks if the resolved file has a corresponding declaration file. /// Checks if the resolved file has a corresponding declaration file.
@ -1101,7 +1044,9 @@ impl<TEnv: NodeResolverEnv> NodeResolver<TEnv> {
let (package_name, package_subpath, _is_scoped) = let (package_name, package_subpath, _is_scoped) =
parse_npm_pkg_name(specifier, referrer)?; parse_npm_pkg_name(specifier, referrer)?;
if let Some(package_config) = self.get_closest_package_json(referrer)? { if let Some(package_config) =
self.pkg_json_resolver.get_closest_package_json(referrer)?
{
// ResolveSelf // ResolveSelf
if package_config.name.as_ref() == Some(&package_name) { if package_config.name.as_ref() == Some(&package_name) {
if let Some(exports) = &package_config.exports { if let Some(exports) = &package_config.exports {
@ -1216,7 +1161,10 @@ impl<TEnv: NodeResolverEnv> NodeResolver<TEnv> {
mode: NodeResolutionMode, mode: NodeResolutionMode,
) -> Result<Url, PackageSubpathResolveError> { ) -> Result<Url, PackageSubpathResolveError> {
let package_json_path = package_dir_path.join("package.json"); let package_json_path = package_dir_path.join("package.json");
match self.load_package_json(&package_json_path)? { match self
.pkg_json_resolver
.load_package_json(&package_json_path)?
{
Some(pkg_json) => self.resolve_package_subpath( Some(pkg_json) => self.resolve_package_subpath(
&pkg_json, &pkg_json,
package_subpath, package_subpath,
@ -1337,70 +1285,6 @@ impl<TEnv: NodeResolverEnv> NodeResolver<TEnv> {
} }
} }
pub fn get_closest_package_json(
&self,
url: &Url,
) -> Result<Option<PackageJsonRc>, ClosestPkgJsonError> {
let Ok(file_path) = deno_path_util::url_to_file_path(url) else {
return Ok(None);
};
self.get_closest_package_json_from_path(&file_path)
}
pub fn get_closest_package_json_from_path(
&self,
file_path: &Path,
) -> Result<Option<PackageJsonRc>, ClosestPkgJsonError> {
// we use this for deno compile using byonm because the script paths
// won't be in virtual file system, but the package.json paths will be
fn canonicalize_first_ancestor_exists(
dir_path: &Path,
env: &dyn NodeResolverEnv,
) -> Result<Option<PathBuf>, std::io::Error> {
for ancestor in dir_path.ancestors() {
match env.realpath_sync(ancestor) {
Ok(dir_path) => return Ok(Some(dir_path)),
Err(err) if err.kind() == std::io::ErrorKind::NotFound => {
// keep searching
}
Err(err) => return Err(err),
}
}
Ok(None)
}
let parent_dir = file_path.parent().unwrap();
let Some(start_dir) = canonicalize_first_ancestor_exists(
parent_dir, &self.env,
)
.map_err(|source| CanonicalizingPkgJsonDirError {
dir_path: parent_dir.to_path_buf(),
source,
})?
else {
return Ok(None);
};
let start_dir = strip_unc_prefix(start_dir);
for current_dir in start_dir.ancestors() {
let package_json_path = current_dir.join("package.json");
if let Some(pkg_json) = self.load_package_json(&package_json_path)? {
return Ok(Some(pkg_json));
}
}
Ok(None)
}
pub fn load_package_json(
&self,
package_json_path: &Path,
) -> Result<Option<PackageJsonRc>, PackageJsonLoadError> {
crate::package_json::load_pkg_json(
self.env.pkg_json_fs(),
package_json_path,
)
}
pub(super) fn legacy_main_resolve( pub(super) fn legacy_main_resolve(
&self, &self,
package_json: &PackageJson, package_json: &PackageJson,

View file

@ -1048,8 +1048,6 @@ mod node {
WorkerThreadsFilenameError::UrlToPathString => "Error", WorkerThreadsFilenameError::UrlToPathString => "Error",
WorkerThreadsFilenameError::UrlToPath => "Error", WorkerThreadsFilenameError::UrlToPath => "Error",
WorkerThreadsFilenameError::FileNotFound(_) => "Error", WorkerThreadsFilenameError::FileNotFound(_) => "Error",
WorkerThreadsFilenameError::NeitherEsmNorCjs => "Error",
WorkerThreadsFilenameError::UrlToNodeResolution(_) => "Error",
WorkerThreadsFilenameError::Fs(e) => super::get_fs_error(e), WorkerThreadsFilenameError::Fs(e) => super::get_fs_error(e),
} }
} }
@ -1058,11 +1056,13 @@ mod node {
match error { match error {
RequireError::UrlParse(e) => get_url_parse_error_class(e), RequireError::UrlParse(e) => get_url_parse_error_class(e),
RequireError::Permission(e) => get_error_class_name(e).unwrap_or("Error"), RequireError::Permission(e) => get_error_class_name(e).unwrap_or("Error"),
RequireError::PackageExportsResolve(_) => "Error", RequireError::PackageExportsResolve(_)
RequireError::PackageJsonLoad(_) => "Error", | RequireError::PackageJsonLoad(_)
RequireError::ClosestPkgJson(_) => "Error", | RequireError::ClosestPkgJson(_)
RequireError::FilePathConversion(_) => "Error", | RequireError::FilePathConversion(_)
RequireError::PackageImportsResolve(_) => "Error", | RequireError::UrlConversion(_)
| RequireError::ReadModule(_)
| RequireError::PackageImportsResolve(_) => "Error",
RequireError::Fs(e) | RequireError::UnableToGetCwd(e) => { RequireError::Fs(e) | RequireError::UnableToGetCwd(e) => {
super::get_fs_error(e) super::get_fs_error(e)
} }

View file

@ -98,6 +98,7 @@ pub fn maybe_transpile_source(
imports_not_used_as_values: deno_ast::ImportsNotUsedAsValues::Remove, imports_not_used_as_values: deno_ast::ImportsNotUsedAsValues::Remove,
..Default::default() ..Default::default()
}, },
&deno_ast::TranspileModuleOptions::default(),
&deno_ast::EmitOptions { &deno_ast::EmitOptions {
source_map: if cfg!(debug_assertions) { source_map: if cfg!(debug_assertions) {
SourceMapOption::Separate SourceMapOption::Separate
@ -109,9 +110,9 @@ pub fn maybe_transpile_source(
)? )?
.into_source(); .into_source();
let maybe_source_map: Option<SourceMapData> = let maybe_source_map: Option<SourceMapData> = transpiled_source
transpiled_source.source_map.map(|sm| sm.into()); .source_map
let source_text = String::from_utf8(transpiled_source.source)?; .map(|sm| sm.into_bytes().into());
let source_text = transpiled_source.text;
Ok((source_text.into(), maybe_source_map)) Ok((source_text.into(), maybe_source_map))
} }

View file

@ -3605,7 +3605,8 @@ fn running_declaration_files() {
temp_dir.write(file, ""); temp_dir.write(file, "");
context context
.new_command() .new_command()
.args_vec(["run", file]) // todo(dsherret): investigate why --allow-read is required here
.args_vec(["run", "--allow-read", file])
.run() .run()
.skip_output_check() .skip_output_check()
.assert_exit_code(0); .assert_exit_code(0);

View file

@ -1 +1,5 @@
export {}; // this module is declared as CommonJS, but during loading we'll
// discover it's ESM and load it fine
export function add(a, b) {
return a + b;
}

View file

@ -1,3 +1,3 @@
error: Expected a JavaScript or TypeScript module, but identified a Unknown module. Importing these types of modules is currently not supported. error: Expected a JavaScript or TypeScript module, but identified a Css module. Importing these types of modules is currently not supported.
Specifier: file:///[WILDLINE]/app.css Specifier: file:///[WILDLINE]/app.css
at file:///[WILDLINE]/exists.ts:2:8 at file:///[WILDLINE]/exists.ts:2:8

View file

@ -0,0 +1,24 @@
{
"tempDir": true,
"steps": [{
"if": "unix",
"args": "compile --output main main.js",
"output": "[WILDCARD]"
}, {
"if": "unix",
"commandName": "./main",
"args": [],
"output": "output.out",
"exitCode": 0
}, {
"if": "windows",
"args": "compile --output main.exe main.js",
"output": "[WILDCARD]"
}, {
"if": "windows",
"commandName": "./main.exe",
"args": [],
"output": "output.out",
"exitCode": 0
}]
}

View file

@ -0,0 +1 @@
module.exports = (a, b) => a + b;

View file

@ -0,0 +1 @@
module.exports.divide = (a: number, b: number) => a / b;

View file

@ -0,0 +1,5 @@
import { add } from "./reexport.cjs";
import { multiply } from "./multiply.cts";
console.log(add(1, 2));
console.log(multiply(2, 3));

View file

@ -0,0 +1,4 @@
/// <reference types="npm:@types/node" />
exports.multiply = function (a: number, b: number): number {
return require("./divide.cts").divide(a, 1 / b);
};

View file

@ -0,0 +1,2 @@
3
6

View file

@ -0,0 +1 @@
module.exports.add = require("./add.cjs");

View file

@ -1,24 +1,27 @@
{ {
"tempDir": true, "tempDir": true,
"steps": [{ "steps": [{
"args": "install",
"output": "[WILDCARD]"
}, {
"if": "unix", "if": "unix",
"args": "compile --allow-read --output main main.js", "args": "compile --output main main.js",
"output": "compile.out" "output": "compile.out"
}, { }, {
"if": "unix", "if": "unix",
"commandName": "./main", "commandName": "./main",
"args": [], "args": [],
"output": "output.out", "output": "output.out",
"exitCode": 1 "exitCode": 0
}, { }, {
"if": "windows", "if": "windows",
"args": "compile --allow-read --output main.exe main.js", "args": "compile --output main.exe main.js",
"output": "compile.out" "output": "compile.out"
}, { }, {
"if": "windows", "if": "windows",
"commandName": "./main.exe", "commandName": "./main.exe",
"args": [], "args": [],
"output": "output.out", "output": "output.out",
"exitCode": 1 "exitCode": 0
}] }]
} }

View file

@ -1,3 +1,3 @@
module.exports.add = function (a, b) { module.exports.add = function (a, b) {
return a + b; return require("./subtract.ts").subtract(a, -b);
}; };

View file

@ -1,3 +1,2 @@
Warning --unstable-detect-cjs is not properly supported in deno compile. The compiled executable may encounter runtime errors.
Check file:///[WILDLINE]/main.js Check file:///[WILDLINE]/main.js
Compile file:///[WILDLINE] Compile file:///[WILDLINE]

View file

@ -1,2 +1 @@
error: Uncaught SyntaxError: The requested module './add.js' does not provide an export named 'add' 3
at <anonymous> (file:///[WILDLINE])

View file

@ -1,3 +1,6 @@
{ {
"type": "commonjs" "type": "commonjs",
"dependencies": {
"@types/node": "*"
}
} }

View file

@ -0,0 +1,2 @@
/// <reference types="npm:@types/node" />
module.exports.subtract = (a: number, b: number) => a - b;

View file

@ -1,5 +1,4 @@
{ {
"args": "run --allow-read --quiet main.ts", "args": "run --allow-read --quiet main.ts",
"output": "main.out", "output": "main.out"
"exitCode": 1
} }

View file

@ -1,4 +1 @@
error: 'import', and 'export' cannot be used outside of module code at file://[WILDCARD]/@denotest/type-commonjs/1.0.0/index.js:1:1 3
export {};
~~~~~~

View file

@ -1 +1,2 @@
import "npm:@denotest/type-commonjs"; import { add } from "npm:@denotest/type-commonjs";
console.log(add(1, 2));

View file

@ -1,5 +1,5 @@
{ {
"args": "run typescript_file_in_package/main.ts", "args": "run main.ts",
"output": "typescript_file_in_package/main.out", "output": "main.out",
"exitCode": 1 "exitCode": 1
} }

View file

@ -0,0 +1,3 @@
Download http://localhost:4260/@denotest%2ftypescript-file
Download http://localhost:4260/@denotest/typescript-file/1.0.0.tgz
error: TypeScript files are not supported in npm packages: file:///[WILDCARD]/@denotest/typescript-file/1.0.0/index.ts

View file

@ -1,6 +0,0 @@
Download http://localhost:4260/@denotest%2ftypescript-file
Download http://localhost:4260/@denotest/typescript-file/1.0.0.tgz
error: Could not resolve 'npm:@denotest/typescript-file@1.0.0'.
Caused by:
TypeScript files are not supported in npm packages: file:///[WILDCARD]/@denotest/typescript-file/1.0.0/index.ts

View file

@ -0,0 +1,4 @@
{
"args": "run --allow-read main.cjs",
"output": "main.out"
}

View file

@ -0,0 +1 @@
console.log(require.main);

View file

@ -0,0 +1,5 @@
Module {
id: ".",
[WILDCARD]
filename: "[WILDCARD]main.cjs",
[WILDCARD]

View file

@ -0,0 +1,4 @@
{
"args": "run -A main.ts",
"output": "main.out"
}

View file

@ -0,0 +1,7 @@
// non-analyzable
const moduleName = "./output.cjs";
function getModuleName() {
return moduleName;
}
require(getModuleName());

View file

@ -0,0 +1 @@
Hello

Some files were not shown because too many files have changed in this diff Show more