Skip to content

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

Open
tadelesh wants to merge 7 commits into
mainfrom
tadelesh/auto-gen-rule-diagnostic-docs
Open

feat(tspd): auto-generate linter rule and diagnostic reference pages#11221
tadelesh wants to merge 7 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, so these pages stop being hand-written and drift-prone, and make that documentation available to editor tooling (not just the website).

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. Markdown files referenced by FileRef are included in the published package so installed libraries can resolve them.

  • Auto-generated reference url. The library definition passed to createTypeSpecLibrary accepts referenceDocs.baseUrl. When set, the compiler auto-fills the url of any documented diagnostic/rule that does not specify one:

    • diagnostics -> ${baseUrl}/diagnostics/<code>
    • linter rules -> ${baseUrl}/rules/<name>

    This gives every documented diagnostic/rule a clickable code link in the editor without hardcoding a url per item, while avoiding links to pages that are not generated.

  • Language server hover. Hovering over a reported error now renders the extended docs of the corresponding diagnostic/rule as markdown, plus a "See documentation" link when a url is available. The LSP Diagnostic message is plain-text only, so hover is the channel that can show rich docs inline at an error. Because emitter/linter diagnostics only exist in the full compile (the lightweight hover compile skips emit and lint), the hover matches the cursor against the last full compile's published ranges, guarded by document version to avoid stale positions. The docs themselves are resolved by reusing the library the compiler already loaded (a new @internal Program.getLoadedLibraryInfo), so hoisted and import-only packages work for free without re-resolving or reloading anything.

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 (declares referenceDocs.baseUrl; hardcoded rule url removed).
  • @typespec/openapi3 — 6 diagnostics (path-query, duplicate-header, inline-cycle, invalid-schema, invalid-server-variable, union-null); declares referenceDocs.baseUrl; 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; 76 targeted compiler URL/linter/hover tests and the op-reference-container-route rule tests pass. npm pack --dry-run confirms all referenced HTTP/OpenAPI3 markdown files are published.

@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: ea625ab

@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 ✏️

The language server now surfaces the extended documentation of diagnostics and linter rules when hovering over a reported error. If the diagnostic/rule provides docs (inline markdown or a FileRef), it is rendered in the hover, together with a link to the generated reference page when a documentation url is available.

@typespec/compiler - feature ✏️

Add a referenceDocs.baseUrl field to the library definition passed to createTypeSpecLibrary. When set, the compiler auto-generates the url of each documented diagnostic and linter rule that does not specify one explicitly, so tooling (CLI and editor) can link to the generated reference pages without hardcoding a URL per rule/diagnostic:,> ,> - diagnostics -> ${baseUrl}/diagnostics/<code>,> - linter rules -> ${baseUrl}/rules/<name>,> ,> ts,> export const $lib = createTypeSpecLibrary({,> name: "@typespec/my-lib",,> referenceDocs: { baseUrl: "https://typespec.io/docs/libraries/my-lib/reference" },,> diagnostics: {,> /* ... */,> },,> });,>

