Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

FUTURE: deno install changes #23498

Merged
merged 32 commits into from May 8, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
32 commits
Select commit Hold shift + click to select a range
28c3950
wip
bartlomieju Apr 9, 2024
0c1c212
works with DENO_FUTURE=1
bartlomieju Apr 22, 2024
6f35a70
revert
bartlomieju Apr 22, 2024
394bd8e
Fix lint
nathanwhit Apr 25, 2024
f043fbe
Fmt
nathanwhit Apr 25, 2024
7209ccd
Don't require global flag if !DENO_FUTURE
nathanwhit Apr 25, 2024
49c77d2
Add integration tests
nathanwhit Apr 25, 2024
5dc90f6
reset CI
bartlomieju Apr 25, 2024
7a6b4c4
Fix output on windows
nathanwhit Apr 25, 2024
59e798d
Npm flag + wire up install flags
nathanwhit Apr 26, 2024
6414516
Factor out a separate future install command
nathanwhit Apr 26, 2024
b3d259c
Include add args
nathanwhit Apr 26, 2024
e875d3b
Fix args
nathanwhit Apr 26, 2024
7fb272d
Deno add / install works on package.json
nathanwhit Apr 27, 2024
6ef7c98
Fix arg
nathanwhit Apr 29, 2024
c584b8f
Handle dependency entry for package.json properly
nathanwhit Apr 29, 2024
4cb4217
Appease linter
nathanwhit Apr 29, 2024
0d806a1
Add test for `deno install npm:<pkg>` under DENO_FUTURE
nathanwhit Apr 29, 2024
1ed7eec
Cache import map deps
nathanwhit Apr 29, 2024
c60aca5
Test caching (+ some renames)
nathanwhit Apr 29, 2024
72d0677
Appease clippy
nathanwhit Apr 29, 2024
f096499
Fix test
nathanwhit Apr 30, 2024
9ac6330
Remove --npm for now, gate behavior change behind deno_future
nathanwhit Apr 30, 2024
f6ad0c0
Test cleanup
nathanwhit May 1, 2024
15eb072
Rm last --npm
nathanwhit May 1, 2024
f19518a
Add "i" alias for install
nathanwhit May 7, 2024
b8e7dec
Fix tests
nathanwhit May 7, 2024
9e1afed
Cleanup
nathanwhit May 7, 2024
6b4486b
Cleanup add logic
nathanwhit May 7, 2024
fc66f9c
Cleanup tests
nathanwhit May 7, 2024
d3901df
Appease clippy
nathanwhit May 7, 2024
8959a58
Update todo
nathanwhit May 7, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
6 changes: 3 additions & 3 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

219 changes: 162 additions & 57 deletions cli/args/flags.rs
Expand Up @@ -38,6 +38,7 @@ use crate::args::resolve_no_prompt;
use crate::util::fs::canonicalize_path;

use super::flags_net;
use super::DENO_FUTURE;

#[derive(Clone, Debug, Default, Eq, PartialEq)]
pub struct FileFlags {
Expand Down Expand Up @@ -195,7 +196,7 @@ pub struct InstallFlagsGlobal {
#[derive(Clone, Debug, Eq, PartialEq)]
pub enum InstallKind {
#[allow(unused)]
Local,
Local(Option<AddFlags>),
Global(InstallFlagsGlobal),
}

Expand Down Expand Up @@ -913,11 +914,18 @@ impl Flags {
}
Task(_) | Check(_) | Coverage(_) | Cache(_) | Info(_) | Eval(_)
| Test(_) | Bench(_) | Repl(_) | Compile(_) | Publish(_) => {
std::env::current_dir().ok()
Some(current_dir.to_path_buf())
}
Add(_) | Bundle(_) | Completions(_) | Doc(_) | Fmt(_) | Init(_)
| Install(_) | Uninstall(_) | Jupyter(_) | Lsp | Lint(_) | Types
| Upgrade(_) | Vendor(_) => None,
| Uninstall(_) | Jupyter(_) | Lsp | Lint(_) | Types | Upgrade(_)
| Vendor(_) => None,
Install(_) => {
if *DENO_FUTURE {
Some(current_dir.to_path_buf())
} else {
None
}
}
}
}

Expand Down Expand Up @@ -1313,7 +1321,11 @@ fn clap_root() -> Command {
.subcommand(fmt_subcommand())
.subcommand(init_subcommand())
.subcommand(info_subcommand())
.subcommand(install_subcommand())
.subcommand(if *DENO_FUTURE {
future_install_subcommand()
} else {
install_subcommand()
})
.subcommand(jupyter_subcommand())
.subcommand(uninstall_subcommand())
.subcommand(lsp_subcommand())
Expand Down Expand Up @@ -2047,11 +2059,110 @@ TypeScript compiler cache: Subdirectory containing TS compiler output.",
))
}

