mirror of
https://github.com/denoland/deno.git
synced 2024-11-21 20:38:55 +00:00
feat(init): add --npm flag to initialize npm projects (#26896)
This commit adds support for `deno init --npm <package>`. Running this will actually call to `npm:create-<package>` package that is equivalent to running `npm create <package>`. User will be prompted if they want to allow all permissions and lifecycle scripts to be executed. Closes https://github.com/denoland/deno/issues/26461 --------- Signed-off-by: Bartek Iwańczuk <biwanczuk@gmail.com> Co-authored-by: crowlkats <crowlkats@toaxl.com> Co-authored-by: David Sherret <dsherret@users.noreply.github.com>
This commit is contained in:
parent
f0b245c8ee
commit
d17f4590a2
@ -222,6 +222,8 @@ impl FmtFlags {
|
|||||||
|
|
||||||
#[derive(Clone, Debug, Eq, PartialEq)]
|
#[derive(Clone, Debug, Eq, PartialEq)]
|
||||||
pub struct InitFlags {
|
pub struct InitFlags {
|
||||||
|
pub package: Option<String>,
|
||||||
|
pub package_args: Vec<String>,
|
||||||
pub dir: Option<String>,
|
pub dir: Option<String>,
|
||||||
pub lib: bool,
|
pub lib: bool,
|
||||||
pub serve: bool,
|
pub serve: bool,
|
||||||
@ -1395,7 +1397,7 @@ pub fn flags_from_vec(args: Vec<OsString>) -> clap::error::Result<Flags> {
|
|||||||
"doc" => doc_parse(&mut flags, &mut m)?,
|
"doc" => doc_parse(&mut flags, &mut m)?,
|
||||||
"eval" => eval_parse(&mut flags, &mut m)?,
|
"eval" => eval_parse(&mut flags, &mut m)?,
|
||||||
"fmt" => fmt_parse(&mut flags, &mut m)?,
|
"fmt" => fmt_parse(&mut flags, &mut m)?,
|
||||||
"init" => init_parse(&mut flags, &mut m),
|
"init" => init_parse(&mut flags, &mut m)?,
|
||||||
"info" => info_parse(&mut flags, &mut m)?,
|
"info" => info_parse(&mut flags, &mut m)?,
|
||||||
"install" => install_parse(&mut flags, &mut m)?,
|
"install" => install_parse(&mut flags, &mut m)?,
|
||||||
"json_reference" => json_reference_parse(&mut flags, &mut m, app),
|
"json_reference" => json_reference_parse(&mut flags, &mut m, app),
|
||||||
@ -2448,7 +2450,19 @@ fn init_subcommand() -> Command {
|
|||||||
command("init", "scaffolds a basic Deno project with a script, test, and configuration file", UnstableArgsConfig::None).defer(
|
command("init", "scaffolds a basic Deno project with a script, test, and configuration file", UnstableArgsConfig::None).defer(
|
||||||
|cmd| {
|
|cmd| {
|
||||||
cmd
|
cmd
|
||||||
.arg(Arg::new("dir").value_hint(ValueHint::DirPath))
|
.arg(Arg::new("args")
|
||||||
|
.num_args(0..)
|
||||||
|
.action(ArgAction::Append)
|
||||||
|
.value_name("DIRECTORY OR PACKAGE")
|
||||||
|
.trailing_var_arg(true)
|
||||||
|
)
|
||||||
|
.arg(
|
||||||
|
Arg::new("npm")
|
||||||
|
.long("npm")
|
||||||
|
.help("Generate a npm create-* project")
|
||||||
|
.conflicts_with_all(["lib", "serve"])
|
||||||
|
.action(ArgAction::SetTrue),
|
||||||
|
)
|
||||||
.arg(
|
.arg(
|
||||||
Arg::new("lib")
|
Arg::new("lib")
|
||||||
.long("lib")
|
.long("lib")
|
||||||
@ -4820,12 +4834,44 @@ fn fmt_parse(
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn init_parse(flags: &mut Flags, matches: &mut ArgMatches) {
|
fn init_parse(
|
||||||
|
flags: &mut Flags,
|
||||||
|
matches: &mut ArgMatches,
|
||||||
|
) -> Result<(), clap::Error> {
|
||||||
|
let mut lib = matches.get_flag("lib");
|
||||||
|
let mut serve = matches.get_flag("serve");
|
||||||
|
let mut dir = None;
|
||||||
|
let mut package = None;
|
||||||
|
let mut package_args = vec![];
|
||||||
|
|
||||||
|
if let Some(mut args) = matches.remove_many::<String>("args") {
|
||||||
|
let name = args.next().unwrap();
|
||||||
|
let mut args = args.collect::<Vec<_>>();
|
||||||
|
|
||||||
|
if matches.get_flag("npm") {
|
||||||
|
package = Some(name);
|
||||||
|
package_args = args;
|
||||||
|
} else {
|
||||||
|
dir = Some(name);
|
||||||
|
|
||||||
|
if !args.is_empty() {
|
||||||
|
args.insert(0, "init".to_string());
|
||||||
|
let inner_matches = init_subcommand().try_get_matches_from_mut(args)?;
|
||||||
|
lib = inner_matches.get_flag("lib");
|
||||||
|
serve = inner_matches.get_flag("serve");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
flags.subcommand = DenoSubcommand::Init(InitFlags {
|
flags.subcommand = DenoSubcommand::Init(InitFlags {
|
||||||
dir: matches.remove_one::<String>("dir"),
|
package,
|
||||||
lib: matches.get_flag("lib"),
|
package_args,
|
||||||
serve: matches.get_flag("serve"),
|
dir,
|
||||||
|
lib,
|
||||||
|
serve,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn info_parse(
|
fn info_parse(
|
||||||
@ -10907,6 +10953,8 @@ mod tests {
|
|||||||
r.unwrap(),
|
r.unwrap(),
|
||||||
Flags {
|
Flags {
|
||||||
subcommand: DenoSubcommand::Init(InitFlags {
|
subcommand: DenoSubcommand::Init(InitFlags {
|
||||||
|
package: None,
|
||||||
|
package_args: vec![],
|
||||||
dir: None,
|
dir: None,
|
||||||
lib: false,
|
lib: false,
|
||||||
serve: false,
|
serve: false,
|
||||||
@ -10920,6 +10968,8 @@ mod tests {
|
|||||||
r.unwrap(),
|
r.unwrap(),
|
||||||
Flags {
|
Flags {
|
||||||
subcommand: DenoSubcommand::Init(InitFlags {
|
subcommand: DenoSubcommand::Init(InitFlags {
|
||||||
|
package: None,
|
||||||
|
package_args: vec![],
|
||||||
dir: Some(String::from("foo")),
|
dir: Some(String::from("foo")),
|
||||||
lib: false,
|
lib: false,
|
||||||
serve: false,
|
serve: false,
|
||||||
@ -10933,6 +10983,8 @@ mod tests {
|
|||||||
r.unwrap(),
|
r.unwrap(),
|
||||||
Flags {
|
Flags {
|
||||||
subcommand: DenoSubcommand::Init(InitFlags {
|
subcommand: DenoSubcommand::Init(InitFlags {
|
||||||
|
package: None,
|
||||||
|
package_args: vec![],
|
||||||
dir: None,
|
dir: None,
|
||||||
lib: false,
|
lib: false,
|
||||||
serve: false,
|
serve: false,
|
||||||
@ -10947,6 +10999,8 @@ mod tests {
|
|||||||
r.unwrap(),
|
r.unwrap(),
|
||||||
Flags {
|
Flags {
|
||||||
subcommand: DenoSubcommand::Init(InitFlags {
|
subcommand: DenoSubcommand::Init(InitFlags {
|
||||||
|
package: None,
|
||||||
|
package_args: vec![],
|
||||||
dir: None,
|
dir: None,
|
||||||
lib: true,
|
lib: true,
|
||||||
serve: false,
|
serve: false,
|
||||||
@ -10960,6 +11014,8 @@ mod tests {
|
|||||||
r.unwrap(),
|
r.unwrap(),
|
||||||
Flags {
|
Flags {
|
||||||
subcommand: DenoSubcommand::Init(InitFlags {
|
subcommand: DenoSubcommand::Init(InitFlags {
|
||||||
|
package: None,
|
||||||
|
package_args: vec![],
|
||||||
dir: None,
|
dir: None,
|
||||||
lib: false,
|
lib: false,
|
||||||
serve: true,
|
serve: true,
|
||||||
@ -10973,6 +11029,8 @@ mod tests {
|
|||||||
r.unwrap(),
|
r.unwrap(),
|
||||||
Flags {
|
Flags {
|
||||||
subcommand: DenoSubcommand::Init(InitFlags {
|
subcommand: DenoSubcommand::Init(InitFlags {
|
||||||
|
package: None,
|
||||||
|
package_args: vec![],
|
||||||
dir: Some(String::from("foo")),
|
dir: Some(String::from("foo")),
|
||||||
lib: true,
|
lib: true,
|
||||||
serve: false,
|
serve: false,
|
||||||
@ -10980,6 +11038,57 @@ mod tests {
|
|||||||
..Flags::default()
|
..Flags::default()
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
|
let r = flags_from_vec(svec!["deno", "init", "--lib", "--npm", "vite"]);
|
||||||
|
assert!(r.is_err());
|
||||||
|
|
||||||
|
let r = flags_from_vec(svec!["deno", "init", "--serve", "--npm", "vite"]);
|
||||||
|
assert!(r.is_err());
|
||||||
|
|
||||||
|
let r = flags_from_vec(svec!["deno", "init", "--npm", "vite", "--lib"]);
|
||||||
|
assert_eq!(
|
||||||
|
r.unwrap(),
|
||||||
|
Flags {
|
||||||
|
subcommand: DenoSubcommand::Init(InitFlags {
|
||||||
|
package: Some("vite".to_string()),
|
||||||
|
package_args: svec!["--lib"],
|
||||||
|
dir: None,
|
||||||
|
lib: false,
|
||||||
|
serve: false,
|
||||||
|
}),
|
||||||
|
..Flags::default()
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
let r = flags_from_vec(svec!["deno", "init", "--npm", "vite", "--serve"]);
|
||||||
|
assert_eq!(
|
||||||
|
r.unwrap(),
|
||||||
|
Flags {
|
||||||
|
subcommand: DenoSubcommand::Init(InitFlags {
|
||||||
|
package: Some("vite".to_string()),
|
||||||
|
package_args: svec!["--serve"],
|
||||||
|
dir: None,
|
||||||
|
lib: false,
|
||||||
|
serve: false,
|
||||||
|
}),
|
||||||
|
..Flags::default()
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
let r = flags_from_vec(svec!["deno", "init", "--npm", "vite", "new_dir"]);
|
||||||
|
assert_eq!(
|
||||||
|
r.unwrap(),
|
||||||
|
Flags {
|
||||||
|
subcommand: DenoSubcommand::Init(InitFlags {
|
||||||
|
package: Some("vite".to_string()),
|
||||||
|
package_args: svec!["new_dir"],
|
||||||
|
dir: None,
|
||||||
|
lib: false,
|
||||||
|
serve: false,
|
||||||
|
}),
|
||||||
|
..Flags::default()
|
||||||
|
}
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
@ -1628,9 +1628,10 @@ impl CliOptions {
|
|||||||
DenoSubcommand::Install(_)
|
DenoSubcommand::Install(_)
|
||||||
| DenoSubcommand::Add(_)
|
| DenoSubcommand::Add(_)
|
||||||
| DenoSubcommand::Remove(_)
|
| DenoSubcommand::Remove(_)
|
||||||
|
| DenoSubcommand::Init(_)
|
||||||
| DenoSubcommand::Outdated(_)
|
| DenoSubcommand::Outdated(_)
|
||||||
) {
|
) {
|
||||||
// For `deno install/add/remove` we want to force the managed resolver so it can set up `node_modules/` directory.
|
// For `deno install/add/remove/init` we want to force the managed resolver so it can set up `node_modules/` directory.
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
if self.node_modules_dir().ok().flatten().is_none()
|
if self.node_modules_dir().ok().flatten().is_none()
|
||||||
|
@ -144,9 +144,7 @@ async fn run_subcommand(flags: Arc<Flags>) -> Result<i32, AnyError> {
|
|||||||
}
|
}
|
||||||
DenoSubcommand::Init(init_flags) => {
|
DenoSubcommand::Init(init_flags) => {
|
||||||
spawn_subcommand(async {
|
spawn_subcommand(async {
|
||||||
// make compiler happy since init_project is sync
|
tools::init::init_project(init_flags).await
|
||||||
tokio::task::yield_now().await;
|
|
||||||
tools::init::init_project(init_flags)
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
DenoSubcommand::Info(info_flags) => {
|
DenoSubcommand::Info(info_flags) => {
|
||||||
|
@ -1,15 +1,28 @@
|
|||||||
// 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::DenoSubcommand;
|
||||||
|
use crate::args::Flags;
|
||||||
use crate::args::InitFlags;
|
use crate::args::InitFlags;
|
||||||
|
use crate::args::PackagesAllowedScripts;
|
||||||
|
use crate::args::PermissionFlags;
|
||||||
|
use crate::args::RunFlags;
|
||||||
use crate::colors;
|
use crate::colors;
|
||||||
|
use color_print::cformat;
|
||||||
|
use color_print::cstr;
|
||||||
use deno_core::anyhow::Context;
|
use deno_core::anyhow::Context;
|
||||||
use deno_core::error::AnyError;
|
use deno_core::error::AnyError;
|
||||||
use deno_core::serde_json::json;
|
use deno_core::serde_json::json;
|
||||||
|
use deno_runtime::WorkerExecutionMode;
|
||||||
use log::info;
|
use log::info;
|
||||||
|
use std::io::IsTerminal;
|
||||||
use std::io::Write;
|
use std::io::Write;
|
||||||
use std::path::Path;
|
use std::path::Path;
|
||||||
|
|
||||||
pub fn init_project(init_flags: InitFlags) -> Result<(), AnyError> {
|
pub async fn init_project(init_flags: InitFlags) -> Result<i32, AnyError> {
|
||||||
|
if let Some(package) = &init_flags.package {
|
||||||
|
return init_npm(package, init_flags.package_args).await;
|
||||||
|
}
|
||||||
|
|
||||||
let cwd =
|
let cwd =
|
||||||
std::env::current_dir().context("Can't read current working directory.")?;
|
std::env::current_dir().context("Can't read current working directory.")?;
|
||||||
let dir = if let Some(dir) = &init_flags.dir {
|
let dir = if let Some(dir) = &init_flags.dir {
|
||||||
@ -235,7 +248,58 @@ Deno.test(function addTest() {
|
|||||||
info!(" {}", colors::gray("# Run the tests"));
|
info!(" {}", colors::gray("# Run the tests"));
|
||||||
info!(" deno test");
|
info!(" deno test");
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(0)
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn init_npm(name: &str, args: Vec<String>) -> Result<i32, AnyError> {
|
||||||
|
let script_name = format!("npm:create-{}", name);
|
||||||
|
|
||||||
|
fn print_manual_usage(script_name: &str, args: &[String]) -> i32 {
|
||||||
|
log::info!("{}", cformat!("You can initialize project manually by running <u>deno run {} {}</> and applying desired permissions.", script_name, args.join(" ")));
|
||||||
|
1
|
||||||
|
}
|
||||||
|
|
||||||
|
if std::io::stdin().is_terminal() {
|
||||||
|
log::info!(
|
||||||
|
cstr!("⚠️ Do you fully trust <y>{}</> package? Deno will invoke code from it with all permissions. Do you want to continue? <p(245)>[y/n]</>"),
|
||||||
|
script_name
|
||||||
|
);
|
||||||
|
loop {
|
||||||
|
let _ = std::io::stdout().write(b"> ")?;
|
||||||
|
std::io::stdout().flush()?;
|
||||||
|
let mut answer = String::new();
|
||||||
|
if std::io::stdin().read_line(&mut answer).is_ok() {
|
||||||
|
let answer = answer.trim().to_ascii_lowercase();
|
||||||
|
if answer != "y" {
|
||||||
|
return Ok(print_manual_usage(&script_name, &args));
|
||||||
|
} else {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return Ok(print_manual_usage(&script_name, &args));
|
||||||
|
}
|
||||||
|
|
||||||
|
let new_flags = Flags {
|
||||||
|
permissions: PermissionFlags {
|
||||||
|
allow_all: true,
|
||||||
|
..Default::default()
|
||||||
|
},
|
||||||
|
allow_scripts: PackagesAllowedScripts::All,
|
||||||
|
argv: args,
|
||||||
|
subcommand: DenoSubcommand::Run(RunFlags {
|
||||||
|
script: script_name,
|
||||||
|
..Default::default()
|
||||||
|
}),
|
||||||
|
..Default::default()
|
||||||
|
};
|
||||||
|
crate::tools::run::run_script(
|
||||||
|
WorkerExecutionMode::Run,
|
||||||
|
new_flags.into(),
|
||||||
|
None,
|
||||||
|
)
|
||||||
|
.await
|
||||||
}
|
}
|
||||||
|
|
||||||
fn create_json_file(
|
fn create_json_file(
|
||||||
|
6
tests/specs/init/npm/__test__.jsonc
Normal file
6
tests/specs/init/npm/__test__.jsonc
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
{
|
||||||
|
"tempDir": true,
|
||||||
|
"args": "init --npm vite my-project",
|
||||||
|
"output": "init.out",
|
||||||
|
"exitCode": 1
|
||||||
|
}
|
1
tests/specs/init/npm/init.out
Normal file
1
tests/specs/init/npm/init.out
Normal file
@ -0,0 +1 @@
|
|||||||
|
You can initialize project manually by running deno run npm:create-vite my-project and applying desired permissions.
|
Loading…
Reference in New Issue
Block a user