summaryrefslogtreecommitdiff
path: root/cli/standalone
diff options
context:
space:
mode:
Diffstat (limited to 'cli/standalone')
-rw-r--r--cli/standalone/binary.rs313
-rw-r--r--cli/standalone/mod.rs449
-rw-r--r--cli/standalone/virtual_fs.rs41
3 files changed, 491 insertions, 312 deletions
diff --git a/cli/standalone/binary.rs b/cli/standalone/binary.rs
index 98af5fa77..bf035577c 100644
--- a/cli/standalone/binary.rs
+++ b/cli/standalone/binary.rs
@@ -2,6 +2,7 @@
use std::borrow::Cow;
use std::collections::BTreeMap;
+use std::collections::VecDeque;
use std::env::current_exe;
use std::ffi::OsString;
use std::fs;
@@ -15,8 +16,8 @@ use std::path::PathBuf;
use std::process::Command;
use deno_ast::ModuleSpecifier;
-use deno_config::package_json::PackageJsonDepValueParseError;
-use deno_config::package_json::PackageJsonDeps;
+use deno_config::workspace::PackageJsonDepResolution;
+use deno_config::workspace::Workspace;
use deno_core::anyhow::bail;
use deno_core::anyhow::Context;
use deno_core::error::AnyError;
@@ -26,9 +27,12 @@ use deno_core::futures::AsyncSeekExt;
use deno_core::serde_json;
use deno_core::url::Url;
use deno_npm::NpmSystemInfo;
+use deno_runtime::deno_node::PackageJson;
use deno_semver::npm::NpmVersionReqParseError;
use deno_semver::package::PackageReq;
use deno_semver::VersionReqSpecifierParseError;
+use eszip::EszipRelativeFileBaseUrl;
+use indexmap::IndexMap;
use log::Level;
use serde::Deserialize;
use serde::Serialize;
@@ -36,7 +40,7 @@ use serde::Serialize;
use crate::args::CaData;
use crate::args::CliOptions;
use crate::args::CompileFlags;
-use crate::args::PackageJsonDepsProvider;
+use crate::args::PackageJsonInstallDepsProvider;
use crate::args::PermissionFlags;
use crate::args::UnstableConfig;
use crate::cache::DenoDir;
@@ -44,6 +48,8 @@ use crate::file_fetcher::FileFetcher;
use crate::http_util::HttpClientProvider;
use crate::npm::CliNpmResolver;
use crate::npm::InnerCliNpmResolverRef;
+use crate::standalone::virtual_fs::VfsEntry;
+use crate::util::fs::canonicalize_path_maybe_not_exists;
use crate::util::progress_bar::ProgressBar;
use crate::util::progress_bar::ProgressBarStyle;
@@ -54,82 +60,31 @@ use super::virtual_fs::VirtualDirectory;
const MAGIC_TRAILER: &[u8; 8] = b"d3n0l4nd";
-#[derive(Serialize, Deserialize)]
-enum SerializablePackageJsonDepValueParseError {
- VersionReq(String),
- Unsupported { scheme: String },
-}
-
-impl SerializablePackageJsonDepValueParseError {
- pub fn from_err(err: PackageJsonDepValueParseError) -> Self {
- match err {
- PackageJsonDepValueParseError::VersionReq(err) => {
- Self::VersionReq(err.source.to_string())
- }
- PackageJsonDepValueParseError::Unsupported { scheme } => {
- Self::Unsupported { scheme }
- }
- }
- }
-
- pub fn into_err(self) -> PackageJsonDepValueParseError {
- match self {
- SerializablePackageJsonDepValueParseError::VersionReq(source) => {
- PackageJsonDepValueParseError::VersionReq(NpmVersionReqParseError {
- source: monch::ParseErrorFailureError::new(source),
- })
- }
- SerializablePackageJsonDepValueParseError::Unsupported { scheme } => {
- PackageJsonDepValueParseError::Unsupported { scheme }
- }
- }
- }
-}
-
-#[derive(Serialize, Deserialize)]
-pub struct SerializablePackageJsonDeps(
- BTreeMap<
- String,
- Result<PackageReq, SerializablePackageJsonDepValueParseError>,
- >,
-);
-
-impl SerializablePackageJsonDeps {
- pub fn from_deps(deps: PackageJsonDeps) -> Self {
- Self(
- deps
- .into_iter()
- .map(|(name, req)| {
- let res =
- req.map_err(SerializablePackageJsonDepValueParseError::from_err);
- (name, res)
- })
- .collect(),
- )
- }
-
- pub fn into_deps(self) -> PackageJsonDeps {
- self
- .0
- .into_iter()
- .map(|(name, res)| (name, res.map_err(|err| err.into_err())))
- .collect()
- }
-}
-
#[derive(Deserialize, Serialize)]
pub enum NodeModules {
Managed {
- /// Whether this uses a node_modules directory (true) or the global cache (false).
- node_modules_dir: bool,
- package_json_deps: Option<SerializablePackageJsonDeps>,
+ /// Relative path for the node_modules directory in the vfs.
+ node_modules_dir: Option<String>,
},
Byonm {
- package_json_deps: Option<SerializablePackageJsonDeps>,
+ root_node_modules_dir: String,
},
}
#[derive(Deserialize, Serialize)]
+pub struct SerializedWorkspaceResolverImportMap {
+ pub specifier: String,
+ pub json: String,
+}
+
+#[derive(Deserialize, Serialize)]
+pub struct SerializedWorkspaceResolver {
+ pub import_map: Option<SerializedWorkspaceResolverImportMap>,
+ pub package_jsons: BTreeMap<String, serde_json::Value>,
+ pub pkg_json_resolution: PackageJsonDepResolution,
+}
+
+#[derive(Deserialize, Serialize)]
pub struct Metadata {
pub argv: Vec<String>,
pub seed: Option<u64>,
@@ -140,8 +95,8 @@ pub struct Metadata {
pub ca_stores: Option<Vec<String>>,
pub ca_data: Option<Vec<u8>>,
pub unsafely_ignore_certificate_errors: Option<Vec<String>>,
- pub maybe_import_map: Option<(Url, String)>,
- pub entrypoint: ModuleSpecifier,
+ pub workspace_resolver: SerializedWorkspaceResolver,
+ pub entrypoint_key: String,
pub node_modules: Option<NodeModules>,
pub disable_deprecated_api_warning: bool,
pub unstable_config: UnstableConfig,
@@ -415,13 +370,13 @@ pub fn unpack_into_dir(
fs::remove_file(&archive_path)?;
Ok(exe_path)
}
+
pub struct DenoCompileBinaryWriter<'a> {
deno_dir: &'a DenoDir,
file_fetcher: &'a FileFetcher,
http_client_provider: &'a HttpClientProvider,
npm_resolver: &'a dyn CliNpmResolver,
npm_system_info: NpmSystemInfo,
- package_json_deps_provider: &'a PackageJsonDepsProvider,
}
impl<'a> DenoCompileBinaryWriter<'a> {
@@ -432,7 +387,6 @@ impl<'a> DenoCompileBinaryWriter<'a> {
http_client_provider: &'a HttpClientProvider,
npm_resolver: &'a dyn CliNpmResolver,
npm_system_info: NpmSystemInfo,
- package_json_deps_provider: &'a PackageJsonDepsProvider,
) -> Self {
Self {
deno_dir,
@@ -440,7 +394,6 @@ impl<'a> DenoCompileBinaryWriter<'a> {
http_client_provider,
npm_resolver,
npm_system_info,
- package_json_deps_provider,
}
}
@@ -448,7 +401,8 @@ impl<'a> DenoCompileBinaryWriter<'a> {
&self,
writer: &mut impl Write,
eszip: eszip::EszipV2,
- module_specifier: &ModuleSpecifier,
+ root_dir_url: EszipRelativeFileBaseUrl<'_>,
+ entrypoint: &ModuleSpecifier,
compile_flags: &CompileFlags,
cli_options: &CliOptions,
) -> Result<(), AnyError> {
@@ -465,13 +419,13 @@ impl<'a> DenoCompileBinaryWriter<'a> {
}
set_windows_binary_to_gui(&mut original_binary)?;
}
-
self
.write_standalone_binary(
writer,
original_binary,
eszip,
- module_specifier,
+ root_dir_url,
+ entrypoint,
cli_options,
compile_flags,
)
@@ -557,11 +511,13 @@ impl<'a> DenoCompileBinaryWriter<'a> {
/// This functions creates a standalone deno binary by appending a bundle
/// and magic trailer to the currently executing binary.
+ #[allow(clippy::too_many_arguments)]
async fn write_standalone_binary(
&self,
writer: &mut impl Write,
original_bin: Vec<u8>,
mut eszip: eszip::EszipV2,
+ root_dir_url: EszipRelativeFileBaseUrl<'_>,
entrypoint: &ModuleSpecifier,
cli_options: &CliOptions,
compile_flags: &CompileFlags,
@@ -574,48 +530,60 @@ impl<'a> DenoCompileBinaryWriter<'a> {
Some(CaData::Bytes(bytes)) => Some(bytes.clone()),
None => None,
};
- let maybe_import_map = cli_options
- .resolve_import_map(self.file_fetcher)
- .await?
- .map(|import_map| (import_map.base_url().clone(), import_map.to_json()));
- let (npm_vfs, npm_files, node_modules) =
- match self.npm_resolver.as_inner() {
- InnerCliNpmResolverRef::Managed(managed) => {
- let snapshot =
- managed.serialized_valid_snapshot_for_system(&self.npm_system_info);
- if !snapshot.as_serialized().packages.is_empty() {
- let (root_dir, files) = self.build_vfs()?.into_dir_and_files();
- eszip.add_npm_snapshot(snapshot);
- (
- Some(root_dir),
- files,
- Some(NodeModules::Managed {
- node_modules_dir: self
- .npm_resolver
- .root_node_modules_path()
- .is_some(),
- package_json_deps: self.package_json_deps_provider.deps().map(
- |deps| SerializablePackageJsonDeps::from_deps(deps.clone()),
- ),
- }),
- )
- } else {
- (None, Vec::new(), None)
- }
- }
- InnerCliNpmResolverRef::Byonm(_) => {
- let (root_dir, files) = self.build_vfs()?.into_dir_and_files();
+ let workspace_resolver = cli_options
+ .create_workspace_resolver(self.file_fetcher)
+ .await?;
+ let root_path = root_dir_url.inner().to_file_path().unwrap();
+ let (npm_vfs, npm_files, node_modules) = match self.npm_resolver.as_inner()
+ {
+ InnerCliNpmResolverRef::Managed(managed) => {
+ let snapshot =
+ managed.serialized_valid_snapshot_for_system(&self.npm_system_info);
+ if !snapshot.as_serialized().packages.is_empty() {
+ let (root_dir, files) = self
+ .build_vfs(&root_path, cli_options)?
+ .into_dir_and_files();
+ eszip.add_npm_snapshot(snapshot);
(
Some(root_dir),
files,
- Some(NodeModules::Byonm {
- package_json_deps: self.package_json_deps_provider.deps().map(
- |deps| SerializablePackageJsonDeps::from_deps(deps.clone()),
+ Some(NodeModules::Managed {
+ node_modules_dir: self.npm_resolver.root_node_modules_path().map(
+ |path| {
+ root_dir_url
+ .specifier_key(
+ &ModuleSpecifier::from_directory_path(path).unwrap(),
+ )
+ .into_owned()
+ },
),
}),
)
+ } else {
+ (None, Vec::new(), None)
}
- };
+ }
+ InnerCliNpmResolverRef::Byonm(resolver) => {
+ let (root_dir, files) = self
+ .build_vfs(&root_path, cli_options)?
+ .into_dir_and_files();
+ (
+ Some(root_dir),
+ files,
+ Some(NodeModules::Byonm {
+ root_node_modules_dir: root_dir_url
+ .specifier_key(
+ &ModuleSpecifier::from_directory_path(
+ // will always be set for byonm
+ resolver.root_node_modules_path().unwrap(),
+ )
+ .unwrap(),
+ )
+ .into_owned(),
+ }),
+ )
+ }
+ };
let metadata = Metadata {
argv: compile_flags.args.clone(),
@@ -629,8 +597,32 @@ impl<'a> DenoCompileBinaryWriter<'a> {
log_level: cli_options.log_level(),
ca_stores: cli_options.ca_stores().clone(),
ca_data,
- entrypoint: entrypoint.clone(),
- maybe_import_map,
+ entrypoint_key: root_dir_url.specifier_key(entrypoint).into_owned(),
+ workspace_resolver: SerializedWorkspaceResolver {
+ import_map: workspace_resolver.maybe_import_map().map(|i| {
+ SerializedWorkspaceResolverImportMap {
+ specifier: if i.base_url().scheme() == "file" {
+ root_dir_url.specifier_key(i.base_url()).into_owned()
+ } else {
+ // just make a remote url local
+ "deno.json".to_string()
+ },
+ json: i.to_json(),
+ }
+ }),
+ package_jsons: workspace_resolver
+ .package_jsons()
+ .map(|pkg_json| {
+ (
+ root_dir_url
+ .specifier_key(&pkg_json.specifier())
+ .into_owned(),
+ serde_json::to_value(pkg_json).unwrap(),
+ )
+ })
+ .collect(),
+ pkg_json_resolution: workspace_resolver.pkg_json_dep_resolution(),
+ },
node_modules,
disable_deprecated_api_warning: cli_options
.disable_deprecated_api_warning,
@@ -653,7 +645,11 @@ impl<'a> DenoCompileBinaryWriter<'a> {
)
}
- fn build_vfs(&self) -> Result<VfsBuilder, AnyError> {
+ fn build_vfs(
+ &self,
+ root_path: &Path,
+ cli_options: &CliOptions,
+ ) -> Result<VfsBuilder, AnyError> {
fn maybe_warn_different_system(system_info: &NpmSystemInfo) {
if system_info != &NpmSystemInfo::default() {
log::warn!("{} The node_modules directory may be incompatible with the target system.", crate::colors::yellow("Warning"));
@@ -664,7 +660,7 @@ impl<'a> DenoCompileBinaryWriter<'a> {
InnerCliNpmResolverRef::Managed(npm_resolver) => {
if let Some(node_modules_path) = npm_resolver.root_node_modules_path() {
maybe_warn_different_system(&self.npm_system_info);
- let mut builder = VfsBuilder::new(node_modules_path.clone())?;
+ let mut builder = VfsBuilder::new(root_path.to_path_buf())?;
builder.add_dir_recursive(node_modules_path)?;
Ok(builder)
} else {
@@ -678,23 +674,82 @@ impl<'a> DenoCompileBinaryWriter<'a> {
npm_resolver.resolve_pkg_folder_from_pkg_id(&package.id)?;
builder.add_dir_recursive(&folder)?;
}
- // overwrite the root directory's name to obscure the user's registry url
- builder.set_root_dir_name("node_modules".to_string());
+
+ // Flatten all the registries folders into a single "node_modules/localhost" folder
+ // that will be used by denort when loading the npm cache. This avoids us exposing
+ // the user's private registry information and means we don't have to bother
+ // serializing all the different registry config into the binary.
+ builder.with_root_dir(|root_dir| {
+ root_dir.name = "node_modules".to_string();
+ let mut new_entries = Vec::with_capacity(root_dir.entries.len());
+ let mut localhost_entries = IndexMap::new();
+ for entry in std::mem::take(&mut root_dir.entries) {
+ match entry {
+ VfsEntry::Dir(dir) => {
+ for entry in dir.entries {
+ log::debug!(
+ "Flattening {} into node_modules",
+ entry.name()
+ );
+ if let Some(existing) =
+ localhost_entries.insert(entry.name().to_string(), entry)
+ {
+ panic!(
+ "Unhandled scenario where a duplicate entry was found: {:?}",
+ existing
+ );
+ }
+ }
+ }
+ VfsEntry::File(_) | VfsEntry::Symlink(_) => {
+ new_entries.push(entry);
+ }
+ }
+ }
+ new_entries.push(VfsEntry::Dir(VirtualDirectory {
+ name: "localhost".to_string(),
+ entries: localhost_entries.into_iter().map(|(_, v)| v).collect(),
+ }));
+ // needs to be sorted by name
+ new_entries.sort_by(|a, b| a.name().cmp(b.name()));
+ root_dir.entries = new_entries;
+ });
+
Ok(builder)
}
}
- InnerCliNpmResolverRef::Byonm(npm_resolver) => {
+ InnerCliNpmResolverRef::Byonm(_) => {
maybe_warn_different_system(&self.npm_system_info);
- // the root_node_modules directory will always exist for byonm
- let node_modules_path = npm_resolver.root_node_modules_path().unwrap();
- let parent_path = node_modules_path.parent().unwrap();
- let mut builder = VfsBuilder::new(parent_path.to_path_buf())?;
- let package_json_path = parent_path.join("package.json");
- if package_json_path.exists() {
- builder.add_file_at_path(&package_json_path)?;
+ let mut builder = VfsBuilder::new(root_path.to_path_buf())?;
+ for pkg_json in cli_options.workspace.package_jsons() {
+ builder.add_file_at_path(&pkg_json.path)?;
}
- if node_modules_path.exists() {
- builder.add_dir_recursive(node_modules_path)?;
+ // traverse and add all the node_modules directories in the workspace
+ let mut pending_dirs = VecDeque::new();
+ pending_dirs.push_back(
+ cli_options
+ .workspace
+ .root_folder()
+ .0
+ .to_file_path()
+ .unwrap(),
+ );
+ while let Some(pending_dir) = pending_dirs.pop_front() {
+ let entries = fs::read_dir(&pending_dir).with_context(|| {
+ format!("Failed reading: {}", pending_dir.display())
+ })?;
+ for entry in entries {
+ let entry = entry?;
+ let path = entry.path();
+ if !path.is_dir() {
+ continue;
+ }
+ if path.ends_with("node_modules") {
+ builder.add_dir_recursive(&path)?;
+ } else {
+ pending_dirs.push_back(path);
+ }
+ }
}
Ok(builder)
}
diff --git a/cli/standalone/mod.rs b/cli/standalone/mod.rs
index 24ba7c9db..cbd14db4f 100644
--- a/cli/standalone/mod.rs
+++ b/cli/standalone/mod.rs
@@ -10,7 +10,7 @@ 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::PackageJsonDepsProvider;
+use crate::args::PackageJsonInstallDepsProvider;
use crate::args::StorageKeyResolver;
use crate::cache::Caches;
use crate::cache::DenoDirProvider;
@@ -25,7 +25,6 @@ use crate::npm::CliNpmResolverManagedSnapshotOption;
use crate::npm::NpmCacheDir;
use crate::resolver::CjsResolutionStore;
use crate::resolver::CliNodeResolver;
-use crate::resolver::MappedSpecifierResolver;
use crate::resolver::NpmModuleLoader;
use crate::util::progress_bar::ProgressBar;
use crate::util::progress_bar::ProgressBarStyle;
@@ -35,6 +34,10 @@ use crate::worker::CliMainWorkerOptions;
use crate::worker::ModuleLoaderAndSourceMapGetter;
use crate::worker::ModuleLoaderFactory;
use deno_ast::MediaType;
+use deno_config::package_json::PackageJsonDepValue;
+use deno_config::workspace::MappedResolution;
+use deno_config::workspace::MappedResolutionError;
+use deno_config::workspace::WorkspaceResolver;
use deno_core::anyhow::Context;
use deno_core::error::generic_error;
use deno_core::error::type_error;
@@ -48,6 +51,7 @@ use deno_core::ModuleSpecifier;
use deno_core::ModuleType;
use deno_core::RequestedModuleType;
use deno_core::ResolutionKind;
+use deno_npm::npm_rc::ResolvedNpmRc;
use deno_runtime::deno_fs;
use deno_runtime::deno_node::analyze::NodeCodeTranslator;
use deno_runtime::deno_node::NodeResolutionMode;
@@ -59,7 +63,9 @@ use deno_runtime::deno_tls::RootCertStoreProvider;
use deno_runtime::WorkerExecutionMode;
use deno_runtime::WorkerLogLevel;
use deno_semver::npm::NpmPackageReqReference;
+use eszip::EszipRelativeFileBaseUrl;
use import_map::parse_from_json;
+use std::borrow::Cow;
use std::rc::Rc;
use std::sync::Arc;
@@ -75,9 +81,43 @@ use self::binary::load_npm_vfs;
use self::binary::Metadata;
use self::file_system::DenoCompileFileSystem;
-struct SharedModuleLoaderState {
+struct WorkspaceEszipModule {
+ specifier: ModuleSpecifier,
+ inner: eszip::Module,
+}
+
+struct WorkspaceEszip {
eszip: eszip::EszipV2,
- mapped_specifier_resolver: MappedSpecifierResolver,
+ root_dir_url: ModuleSpecifier,
+}
+
+impl WorkspaceEszip {
+ pub fn get_module(
+ &self,
+ specifier: &ModuleSpecifier,
+ ) -> Option<WorkspaceEszipModule> {
+ if specifier.scheme() == "file" {
+ let specifier_key = EszipRelativeFileBaseUrl::new(&self.root_dir_url)
+ .specifier_key(specifier);
+ let module = self.eszip.get_module(&specifier_key)?;
+ let specifier = self.root_dir_url.join(&module.specifier).unwrap();
+ Some(WorkspaceEszipModule {
+ specifier,
+ inner: module,
+ })
+ } else {
+ let module = self.eszip.get_module(specifier.as_str())?;
+ Some(WorkspaceEszipModule {
+ specifier: ModuleSpecifier::parse(&module.specifier).unwrap(),
+ inner: module,
+ })
+ }
+ }
+}
+
+struct SharedModuleLoaderState {
+ eszip: WorkspaceEszip,
+ workspace_resolver: WorkspaceResolver,
node_resolver: Arc<CliNodeResolver>,
npm_module_loader: Arc<NpmModuleLoader>,
}
@@ -122,44 +162,92 @@ impl ModuleLoader for EmbeddedModuleLoader {
};
}
- let maybe_mapped = self
- .shared
- .mapped_specifier_resolver
- .resolve(specifier, &referrer)?
- .into_specifier();
-
- // npm specifier
- let specifier_text = maybe_mapped
- .as_ref()
- .map(|r| r.as_str())
- .unwrap_or(specifier);
- if let Ok(reference) = NpmPackageReqReference::from_str(specifier_text) {
- return self
- .shared
- .node_resolver
- .resolve_req_reference(
- &reference,
- &referrer,
- NodeResolutionMode::Execution,
- )
- .map(|res| res.into_url());
- }
+ let mapped_resolution =
+ self.shared.workspace_resolver.resolve(specifier, &referrer);
- let specifier = match maybe_mapped {
- Some(resolved) => resolved,
- None => deno_core::resolve_import(specifier, referrer.as_str())?,
- };
+ match mapped_resolution {
+ 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
+ .node_resolver
+ .resolve_req_with_sub_path(
+ req,
+ sub_path.as_deref(),
+ &referrer,
+ NodeResolutionMode::Execution,
+ )
+ .map(|res| res.into_url()),
+ 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_sub_path_from_deno_module(
+ pkg_folder,
+ sub_path.as_deref(),
+ &referrer,
+ NodeResolutionMode::Execution,
+ )?
+ .into_url(),
+ )
+ }
+ },
+ Ok(MappedResolution::Normal(specifier))
+ | Ok(MappedResolution::ImportMap(specifier)) => {
+ if let Ok(reference) =
+ NpmPackageReqReference::from_specifier(&specifier)
+ {
+ return self
+ .shared
+ .node_resolver
+ .resolve_req_reference(
+ &reference,
+ &referrer,
+ NodeResolutionMode::Execution,
+ )
+ .map(|res| res.into_url());
+ }
+
+ if specifier.scheme() == "jsr" {
+ if let Some(module) = self.shared.eszip.get_module(&specifier) {
+ return Ok(module.specifier);
+ }
+ }
- if specifier.scheme() == "jsr" {
- if let Some(module) = self.shared.eszip.get_module(specifier.as_str()) {
- return Ok(ModuleSpecifier::parse(&module.specifier).unwrap());
+ self
+ .shared
+ .node_resolver
+ .handle_if_in_node_modules(specifier)
}
+ Err(err)
+ if err.is_unmapped_bare_specifier() && referrer.scheme() == "file" =>
+ {
+ // todo(dsherret): return a better error from node resolution so that
+ // we can more easily tell whether to surface it or not
+ let node_result = self.shared.node_resolver.resolve(
+ specifier,
+ &referrer,
+ NodeResolutionMode::Execution,
+ );
+ if let Ok(Some(res)) = node_result {
+ return Ok(res.into_url());
+ }
+ Err(err.into())
+ }
+ Err(err) => Err(err.into()),
}
-
- self
- .shared
- .node_resolver
- .handle_if_in_node_modules(specifier)
}
fn load(
@@ -215,27 +303,23 @@ impl ModuleLoader for EmbeddedModuleLoader {
);
}
- let Some(module) =
- self.shared.eszip.get_module(original_specifier.as_str())
- else {
+ let Some(module) = self.shared.eszip.get_module(original_specifier) else {
return deno_core::ModuleLoadResponse::Sync(Err(type_error(format!(
"Module not found: {}",
original_specifier
))));
};
let original_specifier = original_specifier.clone();
- let found_specifier =
- ModuleSpecifier::parse(&module.specifier).expect("invalid url in eszip");
deno_core::ModuleLoadResponse::Async(
async move {
- let code = module.source().await.ok_or_else(|| {
+ let code = module.inner.source().await.ok_or_else(|| {
type_error(format!("Module not found: {}", original_specifier))
})?;
let code = arc_u8_to_arc_str(code)
.map_err(|_| type_error("Module source is not utf-8"))?;
Ok(deno_core::ModuleSource::new_with_redirect(
- match module.kind {
+ match module.inner.kind {
eszip::ModuleKind::JavaScript => ModuleType::JavaScript,
eszip::ModuleKind::Json => ModuleType::Json,
eszip::ModuleKind::Jsonc => {
@@ -247,7 +331,7 @@ impl ModuleLoader for EmbeddedModuleLoader {
},
ModuleSourceCode::String(code.into()),
&original_specifier,
- &found_specifier,
+ &module.specifier,
None,
))
}
@@ -324,10 +408,10 @@ pub async fn run(
mut eszip: eszip::EszipV2,
metadata: Metadata,
) -> Result<i32, AnyError> {
- let main_module = &metadata.entrypoint;
let current_exe_path = std::env::current_exe().unwrap();
let current_exe_name =
current_exe_path.file_name().unwrap().to_string_lossy();
+ let maybe_cwd = std::env::current_dir().ok();
let deno_dir_provider = Arc::new(DenoDirProvider::new(None));
let root_cert_store_provider = Arc::new(StandaloneRootCertStoreProvider {
ca_stores: metadata.ca_stores,
@@ -341,119 +425,109 @@ pub async fn run(
));
// use a dummy npm registry url
let npm_registry_url = ModuleSpecifier::parse("https://localhost/").unwrap();
- let root_path = std::env::temp_dir()
- .join(format!("deno-compile-{}", current_exe_name))
- .join("node_modules");
- let npm_cache_dir =
- NpmCacheDir::new(root_path.clone(), vec![npm_registry_url.clone()]);
+ let root_path =
+ std::env::temp_dir().join(format!("deno-compile-{}", current_exe_name));
+ let root_dir_url = ModuleSpecifier::from_directory_path(&root_path).unwrap();
+ let main_module = root_dir_url.join(&metadata.entrypoint_key).unwrap();
+ let root_node_modules_path = root_path.join("node_modules");
+ let npm_cache_dir = NpmCacheDir::new(
+ root_node_modules_path.clone(),
+ vec![npm_registry_url.clone()],
+ );
let npm_global_cache_dir = npm_cache_dir.get_cache_location();
let cache_setting = CacheSetting::Only;
- let (package_json_deps_provider, fs, npm_resolver, maybe_vfs_root) =
- match metadata.node_modules {
- Some(binary::NodeModules::Managed {
- node_modules_dir,
- package_json_deps,
- }) => {
- // this will always have a snapshot
- let snapshot = eszip.take_npm_snapshot().unwrap();
- let vfs_root_dir_path = if node_modules_dir {
- root_path
- } else {
- npm_cache_dir.root_dir().to_owned()
- };
- let vfs = load_npm_vfs(vfs_root_dir_path.clone())
- .context("Failed to load npm vfs.")?;
- let maybe_node_modules_path = if node_modules_dir {
- Some(vfs.root().to_path_buf())
- } else {
- None
- };
- let package_json_deps_provider =
- Arc::new(PackageJsonDepsProvider::new(
- package_json_deps.map(|serialized| serialized.into_deps()),
- ));
- let fs = Arc::new(DenoCompileFileSystem::new(vfs))
- as Arc<dyn deno_fs::FileSystem>;
- let npm_resolver =
- create_cli_npm_resolver(CliNpmResolverCreateOptions::Managed(
- CliNpmResolverManagedCreateOptions {
- snapshot: CliNpmResolverManagedSnapshotOption::Specified(Some(
- snapshot,
- )),
- maybe_lockfile: None,
- fs: fs.clone(),
- http_client_provider: http_client_provider.clone(),
- npm_global_cache_dir,
- cache_setting,
- text_only_progress_bar: progress_bar,
- maybe_node_modules_path,
- package_json_deps_provider: package_json_deps_provider.clone(),
- npm_system_info: Default::default(),
- // Packages from different registries are already inlined in the ESZip,
- // so no need to create actual `.npmrc` configuration.
- npmrc: create_default_npmrc(),
- },
- ))
- .await?;
- (
- package_json_deps_provider,
- fs,
- npm_resolver,
- Some(vfs_root_dir_path),
- )
- }
- Some(binary::NodeModules::Byonm { package_json_deps }) => {
- let vfs_root_dir_path = root_path;
- let vfs = load_npm_vfs(vfs_root_dir_path.clone())
- .context("Failed to load npm vfs.")?;
- let node_modules_path = vfs.root().join("node_modules");
- let package_json_deps_provider =
- Arc::new(PackageJsonDepsProvider::new(
- package_json_deps.map(|serialized| serialized.into_deps()),
- ));
- let fs = Arc::new(DenoCompileFileSystem::new(vfs))
- as Arc<dyn deno_fs::FileSystem>;
- let npm_resolver =
- create_cli_npm_resolver(CliNpmResolverCreateOptions::Byonm(
- CliNpmResolverByonmCreateOptions {
- fs: fs.clone(),
- root_node_modules_dir: node_modules_path,
- },
- ))
- .await?;
- (
- package_json_deps_provider,
- fs,
- npm_resolver,
- Some(vfs_root_dir_path),
- )
- }
- None => {
- let package_json_deps_provider =
- Arc::new(PackageJsonDepsProvider::new(None));
- let fs = Arc::new(deno_fs::RealFs) as Arc<dyn deno_fs::FileSystem>;
- let npm_resolver =
- create_cli_npm_resolver(CliNpmResolverCreateOptions::Managed(
- CliNpmResolverManagedCreateOptions {
- snapshot: CliNpmResolverManagedSnapshotOption::Specified(None),
- maybe_lockfile: None,
- fs: fs.clone(),
- http_client_provider: http_client_provider.clone(),
- npm_global_cache_dir,
- cache_setting,
- text_only_progress_bar: progress_bar,
- maybe_node_modules_path: None,
- package_json_deps_provider: package_json_deps_provider.clone(),
- npm_system_info: Default::default(),
- // Packages from different registries are already inlined in the ESZip,
- // so no need to create actual `.npmrc` configuration.
- npmrc: create_default_npmrc(),
- },
- ))
- .await?;
- (package_json_deps_provider, fs, npm_resolver, None)
- }
- };
+ let (fs, npm_resolver, maybe_vfs_root) = match metadata.node_modules {
+ Some(binary::NodeModules::Managed { node_modules_dir }) => {
+ // this will always have a snapshot
+ let snapshot = eszip.take_npm_snapshot().unwrap();
+ let vfs_root_dir_path = if node_modules_dir.is_some() {
+ root_path.clone()
+ } else {
+ npm_cache_dir.root_dir().to_owned()
+ };
+ let vfs = load_npm_vfs(vfs_root_dir_path.clone())
+ .context("Failed to load npm vfs.")?;
+ let maybe_node_modules_path = node_modules_dir
+ .map(|node_modules_dir| vfs_root_dir_path.join(node_modules_dir));
+ let fs = Arc::new(DenoCompileFileSystem::new(vfs))
+ as Arc<dyn deno_fs::FileSystem>;
+ let npm_resolver =
+ create_cli_npm_resolver(CliNpmResolverCreateOptions::Managed(
+ CliNpmResolverManagedCreateOptions {
+ snapshot: CliNpmResolverManagedSnapshotOption::Specified(Some(
+ snapshot,
+ )),
+ maybe_lockfile: None,
+ fs: fs.clone(),
+ http_client_provider: http_client_provider.clone(),
+ npm_global_cache_dir,
+ cache_setting,
+ text_only_progress_bar: progress_bar,
+ maybe_node_modules_path,
+ npm_system_info: Default::default(),
+ package_json_deps_provider: Arc::new(
+ // this is only used for installing packages, which isn't necessary with deno compile
+ PackageJsonInstallDepsProvider::empty(),
+ ),
+ // create an npmrc that uses the fake npm_registry_url to resolve packages
+ 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(),
+ }),
+ },
+ ))
+ .await?;
+ (fs, npm_resolver, Some(vfs_root_dir_path))
+ }
+ Some(binary::NodeModules::Byonm {
+ root_node_modules_dir,
+ }) => {
+ let vfs_root_dir_path = root_path.clone();
+ let vfs = load_npm_vfs(vfs_root_dir_path.clone())
+ .context("Failed to load vfs.")?;
+ let root_node_modules_dir = vfs.root().join(root_node_modules_dir);
+ let fs = Arc::new(DenoCompileFileSystem::new(vfs))
+ as Arc<dyn deno_fs::FileSystem>;
+ let npm_resolver = create_cli_npm_resolver(
+ CliNpmResolverCreateOptions::Byonm(CliNpmResolverByonmCreateOptions {
+ fs: fs.clone(),
+ root_node_modules_dir,
+ }),
+ )
+ .await?;
+ (fs, npm_resolver, Some(vfs_root_dir_path))
+ }
+ None => {
+ let fs = Arc::new(deno_fs::RealFs) as Arc<dyn deno_fs::FileSystem>;
+ let npm_resolver =
+ create_cli_npm_resolver(CliNpmResolverCreateOptions::Managed(
+ CliNpmResolverManagedCreateOptions {
+ snapshot: CliNpmResolverManagedSnapshotOption::Specified(None),
+ maybe_lockfile: None,
+ fs: fs.clone(),
+ http_client_provider: http_client_provider.clone(),
+ npm_global_cache_dir,
+ cache_setting,
+ text_only_progress_bar: progress_bar,
+ maybe_node_modules_path: None,
+ npm_system_info: Default::default(),
+ package_json_deps_provider: Arc::new(
+ // this is only used for installing packages, which isn't necessary with deno compile
+ PackageJsonInstallDepsProvider::empty(),
+ ),
+ // Packages from different registries are already inlined in the ESZip,
+ // so no need to create actual `.npmrc` configuration.
+ npmrc: create_default_npmrc(),
+ },
+ ))
+ .await?;
+ (fs, npm_resolver, None)
+ }
+ };
let has_node_modules_dir = npm_resolver.root_node_modules_path().is_some();
let node_resolver = Arc::new(NodeResolver::new(
@@ -471,9 +545,42 @@ pub async fn run(
node_resolver.clone(),
npm_resolver.clone().into_npm_resolver(),
));
- let maybe_import_map = metadata.maybe_import_map.map(|(base, source)| {
- Arc::new(parse_from_json(base, &source).unwrap().import_map)
- });
+ 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_config::package_json::PackageJson::load_from_value(path, json);
+ Arc::new(pkg_json)
+ })
+ .collect();
+ WorkspaceResolver::new_raw(
+ import_map,
+ pkg_jsons,
+ metadata.workspace_resolver.pkg_json_resolution,
+ )
+ };
let cli_node_resolver = Arc::new(CliNodeResolver::new(
Some(cjs_resolutions.clone()),
fs.clone(),
@@ -482,11 +589,11 @@ pub async fn run(
));
let module_loader_factory = StandaloneModuleLoaderFactory {
shared: Arc::new(SharedModuleLoaderState {
- eszip,
- mapped_specifier_resolver: MappedSpecifierResolver::new(
- maybe_import_map.clone(),
- package_json_deps_provider.clone(),
- ),
+ eszip: WorkspaceEszip {
+ eszip,
+ root_dir_url,
+ },
+ workspace_resolver,
node_resolver: cli_node_resolver.clone(),
npm_module_loader: Arc::new(NpmModuleLoader::new(
cjs_resolutions,
@@ -498,7 +605,6 @@ pub async fn run(
};
let permissions = {
- let maybe_cwd = std::env::current_dir().ok();
let mut permissions =
metadata.permissions.to_options(maybe_cwd.as_deref())?;
// if running with an npm vfs, grant read access to it
@@ -561,7 +667,7 @@ pub async fn run(
is_npm_main: main_module.scheme() == "npm",
skip_op_registration: true,
location: metadata.location,
- argv0: NpmPackageReqReference::from_specifier(main_module)
+ argv0: NpmPackageReqReference::from_specifier(&main_module)
.ok()
.map(|req_ref| npm_pkg_req_ref_to_binary_command(&req_ref))
.or(std::env::args().next()),
@@ -571,7 +677,6 @@ pub async fn run(
unsafely_ignore_certificate_errors: metadata
.unsafely_ignore_certificate_errors,
unstable: metadata.unstable_config.legacy_flag_enabled,
- maybe_root_package_json_deps: package_json_deps_provider.deps().cloned(),
create_hmr_runner: None,
create_coverage_collector: None,
},
@@ -592,11 +697,7 @@ pub async fn run(
deno_core::JsRuntime::init_platform(None);
let mut worker = worker_factory
- .create_main_worker(
- WorkerExecutionMode::Run,
- main_module.clone(),
- permissions,
- )
+ .create_main_worker(WorkerExecutionMode::Run, main_module, permissions)
.await?;
let exit_code = worker.run().await?;
diff --git a/cli/standalone/virtual_fs.rs b/cli/standalone/virtual_fs.rs
index 3e6823d50..ee91b9f7f 100644
--- a/cli/standalone/virtual_fs.rs
+++ b/cli/standalone/virtual_fs.rs
@@ -12,6 +12,7 @@ use std::path::PathBuf;
use std::rc::Rc;
use std::sync::Arc;
+use deno_core::anyhow::anyhow;
use deno_core::anyhow::Context;
use deno_core::error::AnyError;
use deno_core::parking_lot::Mutex;
@@ -55,9 +56,8 @@ impl VfsBuilder {
root_dir: VirtualDirectory {
name: root_path
.file_stem()
- .unwrap()
- .to_string_lossy()
- .into_owned(),
+ .map(|s| s.to_string_lossy().into_owned())
+ .unwrap_or("root".to_string()),
entries: Vec::new(),
},
root_path,
@@ -67,13 +67,19 @@ impl VfsBuilder {
})
}
- pub fn set_root_dir_name(&mut self, name: String) {
- self.root_dir.name = name;
+ pub fn with_root_dir<R>(
+ &mut self,
+ with_root: impl FnOnce(&mut VirtualDirectory) -> R,
+ ) -> R {
+ with_root(&mut self.root_dir)
}
pub fn add_dir_recursive(&mut self, path: &Path) -> Result<(), AnyError> {
- let path = canonicalize_path(path)?;
- self.add_dir_recursive_internal(&path)
+ let target_path = canonicalize_path(path)?;
+ if path != target_path {
+ self.add_symlink(path, &target_path)?;
+ }
+ self.add_dir_recursive_internal(&target_path)
}
fn add_dir_recursive_internal(
@@ -92,7 +98,7 @@ impl VfsBuilder {
if file_type.is_dir() {
self.add_dir_recursive_internal(&path)?;
} else if file_type.is_file() {
- self.add_file_at_path(&path)?;
+ self.add_file_at_path_not_symlink(&path)?;
} else if file_type.is_symlink() {
match util::fs::canonicalize_path(&path) {
Ok(target) => {
@@ -175,6 +181,17 @@ impl VfsBuilder {
}
pub fn add_file_at_path(&mut self, path: &Path) -> Result<(), AnyError> {
+ let target_path = canonicalize_path(path)?;
+ if target_path != path {
+ self.add_symlink(path, &target_path)?;
+ }
+ self.add_file_at_path_not_symlink(&target_path)
+ }
+
+ pub fn add_file_at_path_not_symlink(
+ &mut self,
+ path: &Path,
+ ) -> Result<(), AnyError> {
let file_bytes = std::fs::read(path)
.with_context(|| format!("Reading {}", path.display()))?;
self.add_file(path, file_bytes)
@@ -195,7 +212,9 @@ impl VfsBuilder {
let name = path.file_name().unwrap().to_string_lossy();
let data_len = data.len();
match dir.entries.binary_search_by(|e| e.name().cmp(&name)) {
- Ok(_) => unreachable!(),
+ Ok(_) => {
+ // already added, just ignore
+ }
Err(insert_index) => {
dir.entries.insert(
insert_index,
@@ -228,6 +247,10 @@ impl VfsBuilder {
target.display()
);
let dest = self.path_relative_root(target)?;
+ if dest == self.path_relative_root(path)? {
+ // it's the same, ignore
+ return Ok(());
+ }
let dir = self.add_dir(path.parent().unwrap())?;
let name = path.file_name().unwrap().to_string_lossy();
match dir.entries.binary_search_by(|e| e.name().cmp(&name)) {