mirror of
https://github.com/denoland/deno.git
synced 2024-12-20 14:24:48 -05:00
6f506208f6
Currently deno eagerly caches all npm packages in the workspace's npm resolution. So, for instance, running a file `foo.ts` that imports `npm:chalk` will also install all dependencies listed in `package.json` and all `npm` dependencies listed in the lockfile. This PR refactors things to give more control over when and what npm packages are automatically cached while building the module graph. After this PR, by default the current behavior is unchanged _except_ for `deno install --entrypoint`, which will only cache npm packages used by the given entrypoint. For the other subcommands, this behavior can be enabled with `--unstable-npm-lazy-caching` Fixes #25782. --------- Signed-off-by: Nathan Whitaker <17734409+nathanwhit@users.noreply.github.com> Co-authored-by: Luca Casonato <hello@lcas.dev>
941 lines
30 KiB
Rust
941 lines
30 KiB
Rust
// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license.
|
|
|
|
// Allow unused code warnings because we share
|
|
// code between the two bin targets.
|
|
#![allow(dead_code)]
|
|
#![allow(unused_imports)]
|
|
|
|
use binary::StandaloneData;
|
|
use binary::StandaloneModules;
|
|
use code_cache::DenoCompileCodeCache;
|
|
use deno_ast::MediaType;
|
|
use deno_cache_dir::npm::NpmCacheDir;
|
|
use deno_config::workspace::MappedResolution;
|
|
use deno_config::workspace::MappedResolutionError;
|
|
use deno_config::workspace::ResolverWorkspaceJsrPackage;
|
|
use deno_config::workspace::WorkspaceResolver;
|
|
use deno_core::anyhow::Context;
|
|
use deno_core::error::generic_error;
|
|
use deno_core::error::type_error;
|
|
use deno_core::error::AnyError;
|
|
use deno_core::futures::future::LocalBoxFuture;
|
|
use deno_core::futures::FutureExt;
|
|
use deno_core::v8_set_flags;
|
|
use deno_core::FastString;
|
|
use deno_core::FeatureChecker;
|
|
use deno_core::ModuleLoader;
|
|
use deno_core::ModuleSourceCode;
|
|
use deno_core::ModuleSpecifier;
|
|
use deno_core::ModuleType;
|
|
use deno_core::RequestedModuleType;
|
|
use deno_core::ResolutionKind;
|
|
use deno_core::SourceCodeCacheInfo;
|
|
use deno_npm::npm_rc::ResolvedNpmRc;
|
|
use deno_package_json::PackageJsonDepValue;
|
|
use deno_resolver::cjs::IsCjsResolutionMode;
|
|
use deno_resolver::npm::NpmReqResolverOptions;
|
|
use deno_runtime::deno_fs;
|
|
use deno_runtime::deno_node::create_host_defined_options;
|
|
use deno_runtime::deno_node::NodeRequireLoader;
|
|
use deno_runtime::deno_node::NodeResolver;
|
|
use deno_runtime::deno_node::PackageJsonResolver;
|
|
use deno_runtime::deno_permissions::Permissions;
|
|
use deno_runtime::deno_permissions::PermissionsContainer;
|
|
use deno_runtime::deno_tls::rustls::RootCertStore;
|
|
use deno_runtime::deno_tls::RootCertStoreProvider;
|
|
use deno_runtime::deno_web::BlobStore;
|
|
use deno_runtime::permissions::RuntimePermissionDescriptorParser;
|
|
use deno_runtime::WorkerExecutionMode;
|
|
use deno_runtime::WorkerLogLevel;
|
|
use deno_semver::npm::NpmPackageReqReference;
|
|
use import_map::parse_from_json;
|
|
use node_resolver::analyze::NodeCodeTranslator;
|
|
use node_resolver::errors::ClosestPkgJsonError;
|
|
use node_resolver::NodeResolutionKind;
|
|
use node_resolver::ResolutionMode;
|
|
use serialization::DenoCompileModuleSource;
|
|
use std::borrow::Cow;
|
|
use std::rc::Rc;
|
|
use std::sync::Arc;
|
|
use virtual_fs::FileBackedVfs;
|
|
use virtual_fs::VfsFileSubDataKind;
|
|
|
|
use crate::args::create_default_npmrc;
|
|
use crate::args::get_root_cert_store;
|
|
use crate::args::npm_pkg_req_ref_to_binary_command;
|
|
use crate::args::CaData;
|
|
use crate::args::CacheSetting;
|
|
use crate::args::NpmInstallDepsProvider;
|
|
use crate::args::StorageKeyResolver;
|
|
use crate::cache::Caches;
|
|
use crate::cache::DenoCacheEnvFsAdapter;
|
|
use crate::cache::DenoDirProvider;
|
|
use crate::cache::FastInsecureHasher;
|
|
use crate::cache::NodeAnalysisCache;
|
|
use crate::cache::RealDenoCacheEnv;
|
|
use crate::http_util::HttpClientProvider;
|
|
use crate::node::CliCjsCodeAnalyzer;
|
|
use crate::node::CliNodeCodeTranslator;
|
|
use crate::npm::create_cli_npm_resolver;
|
|
use crate::npm::create_in_npm_pkg_checker;
|
|
use crate::npm::CliByonmNpmResolverCreateOptions;
|
|
use crate::npm::CliManagedInNpmPkgCheckerCreateOptions;
|
|
use crate::npm::CliManagedNpmResolverCreateOptions;
|
|
use crate::npm::CliNpmResolver;
|
|
use crate::npm::CliNpmResolverCreateOptions;
|
|
use crate::npm::CliNpmResolverManagedSnapshotOption;
|
|
use crate::npm::CreateInNpmPkgCheckerOptions;
|
|
use crate::resolver::CjsTracker;
|
|
use crate::resolver::CliDenoResolverFs;
|
|
use crate::resolver::CliNpmReqResolver;
|
|
use crate::resolver::NpmModuleLoader;
|
|
use crate::util::progress_bar::ProgressBar;
|
|
use crate::util::progress_bar::ProgressBarStyle;
|
|
use crate::util::text_encoding::from_utf8_lossy_cow;
|
|
use crate::util::v8::construct_v8_flags;
|
|
use crate::worker::CliCodeCache;
|
|
use crate::worker::CliMainWorkerFactory;
|
|
use crate::worker::CliMainWorkerOptions;
|
|
use crate::worker::CreateModuleLoaderResult;
|
|
use crate::worker::ModuleLoaderFactory;
|
|
|
|
pub mod binary;
|
|
mod code_cache;
|
|
mod file_system;
|
|
mod serialization;
|
|
mod virtual_fs;
|
|
|
|
pub use binary::extract_standalone;
|
|
pub use binary::is_standalone_binary;
|
|
pub use binary::DenoCompileBinaryWriter;
|
|
|
|
use self::binary::Metadata;
|
|
use self::file_system::DenoCompileFileSystem;
|
|
|
|
struct SharedModuleLoaderState {
|
|
cjs_tracker: Arc<CjsTracker>,
|
|
code_cache: Option<Arc<dyn CliCodeCache>>,
|
|
fs: Arc<dyn deno_fs::FileSystem>,
|
|
modules: StandaloneModules,
|
|
node_code_translator: Arc<CliNodeCodeTranslator>,
|
|
node_resolver: Arc<NodeResolver>,
|
|
npm_module_loader: Arc<NpmModuleLoader>,
|
|
npm_req_resolver: Arc<CliNpmReqResolver>,
|
|
npm_resolver: Arc<dyn CliNpmResolver>,
|
|
vfs: Arc<FileBackedVfs>,
|
|
workspace_resolver: WorkspaceResolver,
|
|
}
|
|
|
|
impl SharedModuleLoaderState {
|
|
fn get_code_cache(
|
|
&self,
|
|
specifier: &ModuleSpecifier,
|
|
source: &[u8],
|
|
) -> Option<SourceCodeCacheInfo> {
|
|
let Some(code_cache) = &self.code_cache else {
|
|
return None;
|
|
};
|
|
if !code_cache.enabled() {
|
|
return None;
|
|
}
|
|
// deno version is already included in the root cache key
|
|
let hash = FastInsecureHasher::new_without_deno_version()
|
|
.write_hashable(source)
|
|
.finish();
|
|
let data = code_cache.get_sync(
|
|
specifier,
|
|
deno_runtime::code_cache::CodeCacheType::EsModule,
|
|
hash,
|
|
);
|
|
Some(SourceCodeCacheInfo {
|
|
hash,
|
|
data: data.map(Cow::Owned),
|
|
})
|
|
}
|
|
}
|
|
|
|
#[derive(Clone)]
|
|
struct EmbeddedModuleLoader {
|
|
shared: Arc<SharedModuleLoaderState>,
|
|
}
|
|
|
|
impl std::fmt::Debug for EmbeddedModuleLoader {
|
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
|
f.debug_struct("EmbeddedModuleLoader").finish()
|
|
}
|
|
}
|
|
|
|
pub const MODULE_NOT_FOUND: &str = "Module not found";
|
|
pub const UNSUPPORTED_SCHEME: &str = "Unsupported scheme";
|
|
|
|
impl ModuleLoader for EmbeddedModuleLoader {
|
|
fn resolve(
|
|
&self,
|
|
raw_specifier: &str,
|
|
referrer: &str,
|
|
kind: ResolutionKind,
|
|
) -> Result<ModuleSpecifier, AnyError> {
|
|
let referrer = if referrer == "." {
|
|
if kind != ResolutionKind::MainModule {
|
|
return Err(generic_error(format!(
|
|
"Expected to resolve main module, got {:?} instead.",
|
|
kind
|
|
)));
|
|
}
|
|
let current_dir = std::env::current_dir().unwrap();
|
|
deno_core::resolve_path(".", ¤t_dir)?
|
|
} else {
|
|
ModuleSpecifier::parse(referrer).map_err(|err| {
|
|
type_error(format!("Referrer uses invalid specifier: {}", err))
|
|
})?
|
|
};
|
|
let referrer_kind = if self
|
|
.shared
|
|
.cjs_tracker
|
|
.is_maybe_cjs(&referrer, MediaType::from_specifier(&referrer))?
|
|
{
|
|
ResolutionMode::Require
|
|
} else {
|
|
ResolutionMode::Import
|
|
};
|
|
|
|
if self.shared.node_resolver.in_npm_package(&referrer) {
|
|
return Ok(
|
|
self
|
|
.shared
|
|
.node_resolver
|
|
.resolve(
|
|
raw_specifier,
|
|
&referrer,
|
|
referrer_kind,
|
|
NodeResolutionKind::Execution,
|
|
)?
|
|
.into_url(),
|
|
);
|
|
}
|
|
|
|
let mapped_resolution = self
|
|
.shared
|
|
.workspace_resolver
|
|
.resolve(raw_specifier, &referrer);
|
|
|
|
match mapped_resolution {
|
|
Ok(MappedResolution::WorkspaceJsrPackage { specifier, .. }) => {
|
|
Ok(specifier)
|
|
}
|
|
Ok(MappedResolution::WorkspaceNpmPackage {
|
|
target_pkg_json: pkg_json,
|
|
sub_path,
|
|
..
|
|
}) => Ok(
|
|
self
|
|
.shared
|
|
.node_resolver
|
|
.resolve_package_subpath_from_deno_module(
|
|
pkg_json.dir_path(),
|
|
sub_path.as_deref(),
|
|
Some(&referrer),
|
|
referrer_kind,
|
|
NodeResolutionKind::Execution,
|
|
)?,
|
|
),
|
|
Ok(MappedResolution::PackageJson {
|
|
dep_result,
|
|
sub_path,
|
|
alias,
|
|
..
|
|
}) => match dep_result.as_ref().map_err(|e| AnyError::from(e.clone()))? {
|
|
PackageJsonDepValue::Req(req) => self
|
|
.shared
|
|
.npm_req_resolver
|
|
.resolve_req_with_sub_path(
|
|
req,
|
|
sub_path.as_deref(),
|
|
&referrer,
|
|
referrer_kind,
|
|
NodeResolutionKind::Execution,
|
|
)
|
|
.map_err(AnyError::from),
|
|
PackageJsonDepValue::Workspace(version_req) => {
|
|
let pkg_folder = self
|
|
.shared
|
|
.workspace_resolver
|
|
.resolve_workspace_pkg_json_folder_for_pkg_json_dep(
|
|
alias,
|
|
version_req,
|
|
)?;
|
|
Ok(
|
|
self
|
|
.shared
|
|
.node_resolver
|
|
.resolve_package_subpath_from_deno_module(
|
|
pkg_folder,
|
|
sub_path.as_deref(),
|
|
Some(&referrer),
|
|
referrer_kind,
|
|
NodeResolutionKind::Execution,
|
|
)?,
|
|
)
|
|
}
|
|
},
|
|
Ok(MappedResolution::Normal { specifier, .. })
|
|
| Ok(MappedResolution::ImportMap { specifier, .. }) => {
|
|
if let Ok(reference) =
|
|
NpmPackageReqReference::from_specifier(&specifier)
|
|
{
|
|
return Ok(self.shared.npm_req_resolver.resolve_req_reference(
|
|
&reference,
|
|
&referrer,
|
|
referrer_kind,
|
|
NodeResolutionKind::Execution,
|
|
)?);
|
|
}
|
|
|
|
if specifier.scheme() == "jsr" {
|
|
if let Some(specifier) =
|
|
self.shared.modules.resolve_specifier(&specifier)?
|
|
{
|
|
return Ok(specifier.clone());
|
|
}
|
|
}
|
|
|
|
Ok(
|
|
self
|
|
.shared
|
|
.node_resolver
|
|
.handle_if_in_node_modules(&specifier)
|
|
.unwrap_or(specifier),
|
|
)
|
|
}
|
|
Err(err)
|
|
if err.is_unmapped_bare_specifier() && referrer.scheme() == "file" =>
|
|
{
|
|
let maybe_res = self.shared.npm_req_resolver.resolve_if_for_npm_pkg(
|
|
raw_specifier,
|
|
&referrer,
|
|
referrer_kind,
|
|
NodeResolutionKind::Execution,
|
|
)?;
|
|
if let Some(res) = maybe_res {
|
|
return Ok(res.into_url());
|
|
}
|
|
Err(err.into())
|
|
}
|
|
Err(err) => Err(err.into()),
|
|
}
|
|
}
|
|
|
|
fn get_host_defined_options<'s>(
|
|
&self,
|
|
scope: &mut deno_core::v8::HandleScope<'s>,
|
|
name: &str,
|
|
) -> Option<deno_core::v8::Local<'s, deno_core::v8::Data>> {
|
|
let name = deno_core::ModuleSpecifier::parse(name).ok()?;
|
|
if self.shared.node_resolver.in_npm_package(&name) {
|
|
Some(create_host_defined_options(scope))
|
|
} else {
|
|
None
|
|
}
|
|
}
|
|
|
|
fn load(
|
|
&self,
|
|
original_specifier: &ModuleSpecifier,
|
|
maybe_referrer: Option<&ModuleSpecifier>,
|
|
_is_dynamic: bool,
|
|
_requested_module_type: RequestedModuleType,
|
|
) -> deno_core::ModuleLoadResponse {
|
|
if original_specifier.scheme() == "data" {
|
|
let data_url_text =
|
|
match deno_graph::source::RawDataUrl::parse(original_specifier)
|
|
.and_then(|url| url.decode())
|
|
{
|
|
Ok(response) => response,
|
|
Err(err) => {
|
|
return deno_core::ModuleLoadResponse::Sync(Err(type_error(
|
|
format!("{:#}", err),
|
|
)));
|
|
}
|
|
};
|
|
return deno_core::ModuleLoadResponse::Sync(Ok(
|
|
deno_core::ModuleSource::new(
|
|
deno_core::ModuleType::JavaScript,
|
|
ModuleSourceCode::String(data_url_text.into()),
|
|
original_specifier,
|
|
None,
|
|
),
|
|
));
|
|
}
|
|
|
|
if self.shared.node_resolver.in_npm_package(original_specifier) {
|
|
let shared = self.shared.clone();
|
|
let original_specifier = original_specifier.clone();
|
|
let maybe_referrer = maybe_referrer.cloned();
|
|
return deno_core::ModuleLoadResponse::Async(
|
|
async move {
|
|
let code_source = shared
|
|
.npm_module_loader
|
|
.load(&original_specifier, maybe_referrer.as_ref())
|
|
.await?;
|
|
let code_cache_entry = shared.get_code_cache(
|
|
&code_source.found_url,
|
|
code_source.code.as_bytes(),
|
|
);
|
|
Ok(deno_core::ModuleSource::new_with_redirect(
|
|
match code_source.media_type {
|
|
MediaType::Json => ModuleType::Json,
|
|
_ => ModuleType::JavaScript,
|
|
},
|
|
code_source.code,
|
|
&original_specifier,
|
|
&code_source.found_url,
|
|
code_cache_entry,
|
|
))
|
|
}
|
|
.boxed_local(),
|
|
);
|
|
}
|
|
|
|
match self.shared.modules.read(original_specifier) {
|
|
Ok(Some(module)) => {
|
|
let media_type = module.media_type;
|
|
let (module_specifier, module_type, module_source) =
|
|
module.into_parts();
|
|
let is_maybe_cjs = match self
|
|
.shared
|
|
.cjs_tracker
|
|
.is_maybe_cjs(original_specifier, media_type)
|
|
{
|
|
Ok(is_maybe_cjs) => is_maybe_cjs,
|
|
Err(err) => {
|
|
return deno_core::ModuleLoadResponse::Sync(Err(type_error(
|
|
format!("{:?}", err),
|
|
)));
|
|
}
|
|
};
|
|
if is_maybe_cjs {
|
|
let original_specifier = original_specifier.clone();
|
|
let module_specifier = module_specifier.clone();
|
|
let shared = self.shared.clone();
|
|
deno_core::ModuleLoadResponse::Async(
|
|
async move {
|
|
let source = match module_source {
|
|
DenoCompileModuleSource::String(string) => {
|
|
Cow::Borrowed(string)
|
|
}
|
|
DenoCompileModuleSource::Bytes(module_code_bytes) => {
|
|
match module_code_bytes {
|
|
Cow::Owned(bytes) => Cow::Owned(
|
|
crate::util::text_encoding::from_utf8_lossy_owned(bytes),
|
|
),
|
|
Cow::Borrowed(bytes) => String::from_utf8_lossy(bytes),
|
|
}
|
|
}
|
|
};
|
|
let source = shared
|
|
.node_code_translator
|
|
.translate_cjs_to_esm(&module_specifier, Some(source))
|
|
.await?;
|
|
let module_source = match source {
|
|
Cow::Owned(source) => ModuleSourceCode::String(source.into()),
|
|
Cow::Borrowed(source) => {
|
|
ModuleSourceCode::String(FastString::from_static(source))
|
|
}
|
|
};
|
|
let code_cache_entry = shared
|
|
.get_code_cache(&module_specifier, module_source.as_bytes());
|
|
Ok(deno_core::ModuleSource::new_with_redirect(
|
|
module_type,
|
|
module_source,
|
|
&original_specifier,
|
|
&module_specifier,
|
|
code_cache_entry,
|
|
))
|
|
}
|
|
.boxed_local(),
|
|
)
|
|
} else {
|
|
let module_source = module_source.into_for_v8();
|
|
let code_cache_entry = self
|
|
.shared
|
|
.get_code_cache(module_specifier, module_source.as_bytes());
|
|
deno_core::ModuleLoadResponse::Sync(Ok(
|
|
deno_core::ModuleSource::new_with_redirect(
|
|
module_type,
|
|
module_source,
|
|
original_specifier,
|
|
module_specifier,
|
|
code_cache_entry,
|
|
),
|
|
))
|
|
}
|
|
}
|
|
Ok(None) => deno_core::ModuleLoadResponse::Sync(Err(type_error(
|
|
format!("{MODULE_NOT_FOUND}: {}", original_specifier),
|
|
))),
|
|
Err(err) => deno_core::ModuleLoadResponse::Sync(Err(type_error(
|
|
format!("{:?}", err),
|
|
))),
|
|
}
|
|
}
|
|
|
|
fn code_cache_ready(
|
|
&self,
|
|
specifier: ModuleSpecifier,
|
|
source_hash: u64,
|
|
code_cache_data: &[u8],
|
|
) -> LocalBoxFuture<'static, ()> {
|
|
if let Some(code_cache) = &self.shared.code_cache {
|
|
code_cache.set_sync(
|
|
specifier,
|
|
deno_runtime::code_cache::CodeCacheType::EsModule,
|
|
source_hash,
|
|
code_cache_data,
|
|
);
|
|
}
|
|
std::future::ready(()).boxed_local()
|
|
}
|
|
}
|
|
|
|
impl NodeRequireLoader for EmbeddedModuleLoader {
|
|
fn ensure_read_permission<'a>(
|
|
&self,
|
|
permissions: &mut dyn deno_runtime::deno_node::NodePermissions,
|
|
path: &'a std::path::Path,
|
|
) -> Result<Cow<'a, std::path::Path>, AnyError> {
|
|
if self.shared.modules.has_file(path) {
|
|
// allow reading if the file is in the snapshot
|
|
return Ok(Cow::Borrowed(path));
|
|
}
|
|
|
|
self
|
|
.shared
|
|
.npm_resolver
|
|
.ensure_read_permission(permissions, path)
|
|
}
|
|
|
|
fn load_text_file_lossy(
|
|
&self,
|
|
path: &std::path::Path,
|
|
) -> Result<Cow<'static, str>, AnyError> {
|
|
let file_entry = self.shared.vfs.file_entry(path)?;
|
|
let file_bytes = self
|
|
.shared
|
|
.vfs
|
|
.read_file_all(file_entry, VfsFileSubDataKind::ModuleGraph)?;
|
|
Ok(from_utf8_lossy_cow(file_bytes))
|
|
}
|
|
|
|
fn is_maybe_cjs(
|
|
&self,
|
|
specifier: &ModuleSpecifier,
|
|
) -> Result<bool, ClosestPkgJsonError> {
|
|
let media_type = MediaType::from_specifier(specifier);
|
|
self.shared.cjs_tracker.is_maybe_cjs(specifier, media_type)
|
|
}
|
|
}
|
|
|
|
struct StandaloneModuleLoaderFactory {
|
|
shared: Arc<SharedModuleLoaderState>,
|
|
}
|
|
|
|
impl StandaloneModuleLoaderFactory {
|
|
pub fn create_result(&self) -> CreateModuleLoaderResult {
|
|
let loader = Rc::new(EmbeddedModuleLoader {
|
|
shared: self.shared.clone(),
|
|
});
|
|
CreateModuleLoaderResult {
|
|
module_loader: loader.clone(),
|
|
node_require_loader: loader,
|
|
}
|
|
}
|
|
}
|
|
|
|
impl ModuleLoaderFactory for StandaloneModuleLoaderFactory {
|
|
fn create_for_main(
|
|
&self,
|
|
_root_permissions: PermissionsContainer,
|
|
) -> CreateModuleLoaderResult {
|
|
self.create_result()
|
|
}
|
|
|
|
fn create_for_worker(
|
|
&self,
|
|
_parent_permissions: PermissionsContainer,
|
|
_permissions: PermissionsContainer,
|
|
) -> CreateModuleLoaderResult {
|
|
self.create_result()
|
|
}
|
|
}
|
|
|
|
struct StandaloneRootCertStoreProvider {
|
|
ca_stores: Option<Vec<String>>,
|
|
ca_data: Option<CaData>,
|
|
cell: once_cell::sync::OnceCell<RootCertStore>,
|
|
}
|
|
|
|
impl RootCertStoreProvider for StandaloneRootCertStoreProvider {
|
|
fn get_or_try_init(&self) -> Result<&RootCertStore, AnyError> {
|
|
self.cell.get_or_try_init(|| {
|
|
get_root_cert_store(None, self.ca_stores.clone(), self.ca_data.clone())
|
|
.map_err(|err| err.into())
|
|
})
|
|
}
|
|
}
|
|
|
|
pub async fn run(data: StandaloneData) -> Result<i32, AnyError> {
|
|
let StandaloneData {
|
|
fs,
|
|
metadata,
|
|
modules,
|
|
npm_snapshot,
|
|
root_path,
|
|
vfs,
|
|
} = data;
|
|
let deno_dir_provider = Arc::new(DenoDirProvider::new(None));
|
|
let root_cert_store_provider = Arc::new(StandaloneRootCertStoreProvider {
|
|
ca_stores: metadata.ca_stores,
|
|
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(PackageJsonResolver::new(
|
|
deno_runtime::deno_node::DenoFsNodeResolverEnv::new(fs.clone()),
|
|
));
|
|
let (in_npm_pkg_checker, npm_resolver) = match metadata.node_modules {
|
|
Some(binary::NodeModules::Managed { node_modules_dir }) => {
|
|
// create an npmrc that uses the fake npm_registry_url to resolve packages
|
|
let npmrc = Arc::new(ResolvedNpmRc {
|
|
default_config: deno_npm::npm_rc::RegistryConfigWithUrl {
|
|
registry_url: npm_registry_url.clone(),
|
|
config: Default::default(),
|
|
},
|
|
scopes: Default::default(),
|
|
registry_configs: Default::default(),
|
|
});
|
|
let npm_cache_dir = Arc::new(NpmCacheDir::new(
|
|
&DenoCacheEnvFsAdapter(fs.as_ref()),
|
|
npm_global_cache_dir,
|
|
npmrc.get_all_known_registries_urls(),
|
|
));
|
|
let snapshot = npm_snapshot.unwrap();
|
|
let maybe_node_modules_path = node_modules_dir
|
|
.map(|node_modules_dir| root_path.join(node_modules_dir));
|
|
let in_npm_pkg_checker =
|
|
create_in_npm_pkg_checker(CreateInNpmPkgCheckerOptions::Managed(
|
|
CliManagedInNpmPkgCheckerCreateOptions {
|
|
root_cache_dir_url: npm_cache_dir.root_dir_url(),
|
|
maybe_node_modules_path: maybe_node_modules_path.as_deref(),
|
|
},
|
|
));
|
|
let npm_resolver =
|
|
create_cli_npm_resolver(CliNpmResolverCreateOptions::Managed(
|
|
CliManagedNpmResolverCreateOptions {
|
|
snapshot: CliNpmResolverManagedSnapshotOption::Specified(Some(
|
|
snapshot,
|
|
)),
|
|
maybe_lockfile: None,
|
|
fs: fs.clone(),
|
|
http_client_provider: http_client_provider.clone(),
|
|
npm_cache_dir,
|
|
cache_setting,
|
|
text_only_progress_bar: progress_bar,
|
|
maybe_node_modules_path,
|
|
npm_system_info: Default::default(),
|
|
npm_install_deps_provider: Arc::new(
|
|
// this is only used for installing packages, which isn't necessary with deno compile
|
|
NpmInstallDepsProvider::empty(),
|
|
),
|
|
npmrc,
|
|
lifecycle_scripts: Default::default(),
|
|
},
|
|
))
|
|
.await?;
|
|
(in_npm_pkg_checker, npm_resolver)
|
|
}
|
|
Some(binary::NodeModules::Byonm {
|
|
root_node_modules_dir,
|
|
}) => {
|
|
let root_node_modules_dir =
|
|
root_node_modules_dir.map(|p| vfs.root().join(p));
|
|
let in_npm_pkg_checker =
|
|
create_in_npm_pkg_checker(CreateInNpmPkgCheckerOptions::Byonm);
|
|
let npm_resolver = create_cli_npm_resolver(
|
|
CliNpmResolverCreateOptions::Byonm(CliByonmNpmResolverCreateOptions {
|
|
fs: CliDenoResolverFs(fs.clone()),
|
|
pkg_json_resolver: pkg_json_resolver.clone(),
|
|
root_node_modules_dir,
|
|
}),
|
|
)
|
|
.await?;
|
|
(in_npm_pkg_checker, npm_resolver)
|
|
}
|
|
None => {
|
|
// Packages from different registries are already inlined in the binary,
|
|
// so no need to create actual `.npmrc` configuration.
|
|
let npmrc = create_default_npmrc();
|
|
let npm_cache_dir = Arc::new(NpmCacheDir::new(
|
|
&DenoCacheEnvFsAdapter(fs.as_ref()),
|
|
npm_global_cache_dir,
|
|
npmrc.get_all_known_registries_urls(),
|
|
));
|
|
let in_npm_pkg_checker =
|
|
create_in_npm_pkg_checker(CreateInNpmPkgCheckerOptions::Managed(
|
|
CliManagedInNpmPkgCheckerCreateOptions {
|
|
root_cache_dir_url: npm_cache_dir.root_dir_url(),
|
|
maybe_node_modules_path: None,
|
|
},
|
|
));
|
|
let npm_resolver =
|
|
create_cli_npm_resolver(CliNpmResolverCreateOptions::Managed(
|
|
CliManagedNpmResolverCreateOptions {
|
|
snapshot: CliNpmResolverManagedSnapshotOption::Specified(None),
|
|
maybe_lockfile: None,
|
|
fs: fs.clone(),
|
|
http_client_provider: http_client_provider.clone(),
|
|
npm_cache_dir,
|
|
cache_setting,
|
|
text_only_progress_bar: progress_bar,
|
|
maybe_node_modules_path: None,
|
|
npm_system_info: Default::default(),
|
|
npm_install_deps_provider: Arc::new(
|
|
// this is only used for installing packages, which isn't necessary with deno compile
|
|
NpmInstallDepsProvider::empty(),
|
|
),
|
|
npmrc: create_default_npmrc(),
|
|
lifecycle_scripts: Default::default(),
|
|
},
|
|
))
|
|
.await?;
|
|
(in_npm_pkg_checker, npm_resolver)
|
|
}
|
|
};
|
|
|
|
let has_node_modules_dir = npm_resolver.root_node_modules_path().is_some();
|
|
let node_resolver = Arc::new(NodeResolver::new(
|
|
deno_runtime::deno_node::DenoFsNodeResolverEnv::new(fs.clone()),
|
|
in_npm_pkg_checker.clone(),
|
|
npm_resolver.clone().into_npm_pkg_folder_resolver(),
|
|
pkg_json_resolver.clone(),
|
|
));
|
|
let cjs_tracker = Arc::new(CjsTracker::new(
|
|
in_npm_pkg_checker.clone(),
|
|
pkg_json_resolver.clone(),
|
|
if metadata.unstable_config.detect_cjs {
|
|
IsCjsResolutionMode::ImplicitTypeCommonJs
|
|
} else if metadata.workspace_resolver.package_jsons.is_empty() {
|
|
IsCjsResolutionMode::Disabled
|
|
} else {
|
|
IsCjsResolutionMode::ExplicitTypeCommonJs
|
|
},
|
|
));
|
|
let cache_db = Caches::new(deno_dir_provider.clone());
|
|
let node_analysis_cache = NodeAnalysisCache::new(cache_db.node_analysis_db());
|
|
let npm_req_resolver =
|
|
Arc::new(CliNpmReqResolver::new(NpmReqResolverOptions {
|
|
byonm_resolver: (npm_resolver.clone()).into_maybe_byonm(),
|
|
fs: CliDenoResolverFs(fs.clone()),
|
|
in_npm_pkg_checker: in_npm_pkg_checker.clone(),
|
|
node_resolver: node_resolver.clone(),
|
|
npm_req_resolver: npm_resolver.clone().into_npm_req_resolver(),
|
|
}));
|
|
let cjs_esm_code_analyzer = CliCjsCodeAnalyzer::new(
|
|
node_analysis_cache,
|
|
cjs_tracker.clone(),
|
|
fs.clone(),
|
|
None,
|
|
);
|
|
let node_code_translator = Arc::new(NodeCodeTranslator::new(
|
|
cjs_esm_code_analyzer,
|
|
deno_runtime::deno_node::DenoFsNodeResolverEnv::new(fs.clone()),
|
|
in_npm_pkg_checker,
|
|
node_resolver.clone(),
|
|
npm_resolver.clone().into_npm_pkg_folder_resolver(),
|
|
pkg_json_resolver.clone(),
|
|
));
|
|
let workspace_resolver = {
|
|
let import_map = match metadata.workspace_resolver.import_map {
|
|
Some(import_map) => Some(
|
|
import_map::parse_from_json_with_options(
|
|
root_dir_url.join(&import_map.specifier).unwrap(),
|
|
&import_map.json,
|
|
import_map::ImportMapOptions {
|
|
address_hook: None,
|
|
expand_imports: true,
|
|
},
|
|
)?
|
|
.import_map,
|
|
),
|
|
None => None,
|
|
};
|
|
let pkg_jsons = metadata
|
|
.workspace_resolver
|
|
.package_jsons
|
|
.into_iter()
|
|
.map(|(relative_path, json)| {
|
|
let path = root_dir_url
|
|
.join(&relative_path)
|
|
.unwrap()
|
|
.to_file_path()
|
|
.unwrap();
|
|
let pkg_json =
|
|
deno_package_json::PackageJson::load_from_value(path, json);
|
|
Arc::new(pkg_json)
|
|
})
|
|
.collect();
|
|
WorkspaceResolver::new_raw(
|
|
root_dir_url.clone(),
|
|
import_map,
|
|
metadata
|
|
.workspace_resolver
|
|
.jsr_pkgs
|
|
.iter()
|
|
.map(|pkg| ResolverWorkspaceJsrPackage {
|
|
is_patch: false, // only used for enhancing the diagnostic, which isn't shown in deno compile
|
|
base: root_dir_url.join(&pkg.relative_base).unwrap(),
|
|
name: pkg.name.clone(),
|
|
version: pkg.version.clone(),
|
|
exports: pkg.exports.clone(),
|
|
})
|
|
.collect(),
|
|
pkg_jsons,
|
|
metadata.workspace_resolver.pkg_json_resolution,
|
|
)
|
|
};
|
|
let code_cache = match metadata.code_cache_key {
|
|
Some(code_cache_key) => Some(Arc::new(DenoCompileCodeCache::new(
|
|
root_path.with_file_name(format!(
|
|
"{}.cache",
|
|
root_path.file_name().unwrap().to_string_lossy()
|
|
)),
|
|
code_cache_key,
|
|
)) as Arc<dyn CliCodeCache>),
|
|
None => {
|
|
log::debug!("Code cache disabled.");
|
|
None
|
|
}
|
|
};
|
|
let module_loader_factory = StandaloneModuleLoaderFactory {
|
|
shared: Arc::new(SharedModuleLoaderState {
|
|
cjs_tracker: cjs_tracker.clone(),
|
|
code_cache: code_cache.clone(),
|
|
fs: fs.clone(),
|
|
modules,
|
|
node_code_translator: node_code_translator.clone(),
|
|
node_resolver: node_resolver.clone(),
|
|
npm_module_loader: Arc::new(NpmModuleLoader::new(
|
|
cjs_tracker.clone(),
|
|
fs.clone(),
|
|
node_code_translator,
|
|
)),
|
|
npm_resolver: npm_resolver.clone(),
|
|
npm_req_resolver,
|
|
vfs,
|
|
workspace_resolver,
|
|
}),
|
|
};
|
|
|
|
let permissions = {
|
|
let mut permissions =
|
|
metadata.permissions.to_options(/* cli_arg_urls */ &[]);
|
|
// grant read access to the vfs
|
|
match &mut permissions.allow_read {
|
|
Some(vec) if vec.is_empty() => {
|
|
// do nothing, already granted
|
|
}
|
|
Some(vec) => {
|
|
vec.push(root_path.to_string_lossy().to_string());
|
|
}
|
|
None => {
|
|
permissions.allow_read =
|
|
Some(vec![root_path.to_string_lossy().to_string()]);
|
|
}
|
|
}
|
|
|
|
let desc_parser =
|
|
Arc::new(RuntimePermissionDescriptorParser::new(fs.clone()));
|
|
let permissions =
|
|
Permissions::from_options(desc_parser.as_ref(), &permissions)?;
|
|
PermissionsContainer::new(desc_parser, permissions)
|
|
};
|
|
let feature_checker = Arc::new({
|
|
let mut checker = FeatureChecker::default();
|
|
checker.set_exit_cb(Box::new(crate::unstable_exit_cb));
|
|
for feature in metadata.unstable_config.features {
|
|
// `metadata` is valid for the whole lifetime of the program, so we
|
|
// can leak the string here.
|
|
checker.enable_feature(feature.leak());
|
|
}
|
|
checker
|
|
});
|
|
let worker_factory = CliMainWorkerFactory::new(
|
|
Arc::new(BlobStore::default()),
|
|
code_cache,
|
|
feature_checker,
|
|
fs,
|
|
None,
|
|
None,
|
|
None,
|
|
Box::new(module_loader_factory),
|
|
node_resolver,
|
|
npm_resolver,
|
|
pkg_json_resolver,
|
|
root_cert_store_provider,
|
|
permissions,
|
|
StorageKeyResolver::empty(),
|
|
crate::args::DenoSubcommand::Run(Default::default()),
|
|
CliMainWorkerOptions {
|
|
argv: metadata.argv,
|
|
log_level: WorkerLogLevel::Info,
|
|
enable_op_summary_metrics: false,
|
|
enable_testing_features: false,
|
|
has_node_modules_dir,
|
|
hmr: false,
|
|
inspect_brk: false,
|
|
inspect_wait: false,
|
|
strace_ops: None,
|
|
is_inspecting: false,
|
|
skip_op_registration: true,
|
|
location: metadata.location,
|
|
argv0: NpmPackageReqReference::from_specifier(&main_module)
|
|
.ok()
|
|
.map(|req_ref| npm_pkg_req_ref_to_binary_command(&req_ref))
|
|
.or(std::env::args().next()),
|
|
node_debug: std::env::var("NODE_DEBUG").ok(),
|
|
origin_data_folder_path: None,
|
|
seed: metadata.seed,
|
|
unsafely_ignore_certificate_errors: metadata
|
|
.unsafely_ignore_certificate_errors,
|
|
create_hmr_runner: None,
|
|
create_coverage_collector: None,
|
|
node_ipc: None,
|
|
serve_port: None,
|
|
serve_host: None,
|
|
},
|
|
metadata.otel_config,
|
|
crate::args::NpmCachingStrategy::Lazy,
|
|
);
|
|
|
|
// Initialize v8 once from the main thread.
|
|
v8_set_flags(construct_v8_flags(&[], &metadata.v8_flags, vec![]));
|
|
// TODO(bartlomieju): remove last argument once Deploy no longer needs it
|
|
deno_core::JsRuntime::init_platform(None, true);
|
|
|
|
let mut worker = worker_factory
|
|
.create_main_worker(WorkerExecutionMode::Run, main_module)
|
|
.await?;
|
|
|
|
let exit_code = worker.run().await?;
|
|
Ok(exit_code)
|
|
}
|