@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: docs are authored as co-located markdown (src/rules/<name>.md, src/diagnostics/<code>.md) and read from source by tspd at doc-gen time (no runtime loading, no codegen, browser/playground-safe). 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
Comment thread packages/tspd/src/ref-doc/extractor.ts Outdated
refDoc.linter = extractLinterRefDoc(lib.name, resolved, libraryPath);
// Only nag about missing docs for libraries that have started documenting their
// rules, to avoid noise for libraries that haven't opted in yet.
if (refDoc.linter.rules.some((r) => r.doc)) {

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.

can we maybe instead have a config in package.json like under tspd

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

tspd doc now simply reports a documentation-missing warning for every rule/diagnostic without docs — the intent is that all rules and diagnostics should be documented, so there is no opt-in gating.

Comment thread packages/tspd/src/ref-doc/extractor.ts Outdated
id: `${libName}/${name}`,
name,
severity: def.severity,
doc: tryReadDoc(libraryPath, `src/diagnostics/${name}.md`),

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.

Not a big fan of forcing this to be in a specific place like that. The point IMO of having this autogenereted thing was it was available in the compilation of typespec as well not just for tspd, otherwise feels this is just an unncvessary extra step from having the doc on your website.

So I think I see 2 options

  1. we keep a naming convention for where the docs can be. This might be a problem if we ever do remote loading of libraries
src/rules/<rule-id>.md
src/rules/<rule-id>/<rule-id>.md
rules/<rule-id>.md
docs/rules/<rule-id>.md
diagnostics/<diag-id>.md
src/diagnostics/<diag-id>.md
docs/diagnostics/<diag-id>.md
  1. we just allow docs: fields on diagnostics and linting rule
docs?: string | FileRef

where FileRef is maybe something like fileRef.fromPackageRoot("src/rules/foo.ts")

  1. We have the pattern define in a config field in package.json

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

  • Inline markdown string, or fileRef.fromPackageRoot("src/rules/foo.md").
  • FileRef is a plain { kind, path } data object read lazily by tooling (never at import time), so definitions stay safe to bundle for the browser/playground.
  • Since docs lives on the definition, it is available at compile time too (editor completion/hover), not just for tspd reference-page generation.
  • I looked for an existing file-reference type in core to reuse — SourceFile is eager (already holds content) and ResolvedFile is a resolved absolute path from module resolution, so neither fit a lazy package-root-relative reference. Hence the small new FileRef.

@tadelesh tadelesh Jul 14, 2026

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Also added:

  • Auto-generated reference url: added referenceDocs.baseUrl to the library definition; the compiler auto-fills the url of diagnostics/rules that do not set one (<base>/diagnostics/<code>, <base>/rules/<name>). This means every diagnostic now gets a clickable code link in the editor without hardcoding a url per item, and the hardcoded rule url was removed.
  • Hover: hovering over a reported error renders the diagnostic/rule docs as markdown plus a "See documentation" link. Since the LSP Diagnostic message itself is plain-text only, the hover channel is the way to show rich docs inline at an error.

@tadelesh tadelesh force-pushed the tadelesh/auto-gen-rule-diagnostic-docs branch from 14a3630 to 74da11a Compare July 14, 2026 03:48
Address review feedback:
- Add a docs field (string | FileRef) to linter rule and diagnostic
  definitions in the compiler; docs are read lazily by tspd so definitions
  stay browser-bundle safe.
- Always report documentation-missing for any linter rule or diagnostic
  without docs, instead of gating the check.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@tadelesh tadelesh force-pushed the tadelesh/auto-gen-rule-diagnostic-docs branch from 74da11a to 3db903d Compare July 14, 2026 04:01
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

Copy link
Copy Markdown
Member Author

Follow-up review fixes are in aad00933e:

  • publish every markdown file referenced by FileRef in the HTTP/OpenAPI3 packages;
  • only auto-generate a reference URL when docs exists, avoiding links to pages that are not generated;
  • index hover diagnostics per compile root + document/version using the actual published LSP ranges (including related ranges), retaining other projects and clearing closed documents;
  • resolve documentation packages with standard module resolution, including hoisted dependencies and import-only conditional exports;
  • log documentation resolution failures instead of silently swallowing them.

Added regression coverage for URL generation/explicit URL preservation, direct and hoisted FileRef docs, import-only exports, multi-project hover state, dead links, and zero-length diagnostic ranges. Final targeted result: 76 tests passed; package dry-runs include all referenced markdown files.

Publish FileRef markdown assets and avoid generating reference links for
undocumented diagnostics/rules. On hover, match reported diagnostics against
the last full compile using their published ranges (guarded by document
version) and resolve their extended documentation 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 aad0093 to ea625ab Compare July 14, 2026 10:47
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