mirror of
https://github.com/denoland/deno.git
synced 2025-01-21 04:52:26 -05:00
Merge branch 'main' into lsc_cache
This commit is contained in:
commit
0df9a78956
88 changed files with 15950 additions and 4599 deletions
9
Cargo.lock
generated
9
Cargo.lock
generated
|
@ -1508,11 +1508,12 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "deno_config"
|
||||
version = "0.43.0"
|
||||
version = "0.45.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6c4c11bd51ef6738cabfc3c53f16c209a0b8615cb1e4e5bf3b14e3b5deebfe21"
|
||||
checksum = "47a47412627aa0d08414eca0e8329128013ab70bdb2cdfdc5456c2214cf24c8f"
|
||||
dependencies = [
|
||||
"boxed_error",
|
||||
"capacity_builder 0.5.0",
|
||||
"deno_error",
|
||||
"deno_package_json",
|
||||
"deno_path_util",
|
||||
|
@ -2085,9 +2086,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "deno_npm"
|
||||
version = "0.27.0"
|
||||
version = "0.27.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5f818ad5dc4c206b50b5cfa6f10b4b94b127e15c8342c152768eba40c225ca23"
|
||||
checksum = "4adceb4c34f10e837d0e3ae76e88dddefb13e83c05c1ef1699fa5519241c9d27"
|
||||
dependencies = [
|
||||
"async-trait",
|
||||
"capacity_builder 0.5.0",
|
||||
|
|
|
@ -51,10 +51,10 @@ deno_ast = { version = "=0.44.0", features = ["transpiling"] }
|
|||
deno_core = { version = "0.330.0" }
|
||||
|
||||
deno_bench_util = { version = "0.179.0", path = "./bench_util" }
|
||||
deno_config = { version = "=0.43.0", features = ["workspace", "sync"] }
|
||||
deno_config = { version = "=0.45.0", features = ["workspace", "sync"] }
|
||||
deno_lockfile = "=0.24.0"
|
||||
deno_media_type = { version = "0.2.3", features = ["module_specifier"] }
|
||||
deno_npm = "=0.27.0"
|
||||
deno_npm = "=0.27.2"
|
||||
deno_path_util = "=0.3.0"
|
||||
deno_permissions = { version = "0.44.0", path = "./runtime/permissions" }
|
||||
deno_runtime = { version = "0.193.0", path = "./runtime" }
|
||||
|
|
|
@ -56,7 +56,6 @@ use deno_core::error::AnyError;
|
|||
use deno_core::resolve_url_or_path;
|
||||
use deno_core::serde_json;
|
||||
use deno_core::url::Url;
|
||||
use deno_error::JsErrorBox;
|
||||
use deno_graph::GraphKind;
|
||||
pub use deno_json::check_warn_tsconfig;
|
||||
use deno_lint::linter::LintConfig as DenoLintConfig;
|
||||
|
@ -842,8 +841,6 @@ impl CliOptions {
|
|||
} else {
|
||||
&[]
|
||||
};
|
||||
let config_parse_options =
|
||||
deno_config::deno_json::ConfigParseOptions::default();
|
||||
let discover_pkg_json = flags.config_flag != ConfigFlag::Disabled
|
||||
&& !flags.no_npm
|
||||
&& !has_flag_env_var("DENO_NO_PACKAGE_JSON");
|
||||
|
@ -854,7 +851,6 @@ impl CliOptions {
|
|||
deno_json_cache: None,
|
||||
pkg_json_cache: Some(&node_resolver::PackageJsonThreadLocalCache),
|
||||
workspace_cache: None,
|
||||
config_parse_options,
|
||||
additional_config_file_names,
|
||||
discover_pkg_json,
|
||||
maybe_vendor_override,
|
||||
|
@ -1103,11 +1099,11 @@ impl CliOptions {
|
|||
}
|
||||
};
|
||||
Ok(self.workspace().create_resolver(
|
||||
&CliSys::default(),
|
||||
CreateResolverOptions {
|
||||
pkg_json_dep_resolution,
|
||||
specified_import_map: cli_arg_specified_import_map,
|
||||
},
|
||||
|path| std::fs::read_to_string(path).map_err(JsErrorBox::from_err),
|
||||
)?)
|
||||
}
|
||||
|
||||
|
@ -2126,12 +2122,7 @@ mod test {
|
|||
let cwd = &std::env::current_dir().unwrap();
|
||||
let config_specifier =
|
||||
ModuleSpecifier::parse("file:///deno/deno.jsonc").unwrap();
|
||||
let config_file = ConfigFile::new(
|
||||
config_text,
|
||||
config_specifier,
|
||||
&deno_config::deno_json::ConfigParseOptions::default(),
|
||||
)
|
||||
.unwrap();
|
||||
let config_file = ConfigFile::new(config_text, config_specifier).unwrap();
|
||||
let actual = resolve_import_map_specifier(
|
||||
Some("import-map.json"),
|
||||
Some(&config_file),
|
||||
|
@ -2150,12 +2141,7 @@ mod test {
|
|||
let config_text = r#"{}"#;
|
||||
let config_specifier =
|
||||
ModuleSpecifier::parse("file:///deno/deno.jsonc").unwrap();
|
||||
let config_file = ConfigFile::new(
|
||||
config_text,
|
||||
config_specifier,
|
||||
&deno_config::deno_json::ConfigParseOptions::default(),
|
||||
)
|
||||
.unwrap();
|
||||
let config_file = ConfigFile::new(config_text, config_specifier).unwrap();
|
||||
let actual = resolve_import_map_specifier(
|
||||
None,
|
||||
Some(&config_file),
|
||||
|
|
224
cli/factory.rs
224
cli/factory.rs
|
@ -11,8 +11,10 @@ use deno_core::error::AnyError;
|
|||
use deno_core::futures::FutureExt;
|
||||
use deno_core::FeatureChecker;
|
||||
use deno_error::JsErrorBox;
|
||||
use deno_npm_cache::NpmCacheSetting;
|
||||
use deno_resolver::cjs::IsCjsResolutionMode;
|
||||
use deno_resolver::npm::managed::ManagedInNpmPkgCheckerCreateOptions;
|
||||
use deno_resolver::npm::managed::NpmResolutionCell;
|
||||
use deno_resolver::npm::CreateInNpmPkgCheckerOptions;
|
||||
use deno_resolver::npm::NpmReqResolverOptions;
|
||||
use deno_resolver::sloppy_imports::SloppyImportsCachedFs;
|
||||
|
@ -67,19 +69,27 @@ use crate::node::CliNodeCodeTranslator;
|
|||
use crate::node::CliNodeResolver;
|
||||
use crate::node::CliPackageJsonResolver;
|
||||
use crate::npm::create_cli_npm_resolver;
|
||||
use crate::npm::installer::NpmInstaller;
|
||||
use crate::npm::installer::NpmResolutionInstaller;
|
||||
use crate::npm::CliByonmNpmResolverCreateOptions;
|
||||
use crate::npm::CliManagedNpmResolverCreateOptions;
|
||||
use crate::npm::CliNpmCache;
|
||||
use crate::npm::CliNpmCacheHttpClient;
|
||||
use crate::npm::CliNpmRegistryInfoProvider;
|
||||
use crate::npm::CliNpmResolver;
|
||||
use crate::npm::CliNpmResolverCreateOptions;
|
||||
use crate::npm::CliNpmResolverManagedSnapshotOption;
|
||||
use crate::npm::CliNpmTarballCache;
|
||||
use crate::npm::NpmRegistryReadPermissionChecker;
|
||||
use crate::npm::NpmRegistryReadPermissionCheckerMode;
|
||||
use crate::npm::NpmResolutionInitializer;
|
||||
use crate::resolver::CjsTracker;
|
||||
use crate::resolver::CliDenoResolver;
|
||||
use crate::resolver::CliNpmGraphResolver;
|
||||
use crate::resolver::CliNpmReqResolver;
|
||||
use crate::resolver::CliResolver;
|
||||
use crate::resolver::CliResolverOptions;
|
||||
use crate::resolver::CliSloppyImportsResolver;
|
||||
use crate::resolver::FoundPackageJsonDepFlag;
|
||||
use crate::resolver::NpmModuleLoader;
|
||||
use crate::standalone::binary::DenoCompileBinaryWriter;
|
||||
use crate::sys::CliSys;
|
||||
|
@ -188,6 +198,7 @@ struct CliFactoryServices {
|
|||
emitter: Deferred<Arc<Emitter>>,
|
||||
feature_checker: Deferred<Arc<FeatureChecker>>,
|
||||
file_fetcher: Deferred<Arc<CliFileFetcher>>,
|
||||
found_pkg_json_dep_flag: Arc<FoundPackageJsonDepFlag>,
|
||||
fs: Deferred<Arc<dyn deno_fs::FileSystem>>,
|
||||
global_http_cache: Deferred<Arc<GlobalHttpCache>>,
|
||||
http_cache: Deferred<Arc<dyn HttpCache>>,
|
||||
|
@ -202,9 +213,18 @@ struct CliFactoryServices {
|
|||
module_load_preparer: Deferred<Arc<ModuleLoadPreparer>>,
|
||||
node_code_translator: Deferred<Arc<CliNodeCodeTranslator>>,
|
||||
node_resolver: Deferred<Arc<CliNodeResolver>>,
|
||||
npm_cache: Deferred<Arc<CliNpmCache>>,
|
||||
npm_cache_dir: Deferred<Arc<NpmCacheDir>>,
|
||||
npm_cache_http_client: Deferred<Arc<CliNpmCacheHttpClient>>,
|
||||
npm_graph_resolver: Deferred<Arc<CliNpmGraphResolver>>,
|
||||
npm_installer: Deferred<Arc<NpmInstaller>>,
|
||||
npm_registry_info_provider: Deferred<Arc<CliNpmRegistryInfoProvider>>,
|
||||
npm_req_resolver: Deferred<Arc<CliNpmReqResolver>>,
|
||||
npm_resolution: Arc<NpmResolutionCell>,
|
||||
npm_resolution_initializer: Deferred<Arc<NpmResolutionInitializer>>,
|
||||
npm_resolution_installer: Deferred<Arc<NpmResolutionInstaller>>,
|
||||
npm_resolver: Deferred<Arc<dyn CliNpmResolver>>,
|
||||
npm_tarball_cache: Deferred<Arc<CliNpmTarballCache>>,
|
||||
parsed_source_cache: Deferred<Arc<ParsedSourceCache>>,
|
||||
permission_desc_parser:
|
||||
Deferred<Arc<RuntimePermissionDescriptorParser<CliSys>>>,
|
||||
|
@ -398,6 +418,18 @@ impl CliFactory {
|
|||
})
|
||||
}
|
||||
|
||||
pub fn npm_cache(&self) -> Result<&Arc<CliNpmCache>, AnyError> {
|
||||
self.services.npm_cache.get_or_try_init(|| {
|
||||
let cli_options = self.cli_options()?;
|
||||
Ok(Arc::new(CliNpmCache::new(
|
||||
self.npm_cache_dir()?.clone(),
|
||||
self.sys(),
|
||||
NpmCacheSetting::from_cache_setting(&cli_options.cache_setting()),
|
||||
cli_options.npmrc().clone(),
|
||||
)))
|
||||
})
|
||||
}
|
||||
|
||||
pub fn npm_cache_dir(&self) -> Result<&Arc<NpmCacheDir>, AnyError> {
|
||||
self.services.npm_cache_dir.get_or_try_init(|| {
|
||||
let global_path = self.deno_dir()?.npm_folder_path();
|
||||
|
@ -410,6 +442,123 @@ impl CliFactory {
|
|||
})
|
||||
}
|
||||
|
||||
pub fn npm_cache_http_client(&self) -> &Arc<CliNpmCacheHttpClient> {
|
||||
self.services.npm_cache_http_client.get_or_init(|| {
|
||||
Arc::new(CliNpmCacheHttpClient::new(
|
||||
self.http_client_provider().clone(),
|
||||
self.text_only_progress_bar().clone(),
|
||||
))
|
||||
})
|
||||
}
|
||||
|
||||
pub fn npm_graph_resolver(
|
||||
&self,
|
||||
) -> Result<&Arc<CliNpmGraphResolver>, AnyError> {
|
||||
self.services.npm_graph_resolver.get_or_try_init(|| {
|
||||
let cli_options = self.cli_options()?;
|
||||
Ok(Arc::new(CliNpmGraphResolver::new(
|
||||
self.npm_installer_if_managed()?.cloned(),
|
||||
self.services.found_pkg_json_dep_flag.clone(),
|
||||
cli_options.unstable_bare_node_builtins(),
|
||||
cli_options.default_npm_caching_strategy(),
|
||||
)))
|
||||
})
|
||||
}
|
||||
|
||||
pub fn npm_installer_if_managed(
|
||||
&self,
|
||||
) -> Result<Option<&Arc<NpmInstaller>>, AnyError> {
|
||||
let options = self.cli_options()?;
|
||||
if options.use_byonm() || options.no_npm() {
|
||||
Ok(None)
|
||||
} else {
|
||||
Ok(Some(self.npm_installer()?))
|
||||
}
|
||||
}
|
||||
|
||||
pub fn npm_installer(&self) -> Result<&Arc<NpmInstaller>, AnyError> {
|
||||
self.services.npm_installer.get_or_try_init(|| {
|
||||
let cli_options = self.cli_options()?;
|
||||
Ok(Arc::new(NpmInstaller::new(
|
||||
self.npm_cache()?.clone(),
|
||||
Arc::new(NpmInstallDepsProvider::from_workspace(
|
||||
cli_options.workspace(),
|
||||
)),
|
||||
self.npm_resolution().clone(),
|
||||
self.npm_resolution_initializer()?.clone(),
|
||||
self.npm_resolution_installer()?.clone(),
|
||||
self.text_only_progress_bar(),
|
||||
self.sys(),
|
||||
self.npm_tarball_cache()?.clone(),
|
||||
cli_options.maybe_lockfile().cloned(),
|
||||
cli_options.node_modules_dir_path().cloned(),
|
||||
cli_options.lifecycle_scripts_config(),
|
||||
cli_options.npm_system_info(),
|
||||
)))
|
||||
})
|
||||
}
|
||||
|
||||
pub fn npm_registry_info_provider(
|
||||
&self,
|
||||
) -> Result<&Arc<CliNpmRegistryInfoProvider>, AnyError> {
|
||||
self
|
||||
.services
|
||||
.npm_registry_info_provider
|
||||
.get_or_try_init(|| {
|
||||
let cli_options = self.cli_options()?;
|
||||
Ok(Arc::new(CliNpmRegistryInfoProvider::new(
|
||||
self.npm_cache()?.clone(),
|
||||
self.npm_cache_http_client().clone(),
|
||||
cli_options.npmrc().clone(),
|
||||
)))
|
||||
})
|
||||
}
|
||||
|
||||
pub fn npm_resolution(&self) -> &Arc<NpmResolutionCell> {
|
||||
&self.services.npm_resolution
|
||||
}
|
||||
|
||||
pub fn npm_resolution_initializer(
|
||||
&self,
|
||||
) -> Result<&Arc<NpmResolutionInitializer>, AnyError> {
|
||||
self
|
||||
.services
|
||||
.npm_resolution_initializer
|
||||
.get_or_try_init(|| {
|
||||
let cli_options = self.cli_options()?;
|
||||
Ok(Arc::new(NpmResolutionInitializer::new(
|
||||
self.npm_registry_info_provider()?.clone(),
|
||||
self.npm_resolution().clone(),
|
||||
match cli_options.resolve_npm_resolution_snapshot()? {
|
||||
Some(snapshot) => {
|
||||
CliNpmResolverManagedSnapshotOption::Specified(Some(snapshot))
|
||||
}
|
||||
None => match cli_options.maybe_lockfile() {
|
||||
Some(lockfile) => {
|
||||
CliNpmResolverManagedSnapshotOption::ResolveFromLockfile(
|
||||
lockfile.clone(),
|
||||
)
|
||||
}
|
||||
None => CliNpmResolverManagedSnapshotOption::Specified(None),
|
||||
},
|
||||
},
|
||||
)))
|
||||
})
|
||||
}
|
||||
|
||||
pub fn npm_resolution_installer(
|
||||
&self,
|
||||
) -> Result<&Arc<NpmResolutionInstaller>, AnyError> {
|
||||
self.services.npm_resolution_installer.get_or_try_init(|| {
|
||||
let cli_options = self.cli_options()?;
|
||||
Ok(Arc::new(NpmResolutionInstaller::new(
|
||||
self.npm_registry_info_provider()?.clone(),
|
||||
self.npm_resolution().clone(),
|
||||
cli_options.maybe_lockfile().cloned(),
|
||||
)))
|
||||
})
|
||||
}
|
||||
|
||||
pub async fn npm_resolver(
|
||||
&self,
|
||||
) -> Result<&Arc<dyn CliNpmResolver>, AnyError> {
|
||||
|
@ -419,7 +568,7 @@ impl CliFactory {
|
|||
.get_or_try_init_async(
|
||||
async {
|
||||
let cli_options = self.cli_options()?;
|
||||
create_cli_npm_resolver(if cli_options.use_byonm() {
|
||||
Ok(create_cli_npm_resolver(if cli_options.use_byonm() {
|
||||
CliNpmResolverCreateOptions::Byonm(
|
||||
CliByonmNpmResolverCreateOptions {
|
||||
sys: self.sys(),
|
||||
|
@ -438,52 +587,43 @@ impl CliFactory {
|
|||
},
|
||||
)
|
||||
} else {
|
||||
self
|
||||
.npm_resolution_initializer()?
|
||||
.ensure_initialized()
|
||||
.await?;
|
||||
CliNpmResolverCreateOptions::Managed(
|
||||
CliManagedNpmResolverCreateOptions {
|
||||
http_client_provider: self.http_client_provider().clone(),
|
||||
npm_install_deps_provider: Arc::new(
|
||||
NpmInstallDepsProvider::from_workspace(
|
||||
cli_options.workspace(),
|
||||
),
|
||||
),
|
||||
sys: self.sys(),
|
||||
snapshot: match cli_options.resolve_npm_resolution_snapshot()? {
|
||||
Some(snapshot) => {
|
||||
CliNpmResolverManagedSnapshotOption::Specified(Some(
|
||||
snapshot,
|
||||
))
|
||||
}
|
||||
None => match cli_options.maybe_lockfile() {
|
||||
Some(lockfile) => {
|
||||
CliNpmResolverManagedSnapshotOption::ResolveFromLockfile(
|
||||
lockfile.clone(),
|
||||
)
|
||||
}
|
||||
None => {
|
||||
CliNpmResolverManagedSnapshotOption::Specified(None)
|
||||
}
|
||||
},
|
||||
},
|
||||
maybe_lockfile: cli_options.maybe_lockfile().cloned(),
|
||||
npm_resolution: self.npm_resolution().clone(),
|
||||
npm_cache_dir: self.npm_cache_dir()?.clone(),
|
||||
cache_setting: cli_options.cache_setting(),
|
||||
text_only_progress_bar: self.text_only_progress_bar().clone(),
|
||||
maybe_node_modules_path: cli_options
|
||||
.node_modules_dir_path()
|
||||
.cloned(),
|
||||
npm_system_info: cli_options.npm_system_info(),
|
||||
npmrc: cli_options.npmrc().clone(),
|
||||
lifecycle_scripts: cli_options.lifecycle_scripts_config(),
|
||||
},
|
||||
)
|
||||
})
|
||||
.await
|
||||
}))
|
||||
}
|
||||
.boxed_local(),
|
||||
)
|
||||
.await
|
||||
}
|
||||
|
||||
pub fn npm_tarball_cache(
|
||||
&self,
|
||||
) -> Result<&Arc<CliNpmTarballCache>, AnyError> {
|
||||
self.services.npm_tarball_cache.get_or_try_init(|| {
|
||||
let cli_options = self.cli_options()?;
|
||||
Ok(Arc::new(CliNpmTarballCache::new(
|
||||
self.npm_cache()?.clone(),
|
||||
self.npm_cache_http_client().clone(),
|
||||
self.sys(),
|
||||
cli_options.npmrc().clone(),
|
||||
)))
|
||||
})
|
||||
}
|
||||
|
||||
pub fn sloppy_imports_resolver(
|
||||
&self,
|
||||
) -> Result<Option<&Arc<CliSloppyImportsResolver>>, AnyError> {
|
||||
|
@ -571,17 +711,10 @@ impl CliFactory {
|
|||
.resolver
|
||||
.get_or_try_init_async(
|
||||
async {
|
||||
let cli_options = self.cli_options()?;
|
||||
Ok(Arc::new(CliResolver::new(CliResolverOptions {
|
||||
npm_resolver: if cli_options.no_npm() {
|
||||
None
|
||||
} else {
|
||||
Some(self.npm_resolver().await?.clone())
|
||||
},
|
||||
bare_node_builtins_enabled: cli_options
|
||||
.unstable_bare_node_builtins(),
|
||||
deno_resolver: self.deno_resolver().await?.clone(),
|
||||
})))
|
||||
Ok(Arc::new(CliResolver::new(
|
||||
self.deno_resolver().await?.clone(),
|
||||
self.services.found_pkg_json_dep_flag.clone(),
|
||||
)))
|
||||
}
|
||||
.boxed_local(),
|
||||
)
|
||||
|
@ -670,6 +803,7 @@ impl CliFactory {
|
|||
.into_npm_pkg_folder_resolver(),
|
||||
self.pkg_json_resolver().clone(),
|
||||
self.sys(),
|
||||
node_resolver::ConditionsFromResolutionMode::default(),
|
||||
)))
|
||||
}
|
||||
.boxed_local(),
|
||||
|
@ -751,6 +885,7 @@ impl CliFactory {
|
|||
cli_options.clone(),
|
||||
self.module_graph_builder().await?.clone(),
|
||||
self.node_resolver().await?.clone(),
|
||||
self.npm_installer_if_managed()?.cloned(),
|
||||
self.npm_resolver().await?.clone(),
|
||||
self.sys(),
|
||||
)))
|
||||
|
@ -776,6 +911,8 @@ impl CliFactory {
|
|||
cli_options.maybe_lockfile().cloned(),
|
||||
self.maybe_file_watcher_reporter().clone(),
|
||||
self.module_info_cache()?.clone(),
|
||||
self.npm_graph_resolver()?.clone(),
|
||||
self.npm_installer_if_managed()?.cloned(),
|
||||
self.npm_resolver().await?.clone(),
|
||||
self.parsed_source_cache().clone(),
|
||||
self.resolver().await?.clone(),
|
||||
|
@ -796,7 +933,7 @@ impl CliFactory {
|
|||
let cli_options = self.cli_options()?;
|
||||
Ok(Arc::new(ModuleGraphCreator::new(
|
||||
cli_options.clone(),
|
||||
self.npm_resolver().await?.clone(),
|
||||
self.npm_installer_if_managed()?.cloned(),
|
||||
self.module_graph_builder().await?.clone(),
|
||||
self.type_checker().await?.clone(),
|
||||
)))
|
||||
|
@ -996,6 +1133,7 @@ impl CliFactory {
|
|||
self.sys(),
|
||||
)),
|
||||
node_resolver.clone(),
|
||||
self.npm_installer_if_managed()?.cloned(),
|
||||
npm_resolver.clone(),
|
||||
pkg_json_resolver,
|
||||
self.root_cert_store_provider().clone(),
|
||||
|
|
|
@ -7,6 +7,7 @@ use std::sync::Arc;
|
|||
|
||||
use deno_config::deno_json;
|
||||
use deno_config::deno_json::JsxImportSourceConfig;
|
||||
use deno_config::deno_json::NodeModulesDirMode;
|
||||
use deno_config::workspace::JsrPackageConfig;
|
||||
use deno_core::error::AnyError;
|
||||
use deno_core::parking_lot::Mutex;
|
||||
|
@ -36,7 +37,6 @@ use deno_runtime::deno_permissions::PermissionsContainer;
|
|||
use deno_semver::jsr::JsrDepPackageReq;
|
||||
use deno_semver::package::PackageNv;
|
||||
use deno_semver::SmallStackString;
|
||||
use import_map::ImportMapError;
|
||||
use node_resolver::InNpmPackageChecker;
|
||||
|
||||
use crate::args::config_to_deno_graph_workspace_member;
|
||||
|
@ -52,8 +52,11 @@ use crate::cache::ModuleInfoCache;
|
|||
use crate::cache::ParsedSourceCache;
|
||||
use crate::colors;
|
||||
use crate::file_fetcher::CliFileFetcher;
|
||||
use crate::npm::installer::NpmInstaller;
|
||||
use crate::npm::installer::PackageCaching;
|
||||
use crate::npm::CliNpmResolver;
|
||||
use crate::resolver::CjsTracker;
|
||||
use crate::resolver::CliNpmGraphResolver;
|
||||
use crate::resolver::CliResolver;
|
||||
use crate::resolver::CliSloppyImportsResolver;
|
||||
use crate::sys::CliSys;
|
||||
|
@ -264,7 +267,7 @@ pub struct CreateGraphOptions<'a> {
|
|||
|
||||
pub struct ModuleGraphCreator {
|
||||
options: Arc<CliOptions>,
|
||||
npm_resolver: Arc<dyn CliNpmResolver>,
|
||||
npm_installer: Option<Arc<NpmInstaller>>,
|
||||
module_graph_builder: Arc<ModuleGraphBuilder>,
|
||||
type_checker: Arc<TypeChecker>,
|
||||
}
|
||||
|
@ -272,13 +275,13 @@ pub struct ModuleGraphCreator {
|
|||
impl ModuleGraphCreator {
|
||||
pub fn new(
|
||||
options: Arc<CliOptions>,
|
||||
npm_resolver: Arc<dyn CliNpmResolver>,
|
||||
npm_installer: Option<Arc<NpmInstaller>>,
|
||||
module_graph_builder: Arc<ModuleGraphBuilder>,
|
||||
type_checker: Arc<TypeChecker>,
|
||||
) -> Self {
|
||||
Self {
|
||||
options,
|
||||
npm_resolver,
|
||||
npm_installer,
|
||||
module_graph_builder,
|
||||
type_checker,
|
||||
}
|
||||
|
@ -401,9 +404,9 @@ impl ModuleGraphCreator {
|
|||
.build_graph_with_npm_resolution(&mut graph, options)
|
||||
.await?;
|
||||
|
||||
if let Some(npm_resolver) = self.npm_resolver.as_managed() {
|
||||
if let Some(npm_installer) = &self.npm_installer {
|
||||
if graph.has_node_specifier && self.options.type_check_mode().is_true() {
|
||||
npm_resolver.inject_synthetic_types_node_package().await?;
|
||||
npm_installer.inject_synthetic_types_node_package().await?;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -498,6 +501,8 @@ pub struct ModuleGraphBuilder {
|
|||
lockfile: Option<Arc<CliLockfile>>,
|
||||
maybe_file_watcher_reporter: Option<FileWatcherReporter>,
|
||||
module_info_cache: Arc<ModuleInfoCache>,
|
||||
npm_graph_resolver: Arc<CliNpmGraphResolver>,
|
||||
npm_installer: Option<Arc<NpmInstaller>>,
|
||||
npm_resolver: Arc<dyn CliNpmResolver>,
|
||||
parsed_source_cache: Arc<ParsedSourceCache>,
|
||||
resolver: Arc<CliResolver>,
|
||||
|
@ -517,6 +522,8 @@ impl ModuleGraphBuilder {
|
|||
lockfile: Option<Arc<CliLockfile>>,
|
||||
maybe_file_watcher_reporter: Option<FileWatcherReporter>,
|
||||
module_info_cache: Arc<ModuleInfoCache>,
|
||||
npm_graph_resolver: Arc<CliNpmGraphResolver>,
|
||||
npm_installer: Option<Arc<NpmInstaller>>,
|
||||
npm_resolver: Arc<dyn CliNpmResolver>,
|
||||
parsed_source_cache: Arc<ParsedSourceCache>,
|
||||
resolver: Arc<CliResolver>,
|
||||
|
@ -533,6 +540,8 @@ impl ModuleGraphBuilder {
|
|||
lockfile,
|
||||
maybe_file_watcher_reporter,
|
||||
module_info_cache,
|
||||
npm_graph_resolver,
|
||||
npm_installer,
|
||||
npm_resolver,
|
||||
parsed_source_cache,
|
||||
resolver,
|
||||
|
@ -631,10 +640,7 @@ impl ModuleGraphBuilder {
|
|||
Some(loader) => MutLoaderRef::Borrowed(loader),
|
||||
None => MutLoaderRef::Owned(self.create_graph_loader()),
|
||||
};
|
||||
let cli_resolver = &self.resolver;
|
||||
let graph_resolver = self.create_graph_resolver()?;
|
||||
let graph_npm_resolver =
|
||||
cli_resolver.create_graph_npm_resolver(options.npm_caching);
|
||||
let maybe_file_watcher_reporter = self
|
||||
.maybe_file_watcher_reporter
|
||||
.as_ref()
|
||||
|
@ -655,7 +661,7 @@ impl ModuleGraphBuilder {
|
|||
executor: Default::default(),
|
||||
file_system: &self.sys,
|
||||
jsr_url_provider: &CliJsrUrlProvider,
|
||||
npm_resolver: Some(&graph_npm_resolver),
|
||||
npm_resolver: Some(self.npm_graph_resolver.as_ref()),
|
||||
module_analyzer: &analyzer,
|
||||
reporter: maybe_file_watcher_reporter,
|
||||
resolver: Some(&graph_resolver),
|
||||
|
@ -679,16 +685,15 @@ impl ModuleGraphBuilder {
|
|||
if self
|
||||
.cli_options
|
||||
.node_modules_dir()?
|
||||
.map(|m| m.uses_node_modules_dir())
|
||||
.map(|m| m == NodeModulesDirMode::Auto)
|
||||
.unwrap_or(false)
|
||||
{
|
||||
if let Some(npm_resolver) = self.npm_resolver.as_managed() {
|
||||
let already_done =
|
||||
npm_resolver.ensure_top_level_package_json_install().await?;
|
||||
if let Some(npm_installer) = &self.npm_installer {
|
||||
let already_done = npm_installer
|
||||
.ensure_top_level_package_json_install()
|
||||
.await?;
|
||||
if !already_done && matches!(npm_caching, NpmCachingStrategy::Eager) {
|
||||
npm_resolver
|
||||
.cache_packages(crate::npm::PackageCaching::All)
|
||||
.await?;
|
||||
npm_installer.cache_packages(PackageCaching::All).await?;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -776,11 +781,7 @@ impl ModuleGraphBuilder {
|
|||
None
|
||||
};
|
||||
let parser = self.parsed_source_cache.as_capturing_parser();
|
||||
let cli_resolver = &self.resolver;
|
||||
let graph_resolver = self.create_graph_resolver()?;
|
||||
let graph_npm_resolver = cli_resolver.create_graph_npm_resolver(
|
||||
self.cli_options.default_npm_caching_strategy(),
|
||||
);
|
||||
|
||||
graph.build_fast_check_type_graph(
|
||||
deno_graph::BuildFastCheckTypeGraphOptions {
|
||||
|
@ -789,7 +790,7 @@ impl ModuleGraphBuilder {
|
|||
fast_check_dts: false,
|
||||
jsr_url_provider: &CliJsrUrlProvider,
|
||||
resolver: Some(&graph_resolver),
|
||||
npm_resolver: Some(&graph_npm_resolver),
|
||||
npm_resolver: Some(self.npm_graph_resolver.as_ref()),
|
||||
workspace_fast_check: options.workspace_fast_check,
|
||||
},
|
||||
);
|
||||
|
@ -1024,14 +1025,11 @@ fn get_resolution_error_bare_specifier(
|
|||
{
|
||||
Some(specifier.as_str())
|
||||
} else if let ResolutionError::ResolverError { error, .. } = error {
|
||||
if let ResolveError::Other(error) = (*error).as_ref() {
|
||||
if let Some(import_map::ImportMapErrorKind::UnmappedBareSpecifier(
|
||||
if let ResolveError::ImportMap(error) = (*error).as_ref() {
|
||||
if let import_map::ImportMapErrorKind::UnmappedBareSpecifier(
|
||||
specifier,
|
||||
_,
|
||||
)) = error
|
||||
.as_any()
|
||||
.downcast_ref::<ImportMapError>()
|
||||
.map(|e| &**e)
|
||||
) = error.as_kind()
|
||||
{
|
||||
Some(specifier.as_str())
|
||||
} else {
|
||||
|
@ -1324,7 +1322,7 @@ mod test {
|
|||
let specifier = ModuleSpecifier::parse("file:///file.ts").unwrap();
|
||||
let err = import_map.resolve(input, &specifier).err().unwrap();
|
||||
let err = ResolutionError::ResolverError {
|
||||
error: Arc::new(ResolveError::Other(JsErrorBox::from_err(err))),
|
||||
error: Arc::new(ResolveError::ImportMap(err)),
|
||||
specifier: input.to_string(),
|
||||
range: Range {
|
||||
specifier,
|
||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -744,8 +744,7 @@ export function compileSelector(selector) {
|
|||
fn = matchNthChild(node, fn);
|
||||
break;
|
||||
case PSEUDO_HAS:
|
||||
// FIXME
|
||||
// fn = matchIs(part, fn);
|
||||
// TODO(@marvinhagemeister)
|
||||
throw new Error("TODO: :has");
|
||||
case PSEUDO_NOT:
|
||||
fn = matchNot(node.selectors, fn);
|
||||
|
@ -767,8 +766,7 @@ export function compileSelector(selector) {
|
|||
*/
|
||||
function matchFirstChild(next) {
|
||||
return (ctx, id) => {
|
||||
const parent = ctx.getParent(id);
|
||||
const first = ctx.getFirstChild(parent);
|
||||
const first = ctx.getFirstChild(id);
|
||||
return first === id && next(ctx, first);
|
||||
};
|
||||
}
|
||||
|
@ -779,8 +777,7 @@ function matchFirstChild(next) {
|
|||
*/
|
||||
function matchLastChild(next) {
|
||||
return (ctx, id) => {
|
||||
const parent = ctx.getParent(id);
|
||||
const last = ctx.getLastChild(parent);
|
||||
const last = ctx.getLastChild(id);
|
||||
return last === id && next(ctx, id);
|
||||
};
|
||||
}
|
||||
|
@ -955,7 +952,9 @@ function matchElem(part, next) {
|
|||
else if (part.elem === 0) return false;
|
||||
|
||||
const type = ctx.getType(id);
|
||||
if (type > 0 && type === part.elem) return next(ctx, id);
|
||||
if (type > 0 && type === part.elem) {
|
||||
return next(ctx, id);
|
||||
}
|
||||
|
||||
return false;
|
||||
};
|
||||
|
@ -968,7 +967,16 @@ function matchElem(part, next) {
|
|||
*/
|
||||
function matchAttrExists(attr, next) {
|
||||
return (ctx, id) => {
|
||||
return ctx.hasAttrPath(id, attr.prop, 0) ? next(ctx, id) : false;
|
||||
try {
|
||||
ctx.getAttrPathValue(id, attr.prop, 0);
|
||||
return next(ctx, id);
|
||||
} catch (err) {
|
||||
if (err === -1) {
|
||||
return false;
|
||||
}
|
||||
|
||||
throw err;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
|
@ -979,9 +987,15 @@ function matchAttrExists(attr, next) {
|
|||
*/
|
||||
function matchAttrBin(attr, next) {
|
||||
return (ctx, id) => {
|
||||
if (!ctx.hasAttrPath(id, attr.prop, 0)) return false;
|
||||
const value = ctx.getAttrPathValue(id, attr.prop, 0);
|
||||
if (!matchAttrValue(attr, value)) return false;
|
||||
try {
|
||||
const value = ctx.getAttrPathValue(id, attr.prop, 0);
|
||||
if (!matchAttrValue(attr, value)) return false;
|
||||
} catch (err) {
|
||||
if (err === -1) {
|
||||
return false;
|
||||
}
|
||||
throw err;
|
||||
}
|
||||
return next(ctx, id);
|
||||
};
|
||||
}
|
||||
|
|
9
cli/js/40_lint_types.d.ts
vendored
9
cli/js/40_lint_types.d.ts
vendored
|
@ -12,6 +12,8 @@ export interface AstContext {
|
|||
strTableOffset: number;
|
||||
rootOffset: number;
|
||||
nodes: Map<number, NodeFacade>;
|
||||
spansOffset: number;
|
||||
propsOffset: number;
|
||||
strByType: number[];
|
||||
strByProp: number[];
|
||||
typeByStr: Map<string, number>;
|
||||
|
@ -19,6 +21,12 @@ export interface AstContext {
|
|||
matcher: MatchContext;
|
||||
}
|
||||
|
||||
export interface Node {
|
||||
range: Range;
|
||||
}
|
||||
|
||||
export type Range = [number, number];
|
||||
|
||||
// TODO(@marvinhagemeister) Remove once we land "official" types
|
||||
export interface RuleContext {
|
||||
id: string;
|
||||
|
@ -121,7 +129,6 @@ export interface MatchContext {
|
|||
getSiblings(id: number): number[];
|
||||
getParent(id: number): number;
|
||||
getType(id: number): number;
|
||||
hasAttrPath(id: number, propIds: number[], idx: number): boolean;
|
||||
getAttrPathValue(id: number, propIds: number[], idx: number): unknown;
|
||||
}
|
||||
|
||||
|
|
|
@ -41,7 +41,6 @@ use deno_core::serde_json::json;
|
|||
use deno_core::serde_json::Value;
|
||||
use deno_core::url::Url;
|
||||
use deno_core::ModuleSpecifier;
|
||||
use deno_error::JsErrorBox;
|
||||
use deno_lint::linter::LintConfig as DenoLintConfig;
|
||||
use deno_npm::npm_rc::ResolvedNpmRc;
|
||||
use deno_package_json::PackageJsonCache;
|
||||
|
@ -1247,7 +1246,6 @@ impl ConfigData {
|
|||
pkg_json_cache: Some(pkg_json_cache),
|
||||
workspace_cache: Some(workspace_cache),
|
||||
discover_pkg_json: !has_flag_env_var("DENO_NO_PACKAGE_JSON"),
|
||||
config_parse_options: Default::default(),
|
||||
maybe_vendor_override: None,
|
||||
},
|
||||
)
|
||||
|
@ -1572,11 +1570,11 @@ impl ConfigData {
|
|||
let resolver = member_dir
|
||||
.workspace
|
||||
.create_resolver(
|
||||
&CliSys::default(),
|
||||
CreateResolverOptions {
|
||||
pkg_json_dep_resolution,
|
||||
specified_import_map,
|
||||
},
|
||||
|path| std::fs::read_to_string(path).map_err(JsErrorBox::from_err),
|
||||
)
|
||||
.inspect_err(|err| {
|
||||
lsp_warn!(
|
||||
|
@ -2078,7 +2076,6 @@ impl deno_config::workspace::WorkspaceCache for WorkspaceMemCache {
|
|||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use deno_config::deno_json::ConfigParseOptions;
|
||||
use deno_core::resolve_url;
|
||||
use deno_core::serde_json;
|
||||
use deno_core::serde_json::json;
|
||||
|
@ -2352,12 +2349,7 @@ mod tests {
|
|||
config
|
||||
.tree
|
||||
.inject_config_file(
|
||||
ConfigFile::new(
|
||||
"{}",
|
||||
root_uri.join("deno.json").unwrap(),
|
||||
&ConfigParseOptions::default(),
|
||||
)
|
||||
.unwrap(),
|
||||
ConfigFile::new("{}", root_uri.join("deno.json").unwrap()).unwrap(),
|
||||
)
|
||||
.await;
|
||||
assert!(config.specifier_enabled(&root_uri));
|
||||
|
@ -2413,7 +2405,6 @@ mod tests {
|
|||
})
|
||||
.to_string(),
|
||||
root_uri.join("deno.json").unwrap(),
|
||||
&ConfigParseOptions::default(),
|
||||
)
|
||||
.unwrap(),
|
||||
)
|
||||
|
@ -2439,7 +2430,6 @@ mod tests {
|
|||
})
|
||||
.to_string(),
|
||||
root_uri.join("deno.json").unwrap(),
|
||||
&ConfigParseOptions::default(),
|
||||
)
|
||||
.unwrap(),
|
||||
)
|
||||
|
@ -2457,7 +2447,6 @@ mod tests {
|
|||
})
|
||||
.to_string(),
|
||||
root_uri.join("deno.json").unwrap(),
|
||||
&ConfigParseOptions::default(),
|
||||
)
|
||||
.unwrap(),
|
||||
)
|
||||
|
|
|
@ -1695,12 +1695,7 @@ mod tests {
|
|||
let mut config = Config::new_with_roots([root_uri.clone()]);
|
||||
if let Some((relative_path, json_string)) = maybe_import_map {
|
||||
let base_url = root_uri.join(relative_path).unwrap();
|
||||
let config_file = ConfigFile::new(
|
||||
json_string,
|
||||
base_url,
|
||||
&deno_config::deno_json::ConfigParseOptions::default(),
|
||||
)
|
||||
.unwrap();
|
||||
let config_file = ConfigFile::new(json_string, base_url).unwrap();
|
||||
config.tree.inject_config_file(config_file).await;
|
||||
}
|
||||
let resolver =
|
||||
|
|
|
@ -480,7 +480,7 @@ impl Document {
|
|||
let is_cjs_resolver =
|
||||
resolver.as_is_cjs_resolver(self.file_referrer.as_ref());
|
||||
let npm_resolver =
|
||||
resolver.create_graph_npm_resolver(self.file_referrer.as_ref());
|
||||
resolver.as_graph_npm_resolver(self.file_referrer.as_ref());
|
||||
let config_data = resolver.as_config_data(self.file_referrer.as_ref());
|
||||
let jsx_import_source_config =
|
||||
config_data.and_then(|d| d.maybe_jsx_import_source_config());
|
||||
|
@ -503,7 +503,7 @@ impl Document {
|
|||
s,
|
||||
&CliJsrUrlProvider,
|
||||
Some(&resolver),
|
||||
Some(&npm_resolver),
|
||||
Some(npm_resolver.as_ref()),
|
||||
),
|
||||
)
|
||||
})
|
||||
|
@ -513,7 +513,7 @@ impl Document {
|
|||
Arc::new(d.with_new_resolver(
|
||||
&CliJsrUrlProvider,
|
||||
Some(&resolver),
|
||||
Some(&npm_resolver),
|
||||
Some(npm_resolver.as_ref()),
|
||||
))
|
||||
});
|
||||
is_script = self.is_script;
|
||||
|
@ -1702,7 +1702,7 @@ fn analyze_module(
|
|||
) -> (ModuleResult, ResolutionMode) {
|
||||
match parsed_source_result {
|
||||
Ok(parsed_source) => {
|
||||
let npm_resolver = resolver.create_graph_npm_resolver(file_referrer);
|
||||
let npm_resolver = resolver.as_graph_npm_resolver(file_referrer);
|
||||
let cli_resolver = resolver.as_cli_resolver(file_referrer);
|
||||
let is_cjs_resolver = resolver.as_is_cjs_resolver(file_referrer);
|
||||
let config_data = resolver.as_config_data(file_referrer);
|
||||
|
@ -1731,7 +1731,7 @@ fn analyze_module(
|
|||
file_system: &deno_graph::source::NullFileSystem,
|
||||
jsr_url_provider: &CliJsrUrlProvider,
|
||||
maybe_resolver: Some(&resolver),
|
||||
maybe_npm_resolver: Some(&npm_resolver),
|
||||
maybe_npm_resolver: Some(npm_resolver.as_ref()),
|
||||
},
|
||||
)),
|
||||
module_resolution_mode,
|
||||
|
@ -1767,7 +1767,6 @@ fn bytes_to_content(
|
|||
#[cfg(test)]
|
||||
mod tests {
|
||||
use deno_config::deno_json::ConfigFile;
|
||||
use deno_config::deno_json::ConfigParseOptions;
|
||||
use deno_core::serde_json;
|
||||
use deno_core::serde_json::json;
|
||||
use pretty_assertions::assert_eq;
|
||||
|
@ -1924,7 +1923,6 @@ console.log(b, "hello deno");
|
|||
})
|
||||
.to_string(),
|
||||
config.root_uri().unwrap().join("deno.json").unwrap(),
|
||||
&ConfigParseOptions::default(),
|
||||
)
|
||||
.unwrap(),
|
||||
)
|
||||
|
@ -1968,7 +1966,6 @@ console.log(b, "hello deno");
|
|||
})
|
||||
.to_string(),
|
||||
config.root_uri().unwrap().join("deno.json").unwrap(),
|
||||
&ConfigParseOptions::default(),
|
||||
)
|
||||
.unwrap(),
|
||||
)
|
||||
|
|
|
@ -1419,18 +1419,16 @@ impl Inner {
|
|||
// the file path is only used to determine what formatter should
|
||||
// be used to format the file, so give the filepath an extension
|
||||
// that matches what the user selected as the language
|
||||
let file_path = document
|
||||
let ext = document
|
||||
.maybe_language_id()
|
||||
.and_then(|id| id.as_extension())
|
||||
.map(|ext| file_path.with_extension(ext))
|
||||
.unwrap_or(file_path);
|
||||
.and_then(|id| id.as_extension().map(|s| s.to_string()));
|
||||
// it's not a js/ts file, so attempt to format its contents
|
||||
format_file(
|
||||
&file_path,
|
||||
document.content(),
|
||||
&fmt_options,
|
||||
&unstable_options,
|
||||
None,
|
||||
ext,
|
||||
)
|
||||
}
|
||||
};
|
||||
|
@ -3623,8 +3621,6 @@ impl Inner {
|
|||
deno_json_cache: None,
|
||||
pkg_json_cache: None,
|
||||
workspace_cache: None,
|
||||
config_parse_options:
|
||||
deno_config::deno_json::ConfigParseOptions::default(),
|
||||
additional_config_file_names: &[],
|
||||
discover_pkg_json: !has_flag_env_var("DENO_NO_PACKAGE_JSON"),
|
||||
maybe_vendor_override: if force_global_cache {
|
||||
|
|
|
@ -9,7 +9,6 @@ use std::sync::Arc;
|
|||
|
||||
use dashmap::DashMap;
|
||||
use deno_ast::MediaType;
|
||||
use deno_cache_dir::file_fetcher::CacheSetting;
|
||||
use deno_cache_dir::npm::NpmCacheDir;
|
||||
use deno_cache_dir::HttpCache;
|
||||
use deno_config::deno_json::JsxImportSourceConfig;
|
||||
|
@ -21,9 +20,11 @@ use deno_graph::GraphImport;
|
|||
use deno_graph::ModuleSpecifier;
|
||||
use deno_graph::Range;
|
||||
use deno_npm::NpmSystemInfo;
|
||||
use deno_npm_cache::TarballCache;
|
||||
use deno_path_util::url_to_file_path;
|
||||
use deno_resolver::cjs::IsCjsResolutionMode;
|
||||
use deno_resolver::npm::managed::ManagedInNpmPkgCheckerCreateOptions;
|
||||
use deno_resolver::npm::managed::NpmResolutionCell;
|
||||
use deno_resolver::npm::CreateInNpmPkgCheckerOptions;
|
||||
use deno_resolver::npm::NpmReqResolverOptions;
|
||||
use deno_resolver::DenoResolverOptions;
|
||||
|
@ -42,6 +43,8 @@ use super::cache::LspCache;
|
|||
use super::jsr::JsrCacheResolver;
|
||||
use crate::args::create_default_npmrc;
|
||||
use crate::args::CliLockfile;
|
||||
use crate::args::LifecycleScriptsConfig;
|
||||
use crate::args::NpmCachingStrategy;
|
||||
use crate::args::NpmInstallDepsProvider;
|
||||
use crate::factory::Deferred;
|
||||
use crate::graph_util::to_node_resolution_kind;
|
||||
|
@ -53,19 +56,25 @@ use crate::lsp::config::ConfigData;
|
|||
use crate::lsp::logging::lsp_warn;
|
||||
use crate::node::CliNodeResolver;
|
||||
use crate::node::CliPackageJsonResolver;
|
||||
use crate::npm::create_cli_npm_resolver_for_lsp;
|
||||
use crate::npm::create_cli_npm_resolver;
|
||||
use crate::npm::installer::NpmInstaller;
|
||||
use crate::npm::installer::NpmResolutionInstaller;
|
||||
use crate::npm::CliByonmNpmResolverCreateOptions;
|
||||
use crate::npm::CliManagedNpmResolverCreateOptions;
|
||||
use crate::npm::CliNpmCache;
|
||||
use crate::npm::CliNpmCacheHttpClient;
|
||||
use crate::npm::CliNpmRegistryInfoProvider;
|
||||
use crate::npm::CliNpmResolver;
|
||||
use crate::npm::CliNpmResolverCreateOptions;
|
||||
use crate::npm::CliNpmResolverManagedSnapshotOption;
|
||||
use crate::npm::ManagedCliNpmResolver;
|
||||
use crate::npm::NpmResolutionInitializer;
|
||||
use crate::resolver::CliDenoResolver;
|
||||
use crate::resolver::CliNpmGraphResolver;
|
||||
use crate::resolver::CliNpmReqResolver;
|
||||
use crate::resolver::CliResolver;
|
||||
use crate::resolver::CliResolverOptions;
|
||||
use crate::resolver::FoundPackageJsonDepFlag;
|
||||
use crate::resolver::IsCjsResolver;
|
||||
use crate::resolver::WorkerCliNpmGraphResolver;
|
||||
use crate::sys::CliSys;
|
||||
use crate::tsc::into_specifier_and_media_type;
|
||||
use crate::util::progress_bar::ProgressBar;
|
||||
|
@ -77,6 +86,8 @@ struct LspScopeResolver {
|
|||
in_npm_pkg_checker: Arc<dyn InNpmPackageChecker>,
|
||||
is_cjs_resolver: Arc<IsCjsResolver>,
|
||||
jsr_resolver: Option<Arc<JsrCacheResolver>>,
|
||||
npm_graph_resolver: Arc<CliNpmGraphResolver>,
|
||||
npm_installer: Option<Arc<NpmInstaller>>,
|
||||
npm_resolver: Option<Arc<dyn CliNpmResolver>>,
|
||||
node_resolver: Option<Arc<CliNodeResolver>>,
|
||||
npm_pkg_req_resolver: Option<Arc<CliNpmReqResolver>>,
|
||||
|
@ -96,6 +107,8 @@ impl Default for LspScopeResolver {
|
|||
in_npm_pkg_checker: factory.in_npm_pkg_checker().clone(),
|
||||
is_cjs_resolver: factory.is_cjs_resolver().clone(),
|
||||
jsr_resolver: None,
|
||||
npm_graph_resolver: factory.npm_graph_resolver().clone(),
|
||||
npm_installer: None,
|
||||
npm_resolver: None,
|
||||
node_resolver: None,
|
||||
npm_pkg_req_resolver: None,
|
||||
|
@ -121,6 +134,7 @@ impl LspScopeResolver {
|
|||
}
|
||||
let in_npm_pkg_checker = factory.in_npm_pkg_checker().clone();
|
||||
let npm_resolver = factory.npm_resolver().cloned();
|
||||
let npm_installer = factory.npm_installer().cloned();
|
||||
let node_resolver = factory.node_resolver().cloned();
|
||||
let npm_pkg_req_resolver = factory.npm_pkg_req_resolver().cloned();
|
||||
let cli_resolver = factory.cli_resolver().clone();
|
||||
|
@ -133,8 +147,7 @@ impl LspScopeResolver {
|
|||
cache.for_specifier(config_data.map(|d| d.scope.as_ref())),
|
||||
config_data.and_then(|d| d.lockfile.clone()),
|
||||
)));
|
||||
let npm_graph_resolver = cli_resolver
|
||||
.create_graph_npm_resolver(crate::graph_util::NpmCachingStrategy::Eager);
|
||||
let npm_graph_resolver = factory.npm_graph_resolver();
|
||||
let maybe_jsx_import_source_config =
|
||||
config_data.and_then(|d| d.maybe_jsx_import_source_config());
|
||||
let graph_imports = config_data
|
||||
|
@ -156,7 +169,7 @@ impl LspScopeResolver {
|
|||
imports,
|
||||
&CliJsrUrlProvider,
|
||||
Some(&resolver),
|
||||
Some(&npm_graph_resolver),
|
||||
Some(npm_graph_resolver.as_ref()),
|
||||
);
|
||||
(referrer, graph_import)
|
||||
})
|
||||
|
@ -207,8 +220,10 @@ impl LspScopeResolver {
|
|||
in_npm_pkg_checker,
|
||||
is_cjs_resolver: factory.is_cjs_resolver().clone(),
|
||||
jsr_resolver,
|
||||
npm_graph_resolver: factory.npm_graph_resolver().clone(),
|
||||
npm_pkg_req_resolver,
|
||||
npm_resolver,
|
||||
npm_installer,
|
||||
node_resolver,
|
||||
pkg_json_resolver,
|
||||
redirect_resolver,
|
||||
|
@ -231,6 +246,9 @@ impl LspScopeResolver {
|
|||
in_npm_pkg_checker: factory.in_npm_pkg_checker().clone(),
|
||||
is_cjs_resolver: factory.is_cjs_resolver().clone(),
|
||||
jsr_resolver: self.jsr_resolver.clone(),
|
||||
npm_graph_resolver: factory.npm_graph_resolver().clone(),
|
||||
// npm installer isn't necessary for a snapshot
|
||||
npm_installer: None,
|
||||
npm_pkg_req_resolver: factory.npm_pkg_req_resolver().cloned(),
|
||||
npm_resolver: factory.npm_resolver().cloned(),
|
||||
node_resolver: factory.node_resolver().cloned(),
|
||||
|
@ -318,14 +336,12 @@ impl LspResolver {
|
|||
if let Some(dep_info) = dep_info {
|
||||
*resolver.dep_info.lock() = dep_info.clone();
|
||||
}
|
||||
if let Some(npm_resolver) = resolver.npm_resolver.as_ref() {
|
||||
if let Some(npm_resolver) = npm_resolver.as_managed() {
|
||||
let reqs = dep_info
|
||||
.map(|i| i.npm_reqs.iter().cloned().collect::<Vec<_>>())
|
||||
.unwrap_or_default();
|
||||
if let Err(err) = npm_resolver.set_package_reqs(&reqs).await {
|
||||
lsp_warn!("Could not set npm package requirements: {:#}", err);
|
||||
}
|
||||
if let Some(npm_installer) = resolver.npm_installer.as_ref() {
|
||||
let reqs = dep_info
|
||||
.map(|i| i.npm_reqs.iter().cloned().collect::<Vec<_>>())
|
||||
.unwrap_or_default();
|
||||
if let Err(err) = npm_installer.set_package_reqs(&reqs).await {
|
||||
lsp_warn!("Could not set npm package requirements: {:#}", err);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -339,14 +355,12 @@ impl LspResolver {
|
|||
resolver.resolver.as_ref()
|
||||
}
|
||||
|
||||
pub fn create_graph_npm_resolver(
|
||||
pub fn as_graph_npm_resolver(
|
||||
&self,
|
||||
file_referrer: Option<&ModuleSpecifier>,
|
||||
) -> WorkerCliNpmGraphResolver {
|
||||
) -> &Arc<CliNpmGraphResolver> {
|
||||
let resolver = self.get_scope_resolver(file_referrer);
|
||||
resolver
|
||||
.resolver
|
||||
.create_graph_npm_resolver(crate::graph_util::NpmCachingStrategy::Eager)
|
||||
&resolver.npm_graph_resolver
|
||||
}
|
||||
|
||||
pub fn as_is_cjs_resolver(
|
||||
|
@ -590,9 +604,12 @@ pub struct ScopeDepInfo {
|
|||
#[derive(Default)]
|
||||
struct ResolverFactoryServices {
|
||||
cli_resolver: Deferred<Arc<CliResolver>>,
|
||||
found_pkg_json_dep_flag: Arc<FoundPackageJsonDepFlag>,
|
||||
in_npm_pkg_checker: Deferred<Arc<dyn InNpmPackageChecker>>,
|
||||
is_cjs_resolver: Deferred<Arc<IsCjsResolver>>,
|
||||
node_resolver: Deferred<Option<Arc<CliNodeResolver>>>,
|
||||
npm_graph_resolver: Deferred<Arc<CliNpmGraphResolver>>,
|
||||
npm_installer: Option<Arc<NpmInstaller>>,
|
||||
npm_pkg_req_resolver: Deferred<Option<Arc<CliNpmReqResolver>>>,
|
||||
npm_resolver: Option<Arc<dyn CliNpmResolver>>,
|
||||
}
|
||||
|
@ -616,6 +633,10 @@ impl<'a> ResolverFactory<'a> {
|
|||
}
|
||||
}
|
||||
|
||||
// todo(dsherret): probably this method could be removed in the future
|
||||
// and instead just `npm_resolution_initializer.ensure_initialized()` could
|
||||
// be called. The reason this exists is because creating the npm resolvers
|
||||
// used to be async.
|
||||
async fn init_npm_resolver(
|
||||
&mut self,
|
||||
http_client_provider: &Arc<HttpClientProvider>,
|
||||
|
@ -645,11 +666,31 @@ impl<'a> ResolverFactory<'a> {
|
|||
cache.deno_dir().npm_folder_path(),
|
||||
npmrc.get_all_known_registries_urls(),
|
||||
));
|
||||
CliNpmResolverCreateOptions::Managed(CliManagedNpmResolverCreateOptions {
|
||||
http_client_provider: http_client_provider.clone(),
|
||||
// only used for top level install, so we can ignore this
|
||||
npm_install_deps_provider: Arc::new(NpmInstallDepsProvider::empty()),
|
||||
snapshot: match self.config_data.and_then(|d| d.lockfile.as_ref()) {
|
||||
let npm_cache = Arc::new(CliNpmCache::new(
|
||||
npm_cache_dir.clone(),
|
||||
sys.clone(),
|
||||
// Use an "only" cache setting in order to make the
|
||||
// user do an explicit "cache" command and prevent
|
||||
// the cache from being filled with lots of packages while
|
||||
// the user is typing.
|
||||
deno_npm_cache::NpmCacheSetting::Only,
|
||||
npmrc.clone(),
|
||||
));
|
||||
let pb = ProgressBar::new(ProgressBarStyle::TextOnly);
|
||||
let npm_client = Arc::new(CliNpmCacheHttpClient::new(
|
||||
http_client_provider.clone(),
|
||||
pb.clone(),
|
||||
));
|
||||
let registry_info_provider = Arc::new(CliNpmRegistryInfoProvider::new(
|
||||
npm_cache.clone(),
|
||||
npm_client.clone(),
|
||||
npmrc.clone(),
|
||||
));
|
||||
let npm_resolution = Arc::new(NpmResolutionCell::default());
|
||||
let npm_resolution_initializer = Arc::new(NpmResolutionInitializer::new(
|
||||
registry_info_provider.clone(),
|
||||
npm_resolution.clone(),
|
||||
match self.config_data.and_then(|d| d.lockfile.as_ref()) {
|
||||
Some(lockfile) => {
|
||||
CliNpmResolverManagedSnapshotOption::ResolveFromLockfile(
|
||||
lockfile.clone(),
|
||||
|
@ -657,26 +698,62 @@ impl<'a> ResolverFactory<'a> {
|
|||
}
|
||||
None => CliNpmResolverManagedSnapshotOption::Specified(None),
|
||||
},
|
||||
));
|
||||
// Don't provide the lockfile. We don't want these resolvers
|
||||
// updating it. Only the cache request should update the lockfile.
|
||||
let maybe_lockfile: Option<Arc<CliLockfile>> = None;
|
||||
let maybe_node_modules_path =
|
||||
self.config_data.and_then(|d| d.node_modules_dir.clone());
|
||||
let tarball_cache = Arc::new(TarballCache::new(
|
||||
npm_cache.clone(),
|
||||
npm_client.clone(),
|
||||
sys.clone(),
|
||||
npmrc.clone(),
|
||||
));
|
||||
let npm_resolution_installer = Arc::new(NpmResolutionInstaller::new(
|
||||
registry_info_provider,
|
||||
npm_resolution.clone(),
|
||||
maybe_lockfile.clone(),
|
||||
));
|
||||
let npm_installer = Arc::new(NpmInstaller::new(
|
||||
npm_cache.clone(),
|
||||
Arc::new(NpmInstallDepsProvider::empty()),
|
||||
npm_resolution.clone(),
|
||||
npm_resolution_initializer.clone(),
|
||||
npm_resolution_installer,
|
||||
&pb,
|
||||
sys.clone(),
|
||||
tarball_cache.clone(),
|
||||
maybe_lockfile,
|
||||
maybe_node_modules_path.clone(),
|
||||
LifecycleScriptsConfig::default(),
|
||||
NpmSystemInfo::default(),
|
||||
));
|
||||
self.set_npm_installer(npm_installer);
|
||||
// spawn due to the lsp's `Send` requirement
|
||||
deno_core::unsync::spawn(async move {
|
||||
if let Err(err) = npm_resolution_initializer.ensure_initialized().await
|
||||
{
|
||||
log::warn!("failed to initialize npm resolution: {}", err);
|
||||
}
|
||||
})
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
CliNpmResolverCreateOptions::Managed(CliManagedNpmResolverCreateOptions {
|
||||
sys: CliSys::default(),
|
||||
npm_cache_dir,
|
||||
// Use an "only" cache setting in order to make the
|
||||
// user do an explicit "cache" command and prevent
|
||||
// the cache from being filled with lots of packages while
|
||||
// the user is typing.
|
||||
cache_setting: CacheSetting::Only,
|
||||
text_only_progress_bar: ProgressBar::new(ProgressBarStyle::TextOnly),
|
||||
// Don't provide the lockfile. We don't want these resolvers
|
||||
// updating it. Only the cache request should update the lockfile.
|
||||
maybe_lockfile: None,
|
||||
maybe_node_modules_path: self
|
||||
.config_data
|
||||
.and_then(|d| d.node_modules_dir.clone()),
|
||||
maybe_node_modules_path,
|
||||
npmrc,
|
||||
npm_resolution,
|
||||
npm_system_info: NpmSystemInfo::default(),
|
||||
lifecycle_scripts: Default::default(),
|
||||
})
|
||||
};
|
||||
self.set_npm_resolver(create_cli_npm_resolver_for_lsp(options).await);
|
||||
self.set_npm_resolver(create_cli_npm_resolver(options));
|
||||
}
|
||||
|
||||
pub fn set_npm_installer(&mut self, npm_installer: Arc<NpmInstaller>) {
|
||||
self.services.npm_installer = Some(npm_installer);
|
||||
}
|
||||
|
||||
pub fn set_npm_resolver(&mut self, npm_resolver: Arc<dyn CliNpmResolver>) {
|
||||
|
@ -720,13 +797,27 @@ impl<'a> ResolverFactory<'a> {
|
|||
is_byonm: self.config_data.map(|d| d.byonm).unwrap_or(false),
|
||||
maybe_vendor_dir: self.config_data.and_then(|d| d.vendor_dir.as_ref()),
|
||||
}));
|
||||
Arc::new(CliResolver::new(CliResolverOptions {
|
||||
Arc::new(CliResolver::new(
|
||||
deno_resolver,
|
||||
npm_resolver: self.npm_resolver().cloned(),
|
||||
bare_node_builtins_enabled: self
|
||||
self.services.found_pkg_json_dep_flag.clone(),
|
||||
))
|
||||
})
|
||||
}
|
||||
|
||||
pub fn npm_installer(&self) -> Option<&Arc<NpmInstaller>> {
|
||||
self.services.npm_installer.as_ref()
|
||||
}
|
||||
|
||||
pub fn npm_graph_resolver(&self) -> &Arc<CliNpmGraphResolver> {
|
||||
self.services.npm_graph_resolver.get_or_init(|| {
|
||||
Arc::new(CliNpmGraphResolver::new(
|
||||
None,
|
||||
self.services.found_pkg_json_dep_flag.clone(),
|
||||
self
|
||||
.config_data
|
||||
.is_some_and(|d| d.unstable.contains("bare-node-builtins")),
|
||||
}))
|
||||
NpmCachingStrategy::Eager,
|
||||
))
|
||||
})
|
||||
}
|
||||
|
||||
|
@ -783,6 +874,7 @@ impl<'a> ResolverFactory<'a> {
|
|||
npm_resolver.clone().into_npm_pkg_folder_resolver(),
|
||||
self.pkg_json_resolver.clone(),
|
||||
self.sys.clone(),
|
||||
node_resolver::ConditionsFromResolutionMode::default(),
|
||||
)))
|
||||
})
|
||||
.as_ref()
|
||||
|
|
|
@ -4509,11 +4509,12 @@ fn op_release(
|
|||
|
||||
#[op2]
|
||||
#[serde]
|
||||
#[allow(clippy::type_complexity)]
|
||||
fn op_resolve(
|
||||
state: &mut OpState,
|
||||
#[string] base: String,
|
||||
#[serde] specifiers: Vec<(bool, String)>,
|
||||
) -> Result<Vec<Option<(String, String)>>, deno_core::url::ParseError> {
|
||||
) -> Result<Vec<Option<(String, Option<String>)>>, deno_core::url::ParseError> {
|
||||
op_resolve_inner(state, ResolveArgs { base, specifiers })
|
||||
}
|
||||
|
||||
|
@ -4595,10 +4596,11 @@ async fn op_poll_requests(
|
|||
}
|
||||
|
||||
#[inline]
|
||||
#[allow(clippy::type_complexity)]
|
||||
fn op_resolve_inner(
|
||||
state: &mut OpState,
|
||||
args: ResolveArgs,
|
||||
) -> Result<Vec<Option<(String, String)>>, deno_core::url::ParseError> {
|
||||
) -> Result<Vec<Option<(String, Option<String>)>>, deno_core::url::ParseError> {
|
||||
let state = state.borrow_mut::<State>();
|
||||
let mark = state.performance.mark_with_args("tsc.op.op_resolve", &args);
|
||||
let referrer = state.specifier_map.normalize(&args.base)?;
|
||||
|
@ -4611,7 +4613,11 @@ fn op_resolve_inner(
|
|||
o.map(|(s, mt)| {
|
||||
(
|
||||
state.specifier_map.denormalize(&s),
|
||||
mt.as_ts_extension().to_string(),
|
||||
if matches!(mt, MediaType::Unknown) {
|
||||
None
|
||||
} else {
|
||||
Some(mt.as_ts_extension().to_string())
|
||||
},
|
||||
)
|
||||
})
|
||||
})
|
||||
|
@ -5583,7 +5589,6 @@ mod tests {
|
|||
})
|
||||
.to_string(),
|
||||
temp_dir.url().join("deno.json").unwrap(),
|
||||
&Default::default(),
|
||||
)
|
||||
.unwrap(),
|
||||
)
|
||||
|
@ -6461,7 +6466,7 @@ mod tests {
|
|||
resolved,
|
||||
vec![Some((
|
||||
temp_dir.url().join("b.ts").unwrap().to_string(),
|
||||
MediaType::TypeScript.as_ts_extension().to_string()
|
||||
Some(MediaType::TypeScript.as_ts_extension().to_string())
|
||||
))]
|
||||
);
|
||||
}
|
||||
|
|
|
@ -282,24 +282,26 @@ impl LspUrlMap {
|
|||
}
|
||||
}
|
||||
|
||||
/// Convert a e.g. `deno-notebook-cell:` specifier to a `file:` specifier.
|
||||
/// Convert a e.g. `vscode-notebook-cell:` specifier to a `file:` specifier.
|
||||
/// ```rust
|
||||
/// assert_eq!(
|
||||
/// file_like_to_file_specifier(
|
||||
/// &Url::parse("deno-notebook-cell:/path/to/file.ipynb#abc").unwrap(),
|
||||
/// &Url::parse("vscode-notebook-cell:/path/to/file.ipynb#abc").unwrap(),
|
||||
/// ),
|
||||
/// Some(Url::parse("file:///path/to/file.ipynb.ts?scheme=deno-notebook-cell#abc").unwrap()),
|
||||
/// Some(Url::parse("file:///path/to/file.ipynb?scheme=untitled#abc").unwrap()),
|
||||
/// );
|
||||
fn file_like_to_file_specifier(specifier: &Url) -> Option<Url> {
|
||||
if matches!(specifier.scheme(), "untitled" | "deno-notebook-cell") {
|
||||
if matches!(
|
||||
specifier.scheme(),
|
||||
"untitled" | "vscode-notebook-cell" | "deno-notebook-cell"
|
||||
) {
|
||||
if let Ok(mut s) = ModuleSpecifier::parse(&format!(
|
||||
"file://{}",
|
||||
"file:///{}",
|
||||
&specifier.as_str()[deno_core::url::quirks::internal_components(specifier)
|
||||
.host_end as usize..],
|
||||
.host_end as usize..].trim_start_matches('/'),
|
||||
)) {
|
||||
s.query_pairs_mut()
|
||||
.append_pair("scheme", specifier.scheme());
|
||||
s.set_path(&format!("{}.ts", s.path()));
|
||||
return Some(s);
|
||||
}
|
||||
}
|
||||
|
@ -432,11 +434,11 @@ mod tests {
|
|||
fn test_file_like_to_file_specifier() {
|
||||
assert_eq!(
|
||||
file_like_to_file_specifier(
|
||||
&Url::parse("deno-notebook-cell:/path/to/file.ipynb#abc").unwrap(),
|
||||
&Url::parse("vscode-notebook-cell:/path/to/file.ipynb#abc").unwrap(),
|
||||
),
|
||||
Some(
|
||||
Url::parse(
|
||||
"file:///path/to/file.ipynb.ts?scheme=deno-notebook-cell#abc"
|
||||
"file:///path/to/file.ipynb?scheme=vscode-notebook-cell#abc"
|
||||
)
|
||||
.unwrap()
|
||||
),
|
||||
|
@ -446,8 +448,7 @@ mod tests {
|
|||
&Url::parse("untitled:/path/to/file.ipynb#123").unwrap(),
|
||||
),
|
||||
Some(
|
||||
Url::parse("file:///path/to/file.ipynb.ts?scheme=untitled#123")
|
||||
.unwrap()
|
||||
Url::parse("file:///path/to/file.ipynb?scheme=untitled#123").unwrap()
|
||||
),
|
||||
);
|
||||
}
|
||||
|
|
12
cli/main.rs
12
cli/main.rs
|
@ -40,7 +40,6 @@ use deno_core::error::AnyError;
|
|||
use deno_core::error::CoreError;
|
||||
use deno_core::futures::FutureExt;
|
||||
use deno_core::unsync::JoinHandle;
|
||||
use deno_npm::resolution::SnapshotFromLockfileError;
|
||||
use deno_resolver::npm::ByonmResolvePkgFolderFromDenoReqError;
|
||||
use deno_resolver::npm::ResolvePkgFolderFromDenoReqError;
|
||||
use deno_runtime::fmt_errors::format_js_error;
|
||||
|
@ -52,6 +51,7 @@ use factory::CliFactory;
|
|||
use standalone::MODULE_NOT_FOUND;
|
||||
use standalone::UNSUPPORTED_SCHEME;
|
||||
|
||||
use self::npm::ResolveSnapshotError;
|
||||
use crate::args::flags_from_vec;
|
||||
use crate::args::DenoSubcommand;
|
||||
use crate::args::Flags;
|
||||
|
@ -376,13 +376,15 @@ fn exit_for_error(error: AnyError) -> ! {
|
|||
util::result::any_and_jserrorbox_downcast_ref::<CoreError>(&error)
|
||||
{
|
||||
error_string = format_js_error(e);
|
||||
} else if let Some(SnapshotFromLockfileError::IntegrityCheckFailed(e)) =
|
||||
util::result::any_and_jserrorbox_downcast_ref::<SnapshotFromLockfileError>(
|
||||
} else if let Some(e @ ResolveSnapshotError { .. }) =
|
||||
util::result::any_and_jserrorbox_downcast_ref::<ResolveSnapshotError>(
|
||||
&error,
|
||||
)
|
||||
{
|
||||
error_string = e.to_string();
|
||||
error_code = 10;
|
||||
if let Some(e) = e.maybe_integrity_check_error() {
|
||||
error_string = e.to_string();
|
||||
error_code = 10;
|
||||
}
|
||||
}
|
||||
|
||||
exit_with_message(&error_string, error_code);
|
||||
|
|
|
@ -8,8 +8,7 @@ use std::path::PathBuf;
|
|||
|
||||
use deno_npm::resolution::NpmResolutionSnapshot;
|
||||
use deno_npm::NpmPackageId;
|
||||
|
||||
use crate::npm::managed::NpmResolutionPackage;
|
||||
use deno_npm::NpmResolutionPackage;
|
||||
|
||||
#[derive(Default)]
|
||||
pub struct BinEntries<'a> {
|
|
@ -3,14 +3,14 @@
|
|||
use async_trait::async_trait;
|
||||
use deno_error::JsErrorBox;
|
||||
|
||||
use crate::npm::PackageCaching;
|
||||
use super::PackageCaching;
|
||||
|
||||
pub mod bin_entries;
|
||||
pub mod lifecycle_scripts;
|
||||
|
||||
/// Part of the resolution that interacts with the file system.
|
||||
#[async_trait(?Send)]
|
||||
pub trait NpmPackageFsInstaller: Send + Sync {
|
||||
pub trait NpmPackageFsInstaller: std::fmt::Debug + Send + Sync {
|
||||
async fn cache_packages<'a>(
|
||||
&self,
|
||||
caching: PackageCaching<'a>,
|
|
@ -11,14 +11,14 @@ use deno_core::futures::StreamExt;
|
|||
use deno_error::JsErrorBox;
|
||||
use deno_npm::NpmResolutionPackage;
|
||||
use deno_npm::NpmSystemInfo;
|
||||
use deno_resolver::npm::managed::NpmResolution;
|
||||
use deno_resolver::npm::managed::NpmResolutionCell;
|
||||
|
||||
use super::common::lifecycle_scripts::LifecycleScriptsStrategy;
|
||||
use super::common::NpmPackageFsInstaller;
|
||||
use super::PackageCaching;
|
||||
use crate::args::LifecycleScriptsConfig;
|
||||
use crate::cache::FastInsecureHasher;
|
||||
use crate::colors;
|
||||
use crate::npm::managed::PackageCaching;
|
||||
use crate::npm::CliNpmCache;
|
||||
use crate::npm::CliNpmTarballCache;
|
||||
|
||||
|
@ -27,25 +27,25 @@ use crate::npm::CliNpmTarballCache;
|
|||
pub struct GlobalNpmPackageInstaller {
|
||||
cache: Arc<CliNpmCache>,
|
||||
tarball_cache: Arc<CliNpmTarballCache>,
|
||||
resolution: Arc<NpmResolution>,
|
||||
system_info: NpmSystemInfo,
|
||||
resolution: Arc<NpmResolutionCell>,
|
||||
lifecycle_scripts: LifecycleScriptsConfig,
|
||||
system_info: NpmSystemInfo,
|
||||
}
|
||||
|
||||
impl GlobalNpmPackageInstaller {
|
||||
pub fn new(
|
||||
cache: Arc<CliNpmCache>,
|
||||
tarball_cache: Arc<CliNpmTarballCache>,
|
||||
resolution: Arc<NpmResolution>,
|
||||
system_info: NpmSystemInfo,
|
||||
resolution: Arc<NpmResolutionCell>,
|
||||
lifecycle_scripts: LifecycleScriptsConfig,
|
||||
system_info: NpmSystemInfo,
|
||||
) -> Self {
|
||||
Self {
|
||||
cache,
|
||||
tarball_cache,
|
||||
resolution,
|
||||
system_info,
|
||||
lifecycle_scripts,
|
||||
system_info,
|
||||
}
|
||||
}
|
||||
}
|
|
@ -25,7 +25,7 @@ use deno_npm::NpmResolutionPackage;
|
|||
use deno_npm::NpmSystemInfo;
|
||||
use deno_path_util::fs::atomic_write_file_with_retries;
|
||||
use deno_resolver::npm::get_package_folder_id_folder_name;
|
||||
use deno_resolver::npm::managed::NpmResolution;
|
||||
use deno_resolver::npm::managed::NpmResolutionCell;
|
||||
use deno_semver::package::PackageNv;
|
||||
use deno_semver::StackString;
|
||||
use serde::Deserialize;
|
||||
|
@ -33,11 +33,11 @@ use serde::Serialize;
|
|||
|
||||
use super::common::bin_entries;
|
||||
use super::common::NpmPackageFsInstaller;
|
||||
use super::PackageCaching;
|
||||
use crate::args::LifecycleScriptsConfig;
|
||||
use crate::args::NpmInstallDepsProvider;
|
||||
use crate::cache::CACHE_PERM;
|
||||
use crate::colors;
|
||||
use crate::npm::managed::PackageCaching;
|
||||
use crate::npm::CliNpmCache;
|
||||
use crate::npm::CliNpmTarballCache;
|
||||
use crate::sys::CliSys;
|
||||
|
@ -54,12 +54,12 @@ pub struct LocalNpmPackageInstaller {
|
|||
cache: Arc<CliNpmCache>,
|
||||
npm_install_deps_provider: Arc<NpmInstallDepsProvider>,
|
||||
progress_bar: ProgressBar,
|
||||
resolution: Arc<NpmResolution>,
|
||||
resolution: Arc<NpmResolutionCell>,
|
||||
sys: CliSys,
|
||||
tarball_cache: Arc<CliNpmTarballCache>,
|
||||
lifecycle_scripts: LifecycleScriptsConfig,
|
||||
root_node_modules_path: PathBuf,
|
||||
system_info: NpmSystemInfo,
|
||||
lifecycle_scripts: LifecycleScriptsConfig,
|
||||
}
|
||||
|
||||
impl LocalNpmPackageInstaller {
|
||||
|
@ -68,12 +68,12 @@ impl LocalNpmPackageInstaller {
|
|||
cache: Arc<CliNpmCache>,
|
||||
npm_install_deps_provider: Arc<NpmInstallDepsProvider>,
|
||||
progress_bar: ProgressBar,
|
||||
resolution: Arc<NpmResolution>,
|
||||
resolution: Arc<NpmResolutionCell>,
|
||||
sys: CliSys,
|
||||
tarball_cache: Arc<CliNpmTarballCache>,
|
||||
node_modules_folder: PathBuf,
|
||||
system_info: NpmSystemInfo,
|
||||
lifecycle_scripts: LifecycleScriptsConfig,
|
||||
system_info: NpmSystemInfo,
|
||||
) -> Self {
|
||||
Self {
|
||||
cache,
|
||||
|
@ -82,9 +82,9 @@ impl LocalNpmPackageInstaller {
|
|||
resolution,
|
||||
tarball_cache,
|
||||
sys,
|
||||
lifecycle_scripts,
|
||||
root_node_modules_path: node_modules_folder,
|
||||
system_info,
|
||||
lifecycle_scripts,
|
||||
}
|
||||
}
|
||||
}
|
283
cli/npm/installer/mod.rs
Normal file
283
cli/npm/installer/mod.rs
Normal file
|
@ -0,0 +1,283 @@
|
|||
// Copyright 2018-2025 the Deno authors. MIT license.
|
||||
|
||||
use std::borrow::Cow;
|
||||
use std::path::PathBuf;
|
||||
use std::sync::Arc;
|
||||
|
||||
use deno_core::error::AnyError;
|
||||
use deno_core::unsync::sync::AtomicFlag;
|
||||
use deno_error::JsErrorBox;
|
||||
use deno_npm::registry::NpmPackageInfo;
|
||||
use deno_npm::registry::NpmRegistryPackageInfoLoadError;
|
||||
use deno_npm::NpmSystemInfo;
|
||||
use deno_resolver::npm::managed::NpmResolutionCell;
|
||||
use deno_runtime::colors;
|
||||
use deno_semver::package::PackageReq;
|
||||
|
||||
pub use self::common::NpmPackageFsInstaller;
|
||||
use self::global::GlobalNpmPackageInstaller;
|
||||
use self::local::LocalNpmPackageInstaller;
|
||||
pub use self::resolution::AddPkgReqsResult;
|
||||
pub use self::resolution::NpmResolutionInstaller;
|
||||
use super::NpmResolutionInitializer;
|
||||
use crate::args::CliLockfile;
|
||||
use crate::args::LifecycleScriptsConfig;
|
||||
use crate::args::NpmInstallDepsProvider;
|
||||
use crate::args::PackageJsonDepValueParseWithLocationError;
|
||||
use crate::npm::CliNpmCache;
|
||||
use crate::npm::CliNpmTarballCache;
|
||||
use crate::sys::CliSys;
|
||||
use crate::util::progress_bar::ProgressBar;
|
||||
|
||||
mod common;
|
||||
mod global;
|
||||
mod local;
|
||||
mod resolution;
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
pub enum PackageCaching<'a> {
|
||||
Only(Cow<'a, [PackageReq]>),
|
||||
All,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct NpmInstaller {
|
||||
fs_installer: Arc<dyn NpmPackageFsInstaller>,
|
||||
npm_install_deps_provider: Arc<NpmInstallDepsProvider>,
|
||||
npm_resolution_initializer: Arc<NpmResolutionInitializer>,
|
||||
npm_resolution_installer: Arc<NpmResolutionInstaller>,
|
||||
maybe_lockfile: Option<Arc<CliLockfile>>,
|
||||
npm_resolution: Arc<NpmResolutionCell>,
|
||||
top_level_install_flag: AtomicFlag,
|
||||
}
|
||||
|
||||
impl NpmInstaller {
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
pub fn new(
|
||||
npm_cache: Arc<CliNpmCache>,
|
||||
npm_install_deps_provider: Arc<NpmInstallDepsProvider>,
|
||||
npm_resolution: Arc<NpmResolutionCell>,
|
||||
npm_resolution_initializer: Arc<NpmResolutionInitializer>,
|
||||
npm_resolution_installer: Arc<NpmResolutionInstaller>,
|
||||
progress_bar: &ProgressBar,
|
||||
sys: CliSys,
|
||||
tarball_cache: Arc<CliNpmTarballCache>,
|
||||
maybe_lockfile: Option<Arc<CliLockfile>>,
|
||||
maybe_node_modules_path: Option<PathBuf>,
|
||||
lifecycle_scripts: LifecycleScriptsConfig,
|
||||
system_info: NpmSystemInfo,
|
||||
) -> Self {
|
||||
let fs_installer: Arc<dyn NpmPackageFsInstaller> =
|
||||
match maybe_node_modules_path {
|
||||
Some(node_modules_folder) => Arc::new(LocalNpmPackageInstaller::new(
|
||||
npm_cache,
|
||||
npm_install_deps_provider.clone(),
|
||||
progress_bar.clone(),
|
||||
npm_resolution.clone(),
|
||||
sys,
|
||||
tarball_cache,
|
||||
node_modules_folder,
|
||||
lifecycle_scripts,
|
||||
system_info,
|
||||
)),
|
||||
None => Arc::new(GlobalNpmPackageInstaller::new(
|
||||
npm_cache,
|
||||
tarball_cache,
|
||||
npm_resolution.clone(),
|
||||
lifecycle_scripts,
|
||||
system_info,
|
||||
)),
|
||||
};
|
||||
Self {
|
||||
fs_installer,
|
||||
npm_install_deps_provider,
|
||||
npm_resolution,
|
||||
npm_resolution_initializer,
|
||||
npm_resolution_installer,
|
||||
maybe_lockfile,
|
||||
top_level_install_flag: Default::default(),
|
||||
}
|
||||
}
|
||||
|
||||
/// Adds package requirements to the resolver and ensures everything is setup.
|
||||
/// This includes setting up the `node_modules` directory, if applicable.
|
||||
pub async fn add_and_cache_package_reqs(
|
||||
&self,
|
||||
packages: &[PackageReq],
|
||||
) -> Result<(), JsErrorBox> {
|
||||
self.npm_resolution_initializer.ensure_initialized().await?;
|
||||
self
|
||||
.add_package_reqs_raw(
|
||||
packages,
|
||||
Some(PackageCaching::Only(packages.into())),
|
||||
)
|
||||
.await
|
||||
.dependencies_result
|
||||
}
|
||||
|
||||
pub async fn add_package_reqs_no_cache(
|
||||
&self,
|
||||
packages: &[PackageReq],
|
||||
) -> Result<(), JsErrorBox> {
|
||||
self.npm_resolution_initializer.ensure_initialized().await?;
|
||||
self
|
||||
.add_package_reqs_raw(packages, None)
|
||||
.await
|
||||
.dependencies_result
|
||||
}
|
||||
|
||||
pub async fn add_package_reqs(
|
||||
&self,
|
||||
packages: &[PackageReq],
|
||||
caching: PackageCaching<'_>,
|
||||
) -> Result<(), JsErrorBox> {
|
||||
self
|
||||
.add_package_reqs_raw(packages, Some(caching))
|
||||
.await
|
||||
.dependencies_result
|
||||
}
|
||||
|
||||
pub async fn add_package_reqs_raw<'a>(
|
||||
&self,
|
||||
packages: &[PackageReq],
|
||||
caching: Option<PackageCaching<'a>>,
|
||||
) -> AddPkgReqsResult {
|
||||
if packages.is_empty() {
|
||||
return AddPkgReqsResult {
|
||||
dependencies_result: Ok(()),
|
||||
results: vec![],
|
||||
};
|
||||
}
|
||||
|
||||
#[cfg(debug_assertions)]
|
||||
self.npm_resolution_initializer.debug_assert_initialized();
|
||||
|
||||
let mut result = self
|
||||
.npm_resolution_installer
|
||||
.add_package_reqs(packages)
|
||||
.await;
|
||||
|
||||
if result.dependencies_result.is_ok() {
|
||||
if let Some(lockfile) = self.maybe_lockfile.as_ref() {
|
||||
result.dependencies_result = lockfile.error_if_changed();
|
||||
}
|
||||
}
|
||||
if result.dependencies_result.is_ok() {
|
||||
if let Some(caching) = caching {
|
||||
result.dependencies_result = self.cache_packages(caching).await;
|
||||
}
|
||||
}
|
||||
|
||||
result
|
||||
}
|
||||
|
||||
/// Sets package requirements to the resolver, removing old requirements and adding new ones.
|
||||
///
|
||||
/// This will retrieve and resolve package information, but not cache any package files.
|
||||
pub async fn set_package_reqs(
|
||||
&self,
|
||||
packages: &[PackageReq],
|
||||
) -> Result<(), AnyError> {
|
||||
self
|
||||
.npm_resolution_installer
|
||||
.set_package_reqs(packages)
|
||||
.await
|
||||
}
|
||||
|
||||
pub async fn inject_synthetic_types_node_package(
|
||||
&self,
|
||||
) -> Result<(), JsErrorBox> {
|
||||
self.npm_resolution_initializer.ensure_initialized().await?;
|
||||
let reqs = &[PackageReq::from_str("@types/node").unwrap()];
|
||||
// add and ensure this isn't added to the lockfile
|
||||
self
|
||||
.add_package_reqs(reqs, PackageCaching::Only(reqs.into()))
|
||||
.await?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub async fn cache_package_info(
|
||||
&self,
|
||||
package_name: &str,
|
||||
) -> Result<Arc<NpmPackageInfo>, NpmRegistryPackageInfoLoadError> {
|
||||
self
|
||||
.npm_resolution_installer
|
||||
.cache_package_info(package_name)
|
||||
.await
|
||||
}
|
||||
|
||||
pub async fn cache_packages(
|
||||
&self,
|
||||
caching: PackageCaching<'_>,
|
||||
) -> Result<(), JsErrorBox> {
|
||||
self.npm_resolution_initializer.ensure_initialized().await?;
|
||||
self.fs_installer.cache_packages(caching).await
|
||||
}
|
||||
|
||||
pub fn ensure_no_pkg_json_dep_errors(
|
||||
&self,
|
||||
) -> Result<(), Box<PackageJsonDepValueParseWithLocationError>> {
|
||||
for err in self.npm_install_deps_provider.pkg_json_dep_errors() {
|
||||
match err.source.as_kind() {
|
||||
deno_package_json::PackageJsonDepValueParseErrorKind::VersionReq(_) => {
|
||||
return Err(Box::new(err.clone()));
|
||||
}
|
||||
deno_package_json::PackageJsonDepValueParseErrorKind::Unsupported {
|
||||
..
|
||||
} => {
|
||||
// only warn for this one
|
||||
log::warn!(
|
||||
"{} {}\n at {}",
|
||||
colors::yellow("Warning"),
|
||||
err.source,
|
||||
err.location,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Ensures that the top level `package.json` dependencies are installed.
|
||||
/// This may set up the `node_modules` directory.
|
||||
///
|
||||
/// Returns `true` if the top level packages are already installed. A
|
||||
/// return value of `false` means that new packages were added to the NPM resolution.
|
||||
pub async fn ensure_top_level_package_json_install(
|
||||
&self,
|
||||
) -> Result<bool, JsErrorBox> {
|
||||
if !self.top_level_install_flag.raise() {
|
||||
return Ok(true); // already did this
|
||||
}
|
||||
|
||||
self.npm_resolution_initializer.ensure_initialized().await?;
|
||||
|
||||
let pkg_json_remote_pkgs = self.npm_install_deps_provider.remote_pkgs();
|
||||
if pkg_json_remote_pkgs.is_empty() {
|
||||
return Ok(true);
|
||||
}
|
||||
|
||||
// check if something needs resolving before bothering to load all
|
||||
// the package information (which is slow)
|
||||
if pkg_json_remote_pkgs.iter().all(|pkg| {
|
||||
self
|
||||
.npm_resolution
|
||||
.resolve_pkg_id_from_pkg_req(&pkg.req)
|
||||
.is_ok()
|
||||
}) {
|
||||
log::debug!(
|
||||
"All package.json deps resolvable. Skipping top level install."
|
||||
);
|
||||
return Ok(true); // everything is already resolvable
|
||||
}
|
||||
|
||||
let pkg_reqs = pkg_json_remote_pkgs
|
||||
.iter()
|
||||
.map(|pkg| pkg.req.clone())
|
||||
.collect::<Vec<_>>();
|
||||
self.add_package_reqs_no_cache(&pkg_reqs).await?;
|
||||
|
||||
Ok(false)
|
||||
}
|
||||
}
|
|
@ -8,12 +8,14 @@ use deno_core::error::AnyError;
|
|||
use deno_error::JsErrorBox;
|
||||
use deno_lockfile::NpmPackageDependencyLockfileInfo;
|
||||
use deno_lockfile::NpmPackageLockfileInfo;
|
||||
use deno_npm::registry::NpmPackageInfo;
|
||||
use deno_npm::registry::NpmRegistryApi;
|
||||
use deno_npm::registry::NpmRegistryPackageInfoLoadError;
|
||||
use deno_npm::resolution::AddPkgReqsOptions;
|
||||
use deno_npm::resolution::NpmResolutionError;
|
||||
use deno_npm::resolution::NpmResolutionSnapshot;
|
||||
use deno_npm::NpmResolutionPackage;
|
||||
use deno_resolver::npm::managed::NpmResolution;
|
||||
use deno_resolver::npm::managed::NpmResolutionCell;
|
||||
use deno_semver::jsr::JsrDepPackageReq;
|
||||
use deno_semver::package::PackageNv;
|
||||
use deno_semver::package::PackageReq;
|
||||
|
@ -35,9 +37,10 @@ pub struct AddPkgReqsResult {
|
|||
}
|
||||
|
||||
/// Updates the npm resolution with the provided package requirements.
|
||||
#[derive(Debug)]
|
||||
pub struct NpmResolutionInstaller {
|
||||
registry_info_provider: Arc<CliNpmRegistryInfoProvider>,
|
||||
resolution: Arc<NpmResolution>,
|
||||
resolution: Arc<NpmResolutionCell>,
|
||||
maybe_lockfile: Option<Arc<CliLockfile>>,
|
||||
update_queue: TaskQueue,
|
||||
}
|
||||
|
@ -45,7 +48,7 @@ pub struct NpmResolutionInstaller {
|
|||
impl NpmResolutionInstaller {
|
||||
pub fn new(
|
||||
registry_info_provider: Arc<CliNpmRegistryInfoProvider>,
|
||||
resolution: Arc<NpmResolution>,
|
||||
resolution: Arc<NpmResolutionCell>,
|
||||
maybe_lockfile: Option<Arc<CliLockfile>>,
|
||||
) -> Self {
|
||||
Self {
|
||||
|
@ -56,6 +59,14 @@ impl NpmResolutionInstaller {
|
|||
}
|
||||
}
|
||||
|
||||
pub async fn cache_package_info(
|
||||
&self,
|
||||
package_name: &str,
|
||||
) -> Result<Arc<NpmPackageInfo>, NpmRegistryPackageInfoLoadError> {
|
||||
// this will internally cache the package information
|
||||
self.registry_info_provider.package_info(package_name).await
|
||||
}
|
||||
|
||||
pub async fn add_package_reqs(
|
||||
&self,
|
||||
package_reqs: &[PackageReq],
|
496
cli/npm/managed.rs
Normal file
496
cli/npm/managed.rs
Normal file
|
@ -0,0 +1,496 @@
|
|||
// Copyright 2018-2025 the Deno authors. MIT license.
|
||||
|
||||
use std::path::Path;
|
||||
use std::path::PathBuf;
|
||||
use std::sync::Arc;
|
||||
|
||||
use deno_ast::ModuleSpecifier;
|
||||
use deno_cache_dir::npm::NpmCacheDir;
|
||||
use deno_core::parking_lot::Mutex;
|
||||
use deno_core::serde_json;
|
||||
use deno_core::url::Url;
|
||||
use deno_error::JsError;
|
||||
use deno_error::JsErrorBox;
|
||||
use deno_npm::npm_rc::ResolvedNpmRc;
|
||||
use deno_npm::registry::NpmRegistryApi;
|
||||
use deno_npm::resolution::NpmResolutionSnapshot;
|
||||
use deno_npm::resolution::PackageReqNotFoundError;
|
||||
use deno_npm::resolution::ValidSerializedNpmResolutionSnapshot;
|
||||
use deno_npm::NpmPackageId;
|
||||
use deno_npm::NpmResolutionPackage;
|
||||
use deno_npm::NpmSystemInfo;
|
||||
use deno_resolver::npm::managed::NpmResolutionCell;
|
||||
use deno_resolver::npm::managed::ResolvePkgFolderFromDenoModuleError;
|
||||
use deno_resolver::npm::managed::ResolvePkgFolderFromPkgIdError;
|
||||
use deno_resolver::npm::managed::ResolvePkgIdFromSpecifierError;
|
||||
use deno_resolver::npm::ByonmOrManagedNpmResolver;
|
||||
use deno_resolver::npm::ManagedNpmResolver;
|
||||
use deno_resolver::npm::ResolvePkgFolderFromDenoReqError;
|
||||
use deno_runtime::ops::process::NpmProcessStateProvider;
|
||||
use deno_semver::package::PackageNv;
|
||||
use deno_semver::package::PackageReq;
|
||||
use node_resolver::NpmPackageFolderResolver;
|
||||
use sys_traits::FsMetadata;
|
||||
use thiserror::Error;
|
||||
|
||||
use super::CliNpmRegistryInfoProvider;
|
||||
use super::CliNpmResolver;
|
||||
use super::InnerCliNpmResolverRef;
|
||||
use crate::args::CliLockfile;
|
||||
use crate::args::NpmProcessState;
|
||||
use crate::args::NpmProcessStateKind;
|
||||
use crate::cache::FastInsecureHasher;
|
||||
use crate::sys::CliSys;
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub enum CliNpmResolverManagedSnapshotOption {
|
||||
ResolveFromLockfile(Arc<CliLockfile>),
|
||||
Specified(Option<ValidSerializedNpmResolutionSnapshot>),
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
enum SyncState {
|
||||
Pending(Option<CliNpmResolverManagedSnapshotOption>),
|
||||
Err(ResolveSnapshotError),
|
||||
Success,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct NpmResolutionInitializer {
|
||||
npm_registry_info_provider: Arc<CliNpmRegistryInfoProvider>,
|
||||
npm_resolution: Arc<NpmResolutionCell>,
|
||||
queue: tokio::sync::Mutex<()>,
|
||||
sync_state: Mutex<SyncState>,
|
||||
}
|
||||
|
||||
impl NpmResolutionInitializer {
|
||||
pub fn new(
|
||||
npm_registry_info_provider: Arc<CliNpmRegistryInfoProvider>,
|
||||
npm_resolution: Arc<NpmResolutionCell>,
|
||||
snapshot_option: CliNpmResolverManagedSnapshotOption,
|
||||
) -> Self {
|
||||
Self {
|
||||
npm_registry_info_provider,
|
||||
npm_resolution,
|
||||
queue: tokio::sync::Mutex::new(()),
|
||||
sync_state: Mutex::new(SyncState::Pending(Some(snapshot_option))),
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(debug_assertions)]
|
||||
pub fn debug_assert_initialized(&self) {
|
||||
if !matches!(*self.sync_state.lock(), SyncState::Success) {
|
||||
panic!("debug assert: npm resolution must be initialized before calling this code");
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn ensure_initialized(&self) -> Result<(), JsErrorBox> {
|
||||
// fast exit if not pending
|
||||
{
|
||||
match &*self.sync_state.lock() {
|
||||
SyncState::Pending(_) => {}
|
||||
SyncState::Err(err) => return Err(JsErrorBox::from_err(err.clone())),
|
||||
SyncState::Success => return Ok(()),
|
||||
}
|
||||
}
|
||||
|
||||
// only allow one task in here at a time
|
||||
let _guard = self.queue.lock().await;
|
||||
|
||||
let snapshot_option = {
|
||||
let mut sync_state = self.sync_state.lock();
|
||||
match &mut *sync_state {
|
||||
SyncState::Pending(snapshot_option) => {
|
||||
// this should never panic, but if it does it means that a
|
||||
// previous future was dropped while initialization occurred...
|
||||
// that should never happen because this is initialized during
|
||||
// startup
|
||||
snapshot_option.take().unwrap()
|
||||
}
|
||||
// another thread updated the state while we were waiting
|
||||
SyncState::Err(resolve_snapshot_error) => {
|
||||
return Err(JsErrorBox::from_err(resolve_snapshot_error.clone()));
|
||||
}
|
||||
SyncState::Success => {
|
||||
return Ok(());
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
match resolve_snapshot(&self.npm_registry_info_provider, snapshot_option)
|
||||
.await
|
||||
{
|
||||
Ok(maybe_snapshot) => {
|
||||
if let Some(snapshot) = maybe_snapshot {
|
||||
self
|
||||
.npm_resolution
|
||||
.set_snapshot(NpmResolutionSnapshot::new(snapshot));
|
||||
}
|
||||
let mut sync_state = self.sync_state.lock();
|
||||
*sync_state = SyncState::Success;
|
||||
Ok(())
|
||||
}
|
||||
Err(err) => {
|
||||
let mut sync_state = self.sync_state.lock();
|
||||
*sync_state = SyncState::Err(err.clone());
|
||||
Err(JsErrorBox::from_err(err))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub struct CliManagedNpmResolverCreateOptions {
|
||||
pub npm_cache_dir: Arc<NpmCacheDir>,
|
||||
pub sys: CliSys,
|
||||
pub maybe_node_modules_path: Option<PathBuf>,
|
||||
pub npm_system_info: NpmSystemInfo,
|
||||
pub npmrc: Arc<ResolvedNpmRc>,
|
||||
pub npm_resolution: Arc<NpmResolutionCell>,
|
||||
}
|
||||
|
||||
pub fn create_managed_npm_resolver(
|
||||
options: CliManagedNpmResolverCreateOptions,
|
||||
) -> Arc<dyn CliNpmResolver> {
|
||||
let managed_npm_resolver =
|
||||
Arc::new(ManagedNpmResolver::<CliSys>::new::<CliSys>(
|
||||
&options.npm_cache_dir,
|
||||
&options.npmrc,
|
||||
options.npm_resolution.clone(),
|
||||
options.sys.clone(),
|
||||
options.maybe_node_modules_path,
|
||||
));
|
||||
Arc::new(ManagedCliNpmResolver::new(
|
||||
managed_npm_resolver,
|
||||
options.npm_cache_dir,
|
||||
options.npmrc,
|
||||
options.npm_resolution,
|
||||
options.sys,
|
||||
options.npm_system_info,
|
||||
))
|
||||
}
|
||||
|
||||
#[derive(Debug, Error, Clone, JsError)]
|
||||
#[error("failed reading lockfile '{}'", lockfile_path.display())]
|
||||
#[class(inherit)]
|
||||
pub struct ResolveSnapshotError {
|
||||
lockfile_path: PathBuf,
|
||||
#[inherit]
|
||||
#[source]
|
||||
source: SnapshotFromLockfileError,
|
||||
}
|
||||
|
||||
impl ResolveSnapshotError {
|
||||
pub fn maybe_integrity_check_error(
|
||||
&self,
|
||||
) -> Option<&deno_npm::resolution::IntegrityCheckFailedError> {
|
||||
match &self.source {
|
||||
SnapshotFromLockfileError::SnapshotFromLockfile(
|
||||
deno_npm::resolution::SnapshotFromLockfileError::IntegrityCheckFailed(
|
||||
err,
|
||||
),
|
||||
) => Some(err),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async fn resolve_snapshot(
|
||||
registry_info_provider: &Arc<CliNpmRegistryInfoProvider>,
|
||||
snapshot: CliNpmResolverManagedSnapshotOption,
|
||||
) -> Result<Option<ValidSerializedNpmResolutionSnapshot>, ResolveSnapshotError>
|
||||
{
|
||||
match snapshot {
|
||||
CliNpmResolverManagedSnapshotOption::ResolveFromLockfile(lockfile) => {
|
||||
if !lockfile.overwrite() {
|
||||
let snapshot = snapshot_from_lockfile(
|
||||
lockfile.clone(),
|
||||
®istry_info_provider.as_npm_registry_api(),
|
||||
)
|
||||
.await
|
||||
.map_err(|source| ResolveSnapshotError {
|
||||
lockfile_path: lockfile.filename.clone(),
|
||||
source,
|
||||
})?;
|
||||
Ok(Some(snapshot))
|
||||
} else {
|
||||
Ok(None)
|
||||
}
|
||||
}
|
||||
CliNpmResolverManagedSnapshotOption::Specified(snapshot) => Ok(snapshot),
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Error, Clone, JsError)]
|
||||
pub enum SnapshotFromLockfileError {
|
||||
#[error(transparent)]
|
||||
#[class(inherit)]
|
||||
IncompleteError(
|
||||
#[from] deno_npm::resolution::IncompleteSnapshotFromLockfileError,
|
||||
),
|
||||
#[error(transparent)]
|
||||
#[class(inherit)]
|
||||
SnapshotFromLockfile(#[from] deno_npm::resolution::SnapshotFromLockfileError),
|
||||
}
|
||||
|
||||
async fn snapshot_from_lockfile(
|
||||
lockfile: Arc<CliLockfile>,
|
||||
api: &dyn NpmRegistryApi,
|
||||
) -> Result<ValidSerializedNpmResolutionSnapshot, SnapshotFromLockfileError> {
|
||||
let (incomplete_snapshot, skip_integrity_check) = {
|
||||
let lock = lockfile.lock();
|
||||
(
|
||||
deno_npm::resolution::incomplete_snapshot_from_lockfile(&lock)?,
|
||||
lock.overwrite,
|
||||
)
|
||||
};
|
||||
let snapshot = deno_npm::resolution::snapshot_from_lockfile(
|
||||
deno_npm::resolution::SnapshotFromLockfileParams {
|
||||
incomplete_snapshot,
|
||||
api,
|
||||
skip_integrity_check,
|
||||
},
|
||||
)
|
||||
.await?;
|
||||
Ok(snapshot)
|
||||
}
|
||||
|
||||
/// An npm resolver where the resolution is managed by Deno rather than
|
||||
/// the user bringing their own node_modules (BYONM) on the file system.
|
||||
pub struct ManagedCliNpmResolver {
|
||||
managed_npm_resolver: Arc<ManagedNpmResolver<CliSys>>,
|
||||
npm_cache_dir: Arc<NpmCacheDir>,
|
||||
npm_rc: Arc<ResolvedNpmRc>,
|
||||
sys: CliSys,
|
||||
resolution: Arc<NpmResolutionCell>,
|
||||
system_info: NpmSystemInfo,
|
||||
}
|
||||
|
||||
impl std::fmt::Debug for ManagedCliNpmResolver {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
f.debug_struct("ManagedCliNpmResolver")
|
||||
.field("<omitted>", &"<omitted>")
|
||||
.finish()
|
||||
}
|
||||
}
|
||||
|
||||
impl ManagedCliNpmResolver {
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
pub fn new(
|
||||
managed_npm_resolver: Arc<ManagedNpmResolver<CliSys>>,
|
||||
npm_cache_dir: Arc<NpmCacheDir>,
|
||||
npm_rc: Arc<ResolvedNpmRc>,
|
||||
resolution: Arc<NpmResolutionCell>,
|
||||
sys: CliSys,
|
||||
system_info: NpmSystemInfo,
|
||||
) -> Self {
|
||||
Self {
|
||||
managed_npm_resolver,
|
||||
npm_cache_dir,
|
||||
npm_rc,
|
||||
resolution,
|
||||
sys,
|
||||
system_info,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn resolve_pkg_folder_from_pkg_id(
|
||||
&self,
|
||||
pkg_id: &NpmPackageId,
|
||||
) -> Result<PathBuf, ResolvePkgFolderFromPkgIdError> {
|
||||
self
|
||||
.managed_npm_resolver
|
||||
.resolve_pkg_folder_from_pkg_id(pkg_id)
|
||||
}
|
||||
|
||||
/// Resolves the package id from the provided specifier.
|
||||
pub fn resolve_pkg_id_from_specifier(
|
||||
&self,
|
||||
specifier: &ModuleSpecifier,
|
||||
) -> Result<Option<NpmPackageId>, ResolvePkgIdFromSpecifierError> {
|
||||
self
|
||||
.managed_npm_resolver
|
||||
.resolve_pkg_id_from_specifier(specifier)
|
||||
}
|
||||
|
||||
pub fn resolve_pkg_reqs_from_pkg_id(
|
||||
&self,
|
||||
id: &NpmPackageId,
|
||||
) -> Vec<PackageReq> {
|
||||
self.resolution.resolve_pkg_reqs_from_pkg_id(id)
|
||||
}
|
||||
|
||||
pub fn all_system_packages(
|
||||
&self,
|
||||
system_info: &NpmSystemInfo,
|
||||
) -> Vec<NpmResolutionPackage> {
|
||||
self.resolution.all_system_packages(system_info)
|
||||
}
|
||||
|
||||
/// Checks if the provided package req's folder is cached.
|
||||
pub fn is_pkg_req_folder_cached(&self, req: &PackageReq) -> bool {
|
||||
self
|
||||
.resolve_pkg_id_from_pkg_req(req)
|
||||
.ok()
|
||||
.and_then(|id| {
|
||||
self
|
||||
.managed_npm_resolver
|
||||
.resolve_pkg_folder_from_pkg_id(&id)
|
||||
.ok()
|
||||
})
|
||||
.map(|folder| self.sys.fs_exists_no_err(folder))
|
||||
.unwrap_or(false)
|
||||
}
|
||||
|
||||
pub fn snapshot(&self) -> NpmResolutionSnapshot {
|
||||
self.resolution.snapshot()
|
||||
}
|
||||
|
||||
pub fn top_package_req_for_name(&self, name: &str) -> Option<PackageReq> {
|
||||
let package_reqs = self.resolution.package_reqs();
|
||||
let mut entries = package_reqs
|
||||
.iter()
|
||||
.filter(|(_, nv)| nv.name == name)
|
||||
.collect::<Vec<_>>();
|
||||
entries.sort_by_key(|(_, nv)| &nv.version);
|
||||
Some(entries.last()?.0.clone())
|
||||
}
|
||||
|
||||
pub fn serialized_valid_snapshot_for_system(
|
||||
&self,
|
||||
system_info: &NpmSystemInfo,
|
||||
) -> ValidSerializedNpmResolutionSnapshot {
|
||||
self
|
||||
.resolution
|
||||
.serialized_valid_snapshot_for_system(system_info)
|
||||
}
|
||||
|
||||
pub fn resolve_pkg_folder_from_deno_module(
|
||||
&self,
|
||||
nv: &PackageNv,
|
||||
) -> Result<PathBuf, ResolvePkgFolderFromDenoModuleError> {
|
||||
self
|
||||
.managed_npm_resolver
|
||||
.resolve_pkg_folder_from_deno_module(nv)
|
||||
}
|
||||
|
||||
pub fn resolve_pkg_id_from_pkg_req(
|
||||
&self,
|
||||
req: &PackageReq,
|
||||
) -> Result<NpmPackageId, PackageReqNotFoundError> {
|
||||
self.resolution.resolve_pkg_id_from_pkg_req(req)
|
||||
}
|
||||
|
||||
pub fn maybe_node_modules_path(&self) -> Option<&Path> {
|
||||
self.managed_npm_resolver.node_modules_path()
|
||||
}
|
||||
|
||||
pub fn global_cache_root_path(&self) -> &Path {
|
||||
self.npm_cache_dir.root_dir()
|
||||
}
|
||||
|
||||
pub fn global_cache_root_url(&self) -> &Url {
|
||||
self.npm_cache_dir.root_dir_url()
|
||||
}
|
||||
}
|
||||
|
||||
pub fn npm_process_state(
|
||||
snapshot: ValidSerializedNpmResolutionSnapshot,
|
||||
node_modules_path: Option<&Path>,
|
||||
) -> String {
|
||||
serde_json::to_string(&NpmProcessState {
|
||||
kind: NpmProcessStateKind::Snapshot(snapshot.into_serialized()),
|
||||
local_node_modules_path: node_modules_path
|
||||
.map(|p| p.to_string_lossy().to_string()),
|
||||
})
|
||||
.unwrap()
|
||||
}
|
||||
|
||||
impl NpmProcessStateProvider for ManagedCliNpmResolver {
|
||||
fn get_npm_process_state(&self) -> String {
|
||||
npm_process_state(
|
||||
self.resolution.serialized_valid_snapshot(),
|
||||
self.managed_npm_resolver.node_modules_path(),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
impl CliNpmResolver for ManagedCliNpmResolver {
|
||||
fn into_npm_pkg_folder_resolver(
|
||||
self: Arc<Self>,
|
||||
) -> Arc<dyn NpmPackageFolderResolver> {
|
||||
self.managed_npm_resolver.clone()
|
||||
}
|
||||
|
||||
fn into_process_state_provider(
|
||||
self: Arc<Self>,
|
||||
) -> Arc<dyn NpmProcessStateProvider> {
|
||||
self
|
||||
}
|
||||
|
||||
fn into_byonm_or_managed(
|
||||
self: Arc<Self>,
|
||||
) -> ByonmOrManagedNpmResolver<CliSys> {
|
||||
ByonmOrManagedNpmResolver::Managed(self.managed_npm_resolver.clone())
|
||||
}
|
||||
|
||||
fn clone_snapshotted(&self) -> Arc<dyn CliNpmResolver> {
|
||||
// create a new snapshotted npm resolution and resolver
|
||||
let npm_resolution =
|
||||
Arc::new(NpmResolutionCell::new(self.resolution.snapshot()));
|
||||
|
||||
Arc::new(ManagedCliNpmResolver::new(
|
||||
Arc::new(ManagedNpmResolver::<CliSys>::new::<CliSys>(
|
||||
&self.npm_cache_dir,
|
||||
&self.npm_rc,
|
||||
npm_resolution.clone(),
|
||||
self.sys.clone(),
|
||||
self.root_node_modules_path().map(ToOwned::to_owned),
|
||||
)),
|
||||
self.npm_cache_dir.clone(),
|
||||
self.npm_rc.clone(),
|
||||
npm_resolution,
|
||||
self.sys.clone(),
|
||||
self.system_info.clone(),
|
||||
))
|
||||
}
|
||||
|
||||
fn as_inner(&self) -> InnerCliNpmResolverRef {
|
||||
InnerCliNpmResolverRef::Managed(self)
|
||||
}
|
||||
|
||||
fn root_node_modules_path(&self) -> Option<&Path> {
|
||||
self.managed_npm_resolver.node_modules_path()
|
||||
}
|
||||
|
||||
fn check_state_hash(&self) -> Option<u64> {
|
||||
// We could go further and check all the individual
|
||||
// npm packages, but that's probably overkill.
|
||||
let mut package_reqs = self
|
||||
.resolution
|
||||
.package_reqs()
|
||||
.into_iter()
|
||||
.collect::<Vec<_>>();
|
||||
package_reqs.sort_by(|a, b| a.0.cmp(&b.0)); // determinism
|
||||
let mut hasher = FastInsecureHasher::new_without_deno_version();
|
||||
// ensure the cache gets busted when turning nodeModulesDir on or off
|
||||
// as this could cause changes in resolution
|
||||
hasher
|
||||
.write_hashable(self.managed_npm_resolver.node_modules_path().is_some());
|
||||
for (pkg_req, pkg_nv) in package_reqs {
|
||||
hasher.write_hashable(&pkg_req);
|
||||
hasher.write_hashable(&pkg_nv);
|
||||
}
|
||||
Some(hasher.finish())
|
||||
}
|
||||
|
||||
fn resolve_pkg_folder_from_deno_module_req(
|
||||
&self,
|
||||
req: &PackageReq,
|
||||
referrer: &Url,
|
||||
) -> Result<PathBuf, ResolvePkgFolderFromDenoReqError> {
|
||||
self
|
||||
.managed_npm_resolver
|
||||
.resolve_pkg_folder_from_deno_module_req(req, referrer)
|
||||
.map_err(ResolvePkgFolderFromDenoReqError::Managed)
|
||||
}
|
||||
}
|
|
@ -1,55 +0,0 @@
|
|||
// Copyright 2018-2025 the Deno authors. MIT license.
|
||||
|
||||
use std::path::PathBuf;
|
||||
use std::sync::Arc;
|
||||
|
||||
use deno_npm::NpmSystemInfo;
|
||||
use deno_resolver::npm::managed::NpmResolution;
|
||||
|
||||
pub use self::common::NpmPackageFsInstaller;
|
||||
use self::global::GlobalNpmPackageInstaller;
|
||||
use self::local::LocalNpmPackageInstaller;
|
||||
use crate::args::LifecycleScriptsConfig;
|
||||
use crate::args::NpmInstallDepsProvider;
|
||||
use crate::npm::CliNpmCache;
|
||||
use crate::npm::CliNpmTarballCache;
|
||||
use crate::sys::CliSys;
|
||||
use crate::util::progress_bar::ProgressBar;
|
||||
|
||||
mod common;
|
||||
mod global;
|
||||
mod local;
|
||||
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
pub fn create_npm_fs_installer(
|
||||
npm_cache: Arc<CliNpmCache>,
|
||||
npm_install_deps_provider: &Arc<NpmInstallDepsProvider>,
|
||||
progress_bar: &ProgressBar,
|
||||
resolution: Arc<NpmResolution>,
|
||||
sys: CliSys,
|
||||
tarball_cache: Arc<CliNpmTarballCache>,
|
||||
maybe_node_modules_path: Option<PathBuf>,
|
||||
system_info: NpmSystemInfo,
|
||||
lifecycle_scripts: LifecycleScriptsConfig,
|
||||
) -> Arc<dyn NpmPackageFsInstaller> {
|
||||
match maybe_node_modules_path {
|
||||
Some(node_modules_folder) => Arc::new(LocalNpmPackageInstaller::new(
|
||||
npm_cache,
|
||||
npm_install_deps_provider.clone(),
|
||||
progress_bar.clone(),
|
||||
resolution,
|
||||
sys,
|
||||
tarball_cache,
|
||||
node_modules_folder,
|
||||
system_info,
|
||||
lifecycle_scripts,
|
||||
)),
|
||||
None => Arc::new(GlobalNpmPackageInstaller::new(
|
||||
npm_cache,
|
||||
tarball_cache,
|
||||
resolution,
|
||||
system_info,
|
||||
lifecycle_scripts,
|
||||
)),
|
||||
}
|
||||
}
|
|
@ -1,768 +0,0 @@
|
|||
// Copyright 2018-2025 the Deno authors. MIT license.
|
||||
|
||||
use std::borrow::Cow;
|
||||
use std::path::Path;
|
||||
use std::path::PathBuf;
|
||||
use std::sync::Arc;
|
||||
|
||||
use deno_ast::ModuleSpecifier;
|
||||
use deno_cache_dir::npm::NpmCacheDir;
|
||||
use deno_core::anyhow::Context;
|
||||
use deno_core::error::AnyError;
|
||||
use deno_core::serde_json;
|
||||
use deno_core::url::Url;
|
||||
use deno_error::JsErrorBox;
|
||||
use deno_npm::npm_rc::ResolvedNpmRc;
|
||||
use deno_npm::registry::NpmPackageInfo;
|
||||
use deno_npm::registry::NpmRegistryApi;
|
||||
use deno_npm::resolution::NpmResolutionSnapshot;
|
||||
use deno_npm::resolution::PackageReqNotFoundError;
|
||||
use deno_npm::resolution::ValidSerializedNpmResolutionSnapshot;
|
||||
use deno_npm::NpmPackageId;
|
||||
use deno_npm::NpmResolutionPackage;
|
||||
use deno_npm::NpmSystemInfo;
|
||||
use deno_npm_cache::NpmCacheSetting;
|
||||
use deno_resolver::npm::managed::NpmResolution;
|
||||
use deno_resolver::npm::managed::ResolvePkgFolderFromPkgIdError;
|
||||
use deno_resolver::npm::ByonmOrManagedNpmResolver;
|
||||
use deno_resolver::npm::ManagedNpmResolver;
|
||||
use deno_resolver::npm::ResolvePkgFolderFromDenoReqError;
|
||||
use deno_runtime::colors;
|
||||
use deno_runtime::ops::process::NpmProcessStateProvider;
|
||||
use deno_semver::package::PackageNv;
|
||||
use deno_semver::package::PackageReq;
|
||||
use installer::AddPkgReqsResult;
|
||||
use installer::NpmResolutionInstaller;
|
||||
use installers::create_npm_fs_installer;
|
||||
use installers::NpmPackageFsInstaller;
|
||||
use node_resolver::NpmPackageFolderResolver;
|
||||
|
||||
use super::CliNpmCache;
|
||||
use super::CliNpmCacheHttpClient;
|
||||
use super::CliNpmRegistryInfoProvider;
|
||||
use super::CliNpmResolver;
|
||||
use super::CliNpmTarballCache;
|
||||
use super::InnerCliNpmResolverRef;
|
||||
use crate::args::CliLockfile;
|
||||
use crate::args::LifecycleScriptsConfig;
|
||||
use crate::args::NpmInstallDepsProvider;
|
||||
use crate::args::NpmProcessState;
|
||||
use crate::args::NpmProcessStateKind;
|
||||
use crate::args::PackageJsonDepValueParseWithLocationError;
|
||||
use crate::cache::FastInsecureHasher;
|
||||
use crate::sys::CliSys;
|
||||
use crate::util::progress_bar::ProgressBar;
|
||||
use crate::util::sync::AtomicFlag;
|
||||
|
||||
mod installer;
|
||||
mod installers;
|
||||
|
||||
pub enum CliNpmResolverManagedSnapshotOption {
|
||||
ResolveFromLockfile(Arc<CliLockfile>),
|
||||
Specified(Option<ValidSerializedNpmResolutionSnapshot>),
|
||||
}
|
||||
|
||||
pub struct CliManagedNpmResolverCreateOptions {
|
||||
pub snapshot: CliNpmResolverManagedSnapshotOption,
|
||||
pub maybe_lockfile: Option<Arc<CliLockfile>>,
|
||||
pub http_client_provider: Arc<crate::http_util::HttpClientProvider>,
|
||||
pub npm_cache_dir: Arc<NpmCacheDir>,
|
||||
pub sys: CliSys,
|
||||
pub cache_setting: deno_cache_dir::file_fetcher::CacheSetting,
|
||||
pub text_only_progress_bar: crate::util::progress_bar::ProgressBar,
|
||||
pub maybe_node_modules_path: Option<PathBuf>,
|
||||
pub npm_system_info: NpmSystemInfo,
|
||||
pub npm_install_deps_provider: Arc<NpmInstallDepsProvider>,
|
||||
pub npmrc: Arc<ResolvedNpmRc>,
|
||||
pub lifecycle_scripts: LifecycleScriptsConfig,
|
||||
}
|
||||
|
||||
pub async fn create_managed_npm_resolver_for_lsp(
|
||||
options: CliManagedNpmResolverCreateOptions,
|
||||
) -> Arc<dyn CliNpmResolver> {
|
||||
let npm_cache = create_cache(&options);
|
||||
let http_client = Arc::new(CliNpmCacheHttpClient::new(
|
||||
options.http_client_provider.clone(),
|
||||
options.text_only_progress_bar.clone(),
|
||||
));
|
||||
let npm_api = create_api(npm_cache.clone(), http_client.clone(), &options);
|
||||
// spawn due to the lsp's `Send` requirement
|
||||
deno_core::unsync::spawn(async move {
|
||||
let snapshot = match resolve_snapshot(&npm_api, options.snapshot).await {
|
||||
Ok(snapshot) => snapshot,
|
||||
Err(err) => {
|
||||
log::warn!("failed to resolve snapshot: {}", err);
|
||||
None
|
||||
}
|
||||
};
|
||||
create_inner(
|
||||
http_client,
|
||||
npm_cache,
|
||||
options.npm_cache_dir,
|
||||
options.npm_install_deps_provider,
|
||||
npm_api,
|
||||
options.sys,
|
||||
options.text_only_progress_bar,
|
||||
options.maybe_lockfile,
|
||||
options.npmrc,
|
||||
options.maybe_node_modules_path,
|
||||
options.npm_system_info,
|
||||
snapshot,
|
||||
options.lifecycle_scripts,
|
||||
)
|
||||
})
|
||||
.await
|
||||
.unwrap()
|
||||
}
|
||||
|
||||
pub async fn create_managed_npm_resolver(
|
||||
options: CliManagedNpmResolverCreateOptions,
|
||||
) -> Result<Arc<dyn CliNpmResolver>, AnyError> {
|
||||
let npm_cache = create_cache(&options);
|
||||
let http_client = Arc::new(CliNpmCacheHttpClient::new(
|
||||
options.http_client_provider.clone(),
|
||||
options.text_only_progress_bar.clone(),
|
||||
));
|
||||
let api = create_api(npm_cache.clone(), http_client.clone(), &options);
|
||||
let snapshot = resolve_snapshot(&api, options.snapshot).await?;
|
||||
Ok(create_inner(
|
||||
http_client,
|
||||
npm_cache,
|
||||
options.npm_cache_dir,
|
||||
options.npm_install_deps_provider,
|
||||
api,
|
||||
options.sys,
|
||||
options.text_only_progress_bar,
|
||||
options.maybe_lockfile,
|
||||
options.npmrc,
|
||||
options.maybe_node_modules_path,
|
||||
options.npm_system_info,
|
||||
snapshot,
|
||||
options.lifecycle_scripts,
|
||||
))
|
||||
}
|
||||
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
fn create_inner(
|
||||
http_client: Arc<CliNpmCacheHttpClient>,
|
||||
npm_cache: Arc<CliNpmCache>,
|
||||
npm_cache_dir: Arc<NpmCacheDir>,
|
||||
npm_install_deps_provider: Arc<NpmInstallDepsProvider>,
|
||||
registry_info_provider: Arc<CliNpmRegistryInfoProvider>,
|
||||
sys: CliSys,
|
||||
text_only_progress_bar: crate::util::progress_bar::ProgressBar,
|
||||
maybe_lockfile: Option<Arc<CliLockfile>>,
|
||||
npm_rc: Arc<ResolvedNpmRc>,
|
||||
node_modules_dir_path: Option<PathBuf>,
|
||||
npm_system_info: NpmSystemInfo,
|
||||
snapshot: Option<ValidSerializedNpmResolutionSnapshot>,
|
||||
lifecycle_scripts: LifecycleScriptsConfig,
|
||||
) -> Arc<dyn CliNpmResolver> {
|
||||
let resolution = Arc::new(NpmResolution::from_serialized(snapshot));
|
||||
let tarball_cache = Arc::new(CliNpmTarballCache::new(
|
||||
npm_cache.clone(),
|
||||
http_client,
|
||||
sys.clone(),
|
||||
npm_rc.clone(),
|
||||
));
|
||||
|
||||
let fs_installer = create_npm_fs_installer(
|
||||
npm_cache.clone(),
|
||||
&npm_install_deps_provider,
|
||||
&text_only_progress_bar,
|
||||
resolution.clone(),
|
||||
sys.clone(),
|
||||
tarball_cache.clone(),
|
||||
node_modules_dir_path.clone(),
|
||||
npm_system_info.clone(),
|
||||
lifecycle_scripts.clone(),
|
||||
);
|
||||
let managed_npm_resolver =
|
||||
Arc::new(ManagedNpmResolver::<CliSys>::new::<CliSys>(
|
||||
&npm_cache_dir,
|
||||
&npm_rc,
|
||||
resolution.clone(),
|
||||
sys.clone(),
|
||||
node_modules_dir_path,
|
||||
));
|
||||
Arc::new(ManagedCliNpmResolver::new(
|
||||
fs_installer,
|
||||
maybe_lockfile,
|
||||
managed_npm_resolver,
|
||||
npm_cache,
|
||||
npm_cache_dir,
|
||||
npm_install_deps_provider,
|
||||
npm_rc,
|
||||
registry_info_provider,
|
||||
resolution,
|
||||
sys,
|
||||
tarball_cache,
|
||||
text_only_progress_bar,
|
||||
npm_system_info,
|
||||
lifecycle_scripts,
|
||||
))
|
||||
}
|
||||
|
||||
fn create_cache(
|
||||
options: &CliManagedNpmResolverCreateOptions,
|
||||
) -> Arc<CliNpmCache> {
|
||||
Arc::new(CliNpmCache::new(
|
||||
options.npm_cache_dir.clone(),
|
||||
options.sys.clone(),
|
||||
NpmCacheSetting::from_cache_setting(&options.cache_setting),
|
||||
options.npmrc.clone(),
|
||||
))
|
||||
}
|
||||
|
||||
fn create_api(
|
||||
cache: Arc<CliNpmCache>,
|
||||
http_client: Arc<CliNpmCacheHttpClient>,
|
||||
options: &CliManagedNpmResolverCreateOptions,
|
||||
) -> Arc<CliNpmRegistryInfoProvider> {
|
||||
Arc::new(CliNpmRegistryInfoProvider::new(
|
||||
cache,
|
||||
http_client,
|
||||
options.npmrc.clone(),
|
||||
))
|
||||
}
|
||||
|
||||
async fn resolve_snapshot(
|
||||
registry_info_provider: &Arc<CliNpmRegistryInfoProvider>,
|
||||
snapshot: CliNpmResolverManagedSnapshotOption,
|
||||
) -> Result<Option<ValidSerializedNpmResolutionSnapshot>, AnyError> {
|
||||
match snapshot {
|
||||
CliNpmResolverManagedSnapshotOption::ResolveFromLockfile(lockfile) => {
|
||||
if !lockfile.overwrite() {
|
||||
let snapshot = snapshot_from_lockfile(
|
||||
lockfile.clone(),
|
||||
®istry_info_provider.as_npm_registry_api(),
|
||||
)
|
||||
.await
|
||||
.with_context(|| {
|
||||
format!("failed reading lockfile '{}'", lockfile.filename.display())
|
||||
})?;
|
||||
Ok(Some(snapshot))
|
||||
} else {
|
||||
Ok(None)
|
||||
}
|
||||
}
|
||||
CliNpmResolverManagedSnapshotOption::Specified(snapshot) => Ok(snapshot),
|
||||
}
|
||||
}
|
||||
|
||||
async fn snapshot_from_lockfile(
|
||||
lockfile: Arc<CliLockfile>,
|
||||
api: &dyn NpmRegistryApi,
|
||||
) -> Result<ValidSerializedNpmResolutionSnapshot, AnyError> {
|
||||
let (incomplete_snapshot, skip_integrity_check) = {
|
||||
let lock = lockfile.lock();
|
||||
(
|
||||
deno_npm::resolution::incomplete_snapshot_from_lockfile(&lock)?,
|
||||
lock.overwrite,
|
||||
)
|
||||
};
|
||||
let snapshot = deno_npm::resolution::snapshot_from_lockfile(
|
||||
deno_npm::resolution::SnapshotFromLockfileParams {
|
||||
incomplete_snapshot,
|
||||
api,
|
||||
skip_integrity_check,
|
||||
},
|
||||
)
|
||||
.await?;
|
||||
Ok(snapshot)
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
pub enum PackageCaching<'a> {
|
||||
Only(Cow<'a, [PackageReq]>),
|
||||
All,
|
||||
}
|
||||
|
||||
#[derive(Debug, thiserror::Error, deno_error::JsError)]
|
||||
pub enum ResolvePkgFolderFromDenoModuleError {
|
||||
#[class(inherit)]
|
||||
#[error(transparent)]
|
||||
PackageNvNotFound(#[from] deno_npm::resolution::PackageNvNotFoundError),
|
||||
#[class(inherit)]
|
||||
#[error(transparent)]
|
||||
ResolvePkgFolderFromPkgId(#[from] ResolvePkgFolderFromPkgIdError),
|
||||
}
|
||||
|
||||
/// An npm resolver where the resolution is managed by Deno rather than
|
||||
/// the user bringing their own node_modules (BYONM) on the file system.
|
||||
pub struct ManagedCliNpmResolver {
|
||||
fs_installer: Arc<dyn NpmPackageFsInstaller>,
|
||||
maybe_lockfile: Option<Arc<CliLockfile>>,
|
||||
registry_info_provider: Arc<CliNpmRegistryInfoProvider>,
|
||||
managed_npm_resolver: Arc<ManagedNpmResolver<CliSys>>,
|
||||
npm_cache: Arc<CliNpmCache>,
|
||||
npm_cache_dir: Arc<NpmCacheDir>,
|
||||
npm_install_deps_provider: Arc<NpmInstallDepsProvider>,
|
||||
npm_rc: Arc<ResolvedNpmRc>,
|
||||
sys: CliSys,
|
||||
resolution: Arc<NpmResolution>,
|
||||
resolution_installer: NpmResolutionInstaller,
|
||||
tarball_cache: Arc<CliNpmTarballCache>,
|
||||
text_only_progress_bar: ProgressBar,
|
||||
npm_system_info: NpmSystemInfo,
|
||||
top_level_install_flag: AtomicFlag,
|
||||
lifecycle_scripts: LifecycleScriptsConfig,
|
||||
}
|
||||
|
||||
impl std::fmt::Debug for ManagedCliNpmResolver {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
f.debug_struct("ManagedCliNpmResolver")
|
||||
.field("<omitted>", &"<omitted>")
|
||||
.finish()
|
||||
}
|
||||
}
|
||||
|
||||
impl ManagedCliNpmResolver {
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
pub fn new(
|
||||
fs_installer: Arc<dyn NpmPackageFsInstaller>,
|
||||
maybe_lockfile: Option<Arc<CliLockfile>>,
|
||||
managed_npm_resolver: Arc<ManagedNpmResolver<CliSys>>,
|
||||
npm_cache: Arc<CliNpmCache>,
|
||||
npm_cache_dir: Arc<NpmCacheDir>,
|
||||
npm_install_deps_provider: Arc<NpmInstallDepsProvider>,
|
||||
npm_rc: Arc<ResolvedNpmRc>,
|
||||
registry_info_provider: Arc<CliNpmRegistryInfoProvider>,
|
||||
resolution: Arc<NpmResolution>,
|
||||
sys: CliSys,
|
||||
tarball_cache: Arc<CliNpmTarballCache>,
|
||||
text_only_progress_bar: ProgressBar,
|
||||
npm_system_info: NpmSystemInfo,
|
||||
lifecycle_scripts: LifecycleScriptsConfig,
|
||||
) -> Self {
|
||||
let resolution_installer = NpmResolutionInstaller::new(
|
||||
registry_info_provider.clone(),
|
||||
resolution.clone(),
|
||||
maybe_lockfile.clone(),
|
||||
);
|
||||
Self {
|
||||
fs_installer,
|
||||
maybe_lockfile,
|
||||
managed_npm_resolver,
|
||||
npm_cache,
|
||||
npm_cache_dir,
|
||||
npm_install_deps_provider,
|
||||
npm_rc,
|
||||
registry_info_provider,
|
||||
text_only_progress_bar,
|
||||
resolution,
|
||||
resolution_installer,
|
||||
sys,
|
||||
tarball_cache,
|
||||
npm_system_info,
|
||||
top_level_install_flag: Default::default(),
|
||||
lifecycle_scripts,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn resolve_pkg_folder_from_pkg_id(
|
||||
&self,
|
||||
pkg_id: &NpmPackageId,
|
||||
) -> Result<PathBuf, ResolvePkgFolderFromPkgIdError> {
|
||||
self
|
||||
.managed_npm_resolver
|
||||
.resolve_pkg_folder_from_pkg_id(pkg_id)
|
||||
}
|
||||
|
||||
/// Resolves the package id from the provided specifier.
|
||||
pub fn resolve_pkg_id_from_specifier(
|
||||
&self,
|
||||
specifier: &ModuleSpecifier,
|
||||
) -> Result<Option<NpmPackageId>, AnyError> {
|
||||
let Some(cache_folder_id) = self
|
||||
.managed_npm_resolver
|
||||
.resolve_package_cache_folder_id_from_specifier(specifier)?
|
||||
else {
|
||||
return Ok(None);
|
||||
};
|
||||
Ok(Some(
|
||||
self
|
||||
.resolution
|
||||
.resolve_pkg_id_from_pkg_cache_folder_id(&cache_folder_id)?,
|
||||
))
|
||||
}
|
||||
|
||||
pub fn resolve_pkg_reqs_from_pkg_id(
|
||||
&self,
|
||||
id: &NpmPackageId,
|
||||
) -> Vec<PackageReq> {
|
||||
self.resolution.resolve_pkg_reqs_from_pkg_id(id)
|
||||
}
|
||||
|
||||
/// Attempts to get the package size in bytes.
|
||||
pub fn package_size(
|
||||
&self,
|
||||
package_id: &NpmPackageId,
|
||||
) -> Result<u64, AnyError> {
|
||||
let package_folder = self
|
||||
.managed_npm_resolver
|
||||
.resolve_pkg_folder_from_pkg_id(package_id)?;
|
||||
Ok(crate::util::fs::dir_size(&package_folder)?)
|
||||
}
|
||||
|
||||
pub fn all_system_packages(
|
||||
&self,
|
||||
system_info: &NpmSystemInfo,
|
||||
) -> Vec<NpmResolutionPackage> {
|
||||
self.resolution.all_system_packages(system_info)
|
||||
}
|
||||
|
||||
/// Checks if the provided package req's folder is cached.
|
||||
pub fn is_pkg_req_folder_cached(&self, req: &PackageReq) -> bool {
|
||||
self
|
||||
.resolve_pkg_id_from_pkg_req(req)
|
||||
.ok()
|
||||
.and_then(|id| {
|
||||
self
|
||||
.managed_npm_resolver
|
||||
.resolve_pkg_folder_from_pkg_id(&id)
|
||||
.ok()
|
||||
})
|
||||
.map(|folder| folder.exists())
|
||||
.unwrap_or(false)
|
||||
}
|
||||
|
||||
/// Adds package requirements to the resolver and ensures everything is setup.
|
||||
/// This includes setting up the `node_modules` directory, if applicable.
|
||||
pub async fn add_and_cache_package_reqs(
|
||||
&self,
|
||||
packages: &[PackageReq],
|
||||
) -> Result<(), JsErrorBox> {
|
||||
self
|
||||
.add_package_reqs_raw(
|
||||
packages,
|
||||
Some(PackageCaching::Only(packages.into())),
|
||||
)
|
||||
.await
|
||||
.dependencies_result
|
||||
}
|
||||
|
||||
pub async fn add_package_reqs_no_cache(
|
||||
&self,
|
||||
packages: &[PackageReq],
|
||||
) -> Result<(), JsErrorBox> {
|
||||
self
|
||||
.add_package_reqs_raw(packages, None)
|
||||
.await
|
||||
.dependencies_result
|
||||
}
|
||||
|
||||
pub async fn add_package_reqs(
|
||||
&self,
|
||||
packages: &[PackageReq],
|
||||
caching: PackageCaching<'_>,
|
||||
) -> Result<(), JsErrorBox> {
|
||||
self
|
||||
.add_package_reqs_raw(packages, Some(caching))
|
||||
.await
|
||||
.dependencies_result
|
||||
}
|
||||
|
||||
pub async fn add_package_reqs_raw<'a>(
|
||||
&self,
|
||||
packages: &[PackageReq],
|
||||
caching: Option<PackageCaching<'a>>,
|
||||
) -> AddPkgReqsResult {
|
||||
if packages.is_empty() {
|
||||
return AddPkgReqsResult {
|
||||
dependencies_result: Ok(()),
|
||||
results: vec![],
|
||||
};
|
||||
}
|
||||
|
||||
let mut result = self.resolution_installer.add_package_reqs(packages).await;
|
||||
|
||||
if result.dependencies_result.is_ok() {
|
||||
if let Some(lockfile) = self.maybe_lockfile.as_ref() {
|
||||
result.dependencies_result = lockfile.error_if_changed();
|
||||
}
|
||||
}
|
||||
if result.dependencies_result.is_ok() {
|
||||
if let Some(caching) = caching {
|
||||
result.dependencies_result = self.cache_packages(caching).await;
|
||||
}
|
||||
}
|
||||
|
||||
result
|
||||
}
|
||||
|
||||
/// Sets package requirements to the resolver, removing old requirements and adding new ones.
|
||||
///
|
||||
/// This will retrieve and resolve package information, but not cache any package files.
|
||||
pub async fn set_package_reqs(
|
||||
&self,
|
||||
packages: &[PackageReq],
|
||||
) -> Result<(), AnyError> {
|
||||
self.resolution_installer.set_package_reqs(packages).await
|
||||
}
|
||||
|
||||
pub fn snapshot(&self) -> NpmResolutionSnapshot {
|
||||
self.resolution.snapshot()
|
||||
}
|
||||
|
||||
pub fn top_package_req_for_name(&self, name: &str) -> Option<PackageReq> {
|
||||
let package_reqs = self.resolution.package_reqs();
|
||||
let mut entries = package_reqs
|
||||
.iter()
|
||||
.filter(|(_, nv)| nv.name == name)
|
||||
.collect::<Vec<_>>();
|
||||
entries.sort_by_key(|(_, nv)| &nv.version);
|
||||
Some(entries.last()?.0.clone())
|
||||
}
|
||||
|
||||
pub fn serialized_valid_snapshot_for_system(
|
||||
&self,
|
||||
system_info: &NpmSystemInfo,
|
||||
) -> ValidSerializedNpmResolutionSnapshot {
|
||||
self
|
||||
.resolution
|
||||
.serialized_valid_snapshot_for_system(system_info)
|
||||
}
|
||||
|
||||
pub async fn inject_synthetic_types_node_package(
|
||||
&self,
|
||||
) -> Result<(), JsErrorBox> {
|
||||
let reqs = &[PackageReq::from_str("@types/node").unwrap()];
|
||||
// add and ensure this isn't added to the lockfile
|
||||
self
|
||||
.add_package_reqs(reqs, PackageCaching::Only(reqs.into()))
|
||||
.await?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub async fn cache_packages(
|
||||
&self,
|
||||
caching: PackageCaching<'_>,
|
||||
) -> Result<(), JsErrorBox> {
|
||||
self.fs_installer.cache_packages(caching).await
|
||||
}
|
||||
|
||||
pub fn resolve_pkg_folder_from_deno_module(
|
||||
&self,
|
||||
nv: &PackageNv,
|
||||
) -> Result<PathBuf, ResolvePkgFolderFromDenoModuleError> {
|
||||
let pkg_id = self.resolution.resolve_pkg_id_from_deno_module(nv)?;
|
||||
Ok(self.resolve_pkg_folder_from_pkg_id(&pkg_id)?)
|
||||
}
|
||||
|
||||
pub fn resolve_pkg_id_from_pkg_req(
|
||||
&self,
|
||||
req: &PackageReq,
|
||||
) -> Result<NpmPackageId, PackageReqNotFoundError> {
|
||||
self.resolution.resolve_pkg_id_from_pkg_req(req)
|
||||
}
|
||||
|
||||
pub fn ensure_no_pkg_json_dep_errors(
|
||||
&self,
|
||||
) -> Result<(), Box<PackageJsonDepValueParseWithLocationError>> {
|
||||
for err in self.npm_install_deps_provider.pkg_json_dep_errors() {
|
||||
match err.source.as_kind() {
|
||||
deno_package_json::PackageJsonDepValueParseErrorKind::VersionReq(_) => {
|
||||
return Err(Box::new(err.clone()));
|
||||
}
|
||||
deno_package_json::PackageJsonDepValueParseErrorKind::Unsupported {
|
||||
..
|
||||
} => {
|
||||
// only warn for this one
|
||||
log::warn!(
|
||||
"{} {}\n at {}",
|
||||
colors::yellow("Warning"),
|
||||
err.source,
|
||||
err.location,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Ensures that the top level `package.json` dependencies are installed.
|
||||
/// This may set up the `node_modules` directory.
|
||||
///
|
||||
/// Returns `true` if the top level packages are already installed. A
|
||||
/// return value of `false` means that new packages were added to the NPM resolution.
|
||||
pub async fn ensure_top_level_package_json_install(
|
||||
&self,
|
||||
) -> Result<bool, JsErrorBox> {
|
||||
if !self.top_level_install_flag.raise() {
|
||||
return Ok(true); // already did this
|
||||
}
|
||||
|
||||
let pkg_json_remote_pkgs = self.npm_install_deps_provider.remote_pkgs();
|
||||
if pkg_json_remote_pkgs.is_empty() {
|
||||
return Ok(true);
|
||||
}
|
||||
|
||||
// check if something needs resolving before bothering to load all
|
||||
// the package information (which is slow)
|
||||
if pkg_json_remote_pkgs.iter().all(|pkg| {
|
||||
self
|
||||
.resolution
|
||||
.resolve_pkg_id_from_pkg_req(&pkg.req)
|
||||
.is_ok()
|
||||
}) {
|
||||
log::debug!(
|
||||
"All package.json deps resolvable. Skipping top level install."
|
||||
);
|
||||
return Ok(true); // everything is already resolvable
|
||||
}
|
||||
|
||||
let pkg_reqs = pkg_json_remote_pkgs
|
||||
.iter()
|
||||
.map(|pkg| pkg.req.clone())
|
||||
.collect::<Vec<_>>();
|
||||
self.add_package_reqs_no_cache(&pkg_reqs).await?;
|
||||
|
||||
Ok(false)
|
||||
}
|
||||
|
||||
pub async fn cache_package_info(
|
||||
&self,
|
||||
package_name: &str,
|
||||
) -> Result<Arc<NpmPackageInfo>, AnyError> {
|
||||
// this will internally cache the package information
|
||||
self
|
||||
.registry_info_provider
|
||||
.package_info(package_name)
|
||||
.await
|
||||
.map_err(|err| err.into())
|
||||
}
|
||||
|
||||
pub fn maybe_node_modules_path(&self) -> Option<&Path> {
|
||||
self.managed_npm_resolver.node_modules_path()
|
||||
}
|
||||
|
||||
pub fn global_cache_root_path(&self) -> &Path {
|
||||
self.npm_cache_dir.root_dir()
|
||||
}
|
||||
|
||||
pub fn global_cache_root_url(&self) -> &Url {
|
||||
self.npm_cache_dir.root_dir_url()
|
||||
}
|
||||
}
|
||||
|
||||
fn npm_process_state(
|
||||
snapshot: ValidSerializedNpmResolutionSnapshot,
|
||||
node_modules_path: Option<&Path>,
|
||||
) -> String {
|
||||
serde_json::to_string(&NpmProcessState {
|
||||
kind: NpmProcessStateKind::Snapshot(snapshot.into_serialized()),
|
||||
local_node_modules_path: node_modules_path
|
||||
.map(|p| p.to_string_lossy().to_string()),
|
||||
})
|
||||
.unwrap()
|
||||
}
|
||||
|
||||
impl NpmProcessStateProvider for ManagedCliNpmResolver {
|
||||
fn get_npm_process_state(&self) -> String {
|
||||
npm_process_state(
|
||||
self.resolution.serialized_valid_snapshot(),
|
||||
self.managed_npm_resolver.node_modules_path(),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
impl CliNpmResolver for ManagedCliNpmResolver {
|
||||
fn into_npm_pkg_folder_resolver(
|
||||
self: Arc<Self>,
|
||||
) -> Arc<dyn NpmPackageFolderResolver> {
|
||||
self.managed_npm_resolver.clone()
|
||||
}
|
||||
|
||||
fn into_process_state_provider(
|
||||
self: Arc<Self>,
|
||||
) -> Arc<dyn NpmProcessStateProvider> {
|
||||
self
|
||||
}
|
||||
|
||||
fn into_byonm_or_managed(
|
||||
self: Arc<Self>,
|
||||
) -> ByonmOrManagedNpmResolver<CliSys> {
|
||||
ByonmOrManagedNpmResolver::Managed(self.managed_npm_resolver.clone())
|
||||
}
|
||||
|
||||
fn clone_snapshotted(&self) -> Arc<dyn CliNpmResolver> {
|
||||
// create a new snapshotted npm resolution and resolver
|
||||
let npm_resolution =
|
||||
Arc::new(NpmResolution::new(self.resolution.snapshot()));
|
||||
|
||||
Arc::new(ManagedCliNpmResolver::new(
|
||||
create_npm_fs_installer(
|
||||
self.npm_cache.clone(),
|
||||
&self.npm_install_deps_provider,
|
||||
&self.text_only_progress_bar,
|
||||
npm_resolution.clone(),
|
||||
self.sys.clone(),
|
||||
self.tarball_cache.clone(),
|
||||
self.root_node_modules_path().map(ToOwned::to_owned),
|
||||
self.npm_system_info.clone(),
|
||||
self.lifecycle_scripts.clone(),
|
||||
),
|
||||
self.maybe_lockfile.clone(),
|
||||
Arc::new(ManagedNpmResolver::<CliSys>::new::<CliSys>(
|
||||
&self.npm_cache_dir,
|
||||
&self.npm_rc,
|
||||
npm_resolution.clone(),
|
||||
self.sys.clone(),
|
||||
self.root_node_modules_path().map(ToOwned::to_owned),
|
||||
)),
|
||||
self.npm_cache.clone(),
|
||||
self.npm_cache_dir.clone(),
|
||||
self.npm_install_deps_provider.clone(),
|
||||
self.npm_rc.clone(),
|
||||
self.registry_info_provider.clone(),
|
||||
npm_resolution,
|
||||
self.sys.clone(),
|
||||
self.tarball_cache.clone(),
|
||||
self.text_only_progress_bar.clone(),
|
||||
self.npm_system_info.clone(),
|
||||
self.lifecycle_scripts.clone(),
|
||||
))
|
||||
}
|
||||
|
||||
fn as_inner(&self) -> InnerCliNpmResolverRef {
|
||||
InnerCliNpmResolverRef::Managed(self)
|
||||
}
|
||||
|
||||
fn root_node_modules_path(&self) -> Option<&Path> {
|
||||
self.managed_npm_resolver.node_modules_path()
|
||||
}
|
||||
|
||||
fn check_state_hash(&self) -> Option<u64> {
|
||||
// We could go further and check all the individual
|
||||
// npm packages, but that's probably overkill.
|
||||
let mut package_reqs = self
|
||||
.resolution
|
||||
.package_reqs()
|
||||
.into_iter()
|
||||
.collect::<Vec<_>>();
|
||||
package_reqs.sort_by(|a, b| a.0.cmp(&b.0)); // determinism
|
||||
let mut hasher = FastInsecureHasher::new_without_deno_version();
|
||||
// ensure the cache gets busted when turning nodeModulesDir on or off
|
||||
// as this could cause changes in resolution
|
||||
hasher
|
||||
.write_hashable(self.managed_npm_resolver.node_modules_path().is_some());
|
||||
for (pkg_req, pkg_nv) in package_reqs {
|
||||
hasher.write_hashable(&pkg_req);
|
||||
hasher.write_hashable(&pkg_nv);
|
||||
}
|
||||
Some(hasher.finish())
|
||||
}
|
||||
|
||||
fn resolve_pkg_folder_from_deno_module_req(
|
||||
&self,
|
||||
req: &PackageReq,
|
||||
referrer: &Url,
|
||||
) -> Result<PathBuf, ResolvePkgFolderFromDenoReqError> {
|
||||
self
|
||||
.managed_npm_resolver
|
||||
.resolve_pkg_folder_from_deno_module_req(req, referrer)
|
||||
.map_err(ResolvePkgFolderFromDenoReqError::Managed)
|
||||
}
|
||||
}
|
|
@ -1,6 +1,7 @@
|
|||
// Copyright 2018-2025 the Deno authors. MIT license.
|
||||
|
||||
mod byonm;
|
||||
pub mod installer;
|
||||
mod managed;
|
||||
mod permission_checker;
|
||||
|
||||
|
@ -9,7 +10,6 @@ use std::path::PathBuf;
|
|||
use std::sync::Arc;
|
||||
|
||||
use dashmap::DashMap;
|
||||
use deno_core::error::AnyError;
|
||||
use deno_core::serde_json;
|
||||
use deno_core::url::Url;
|
||||
use deno_error::JsErrorBox;
|
||||
|
@ -30,8 +30,8 @@ pub use self::byonm::CliByonmNpmResolverCreateOptions;
|
|||
pub use self::managed::CliManagedNpmResolverCreateOptions;
|
||||
pub use self::managed::CliNpmResolverManagedSnapshotOption;
|
||||
pub use self::managed::ManagedCliNpmResolver;
|
||||
pub use self::managed::PackageCaching;
|
||||
pub use self::managed::ResolvePkgFolderFromDenoModuleError;
|
||||
pub use self::managed::NpmResolutionInitializer;
|
||||
pub use self::managed::ResolveSnapshotError;
|
||||
pub use self::permission_checker::NpmRegistryReadPermissionChecker;
|
||||
pub use self::permission_checker::NpmRegistryReadPermissionCheckerMode;
|
||||
use crate::file_fetcher::CliFileFetcher;
|
||||
|
@ -109,28 +109,16 @@ pub enum CliNpmResolverCreateOptions {
|
|||
Byonm(CliByonmNpmResolverCreateOptions),
|
||||
}
|
||||
|
||||
pub async fn create_cli_npm_resolver_for_lsp(
|
||||
pub fn create_cli_npm_resolver(
|
||||
options: CliNpmResolverCreateOptions,
|
||||
) -> Arc<dyn CliNpmResolver> {
|
||||
use CliNpmResolverCreateOptions::*;
|
||||
match options {
|
||||
Managed(options) => {
|
||||
managed::create_managed_npm_resolver_for_lsp(options).await
|
||||
}
|
||||
Managed(options) => managed::create_managed_npm_resolver(options),
|
||||
Byonm(options) => Arc::new(ByonmNpmResolver::new(options)),
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn create_cli_npm_resolver(
|
||||
options: CliNpmResolverCreateOptions,
|
||||
) -> Result<Arc<dyn CliNpmResolver>, AnyError> {
|
||||
use CliNpmResolverCreateOptions::*;
|
||||
match options {
|
||||
Managed(options) => managed::create_managed_npm_resolver(options).await,
|
||||
Byonm(options) => Ok(Arc::new(ByonmNpmResolver::new(options))),
|
||||
}
|
||||
}
|
||||
|
||||
pub enum InnerCliNpmResolverRef<'a> {
|
||||
Managed(&'a ManagedCliNpmResolver),
|
||||
#[allow(dead_code)]
|
||||
|
|
107
cli/resolver.rs
107
cli/resolver.rs
|
@ -33,8 +33,8 @@ use thiserror::Error;
|
|||
use crate::args::NpmCachingStrategy;
|
||||
use crate::args::DENO_DISABLE_PEDANTIC_NODE_WARNINGS;
|
||||
use crate::node::CliNodeCodeTranslator;
|
||||
use crate::npm::CliNpmResolver;
|
||||
use crate::npm::InnerCliNpmResolverRef;
|
||||
use crate::npm::installer::NpmInstaller;
|
||||
use crate::npm::installer::PackageCaching;
|
||||
use crate::sys::CliSys;
|
||||
use crate::util::sync::AtomicFlag;
|
||||
use crate::util::text_encoding::from_utf8_lossy_cow;
|
||||
|
@ -164,48 +164,30 @@ impl NpmModuleLoader {
|
|||
}
|
||||
}
|
||||
|
||||
pub struct CliResolverOptions {
|
||||
pub deno_resolver: Arc<CliDenoResolver>,
|
||||
pub npm_resolver: Option<Arc<dyn CliNpmResolver>>,
|
||||
pub bare_node_builtins_enabled: bool,
|
||||
}
|
||||
#[derive(Debug, Default)]
|
||||
pub struct FoundPackageJsonDepFlag(AtomicFlag);
|
||||
|
||||
/// A resolver that takes care of resolution, taking into account loaded
|
||||
/// import map, JSX settings.
|
||||
#[derive(Debug)]
|
||||
pub struct CliResolver {
|
||||
deno_resolver: Arc<CliDenoResolver>,
|
||||
npm_resolver: Option<Arc<dyn CliNpmResolver>>,
|
||||
found_package_json_dep_flag: AtomicFlag,
|
||||
bare_node_builtins_enabled: bool,
|
||||
found_package_json_dep_flag: Arc<FoundPackageJsonDepFlag>,
|
||||
warned_pkgs: DashSet<PackageReq>,
|
||||
}
|
||||
|
||||
impl CliResolver {
|
||||
pub fn new(options: CliResolverOptions) -> Self {
|
||||
pub fn new(
|
||||
deno_resolver: Arc<CliDenoResolver>,
|
||||
found_package_json_dep_flag: Arc<FoundPackageJsonDepFlag>,
|
||||
) -> Self {
|
||||
Self {
|
||||
deno_resolver: options.deno_resolver,
|
||||
npm_resolver: options.npm_resolver,
|
||||
found_package_json_dep_flag: Default::default(),
|
||||
bare_node_builtins_enabled: options.bare_node_builtins_enabled,
|
||||
deno_resolver,
|
||||
found_package_json_dep_flag,
|
||||
warned_pkgs: Default::default(),
|
||||
}
|
||||
}
|
||||
|
||||
// todo(dsherret): move this off CliResolver as CliResolver is acting
|
||||
// like a factory by doing this (it's beyond its responsibility)
|
||||
pub fn create_graph_npm_resolver(
|
||||
&self,
|
||||
npm_caching: NpmCachingStrategy,
|
||||
) -> WorkerCliNpmGraphResolver {
|
||||
WorkerCliNpmGraphResolver {
|
||||
npm_resolver: self.npm_resolver.as_ref(),
|
||||
found_package_json_dep_flag: &self.found_package_json_dep_flag,
|
||||
bare_node_builtins_enabled: self.bare_node_builtins_enabled,
|
||||
npm_caching,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn resolve(
|
||||
&self,
|
||||
raw_specifier: &str,
|
||||
|
@ -233,7 +215,7 @@ impl CliResolver {
|
|||
|
||||
if resolution.found_package_json_dep {
|
||||
// mark that we need to do an "npm install" later
|
||||
self.found_package_json_dep_flag.raise();
|
||||
self.found_package_json_dep_flag.0.raise();
|
||||
}
|
||||
|
||||
if let Some(diagnostic) = resolution.maybe_diagnostic {
|
||||
|
@ -260,15 +242,31 @@ impl CliResolver {
|
|||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct WorkerCliNpmGraphResolver<'a> {
|
||||
npm_resolver: Option<&'a Arc<dyn CliNpmResolver>>,
|
||||
found_package_json_dep_flag: &'a AtomicFlag,
|
||||
pub struct CliNpmGraphResolver {
|
||||
npm_installer: Option<Arc<NpmInstaller>>,
|
||||
found_package_json_dep_flag: Arc<FoundPackageJsonDepFlag>,
|
||||
bare_node_builtins_enabled: bool,
|
||||
npm_caching: NpmCachingStrategy,
|
||||
}
|
||||
|
||||
impl CliNpmGraphResolver {
|
||||
pub fn new(
|
||||
npm_installer: Option<Arc<NpmInstaller>>,
|
||||
found_package_json_dep_flag: Arc<FoundPackageJsonDepFlag>,
|
||||
bare_node_builtins_enabled: bool,
|
||||
npm_caching: NpmCachingStrategy,
|
||||
) -> Self {
|
||||
Self {
|
||||
npm_installer,
|
||||
found_package_json_dep_flag,
|
||||
bare_node_builtins_enabled,
|
||||
npm_caching,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[async_trait(?Send)]
|
||||
impl<'a> deno_graph::source::NpmResolver for WorkerCliNpmGraphResolver<'a> {
|
||||
impl deno_graph::source::NpmResolver for CliNpmGraphResolver {
|
||||
fn resolve_builtin_node_module(
|
||||
&self,
|
||||
specifier: &ModuleSpecifier,
|
||||
|
@ -298,17 +296,12 @@ impl<'a> deno_graph::source::NpmResolver for WorkerCliNpmGraphResolver<'a> {
|
|||
}
|
||||
|
||||
fn load_and_cache_npm_package_info(&self, package_name: &str) {
|
||||
match self.npm_resolver {
|
||||
Some(npm_resolver) if npm_resolver.as_managed().is_some() => {
|
||||
let npm_resolver = npm_resolver.clone();
|
||||
let package_name = package_name.to_string();
|
||||
deno_core::unsync::spawn(async move {
|
||||
if let Some(managed) = npm_resolver.as_managed() {
|
||||
let _ignore = managed.cache_package_info(&package_name).await;
|
||||
}
|
||||
});
|
||||
}
|
||||
_ => {}
|
||||
if let Some(npm_installer) = &self.npm_installer {
|
||||
let npm_installer = npm_installer.clone();
|
||||
let package_name = package_name.to_string();
|
||||
deno_core::unsync::spawn(async move {
|
||||
let _ignore = npm_installer.cache_package_info(&package_name).await;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -316,17 +309,11 @@ impl<'a> deno_graph::source::NpmResolver for WorkerCliNpmGraphResolver<'a> {
|
|||
&self,
|
||||
package_reqs: &[PackageReq],
|
||||
) -> NpmResolvePkgReqsResult {
|
||||
match &self.npm_resolver {
|
||||
Some(npm_resolver) => {
|
||||
let npm_resolver = match npm_resolver.as_inner() {
|
||||
InnerCliNpmResolverRef::Managed(npm_resolver) => npm_resolver,
|
||||
// if we are using byonm, then this should never be called because
|
||||
// we don't use deno_graph's npm resolution in this case
|
||||
InnerCliNpmResolverRef::Byonm(_) => unreachable!(),
|
||||
};
|
||||
|
||||
let top_level_result = if self.found_package_json_dep_flag.is_raised() {
|
||||
npm_resolver
|
||||
match &self.npm_installer {
|
||||
Some(npm_installer) => {
|
||||
let top_level_result = if self.found_package_json_dep_flag.0.is_raised()
|
||||
{
|
||||
npm_installer
|
||||
.ensure_top_level_package_json_install()
|
||||
.await
|
||||
.map(|_| ())
|
||||
|
@ -334,15 +321,13 @@ impl<'a> deno_graph::source::NpmResolver for WorkerCliNpmGraphResolver<'a> {
|
|||
Ok(())
|
||||
};
|
||||
|
||||
let result = npm_resolver
|
||||
let result = npm_installer
|
||||
.add_package_reqs_raw(
|
||||
package_reqs,
|
||||
match self.npm_caching {
|
||||
NpmCachingStrategy::Eager => {
|
||||
Some(crate::npm::PackageCaching::All)
|
||||
}
|
||||
NpmCachingStrategy::Eager => Some(PackageCaching::All),
|
||||
NpmCachingStrategy::Lazy => {
|
||||
Some(crate::npm::PackageCaching::Only(package_reqs.into()))
|
||||
Some(PackageCaching::Only(package_reqs.into()))
|
||||
}
|
||||
NpmCachingStrategy::Manual => None,
|
||||
},
|
||||
|
|
|
@ -73,6 +73,7 @@ use super::serialization::SourceMapStore;
|
|||
use super::virtual_fs::output_vfs;
|
||||
use super::virtual_fs::BuiltVfs;
|
||||
use super::virtual_fs::FileBackedVfs;
|
||||
use super::virtual_fs::FileSystemCaseSensitivity;
|
||||
use super::virtual_fs::VfsBuilder;
|
||||
use super::virtual_fs::VfsFileSubDataKind;
|
||||
use super::virtual_fs::VfsRoot;
|
||||
|
@ -202,6 +203,7 @@ pub struct Metadata {
|
|||
pub node_modules: Option<NodeModules>,
|
||||
pub unstable_config: UnstableConfig,
|
||||
pub otel_config: OtelConfig,
|
||||
pub vfs_case_sensitivity: FileSystemCaseSensitivity,
|
||||
}
|
||||
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
|
@ -379,7 +381,11 @@ pub fn extract_standalone(
|
|||
root_path: root_path.clone(),
|
||||
start_file_offset: 0,
|
||||
};
|
||||
Arc::new(FileBackedVfs::new(Cow::Borrowed(vfs_files_data), fs_root))
|
||||
Arc::new(FileBackedVfs::new(
|
||||
Cow::Borrowed(vfs_files_data),
|
||||
fs_root,
|
||||
metadata.vfs_case_sensitivity,
|
||||
))
|
||||
};
|
||||
Ok(Some(StandaloneData {
|
||||
metadata,
|
||||
|
@ -851,6 +857,7 @@ impl<'a> DenoCompileBinaryWriter<'a> {
|
|||
npm_lazy_caching: self.cli_options.unstable_npm_lazy_caching(),
|
||||
},
|
||||
otel_config: self.cli_options.otel_config(),
|
||||
vfs_case_sensitivity: vfs.case_sensitivity,
|
||||
};
|
||||
|
||||
write_binary_bytes(
|
||||
|
@ -984,11 +991,15 @@ impl<'a> DenoCompileBinaryWriter<'a> {
|
|||
|
||||
// it's better to not expose the user's cache directory, so take it out
|
||||
// of there
|
||||
let case_sensitivity = vfs.case_sensitivity();
|
||||
let parent = global_cache_root_path.parent().unwrap();
|
||||
let parent_dir = vfs.get_dir_mut(parent).unwrap();
|
||||
let index = parent_dir
|
||||
.entries
|
||||
.binary_search(DENO_COMPILE_GLOBAL_NODE_MODULES_DIR_NAME)
|
||||
.binary_search(
|
||||
DENO_COMPILE_GLOBAL_NODE_MODULES_DIR_NAME,
|
||||
case_sensitivity,
|
||||
)
|
||||
.unwrap();
|
||||
let npm_global_cache_dir_entry = parent_dir.entries.remove(index);
|
||||
|
||||
|
@ -996,9 +1007,19 @@ impl<'a> DenoCompileBinaryWriter<'a> {
|
|||
// this is not as optimized as it could be
|
||||
let mut last_name =
|
||||
Cow::Borrowed(DENO_COMPILE_GLOBAL_NODE_MODULES_DIR_NAME);
|
||||
for ancestor in parent.ancestors() {
|
||||
let dir = vfs.get_dir_mut(ancestor).unwrap();
|
||||
if let Ok(index) = dir.entries.binary_search(&last_name) {
|
||||
for ancestor in
|
||||
parent.ancestors().map(Some).chain(std::iter::once(None))
|
||||
{
|
||||
let dir = if let Some(ancestor) = ancestor {
|
||||
vfs.get_dir_mut(ancestor).unwrap()
|
||||
} else if cfg!(windows) {
|
||||
vfs.get_system_root_dir_mut()
|
||||
} else {
|
||||
break;
|
||||
};
|
||||
if let Ok(index) =
|
||||
dir.entries.binary_search(&last_name, case_sensitivity)
|
||||
{
|
||||
dir.entries.remove(index);
|
||||
}
|
||||
last_name = Cow::Owned(dir.name.clone());
|
||||
|
@ -1009,7 +1030,9 @@ impl<'a> DenoCompileBinaryWriter<'a> {
|
|||
|
||||
// now build the vfs and add the global cache dir entry there
|
||||
let mut built_vfs = vfs.build();
|
||||
built_vfs.entries.insert(npm_global_cache_dir_entry);
|
||||
built_vfs
|
||||
.entries
|
||||
.insert(npm_global_cache_dir_entry, case_sensitivity);
|
||||
built_vfs
|
||||
}
|
||||
InnerCliNpmResolverRef::Byonm(_) => vfs.build(),
|
||||
|
|
|
@ -37,10 +37,12 @@ use deno_core::ResolutionKind;
|
|||
use deno_core::SourceCodeCacheInfo;
|
||||
use deno_error::JsErrorBox;
|
||||
use deno_npm::npm_rc::ResolvedNpmRc;
|
||||
use deno_npm::resolution::NpmResolutionSnapshot;
|
||||
use deno_package_json::PackageJsonDepValue;
|
||||
use deno_resolver::cjs::IsCjsResolutionMode;
|
||||
use deno_resolver::npm::create_in_npm_pkg_checker;
|
||||
use deno_resolver::npm::managed::ManagedInNpmPkgCheckerCreateOptions;
|
||||
use deno_resolver::npm::managed::NpmResolutionCell;
|
||||
use deno_resolver::npm::CreateInNpmPkgCheckerOptions;
|
||||
use deno_resolver::npm::NpmReqResolverOptions;
|
||||
use deno_runtime::deno_fs;
|
||||
|
@ -91,6 +93,7 @@ use crate::npm::CliNpmResolverCreateOptions;
|
|||
use crate::npm::CliNpmResolverManagedSnapshotOption;
|
||||
use crate::npm::NpmRegistryReadPermissionChecker;
|
||||
use crate::npm::NpmRegistryReadPermissionCheckerMode;
|
||||
use crate::npm::NpmResolutionInitializer;
|
||||
use crate::resolver::CjsTracker;
|
||||
use crate::resolver::CliNpmReqResolver;
|
||||
use crate::resolver::NpmModuleLoader;
|
||||
|
@ -687,18 +690,12 @@ pub async fn run(
|
|||
ca_data: metadata.ca_data.map(CaData::Bytes),
|
||||
cell: Default::default(),
|
||||
});
|
||||
let progress_bar = ProgressBar::new(ProgressBarStyle::TextOnly);
|
||||
let http_client_provider = Arc::new(HttpClientProvider::new(
|
||||
Some(root_cert_store_provider.clone()),
|
||||
metadata.unsafely_ignore_certificate_errors.clone(),
|
||||
));
|
||||
// use a dummy npm registry url
|
||||
let npm_registry_url = ModuleSpecifier::parse("https://localhost/").unwrap();
|
||||
let root_dir_url =
|
||||
Arc::new(ModuleSpecifier::from_directory_path(&root_path).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 cache_setting = CacheSetting::Only;
|
||||
let pkg_json_resolver = Arc::new(CliPackageJsonResolver::new(sys.clone()));
|
||||
let npm_registry_permission_checker = {
|
||||
let mode = match &metadata.node_modules {
|
||||
|
@ -743,29 +740,19 @@ pub async fn run(
|
|||
maybe_node_modules_path: maybe_node_modules_path.as_deref(),
|
||||
},
|
||||
));
|
||||
let npm_resolution =
|
||||
Arc::new(NpmResolutionCell::new(NpmResolutionSnapshot::new(snapshot)));
|
||||
let npm_resolver =
|
||||
create_cli_npm_resolver(CliNpmResolverCreateOptions::Managed(
|
||||
CliManagedNpmResolverCreateOptions {
|
||||
snapshot: CliNpmResolverManagedSnapshotOption::Specified(Some(
|
||||
snapshot,
|
||||
)),
|
||||
maybe_lockfile: None,
|
||||
http_client_provider: http_client_provider.clone(),
|
||||
npm_resolution,
|
||||
npm_cache_dir,
|
||||
npm_install_deps_provider: Arc::new(
|
||||
// this is only used for installing packages, which isn't necessary with deno compile
|
||||
NpmInstallDepsProvider::empty(),
|
||||
),
|
||||
sys: sys.clone(),
|
||||
text_only_progress_bar: progress_bar,
|
||||
cache_setting,
|
||||
maybe_node_modules_path,
|
||||
npm_system_info: Default::default(),
|
||||
npmrc,
|
||||
lifecycle_scripts: Default::default(),
|
||||
},
|
||||
))
|
||||
.await?;
|
||||
));
|
||||
(in_npm_pkg_checker, npm_resolver)
|
||||
}
|
||||
Some(binary::NodeModules::Byonm {
|
||||
|
@ -781,8 +768,7 @@ pub async fn run(
|
|||
pkg_json_resolver: pkg_json_resolver.clone(),
|
||||
root_node_modules_dir,
|
||||
}),
|
||||
)
|
||||
.await?;
|
||||
);
|
||||
(in_npm_pkg_checker, npm_resolver)
|
||||
}
|
||||
None => {
|
||||
|
@ -801,27 +787,18 @@ pub async fn run(
|
|||
maybe_node_modules_path: None,
|
||||
},
|
||||
));
|
||||
let npm_resolution = Arc::new(NpmResolutionCell::default());
|
||||
let npm_resolver =
|
||||
create_cli_npm_resolver(CliNpmResolverCreateOptions::Managed(
|
||||
CliManagedNpmResolverCreateOptions {
|
||||
snapshot: CliNpmResolverManagedSnapshotOption::Specified(None),
|
||||
http_client_provider: http_client_provider.clone(),
|
||||
npm_install_deps_provider: Arc::new(
|
||||
// this is only used for installing packages, which isn't necessary with deno compile
|
||||
NpmInstallDepsProvider::empty(),
|
||||
),
|
||||
npm_resolution,
|
||||
sys: sys.clone(),
|
||||
cache_setting,
|
||||
text_only_progress_bar: progress_bar,
|
||||
npm_cache_dir,
|
||||
maybe_lockfile: None,
|
||||
maybe_node_modules_path: None,
|
||||
npm_system_info: Default::default(),
|
||||
npmrc: create_default_npmrc(),
|
||||
lifecycle_scripts: Default::default(),
|
||||
},
|
||||
))
|
||||
.await?;
|
||||
));
|
||||
(in_npm_pkg_checker, npm_resolver)
|
||||
}
|
||||
};
|
||||
|
@ -833,6 +810,7 @@ pub async fn run(
|
|||
npm_resolver.clone().into_npm_pkg_folder_resolver(),
|
||||
pkg_json_resolver.clone(),
|
||||
sys.clone(),
|
||||
node_resolver::ConditionsFromResolutionMode::default(),
|
||||
));
|
||||
let cjs_tracker = Arc::new(CjsTracker::new(
|
||||
in_npm_pkg_checker.clone(),
|
||||
|
@ -994,6 +972,7 @@ pub async fn run(
|
|||
None,
|
||||
Box::new(module_loader_factory),
|
||||
node_resolver,
|
||||
None,
|
||||
npm_resolver,
|
||||
pkg_json_resolver,
|
||||
root_cert_store_provider,
|
||||
|
|
|
@ -27,6 +27,7 @@ use indexmap::IndexMap;
|
|||
|
||||
use super::binary::Metadata;
|
||||
use super::virtual_fs::BuiltVfs;
|
||||
use super::virtual_fs::FileSystemCaseSensitivity;
|
||||
use super::virtual_fs::VfsBuilder;
|
||||
use super::virtual_fs::VirtualDirectoryEntries;
|
||||
use crate::standalone::virtual_fs::VirtualDirectory;
|
||||
|
|
|
@ -2,6 +2,7 @@
|
|||
|
||||
use std::borrow::Cow;
|
||||
use std::cell::RefCell;
|
||||
use std::cmp::Ordering;
|
||||
use std::collections::HashMap;
|
||||
use std::collections::HashSet;
|
||||
use std::fs::File;
|
||||
|
@ -74,6 +75,7 @@ impl WindowsSystemRootablePath {
|
|||
#[derive(Debug)]
|
||||
pub struct BuiltVfs {
|
||||
pub root_path: WindowsSystemRootablePath,
|
||||
pub case_sensitivity: FileSystemCaseSensitivity,
|
||||
pub entries: VirtualDirectoryEntries,
|
||||
pub files: Vec<Vec<u8>>,
|
||||
}
|
||||
|
@ -95,6 +97,7 @@ pub struct VfsBuilder {
|
|||
file_offsets: HashMap<String, u64>,
|
||||
/// The minimum root directory that should be included in the VFS.
|
||||
min_root_dir: Option<WindowsSystemRootablePath>,
|
||||
case_sensitivity: FileSystemCaseSensitivity,
|
||||
}
|
||||
|
||||
impl VfsBuilder {
|
||||
|
@ -108,9 +111,23 @@ impl VfsBuilder {
|
|||
current_offset: 0,
|
||||
file_offsets: Default::default(),
|
||||
min_root_dir: Default::default(),
|
||||
// This is not exactly correct because file systems on these OSes
|
||||
// may be case-sensitive or not based on the directory, but this
|
||||
// is a good enough approximation and limitation. In the future,
|
||||
// we may want to store this information per directory instead
|
||||
// depending on the feedback we get.
|
||||
case_sensitivity: if cfg!(windows) || cfg!(target_os = "macos") {
|
||||
FileSystemCaseSensitivity::Insensitive
|
||||
} else {
|
||||
FileSystemCaseSensitivity::Sensitive
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
pub fn case_sensitivity(&self) -> FileSystemCaseSensitivity {
|
||||
self.case_sensitivity
|
||||
}
|
||||
|
||||
/// Add a directory that might be the minimum root directory
|
||||
/// of the VFS.
|
||||
///
|
||||
|
@ -215,7 +232,10 @@ impl VfsBuilder {
|
|||
continue;
|
||||
}
|
||||
let name = component.as_os_str().to_string_lossy();
|
||||
let index = match current_dir.entries.binary_search(&name) {
|
||||
let index = match current_dir
|
||||
.entries
|
||||
.binary_search(&name, self.case_sensitivity)
|
||||
{
|
||||
Ok(index) => index,
|
||||
Err(insert_index) => {
|
||||
current_dir.entries.0.insert(
|
||||
|
@ -252,7 +272,9 @@ impl VfsBuilder {
|
|||
continue;
|
||||
}
|
||||
let name = component.as_os_str().to_string_lossy();
|
||||
let entry = current_dir.entries.get_mut_by_name(&name)?;
|
||||
let entry = current_dir
|
||||
.entries
|
||||
.get_mut_by_name(&name, self.case_sensitivity)?;
|
||||
match entry {
|
||||
VfsEntry::Dir(dir) => {
|
||||
current_dir = dir;
|
||||
|
@ -304,6 +326,7 @@ impl VfsBuilder {
|
|||
) -> Result<(), AnyError> {
|
||||
log::debug!("Adding file '{}'", path.display());
|
||||
let checksum = util::checksum::gen(&[&data]);
|
||||
let case_sensitivity = self.case_sensitivity;
|
||||
let offset = if let Some(offset) = self.file_offsets.get(&checksum) {
|
||||
// duplicate file, reuse an old offset
|
||||
*offset
|
||||
|
@ -318,7 +341,7 @@ impl VfsBuilder {
|
|||
offset,
|
||||
len: data.len() as u64,
|
||||
};
|
||||
match dir.entries.binary_search(&name) {
|
||||
match dir.entries.binary_search(&name, case_sensitivity) {
|
||||
Ok(index) => {
|
||||
let entry = &mut dir.entries.0[index];
|
||||
match entry {
|
||||
|
@ -379,10 +402,11 @@ impl VfsBuilder {
|
|||
std::fs::read_link(path)
|
||||
.with_context(|| format!("Reading symlink '{}'", path.display()))?,
|
||||
);
|
||||
let case_sensitivity = self.case_sensitivity;
|
||||
let target = normalize_path(path.parent().unwrap().join(&target));
|
||||
let dir = self.add_dir_raw(path.parent().unwrap());
|
||||
let name = path.file_name().unwrap().to_string_lossy();
|
||||
match dir.entries.binary_search(&name) {
|
||||
match dir.entries.binary_search(&name, case_sensitivity) {
|
||||
Ok(_) => {} // previously inserted
|
||||
Err(insert_index) => {
|
||||
dir.entries.0.insert(
|
||||
|
@ -478,6 +502,7 @@ impl VfsBuilder {
|
|||
}
|
||||
BuiltVfs {
|
||||
root_path: current_path,
|
||||
case_sensitivity: self.case_sensitivity,
|
||||
entries: current_dir.entries,
|
||||
files: self.files,
|
||||
}
|
||||
|
@ -906,6 +931,14 @@ impl VfsEntry {
|
|||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Copy, Clone, Serialize, Deserialize)]
|
||||
pub enum FileSystemCaseSensitivity {
|
||||
#[serde(rename = "s")]
|
||||
Sensitive,
|
||||
#[serde(rename = "i")]
|
||||
Insensitive,
|
||||
}
|
||||
|
||||
#[derive(Debug, Default, Serialize, Deserialize)]
|
||||
pub struct VirtualDirectoryEntries(Vec<VfsEntry>);
|
||||
|
||||
|
@ -928,23 +961,54 @@ impl VirtualDirectoryEntries {
|
|||
self.0.len()
|
||||
}
|
||||
|
||||
pub fn get_by_name(&self, name: &str) -> Option<&VfsEntry> {
|
||||
self.binary_search(name).ok().map(|index| &self.0[index])
|
||||
pub fn get_by_name(
|
||||
&self,
|
||||
name: &str,
|
||||
case_sensitivity: FileSystemCaseSensitivity,
|
||||
) -> Option<&VfsEntry> {
|
||||
self
|
||||
.binary_search(name, case_sensitivity)
|
||||
.ok()
|
||||
.map(|index| &self.0[index])
|
||||
}
|
||||
|
||||
pub fn get_mut_by_name(&mut self, name: &str) -> Option<&mut VfsEntry> {
|
||||
pub fn get_mut_by_name(
|
||||
&mut self,
|
||||
name: &str,
|
||||
case_sensitivity: FileSystemCaseSensitivity,
|
||||
) -> Option<&mut VfsEntry> {
|
||||
self
|
||||
.binary_search(name)
|
||||
.binary_search(name, case_sensitivity)
|
||||
.ok()
|
||||
.map(|index| &mut self.0[index])
|
||||
}
|
||||
|
||||
pub fn binary_search(&self, name: &str) -> Result<usize, usize> {
|
||||
self.0.binary_search_by(|e| e.name().cmp(name))
|
||||
pub fn binary_search(
|
||||
&self,
|
||||
name: &str,
|
||||
case_sensitivity: FileSystemCaseSensitivity,
|
||||
) -> Result<usize, usize> {
|
||||
match case_sensitivity {
|
||||
FileSystemCaseSensitivity::Sensitive => {
|
||||
self.0.binary_search_by(|e| e.name().cmp(name))
|
||||
}
|
||||
FileSystemCaseSensitivity::Insensitive => self.0.binary_search_by(|e| {
|
||||
e.name()
|
||||
.chars()
|
||||
.zip(name.chars())
|
||||
.map(|(a, b)| a.to_ascii_lowercase().cmp(&b.to_ascii_lowercase()))
|
||||
.find(|&ord| ord != Ordering::Equal)
|
||||
.unwrap_or_else(|| e.name().len().cmp(&name.len()))
|
||||
}),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn insert(&mut self, entry: VfsEntry) {
|
||||
match self.binary_search(entry.name()) {
|
||||
pub fn insert(
|
||||
&mut self,
|
||||
entry: VfsEntry,
|
||||
case_sensitivity: FileSystemCaseSensitivity,
|
||||
) {
|
||||
match self.binary_search(entry.name(), case_sensitivity) {
|
||||
Ok(index) => {
|
||||
self.0[index] = entry;
|
||||
}
|
||||
|
@ -1039,19 +1103,21 @@ impl VfsRoot {
|
|||
fn find_entry<'a>(
|
||||
&'a self,
|
||||
path: &Path,
|
||||
case_sensitivity: FileSystemCaseSensitivity,
|
||||
) -> std::io::Result<(PathBuf, VfsEntryRef<'a>)> {
|
||||
self.find_entry_inner(path, &mut HashSet::new())
|
||||
self.find_entry_inner(path, &mut HashSet::new(), case_sensitivity)
|
||||
}
|
||||
|
||||
fn find_entry_inner<'a>(
|
||||
&'a self,
|
||||
path: &Path,
|
||||
seen: &mut HashSet<PathBuf>,
|
||||
case_sensitivity: FileSystemCaseSensitivity,
|
||||
) -> std::io::Result<(PathBuf, VfsEntryRef<'a>)> {
|
||||
let mut path = Cow::Borrowed(path);
|
||||
loop {
|
||||
let (resolved_path, entry) =
|
||||
self.find_entry_no_follow_inner(&path, seen)?;
|
||||
self.find_entry_no_follow_inner(&path, seen, case_sensitivity)?;
|
||||
match entry {
|
||||
VfsEntryRef::Symlink(symlink) => {
|
||||
if !seen.insert(path.to_path_buf()) {
|
||||
|
@ -1072,14 +1138,16 @@ impl VfsRoot {
|
|||
fn find_entry_no_follow(
|
||||
&self,
|
||||
path: &Path,
|
||||
case_sensitivity: FileSystemCaseSensitivity,
|
||||
) -> std::io::Result<(PathBuf, VfsEntryRef)> {
|
||||
self.find_entry_no_follow_inner(path, &mut HashSet::new())
|
||||
self.find_entry_no_follow_inner(path, &mut HashSet::new(), case_sensitivity)
|
||||
}
|
||||
|
||||
fn find_entry_no_follow_inner<'a>(
|
||||
&'a self,
|
||||
path: &Path,
|
||||
seen: &mut HashSet<PathBuf>,
|
||||
case_sensitivity: FileSystemCaseSensitivity,
|
||||
) -> std::io::Result<(PathBuf, VfsEntryRef<'a>)> {
|
||||
let relative_path = match path.strip_prefix(&self.root_path) {
|
||||
Ok(p) => p,
|
||||
|
@ -1101,7 +1169,8 @@ impl VfsRoot {
|
|||
}
|
||||
VfsEntryRef::Symlink(symlink) => {
|
||||
let dest = symlink.resolve_dest_from_root(&self.root_path);
|
||||
let (resolved_path, entry) = self.find_entry_inner(&dest, seen)?;
|
||||
let (resolved_path, entry) =
|
||||
self.find_entry_inner(&dest, seen, case_sensitivity)?;
|
||||
final_path = resolved_path; // overwrite with the new resolved path
|
||||
match entry {
|
||||
VfsEntryRef::Dir(dir) => {
|
||||
|
@ -1126,7 +1195,7 @@ impl VfsRoot {
|
|||
let component = component.to_string_lossy();
|
||||
current_entry = current_dir
|
||||
.entries
|
||||
.get_by_name(&component)
|
||||
.get_by_name(&component, case_sensitivity)
|
||||
.ok_or_else(|| {
|
||||
std::io::Error::new(std::io::ErrorKind::NotFound, "path not found")
|
||||
})?
|
||||
|
@ -1392,13 +1461,19 @@ impl FileBackedVfsMetadata {
|
|||
pub struct FileBackedVfs {
|
||||
vfs_data: Cow<'static, [u8]>,
|
||||
fs_root: VfsRoot,
|
||||
case_sensitivity: FileSystemCaseSensitivity,
|
||||
}
|
||||
|
||||
impl FileBackedVfs {
|
||||
pub fn new(data: Cow<'static, [u8]>, fs_root: VfsRoot) -> Self {
|
||||
pub fn new(
|
||||
data: Cow<'static, [u8]>,
|
||||
fs_root: VfsRoot,
|
||||
case_sensitivity: FileSystemCaseSensitivity,
|
||||
) -> Self {
|
||||
Self {
|
||||
vfs_data: data,
|
||||
fs_root,
|
||||
case_sensitivity,
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1451,7 +1526,9 @@ impl FileBackedVfs {
|
|||
}
|
||||
|
||||
pub fn read_link(&self, path: &Path) -> std::io::Result<PathBuf> {
|
||||
let (_, entry) = self.fs_root.find_entry_no_follow(path)?;
|
||||
let (_, entry) = self
|
||||
.fs_root
|
||||
.find_entry_no_follow(path, self.case_sensitivity)?;
|
||||
match entry {
|
||||
VfsEntryRef::Symlink(symlink) => {
|
||||
Ok(symlink.resolve_dest_from_root(&self.fs_root.root_path))
|
||||
|
@ -1464,17 +1541,19 @@ impl FileBackedVfs {
|
|||
}
|
||||
|
||||
pub fn lstat(&self, path: &Path) -> std::io::Result<FileBackedVfsMetadata> {
|
||||
let (_, entry) = self.fs_root.find_entry_no_follow(path)?;
|
||||
let (_, entry) = self
|
||||
.fs_root
|
||||
.find_entry_no_follow(path, self.case_sensitivity)?;
|
||||
Ok(entry.as_metadata())
|
||||
}
|
||||
|
||||
pub fn stat(&self, path: &Path) -> std::io::Result<FileBackedVfsMetadata> {
|
||||
let (_, entry) = self.fs_root.find_entry(path)?;
|
||||
let (_, entry) = self.fs_root.find_entry(path, self.case_sensitivity)?;
|
||||
Ok(entry.as_metadata())
|
||||
}
|
||||
|
||||
pub fn canonicalize(&self, path: &Path) -> std::io::Result<PathBuf> {
|
||||
let (path, _) = self.fs_root.find_entry(path)?;
|
||||
let (path, _) = self.fs_root.find_entry(path, self.case_sensitivity)?;
|
||||
Ok(path)
|
||||
}
|
||||
|
||||
|
@ -1536,7 +1615,7 @@ impl FileBackedVfs {
|
|||
}
|
||||
|
||||
pub fn dir_entry(&self, path: &Path) -> std::io::Result<&VirtualDirectory> {
|
||||
let (_, entry) = self.fs_root.find_entry(path)?;
|
||||
let (_, entry) = self.fs_root.find_entry(path, self.case_sensitivity)?;
|
||||
match entry {
|
||||
VfsEntryRef::Dir(dir) => Ok(dir),
|
||||
VfsEntryRef::Symlink(_) => unreachable!(),
|
||||
|
@ -1548,7 +1627,7 @@ impl FileBackedVfs {
|
|||
}
|
||||
|
||||
pub fn file_entry(&self, path: &Path) -> std::io::Result<&VirtualFile> {
|
||||
let (_, entry) = self.fs_root.find_entry(path)?;
|
||||
let (_, entry) = self.fs_root.find_entry(path, self.case_sensitivity)?;
|
||||
match entry {
|
||||
VfsEntryRef::Dir(_) => Err(std::io::Error::new(
|
||||
std::io::ErrorKind::Other,
|
||||
|
@ -1754,6 +1833,7 @@ mod test {
|
|||
root_path: dest_path.to_path_buf(),
|
||||
start_file_offset: 0,
|
||||
},
|
||||
FileSystemCaseSensitivity::Sensitive,
|
||||
),
|
||||
)
|
||||
}
|
||||
|
|
|
@ -34,6 +34,7 @@ use crate::graph_util::maybe_additional_sloppy_imports_message;
|
|||
use crate::graph_util::BuildFastCheckGraphOptions;
|
||||
use crate::graph_util::ModuleGraphBuilder;
|
||||
use crate::node::CliNodeResolver;
|
||||
use crate::npm::installer::NpmInstaller;
|
||||
use crate::npm::CliNpmResolver;
|
||||
use crate::sys::CliSys;
|
||||
use crate::tsc;
|
||||
|
@ -109,6 +110,7 @@ pub struct TypeChecker {
|
|||
cjs_tracker: Arc<TypeCheckingCjsTracker>,
|
||||
cli_options: Arc<CliOptions>,
|
||||
module_graph_builder: Arc<ModuleGraphBuilder>,
|
||||
npm_installer: Option<Arc<NpmInstaller>>,
|
||||
node_resolver: Arc<CliNodeResolver>,
|
||||
npm_resolver: Arc<dyn CliNpmResolver>,
|
||||
sys: CliSys,
|
||||
|
@ -136,12 +138,14 @@ pub enum CheckError {
|
|||
}
|
||||
|
||||
impl TypeChecker {
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
pub fn new(
|
||||
caches: Arc<Caches>,
|
||||
cjs_tracker: Arc<TypeCheckingCjsTracker>,
|
||||
cli_options: Arc<CliOptions>,
|
||||
module_graph_builder: Arc<ModuleGraphBuilder>,
|
||||
node_resolver: Arc<CliNodeResolver>,
|
||||
npm_installer: Option<Arc<NpmInstaller>>,
|
||||
npm_resolver: Arc<dyn CliNpmResolver>,
|
||||
sys: CliSys,
|
||||
) -> Self {
|
||||
|
@ -151,6 +155,7 @@ impl TypeChecker {
|
|||
cli_options,
|
||||
module_graph_builder,
|
||||
node_resolver,
|
||||
npm_installer,
|
||||
npm_resolver,
|
||||
sys,
|
||||
}
|
||||
|
@ -191,9 +196,9 @@ impl TypeChecker {
|
|||
// node built-in specifiers use the @types/node package to determine
|
||||
// types, so inject that now (the caller should do this after the lockfile
|
||||
// has been written)
|
||||
if let Some(npm_resolver) = self.npm_resolver.as_managed() {
|
||||
if let Some(npm_installer) = &self.npm_installer {
|
||||
if graph.has_node_specifier {
|
||||
npm_resolver.inject_synthetic_types_node_package().await?;
|
||||
npm_installer.inject_synthetic_types_node_package().await?;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -386,8 +386,11 @@ impl NpmInfo {
|
|||
npm_snapshot: &'a NpmResolutionSnapshot,
|
||||
) {
|
||||
self.packages.insert(package.id.clone(), package.clone());
|
||||
if let Ok(size) = npm_resolver.package_size(&package.id) {
|
||||
self.package_sizes.insert(package.id.clone(), size);
|
||||
if let Ok(folder) = npm_resolver.resolve_pkg_folder_from_pkg_id(&package.id)
|
||||
{
|
||||
if let Ok(size) = crate::util::fs::dir_size(&folder) {
|
||||
self.package_sizes.insert(package.id.clone(), size);
|
||||
}
|
||||
}
|
||||
for id in package.dependencies.values() {
|
||||
if !self.packages.contains_key(id) {
|
||||
|
|
|
@ -300,8 +300,8 @@ async fn install_local(
|
|||
InstallFlagsLocal::TopLevel => {
|
||||
let factory = CliFactory::from_flags(flags);
|
||||
// surface any errors in the package.json
|
||||
if let Some(npm_resolver) = factory.npm_resolver().await?.as_managed() {
|
||||
npm_resolver.ensure_no_pkg_json_dep_errors()?;
|
||||
if let Some(npm_installer) = factory.npm_installer_if_managed()? {
|
||||
npm_installer.ensure_no_pkg_json_dep_errors()?;
|
||||
}
|
||||
crate::tools::registry::cache_top_level_deps(&factory, None).await?;
|
||||
|
||||
|
|
|
@ -67,7 +67,7 @@ pub async fn kernel(
|
|||
// TODO(bartlomieju): should we run with all permissions?
|
||||
let permissions =
|
||||
PermissionsContainer::allow_all(factory.permission_desc_parser()?.clone());
|
||||
let npm_resolver = factory.npm_resolver().await?.clone();
|
||||
let npm_installer = factory.npm_installer_if_managed()?.cloned();
|
||||
let resolver = factory.resolver().await?.clone();
|
||||
let worker_factory = factory.create_cli_main_worker_factory().await?;
|
||||
let (stdio_tx, stdio_rx) = mpsc::unbounded_channel();
|
||||
|
@ -115,7 +115,7 @@ pub async fn kernel(
|
|||
let worker = worker.into_main_worker();
|
||||
let mut repl_session = repl::ReplSession::initialize(
|
||||
cli_options,
|
||||
npm_resolver,
|
||||
npm_installer,
|
||||
resolver,
|
||||
worker,
|
||||
main_module,
|
||||
|
|
|
@ -14,9 +14,14 @@ pub enum PropFlags {
|
|||
Ref,
|
||||
RefArr,
|
||||
String,
|
||||
Number,
|
||||
Bool,
|
||||
Null,
|
||||
Undefined,
|
||||
Object,
|
||||
Regex,
|
||||
BigInt,
|
||||
Array,
|
||||
}
|
||||
|
||||
impl From<PropFlags> for u8 {
|
||||
|
@ -33,21 +38,29 @@ impl TryFrom<u8> for PropFlags {
|
|||
0 => Ok(PropFlags::Ref),
|
||||
1 => Ok(PropFlags::RefArr),
|
||||
2 => Ok(PropFlags::String),
|
||||
3 => Ok(PropFlags::Bool),
|
||||
4 => Ok(PropFlags::Null),
|
||||
5 => Ok(PropFlags::Undefined),
|
||||
3 => Ok(PropFlags::Number),
|
||||
4 => Ok(PropFlags::Bool),
|
||||
5 => Ok(PropFlags::Null),
|
||||
6 => Ok(PropFlags::Undefined),
|
||||
7 => Ok(PropFlags::Object),
|
||||
8 => Ok(PropFlags::Regex),
|
||||
9 => Ok(PropFlags::BigInt),
|
||||
10 => Ok(PropFlags::Array),
|
||||
_ => Err("Unknown Prop flag"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub type Index = u32;
|
||||
|
||||
const GROUP_KIND: u8 = 1;
|
||||
const MASK_U32_1: u32 = 0b11111111_00000000_00000000_00000000;
|
||||
const MASK_U32_2: u32 = 0b00000000_11111111_00000000_00000000;
|
||||
const MASK_U32_3: u32 = 0b00000000_00000000_11111111_00000000;
|
||||
const MASK_U32_4: u32 = 0b00000000_00000000_00000000_11111111;
|
||||
|
||||
// TODO: There is probably a native Rust function to do this.
|
||||
pub fn append_u32(result: &mut Vec<u8>, value: u32) {
|
||||
#[inline]
|
||||
fn append_u32(result: &mut Vec<u8>, value: u32) {
|
||||
let v1: u8 = ((value & MASK_U32_1) >> 24) as u8;
|
||||
let v2: u8 = ((value & MASK_U32_2) >> 16) as u8;
|
||||
let v3: u8 = ((value & MASK_U32_3) >> 8) as u8;
|
||||
|
@ -59,25 +72,11 @@ pub fn append_u32(result: &mut Vec<u8>, value: u32) {
|
|||
result.push(v4);
|
||||
}
|
||||
|
||||
pub fn append_usize(result: &mut Vec<u8>, value: usize) {
|
||||
fn append_usize(result: &mut Vec<u8>, value: usize) {
|
||||
let raw = u32::try_from(value).unwrap();
|
||||
append_u32(result, raw);
|
||||
}
|
||||
|
||||
pub fn write_usize(result: &mut [u8], value: usize, idx: usize) {
|
||||
let raw = u32::try_from(value).unwrap();
|
||||
|
||||
let v1: u8 = ((raw & MASK_U32_1) >> 24) as u8;
|
||||
let v2: u8 = ((raw & MASK_U32_2) >> 16) as u8;
|
||||
let v3: u8 = ((raw & MASK_U32_3) >> 8) as u8;
|
||||
let v4: u8 = (raw & MASK_U32_4) as u8;
|
||||
|
||||
result[idx] = v1;
|
||||
result[idx + 1] = v2;
|
||||
result[idx + 2] = v3;
|
||||
result[idx + 3] = v4;
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct StringTable {
|
||||
id: usize,
|
||||
|
@ -119,71 +118,47 @@ impl StringTable {
|
|||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq)]
|
||||
pub struct NodeRef(pub usize);
|
||||
|
||||
/// Represents an offset to a node whose schema hasn't been committed yet
|
||||
pub struct NodeRef(pub Index);
|
||||
#[derive(Debug, Clone, Copy, PartialEq)]
|
||||
pub struct PendingNodeRef(pub NodeRef);
|
||||
pub struct PendingRef(pub Index);
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct BoolPos(pub usize);
|
||||
#[derive(Debug)]
|
||||
pub struct FieldPos(pub usize);
|
||||
#[derive(Debug)]
|
||||
pub struct FieldArrPos(pub usize);
|
||||
#[derive(Debug)]
|
||||
pub struct StrPos(pub usize);
|
||||
#[derive(Debug)]
|
||||
pub struct UndefPos(pub usize);
|
||||
#[derive(Debug)]
|
||||
pub struct NullPos(pub usize);
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum NodePos {
|
||||
Bool(BoolPos),
|
||||
#[allow(dead_code)]
|
||||
Field(FieldPos),
|
||||
#[allow(dead_code)]
|
||||
FieldArr(FieldArrPos),
|
||||
Str(StrPos),
|
||||
Undef(UndefPos),
|
||||
#[allow(dead_code)]
|
||||
Null(NullPos),
|
||||
pub trait AstBufSerializer {
|
||||
fn serialize(&mut self) -> Vec<u8>;
|
||||
}
|
||||
|
||||
pub trait AstBufSerializer<K, P>
|
||||
where
|
||||
K: Into<u8> + Display,
|
||||
P: Into<u8> + Display,
|
||||
{
|
||||
fn header(&mut self, kind: K, parent: NodeRef, span: &Span)
|
||||
-> PendingNodeRef;
|
||||
fn ref_field(&mut self, prop: P) -> FieldPos;
|
||||
fn ref_vec_field(&mut self, prop: P, len: usize) -> FieldArrPos;
|
||||
fn str_field(&mut self, prop: P) -> StrPos;
|
||||
fn bool_field(&mut self, prop: P) -> BoolPos;
|
||||
fn undefined_field(&mut self, prop: P) -> UndefPos;
|
||||
#[allow(dead_code)]
|
||||
fn null_field(&mut self, prop: P) -> NullPos;
|
||||
fn commit_schema(&mut self, offset: PendingNodeRef) -> NodeRef;
|
||||
|
||||
fn write_ref(&mut self, pos: FieldPos, value: NodeRef);
|
||||
fn write_maybe_ref(&mut self, pos: FieldPos, value: Option<NodeRef>);
|
||||
fn write_refs(&mut self, pos: FieldArrPos, value: Vec<NodeRef>);
|
||||
fn write_str(&mut self, pos: StrPos, value: &str);
|
||||
fn write_bool(&mut self, pos: BoolPos, value: bool);
|
||||
|
||||
fn serialize(&mut self) -> Vec<u8>;
|
||||
/// <type u8>
|
||||
/// <prop offset u32>
|
||||
/// <child idx u32>
|
||||
/// <next idx u32>
|
||||
/// <parent idx u32>
|
||||
#[derive(Debug)]
|
||||
struct Node {
|
||||
kind: u8,
|
||||
prop_offset: u32,
|
||||
child: u32,
|
||||
next: u32,
|
||||
parent: u32,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct SerializeCtx {
|
||||
buf: Vec<u8>,
|
||||
start_buf: NodeRef,
|
||||
root_idx: Index,
|
||||
|
||||
nodes: Vec<Node>,
|
||||
prop_stack: Vec<Vec<u8>>,
|
||||
field_count: Vec<usize>,
|
||||
field_buf: Vec<u8>,
|
||||
prev_sibling_stack: Vec<Index>,
|
||||
|
||||
/// Vec of spans
|
||||
spans: Vec<u32>,
|
||||
|
||||
/// Maps string id to the actual string
|
||||
str_table: StringTable,
|
||||
kind_map: Vec<usize>,
|
||||
prop_map: Vec<usize>,
|
||||
field_count: u8,
|
||||
/// Maps kind id to string id
|
||||
kind_name_map: Vec<usize>,
|
||||
/// Maps prop id to string id
|
||||
prop_name_map: Vec<usize>,
|
||||
}
|
||||
|
||||
/// This is the internal context used to allocate and fill the buffer. The point
|
||||
|
@ -198,20 +173,24 @@ impl SerializeCtx {
|
|||
let kind_size = kind_len as usize;
|
||||
let prop_size = prop_len as usize;
|
||||
let mut ctx = Self {
|
||||
start_buf: NodeRef(0),
|
||||
buf: vec![],
|
||||
spans: Vec::with_capacity(512),
|
||||
root_idx: 0,
|
||||
nodes: Vec::with_capacity(512),
|
||||
prop_stack: vec![vec![]],
|
||||
prev_sibling_stack: vec![0],
|
||||
field_count: vec![0],
|
||||
field_buf: Vec::with_capacity(1024),
|
||||
str_table: StringTable::new(),
|
||||
kind_map: vec![0; kind_size],
|
||||
prop_map: vec![0; prop_size],
|
||||
field_count: 0,
|
||||
kind_name_map: vec![0; kind_size],
|
||||
prop_name_map: vec![0; prop_size],
|
||||
};
|
||||
|
||||
let empty_str = ctx.str_table.insert("");
|
||||
|
||||
// Placeholder node is always 0
|
||||
ctx.append_node(0, NodeRef(0), &DUMMY_SP, 0);
|
||||
ctx.kind_map[0] = empty_str;
|
||||
ctx.start_buf = NodeRef(ctx.buf.len());
|
||||
ctx.append_node(0, &DUMMY_SP);
|
||||
ctx.kind_name_map[0] = empty_str;
|
||||
ctx.kind_name_map[1] = empty_str;
|
||||
|
||||
// Insert default props that are always present
|
||||
let type_str = ctx.str_table.insert("type");
|
||||
|
@ -220,258 +199,306 @@ impl SerializeCtx {
|
|||
let length_str = ctx.str_table.insert("length");
|
||||
|
||||
// These values are expected to be in this order on the JS side
|
||||
ctx.prop_map[0] = empty_str;
|
||||
ctx.prop_map[1] = type_str;
|
||||
ctx.prop_map[2] = parent_str;
|
||||
ctx.prop_map[3] = range_str;
|
||||
ctx.prop_map[4] = length_str;
|
||||
ctx.prop_name_map[0] = empty_str;
|
||||
ctx.prop_name_map[1] = type_str;
|
||||
ctx.prop_name_map[2] = parent_str;
|
||||
ctx.prop_name_map[3] = range_str;
|
||||
ctx.prop_name_map[4] = length_str;
|
||||
|
||||
ctx
|
||||
}
|
||||
|
||||
pub fn set_root_idx(&mut self, idx: Index) {
|
||||
self.root_idx = idx;
|
||||
}
|
||||
|
||||
/// Allocate a node's header
|
||||
fn field_header<P>(&mut self, prop: P, prop_flags: PropFlags) -> usize
|
||||
fn field_header<P>(&mut self, prop: P, prop_flags: PropFlags)
|
||||
where
|
||||
P: Into<u8> + Display + Clone,
|
||||
{
|
||||
self.field_count += 1;
|
||||
|
||||
let offset = self.buf.len();
|
||||
|
||||
let flags: u8 = prop_flags.into();
|
||||
let n: u8 = prop.clone().into();
|
||||
self.buf.push(n);
|
||||
|
||||
if let Some(v) = self.prop_map.get::<usize>(n.into()) {
|
||||
if let Some(v) = self.prop_name_map.get::<usize>(n.into()) {
|
||||
if *v == 0 {
|
||||
let id = self.str_table.insert(&format!("{prop}"));
|
||||
self.prop_map[n as usize] = id;
|
||||
self.prop_name_map[n as usize] = id;
|
||||
}
|
||||
}
|
||||
|
||||
let flags: u8 = prop_flags.into();
|
||||
self.buf.push(flags);
|
||||
// Increment field counter
|
||||
let idx = self.field_count.len() - 1;
|
||||
let count = self.field_count[idx];
|
||||
self.field_count[idx] = count + 1;
|
||||
|
||||
offset
|
||||
let buf = self.prop_stack.last_mut().unwrap();
|
||||
buf.push(n);
|
||||
buf.push(flags);
|
||||
}
|
||||
|
||||
/// Allocate a property pointing to another node.
|
||||
fn field<P>(&mut self, prop: P, prop_flags: PropFlags) -> usize
|
||||
fn get_node(&mut self, id: Index) -> &mut Node {
|
||||
self.nodes.get_mut(id as usize).unwrap()
|
||||
}
|
||||
|
||||
fn set_parent(&mut self, child_id: Index, parent_id: Index) {
|
||||
let child = self.get_node(child_id);
|
||||
child.parent = parent_id;
|
||||
}
|
||||
|
||||
fn set_child(&mut self, parent_id: Index, child_id: Index) {
|
||||
let parent = self.get_node(parent_id);
|
||||
parent.child = child_id;
|
||||
}
|
||||
|
||||
fn set_next(&mut self, node_id: Index, next_id: Index) {
|
||||
let node = self.get_node(node_id);
|
||||
node.next = next_id;
|
||||
}
|
||||
|
||||
fn update_ref_links(&mut self, parent_id: Index, child_id: Index) {
|
||||
let last_idx = self.prev_sibling_stack.len() - 1;
|
||||
let parent = self.get_node(parent_id);
|
||||
if parent.child == 0 {
|
||||
parent.child = child_id;
|
||||
} else {
|
||||
let prev_id = self.prev_sibling_stack[last_idx];
|
||||
self.set_next(prev_id, child_id);
|
||||
}
|
||||
|
||||
self.prev_sibling_stack[last_idx] = child_id;
|
||||
self.set_parent(child_id, parent_id);
|
||||
}
|
||||
|
||||
pub fn append_node<K>(&mut self, kind: K, span: &Span) -> PendingRef
|
||||
where
|
||||
P: Into<u8> + Display + Clone,
|
||||
K: Into<u8> + Display + Clone,
|
||||
{
|
||||
let offset = self.field_header(prop, prop_flags);
|
||||
|
||||
append_usize(&mut self.buf, 0);
|
||||
|
||||
offset
|
||||
self.append_inner(kind, span.lo.0, span.hi.0)
|
||||
}
|
||||
|
||||
fn append_node(
|
||||
pub fn append_inner<K>(
|
||||
&mut self,
|
||||
kind: u8,
|
||||
parent: NodeRef,
|
||||
span: &Span,
|
||||
prop_count: usize,
|
||||
) -> PendingNodeRef {
|
||||
let offset = self.buf.len();
|
||||
|
||||
// Node type fits in a u8
|
||||
self.buf.push(kind);
|
||||
|
||||
// Offset to the parent node. Will be 0 if none exists
|
||||
append_usize(&mut self.buf, parent.0);
|
||||
|
||||
// Span, the start and end location of this node
|
||||
append_u32(&mut self.buf, span.lo.0);
|
||||
append_u32(&mut self.buf, span.hi.0);
|
||||
|
||||
// No node has more than <10 properties
|
||||
debug_assert!(prop_count < 10);
|
||||
self.buf.push(prop_count as u8);
|
||||
|
||||
PendingNodeRef(NodeRef(offset))
|
||||
}
|
||||
|
||||
pub fn commit_schema(&mut self, node_ref: PendingNodeRef) -> NodeRef {
|
||||
let mut offset = node_ref.0 .0;
|
||||
|
||||
// type + parentId + span lo + span hi
|
||||
offset += 1 + 4 + 4 + 4;
|
||||
|
||||
self.buf[offset] = self.field_count;
|
||||
self.field_count = 0;
|
||||
|
||||
node_ref.0
|
||||
}
|
||||
|
||||
/// Allocate the node header. It's always the same for every node.
|
||||
/// <type u8>
|
||||
/// <parent offset u32>
|
||||
/// <span lo u32>
|
||||
/// <span high u32>
|
||||
/// <property count u8> (There is no node with more than 10 properties)
|
||||
pub fn header<N>(
|
||||
&mut self,
|
||||
kind: N,
|
||||
parent: NodeRef,
|
||||
span: &Span,
|
||||
) -> PendingNodeRef
|
||||
kind: K,
|
||||
span_lo: u32,
|
||||
span_hi: u32,
|
||||
) -> PendingRef
|
||||
where
|
||||
N: Into<u8> + Display + Clone,
|
||||
K: Into<u8> + Display + Clone,
|
||||
{
|
||||
let n: u8 = kind.clone().into();
|
||||
let kind_u8: u8 = kind.clone().into();
|
||||
|
||||
if let Some(v) = self.kind_map.get::<usize>(n.into()) {
|
||||
let id: Index = self.nodes.len() as u32;
|
||||
|
||||
self.nodes.push(Node {
|
||||
kind: kind_u8,
|
||||
prop_offset: 0,
|
||||
child: 0,
|
||||
next: 0,
|
||||
parent: 0,
|
||||
});
|
||||
|
||||
if let Some(v) = self.kind_name_map.get::<usize>(kind_u8.into()) {
|
||||
if *v == 0 {
|
||||
let id = self.str_table.insert(&format!("{kind}"));
|
||||
self.kind_map[n as usize] = id;
|
||||
let s_id = self.str_table.insert(&format!("{kind}"));
|
||||
self.kind_name_map[kind_u8 as usize] = s_id;
|
||||
}
|
||||
}
|
||||
|
||||
// Prop count will be filled with the actual value when the
|
||||
// schema is committed.
|
||||
self.append_node(n, parent, span, 0)
|
||||
self.field_count.push(0);
|
||||
self.prop_stack.push(vec![]);
|
||||
self.prev_sibling_stack.push(0);
|
||||
|
||||
// write spans
|
||||
self.spans.push(span_lo);
|
||||
self.spans.push(span_hi);
|
||||
|
||||
PendingRef(id)
|
||||
}
|
||||
|
||||
/// Allocate a reference property that will hold the offset of
|
||||
/// another node.
|
||||
pub fn ref_field<P>(&mut self, prop: P) -> usize
|
||||
pub fn commit_node(&mut self, id: PendingRef) -> NodeRef {
|
||||
let mut buf = self.prop_stack.pop().unwrap();
|
||||
let count = self.field_count.pop().unwrap();
|
||||
let offset = self.field_buf.len();
|
||||
|
||||
// All nodes have <10 fields
|
||||
self.field_buf.push(count as u8);
|
||||
self.field_buf.append(&mut buf);
|
||||
|
||||
let node = self.nodes.get_mut(id.0 as usize).unwrap();
|
||||
node.prop_offset = offset as u32;
|
||||
|
||||
self.prev_sibling_stack.pop();
|
||||
|
||||
NodeRef(id.0)
|
||||
}
|
||||
|
||||
// Allocate an object field
|
||||
pub fn open_obj(&mut self) {
|
||||
self.field_count.push(0);
|
||||
self.prop_stack.push(vec![]);
|
||||
}
|
||||
|
||||
pub fn commit_obj<P>(&mut self, prop: P)
|
||||
where
|
||||
P: Into<u8> + Display + Clone,
|
||||
{
|
||||
self.field(prop, PropFlags::Ref)
|
||||
let mut buf = self.prop_stack.pop().unwrap();
|
||||
let count = self.field_count.pop().unwrap();
|
||||
let offset = self.field_buf.len();
|
||||
append_usize(&mut self.field_buf, count);
|
||||
self.field_buf.append(&mut buf);
|
||||
|
||||
self.field_header(prop, PropFlags::Object);
|
||||
let buf = self.prop_stack.last_mut().unwrap();
|
||||
append_usize(buf, offset);
|
||||
}
|
||||
|
||||
/// Allocate a property that is a vec of node offsets pointing to other
|
||||
/// nodes.
|
||||
pub fn ref_vec_field<P>(&mut self, prop: P, len: usize) -> usize
|
||||
/// Allocate an null field
|
||||
pub fn write_null<P>(&mut self, prop: P)
|
||||
where
|
||||
P: Into<u8> + Display + Clone,
|
||||
{
|
||||
let offset = self.field(prop, PropFlags::RefArr);
|
||||
self.field_header(prop, PropFlags::Null);
|
||||
|
||||
for _ in 0..len {
|
||||
append_u32(&mut self.buf, 0);
|
||||
}
|
||||
|
||||
offset
|
||||
let buf = self.prop_stack.last_mut().unwrap();
|
||||
append_u32(buf, 0);
|
||||
}
|
||||
|
||||
// Allocate a property representing a string. Strings are deduplicated
|
||||
// in the message and the property will only contain the string id.
|
||||
pub fn str_field<P>(&mut self, prop: P) -> usize
|
||||
/// Allocate an null field
|
||||
pub fn write_undefined<P>(&mut self, prop: P)
|
||||
where
|
||||
P: Into<u8> + Display + Clone,
|
||||
{
|
||||
self.field(prop, PropFlags::String)
|
||||
self.field_header(prop, PropFlags::Undefined);
|
||||
|
||||
let buf = self.prop_stack.last_mut().unwrap();
|
||||
append_u32(buf, 0);
|
||||
}
|
||||
|
||||
/// Allocate a bool field
|
||||
pub fn bool_field<P>(&mut self, prop: P) -> usize
|
||||
/// Allocate a number field
|
||||
pub fn write_num<P>(&mut self, prop: P, value: &str)
|
||||
where
|
||||
P: Into<u8> + Display + Clone,
|
||||
{
|
||||
let offset = self.field_header(prop, PropFlags::Bool);
|
||||
self.buf.push(0);
|
||||
offset
|
||||
self.field_header(prop, PropFlags::Number);
|
||||
|
||||
let id = self.str_table.insert(value);
|
||||
let buf = self.prop_stack.last_mut().unwrap();
|
||||
append_usize(buf, id);
|
||||
}
|
||||
|
||||
/// Allocate an undefined field
|
||||
pub fn undefined_field<P>(&mut self, prop: P) -> usize
|
||||
/// Allocate a bigint field
|
||||
pub fn write_bigint<P>(&mut self, prop: P, value: &str)
|
||||
where
|
||||
P: Into<u8> + Display + Clone,
|
||||
{
|
||||
self.field_header(prop, PropFlags::Undefined)
|
||||
self.field_header(prop, PropFlags::BigInt);
|
||||
|
||||
let id = self.str_table.insert(value);
|
||||
let buf = self.prop_stack.last_mut().unwrap();
|
||||
append_usize(buf, id);
|
||||
}
|
||||
|
||||
/// Allocate an undefined field
|
||||
#[allow(dead_code)]
|
||||
pub fn null_field<P>(&mut self, prop: P) -> usize
|
||||
/// Allocate a RegExp field
|
||||
pub fn write_regex<P>(&mut self, prop: P, value: &str)
|
||||
where
|
||||
P: Into<u8> + Display + Clone,
|
||||
{
|
||||
self.field_header(prop, PropFlags::Null)
|
||||
}
|
||||
self.field_header(prop, PropFlags::Regex);
|
||||
|
||||
/// Replace the placeholder of a reference field with the actual offset
|
||||
/// to the node we want to point to.
|
||||
pub fn write_ref(&mut self, field_offset: usize, value: NodeRef) {
|
||||
#[cfg(debug_assertions)]
|
||||
{
|
||||
let value_kind = self.buf[field_offset + 1];
|
||||
if PropFlags::try_from(value_kind).unwrap() != PropFlags::Ref {
|
||||
panic!("Trying to write a ref into a non-ref field")
|
||||
}
|
||||
}
|
||||
|
||||
write_usize(&mut self.buf, value.0, field_offset + 2);
|
||||
}
|
||||
|
||||
/// Helper for writing optional node offsets
|
||||
pub fn write_maybe_ref(
|
||||
&mut self,
|
||||
field_offset: usize,
|
||||
value: Option<NodeRef>,
|
||||
) {
|
||||
#[cfg(debug_assertions)]
|
||||
{
|
||||
let value_kind = self.buf[field_offset + 1];
|
||||
if PropFlags::try_from(value_kind).unwrap() != PropFlags::Ref {
|
||||
panic!("Trying to write a ref into a non-ref field")
|
||||
}
|
||||
}
|
||||
|
||||
let ref_value = if let Some(v) = value { v } else { NodeRef(0) };
|
||||
write_usize(&mut self.buf, ref_value.0, field_offset + 2);
|
||||
}
|
||||
|
||||
/// Write a vec of node offsets into the property. The necessary space
|
||||
/// has been reserved earlier.
|
||||
pub fn write_refs(&mut self, field_offset: usize, value: Vec<NodeRef>) {
|
||||
#[cfg(debug_assertions)]
|
||||
{
|
||||
let value_kind = self.buf[field_offset + 1];
|
||||
if PropFlags::try_from(value_kind).unwrap() != PropFlags::RefArr {
|
||||
panic!("Trying to write a ref into a non-ref array field")
|
||||
}
|
||||
}
|
||||
|
||||
let mut offset = field_offset + 2;
|
||||
write_usize(&mut self.buf, value.len(), offset);
|
||||
offset += 4;
|
||||
|
||||
for item in value {
|
||||
write_usize(&mut self.buf, item.0, offset);
|
||||
offset += 4;
|
||||
}
|
||||
let id = self.str_table.insert(value);
|
||||
let buf = self.prop_stack.last_mut().unwrap();
|
||||
append_usize(buf, id);
|
||||
}
|
||||
|
||||
/// Store the string in our string table and save the id of the string
|
||||
/// in the current field.
|
||||
pub fn write_str(&mut self, field_offset: usize, value: &str) {
|
||||
#[cfg(debug_assertions)]
|
||||
{
|
||||
let value_kind = self.buf[field_offset + 1];
|
||||
if PropFlags::try_from(value_kind).unwrap() != PropFlags::String {
|
||||
panic!("Trying to write a ref into a non-string field")
|
||||
}
|
||||
}
|
||||
pub fn write_str<P>(&mut self, prop: P, value: &str)
|
||||
where
|
||||
P: Into<u8> + Display + Clone,
|
||||
{
|
||||
self.field_header(prop, PropFlags::String);
|
||||
|
||||
let id = self.str_table.insert(value);
|
||||
write_usize(&mut self.buf, id, field_offset + 2);
|
||||
let buf = self.prop_stack.last_mut().unwrap();
|
||||
append_usize(buf, id);
|
||||
}
|
||||
|
||||
/// Write a bool to a field.
|
||||
pub fn write_bool(&mut self, field_offset: usize, value: bool) {
|
||||
#[cfg(debug_assertions)]
|
||||
{
|
||||
let value_kind = self.buf[field_offset + 1];
|
||||
if PropFlags::try_from(value_kind).unwrap() != PropFlags::Bool {
|
||||
panic!("Trying to write a ref into a non-bool field")
|
||||
}
|
||||
}
|
||||
pub fn write_bool<P>(&mut self, prop: P, value: bool)
|
||||
where
|
||||
P: Into<u8> + Display + Clone,
|
||||
{
|
||||
self.field_header(prop, PropFlags::Bool);
|
||||
|
||||
self.buf[field_offset + 2] = if value { 1 } else { 0 };
|
||||
let n = if value { 1 } else { 0 };
|
||||
let buf = self.prop_stack.last_mut().unwrap();
|
||||
append_u32(buf, n);
|
||||
}
|
||||
|
||||
/// Replace the placeholder of a reference field with the actual offset
|
||||
/// to the node we want to point to.
|
||||
pub fn write_ref<P>(&mut self, prop: P, parent: &PendingRef, value: NodeRef)
|
||||
where
|
||||
P: Into<u8> + Display + Clone,
|
||||
{
|
||||
self.field_header(prop, PropFlags::Ref);
|
||||
let buf = self.prop_stack.last_mut().unwrap();
|
||||
append_u32(buf, value.0);
|
||||
|
||||
if parent.0 > 0 {
|
||||
self.update_ref_links(parent.0, value.0);
|
||||
}
|
||||
}
|
||||
|
||||
/// Helper for writing optional node offsets
|
||||
pub fn write_maybe_ref<P>(
|
||||
&mut self,
|
||||
prop: P,
|
||||
parent: &PendingRef,
|
||||
value: Option<NodeRef>,
|
||||
) where
|
||||
P: Into<u8> + Display + Clone,
|
||||
{
|
||||
if let Some(v) = value {
|
||||
self.write_ref(prop, parent, v);
|
||||
} else {
|
||||
self.write_null(prop);
|
||||
};
|
||||
}
|
||||
|
||||
/// Write a vec of node offsets into the property. The necessary space
|
||||
/// has been reserved earlier.
|
||||
pub fn write_ref_vec<P>(
|
||||
&mut self,
|
||||
prop: P,
|
||||
parent_ref: &PendingRef,
|
||||
value: Vec<NodeRef>,
|
||||
) where
|
||||
P: Into<u8> + Display + Clone,
|
||||
{
|
||||
self.field_header(prop, PropFlags::RefArr);
|
||||
let group_id = self.append_node(GROUP_KIND, &DUMMY_SP);
|
||||
let group_id = self.commit_node(group_id).0;
|
||||
|
||||
let buf = self.prop_stack.last_mut().unwrap();
|
||||
append_u32(buf, group_id);
|
||||
|
||||
self.update_ref_links(parent_ref.0, group_id);
|
||||
|
||||
let mut prev_id = 0;
|
||||
for (i, item) in value.iter().enumerate() {
|
||||
self.set_parent(item.0, group_id);
|
||||
|
||||
if i == 0 {
|
||||
self.set_child(group_id, item.0);
|
||||
} else {
|
||||
self.set_next(prev_id, item.0);
|
||||
}
|
||||
|
||||
prev_id = item.0;
|
||||
}
|
||||
}
|
||||
|
||||
/// Serialize all information we have into a buffer that can be sent to JS.
|
||||
|
@ -481,6 +508,8 @@ impl SerializeCtx {
|
|||
/// <string table>
|
||||
/// <node kind map> <- node kind id maps to string id
|
||||
/// <node prop map> <- node property id maps to string id
|
||||
/// <spans> <- List of spans, rarely needed
|
||||
/// <offset spans>
|
||||
/// <offset kind map>
|
||||
/// <offset prop map>
|
||||
/// <offset str table>
|
||||
|
@ -490,7 +519,13 @@ impl SerializeCtx {
|
|||
// The buffer starts with the serialized AST first, because that
|
||||
// contains absolute offsets. By butting this at the start of the
|
||||
// message we don't have to waste time updating any offsets.
|
||||
buf.append(&mut self.buf);
|
||||
for node in &self.nodes {
|
||||
buf.push(node.kind);
|
||||
append_u32(&mut buf, node.prop_offset);
|
||||
append_u32(&mut buf, node.child);
|
||||
append_u32(&mut buf, node.next);
|
||||
append_u32(&mut buf, node.parent);
|
||||
}
|
||||
|
||||
// Next follows the string table. We'll keep track of the offset
|
||||
// in the message of where the string table begins
|
||||
|
@ -507,8 +542,8 @@ impl SerializeCtx {
|
|||
|
||||
// Write the total number of entries in the kind -> str mapping table
|
||||
// TODO: make this a u8
|
||||
append_usize(&mut buf, self.kind_map.len());
|
||||
for v in &self.kind_map {
|
||||
append_usize(&mut buf, self.kind_name_map.len());
|
||||
for v in &self.kind_name_map {
|
||||
append_usize(&mut buf, *v);
|
||||
}
|
||||
|
||||
|
@ -517,19 +552,35 @@ impl SerializeCtx {
|
|||
// as u8.
|
||||
let offset_prop_map = buf.len();
|
||||
// Write the total number of entries in the kind -> str mapping table
|
||||
append_usize(&mut buf, self.prop_map.len());
|
||||
for v in &self.prop_map {
|
||||
append_usize(&mut buf, self.prop_name_map.len());
|
||||
for v in &self.prop_name_map {
|
||||
append_usize(&mut buf, *v);
|
||||
}
|
||||
|
||||
// Spans are rarely needed, so they're stored in a separate array.
|
||||
// They're indexed by the node id.
|
||||
let offset_spans = buf.len();
|
||||
for v in &self.spans {
|
||||
append_u32(&mut buf, *v);
|
||||
}
|
||||
|
||||
// The field value table. They're detached from nodes as they're not
|
||||
// as frequently needed as the nodes themselves. The most common
|
||||
// operation is traversal and we can traverse nodes without knowing
|
||||
// about the fields.
|
||||
let offset_props = buf.len();
|
||||
buf.append(&mut self.field_buf);
|
||||
|
||||
// Putting offsets of relevant parts of the buffer at the end. This
|
||||
// allows us to hop to the relevant part by merely looking at the last
|
||||
// for values in the message. Each value represents an offset into the
|
||||
// buffer.
|
||||
append_usize(&mut buf, offset_props);
|
||||
append_usize(&mut buf, offset_spans);
|
||||
append_usize(&mut buf, offset_kind_map);
|
||||
append_usize(&mut buf, offset_prop_map);
|
||||
append_usize(&mut buf, offset_str_table);
|
||||
append_usize(&mut buf, self.start_buf.0);
|
||||
append_u32(&mut buf, self.root_idx);
|
||||
|
||||
buf
|
||||
}
|
||||
|
|
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
|
@ -861,10 +861,8 @@ async fn npm_install_after_modification(
|
|||
// make a new CliFactory to pick up the updated config file
|
||||
let cli_factory = CliFactory::from_flags(flags);
|
||||
// surface any errors in the package.json
|
||||
let npm_resolver = cli_factory.npm_resolver().await?;
|
||||
if let Some(npm_resolver) = npm_resolver.as_managed() {
|
||||
npm_resolver.ensure_no_pkg_json_dep_errors()?;
|
||||
}
|
||||
let npm_installer = cli_factory.npm_installer()?;
|
||||
npm_installer.ensure_no_pkg_json_dep_errors()?;
|
||||
// npm install
|
||||
cache_deps::cache_top_level_deps(&cli_factory, jsr_resolver).await?;
|
||||
|
||||
|
|
|
@ -12,16 +12,19 @@ use crate::factory::CliFactory;
|
|||
use crate::graph_container::ModuleGraphContainer;
|
||||
use crate::graph_container::ModuleGraphUpdatePermit;
|
||||
use crate::graph_util::CreateGraphOptions;
|
||||
use crate::npm::installer::PackageCaching;
|
||||
|
||||
pub async fn cache_top_level_deps(
|
||||
// todo(dsherret): don't pass the factory into this function. Instead use ctor deps
|
||||
factory: &CliFactory,
|
||||
jsr_resolver: Option<Arc<crate::jsr::JsrFetchResolver>>,
|
||||
) -> Result<(), AnyError> {
|
||||
let npm_resolver = factory.npm_resolver().await?;
|
||||
let npm_installer = factory.npm_installer_if_managed()?;
|
||||
let cli_options = factory.cli_options()?;
|
||||
if let Some(npm_resolver) = npm_resolver.as_managed() {
|
||||
npm_resolver.ensure_top_level_package_json_install().await?;
|
||||
if let Some(npm_installer) = &npm_installer {
|
||||
npm_installer
|
||||
.ensure_top_level_package_json_install()
|
||||
.await?;
|
||||
if let Some(lockfile) = cli_options.maybe_lockfile() {
|
||||
lockfile.error_if_changed()?;
|
||||
}
|
||||
|
@ -138,10 +141,8 @@ pub async fn cache_top_level_deps(
|
|||
maybe_graph_error = graph_builder.graph_roots_valid(graph, &roots);
|
||||
}
|
||||
|
||||
if let Some(npm_resolver) = npm_resolver.as_managed() {
|
||||
npm_resolver
|
||||
.cache_packages(crate::npm::PackageCaching::All)
|
||||
.await?;
|
||||
if let Some(npm_installer) = &npm_installer {
|
||||
npm_installer.cache_packages(PackageCaching::All).await?;
|
||||
}
|
||||
|
||||
maybe_graph_error?;
|
||||
|
|
|
@ -42,6 +42,7 @@ use crate::graph_container::ModuleGraphContainer;
|
|||
use crate::graph_container::ModuleGraphUpdatePermit;
|
||||
use crate::jsr::JsrFetchResolver;
|
||||
use crate::module_loader::ModuleLoadPreparer;
|
||||
use crate::npm::installer::NpmInstaller;
|
||||
use crate::npm::CliNpmResolver;
|
||||
use crate::npm::NpmFetchResolver;
|
||||
use crate::util::sync::AtomicFlag;
|
||||
|
@ -451,6 +452,7 @@ pub struct DepManager {
|
|||
pub(crate) jsr_fetch_resolver: Arc<JsrFetchResolver>,
|
||||
pub(crate) npm_fetch_resolver: Arc<NpmFetchResolver>,
|
||||
npm_resolver: Arc<dyn CliNpmResolver>,
|
||||
npm_installer: Arc<NpmInstaller>,
|
||||
permissions_container: PermissionsContainer,
|
||||
main_module_graph_container: Arc<MainModuleGraphContainer>,
|
||||
lockfile: Option<Arc<CliLockfile>>,
|
||||
|
@ -460,6 +462,7 @@ pub struct DepManagerArgs {
|
|||
pub module_load_preparer: Arc<ModuleLoadPreparer>,
|
||||
pub jsr_fetch_resolver: Arc<JsrFetchResolver>,
|
||||
pub npm_fetch_resolver: Arc<NpmFetchResolver>,
|
||||
pub npm_installer: Arc<NpmInstaller>,
|
||||
pub npm_resolver: Arc<dyn CliNpmResolver>,
|
||||
pub permissions_container: PermissionsContainer,
|
||||
pub main_module_graph_container: Arc<MainModuleGraphContainer>,
|
||||
|
@ -477,6 +480,7 @@ impl DepManager {
|
|||
module_load_preparer,
|
||||
jsr_fetch_resolver,
|
||||
npm_fetch_resolver,
|
||||
npm_installer,
|
||||
npm_resolver,
|
||||
permissions_container,
|
||||
main_module_graph_container,
|
||||
|
@ -490,6 +494,7 @@ impl DepManager {
|
|||
dependencies_resolved: AtomicFlag::lowered(),
|
||||
module_load_preparer,
|
||||
npm_fetch_resolver,
|
||||
npm_installer,
|
||||
npm_resolver,
|
||||
permissions_container,
|
||||
main_module_graph_container,
|
||||
|
@ -556,7 +561,10 @@ impl DepManager {
|
|||
return Ok(());
|
||||
}
|
||||
|
||||
npm_resolver.ensure_top_level_package_json_install().await?;
|
||||
self
|
||||
.npm_installer
|
||||
.ensure_top_level_package_json_install()
|
||||
.await?;
|
||||
let mut roots = Vec::new();
|
||||
let mut info_futures = FuturesUnordered::new();
|
||||
for dep in &self.deps {
|
||||
|
|
|
@ -445,6 +445,7 @@ async fn dep_manager_args(
|
|||
jsr_fetch_resolver,
|
||||
npm_fetch_resolver,
|
||||
npm_resolver: factory.npm_resolver().await?.clone(),
|
||||
npm_installer: factory.npm_installer()?.clone(),
|
||||
permissions_container: factory.root_permissions_container()?.clone(),
|
||||
main_module_graph_container: factory
|
||||
.main_module_graph_container()
|
||||
|
|
|
@ -164,7 +164,7 @@ pub async fn run(
|
|||
let cli_options = factory.cli_options()?;
|
||||
let main_module = cli_options.resolve_main_module()?;
|
||||
let permissions = factory.root_permissions_container()?;
|
||||
let npm_resolver = factory.npm_resolver().await?.clone();
|
||||
let npm_installer = factory.npm_installer_if_managed()?.cloned();
|
||||
let resolver = factory.resolver().await?.clone();
|
||||
let file_fetcher = factory.file_fetcher()?;
|
||||
let worker_factory = factory.create_cli_main_worker_factory().await?;
|
||||
|
@ -187,7 +187,7 @@ pub async fn run(
|
|||
let worker = worker.into_main_worker();
|
||||
let session = ReplSession::initialize(
|
||||
cli_options,
|
||||
npm_resolver,
|
||||
npm_installer,
|
||||
resolver,
|
||||
worker,
|
||||
main_module.clone(),
|
||||
|
|
|
@ -45,7 +45,7 @@ use crate::args::CliOptions;
|
|||
use crate::cdp;
|
||||
use crate::colors;
|
||||
use crate::lsp::ReplLanguageServer;
|
||||
use crate::npm::CliNpmResolver;
|
||||
use crate::npm::installer::NpmInstaller;
|
||||
use crate::resolver::CliResolver;
|
||||
use crate::tools::test::report_tests;
|
||||
use crate::tools::test::reporters::PrettyTestReporter;
|
||||
|
@ -181,7 +181,7 @@ struct ReplJsxState {
|
|||
}
|
||||
|
||||
pub struct ReplSession {
|
||||
npm_resolver: Arc<dyn CliNpmResolver>,
|
||||
npm_installer: Option<Arc<NpmInstaller>>,
|
||||
resolver: Arc<CliResolver>,
|
||||
pub worker: MainWorker,
|
||||
session: LocalInspectorSession,
|
||||
|
@ -200,7 +200,7 @@ pub struct ReplSession {
|
|||
impl ReplSession {
|
||||
pub async fn initialize(
|
||||
cli_options: &CliOptions,
|
||||
npm_resolver: Arc<dyn CliNpmResolver>,
|
||||
npm_installer: Option<Arc<NpmInstaller>>,
|
||||
resolver: Arc<CliResolver>,
|
||||
mut worker: MainWorker,
|
||||
main_module: ModuleSpecifier,
|
||||
|
@ -265,7 +265,7 @@ impl ReplSession {
|
|||
)?;
|
||||
let experimental_decorators = transpile_options.use_ts_decorators;
|
||||
let mut repl_session = ReplSession {
|
||||
npm_resolver,
|
||||
npm_installer,
|
||||
resolver,
|
||||
worker,
|
||||
session,
|
||||
|
@ -704,8 +704,8 @@ impl ReplSession {
|
|||
&mut self,
|
||||
program: &swc_ast::Program,
|
||||
) -> Result<(), AnyError> {
|
||||
let Some(npm_resolver) = self.npm_resolver.as_managed() else {
|
||||
return Ok(()); // don't auto-install for byonm
|
||||
let Some(npm_installer) = &self.npm_installer else {
|
||||
return Ok(());
|
||||
};
|
||||
|
||||
let mut collector = ImportCollector::new();
|
||||
|
@ -737,13 +737,13 @@ impl ReplSession {
|
|||
let has_node_specifier =
|
||||
resolved_imports.iter().any(|url| url.scheme() == "node");
|
||||
if !npm_imports.is_empty() || has_node_specifier {
|
||||
npm_resolver
|
||||
npm_installer
|
||||
.add_and_cache_package_reqs(&npm_imports)
|
||||
.await?;
|
||||
|
||||
// prevent messages in the repl about @types/node not being cached
|
||||
if has_node_specifier {
|
||||
npm_resolver.inject_synthetic_types_node_package().await?;
|
||||
npm_installer.inject_synthetic_types_node_package().await?;
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
|
|
|
@ -12,6 +12,7 @@ use crate::args::EvalFlags;
|
|||
use crate::args::Flags;
|
||||
use crate::args::WatchFlagsWithPaths;
|
||||
use crate::factory::CliFactory;
|
||||
use crate::npm::installer::PackageCaching;
|
||||
use crate::util;
|
||||
use crate::util::file_watcher::WatcherRestartMode;
|
||||
|
||||
|
@ -202,18 +203,17 @@ pub async fn maybe_npm_install(factory: &CliFactory) -> Result<(), AnyError> {
|
|||
// ensure an "npm install" is done if the user has explicitly
|
||||
// opted into using a managed node_modules directory
|
||||
if cli_options.node_modules_dir()? == Some(NodeModulesDirMode::Auto) {
|
||||
if let Some(npm_resolver) = factory.npm_resolver().await?.as_managed() {
|
||||
let already_done =
|
||||
npm_resolver.ensure_top_level_package_json_install().await?;
|
||||
if let Some(npm_installer) = factory.npm_installer_if_managed()? {
|
||||
let already_done = npm_installer
|
||||
.ensure_top_level_package_json_install()
|
||||
.await?;
|
||||
if !already_done
|
||||
&& matches!(
|
||||
cli_options.default_npm_caching_strategy(),
|
||||
crate::graph_util::NpmCachingStrategy::Eager
|
||||
)
|
||||
{
|
||||
npm_resolver
|
||||
.cache_packages(crate::npm::PackageCaching::All)
|
||||
.await?;
|
||||
npm_installer.cache_packages(PackageCaching::All).await?;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -36,6 +36,8 @@ use crate::args::TaskFlags;
|
|||
use crate::colors;
|
||||
use crate::factory::CliFactory;
|
||||
use crate::node::CliNodeResolver;
|
||||
use crate::npm::installer::NpmInstaller;
|
||||
use crate::npm::installer::PackageCaching;
|
||||
use crate::npm::CliNpmResolver;
|
||||
use crate::task_runner;
|
||||
use crate::task_runner::run_future_forwarding_signals;
|
||||
|
@ -203,6 +205,7 @@ pub async fn execute_script(
|
|||
}]
|
||||
};
|
||||
|
||||
let npm_installer = factory.npm_installer_if_managed()?;
|
||||
let npm_resolver = factory.npm_resolver().await?;
|
||||
let node_resolver = factory.node_resolver().await?;
|
||||
let env_vars = task_runner::real_env_vars();
|
||||
|
@ -216,6 +219,7 @@ pub async fn execute_script(
|
|||
|
||||
let task_runner = TaskRunner {
|
||||
task_flags: &task_flags,
|
||||
npm_installer: npm_installer.map(|n| n.as_ref()),
|
||||
npm_resolver: npm_resolver.as_ref(),
|
||||
node_resolver: node_resolver.as_ref(),
|
||||
env_vars,
|
||||
|
@ -266,6 +270,7 @@ struct RunSingleOptions<'a> {
|
|||
|
||||
struct TaskRunner<'a> {
|
||||
task_flags: &'a TaskFlags,
|
||||
npm_installer: Option<&'a NpmInstaller>,
|
||||
npm_resolver: &'a dyn CliNpmResolver,
|
||||
node_resolver: &'a CliNodeResolver,
|
||||
env_vars: HashMap<String, String>,
|
||||
|
@ -458,11 +463,11 @@ impl<'a> TaskRunner<'a> {
|
|||
return Ok(0);
|
||||
};
|
||||
|
||||
if let Some(npm_resolver) = self.npm_resolver.as_managed() {
|
||||
npm_resolver.ensure_top_level_package_json_install().await?;
|
||||
npm_resolver
|
||||
.cache_packages(crate::npm::PackageCaching::All)
|
||||
if let Some(npm_installer) = self.npm_installer {
|
||||
npm_installer
|
||||
.ensure_top_level_package_json_install()
|
||||
.await?;
|
||||
npm_installer.cache_packages(PackageCaching::All).await?;
|
||||
}
|
||||
|
||||
let cwd = match &self.task_flags.cwd {
|
||||
|
@ -497,11 +502,11 @@ impl<'a> TaskRunner<'a> {
|
|||
argv: &[String],
|
||||
) -> Result<i32, deno_core::anyhow::Error> {
|
||||
// ensure the npm packages are installed if using a managed resolver
|
||||
if let Some(npm_resolver) = self.npm_resolver.as_managed() {
|
||||
npm_resolver.ensure_top_level_package_json_install().await?;
|
||||
npm_resolver
|
||||
.cache_packages(crate::npm::PackageCaching::All)
|
||||
if let Some(npm_installer) = self.npm_installer {
|
||||
npm_installer
|
||||
.ensure_top_level_package_json_install()
|
||||
.await?;
|
||||
npm_installer.cache_packages(PackageCaching::All).await?;
|
||||
}
|
||||
|
||||
let cwd = match &self.task_flags.cwd {
|
||||
|
|
|
@ -723,7 +723,7 @@ delete Object.prototype.__proto__;
|
|||
}
|
||||
: arg;
|
||||
if (fileReference.fileName.startsWith("npm:")) {
|
||||
/** @type {[string, ts.Extension] | undefined} */
|
||||
/** @type {[string, ts.Extension | null] | undefined} */
|
||||
const resolved = ops.op_resolve(
|
||||
containingFilePath,
|
||||
[
|
||||
|
@ -735,7 +735,7 @@ delete Object.prototype.__proto__;
|
|||
],
|
||||
],
|
||||
)?.[0];
|
||||
if (resolved) {
|
||||
if (resolved && resolved[1]) {
|
||||
return {
|
||||
resolvedTypeReferenceDirective: {
|
||||
primary: true,
|
||||
|
@ -785,7 +785,7 @@ delete Object.prototype.__proto__;
|
|||
debug(` base: ${base}`);
|
||||
debug(` specifiers: ${specifiers.map((s) => s[1]).join(", ")}`);
|
||||
}
|
||||
/** @type {Array<[string, ts.Extension] | undefined>} */
|
||||
/** @type {Array<[string, ts.Extension | null] | undefined>} */
|
||||
const resolved = ops.op_resolve(
|
||||
base,
|
||||
specifiers,
|
||||
|
@ -793,7 +793,7 @@ delete Object.prototype.__proto__;
|
|||
if (resolved) {
|
||||
/** @type {Array<ts.ResolvedModuleWithFailedLookupLocations>} */
|
||||
const result = resolved.map((item) => {
|
||||
if (item) {
|
||||
if (item && item[1]) {
|
||||
const [resolvedFileName, extension] = item;
|
||||
return {
|
||||
resolvedModule: {
|
||||
|
|
|
@ -28,6 +28,7 @@ use deno_graph::GraphKind;
|
|||
use deno_graph::Module;
|
||||
use deno_graph::ModuleGraph;
|
||||
use deno_graph::ResolutionResolved;
|
||||
use deno_resolver::npm::managed::ResolvePkgFolderFromDenoModuleError;
|
||||
use deno_resolver::npm::ResolvePkgFolderFromDenoReqError;
|
||||
use deno_semver::npm::NpmPackageReqReference;
|
||||
use node_resolver::errors::NodeJsErrorCode;
|
||||
|
@ -709,9 +710,7 @@ pub enum ResolveError {
|
|||
PackageSubpathResolve(PackageSubpathResolveError),
|
||||
#[class(inherit)]
|
||||
#[error("{0}")]
|
||||
ResolvePkgFolderFromDenoModule(
|
||||
#[from] crate::npm::ResolvePkgFolderFromDenoModuleError,
|
||||
),
|
||||
ResolvePkgFolderFromDenoModule(#[from] ResolvePkgFolderFromDenoModuleError),
|
||||
#[class(inherit)]
|
||||
#[error("{0}")]
|
||||
ResolveNonGraphSpecifierTypes(#[from] ResolveNonGraphSpecifierTypesError),
|
||||
|
@ -746,7 +745,7 @@ fn op_resolve(
|
|||
state: &mut OpState,
|
||||
#[string] base: String,
|
||||
#[serde] specifiers: Vec<(bool, String)>,
|
||||
) -> Result<Vec<(String, &'static str)>, ResolveError> {
|
||||
) -> Result<Vec<(String, Option<&'static str>)>, ResolveError> {
|
||||
op_resolve_inner(state, ResolveArgs { base, specifiers })
|
||||
}
|
||||
|
||||
|
@ -754,9 +753,9 @@ fn op_resolve(
|
|||
fn op_resolve_inner(
|
||||
state: &mut OpState,
|
||||
args: ResolveArgs,
|
||||
) -> Result<Vec<(String, &'static str)>, ResolveError> {
|
||||
) -> Result<Vec<(String, Option<&'static str>)>, ResolveError> {
|
||||
let state = state.borrow_mut::<State>();
|
||||
let mut resolved: Vec<(String, &'static str)> =
|
||||
let mut resolved: Vec<(String, Option<&'static str>)> =
|
||||
Vec::with_capacity(args.specifiers.len());
|
||||
let referrer = if let Some(remapped_specifier) =
|
||||
state.maybe_remapped_specifier(&args.base)
|
||||
|
@ -770,14 +769,14 @@ fn op_resolve_inner(
|
|||
if specifier.starts_with("node:") {
|
||||
resolved.push((
|
||||
MISSING_DEPENDENCY_SPECIFIER.to_string(),
|
||||
MediaType::Dts.as_ts_extension(),
|
||||
Some(MediaType::Dts.as_ts_extension()),
|
||||
));
|
||||
continue;
|
||||
}
|
||||
|
||||
if specifier.starts_with("asset:///") {
|
||||
let ext = MediaType::from_str(&specifier).as_ts_extension();
|
||||
resolved.push((specifier, ext));
|
||||
resolved.push((specifier, Some(ext)));
|
||||
continue;
|
||||
}
|
||||
|
||||
|
@ -857,14 +856,15 @@ fn op_resolve_inner(
|
|||
(
|
||||
specifier_str,
|
||||
match media_type {
|
||||
MediaType::Css => ".js", // surface these as .js for typescript
|
||||
media_type => media_type.as_ts_extension(),
|
||||
MediaType::Css => Some(".js"), // surface these as .js for typescript
|
||||
MediaType::Unknown => None,
|
||||
media_type => Some(media_type.as_ts_extension()),
|
||||
},
|
||||
)
|
||||
}
|
||||
None => (
|
||||
MISSING_DEPENDENCY_SPECIFIER.to_string(),
|
||||
MediaType::Dts.as_ts_extension(),
|
||||
Some(MediaType::Dts.as_ts_extension()),
|
||||
),
|
||||
};
|
||||
log::debug!("Resolved {} from {} to {:?}", specifier, referrer, result);
|
||||
|
@ -1441,7 +1441,10 @@ mod tests {
|
|||
},
|
||||
)
|
||||
.expect("should have invoked op");
|
||||
assert_eq!(actual, vec![("https://deno.land/x/b.ts".into(), ".ts")]);
|
||||
assert_eq!(
|
||||
actual,
|
||||
vec![("https://deno.land/x/b.ts".into(), Some(".ts"))]
|
||||
);
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
|
@ -1460,7 +1463,10 @@ mod tests {
|
|||
},
|
||||
)
|
||||
.expect("should have not errored");
|
||||
assert_eq!(actual, vec![(MISSING_DEPENDENCY_SPECIFIER.into(), ".d.ts")]);
|
||||
assert_eq!(
|
||||
actual,
|
||||
vec![(MISSING_DEPENDENCY_SPECIFIER.into(), Some(".d.ts"))]
|
||||
);
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
|
|
|
@ -54,6 +54,8 @@ use crate::args::NpmCachingStrategy;
|
|||
use crate::args::StorageKeyResolver;
|
||||
use crate::node::CliNodeResolver;
|
||||
use crate::node::CliPackageJsonResolver;
|
||||
use crate::npm::installer::NpmInstaller;
|
||||
use crate::npm::installer::PackageCaching;
|
||||
use crate::npm::CliNpmResolver;
|
||||
use crate::sys::CliSys;
|
||||
use crate::util::checksum;
|
||||
|
@ -148,6 +150,7 @@ struct SharedWorkerState {
|
|||
maybe_lockfile: Option<Arc<CliLockfile>>,
|
||||
module_loader_factory: Box<dyn ModuleLoaderFactory>,
|
||||
node_resolver: Arc<CliNodeResolver>,
|
||||
npm_installer: Option<Arc<NpmInstaller>>,
|
||||
npm_resolver: Arc<dyn CliNpmResolver>,
|
||||
pkg_json_resolver: Arc<CliPackageJsonResolver>,
|
||||
root_cert_store_provider: Arc<dyn RootCertStoreProvider>,
|
||||
|
@ -423,6 +426,7 @@ impl CliMainWorkerFactory {
|
|||
maybe_lockfile: Option<Arc<CliLockfile>>,
|
||||
module_loader_factory: Box<dyn ModuleLoaderFactory>,
|
||||
node_resolver: Arc<CliNodeResolver>,
|
||||
npm_installer: Option<Arc<NpmInstaller>>,
|
||||
npm_resolver: Arc<dyn CliNpmResolver>,
|
||||
pkg_json_resolver: Arc<CliPackageJsonResolver>,
|
||||
root_cert_store_provider: Arc<dyn RootCertStoreProvider>,
|
||||
|
@ -447,6 +451,7 @@ impl CliMainWorkerFactory {
|
|||
maybe_lockfile,
|
||||
module_loader_factory,
|
||||
node_resolver,
|
||||
npm_installer,
|
||||
npm_resolver,
|
||||
pkg_json_resolver,
|
||||
root_cert_store_provider,
|
||||
|
@ -496,18 +501,18 @@ impl CliMainWorkerFactory {
|
|||
let main_module = if let Ok(package_ref) =
|
||||
NpmPackageReqReference::from_specifier(&main_module)
|
||||
{
|
||||
if let Some(npm_resolver) = shared.npm_resolver.as_managed() {
|
||||
if let Some(npm_installer) = &shared.npm_installer {
|
||||
let reqs = &[package_ref.req().clone()];
|
||||
npm_resolver
|
||||
npm_installer
|
||||
.add_package_reqs(
|
||||
reqs,
|
||||
if matches!(
|
||||
shared.default_npm_caching_strategy,
|
||||
NpmCachingStrategy::Lazy
|
||||
) {
|
||||
crate::npm::PackageCaching::Only(reqs.into())
|
||||
PackageCaching::Only(reqs.into())
|
||||
} else {
|
||||
crate::npm::PackageCaching::All
|
||||
PackageCaching::All
|
||||
},
|
||||
)
|
||||
.await?;
|
||||
|
|
|
@ -487,7 +487,7 @@ deno_core::extension!(deno_node,
|
|||
"_fs/_fs_watch.ts",
|
||||
"_fs/_fs_write.mjs",
|
||||
"_fs/_fs_writeFile.ts",
|
||||
"_fs/_fs_writev.mjs",
|
||||
"_fs/_fs_writev.ts",
|
||||
"_next_tick.ts",
|
||||
"_process/exiting.ts",
|
||||
"_process/process.ts",
|
||||
|
|
65
ext/node/polyfills/_fs/_fs_writev.d.ts
vendored
65
ext/node/polyfills/_fs/_fs_writev.d.ts
vendored
|
@ -1,65 +0,0 @@
|
|||
// Copyright 2018-2025 the Deno authors. MIT license.
|
||||
// Forked from https://github.com/DefinitelyTyped/DefinitelyTyped/blob/d9df51e34526f48bef4e2546a006157b391ad96c/types/node/fs.d.ts
|
||||
|
||||
import { ErrnoException } from "ext:deno_node/_global.d.ts";
|
||||
|
||||
/**
|
||||
* Write an array of `ArrayBufferView`s to the file specified by `fd` using`writev()`.
|
||||
*
|
||||
* `position` is the offset from the beginning of the file where this data
|
||||
* should be written. If `typeof position !== 'number'`, the data will be written
|
||||
* at the current position.
|
||||
*
|
||||
* The callback will be given three arguments: `err`, `bytesWritten`, and`buffers`. `bytesWritten` is how many bytes were written from `buffers`.
|
||||
*
|
||||
* If this method is `util.promisify()` ed, it returns a promise for an`Object` with `bytesWritten` and `buffers` properties.
|
||||
*
|
||||
* It is unsafe to use `fs.writev()` multiple times on the same file without
|
||||
* waiting for the callback. For this scenario, use {@link createWriteStream}.
|
||||
*
|
||||
* On Linux, positional writes don't work when the file is opened in append mode.
|
||||
* The kernel ignores the position argument and always appends the data to
|
||||
* the end of the file.
|
||||
* @since v12.9.0
|
||||
*/
|
||||
export function writev(
|
||||
fd: number,
|
||||
buffers: ReadonlyArray<ArrayBufferView>,
|
||||
cb: (
|
||||
err: ErrnoException | null,
|
||||
bytesWritten: number,
|
||||
buffers: ArrayBufferView[],
|
||||
) => void,
|
||||
): void;
|
||||
export function writev(
|
||||
fd: number,
|
||||
buffers: ReadonlyArray<ArrayBufferView>,
|
||||
position: number | null,
|
||||
cb: (
|
||||
err: ErrnoException | null,
|
||||
bytesWritten: number,
|
||||
buffers: ArrayBufferView[],
|
||||
) => void,
|
||||
): void;
|
||||
export interface WriteVResult {
|
||||
bytesWritten: number;
|
||||
buffers: ArrayBufferView[];
|
||||
}
|
||||
export namespace writev {
|
||||
function __promisify__(
|
||||
fd: number,
|
||||
buffers: ReadonlyArray<ArrayBufferView>,
|
||||
position?: number,
|
||||
): Promise<WriteVResult>;
|
||||
}
|
||||
/**
|
||||
* For detailed information, see the documentation of the asynchronous version of
|
||||
* this API: {@link writev}.
|
||||
* @since v12.9.0
|
||||
* @return The number of bytes written.
|
||||
*/
|
||||
export function writevSync(
|
||||
fd: number,
|
||||
buffers: ReadonlyArray<ArrayBufferView>,
|
||||
position?: number,
|
||||
): number;
|
|
@ -5,15 +5,53 @@
|
|||
// deno-lint-ignore-file prefer-primordials
|
||||
|
||||
import { Buffer } from "node:buffer";
|
||||
import process from "node:process";
|
||||
import { ErrnoException } from "ext:deno_node/_global.d.ts";
|
||||
import { validateBufferArray } from "ext:deno_node/internal/fs/utils.mjs";
|
||||
import { getValidatedFd } from "ext:deno_node/internal/fs/utils.mjs";
|
||||
import { WriteVResult } from "ext:deno_node/internal/fs/handle.ts";
|
||||
import { maybeCallback } from "ext:deno_node/_fs/_fs_common.ts";
|
||||
import * as io from "ext:deno_io/12_io.js";
|
||||
import { op_fs_seek_async, op_fs_seek_sync } from "ext:core/ops";
|
||||
|
||||
export function writev(fd, buffers, position, callback) {
|
||||
export interface WriteVResult {
|
||||
bytesWritten: number;
|
||||
buffers: ReadonlyArray<ArrayBufferView>;
|
||||
}
|
||||
|
||||
type writeVCallback = (
|
||||
err: ErrnoException | null,
|
||||
bytesWritten: number,
|
||||
buffers: ReadonlyArray<ArrayBufferView>,
|
||||
) => void;
|
||||
|
||||
/**
|
||||
* Write an array of `ArrayBufferView`s to the file specified by `fd` using`writev()`.
|
||||
*
|
||||
* `position` is the offset from the beginning of the file where this data
|
||||
* should be written. If `typeof position !== 'number'`, the data will be written
|
||||
* at the current position.
|
||||
*
|
||||
* The callback will be given three arguments: `err`, `bytesWritten`, and`buffers`. `bytesWritten` is how many bytes were written from `buffers`.
|
||||
*
|
||||
* If this method is `util.promisify()` ed, it returns a promise for an`Object` with `bytesWritten` and `buffers` properties.
|
||||
*
|
||||
* It is unsafe to use `fs.writev()` multiple times on the same file without
|
||||
* waiting for the callback. For this scenario, use {@link createWriteStream}.
|
||||
*
|
||||
* On Linux, positional writes don't work when the file is opened in append mode.
|
||||
* The kernel ignores the position argument and always appends the data to
|
||||
* the end of the file.
|
||||
* @since v12.9.0
|
||||
*/
|
||||
export function writev(
|
||||
fd: number,
|
||||
buffers: ReadonlyArray<ArrayBufferView>,
|
||||
position?: number | null,
|
||||
callback?: writeVCallback,
|
||||
): void {
|
||||
const innerWritev = async (fd, buffers, position) => {
|
||||
const chunks = [];
|
||||
const chunks: Buffer[] = [];
|
||||
const offset = 0;
|
||||
for (let i = 0; i < buffers.length; i++) {
|
||||
if (Buffer.isBuffer(buffers[i])) {
|
||||
|
@ -45,16 +83,24 @@ export function writev(fd, buffers, position, callback) {
|
|||
if (typeof position !== "number") position = null;
|
||||
|
||||
innerWritev(fd, buffers, position).then(
|
||||
(nwritten) => {
|
||||
callback(null, nwritten, buffers);
|
||||
},
|
||||
(nwritten) => callback(null, nwritten, buffers),
|
||||
(err) => callback(err),
|
||||
);
|
||||
}
|
||||
|
||||
export function writevSync(fd, buffers, position) {
|
||||
/**
|
||||
* For detailed information, see the documentation of the asynchronous version of
|
||||
* this API: {@link writev}.
|
||||
* @since v12.9.0
|
||||
* @return The number of bytes written.
|
||||
*/
|
||||
export function writevSync(
|
||||
fd: number,
|
||||
buffers: ArrayBufferView[],
|
||||
position?: number | null,
|
||||
): number {
|
||||
const innerWritev = (fd, buffers, position) => {
|
||||
const chunks = [];
|
||||
const chunks: Buffer[] = [];
|
||||
const offset = 0;
|
||||
for (let i = 0; i < buffers.length; i++) {
|
||||
if (Buffer.isBuffer(buffers[i])) {
|
||||
|
@ -85,3 +131,16 @@ export function writevSync(fd, buffers, position) {
|
|||
|
||||
return innerWritev(fd, buffers, position);
|
||||
}
|
||||
|
||||
export function writevPromise(
|
||||
fd: number,
|
||||
buffers: ArrayBufferView[],
|
||||
position?: number,
|
||||
): Promise<WriteVResult> {
|
||||
return new Promise((resolve, reject) => {
|
||||
writev(fd, buffers, position, (err, bytesWritten, buffers) => {
|
||||
if (err) reject(err);
|
||||
else resolve({ bytesWritten, buffers });
|
||||
});
|
||||
});
|
||||
}
|
|
@ -119,7 +119,7 @@ import {
|
|||
// @deno-types="./_fs/_fs_write.d.ts"
|
||||
import { write, writeSync } from "ext:deno_node/_fs/_fs_write.mjs";
|
||||
// @deno-types="./_fs/_fs_writev.d.ts"
|
||||
import { writev, writevSync } from "ext:deno_node/_fs/_fs_writev.mjs";
|
||||
import { writev, writevSync } from "ext:deno_node/_fs/_fs_writev.ts";
|
||||
import { readv, readvSync } from "ext:deno_node/_fs/_fs_readv.ts";
|
||||
import {
|
||||
writeFile,
|
||||
|
|
|
@ -277,6 +277,7 @@ export class ChildProcess extends EventEmitter {
|
|||
try {
|
||||
this.#process = new Deno.Command(cmd, {
|
||||
args: cmdArgs,
|
||||
clearEnv: true,
|
||||
cwd,
|
||||
env: stringEnv,
|
||||
stdin: toDenoStdio(stdin),
|
||||
|
@ -839,6 +840,7 @@ export function normalizeSpawnArguments(
|
|||
args,
|
||||
cwd,
|
||||
detached: !!options.detached,
|
||||
env,
|
||||
envPairs,
|
||||
file,
|
||||
windowsHide: !!options.windowsHide,
|
||||
|
|
|
@ -6,7 +6,7 @@
|
|||
import { EventEmitter } from "node:events";
|
||||
import { Buffer } from "node:buffer";
|
||||
import { Mode, promises, read, write } from "node:fs";
|
||||
export type { BigIntStats, Stats } from "ext:deno_node/_fs/_fs_stat.ts";
|
||||
import { core } from "ext:core/mod.js";
|
||||
import {
|
||||
BinaryOptionsArgument,
|
||||
FileOptionsArgument,
|
||||
|
@ -14,7 +14,8 @@ import {
|
|||
TextOptionsArgument,
|
||||
} from "ext:deno_node/_fs/_fs_common.ts";
|
||||
import { ftruncatePromise } from "ext:deno_node/_fs/_fs_ftruncate.ts";
|
||||
import { core } from "ext:core/mod.js";
|
||||
export type { BigIntStats, Stats } from "ext:deno_node/_fs/_fs_stat.ts";
|
||||
import { writevPromise, WriteVResult } from "ext:deno_node/_fs/_fs_writev.ts";
|
||||
|
||||
interface WriteResult {
|
||||
bytesWritten: number;
|
||||
|
@ -64,7 +65,7 @@ export class FileHandle extends EventEmitter {
|
|||
position,
|
||||
(err, bytesRead, buffer) => {
|
||||
if (err) reject(err);
|
||||
else resolve({ buffer: buffer, bytesRead: bytesRead });
|
||||
else resolve({ buffer, bytesRead });
|
||||
},
|
||||
);
|
||||
});
|
||||
|
@ -72,7 +73,7 @@ export class FileHandle extends EventEmitter {
|
|||
return new Promise((resolve, reject) => {
|
||||
read(this.fd, bufferOrOpt, (err, bytesRead, buffer) => {
|
||||
if (err) reject(err);
|
||||
else resolve({ buffer: buffer, bytesRead: bytesRead });
|
||||
else resolve({ buffer, bytesRead });
|
||||
});
|
||||
});
|
||||
}
|
||||
|
@ -137,6 +138,10 @@ export class FileHandle extends EventEmitter {
|
|||
return fsCall(promises.writeFile, this, data, options);
|
||||
}
|
||||
|
||||
writev(buffers: ArrayBufferView[], position?: number): Promise<WriteVResult> {
|
||||
return fsCall(writevPromise, this, buffers, position);
|
||||
}
|
||||
|
||||
close(): Promise<void> {
|
||||
// Note that Deno.close is not async
|
||||
return Promise.resolve(core.close(this.fd));
|
||||
|
@ -152,6 +157,14 @@ export class FileHandle extends EventEmitter {
|
|||
assertNotClosed(this, promises.chmod.name);
|
||||
return promises.chmod(this.#path, mode);
|
||||
}
|
||||
|
||||
utimes(
|
||||
atime: number | string | Date,
|
||||
mtime: number | string | Date,
|
||||
): Promise<void> {
|
||||
assertNotClosed(this, promises.utimes.name);
|
||||
return promises.utimes(this.#path, atime, mtime);
|
||||
}
|
||||
}
|
||||
|
||||
function assertNotClosed(handle: FileHandle, syscall: string) {
|
||||
|
|
|
@ -18,7 +18,7 @@ import { errorOrDestroy } from "ext:deno_node/internal/streams/destroy.mjs";
|
|||
import { open as fsOpen } from "ext:deno_node/_fs/_fs_open.ts";
|
||||
import { read as fsRead } from "ext:deno_node/_fs/_fs_read.ts";
|
||||
import { write as fsWrite } from "ext:deno_node/_fs/_fs_write.mjs";
|
||||
import { writev as fsWritev } from "ext:deno_node/_fs/_fs_writev.mjs";
|
||||
import { writev as fsWritev } from "ext:deno_node/_fs/_fs_writev.ts";
|
||||
import { close as fsClose } from "ext:deno_node/_fs/_fs_close.ts";
|
||||
import { Buffer } from "node:buffer";
|
||||
import {
|
||||
|
|
|
@ -1224,15 +1224,20 @@ export class Socket extends Duplex {
|
|||
|
||||
super(options);
|
||||
|
||||
// Note: If the socket is created from @npmcli/agent, the 'socket' event
|
||||
// on ClientRequest object happens after 'connect' event on Socket object.
|
||||
// Note: If the socket is created from one of:
|
||||
// - @npmcli/agent
|
||||
// - npm-check-updates (bundles @npmcli/agent as a dependency)
|
||||
// the 'socket' event on ClientRequest object happens after 'connect' event on Socket object.
|
||||
// That swaps the sequence of op_node_http_request_with_conn() call and
|
||||
// initial socket read. That causes op_node_http_request_with_conn() not
|
||||
// working.
|
||||
// To avoid the above situation, we detect the socket created from
|
||||
// @npmcli/agent and pause the socket (and also skips the startTls call
|
||||
// if it's TLSSocket)
|
||||
this._isNpmAgent = new Error().stack?.includes("@npmcli/agent") || false;
|
||||
// TODO(kt3k): Remove this workaround
|
||||
const errorStack = new Error().stack;
|
||||
this._isNpmAgent = errorStack?.includes("@npmcli/agent") ||
|
||||
errorStack?.includes("npm-check-updates") || false;
|
||||
if (this._isNpmAgent) {
|
||||
this.pause();
|
||||
}
|
||||
|
|
|
@ -16,7 +16,7 @@ use node_resolver::errors::ReferrerNotFoundError;
|
|||
use node_resolver::NpmPackageFolderResolver;
|
||||
use url::Url;
|
||||
|
||||
use super::resolution::NpmResolutionRc;
|
||||
use super::resolution::NpmResolutionCellRc;
|
||||
use super::NpmCacheDirRc;
|
||||
use super::NpmPackageFsResolver;
|
||||
use crate::ResolvedNpmRcRc;
|
||||
|
@ -26,14 +26,14 @@ use crate::ResolvedNpmRcRc;
|
|||
pub struct GlobalNpmPackageResolver {
|
||||
cache: NpmCacheDirRc,
|
||||
npm_rc: ResolvedNpmRcRc,
|
||||
resolution: NpmResolutionRc,
|
||||
resolution: NpmResolutionCellRc,
|
||||
}
|
||||
|
||||
impl GlobalNpmPackageResolver {
|
||||
pub fn new(
|
||||
cache: NpmCacheDirRc,
|
||||
npm_rc: ResolvedNpmRcRc,
|
||||
resolution: NpmResolutionRc,
|
||||
resolution: NpmResolutionCellRc,
|
||||
) -> Self {
|
||||
Self {
|
||||
cache,
|
||||
|
|
|
@ -19,7 +19,7 @@ use sys_traits::FsCanonicalize;
|
|||
use sys_traits::FsMetadata;
|
||||
use url::Url;
|
||||
|
||||
use super::resolution::NpmResolutionRc;
|
||||
use super::resolution::NpmResolutionCellRc;
|
||||
use super::NpmPackageFsResolver;
|
||||
use crate::npm::local::get_package_folder_id_folder_name_from_parts;
|
||||
use crate::npm::local::get_package_folder_id_from_folder_name;
|
||||
|
@ -32,7 +32,7 @@ use crate::sync::MaybeSync;
|
|||
pub struct LocalNpmPackageResolver<
|
||||
TSys: FsCanonicalize + FsMetadata + MaybeSend + MaybeSync,
|
||||
> {
|
||||
resolution: NpmResolutionRc,
|
||||
resolution: NpmResolutionCellRc,
|
||||
sys: TSys,
|
||||
root_node_modules_path: PathBuf,
|
||||
root_node_modules_url: Url,
|
||||
|
@ -43,7 +43,7 @@ impl<TSys: FsCanonicalize + FsMetadata + MaybeSend + MaybeSync>
|
|||
{
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
pub fn new(
|
||||
resolution: NpmResolutionRc,
|
||||
resolution: NpmResolutionCellRc,
|
||||
sys: TSys,
|
||||
node_modules_folder: PathBuf,
|
||||
) -> Self {
|
||||
|
|
|
@ -8,10 +8,13 @@ mod resolution;
|
|||
use std::path::Path;
|
||||
use std::path::PathBuf;
|
||||
|
||||
use deno_npm::resolution::PackageCacheFolderIdNotFoundError;
|
||||
use deno_npm::resolution::PackageNvNotFoundError;
|
||||
use deno_npm::resolution::PackageReqNotFoundError;
|
||||
use deno_npm::NpmPackageCacheFolderId;
|
||||
use deno_npm::NpmPackageId;
|
||||
use deno_path_util::fs::canonicalize_path_maybe_not_exists;
|
||||
use deno_semver::package::PackageNv;
|
||||
use deno_semver::package::PackageReq;
|
||||
use node_resolver::InNpmPackageChecker;
|
||||
use node_resolver::NpmPackageFolderResolver;
|
||||
|
@ -23,14 +26,24 @@ use self::common::NpmPackageFsResolver;
|
|||
use self::common::NpmPackageFsResolverRc;
|
||||
use self::global::GlobalNpmPackageResolver;
|
||||
use self::local::LocalNpmPackageResolver;
|
||||
pub use self::resolution::NpmResolution;
|
||||
pub use self::resolution::NpmResolutionRc;
|
||||
pub use self::resolution::NpmResolutionCell;
|
||||
pub use self::resolution::NpmResolutionCellRc;
|
||||
use crate::sync::new_rc;
|
||||
use crate::sync::MaybeSend;
|
||||
use crate::sync::MaybeSync;
|
||||
use crate::NpmCacheDirRc;
|
||||
use crate::ResolvedNpmRcRc;
|
||||
|
||||
#[derive(Debug, thiserror::Error, deno_error::JsError)]
|
||||
pub enum ResolvePkgFolderFromDenoModuleError {
|
||||
#[class(inherit)]
|
||||
#[error(transparent)]
|
||||
PackageNvNotFound(#[from] PackageNvNotFoundError),
|
||||
#[class(inherit)]
|
||||
#[error(transparent)]
|
||||
ResolvePkgFolderFromPkgId(#[from] ResolvePkgFolderFromPkgIdError),
|
||||
}
|
||||
|
||||
#[derive(Debug, thiserror::Error, deno_error::JsError)]
|
||||
#[error(transparent)]
|
||||
pub enum ResolvePkgFolderFromPkgIdError {
|
||||
|
@ -66,6 +79,16 @@ pub enum ManagedResolvePkgFolderFromDenoReqError {
|
|||
ResolvePkgFolderFromPkgId(#[from] ResolvePkgFolderFromPkgIdError),
|
||||
}
|
||||
|
||||
#[derive(Debug, thiserror::Error, deno_error::JsError)]
|
||||
pub enum ResolvePkgIdFromSpecifierError {
|
||||
#[class(inherit)]
|
||||
#[error(transparent)]
|
||||
Io(#[from] std::io::Error),
|
||||
#[class(inherit)]
|
||||
#[error(transparent)]
|
||||
NotFound(#[from] PackageCacheFolderIdNotFoundError),
|
||||
}
|
||||
|
||||
#[allow(clippy::disallowed_types)]
|
||||
pub type ManagedNpmResolverRc<TSys> =
|
||||
crate::sync::MaybeArc<ManagedNpmResolver<TSys>>;
|
||||
|
@ -73,7 +96,7 @@ pub type ManagedNpmResolverRc<TSys> =
|
|||
#[derive(Debug)]
|
||||
pub struct ManagedNpmResolver<TSys: FsCanonicalize> {
|
||||
fs_resolver: NpmPackageFsResolverRc,
|
||||
resolution: NpmResolutionRc,
|
||||
resolution: NpmResolutionCellRc,
|
||||
sys: TSys,
|
||||
}
|
||||
|
||||
|
@ -89,7 +112,7 @@ impl<TSys: FsCanonicalize> ManagedNpmResolver<TSys> {
|
|||
>(
|
||||
npm_cache_dir: &NpmCacheDirRc,
|
||||
npm_rc: &ResolvedNpmRcRc,
|
||||
resolution: NpmResolutionRc,
|
||||
resolution: NpmResolutionCellRc,
|
||||
sys: TCreateSys,
|
||||
maybe_node_modules_path: Option<PathBuf>,
|
||||
) -> ManagedNpmResolver<TCreateSys> {
|
||||
|
@ -144,6 +167,14 @@ impl<TSys: FsCanonicalize> ManagedNpmResolver<TSys> {
|
|||
Ok(path)
|
||||
}
|
||||
|
||||
pub fn resolve_pkg_folder_from_deno_module(
|
||||
&self,
|
||||
nv: &PackageNv,
|
||||
) -> Result<PathBuf, ResolvePkgFolderFromDenoModuleError> {
|
||||
let pkg_id = self.resolution.resolve_pkg_id_from_deno_module(nv)?;
|
||||
Ok(self.resolve_pkg_folder_from_pkg_id(&pkg_id)?)
|
||||
}
|
||||
|
||||
pub fn resolve_pkg_folder_from_deno_module_req(
|
||||
&self,
|
||||
req: &PackageReq,
|
||||
|
@ -162,6 +193,24 @@ impl<TSys: FsCanonicalize> ManagedNpmResolver<TSys> {
|
|||
.fs_resolver
|
||||
.resolve_package_cache_folder_id_from_specifier(specifier)
|
||||
}
|
||||
|
||||
/// Resolves the package id from the provided specifier.
|
||||
pub fn resolve_pkg_id_from_specifier(
|
||||
&self,
|
||||
specifier: &Url,
|
||||
) -> Result<Option<NpmPackageId>, ResolvePkgIdFromSpecifierError> {
|
||||
let Some(cache_folder_id) = self
|
||||
.fs_resolver
|
||||
.resolve_package_cache_folder_id_from_specifier(specifier)?
|
||||
else {
|
||||
return Ok(None);
|
||||
};
|
||||
Ok(Some(
|
||||
self
|
||||
.resolution
|
||||
.resolve_pkg_id_from_pkg_cache_folder_id(&cache_folder_id)?,
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
impl<TSys: FsCanonicalize + std::fmt::Debug + MaybeSend + MaybeSync>
|
||||
|
|
|
@ -18,16 +18,17 @@ use deno_semver::package::PackageReq;
|
|||
use parking_lot::RwLock;
|
||||
|
||||
#[allow(clippy::disallowed_types)]
|
||||
pub type NpmResolutionRc = crate::sync::MaybeArc<NpmResolution>;
|
||||
pub type NpmResolutionCellRc = crate::sync::MaybeArc<NpmResolutionCell>;
|
||||
|
||||
/// Handles updating and storing npm resolution in memory.
|
||||
///
|
||||
/// This does not interact with the file system.
|
||||
pub struct NpmResolution {
|
||||
#[derive(Default)]
|
||||
pub struct NpmResolutionCell {
|
||||
snapshot: RwLock<NpmResolutionSnapshot>,
|
||||
}
|
||||
|
||||
impl std::fmt::Debug for NpmResolution {
|
||||
impl std::fmt::Debug for NpmResolutionCell {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
let snapshot = self.snapshot.read();
|
||||
f.debug_struct("NpmResolution")
|
||||
|
@ -36,7 +37,7 @@ impl std::fmt::Debug for NpmResolution {
|
|||
}
|
||||
}
|
||||
|
||||
impl NpmResolution {
|
||||
impl NpmResolutionCell {
|
||||
pub fn from_serialized(
|
||||
initial_snapshot: Option<ValidSerializedNpmResolutionSnapshot>,
|
||||
) -> Self {
|
||||
|
|
|
@ -9,6 +9,7 @@ mod npm;
|
|||
mod package_json;
|
||||
mod path;
|
||||
mod resolution;
|
||||
|
||||
mod sync;
|
||||
|
||||
pub use deno_package_json::PackageJson;
|
||||
|
@ -22,6 +23,7 @@ pub use package_json::PackageJsonThreadLocalCache;
|
|||
pub use path::PathClean;
|
||||
pub use resolution::parse_npm_pkg_name;
|
||||
pub use resolution::resolve_specifier_into_node_modules;
|
||||
pub use resolution::ConditionsFromResolutionMode;
|
||||
pub use resolution::IsBuiltInNodeModuleChecker;
|
||||
pub use resolution::NodeResolution;
|
||||
pub use resolution::NodeResolutionKind;
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
// Copyright 2018-2025 the Deno authors. MIT license.
|
||||
|
||||
use std::borrow::Cow;
|
||||
use std::fmt::Debug;
|
||||
use std::path::Path;
|
||||
use std::path::PathBuf;
|
||||
|
||||
|
@ -54,12 +55,32 @@ pub static DEFAULT_CONDITIONS: &[&str] = &["deno", "node", "import"];
|
|||
pub static REQUIRE_CONDITIONS: &[&str] = &["require", "node"];
|
||||
static TYPES_ONLY_CONDITIONS: &[&str] = &["types"];
|
||||
|
||||
fn conditions_from_resolution_mode(
|
||||
resolution_mode: ResolutionMode,
|
||||
) -> &'static [&'static str] {
|
||||
match resolution_mode {
|
||||
ResolutionMode::Import => DEFAULT_CONDITIONS,
|
||||
ResolutionMode::Require => REQUIRE_CONDITIONS,
|
||||
type ConditionsFromResolutionModeFn = Box<
|
||||
dyn Fn(ResolutionMode) -> &'static [&'static str] + Send + Sync + 'static,
|
||||
>;
|
||||
|
||||
#[derive(Default)]
|
||||
pub struct ConditionsFromResolutionMode(Option<ConditionsFromResolutionModeFn>);
|
||||
|
||||
impl Debug for ConditionsFromResolutionMode {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
f.debug_struct("ConditionsFromResolutionMode").finish()
|
||||
}
|
||||
}
|
||||
|
||||
impl ConditionsFromResolutionMode {
|
||||
pub fn new(func: ConditionsFromResolutionModeFn) -> Self {
|
||||
Self(Some(func))
|
||||
}
|
||||
|
||||
fn resolve(
|
||||
&self,
|
||||
resolution_mode: ResolutionMode,
|
||||
) -> &'static [&'static str] {
|
||||
match &self.0 {
|
||||
Some(func) => func(ResolutionMode::Import),
|
||||
None => resolution_mode.default_conditions(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -69,6 +90,15 @@ pub enum ResolutionMode {
|
|||
Require,
|
||||
}
|
||||
|
||||
impl ResolutionMode {
|
||||
pub fn default_conditions(&self) -> &'static [&'static str] {
|
||||
match self {
|
||||
ResolutionMode::Import => DEFAULT_CONDITIONS,
|
||||
ResolutionMode::Require => REQUIRE_CONDITIONS,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
|
||||
pub enum NodeResolutionKind {
|
||||
Execution,
|
||||
|
@ -120,6 +150,7 @@ pub struct NodeResolver<
|
|||
npm_pkg_folder_resolver: NpmPackageFolderResolverRc,
|
||||
pkg_json_resolver: PackageJsonResolverRc<TSys>,
|
||||
sys: TSys,
|
||||
conditions_from_resolution_mode: ConditionsFromResolutionMode,
|
||||
}
|
||||
|
||||
impl<
|
||||
|
@ -133,6 +164,7 @@ impl<
|
|||
npm_pkg_folder_resolver: NpmPackageFolderResolverRc,
|
||||
pkg_json_resolver: PackageJsonResolverRc<TSys>,
|
||||
sys: TSys,
|
||||
conditions_from_resolution_mode: ConditionsFromResolutionMode,
|
||||
) -> Self {
|
||||
Self {
|
||||
in_npm_pkg_checker,
|
||||
|
@ -140,6 +172,7 @@ impl<
|
|||
npm_pkg_folder_resolver,
|
||||
pkg_json_resolver,
|
||||
sys,
|
||||
conditions_from_resolution_mode,
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -197,11 +230,14 @@ impl<
|
|||
}
|
||||
}
|
||||
|
||||
let conditions = self
|
||||
.conditions_from_resolution_mode
|
||||
.resolve(resolution_mode);
|
||||
let url = self.module_resolve(
|
||||
specifier,
|
||||
referrer,
|
||||
resolution_mode,
|
||||
conditions_from_resolution_mode(resolution_mode),
|
||||
conditions,
|
||||
resolution_kind,
|
||||
)?;
|
||||
|
||||
|
@ -211,6 +247,7 @@ impl<
|
|||
&file_path,
|
||||
Some(referrer),
|
||||
resolution_mode,
|
||||
conditions,
|
||||
)?
|
||||
} else {
|
||||
url
|
||||
|
@ -343,7 +380,9 @@ impl<
|
|||
&package_subpath,
|
||||
maybe_referrer,
|
||||
resolution_mode,
|
||||
conditions_from_resolution_mode(resolution_mode),
|
||||
self
|
||||
.conditions_from_resolution_mode
|
||||
.resolve(resolution_mode),
|
||||
resolution_kind,
|
||||
)?;
|
||||
// TODO(bartlomieju): skipped checking errors for commonJS resolution and
|
||||
|
@ -411,6 +450,7 @@ impl<
|
|||
path: &Path,
|
||||
maybe_referrer: Option<&Url>,
|
||||
resolution_mode: ResolutionMode,
|
||||
conditions: &[&str],
|
||||
) -> Result<Url, TypesNotFoundError> {
|
||||
fn probe_extensions<TSys: FsMetadata>(
|
||||
sys: &TSys,
|
||||
|
@ -474,7 +514,7 @@ impl<
|
|||
/* sub path */ ".",
|
||||
maybe_referrer,
|
||||
resolution_mode,
|
||||
conditions_from_resolution_mode(resolution_mode),
|
||||
conditions,
|
||||
NodeResolutionKind::Types,
|
||||
);
|
||||
if let Ok(resolution) = resolution_result {
|
||||
|
@ -855,6 +895,7 @@ impl<
|
|||
&path,
|
||||
maybe_referrer,
|
||||
resolution_mode,
|
||||
conditions,
|
||||
)?));
|
||||
} else {
|
||||
return Ok(Some(url));
|
||||
|
@ -1212,6 +1253,7 @@ impl<
|
|||
package_subpath,
|
||||
maybe_referrer,
|
||||
resolution_mode,
|
||||
conditions,
|
||||
resolution_kind,
|
||||
)
|
||||
.map_err(|err| {
|
||||
|
@ -1249,6 +1291,7 @@ impl<
|
|||
package_json,
|
||||
referrer,
|
||||
resolution_mode,
|
||||
conditions,
|
||||
resolution_kind,
|
||||
)
|
||||
.map_err(|err| {
|
||||
|
@ -1268,6 +1311,7 @@ impl<
|
|||
package_json,
|
||||
referrer,
|
||||
resolution_mode,
|
||||
conditions,
|
||||
resolution_kind,
|
||||
)
|
||||
.map_err(|err| {
|
||||
|
@ -1281,6 +1325,7 @@ impl<
|
|||
package_subpath,
|
||||
referrer,
|
||||
resolution_mode,
|
||||
conditions,
|
||||
resolution_kind,
|
||||
)
|
||||
.map_err(|err| {
|
||||
|
@ -1294,12 +1339,18 @@ impl<
|
|||
package_subpath: &str,
|
||||
referrer: Option<&Url>,
|
||||
resolution_mode: ResolutionMode,
|
||||
conditions: &[&str],
|
||||
resolution_kind: NodeResolutionKind,
|
||||
) -> Result<Url, TypesNotFoundError> {
|
||||
assert_ne!(package_subpath, ".");
|
||||
let file_path = directory.join(package_subpath);
|
||||
if resolution_kind.is_types() {
|
||||
Ok(self.path_to_declaration_url(&file_path, referrer, resolution_mode)?)
|
||||
Ok(self.path_to_declaration_url(
|
||||
&file_path,
|
||||
referrer,
|
||||
resolution_mode,
|
||||
conditions,
|
||||
)?)
|
||||
} else {
|
||||
Ok(url_from_file_path(&file_path).unwrap())
|
||||
}
|
||||
|
@ -1311,6 +1362,7 @@ impl<
|
|||
package_subpath: &str,
|
||||
maybe_referrer: Option<&Url>,
|
||||
resolution_mode: ResolutionMode,
|
||||
conditions: &[&str],
|
||||
resolution_kind: NodeResolutionKind,
|
||||
) -> Result<Url, LegacyResolveError> {
|
||||
if package_subpath == "." {
|
||||
|
@ -1327,6 +1379,7 @@ impl<
|
|||
package_subpath,
|
||||
maybe_referrer,
|
||||
resolution_mode,
|
||||
conditions,
|
||||
resolution_kind,
|
||||
)
|
||||
.map_err(|err| err.into())
|
||||
|
@ -1338,6 +1391,7 @@ impl<
|
|||
package_json: &PackageJson,
|
||||
maybe_referrer: Option<&Url>,
|
||||
resolution_mode: ResolutionMode,
|
||||
conditions: &[&str],
|
||||
resolution_kind: NodeResolutionKind,
|
||||
) -> Result<Url, LegacyResolveError> {
|
||||
let pkg_json_kind = match resolution_mode {
|
||||
|
@ -1356,6 +1410,7 @@ impl<
|
|||
&main,
|
||||
maybe_referrer,
|
||||
resolution_mode,
|
||||
conditions,
|
||||
);
|
||||
// don't surface errors, fallback to checking the index now
|
||||
if let Ok(url) = decl_url_result {
|
||||
|
|
|
@ -140,7 +140,9 @@ pub trait NpmProcessStateProvider:
|
|||
|
||||
#[derive(Debug)]
|
||||
pub struct EmptyNpmProcessStateProvider;
|
||||
|
||||
impl NpmProcessStateProvider for EmptyNpmProcessStateProvider {}
|
||||
|
||||
deno_core::extension!(
|
||||
deno_process,
|
||||
ops = [
|
||||
|
|
|
@ -11690,6 +11690,46 @@ fn lsp_format_exclude_default_config() {
|
|||
client.shutdown();
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn lsp_format_untitled() {
|
||||
let context = TestContextBuilder::new().use_temp_cwd().build();
|
||||
let mut client = context.new_lsp_command().build();
|
||||
client.initialize_default();
|
||||
client.did_open(json!({
|
||||
"textDocument": {
|
||||
"uri": "untitled:Untitled-1",
|
||||
"languageId": "typescript",
|
||||
"version": 1,
|
||||
"text": " console.log();\n",
|
||||
},
|
||||
}));
|
||||
let res = client.write_request(
|
||||
"textDocument/formatting",
|
||||
json!({
|
||||
"textDocument": {
|
||||
"uri": "untitled:Untitled-1",
|
||||
},
|
||||
"options": {
|
||||
"tabSize": 2,
|
||||
"insertSpaces": true,
|
||||
},
|
||||
}),
|
||||
);
|
||||
assert_eq!(
|
||||
res,
|
||||
json!([
|
||||
{
|
||||
"range": {
|
||||
"start": { "line": 0, "character": 0 },
|
||||
"end": { "line": 0, "character": 2 },
|
||||
},
|
||||
"newText": "",
|
||||
},
|
||||
])
|
||||
);
|
||||
client.shutdown();
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn lsp_format_json() {
|
||||
let context = TestContextBuilder::new().use_temp_cwd().build();
|
||||
|
@ -17221,3 +17261,38 @@ fn lsp_wasm_module() {
|
|||
);
|
||||
client.shutdown();
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn wildcard_augment() {
|
||||
let context = TestContextBuilder::new().use_temp_cwd().build();
|
||||
let mut client = context.new_lsp_command().build();
|
||||
let temp_dir = context.temp_dir().path();
|
||||
let source = source_file(
|
||||
temp_dir.join("index.ts"),
|
||||
r#"
|
||||
import styles from "./hello_world.scss";
|
||||
|
||||
function bar(v: string): string {
|
||||
return v;
|
||||
}
|
||||
|
||||
bar(styles);
|
||||
"#,
|
||||
);
|
||||
temp_dir.join("index.d.ts").write(
|
||||
r#"
|
||||
declare module '*.scss' {
|
||||
const content: string;
|
||||
export default content;
|
||||
}
|
||||
"#,
|
||||
);
|
||||
temp_dir
|
||||
.join("hello_world.scss")
|
||||
.write("body { color: red; }");
|
||||
|
||||
client.initialize_default();
|
||||
|
||||
let diagnostics = client.did_open_file(&source);
|
||||
assert_eq!(diagnostics.all().len(), 0);
|
||||
}
|
||||
|
|
Binary file not shown.
201
tests/registry/npm/npm-check-updates/registry.json
Normal file
201
tests/registry/npm/npm-check-updates/registry.json
Normal file
|
@ -0,0 +1,201 @@
|
|||
{
|
||||
"name": "npm-check-updates",
|
||||
"dist-tags": {
|
||||
"latest": "17.1.13"
|
||||
},
|
||||
"versions": {
|
||||
"17.1.13": {
|
||||
"name": "npm-check-updates",
|
||||
"version": "17.1.13",
|
||||
"author": {
|
||||
"name": "Tomas Junnonen",
|
||||
"email": "tomas1@gmail.com"
|
||||
},
|
||||
"license": "Apache-2.0",
|
||||
"description": "Find newer versions of dependencies than what your package.json allows",
|
||||
"engines": {
|
||||
"node": "^18.18.0 || >=20.0.0",
|
||||
"npm": ">=8.12.1"
|
||||
},
|
||||
"main": "build/index.js",
|
||||
"types": "build/index.d.ts",
|
||||
"scripts": {
|
||||
"build": "rimraf build && npm run build:options && vite build",
|
||||
"build:options": "vite-node src/scripts/build-options.ts",
|
||||
"build:analyze": "rimraf build && npm run build:options && ANALYZER=true vite build",
|
||||
"lint": "cross-env FORCE_COLOR=1 npm-run-all --parallel --aggregate-output lint:*",
|
||||
"lint:lockfile": "lockfile-lint",
|
||||
"lint:markdown": "markdownlint \"**/*.md\" --ignore \"**/node_modules/**/*.md\" --ignore build --config .markdownlint.js",
|
||||
"lint:src": "eslint --cache --cache-location node_modules/.cache/.eslintcache --ignore-path .gitignore --report-unused-disable-directives .",
|
||||
"prepare": "src/scripts/install-hooks",
|
||||
"prepublishOnly": "npm run build",
|
||||
"prettier": "prettier . --check",
|
||||
"test": "npm run test:unit && npm run test:e2e",
|
||||
"test:bun": "test/bun-install.sh && mocha test/bun",
|
||||
"test:unit": "mocha test test/package-managers/*",
|
||||
"test:e2e": "./test/e2e.sh",
|
||||
"ncu": "node build/cli.js"
|
||||
},
|
||||
"bin": {
|
||||
"npm-check-updates": "build/cli.js",
|
||||
"ncu": "build/cli.js"
|
||||
},
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "git+https://github.com/raineorshine/npm-check-updates.git"
|
||||
},
|
||||
"bugs": {
|
||||
"url": "https://github.com/raineorshine/npm-check-updates/issues"
|
||||
},
|
||||
"overrides": {
|
||||
"ip": "2.0.1",
|
||||
"jsonparse": "https://github.com/ARitz-Cracker/jsonparse/tree/patch-1",
|
||||
"@yarnpkg/parsers": "2.6.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@trivago/prettier-plugin-sort-imports": "^4.3.0",
|
||||
"@types/chai": "^4.3.19",
|
||||
"@types/chai-as-promised": "^8.0.0",
|
||||
"@types/chai-string": "^1.4.5",
|
||||
"@types/cli-table": "^0.3.4",
|
||||
"@types/hosted-git-info": "^3.0.5",
|
||||
"@types/ini": "^4.1.1",
|
||||
"@types/js-yaml": "^4.0.9",
|
||||
"@types/json-parse-helpfulerror": "^1.0.3",
|
||||
"@types/jsonlines": "^0.1.5",
|
||||
"@types/lodash": "^4.17.10",
|
||||
"@types/mocha": "^10.0.9",
|
||||
"@types/node": "^22.7.5",
|
||||
"@types/npm-registry-fetch": "^8.0.7",
|
||||
"@types/parse-github-url": "^1.0.3",
|
||||
"@types/picomatch": "^3.0.1",
|
||||
"@types/progress": "^2.0.7",
|
||||
"@types/prompts": "^2.4.9",
|
||||
"@types/remote-git-tags": "^4.0.2",
|
||||
"@types/semver": "^7.5.8",
|
||||
"@types/semver-utils": "^1.1.3",
|
||||
"@types/sinon": "^17.0.3",
|
||||
"@types/update-notifier": "^6.0.8",
|
||||
"@typescript-eslint/eslint-plugin": "^8.9.0",
|
||||
"@typescript-eslint/parser": "^8.9.0",
|
||||
"camelcase": "^6.3.0",
|
||||
"chai": "^4.3.10",
|
||||
"chai-as-promised": "^7.1.2",
|
||||
"chai-string": "^1.5.0",
|
||||
"chalk": "^5.3.0",
|
||||
"cli-table3": "^0.6.5",
|
||||
"commander": "^12.1.0",
|
||||
"cross-env": "^7.0.3",
|
||||
"dequal": "^2.0.3",
|
||||
"eslint": "^8.57.0",
|
||||
"eslint-config-prettier": "^9.1.0",
|
||||
"eslint-config-raine": "^0.5.0",
|
||||
"eslint-config-standard": "^17.1.0",
|
||||
"eslint-plugin-import": "^2.31.0",
|
||||
"eslint-plugin-jsdoc": "^50.4.1",
|
||||
"eslint-plugin-n": "^16.6.2",
|
||||
"eslint-plugin-promise": "^6.6.0",
|
||||
"fast-glob": "^3.3.2",
|
||||
"fast-memoize": "^2.5.2",
|
||||
"find-up": "5.0.0",
|
||||
"fp-and-or": "^1.0.2",
|
||||
"hosted-git-info": "^8.0.0",
|
||||
"ini": "^5.0.0",
|
||||
"js-yaml": "^4.1.0",
|
||||
"json-parse-helpfulerror": "^1.0.3",
|
||||
"jsonlines": "^0.1.1",
|
||||
"lockfile-lint": "^4.14.0",
|
||||
"lodash": "^4.17.21",
|
||||
"markdownlint-cli": "^0.42.0",
|
||||
"mocha": "^10.7.3",
|
||||
"npm-registry-fetch": "^18.0.2",
|
||||
"npm-run-all": "^4.1.5",
|
||||
"p-map": "^4.0.0",
|
||||
"parse-github-url": "^1.0.3",
|
||||
"picomatch": "^4.0.2",
|
||||
"prettier": "^3.3.3",
|
||||
"progress": "^2.0.3",
|
||||
"prompts-ncu": "^3.0.2",
|
||||
"rc-config-loader": "^4.1.3",
|
||||
"remote-git-tags": "^3.0.0",
|
||||
"rfdc": "^1.4.1",
|
||||
"rimraf": "^6.0.1",
|
||||
"rollup-plugin-node-externals": "^7.1.3",
|
||||
"semver": "^7.6.3",
|
||||
"semver-utils": "^1.1.4",
|
||||
"should": "^13.2.3",
|
||||
"sinon": "^19.0.2",
|
||||
"source-map-support": "^0.5.21",
|
||||
"spawn-please": "^3.0.0",
|
||||
"strip-ansi": "^7.1.0",
|
||||
"strip-json-comments": "^5.0.1",
|
||||
"ts-node": "^10.9.2",
|
||||
"typescript": "^5.6.3",
|
||||
"typescript-json-schema": "^0.65.1",
|
||||
"untildify": "^4.0.0",
|
||||
"update-notifier": "^7.3.1",
|
||||
"verdaccio": "^6.0.1",
|
||||
"vite": "^5.4.9",
|
||||
"vite-bundle-analyzer": "^0.12.1",
|
||||
"vite-node": "^2.1.3",
|
||||
"vite-plugin-dts": "^4.2.4",
|
||||
"yarn": "^1.22.22"
|
||||
},
|
||||
"lockfile-lint": {
|
||||
"allowed-schemes": [
|
||||
"https:",
|
||||
"git+ssh:"
|
||||
],
|
||||
"allowed-hosts": [
|
||||
"npm",
|
||||
"github.com"
|
||||
],
|
||||
"empty-hostname": false,
|
||||
"type": "npm ",
|
||||
"path": "package-lock.json"
|
||||
},
|
||||
"mocha": {
|
||||
"check-leaks": true,
|
||||
"extension": [
|
||||
"test.ts"
|
||||
],
|
||||
"require": [
|
||||
"source-map-support/register",
|
||||
"ts-node/register"
|
||||
],
|
||||
"timeout": 60000,
|
||||
"trace-deprecation": true,
|
||||
"trace-warnings": true,
|
||||
"use_strict": true
|
||||
},
|
||||
"_id": "npm-check-updates@17.1.13",
|
||||
"gitHead": "f514093647f1833c83653765493c694372d14fea",
|
||||
"_nodeVersion": "22.0.0",
|
||||
"_npmVersion": "10.9.1",
|
||||
"dist": {
|
||||
"integrity": "sha512-m9Woo2J5XVab0VcQpYvrQ0hx3ySI1mGbiHR595mc6Lr1/FIaTWvv+oU+T1WKSfXRiluKC/V5P6Bdk5agaYpqqg==",
|
||||
"shasum": "93e1c5fa5b8e11bca0bd143650b14ffcf9fc6b5a",
|
||||
"tarball": "http://localhost:4260/npm-check-updates/npm-check-updates-17.1.13.tgz",
|
||||
"fileCount": 19,
|
||||
"unpackedSize": 5336239
|
||||
},
|
||||
"directories": {},
|
||||
"_hasShrinkwrap": false
|
||||
}
|
||||
},
|
||||
"bugs": {
|
||||
"url": "https://github.com/raineorshine/npm-check-updates/issues"
|
||||
},
|
||||
"author": {
|
||||
"name": "Tomas Junnonen",
|
||||
"email": "tomas1@gmail.com"
|
||||
},
|
||||
"license": "Apache-2.0",
|
||||
"homepage": "https://github.com/raineorshine/npm-check-updates",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "git+https://github.com/raineorshine/npm-check-updates.git"
|
||||
},
|
||||
"description": "Find newer versions of dependencies than what your package.json allows",
|
||||
"readmeFilename": "README.md"
|
||||
}
|
24
tests/specs/compile/case_insensitive_building/__test__.jsonc
Normal file
24
tests/specs/compile/case_insensitive_building/__test__.jsonc
Normal file
|
@ -0,0 +1,24 @@
|
|||
{
|
||||
"tempDir": true,
|
||||
"steps": [{
|
||||
"if": "mac",
|
||||
"args": "compile --output main --include file.txt --include FILE.txt main.js",
|
||||
"output": "compile.out"
|
||||
}, {
|
||||
"if": "mac",
|
||||
"commandName": "./main",
|
||||
"args": [],
|
||||
"output": "main.out",
|
||||
"exitCode": 0
|
||||
}, {
|
||||
"if": "windows",
|
||||
"args": "compile --output main.exe --include file.txt --include FILE.txt main.js",
|
||||
"output": "compile.out"
|
||||
}, {
|
||||
"if": "windows",
|
||||
"commandName": "./main.exe",
|
||||
"args": [],
|
||||
"output": "main.out",
|
||||
"exitCode": 0
|
||||
}]
|
||||
}
|
10
tests/specs/compile/case_insensitive_building/compile.out
Normal file
10
tests/specs/compile/case_insensitive_building/compile.out
Normal file
|
@ -0,0 +1,10 @@
|
|||
Compile file:///[WILDLINE]/main.js to main[WILDLINE]
|
||||
|
||||
Embedded Files
|
||||
|
||||
main[WILDLINE]
|
||||
├── file.txt ([WILDLINE])
|
||||
└── main.js ([WILDLINE])
|
||||
|
||||
Size: [WILDLINE]
|
||||
|
1
tests/specs/compile/case_insensitive_building/file.txt
Normal file
1
tests/specs/compile/case_insensitive_building/file.txt
Normal file
|
@ -0,0 +1 @@
|
|||
Hi
|
1
tests/specs/compile/case_insensitive_building/main.js
Normal file
1
tests/specs/compile/case_insensitive_building/main.js
Normal file
|
@ -0,0 +1 @@
|
|||
console.log(Deno.readTextFileSync(import.meta.dirname + "/file.txt"));
|
1
tests/specs/compile/case_insensitive_building/main.out
Normal file
1
tests/specs/compile/case_insensitive_building/main.out
Normal file
|
@ -0,0 +1 @@
|
|||
Hi
|
|
@ -1,4 +1,4 @@
|
|||
error: Failed to parse "lint" configuration
|
||||
|
||||
Caused by:
|
||||
unknown field `dont_know_this_field`, expected one of `rules`, `include`, `exclude`, `files`, `report`
|
||||
unknown field `dont_know_this_field`, expected one of `rules`, `include`, `exclude`, `files`, `report`, `plugins`
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
error: Failed to parse "lint" configuration
|
||||
|
||||
Caused by:
|
||||
unknown field `dont_know_this_field`, expected one of `rules`, `include`, `exclude`, `files`, `report`
|
||||
unknown field `dont_know_this_field`, expected one of `rules`, `include`, `exclude`, `files`, `report`, `plugins`
|
||||
|
|
7
tests/specs/npm/npm_check_updates/__test__.jsonc
Normal file
7
tests/specs/npm/npm_check_updates/__test__.jsonc
Normal file
|
@ -0,0 +1,7 @@
|
|||
{
|
||||
"tempDir": true,
|
||||
"steps": [{
|
||||
"args": "run -A npm:npm-check-updates",
|
||||
"output": "output.out"
|
||||
}]
|
||||
}
|
2
tests/specs/npm/npm_check_updates/output.out
Normal file
2
tests/specs/npm/npm_check_updates/output.out
Normal file
|
@ -0,0 +1,2 @@
|
|||
[WILDCARD]
|
||||
All dependencies match the latest package versions[WILDCARD]
|
5
tests/specs/npm/npm_check_updates/package.json
Normal file
5
tests/specs/npm/npm_check_updates/package.json
Normal file
|
@ -0,0 +1,5 @@
|
|||
{
|
||||
"dependencies": {
|
||||
"chalk": "^5.0.1"
|
||||
}
|
||||
}
|
|
@ -5,6 +5,11 @@
|
|||
"output": "main.ts.out",
|
||||
"exitCode": 1
|
||||
},
|
||||
"basic_no_config": {
|
||||
"args": "run --quiet --no-config main.ts",
|
||||
"output": "main_no_config.out",
|
||||
"exitCode": 1
|
||||
},
|
||||
"unstable_bare_node_builtins_enabled": {
|
||||
"args": "run --unstable-bare-node-builtins main.ts",
|
||||
"output": "feature_enabled.out"
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
{
|
||||
"imports": {},
|
||||
"unstable": ["bare-node-builtins"]
|
||||
}
|
||||
|
|
3
tests/specs/run/node_prefix_missing/deno.json
Normal file
3
tests/specs/run/node_prefix_missing/deno.json
Normal file
|
@ -0,0 +1,3 @@
|
|||
{
|
||||
"imports": {}
|
||||
}
|
|
@ -1,3 +1,3 @@
|
|||
error: Relative import path "fs" not prefixed with / or ./ or ../
|
||||
error: Relative import path "fs" not prefixed with / or ./ or ../ and not in import map from "[WILDLINE]/main.ts"
|
||||
hint: If you want to use a built-in Node module, add a "node:" prefix (ex. "node:fs").
|
||||
at file:///[WILDCARD]/main.ts:1:16
|
||||
|
|
3
tests/specs/run/node_prefix_missing/main_no_config.out
Normal file
3
tests/specs/run/node_prefix_missing/main_no_config.out
Normal file
|
@ -0,0 +1,3 @@
|
|||
error: Relative import path "fs" not prefixed with / or ./ or ../
|
||||
hint: If you want to use a built-in Node module, add a "node:" prefix (ex. "node:fs").
|
||||
at file:///[WILDCARD]/main.ts:1:16
|
8608
tests/unit/__snapshots__/lint_plugin_test.ts.snap
Normal file
8608
tests/unit/__snapshots__/lint_plugin_test.ts.snap
Normal file
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
|
@ -118,6 +118,45 @@ Deno.test("[node/fs filehandle.writeFile] Write to file", async function () {
|
|||
assertEquals(decoder.decode(data), "hello world");
|
||||
});
|
||||
|
||||
Deno.test(
|
||||
"[node/fs filehandle.writev] Write array of buffers to file",
|
||||
async function () {
|
||||
const tempFile: string = await Deno.makeTempFile();
|
||||
const fileHandle = await fs.open(tempFile, "w");
|
||||
|
||||
const buffer1 = Buffer.from("hello ");
|
||||
const buffer2 = Buffer.from("world");
|
||||
const res = await fileHandle.writev([buffer1, buffer2]);
|
||||
|
||||
const data = Deno.readFileSync(tempFile);
|
||||
await Deno.remove(tempFile);
|
||||
await fileHandle.close();
|
||||
|
||||
assertEquals(res.bytesWritten, 11);
|
||||
assertEquals(decoder.decode(data), "hello world");
|
||||
},
|
||||
);
|
||||
|
||||
Deno.test(
|
||||
"[node/fs filehandle.writev] Write array of buffers to file with position",
|
||||
async function () {
|
||||
const tempFile: string = await Deno.makeTempFile();
|
||||
const fileHandle = await fs.open(tempFile, "w");
|
||||
|
||||
const buffer1 = Buffer.from("hello ");
|
||||
const buffer2 = Buffer.from("world");
|
||||
await fileHandle.writev([buffer1, buffer2], 0);
|
||||
const buffer3 = Buffer.from("lorem ipsum");
|
||||
await fileHandle.writev([buffer3], 6);
|
||||
|
||||
const data = Deno.readFileSync(tempFile);
|
||||
await Deno.remove(tempFile);
|
||||
await fileHandle.close();
|
||||
|
||||
assertEquals(decoder.decode(data), "hello lorem ipsum");
|
||||
},
|
||||
);
|
||||
|
||||
Deno.test(
|
||||
"[node/fs filehandle.truncate] Truncate file with length",
|
||||
async function () {
|
||||
|
@ -217,3 +256,20 @@ Deno.test({
|
|||
await fileHandle.close();
|
||||
},
|
||||
});
|
||||
|
||||
Deno.test({
|
||||
name:
|
||||
"[node/fs filehandle.utimes] Change the file system timestamps of the file",
|
||||
async fn() {
|
||||
const fileHandle = await fs.open(testData);
|
||||
|
||||
const atime = new Date();
|
||||
const mtime = new Date(0);
|
||||
|
||||
await fileHandle.utimes(atime, mtime);
|
||||
assertEquals(Deno.statSync(testData).atime!, atime);
|
||||
assertEquals(Deno.statSync(testData).mtime!, mtime);
|
||||
|
||||
await fileHandle.close();
|
||||
},
|
||||
});
|
||||
|
|
|
@ -656,6 +656,73 @@ Deno.test({
|
|||
},
|
||||
});
|
||||
|
||||
Deno.test({
|
||||
name:
|
||||
"[node/child_process spawn] child inherits Deno.env when options.env is not provided",
|
||||
async fn() {
|
||||
const deferred = withTimeout<string>();
|
||||
Deno.env.set("BAR", "BAR");
|
||||
const env = spawn(
|
||||
`"${Deno.execPath()}" eval -p "Deno.env.toObject().BAR"`,
|
||||
{
|
||||
shell: true,
|
||||
},
|
||||
);
|
||||
try {
|
||||
let envOutput = "";
|
||||
|
||||
assert(env.stdout);
|
||||
env.on("error", (err: Error) => deferred.reject(err));
|
||||
env.stdout.on("data", (data) => {
|
||||
envOutput += data;
|
||||
});
|
||||
env.on("close", () => {
|
||||
deferred.resolve(envOutput.trim());
|
||||
});
|
||||
await deferred.promise;
|
||||
} finally {
|
||||
env.kill();
|
||||
Deno.env.delete("BAR");
|
||||
}
|
||||
const value = await deferred.promise;
|
||||
assertEquals(value, "BAR");
|
||||
},
|
||||
});
|
||||
|
||||
Deno.test({
|
||||
name:
|
||||
"[node/child_process spawn] child doesn't inherit Deno.env when options.env is provided",
|
||||
async fn() {
|
||||
const deferred = withTimeout<string>();
|
||||
Deno.env.set("BAZ", "BAZ");
|
||||
const env = spawn(
|
||||
`"${Deno.execPath()}" eval -p "Deno.env.toObject().BAZ"`,
|
||||
{
|
||||
env: {},
|
||||
shell: true,
|
||||
},
|
||||
);
|
||||
try {
|
||||
let envOutput = "";
|
||||
|
||||
assert(env.stdout);
|
||||
env.on("error", (err: Error) => deferred.reject(err));
|
||||
env.stdout.on("data", (data) => {
|
||||
envOutput += data;
|
||||
});
|
||||
env.on("close", () => {
|
||||
deferred.resolve(envOutput.trim());
|
||||
});
|
||||
await deferred.promise;
|
||||
} finally {
|
||||
env.kill();
|
||||
Deno.env.delete("BAZ");
|
||||
}
|
||||
const value = await deferred.promise;
|
||||
assertEquals(value, "undefined");
|
||||
},
|
||||
});
|
||||
|
||||
// Regression test for https://github.com/denoland/deno/issues/20373
|
||||
Deno.test(async function undefinedValueInEnvVar() {
|
||||
const deferred = withTimeout<string>();
|
||||
|
|
|
@ -38,7 +38,7 @@
|
|||
"ext:deno_node/_fs/_fs_stat.ts": "../ext/node/polyfills/_fs/_fs_stat.ts",
|
||||
"ext:deno_node/_fs/_fs_watch.ts": "../ext/node/polyfills/_fs/_fs_watch.ts",
|
||||
"ext:deno_node/_fs/_fs_write.mjs": "../ext/node/polyfills/_fs/_fs_write.mjs",
|
||||
"ext:deno_node/_fs/_fs_writev.mjs": "../ext/node/polyfills/_fs/_fs_writev.mjs",
|
||||
"ext:deno_node/_fs/_fs_writev.ts": "../ext/node/polyfills/_fs/_fs_writev.ts",
|
||||
"ext:deno_node/_global.d.ts": "../ext/node/polyfills/_global.d.ts",
|
||||
"node:_http_agent": "../ext/node/polyfills/_http_agent.mjs",
|
||||
"node:_http_common": "../ext/node/polyfills/_http_common.ts",
|
||||
|
|
Loading…
Add table
Reference in a new issue