mirror of
https://github.com/denoland/deno.git
synced 2024-12-25 16:49:18 -05:00
FUTURE: deno install
changes (#23498)
This PR implements the changes we plan to make to `deno install` in deno 2.0. - `deno install` without arguments caches dependencies from `package.json` / `deno.json` and sets up the `node_modules` folder - `deno install <pkg>` adds the package to the config file (either `package.json` or `deno.json`), i.e. it aliases `deno add` - `deno add` can also add deps to `package.json` (this is gated behind `DENO_FUTURE` due to uncertainty around handling projects with both `deno.json` and `package.json`) - `deno install -g <bin>` installs a package as a globally available binary (the same as `deno install <bin>` in 1.0) --------- Co-authored-by: Nathan Whitaker <nathan@deno.com>
This commit is contained in:
parent
525b3c8d74
commit
4e23a5b1fc
34 changed files with 642 additions and 106 deletions
6
Cargo.lock
generated
6
Cargo.lock
generated
|
@ -5900,12 +5900,12 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "socket2"
|
name = "socket2"
|
||||||
version = "0.5.6"
|
version = "0.5.5"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "05ffd9c0a93b7543e062e759284fcf5f5e3b098501104bfbdde4d404db792871"
|
checksum = "7b5fac59a5cb5dd637972e5fca70daf0523c9067fcdc4842f053dae04a18f8e9"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"libc",
|
"libc",
|
||||||
"windows-sys 0.52.0",
|
"windows-sys 0.48.0",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
|
|
@ -38,6 +38,7 @@ use crate::args::resolve_no_prompt;
|
||||||
use crate::util::fs::canonicalize_path;
|
use crate::util::fs::canonicalize_path;
|
||||||
|
|
||||||
use super::flags_net;
|
use super::flags_net;
|
||||||
|
use super::DENO_FUTURE;
|
||||||
|
|
||||||
#[derive(Clone, Debug, Default, Eq, PartialEq)]
|
#[derive(Clone, Debug, Default, Eq, PartialEq)]
|
||||||
pub struct FileFlags {
|
pub struct FileFlags {
|
||||||
|
@ -195,7 +196,7 @@ pub struct InstallFlagsGlobal {
|
||||||
#[derive(Clone, Debug, Eq, PartialEq)]
|
#[derive(Clone, Debug, Eq, PartialEq)]
|
||||||
pub enum InstallKind {
|
pub enum InstallKind {
|
||||||
#[allow(unused)]
|
#[allow(unused)]
|
||||||
Local,
|
Local(Option<AddFlags>),
|
||||||
Global(InstallFlagsGlobal),
|
Global(InstallFlagsGlobal),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -913,11 +914,18 @@ impl Flags {
|
||||||
}
|
}
|
||||||
Task(_) | Check(_) | Coverage(_) | Cache(_) | Info(_) | Eval(_)
|
Task(_) | Check(_) | Coverage(_) | Cache(_) | Info(_) | Eval(_)
|
||||||
| Test(_) | Bench(_) | Repl(_) | Compile(_) | Publish(_) => {
|
| Test(_) | Bench(_) | Repl(_) | Compile(_) | Publish(_) => {
|
||||||
std::env::current_dir().ok()
|
Some(current_dir.to_path_buf())
|
||||||
}
|
}
|
||||||
Add(_) | Bundle(_) | Completions(_) | Doc(_) | Fmt(_) | Init(_)
|
Add(_) | Bundle(_) | Completions(_) | Doc(_) | Fmt(_) | Init(_)
|
||||||
| Install(_) | Uninstall(_) | Jupyter(_) | Lsp | Lint(_) | Types
|
| Uninstall(_) | Jupyter(_) | Lsp | Lint(_) | Types | Upgrade(_)
|
||||||
| Upgrade(_) | Vendor(_) => None,
|
| Vendor(_) => None,
|
||||||
|
Install(_) => {
|
||||||
|
if *DENO_FUTURE {
|
||||||
|
Some(current_dir.to_path_buf())
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1313,7 +1321,11 @@ fn clap_root() -> Command {
|
||||||
.subcommand(fmt_subcommand())
|
.subcommand(fmt_subcommand())
|
||||||
.subcommand(init_subcommand())
|
.subcommand(init_subcommand())
|
||||||
.subcommand(info_subcommand())
|
.subcommand(info_subcommand())
|
||||||
.subcommand(install_subcommand())
|
.subcommand(if *DENO_FUTURE {
|
||||||
|
future_install_subcommand()
|
||||||
|
} else {
|
||||||
|
install_subcommand()
|
||||||
|
})
|
||||||
.subcommand(jupyter_subcommand())
|
.subcommand(jupyter_subcommand())
|
||||||
.subcommand(uninstall_subcommand())
|
.subcommand(uninstall_subcommand())
|
||||||
.subcommand(lsp_subcommand())
|
.subcommand(lsp_subcommand())
|
||||||
|
@ -2047,11 +2059,73 @@ TypeScript compiler cache: Subdirectory containing TS compiler output.",
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn install_subcommand() -> Command {
|
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")
|
Command::new("install")
|
||||||
.about("Install script as an executable")
|
.visible_alias("i")
|
||||||
|
.about("Install dependencies")
|
||||||
.long_about(
|
.long_about(
|
||||||
"Installs a script as an executable in the installation root's bin directory.
|
"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 --global --allow-net --allow-read jsr:@std/http/file-server
|
||||||
deno install -g https://examples.deno.land/color-logging.ts
|
deno install -g https://examples.deno.land/color-logging.ts
|
||||||
|
@ -2078,34 +2152,47 @@ The installation root is determined, in order of precedence:
|
||||||
- $HOME/.deno
|
- $HOME/.deno
|
||||||
|
|
||||||
These must be added to the path manually if required.")
|
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))
|
.defer(|cmd| {
|
||||||
.arg(check_arg(true))
|
let cmd = runtime_args(cmd, true, true).arg(check_arg(true));
|
||||||
.arg(
|
install_args(cmd, true)
|
||||||
Arg::new("name")
|
})
|
||||||
.long("name")
|
}
|
||||||
.short('n')
|
|
||||||
.help("Executable file name")
|
fn install_subcommand() -> Command {
|
||||||
.required(false))
|
Command::new("install")
|
||||||
.arg(
|
.about("Install script as an executable")
|
||||||
Arg::new("root")
|
.long_about(
|
||||||
.long("root")
|
"Installs a script as an executable in the installation root's bin directory.
|
||||||
.help("Installation root")
|
|
||||||
.value_hint(ValueHint::DirPath))
|
deno install --global --allow-net --allow-read jsr:@std/http/file-server
|
||||||
.arg(
|
deno install -g https://examples.deno.land/color-logging.ts
|
||||||
Arg::new("force")
|
|
||||||
.long("force")
|
To change the executable name, use -n/--name:
|
||||||
.short('f')
|
|
||||||
.help("Forcefully overwrite existing installation")
|
deno install -g --allow-net --allow-read -n serve jsr:@std/http/file-server
|
||||||
.action(ArgAction::SetTrue))
|
|
||||||
)
|
The executable name is inferred by default:
|
||||||
.arg(
|
- Attempt to take the file stem of the URL path. The above example would
|
||||||
Arg::new("global")
|
become 'file_server'.
|
||||||
.long("global")
|
- If the file stem is something generic like 'main', 'mod', 'index' or 'cli',
|
||||||
.short('g')
|
and the path has no parent, take the file name of the parent path. Otherwise
|
||||||
.help("Install a package or script as a globally available executable")
|
settle with the generic name.
|
||||||
.action(ArgAction::SetTrue)
|
- If the resulting name has an '@...' suffix, strip it.
|
||||||
)
|
|
||||||
.arg(env_file_arg())
|
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.")
|
||||||
|
.defer(|cmd| {
|
||||||
|
let cmd = runtime_args(cmd, true, true).arg(check_arg(true));
|
||||||
|
install_args(cmd, false)
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
fn jupyter_subcommand() -> Command {
|
fn jupyter_subcommand() -> Command {
|
||||||
|
@ -3555,8 +3642,17 @@ fn unsafely_ignore_certificate_errors_arg() -> Arg {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn add_parse(flags: &mut Flags, matches: &mut ArgMatches) {
|
fn add_parse(flags: &mut Flags, matches: &mut ArgMatches) {
|
||||||
let packages = matches.remove_many::<String>("packages").unwrap().collect();
|
flags.subcommand = DenoSubcommand::Add(add_parse_inner(matches, None));
|
||||||
flags.subcommand = DenoSubcommand::Add(AddFlags { packages });
|
}
|
||||||
|
|
||||||
|
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) {
|
fn bench_parse(flags: &mut Flags, matches: &mut ArgMatches) {
|
||||||
|
@ -3871,28 +3967,37 @@ fn info_parse(flags: &mut Flags, matches: &mut ArgMatches) {
|
||||||
fn install_parse(flags: &mut Flags, matches: &mut ArgMatches) {
|
fn install_parse(flags: &mut Flags, matches: &mut ArgMatches) {
|
||||||
runtime_args_parse(flags, matches, true, true);
|
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 global = matches.get_flag("global");
|
||||||
let name = matches.remove_one::<String>("name");
|
if global || !*DENO_FUTURE {
|
||||||
let mut cmd_values = matches.remove_many::<String>("cmd").unwrap();
|
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 module_url = cmd_values.next().unwrap();
|
||||||
let args = cmd_values.collect();
|
let args = cmd_values.collect();
|
||||||
|
|
||||||
flags.subcommand = DenoSubcommand::Install(InstallFlags {
|
flags.subcommand = DenoSubcommand::Install(InstallFlags {
|
||||||
// TODO(bartlomieju): remove once `deno install` supports both local and
|
// TODO(bartlomieju): remove for 2.0
|
||||||
// global installs
|
global,
|
||||||
global,
|
kind: InstallKind::Global(InstallFlagsGlobal {
|
||||||
kind: InstallKind::Global(InstallFlagsGlobal {
|
name,
|
||||||
name,
|
module_url,
|
||||||
module_url,
|
args,
|
||||||
args,
|
root,
|
||||||
root,
|
force,
|
||||||
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) {
|
fn jupyter_parse(flags: &mut Flags, matches: &mut ArgMatches) {
|
||||||
|
|
|
@ -408,7 +408,8 @@ 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(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 {
|
CliNpmResolverCreateOptions::Byonm(CliNpmResolverByonmCreateOptions {
|
||||||
fs: fs.clone(),
|
fs: fs.clone(),
|
||||||
root_node_modules_dir: match self.options.node_modules_dir_path() {
|
root_node_modules_dir: match self.options.node_modules_dir_path() {
|
||||||
|
|
|
@ -8,6 +8,7 @@ use crate::cache::CodeCache;
|
||||||
use crate::cache::ModuleInfoCache;
|
use crate::cache::ModuleInfoCache;
|
||||||
use crate::cache::ParsedSourceCache;
|
use crate::cache::ParsedSourceCache;
|
||||||
use crate::emit::Emitter;
|
use crate::emit::Emitter;
|
||||||
|
use crate::factory::CliFactory;
|
||||||
use crate::graph_util::graph_lock_or_exit;
|
use crate::graph_util::graph_lock_or_exit;
|
||||||
use crate::graph_util::CreateGraphOptions;
|
use crate::graph_util::CreateGraphOptions;
|
||||||
use crate::graph_util::ModuleGraphBuilder;
|
use crate::graph_util::ModuleGraphBuilder;
|
||||||
|
@ -64,6 +65,40 @@ use std::rc::Rc;
|
||||||
use std::str;
|
use std::str;
|
||||||
use std::sync::Arc;
|
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 {
|
pub struct ModuleLoadPreparer {
|
||||||
options: Arc<CliOptions>,
|
options: Arc<CliOptions>,
|
||||||
graph_container: Arc<ModuleGraphContainer>,
|
graph_container: Arc<ModuleGraphContainer>,
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license.
|
// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license.
|
||||||
|
|
||||||
use crate::args::resolve_no_prompt;
|
use crate::args::resolve_no_prompt;
|
||||||
|
use crate::args::AddFlags;
|
||||||
use crate::args::CaData;
|
use crate::args::CaData;
|
||||||
use crate::args::Flags;
|
use crate::args::Flags;
|
||||||
use crate::args::InstallFlags;
|
use crate::args::InstallFlags;
|
||||||
|
@ -253,6 +254,20 @@ pub fn uninstall(uninstall_flags: UninstallFlags) -> Result<(), AnyError> {
|
||||||
Ok(())
|
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(
|
pub async fn install_command(
|
||||||
flags: Flags,
|
flags: Flags,
|
||||||
install_flags: InstallFlags,
|
install_flags: InstallFlags,
|
||||||
|
@ -263,7 +278,9 @@ pub async fn install_command(
|
||||||
|
|
||||||
let install_flags_global = match install_flags.kind {
|
let install_flags_global = match install_flags.kind {
|
||||||
InstallKind::Global(flags) => flags,
|
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
|
// ensure the module is cached
|
||||||
|
|
|
@ -1,19 +1,23 @@
|
||||||
// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license.
|
// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license.
|
||||||
|
|
||||||
use std::collections::HashMap;
|
use std::borrow::Cow;
|
||||||
use std::path::PathBuf;
|
use std::path::PathBuf;
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
|
||||||
use deno_ast::TextChange;
|
use deno_ast::TextChange;
|
||||||
use deno_config::FmtOptionsConfig;
|
use deno_config::FmtOptionsConfig;
|
||||||
|
use deno_core::anyhow::anyhow;
|
||||||
use deno_core::anyhow::bail;
|
use deno_core::anyhow::bail;
|
||||||
use deno_core::anyhow::Context;
|
use deno_core::anyhow::Context;
|
||||||
use deno_core::error::AnyError;
|
use deno_core::error::AnyError;
|
||||||
use deno_core::futures::FutureExt;
|
use deno_core::futures::FutureExt;
|
||||||
use deno_core::futures::StreamExt;
|
use deno_core::futures::StreamExt;
|
||||||
use deno_core::serde_json;
|
use deno_core::serde_json;
|
||||||
|
use deno_core::ModuleSpecifier;
|
||||||
|
use deno_runtime::deno_node;
|
||||||
use deno_semver::jsr::JsrPackageReqReference;
|
use deno_semver::jsr::JsrPackageReqReference;
|
||||||
use deno_semver::npm::NpmPackageReqReference;
|
use deno_semver::npm::NpmPackageReqReference;
|
||||||
|
use indexmap::IndexMap;
|
||||||
use jsonc_parser::ast::ObjectProp;
|
use jsonc_parser::ast::ObjectProp;
|
||||||
use jsonc_parser::ast::Value;
|
use jsonc_parser::ast::Value;
|
||||||
|
|
||||||
|
@ -25,22 +29,164 @@ use crate::file_fetcher::FileFetcher;
|
||||||
use crate::jsr::JsrFetchResolver;
|
use crate::jsr::JsrFetchResolver;
|
||||||
use crate::npm::NpmFetchResolver;
|
use crate::npm::NpmFetchResolver;
|
||||||
|
|
||||||
|
enum DenoConfigFormat {
|
||||||
|
Json,
|
||||||
|
Jsonc,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl DenoConfigFormat {
|
||||||
|
fn from_specifier(spec: &ModuleSpecifier) -> Result<Self, AnyError> {
|
||||||
|
let file_name = spec
|
||||||
|
.path_segments()
|
||||||
|
.ok_or_else(|| anyhow!("Empty path in deno config specifier: {spec}"))?
|
||||||
|
.last()
|
||||||
|
.unwrap();
|
||||||
|
match file_name {
|
||||||
|
"deno.json" => Ok(Self::Json),
|
||||||
|
"deno.jsonc" => Ok(Self::Jsonc),
|
||||||
|
_ => bail!("Unsupported deno config file: {file_name}"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
enum DenoOrPackageJson {
|
||||||
|
Deno(deno_config::ConfigFile, DenoConfigFormat),
|
||||||
|
Npm(deno_node::PackageJson, Option<FmtOptionsConfig>),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl DenoOrPackageJson {
|
||||||
|
fn specifier(&self) -> Cow<ModuleSpecifier> {
|
||||||
|
match self {
|
||||||
|
Self::Deno(d, ..) => Cow::Borrowed(&d.specifier),
|
||||||
|
Self::Npm(n, ..) => Cow::Owned(n.specifier()),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns the existing imports/dependencies from the config.
|
||||||
|
fn existing_imports(&self) -> Result<IndexMap<String, String>, AnyError> {
|
||||||
|
match self {
|
||||||
|
DenoOrPackageJson::Deno(deno, ..) => {
|
||||||
|
if let Some(imports) = deno.json.imports.clone() {
|
||||||
|
match serde_json::from_value(imports) {
|
||||||
|
Ok(map) => Ok(map),
|
||||||
|
Err(err) => {
|
||||||
|
bail!("Malformed \"imports\" configuration: {err}")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
Ok(Default::default())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
DenoOrPackageJson::Npm(npm, ..) => {
|
||||||
|
Ok(npm.dependencies.clone().unwrap_or_default())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn fmt_options(&self) -> FmtOptionsConfig {
|
||||||
|
match self {
|
||||||
|
DenoOrPackageJson::Deno(deno, ..) => deno
|
||||||
|
.to_fmt_config()
|
||||||
|
.ok()
|
||||||
|
.flatten()
|
||||||
|
.map(|f| f.options)
|
||||||
|
.unwrap_or_default(),
|
||||||
|
DenoOrPackageJson::Npm(_, config) => config.clone().unwrap_or_default(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn imports_key(&self) -> &'static str {
|
||||||
|
match self {
|
||||||
|
DenoOrPackageJson::Deno(..) => "imports",
|
||||||
|
DenoOrPackageJson::Npm(..) => "dependencies",
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn file_name(&self) -> &'static str {
|
||||||
|
match self {
|
||||||
|
DenoOrPackageJson::Deno(_, format) => match format {
|
||||||
|
DenoConfigFormat::Json => "deno.json",
|
||||||
|
DenoConfigFormat::Jsonc => "deno.jsonc",
|
||||||
|
},
|
||||||
|
DenoOrPackageJson::Npm(..) => "package.json",
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn is_npm(&self) -> bool {
|
||||||
|
matches!(self, Self::Npm(..))
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Get the preferred config file to operate on
|
||||||
|
/// given the flags. If no config file is present,
|
||||||
|
/// creates a `deno.json` file - in this case
|
||||||
|
/// we also return a new `CliFactory` that knows about
|
||||||
|
/// the new config
|
||||||
|
fn from_flags(flags: Flags) -> Result<(Self, CliFactory), AnyError> {
|
||||||
|
let factory = CliFactory::from_flags(flags.clone())?;
|
||||||
|
let options = factory.cli_options().clone();
|
||||||
|
|
||||||
|
match (options.maybe_config_file(), options.maybe_package_json()) {
|
||||||
|
// when both are present, for now,
|
||||||
|
// default to deno.json
|
||||||
|
(Some(deno), Some(_) | None) => Ok((
|
||||||
|
DenoOrPackageJson::Deno(
|
||||||
|
deno.clone(),
|
||||||
|
DenoConfigFormat::from_specifier(&deno.specifier)?,
|
||||||
|
),
|
||||||
|
factory,
|
||||||
|
)),
|
||||||
|
(None, Some(package_json)) if options.enable_future_features() => {
|
||||||
|
Ok((DenoOrPackageJson::Npm(package_json.clone(), None), factory))
|
||||||
|
}
|
||||||
|
(None, Some(_) | None) => {
|
||||||
|
std::fs::write(options.initial_cwd().join("deno.json"), "{}\n")
|
||||||
|
.context("Failed to create deno.json file")?;
|
||||||
|
log::info!("Created deno.json configuration file.");
|
||||||
|
let new_factory = CliFactory::from_flags(flags.clone())?;
|
||||||
|
let new_options = new_factory.cli_options().clone();
|
||||||
|
Ok((
|
||||||
|
DenoOrPackageJson::Deno(
|
||||||
|
new_options
|
||||||
|
.maybe_config_file()
|
||||||
|
.as_ref()
|
||||||
|
.ok_or_else(|| {
|
||||||
|
anyhow!("config not found, but it was just created")
|
||||||
|
})?
|
||||||
|
.clone(),
|
||||||
|
DenoConfigFormat::Json,
|
||||||
|
),
|
||||||
|
new_factory,
|
||||||
|
))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn package_json_dependency_entry(
|
||||||
|
selected: SelectedPackage,
|
||||||
|
) -> (String, String) {
|
||||||
|
if let Some(npm_package) = selected.package_name.strip_prefix("npm:") {
|
||||||
|
(npm_package.into(), selected.version_req)
|
||||||
|
} else if let Some(jsr_package) = selected.package_name.strip_prefix("jsr:") {
|
||||||
|
let jsr_package = jsr_package.strip_prefix('@').unwrap_or(jsr_package);
|
||||||
|
let scope_replaced = jsr_package.replace('/', "__");
|
||||||
|
let version_req =
|
||||||
|
format!("npm:@jsr/{scope_replaced}@{}", selected.version_req);
|
||||||
|
(selected.import_name, version_req)
|
||||||
|
} else {
|
||||||
|
(selected.package_name, selected.version_req)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub async fn add(flags: Flags, add_flags: AddFlags) -> Result<(), AnyError> {
|
pub async fn add(flags: Flags, add_flags: AddFlags) -> Result<(), AnyError> {
|
||||||
let cli_factory = CliFactory::from_flags(flags.clone())?;
|
let (config_file, cli_factory) =
|
||||||
let cli_options = cli_factory.cli_options();
|
DenoOrPackageJson::from_flags(flags.clone())?;
|
||||||
|
|
||||||
let Some(config_file) = cli_options.maybe_config_file() else {
|
let config_specifier = config_file.specifier();
|
||||||
tokio::fs::write(cli_options.initial_cwd().join("deno.json"), "{}\n")
|
if config_specifier.scheme() != "file" {
|
||||||
.await
|
|
||||||
.context("Failed to create deno.json file")?;
|
|
||||||
log::info!("Created deno.json configuration file.");
|
|
||||||
return add(flags, add_flags).boxed_local().await;
|
|
||||||
};
|
|
||||||
|
|
||||||
if config_file.specifier.scheme() != "file" {
|
|
||||||
bail!("Can't add dependencies to a remote configuration file");
|
bail!("Can't add dependencies to a remote configuration file");
|
||||||
}
|
}
|
||||||
let config_file_path = config_file.specifier.to_file_path().unwrap();
|
let config_file_path = config_specifier.to_file_path().unwrap();
|
||||||
|
|
||||||
let http_client = cli_factory.http_client();
|
let http_client = cli_factory.http_client();
|
||||||
|
|
||||||
|
@ -49,10 +195,13 @@ pub async fn add(flags: Flags, add_flags: AddFlags) -> Result<(), AnyError> {
|
||||||
|
|
||||||
for package_name in add_flags.packages.iter() {
|
for package_name in add_flags.packages.iter() {
|
||||||
let req = if package_name.starts_with("npm:") {
|
let req = if package_name.starts_with("npm:") {
|
||||||
let pkg_req = NpmPackageReqReference::from_str(package_name)
|
let pkg_req = NpmPackageReqReference::from_str(&format!(
|
||||||
.with_context(|| {
|
"npm:{}",
|
||||||
format!("Failed to parse package required: {}", package_name)
|
package_name.strip_prefix("npm:").unwrap_or(package_name)
|
||||||
})?;
|
))
|
||||||
|
.with_context(|| {
|
||||||
|
format!("Failed to parse package required: {}", package_name)
|
||||||
|
})?;
|
||||||
AddPackageReq::Npm(pkg_req)
|
AddPackageReq::Npm(pkg_req)
|
||||||
} else {
|
} else {
|
||||||
let pkg_req = JsrPackageReqReference::from_str(&format!(
|
let pkg_req = JsrPackageReqReference::from_str(&format!(
|
||||||
|
@ -128,16 +277,9 @@ pub async fn add(flags: Flags, add_flags: AddFlags) -> Result<(), AnyError> {
|
||||||
_ => bail!("Failed updating config file due to no object."),
|
_ => bail!("Failed updating config file due to no object."),
|
||||||
};
|
};
|
||||||
|
|
||||||
let mut existing_imports =
|
let mut existing_imports = config_file.existing_imports()?;
|
||||||
if let Some(imports) = config_file.json.imports.clone() {
|
|
||||||
match serde_json::from_value::<HashMap<String, String>>(imports) {
|
|
||||||
Ok(i) => i,
|
|
||||||
Err(_) => bail!("Malformed \"imports\" configuration"),
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
HashMap::default()
|
|
||||||
};
|
|
||||||
|
|
||||||
|
let is_npm = config_file.is_npm();
|
||||||
for selected_package in selected_packages {
|
for selected_package in selected_packages {
|
||||||
log::info!(
|
log::info!(
|
||||||
"Add {} - {}@{}",
|
"Add {} - {}@{}",
|
||||||
|
@ -145,13 +287,19 @@ pub async fn add(flags: Flags, add_flags: AddFlags) -> Result<(), AnyError> {
|
||||||
selected_package.package_name,
|
selected_package.package_name,
|
||||||
selected_package.version_req
|
selected_package.version_req
|
||||||
);
|
);
|
||||||
existing_imports.insert(
|
|
||||||
selected_package.import_name,
|
if is_npm {
|
||||||
format!(
|
let (name, version) = package_json_dependency_entry(selected_package);
|
||||||
"{}@{}",
|
existing_imports.insert(name, version)
|
||||||
selected_package.package_name, selected_package.version_req
|
} else {
|
||||||
),
|
existing_imports.insert(
|
||||||
);
|
selected_package.import_name,
|
||||||
|
format!(
|
||||||
|
"{}@{}",
|
||||||
|
selected_package.package_name, selected_package.version_req
|
||||||
|
),
|
||||||
|
)
|
||||||
|
};
|
||||||
}
|
}
|
||||||
let mut import_list: Vec<(String, String)> =
|
let mut import_list: Vec<(String, String)> =
|
||||||
existing_imports.into_iter().collect();
|
existing_imports.into_iter().collect();
|
||||||
|
@ -159,25 +307,29 @@ pub async fn add(flags: Flags, add_flags: AddFlags) -> Result<(), AnyError> {
|
||||||
import_list.sort_by(|(k1, _), (k2, _)| k1.cmp(k2));
|
import_list.sort_by(|(k1, _), (k2, _)| k1.cmp(k2));
|
||||||
let generated_imports = generate_imports(import_list);
|
let generated_imports = generate_imports(import_list);
|
||||||
|
|
||||||
let fmt_config_options = config_file
|
let fmt_config_options = config_file.fmt_options();
|
||||||
.to_fmt_config()
|
|
||||||
.ok()
|
|
||||||
.flatten()
|
|
||||||
.map(|config| config.options)
|
|
||||||
.unwrap_or_default();
|
|
||||||
|
|
||||||
let new_text = update_config_file_content(
|
let new_text = update_config_file_content(
|
||||||
obj,
|
obj,
|
||||||
&config_file_contents,
|
&config_file_contents,
|
||||||
generated_imports,
|
generated_imports,
|
||||||
fmt_config_options,
|
fmt_config_options,
|
||||||
|
config_file.imports_key(),
|
||||||
|
config_file.file_name(),
|
||||||
);
|
);
|
||||||
|
|
||||||
tokio::fs::write(&config_file_path, new_text)
|
tokio::fs::write(&config_file_path, new_text)
|
||||||
.await
|
.await
|
||||||
.context("Failed to update configuration file")?;
|
.context("Failed to update configuration file")?;
|
||||||
|
|
||||||
// TODO(bartlomieju): we should now cache the imports from the config file.
|
// TODO(bartlomieju): we should now cache the imports from the deno.json.
|
||||||
|
|
||||||
|
// make a new CliFactory to pick up the updated config file
|
||||||
|
let cli_factory = CliFactory::from_flags(flags)?;
|
||||||
|
// cache deps
|
||||||
|
if cli_factory.cli_options().enable_future_features() {
|
||||||
|
crate::module_loader::load_top_level_deps(&cli_factory).await?;
|
||||||
|
}
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
@ -259,10 +411,12 @@ fn update_config_file_content(
|
||||||
config_file_contents: &str,
|
config_file_contents: &str,
|
||||||
generated_imports: String,
|
generated_imports: String,
|
||||||
fmt_options: FmtOptionsConfig,
|
fmt_options: FmtOptionsConfig,
|
||||||
|
imports_key: &str,
|
||||||
|
file_name: &str,
|
||||||
) -> String {
|
) -> String {
|
||||||
let mut text_changes = vec![];
|
let mut text_changes = vec![];
|
||||||
|
|
||||||
match obj.get("imports") {
|
match obj.get(imports_key) {
|
||||||
Some(ObjectProp {
|
Some(ObjectProp {
|
||||||
value: Value::Object(lit),
|
value: Value::Object(lit),
|
||||||
..
|
..
|
||||||
|
@ -282,10 +436,10 @@ fn update_config_file_content(
|
||||||
// "<package_name>": "<registry>:<package_name>@<semver>"
|
// "<package_name>": "<registry>:<package_name>@<semver>"
|
||||||
// }
|
// }
|
||||||
// }
|
// }
|
||||||
new_text: format!("\"imports\": {{\n {} }}", generated_imports),
|
new_text: format!("\"{imports_key}\": {{\n {generated_imports} }}"),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
// we verified the shape of `imports` above
|
// we verified the shape of `imports`/`dependencies` above
|
||||||
Some(_) => unreachable!(),
|
Some(_) => unreachable!(),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -293,7 +447,7 @@ fn update_config_file_content(
|
||||||
deno_ast::apply_text_changes(config_file_contents, text_changes);
|
deno_ast::apply_text_changes(config_file_contents, text_changes);
|
||||||
|
|
||||||
crate::tools::fmt::format_json(
|
crate::tools::fmt::format_json(
|
||||||
&PathBuf::from("deno.json"),
|
&PathBuf::from(file_name),
|
||||||
&new_text,
|
&new_text,
|
||||||
&fmt_options,
|
&fmt_options,
|
||||||
)
|
)
|
||||||
|
|
16
tests/specs/install/future_install_global/__test__.jsonc
Normal file
16
tests/specs/install/future_install_global/__test__.jsonc
Normal file
|
@ -0,0 +1,16 @@
|
||||||
|
{
|
||||||
|
"tempDir": true,
|
||||||
|
"envs": {
|
||||||
|
"DENO_FUTURE": "1"
|
||||||
|
},
|
||||||
|
"steps": [
|
||||||
|
{
|
||||||
|
"args": "install --global --root ./bins --name deno-test-bin ./main.js",
|
||||||
|
"output": "install.out"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"args": "run -A assert.js",
|
||||||
|
"output": ""
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
11
tests/specs/install/future_install_global/assert.js
Normal file
11
tests/specs/install/future_install_global/assert.js
Normal file
|
@ -0,0 +1,11 @@
|
||||||
|
const dirs = Deno.readDir("./bins/bin");
|
||||||
|
|
||||||
|
let found = false;
|
||||||
|
for await (const entry of dirs) {
|
||||||
|
if (entry.name.includes("deno-test-bin")) {
|
||||||
|
found = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!found) {
|
||||||
|
throw new Error("Failed to find test bin");
|
||||||
|
}
|
5
tests/specs/install/future_install_global/install.out
Normal file
5
tests/specs/install/future_install_global/install.out
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
Download http://localhost:4260/@denotest/esm-basic
|
||||||
|
Download http://localhost:4260/@denotest/esm-basic/1.0.0.tgz
|
||||||
|
Initialize @denotest/esm-basic@1.0.0
|
||||||
|
✅ Successfully installed deno-test-bin[WILDCARD]
|
||||||
|
[WILDCARD]
|
3
tests/specs/install/future_install_global/main.js
Normal file
3
tests/specs/install/future_install_global/main.js
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
import { setValue } from "@denotest/esm-basic";
|
||||||
|
|
||||||
|
setValue(5);
|
7
tests/specs/install/future_install_global/package.json
Normal file
7
tests/specs/install/future_install_global/package.json
Normal file
|
@ -0,0 +1,7 @@
|
||||||
|
{
|
||||||
|
"name": "deno-test-bin",
|
||||||
|
"dependencies": {
|
||||||
|
"@denotest/esm-basic": "*"
|
||||||
|
},
|
||||||
|
"type": "module"
|
||||||
|
}
|
|
@ -0,0 +1,24 @@
|
||||||
|
{
|
||||||
|
"tempDir": true,
|
||||||
|
"envs": {
|
||||||
|
"DENO_FUTURE": "1"
|
||||||
|
},
|
||||||
|
"steps": [
|
||||||
|
{
|
||||||
|
"args": "install npm:@denotest/esm-basic",
|
||||||
|
"output": "install.out"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
// make sure the dep got cached
|
||||||
|
"args": "run --cached-only main.js",
|
||||||
|
"output": ""
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"args": [
|
||||||
|
"eval",
|
||||||
|
"console.log(Deno.readTextFileSync('deno.json').trim())"
|
||||||
|
],
|
||||||
|
"output": "deno.json.out"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
|
@ -0,0 +1 @@
|
||||||
|
{}
|
|
@ -0,0 +1,5 @@
|
||||||
|
{
|
||||||
|
"imports": {
|
||||||
|
"@denotest/esm-basic": "npm:@denotest/esm-basic@^1.0.0"
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,4 @@
|
||||||
|
⚠️ `deno install` behavior will change in Deno 2. To preserve the current behavior use the `-g` or `--global` flag.
|
||||||
|
Add @denotest/esm-basic - npm:@denotest/esm-basic@^1.0.0
|
||||||
|
Download http://localhost:4260/@denotest/esm-basic
|
||||||
|
Download http://localhost:4260/@denotest/esm-basic/1.0.0.tgz
|
|
@ -0,0 +1,2 @@
|
||||||
|
import { setValue } from "@denotest/esm-basic";
|
||||||
|
setValue(5);
|
|
@ -0,0 +1,25 @@
|
||||||
|
{
|
||||||
|
"tempDir": true,
|
||||||
|
"envs": {
|
||||||
|
"DENO_FUTURE": "1"
|
||||||
|
},
|
||||||
|
"steps": [
|
||||||
|
{
|
||||||
|
"args": "install npm:@denotest/esm-basic",
|
||||||
|
"output": "install.out"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
// make sure the dep got cached
|
||||||
|
"args": "run --cached-only main.js",
|
||||||
|
"exitCode": 0,
|
||||||
|
"output": ""
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"args": [
|
||||||
|
"eval",
|
||||||
|
"console.log(Deno.readTextFileSync('package.json').trim())"
|
||||||
|
],
|
||||||
|
"output": "package.json.out"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
|
@ -0,0 +1,5 @@
|
||||||
|
⚠️ `deno install` behavior will change in Deno 2. To preserve the current behavior use the `-g` or `--global` flag.
|
||||||
|
Add @denotest/esm-basic - npm:@denotest/esm-basic@^1.0.0
|
||||||
|
Download http://localhost:4260/@denotest/esm-basic
|
||||||
|
Download http://localhost:4260/@denotest/esm-basic/1.0.0.tgz
|
||||||
|
Initialize @denotest/esm-basic@1.0.0
|
2
tests/specs/install/future_install_local_add_npm/main.js
Normal file
2
tests/specs/install/future_install_local_add_npm/main.js
Normal file
|
@ -0,0 +1,2 @@
|
||||||
|
import { setValue } from "@denotest/esm-basic";
|
||||||
|
setValue(5);
|
|
@ -0,0 +1,3 @@
|
||||||
|
{
|
||||||
|
"dependencies": {}
|
||||||
|
}
|
|
@ -0,0 +1,3 @@
|
||||||
|
{
|
||||||
|
"dependencies": { "@denotest/esm-basic": "^1.0.0" }
|
||||||
|
}
|
17
tests/specs/install/future_install_local_deno/__test__.jsonc
Normal file
17
tests/specs/install/future_install_local_deno/__test__.jsonc
Normal file
|
@ -0,0 +1,17 @@
|
||||||
|
{
|
||||||
|
"tempDir": true,
|
||||||
|
"envs": {
|
||||||
|
"DENO_FUTURE": "1"
|
||||||
|
},
|
||||||
|
"steps": [
|
||||||
|
{
|
||||||
|
"args": "install",
|
||||||
|
"output": "install.out"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
// ensure deps are actually cached
|
||||||
|
"args": "run --cached-only main.js",
|
||||||
|
"output": ""
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
8
tests/specs/install/future_install_local_deno/deno.json
Normal file
8
tests/specs/install/future_install_local_deno/deno.json
Normal file
|
@ -0,0 +1,8 @@
|
||||||
|
{
|
||||||
|
"imports": {
|
||||||
|
"@std/fs/": "https://deno.land/std@0.224.0/fs/",
|
||||||
|
"@denotest/esm-basic": "npm:@denotest/esm-basic@^1.0.0",
|
||||||
|
"@denotest/add": "jsr:@denotest/add",
|
||||||
|
"test-http": "http://localhost:4545/v1/extensionless"
|
||||||
|
}
|
||||||
|
}
|
10
tests/specs/install/future_install_local_deno/install.out
Normal file
10
tests/specs/install/future_install_local_deno/install.out
Normal file
|
@ -0,0 +1,10 @@
|
||||||
|
⚠️ `deno install` behavior will change in Deno 2. To preserve the current behavior use the `-g` or `--global` flag.
|
||||||
|
Download http://localhost:4545/v1/extensionless
|
||||||
|
Download http://localhost:4545/subdir/mod1.ts
|
||||||
|
Download http://localhost:4545/subdir/subdir2/mod2.ts
|
||||||
|
Download http://localhost:4545/subdir/print_hello.ts
|
||||||
|
Download http://127.0.0.1:4250/@denotest/add/meta.json
|
||||||
|
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
|
||||||
|
Download http://localhost:4260/@denotest/esm-basic/1.0.0.tgz
|
6
tests/specs/install/future_install_local_deno/main.js
Normal file
6
tests/specs/install/future_install_local_deno/main.js
Normal file
|
@ -0,0 +1,6 @@
|
||||||
|
import { setValue } from "@denotest/esm-basic";
|
||||||
|
import { add } from "@denotest/add";
|
||||||
|
import { returnsHi } from "test-http";
|
||||||
|
setValue(5);
|
||||||
|
returnsHi();
|
||||||
|
add(2, 2);
|
17
tests/specs/install/future_install_node_modules/__test__.jsonc
generated
Normal file
17
tests/specs/install/future_install_node_modules/__test__.jsonc
generated
Normal file
|
@ -0,0 +1,17 @@
|
||||||
|
{
|
||||||
|
"tempDir": true,
|
||||||
|
"envs": {
|
||||||
|
"DENO_FUTURE": "1"
|
||||||
|
},
|
||||||
|
"steps": [
|
||||||
|
{
|
||||||
|
"args": "install",
|
||||||
|
"output": "install.out"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
// ensure deps are actually cached
|
||||||
|
"args": "run --cached-only main.js",
|
||||||
|
"output": ""
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
4
tests/specs/install/future_install_node_modules/install.out
generated
Normal file
4
tests/specs/install/future_install_node_modules/install.out
generated
Normal file
|
@ -0,0 +1,4 @@
|
||||||
|
⚠️ `deno install` behavior will change in Deno 2. To preserve the current behavior use the `-g` or `--global` flag.
|
||||||
|
Download http://localhost:4260/@denotest/esm-basic
|
||||||
|
Download http://localhost:4260/@denotest/esm-basic/1.0.0.tgz
|
||||||
|
Initialize @denotest/esm-basic@1.0.0
|
3
tests/specs/install/future_install_node_modules/main.js
generated
Normal file
3
tests/specs/install/future_install_node_modules/main.js
generated
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
import { setValue } from "@denotest/esm-basic";
|
||||||
|
|
||||||
|
setValue(5);
|
5
tests/specs/install/future_install_node_modules/package.json
generated
Normal file
5
tests/specs/install/future_install_node_modules/package.json
generated
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
{
|
||||||
|
"dependencies": {
|
||||||
|
"@denotest/esm-basic": "*"
|
||||||
|
}
|
||||||
|
}
|
13
tests/specs/install/no_future_install_global/__test__.jsonc
Normal file
13
tests/specs/install/no_future_install_global/__test__.jsonc
Normal file
|
@ -0,0 +1,13 @@
|
||||||
|
{
|
||||||
|
"tempDir": true,
|
||||||
|
"steps": [
|
||||||
|
{
|
||||||
|
"args": "install --root ./bins --name deno-test-bin ./main.js",
|
||||||
|
"output": "install.out"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"args": "run -A ./assert.js",
|
||||||
|
"output": ""
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
11
tests/specs/install/no_future_install_global/assert.js
Normal file
11
tests/specs/install/no_future_install_global/assert.js
Normal file
|
@ -0,0 +1,11 @@
|
||||||
|
const dirs = Deno.readDir("./bins/bin");
|
||||||
|
|
||||||
|
let found = false;
|
||||||
|
for await (const entry of dirs) {
|
||||||
|
if (entry.name.includes("deno-test-bin")) {
|
||||||
|
found = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!found) {
|
||||||
|
throw new Error("Failed to find test bin");
|
||||||
|
}
|
5
tests/specs/install/no_future_install_global/install.out
Normal file
5
tests/specs/install/no_future_install_global/install.out
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
⚠️ `deno install` behavior will change in Deno 2. To preserve the current behavior use the `-g` or `--global` flag.
|
||||||
|
Download http://localhost:4260/@denotest/esm-basic
|
||||||
|
Download http://localhost:4260/@denotest/esm-basic/1.0.0.tgz
|
||||||
|
✅ Successfully installed deno-test-bin[WILDCARD]
|
||||||
|
[WILDCARD]
|
3
tests/specs/install/no_future_install_global/main.js
Normal file
3
tests/specs/install/no_future_install_global/main.js
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
import { setValue } from "npm:@denotest/esm-basic";
|
||||||
|
|
||||||
|
setValue(5);
|
|
@ -0,0 +1,6 @@
|
||||||
|
{
|
||||||
|
"name": "deno-test-bin",
|
||||||
|
"dependencies": {
|
||||||
|
"@denotest/esm-basic": "*"
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in a new issue