diff --git a/docs/doc-site/_index.md b/docs/doc-site/_index.md index 014eddb2..1396e4ad 100644 --- a/docs/doc-site/_index.md +++ b/docs/doc-site/_index.md @@ -5,6 +5,8 @@ description: Generate Swagger 2.0 specifications from annotated Go source code. weight: 1 --- +{{< version >}} + `github.com/go-openapi/codescan` is a Go source code scanner that produces [Swagger 2.0][swagger2] (OpenAPI 2.0) specifications. @@ -26,6 +28,8 @@ The only exposed API is `Run()` and `Options`. ### Getting started +To use codescan in your go program: + ```cmd go get github.com/go-openapi/codescan ``` @@ -40,73 +44,36 @@ swaggerSpec, err := codescan.Run(&codescan.Options{ }) ``` -### Where to go next - -{{< cards >}} -{{% card title="About" %}} -What codescan is, why scan source to a spec, and how it relates to go-swagger. - -→ [about]({{% relref "/about" %}}) -{{% /card %}} - -{{% card title="Getting started" %}} -Install the scanner, annotate a package, and produce your first spec. - -→ [getting-started]({{% relref "/getting-started" %}}) -{{% /card %}} - -{{% card title="Tutorials" %}} -Learn by spec concept — model definitions, routes, validations — annotated Go -next to the spec it produces. - -→ [tutorials]({{% relref "/tutorials" %}}) -{{% /card %}} - -{{% card title="Shaping the output" %}} -How-to guides for the rendering knobs: $ref vs inline, aliases, nullable -pointers, extensions. +Or as a command, to run in a build or a pipeline: -→ [shaping-the-output]({{% relref "/shaping-the-output" %}}) -{{% /card %}} - -{{% card title="Annotation index" %}} -Every annotation at a glance, linked to its worked example and its full -reference. - -→ [annotation-index]({{% relref "/annotation-index" %}}) -{{% /card %}} - -{{% card title="Reference (maintainers)" %}} -The complete compendium — annotations, keywords, sub-languages and the formal -grammar. - -→ [maintainers]({{% relref "/maintainers" %}}) -{{% /card %}} +```cmd +go install github.com/go-openapi/codescan/cmd/genspec@latest +``` -{{% card title="Project" %}} -Repo overview, license and links to the shared go-openapi guides. +Or as a terminal front-end, to watch a spec take shape as you annotate: -→ [project]({{% relref "/project" %}}) -{{% /card %}} -{{< /cards >}} +```cmd +go install github.com/go-openapi/codescan/cmd/genspec-tui@latest +``` -## Licensing +Try it out now from your browser in our [Playground]({{% relref "/playground" %}}). -`SPDX-FileCopyrightText: Copyright 2015-2025 go-swagger maintainers` +### Relationship to go-swagger -This library ships under the [Apache-2.0 license](./project/license/). +`go-swagger` is a CLI tool that consumes the codescan library. +It works exactly on the same set of annotations. -## Contributing +The main differences with the newer `genspec` CLI shipped by this project are: -Issues and pull requests welcome. +* release cadence (expect slightly less frequent updates on go-swagger, which has more dependencies and constraints) +* package distribution: at this moment, the codescan CLI tools do not ship as distro packages or docker images +* exposed CLI knobs and default settings (defaults need to be backward-compatible for go-swagger users) -See the shared [go-openapi contributing guidelines][contributing-doc-site] and -the per-repo notes in [project/](./project/). +`genspec` and `genspec-tui` are intended for users who want tools leaner than go-swagger, or who want to +experiment with the latest features. ---- +### Where to go next {{< children type="card" description="true" >}} [swagger2]: https://swagger.io/specification/v2/ -[contributing-doc-site]: https://go-openapi.github.io/doc-site/contributing/contributing/index.html -[maintainers-doc-site]: https://go-openapi.github.io/doc-site/maintainers/index.html diff --git a/docs/doc-site/about.md b/docs/doc-site/about.md index c9c86591..10c52ea9 100644 --- a/docs/doc-site/about.md +++ b/docs/doc-site/about.md @@ -2,8 +2,9 @@ title: About weight: 5 description: | - What codescan is, why you would scan source to produce a spec, and how it - relates to the go-swagger toolkit. + What codescan is, why you would scan source to produce a spec. + + How does it relate to the go-openapi & go-swagger toolkits. --- `codescan` is a **code-first** OpenAPI engine: it reads specially formatted @@ -13,35 +14,43 @@ comments (annotations) in your Go source and produces a ## Two ways to build an API -APIs and their documentation tend to evolve along one of two paths. The -go-openapi / go-swagger toolkit supports both. +APIs and their documentation tend to evolve along one of two paths. The go-openapi / go-swagger toolkit supports both. + +- **Design-first** (contract-first) — you write the OpenAPI document first and treat it as the contract, + then generate servers and clients from it. + +> If this is your workflow, you should reach for [go-swagger][go-swagger] (`swagger generate server` / `swagger generate client`). -- **Design-first** (contract-first) — you write the OpenAPI document first and - treat it as the contract, then generate servers and clients from it. If this - is your workflow, reach for [go-swagger][go-swagger] (`swagger generate - server` / `swagger generate client`). -- **Code-first** — you write annotated Go and scan it to produce the spec. This - keeps the document in sync with the code as it changes, and lets you produce a - valid specification for a service that already exists. +- **Code-first** — you write annotated Go and scan it to produce the spec. + +> This keeps the document in sync with the code as it changes, and lets you produce a valid specification for a service that already exists. **codescan is the engine for the code-first path.** ## Relationship to go-swagger codescan began life as a single package inside [go-swagger][go-swagger] and was -spun out into its own [go-openapi][go-openapi] repository. It is the scanner -**behind** the go-swagger command: +spun out into its own [go-openapi][go-openapi] repository. It is the scanner **behind** the go-swagger command: ```sh swagger generate spec ./... ``` -go-swagger remains the main command-line consumer of this library. This site -documents the **scanner library itself** — the layer beneath `swagger generate -spec` — so it sits upstream of go-swagger's "generate spec" documentation. If -you arrived here from go-swagger: the annotations are exactly the same, and you -can either keep using the `swagger` CLI or call `codescan.Run` directly from -your own program (see [Getting started]({{% relref "/getting-started" %}})). +For now, go-swagger remains the main command-line consumer of this library. + +This site documents the **scanner library itself** — the layer beneath `swagger generate spec` — +so it sits upstream of go-swagger's "generate spec" documentation. + +If you arrived here from go-swagger: the annotations are exactly the same, and you can either keep using the +`swagger` CLI or call `codescan.Run` directly from your own program +(see [Getting started]({{% relref "/getting-started" %}})). + +{{% notice info %}} +codescan now releases standalone CLI tools that ship independently from go-swagger: +`genspec`, `genspec-wasi` and `genspec-tui`. + +These are supplementary tools, not a replacement, and go-swagger will continue shipping updates from this library. +{{% /notice %}} ## Why scan from source @@ -53,18 +62,18 @@ your own program (see [Getting started]({{% relref "/getting-started" %}})). that is already deployed, so it becomes interoperable with new clients and tooling. -When document-level metadata (info, security, servers) is more naturally -hand-authored, you do not have to push it into the code: scan the code for the -operations and models, and **overlay** the result onto a hand-written base -document (see [Shaping the output → Overlaying a spec]({{% relref "overlaying-a-spec" %}})). +When document-level metadata (info, security, servers) is more naturally hand-authored, +you do not have to push it into the code: scan the code for the operations and models, +and **overlay** the result onto a hand-written base document +(see [Shaping the output → Overlaying a spec]({{% relref "overlaying-a-spec" %}})). + +## The go-openapi toolkit -## A community toolkit +go-openapi and go-swagger are community-driven, open-source **building blocks** meant to be assembled and customized +— there are too many ways to approach APIs to cover them all. -go-openapi and go-swagger are community-driven, open-source **building blocks** -meant to be assembled and customized — there are too many ways to approach APIs -to cover them all. Fork, reuse, and adapt what you find useful. See the -[go-swagger project's "About" page][go-swagger-about] for the wider toolkit -story. +Fork, reuse, and adapt what you find useful. See the [go-swagger project's "About" page][go-swagger-about] +for the wider toolkit story. ## Where to go next diff --git a/docs/doc-site/annotation-index/_index.md b/docs/doc-site/annotation-index/_index.md index 0aefcf48..10704488 100644 --- a/docs/doc-site/annotation-index/_index.md +++ b/docs/doc-site/annotation-index/_index.md @@ -1,13 +1,15 @@ --- title: Annotation index -weight: 40 +weight: 60 description: | - Every swagger:* annotation at a glance — what it produces and where it - attaches — linked to both its worked example and its full reference. + Every swagger:* annotation at a glance. + + What it produces and where it attaches — linked to both its worked example and its full reference. --- -The complete `swagger:*` vocabulary, one row each. **By example** jumps to the -tutorial that shows the annotation as runnable Go next to the spec it produces; +The complete `swagger:*` vocabulary, one row each. + +**By example** jumps to the tutorial that shows the annotation as runnable Go next to the spec it produces; **Reference** jumps to the exhaustive rule in the Maintainers compendium. | Annotation | Attaches to | Produces | By example | Reference | diff --git a/docs/doc-site/getting-started/_index.md b/docs/doc-site/getting-started/_index.md index 89926fb4..ac04926f 100644 --- a/docs/doc-site/getting-started/_index.md +++ b/docs/doc-site/getting-started/_index.md @@ -2,8 +2,9 @@ title: Getting started weight: 10 description: | - Install codescan and choose how to drive it — as a Go library from your own - program, or interactively from the terminal UI. + Install codescan and choose how to drive it. + + As a Go library from your own program, as a command in a build, or interactively from the terminal UI. --- ## Install @@ -21,15 +22,33 @@ codescan exposes a deliberately small surface: a single `Run` function and an func Run(opts *Options) (*spec.Swagger, error) ``` +--- + +Or as a command, to run in a build or a pipeline: + +```cmd +go install github.com/go-openapi/codescan/cmd/genspec@latest +``` + +--- + Or as a terminal front-end, to watch a spec take shape as you annotate: ```cmd go install github.com/go-openapi/codescan/cmd/genspec-tui@latest ``` +--- + +If you just want to experiment, learn or reproduce an issue you're currently having, +the easiest way is to try our [Playground]({{% relref "/playground" %}}) in your browser. + ## Ways to use codescan {{< children type="card" description="true" >}} -> Both drive the same scanner over the same annotations, so what you see in the -> terminal UI is what your generator will produce. +> All three drive the same scanner over the same annotations, and every knob is +> spelled the same way in each — so what you see in the terminal UI is what your +> build will produce. +> +> See [Setting an option]({{% relref "setting-options" %}}). diff --git a/docs/doc-site/getting-started/usage-as-a-headless-cli.md b/docs/doc-site/getting-started/usage-as-a-headless-cli.md new file mode 100644 index 00000000..323413cd --- /dev/null +++ b/docs/doc-site/getting-started/usage-as-a-headless-cli.md @@ -0,0 +1,307 @@ +--- +title: Usage as a headless CLI +weight: 3 +description: | + Point genspec at annotated Go source and get a Swagger 2.0 document — on + standard output, in a file, checked against the schema, or as a + machine-readable envelope from a sandbox with no Go toolchain in it. +--- + +`genspec` is the ordinary way to run codescan without writing a program: point it at annotated Go source, +get a Swagger 2.0 document. Everything the library can be told is a flag, the document goes to standard output or to `-output`, +and what the scan observed goes to standard error. + +## Install + +```cmd +go install github.com/go-openapi/codescan/cmd/genspec@latest +``` + +```cmd +# scan the module in the current directory, document to standard output +genspec + +# somewhere else, narrowed, to a file, and checked +genspec -workdir ../my-api -output swagger.yaml -validate ./internal/api/... +``` + +The packages are Go patterns, resolved against `-workdir`. Naming none scans +`./...`. + +{{% notice style="note" %}} +`genspec` does the job of go-swagger's `swagger generate spec`, but is released +independently of it — so fixes and new options reach it at codescan's pace +rather than go-swagger's. go-swagger has a much larger scope, and the +dependencies that go with it. +{{% /notice %}} + +## Two streams, never mixed + +The document goes to standard output; diagnostics go to standard error, colored +when that is a terminal. Nothing is written to standard error when there is +nothing to say. So the obvious pipeline is safe: + +```cmd +genspec > swagger.json +genspec | jq '.definitions | keys' +``` + +This is the command's doing, not the library's: codescan itself never writes to +either stream. Every observation it makes — a dropped construct, a rename, a +prune — is handed to the command through a callback, and `genspec` is what turns +those into lines you can read. + +## Choosing the output + +| Flag | Effect | +|------|--------| +| `-output` | the file to write, or `-` for standard output (the default) | +| `-format` | `json`, `yaml`, or `auto` — which reads the extension of `-output`, and writes JSON when that says nothing | +| `-compact` | JSON with no indentation | +| `-input` | merge the scan's discoveries into an existing document | + +`auto` is why the common cases need no `-format` at all: + +```cmd +genspec -output swagger.yaml # YAML, because of the name +genspec -output swagger.json # JSON +genspec > swagger.json # JSON +genspec -compact # JSON with no indentation +``` + +YAML is derived from the JSON rendering, which costs key order: the document +comes out alphabetical rather than in the order the spec types declare. Same +information, a different diff against a hand-written file. + +`-input` is the place for everything a scanner cannot know — the host, the +security definitions, a hand-written path the annotations do not describe. The +scan is merged on top of it; see +[Overlaying a spec]({{% relref "overlaying-a-spec" %}}). + +## Diagnostics + +| Flag | Effect | +|------|--------| +| `-quiet` | say nothing at all | +| `-verbose` | also report hints, which are muted by default — and say which configuration file was read | +| `-color` | `auto` (a terminal), `always`, `never`. `auto` honours `NO_COLOR` and `TERM=dumb` | +| `-validate` | check the document against the Swagger 2.0 schema and report what is wrong with it | +| `-fail-on` | exit non-zero when something reaches this severity: `error`, `warning`, or `never` | + +`-fail-on` covers what `-validate` found as well as what the scan observed: they reach the reader as one stream, +so a threshold that saw only half of it would be a trap rather than a policy. +It defaults to `never`, because a scan that emits warnings is the ordinary case, +and a command that failed the build over one would mostly teach people to stop reading them. + +## Validating: whether the document is legal Swagger 2.0 + +A scan that reports nothing is not a promise that the document it produced is +valid. The scanner diagnoses what is wrong with your *annotations*; whether the +result is a legal Swagger 2.0 document is a separate question, and `-validate` +is what asks it: + +```cmd +genspec -validate -output swagger.yaml ./internal/api/... +``` + +It checks the rendered JSON rather than the document in memory, so what is +validated is exactly what was written — including whatever the round trip +through JSON did to it. The check is [go-openapi/validate][validate], the same +one behind `swagger validate`. + +Findings arrive through the same stream as the scan's own, because to a reader +they are the same kind of news about the same document. Each is located by the +JSON pointer the validator recorded as it walked the spec, so it names the node +rather than a line: + +```text +ERROR | paths./orders.post.parameters.1.in in body is required at=/paths/~1orders/post/parameters/1 +``` + +A finding about something the document *lacks* — no `info` block, no `paths` — +has an empty pointer, which is a location in RFC 6901 and not the absence of +one. It is reported as the whole document rather than printed as nothing: + +```text +ERROR | info in body is required at=(the whole document) +genspec: the specification is not valid: 1 finding(s) +``` + +Warnings and errors both come out. Only errors make the document invalid: a +warning — an unused definition, say — is worth saying and is not a verdict, and +whether it fails the command is `-fail-on`'s business rather than the +validator's. + +An invalid document exits **4**, and that outranks `-fail-on`'s **3**: it is the +more specific answer. The document is still written either way, so you can look +at what was rejected. + +[validate]: https://github.com/go-openapi/validate + +## Exit status + +A specification is written whenever one could be produced, so a non-zero status +says what was wrong with the document rather than meaning nothing came out. + +| Status | Meaning | +|--------|---------| +| 0 | the scan produced a document, and nothing asked for more | +| 1 | the scan failed | +| 2 | the command line does not make sense | +| 3 | what was reported reached the severity `-fail-on` names | +| 4 | `-validate` found the document invalid | + +An invalid document outranks `-fail-on`: it is the more specific answer. + +## Configuring it once + +Anything that can be a flag can be preset in a `.codescan.yaml`, found by +searching upwards from wherever you are — so a project configures itself once +and the command is run bare: + +```yaml +scan: + workdir: ./api + exclude-tags: [internal] + +document: + format: yaml + output: swagger.yaml + +diagnostics: + validate: true + fail-on: warning +``` + +Anything typed on the command line wins over it. The file's full contract — +where it is looked for, what sections exist, `-config` and `--no-config` — is in +[Setting an option]({{% relref "setting-options" %}}). + +## The scan itself + +`genspec -h` lists every flag. They fall into the four families a configuration +file uses as its sections: + +- **which code** (`scan`) — `-workdir`, the positional patterns, `-build-tags`, + `-include` / `-exclude`, `-include-tags` / `-exclude-tags`, `-exclude-deps` +- **what it is built as** (`go`) — `-goos`, `-goarch`, `-goflags`, `-gowork`, + `-goexperiment`. Each decides what compiles, and so what the document says; + each is a flag rather than inherited state, so a scan is reproducible +- **how it is read** (`load`) — `-loader`, `-stub-stdlib`, + `-skip-compiled-dependencies` +- **what is emitted** (`emit`) — `-scan-models`, `-prune-unused-models`, the + alias and `allOf` knobs, `-skip-extensions`, the naming and doc-comment knobs + +They are the library's own options under their own names: a flag is the +kebab-case of the field it writes, without exception. The +[Options reference]({{% relref "options-reference" %}}) gives each one with its +field, its default and what it does. + +## Without a Go toolchain + +`genspec-wasi` is the same scan with nothing behind it: it depends on nothing +beyond the library, runs no subprocess, and cross-compiles to `wasip1/wasm`. It +is what you reach for where `genspec` cannot go — a sandbox, a CI image with no +Go in it, a WebAssembly runtime — and it is the engine behind the +[Playground]({{% relref "/playground" %}}). + +```cmd +go install github.com/go-openapi/codescan/cmd/genspec-wasi@latest +genspec-wasi -workdir ../my-api ./internal/api/... +``` + +It carries the same scan flags. What it does not carry is `genspec`'s document +and diagnostics surface: no `-validate`, no `-fail-on`, no `-color`. In their +place it offers an envelope. + +### An envelope for a program + +Document on stdout and prose on stderr is what a person or a pipeline wants. It +is not enough for a program: prose carries a position only in the sense that one +is printed in it, and provenance — which Go construct produced this spec node — +has nowhere to go at all. `-format=json` writes one object instead: + +```json +{ + "spec": { "swagger": "2.0", "definitions": { "doc": {} } }, + "diagnostics": [ + {"severity": "hint", "code": "validate.dropped-ref-sibling", + "message": "field \"for\": description dropped …", + "file": "models/m.go", "line": 12, "col": 5} + ], + "provenance": [ + {"pointer": "/definitions/doc", "file": "models/m.go", "line": 8, "col": 6} + ], + "runtime": { "sys": 390070272, "heapAlloc": 369098752, "collections": 2 } +} +``` + +Positions are relative to `-workdir`, so a caller holds `models/pet.go` and need +not know what the guest called it; a position *outside* the module stays +absolute, which is how a consumer tells the two apart. Provenance covers anchors +— type declarations, fields, values, route and meta blocks — sorted by pointer, +so the same scan produces the same bytes. Under `-format=json` standard error +stays clean, which is what makes the envelope safe to read from a pipe. + +Experimental, inheriting the status of `Options.OnProvenance`. + +### Under a WASI runtime + +```cmd +GOOS=wasip1 GOARCH=wasm go build -o genspec-wasi.wasm ./cmd/genspec-wasi + +# wasmtime — :: +wasmtime run --dir "$PWD::$PWD" genspec-wasi.wasm -workdir "$PWD" ./... + +# wazero — :, no separator +wazero run -mount="$PWD:$PWD" genspec-wasi.wasm -workdir "$PWD" ./... +``` + +Two things a guest cannot work out for itself: + +- **`-goos` / `-goarch` must be passed.** Left alone they default to the + platform the scanner is *running* on, which inside a guest is `wasip1` — which + silently drops every `_linux.go` file and produces a different document than + the same scan run natively. +- **GOROOT and the module cache are found by path.** Nothing in a WASI + environment can ask the go command where they live, so if the scan needs them + they must be mounted *and* named through the environment. + +### How much of the host to expose + +The real choice is what the guest may see. Measured on the petstore fixture +under wasmtime: + +| mounted | mode | time | peak RSS | result | +|---------|------|------|----------|--------| +| GOROOT + module cache | default | 7.3 s | 681 MB | identical to a `go list` scan | +| module cache | `-export-data` | 1.0 s | 138 MB | identical | +| module cache | `-stub-stdlib` | 1.0 s | 147 MB | degraded | +| project tree only | `-stub-stdlib` | 0.1 s | 123 MB | degraded | + +Memory is usually the binding constraint rather than time — 681 MB for a fixture +this small is more than a browser tab can host. + +`-export-data` reads dependencies from the export data the compiler already +produced instead of parsing and type-checking them. It costs a fraction of the +time with **no** loss of fidelity, the types being the compiler's own; it is +valid only for the toolchain that produced it. `-stub-stdlib` fabricates +standard-library types from the names the code selects, needs no GOROOT at all, +and is the one option here that is **not** failsafe: recognition by identity +survives (`time.Time` is still a `date-time`) but structure does not, so +`json.RawMessage` stops rendering as a byte array and nothing is seen to +implement `encoding.TextMarshaler`. Every synthesized import raises a +`scan.synthesized-import` diagnostic, so the loss is never silent. + +Full detail, including a build that embeds its own export data and needs nothing +mounted but the project, is in the +[`genspec-wasi` README](https://github.com/go-openapi/codescan/blob/master/cmd/genspec-wasi/README.md). + +## Next + +- [Setting an option]({{% relref "setting-options" %}}) — the configuration file + in full, and which spelling wins. +- [Options reference]({{% relref "options-reference" %}}) — every flag with the + library field it writes. +- [Usage as a terminal UI]({{% relref "usage-as-a-tui" %}}) — the same scan with + the source and the document side by side, while you write the annotations. diff --git a/docs/doc-site/getting-started/usage-as-a-library.md b/docs/doc-site/getting-started/usage-as-a-library.md index 3257a3b5..cdc896b7 100644 --- a/docs/doc-site/getting-started/usage-as-a-library.md +++ b/docs/doc-site/getting-started/usage-as-a-library.md @@ -2,22 +2,33 @@ title: Usage as a library weight: 1 description: | - Import codescan, annotate a package, and produce a Swagger 2.0 specification - from your Go program. + Import codescan, annotate a package. + + Produce a Swagger 2.0 specification from your Go program. --- -The most direct way to use codescan is to import it and call `Run` from your -own Go program — a generator, a `go:generate` step, or a test that keeps your -spec in sync with the source. +The most direct way to use codescan from your own Go program is to import it and call `Run`. + +This supports various use-cases such as a generator, a `go:generate` step, or a test that keeps your spec in sync with the source. + +## Install + +```cmd +go get github.com/go-openapi/codescan +``` + +codescan exposes a deliberately small surface: a single `Run` function and an `Options` struct. + +```go +func Run(opts *Options) (*spec.Swagger, error) +``` ## Annotate your source Annotations are special comments following the [go-swagger][go-swagger] -convention (`swagger:meta`, `swagger:route`, `swagger:model`, -`swagger:parameters`, `swagger:response`, …). +convention (`swagger:meta`, `swagger:route`, `swagger:model`, `swagger:parameters`, `swagger:response`, …). -A package-level `swagger:meta` block carries the top-level metadata of the -spec: +A package-level `swagger:meta` block carries the top-level metadata of the spec: {{< code file="petstore/doc.go" lang="go" lines="3-19" >}} @@ -28,15 +39,13 @@ comments become validations and descriptions: ## Run the scanner -Point codescan at the package(s) to scan. Patterns are relative `go list`-style -patterns, resolved against `WorkDir`: +Point codescan at the package(s) to scan. Patterns are relative `go list`-style patterns, resolved against `WorkDir`: {{< code file="basic/scan.go" lang="go" region="runScan" >}} The returned `*spec.Swagger` is the standard [`github.com/go-openapi/spec`](https://pkg.go.dev/github.com/go-openapi/spec) -document — marshal it to JSON or YAML, feed it to a validator, or merge it onto -an existing spec via `Options.InputSpec`. +document — marshal it to JSON or YAML, feed it to a validator, or merge it onto an existing spec via `Options.InputSpec`. ## Options worth knowing @@ -45,21 +54,30 @@ an existing spec via `Options.InputSpec`. | `Packages` | Relative `go list` patterns to scan (e.g. `./...`). | | `WorkDir` | Directory the patterns resolve against. | | `ScanModels` | Also emit definitions for `swagger:model` types. | +| `PruneUnusedModels` | With `ScanModels`, drop what nothing references — see [Pruning unused models]({{% relref "pruning-unused-models" %}}). | | `InputSpec` | Overlay: merge discoveries on top of an existing spec. | | `BuildTags`, `Include`/`Exclude` | Scope control over what gets scanned. | -| `RefAliases`, `TransparentAliases` | Alias-handling knobs. | -| `EmitRefSiblings` | Emit a `$ref`'d field's description and extensions as direct `$ref` siblings instead of an `allOf` wrap — see [Descriptions beside a $ref]({{% relref "descriptions-beside-a-ref" %}}). | -| `SkipAllOfCompounding` | Never wrap a `$ref`'d field in an `allOf`; emit a bare `$ref` and drop the decorations that need a compound — see [Descriptions beside a $ref]({{% relref "descriptions-beside-a-ref" %}}). | -| `DescWithRef` | _Deprecated_ — preserve a description-only `$ref` field via a single-arm `allOf`; prefer `EmitRefSiblings`. | -| `SkipExtensions` | Suppress `x-go-*` vendor extensions. | -| `SkipEnumDescriptions` | Keep the `swagger:enum` const→value mapping out of property/parameter descriptions (it still rides `x-go-enum-desc`). | -| `EmitXGoType` | Stamp `x-go-type` (the fully-qualified Go type) on every definition — see [Vendor extensions]({{% relref "vendor-extensions#stamping-x-go-type" %}}). | -| `SingleLineCommentAsDescription` | Route single-line comments to `description` instead of `title`/`summary` — see [Single-line comments]({{% relref "single-line-comments" %}}). | +| `OnDiagnostic` | Where everything the scan observed goes. codescan writes to no stream of its own, so without this the observations are lost. | + +Those are the ones a first call tends to need. Everything else — alias handling, +`$ref` siblings, naming, doc-comment cleanup, the loader, the go environment — +is in the [Options reference]({{% relref "options-reference" %}}), which gives +each field with its command-line and configuration-file spellings beside it. The +[godoc][godoc] is the normative source. + +## Dependencies + +Code loading relies by default on the go toolchain and this requires go to be installed. -See the [godoc][godoc] for the full list. +To alleviate this constraint, you may want to use the pure-go `ToolchainFreeLoader` loader in your options, +so programs that build on top of codescan don't shell out a `go list` command. ## Next +- [Scan a package]({{% relref "scan-a-package" %}}) — the whole of the above as + one runnable, test-covered example. +- [Usage as a headless CLI]({{% relref "usage-as-a-headless-cli" %}}) — the same + scan without writing a program, for a build or a pipeline. - [Tutorials]({{% relref "/tutorials" %}}) — the worked, by-concept version of the above, each with the spec it produces. - [Annotation index]({{% relref "/annotation-index" %}}) — every annotation at a diff --git a/docs/doc-site/getting-started/usage-as-a-tui.md b/docs/doc-site/getting-started/usage-as-a-tui.md index fe28c889..085c3d6c 100644 --- a/docs/doc-site/getting-started/usage-as-a-tui.md +++ b/docs/doc-site/getting-started/usage-as-a-tui.md @@ -1,31 +1,32 @@ --- title: Usage as a terminal UI -weight: 2 +weight: 10 description: | - Drive codescan interactively: browse an annotated source tree, watch the spec - it produces re-render on every save, and follow any node back to the code that - made it. + Drive codescan interactively: browse an annotated source tree, watch the spec it produces. + + It re-renders on every save, and follow any node back to the code that made it. --- -`genspec-tui` is an interactive terminal front-end for codescan. It puts the Go -source on the left, the Swagger document that source produces on the right, and -the scanner's diagnostics underneath — all regenerated every time you save. +`genspec-tui` is an interactive terminal front-end for codescan. + +It puts the Go source on the left, the Swagger document that source produces on the right, +and the scanner's diagnostics underneath — all regenerated every time you save. + +Its reason to exist is that loop: **change an annotation, save, see the spec change.** -Its reason to exist is that loop: **change an annotation, save, see the spec -change.** Predicting what an annotation will produce is the slow part of writing -one, and reading a golden file after a build is a poor substitute for watching -the node appear. +Predicting what an annotation will produce is the slow part of writing one, +and reading a golden file after a build is a poor substitute for watching the node appear. -Beyond the loop, it links the two sides together. You can ask "which Go -declaration produced this node?" and "what did this field turn into?" and get an -answer by *position* — not by matching names by eye. +Beyond the loop, it links the two sides together. You can ask "which Go declaration produced this node?" or +"what did this field turn into?" and get an answer by *position* — not by matching names by eye. ![An open Go file on the left, the spec it produces on the right, and the scan's diagnostics underneath](images/tui/scan.png) {{% notice style="note" %}} -The TUI is a **separate Go module** inside the codescan repository, so bubbletea -and its dependency tree never reach the lean library. Installing it pulls none of -that into your own project. +The TUI is a separate Go module inside the codescan repository, so bubbletea and +its dependency tree never reach the library — installing it pulls none of that +into your own project. Why the commands are split that way is in +[The commands]({{% relref "commands" %}}). {{% /notice %}} ## Install and run @@ -39,17 +40,24 @@ go install github.com/go-openapi/codescan/cmd/genspec-tui@latest genspec-tui # or point it somewhere, and narrow the scope -genspec-tui -workdir ../my-api -packages ./internal/models/...,./internal/api/... +genspec-tui -workdir ../my-api ./internal/models/... ./internal/api/... ``` -The flags carry what a checkbox cannot — the scan's scope and the names it -derives — from the same [`codescan.Options`]({{% relref "options" %}}) the library -takes: +The selected packages are a positional argument, resolved against `-workdir`; naming none scans `./...`. + +> `-packages`, taking one comma-separated list, is the older spelling and still works + +The TUI registers the **same flags as the other commands** — one per field of +[`codescan.Options`]({{% relref "options-reference" %}}) — and reads the same +[`.codescan.yaml`]({{% relref "setting-options" %}}), so a session starts where +your build leaves off. What the flags decide is the *first* scan; almost all of +them are also live toggles, below. + +The ones worth knowing at the point of starting a session: | Flag | Default | Meaning | |------|---------|---------| | `-workdir` | `.` | module directory the scan runs in (`WorkDir`) | -| `-packages` | `./...` | comma-separated package patterns, relative to `-workdir` | | `-scan-models` | `true` | also emit definitions for `swagger:model` types | | `-build-tags` | — | comma-separated build tags to apply while loading | | `-include` / `-exclude` | — | patterns selecting which packages are scanned | @@ -70,18 +78,18 @@ A second group settles what gets built, and how it is loaded — the environment | `-goflags` | — | default go command flags, as `GOFLAGS` — `-build-tags` wins over a `-tags` given here | | `-gowork` | search upwards | workspace selection, as `GOWORK`: `off` to ignore a `go.work`, or the path to one | | `-goexperiment` | — | toolchain experiments, as `GOEXPERIMENT` | -| `-toolchain-free-loader` | `false` | load packages with codescan's own loader rather than the go command (experimental) | -| `-stub-stdlib` | `false` | synthesize the standard library instead of reading GOROOT (needs `-toolchain-free-loader`) | +| `-loader` | `auto` | `go` runs `go list`; `own` uses codescan's own loader and needs no toolchain; `auto` picks `own` wherever the build cannot exec (experimental) | +| `-stub-stdlib` | `false` | synthesize the standard library instead of reading GOROOT (needs `-loader=own`) | -Everything else is a **live toggle** rather than a flag. Press `o` for the options +Everything the flags set is also a **live toggle**. Press `o` for the options popup, `space` to flip a row, `Esc` to apply — the spec re-renders on close, which makes the popup the fastest way to find out what a knob such as `EmitRefSiblings` actually changes. Rows that only bite in combination say so: `PruneUnusedModels` reads `(needs ScanModels)` until that one is on. -The three booleans above are in both places, so you can start a session one way -and change your mind without restarting. The one option with no route in at all is -`InputSpec` (overlay mode). +Being in both places is the point: you can start a session one way and change your +mind without restarting. The one option with no route in at all is `InputSpec` +(overlay mode). ## Scanning: the edit-save-see loop @@ -196,6 +204,57 @@ document missing its `info` block lands on the document, which `Enter` takes you to the top of. {{% /notice %}} +## What a scan cost: `m` + +`m` opens a card describing the run that just finished — how long it took, what +it allocated, what it left live, and how much the process holds from the OS. + +![The run-cost card: a split line recapping time and memory as ratios, then elapsed, allocated, retained, live objects, GC cycles and memory held from the OS — each split between scanning and rendering](images/tui/stats.png) + +Time and memory are split between **scanning** and **rendering** the document, +and recapped as ratios on the `split` line, because the question a reader +arrives with is usually *which phase is this?* The two can disagree sharply, +which is why both are there. + +**Allocated** is what the run churned through, garbage included; **retained** is +what it left behind. A scan that allocates half a gigabyte and retains a few +megabytes is not the same problem as one that keeps what it takes, and one +number could not tell you which you have. + +Two things the card says about itself, worth repeating: the window is +process-wide, so the redraw loop and the file watcher are in the figures; and a +rescan holds two documents at once, so the retained figure reads high by about +one. That is arithmetic, not a leak. + +A scalar cannot say *who* spent it, which is what `-profile` is for. Start the +session with it and each scan is profiled as well as timed, so the card also +reports where the CPU went and what allocated it, by function and per phase: + +```cmd +genspec-tui -profile -workdir ../my-api + +# every allocation counted rather than sampled every 512 KiB — accurate, and slow +genspec-tui -profile -mem-profile-rate=1 -workdir ../my-api +``` + +![The profiled card: the same figures plus a cpu ratio, then a "where the CPU went" table charging each sample to the call of ours that led there, and a "what allocated it" table by function](images/tui/profile.png) + +CPU is charged to **the call of ours that led there**, not to the leaf frame. A +leaf answers "what was executing", which for this program is mostly the +allocator and the collector — true, and nothing anyone can act on. Charged our +way, a row names the boundary where codescan hands the work to somebody else's +code, and covers everything under it. + +Profiling is not free, and the card says so rather than letting you compare +across runs by accident: under `-profile` the scalars at the top carry the +profiler's own overhead and its collections, while the tables below exclude +them. The card scrolls when it outgrows the terminal (`↑↓`/`jk`, `PgUp`/`PgDn`, +`Home`/`End`). + +It names the `.pprof` files it wrote and the `go tool pprof` commands that open +them. All three flags are addressed in the `profile` section of a +[`.codescan.yaml`]({{% relref "setting-options" %}}). + ## Looking up an annotation ![The annotation reference popup: the annotation, what it does, its syntax, and the keywords its body accepts](images/tui/annotation-reference.png) @@ -234,6 +293,7 @@ header carries a standing `h: help` banner. | `K` | what the `swagger:` annotation on this line means | | `v` / `V` | validate the spec / switch diagnostics tab | | `o` | scanner options | +| `m` | what the last scan cost | | `r` / `F5` | rescan now / re-read the open file from disk | | `c` | copy the focused pane to the clipboard | | `ctrl+q` | quit | @@ -273,8 +333,9 @@ change up. do not. Where they are missing nothing misfires — there is simply no previous-reference key, and no resize keys. - **Split sizes last for the session only.** They survive rescans and terminal - resizes, but not a restart; persisting them needs a config file, which the TUI - does not have. + resizes, but not a restart. The TUI does read a + [`.codescan.yaml`]({{% relref "setting-options" %}}) — but what a file can set + is flags, and where the splits sit is not one of them. The [module README][tui-readme] carries the full list. @@ -283,7 +344,9 @@ carries the full list. - [Usage as a library]({{% relref "usage-as-a-library" %}}) — drive the same scanner from your own program, a `go:generate` step, or a test. -- [Options reference]({{% relref "options" %}}) — every knob the `o` popup +- [Usage as a headless CLI]({{% relref "usage-as-a-headless-cli" %}}) — the same + scan in a build, with the settings you converged on here. +- [Options reference]({{% relref "options-reference" %}}) — every knob the `o` popup toggles, and what it does to the document. - [Tutorials]({{% relref "/tutorials" %}}) — annotate a package from meta to definitions, with the TUI open beside you. diff --git a/docs/doc-site/maintainers/_index.md b/docs/doc-site/maintainers/_index.md index c00bfe4d..9f3643f0 100644 --- a/docs/doc-site/maintainers/_index.md +++ b/docs/doc-site/maintainers/_index.md @@ -1,17 +1,22 @@ --- title: Maintainers -weight: 50 +weight: 100 description: | - The complete, normative reference for the codescan annotation language — - every annotation, every keyword, the embedded sub-languages, and the formal - grammar the parser implements. + The complete, normative reference for the codescan annotation language. + + Every annotation, every keyword, the embedded sub-languages, the formal + grammar the parser implements, and how the commands are put together. --- This section is the **reference compendium**: the precise, exhaustive -description of the language codescan parses and the options that drive it. It is -written for people who want the full contract — annotation authors looking up an -exact rule, library callers looking up an option, and contributors porting, -extending, or debugging the parser. +description of the language codescan parses, and of the tools built around it. +It is written for people who want the full contract — annotation authors looking +up an exact rule, and contributors porting, extending, or debugging the parser. + +Looking up an *option* rather than an annotation? That moved: the +[Options reference]({{% relref "options-reference" %}}) lives under +[Usage]({{% relref "/usage" %}}), beside the flag and configuration-key +spellings of the same knobs. If you are learning codescan by example, start with the [Tutorials]({{% relref "/tutorials" %}}) instead — they show the same concepts @@ -29,11 +34,11 @@ annotation to both its tutorial and its entry here. - **[Keywords]({{% relref "keywords" %}})** — the per-keyword reference card: every `keyword: value` form, its value shape, and the contexts where it is legal. -- **[Options]({{% relref "options" %}})** — every field of `codescan.Options`: - its type, default, and effect, cross-linked to the how-to that shows it in - action. The library-caller reference. - **[Sub-languages]({{% relref "sub-languages" %}})** — the smaller languages embedded inside annotation bodies (`Parameters:` / `Responses:` grammars, YAML surfaces, prose classification). - **[Grammar]({{% relref "grammar" %}})** — the formal ISO-14977 EBNF the parser implements, from comment preprocessing through the typed walker. +- **[The commands]({{% relref "commands" %}})** — how the three CLI tools are put + together: why each lives where it does, where their shared flag surface is + declared, and what keeps it whole. diff --git a/docs/doc-site/maintainers/commands.md b/docs/doc-site/maintainers/commands.md new file mode 100644 index 00000000..9696e151 --- /dev/null +++ b/docs/doc-site/maintainers/commands.md @@ -0,0 +1,106 @@ +--- +title: "The commands" +weight: 50 +description: | + How the three CLI tools are put together. + + Why there are three of them, where their shared flag surface is declared, and what keeps it whole. +--- + +Three commands ship from this repository — [`genspec`]({{% relref "usage-as-a-headless-cli" %}}), +[`genspec-tui`]({{% relref "usage-as-a-tui" %}}) and `genspec-wasi` — and they run +the same scan. This page is about how they are arranged, which matters to anyone +adding an option, adding a command, or wondering why the library stays as light +as it does. + +For what the flags and configuration keys *mean*, see +[Setting options]({{% relref "setting-options" %}}) and the +[Options reference]({{% relref "options-reference" %}}). + +## Why three, and where each one lives + +The split is about dependencies, not about features. + +| Command | Module | Because | +|---------|--------|---------| +| `genspec-wasi` | the **main** module | it must add nothing to what the library already needs — that is what lets it cross-compile to `wasip1/wasm` and run with no toolchain and no subprocess | +| `genspec` | its **own** module | it takes koanf, for the configuration sources it will be asked for next | +| `genspec-tui` | its **own** module | it takes bubbletea and the tree that comes with it | + +So installing the terminal UI pulls none of bubbletea into *your* project, and a +program importing the library gets neither. A command that needed a dependency +the library does not want is a reason to give it a module, not a reason to argue +about the dependency. + +{{% notice style="note" %}} +The submodules are tagged and have no `replace` directive, so +`go install github.com/go-openapi/codescan/cmd/genspec-tui@latest` really works +from a clean environment. +{{% /notice %}} + +## One flag surface, declared once + +Every knob the library takes is declared as a flag in **`cmd/internal/cliopts`**, +once, and each command registers the whole of it. That is what makes +`-name-from-tags` mean the same thing whichever one you reach for, and what makes +a knob added to the library reachable from all three at the same moment. + +Written per command instead, the mapping would be partial in a different way each +time, and an option added to `Options` would reach whichever command somebody +remembered. + +Three properties hold it together: + +- **Entries are keyed by the field's setter**, never by a name derived from the + field. Naming the field in a string would let a rename pass the compiler and + leave the flag writing nowhere. +- **A guard in the package tests fails** when a value-typed option lands with no + flag — or no recorded excuse. A caller cannot use an option that has no flag, + and would find out by meeting `flag provided but not defined` after writing + something against a surface that was never there. The pull request that adds + the option is a better place to find out. +- **A flag is the kebab-case of the field**, without exception. No rule can derive + that `JSONify` is one word where `HTTPServer` is two, so coverage is decided by + writing through a setter and seeing what moved — never by mangling a name. + +The package sits under `cmd/` rather than under the root `internal/`, which is +what lets the commands that are modules of their own import it. + +Options that are not values — a filesystem, a document to merge into, the +callbacks — are deliberately absent from the table. Those are the command's +business, and the tests carry the list with a reason for each. See +[What has no flag]({{% relref "setting-options#what-has-no-flag" %}}). + +## One configuration contract, two readers + +**`cmd/internal/cliconf`** owns where a `.codescan.yaml` is found, what may be in +it, and how it loses to anything typed — and nothing else. The values themselves +arrive as a plain flat map, so how they are *read* stays the command's business: + +- `genspec` feeds the file through **koanf**, for the environment variables and + further formats it will be asked for next; +- `genspec-tui` — which reads one file, once, to decide what a session starts + with — calls **`cliconf.Parse`**, which needs nothing this repository does not + already have. + +That seam is why the package can be shared with a command that must +cross-compile to WebAssembly: `cliconf.YAML` satisfies koanf's parser interface +*structurally*, so the package owes koanf no import. + +Sections come from `cliopts.ConfigSchema()` merged with each command's own, which +is why a section one command does not know is skipped rather than refused, and a +key inside a section it *does* know must name one of its flags. + +Everything a file sets lands through `flag.FlagSet.Set` — the same path the +command line takes — so a value is parsed and validated exactly once, and a file +cannot express anything an argument could not. + +## See also + +- [Setting options]({{% relref "setting-options" %}}) — the same contract from + the reader's side. +- [`genspec` README](https://github.com/go-openapi/codescan/blob/master/cmd/genspec/README.md), + [`genspec-tui` README](https://github.com/go-openapi/codescan/blob/master/cmd/genspec-tui/README.md), + [`genspec-wasi` README](https://github.com/go-openapi/codescan/blob/master/cmd/genspec-wasi/README.md) + — each command's own deep documentation, including the TUI's full keymap and the + WASI mount and export-data recipes. diff --git a/docs/doc-site/maintainers/options.md b/docs/doc-site/maintainers/options.md deleted file mode 100644 index 5f2fffd0..00000000 --- a/docs/doc-site/maintainers/options.md +++ /dev/null @@ -1,180 +0,0 @@ ---- -title: "Options reference" -weight: 25 -description: | - Every field of codescan.Options — its type, default, and effect — grouped by - concern and cross-linked to the how-to that shows it in action. ---- - -[`codescan.Options`](https://pkg.go.dev/github.com/go-openapi/codescan#Options) -is the single configuration struct passed to -[`codescan.Run`](https://pkg.go.dev/github.com/go-openapi/codescan#Run). **The -zero value is a valid configuration** — every flag defaults to `false`, every -slice/map to `nil`, every numeric tunable to its built-in default. You set only -what you need. - -This page is the field-by-field catalogue. The -[godoc](https://pkg.go.dev/github.com/go-openapi/codescan#Options) is the -normative source; each field here links to the -[how-to guide]({{% relref "/shaping-the-output" %}}) that shows it on real -input where one exists. - -{{% notice style="note" %}} -codescan never writes to stdout or stderr. Every scan-time observation — a -dropped construct, a rename, a prune — flows through the `OnDiagnostic` -callback. See [Diagnostics & observability](#diagnostics--observability) below. -{{% /notice %}} - -## Inputs & scope - -What gets loaded and which packages and types are in play. See -[Scope & discovery]({{% relref "/shaping-the-output/scope-and-discovery" %}}). - -| Option | Type | Default | Effect | -|--------|------|---------|--------| -| `Packages` | `[]string` | `nil` | Package patterns to scan (e.g. `./...`), resolved relative to `WorkDir`. | -| `WorkDir` | `string` | `""` (cwd) | Working directory the package patterns and module resolution are rooted at. | -| `BuildTags` | `string` | `""` | Go build tags to activate while loading, so tag-guarded source is scanned. See [Build tags]({{% relref "build-tags" %}}). | -| `Include` | `[]string` | `nil` | Allow-list of package path patterns; when non-empty only matching packages are scanned. See [Scoping the scan]({{% relref "scoping-the-scan" %}}). | -| `Exclude` | `[]string` | `nil` | Deny-list of package path patterns, applied after `Include`. See [Scoping the scan]({{% relref "scoping-the-scan" %}}). | -| `IncludeTags` | `[]string` | `nil` | Allow-list filtering routes/operations by their swagger `tags`. | -| `ExcludeTags` | `[]string` | `nil` | Deny-list filtering routes/operations by their swagger `tags`. | -| `ExcludeDeps` | `bool` | `false` | Skip types reached through module dependencies, keeping the scan to first-party packages. | -| `ScanModels` | `bool` | `false` | Also emit a definition for every `swagger:model` type, not just route-reachable ones. See [When the scanner emits a type]({{% relref "type-discovery" %}}). | -| `PruneUnusedModels` | `bool` | `false` | With `ScanModels`, drop discovered definitions not transitively reachable from a path, shared response/parameter, or `InputSpec` root. Runs before name reduction; `InputSpec` definitions are pinned. No-op without `ScanModels`. See [Pruning unused models]({{% relref "pruning-unused-models" %}}). | -| `InputSpec` | `*spec.Swagger` | `nil` | Base document to overlay scanned discoveries onto; its definitions are pinned and seed pruning roots. See [Overlaying a spec]({{% relref "overlaying-a-spec" %}}). | - -## Loading & the go environment - -Where the package graph comes from, and which platform it is built for. These -change the emitted spec the way `BuildTags` does — by deciding which files each -package is made of — or change what the scan needs in order to run at all. - -They are options rather than inherited process state so that a scan is -reproducible: a value picked up from whatever shell started it is easy to apply on -one code path and forget on another. - -### Which loader, and why - -Three ways to get a package graph. The table below catalogues the fields; this is -how to choose between them. - -**Standard loader** — the default. Loads your code with the Go toolchain, through -`golang.org/x/tools/go/packages`. Maintained by the Go team, and the reference for -how patterns and imports resolve: where either of the others disagrees with it, the -other one is wrong. Requires Go installed, and uses the build cache. - -**Pure-Go loader** (`ToolchainFreeLoader`) — loads your code with codescan's own -reimplementation. Cuts memory by roughly 45%, and needs no `go` command and no -subprocess. It still reads `GOROOT/src` for the standard library, so it wants a Go -*installation* — just not a runnable toolchain. Modules only. It uses no build -cache, so cold costs what warm costs: about level with the standard loader on a -warm cache, roughly 30% faster on a cold one, and the only choice whose cost does -not depend on cache state. Usually the right pick for CI. - -**Compiled dependencies** — since v0.36.4 the standard loader takes dependency types -from the compiler's export data instead of reading their source. It produces the -same document either way — whatever the spec needs out of a dependency is read at -the moment it is needed — and on a warm build cache it is the fastest by a wide -margin, and several times smaller. - -It must *compile* the dependency closure rather than type-check it, so on a cold -cache it is an order of magnitude slower and writes a large build cache. That is -what `SkipCompiledDependencies` is for, and CI regenerating a spec from a clean -checkout is the case to reach for it. Code that does not compile is **not** a -reason: such a load is retried from source automatically. - -Two further options drop the `GOROOT` requirement altogether, for environments with -no Go installation at all — a WASI guest, a browser. `StubStdlib` synthesizes the -standard library, and pays for the reach in fidelity: a fabricated type has the -right name and no structure. `ExportData` serves dependencies from a blob you -prepare in advance, and pays in preparation instead — the types are the compiler's -own, but the blob is only valid for the toolchain that produced it, and a package -it does not cover falls back to source and then to synthesis. - -{{% notice style="note" %}} -The percentages are indicative, not a promise: the balance moves with the size of -the tree being scanned, and on a small one the pure-Go loader is *slower* warm than -the standard loader. Measure your own corpus with -[`hack/loader-benchmark`](https://github.com/go-openapi/codescan/tree/master/hack/loader-benchmark), -which compares a released version against a working tree and carries the tables and -the method. -{{% /notice %}} - -| Option | Type | Default | Effect | -|--------|------|---------|--------| -| `GOOS` / `GOARCH` | `string` | `""` (this machine) | The platform the scanned code is built for. `//go:build` lines and `_linux.go` / `_amd64.go` filename suffixes resolve against them, so they select which files a package is made of. | -| `GOFLAGS` | `string` | `""` (process env) | Default go command flags, e.g. `-tags=integration`. Flags given through `BuildTags` win, as they do for the go command. | -| `GOWORK` | `string` | `""` (search upwards) | Workspace selection: `off` disables it, a path names a `go.work`. Inside a workspace a sibling module resolves to the copy being worked on rather than to the module cache — miss that and its types are read stale, or synthesized empty. | -| `GOEXPERIMENT` | `string` | `""` (process env) | Toolchain experiments, e.g. `jsonv2`; each contributes a `goexperiment.` build tag. | -| `ToolchainFreeLoader` | `bool` | `false` | Resolve the package graph with codescan's own loader instead of `golang.org/x/tools/go/packages`. Same job and, across the fixture corpus, the same spec; it differs in needing no installed toolchain and no subprocess, since it never runs `go list`. **Experimental.** | -| `FS` | `fs.FS` | `nil` | Read source through a virtual filesystem — an in-memory tree, an uploaded archive, an `embed.FS` — instead of the real one. Implies `ToolchainFreeLoader`, since `go list` can only read the real filesystem. **`FS` is the whole world the scan can read**: dependencies and GOROOT come through it too, absolute paths map by dropping the leading separator, and anything unreachable is synthesized — a valid but quietly thinner spec, announced by `scan.synthesized-import` and `scan.degraded-load`. **Experimental.** | -| `StubStdlib` | `bool` | `false` | Synthesize the standard library from the names the code selects, rather than reading GOROOT. Toolchain-free loader only. Identity recognition still works (`time.Time`, `json.RawMessage` are matched on package and name), but a synthesized type has no fields and no method set — so `json.RawMessage` stops rendering as a byte array and nothing is seen to implement `encoding.TextMarshaler`. Trades fidelity for reach, quietly; prefer a full graph where GOROOT is available. **Experimental.** | -| `ExportData` | `fs.FS` | `nil` | Serve dependencies from pre-computed export data (one `.export` file per package) under the toolchain-free loader. Unlike `StubStdlib` this costs no fidelity, the types being the ones the compiler computed — but it is valid only for the toolchain that produced it, and an uncovered package falls back to source, then to synthesis. The module under scan is never read this way, and neither is a dependency whose source carries annotations or one the spec later needs a declaration from. **Experimental.** | -| `SkipCompiledDependencies` | `bool` | `false` | Read every dependency from source, instead of taking its types from the compiler's export data under the go/packages loader. Unset (the default since v0.36.4) costs no meaning: a dependency whose source carries annotations is read back after the load, and one that merely *declares* a type the spec carries is read at the lookup that wants it — so a `swagger:strfmt` written in a library still counts, and a model declared in an unannotated dependency keeps its doc comment and its fields. Set it for cost alone: the default is markedly faster on a warm build cache and markedly slower on a cold one, since `go list -export` compiles the closure before it can read it. A closure that does not compile is handled without this — the load falls back to source and raises `scan.compiled-dependencies`. | - -{{% notice style="note" %}} -The virtual-filesystem and export-data options exist to make a scan possible -where no Go toolchain is: they are what lets codescan run compiled to -WebAssembly. See the [Playground]({{% relref "playground" %}}). -{{% /notice %}} - -## Names & references - -How definitions are named and how `$ref`s render. See -[Names & `$ref`s]({{% relref "/shaping-the-output/names-and-refs" %}}). - -| Option | Type | Default | Effect | -|--------|------|---------|--------| -| `NameFromTags` | `[]string` | `nil` (⇒ `["json"]`) | Ordered struct-tag types a property/parameter/header name is derived from; first that supplies a name wins. Explicit empty slice ⇒ Go field name. Only the name — `json` encoding directives (`-`, `,omitempty`, `,string`) always come from `json`. See [Naming from struct tags]({{% relref "naming-from-tags" %}}). | -| `SkipJSONifyInterfaceMethods` | `bool` | `false` | Emit interface-method property names verbatim (`ID`, `CreatedAt`) instead of auto-jsonifying them (`id`, `createdAt`). Only affects interface methods; struct fields and `swagger:name` overrides are unchanged. See [Interface-method property names]({{% relref "interface-method-names" %}}). | -| `RefAliases` | `bool` | `false` | Render Go type aliases as a first-class `$ref` (via `swagger:model`) instead of expanding them inline. See [Alias rendering]({{% relref "alias-rendering" %}}). | -| `TransparentAliases` | `bool` | `false` | Make aliases fully transparent — never creating a definition. See [Alias rendering]({{% relref "alias-rendering" %}}). | -| `DefaultAllOfForEmbeds` | `bool` | `false` | Render a plain (untagged, unnamed) struct embed as an `allOf` member — a `$ref` for a model embed, an inline member otherwise — with the embedding struct's own fields in a sibling member, instead of inlining promoted properties. json-named embeds, `swagger:allOf` embeds, and interface embeds are unaffected. See [Composing embeds with allOf]({{% relref "composing-embeds-with-allof" %}}). | -| `NameConcatBudget` | `float64` | `0` (⇒ `0.65`) | Readability cutoff `[0,1]` for the package-segment concatenation that deconflicts colliding definition names; lower scores are more readable. A group whose best concat scores above the budget is a candidate for the hierarchical fallback. See [Resolving `$ref` name conflicts]({{% relref "resolving-name-conflicts" %}}). | -| `EmitHierarchicalNames` | `bool` | `false` | For the rare collision group whose best flat concat exceeds `NameConcatBudget`, emit nested container definitions (`#/definitions//`) instead of a long flat concat, with an explanatory diagnostic. The always-correct flat concat is the default. See [Resolving `$ref` name conflicts]({{% relref "resolving-name-conflicts" %}}). | -| `EmitRefSiblings` | `bool` | `false` | Emit a `$ref`'d field's description and vendor extensions as direct `$ref` siblings (`{$ref, description, x-*}`) instead of an `allOf` wrap. Validations/externalDocs still force a compound. See [Descriptions beside a `$ref`]({{% relref "descriptions-beside-a-ref" %}}). | -| `SkipAllOfCompounding` | `bool` | `false` | Never emit an `allOf` compound for a `$ref`'d field. Validations/externalDocs are dropped (description/extensions too, unless `EmitRefSiblings` keeps them as siblings); each drop raises a diagnostic. `required` is unaffected. See [Descriptions beside a `$ref`]({{% relref "descriptions-beside-a-ref" %}}). | -| `DescWithRef` | `bool` | `false` | **Deprecated** — prefer `EmitRefSiblings`. In the description-only case, wrap the `$ref` in a single-arm `allOf` to preserve the description (strict draft-4 shape). No-op when `EmitRefSiblings` is set. See [Descriptions beside a `$ref`]({{% relref "descriptions-beside-a-ref" %}}). | - -## Titles & descriptions - -The human-readable text the spec carries. See -[Titles & descriptions]({{% relref "/shaping-the-output/titles-and-descriptions" %}}). - -| Option | Type | Default | Effect | -|--------|------|---------|--------| -| `SingleLineCommentAsDescription` | `bool` | `false` | Route every single-line doc comment to `description`, never to `title`/`summary` (the first-sentence convention otherwise applies). Multi-line comments keep the title/description split. See [Single-line comments as descriptions]({{% relref "single-line-comments" %}}). | -| `AfterDeclComments` | `bool` | `false` | Let swagger annotations live inside a struct body or as a trailing comment, in addition to the doc comment above the declaration, so the godoc stays clean. v0.36 scope: type declarations (struct inside-body + alias trailing comment). See [Keeping annotations out of the godoc]({{% relref "keeping-annotations-out-of-the-godoc" %}}). | -| `CleanGoDoc` | `bool` | `false` | Strip godoc doc-link brackets from generated `title`/`description` (humanizing unresolved ones, dropping reference-definition lines, recomposing resolved links to each schema's exposed name). Applies only to godoc-derived prose; overrides are untouched. See [Cleaning godoc doc-links]({{% relref "cleaning-godoc-doc-links" %}}). | - -## Field types, formats & extensions - -How an individual property renders. See -[Field types & formats]({{% relref "/shaping-the-output/field-types-and-formats" %}}). - -| Option | Type | Default | Effect | -|--------|------|---------|--------| -| `SetXNullableForPointers` | `bool` | `false` | Emit `x-nullable: true` on pointer-typed fields. See [Nullable pointers]({{% relref "nullable-pointers" %}}). | -| `SkipExtensions` | `bool` | `false` | Suppress all `x-go-*` vendor extensions in the output. See [Vendor extensions]({{% relref "vendor-extensions" %}}). | -| `EmitXGoType` | `bool` | `false` | Stamp an `x-go-type` extension (fully-qualified originating Go type) on every emitted definition, for round-tripping a spec back to its Go types. Suppressed under `SkipExtensions`. See [Vendor extensions]({{% relref "vendor-extensions" %}}). | -| `SkipEnumDescriptions` | `bool` | `false` | Keep the per-enum-value const-name mapping (from `swagger:enum`) out of the `description`, exposing it only via the `x-go-enum-desc` extension. Suppressed entirely under `SkipExtensions`. | - -## Diagnostics & observability - -Channels for what the scan observed; these do not change the output spec. - -| Option | Type | Default | Effect | -|--------|------|---------|--------| -| `OnDiagnostic` | `func(Diagnostic)` | `nil` | Invoked once per diagnostic in source order (parser warnings, validation failures, prunes, renames). Diagnostics never block the build — invalid constructs are dropped from the spec while their explanation flows here. The only output channel. **Experimental** while LSP integration matures. | -| `OnProvenance` | `func(Provenance)` | `nil` | Invoked once per anchor node in the produced spec, carrying its JSON pointer and the source position of the Go construct that produced it. Never blocks the build. **Experimental** while LSP/TUI integration matures. | -| `Debug` | `bool` | `false` | **Deprecated, ignored.** The legacy stderr debug logger was retired; wire `OnDiagnostic` instead. Retained for API compatibility. | - -## See also - -- [Annotations]({{% relref "annotations" %}}) — the `swagger:*` vocabulary the - scanner reads from comments. -- [Keyword reference]({{% relref "keywords" %}}) — the `keyword: value` forms - inside annotation bodies. -- [Shaping the output]({{% relref "/shaping-the-output" %}}) — task-oriented - how-tos that put these options to work on real input. diff --git a/docs/doc-site/playground.md b/docs/doc-site/playground.md index 4599987f..dd22561a 100644 --- a/docs/doc-site/playground.md +++ b/docs/doc-site/playground.md @@ -1,34 +1,35 @@ --- title: "Playground" -weight: 3 +weight: 15 description: | - Scan Go source in your browser: edit annotations, watch the specification - change, and follow a spec node back to the code that produced it. + Scan Go source in your browser. + + Edit annotations, watch the specification change, and follow a spec node back to the code that produced it. --- -Everything below runs in this tab. There is no server: the scanner is `codescan` -itself, compiled to WebAssembly, and the source you open never leaves your -browser. +Everything below runs in this tab. There is no server: the scanner is `codescan` itself, compiled to WebAssembly. + +The source you open never leaves your browser. {{% notice style="note" title="Experimental" %}} -Offered for demonstration. It follows [`genspec-tui`](https://github.com/go-openapi/codescan/tree/master/cmd/genspec-tui) -closely, and its interface is checked by hand rather than by tests. +Offered for demonstration. +It follows [`genspec-tui`](https://github.com/go-openapi/codescan/tree/master/cmd/genspec-tui) closely, +and its interface is checked by hand rather than by tests. {{% /notice %}} {{< playground >}} ## What to try -The **Examples** menu carries five modules, each one whole and each scanning as -it stands: +The **Examples** menu carries five modules, each one whole and each scanning as it stands: -| | shows | -|---|---| -| Models | a struct becoming a definition: validations, an enum, an example, a `$ref` | -| Routes | `swagger:route` with its parameters and responses | -| Operation | `swagger:operation`, where you write the OpenAPI directly in YAML | -| Enums | a Go constant set becoming an enum, typed from the declaration | -| Polymorphism | a discriminated base and its subtypes under `swagger:allOf` | +| | shows | +|--------------|----------------------------------------------------------------------------| +| Models | a struct becoming a definition: validations, an enum, an example, a `$ref` | +| Routes | `swagger:route` with its parameters and responses | +| Operation | `swagger:operation`, where you write the OpenAPI directly in YAML | +| Enums | a Go constant set becoming an enum, typed from the declaration | +| Polymorphism | a discriminated base and its subtypes under `swagger:allOf` | Edit anything on the left and it rescans after a pause. @@ -37,14 +38,12 @@ highlights what that line produced; put it on a spec line and the source highlights what produced it; click a diagnostic and both light up. It answers by position rather than by matching names, which is why it survives a rename. -One direction is exact and the other is not. The scanner records where a field -*starts* and not where it ends, so a cursor sitting in a doc comment is -attributed to the nearest anchor, ties resolving downwards because Go -documentation sits above what it documents. +One direction is exact and the other is not. The scanner records where a field *starts* and not where it ends, +so a cursor sitting in a doc comment is attributed to the nearest anchor, +ties resolving downwards because Go documentation sits above what it documents. -Press / in the specification to search it, n and -N to step through matches. The **Swagger UI** tab renders the -document as a reader of your API would see it. +Press / in the specification to search it, n and N to step through matches. +The **Swagger UI** tab renders the document as a reader of your API would see it. ## Scanning your own code @@ -76,14 +75,14 @@ immediately. ## The same thing without a browser [`genspec-wasi`](https://github.com/go-openapi/codescan/tree/master/cmd/genspec-wasi) -is the command this page is built around. It writes a specification to standard -output, and `-format=json` wraps it with the diagnostics and cross-references -that drive the two panes above. +is the command this page is built around. +It writes a specification to standard output, +and `-format=json` wraps it with the diagnostics and cross-references that drive the two panes above. ```sh go install github.com/go-openapi/codescan/cmd/genspec-wasi@latest genspec-wasi -workdir ./my-api ./... ``` -For an interactive scan in a terminal, with the same tracking and the same -diagnostics, use [`genspec-tui`](https://github.com/go-openapi/codescan/tree/master/cmd/genspec-tui). +For an interactive scan in a terminal, with the same tracking and the same diagnostics, +use [`genspec-tui`](https://github.com/go-openapi/codescan/tree/master/cmd/genspec-tui). diff --git a/docs/doc-site/project/_index.md b/docs/doc-site/project/_index.md index 818b8038..334e650b 100644 --- a/docs/doc-site/project/_index.md +++ b/docs/doc-site/project/_index.md @@ -1,19 +1,20 @@ --- title: "Project" -weight: 4 +weight: 30 description: | Repo-level information for github.com/go-openapi/codescan. - Cross-org contributing and maintainer guides live in the shared - go-openapi doc-site. + + Contributing guidelines & maintainers documentation. --- -This section holds material specific to this repository: +Everything else on this site is about *using* codescan. This section is about the repository it +comes from — who publishes it, under what licence, and where to send a patch. * [README](./readme/) — repo overview and announcements * [License](./license/) — Apache-2.0 -Cross-org documentation that applies to every go-openapi repo lives in the -shared doc-site: +codescan follows the conventions of every go-openapi repository rather than inventing its own, so +how to contribute to it is documented once, for all of them, in the shared doc-site: * [Contributing guidelines][contributing-doc-site] * [Maintainers documentation][maintainers-doc-site] diff --git a/docs/doc-site/shaping-the-output/_index.md b/docs/doc-site/shaping-the-output/_index.md index a0da9022..79d8a4bd 100644 --- a/docs/doc-site/shaping-the-output/_index.md +++ b/docs/doc-site/shaping-the-output/_index.md @@ -1,16 +1,18 @@ --- title: Shaping the output -weight: 30 +weight: 50 description: | - How-to guides for the knobs that change how the same Go source renders into - the spec — grouped by what they shape: scope & discovery, names & $refs, - titles & descriptions, field types & formats, and response bodies. + How-to guides for the knobs that change how the same Go source renders into the spec. + + Grouped by what they shape: scope & discovery, names & $refs, titles & descriptions, field types & formats, + and response bodies. --- The same annotated Go can render into the spec in more than one shape, and a handful of [`codescan.Options`](https://pkg.go.dev/github.com/go-openapi/codescan#Options) -(plus a few field-level annotations) let you choose. The guides are grouped by -*what* they shape: +(plus a few field-level annotations) let you choose. + +The guides are grouped by *what* they shape: - **Scope & discovery** — which packages are read and which types become definitions. @@ -23,8 +25,7 @@ handful of [`codescan.Options`](https://pkg.go.dev/github.com/go-openapi/codesca {{< children type="card" description="true" >}} -Each guide is task-oriented — *"I want the output to look like this"* — and shows -the **same input rendered both ways**, as before/after golden output the example -tests verify. For the field-by-field meaning of every option, see the -[Options reference]({{% relref "options" %}}) or the -[`Options` godoc](https://pkg.go.dev/github.com/go-openapi/codescan#Options). +Each guide is task-oriented — *"I want the output to look like this"* — and shows the **same input rendered both ways**, +as before/after golden output the example tests verify. For the field-by-field meaning of every option, +see the [Options reference]({{% relref "options-reference" %}}) +or the [`Options` godoc](https://pkg.go.dev/github.com/go-openapi/codescan#Options). diff --git a/docs/doc-site/tutorials/_index.md b/docs/doc-site/tutorials/_index.md index b899f170..ecca9bc3 100644 --- a/docs/doc-site/tutorials/_index.md +++ b/docs/doc-site/tutorials/_index.md @@ -1,21 +1,21 @@ --- title: Tutorials -weight: 20 +weight: 40 description: | - Learn codescan by spec concept — model definitions, routes and operations, - validations, examples, and document metadata — each shown as annotated Go - next to the Swagger it produces. + Learn codescan by spec concept: model definitions, routes and operations, + validations, examples, and document metadata. + + Each demonstrated as annotated Go next to the Swagger it produces. --- -These tutorials teach codescan **by spec concept**, not annotation by -annotation. Each page takes one thing you want in your OpenAPI document — a -model definition, a route, a validated field — and shows the Go annotation that -produces it next to the resulting JSON, side by side. +These tutorials teach codescan **by spec concept**, not annotation by annotation. + +Each page takes one thing you want in your OpenAPI document — a model definition, a route, a validated field +— and shows the Go annotation that produces it next to the resulting JSON, side by side. Every Go snippet on these pages comes from the test-covered [`docs/examples`](https://github.com/go-openapi/codescan/tree/master/docs/examples) -module, and every JSON pane is a golden file a test regenerates — so the -examples cannot drift from what the scanner actually emits. +module, and every JSON pane is a golden file a test regenerates — so what you see published here _has been tested_. ## Reading the panes diff --git a/docs/doc-site/tutorials/enumerations.md b/docs/doc-site/tutorials/enumerations.md index 6859e183..b06a11ce 100644 --- a/docs/doc-site/tutorials/enumerations.md +++ b/docs/doc-site/tutorials/enumerations.md @@ -61,7 +61,7 @@ definition (carrying the `enum` array) that fields `$ref` instead — the genera Each member's doc comment becomes a line of the `x-go-enum-desc` extension, and is appended to the property description. Set -[`SkipEnumDescriptions`]({{% relref "/maintainers/options" %}}) to keep the +[`SkipEnumDescriptions`]({{% relref "options-reference" %}}) to keep the mapping on the extension only. ## Any constant expression, not just literals diff --git a/docs/doc-site/usage/examples/basic-scan.md b/docs/doc-site/tutorials/scan-a-package.md similarity index 86% rename from docs/doc-site/usage/examples/basic-scan.md rename to docs/doc-site/tutorials/scan-a-package.md index 72bed493..35dabf73 100644 --- a/docs/doc-site/usage/examples/basic-scan.md +++ b/docs/doc-site/tutorials/scan-a-package.md @@ -1,14 +1,15 @@ --- title: Scan a package -weight: 1 +weight: 5 description: | The smallest end-to-end use of codescan: annotate a package, scan it, and get back a Swagger 2.0 document. --- -This example scans a tiny annotated "petstore" package and produces a Swagger +This tutorial scans a tiny annotated "petstore" package and produces a Swagger 2.0 spec. It is the worked version of -[usage as a library](../../getting-started/usage-as-a-library/). +[Usage as a library]({{% relref "usage-as-a-library" %}}) — start here, then take +one concept at a time from the pages that follow. ## The annotated API diff --git a/docs/doc-site/usage/_index.md b/docs/doc-site/usage/_index.md index f4c1e80c..88b90e98 100644 --- a/docs/doc-site/usage/_index.md +++ b/docs/doc-site/usage/_index.md @@ -1,11 +1,38 @@ --- -title: "Usage" -weight: 2 +title: Advanced Usage +weight: 20 description: | - How to drive codescan: getting started, runnable examples, and the - annotation / grammar reference. + All the knobs codescan takes. What they are, and how they relate. + + There are three spellings: a Go field, a command-line flag, and a key in a configuration file. --- -This section covers how to use `codescan` in practice. +codescan is a go library that ships with a few utility commands. +These commands are thin wrappers around the library's scanner. + +The library's behavior is tuned by fields on [`codescan.Options`](https://pkg.go.dev/github.com/go-openapi/codescan#Options). +The commands — [`genspec`]({{% relref "usage-as-a-headless-cli" %}}), +[`genspec-tui`]({{% relref "usage-as-a-tui" %}}) and `genspec-wasi` — register one flag per field over that same struct. + +So every available knob has **three spellings for one meaning**: + +| Spelling | Looks like | +|----------|------------| +| A Go field | `opts.NameFromTags = []string{"form", "json"}` | +| A command-line flag | `genspec -name-from-tags form,json` | +| A configuration key | `emit:`
  `name-from-tags: [form, json]` | + +The mapping is mechanical: a flag is the kebab-case of the field it writes, without exception, +and a configuration key **is** the flag, spelled exactly as on the command line, under the section that flag belongs to. + +That is why `genspec -h` doubles as the reference for the file — and why the one +reference below serves all three. + +{{% notice style="note" %}} +The commands do not each declare that surface: it is written once, and a guard +fails the build when an option lands with no flag — so a knob added to the +library is reachable from all of them at the same moment. How that is arranged is +in [The commands]({{% relref "commands" %}}). +{{% /notice %}} {{< children type="card" description="true" >}} diff --git a/docs/doc-site/usage/examples/_index.md b/docs/doc-site/usage/examples/_index.md deleted file mode 100644 index 95182c6e..00000000 --- a/docs/doc-site/usage/examples/_index.md +++ /dev/null @@ -1,14 +0,0 @@ ---- -title: Examples -weight: 2 -description: | - Runnable Go examples. Every snippet on these pages is extracted from real, - compilable, test-covered source under docs/examples. ---- - -The code shown on these pages lives under -[`docs/examples`](https://github.com/go-openapi/codescan/tree/master/docs/examples) -as a separate Go module. Snippets are injected via the `code` Hugo shortcode, -so what you read here is exactly what CI compiles and tests. - -{{< children type="card" description="true" >}} diff --git a/docs/doc-site/usage/options-reference.md b/docs/doc-site/usage/options-reference.md new file mode 100644 index 00000000..bba476fd --- /dev/null +++ b/docs/doc-site/usage/options-reference.md @@ -0,0 +1,195 @@ +--- +title: "Options reference" +weight: 20 +description: | + Every option codescan takes - Go field, command-line flag, configuration key. +--- + +[`codescan.Options`](https://pkg.go.dev/github.com/go-openapi/codescan#Options) +is the single configuration struct passed to [`codescan.Run`](https://pkg.go.dev/github.com/go-openapi/codescan#Run). + +**The zero value is a valid configuration** — every flag defaults to `false`, every slice/map to `nil`, +every numeric tunable to its built-in default. You set only what you need. + +This page is the field-by-field catalogue, and serves the commands as much as the +library: **Flag** is what to type, and **Section** is where a `.codescan.yaml` +addresses it — the key inside that section is the flag without its dash. See +[Setting an option]({{% relref "setting-options" %}}) for the rules those two +columns follow, and for the handful of options that are nobody's flag. + +The [godoc](https://pkg.go.dev/github.com/go-openapi/codescan#Options) is the +normative source; each field here links to the +[how-to guide]({{% relref "/shaping-the-output" %}}) that shows it on real +input where one exists. + +{{% notice style="note" %}} +**Default** is the library's. The commands agree with it everywhere but one: +`-scan-models` defaults to `true`, because a command asked for a specification +and handed a package of annotated models should produce their definitions. +{{% /notice %}} + +{{% notice style="note" %}} +codescan never writes to stdout or stderr. Every scan-time observation — a +dropped construct, a rename, a prune — flows through the `OnDiagnostic` +callback. See [Diagnostics & observability](#diagnostics--observability) below. +{{% /notice %}} + +## Inputs & scope + +What gets loaded and which packages and types are in play. See +[Scope & discovery]({{% relref "/shaping-the-output/scope-and-discovery" %}}). + +| Option | Type | Default | Flag | Config Section | Effect | +|--------|------|---------|------|---------|--------| +| `Packages` | `[]string` | `nil` | *(positional)* | — | Package patterns to scan (e.g. `./...`), resolved relative to `WorkDir`. | +| `WorkDir` | `string` | `""` (cwd) | `-workdir` | `scan` | Working directory the package patterns and module resolution are rooted at. | +| `BuildTags` | `string` | `""` | `-build-tags` | `scan` | Go build tags to activate while loading, so tag-guarded source is scanned. See [Build tags]({{% relref "build-tags" %}}). | +| `Include` | `[]string` | `nil` | `-include` | `scan` | Allow-list of package path patterns; when non-empty only matching packages are scanned. See [Scoping the scan]({{% relref "scoping-the-scan" %}}). | +| `Exclude` | `[]string` | `nil` | `-exclude` | `scan` | Deny-list of package path patterns, applied after `Include`. See [Scoping the scan]({{% relref "scoping-the-scan" %}}). | +| `IncludeTags` | `[]string` | `nil` | `-include-tags` | `scan` | Allow-list filtering routes/operations by their swagger `tags`. | +| `ExcludeTags` | `[]string` | `nil` | `-exclude-tags` | `scan` | Deny-list filtering routes/operations by their swagger `tags`. | +| `ExcludeDeps` | `bool` | `false` | `-exclude-deps` | `scan` | Skip types reached through module dependencies, keeping the scan to first-party packages. | +| `ScanModels` | `bool` | `false` | `-scan-models` | `emit` | Also emit a definition for every `swagger:model` type, not just route-reachable ones. See [When the scanner emits a type]({{% relref "type-discovery" %}}). | +| `PruneUnusedModels` | `bool` | `false` | `-prune-unused-models` | `emit` | With `ScanModels`, drop discovered definitions not transitively reachable from a path, shared response/parameter, or `InputSpec` root. Runs before name reduction; `InputSpec` definitions are pinned. No-op without `ScanModels`. See [Pruning unused models]({{% relref "pruning-unused-models" %}}). | +| `InputSpec` | `*spec.Swagger` | `nil` | `-input` *(genspec)* | `document` | Base document to overlay scanned discoveries onto; its definitions are pinned and seed pruning roots. See [Overlaying a spec]({{% relref "overlaying-a-spec" %}}). | + +## Loading & the go environment + +Where the package graph comes from, and which platform it is built for. These +change the emitted spec the way `BuildTags` does — by deciding which files each +package is made of — or change what the scan needs in order to run at all. + +They are options rather than inherited process state so that a scan is +reproducible: a value picked up from whatever shell started it is easy to apply on +one code path and forget on another. + +### Which loader, and why + +Three ways to get a package graph. The table below catalogues the fields; this is +how to choose between them. + +**Standard loader** — the default. Loads your code with the Go toolchain, through +`golang.org/x/tools/go/packages`. Maintained by the Go team, and the reference for +how patterns and imports resolve: where either of the others disagrees with it, the +other one is wrong. Requires Go installed, and uses the build cache. + +**Pure-Go loader** (`ToolchainFreeLoader`) — loads your code with codescan's own +reimplementation. Cuts memory by roughly 45%, and needs no `go` command and no +subprocess. It still reads `GOROOT/src` for the standard library, so it wants a Go +*installation* — just not a runnable toolchain. Modules only. It uses no build +cache, so cold costs what warm costs: about level with the standard loader on a +warm cache, roughly 30% faster on a cold one, and the only choice whose cost does +not depend on cache state. Usually the right pick for CI. + +**Compiled dependencies** — since v0.36.4 the standard loader takes dependency types +from the compiler's export data instead of reading their source. It produces the +same document either way — whatever the spec needs out of a dependency is read at +the moment it is needed — and on a warm build cache it is the fastest by a wide +margin, and several times smaller. + +It must *compile* the dependency closure rather than type-check it, so on a cold +cache it is an order of magnitude slower and writes a large build cache. That is +what `SkipCompiledDependencies` is for, and CI regenerating a spec from a clean +checkout is the case to reach for it. Code that does not compile is **not** a +reason: such a load is retried from source automatically. + +Two further options drop the `GOROOT` requirement altogether, for environments with +no Go installation at all — a WASI guest, a browser. `StubStdlib` synthesizes the +standard library, and pays for the reach in fidelity: a fabricated type has the +right name and no structure. `ExportData` serves dependencies from a blob you +prepare in advance, and pays in preparation instead — the types are the compiler's +own, but the blob is only valid for the toolchain that produced it, and a package +it does not cover falls back to source and then to synthesis. + +{{% notice style="note" %}} +The percentages are indicative, not a promise: the balance moves with the size of +the tree being scanned, and on a small one the pure-Go loader is *slower* warm than +the standard loader. Measure your own corpus with +[`hack/loader-benchmark`](https://github.com/go-openapi/codescan/tree/master/hack/loader-benchmark), +which compares a released version against a working tree and carries the tables and +the method. +{{% /notice %}} + +| Option | Type | Default | Flag | Section | Effect | +|--------|------|---------|------|---------|--------| +| `GOOS` / `GOARCH` | `string` | `""` (this machine) | `-goos` / `-goarch` | `go` | The platform the scanned code is built for. `//go:build` lines and `_linux.go` / `_amd64.go` filename suffixes resolve against them, so they select which files a package is made of. | +| `GOFLAGS` | `string` | `""` (process env) | `-goflags` | `go` | Default go command flags, e.g. `-tags=integration`. Flags given through `BuildTags` win, as they do for the go command. | +| `GOWORK` | `string` | `""` (search upwards) | `-gowork` | `go` | Workspace selection: `off` disables it, a path names a `go.work`. Inside a workspace a sibling module resolves to the copy being worked on rather than to the module cache — miss that and its types are read stale, or synthesized empty. | +| `GOEXPERIMENT` | `string` | `""` (process env) | `-goexperiment` | `go` | Toolchain experiments, e.g. `jsonv2`; each contributes a `goexperiment.` build tag. | +| `ToolchainFreeLoader` | `bool` | `false` | `-loader=own` | `load` | Resolve the package graph with codescan's own loader instead of `golang.org/x/tools/go/packages`. Same job and, across the fixture corpus, the same spec; it differs in needing no installed toolchain and no subprocess, since it never runs `go list`. **Experimental.** | +| `FS` | `fs.FS` | `nil` | — | — | Read source through a virtual filesystem — an in-memory tree, an uploaded archive, an `embed.FS` — instead of the real one. Implies `ToolchainFreeLoader`, since `go list` can only read the real filesystem. **`FS` is the whole world the scan can read**: dependencies and GOROOT come through it too, absolute paths map by dropping the leading separator, and anything unreachable is synthesized — a valid but quietly thinner spec, announced by `scan.synthesized-import` and `scan.degraded-load`. **Experimental.** | +| `StubStdlib` | `bool` | `false` | `-stub-stdlib` | `load` | Synthesize the standard library from the names the code selects, rather than reading GOROOT. Toolchain-free loader only. Identity recognition still works (`time.Time`, `json.RawMessage` are matched on package and name), but a synthesized type has no fields and no method set — so `json.RawMessage` stops rendering as a byte array and nothing is seen to implement `encoding.TextMarshaler`. Trades fidelity for reach, quietly; prefer a full graph where GOROOT is available. **Experimental.** | +| `ExportData` | `fs.FS` | `nil` | `-export-data` *(genspec-wasi)* | — | Serve dependencies from pre-computed export data (one `.export` file per package) under the toolchain-free loader. Unlike `StubStdlib` this costs no fidelity, the types being the ones the compiler computed — but it is valid only for the toolchain that produced it, and an uncovered package falls back to source, then to synthesis. The module under scan is never read this way, and neither is a dependency whose source carries annotations or one the spec later needs a declaration from. **Experimental.** | +| `SkipCompiledDependencies` | `bool` | `false` | `-skip-compiled-dependencies` | `load` | Read every dependency from source, instead of taking its types from the compiler's export data under the go/packages loader. Unset (the default since v0.36.4) costs no meaning: a dependency whose source carries annotations is read back after the load, and one that merely *declares* a type the spec carries is read at the lookup that wants it — so a `swagger:strfmt` written in a library still counts, and a model declared in an unannotated dependency keeps its doc comment and its fields. Set it for cost alone: the default is markedly faster on a warm build cache and markedly slower on a cold one, since `go list -export` compiles the closure before it can read it. A closure that does not compile is handled without this — the load falls back to source and raises `scan.compiled-dependencies`. | + +{{% notice style="note" %}} +The virtual-filesystem and export-data options exist to make a scan possible +where no Go toolchain is: they are what lets codescan run compiled to +WebAssembly. See the [Playground]({{% relref "playground" %}}). +{{% /notice %}} + +## Names & references + +How definitions are named and how `$ref`s render. See +[Names & `$ref`s]({{% relref "/shaping-the-output/names-and-refs" %}}). + +| Option | Type | Default | Flag | Section | Effect | +|--------|------|---------|------|---------|--------| +| `NameFromTags` | `[]string` | `nil` (⇒ `["json"]`) | `-name-from-tags` | `emit` | Ordered struct-tag types a property/parameter/header name is derived from; first that supplies a name wins. Explicit empty slice ⇒ Go field name. Only the name — `json` encoding directives (`-`, `,omitempty`, `,string`) always come from `json`. See [Naming from struct tags]({{% relref "naming-from-tags" %}}). | +| `SkipJSONifyInterfaceMethods` | `bool` | `false` | `-skip-jsonify-interface-methods` | `emit` | Emit interface-method property names verbatim (`ID`, `CreatedAt`) instead of auto-jsonifying them (`id`, `createdAt`). Only affects interface methods; struct fields and `swagger:name` overrides are unchanged. See [Interface-method property names]({{% relref "interface-method-names" %}}). | +| `RefAliases` | `bool` | `false` | `-ref-aliases` | `emit` | Render Go type aliases as a first-class `$ref` (via `swagger:model`) instead of expanding them inline. See [Alias rendering]({{% relref "alias-rendering" %}}). | +| `TransparentAliases` | `bool` | `false` | `-transparent-aliases` | `emit` | Make aliases fully transparent — never creating a definition. See [Alias rendering]({{% relref "alias-rendering" %}}). | +| `DefaultAllOfForEmbeds` | `bool` | `false` | `-default-all-of-for-embeds` | `emit` | Render a plain (untagged, unnamed) struct embed as an `allOf` member — a `$ref` for a model embed, an inline member otherwise — with the embedding struct's own fields in a sibling member, instead of inlining promoted properties. json-named embeds, `swagger:allOf` embeds, and interface embeds are unaffected. See [Composing embeds with allOf]({{% relref "composing-embeds-with-allof" %}}). | +| `NameConcatBudget` | `float64` | `0` (⇒ `0.65`) | `-name-concat-budget` | `emit` | Readability cutoff `[0,1]` for the package-segment concatenation that deconflicts colliding definition names; lower scores are more readable. A group whose best concat scores above the budget is a candidate for the hierarchical fallback. See [Resolving `$ref` name conflicts]({{% relref "resolving-name-conflicts" %}}). | +| `EmitHierarchicalNames` | `bool` | `false` | `-emit-hierarchical-names` | `emit` | For the rare collision group whose best flat concat exceeds `NameConcatBudget`, emit nested container definitions (`#/definitions//`) instead of a long flat concat, with an explanatory diagnostic. The always-correct flat concat is the default. See [Resolving `$ref` name conflicts]({{% relref "resolving-name-conflicts" %}}). | +| `EmitRefSiblings` | `bool` | `false` | `-emit-ref-siblings` | `emit` | Emit a `$ref`'d field's description and vendor extensions as direct `$ref` siblings (`{$ref, description, x-*}`) instead of an `allOf` wrap. Validations/externalDocs still force a compound. See [Descriptions beside a `$ref`]({{% relref "descriptions-beside-a-ref" %}}). | +| `SkipAllOfCompounding` | `bool` | `false` | `-skip-all-of-compounding` | `emit` | Never emit an `allOf` compound for a `$ref`'d field. Validations/externalDocs are dropped (description/extensions too, unless `EmitRefSiblings` keeps them as siblings); each drop raises a diagnostic. `required` is unaffected. See [Descriptions beside a `$ref`]({{% relref "descriptions-beside-a-ref" %}}). | +| `DescWithRef` | `bool` | `false` | — | — | **Deprecated** — prefer `EmitRefSiblings`. In the description-only case, wrap the `$ref` in a single-arm `allOf` to preserve the description (strict draft-4 shape). No-op when `EmitRefSiblings` is set. See [Descriptions beside a `$ref`]({{% relref "descriptions-beside-a-ref" %}}). | + +## Titles & descriptions + +The human-readable text the spec carries. See +[Titles & descriptions]({{% relref "/shaping-the-output/titles-and-descriptions" %}}). + +| Option | Type | Default | Flag | Section | Effect | +|--------|------|---------|------|---------|--------| +| `SingleLineCommentAsDescription` | `bool` | `false` | `-single-line-comment-as-description` | `emit` | Route every single-line doc comment to `description`, never to `title`/`summary` (the first-sentence convention otherwise applies). Multi-line comments keep the title/description split. See [Single-line comments as descriptions]({{% relref "single-line-comments" %}}). | +| `AfterDeclComments` | `bool` | `false` | `-after-decl-comments` | `emit` | Let swagger annotations live inside a struct body or as a trailing comment, in addition to the doc comment above the declaration, so the godoc stays clean. v0.36 scope: type declarations (struct inside-body + alias trailing comment). See [Keeping annotations out of the godoc]({{% relref "keeping-annotations-out-of-the-godoc" %}}). | +| `CleanGoDoc` | `bool` | `false` | `-clean-go-doc` | `emit` | Strip godoc doc-link brackets from generated `title`/`description` (humanizing unresolved ones, dropping reference-definition lines, recomposing resolved links to each schema's exposed name). Applies only to godoc-derived prose; overrides are untouched. See [Cleaning godoc doc-links]({{% relref "cleaning-godoc-doc-links" %}}). | + +## Field types, formats & extensions + +How an individual property renders. See +[Field types & formats]({{% relref "/shaping-the-output/field-types-and-formats" %}}). + +| Option | Type | Default | Flag | Section | Effect | +|--------|------|---------|------|---------|--------| +| `SetXNullableForPointers` | `bool` | `false` | `-set-x-nullable-for-pointers` | `emit` | Emit `x-nullable: true` on pointer-typed fields. See [Nullable pointers]({{% relref "nullable-pointers" %}}). | +| `SkipExtensions` | `bool` | `false` | `-skip-extensions` | `emit` | Suppress all `x-go-*` vendor extensions in the output. See [Vendor extensions]({{% relref "vendor-extensions" %}}). | +| `EmitXGoType` | `bool` | `false` | `-emit-x-go-type` | `emit` | Stamp an `x-go-type` extension (fully-qualified originating Go type) on every emitted definition, for round-tripping a spec back to its Go types. Suppressed under `SkipExtensions`. See [Vendor extensions]({{% relref "vendor-extensions" %}}). | +| `SkipEnumDescriptions` | `bool` | `false` | `-skip-enum-descriptions` | `emit` | Keep the per-enum-value const-name mapping (from `swagger:enum`) out of the `description`, exposing it only via the `x-go-enum-desc` extension. Suppressed entirely under `SkipExtensions`. | + +## Diagnostics & observability + +Channels for what the scan observed; these do not change the output spec. + +| Option | Type | Default | Flag | Section | Effect | +|--------|------|---------|------|---------|--------| +| `OnDiagnostic` | `func(Diagnostic)` | `nil` | — | — | Invoked once per diagnostic in source order (parser warnings, validation failures, prunes, renames). Diagnostics never block the build — invalid constructs are dropped from the spec while their explanation flows here. The only output channel. **Experimental** while LSP integration matures. | +| `OnProvenance` | `func(Provenance)` | `nil` | — | — | Invoked once per anchor node in the produced spec, carrying its JSON pointer and the source position of the Go construct that produced it. Never blocks the build. **Experimental** while LSP/TUI integration matures. | +| `Debug` | `bool` | `false` | — | — | **Deprecated, ignored.** The legacy stderr debug logger was retired; wire `OnDiagnostic` instead. Retained for API compatibility. | + +The two callbacks are how the commands report: `genspec` renders diagnostics on +standard error, and `genspec-wasi -format=json` carries both in its envelope for +a program to read. + +## See also + +- [Setting an option]({{% relref "setting-options" %}}) — the flag and key rules + the two middle columns follow, and which spelling wins. +- [Annotations]({{% relref "annotations" %}}) — the `swagger:*` vocabulary the + scanner reads from comments. +- [Keyword reference]({{% relref "keywords" %}}) — the `keyword: value` forms + inside annotation bodies. +- [Shaping the output]({{% relref "/shaping-the-output" %}}) — task-oriented + how-tos that put these options to work on real input. diff --git a/docs/doc-site/usage/reference/_index.md b/docs/doc-site/usage/reference/_index.md deleted file mode 100644 index 6af20601..00000000 --- a/docs/doc-site/usage/reference/_index.md +++ /dev/null @@ -1,25 +0,0 @@ ---- -title: Reference -weight: 3 -description: | - The annotation vocabulary, keyword reference and the formal grammar the - scanner parses. ---- - -codescan parses a small annotation language layered on top of Go doc comments. -The reference material currently lives alongside the source in -[`docs/`](https://github.com/go-openapi/codescan/tree/master/docs): - -- [Annotations](https://github.com/go-openapi/codescan/blob/master/docs/annotations.md) - — the full annotation vocabulary (`swagger:meta`, `swagger:route`, - `swagger:model`, `swagger:parameters`, `swagger:response`, …). -- [Keywords](https://github.com/go-openapi/codescan/blob/master/docs/keywords.md) - — every keyword recognized inside annotation blocks and where it applies. -- [Grammar](https://github.com/go-openapi/codescan/blob/master/docs/grammar.md) - — the formal grammar the parser implements. -- [Sub-languages](https://github.com/go-openapi/codescan/blob/master/docs/sub-languages.md) - — the embedded YAML / simple-schema surfaces. - -> **TODO (scaffold)**: these documents are good candidates to migrate into the -> doc-site as first-class pages (one section per file), so they render with -> navigation and search rather than linking out to GitHub. diff --git a/docs/doc-site/usage/setting-options.md b/docs/doc-site/usage/setting-options.md new file mode 100644 index 00000000..77586593 --- /dev/null +++ b/docs/doc-site/usage/setting-options.md @@ -0,0 +1,202 @@ +--- +title: Setting options +weight: 10 +description: | + Three ways to set the same knob +--- + +There are three ways to set the same knob — a Go field, a command-line flag, a key +in a .codescan.yaml — where the file is looked for, what may be in it, and +which spelling wins when they disagree. + +{{}} +{{% tab title="Go field" %}} +## As a Go field + +Calling the library, an option is a field on +[`codescan.Options`](https://pkg.go.dev/github.com/go-openapi/codescan#Options), passed to `Run`. +**The zero value is a valid configuration** — every boolean defaults to `false`, +every slice to `nil`, every numeric tunable to its built-in default — so you set only what you need: + +{{< code file="basic/scan.go" lang="go" region="runScan" >}} + +This is the only spelling that reaches *everything*: a few options are not +values a command line can carry — a filesystem to read, a document to merge +into, a callback to receive diagnostics. See [What has no flag](#what-has-no-flag). + +{{% /tab %}} + +{{% tab title="CLI flag" %}} +## As a flag + +This is available for every CLI tool shipped: `genspec`, `genspec-tui`, `genspec-wasi`. +The library itself is not bound to command line flags. + +Every value-typed option is a flag, on every command: + +```cmd +genspec -name-from-tags form,json -prune-unused-models ./internal/api/... +``` + +**A flag is the kebab-case of the field it writes, without exception.** +`SkipJSONifyInterfaceMethods` is `-skip-jsonify-interface-methods`; no shorter +spelling is offered, because guessing one is how a caller ends up guessing wrong. +`genspec -h` lists them all, and the [Options reference]({{% relref "options-reference" %}}) +gives the flag beside the field for each. + +Two shapes do not follow from a field name: + +- **the packages to scan** are positional arguments — `genspec ./api/...` — + so that the command reads like the `go` commands it resolves patterns for. + Naming none scans `./...`; +- **`-loader`** takes `go`, `own` or `auto` where the field + (`ToolchainFreeLoader`) is a boolean: the useful default is the third answer, + "pick `own` wherever the build cannot exec". `auto` is what lets the same + source build for a WASI guest, which has no process model and so can never run + `go list`. + +**Commands may add some extra flags that do not affect the scanning itself**. + +- `genspec` adds its own `-output`, `-validate` or `-fail-on` (these are not library options) +- `genspec-tui` adds a unique `-profile` flag and related flags to store profiling data +- `genspec-wasi` adds a unique `-export-data` flag to preload a compiled go standard library + +Those are documented with the command that owns them. +{{% /tab %}} + +{{% tab title="Config file key" %}} +## As a configuration key + +Anything that can be a flag can be preset in a `.codescan.yaml`, so a project +configures itself once and the command is run bare. + +This is available for `genspec` and `genspec-tui` — not, for now, for `genspec-wasi`, +which reads its whole configuration from the arguments its host hands it. +The library itself is not bound to config files. + +```yaml +scan: + workdir: ./api + exclude-tags: [internal] + +emit: + scan-models: true + name-from-tags: [form, json] + +document: + format: yaml + output: swagger.yaml + +diagnostics: + validate: true + fail-on: warning +``` + +Keys are grouped into sections, and inside a section **a key is the flag it +sets**, spelled exactly as on the command line. There is no second vocabulary to +learn and no mapping table to keep in step: `genspec -h` is the reference for the +file. + +### The sections + +The library's four are the questions a scan answers, in the order it answers +them. Each command adds its own for the flags that are its business: + +| Section | The question | Declared by | +|---------|--------------|-------------| +| `scan` | which code is looked at | the library — every command | +| `go` | what it is built as: the go environment that decides what compiles | the library — every command | +| `load` | how the packages are read | the library — every command | +| `emit` | what the specification ends up saying | the library — every command | +| `document` | what is read and written: `input`, `output`, `format`, `compact` | `genspec` | +| `diagnostics` | how loud it is about what it saw: `color`, `quiet`, `verbose`, `validate`, `fail-on` | `genspec` | +| `profile` | whether a run is profiled: `profile`, `profile-dir`, `mem-profile-rate` | `genspec-tui` | + +### Where the file is found + +The search walks **upwards** from where the command was run, so running it from +anywhere inside a project finds the project's file. It stops at the first hit +rather than merging what it passed: a file half-overridden by another three +directories up is not something anyone can read off the page. + +`.codescan.yaml`, `.codescan.yml` and `.codescan.json` are looked for in that +order. JSON is a subset of YAML, so it needs no parser of its own — and a +generated file is as likely to be JSON. + +| Flag | Effect | +|------|--------| +| `-config `, `-c ` | read this file, which **must** exist — a caller who named one meant that file | +| `--no-config` | read none, whatever is lying around, for a run that has to be reproducible | + +Asking for both at once is an error rather than a coin toss. So is `-config` and +`-c` naming different files. + +{{% notice style="note" %}} +The search starts from the current directory, **not** from `-workdir`. The file +is free to set `-workdir`, and one found through the very value it sets would be +reasoning in a circle. +{{% /notice %}} + +{{% /tab %}} +{{< /tabs >}} + +## Which spelling wins + +**Flags win over config**. That holds for a flag typed with the value it already had: + +```cmd +# false, even where the file says scan-models: true +genspec -scan-models=false +``` + +It is decided by asking the flag set which flags were actually *seen*, not by +comparing values against defaults — which is what makes the rule statable in one +sentence instead of one per flag. + +Everything the file sets lands through the same path a command-line argument +takes, so a value is parsed and validated exactly once, and a file cannot +express anything an argument could not. A malformed value is refused the same +way, naming the file. + +Between the two, the ordinary defaults apply — with one deliberate divergence: +**the commands default `-scan-models` to `true`** where the library field is +`false`. A command asked to produce a specification and handed a package of +annotated models should produce their definitions. + +## One file, several commands + +There is one file name for the whole family, not one per command: a project +configuring a scan has configured it for all of them. What tells them apart is +the sections. + +- A section a command does not know is **skipped**, not refused — that is how + `genspec-tui`'s `profile:` settings sit beside `genspec`'s `document:` in the + same file. +- A key inside a section it *does* know must name one of its flags. That is what + makes a typo an error rather than a setting that quietly never applied. + +Run `genspec -verbose` to see which file was read and which keys were skipped. + +## What has no flag + +Four options are not values a command line can carry. + +The command owns them instead — and each of the commands makes its own choice about how, +which is why they are not in the shared table: + +| Option | Reached by | +|--------|-----------| +| `InputSpec` | `genspec -input ` (`document.input`) — the command loads the document | +| `FS` | no flag: a filesystem is a Go value. The commands read the real one; the [Playground]({{% relref "/playground" %}}) hands the browser's | +| `ExportData` | `genspec-wasi -export-data ` — the command opens the path. This option is specific to the wasi CLI for now | +| `OnDiagnostic`, `OnProvenance` | no flag: the commands wire the sinks. `genspec` prints diagnostics to standard error; `genspec-wasi -format=json` puts both in its envelope. Custom hooks are only available to the library. | + +Two more fields carry no flag because nothing should be reaching for them: +`DescWithRef` and `Debug` are deprecated. + +## Next + +- [Options reference]({{% relref "options-reference" %}}) — every field, with its + flag and its section. +- [Usage as a headless CLI]({{% relref "usage-as-a-headless-cli" %}}) — the flags + `genspec` adds of its own. diff --git a/hack/doc-site/genspec-wasi/README.md b/hack/doc-site/genspec-wasi/README.md index feee38ca..e017cbb7 100644 --- a/hack/doc-site/genspec-wasi/README.md +++ b/hack/doc-site/genspec-wasi/README.md @@ -24,10 +24,19 @@ npm run wasm # cross-compiles cmd/genspec-wasi into public/ npm run dev # http://localhost:5174 npm run build # dist/ npm run check # svelte-check +npm run dist # wasm + build + pack into ../hugo, which is what the doc site serves ``` `public/genspec-wasi.wasm`, `node_modules/` and `dist/` are generated; none is committed. +> **Node 22 or 20.19+.** `package.json` says so under `engines`, and it is worth reading as a +> requirement rather than a preference: rolldown ships its native binding as an *optional* +> dependency gated on that same range, so an older node installs everything except the binding — +> with no error — and the first `vite`/`vitest`/`svelte-check` run dies with **"Cannot find native +> binding … npm has a bug related to optional dependencies"**. It is not an npm bug and reinstalling +> will not fix it. Switch node, `rm -rf node_modules`, and install again: `npm ci` will not notice +> that the tree it is reusing was built by the wrong one. + ## Using it Both panes are editors: the left is Go, the right is the document the scan produced, read-only but diff --git a/hack/doc-site/hugo/README.md b/hack/doc-site/hugo/README.md index 7b17ec46..62e14335 100644 --- a/hack/doc-site/hugo/README.md +++ b/hack/doc-site/hugo/README.md @@ -81,8 +81,10 @@ Two-layer config, mirroring the pattern used by other go-openapi doc sites: build timestamp), generated from `codescan.yaml.template` Both files are passed together via `--config hugo.yaml,codescan.yaml`. The -dynamic values land under `params.codescan.*` and are referenced from the -markdown content. +dynamic values land under `params.codescan.*`, and the `version` shortcode is +what renders them — a build that omits `codescan.yaml` renders nothing there +rather than failing, which is what makes a bare `hugo` usable for a quick local +check. ## Deployment diff --git a/hack/doc-site/hugo/layouts/shortcodes/playground.html b/hack/doc-site/hugo/layouts/shortcodes/playground.html index 5e0435b1..3d843848 100644 --- a/hack/doc-site/hugo/layouts/shortcodes/playground.html +++ b/hack/doc-site/hugo/layouts/shortcodes/playground.html @@ -21,6 +21,26 @@ committed. If it is missing the shortcode says so on the page rather than rendering an empty box, because a silent blank is indistinguishable from a broken build. + + # Room to work + + The page around the playground is mostly prose, so the mount is sized to sit in + it politely: a reader scrolling past should meet a figure, not a screen. That is + the wrong size the moment they stop reading and start using it - two editor panes + in a documentation column, 38rem tall, is not an interface anybody can work in. + + So the box carries its own controls. "Expand" takes all the room the page can + give: always more height, and more width where the theme was holding some back. + "Full screen" hands it the display. Both are reversible and neither reloads the + app, which matters because the artifact is 20 MB and the source in the editor is + the reader's own. + + How much width there is to reclaim was measured rather than assumed, and it is + less than it looks. The theme caps an article at MAIN-WIDTH-MAX less the menu and + the gutters, but that cap only binds once the window is wide enough to reach it: + at 1600 and at 1920 the box already fills the column and expanding gains nothing + horizontally; at 2560 it gains about 560px. Height is the gain that is always + there, which is why "Expand" does not promise width in its name. */ -}} {{- $height := .Get "height" | default "38rem" -}} @@ -35,17 +55,35 @@ {{- else -}} - {{- /* - The artifact address is passed in rather than guessed. The app would - otherwise look for it beside its own module, which is right here and wrong - the moment the pack is served from somewhere else. - */ -}} -
+
+ {{- /* + The controls sit outside the mount rather than inside the app: the app is a component that + knows nothing about the page hosting it, and how much of a page it may occupy is the page's + business. It is also what keeps them reachable while the app is still fetching the artifact. + */ -}} +
+ Room to work: + + +
+ + {{- /* + The artifact address is passed in rather than guessed. The app would + otherwise look for it beside its own module, which is right here and wrong + the moment the pack is served from somewhere else. + */ -}} +
+