Skip to content

docs(SDK-899): add RFC for autogenerated API docs#1942

Merged
mariechatfield merged 6 commits into
mainfrom
marie/SDK-899-autogen-docs-rfc
May 27, 2026
Merged

docs(SDK-899): add RFC for autogenerated API docs#1942
mariechatfield merged 6 commits into
mainfrom
marie/SDK-899-autogen-docs-rfc

Conversation

@mariechatfield
Copy link
Copy Markdown
Contributor

@mariechatfield mariechatfield commented May 26, 2026

Summary

Add an RFC to autogenerate API docs based on inline comments. This is a complementary workstream to #1507, which sets up the infrastructure pipeline for a docs site based on markdown files.

This proposal outlines the strategy to improve our inline comment coverage, which can be used for tooltips and other intellisense features in IDEs and to generate high-quality markdown documentation.

Related

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
@mariechatfield mariechatfield marked this pull request as ready for review May 26, 2026 23:54
@mariechatfield mariechatfield requested a review from a team as a code owner May 26, 2026 23:54
Copy link
Copy Markdown
Contributor

@jeffredodd jeffredodd left a comment

Choose a reason for hiding this comment

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

This looks great! Thanks for putting this together 🙏


The pipeline is: **TSDoc comments inline in source → TypeDoc generates Markdown → committed to the repo.**

1. Engineers write TSDoc comments inline on exported symbols. This is the only authoring step.
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

We should maybe enforce the requirement of docs perhaps at some point so we don't forget to write the docs.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Ah forgot to mention it in the top-level approach but that is where the beautiful eslint plugins come in! We can set them up to error and fail the build if you don't add documentation to an exported symbol. Unfortunately, it can't tell the difference between "I exported this from a /shared directory to use in another function" versus "I exported this from a barrel file so we can export it for public usage" so it will yell at every export without documentation

But that's what we can use the @internal tag for (to indicate this really is private implementation detail only) along with an allowlist / blocklist to let us roll out the lint rules incrementally

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.

One thing worth thinking about with this: where are TSDoc comments enforced? Would it just be on any publicly exported component/function/hook/utility? or all regardless of export?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

