mirror of
https://github.com/denoland/deno.git
synced 2024-11-22 15:06:54 -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>,
|
pub prose_wrap: Option<String>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Debug, PartialEq, Deserialize, Serialize)]
|
||||||
|
pub struct InitFlags {
|
||||||
|
pub dir: Option<String>,
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug, PartialEq, Deserialize, Serialize)]
|
#[derive(Clone, Debug, PartialEq, Deserialize, Serialize)]
|
||||||
pub struct InfoFlags {
|
pub struct InfoFlags {
|
||||||
pub json: bool,
|
pub json: bool,
|
||||||
|
@ -217,6 +222,7 @@ pub enum DenoSubcommand {
|
||||||
Doc(DocFlags),
|
Doc(DocFlags),
|
||||||
Eval(EvalFlags),
|
Eval(EvalFlags),
|
||||||
Fmt(FmtFlags),
|
Fmt(FmtFlags),
|
||||||
|
Init(InitFlags),
|
||||||
Info(InfoFlags),
|
Info(InfoFlags),
|
||||||
Install(InstallFlags),
|
Install(InstallFlags),
|
||||||
Uninstall(UninstallFlags),
|
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(("doc", m)) => doc_parse(&mut flags, m),
|
||||||
Some(("eval", m)) => eval_parse(&mut flags, m),
|
Some(("eval", m)) => eval_parse(&mut flags, m),
|
||||||
Some(("fmt", m)) => fmt_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(("info", m)) => info_parse(&mut flags, m),
|
||||||
Some(("install", m)) => install_parse(&mut flags, m),
|
Some(("install", m)) => install_parse(&mut flags, m),
|
||||||
Some(("lint", m)) => lint_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(doc_subcommand())
|
||||||
.subcommand(eval_subcommand())
|
.subcommand(eval_subcommand())
|
||||||
.subcommand(fmt_subcommand())
|
.subcommand(fmt_subcommand())
|
||||||
|
.subcommand(init_subcommand())
|
||||||
.subcommand(info_subcommand())
|
.subcommand(info_subcommand())
|
||||||
.subcommand(install_subcommand())
|
.subcommand(install_subcommand())
|
||||||
.subcommand(uninstall_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> {
|
fn info_subcommand<'a>() -> Command<'a> {
|
||||||
Command::new("info")
|
Command::new("info")
|
||||||
.about("Show info about cache or info related to source file")
|
.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) {
|
fn info_parse(flags: &mut Flags, matches: &clap::ArgMatches) {
|
||||||
reload_arg_parse(flags, matches);
|
reload_arg_parse(flags, matches);
|
||||||
config_args_parse(flags, matches);
|
config_args_parse(flags, matches);
|
||||||
|
@ -5951,4 +5974,27 @@ mod tests {
|
||||||
]);
|
]);
|
||||||
assert!(r.is_err());
|
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::Flags;
|
||||||
use crate::args::FmtFlags;
|
use crate::args::FmtFlags;
|
||||||
use crate::args::InfoFlags;
|
use crate::args::InfoFlags;
|
||||||
|
use crate::args::InitFlags;
|
||||||
use crate::args::InstallFlags;
|
use crate::args::InstallFlags;
|
||||||
use crate::args::LintFlags;
|
use crate::args::LintFlags;
|
||||||
use crate::args::ReplFlags;
|
use crate::args::ReplFlags;
|
||||||
|
@ -273,6 +274,14 @@ async fn compile_command(
|
||||||
Ok(0)
|
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(
|
async fn info_command(
|
||||||
flags: Flags,
|
flags: Flags,
|
||||||
info_flags: InfoFlags,
|
info_flags: InfoFlags,
|
||||||
|
@ -941,6 +950,9 @@ fn get_subcommand(
|
||||||
DenoSubcommand::Fmt(fmt_flags) => {
|
DenoSubcommand::Fmt(fmt_flags) => {
|
||||||
format_command(flags, fmt_flags).boxed_local()
|
format_command(flags, fmt_flags).boxed_local()
|
||||||
}
|
}
|
||||||
|
DenoSubcommand::Init(init_flags) => {
|
||||||
|
init_command(flags, init_flags).boxed_local()
|
||||||
|
}
|
||||||
DenoSubcommand::Info(info_flags) => {
|
DenoSubcommand::Info(info_flags) => {
|
||||||
info_command(flags, info_flags).boxed_local()
|
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;
|
mod fmt;
|
||||||
#[path = "info_tests.rs"]
|
#[path = "info_tests.rs"]
|
||||||
mod info;
|
mod info;
|
||||||
|
#[path = "init_tests.rs"]
|
||||||
|
mod init;
|
||||||
#[path = "inspector_tests.rs"]
|
#[path = "inspector_tests.rs"]
|
||||||
mod inspector;
|
mod inspector;
|
||||||
#[path = "install_tests.rs"]
|
#[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 coverage;
|
||||||
pub mod doc;
|
pub mod doc;
|
||||||
pub mod fmt;
|
pub mod fmt;
|
||||||
|
pub mod init;
|
||||||
pub mod installer;
|
pub mod installer;
|
||||||
pub mod lint;
|
pub mod lint;
|
||||||
pub mod repl;
|
pub mod repl;
|
||||||
|
|
Loading…
Reference in a new issue