Skip to content

feat(tspd): auto-generate linter rule and diagnostic reference pages#11221

Merged
tadelesh merged 10 commits into
mainfrom
tadelesh/auto-gen-rule-diagnostic-docs
Jul 20, 2026
Merged

feat(tspd): auto-generate linter rule and diagnostic reference pages#11221
tadelesh merged 10 commits into
mainfrom
tadelesh/auto-gen-rule-diagnostic-docs

Conversation

@tadelesh

@tadelesh tadelesh commented Jul 9, 2026

Copy link
Copy Markdown
Member

Fixes #11141

Auto-generate a reference documentation page per linter rule and per diagnostic with tspd, so these pages stop being hand-written and drift-prone.

Note

This PR is scoped to the tspd documentation generation. The referenceDocs.baseUrl url auto-generation and the language server hover that surfaces these docs at a reported error are split into a follow-up PR.

Compiler

  • docs field on diagnostics and linter rules. Both createRule and diagnostic definitions accept a docs field for extended documentation:

    export const myRule = createRule({
      name: "my-rule",
      severity: "warning",
      description: "Short description.",
      docs: fileRef.fromPackageRoot("src/rules/my-rule.md"),
      messages: {
        /* ... */
      },
    });

    The value is string | FileRef: either inline markdown, or a FileRef created via fileRef.fromPackageRoot("..."). A FileRef is a plain { kind, path } data object read lazily by tooling, so it is safe to include in code bundled for the browser (e.g. the playground). Because the docs live on the definition, they are available at compile time, not just to tspd.

tspd

tspd doc now renders:

  • one page per rule -> reference/rules/<name>.md
  • one page per diagnostic -> reference/diagnostics/<code>.md (each shows its severity)
  • a documentation-missing warning for every rule/diagnostic without docs.

Pilots

Content is faithfully migrated from the hand-written pages, which are then deleted:

  • @typespec/http — the op-reference-container-route rule.
  • @typespec/openapi3 — 6 diagnostics (path-query, duplicate-header, inline-cycle, invalid-schema, invalid-server-variable, union-null); the old hand-written emitters/openapi3/diagnostics.md is replaced by the generated pages under reference/diagnostics/.

Validation

  • Built compiler, tspd, http, openapi3; regen is stable (running it twice produces no diff).
  • lint green for the touched packages; the op-reference-container-route rule tests pass.

@microsoft-github-policy-service microsoft-github-policy-service Bot added compiler:core Issues for @typespec/compiler lib:http lib:openapi meta:website TypeSpec.io updates tspd Issues for the tspd tool emitter:openapi3 Issues for @typespec/openapi3 emitter labels Jul 9, 2026
@tadelesh
tadelesh marked this pull request as draft July 9, 2026 12:50
@pkg-pr-new

pkg-pr-new Bot commented Jul 9, 2026

Copy link
Copy Markdown

Open in StackBlitz

npm i https://pkg.pr.new/@typespec/compiler@11221
npm i https://pkg.pr.new/@typespec/http@11221
npm i https://pkg.pr.new/@typespec/openapi3@11221
npm i https://pkg.pr.new/@typespec/tspd@11221

commit: f614bf9

@github-actions

github-actions Bot commented Jul 9, 2026

Copy link
Copy Markdown
Contributor

All changed packages have been documented.

  • @typespec/compiler
  • @typespec/http
  • @typespec/openapi3
  • @typespec/tspd
Show changes

@typespec/openapi3 - internal ✏️

Provide extended documentation for several diagnostics (path-query, duplicate-header, inline-cycle, invalid-schema, invalid-server-variable, union-null) via co-located markdown files.

@typespec/compiler - feature ✏️

Add a docs field to linter rule and diagnostic definitions to provide extended reference documentation. The value can be an inline markdown string or a FileRef created with fileRef.fromPackageRoot("src/rules/my-rule.md"), which is read lazily by tooling so it stays safe to bundle for the browser.,> ,> ts,> export const myRule = createRule({,> name: "my-rule",,> severity: "warning",,> description: "Short description.",,> docs: fileRef.fromPackageRoot("src/rules/my-rule.md"),,> messages: {,> /* ... */,> },,> });,>

@typespec/tspd - feature ✏️

tspd doc now generates a documentation page per linter rule (reference/rules/<name>.md) and per diagnostic (reference/diagnostics/<code>.md), sourced from the docs field on the rule and diagnostic definitions. A documentation-missing warning is reported for any linter rule or diagnostic that does not provide documentation.

@typespec/http - internal ✏️

Provide extended documentation for the op-reference-container-route linter rule via a co-located markdown file.

@azure-sdk-automation

azure-sdk-automation Bot commented Jul 9, 2026

Copy link
Copy Markdown

You can try these changes here

🛝 Playground 🌐 Website 🛝 VSCode Extension

