Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
import * as React from 'react';
import * as ComponentDataAttributes from './ComponentDataAttributes';

interface Props {
/** The title to display */
title: string;
/** Whether the component is disabled */
disabled?: boolean;
/** Child elements */
children?: React.ReactNode;
}

/**
* A simple component that displays a title and optional children.
*/
export function Component(props: Props) {
// @focus-start @padding 1
const handleClick = (event: React.MouseEvent) => {
console.warn('Clicked', event);
};

return (
<button type="button" onClick={handleClick} {...{ [ComponentDataAttributes.type]: 'example' }}>
{props.title}
{!props.disabled ? props.children : null}
</button>
);
// @focus-end
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
/**
* The type of the component.
* @type {string}
*/
export const type = 'data-type';
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
import { createDemo } from '@/functions/createDemo';
import { TypesComponentDataAttrEsm } from './types';

export const DemoUseTypesDataAttrEsm = createDemo(import.meta.url, TypesComponentDataAttrEsm);
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
import * as React from 'react';
import { DemoUseTypesDataAttrEsm } from '.';

export default function Page() {
return <DemoUseTypesDataAttrEsm />;
}
20 changes: 20 additions & 0 deletions docs/app/docs-infra/hooks/use-types/demos/data-attr-esm/test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
import { test, expect } from '@playwright/test';
import { appRoute } from '@/appRoute';

// The standalone demo route, derived from this file's location under `app`.
const route = appRoute(import.meta.url);

test('use-types/data-attr-esm renders its content', async ({ page }) => {
const pageErrors: Error[] = [];
page.on('pageerror', (error) => pageErrors.push(error));

await page.goto(route);
const demo = page.locator('.demo').first();

// The data attribute is declared as a named constant, so seeing it in the table
// confirms the constant group was extracted.
await expect(demo).toContainText('data-type', { timeout: 15000 });

// A working demo mounts and renders its content without throwing.
expect(pageErrors, 'the demo should mount without uncaught errors').toEqual([]);
});
23 changes: 23 additions & 0 deletions docs/app/docs-infra/hooks/use-types/demos/data-attr-esm/types.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
# Data Attr Esm

[//]: types.ts '<-- Autogenerated By (do not edit the following markdown directly), run: pnpm docs:validate docs-infra/hooks/use-types/demos/data-attr-esm'

## API Reference

### Component

A simple component that displays a title and optional children.

**Component Props:**

| Prop | Type | Default | Description |
| :------- | :---------------- | :------ | :-------------------------------- |
| title\* | `string` | - | The title to display |
| disabled | `boolean` | - | Whether the component is disabled |
| children | `React.ReactNode` | - | Child elements |

**Component Data Attributes:**

| Attribute | Type | Description |
| :-------- | :------- | :------------------------- |
| data-type | `string` | The type of the component. |
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
import { createTypes } from '../createTypes';
import { Component } from './Component';

export const TypesComponentDataAttrEsm = createTypes(import.meta.url, Component);
18 changes: 18 additions & 0 deletions docs/app/docs-infra/hooks/use-types/demos/page.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
- Blocks Inherited - ([Outline](#blocks-inherited), [Contents](./blocks-inherited/page.mdx))
- Function - ([Outline](#function), [Contents](./function/page.mdx))
- Class [New] - ([Outline](#class), [Contents](./class/page.mdx))
- Data Attr Esm [New] - ([Outline](#data-attr-esm), [Contents](./data-attr-esm/page.mdx))

[//]: # 'This section is autogenerated, DO NOT EDIT AFTER THIS LINE, run: pnpm docs:validate docs-infra/hooks/use-types/demos'

Expand Down Expand Up @@ -151,6 +152,23 @@ No description available

[Read more](./class/page.mdx)

## Data Attr Esm

No description available

<details>

<summary>Outline</summary>

- Exports:
- Component
- Props: children, disabled, title
- Data Attributes: data-type

</details>

[Read more](./data-attr-esm/page.mdx)

[//]: # 'The above section is autogenerated, but the remainder of the file can be modified.'

export const metadata =
Expand Down
12 changes: 12 additions & 0 deletions docs/app/docs-infra/hooks/use-types/page.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,18 @@ This demo shows how to display data attributes defined in a component using the

---

import { DemoUseTypesDataAttrEsm } from './demos/data-attr-esm';

<DemoUseTypesDataAttrEsm.Title />

This demo shows the same data attributes declared as named constants rather than an enum. Both forms produce identical documentation.

<DemoUseTypesDataAttrEsm />

[See Demo](./demos/data-attr-esm/)

---

import { DemoUseTypesHook } from './demos/hook';

<DemoUseTypesHook.Title />
Expand Down
2 changes: 1 addition & 1 deletion docs/app/docs-infra/overview/architecture/page.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -132,7 +132,7 @@ types.ts (createTypes call)
┌─────────┐
│ Format │ Format props, find DataAttributes/CssVars enums
│ Format │ Format props, find DataAttributes/CssVars constant groups
│ Types │ Parse JSDoc descriptions as markdown
└────┬────┘
Expand Down
26 changes: 25 additions & 1 deletion docs/app/docs-infra/pipeline/load-server-types-meta/page.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -136,14 +136,38 @@ Resolves variant paths to actual file system locations, handling three categorie

Recursively walks entrypoint directories and re-exported directories to discover `DataAttributes` and `CssVars` files. Re-exported directories are found by parsing relative exports (e.g., `export * from '../menu/'`) from the entrypoint source files.

A meta file declares a **constant group** — a named, documented set of key/value constants belonging to one component. The group is named after its file, so `AccordionPanelCssVars.ts` supplies the CSS variables for `AccordionPanel`. Two authoring styles are recognized:

```ts
// An enum named after the file.
export enum AccordionPanelCssVars {
/**
* The accordion panel's height.
* @type {number}
*/
accordionPanelHeight = '--accordion-panel-height',
}
```

```ts
// Named literal constants, collapsed into a group under the file's name.
/**
* The accordion panel's height.
* @type {number}
*/
export const accordionPanelHeight = '--accordion-panel-height';
```

Both produce identical documentation. The constant form lets application bundlers inline and tree-shake the values, since nothing references a runtime enum object. Constants re-exported from another meta file (`export * from '../combobox/clear/ComboboxClearDataAttributes'`) are collected too, and belong to the group named after the re-exporting file.

### 4. Process Types in Worker

Type extraction is offloaded to a dedicated worker thread. Inside the worker:

- **Creates an optimized TypeScript program** using an in-memory `LanguageServiceHost` singleton that caches file contents and tracks versions for incremental updates
- **Parses exports** using `typescript-api-extractor`'s `parseFromProgram`, which extracts components, hooks, functions, classes, type aliases, and interfaces with their full type trees
- **Builds a type name map** from flat export names to their dotted equivalents (e.g., `AccordionRootState` → `Accordion.Root.State`), only when both the flat and dotted names exist as exports
- **Parses meta files** separately to include DataAttributes/CssVars type info that isn't imported from the entrypoint
- **Parses meta files** separately to include DataAttributes/CssVars type info that isn't imported from the entrypoint, then normalizes each file's exports into a single constant group named after the file
- **Collects dependencies** by walking the import graph from each entrypoint for HMR file watching
- **Strips functions** from the result for serialization across the worker boundary (structured clone can't handle functions)

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import ts from 'typescript';
import { createOptimizedProgram } from './createOptimizedProgram';
import { augmentComponentsWithInheritedProps } from './inheritedExternalProps';
import type { InheritedExternalPropsConfig } from './inheritedExternalProps';
import { transformConstantGroup } from './transformConstantGroup';
import { extractJSDocText, isJSDocNodeArray } from './extractJSDocText';
import { PerformanceTracker } from './performanceTracking';
import type { PerformanceLog } from './performanceTracking';
Expand Down Expand Up @@ -447,8 +448,12 @@ export async function processTypes(request: WorkerRequest): Promise<WorkerRespon

const { exports: internalExport } = parseFromProgram(file, program, parserOptions);

internalTypesCache[file] = internalExport;
return internalExport;
// Metadata files may declare their members as an enum or as named constants;
// normalize both to a single constant group named after the file.
const groupExports = transformConstantGroup(file, internalExport);

internalTypesCache[file] = groupExports;
return groupExports;
});

const internalTypes = allInternalTypes.reduce((acc, cur) => {
Expand Down
Loading
Loading