# API Docs Builder
Generates interactive API documentation from TypeScript sources for Video.js 10 components.
## Architecture
```
TypeScript Sources (core/html/react packages)
↓
api-docs-builder (typescript-api-extractor)
↓
JSON files (site/src/content/generated-api-reference/)
↓
Astro component
↓
Interactive tables in MDX pages
```
## How It Works
### 1. Source Discovery
The builder scans `packages/core/src/core/ui/` for component directories. For each component (e.g., `play-button`), it looks for:
- **Core file**: `play-button-core.ts` → Extracts `PlayButtonProps`, `PlayButtonState`, and `defaultProps`
- **Data attrs file**: `play-button-data-attrs.ts` → Extracts data attributes with JSDoc descriptions
- **HTML element file**: `packages/html/src/ui/play-button/play-button-element.ts` → Extracts `tagName`
- **Parts index**: `packages/react/src/ui/play-button/index.parts.ts` → Detects multi-part components
### 2. TypeScript Extraction
Uses `typescript-api-extractor` to parse TypeScript AST and extract:
- Interface properties with types and JSDoc descriptions
- Default values from `static defaultProps = { ... }`
- Data attributes from `const PlayButtonDataAttrs = { ... } as const`
- Lit element tag names from `static tagName = 'media-play-button'`
### 3. JSON Output
Generates one JSON file per component at `site/src/content/generated-api-reference/{kebab-case-name}.json`:
```json
{
"name": "PlayButton",
"props": {
"label": {
"type": "string | ((state: PlayButtonState) => string)",
"description": "Custom label for the button.",
"default": "''"
}
},
"state": {
"paused": {
"type": "boolean",
"description": "Whether playback is paused."
}
},
"dataAttributes": {
"data-paused": {
"description": "Present when the media is paused."
}
},
"platforms": {
"html": {
"tagName": "media-play-button"
}
}
}
```
### 4. Astro Components
The `` component:
1. Loads the JSON via Astro Content Collections (`getEntry('apiReference', 'play-button')`)
2. For single-part components: renders Props, State, and Data Attributes sections with h3 headings
3. For multi-part components: renders each part with a framework-aware h3 heading, part description, and h4 sub-sections
4. Renders interactive tables with expandable prop details
## Usage
### In MDX
Use the unified `` component for both single-part and multi-part components:
```mdx
import ApiReference from "@/components/docs/api-reference/ApiReference.astro";
```
For multi-part components, the same pattern applies — the component automatically renders part headings, descriptions, and sub-sections:
```mdx
import ApiReference from "@/components/docs/api-reference/ApiReference.astro";
```
Part descriptions are extracted from JSDoc on the React component exports (e.g., `packages/react/src/ui/time/time-value.tsx`).
### Building
The builder runs automatically before dev/build via npm scripts:
```bash
# Run manually
pnpm api-docs
# Runs automatically on:
pnpm dev # via predev hook
pnpm build # via prebuild hook
```
## Multi-Part Components
Some components are composed of multiple parts (e.g., Time has Value, Group, Separator). The builder auto-discovers these via convention.
### Detection
**Trigger**: Presence of `packages/react/src/ui/{name}/index.parts.ts`.
Single-part components (PlayButton, MuteButton) don't have this file and are unaffected.
### Discovery Algorithm
1. **Part name discovery**: Named (non-type-only) exports are parsed from `index.parts.ts`. Each value export becomes a part.
2. **Kebab segment derivation**: Source path `./time-group` → strip `./time-` prefix → `group`.
3. **HTML element matching**: Each part's kebab segment is used to find `{name}-{kebab}-element.ts` in the HTML directory (e.g., `time-group-element.ts`).
4. **Primary part identification**: The part with NO `{name}-{part}-element.ts` match, whose element is just `{name}-element.ts`, is the primary part.
5. **Shared resource attribution**: `{name}-core.ts` and `{name}-data-attrs.ts` are attributed to the primary part only.
### Naming Conventions Required
- Core interfaces must be `{Name}Props` and `{Name}State` (not `{Name}CoreProps` etc.)
- Part exports in `index.parts.ts` must be value exports (not type-only)
- HTML element files must follow `{name}-{part}-element.ts` naming
- Element classes must be `{Name}{Part}Element` (e.g., `TimeGroupElement`)
### JSON Output
Multi-part components have empty top-level `props`/`state`/`dataAttributes`. All data lives in the `parts` record:
```json
{
"name": "Time",
"props": {},
"state": {},
"dataAttributes": {},
"platforms": {},
"parts": {
"value": { "name": "Value", "description": "Displays a formatted time value.", "props": { ... }, ... },
"group": { "name": "Group", "description": "Container for composed time displays.", "props": {}, ... },
"separator": { "name": "Separator", "description": "Divider between time values.", "props": {}, ... }
}
}
```
### Troubleshooting
- **Part not appearing in JSON?** Check `index.parts.ts` exports the part as a value export (not type-only).
- **Props/state empty for primary part?** Verify core interfaces are named `{Name}Props`/`{Name}State`.
- **HTML tag name missing?** Verify element file follows `{name}-{part}-element.ts` naming and has `static tagName`.
- **No primary part warning?** Ensure the primary part's element file is just `{name}-element.ts` (not `{name}-{part}-element.ts`).
- **Part description missing?** Add a JSDoc comment to the React component export (e.g., `/** Displays a formatted time value. */` above `export const Value`).
## File Structure
```
site/scripts/api-docs-builder/
├── README.md # This file
└── src/
├── index.ts # Main entry point, orchestrates handlers
├── types.ts # TypeScript interfaces
├── formatter.ts # Type formatting utilities
├── utils.ts # Utility functions (naming helpers)
├── core-handler.ts # Extracts Props/State from core packages
├── data-attrs-handler.ts # Extracts data attributes
├── html-handler.ts # Extracts Lit element info
├── parts-handler.ts # Parses index.parts.ts for multi-part components
└── tests/
├── test-utils.ts
├── core-handler.test.ts
├── data-attrs-handler.test.ts
├── formatter.test.ts
├── html-handler.test.ts
├── parts-handler.test.ts
└── utils.test.ts
site/src/
├── content/generated-api-reference/ # Generated JSON files (gitignored)
│ ├── play-button.json
│ ├── mute-button.json
│ └── time.json
└── components/docs/api-reference/
├── ApiReference.astro # Unified component — renders full API reference from JSON
├── ApiPropsTable.astro # Props table
├── ApiStateTable.astro # State interface table
├── ApiDataAttrsTable.astro # Data attributes table
└── PropRow.astro # Expandable prop row
```
## Adding a New Component
### Single-Part Component
1. Create the component in `packages/core/src/core/ui/{name}/`
2. Export `{Name}Props` interface and `{Name}State` interface
3. Optionally create `{name}-data-attrs.ts` with data attribute definitions
4. Create the HTML element in `packages/html/src/ui/{name}/` with `static tagName`
5. Run `pnpm api-docs` to generate JSON
6. Use `` in MDX
### Multi-Part Component
1. Follow the single-part steps above for the primary part's core/data-attrs/element files
2. Create `packages/react/src/ui/{name}/index.parts.ts` exporting each part
3. Add JSDoc descriptions to each React component export for part descriptions
4. Create HTML element files for each non-primary part at `packages/html/src/ui/{name}/{name}-{part}-element.ts`
5. Run `pnpm api-docs` to generate JSON
6. Use `` in MDX
## Acknowledgements
This builder's architecture and approach were inspired by [Base UI](https://github.com/mui/base-ui)'s
`api-docs-builder`, maintained by MUI. Base UI is licensed under the
[MIT License](https://github.com/mui/base-ui/blob/master/LICENSE) (Copyright 2019 Material-UI SAS).
Thank you to the MUI team for the excellent reference implementation.
### Key differences from Base UI's builder
1. **Multi-platform**: One JSON per component containing all platform variants (React/HTML)
2. **Core-first**: Props come from core package, not platform-specific components
3. **Data attributes**: Extracted from dedicated `*-data-attrs.ts` files
4. **HTML elements**: Extracts Lit element `static tagName`
5. **No prettier**: Uses biome for formatting (removed prettier dependency)
## Dependencies
- `typescript-api-extractor`: AST parsing for TypeScript types
- `es-toolkit`: Utility functions (kebabCase, etc.)
- `tsx`: TypeScript execution
All dependencies are in `site/package.json` devDependencies.