From c32d692a8f37c50fd700bb320571f76a107a44c2 Mon Sep 17 00:00:00 2001 From: Nathan Whitaker <17734409+nathanwhit@users.noreply.github.com> Date: Wed, 4 Sep 2024 13:06:16 -0700 Subject: [PATCH] feat(install): deno install with entrypoint (#25411) ``` deno install --entrypoint one.ts two.ts ``` effectively equivalent to `deno cache` --- cli/args/flags.rs | 52 +++++++++++++-- cli/main.rs | 10 +-- cli/tools/installer.rs | 65 +++++++++++++------ .../install/install_entrypoint/__test__.jsonc | 21 ++++++ .../install/install_entrypoint/install.out | 7 ++ .../install/install_entrypoint/lifecycle.out | 22 +++++++ .../install/install_entrypoint/lifecycle.ts | 3 + .../specs/install/install_entrypoint/main.ts | 4 ++ .../install/install_entrypoint/second.ts | 1 + 9 files changed, 149 insertions(+), 36 deletions(-) create mode 100644 tests/specs/install/install_entrypoint/__test__.jsonc create mode 100644 tests/specs/install/install_entrypoint/install.out create mode 100644 tests/specs/install/install_entrypoint/lifecycle.out create mode 100644 tests/specs/install/install_entrypoint/lifecycle.ts create mode 100644 tests/specs/install/install_entrypoint/main.ts create mode 100644 tests/specs/install/install_entrypoint/second.ts diff --git a/cli/args/flags.rs b/cli/args/flags.rs index 5ea8b8ecf3..9397f21807 100644 --- a/cli/args/flags.rs +++ b/cli/args/flags.rs @@ -240,10 +240,17 @@ pub struct InstallFlagsGlobal { #[derive(Clone, Debug, Eq, PartialEq)] pub enum InstallKind { - Local(Option), + Local(InstallFlagsLocal), Global(InstallFlagsGlobal), } +#[derive(Clone, Debug, Eq, PartialEq)] +pub enum InstallFlagsLocal { + Add(AddFlags), + TopLevel, + Entrypoints(Vec), +} + #[derive(Clone, Debug, Eq, PartialEq)] pub struct InstallFlags { pub global: bool, @@ -2365,10 +2372,12 @@ fn install_subcommand() -> Command { Add dependencies to the local project's configuration (deno.json / package.json) and installs them in the package cache. If no dependency is specified, installs all dependencies listed in the config file. +If the --entrypoint flag is passed, installs the dependencies of the specified entrypoint(s). deno install deno install @std/bytes deno install npm:chalk + deno install --entrypoint entry1.ts entry2.ts Global installation @@ -2405,6 +2414,7 @@ These must be added to the path manually if required."), UnstableArgsConfig::Res .arg( Arg::new("cmd") .required_if_eq("global", "true") + .required_if_eq("entrypoint", "true") .num_args(1..) .value_hint(ValueHint::FilePath), ) @@ -2412,17 +2422,20 @@ These must be added to the path manually if required."), UnstableArgsConfig::Res Arg::new("name") .long("name") .short('n') + .requires("global") .help("Executable file name"), ) .arg( Arg::new("root") .long("root") + .requires("global") .help("Installation root") .value_hint(ValueHint::DirPath), ) .arg( Arg::new("force") .long("force") + .requires("global") .short('f') .help("Forcefully overwrite existing installation") .action(ArgAction::SetTrue), @@ -2434,6 +2447,14 @@ These must be added to the path manually if required."), UnstableArgsConfig::Res .help("Install a package or script as a globally available executable") .action(ArgAction::SetTrue), ) + .arg( + Arg::new("entrypoint") + .long("entrypoint") + .short('e') + .conflicts_with("global") + .action(ArgAction::SetTrue) + .help("Install dependents of the specified entrypoint(s)"), + ) .arg(env_file_arg()) }) } @@ -4419,15 +4440,32 @@ fn install_parse(flags: &mut Flags, matches: &mut ArgMatches) { force, }), }); - } else { - let local_flags = matches - .remove_many("cmd") - .map(|packages| add_parse_inner(matches, Some(packages))); - allow_scripts_arg_parse(flags, matches); + return; + } + + // allow scripts only applies to local install + allow_scripts_arg_parse(flags, matches); + if matches.get_flag("entrypoint") { + let entrypoints = matches.remove_many::("cmd").unwrap_or_default(); flags.subcommand = DenoSubcommand::Install(InstallFlags { global, - kind: InstallKind::Local(local_flags), + kind: InstallKind::Local(InstallFlagsLocal::Entrypoints( + entrypoints.collect(), + )), + }); + } else if let Some(add_files) = matches + .remove_many("cmd") + .map(|packages| add_parse_inner(matches, Some(packages))) + { + flags.subcommand = DenoSubcommand::Install(InstallFlags { + global, + kind: InstallKind::Local(InstallFlagsLocal::Add(add_files)), }) + } else { + flags.subcommand = DenoSubcommand::Install(InstallFlags { + global, + kind: InstallKind::Local(InstallFlagsLocal::TopLevel), + }); } } diff --git a/cli/main.rs b/cli/main.rs index c963cb21ca..c0899ee100 100644 --- a/cli/main.rs +++ b/cli/main.rs @@ -32,7 +32,6 @@ mod worker; use crate::args::flags_from_vec; use crate::args::DenoSubcommand; use crate::args::Flags; -use crate::graph_container::ModuleGraphContainer; use crate::util::display; use crate::util::v8::get_v8_flags_from_env; use crate::util::v8::init_v8_flags; @@ -118,14 +117,7 @@ async fn run_subcommand(flags: Arc) -> Result { tools::run::eval_command(flags, eval_flags).await }), DenoSubcommand::Cache(cache_flags) => spawn_subcommand(async move { - let factory = CliFactory::from_flags(flags); - let emitter = factory.emitter()?; - let main_graph_container = - factory.main_module_graph_container().await?; - main_graph_container - .load_and_type_check_files(&cache_flags.files) - .await?; - emitter.cache_module_emits(&main_graph_container.graph()).await + tools::installer::install_from_entrypoints(flags, &cache_flags.files).await }), DenoSubcommand::Check(check_flags) => spawn_subcommand(async move { let factory = CliFactory::from_flags(flags); diff --git a/cli/tools/installer.rs b/cli/tools/installer.rs index 6965d5c6d3..987f3c0694 100644 --- a/cli/tools/installer.rs +++ b/cli/tools/installer.rs @@ -7,11 +7,13 @@ use crate::args::ConfigFlag; use crate::args::Flags; use crate::args::InstallFlags; use crate::args::InstallFlagsGlobal; +use crate::args::InstallFlagsLocal; use crate::args::InstallKind; use crate::args::TypeCheckMode; use crate::args::UninstallFlags; use crate::args::UninstallKind; use crate::factory::CliFactory; +use crate::graph_container::ModuleGraphContainer; use crate::http_util::HttpClientProvider; use crate::util::fs::canonicalize_path_maybe_not_exists; @@ -262,27 +264,48 @@ pub fn uninstall(uninstall_flags: UninstallFlags) -> Result<(), AnyError> { Ok(()) } +pub(crate) async fn install_from_entrypoints( + flags: Arc, + entrypoints: &[String], +) -> Result<(), AnyError> { + let factory = CliFactory::from_flags(flags.clone()); + let emitter = factory.emitter()?; + let main_graph_container = factory.main_module_graph_container().await?; + main_graph_container + .load_and_type_check_files(entrypoints) + .await?; + emitter + .cache_module_emits(&main_graph_container.graph()) + .await +} + async fn install_local( flags: Arc, - maybe_add_flags: Option, + install_flags: InstallFlagsLocal, ) -> Result<(), AnyError> { - if let Some(add_flags) = maybe_add_flags { - return super::registry::add( - flags, - add_flags, - super::registry::AddCommandName::Install, - ) - .await; + match install_flags { + InstallFlagsLocal::Add(add_flags) => { + super::registry::add( + flags, + add_flags, + super::registry::AddCommandName::Install, + ) + .await + } + InstallFlagsLocal::Entrypoints(entrypoints) => { + install_from_entrypoints(flags, &entrypoints).await + } + InstallFlagsLocal::TopLevel => { + let factory = CliFactory::from_flags(flags); + crate::tools::registry::cache_top_level_deps(&factory, None).await?; + + if let Some(lockfile) = factory.cli_options()?.maybe_lockfile() { + lockfile.write_if_changed()?; + } + + Ok(()) + } } - - let factory = CliFactory::from_flags(flags); - crate::tools::registry::cache_top_level_deps(&factory, None).await?; - - if let Some(lockfile) = factory.cli_options()?.maybe_lockfile() { - lockfile.write_if_changed()?; - } - - Ok(()) } fn check_if_installs_a_single_package_globally( @@ -315,9 +338,11 @@ pub async fn install_command( install_global(flags, global_flags).await } - InstallKind::Local(maybe_add_flags) => { - check_if_installs_a_single_package_globally(maybe_add_flags.as_ref())?; - install_local(flags, maybe_add_flags).await + InstallKind::Local(local_flags) => { + if let InstallFlagsLocal::Add(add_flags) = &local_flags { + check_if_installs_a_single_package_globally(Some(add_flags))?; + } + install_local(flags, local_flags).await } } } diff --git a/tests/specs/install/install_entrypoint/__test__.jsonc b/tests/specs/install/install_entrypoint/__test__.jsonc new file mode 100644 index 0000000000..1afe195e28 --- /dev/null +++ b/tests/specs/install/install_entrypoint/__test__.jsonc @@ -0,0 +1,21 @@ +{ + "tempDir": true, + "tests": { + "basic": { + "steps": [ + { + "args": "install --entrypoint main.ts second.ts", + "output": "install.out" + } + ] + }, + "allow_scripts": { + "steps": [ + { + "args": "install --allow-scripts --node-modules-dir=auto --entrypoint lifecycle.ts", + "output": "lifecycle.out" + } + ] + } + } +} diff --git a/tests/specs/install/install_entrypoint/install.out b/tests/specs/install/install_entrypoint/install.out new file mode 100644 index 0000000000..d702cf45ac --- /dev/null +++ b/tests/specs/install/install_entrypoint/install.out @@ -0,0 +1,7 @@ +[UNORDERED_START] +Download http://127.0.0.1:4250/@denotest/add/meta.json +Download http://localhost:4260/@denotest/esm-basic +Download http://127.0.0.1:4250/@denotest/add/1.0.0_meta.json +Download http://127.0.0.1:4250/@denotest/add/1.0.0/mod.ts +Download http://localhost:4260/@denotest/esm-basic/1.0.0.tgz +[UNORDERED_END] diff --git a/tests/specs/install/install_entrypoint/lifecycle.out b/tests/specs/install/install_entrypoint/lifecycle.out new file mode 100644 index 0000000000..8eae8ee120 --- /dev/null +++ b/tests/specs/install/install_entrypoint/lifecycle.out @@ -0,0 +1,22 @@ +[UNORDERED_START] +Download http://localhost:4260/@denotest/node-lifecycle-scripts +Download http://localhost:4260/@denotest/bin +Download http://localhost:4260/@denotest/node-lifecycle-scripts/1.0.0.tgz +Download http://localhost:4260/@denotest/bin/1.0.0.tgz +Initialize @denotest/node-lifecycle-scripts@1.0.0 +Initialize @denotest/bin@1.0.0 +[UNORDERED_END] +preinstall +deno preinstall.js +node preinstall.js +install +hello from install script +postinstall[WILDCARD] + _____________ +< postinstall > + ------------- + \ ^__^ + \ (oo)\_______ + (__)\ )\/\ + ||----w | + || || diff --git a/tests/specs/install/install_entrypoint/lifecycle.ts b/tests/specs/install/install_entrypoint/lifecycle.ts new file mode 100644 index 0000000000..e9fb2cf9cc --- /dev/null +++ b/tests/specs/install/install_entrypoint/lifecycle.ts @@ -0,0 +1,3 @@ +import { value } from "npm:@denotest/node-lifecycle-scripts"; + +console.log(`value is ${value}`); diff --git a/tests/specs/install/install_entrypoint/main.ts b/tests/specs/install/install_entrypoint/main.ts new file mode 100644 index 0000000000..aceb2b3c88 --- /dev/null +++ b/tests/specs/install/install_entrypoint/main.ts @@ -0,0 +1,4 @@ +import { getValue, setValue } from "npm:@denotest/esm-basic"; + +setValue(5); +console.log(getValue()); diff --git a/tests/specs/install/install_entrypoint/second.ts b/tests/specs/install/install_entrypoint/second.ts new file mode 100644 index 0000000000..2c781286f8 --- /dev/null +++ b/tests/specs/install/install_entrypoint/second.ts @@ -0,0 +1 @@ +import { add } from "jsr:@denotest/add@1.0.0";