nest-rs-cli 1.1.0

Scaffolding CLI for NestRS — new projects, feature generators, and project health checks.
//! **Workspace mode** — monorepo: `crates/features/` + thin `apps/*` binaries.
//!
//! Generated by `nestrs new <name>`. Product logic lives in
//! features; each app root `module.rs` composes edge modules only.
//! Contrasts with [`super::standalone`] (single crate, logic in `src/`).

/// Root `Cargo.toml` for a greenfield NestRS monorepo (crates.io deps).
pub const ROOT_CARGO: &str = r#"[workspace]
resolver = "2"
members = ["crates/*", "apps/*"]

[workspace.package]
version = "0.1.0"
edition = "2024"
rust-version = "1.96"

[workspace.dependencies]
anyhow = "1"
tokio = { version = "1", features = ["macros", "rt-multi-thread"] }
tracing-subscriber = { version = "0.3", features = ["env-filter"] }
features = { path = "crates/features" }
migrations = { path = "crates/migrations" }
nest-rs-core = "{{nestrs_version}}"
nest-rs-config = "{{nestrs_version}}"
nest-rs-guards = "{{nestrs_version}}"
nest-rs-http = "{{nestrs_version}}"
nest-rs-interceptors = "{{nestrs_version}}"
nest-rs-seaorm = { version = "{{nestrs_version}}", features = ["http"] }
nest-rs-testing = "{{nestrs_version}}"
poem = { version = "3", features = ["tower-compat", "anyhow", "rustls"] }
sea-orm = { version = "2.0", default-features = false, features = ["sqlx-postgres", "runtime-tokio-rustls", "macros", "with-uuid", "with-chrono"] }
sea-orm-migration = { version = "2.0", features = ["sqlx-postgres", "runtime-tokio-rustls"] }

# Release: the smallest, fastest single binary — production defaults.
[profile.release]
opt-level = 3       # maximum runtime performance
lto = "fat"         # whole-program optimisation across all crates
codegen-units = 1   # give the optimiser the whole crate at once
strip = true        # drop symbols for a smaller binary

# Dev: fastest iterative rebuilds; keep file:line in backtraces.
[profile.dev]
debug = "line-tables-only"
"#;

pub const FEATURES_CARGO: &str = r#"[package]
name = "features"
version.workspace = true
edition.workspace = true
publish = false

[dependencies]
nest-rs-core.workspace = true
nest-rs-guards.workspace = true
nest-rs-http.workspace = true
nest-rs-interceptors.workspace = true
poem.workspace = true
"#;

/// The feature-crate root. Every workspace starts with the app's own `hello`
/// feature declared here (see [`super::hello`]).
pub const FEATURES_LIB: &str = r#"//! Product features — vertical slices shared across apps.

pub mod {{snake}};

pub use {{snake}}::{{http_module}};
"#;

/// Thin workspace app — composition only (`apps/api` shape).
pub const APP_CARGO: &str = r#"[package]
name = "{{kebab}}"
version.workspace = true
edition.workspace = true
publish = false

[dependencies]
features.workspace = true
nest-rs-core.workspace = true
nest-rs-config.workspace = true
nest-rs-http.workspace = true
tokio.workspace = true
anyhow.workspace = true

[dev-dependencies]
nest-rs-testing.workspace = true
"#;

pub const APP_LIB: &str = r#"mod module;

pub use module::{{module}};
"#;

pub const APP_MAIN: &str = r#"use anyhow::Result;
use nest_rs_config::Environment;
use nest_rs_core::App;

use {{snake}}::{{module}};

#[tokio::main]
async fn main() -> Result<()> {
    let _environment = Environment::init();

    App::builder()
        .module::<{{module}}>()
        .build()
        .await?
        .run()
        .await
}
"#;