fn install_args(cmd: Command, deno_future: bool) -> Command {
let cmd = if deno_future {
cmd.arg(
Arg::new("cmd")
.required_if_eq("global", "true")
.num_args(1..)
.value_hint(ValueHint::FilePath),
)
} else {
cmd.arg(
Arg::new("cmd")
.required(true)
.num_args(1..)
.value_hint(ValueHint::FilePath),
)
};
cmd
.arg(
Arg::new("name")
.long("name")
.short('n')
.help("Executable file name")
.required(false),
)
.arg(
Arg::new("root")
.long("root")
.help("Installation root")
.value_hint(ValueHint::DirPath),
)
.arg(
Arg::new("force")
.long("force")
.short('f')
.help("Forcefully overwrite existing installation")
.action(ArgAction::SetTrue),
)
.arg(
Arg::new("global")
.long("global")
.short('g')
.help("Install a package or script as a globally available executable")
.action(ArgAction::SetTrue),
)
.arg(env_file_arg())
}

fn future_install_subcommand() -> Command {
Command::new("install")
.visible_alias("i")
.about("Install dependencies")
.long_about(
"Installs dependencies either in the local project or globally to a bin directory.

Local installation
-------------------
If the --global flag is not set, adds dependencies to the local project's configuration
(package.json / deno.json) and installs them in the package cache. If no dependency
is specified, installs all dependencies listed in package.json.

deno install
deno install @std/bytes
deno install npm:chalk

Global installation
-------------------
If the --global flag is set, installs a script as an executable in the installation root's bin directory.

deno install --global --allow-net --allow-read jsr:@std/http/file-server
deno install -g https://examples.deno.land/color-logging.ts

To change the executable name, use -n/--name:

deno install -g --allow-net --allow-read -n serve jsr:@std/http/file-server

The executable name is inferred by default:
- Attempt to take the file stem of the URL path. The above example would
become 'file_server'.
- If the file stem is something generic like 'main', 'mod', 'index' or 'cli',
and the path has no parent, take the file name of the parent path. Otherwise
settle with the generic name.
- If the resulting name has an '@...' suffix, strip it.

To change the installation root, use --root:

deno install -g --allow-net --allow-read --root /usr/local jsr:@std/http/file-server

The installation root is determined, in order of precedence:
- --root option
- DENO_INSTALL_ROOT environment variable
- $HOME/.deno

These must be added to the path manually if required.")
nathanwhit marked this conversation as resolved.
Show resolved Hide resolved
.defer(|cmd| {
let cmd = runtime_args(cmd, true, true).arg(check_arg(true));
install_args(cmd, true)
})
}

fn install_subcommand() -> Command {
Command::new("install")
.about("Install script as an executable")
.long_about(
"Installs a script as an executable in the installation root's bin directory.
"Installs a script as an executable in the installation root's bin directory.

deno install --global --allow-net --allow-read jsr:@std/http/file-server
deno install -g https://examples.deno.land/color-logging.ts
Expand All @@ -2078,34 +2189,10 @@ The installation root is determined, in order of precedence:
- $HOME/.deno

These must be added to the path manually if required.")
.defer(|cmd| runtime_args(cmd, true, true).arg(Arg::new("cmd").required(true).num_args(1..).value_hint(ValueHint::FilePath))
.arg(check_arg(true))
.arg(
Arg::new("name")
.long("name")
.short('n')
.help("Executable file name")
.required(false))
.arg(
Arg::new("root")
.long("root")
.help("Installation root")
.value_hint(ValueHint::DirPath))
.arg(
Arg::new("force")
.long("force")
.short('f')
.help("Forcefully overwrite existing installation")
.action(ArgAction::SetTrue))
)
.arg(
Arg::new("global")
.long("global")
.short('g')
.help("Install a package or script as a globally available executable")
.action(ArgAction::SetTrue)
)
.arg(env_file_arg())
.defer(|cmd| {
let cmd = runtime_args(cmd, true, true).arg(check_arg(true));
install_args(cmd, false)
})
}

fn jupyter_subcommand() -> Command {
Expand Down Expand Up @@ -3555,8 +3642,17 @@ fn unsafely_ignore_certificate_errors_arg() -> Arg {
}

fn add_parse(flags: &mut Flags, matches: &mut ArgMatches) {
let packages = matches.remove_many::<String>("packages").unwrap().collect();
flags.subcommand = DenoSubcommand::Add(AddFlags { packages });
flags.subcommand = DenoSubcommand::Add(add_parse_inner(matches, None));
}

fn add_parse_inner(
matches: &mut ArgMatches,
packages: Option<clap::parser::Values<String>>,
) -> AddFlags {
let packages = packages
.unwrap_or_else(|| matches.remove_many::<String>("packages").unwrap())
.collect();
AddFlags { packages }
}

fn bench_parse(flags: &mut Flags, matches: &mut ArgMatches) {
Expand Down Expand Up @@ -3871,28 +3967,37 @@ fn info_parse(flags: &mut Flags, matches: &mut ArgMatches) {
fn install_parse(flags: &mut Flags, matches: &mut ArgMatches) {
runtime_args_parse(flags, matches, true, true);

let root = matches.remove_one::<String>("root");

let force = matches.get_flag("force");
let global = matches.get_flag("global");
let name = matches.remove_one::<String>("name");
let mut cmd_values = matches.remove_many::<String>("cmd").unwrap();

let module_url = cmd_values.next().unwrap();
let args = cmd_values.collect();

flags.subcommand = DenoSubcommand::Install(InstallFlags {
// TODO(bartlomieju): remove once `deno install` supports both local and
// global installs
global,
kind: InstallKind::Global(InstallFlagsGlobal {
name,
module_url,
args,
root,
force,
}),
});
if global || !*DENO_FUTURE {
let root = matches.remove_one::<String>("root");
let force = matches.get_flag("force");
let name = matches.remove_one::<String>("name");
let mut cmd_values =
matches.remove_many::<String>("cmd").unwrap_or_default();

let module_url = cmd_values.next().unwrap();
let args = cmd_values.collect();

flags.subcommand = DenoSubcommand::Install(InstallFlags {
// TODO(bartlomieju): remove for 2.0
global,
kind: InstallKind::Global(InstallFlagsGlobal {
name,
module_url,
args,
root,
force,
}),
});
} else {
let local_flags = matches
.remove_many("cmd")
.map(|packages| add_parse_inner(matches, Some(packages)));
flags.subcommand = DenoSubcommand::Install(InstallFlags {
global,
kind: InstallKind::Local(local_flags),
})
}
}

fn jupyter_parse(flags: &mut Flags, matches: &mut ArgMatches) {
Expand Down
3 changes: 2 additions & 1 deletion cli/factory.rs
Expand Up @@ -408,7 +408,8 @@ impl CliFactory {
.npm_resolver
.get_or_try_init_async(async {
let fs = self.fs();
create_cli_npm_resolver(if self.options.use_byonm() {
// For `deno install` we want to force the managed resolver so it can set up `node_modules/` directory.
create_cli_npm_resolver(if self.options.use_byonm() && !matches!(self.options.sub_command(), DenoSubcommand::Install(_)) {
CliNpmResolverCreateOptions::Byonm(CliNpmResolverByonmCreateOptions {
fs: fs.clone(),
root_node_modules_dir: match self.options.node_modules_dir_path() {
Expand Down
35 changes: 35 additions & 0 deletions cli/module_loader.rs
Expand Up @@ -8,6 +8,7 @@ use crate::cache::CodeCache;
use crate::cache::ModuleInfoCache;
use crate::cache::ParsedSourceCache;
use crate::emit::Emitter;
use crate::factory::CliFactory;
use crate::graph_util::graph_lock_or_exit;
use crate::graph_util::CreateGraphOptions;
use crate::graph_util::ModuleGraphBuilder;
Expand Down Expand Up @@ -64,6 +65,40 @@ use std::rc::Rc;
use std::str;
use std::sync::Arc;

pub async fn load_top_level_deps(factory: &CliFactory) -> Result<(), AnyError> {
let npm_resolver = factory.npm_resolver().await?;
if let Some(npm_resolver) = npm_resolver.as_managed() {
npm_resolver.ensure_top_level_package_json_install().await?;
npm_resolver.resolve_pending().await?;
}
// cache as many entries in the import map as we can
if let Some(import_map) = factory.maybe_import_map().await? {
let roots = import_map
.imports()
.entries()
.filter_map(|entry| {
if entry.key.ends_with('/') {
None
} else {
entry.value.cloned()
}
})
.collect();
factory
.module_load_preparer()
.await?
.prepare_module_load(
roots,
false,
factory.cli_options().ts_type_lib_window(),
deno_runtime::permissions::PermissionsContainer::allow_all(),
)
.await?;
}

Ok(())
}

pub struct ModuleLoadPreparer {
options: Arc<CliOptions>,
graph_container: Arc<ModuleGraphContainer>,
Expand Down
19 changes: 18 additions & 1 deletion cli/tools/installer.rs
@@ -1,6 +1,7 @@
// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license.

use crate::args::resolve_no_prompt;
use crate::args::AddFlags;
use crate::args::CaData;
use crate::args::Flags;
use crate::args::InstallFlags;
Expand Down Expand Up @@ -253,6 +254,20 @@ pub fn uninstall(uninstall_flags: UninstallFlags) -> Result<(), AnyError> {
Ok(())
}

async fn install_local(
flags: Flags,
maybe_add_flags: Option<AddFlags>,
) -> Result<(), AnyError> {
if let Some(add_flags) = maybe_add_flags {
return super::registry::add(flags, add_flags).await;
}

let factory = CliFactory::from_flags(flags)?;
crate::module_loader::load_top_level_deps(&factory).await?;

Ok(())
}

pub async fn install_command(
flags: Flags,
install_flags: InstallFlags,
Expand All @@ -263,7 +278,9 @@ pub async fn install_command(

let install_flags_global = match install_flags.kind {
InstallKind::Global(flags) => flags,
InstallKind::Local => unreachable!(),
InstallKind::Local(maybe_add_flags) => {
return install_local(flags, maybe_add_flags).await
}
};

// ensure the module is cached
Expand Down