mirror of
https://github.com/denoland/deno.git
synced 2024-11-21 15:04:11 -05:00
feat: add "deno init" subcommand (#15469)
This adds an init subcommand to that creates a project starter similar to cargo init. ``` $ deno init my_project Project initialized Run these commands to get started: cd my_project deno run main.ts deno run main_test.ts $ deno run main.ts Add 2 + 3 5 $ cat main.ts export function add(a: number, b: number): number { return a + b; } if (import.meta.main) { console.log("Add 2 + 3", add(2, 3)); } $ cat main_test.ts import { assertEquals } from "https://deno.land/std@0.151.0/testing/asserts.ts"; import { add } from "./main.ts"; Deno.test(function addTest() { assertEquals(add(2, 3), 5); }); ``` Co-authored-by: Bartek Iwańczuk <biwanczuk@gmail.com>
This commit is contained in:
parent
5beec3f106
commit
1ffbd56164
8 changed files with 234 additions and 0 deletions
|
@ -119,6 +119,11 @@ pub struct FmtFlags {
|
|||
pub prose_wrap: Option<String>,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, Deserialize, Serialize)]
|
||||
pub struct InitFlags {
|
||||
pub dir: Option<String>,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, Deserialize, Serialize)]
|
||||
pub struct InfoFlags {
|
||||
pub json: bool,
|
||||
|
@ -217,6 +222,7 @@ pub enum DenoSubcommand {
|
|||
Doc(DocFlags),
|
||||
Eval(EvalFlags),
|
||||
Fmt(FmtFlags),
|
||||
Init(InitFlags),
|
||||
Info(InfoFlags),
|
||||
Install(InstallFlags),
|
||||
Uninstall(UninstallFlags),
|
||||
|
@ -554,6 +560,7 @@ pub fn flags_from_vec(args: Vec<String>) -> clap::Result<Flags> {
|
|||
Some(("doc", m)) => doc_parse(&mut flags, m),
|
||||
Some(("eval", m)) => eval_parse(&mut flags, m),
|
||||
Some(("fmt", m)) => fmt_parse(&mut flags, m),
|
||||
Some(("init", m)) => init_parse(&mut flags, m),
|
||||
Some(("info", m)) => info_parse(&mut flags, m),
|
||||
Some(("install", m)) => install_parse(&mut flags, m),
|
||||
Some(("lint", m)) => lint_parse(&mut flags, m),
|
||||
|
@ -629,6 +636,7 @@ fn clap_root(version: &str) -> Command {
|
|||
.subcommand(doc_subcommand())
|
||||
.subcommand(eval_subcommand())
|
||||
.subcommand(fmt_subcommand())
|
||||
.subcommand(init_subcommand())
|
||||
.subcommand(info_subcommand())
|
||||
.subcommand(install_subcommand())
|
||||
.subcommand(uninstall_subcommand())
|
||||
|
@ -1140,6 +1148,15 @@ Ignore formatting a file by adding an ignore comment at the top of the file:
|
|||
)
|
||||
}
|
||||
|
||||
fn init_subcommand<'a>() -> Command<'a> {
|
||||
Command::new("init").about("Initialize a new project").arg(
|
||||
Arg::new("dir")
|
||||
.takes_value(true)
|
||||
.required(false)
|
||||
.value_hint(ValueHint::DirPath),
|
||||
)
|
||||
}
|
||||
|
||||
fn info_subcommand<'a>() -> Command<'a> {
|
||||
Command::new("info")
|
||||
.about("Show info about cache or info related to source file")
|
||||
|
@ -2436,6 +2453,12 @@ fn fmt_parse(flags: &mut Flags, matches: &clap::ArgMatches) {
|
|||
});
|
||||
}
|
||||
|
||||
fn init_parse(flags: &mut Flags, matches: &clap::ArgMatches) {
|
||||
flags.subcommand = DenoSubcommand::Init(InitFlags {
|
||||
dir: matches.value_of("dir").map(|f| f.to_string()),
|
||||
});
|
||||
}
|
||||
|
||||
fn info_parse(flags: &mut Flags, matches: &clap::ArgMatches) {
|
||||
reload_arg_parse(flags, matches);
|
||||
config_args_parse(flags, matches);
|
||||
|
@ -5951,4 +5974,27 @@ mod tests {
|
|||
]);
|
||||
assert!(r.is_err());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn init() {
|
||||
let r = flags_from_vec(svec!["deno", "init"]);
|
||||
assert_eq!(
|
||||
r.unwrap(),
|
||||
Flags {
|
||||
subcommand: DenoSubcommand::Init(InitFlags { dir: None }),
|
||||
..Flags::default()
|
||||
}
|
||||
);
|
||||
|
||||
let r = flags_from_vec(svec!["deno", "init", "foo"]);
|
||||
assert_eq!(
|
||||
r.unwrap(),
|
||||
Flags {
|
||||
subcommand: DenoSubcommand::Init(InitFlags {
|
||||
dir: Some(String::from("foo")),
|
||||
}),
|
||||
..Flags::default()
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
12
cli/main.rs
12
cli/main.rs
|
@ -51,6 +51,7 @@ use crate::args::EvalFlags;
|
|||
use crate::args::Flags;
|
||||
use crate::args::FmtFlags;
|
||||
use crate::args::InfoFlags;
|
||||
use crate::args::InitFlags;
|
||||
use crate::args::InstallFlags;
|
||||
use crate::args::LintFlags;
|
||||
use crate::args::ReplFlags;
|
||||
|
@ -273,6 +274,14 @@ async fn compile_command(
|
|||
Ok(0)
|
||||
}
|
||||
|
||||
async fn init_command(
|
||||
_flags: Flags,
|
||||
init_flags: InitFlags,
|
||||
) -> Result<i32, AnyError> {
|
||||
tools::init::init_project(init_flags).await?;
|
||||
Ok(0)
|
||||
}
|
||||
|
||||
async fn info_command(
|
||||
flags: Flags,
|
||||
info_flags: InfoFlags,
|
||||
|
@ -941,6 +950,9 @@ fn get_subcommand(
|
|||
DenoSubcommand::Fmt(fmt_flags) => {
|
||||
format_command(flags, fmt_flags).boxed_local()
|
||||
}
|
||||
DenoSubcommand::Init(init_flags) => {
|
||||
init_command(flags, init_flags).boxed_local()
|
||||
}
|
||||
DenoSubcommand::Info(info_flags) => {
|
||||
info_command(flags, info_flags).boxed_local()
|
||||
}
|
||||
|
|
110
cli/tests/integration/init_tests.rs
Normal file
110
cli/tests/integration/init_tests.rs
Normal file
|
@ -0,0 +1,110 @@
|
|||
// Copyright 2018-2022 the Deno authors. All rights reserved. MIT license.
|
||||
|
||||
use std::process::Stdio;
|
||||
use test_util as util;
|
||||
use test_util::TempDir;
|
||||
use util::assert_contains;
|
||||
|
||||
#[test]
|
||||
fn init_subcommand_without_dir() {
|
||||
let temp_dir = TempDir::new();
|
||||
let cwd = temp_dir.path();
|
||||
let deno_dir = util::new_deno_dir();
|
||||
|
||||
let mut deno_cmd = util::deno_cmd_with_deno_dir(&deno_dir);
|
||||
let output = deno_cmd
|
||||
.current_dir(cwd)
|
||||
.arg("init")
|
||||
.stdout(Stdio::piped())
|
||||
.spawn()
|
||||
.unwrap()
|
||||
.wait_with_output()
|
||||
.unwrap();
|
||||
assert!(output.status.success());
|
||||
let stdout = String::from_utf8(output.stdout).unwrap();
|
||||
assert_contains!(stdout, "Project initialized");
|
||||
assert!(!stdout.contains("cd"));
|
||||
assert_contains!(stdout, "deno run main.ts");
|
||||
assert_contains!(stdout, "deno test");
|
||||
|
||||
let mut deno_cmd = util::deno_cmd_with_deno_dir(&deno_dir);
|
||||
let output = deno_cmd
|
||||
.current_dir(cwd)
|
||||
.env("NO_COLOR", "1")
|
||||
.arg("run")
|
||||
.arg("main.ts")
|
||||
.stdout(Stdio::piped())
|
||||
.spawn()
|
||||
.unwrap()
|
||||
.wait_with_output()
|
||||
.unwrap();
|
||||
assert!(output.status.success());
|
||||
assert_eq!(output.stdout, b"Add 2 + 3 = 5\n");
|
||||
|
||||
let mut deno_cmd = util::deno_cmd_with_deno_dir(&deno_dir);
|
||||
let output = deno_cmd
|
||||
.current_dir(cwd)
|
||||
.env("NO_COLOR", "1")
|
||||
.arg("test")
|
||||
.stdout(Stdio::piped())
|
||||
.spawn()
|
||||
.unwrap()
|
||||
.wait_with_output()
|
||||
.unwrap();
|
||||
assert!(output.status.success());
|
||||
let stdout = String::from_utf8(output.stdout).unwrap();
|
||||
assert_contains!(stdout, "1 passed");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn init_subcommand_with_dir_arg() {
|
||||
let temp_dir = TempDir::new();
|
||||
let cwd = temp_dir.path();
|
||||
let deno_dir = util::new_deno_dir();
|
||||
|
||||
let mut deno_cmd = util::deno_cmd_with_deno_dir(&deno_dir);
|
||||
let output = deno_cmd
|
||||
.current_dir(cwd)
|
||||
.arg("init")
|
||||
.arg("my_dir")
|
||||
.stdout(Stdio::piped())
|
||||
.spawn()
|
||||
.unwrap()
|
||||
.wait_with_output()
|
||||
.unwrap();
|
||||
assert!(output.status.success());
|
||||
let stdout = String::from_utf8(output.stdout).unwrap();
|
||||
assert_contains!(stdout, "Project initialized");
|
||||
assert_contains!(stdout, "cd my_dir");
|
||||
assert_contains!(stdout, "deno run main.ts");
|
||||
assert_contains!(stdout, "deno test");
|
||||
|
||||
let mut deno_cmd = util::deno_cmd_with_deno_dir(&deno_dir);
|
||||
let output = deno_cmd
|
||||
.current_dir(cwd)
|
||||
.env("NO_COLOR", "1")
|
||||
.arg("run")
|
||||
.arg("my_dir/main.ts")
|
||||
.stdout(Stdio::piped())
|
||||
.spawn()
|
||||
.unwrap()
|
||||
.wait_with_output()
|
||||
.unwrap();
|
||||
assert!(output.status.success());
|
||||
assert_eq!(output.stdout, b"Add 2 + 3 = 5\n");
|
||||
|
||||
let mut deno_cmd = util::deno_cmd_with_deno_dir(&deno_dir);
|
||||
let output = deno_cmd
|
||||
.current_dir(cwd)
|
||||
.env("NO_COLOR", "1")
|
||||
.arg("test")
|
||||
.arg("my_dir/main_test.ts")
|
||||
.stdout(Stdio::piped())
|
||||
.spawn()
|
||||
.unwrap()
|
||||
.wait_with_output()
|
||||
.unwrap();
|
||||
assert!(output.status.success());
|
||||
let stdout = String::from_utf8(output.stdout).unwrap();
|
||||
assert_contains!(stdout, "1 passed");
|
||||
}
|
|
@ -74,6 +74,8 @@ mod eval;
|
|||
mod fmt;
|
||||
#[path = "info_tests.rs"]
|
||||
mod info;
|
||||
#[path = "init_tests.rs"]
|
||||
mod init;
|
||||
#[path = "inspector_tests.rs"]
|
||||
mod inspector;
|
||||
#[path = "install_tests.rs"]
|
||||
|
|
49
cli/tools/init/mod.rs
Normal file
49
cli/tools/init/mod.rs
Normal file
|
@ -0,0 +1,49 @@
|
|||
// Copyright 2018-2022 the Deno authors. All rights reserved. MIT license.
|
||||
|
||||
use crate::args::InitFlags;
|
||||
use crate::compat;
|
||||
use deno_core::{anyhow::Context, error::AnyError};
|
||||
use std::io::Write;
|
||||
use std::path::Path;
|
||||
|
||||
fn create_file(
|
||||
dir: &Path,
|
||||
filename: &str,
|
||||
content: &str,
|
||||
) -> Result<(), AnyError> {
|
||||
let mut file = std::fs::OpenOptions::new()
|
||||
.write(true)
|
||||
.create_new(true)
|
||||
.open(dir.join(filename))
|
||||
.with_context(|| format!("Failed to create {} file", filename))?;
|
||||
file.write_all(content.as_bytes())?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub async fn init_project(init_flags: InitFlags) -> Result<(), AnyError> {
|
||||
let cwd =
|
||||
std::env::current_dir().context("Can't read current working directory.")?;
|
||||
let dir = if let Some(dir) = &init_flags.dir {
|
||||
let dir = cwd.join(dir);
|
||||
std::fs::create_dir_all(&dir)?;
|
||||
dir
|
||||
} else {
|
||||
cwd
|
||||
};
|
||||
|
||||
let main_ts = include_str!("./templates/main.ts");
|
||||
create_file(&dir, "main.ts", main_ts)?;
|
||||
|
||||
let main_test_ts = include_str!("./templates/main_test.ts")
|
||||
.replace("{CURRENT_STD_URL}", compat::STD_URL_STR);
|
||||
create_file(&dir, "main_test.ts", &main_test_ts)?;
|
||||
|
||||
println!("✅ Project initialized");
|
||||
println!("Run these commands to get started");
|
||||
if let Some(dir) = init_flags.dir {
|
||||
println!(" cd {}", dir);
|
||||
}
|
||||
println!(" deno run main.ts");
|
||||
println!(" deno test");
|
||||
Ok(())
|
||||
}
|
8
cli/tools/init/templates/main.ts
Normal file
8
cli/tools/init/templates/main.ts
Normal file
|
@ -0,0 +1,8 @@
|
|||
export function add(a: number, b: number): number {
|
||||
return a + b;
|
||||
}
|
||||
|
||||
// Learn more at https://deno.land/manual/examples/module_metadata#concepts
|
||||
if (import.meta.main) {
|
||||
console.log("Add 2 + 3 =", add(2, 3));
|
||||
}
|
6
cli/tools/init/templates/main_test.ts
Normal file
6
cli/tools/init/templates/main_test.ts
Normal file
|
@ -0,0 +1,6 @@
|
|||
import { assertEquals } from "{CURRENT_STD_URL}testing/asserts.ts";
|
||||
import { add } from "./main.ts";
|
||||
|
||||
Deno.test(function addTest() {
|
||||
assertEquals(add(2, 3), 5);
|
||||
});
|
|
@ -4,6 +4,7 @@ pub mod bench;
|
|||
pub mod coverage;
|
||||
pub mod doc;
|
||||
pub mod fmt;
|
||||
pub mod init;
|
||||
pub mod installer;
|
||||
pub mod lint;
|
||||
pub mod repl;
|
||||
|
|
Loading…
Reference in a new issue