/// Composition only — the app's own module name and the feature module it
/// serves share a snake name, in two different crates (`BlogModule` in
/// `apps/blog/src/module.rs`, `BlogModule` in `crates/features/src/blog/`).
pub const APP_MODULE: &str = r#"use nest_rs_core::module;
use nest_rs_http::{HttpConfig, HttpModule};

use features::{{snake}}::{{http_module}};

#[module(
    imports = [
        HttpModule::for_root(HttpConfig { port: {{port}}, ..Default::default() }),
        {{http_module}},
    ],
)]
pub struct {{module}};
"#;

pub const README: &str = r#"# {{kebab}}

**Workspace** NestRS monorepo — product logic in `crates/features/`, thin
composition apps under `apps/`. Generated by `nestrs new {{kebab}}`.

## Run the default app

```bash
nestrs run dev hello
nestrs run build              # default app → target/release/hello
nestrs run build blog         # one app by name
nestrs run build --all        # every app
```

Open **http://localhost:3000/** in your browser — `Hello World` from
`crates/features/src/hello/`. `apps/hello/` only wires modules; the listen
port is set in `apps/hello/src/module.rs`, not in `.env`.

Need a single crate instead? Use
[standalone mode](https://nestrs.dev/cli/#layout-detection):
`nestrs new <name> --standalone`.

## Grow the workspace

```bash
nestrs new blog                    # another app + its own hello on GET /
nestrs g resource posts            # DB-backed CRUD, behind the app's guards
nestrs g migration create_posts    # its table
docker compose up -d && nestrs run db up
```

Every app is scaffolded with a `hello` feature named after it, so it answers on
`/` from the first run — delete `crates/features/src/<app>/` once the app serves
something real. `crates/migrations/` and `crates/seed/` back the
`nestrs run db …` verbs.

Import edge modules in each app's root `module.rs` — see
[`users/`](https://github.com/YV17labs/NestRS/tree/main/demo/crates/features/src/users)
in the framework repo.
"#;

pub const JUSTFILE: &str = r#"_default:
    @just --list

# Run an app with auto-reload — watches the source, rebuilds and restarts on
# save. Default: hello. Usage: nestrs run dev blog
dev app="hello":
    bacon run-long -- --bin {{app}}

# Run an app in release mode. Usage: nestrs run start blog
start app="hello":
    cargo run --release --bin {{app}}

# Build in release: one app (default hello), or every app with `--all`.
# Usage: nestrs run build blog   |   nestrs run build --all
build app="hello":
    cargo build --release {{ if app == "--all" { "--workspace" } else { "-p " + app } }}

# Type-check the workspace.
check:
    cargo check --workspace

# Tests — unit/integration/e2e/doctests. Usage: nestrs run test [unit|e2e|doc]
mod test

# Database lifecycle. Usage: nestrs run db up|down|fresh|status|seed|reset
mod db

# Apply rustfmt across the workspace.
fmt:
    cargo fmt --all

# Clippy (strict) + format check.
lint:
    cargo clippy --workspace --all-targets -- -D warnings
    cargo fmt --all --check
"#;

pub const TEST_JUSTFILE: &str = r#"# Test recipes, exposed as `nestrs run test <kind>` (see `mod test` in the
# Justfile). Bare `nestrs run test` lists these — pick a kind to run.

# Bare `nestrs run test` lists the kinds instead of running one.
_default:
    @just --list test

# Unit + integration + doctests, no DB.
unit:
    cargo nextest run --workspace -E 'not binary(e2e)'
    cargo test --workspace --doc          # nextest skips doctests; run them too

# e2e tests — the `tests/e2e/main.rs` suites (live Postgres/Redis if the app
# needs them). Each app is scaffolded with an empty one, so `--no-tests=pass`
# keeps this green until you write the first.
e2e:
    cargo nextest run --workspace -E 'binary(e2e)' --no-tests=pass

# Doctests only — the code examples inside `///` doc comments.
doc:
    cargo test --workspace --doc
"#;