1
0
Fork 0
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:
Leo Kettmeir 2022-08-20 01:37:05 +02:00 committed by GitHub
parent 5beec3f106
commit 1ffbd56164
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
8 changed files with 234 additions and 0 deletions

View file

@ -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()
}
);
}
} }

View file

@ -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()
} }

View 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");
}

View file

@ -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
View 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(())
}

View 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));
}

View 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);
});

View file

@ -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;