1
0
Fork 0
mirror of https://github.com/denoland/deno.git synced 2025-01-11 16:42:21 -05:00

feat(unstable): ability to npm install then deno run main.ts (#20967)

This PR adds a new unstable "bring your own node_modules" (BYONM)
functionality currently behind a `--unstable-byonm` flag (`"unstable":
["byonm"]` in a deno.json).

This enables users to run a separate install command (ex. `npm install`,
`pnpm install`) then run `deno run main.ts` and Deno will respect the
layout of the node_modules directory as setup by the separate install
command. It also works with npm/yarn/pnpm workspaces.

For this PR, the behaviour is opted into by specifying
`--unstable-byonm`/`"unstable": ["byonm"]`, but in the future we may
make this the default behaviour as outlined in
https://github.com/denoland/deno/issues/18967#issuecomment-1761248941

This is an extremely rough initial implementation. Errors are
terrible in this and the LSP requires frequent restarts. Improvements
will be done in follow up PRs.
This commit is contained in:
David Sherret 2023-10-25 14:39:00 -04:00 committed by GitHub
parent 093b3eee58
commit be97170a19
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
37 changed files with 935 additions and 218 deletions

View file

@ -407,6 +407,7 @@ pub struct Flags {
pub seed: Option<u64>, pub seed: Option<u64>,
pub unstable: bool, pub unstable: bool,
pub unstable_bare_node_builtlins: bool, pub unstable_bare_node_builtlins: bool,
pub unstable_byonm: bool,
pub unsafely_ignore_certificate_errors: Option<Vec<String>>, pub unsafely_ignore_certificate_errors: Option<Vec<String>>,
pub v8_flags: Vec<String>, pub v8_flags: Vec<String>,
} }
@ -803,9 +804,9 @@ pub fn flags_from_vec(args: Vec<String>) -> clap::error::Result<Flags> {
flags.unstable = true; flags.unstable = true;
} }
if matches.get_flag("unstable-bare-node-builtins") { flags.unstable_bare_node_builtlins =
flags.unstable_bare_node_builtlins = true; matches.get_flag("unstable-bare-node-builtins");
} flags.unstable_byonm = matches.get_flag("unstable-byonm");
if matches.get_flag("quiet") { if matches.get_flag("quiet") {
flags.log_level = Some(Level::Error); flags.log_level = Some(Level::Error);
@ -911,6 +912,15 @@ fn clap_root() -> Command {
.action(ArgAction::SetTrue) .action(ArgAction::SetTrue)
.global(true), .global(true),
) )
.arg(
Arg::new("unstable-byonm")
.long("unstable-byonm")
.help("Enable unstable 'bring your own node_modules' feature")
.env("DENO_UNSTABLE_BYONM")
.value_parser(FalseyValueParser::new())
.action(ArgAction::SetTrue)
.global(true),
)
.arg( .arg(
Arg::new("log-level") Arg::new("log-level")
.short('L') .short('L')

View file

@ -76,25 +76,29 @@ use deno_config::FmtConfig;
use deno_config::LintConfig; use deno_config::LintConfig;
use deno_config::TestConfig; use deno_config::TestConfig;
static NPM_REGISTRY_DEFAULT_URL: Lazy<Url> = Lazy::new(|| { pub fn npm_registry_default_url() -> &'static Url {
let env_var_name = "NPM_CONFIG_REGISTRY"; static NPM_REGISTRY_DEFAULT_URL: Lazy<Url> = Lazy::new(|| {
if let Ok(registry_url) = std::env::var(env_var_name) { let env_var_name = "NPM_CONFIG_REGISTRY";
// ensure there is a trailing slash for the directory if let Ok(registry_url) = std::env::var(env_var_name) {
let registry_url = format!("{}/", registry_url.trim_end_matches('/')); // ensure there is a trailing slash for the directory
match Url::parse(&registry_url) { let registry_url = format!("{}/", registry_url.trim_end_matches('/'));
Ok(url) => { match Url::parse(&registry_url) {
return url; Ok(url) => {
} return url;
Err(err) => { }
log::debug!("Invalid {} environment variable: {:#}", env_var_name, err,); Err(err) => {
log::debug!(
"Invalid {} environment variable: {:#}",
env_var_name,
err,
);
}
} }
} }
}
Url::parse("https://registry.npmjs.org").unwrap() Url::parse("https://registry.npmjs.org").unwrap()
}); });
pub fn npm_registry_default_url() -> &'static Url {
&NPM_REGISTRY_DEFAULT_URL &NPM_REGISTRY_DEFAULT_URL
} }
@ -570,10 +574,16 @@ pub fn get_root_cert_store(
/// State provided to the process via an environment variable. /// State provided to the process via an environment variable.
#[derive(Clone, Debug, Serialize, Deserialize)] #[derive(Clone, Debug, Serialize, Deserialize)]
pub struct NpmProcessState { pub struct NpmProcessState {
pub snapshot: deno_npm::resolution::SerializedNpmResolutionSnapshot, pub kind: NpmProcessStateKind,
pub local_node_modules_path: Option<String>, pub local_node_modules_path: Option<String>,
} }
#[derive(Clone, Debug, Serialize, Deserialize)]
pub enum NpmProcessStateKind {
Snapshot(deno_npm::resolution::SerializedNpmResolutionSnapshot),
Byonm,
}
const RESOLUTION_STATE_ENV_VAR_NAME: &str = const RESOLUTION_STATE_ENV_VAR_NAME: &str =
"DENO_DONT_USE_INTERNAL_NODE_COMPAT_STATE"; "DENO_DONT_USE_INTERNAL_NODE_COMPAT_STATE";
@ -875,9 +885,11 @@ impl CliOptions {
pub fn resolve_npm_resolution_snapshot( pub fn resolve_npm_resolution_snapshot(
&self, &self,
) -> Result<Option<ValidSerializedNpmResolutionSnapshot>, AnyError> { ) -> Result<Option<ValidSerializedNpmResolutionSnapshot>, AnyError> {
if let Some(state) = &*NPM_PROCESS_STATE { if let Some(NpmProcessStateKind::Snapshot(snapshot)) =
NPM_PROCESS_STATE.as_ref().map(|s| &s.kind)
{
// TODO(bartlomieju): remove this clone // TODO(bartlomieju): remove this clone
Ok(Some(state.snapshot.clone().into_valid()?)) Ok(Some(snapshot.clone().into_valid()?))
} else { } else {
Ok(None) Ok(None)
} }
@ -926,13 +938,6 @@ impl CliOptions {
}) })
} }
pub fn node_modules_dir_specifier(&self) -> Option<ModuleSpecifier> {
self
.maybe_node_modules_folder
.as_ref()
.map(|path| ModuleSpecifier::from_directory_path(path).unwrap())
}
pub fn vendor_dir_path(&self) -> Option<&PathBuf> { pub fn vendor_dir_path(&self) -> Option<&PathBuf> {
self.maybe_vendor_folder.as_ref() self.maybe_vendor_folder.as_ref()
} }
@ -1226,6 +1231,19 @@ impl CliOptions {
.unwrap_or(false) .unwrap_or(false)
} }
pub fn unstable_byonm(&self) -> bool {
self.flags.unstable_byonm
|| NPM_PROCESS_STATE
.as_ref()
.map(|s| matches!(s.kind, NpmProcessStateKind::Byonm))
.unwrap_or(false)
|| self
.maybe_config_file()
.as_ref()
.map(|c| c.json.unstable.iter().any(|c| c == "byonm"))
.unwrap_or(false)
}
pub fn v8_flags(&self) -> &Vec<String> { pub fn v8_flags(&self) -> &Vec<String> {
&self.flags.v8_flags &self.flags.v8_flags
} }

29
cli/cache/mod.rs vendored
View file