@tadelesh
tadelesh force-pushed the tadelesh/auto-gen-rule-diagnostic-docs branch 6 times, most recently from c8f4342 to 31bbb0a Compare July 13, 2026 02:46
Add an optional docs field (raw markdown, loaded from a co-located .md file) to linter rule (createRule) and diagnostic (createTypeSpecLibrary) definitions. tspd renders a page per rule (reference/rules/<name>.md) and per diagnostic (reference/diagnostics/<code>.md) plus a diagnostics index, and reports documentation-missing for undocumented rules/diagnostics. Migrates @typespec/http op-reference-container-route and @typespec/openapi3 path-query as pilots.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@tadelesh
tadelesh force-pushed the tadelesh/auto-gen-rule-diagnostic-docs branch from 31bbb0a to 14d52c9 Compare July 13, 2026 02:53
@tadelesh
tadelesh marked this pull request as ready for review July 13, 2026 02:56
@tadelesh

tadelesh commented Jul 13, 2026

Copy link
Copy Markdown
Member Author

@timotheeguerin updated the PR to the latest approach (option 2 from your review): diagnostics and linter rules take a docs?: string | FileRef field. docs is either inline markdown or fileRef.fromPackageRoot("src/rules/<name>.md") — a lazy { kind, path } reference read by tooling, never at import time, so it stays browser/playground-safe. tspd renders a page per rule/diagnostic from it, and because it lives on the definition it is also available at compile time (editor hover). Pilots are the http op-reference-container-route rule and 6 openapi3 diagnostics (old hand-written page removed). Would appreciate your review.

@tadelesh
tadelesh force-pushed the tadelesh/auto-gen-rule-diagnostic-docs branch 2 times, most recently from 389c229 to 45b365f Compare July 13, 2026 05:23
@tadelesh
tadelesh force-pushed the tadelesh/auto-gen-rule-diagnostic-docs branch from 45b365f to 87bfff5 Compare July 13, 2026 05:29
tadelesh and others added 2 commits July 14, 2026 14:45
…ules

Add a referenceDocs.baseUrl field to the library definition. When set, the
compiler auto-fills the url of each diagnostic and linter rule that does not
specify one explicitly (diagnostics -> <base>/diagnostics/<code>, rules ->
<base>/rules/<name>), so the editor and CLI get a documentation link without
hardcoding a url per item. Migrate @typespec/http and @typespec/openapi3 to
declare referenceDocs and drop the hardcoded rule url.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…e server

When hovering over a reported error, the language server now renders the
extended documentation of the corresponding diagnostic or linter rule (its
docs field, inline markdown or a FileRef read lazily) together with a link to
the generated reference page when a documentation url is available.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@tadelesh
tadelesh requested a review from RodgeFu as a code owner July 14, 2026 07:30
…e-diagnostic-docs

# Conflicts:
#	packages/compiler/test/server/get-hover.test.ts
@tadelesh

tadelesh commented Jul 14, 2026

Copy link
Copy Markdown
Member Author

Follow-up review fixes (latest in 7758081):

  • publish every markdown file referenced by FileRef in the HTTP/OpenAPI3 packages;
  • only auto-generate a reference URL when docs exists, so we never link to a page that isn't generated (an explicit url still takes precedence);
  • resolve the hover documentation by reusing the library the compiler already loaded (new @internal Program.getLoadedLibraryInfo) instead of re-resolving/reloading the package, so hoisted and import-only layouts work for free;
  • on hover, reuse the diagnostics from the last full compile (matched by source location, the same way the squiggles are) instead of maintaining a separate diagnostic index;
  • log documentation read failures instead of silently swallowing them.

Added regression coverage for URL generation / explicit-URL preservation and for inline and FileRef docs (including a hoisted dependency) plus the no-dead-link case. The compiler server suite passes.

@tadelesh
tadelesh force-pushed the tadelesh/auto-gen-rule-diagnostic-docs branch 2 times, most recently from ea625ab to 7758081 Compare July 16, 2026 06:54
@tadelesh
tadelesh requested a review from timotheeguerin July 16, 2026 07:07
Publish FileRef markdown assets and avoid generating reference links for
undocumented diagnostics/rules. On hover, reuse the diagnostics from the last
full compile (matched by source location the same way squiggles are) to show
the extended documentation of the diagnostic/rule under the cursor, resolved
by reusing the library already loaded by the compiler instead of re-resolving
and reloading it.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: de8537de-5e61-4a0d-b2de-59ec12c15dc4
@tadelesh
tadelesh force-pushed the tadelesh/auto-gen-rule-diagnostic-docs branch from 7758081 to 69c49cd Compare July 16, 2026 08:43
Comment thread packages/compiler/src/core/program.ts Outdated
…ver into a follow-up PR

This PR now only contains the tspd documentation generation (the \docs\ field
+ \FileRef\, and tspd rendering a page per rule/diagnostic). The
\
eferenceDocs.baseUrl\ url auto-generation and the language server hover that
surfaces the docs are moved to a separate PR.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: de8537de-5e61-4a0d-b2de-59ec12c15dc4
@tadelesh

Copy link
Copy Markdown
Member Author

Per discussion with @timotheeguerin, this PR is now scoped to just the tspd documentation generation (the docs field + FileRef, and tspd rendering a page per rule/diagnostic). The referenceDocs.baseUrl url auto-generation and the language-server hover that surfaces these docs at a reported error have been split out into a follow-up PR.

@timotheeguerin timotheeguerin left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

