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

feat(unstable): support caching npm dependencies only as they're needed (#27300)

Currently deno eagerly caches all npm packages in the workspace's npm
resolution. So, for instance, running a file `foo.ts` that imports
`npm:chalk` will also install all dependencies listed in `package.json`
and all `npm` dependencies listed in the lockfile.

This PR refactors things to give more control over when and what npm
packages are automatically cached while building the module graph.

After this PR, by default the current behavior is unchanged _except_ for
`deno install --entrypoint`, which will only cache npm packages used by
the given entrypoint. For the other subcommands, this behavior can be
enabled with `--unstable-npm-lazy-caching`


Fixes #25782.

---------

Signed-off-by: Nathan Whitaker <17734409+nathanwhit@users.noreply.github.com>
Co-authored-by: Luca Casonato <hello@lcas.dev>
This commit is contained in:
Nathan Whitaker 2024-12-10 18:24:23 -08:00 committed by GitHub
parent dd42a64c43
commit 6f506208f6
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
39 changed files with 371 additions and 109 deletions

View file

@ -245,7 +245,7 @@ pub struct InstallFlagsGlobal {
} }
#[derive(Clone, Debug, Eq, PartialEq)] #[derive(Clone, Debug, Eq, PartialEq)]
pub enum InstallKind { pub enum InstallFlags {
Local(InstallFlagsLocal), Local(InstallFlagsLocal),
Global(InstallFlagsGlobal), Global(InstallFlagsGlobal),
} }
@ -257,11 +257,6 @@ pub enum InstallFlagsLocal {
Entrypoints(Vec<String>), Entrypoints(Vec<String>),
} }
#[derive(Clone, Debug, Eq, PartialEq)]
pub struct InstallFlags {
pub kind: InstallKind,
}
#[derive(Clone, Debug, Eq, PartialEq)] #[derive(Clone, Debug, Eq, PartialEq)]
pub struct JSONReferenceFlags { pub struct JSONReferenceFlags {
pub json: deno_core::serde_json::Value, pub json: deno_core::serde_json::Value,
@ -600,6 +595,7 @@ pub struct UnstableConfig {
pub bare_node_builtins: bool, pub bare_node_builtins: bool,
pub detect_cjs: bool, pub detect_cjs: bool,
pub sloppy_imports: bool, pub sloppy_imports: bool,
pub npm_lazy_caching: bool,
pub features: Vec<String>, // --unstabe-kv --unstable-cron pub features: Vec<String>, // --unstabe-kv --unstable-cron
} }
@ -4407,6 +4403,16 @@ impl CommandExt for Command {
}) })
.help_heading(UNSTABLE_HEADING) .help_heading(UNSTABLE_HEADING)
.display_order(next_display_order()) .display_order(next_display_order())
).arg(
Arg::new("unstable-npm-lazy-caching")
.long("unstable-npm-lazy-caching")
.help("Enable unstable lazy caching of npm dependencies, downloading them only as needed (disabled: all npm packages in package.json are installed on startup; enabled: only npm packages that are actually referenced in an import are installed")
.env("DENO_UNSTABLE_NPM_LAZY_CACHING")
.value_parser(FalseyValueParser::new())
.action(ArgAction::SetTrue)
.hide(true)
.help_heading(UNSTABLE_HEADING)
.display_order(next_display_order()),
); );
for granular_flag in crate::UNSTABLE_GRANULAR_FLAGS.iter() { for granular_flag in crate::UNSTABLE_GRANULAR_FLAGS.iter() {
@ -4920,15 +4926,14 @@ fn install_parse(
let module_url = cmd_values.next().unwrap(); let module_url = cmd_values.next().unwrap();
let args = cmd_values.collect(); let args = cmd_values.collect();
flags.subcommand = DenoSubcommand::Install(InstallFlags { flags.subcommand =
kind: InstallKind::Global(InstallFlagsGlobal { DenoSubcommand::Install(InstallFlags::Global(InstallFlagsGlobal {
name, name,
module_url, module_url,
args, args,
root, root,
force, force,
}), }));
});
return Ok(()); return Ok(());
} }
@ -4937,22 +4942,19 @@ fn install_parse(
allow_scripts_arg_parse(flags, matches)?; allow_scripts_arg_parse(flags, matches)?;
if matches.get_flag("entrypoint") { if matches.get_flag("entrypoint") {
let entrypoints = matches.remove_many::<String>("cmd").unwrap_or_default(); let entrypoints = matches.remove_many::<String>("cmd").unwrap_or_default();
flags.subcommand = DenoSubcommand::Install(InstallFlags { flags.subcommand = DenoSubcommand::Install(InstallFlags::Local(
kind: InstallKind::Local(InstallFlagsLocal::Entrypoints( InstallFlagsLocal::Entrypoints(entrypoints.collect()),
entrypoints.collect(), ));
)),
});
} else if let Some(add_files) = matches } else if let Some(add_files) = matches
.remove_many("cmd") .remove_many("cmd")
.map(|packages| add_parse_inner(matches, Some(packages))) .map(|packages| add_parse_inner(matches, Some(packages)))
{ {
flags.subcommand = DenoSubcommand::Install(InstallFlags { flags.subcommand = DenoSubcommand::Install(InstallFlags::Local(
kind: InstallKind::Local(InstallFlagsLocal::Add(add_files)), InstallFlagsLocal::Add(add_files),
}) ))
} else { } else {
flags.subcommand = DenoSubcommand::Install(InstallFlags { flags.subcommand =
kind: InstallKind::Local(InstallFlagsLocal::TopLevel), DenoSubcommand::Install(InstallFlags::Local(InstallFlagsLocal::TopLevel));
});
} }
Ok(()) Ok(())
} }
@ -5998,6 +6000,8 @@ fn unstable_args_parse(
flags.unstable_config.detect_cjs = matches.get_flag("unstable-detect-cjs"); flags.unstable_config.detect_cjs = matches.get_flag("unstable-detect-cjs");
flags.unstable_config.sloppy_imports = flags.unstable_config.sloppy_imports =
matches.get_flag("unstable-sloppy-imports"); matches.get_flag("unstable-sloppy-imports");
flags.unstable_config.npm_lazy_caching =
matches.get_flag("unstable-npm-lazy-caching");
if matches!(cfg, UnstableArgsConfig::ResolutionAndRuntime) { if matches!(cfg, UnstableArgsConfig::ResolutionAndRuntime) {
for granular_flag in crate::UNSTABLE_GRANULAR_FLAGS { for granular_flag in crate::UNSTABLE_GRANULAR_FLAGS {
@ -8606,15 +8610,15 @@ mod tests {
assert_eq!( assert_eq!(
r.unwrap(), r.unwrap(),
Flags { Flags {
subcommand: DenoSubcommand::Install(InstallFlags { subcommand: DenoSubcommand::Install(InstallFlags::Global(
kind: InstallKind::Global(InstallFlagsGlobal { InstallFlagsGlobal {
name: None, name: None,
module_url: "jsr:@std/http/file-server".to_string(), module_url: "jsr:@std/http/file-server".to_string(),
args: vec![], args: vec![],
root: None, root: None,
force: false, force: false,
}), }
}), ),),
..Flags::default() ..Flags::default()
} }
); );
@ -8628,15 +8632,15 @@ mod tests {
assert_eq!( assert_eq!(
r.unwrap(), r.unwrap(),
Flags { Flags {
subcommand: DenoSubcommand::Install(InstallFlags { subcommand: DenoSubcommand::Install(InstallFlags::Global(
kind: InstallKind::Global(InstallFlagsGlobal { InstallFlagsGlobal {
name: None, name: None,
module_url: "jsr:@std/http/file-server".to_string(), module_url: "jsr:@std/http/file-server".to_string(),
args: vec![], args: vec![],
root: None, root: None,
force: false, force: false,
}), }
}), ),),
..Flags::default() ..Flags::default()
} }
); );
@ -8649,15 +8653,15 @@ mod tests {
assert_eq!( assert_eq!(
r.unwrap(), r.unwrap(),
Flags { Flags {
subcommand: DenoSubcommand::Install(InstallFlags { subcommand: DenoSubcommand::Install(InstallFlags::Global(
kind: InstallKind::Global(InstallFlagsGlobal { InstallFlagsGlobal {
name: Some("file_server".to_string()), name: Some("file_server".to_string()),
module_url: "jsr:@std/http/file-server".to_string(), module_url: "jsr:@std/http/file-server".to_string(),
args: svec!["foo", "bar"], args: svec!["foo", "bar"],
root: Some("/foo".to_string()), root: Some("/foo".to_string()),
force: true, force: true,
}), }
}), ),),
import_map_path: Some("import_map.json".to_string()), import_map_path: Some("import_map.json".to_string()),
no_remote: true, no_remote: true,
config_flag: ConfigFlag::Path("tsconfig.json".to_owned()), config_flag: ConfigFlag::Path("tsconfig.json".to_owned()),
@ -11211,9 +11215,9 @@ mod tests {
..Flags::default() ..Flags::default()
}, },
"install" => Flags { "install" => Flags {
subcommand: DenoSubcommand::Install(InstallFlags { subcommand: DenoSubcommand::Install(InstallFlags::Local(
kind: InstallKind::Local(InstallFlagsLocal::Add(flags)), InstallFlagsLocal::Add(flags),
}), )),
..Flags::default() ..Flags::default()
}, },
_ => unreachable!(), _ => unreachable!(),

View file

@ -20,7 +20,6 @@ use crate::Flags;
use crate::args::DenoSubcommand; use crate::args::DenoSubcommand;
use crate::args::InstallFlags; use crate::args::InstallFlags;
use crate::args::InstallKind;
use deno_lockfile::Lockfile; use deno_lockfile::Lockfile;
@ -136,10 +135,8 @@ impl CliLockfile {
if flags.no_lock if flags.no_lock
|| matches!( || matches!(
flags.subcommand, flags.subcommand,
DenoSubcommand::Install(InstallFlags { DenoSubcommand::Install(InstallFlags::Global(..))
kind: InstallKind::Global(..), | DenoSubcommand::Uninstall(_)
..
}) | DenoSubcommand::Uninstall(_)
) )
{ {
return Ok(None); return Ok(None);

View file

@ -970,9 +970,7 @@ impl CliOptions {
match self.sub_command() { match self.sub_command() {
DenoSubcommand::Cache(_) => GraphKind::All, DenoSubcommand::Cache(_) => GraphKind::All,
DenoSubcommand::Check(_) => GraphKind::TypesOnly, DenoSubcommand::Check(_) => GraphKind::TypesOnly,
DenoSubcommand::Install(InstallFlags { DenoSubcommand::Install(InstallFlags::Local(_)) => GraphKind::All,
kind: InstallKind::Local(_),
}) => GraphKind::All,
_ => self.type_check_mode().as_graph_kind(), _ => self.type_check_mode().as_graph_kind(),
} }
} }
@ -1549,11 +1547,11 @@ impl CliOptions {
DenoSubcommand::Check(check_flags) => { DenoSubcommand::Check(check_flags) => {
Some(files_to_urls(&check_flags.files)) Some(files_to_urls(&check_flags.files))
} }
DenoSubcommand::Install(InstallFlags { DenoSubcommand::Install(InstallFlags::Global(flags)) => {
kind: InstallKind::Global(flags), Url::parse(&flags.module_url)
}) => Url::parse(&flags.module_url) .ok()
.ok() .map(|url| vec![Cow::Owned(url)])
.map(|url| vec![Cow::Owned(url)]), }
DenoSubcommand::Doc(DocFlags { DenoSubcommand::Doc(DocFlags {
source_files: DocSourceFileFlag::Paths(paths), source_files: DocSourceFileFlag::Paths(paths),
.. ..
@ -1689,6 +1687,7 @@ impl CliOptions {
"detect-cjs", "detect-cjs",
"fmt-component", "fmt-component",
"fmt-sql", "fmt-sql",
"lazy-npm-caching",
]) ])
.collect(); .collect();
@ -1767,6 +1766,19 @@ impl CliOptions {
), ),
} }
} }
pub fn unstable_npm_lazy_caching(&self) -> bool {
self.flags.unstable_config.npm_lazy_caching
|| self.workspace().has_unstable("npm-lazy-caching")
}
pub fn default_npm_caching_strategy(&self) -> NpmCachingStrategy {
if self.flags.unstable_config.npm_lazy_caching {
NpmCachingStrategy::Lazy
} else {
NpmCachingStrategy::Eager
}
}
} }
/// Resolves the path to use for a local node_modules folder. /// Resolves the path to use for a local node_modules folder.
@ -1981,6 +1993,13 @@ fn load_env_variables_from_env_file(filename: Option<&Vec<String>>) {
} }
} }
#[derive(Debug, Clone, Copy)]
pub enum NpmCachingStrategy {
Eager,
Lazy,
Manual,
}
#[cfg(test)] #[cfg(test)]
mod test { mod test {
use pretty_assertions::assert_eq; use pretty_assertions::assert_eq;

View file

@ -984,6 +984,7 @@ impl CliFactory {
cli_options.sub_command().clone(), cli_options.sub_command().clone(),
self.create_cli_main_worker_options()?, self.create_cli_main_worker_options()?,
self.cli_options()?.otel_config(), self.cli_options()?.otel_config(),
self.cli_options()?.default_npm_caching_strategy(),
)) ))
} }

View file

@ -4,6 +4,7 @@ use crate::args::config_to_deno_graph_workspace_member;
use crate::args::jsr_url; use crate::args::jsr_url;
use crate::args::CliLockfile; use crate::args::CliLockfile;
use crate::args::CliOptions; use crate::args::CliOptions;
pub use crate::args::NpmCachingStrategy;
use crate::args::DENO_DISABLE_PEDANTIC_NODE_WARNINGS; use crate::args::DENO_DISABLE_PEDANTIC_NODE_WARNINGS;
use crate::cache; use crate::cache;
use crate::cache::FetchCacher; use crate::cache::FetchCacher;
@ -218,6 +219,7 @@ pub struct CreateGraphOptions<'a> {
pub is_dynamic: bool, pub is_dynamic: bool,
/// Specify `None` to use the default CLI loader. /// Specify `None` to use the default CLI loader.
pub loader: Option<&'a mut dyn Loader>, pub loader: Option<&'a mut dyn Loader>,
pub npm_caching: NpmCachingStrategy,
} }
pub struct ModuleGraphCreator { pub struct ModuleGraphCreator {
@ -246,10 +248,11 @@ impl ModuleGraphCreator {
&self, &self,
graph_kind: GraphKind, graph_kind: GraphKind,
roots: Vec<ModuleSpecifier>, roots: Vec<ModuleSpecifier>,
npm_caching: NpmCachingStrategy,
) -> Result<deno_graph::ModuleGraph, AnyError> { ) -> Result<deno_graph::ModuleGraph, AnyError> {
let mut cache = self.module_graph_builder.create_graph_loader(); let mut cache = self.module_graph_builder.create_graph_loader();
self self
.create_graph_with_loader(graph_kind, roots, &mut cache) .create_graph_with_loader(graph_kind, roots, &mut cache, npm_caching)
.await .await
} }
@ -258,6 +261,7 @@ impl ModuleGraphCreator {
graph_kind: GraphKind, graph_kind: GraphKind,
roots: Vec<ModuleSpecifier>, roots: Vec<ModuleSpecifier>,
loader: &mut dyn Loader, loader: &mut dyn Loader,
npm_caching: NpmCachingStrategy,
) -> Result<ModuleGraph, AnyError> { ) -> Result<ModuleGraph, AnyError> {
self self
.create_graph_with_options(CreateGraphOptions { .create_graph_with_options(CreateGraphOptions {
@ -265,6 +269,7 @@ impl ModuleGraphCreator {
graph_kind, graph_kind,
roots, roots,
loader: Some(loader), loader: Some(loader),
npm_caching,
}) })
.await .await
} }
@ -317,6 +322,7 @@ impl ModuleGraphCreator {
graph_kind: deno_graph::GraphKind::All, graph_kind: deno_graph::GraphKind::All,
roots, roots,
loader: Some(&mut publish_loader), loader: Some(&mut publish_loader),
npm_caching: self.options.default_npm_caching_strategy(),
}) })
.await?; .await?;
self.graph_valid(&graph)?; self.graph_valid(&graph)?;
@ -376,6 +382,7 @@ impl ModuleGraphCreator {
graph_kind, graph_kind,
roots, roots,
loader: None, loader: None,
npm_caching: self.options.default_npm_caching_strategy(),
}) })
.await?; .await?;
@ -565,7 +572,8 @@ impl ModuleGraphBuilder {
}; };
let cli_resolver = &self.resolver; let cli_resolver = &self.resolver;
let graph_resolver = self.create_graph_resolver()?; let graph_resolver = self.create_graph_resolver()?;
let graph_npm_resolver = cli_resolver.create_graph_npm_resolver(); let graph_npm_resolver =
cli_resolver.create_graph_npm_resolver(options.npm_caching);
let maybe_file_watcher_reporter = self let maybe_file_watcher_reporter = self
.maybe_file_watcher_reporter .maybe_file_watcher_reporter
.as_ref() .as_ref()
@ -592,6 +600,7 @@ impl ModuleGraphBuilder {
resolver: Some(&graph_resolver), resolver: Some(&graph_resolver),
locker: locker.as_mut().map(|l| l as _), locker: locker.as_mut().map(|l| l as _),
}, },
options.npm_caching,
) )
.await .await
} }
@ -602,6 +611,7 @@ impl ModuleGraphBuilder {
roots: Vec<ModuleSpecifier>, roots: Vec<ModuleSpecifier>,
loader: &'a mut dyn deno_graph::source::Loader, loader: &'a mut dyn deno_graph::source::Loader,
options: deno_graph::BuildOptions<'a>, options: deno_graph::BuildOptions<'a>,
npm_caching: NpmCachingStrategy,
) -> Result<(), AnyError> { ) -> Result<(), AnyError> {
// ensure an "npm install" is done if the user has explicitly // ensure an "npm install" is done if the user has explicitly
// opted into using a node_modules directory // opted into using a node_modules directory
@ -612,7 +622,13 @@ impl ModuleGraphBuilder {
.unwrap_or(false) .unwrap_or(false)
{ {
if let Some(npm_resolver) = self.npm_resolver.as_managed() { if let Some(npm_resolver) = self.npm_resolver.as_managed() {
npm_resolver.ensure_top_level_package_json_install().await?; let already_done =
npm_resolver.ensure_top_level_package_json_install().await?;
if !already_done && matches!(npm_caching, NpmCachingStrategy::Eager) {
npm_resolver
.cache_packages(crate::npm::PackageCaching::All)
.await?;
}
} }
} }
@ -701,7 +717,9 @@ impl ModuleGraphBuilder {
let parser = self.parsed_source_cache.as_capturing_parser(); let parser = self.parsed_source_cache.as_capturing_parser();
let cli_resolver = &self.resolver; let cli_resolver = &self.resolver;
let graph_resolver = self.create_graph_resolver()?; let graph_resolver = self.create_graph_resolver()?;
let graph_npm_resolver = cli_resolver.create_graph_npm_resolver(); let graph_npm_resolver = cli_resolver.create_graph_npm_resolver(
self.cli_options.default_npm_caching_strategy(),
);
graph.build_fast_check_type_graph( graph.build_fast_check_type_graph(
deno_graph::BuildFastCheckTypeGraphOptions { deno_graph::BuildFastCheckTypeGraphOptions {

View file

@ -270,7 +270,12 @@ impl LanguageServer {
open_docs: &open_docs, open_docs: &open_docs,
}; };
let graph = module_graph_creator let graph = module_graph_creator
.create_graph_with_loader(GraphKind::All, roots.clone(), &mut loader) .create_graph_with_loader(
GraphKind::All,
roots.clone(),
&mut loader,
graph_util::NpmCachingStrategy::Eager,
)
.await?; .await?;
graph_util::graph_valid( graph_util::graph_valid(
&graph, &graph,

View file

@ -133,7 +133,8 @@ impl LspScopeResolver {
cache.for_specifier(config_data.map(|d| d.scope.as_ref())), cache.for_specifier(config_data.map(|d| d.scope.as_ref())),
config_data.and_then(|d| d.lockfile.clone()), config_data.and_then(|d| d.lockfile.clone()),
))); )));
let npm_graph_resolver = cli_resolver.create_graph_npm_resolver(); let npm_graph_resolver = cli_resolver
.create_graph_npm_resolver(crate::graph_util::NpmCachingStrategy::Eager);
let maybe_jsx_import_source_config = let maybe_jsx_import_source_config =
config_data.and_then(|d| d.maybe_jsx_import_source_config()); config_data.and_then(|d| d.maybe_jsx_import_source_config());
let graph_imports = config_data let graph_imports = config_data
@ -343,7 +344,9 @@ impl LspResolver {
file_referrer: Option<&ModuleSpecifier>, file_referrer: Option<&ModuleSpecifier>,
) -> WorkerCliNpmGraphResolver { ) -> WorkerCliNpmGraphResolver {
let resolver = self.get_scope_resolver(file_referrer); let resolver = self.get_scope_resolver(file_referrer);
resolver.resolver.create_graph_npm_resolver() resolver
.resolver
.create_graph_npm_resolver(crate::graph_util::NpmCachingStrategy::Eager)
} }
pub fn as_is_cjs_resolver( pub fn as_is_cjs_resolver(

View file

@ -156,6 +156,7 @@ impl ModuleLoadPreparer {
graph_kind: graph.graph_kind(), graph_kind: graph.graph_kind(),
roots: roots.to_vec(), roots: roots.to_vec(),
loader: Some(&mut cache), loader: Some(&mut cache),
npm_caching: self.options.default_npm_caching_strategy(),
}, },
) )
.await?; .await?;

View file

@ -296,6 +296,12 @@ pub fn create_managed_in_npm_pkg_checker(
Arc::new(ManagedInNpmPackageChecker { root_dir }) Arc::new(ManagedInNpmPackageChecker { root_dir })
} }
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum PackageCaching<'a> {
Only(Cow<'a, [PackageReq]>),
All,
}
/// An npm resolver where the resolution is managed by Deno rather than /// An npm resolver where the resolution is managed by Deno rather than
/// the user bringing their own node_modules (BYONM) on the file system. /// the user bringing their own node_modules (BYONM) on the file system.
pub struct ManagedCliNpmResolver { pub struct ManagedCliNpmResolver {
@ -420,19 +426,44 @@ impl ManagedCliNpmResolver {
/// Adds package requirements to the resolver and ensures everything is setup. /// Adds package requirements to the resolver and ensures everything is setup.
/// This includes setting up the `node_modules` directory, if applicable. /// This includes setting up the `node_modules` directory, if applicable.
pub async fn add_package_reqs( pub async fn add_and_cache_package_reqs(
&self, &self,
packages: &[PackageReq], packages: &[PackageReq],
) -> Result<(), AnyError> { ) -> Result<(), AnyError> {
self self
.add_package_reqs_raw(packages) .add_package_reqs_raw(
packages,
Some(PackageCaching::Only(packages.into())),
)
.await .await
.dependencies_result .dependencies_result
} }
pub async fn add_package_reqs_raw( pub async fn add_package_reqs_no_cache(
&self, &self,
packages: &[PackageReq], packages: &[PackageReq],
) -> Result<(), AnyError> {
self
.add_package_reqs_raw(packages, None)
.await
.dependencies_result
}
pub async fn add_package_reqs(
&self,
packages: &[PackageReq],
caching: PackageCaching<'_>,
) -> Result<(), AnyError> {
self
.add_package_reqs_raw(packages, Some(caching))
.await
.dependencies_result
}
pub async fn add_package_reqs_raw<'a>(
&self,
packages: &[PackageReq],
caching: Option<PackageCaching<'a>>,
) -> AddPkgReqsResult { ) -> AddPkgReqsResult {
if packages.is_empty() { if packages.is_empty() {
return AddPkgReqsResult { return AddPkgReqsResult {
@ -449,7 +480,9 @@ impl ManagedCliNpmResolver {
} }
} }
if result.dependencies_result.is_ok() { if result.dependencies_result.is_ok() {
result.dependencies_result = self.cache_packages().await; if let Some(caching) = caching {
result.dependencies_result = self.cache_packages(caching).await;
}
} }
result result
@ -491,16 +524,20 @@ impl ManagedCliNpmResolver {
pub async fn inject_synthetic_types_node_package( pub async fn inject_synthetic_types_node_package(
&self, &self,
) -> Result<(), AnyError> { ) -> Result<(), AnyError> {
let reqs = &[PackageReq::from_str("@types/node").unwrap()];
// add and ensure this isn't added to the lockfile // add and ensure this isn't added to the lockfile
self self
.add_package_reqs(&[PackageReq::from_str("@types/node").unwrap()]) .add_package_reqs(reqs, PackageCaching::Only(reqs.into()))
.await?; .await?;
Ok(()) Ok(())
} }
pub async fn cache_packages(&self) -> Result<(), AnyError> { pub async fn cache_packages(
self.fs_resolver.cache_packages().await &self,
caching: PackageCaching<'_>,
) -> Result<(), AnyError> {
self.fs_resolver.cache_packages(caching).await
} }
pub fn resolve_pkg_folder_from_deno_module( pub fn resolve_pkg_folder_from_deno_module(
@ -545,18 +582,18 @@ impl ManagedCliNpmResolver {
/// Ensures that the top level `package.json` dependencies are installed. /// Ensures that the top level `package.json` dependencies are installed.
/// This may set up the `node_modules` directory. /// This may set up the `node_modules` directory.
/// ///
/// Returns `true` if any changes (such as caching packages) were made. /// Returns `true` if the top level packages are already installed. A
/// If this returns `false`, `node_modules` has _not_ been set up. /// return value of `false` means that new packages were added to the NPM resolution.
pub async fn ensure_top_level_package_json_install( pub async fn ensure_top_level_package_json_install(
&self, &self,
) -> Result<bool, AnyError> { ) -> Result<bool, AnyError> {
if !self.top_level_install_flag.raise() { if !self.top_level_install_flag.raise() {
return Ok(false); // already did this return Ok(true); // already did this
} }
let pkg_json_remote_pkgs = self.npm_install_deps_provider.remote_pkgs(); let pkg_json_remote_pkgs = self.npm_install_deps_provider.remote_pkgs();
if pkg_json_remote_pkgs.is_empty() { if pkg_json_remote_pkgs.is_empty() {
return Ok(false); return Ok(true);
} }
// check if something needs resolving before bothering to load all // check if something needs resolving before bothering to load all
@ -570,14 +607,16 @@ impl ManagedCliNpmResolver {
log::debug!( log::debug!(
"All package.json deps resolvable. Skipping top level install." "All package.json deps resolvable. Skipping top level install."
); );
return Ok(false); // everything is already resolvable return Ok(true); // everything is already resolvable
} }
let pkg_reqs = pkg_json_remote_pkgs let pkg_reqs = pkg_json_remote_pkgs
.iter() .iter()
.map(|pkg| pkg.req.clone()) .map(|pkg| pkg.req.clone())
.collect::<Vec<_>>(); .collect::<Vec<_>>();
self.add_package_reqs(&pkg_reqs).await.map(|_| true) self.add_package_reqs_no_cache(&pkg_reqs).await?;
Ok(false)
} }
pub async fn cache_package_info( pub async fn cache_package_info(

View file

@ -255,6 +255,10 @@ impl NpmResolution {
.read() .read()
.as_valid_serialized_for_system(system_info) .as_valid_serialized_for_system(system_info)
} }
pub fn subset(&self, package_reqs: &[PackageReq]) -> NpmResolutionSnapshot {
self.snapshot.read().subset(package_reqs)
}
} }
async fn add_package_reqs_to_snapshot( async fn add_package_reqs_to_snapshot(

View file

@ -11,6 +11,7 @@ use std::path::PathBuf;
use std::sync::Arc; use std::sync::Arc;
use std::sync::Mutex; use std::sync::Mutex;
use super::super::PackageCaching;
use async_trait::async_trait; use async_trait::async_trait;
use deno_ast::ModuleSpecifier; use deno_ast::ModuleSpecifier;
use deno_core::anyhow::Context; use deno_core::anyhow::Context;
@ -57,7 +58,10 @@ pub trait NpmPackageFsResolver: Send + Sync {
specifier: &ModuleSpecifier, specifier: &ModuleSpecifier,
) -> Result<Option<NpmPackageCacheFolderId>, AnyError>; ) -> Result<Option<NpmPackageCacheFolderId>, AnyError>;
async fn cache_packages(&self) -> Result<(), AnyError>; async fn cache_packages<'a>(
&self,
caching: PackageCaching<'a>,
) -> Result<(), AnyError>;
#[must_use = "the resolved return value to mitigate time-of-check to time-of-use issues"] #[must_use = "the resolved return value to mitigate time-of-check to time-of-use issues"]
fn ensure_read_permission<'a>( fn ensure_read_permission<'a>(

View file

@ -8,6 +8,7 @@ use std::path::PathBuf;
use std::sync::Arc; use std::sync::Arc;
use crate::colors; use crate::colors;
use crate::npm::managed::PackageCaching;
use crate::npm::CliNpmCache; use crate::npm::CliNpmCache;
use crate::npm::CliNpmTarballCache; use crate::npm::CliNpmTarballCache;
use async_trait::async_trait; use async_trait::async_trait;
@ -150,10 +151,19 @@ impl NpmPackageFsResolver for GlobalNpmPackageResolver {
) )
} }
async fn cache_packages(&self) -> Result<(), AnyError> { async fn cache_packages<'a>(
let package_partitions = self &self,
.resolution caching: PackageCaching<'a>,
.all_system_packages_partitioned(&self.system_info); ) -> Result<(), AnyError> {
let package_partitions = match caching {
PackageCaching::All => self
.resolution
.all_system_packages_partitioned(&self.system_info),
PackageCaching::Only(reqs) => self
.resolution
.subset(&reqs)
.all_system_packages_partitioned(&self.system_info),
};
cache_packages(&package_partitions.packages, &self.tarball_cache).await?; cache_packages(&package_partitions.packages, &self.tarball_cache).await?;
// create the copy package folders // create the copy package folders

View file

@ -17,6 +17,7 @@ use std::sync::Arc;
use crate::args::LifecycleScriptsConfig; use crate::args::LifecycleScriptsConfig;
use crate::colors; use crate::colors;
use crate::npm::managed::PackageCaching;
use crate::npm::CliNpmCache; use crate::npm::CliNpmCache;
use crate::npm::CliNpmTarballCache; use crate::npm::CliNpmTarballCache;
use async_trait::async_trait; use async_trait::async_trait;
@ -253,9 +254,16 @@ impl NpmPackageFsResolver for LocalNpmPackageResolver {
)) ))
} }
async fn cache_packages(&self) -> Result<(), AnyError> { async fn cache_packages<'a>(
&self,
caching: PackageCaching<'a>,
) -> Result<(), AnyError> {
let snapshot = match caching {
PackageCaching::All => self.resolution.snapshot(),
PackageCaching::Only(reqs) => self.resolution.subset(&reqs),
};
sync_resolution_with_fs( sync_resolution_with_fs(
&self.resolution.snapshot(), &snapshot,
&self.cache, &self.cache,
&self.npm_install_deps_provider, &self.npm_install_deps_provider,
&self.progress_bar, &self.progress_bar,

View file

@ -41,6 +41,7 @@ pub use self::managed::CliManagedInNpmPkgCheckerCreateOptions;
pub use self::managed::CliManagedNpmResolverCreateOptions; pub use self::managed::CliManagedNpmResolverCreateOptions;
pub use self::managed::CliNpmResolverManagedSnapshotOption; pub use self::managed::CliNpmResolverManagedSnapshotOption;
pub use self::managed::ManagedCliNpmResolver; pub use self::managed::ManagedCliNpmResolver;
pub use self::managed::PackageCaching;
pub type CliNpmTarballCache = deno_npm_cache::TarballCache<CliNpmCacheEnv>; pub type CliNpmTarballCache = deno_npm_cache::TarballCache<CliNpmCacheEnv>;
pub type CliNpmCache = deno_npm_cache::NpmCache<CliNpmCacheEnv>; pub type CliNpmCache = deno_npm_cache::NpmCache<CliNpmCacheEnv>;

View file

@ -32,6 +32,7 @@ use std::path::PathBuf;
use std::sync::Arc; use std::sync::Arc;
use thiserror::Error; use thiserror::Error;
use crate::args::NpmCachingStrategy;
use crate::args::DENO_DISABLE_PEDANTIC_NODE_WARNINGS; use crate::args::DENO_DISABLE_PEDANTIC_NODE_WARNINGS;
use crate::node::CliNodeCodeTranslator; use crate::node::CliNodeCodeTranslator;
use crate::npm::CliNpmResolver; use crate::npm::CliNpmResolver;
@ -240,11 +241,15 @@ impl CliResolver {
// todo(dsherret): move this off CliResolver as CliResolver is acting // todo(dsherret): move this off CliResolver as CliResolver is acting
// like a factory by doing this (it's beyond its responsibility) // like a factory by doing this (it's beyond its responsibility)
pub fn create_graph_npm_resolver(&self) -> WorkerCliNpmGraphResolver { pub fn create_graph_npm_resolver(
&self,
npm_caching: NpmCachingStrategy,
) -> WorkerCliNpmGraphResolver {
WorkerCliNpmGraphResolver { WorkerCliNpmGraphResolver {
npm_resolver: self.npm_resolver.as_ref(), npm_resolver: self.npm_resolver.as_ref(),
found_package_json_dep_flag: &self.found_package_json_dep_flag, found_package_json_dep_flag: &self.found_package_json_dep_flag,
bare_node_builtins_enabled: self.bare_node_builtins_enabled, bare_node_builtins_enabled: self.bare_node_builtins_enabled,
npm_caching,
} }
} }
@ -304,6 +309,7 @@ pub struct WorkerCliNpmGraphResolver<'a> {
npm_resolver: Option<&'a Arc<dyn CliNpmResolver>>, npm_resolver: Option<&'a Arc<dyn CliNpmResolver>>,
found_package_json_dep_flag: &'a AtomicFlag, found_package_json_dep_flag: &'a AtomicFlag,
bare_node_builtins_enabled: bool, bare_node_builtins_enabled: bool,
npm_caching: NpmCachingStrategy,
} }
#[async_trait(?Send)] #[async_trait(?Send)]
@ -373,7 +379,20 @@ impl<'a> deno_graph::source::NpmResolver for WorkerCliNpmGraphResolver<'a> {
Ok(()) Ok(())
}; };
let result = npm_resolver.add_package_reqs_raw(package_reqs).await; let result = npm_resolver
.add_package_reqs_raw(
package_reqs,
match self.npm_caching {
NpmCachingStrategy::Eager => {
Some(crate::npm::PackageCaching::All)
}
NpmCachingStrategy::Lazy => {
Some(crate::npm::PackageCaching::Only(package_reqs.into()))
}
NpmCachingStrategy::Manual => None,
},
)
.await;
NpmResolvePkgReqsResult { NpmResolvePkgReqsResult {
results: result results: result

View file

@ -779,6 +779,7 @@ impl<'a> DenoCompileBinaryWriter<'a> {
detect_cjs: self.cli_options.unstable_detect_cjs(), detect_cjs: self.cli_options.unstable_detect_cjs(),
sloppy_imports: self.cli_options.unstable_sloppy_imports(), sloppy_imports: self.cli_options.unstable_sloppy_imports(),
features: self.cli_options.unstable_features(), features: self.cli_options.unstable_features(),
npm_lazy_caching: self.cli_options.unstable_npm_lazy_caching(),
}, },
otel_config: self.cli_options.otel_config(), otel_config: self.cli_options.otel_config(),
}; };

View file

@ -924,6 +924,7 @@ pub async fn run(data: StandaloneData) -> Result<i32, AnyError> {
serve_host: None, serve_host: None,
}, },
metadata.otel_config, metadata.otel_config,
crate::args::NpmCachingStrategy::Lazy,
); );
// Initialize v8 once from the main thread. // Initialize v8 once from the main thread.