This is the sample config I used in my spike branch. It does mean every export from a source file will need a comment to pass linting (it doesn't care about what gets exported to the consumer).

const jsdocRequireOptions = {
  // Only require JSDoc for exports
  publicOnly: true,
  require: {
    FunctionDeclaration: true,
    ClassDeclaration: true,
    ArrowFunctionExpression: true,
    FunctionExpression: true,
    MethodDefinition: true,
  },
  contexts: [
    // Require JSDoc for exported variables (often constants)
    'ExportNamedDeclaration > VariableDeclaration',
    // and for class properties (methods enabled via the `require` block)
    'PropertyDefinition',
    // and for types, interfaces, and enums
    'ExportNamedDeclaration > TSTypeAliasDeclaration',
    'ExportNamedDeclaration > TSInterfaceDeclaration',
    'ExportNamedDeclaration > TSEnumDeclaration',
  ],
  // If enabled, --fix will simply add an empty jsdoc comment
  enableFixer: false,
}

Here's a sample of errors that it would currently generate:

src/.../useEmployeeDetailsForm.tsx
   44:8  warning  Missing JSDoc comment  jsdoc/require-jsdoc  →  export interface EmployeeDetailsSubmitCallbacks
   58:8  warning  Missing JSDoc comment  jsdoc/require-jsdoc  →  export type UseEmployeeDetailsFormProps
   62:8  warning  Missing JSDoc comment  jsdoc/require-jsdoc  →  export interface EmployeeDetailsFields
   72:8  warning  Missing JSDoc comment  jsdoc/require-jsdoc  →  export interface UseEmployeeDetailsFormReady
  109:8  warning  Missing JSDoc comment  jsdoc/require-jsdoc  →  export function useEmployeeDetailsForm
  309:8  warning  Missing JSDoc comment  jsdoc/require-jsdoc  →  export type UseEmployeeDetailsFormResult
  310:8  warning  Missing JSDoc comment  jsdoc/require-jsdoc  →  export type EmployeeDetailsFieldsMetadata
  311:8  warning  Missing JSDoc comment  jsdoc/require-jsdoc  →  export type EmployeeDetailsFormFields

✖ 8 problems (0 errors, 8 warnings)

This is why the incremental strategy is important (and also so that as I start adding JSDoc via the skills I'm planning, if we realize it's too much bloat, we can adjust the rules as we go)

2. TypeDoc reads the TypeScript source and generates Markdown reference pages from those comments.
3. The generated Markdown is committed alongside the hand-written `docs/`. CI fails on diff only for symbols already marked `@public` or `@beta` — in-progress and pre-GA APIs are excluded from the generated output and therefore don't block CI.

The same comments that feed TypeDoc also feed the IDE. TypeScript preserves TSDoc comments in emitted `.d.ts` declaration files, and IDEs read these to surface documentation in hover tooltips, autocomplete, and signature help. Partners and their LLM agents get inline access to descriptions, parameter docs, and examples without leaving their editor and without needing the SDK source checked out. This is a single authoring step with two outputs: the reference docs site and the IDE experience.
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Love this idea too


**Alternatives:**
- **Generate at Docusaurus build time** via [`docusaurus-plugin-typedoc`](https://typedoc-plugin-markdown.org/plugins/docusaurus): no committed files, no PR noise from docs diffs, but loses the output diff CI signal. Worth revisiting if PR noise becomes a problem in practice.
- **Regenerate only on version bump**: reduces PR noise but allows docs to drift mid-development — a prop rename between releases wouldn't be caught until the next version cut.
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Interesting idea. I think we will only want to release docs on version bumps so this isn't a bad idea. I think it all depends on the time cost to run these scripts often. 🤔

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Theoretically if we use the @alpha tags when we're working on something that's fully in progress it shouldn't ever change the docs until you promote something to @beta or @public:
https://api-extractor.com/pages/tsdoc/doc_comment_syntax/#release-tags

If we do want to generate the actual docs much less frequently, I think it may be worth adding the API extractor back into the mix, in part because it does do a lot of additional heavy validation. The only diff that would be included in each PR at that point is the type interface and any validation errors (including "this wasn't documented at all.")

For example, if you have a @beta function use an @alpha type for props, this will log an error: https://api-extractor.com/pages/messages/ae-incompatible-release-tags/

Copy link
Copy Markdown
Member

@serikjensen serikjensen left a comment

Choose a reason for hiding this comment

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

This is looking amazing! thanks for the diligence here

Couple of questions

  • Do we have a separate effort ticketed to autogenerate events? That may be part of an additional effort to type the onEvent calls correctly. But events are a big part of our public contract and really hard to maintain. That would definitely be more complex to document, but should be possible with tsMorph and the onEvent prop being passed consistently
  • Do we have a separate effort ticketed to autogenerate translations? It would be nice to consider how we autogenerate all translations available for a given component and show usage examples. Another stretch here would be figuring out how to best show where the translations would map to the ui component so partners know what to override

Left a couple of comments on the RFC as well


The pipeline is: **TSDoc comments inline in source → TypeDoc generates Markdown → committed to the repo.**

1. Engineers write TSDoc comments inline on exported symbols. This is the only authoring step.
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.

One thing worth thinking about with this: where are TSDoc comments enforced? Would it just be on any publicly exported component/function/hook/utility? or all regardless of export?


### The doc site has two layers: Guides and API Reference

Narrative content — workflow overviews, integration patterns, conceptual explanations — lives in a Guides layer and is hand-written. It covers how things fit together and why you'd use them. The existing `docs/` files are the Guides layer; they are complemented by this project, not replaced.
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.

Couple of thoughts here, i worry that having guides layer separate from the component docs might make it easier to forget about the corresponding guide file for any given block or workflow. I've had it in the past where we place a readme file alongside the component source which works as the guide. I also feel like the guide and the api docs should be located next to each other in the end documentation even if they live separately in the source. Ex. having one tab for the guide and usage and another for the props tables

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

I completely agree about guides and API living alongside each other in the docs! I did an audit of a bunch of the docs sites we've said we like, and I think Chakra UI does it best:

Docs
├── Get Started
│   ├── Overview
│   ├── AI for Agents
│   ├── Frameworks
│   └── Environments
├── Components
│   ├── Concepts          ← guide prose: "how to use `as`/`asChild`", "how color
│   │                       mode works", "how to test" — no Props tables
│   ├── Layout            ← component ref: ExampleTabs + Props table per file
│   ├── Typography           (box.mdx, flex.mdx, grid.mdx…)
│   ├── Buttons
│   ├── Date and Time
│   ├── Forms
│   ├── Collections
│   ├── Overlays
│   ├── Disclosure
│   ├── Feedback
│   ├── Data Display
│   ├── Internationalization
│   └── Utilities
├── Charts
│   ├── Overview
│   └── Charts
├── Styling
│   ├── Concepts          ← guide prose: "how responsive design works",
│   │                       "how dark mode works", "what cascade layers are"
│   ├── Compositions
│   └── Style Props       ← reference tables: one page per CSS property group
└── Theming
    ├── Concepts          ← guide prose: "what tokens are", "how recipes work"
    ├── Design Tokens     ← reference: token values per category
    ├── Compositions
    └── Customization

In particular, having each "group" start with Concepts or Overview for embedded guide content, followed by the relevant API reference content for that group. Agree that if we can have the source for that doc live closer to the code it's documenting is even better


### Zod type expansion

All `*FormData` and `*FormOutputs` types are mapped types over an unexported `fieldValidators` const. TypeScript preserves the opaque `{ [K in keyof typeof fieldValidators_N]: z.infer<...> }` expression in `.d.ts` emit. TypeDoc and any other documentation tool reading the source or compiled declarations sees the formula, not the expanded field shape.
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.

I think i'm following on this, the FormData and FormOutputs would definitely be good to generate correctly. fieldValidators is an implementation detail and if i'm reading this right wouldn't be documented (which would be preferable)

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

If you look at our dist/ files today, fieldValidators is declared as a const because we need it to build the type for EmployeeDetailsFormData:

declare const fieldValidators: {
    firstName: z.ZodString;
    middleInitial: z.ZodString;
    lastName: z.ZodString;
    email: z.ZodEmail;
    dateOfBirth: z.ZodISODate;
    ssn: z.ZodString;
    selfOnboarding: z.ZodBoolean;
};
export type EmployeeDetailsFormData = {
    [K in keyof typeof fieldValidators]: z.infer<(typeof fieldValidators)[K]>;
};

The ts-morph proposal would be to go through the dist and replace it inline with the text version, which is the same thing that displays if you hover over that value in an IDE:

type EmployeeDetailsFormData = {
    firstName: string;
    middleInitial: string;
    lastName: string;
    email: string;
    dateOfBirth: string;
    ssn: string;
    selfOnboarding: boolean;
}

fieldValidators right now would end up being referenced in any generated documentation because it's referenced in the d.ts we generate

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.

oh interesting. Are there zod types that wouldn't map cleanly to TS types?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

I don't think so, I think this is specifically a side effect of how we're mapping the data based on zod infer so we don't have to declare them twice?

mariechatfield and others added 3 commits May 27, 2026 13:53
…e projects

- Add Example section with full TSDoc + commentary on IDE/docs outputs
- Clarify TSDoc enforcement scope: all exports required, @internal short-form
  accepted for library-internal symbols
- Replace Zod type expansion future project with "Treat emitted type surface as
  first-class API contract" (interface-first + satisfies, @interface tag caveat)
- Add future project: co-locate guide content with component source
- Add future project: expand autogenerated coverage — accurately reflects existing
  build/ scripts and identifies remaining gaps (translations)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

The same comments that feed TypeDoc also feed the IDE. TypeScript preserves TSDoc comments in emitted `.d.ts` declaration files, and IDEs read these to surface documentation in hover tooltips, autocomplete, and signature help. Partners and their LLM agents get inline access to descriptions, parameter docs, and examples without leaving their editor and without needing the SDK source checked out. This is a single authoring step with two outputs: the reference docs site and the IDE experience.

## Example
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

@serikjensen updated this to show a sample of the kind of TSDoc we'd generate based on markdown. If the prose or example content gets too big, we can also use the @include or @includeCode tags in typedoc to pull in markdown from sibling files (best of both worlds in terms of nice markdown formatting for guides, without adding too much text to source code files)


---

## Future projects
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

More details in this section to cover:

  • general type surface improvements to make it easier to document (not just the zod field validators, but more broadly)
  • moving guide content closer to the source code it documents
  • even more autogenerated documentation, including an audit of which of our current autogen docs could be migrated to a type-based solution (component adapter props, theming variables)

@mariechatfield mariechatfield enabled auto-merge (squash) May 27, 2026 22:39
@mariechatfield mariechatfield merged commit 1380f06 into main May 27, 2026
19 checks passed
@mariechatfield mariechatfield deleted the marie/SDK-899-autogen-docs-rfc branch May 27, 2026 22:46
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants