From ff5163af05d95409cbb3d1a13f49a43fefd4849a Mon Sep 17 00:00:00 2001 From: muddlebee Date: Wed, 10 Jul 2024 06:43:34 +0530 Subject: [PATCH] feat(cli): `deno init --lib` (#22499) Closes #22287 Co-authored-by: Asher Gomez Co-authored-by: David Sherret --- cli/args/flags.rs | 54 +++++++- cli/tools/init/mod.rs | 178 +++++++++++++++++++------- cli/tools/init/templates/deno.json | 5 - cli/tools/init/templates/main.ts | 8 -- cli/tools/init/templates/main_test.ts | 6 - tests/specs/init/lib/__test__.jsonc | 16 +++ tests/specs/init/lib/dry_publish.out | 7 + tests/specs/init/lib/init.out | 14 ++ tests/specs/init/lib/test.out | 14 ++ 9 files changed, 233 insertions(+), 69 deletions(-) delete mode 100644 cli/tools/init/templates/deno.json delete mode 100644 cli/tools/init/templates/main.ts delete mode 100644 cli/tools/init/templates/main_test.ts create mode 100644 tests/specs/init/lib/__test__.jsonc create mode 100644 tests/specs/init/lib/dry_publish.out create mode 100644 tests/specs/init/lib/init.out create mode 100644 tests/specs/init/lib/test.out diff --git a/cli/args/flags.rs b/cli/args/flags.rs index 89f53d5937..d2321f32e5 100644 --- a/cli/args/flags.rs +++ b/cli/args/flags.rs @@ -205,6 +205,7 @@ impl FmtFlags { #[derive(Clone, Debug, Eq, PartialEq)] pub struct InitFlags { pub dir: Option, + pub lib: bool, } #[derive(Clone, Debug, Eq, PartialEq)] @@ -2052,11 +2053,18 @@ fn init_subcommand() -> Command { Command::new("init") .about("Initialize a new project") .defer(|cmd| { - cmd.arg( - Arg::new("dir") - .required(false) - .value_hint(ValueHint::DirPath), - ) + cmd + .arg( + Arg::new("dir") + .required(false) + .value_hint(ValueHint::DirPath), + ) + .arg( + Arg::new("lib") + .long("lib") + .required(false) + .action(ArgAction::SetTrue), + ) }) } @@ -4033,6 +4041,7 @@ fn fmt_parse(flags: &mut Flags, matches: &mut ArgMatches) { fn init_parse(flags: &mut Flags, matches: &mut ArgMatches) { flags.subcommand = DenoSubcommand::Init(InitFlags { dir: matches.remove_one::("dir"), + lib: matches.get_flag("lib"), }); } @@ -9753,7 +9762,10 @@ mod tests { assert_eq!( r.unwrap(), Flags { - subcommand: DenoSubcommand::Init(InitFlags { dir: None }), + subcommand: DenoSubcommand::Init(InitFlags { + dir: None, + lib: false + }), ..Flags::default() } ); @@ -9764,6 +9776,7 @@ mod tests { Flags { subcommand: DenoSubcommand::Init(InitFlags { dir: Some(String::from("foo")), + lib: false }), ..Flags::default() } @@ -9773,11 +9786,38 @@ mod tests { assert_eq!( r.unwrap(), Flags { - subcommand: DenoSubcommand::Init(InitFlags { dir: None }), + subcommand: DenoSubcommand::Init(InitFlags { + dir: None, + lib: false + }), log_level: Some(Level::Error), ..Flags::default() } ); + + let r = flags_from_vec(svec!["deno", "init", "--lib"]); + assert_eq!( + r.unwrap(), + Flags { + subcommand: DenoSubcommand::Init(InitFlags { + dir: None, + lib: true + }), + ..Flags::default() + } + ); + + let r = flags_from_vec(svec!["deno", "init", "foo", "--lib"]); + assert_eq!( + r.unwrap(), + Flags { + subcommand: DenoSubcommand::Init(InitFlags { + dir: Some(String::from("foo")), + lib: true + }), + ..Flags::default() + } + ); } #[test] diff --git a/cli/tools/init/mod.rs b/cli/tools/init/mod.rs index fb2e6e7afd..23b14e17cb 100644 --- a/cli/tools/init/mod.rs +++ b/cli/tools/init/mod.rs @@ -4,10 +4,145 @@ use crate::args::InitFlags; use crate::colors; use deno_core::anyhow::Context; use deno_core::error::AnyError; +use deno_core::serde_json::json; use log::info; use std::io::Write; use std::path::Path; +pub 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 + }; + + if init_flags.lib { + // Extract the directory name to use as the project name + let project_name = dir + .file_name() + .unwrap_or_else(|| dir.as_os_str()) + .to_str() + .unwrap(); + + create_file( + &dir, + "mod.ts", + r#"export function add(a: number, b: number): number { + return a + b; +} +"#, + )?; + create_file( + &dir, + "mod_test.ts", + r#"import { assertEquals } from "jsr:@std/assert"; +import { add } from "./mod.ts"; + +Deno.test(function addTest() { + assertEquals(add(2, 3), 5); +}); +"#, + )?; + + create_json_file( + &dir, + "deno.json", + &json!({ + "name": project_name, + "version": "1.0.0", + "exports": "./mod.ts", + "tasks": { + "dev": "deno test --watch mod.ts" + } + }), + )?; + } else { + create_file( + &dir, + "main.ts", + r#"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)); +} +"#, + )?; + create_file( + &dir, + "main_test.ts", + r#"import { assertEquals } from "jsr:@std/assert"; +import { add } from "./main.ts"; + +Deno.test(function addTest() { + assertEquals(add(2, 3), 5); +}); +"#, + )?; + + create_json_file( + &dir, + "deno.json", + &json!({ + "tasks": { + "dev": "deno run --watch main.ts" + } + }), + )?; + } + + info!("✅ {}", colors::green("Project initialized")); + info!(""); + info!("{}", colors::gray("Run these commands to get started")); + info!(""); + if let Some(dir) = init_flags.dir { + info!(" cd {}", dir); + info!(""); + } + if init_flags.lib { + info!(" {}", colors::gray("# Run the tests")); + info!(" deno test"); + info!(""); + info!( + " {}", + colors::gray("# Run the tests and watch for file changes") + ); + info!(" deno task dev"); + info!(""); + info!(" {}", colors::gray("# Publish to JSR (dry run)")); + info!(" deno publish --dry-run"); + } else { + info!(" {}", colors::gray("# Run the program")); + info!(" deno run main.ts"); + info!(""); + info!( + " {}", + colors::gray("# Run the program and watch for file changes") + ); + info!(" deno task dev"); + info!(""); + info!(" {}", colors::gray("# Run the tests")); + info!(" deno test"); + } + Ok(()) +} + +fn create_json_file( + dir: &Path, + filename: &str, + value: &deno_core::serde_json::Value, +) -> Result<(), AnyError> { + let mut text = deno_core::serde_json::to_string_pretty(value)?; + text.push('\n'); + create_file(dir, filename, &text) +} + fn create_file( dir: &Path, filename: &str, @@ -30,46 +165,3 @@ fn create_file( Ok(()) } } - -pub 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)?; - - create_file( - &dir, - "main_test.ts", - include_str!("./templates/main_test.ts"), - )?; - create_file(&dir, "deno.json", include_str!("./templates/deno.json"))?; - - info!("✅ {}", colors::green("Project initialized")); - info!(""); - info!("{}", colors::gray("Run these commands to get started")); - info!(""); - if let Some(dir) = init_flags.dir { - info!(" cd {}", dir); - info!(""); - } - info!(" {}", colors::gray("# Run the program")); - info!(" deno run main.ts"); - info!(""); - info!( - " {}", - colors::gray("# Run the program and watch for file changes") - ); - info!(" deno task dev"); - info!(""); - info!(" {}", colors::gray("# Run the tests")); - info!(" deno test"); - Ok(()) -} diff --git a/cli/tools/init/templates/deno.json b/cli/tools/init/templates/deno.json deleted file mode 100644 index 3c5130f1d3..0000000000 --- a/cli/tools/init/templates/deno.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "tasks": { - "dev": "deno run --watch main.ts" - } -} diff --git a/cli/tools/init/templates/main.ts b/cli/tools/init/templates/main.ts deleted file mode 100644 index be043e97c8..0000000000 --- a/cli/tools/init/templates/main.ts +++ /dev/null @@ -1,8 +0,0 @@ -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)); -} diff --git a/cli/tools/init/templates/main_test.ts b/cli/tools/init/templates/main_test.ts deleted file mode 100644 index 26af2582bc..0000000000 --- a/cli/tools/init/templates/main_test.ts +++ /dev/null @@ -1,6 +0,0 @@ -import { assertEquals } from "jsr:@std/assert"; -import { add } from "./main.ts"; - -Deno.test(function addTest() { - assertEquals(add(2, 3), 5); -}); diff --git a/tests/specs/init/lib/__test__.jsonc b/tests/specs/init/lib/__test__.jsonc new file mode 100644 index 0000000000..076930ae62 --- /dev/null +++ b/tests/specs/init/lib/__test__.jsonc @@ -0,0 +1,16 @@ +{ + "tempDir": true, + "steps": [{ + "args": "init --lib project", + "output": "init.out" + }, { + "cwd": "project", + "args": "test", + "output": "test.out" + }, { + "cwd": "project", + "args": "publish --dry-run", + "output": "dry_publish.out", + "exitCode": 1 + }] +} diff --git a/tests/specs/init/lib/dry_publish.out b/tests/specs/init/lib/dry_publish.out new file mode 100644 index 0000000000..31f91c38dc --- /dev/null +++ b/tests/specs/init/lib/dry_publish.out @@ -0,0 +1,7 @@ +Check file:///[WILDLINE]/mod.ts +Checking for slow types in the public API... +Check file:///[WILDLINE]/mod.ts +error: Failed preparing 'project'. + +Caused by: + Invalid package name, use '@/ format diff --git a/tests/specs/init/lib/init.out b/tests/specs/init/lib/init.out new file mode 100644 index 0000000000..0f1a83f300 --- /dev/null +++ b/tests/specs/init/lib/init.out @@ -0,0 +1,14 @@ +✅ Project initialized + +Run these commands to get started + + cd project + + # Run the tests + deno test + + # Run the tests and watch for file changes + deno task dev + + # Publish to JSR (dry run) + deno publish --dry-run diff --git a/tests/specs/init/lib/test.out b/tests/specs/init/lib/test.out new file mode 100644 index 0000000000..0b225a52bb --- /dev/null +++ b/tests/specs/init/lib/test.out @@ -0,0 +1,14 @@ +Download http://127.0.0.1:4250/@std/assert/meta.json +Download http://127.0.0.1:4250/@std/assert/0.220.1_meta.json +[UNORDERED_START] +Download http://127.0.0.1:4250/@std/assert/0.220.1/mod.ts +Download http://127.0.0.1:4250/@std/assert/0.220.1/assert_equals.ts +Download http://127.0.0.1:4250/@std/assert/0.220.1/assert.ts +Download http://127.0.0.1:4250/@std/assert/0.220.1/fail.ts +[UNORDERED_END] +Check file:///[WILDLINE]/mod_test.ts +running 1 test from ./mod_test.ts +addTest ... ok ([WILDLINE]) + +ok | 1 passed | 0 failed ([WILDLINE]) +