@ -4,6 +4,7 @@ use crate::args::CacheSetting;
use crate::errors::get_error_class_name; use crate::errors::get_error_class_name;
use crate::file_fetcher::FetchOptions; use crate::file_fetcher::FetchOptions;
use crate::file_fetcher::FileFetcher; use crate::file_fetcher::FileFetcher;
use crate::npm::CliNpmResolver;
use crate::util::fs::atomic_write_file; use crate::util::fs::atomic_write_file;
use deno_ast::MediaType; use deno_ast::MediaType;
@ -101,10 +102,10 @@ pub struct FetchCacher {
file_fetcher: Arc<FileFetcher>, file_fetcher: Arc<FileFetcher>,
file_header_overrides: HashMap<ModuleSpecifier, HashMap<String, String>>, file_header_overrides: HashMap<ModuleSpecifier, HashMap<String, String>>,
global_http_cache: Arc<GlobalHttpCache>, global_http_cache: Arc<GlobalHttpCache>,
npm_resolver: Arc<dyn CliNpmResolver>,
parsed_source_cache: Arc<ParsedSourceCache>, parsed_source_cache: Arc<ParsedSourceCache>,
permissions: PermissionsContainer, permissions: PermissionsContainer,
cache_info_enabled: bool, cache_info_enabled: bool,
maybe_local_node_modules_url: Option<ModuleSpecifier>,
} }
impl FetchCacher { impl FetchCacher {
@ -113,19 +114,19 @@ impl FetchCacher {
file_fetcher: Arc<FileFetcher>, file_fetcher: Arc<FileFetcher>,
file_header_overrides: HashMap<ModuleSpecifier, HashMap<String, String>>, file_header_overrides: HashMap<ModuleSpecifier, HashMap<String, String>>,
global_http_cache: Arc<GlobalHttpCache>, global_http_cache: Arc<GlobalHttpCache>,
npm_resolver: Arc<dyn CliNpmResolver>,
parsed_source_cache: Arc<ParsedSourceCache>, parsed_source_cache: Arc<ParsedSourceCache>,
permissions: PermissionsContainer, permissions: PermissionsContainer,
maybe_local_node_modules_url: Option<ModuleSpecifier>,
) -> Self { ) -> Self {
Self { Self {
emit_cache, emit_cache,
file_fetcher, file_fetcher,
file_header_overrides, file_header_overrides,
global_http_cache, global_http_cache,
npm_resolver,
parsed_source_cache, parsed_source_cache,
permissions, permissions,
cache_info_enabled: false, cache_info_enabled: false,
maybe_local_node_modules_url,
} }
} }
@ -214,20 +215,18 @@ impl Loader for FetchCacher {
) -> LoadFuture { ) -> LoadFuture {
use deno_graph::source::CacheSetting as LoaderCacheSetting; use deno_graph::source::CacheSetting as LoaderCacheSetting;
if let Some(node_modules_url) = self.maybe_local_node_modules_url.as_ref() { if specifier.path().contains("/node_modules/") {
// The specifier might be in a completely different symlinked tree than // The specifier might be in a completely different symlinked tree than
// what the resolved node_modules_url is in (ex. `/my-project-1/node_modules` // what the node_modules url is in (ex. `/my-project-1/node_modules`
// symlinked to `/my-project-2/node_modules`), so first check if the path // symlinked to `/my-project-2/node_modules`), so first we checked if the path
// is in a node_modules dir to avoid needlessly canonicalizing, then compare // is in a node_modules dir to avoid needlessly canonicalizing, then now compare
// against the canonicalized specifier. // against the canonicalized specifier.
if specifier.path().contains("/node_modules/") { let specifier =
let specifier = crate::node::resolve_specifier_into_node_modules(specifier);
crate::node::resolve_specifier_into_node_modules(specifier); if self.npm_resolver.in_npm_package(&specifier) {
if specifier.as_str().starts_with(node_modules_url.as_str()) { return Box::pin(futures::future::ready(Ok(Some(
return Box::pin(futures::future::ready(Ok(Some( LoadResponse::External { specifier },
LoadResponse::External { specifier }, ))));
))));
}
} }
} }

View file

@ -32,6 +32,7 @@ use crate::node::CliCjsCodeAnalyzer;
use crate::node::CliNodeCodeTranslator; use crate::node::CliNodeCodeTranslator;
use crate::npm::create_cli_npm_resolver; use crate::npm::create_cli_npm_resolver;
use crate::npm::CliNpmResolver; use crate::npm::CliNpmResolver;
use crate::npm::CliNpmResolverByonmCreateOptions;
use crate::npm::CliNpmResolverCreateOptions; use crate::npm::CliNpmResolverCreateOptions;
use crate::npm::CliNpmResolverManagedCreateOptions; use crate::npm::CliNpmResolverManagedCreateOptions;
use crate::npm::CliNpmResolverManagedPackageJsonInstallerOption; use crate::npm::CliNpmResolverManagedPackageJsonInstallerOption;
@ -300,7 +301,14 @@ impl CliFactory {
.npm_resolver .npm_resolver
.get_or_try_init_async(async { .get_or_try_init_async(async {
let fs = self.fs(); let fs = self.fs();
create_cli_npm_resolver( create_cli_npm_resolver(if self.options.unstable_byonm() {
CliNpmResolverCreateOptions::Byonm(CliNpmResolverByonmCreateOptions {
fs: fs.clone(),
// todo(byonm): actually resolve this properly because the package.json
// might be in an ancestor directory
root_node_modules_dir: self.options.initial_cwd().join("node_modules"),
})
} else {
CliNpmResolverCreateOptions::Managed(CliNpmResolverManagedCreateOptions { CliNpmResolverCreateOptions::Managed(CliNpmResolverManagedCreateOptions {
snapshot: match self.options.resolve_npm_resolution_snapshot()? { snapshot: match self.options.resolve_npm_resolution_snapshot()? {
Some(snapshot) => { Some(snapshot) => {
@ -329,7 +337,7 @@ impl CliFactory {
npm_system_info: self.options.npm_system_info(), npm_system_info: self.options.npm_system_info(),
npm_registry_url: crate::args::npm_registry_default_url().to_owned(), npm_registry_url: crate::args::npm_registry_default_url().to_owned(),
}) })
).await }).await
}) })
.await .await
} }
@ -365,24 +373,25 @@ impl CliFactory {
.services .services
.resolver .resolver
.get_or_try_init_async(async { .get_or_try_init_async(async {
Ok(Arc::new(CliGraphResolver::new( Ok(Arc::new(CliGraphResolver::new(CliGraphResolverOptions {
if self.options.no_npm() { fs: self.fs().clone(),
cjs_resolutions: Some(self.cjs_resolutions().clone()),
node_resolver: Some(self.node_resolver().await?.clone()),
npm_resolver: if self.options.no_npm() {
None None
} else { } else {
Some(self.npm_resolver().await?.clone()) Some(self.npm_resolver().await?.clone())
}, },
self.package_json_deps_provider().clone(), package_json_deps_provider: self.package_json_deps_provider().clone(),
CliGraphResolverOptions { maybe_jsx_import_source_config: self
maybe_jsx_import_source_config: self .options
.options .to_maybe_jsx_import_source_config()?,
.to_maybe_jsx_import_source_config()?, maybe_import_map: self.maybe_import_map().await?.clone(),
maybe_import_map: self.maybe_import_map().await?.clone(), maybe_vendor_dir: self.options.vendor_dir_path(),
maybe_vendor_dir: self.options.vendor_dir_path(), bare_node_builtins_enabled: self
bare_node_builtins_enabled: self .options
.options .unstable_bare_node_builtlins(),
.unstable_bare_node_builtlins(), })))
},
)))
}) })
.await .await
} }

View file

@ -435,9 +435,9 @@ impl ModuleGraphBuilder {
self.file_fetcher.clone(), self.file_fetcher.clone(),
self.options.resolve_file_header_overrides(), self.options.resolve_file_header_overrides(),
self.global_http_cache.clone(), self.global_http_cache.clone(),
self.npm_resolver.clone(),
self.parsed_source_cache.clone(), self.parsed_source_cache.clone(),
permissions, permissions,
self.options.node_modules_dir_specifier(),
) )
} }

View file

@ -253,7 +253,7 @@ impl<'a> TsResponseImportMapper<'a> {
let root_folder = self let root_folder = self
.npm_resolver .npm_resolver
.as_ref() .as_ref()
.and_then(|r| r.resolve_pkg_folder_from_specifier(specifier).ok()) .and_then(|r| r.resolve_package_folder_from_path(specifier).ok())
.flatten()?; .flatten()?;
let package_json_path = root_folder.join("package.json"); let package_json_path = root_folder.join("package.json");
let package_json_text = std::fs::read_to_string(&package_json_path).ok()?; let package_json_text = std::fs::read_to_string(&package_json_path).ok()?;

View file

@ -37,9 +37,11 @@ use deno_core::ModuleSpecifier;
use deno_graph::source::ResolutionMode; use deno_graph::source::ResolutionMode;
use deno_graph::GraphImport; use deno_graph::GraphImport;
use deno_graph::Resolution; use deno_graph::Resolution;
use deno_runtime::deno_fs::RealFs;
use deno_runtime::deno_node; use deno_runtime::deno_node;
use deno_runtime::deno_node::NodeResolution; use deno_runtime::deno_node::NodeResolution;
use deno_runtime::deno_node::NodeResolutionMode; use deno_runtime::deno_node::NodeResolutionMode;
use deno_runtime::deno_node::NodeResolver;
use deno_runtime::deno_node::PackageJson; use deno_runtime::deno_node::PackageJson;
use deno_runtime::permissions::PermissionsContainer; use deno_runtime::permissions::PermissionsContainer;
use deno_semver::npm::NpmPackageReqReference; use deno_semver::npm::NpmPackageReqReference;
@ -899,6 +901,7 @@ pub struct UpdateDocumentConfigOptions<'a> {
pub maybe_import_map: Option<Arc<import_map::ImportMap>>, pub maybe_import_map: Option<Arc<import_map::ImportMap>>,
pub maybe_config_file: Option<&'a ConfigFile>, pub maybe_config_file: Option<&'a ConfigFile>,
pub maybe_package_json: Option<&'a PackageJson>, pub maybe_package_json: Option<&'a PackageJson>,
pub node_resolver: Option<Arc<NodeResolver>>,
pub npm_resolver: Option<Arc<dyn CliNpmResolver>>, pub npm_resolver: Option<Arc<dyn CliNpmResolver>>,
} }
@ -955,16 +958,17 @@ impl Documents {
file_system_docs: Default::default(), file_system_docs: Default::default(),
resolver_config_hash: 0, resolver_config_hash: 0,
imports: Default::default(), imports: Default::default(),
resolver: Arc::new(CliGraphResolver::new( resolver: Arc::new(CliGraphResolver::new(CliGraphResolverOptions {
None, fs: Arc::new(RealFs),
Arc::new(PackageJsonDepsProvider::default()), node_resolver: None,
CliGraphResolverOptions { npm_resolver: None,
maybe_jsx_import_source_config: None, cjs_resolutions: None,
maybe_import_map: None, package_json_deps_provider: Arc::new(PackageJsonDepsProvider::default()),
maybe_vendor_dir: None, maybe_jsx_import_source_config: None,
bare_node_builtins_enabled: false, maybe_import_map: None,
}, maybe_vendor_dir: None,
)), bare_node_builtins_enabled: false,
})),
npm_specifier_reqs: Default::default(), npm_specifier_reqs: Default::default(),
has_injected_types_node_package: false, has_injected_types_node_package: false,
specifier_resolver: Arc::new(SpecifierResolver::new(cache)), specifier_resolver: Arc::new(SpecifierResolver::new(cache)),
@ -1329,7 +1333,7 @@ impl Documents {
if let Some(package_json_deps) = &maybe_package_json_deps { if let Some(package_json_deps) = &maybe_package_json_deps {
// We need to ensure the hashing is deterministic so explicitly type // We need to ensure the hashing is deterministic so explicitly type
// this in order to catch if the type of package_json_deps ever changes // this in order to catch if the type of package_json_deps ever changes
// from a sorted/deterministic IndexMap to something else. // from a deterministic IndexMap to something else.
let package_json_deps: &IndexMap<_, _> = *package_json_deps; let package_json_deps: &IndexMap<_, _> = *package_json_deps;
for (key, value) in package_json_deps { for (key, value) in package_json_deps {
hasher.write_hashable(key); hasher.write_hashable(key);
@ -1364,27 +1368,28 @@ impl Documents {
); );
let deps_provider = let deps_provider =
Arc::new(PackageJsonDepsProvider::new(maybe_package_json_deps)); Arc::new(PackageJsonDepsProvider::new(maybe_package_json_deps));
self.resolver = Arc::new(CliGraphResolver::new( self.resolver = Arc::new(CliGraphResolver::new(CliGraphResolverOptions {
options.npm_resolver, fs: Arc::new(RealFs),
deps_provider, node_resolver: options.node_resolver,
CliGraphResolverOptions { npm_resolver: options.npm_resolver,
maybe_jsx_import_source_config: maybe_jsx_config, cjs_resolutions: None, // only used for runtime
maybe_import_map: options.maybe_import_map, package_json_deps_provider: deps_provider,
maybe_vendor_dir: options maybe_jsx_import_source_config: maybe_jsx_config,
.maybe_config_file maybe_import_map: options.maybe_import_map,
.and_then(|c| c.vendor_dir_path()) maybe_vendor_dir: options
.as_ref(), .maybe_config_file
bare_node_builtins_enabled: options .and_then(|c| c.vendor_dir_path())
.maybe_config_file .as_ref(),
.map(|config| { bare_node_builtins_enabled: options
config .maybe_config_file
.json .map(|config| {
.unstable config
.contains(&"bare-node-builtins".to_string()) .json
}) .unstable
.unwrap_or(false), .contains(&"bare-node-builtins".to_string())
}, })
)); .unwrap_or(false),
}));
self.imports = Arc::new( self.imports = Arc::new(
if let Some(Ok(imports)) = if let Some(Ok(imports)) =
options.maybe_config_file.map(|cf| cf.to_maybe_imports()) options.maybe_config_file.map(|cf| cf.to_maybe_imports())
@ -2194,6 +2199,7 @@ console.log(b, "hello deno");
maybe_import_map: Some(Arc::new(import_map)), maybe_import_map: Some(Arc::new(import_map)),
maybe_config_file: None, maybe_config_file: None,
maybe_package_json: None, maybe_package_json: None,
node_resolver: None,
npm_resolver: None, npm_resolver: None,
}); });
@ -2235,6 +2241,7 @@ console.log(b, "hello deno");
maybe_import_map: Some(Arc::new(import_map)), maybe_import_map: Some(Arc::new(import_map)),
maybe_config_file: None, maybe_config_file: None,
maybe_package_json: None, maybe_package_json: None,
node_resolver: None,
npm_resolver: None, npm_resolver: None,
}); });