View file

@ -538,7 +538,11 @@ pub async fn run_benchmarks_with_watch(
)?; )?;
let graph = module_graph_creator let graph = module_graph_creator
.create_graph(graph_kind, collected_bench_modules.clone()) .create_graph(
graph_kind,
collected_bench_modules.clone(),
crate::graph_util::NpmCachingStrategy::Eager,
)
.await?; .await?;
module_graph_creator.graph_valid(&graph)?; module_graph_creator.graph_valid(&graph)?;
let bench_modules = &graph.roots; let bench_modules = &graph.roots;

View file

@ -69,7 +69,11 @@ pub async fn compile(
// create a module graph with types information in it. We don't want to // create a module graph with types information in it. We don't want to
// store that in the binary so create a code only module graph from scratch. // store that in the binary so create a code only module graph from scratch.
module_graph_creator module_graph_creator
.create_graph(GraphKind::CodeOnly, module_roots) .create_graph(
GraphKind::CodeOnly,
module_roots,
crate::graph_util::NpmCachingStrategy::Eager,
)
.await? .await?
} else { } else {
graph graph

View file

@ -131,7 +131,11 @@ pub async fn doc(
|_| true, |_| true,
)?; )?;
let graph = module_graph_creator let graph = module_graph_creator
.create_graph(GraphKind::TypesOnly, module_specifiers.clone()) .create_graph(
GraphKind::TypesOnly,
module_specifiers.clone(),
crate::graph_util::NpmCachingStrategy::Eager,
)
.await?; .await?;
graph_exit_integrity_errors(&graph); graph_exit_integrity_errors(&graph);