just perf tweak and I think otherwise this looks good

Comment thread packages/tspd/src/ref-doc/extractor.ts Outdated
Comment thread .chronus/changes/diagnostic-docs-openapi3-2026-6-9.md
…and document the docs field

Address review feedback: read `FileRef` docs with the async `fs/promises`
`readFile` instead of `readFileSync`, and document the new `docs` field in the
linter authoring guide.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: de8537de-5e61-4a0d-b2de-59ec12c15dc4
@tadelesh
tadelesh force-pushed the tadelesh/auto-gen-rule-diagnostic-docs branch from 57ea64d to 5d447dd Compare July 20, 2026 05:33
@tadelesh
tadelesh requested a review from timotheeguerin July 20, 2026 05:36
@tadelesh
tadelesh added this pull request to the merge queue Jul 20, 2026
Merged via the queue into main with commit 6823f65 Jul 20, 2026
33 checks passed
@tadelesh
tadelesh deleted the tadelesh/auto-gen-rule-diagnostic-docs branch July 20, 2026 14:50
msyyc pushed a commit to JennyPng/typespec that referenced this pull request Jul 21, 2026
…t#11320)

## Problem

The tspd change that auto-generates one reference page per linter rule
(microsoft#11221) moved the `@typespec/http` linter rule page from its historical
location:

- **Before:**
`https://typespec.io/docs/libraries/http/rules/op-reference-container-route`
- **After:**
`https://typespec.io/docs/libraries/http/reference/rules/op-reference-container-route`

This is a breaking change to the public documentation URL. The rule's
own `url` field, external links, and the diagnostic "see documentation"
link all still point at the historical `libraries/http/rules/<name>`
location, which now 404s.

## Fix

Use the `--rules-dir ../rules` option (added in microsoft#11316) in http's
`regen-docs` script so the per-rule reference pages are written back to
`libraries/http/rules/<name>`, restoring the original URL.

- `packages/http/package.json` — add `--rules-dir ../rules` to
`regen-docs`.
- Regenerated docs: the rule page moves back to
`libraries/http/rules/op-reference-container-route.md` (100% rename) and
`linter.md` links back to `../rules/...`.
- `current-sidebar.ts` — re-enable the http "Rules" sidebar group
(`hasLinterRules: true`), which autogenerates from
`libraries/http/rules`.

`@typespec/http` is the only library in this repo that ships linter
rules, so no other library is affected.

## Validation

- `regen-docs` is stable (running it twice produces no diff).
- The restored page URL
`libraries/http/rules/op-reference-container-route` matches the rule's
hardcoded `url` again.
- `chronus verify` passes.

Co-authored-by: tadelesh <chenjieshi@microsoft.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
msyyc pushed a commit to msyyc/typespec-azure that referenced this pull request Jul 21, 2026
…Azure#4968)

Bumps the `core` submodule to latest main, which pulls in tspd's
per-rule reference page generation (microsoft/typespec#11221). Left
as-is, that would orphan Azure's rich hand-written rule pages by
emitting barebones pages at a new location.

Instead, this migrates each rule to the tspd `docs` field and keeps the
generated pages at their existing URLs (net-zero URL change).

## Changes

- **core**: bump submodule to latest main — includes the merged tspd
`--rules-dir` change (microsoft/typespec#11316)
- **86 rules**: add `docs:
fileRef.fromPackageRoot("src/rules/<name>.md")`; rich content now lives
in `packages/*/src/rules/*.md`
- **3 `package.json`** regen-docs scripts: pass `--rules-dir ../rules`
so generated pages stay at `libraries/<lib>/rules/<name>`
- **astro.config**: 5 redirects for rules whose historical slug differs
from the rule name (e.g. `prevent-format` → `no-format`)
- **.gitignore**: ignore the generated `libraries/*/rules/` pages
(regenerated during `build:web`)
- **pnpm-lock**: add `@typespec/events` / `@typespec/sse` workspace
links (new `http-specs` peer deps from the bump)
- **eng/feeds e2e**: adapt `init-templates.e2e.ts` to the compiler's new
`TemplateSource` scaffold API — `ScaffoldingConfig.baseUri` was replaced
by `source?: TemplateSource` (microsoft/typespec#11303). Supersedes
Azure#4965.
- Contributing guide, `create-linter-rule` skill + scaffolder updated to
the `docs`-field workflow

## Validation

- `typespec-azure-core`, `typespec-azure-resource-manager`,
`typespec-client-generator-core` tests pass
- `oxlint` clean
- `pnpm install --frozen-lockfile` passes
- `pnpm regen-docs` clean (no drift)
- init-templates e2e: scaffolding tests pass via the new
`TemplateSource` path (the 2 `validate` tests that `npm install` an
unpublished `@azure-tools/typespec-autorest` version fail only on
registry resolution, unrelated to this change)

---------

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

compiler:core Issues for @typespec/compiler emitter:openapi3 Issues for @typespec/openapi3 emitter lib:http lib:openapi meta:website TypeSpec.io updates tspd Issues for the tspd tool

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Auto-generate linter rule and diagnostic documentation pages from structured definitions

2 participants