Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(cli): deno init --lib #22499

Merged
merged 11 commits into from
Jul 10, 2024
54 changes: 47 additions & 7 deletions cli/args/flags.rs
Original file line number Diff line number Diff line change
Expand Up @@ -205,6 +205,7 @@ impl FmtFlags {
#[derive(Clone, Debug, Eq, PartialEq)]
pub struct InitFlags {
pub dir: Option<String>,
pub lib: bool,
}

#[derive(Clone, Debug, Eq, PartialEq)]
Expand Down Expand Up @@ -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),
)
})
}

Expand Down Expand Up @@ -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::<String>("dir"),
lib: matches.get_flag("lib"),
});
}

Expand Down Expand Up @@ -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()
}
);
Expand All @@ -9764,6 +9776,7 @@ mod tests {
Flags {
subcommand: DenoSubcommand::Init(InitFlags {
dir: Some(String::from("foo")),
lib: false
}),
..Flags::default()
}
Expand All @@ -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]
Expand Down
176 changes: 134 additions & 42 deletions cli/tools/init/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,33 +4,11 @@ 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;

fn create_file(
dir: &Path,
filename: &str,
content: &str,
) -> Result<(), AnyError> {
let path = dir.join(filename);
if path.exists() {
info!(
"ℹ️ {}",
colors::gray(format!("Skipped creating {filename} as it already exists"))
);
Ok(())
} else {
let mut file = std::fs::OpenOptions::new()
.write(true)
.create_new(true)
.open(path)
.with_context(|| format!("Failed to create {filename} file"))?;
file.write_all(content.as_bytes())?;
Ok(())
}
}

pub fn init_project(init_flags: InitFlags) -> Result<(), AnyError> {
let cwd =
std::env::current_dir().context("Can't read current working directory.")?;
Expand All @@ -42,15 +20,82 @@ pub fn init_project(init_flags: InitFlags) -> Result<(), AnyError> {
cwd
};

let main_ts = include_str!("./templates/main.ts");
create_file(&dir, "main.ts", main_ts)?;
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())
Comment on lines +25 to +27
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This might be problematic - I don't think all directories are valid JSR specifiers, but maybe fine for the first pass and could be improved in the future.

.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";

create_file(
&dir,
"main_test.ts",
include_str!("./templates/main_test.ts"),
)?;
create_file(&dir, "deno.json", include_str!("./templates/deno.json"))?;
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!("");
Expand All @@ -60,16 +105,63 @@ pub fn init_project(init_flags: InitFlags) -> Result<(), AnyError> {
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");
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,
content: &str,
) -> Result<(), AnyError> {
let path = dir.join(filename);
if path.exists() {
info!(
"ℹ️ {}",
colors::gray(format!("Skipped creating {filename} as it already exists"))
);
Ok(())
} else {
let mut file = std::fs::OpenOptions::new()
.write(true)
.create_new(true)
.open(path)
.with_context(|| format!("Failed to create {filename} file"))?;
file.write_all(content.as_bytes())?;
Ok(())
}
}
5 changes: 0 additions & 5 deletions cli/tools/init/templates/deno.json

This file was deleted.

8 changes: 0 additions & 8 deletions cli/tools/init/templates/main.ts

This file was deleted.

6 changes: 0 additions & 6 deletions cli/tools/init/templates/main_test.ts

This file was deleted.

16 changes: 16 additions & 0 deletions tests/specs/init/lib/__test__.jsonc
Original file line number Diff line number Diff line change
@@ -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
}]
}
7 changes: 7 additions & 0 deletions tests/specs/init/lib/dry_publish.out
Original file line number Diff line number Diff line change
@@ -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 '@<scope_name>/<package_name> format
14 changes: 14 additions & 0 deletions tests/specs/init/lib/init.out
Original file line number Diff line number Diff line change
@@ -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
14 changes: 14 additions & 0 deletions tests/specs/init/lib/test.out
Original file line number Diff line number Diff line change
@@ -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])