diff --git a/docs/docs/guide/codegen/generating-code.md b/docs/docs/guide/codegen/generating-code.md index 004b2c5..727b735 100644 --- a/docs/docs/guide/codegen/generating-code.md +++ b/docs/docs/guide/codegen/generating-code.md @@ -5,8 +5,8 @@ language. This page is the practical guide: what it produces, how to say where i goes, and how to generate more than one version at once. Status: this reflects the current CLI. `rust` and `typescript` are the -generators (both `code` mode; `rust` also does `lib`); the configuration -surface below is stable. +generators, and both do `code` and `lib`; the configuration surface below is +stable. ## The two files @@ -38,9 +38,10 @@ Each entry is a bare `language#lang_version`. `lang_version` selects a version-specific generator (e.g. Rust 1.70's output vs a later edition's). The `= {}` is required and takes no options. -A declared language is a *capability*: "bindings for this language make sense for -this package." It does not force anyone to generate it, and it carries no output -path. +Declaring a language is what makes it generated: `comline generate` emits every +language listed here. The entry carries no output path — where the code lands is +the consumer's business, below — and a consumer who wants fewer says so with +`only`. ## Configuring output — `comline.toml` @@ -48,18 +49,30 @@ path. [generate] out = "generated" # output root, relative to comline.toml layout = "{{language}}/{{namespace}}.{{ext}}" # path of each file under the root -mode = "code" # code | lib | dylib (only `code` today) +mode = "code" # code | lib (dylib is not built yet) package_versions = "latest" # latest | all | ["0.3.0", "0.4.0"] +only = ["rust"] # generate just these, out of the declared languages -# Optional: one block per target you want to pin or override. +# Optional: one block per declared language you want to configure. [[generate.target]] language = "rust" lang_version = "1.70.0" # defaults to what config.idp declared -out = "src/generated" # this target only +out = "src/generated" # this language only ``` -With no `comline.toml` (or an all-commented one) the defaults above apply and the -targets come from `config.idp`'s `code_generation.languages`. +With no `comline.toml` (or an all-commented one) the defaults above apply to +every language `config.idp` declares. + +**A block configures a language; it doesn't choose which languages are +generated.** That comes from `config.idp` alone. A declared language with no +block takes the `[generate]` defaults, several blocks for one language give +several outputs, and a block naming a language the congregation doesn't declare +is an error rather than a silent no-op. + +To generate fewer languages than the package declares — because this checkout +only needs some of them — list the ones you want in `[generate] only`, or pass +`--target` for a single run. A name `config.idp` doesn't declare is an error +there too, so a typo can't quietly stop generating something. **Precedence**, lowest to highest: @@ -159,11 +172,45 @@ mode wires that module tree up itself (`pub mod shared;` in `src/lib.rs`); cross-schema references in `code` mode needs to declare matching `mod` statements itself. -## Modes (planned) +## What TypeScript output looks like + +| Schema | TypeScript | +|---|---| +| `struct` | `export interface` | +| `error` | `export interface` (the payload) + `export class Error` | +| `enum` | `export enum` with string values | +| `protocol` | a client, a dispatcher, and a `serve` helper against `@comline/runtime` | + +A cross-schema reference is emitted as an `import type` from the schema that +declares it, by relative specifier: + +```ts +import type { Greeting } from "./types.js"; +``` + +An alias imports the name the other module actually exports +(`use types::Greeting as G` → `import type { Greeting as G }`), and a `use` +nothing refers to produces no import at all. + +The specifier is relative, so `code` mode assumes your `layout` keeps each +schema's namespace in the **file-name** position — every layout above does, +including the default. A layout that puts the namespace in a directory +position instead (`"{{namespace}}/index.{{ext}}"`) moves the files relative to +each other and the imports won't resolve. + +In `lib` mode the namespace tree becomes the package's shape, so both of these +work: + +```ts +import { types } from "my-api"; // types.Greeting +import { Greeting } from "my-api/types"; +``` + +## Modes -`mode` selects the emit form. Only `code` — plain source text you drop into your -project — exists today. `lib` (a buildable crate skeleton) and `dylib` (that, -compiled, for a language runtime to load) are planned. +`mode` selects the emit form. `code` (plain source text you drop into your +project) and `lib` (a buildable package) both work, for both generators; +`dylib` — that, compiled, for a language runtime to load — is planned. [Library generation](library-generation.md) explains each mode and when to reach for it. diff --git a/docs/docs/guide/codegen/index.md b/docs/docs/guide/codegen/index.md index f9c3bc7..1385952 100755 --- a/docs/docs/guide/codegen/index.md +++ b/docs/docs/guide/codegen/index.md @@ -23,6 +23,6 @@ detail that the generated code is pleasant to use directly rather than only as a full guide, and [`comline.toml`](../../reference/congregation/comline-toml.md) is where a consumer sets the output location, layout and mode. -Today there are `rust` and `typescript` generators (both `code` mode); `rust` -also does `lib` mode. [Library generation](library-generation.md) covers the -modes and `dylib`, which is still planned. +Today there are `rust` and `typescript` generators, and both do `code` and +`lib` mode. [Library generation](library-generation.md) covers the modes and +`dylib`, which is still planned. diff --git a/docs/docs/guide/codegen/library-generation.md b/docs/docs/guide/codegen/library-generation.md index cbcbb10..9d26290 100755 --- a/docs/docs/guide/codegen/library-generation.md +++ b/docs/docs/guide/codegen/library-generation.md @@ -13,7 +13,7 @@ in [`comline.toml`](../../reference/congregation/comline-toml.md) (`[generate] m | `mode` | Output | Status | |---|---|---| | `code` | bare source files you place in a project yourself | built | -| `lib` | a buildable package (manifest + module tree) | **rust only** | +| `lib` | a buildable package (manifest + module tree) | rust, typescript | | `dylib` | a package built with a **stable ABI** and compiled to a dynamic library any language's runtime can load | planned | ## `code` — source you wire in yourself @@ -40,14 +40,43 @@ A whole buildable package: the manifest with the right dependencies, a `src/` module tree, and a top-level module listing every schema. The result is something you `cargo add` / `pip install` / `luarocks install`, or publish. -Implemented for **rust**: `/rust/` gets a `Cargo.toml` (package name from -the congregation, `serde` dependency), `src/lib.rs`, and `src/.rs` per -schema — `/` keeps this target's crate from colliding with another -`lib` target sharing the same `out`; a target with `out` to itself can set +**rust**: `/rust/` gets a `Cargo.toml` (package name from the +congregation, `serde` dependency), `src/lib.rs`, and `src/.rs` per +schema. A nested namespace (`std/http`) becomes a nested module — +`src/std.rs` declaring `pub mod http;` beside `src/std/http.rs`. + +**typescript**: `/typescript/` gets a `package.json` (declaring +`@comline/runtime` when any schema has a protocol), a `tsconfig.json`, and +`src/.ts` per schema. Each namespace level also gets an `index.ts` +naming its children, so the package's shape mirrors your namespaces: + +``` +src/index.ts export * as types from "./types.js"; + export * as std from "./std/index.js"; +src/std/index.ts export * as http from "./http.js"; +``` + +That is why names don't collide: `std` is implicit in every package, and a +flat barrel re-exporting everything into the package root would make your own +`Request` ambiguous with `std/http`'s — which TypeScript refuses outright +(TS2308). Both entry styles work: + +```ts +import { types } from "my-api"; // types.Greeting +import { Greeting } from "my-api/types"; +``` + +One restriction, shared by both generators: a namespace can be a schema or a +parent of deeper schemas, not both. `std.ids` alongside `std/http.ids` leaves +`src/std.rs` (or `export * as std`) with two things to be, so it's refused +with a message naming the pair. + +In either language, `/` keeps this target's package from colliding +with another `lib` target sharing the same `out`; a target with `out` to +itself can set [`flatten = true`](../../reference/congregation/comline-toml.md#flatten) to skip that and -land the crate straight into `out`. `layout` plays no part in any of this — -it only applies to `code` mode. Other languages, multi-version `lib` output, -and a schema whose namespace is nested (`a/b`) are not done yet. +land the package straight into `out`. `layout` plays no part in any of this — +it only applies to `code` mode. Multi-version `lib` output is not done yet. **Use it when** the schema is shared: diff --git a/docs/docs/reference/cli.md b/docs/docs/reference/cli.md index fc7c175..044bcf8 100644 --- a/docs/docs/reference/cli.md +++ b/docs/docs/reference/cli.md @@ -35,7 +35,7 @@ parse → resolve imports → validate → (freeze into .comline/) → (generate | `comline add ` / `comline add --git --commit --version ` | Add a dependency to `config.idp`, imported as `use ::…`: resolved first (a git pin is fetched into `.comline/deps-cache/`), then written with its `hash` pinned. `--no-hash` skips the pin. | | `comline check` | Parse, resolve and validate. No `.comline/` write (beyond fetching git dependencies), no version bump — safe for editors, hooks, CI. | | `comline build [--release] [--watch]` | Compile, validate, freeze a new immutable version into `.comline/`; print the changelog and the bump. `--release` is currently a no-op. | -| `comline generate [--target ] [--out ] [--layout ] [--mode ] [--external-std] [--watch]` | Validate (no freeze), then write generated code. Location/layout from [`comline.toml`](congregation/comline-toml.md); targets from there or `config.idp`. | +| `comline generate [--target ] [--out ] [--layout ] [--mode ] [--external-std] [--no-external-std] [--dry-run] [--watch]` | Validate (no freeze), then write generated code. Location/layout from [`comline.toml`](congregation/comline-toml.md); targets from there or `config.idp`. | | `comline diff []` | Show schema changes between two built versions. Each argument is a version (`0.2.0`), a commit hash (4+ chars), or `HEAD` (`` default). | | `comline clean [--dry-run]` | Remove generated code (the `generate` output root or files). Never touches `.comline/`. | | `comline reset [--force] [--dry-run]` | **Destructive.** Delete `.comline/` — the whole version history, no undo — *and* generated code. Next `build` starts over at `0.0.1`. On a terminal it shows how many versions will be lost and makes you type `reset`; without a terminal it refuses (exit `2`) unless `--force`. | @@ -45,9 +45,13 @@ parse → resolve imports → validate → (freeze into .comline/) → (generate `--out` / `--layout` / `--mode` override one target and need `--target` when several are configured. `COMLINE_GENERATE_OUT` / `_LAYOUT` / `_MODE` do the same from the environment, for every target. `--external-std` makes generated Rust -reference `comline_std` as its own crate instead of inlining it, overriding -[`comline.toml`](congregation/comline-toml.md)'s `[generate] external_std`. -Today `rust` and `typescript` are the generators; `rust` also does `lib` mode. +reference `comline_std` as its own crate instead of inlining it, and +`--no-external-std` inlines it; either overrides +[`comline.toml`](congregation/comline-toml.md)'s `[generate] external_std`, +which itself defaults to on in `lib` mode and off in `code` mode. +`--dry-run` lists the files each target would write, as a tree, and writes +nothing. Today `rust` and `typescript` are the generators, and both do `code` +and `lib` mode. ## Global flags