View file

@ -123,7 +123,12 @@ pub async fn info(
let mut loader = module_graph_builder.create_graph_loader(); let mut loader = module_graph_builder.create_graph_loader();
loader.enable_loading_cache_info(); // for displaying the cache information loader.enable_loading_cache_info(); // for displaying the cache information
let graph = module_graph_creator let graph = module_graph_creator
.create_graph_with_loader(GraphKind::All, vec![specifier], &mut loader) .create_graph_with_loader(
GraphKind::All,
vec![specifier],
&mut loader,
crate::graph_util::NpmCachingStrategy::Eager,
)
.await?; .await?;
// write out the lockfile if there is one // write out the lockfile if there is one

View file

@ -9,7 +9,6 @@ use crate::args::Flags;
use crate::args::InstallFlags; use crate::args::InstallFlags;
use crate::args::InstallFlagsGlobal; use crate::args::InstallFlagsGlobal;
use crate::args::InstallFlagsLocal; use crate::args::InstallFlagsLocal;
use crate::args::InstallKind;
use crate::args::TypeCheckMode; use crate::args::TypeCheckMode;
use crate::args::UninstallFlags; use crate::args::UninstallFlags;
use crate::args::UninstallKind; use crate::args::UninstallKind;
@ -339,11 +338,11 @@ pub async fn install_command(
flags: Arc<Flags>, flags: Arc<Flags>,
install_flags: InstallFlags, install_flags: InstallFlags,
) -> Result<(), AnyError> { ) -> Result<(), AnyError> {
match install_flags.kind { match install_flags {
InstallKind::Global(global_flags) => { InstallFlags::Global(global_flags) => {
install_global(flags, global_flags).await install_global(flags, global_flags).await
} }
InstallKind::Local(local_flags) => { InstallFlags::Local(local_flags) => {
if let InstallFlagsLocal::Add(add_flags) = &local_flags { if let InstallFlagsLocal::Add(add_flags) = &local_flags {
check_if_installs_a_single_package_globally(Some(add_flags))?; check_if_installs_a_single_package_globally(Some(add_flags))?;
} }

View file

@ -6,6 +6,7 @@ use std::sync::Arc;
use crate::factory::CliFactory; use crate::factory::CliFactory;
use crate::graph_container::ModuleGraphContainer; use crate::graph_container::ModuleGraphContainer;
use crate::graph_container::ModuleGraphUpdatePermit; use crate::graph_container::ModuleGraphUpdatePermit;
use crate::graph_util::CreateGraphOptions;
use deno_core::error::AnyError; use deno_core::error::AnyError;
use deno_core::futures::stream::FuturesUnordered; use deno_core::futures::stream::FuturesUnordered;
use deno_core::futures::StreamExt; use deno_core::futures::StreamExt;
@ -18,18 +19,16 @@ pub async fn cache_top_level_deps(
) -> Result<(), AnyError> { ) -> Result<(), AnyError> {
let npm_resolver = factory.npm_resolver().await?; let npm_resolver = factory.npm_resolver().await?;
let cli_options = factory.cli_options()?; let cli_options = factory.cli_options()?;
let root_permissions = factory.root_permissions_container()?;
if let Some(npm_resolver) = npm_resolver.as_managed() { if let Some(npm_resolver) = npm_resolver.as_managed() {
if !npm_resolver.ensure_top_level_package_json_install().await? { npm_resolver.ensure_top_level_package_json_install().await?;
if let Some(lockfile) = cli_options.maybe_lockfile() { if let Some(lockfile) = cli_options.maybe_lockfile() {
lockfile.error_if_changed()?; lockfile.error_if_changed()?;
}
npm_resolver.cache_packages().await?;
} }
} }
// cache as many entries in the import map as we can // cache as many entries in the import map as we can
let resolver = factory.workspace_resolver().await?; let resolver = factory.workspace_resolver().await?;
let mut maybe_graph_error = Ok(());
if let Some(import_map) = resolver.maybe_import_map() { if let Some(import_map) = resolver.maybe_import_map() {
let jsr_resolver = if let Some(resolver) = jsr_resolver { let jsr_resolver = if let Some(resolver) = jsr_resolver {
resolver resolver
@ -122,19 +121,29 @@ pub async fn cache_top_level_deps(
} }
drop(info_futures); drop(info_futures);
factory let graph_builder = factory.module_graph_builder().await?;
.module_load_preparer() graph_builder
.await? .build_graph_with_npm_resolution(
.prepare_module_load(
graph, graph,
&roots, CreateGraphOptions {
false, loader: None,
deno_config::deno_json::TsTypeLib::DenoWorker, graph_kind: graph.graph_kind(),
root_permissions.clone(), is_dynamic: false,
None, roots: roots.clone(),
npm_caching: crate::graph_util::NpmCachingStrategy::Manual,
},
) )
.await?; .await?;
maybe_graph_error = graph_builder.graph_roots_valid(graph, &roots);
}
if let Some(npm_resolver) = npm_resolver.as_managed() {
npm_resolver
.cache_packages(crate::npm::PackageCaching::All)
.await?;
} }
maybe_graph_error?;
Ok(()) Ok(())
} }

View file

@ -727,7 +727,9 @@ impl ReplSession {
let has_node_specifier = let has_node_specifier =
resolved_imports.iter().any(|url| url.scheme() == "node"); resolved_imports.iter().any(|url| url.scheme() == "node");
if !npm_imports.is_empty() || has_node_specifier { if !npm_imports.is_empty() || has_node_specifier {
npm_resolver.add_package_reqs(&npm_imports).await?; npm_resolver
.add_and_cache_package_reqs(&npm_imports)
.await?;
// prevent messages in the repl about @types/node not being cached // prevent messages in the repl about @types/node not being cached
if has_node_specifier { if has_node_specifier {

View file

@ -198,13 +198,23 @@ pub async fn eval_command(
} }
pub async fn maybe_npm_install(factory: &CliFactory) -> Result<(), AnyError> { pub async fn maybe_npm_install(factory: &CliFactory) -> Result<(), AnyError> {
let cli_options = factory.cli_options()?;
// ensure an "npm install" is done if the user has explicitly // ensure an "npm install" is done if the user has explicitly
// opted into using a managed node_modules directory // opted into using a managed node_modules directory
if factory.cli_options()?.node_modules_dir()? if cli_options.node_modules_dir()? == Some(NodeModulesDirMode::Auto) {
== Some(NodeModulesDirMode::Auto)
{
if let Some(npm_resolver) = factory.npm_resolver().await?.as_managed() { if let Some(npm_resolver) = factory.npm_resolver().await?.as_managed() {
npm_resolver.ensure_top_level_package_json_install().await?; let already_done =
npm_resolver.ensure_top_level_package_json_install().await?;
if !already_done
&& matches!(
cli_options.default_npm_caching_strategy(),
crate::graph_util::NpmCachingStrategy::Eager
)
{
npm_resolver
.cache_packages(crate::npm::PackageCaching::All)
.await?;
}
} }
} }
Ok(()) Ok(())

View file

@ -440,6 +440,13 @@ impl<'a> TaskRunner<'a> {
kill_signal: KillSignal, kill_signal: KillSignal,
argv: &'a [String], argv: &'a [String],
) -> Result<i32, deno_core::anyhow::Error> { ) -> Result<i32, deno_core::anyhow::Error> {
if let Some(npm_resolver) = self.npm_resolver.as_managed() {
npm_resolver.ensure_top_level_package_json_install().await?;
npm_resolver
.cache_packages(crate::npm::PackageCaching::All)
.await?;
}
let cwd = match &self.task_flags.cwd { let cwd = match &self.task_flags.cwd {
Some(path) => canonicalize_path(&PathBuf::from(path)) Some(path) => canonicalize_path(&PathBuf::from(path))
.context("failed canonicalizing --cwd")?, .context("failed canonicalizing --cwd")?,
@ -450,6 +457,7 @@ impl<'a> TaskRunner<'a> {
self.npm_resolver, self.npm_resolver,
self.node_resolver, self.node_resolver,
)?; )?;
self self
.run_single(RunSingleOptions { .run_single(RunSingleOptions {
task_name, task_name,
@ -473,6 +481,9 @@ impl<'a> TaskRunner<'a> {
// ensure the npm packages are installed if using a managed resolver // ensure the npm packages are installed if using a managed resolver
if let Some(npm_resolver) = self.npm_resolver.as_managed() { if let Some(npm_resolver) = self.npm_resolver.as_managed() {
npm_resolver.ensure_top_level_package_json_install().await?; npm_resolver.ensure_top_level_package_json_install().await?;
npm_resolver
.cache_packages(crate::npm::PackageCaching::All)
.await?;
} }
let cwd = match &self.task_flags.cwd { let cwd = match &self.task_flags.cwd {
@ -492,6 +503,7 @@ impl<'a> TaskRunner<'a> {
self.npm_resolver, self.npm_resolver,
self.node_resolver, self.node_resolver,
)?; )?;
for task_name in &task_names { for task_name in &task_names {
if let Some(script) = scripts.get(task_name) { if let Some(script) = scripts.get(task_name) {
let exit_code = self let exit_code = self

View file

@ -1716,7 +1716,11 @@ pub async fn run_tests_with_watch(
&cli_options.permissions_options(), &cli_options.permissions_options(),
)?; )?;
let graph = module_graph_creator let graph = module_graph_creator
.create_graph(graph_kind, test_modules) .create_graph(
graph_kind,
test_modules,
crate::graph_util::NpmCachingStrategy::Eager,
)
.await?; .await?;
module_graph_creator.graph_valid(&graph)?; module_graph_creator.graph_valid(&graph)?;
let test_modules = &graph.roots; let test_modules = &graph.roots;

View file

@ -50,6 +50,7 @@ use tokio::select;
use crate::args::CliLockfile; use crate::args::CliLockfile;
use crate::args::DenoSubcommand; use crate::args::DenoSubcommand;
use crate::args::NpmCachingStrategy;
use crate::args::StorageKeyResolver; use crate::args::StorageKeyResolver;
use crate::errors; use crate::errors;
use crate::npm::CliNpmResolver; use crate::npm::CliNpmResolver;
@ -154,6 +155,7 @@ struct SharedWorkerState {
options: CliMainWorkerOptions, options: CliMainWorkerOptions,
subcommand: DenoSubcommand, subcommand: DenoSubcommand,
otel_config: Option<OtelConfig>, // `None` means OpenTelemetry is disabled. otel_config: Option<OtelConfig>, // `None` means OpenTelemetry is disabled.
default_npm_caching_strategy: NpmCachingStrategy,
} }
impl SharedWorkerState { impl SharedWorkerState {
@ -425,6 +427,7 @@ impl CliMainWorkerFactory {
subcommand: DenoSubcommand, subcommand: DenoSubcommand,
options: CliMainWorkerOptions, options: CliMainWorkerOptions,
otel_config: Option<OtelConfig>, otel_config: Option<OtelConfig>,
default_npm_caching_strategy: NpmCachingStrategy,
) -> Self { ) -> Self {
Self { Self {
shared: Arc::new(SharedWorkerState { shared: Arc::new(SharedWorkerState {
@ -448,6 +451,7 @@ impl CliMainWorkerFactory {
options, options,
subcommand, subcommand,
otel_config, otel_config,
default_npm_caching_strategy,
}), }),
} }
} }
@ -487,8 +491,19 @@ impl CliMainWorkerFactory {
NpmPackageReqReference::from_specifier(&main_module) NpmPackageReqReference::from_specifier(&main_module)
{ {
if let Some(npm_resolver) = shared.npm_resolver.as_managed() { if let Some(npm_resolver) = shared.npm_resolver.as_managed() {
let reqs = &[package_ref.req().clone()];
npm_resolver npm_resolver
.add_package_reqs(&[package_ref.req().clone()]) .add_package_reqs(
reqs,
if matches!(
shared.default_npm_caching_strategy,
NpmCachingStrategy::Lazy
) {
crate::npm::PackageCaching::Only(reqs.into())
} else {
crate::npm::PackageCaching::All
},
)
.await?; .await?;
} }

View file

@ -0,0 +1,13 @@
{
"tempDir": true,
"steps": [
{
"args": "install --unstable-npm-lazy-caching --entrypoint main.ts",
"output": "install-entrypoint.out"
},
{
"args": "install",
"output": "install.out"
}
]
}

View file

@ -0,0 +1,6 @@
[UNORDERED_START]
Download http://localhost:4260/@denotest%2fadd
Download http://localhost:4260/@denotest%2fsay-hello
Download http://localhost:4260/@denotest/add/1.0.0.tgz
[UNORDERED_END]
Initialize @denotest/add@1.0.0

View file

@ -0,0 +1,2 @@
Download http://localhost:4260/@denotest/say-hello/1.0.0.tgz
Initialize @denotest/say-hello@1.0.0

View file

@ -0,0 +1,3 @@
import { add } from "@denotest/add";
console.log(add(1, 2));

View file

@ -0,0 +1,6 @@
{
"dependencies": {
"@denotest/add": "1.0.0",
"@denotest/say-hello": "1.0.0"
}
}

View file

@ -1,4 +1,5 @@
{ {
"tempDir": true,
"tests": { "tests": {
"jsx_import_source_error": { "jsx_import_source_error": {
"args": "run --config jsx/deno-jsx-error.jsonc --check jsx_import_source_no_pragma.tsx", "args": "run --config jsx/deno-jsx-error.jsonc --check jsx_import_source_no_pragma.tsx",

View file

@ -0,0 +1,9 @@
{
"tempDir": true,
"steps": [
{
"args": "run --unstable-npm-lazy-caching -A main.ts",
"output": "main.out"
}
]
}

View file

@ -0,0 +1,3 @@
{
"nodeModulesDir": "auto"
}

View file

@ -0,0 +1,7 @@
[UNORDERED_START]
Download http://localhost:4260/@denotest%2fadd
Download http://localhost:4260/@denotest%2fsay-hello
Download http://localhost:4260/@denotest/add/1.0.0.tgz
[UNORDERED_END]
Initialize @denotest/add@1.0.0
3

View file

@ -0,0 +1,3 @@
import { add } from "@denotest/add";
console.log(add(1, 2));

View file

@ -0,0 +1,6 @@
{
"dependencies": {
"@denotest/add": "1.0.0",
"@denotest/say-hello": "1.0.0"
}
}