View file

@ -105,6 +105,7 @@ use crate::lsp::tsc::file_text_changes_to_workspace_edit;
use crate::lsp::urls::LspUrlKind; use crate::lsp::urls::LspUrlKind;
use crate::npm::create_cli_npm_resolver_for_lsp; use crate::npm::create_cli_npm_resolver_for_lsp;
use crate::npm::CliNpmResolver; use crate::npm::CliNpmResolver;
use crate::npm::CliNpmResolverByonmCreateOptions;
use crate::npm::CliNpmResolverCreateOptions; use crate::npm::CliNpmResolverCreateOptions;
use crate::npm::CliNpmResolverManagedCreateOptions; use crate::npm::CliNpmResolverManagedCreateOptions;
use crate::npm::CliNpmResolverManagedPackageJsonInstallerOption; use crate::npm::CliNpmResolverManagedPackageJsonInstallerOption;
@ -131,6 +132,8 @@ struct LspNpmServices {
config_hash: LspNpmConfigHash, config_hash: LspNpmConfigHash,
/// Npm's search api. /// Npm's search api.
search_api: CliNpmSearchApi, search_api: CliNpmSearchApi,
/// Node resolver.
node_resolver: Option<Arc<NodeResolver>>,
/// Resolver for npm packages. /// Resolver for npm packages.
resolver: Option<Arc<dyn CliNpmResolver>>, resolver: Option<Arc<dyn CliNpmResolver>>,
} }
@ -495,6 +498,7 @@ impl Inner {
npm: LspNpmServices { npm: LspNpmServices {
config_hash: LspNpmConfigHash(0), // this will be updated in initialize config_hash: LspNpmConfigHash(0), // this will be updated in initialize
search_api: npm_search_api, search_api: npm_search_api,
node_resolver: None,
resolver: None, resolver: None,
}, },
performance, performance,
@ -815,15 +819,19 @@ impl Inner {
return; // no need to do anything return; // no need to do anything
} }
self.npm.resolver = Some( let npm_resolver = create_npm_resolver(
create_npm_resolver( &deno_dir,
&deno_dir, &self.http_client,
&self.http_client, self.config.maybe_config_file(),
self.config.maybe_lockfile(), self.config.maybe_lockfile(),
self.config.maybe_node_modules_dir_path().cloned(), self.config.maybe_node_modules_dir_path().cloned(),
) )
.await, .await;
); self.npm.node_resolver = Some(Arc::new(NodeResolver::new(
Arc::new(deno_fs::RealFs),
npm_resolver.clone().into_npm_resolver(),
)));
self.npm.resolver = Some(npm_resolver);
// update the hash // update the hash
self.npm.config_hash = config_hash; self.npm.config_hash = config_hash;
@ -1059,11 +1067,24 @@ impl Inner {
async fn create_npm_resolver( async fn create_npm_resolver(
deno_dir: &DenoDir, deno_dir: &DenoDir,
http_client: &Arc<HttpClient>, http_client: &Arc<HttpClient>,
maybe_config_file: Option<&ConfigFile>,
maybe_lockfile: Option<&Arc<Mutex<Lockfile>>>, maybe_lockfile: Option<&Arc<Mutex<Lockfile>>>,
maybe_node_modules_dir_path: Option<PathBuf>, maybe_node_modules_dir_path: Option<PathBuf>,
) -> Arc<dyn CliNpmResolver> { ) -> Arc<dyn CliNpmResolver> {
create_cli_npm_resolver_for_lsp(CliNpmResolverCreateOptions::Managed( let is_byonm = std::env::var("DENO_UNSTABLE_BYONM").as_deref() == Ok("1")
CliNpmResolverManagedCreateOptions { || maybe_config_file
.as_ref()
.map(|c| c.json.unstable.iter().any(|c| c == "byonm"))
.unwrap_or(false);
create_cli_npm_resolver_for_lsp(if is_byonm {
CliNpmResolverCreateOptions::Byonm(CliNpmResolverByonmCreateOptions {
fs: Arc::new(deno_fs::RealFs),
root_node_modules_dir: std::env::current_dir()
.unwrap()
.join("node_modules"),
})
} else {
CliNpmResolverCreateOptions::Managed(CliNpmResolverManagedCreateOptions {
http_client: http_client.clone(), http_client: http_client.clone(),
snapshot: match maybe_lockfile { snapshot: match maybe_lockfile {
Some(lockfile) => { Some(lockfile) => {
@ -1090,8 +1111,8 @@ async fn create_npm_resolver(
CliNpmResolverManagedPackageJsonInstallerOption::NoInstall, CliNpmResolverManagedPackageJsonInstallerOption::NoInstall,
npm_registry_url: crate::args::npm_registry_default_url().to_owned(), npm_registry_url: crate::args::npm_registry_default_url().to_owned(),
npm_system_info: NpmSystemInfo::default(), npm_system_info: NpmSystemInfo::default(),
}, })
)) })
.await .await
} }
@ -1250,6 +1271,7 @@ impl Inner {
maybe_import_map: self.maybe_import_map.clone(), maybe_import_map: self.maybe_import_map.clone(),
maybe_config_file: self.config.maybe_config_file(), maybe_config_file: self.config.maybe_config_file(),
maybe_package_json: self.maybe_package_json.as_ref(), maybe_package_json: self.maybe_package_json.as_ref(),
node_resolver: self.npm.node_resolver.clone(),
npm_resolver: self.npm.resolver.clone(), npm_resolver: self.npm.resolver.clone(),
}); });

View file

@ -738,7 +738,7 @@ impl CliNodeResolver {
.with_context(|| format!("Could not resolve '{}'.", req_ref)) .with_context(|| format!("Could not resolve '{}'.", req_ref))
} }
fn resolve_package_sub_path( pub fn resolve_package_sub_path(
&self, &self,
package_folder: &Path, package_folder: &Path,
sub_path: Option<&str>, sub_path: Option<&str>,
@ -881,7 +881,7 @@ impl NpmModuleLoader {
} }
/// Keeps track of what module specifiers were resolved as CJS. /// Keeps track of what module specifiers were resolved as CJS.
#[derive(Default)] #[derive(Debug, Default)]
pub struct CjsResolutionStore(Mutex<HashSet<ModuleSpecifier>>); pub struct CjsResolutionStore(Mutex<HashSet<ModuleSpecifier>>);
impl CjsResolutionStore { impl CjsResolutionStore {

274
cli/npm/byonm.rs Normal file
View file

@ -0,0 +1,274 @@
// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license.
use std::borrow::Cow;
use std::path::Path;
use std::path::PathBuf;
use std::sync::Arc;
use deno_ast::ModuleSpecifier;
use deno_core::anyhow::bail;
use deno_core::error::AnyError;
use deno_core::serde_json;
use deno_runtime::deno_fs::FileSystem;
use deno_runtime::deno_node::NodePermissions;
use deno_runtime::deno_node::NodeResolutionMode;
use deno_runtime::deno_node::NpmResolver;
use deno_runtime::deno_node::PackageJson;
use deno_semver::package::PackageReq;
use crate::args::package_json::get_local_package_json_version_reqs;
use crate::args::NpmProcessState;
use crate::args::NpmProcessStateKind;
use crate::util::fs::canonicalize_path_maybe_not_exists;
use crate::util::path::specifier_to_file_path;
use super::common::types_package_name;
use super::CliNpmResolver;
use super::InnerCliNpmResolverRef;
pub struct CliNpmResolverByonmCreateOptions {
pub fs: Arc<dyn FileSystem>,
pub root_node_modules_dir: PathBuf,
}
pub fn create_byonm_npm_resolver(
options: CliNpmResolverByonmCreateOptions,
) -> Arc<dyn CliNpmResolver> {
Arc::new(ByonmCliNpmResolver {
fs: options.fs,
root_node_modules_dir: options.root_node_modules_dir,
})
}
#[derive(Debug)]
pub struct ByonmCliNpmResolver {
fs: Arc<dyn FileSystem>,
root_node_modules_dir: PathBuf,
}
impl NpmResolver for ByonmCliNpmResolver {
fn resolve_package_folder_from_package(
&self,
name: &str,
referrer: &ModuleSpecifier,
mode: NodeResolutionMode,
) -> Result<PathBuf, AnyError> {
fn inner(
fs: &dyn FileSystem,
name: &str,
package_root_path: &Path,
referrer: &ModuleSpecifier,
mode: NodeResolutionMode,
) -> Result<PathBuf, AnyError> {
let mut current_folder = package_root_path;
loop {
let node_modules_folder = if current_folder.ends_with("node_modules") {
Cow::Borrowed(current_folder)
} else {
Cow::Owned(current_folder.join("node_modules"))
};
// attempt to resolve the types package first, then fallback to the regular package
if mode.is_types() && !name.starts_with("@types/") {
let sub_dir =
join_package_name(&node_modules_folder, &types_package_name(name));
if fs.is_dir_sync(&sub_dir) {
return Ok(sub_dir);
}
}
let sub_dir = join_package_name(&node_modules_folder, name);
if fs.is_dir_sync(&sub_dir) {
return Ok(sub_dir);
}
if let Some(parent) = current_folder.parent() {
current_folder = parent;
} else {
break;
}
}
bail!(
"could not find package '{}' from referrer '{}'.",
name,
referrer
);
}
let package_root_path =
self.resolve_package_folder_from_path(referrer)?.unwrap(); // todo(byonm): don't unwrap
let path = inner(&*self.fs, name, &package_root_path, referrer, mode)?;
Ok(self.fs.realpath_sync(&path)?)
}
fn resolve_package_folder_from_path(
&self,
specifier: &deno_core::ModuleSpecifier,
) -> Result<Option<PathBuf>, AnyError> {
let path = specifier.to_file_path().unwrap(); // todo(byonm): don't unwrap
let path = self.fs.realpath_sync(&path)?;
if self.in_npm_package(specifier) {
let mut path = path.as_path();
while let Some(parent) = path.parent() {
if parent
.file_name()
.and_then(|f| f.to_str())
.map(|s| s.to_ascii_lowercase())
.as_deref()
== Some("node_modules")
{
return Ok(Some(path.to_path_buf()));
} else {
path = parent;
}
}
} else {
// find the folder with a package.json
// todo(dsherret): not exactly correct, but good enough for a first pass
let mut path = path.as_path();
while let Some(parent) = path.parent() {
if parent.join("package.json").exists() {
return Ok(Some(parent.to_path_buf()));
} else {
path = parent;
}
}
}
Ok(None)
}
fn in_npm_package(&self, specifier: &ModuleSpecifier) -> bool {
specifier.scheme() == "file"
&& specifier
.path()
.to_ascii_lowercase()
.contains("/node_modules/")
}
fn ensure_read_permission(
&self,
permissions: &dyn NodePermissions,
path: &Path,
) -> Result<(), AnyError> {
if !path
.components()
.any(|c| c.as_os_str().to_ascii_lowercase() == "node_modules")
{
permissions.check_read(path)?;
}
Ok(())
}
}
impl CliNpmResolver for ByonmCliNpmResolver {
fn into_npm_resolver(self: Arc<Self>) -> Arc<dyn NpmResolver> {
self
}
fn clone_snapshotted(&self) -> Arc<dyn CliNpmResolver> {
Arc::new(Self {
fs: self.fs.clone(),
root_node_modules_dir: self.root_node_modules_dir.clone(),
})
}
fn as_inner(&self) -> InnerCliNpmResolverRef {
InnerCliNpmResolverRef::Byonm(self)
}
fn root_node_modules_path(&self) -> Option<std::path::PathBuf> {
Some(self.root_node_modules_dir.clone())
}
fn resolve_pkg_folder_from_deno_module_req(
&self,
req: &PackageReq,
referrer: &ModuleSpecifier,
) -> Result<PathBuf, AnyError> {
fn resolve_from_package_json(
req: &PackageReq,
fs: &dyn FileSystem,
path: PathBuf,
) -> Result<PathBuf, AnyError> {
let package_json = PackageJson::load_skip_read_permission(fs, path)?;
let deps = get_local_package_json_version_reqs(&package_json);
for (key, value) in deps {
if let Ok(value) = value {
if value.name == req.name
&& value.version_req.intersects(&req.version_req)
{
let package_path = package_json
.path
.parent()
.unwrap()
.join("node_modules")
.join(key);
return Ok(canonicalize_path_maybe_not_exists(&package_path)?);
}
}
}
bail!(
concat!(
"Could not find a matching package for 'npm:{}' in '{}'. ",
"You must specify this as a package.json dependency when the ",
"node_modules folder is not managed by Deno.",
),
req,
package_json.path.display()
);
}
// attempt to resolve the npm specifier from the referrer's package.json,
// but otherwise fallback to the project's package.json
if let Ok(file_path) = specifier_to_file_path(referrer) {
let mut current_path = file_path.as_path();
while let Some(dir_path) = current_path.parent() {
let package_json_path = dir_path.join("package.json");
if self.fs.exists_sync(&package_json_path) {
return resolve_from_package_json(
req,
self.fs.as_ref(),
package_json_path,
);
}
current_path = dir_path;
}
}
resolve_from_package_json(
req,
self.fs.as_ref(),
self
.root_node_modules_dir
.parent()
.unwrap()
.join("package.json"),
)
}
fn get_npm_process_state(&self) -> String {
serde_json::to_string(&NpmProcessState {
kind: NpmProcessStateKind::Byonm,
local_node_modules_path: Some(
self.root_node_modules_dir.to_string_lossy().to_string(),
),
})
.unwrap()
}
fn check_state_hash(&self) -> Option<u64> {
// it is very difficult to determine the check state hash for byonm
// so we just return None to signify check caching is not supported
None
}
}
fn join_package_name(path: &Path, package_name: &str) -> PathBuf {
let mut path = path.to_path_buf();
// ensure backslashes are used on windows
for part in package_name.split('/') {
path = path.join(part);
}
path
}

View file

@ -27,6 +27,7 @@ use deno_semver::package::PackageReq;
use crate::args::Lockfile; use crate::args::Lockfile;
use crate::args::NpmProcessState; use crate::args::NpmProcessState;
use crate::args::NpmProcessStateKind;
use crate::args::PackageJsonDepsProvider; use crate::args::PackageJsonDepsProvider;
use crate::cache::FastInsecureHasher; use crate::cache::FastInsecureHasher;
use crate::util::fs::canonicalize_path_maybe_not_exists_with_fs; use crate::util::fs::canonicalize_path_maybe_not_exists_with_fs;
@ -508,7 +509,18 @@ impl NpmResolver for ManagedCliNpmResolver {
&self, &self,
specifier: &ModuleSpecifier, specifier: &ModuleSpecifier,
) -> Result<Option<PathBuf>, AnyError> { ) -> Result<Option<PathBuf>, AnyError> {
self.resolve_pkg_folder_from_specifier(specifier) let Some(path) = self
.fs_resolver
.resolve_package_folder_from_specifier(specifier)?
else {
return Ok(None);
};
log::debug!(
"Resolved package folder of {} to {}",
specifier,
path.display()
);
Ok(Some(path))
} }
fn in_npm_package(&self, specifier: &ModuleSpecifier) -> bool { fn in_npm_package(&self, specifier: &ModuleSpecifier) -> bool {
@ -568,27 +580,6 @@ impl CliNpmResolver for ManagedCliNpmResolver {
self.fs_resolver.node_modules_path() self.fs_resolver.node_modules_path()
} }
/// Resolve the root folder of the package the provided specifier is in.
///
/// This will error when the provided specifier is not in an npm package.
fn resolve_pkg_folder_from_specifier(
&self,
specifier: &ModuleSpecifier,
) -> Result<Option<PathBuf>, AnyError> {
let Some(path) = self
.fs_resolver
.resolve_package_folder_from_specifier(specifier)?
else {
return Ok(None);
};
log::debug!(
"Resolved package folder of {} to {}",
specifier,
path.display()
);
Ok(Some(path))
}
fn resolve_pkg_folder_from_deno_module_req( fn resolve_pkg_folder_from_deno_module_req(
&self, &self,
req: &PackageReq, req: &PackageReq,
@ -601,10 +592,12 @@ impl CliNpmResolver for ManagedCliNpmResolver {
/// Gets the state of npm for the process. /// Gets the state of npm for the process.
fn get_npm_process_state(&self) -> String { fn get_npm_process_state(&self) -> String {
serde_json::to_string(&NpmProcessState { serde_json::to_string(&NpmProcessState {
snapshot: self kind: NpmProcessStateKind::Snapshot(
.resolution self
.serialized_valid_snapshot() .resolution
.into_serialized(), .serialized_valid_snapshot()
.into_serialized(),
),
local_node_modules_path: self local_node_modules_path: self
.fs_resolver .fs_resolver
.node_modules_path() .node_modules_path()

View file

@ -36,7 +36,6 @@ use deno_runtime::deno_core::futures;
use deno_runtime::deno_fs; use deno_runtime::deno_fs;
use deno_runtime::deno_node::NodePermissions; use deno_runtime::deno_node::NodePermissions;
use deno_runtime::deno_node::NodeResolutionMode; use deno_runtime::deno_node::NodeResolutionMode;
use deno_runtime::deno_node::PackageJson;
use deno_semver::package::PackageNv; use deno_semver::package::PackageNv;
use serde::Deserialize; use serde::Deserialize;
use serde::Serialize; use serde::Serialize;
@ -181,23 +180,8 @@ impl NpmPackageFsResolver for LocalNpmPackageResolver {
} else { } else {
Cow::Owned(current_folder.join("node_modules")) Cow::Owned(current_folder.join("node_modules"))
}; };
let sub_dir = join_package_name(&node_modules_folder, name);
if self.fs.is_dir_sync(&sub_dir) {
// if doing types resolution, only resolve the package if it specifies a types property
if mode.is_types() && !name.starts_with("@types/") {
let package_json = PackageJson::load_skip_read_permission(
&*self.fs,
sub_dir.join("package.json"),
)?;
if package_json.types.is_some() {
return Ok(sub_dir);
}
} else {
return Ok(sub_dir);
}
}
// if doing type resolution, check for the existence of a @types package // attempt to resolve the types package first, then fallback to the regular package
if mode.is_types() && !name.starts_with("@types/") { if mode.is_types() && !name.starts_with("@types/") {
let sub_dir = let sub_dir =
join_package_name(&node_modules_folder, &types_package_name(name)); join_package_name(&node_modules_folder, &types_package_name(name));
@ -206,6 +190,11 @@ impl NpmPackageFsResolver for LocalNpmPackageResolver {
} }
} }
let sub_dir = join_package_name(&node_modules_folder, name);
if self.fs.is_dir_sync(&sub_dir) {
return Ok(sub_dir);
}
if current_folder == self.root_node_modules_path { if current_folder == self.root_node_modules_path {
bail!( bail!(
"could not find package '{}' from referrer '{}'.", "could not find package '{}' from referrer '{}'.",

View file

@ -52,15 +52,15 @@ fn verify_tarball_integrity(
let mut hash_ctx = Context::new(algo); let mut hash_ctx = Context::new(algo);
hash_ctx.update(data); hash_ctx.update(data);
let digest = hash_ctx.finish(); let digest = hash_ctx.finish();
let tarball_checksum = base64::encode(digest.as_ref()).to_lowercase(); let tarball_checksum = base64::encode(digest.as_ref());
(tarball_checksum, base64_hash.to_lowercase()) (tarball_checksum, base64_hash)
} }
NpmPackageVersionDistInfoIntegrity::LegacySha1Hex(hex) => { NpmPackageVersionDistInfoIntegrity::LegacySha1Hex(hex) => {
let mut hash_ctx = Context::new(&ring::digest::SHA1_FOR_LEGACY_USE_ONLY); let mut hash_ctx = Context::new(&ring::digest::SHA1_FOR_LEGACY_USE_ONLY);
hash_ctx.update(data); hash_ctx.update(data);
let digest = hash_ctx.finish(); let digest = hash_ctx.finish();
let tarball_checksum = hex::encode(digest.as_ref()).to_lowercase(); let tarball_checksum = hex::encode(digest.as_ref());
(tarball_checksum, hex.to_lowercase()) (tarball_checksum, hex)
} }
NpmPackageVersionDistInfoIntegrity::UnknownIntegrity(integrity) => { NpmPackageVersionDistInfoIntegrity::UnknownIntegrity(integrity) => {
bail!( bail!(
@ -71,7 +71,7 @@ fn verify_tarball_integrity(
} }
}; };
if tarball_checksum != expected_checksum { if tarball_checksum != *expected_checksum {
bail!( bail!(
"Tarball checksum did not match what was provided by npm registry for {}.\n\nExpected: {}\nActual: {}", "Tarball checksum did not match what was provided by npm registry for {}.\n\nExpected: {}\nActual: {}",
package, package,
@ -158,7 +158,7 @@ mod test {
version: Version::parse_from_npm("1.0.0").unwrap(), version: Version::parse_from_npm("1.0.0").unwrap(),
}; };
let actual_checksum = let actual_checksum =
"z4phnx7vul3xvchq1m2ab9yg5aulvxxcg/spidns6c5h0ne8xyxysp+dgnkhfuwvy7kxvudbeoglodj6+sfapg=="; "z4PhNX7vuL3xVChQ1m2AB9Yg5AULVxXcg/SpIdNs6c5H0NE8XYXysP+DGNKHfuwvY7kxvUdBeoGlODJ6+SfaPg==";
assert_eq!( assert_eq!(
verify_tarball_integrity( verify_tarball_integrity(
&package, &package,
@ -195,7 +195,7 @@ mod test {
.to_string(), .to_string(),
concat!( concat!(
"Tarball checksum did not match what was provided by npm ", "Tarball checksum did not match what was provided by npm ",
"registry for package@1.0.0.\n\nExpected: test\nActual: 2jmj7l5rsw0yvb/vlwaykk/ybwk=", "registry for package@1.0.0.\n\nExpected: test\nActual: 2jmj7l5rSw0yVb/vlWAYkK/YBwk=",
), ),
); );
assert_eq!( assert_eq!(

View file

@ -1,5 +1,6 @@
// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license. // Copyright 2018-2023 the Deno authors. All rights reserved. MIT license.
mod byonm;
mod cache_dir; mod cache_dir;
mod common; mod common;
mod managed; mod managed;
@ -12,17 +13,18 @@ use deno_core::error::AnyError;
use deno_runtime::deno_node::NpmResolver; use deno_runtime::deno_node::NpmResolver;
use deno_semver::package::PackageReq; use deno_semver::package::PackageReq;
pub use self::byonm::CliNpmResolverByonmCreateOptions;
pub use self::cache_dir::NpmCacheDir; pub use self::cache_dir::NpmCacheDir;
pub use self::managed::CliNpmResolverManagedCreateOptions; pub use self::managed::CliNpmResolverManagedCreateOptions;
pub use self::managed::CliNpmResolverManagedPackageJsonInstallerOption; pub use self::managed::CliNpmResolverManagedPackageJsonInstallerOption;
pub use self::managed::CliNpmResolverManagedSnapshotOption; pub use self::managed::CliNpmResolverManagedSnapshotOption;
pub use self::managed::ManagedCliNpmResolver; pub use self::managed::ManagedCliNpmResolver;
use self::byonm::ByonmCliNpmResolver;
pub enum CliNpmResolverCreateOptions { pub enum CliNpmResolverCreateOptions {
Managed(CliNpmResolverManagedCreateOptions), Managed(CliNpmResolverManagedCreateOptions),
// todo(dsherret): implement this Byonm(CliNpmResolverByonmCreateOptions),
#[allow(dead_code)]
Byonm,
} }
pub async fn create_cli_npm_resolver_for_lsp( pub async fn create_cli_npm_resolver_for_lsp(
@ -33,7 +35,7 @@ pub async fn create_cli_npm_resolver_for_lsp(
Managed(options) => { Managed(options) => {
managed::create_managed_npm_resolver_for_lsp(options).await managed::create_managed_npm_resolver_for_lsp(options).await
} }
Byonm => todo!(), Byonm(options) => byonm::create_byonm_npm_resolver(options),
} }
} }
@ -43,7 +45,7 @@ pub async fn create_cli_npm_resolver(
use CliNpmResolverCreateOptions::*; use CliNpmResolverCreateOptions::*;
match options { match options {
Managed(options) => managed::create_managed_npm_resolver(options).await, Managed(options) => managed::create_managed_npm_resolver(options).await,
Byonm => todo!(), Byonm(options) => Ok(byonm::create_byonm_npm_resolver(options)),
} }
} }
@ -76,12 +78,6 @@ pub trait CliNpmResolver: NpmResolver {
fn root_node_modules_path(&self) -> Option<PathBuf>; fn root_node_modules_path(&self) -> Option<PathBuf>;
/// Resolve the root folder of the package the provided specifier is in.
fn resolve_pkg_folder_from_specifier(
&self,
specifier: &ModuleSpecifier,
) -> Result<Option<PathBuf>, AnyError>;
fn resolve_pkg_folder_from_deno_module_req( fn resolve_pkg_folder_from_deno_module_req(
&self, &self,
req: &PackageReq, req: &PackageReq,
@ -95,6 +91,3 @@ pub trait CliNpmResolver: NpmResolver {
/// or `None` if the state currently can't be determined. /// or `None` if the state currently can't be determined.
fn check_state_hash(&self) -> Option<u64>; fn check_state_hash(&self) -> Option<u64>;
} }
// todo(#18967): implement this
pub struct ByonmCliNpmResolver;

View file

@ -13,7 +13,13 @@ use deno_graph::source::ResolveError;
use deno_graph::source::Resolver; use deno_graph::source::Resolver;
use deno_graph::source::UnknownBuiltInNodeModuleError; use deno_graph::source::UnknownBuiltInNodeModuleError;
use deno_graph::source::DEFAULT_JSX_IMPORT_SOURCE_MODULE; use deno_graph::source::DEFAULT_JSX_IMPORT_SOURCE_MODULE;
use deno_runtime::deno_fs::FileSystem;
use deno_runtime::deno_node::is_builtin_node_module; use deno_runtime::deno_node::is_builtin_node_module;
use deno_runtime::deno_node::NodeResolution;
use deno_runtime::deno_node::NodeResolutionMode;
use deno_runtime::deno_node::NodeResolver;
use deno_runtime::permissions::PermissionsContainer;
use deno_semver::npm::NpmPackageReqReference;
use deno_semver::package::PackageReq; use deno_semver::package::PackageReq;
use import_map::ImportMap; use import_map::ImportMap;
use std::path::PathBuf; use std::path::PathBuf;
@ -22,6 +28,7 @@ use std::sync::Arc;
use crate::args::package_json::PackageJsonDeps; use crate::args::package_json::PackageJsonDeps;
use crate::args::JsxImportSourceConfig; use crate::args::JsxImportSourceConfig;
use crate::args::PackageJsonDepsProvider; use crate::args::PackageJsonDepsProvider;
use crate::module_loader::CjsResolutionStore;
use crate::npm::CliNpmResolver; use crate::npm::CliNpmResolver;
use crate::npm::InnerCliNpmResolverRef; use crate::npm::InnerCliNpmResolverRef;
use crate::util::sync::AtomicFlag; use crate::util::sync::AtomicFlag;
@ -99,16 +106,24 @@ impl MappedSpecifierResolver {
/// import map, JSX settings. /// import map, JSX settings.
#[derive(Debug)] #[derive(Debug)]
pub struct CliGraphResolver { pub struct CliGraphResolver {
fs: Arc<dyn FileSystem>,
mapped_specifier_resolver: MappedSpecifierResolver, mapped_specifier_resolver: MappedSpecifierResolver,
maybe_default_jsx_import_source: Option<String>, maybe_default_jsx_import_source: Option<String>,
maybe_jsx_import_source_module: Option<String>, maybe_jsx_import_source_module: Option<String>,
maybe_vendor_specifier: Option<ModuleSpecifier>, maybe_vendor_specifier: Option<ModuleSpecifier>,
cjs_resolutions: Option<Arc<CjsResolutionStore>>,
node_resolver: Option<Arc<NodeResolver>>,
npm_resolver: Option<Arc<dyn CliNpmResolver>>, npm_resolver: Option<Arc<dyn CliNpmResolver>>,
found_package_json_dep_flag: Arc<AtomicFlag>, found_package_json_dep_flag: Arc<AtomicFlag>,
bare_node_builtins_enabled: bool, bare_node_builtins_enabled: bool,
} }
pub struct CliGraphResolverOptions<'a> { pub struct CliGraphResolverOptions<'a> {
pub fs: Arc<dyn FileSystem>,
pub cjs_resolutions: Option<Arc<CjsResolutionStore>>,
pub node_resolver: Option<Arc<NodeResolver>>,
pub npm_resolver: Option<Arc<dyn CliNpmResolver>>,
pub package_json_deps_provider: Arc<PackageJsonDepsProvider>,
pub maybe_jsx_import_source_config: Option<JsxImportSourceConfig>, pub maybe_jsx_import_source_config: Option<JsxImportSourceConfig>,
pub maybe_import_map: Option<Arc<ImportMap>>, pub maybe_import_map: Option<Arc<ImportMap>>,
pub maybe_vendor_dir: Option<&'a PathBuf>, pub maybe_vendor_dir: Option<&'a PathBuf>,
@ -116,15 +131,23 @@ pub struct CliGraphResolverOptions<'a> {
} }
impl CliGraphResolver { impl CliGraphResolver {
pub fn new( pub fn new(options: CliGraphResolverOptions) -> Self {
npm_resolver: Option<Arc<dyn CliNpmResolver>>, let is_byonm = options
package_json_deps_provider: Arc<PackageJsonDepsProvider>, .npm_resolver
options: CliGraphResolverOptions, .as_ref()
) -> Self { .map(|n| n.as_byonm().is_some())
.unwrap_or(false);
Self { Self {
fs: options.fs,
cjs_resolutions: options.cjs_resolutions,
mapped_specifier_resolver: MappedSpecifierResolver::new( mapped_specifier_resolver: MappedSpecifierResolver::new(
options.maybe_import_map, options.maybe_import_map,
package_json_deps_provider, if is_byonm {
// don't resolve from the root package.json deps for byonm
Arc::new(PackageJsonDepsProvider::new(None))
} else {
options.package_json_deps_provider
},
), ),
maybe_default_jsx_import_source: options maybe_default_jsx_import_source: options
.maybe_jsx_import_source_config .maybe_jsx_import_source_config
@ -136,7 +159,8 @@ impl CliGraphResolver {
maybe_vendor_specifier: options maybe_vendor_specifier: options
.maybe_vendor_dir .maybe_vendor_dir
.and_then(|v| ModuleSpecifier::from_directory_path(v).ok()), .and_then(|v| ModuleSpecifier::from_directory_path(v).ok()),
npm_resolver, node_resolver: options.node_resolver,
npm_resolver: options.npm_resolver,
found_package_json_dep_flag: Default::default(), found_package_json_dep_flag: Default::default(),
bare_node_builtins_enabled: options.bare_node_builtins_enabled, bare_node_builtins_enabled: options.bare_node_builtins_enabled,
} }
@ -171,8 +195,15 @@ impl Resolver for CliGraphResolver {
&self, &self,
specifier: &str, specifier: &str,
referrer: &ModuleSpecifier, referrer: &ModuleSpecifier,
_mode: ResolutionMode, mode: ResolutionMode,
) -> Result<ModuleSpecifier, ResolveError> { ) -> Result<ModuleSpecifier, ResolveError> {
fn to_node_mode(mode: ResolutionMode) -> NodeResolutionMode {
match mode {
ResolutionMode::Execution => NodeResolutionMode::Execution,
ResolutionMode::Types => NodeResolutionMode::Types,
}
}
let result = match self let result = match self
.mapped_specifier_resolver .mapped_specifier_resolver
.resolve(specifier, referrer)? .resolve(specifier, referrer)?
@ -200,6 +231,80 @@ impl Resolver for CliGraphResolver {
} }
} }
if let Some(resolver) =
self.npm_resolver.as_ref().and_then(|r| r.as_byonm())
{
match &result {
Ok(specifier) => {
if let Ok(npm_req_ref) =
NpmPackageReqReference::from_specifier(specifier)
{
let package_folder = resolver
.resolve_pkg_folder_from_deno_module_req(
npm_req_ref.req(),
referrer,
)?;
let node_resolver = self.node_resolver.as_ref().unwrap();
let package_json_path = package_folder.join("package.json");
if !self.fs.exists_sync(&package_json_path) {
return Err(ResolveError::Other(anyhow!(
"Could not find '{}'. Maybe run `npm install`?",
package_json_path.display()
)));
}
let maybe_resolution = node_resolver
.resolve_package_subpath_from_deno_module(
&package_folder,
npm_req_ref.sub_path(),
referrer,
to_node_mode(mode),
&PermissionsContainer::allow_all(),
)?;
match maybe_resolution {
Some(resolution) => {
if let Some(cjs_resolutions) = &self.cjs_resolutions {
if let NodeResolution::CommonJs(specifier) = &resolution {
// remember that this was a common js resolution
cjs_resolutions.insert(specifier.clone());
}
}
return Ok(resolution.into_url());
}
None => {
return Err(ResolveError::Other(anyhow!(
"Failed resolving package subpath for '{}' in '{}'.",
npm_req_ref,
package_folder.display()
)));
}
}
}
}
Err(_) => {
if referrer.scheme() == "file" {
if let Some(node_resolver) = &self.node_resolver {
let node_result = node_resolver.resolve(
specifier,
referrer,
to_node_mode(mode),
&PermissionsContainer::allow_all(),
);
if let Ok(Some(resolution)) = node_result {
if let Some(cjs_resolutions) = &self.cjs_resolutions {
if let NodeResolution::CommonJs(specifier) = &resolution {
// remember that this was a common js resolution
cjs_resolutions.insert(specifier.clone());
}
}
return Ok(resolution.into_url());
}
}
}
}
}
}
result result
} }
} }

View file

@ -118,7 +118,7 @@ fn cafile_compile() {
context context
.new_command() .new_command()
.command_name(output_exe) .name(output_exe)
.run() .run()
.assert_matches_text("[WILDCARD]\nHello\n"); .assert_matches_text("[WILDCARD]\nHello\n");
} }

View file

@ -29,7 +29,7 @@ fn compile_basic() {
.run(); .run();
output.assert_exit_code(0); output.assert_exit_code(0);
output.skip_output_check(); output.skip_output_check();
let output = context.new_command().command_name(&exe).run(); let output = context.new_command().name(&exe).run();
output.assert_matches_text("Welcome to Deno!\n"); output.assert_matches_text("Welcome to Deno!\n");
} }
@ -42,7 +42,7 @@ fn compile_basic() {
.new_command() .new_command()
// it should fail creating this, but still work // it should fail creating this, but still work
.env("DENO_DIR", readonly_sub_dir) .env("DENO_DIR", readonly_sub_dir)
.command_name(exe) .name(exe)
.run(); .run();
output.assert_matches_text("Welcome to Deno!\n"); output.assert_matches_text("Welcome to Deno!\n");
} }
@ -688,11 +688,7 @@ fn workers_not_in_module_map() {
output.assert_exit_code(0); output.assert_exit_code(0);
output.skip_output_check(); output.skip_output_check();
let output = context let output = context.new_command().name(exe).env("NO_COLOR", "").run();
.new_command()
.command_name(exe)
.env("NO_COLOR", "")
.run();
output.assert_exit_code(1); output.assert_exit_code(1);
output.assert_matches_text(concat!( output.assert_matches_text(concat!(
"error: Uncaught (in worker \"\") Module not found: [WILDCARD]", "error: Uncaught (in worker \"\") Module not found: [WILDCARD]",
@ -825,7 +821,7 @@ fn compile_npm_specifiers() {
output.assert_exit_code(0); output.assert_exit_code(0);
output.skip_output_check(); output.skip_output_check();
let output = context.new_command().command_name(&binary_path).run(); let output = context.new_command().name(&binary_path).run();
output.assert_matches_text( output.assert_matches_text(
r#"Node esm importing node cjs r#"Node esm importing node cjs
=========================== ===========================
@ -881,7 +877,7 @@ testing[WILDCARD]this
output.assert_exit_code(0); output.assert_exit_code(0);
output.skip_output_check(); output.skip_output_check();
let output = context.new_command().command_name(binary_path).run(); let output = context.new_command().name(binary_path).run();
output.assert_matches_text("2\n"); output.assert_matches_text("2\n");
} }
@ -1050,7 +1046,7 @@ fn run_npm_bin_compile_test(opts: RunNpmBinCompileOptions) {
}; };
let output = context let output = context
.new_command() .new_command()
.command_name(binary_path) .name(binary_path)
.args_vec(opts.run_args) .args_vec(opts.run_args)
.run(); .run();
output.assert_matches_file(opts.output_file); output.assert_matches_file(opts.output_file);
@ -1114,6 +1110,6 @@ fn compile_node_modules_symlink_outside() {
// run // run
let binary_path = let binary_path =
project_dir.join(if cfg!(windows) { "bin.exe" } else { "bin" }); project_dir.join(if cfg!(windows) { "bin.exe" } else { "bin" });
let output = context.new_command().command_name(binary_path).run(); let output = context.new_command().name(binary_path).run();
output.assert_matches_file("compile/node_modules_symlink_outside/main.out"); output.assert_matches_file("compile/node_modules_symlink_outside/main.out");
} }

View file

@ -2219,3 +2219,240 @@ itest!(require_resolve_url_paths {
cwd: Some("npm/require_resolve_url/"), cwd: Some("npm/require_resolve_url/"),
copy_temp_dir: Some("npm/require_resolve_url/"), copy_temp_dir: Some("npm/require_resolve_url/"),
}); });
#[test]
pub fn byonm_cjs_esm_packages() {
let test_context = TestContextBuilder::for_npm()
.env("DENO_UNSTABLE_BYONM", "1")
.use_temp_cwd()
.build();
let dir = test_context.temp_dir();
let run_npm = |args: &str| {
test_context
.new_command()
.name("npm")
.args(args)
.run()
.skip_output_check();
};
run_npm("init -y");
run_npm("install @denotest/esm-basic @denotest/cjs-default-export @denotest/dual-cjs-esm chalk@4 chai@4.3");
dir.write(
"main.ts",
r#"
import { getValue, setValue } from "@denotest/esm-basic";
setValue(2);
console.log(getValue());
import cjsDefault from "@denotest/cjs-default-export";
console.log(cjsDefault.default());
console.log(cjsDefault.named());
import { getKind } from "@denotest/dual-cjs-esm";
console.log(getKind());
"#,
);
let output = test_context.new_command().args("run --check main.ts").run();
output
.assert_matches_text("Check file:///[WILDCARD]/main.ts\n2\n1\n2\nesm\n");
// should not have created the .deno directory
assert!(!dir.path().join("node_modules/.deno").exists());
// try chai
dir.write(
"chai.ts",
r#"import { expect } from "chai";
const timeout = setTimeout(() => {}, 0);
expect(timeout).to.be.a("number");
clearTimeout(timeout);"#,
);
test_context.new_command().args("run chai.ts").run();
// try chalk cjs
dir.write(
"chalk.ts",
"import chalk from 'chalk'; console.log(chalk.green('chalk cjs loads'));",
);
let output = test_context
.new_command()
.args("run --allow-read chalk.ts")
.run();
output.assert_matches_text("chalk cjs loads\n");
// try using an npm specifier for chalk that matches the version we installed
dir.write(
"chalk.ts",
"import chalk from 'npm:chalk@4'; console.log(chalk.green('chalk cjs loads'));",
);
let output = test_context
.new_command()
.args("run --allow-read chalk.ts")
.run();
output.assert_matches_text("chalk cjs loads\n");
// try with one that doesn't match the package.json
dir.write(
"chalk.ts",
"import chalk from 'npm:chalk@5'; console.log(chalk.green('chalk cjs loads'));",
);
let output = test_context
.new_command()
.args("run --allow-read chalk.ts")
.run();
output.assert_matches_text(
r#"error: Could not find a matching package for 'npm:chalk@5' in '[WILDCARD]package.json'. You must specify this as a package.json dependency when the node_modules folder is not managed by Deno.
at file:///[WILDCARD]chalk.ts:1:19
"#);
output.assert_exit_code(1);
}
#[test]
pub fn byonm_package_npm_specifier_not_installed_and_invalid_subpath() {
let test_context = TestContextBuilder::for_npm()
.env("DENO_UNSTABLE_BYONM", "1")
.use_temp_cwd()
.build();
let dir = test_context.temp_dir();
dir.path().join("package.json").write_json(&json!({
"dependencies": {
"chalk": "4",
"@denotest/conditional-exports-strict": "1"
}
}));
dir.write(
"main.ts",
"import chalk from 'npm:chalk'; console.log(chalk.green('hi'));",
);
// no npm install has been run, so this should give an informative error
let output = test_context.new_command().args("run main.ts").run();
output.assert_matches_text(
r#"error: Could not find '[WILDCARD]package.json'. Maybe run `npm install`?
at file:///[WILDCARD]/main.ts:1:19
"#,
);
output.assert_exit_code(1);
// now test for an invalid sub path after doing an npm install
dir.write(
"main.ts",
"import 'npm:@denotest/conditional-exports-strict/test';",
);
test_context
.new_command()
.name("npm")
.args("install")
.run()
.skip_output_check();
let output = test_context.new_command().args("run main.ts").run();
output.assert_matches_text(
r#"error: Failed resolving package subpath './test' for '[WILDCARD]package.json'
at file:///[WILDCARD]/main.ts:1:8
"#,
);
output.assert_exit_code(1);
}
#[test]
pub fn byonm_npm_workspaces() {
let test_context = TestContextBuilder::for_npm().use_temp_cwd().build();
let dir = test_context.temp_dir();
dir.write(
"deno.json",
r#"{
"unstable": [
"byonm"
]
}"#,
);
dir.write(
"package.json",
r#"{
"name": "my-workspace",
"workspaces": [
"project-a",
"project-b"
]
}
"#,
);
let project_a_dir = dir.path().join("project-a");
project_a_dir.create_dir_all();
project_a_dir.join("package.json").write_json(&json!({
"name": "project-a",
"version": "1.0.0",
"main": "./index.js",
"type": "module",
"dependencies": {
"chai": "^4.2",
"project-b": "^1"
}
}));
project_a_dir.join("index.js").write(
r#"
import { expect } from "chai";
const timeout = setTimeout(() => {}, 0);
expect(timeout).to.be.a("number");
clearTimeout(timeout);
export function add(a, b) {
return a + b;
}
"#,
);
project_a_dir
.join("index.d.ts")
.write("export function add(a: number, b: number): number;");
let project_b_dir = dir.path().join("project-b");
project_b_dir.create_dir_all();
project_b_dir.join("package.json").write_json(&json!({
"name": "project-b",
"version": "1.0.0",
"type": "module",
"dependencies": {
"@denotest/esm-basic": "^1.0",
}
}));
project_b_dir.join("main.ts").write(
r#"
import { getValue, setValue } from "@denotest/esm-basic";
setValue(5);
console.log(getValue());
import { add } from "project-a";
console.log(add(1, 2));
"#,
);
test_context
.new_command()
.name("npm")
.args("install")
.run()
.skip_output_check();
let output = test_context
.new_command()
.args("run ./project-b/main.ts")
.run();
output.assert_matches_text("5\n3\n");
let output = test_context
.new_command()
.args("check ./project-b/main.ts")
.run();
output.assert_matches_text("Check file:///[WILDCARD]/project-b/main.ts\n");
}

View file

@ -0,0 +1,3 @@
module.exports = {
hello: "from cjs"
};

View file

@ -0,0 +1,3 @@
export default {
hello: "from esm client bar",
}

View file

@ -0,0 +1,3 @@
export default {
hello: "from esm client foo",
}

View file

@ -0,0 +1,3 @@
export default {
hello: "from esm client",
}

View file

@ -0,0 +1,3 @@
export default {
hello: "from esm client m",
}

View file

@ -0,0 +1,3 @@
export default {
hello: "from esm",
}

View file

@ -0,0 +1,3 @@
export default {
hello: "from foo",
}

View file

@ -0,0 +1,16 @@
{
"name": "@denotest/conditional-exports-strict",
"version": "1.0.0",
"type": "module",
"exports": {
".": {
"types": "./types/src/index.d.ts",
"require": "./cjs/index.cjs",
"import": "./esm/index.js"
},
"./client": {
"types": "./types/src/client/index.d.ts",
"import": "./esm/client/index.js"
}
}
}

View file

@ -1,3 +1,3 @@
export default { export default {
hello: "from esm client bar", hello: "from esm client bar",
} }

View file

@ -1,3 +1,3 @@
export default { export default {
hello: "from esm client foo", hello: "from esm client foo",
} }

View file

@ -1,3 +1,3 @@
export default { export default {
hello: "from esm client", hello: "from esm client",
} }

View file

@ -1,3 +1,3 @@
export default { export default {
hello: "from esm client m", hello: "from esm client m",
} }

View file

@ -0,0 +1 @@
export function getKind(): string;

View file

@ -0,0 +1 @@
export function getKind(): string;

View file

@ -19,6 +19,7 @@ use deno_graph::source::LoadResponse;
use deno_graph::source::Loader; use deno_graph::source::Loader;
use deno_graph::GraphKind; use deno_graph::GraphKind;
use deno_graph::ModuleGraph; use deno_graph::ModuleGraph;
use deno_runtime::deno_fs::RealFs;
use import_map::ImportMap; use import_map::ImportMap;
use crate::args::JsxImportSourceConfig; use crate::args::JsxImportSourceConfig;
@ -293,16 +294,17 @@ fn build_resolver(
maybe_jsx_import_source_config: Option<JsxImportSourceConfig>, maybe_jsx_import_source_config: Option<JsxImportSourceConfig>,
original_import_map: Option<ImportMap>, original_import_map: Option<ImportMap>,
) -> CliGraphResolver { ) -> CliGraphResolver {
CliGraphResolver::new( CliGraphResolver::new(CliGraphResolverOptions {
None, fs: Arc::new(RealFs),
Default::default(), node_resolver: None,
CliGraphResolverOptions { npm_resolver: None,
maybe_jsx_import_source_config, cjs_resolutions: None,
maybe_import_map: original_import_map.map(Arc::new), package_json_deps_provider: Default::default(),
maybe_vendor_dir: None, maybe_jsx_import_source_config,
bare_node_builtins_enabled: false, maybe_import_map: original_import_map.map(Arc::new),
}, maybe_vendor_dir: None,
) bare_node_builtins_enabled: false,
})
} }
async fn build_test_graph( async fn build_test_graph(

View file

@ -227,7 +227,7 @@ pub struct TestCommandBuilder {
} }
impl TestCommandBuilder { impl TestCommandBuilder {
pub fn command_name(mut self, name: impl AsRef<OsStr>) -> Self { pub fn name(mut self, name: impl AsRef<OsStr>) -> Self {
self.command_name = name.as_ref().to_string_lossy().to_string(); self.command_name = name.as_ref().to_string_lossy().to_string();
self self
} }
@ -306,7 +306,11 @@ impl TestCommandBuilder {
} }
fn build_command_path(&self) -> PathRef { fn build_command_path(&self) -> PathRef {
let command_name = &self.command_name; let command_name = if cfg!(windows) && self.command_name == "npm" {
"npm.cmd"
} else {
&self.command_name
};
if command_name == "deno" { if command_name == "deno" {
deno_exe_path() deno_exe_path()
} else { } else {
@ -407,11 +411,11 @@ impl TestCommandBuilder {
command.env_clear(); command.env_clear();
} }
command.env("DENO_DIR", self.context.deno_dir.path()); command.env("DENO_DIR", self.context.deno_dir.path());
let envs = self.build_envs(); let mut envs = self.build_envs();
if !envs.contains_key("NPM_CONFIG_REGISTRY") { if !envs.contains_key("NPM_CONFIG_REGISTRY") {
command.env("NPM_CONFIG_REGISTRY", npm_registry_unset_url()); envs.insert("NPM_CONFIG_REGISTRY".to_string(), npm_registry_unset_url());
} }
command.envs(self.build_envs()); command.envs(envs);
command.current_dir(cwd); command.current_dir(cwd);
command.stdin(Stdio::piped()); command.stdin(Stdio::piped());
@ -527,6 +531,7 @@ impl Drop for TestCommandOutput {
// now ensure the exit code was asserted // now ensure the exit code was asserted
if !*self.asserted_exit_code.borrow() && self.exit_code != Some(0) { if !*self.asserted_exit_code.borrow() && self.exit_code != Some(0) {
self.print_output();
panic!( panic!(
"The non-zero exit code of the command was not asserted: {:?}", "The non-zero exit code of the command was not asserted: {:?}",
self.exit_code, self.exit_code,

View file

@ -37,7 +37,9 @@ use std::env;
use std::io; use std::io;
use std::io::Write; use std::io::Write;
use std::mem::replace; use std::mem::replace;
use std::net::Ipv6Addr;
use std::net::SocketAddr; use std::net::SocketAddr;
use std::net::SocketAddrV6;
use std::ops::Deref; use std::ops::Deref;
use std::ops::DerefMut; use std::ops::DerefMut;
use std::path::Path; use std::path::Path;
@ -1316,15 +1318,18 @@ async fn main_server(
} }
_ => { _ => {
let mut file_path = testdata_path().to_path_buf(); let mut file_path = testdata_path().to_path_buf();
file_path.push(&req.uri().path()[1..]); file_path.push(&req.uri().path()[1..].replace("%2f", "/"));
if let Ok(file) = tokio::fs::read(&file_path).await { if let Ok(file) = tokio::fs::read(&file_path).await {
let file_resp = custom_headers(req.uri().path(), file); let file_resp = custom_headers(req.uri().path(), file);
return Ok(file_resp); return Ok(file_resp);
} }
// serve npm registry files // serve npm registry files
if let Some(suffix) = if let Some(suffix) = req
req.uri().path().strip_prefix("/npm/registry/@denotest/") .uri()
.path()
.strip_prefix("/npm/registry/@denotest/")
.or_else(|| req.uri().path().strip_prefix("/npm/registry/@denotest%2f"))
{ {
// serve all requests to /npm/registry/@deno using the file system // serve all requests to /npm/registry/@deno using the file system
// at that path // at that path
@ -1571,10 +1576,22 @@ async fn wrap_abs_redirect_server() {
} }
async fn wrap_main_server() { async fn wrap_main_server() {
let main_server_addr = SocketAddr::from(([127, 0, 0, 1], PORT));
wrap_main_server_for_addr(&main_server_addr).await
}
// necessary because on Windows the npm binary will resolve localhost to ::1
async fn wrap_main_ipv6_server() {
let ipv6_loopback = Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0, 1);
let main_server_addr =
SocketAddr::V6(SocketAddrV6::new(ipv6_loopback, PORT, 0, 0));
wrap_main_server_for_addr(&main_server_addr).await
}
async fn wrap_main_server_for_addr(main_server_addr: &SocketAddr) {
let main_server_svc = let main_server_svc =
make_service_fn(|_| async { Ok::<_, Infallible>(service_fn(main_server)) }); make_service_fn(|_| async { Ok::<_, Infallible>(service_fn(main_server)) });
let main_server_addr = SocketAddr::from(([127, 0, 0, 1], PORT)); let main_server = Server::bind(main_server_addr).serve(main_server_svc);
let main_server = Server::bind(&main_server_addr).serve(main_server_svc);
if let Err(e) = main_server.await { if let Err(e) = main_server.await {
eprintln!("HTTP server error: {e:?}"); eprintln!("HTTP server error: {e:?}");
} }
@ -1922,6 +1939,7 @@ pub async fn run_all_servers() {
let tls_client_auth_server_fut = run_tls_client_auth_server(); let tls_client_auth_server_fut = run_tls_client_auth_server();
let client_auth_server_https_fut = wrap_client_auth_https_server(); let client_auth_server_https_fut = wrap_client_auth_https_server();
let main_server_fut = wrap_main_server(); let main_server_fut = wrap_main_server();
let main_server_ipv6_fut = wrap_main_ipv6_server();
let main_server_https_fut = wrap_main_https_server(); let main_server_https_fut = wrap_main_https_server();
let h1_only_server_tls_fut = wrap_https_h1_only_tls_server(); let h1_only_server_tls_fut = wrap_https_h1_only_tls_server();
let h2_only_server_tls_fut = wrap_https_h2_only_tls_server(); let h2_only_server_tls_fut = wrap_https_h2_only_tls_server();
@ -1945,6 +1963,7 @@ pub async fn run_all_servers() {
double_redirects_server_fut, double_redirects_server_fut,
abs_redirect_server_fut, abs_redirect_server_fut,
main_server_fut, main_server_fut,
main_server_ipv6_fut,
main_server_https_fut, main_server_https_fut,
client_auth_server_https_fut, client_auth_server_https_fut,
h1_only_server_tls_fut, h1_only_server_tls_fut,

View file

@ -104,7 +104,7 @@ fn get_npm_package(package_name: &str) -> Result<Option<CustomNpmPackage>> {
let mut hash_ctx = Context::new(&SHA512); let mut hash_ctx = Context::new(&SHA512);
hash_ctx.update(&tarball_bytes); hash_ctx.update(&tarball_bytes);
let digest = hash_ctx.finish(); let digest = hash_ctx.finish();
let tarball_checksum = base64::encode(digest.as_ref()).to_lowercase(); let tarball_checksum = base64::encode(digest.as_ref());
// create the registry file JSON for this version // create the registry file JSON for this version
let mut dist = serde_json::Map::new(); let mut dist = serde_json::Map::new();

View file

@ -21,7 +21,7 @@ if (rs) {
promises.push(clippy()); promises.push(clippy());
} }
if (!js && !rs) { if (js && rs) {
promises.push(checkCopyright()); promises.push(checkCopyright());
} }