@mui/internal-docs-infra 0.13.1-canary.0 → 0.13.1-canary.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (25) hide show
  1. package/CodeHighlighter/buildStringFallback.mjs +1 -1
  2. package/package.json +2 -2
  3. package/pipeline/loadServerTypesMeta/constantGroups.d.mts +48 -0
  4. package/pipeline/loadServerTypesMeta/constantGroups.mjs +202 -0
  5. package/pipeline/loadServerTypesMeta/formatComponent.d.mts +4 -5
  6. package/pipeline/loadServerTypesMeta/formatComponent.mjs +8 -90
  7. package/pipeline/loadServerTypesMeta/formatRaw.d.mts +8 -3
  8. package/pipeline/loadServerTypesMeta/formatRaw.mjs +12 -33
  9. package/pipeline/loadServerTypesMeta/formatType.d.mts +6 -1
  10. package/pipeline/loadServerTypesMeta/formatType.mjs +1 -1
  11. package/pipeline/loadServerTypesMeta/loadServerTypesMeta.d.mts +0 -1
  12. package/pipeline/loadServerTypesMeta/loadServerTypesMeta.mjs +15 -136
  13. package/pipeline/loadServerTypesMeta/parseTestSources.d.mts +11 -1
  14. package/pipeline/loadServerTypesMeta/parseTestSources.mjs +19 -7
  15. package/pipeline/loadServerTypesMeta/processTypes.d.mts +2 -8
  16. package/pipeline/loadServerTypesMeta/processTypes.mjs +14 -49
  17. package/pipeline/parseSource/createPlainTextRoot.d.mts +18 -0
  18. package/pipeline/parseSource/createPlainTextRoot.mjs +38 -0
  19. package/pipeline/parseSource/parseSource.d.mts +2 -18
  20. package/pipeline/parseSource/parseSource.mjs +2 -33
  21. package/pipeline/syncTypes/generateTypesMarkdown.mjs +10 -14
  22. package/pipeline/loadServerTypesMeta/findMetaFiles.d.mts +0 -9
  23. package/pipeline/loadServerTypesMeta/findMetaFiles.mjs +0 -45
  24. package/pipeline/loadServerTypesMeta/transformConstantGroup.d.mts +0 -14
  25. package/pipeline/loadServerTypesMeta/transformConstantGroup.mjs +0 -80
@@ -1,5 +1,5 @@
1
1
  import { buildRootFallback } from "./fallbackFormat.mjs";
2
- import { parsePlainText } from "../pipeline/parseSource/index.mjs";
2
+ import { parsePlainText } from "../pipeline/parseSource/createPlainTextRoot.mjs";
3
3
  function isPromiseLike(value) {
4
4
  return typeof value === 'object' && value !== null && typeof value.then === 'function';
5
5
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mui/internal-docs-infra",
3
- "version": "0.13.1-canary.0",
3
+ "version": "0.13.1-canary.2",
4
4
  "author": "MUI Team",
5
5
  "description": "MUI Infra - internal documentation creation tools.",
6
6
  "license": "MIT",
@@ -814,5 +814,5 @@
814
814
  "bin": {
815
815
  "docs-infra": "./cli/index.mjs"
816
816
  },
817
- "gitSha": "764e3b3cfc14293e74e93f3413609025f502bab9"
817
+ "gitSha": "409e39410c3eac0b4743be41962f632aab3fc4a3"
818
818
  }
@@ -0,0 +1,48 @@
1
+ import ts from 'typescript';
2
+ import type * as tae from 'typescript-api-extractor';
3
+ /** Which of a component's tables a constant group holds. */
4
+ export type ConstantGroupKind = 'dataAttributes' | 'cssVariables';
5
+ export interface ConstantGroupTarget {
6
+ /** The component's name as the entrypoint exports it, e.g. `Toolbar.Button` */
7
+ component: string;
8
+ kind: ConstantGroupKind;
9
+ }
10
+ /** The constant groups holding one component's tables. */
11
+ export type ComponentConstantGroups = Partial<Record<ConstantGroupKind, tae.EnumNode>>;
12
+ /** The value of a constant in a constant group. */
13
+ type ConstantValue = string | number;
14
+ /**
15
+ * Finds the entrypoint's namespace exports whose module holds nothing but literal constants,
16
+ * e.g. `export * as ButtonDataAttributes from './ButtonDataAttributes'`, with their values.
17
+ * Namespaces nested in other namespace exports are found too, under their dotted path
18
+ * (`Toolbar.ButtonDataAttributes`).
19
+ *
20
+ * The parser flattens such a namespace into one export per member and cannot tell a constant
21
+ * from a type alias of the same literal, so the checker is asked instead.
22
+ *
23
+ * @returns Member values keyed by namespace path, then member name
24
+ */
25
+ export declare function findConstantNamespaces(entrypoint: string, program: ts.Program): Map<string, Map<string, ConstantValue>>;
26
+ /**
27
+ * Collapses the flattened members of each constant namespace (`ButtonDataAttributes.open`)
28
+ * into a single enum-shaped export named after the namespace, in place of its first member.
29
+ */
30
+ export declare function foldConstantNamespaces(exports: tae.ExportNode[], namespaces: Map<string, Map<string, ConstantValue>>): tae.ExportNode[];
31
+ /**
32
+ * Writes the declaration of a constant group as it is exported: a namespace of constants
33
+ * for a namespace export, an enum otherwise.
34
+ */
35
+ export declare function formatConstantGroupDeclaration(name: string, group: tae.EnumNode, asNamespace: boolean): string;
36
+ /**
37
+ * Resolves which component each constant group documents from its name: the component's
38
+ * name with its dots removed, followed by `DataAttributes` or `CssVariables`.
39
+ *
40
+ * Throws when the name before the suffix is not exactly one of the exported components.
41
+ *
42
+ * @returns Targets keyed by the group's export name, and each component's groups
43
+ */
44
+ export declare function matchConstantGroups(exports: tae.ExportNode[]): {
45
+ targets: Map<string, ConstantGroupTarget>;
46
+ byComponent: Map<string, ComponentConstantGroups>;
47
+ };
48
+ export {};
@@ -0,0 +1,202 @@
1
+ import ts from 'typescript';
2
+ import { EnumMember, EnumNode, ExportNode, TypeName } from 'typescript-api-extractor';
3
+ import { formatPropertyComment } from "./formatType.mjs";
4
+ import { isComponentType, isEnumType } from "./typeGuards.mjs";
5
+
6
+ /** Which of a component's tables a constant group holds. */
7
+
8
+ /**
9
+ * A constant group is a named set of constant values an entrypoint publishes, either as an
10
+ * enum or as a namespace of constants (`export * as ButtonDataAttributes from './…'`); both
11
+ * are represented as an enum-shaped export. Its name tells which component table it holds:
12
+ * the component's name with its dots removed, followed by one of these suffixes, e.g.
13
+ * `ToolbarButtonDataAttributes` holds the data attributes of `Toolbar.Button`.
14
+ */
15
+ const KIND_SUFFIXES = {
16
+ dataAttributes: 'DataAttributes',
17
+ cssVariables: 'CssVariables'
18
+ };
19
+
20
+ /** The constant groups holding one component's tables. */
21
+
22
+ function resolveAlias(symbol, checker) {
23
+ // eslint-disable-next-line no-bitwise -- TypeScript symbol flags are a bitmask
24
+ return symbol.flags & ts.SymbolFlags.Alias ? checker.getAliasedSymbol(symbol) : symbol;
25
+ }
26
+
27
+ /** The value of a constant in a constant group. */
28
+
29
+ /**
30
+ * Reads the value of a `const` holding a string or number literal.
31
+ */
32
+ function readLiteralConstant(symbol, checker) {
33
+ const declaration = symbol.valueDeclaration;
34
+ if (!declaration || !ts.isVariableDeclaration(declaration) ||
35
+ // eslint-disable-next-line no-bitwise -- TypeScript node flags are a bitmask
36
+ !(ts.getCombinedNodeFlags(declaration) & ts.NodeFlags.Const)) {
37
+ return undefined;
38
+ }
39
+ const type = checker.getTypeOfSymbol(symbol);
40
+ return type.isStringLiteral() || type.isNumberLiteral() ? type.value : undefined;
41
+ }
42
+
43
+ /**
44
+ * Reads a module's exports as constant values, when it exports nothing but literal constants.
45
+ */
46
+ function readConstantModule(moduleSymbol, checker) {
47
+ const values = new Map();
48
+ const members = checker.getExportsOfModule(moduleSymbol);
49
+ const isConstantModule = members.length > 0 && members.every(member => {
50
+ const value = readLiteralConstant(resolveAlias(member, checker), checker);
51
+ if (value === undefined) {
52
+ return false;
53
+ }
54
+ values.set(member.name, value);
55
+ return true;
56
+ });
57
+ return isConstantModule ? values : undefined;
58
+ }
59
+
60
+ /**
61
+ * Finds the entrypoint's namespace exports whose module holds nothing but literal constants,
62
+ * e.g. `export * as ButtonDataAttributes from './ButtonDataAttributes'`, with their values.
63
+ * Namespaces nested in other namespace exports are found too, under their dotted path
64
+ * (`Toolbar.ButtonDataAttributes`).
65
+ *
66
+ * The parser flattens such a namespace into one export per member and cannot tell a constant
67
+ * from a type alias of the same literal, so the checker is asked instead.
68
+ *
69
+ * @returns Member values keyed by namespace path, then member name
70
+ */
71
+ export function findConstantNamespaces(entrypoint, program) {
72
+ const namespaces = new Map();
73
+ const sourceFile = program.getSourceFile(entrypoint);
74
+ const checker = program.getTypeChecker();
75
+ const entrypointSymbol = sourceFile && checker.getSymbolAtLocation(sourceFile);
76
+ const visited = new Set();
77
+ const visit = (moduleSymbol, prefix) => {
78
+ for (const exportSymbol of checker.getExportsOfModule(moduleSymbol)) {
79
+ if (exportSymbol.declarations?.some(ts.isNamespaceExport)) {
80
+ const target = checker.getAliasedSymbol(exportSymbol);
81
+ const path = `${prefix}${exportSymbol.name}`;
82
+ const values = readConstantModule(target, checker);
83
+ if (values) {
84
+ namespaces.set(path, values);
85
+ } else if (!visited.has(target)) {
86
+ visited.add(target);
87
+ visit(target, `${path}.`);
88
+ }
89
+ }
90
+ }
91
+ };
92
+ if (entrypointSymbol) {
93
+ visit(entrypointSymbol, '');
94
+ }
95
+ return namespaces;
96
+ }
97
+
98
+ /**
99
+ * Collapses the flattened members of each constant namespace (`ButtonDataAttributes.open`)
100
+ * into a single enum-shaped export named after the namespace, in place of its first member.
101
+ */
102
+ export function foldConstantNamespaces(exports, namespaces) {
103
+ if (namespaces.size === 0) {
104
+ return exports;
105
+ }
106
+ const groups = new Map();
107
+ const folded = [];
108
+ for (const node of exports) {
109
+ // Members sit directly under their namespace, which may itself be nested
110
+ // (`Toolbar.ButtonDataAttributes.pressed`).
111
+ const dot = node.name.lastIndexOf('.');
112
+ const namespace = dot === -1 ? undefined : node.name.slice(0, dot);
113
+ const memberName = node.name.slice(dot + 1);
114
+ const value = namespace === undefined ? undefined : namespaces.get(namespace)?.get(memberName);
115
+ if (namespace === undefined || value === undefined) {
116
+ folded.push(node);
117
+ } else {
118
+ let members = groups.get(namespace);
119
+ if (!members) {
120
+ members = [];
121
+ groups.set(namespace, members);
122
+ folded.push(new ExportNode(namespace, new EnumNode(new TypeName(namespace), members, undefined), undefined));
123
+ }
124
+ // Typed as a string, but the parser stores numeric enum values as numbers too; do
125
+ // the same so both forms render alike.
126
+ members.push(new EnumMember(memberName, value, node.documentation));
127
+ }
128
+ }
129
+ return folded;
130
+ }
131
+
132
+ /**
133
+ * Writes the declaration of a constant group as it is exported: a namespace of constants
134
+ * for a namespace export, an enum otherwise.
135
+ */
136
+ export function formatConstantGroupDeclaration(name, group, asNamespace) {
137
+ const members = group.members.map(member => {
138
+ // Enum members hold the literal's value, numbers included; a computed member has none.
139
+ const value = member.value;
140
+ const literal = typeof value === 'number' ? String(value) : JSON.stringify(value);
141
+ const comment = member.documentation ? formatPropertyComment(member.documentation) : undefined;
142
+ let declaration = `${member.name} = ${literal},`;
143
+ if (asNamespace) {
144
+ declaration = `const ${member.name}: ${literal};`;
145
+ } else if (value === undefined) {
146
+ declaration = `${member.name},`;
147
+ }
148
+ return comment ? `${comment}\n${declaration}` : declaration;
149
+ });
150
+ return asNamespace ? `declare namespace ${name} {\n${members.join('\n')}\n}` : `enum ${name} {\n${members.join('\n')}\n}`;
151
+ }
152
+
153
+ /**
154
+ * Resolves which component each constant group documents from its name: the component's
155
+ * name with its dots removed, followed by `DataAttributes` or `CssVariables`.
156
+ *
157
+ * Throws when the name before the suffix is not exactly one of the exported components.
158
+ *
159
+ * @returns Targets keyed by the group's export name, and each component's groups
160
+ */
161
+ export function matchConstantGroups(exports) {
162
+ const targets = new Map();
163
+ const byComponent = new Map();
164
+ const componentsByFlatName = new Map();
165
+ for (const node of exports) {
166
+ if (isComponentType(node.type)) {
167
+ const flatName = node.name.replaceAll('.', '');
168
+ const names = componentsByFlatName.get(flatName);
169
+ if (names) {
170
+ names.push(node.name);
171
+ } else {
172
+ componentsByFlatName.set(flatName, [node.name]);
173
+ }
174
+ }
175
+ }
176
+ const kinds = Object.keys(KIND_SUFFIXES);
177
+ for (const node of exports) {
178
+ // Names are compared without dots, like the component names they stand for, so a group
179
+ // exported inside a namespace (`Toolbar.ButtonDataAttributes`) matches too.
180
+ const groupName = node.name.replaceAll('.', '');
181
+ const kind = kinds.find(candidate => groupName.endsWith(KIND_SUFFIXES[candidate]) && groupName.length > KIND_SUFFIXES[candidate].length);
182
+ if (kind && isEnumType(node.type)) {
183
+ const flatName = groupName.slice(0, -KIND_SUFFIXES[kind].length);
184
+ const components = componentsByFlatName.get(flatName) ?? [];
185
+ if (components.length !== 1) {
186
+ throw new Error(components.length === 0 ? `[constantGroups] ${node.name} - no exported component is named ${flatName}` : `[constantGroups] ${node.name} - ${components.join(', ')} are all named ${flatName}`);
187
+ }
188
+ const [component] = components;
189
+ targets.set(node.name, {
190
+ component,
191
+ kind
192
+ });
193
+ const groups = byComponent.get(component) ?? {};
194
+ groups[kind] ??= node.type;
195
+ byComponent.set(component, groups);
196
+ }
197
+ }
198
+ return {
199
+ targets,
200
+ byComponent
201
+ };
202
+ }
@@ -1,5 +1,6 @@
1
1
  import type * as tae from 'typescript-api-extractor';
2
2
  import type { FormattedProperty, FormattedEnumMember, FormatInlineTypeOptions, DescriptionReplacement } from "./format.mjs";
3
+ import type { ComponentConstantGroups } from "./constantGroups.mjs";
3
4
  import type { TypeRewriteContext } from "./rewriteTypes.mjs";
4
5
  import type { ExternalTypesCollector } from "./externalTypes.mjs";
5
6
  import type { HastRoot } from "../../CodeHighlighter/types.mjs";
@@ -20,10 +21,8 @@ export type ComponentTypeMeta = {
20
21
  * Options for customizing component data formatting.
21
22
  */
22
23
  export interface FormatComponentOptions {
23
- /** Suffix for data attributes enum name (default: 'DataAttributes') */
24
- dataAttributesSuffix?: string;
25
- /** Suffix for CSS variables enum name (default: 'CssVars') */
26
- cssVariablesSuffix?: string;
24
+ /** The constant groups holding this component's tables (see `matchConstantGroups`) */
25
+ constantGroups?: ComponentConstantGroups;
27
26
  /** Pattern/replacement pairs to apply to descriptions */
28
27
  descriptionReplacements?: DescriptionReplacement[];
29
28
  /** Options for inline type formatting (e.g., unionPrintWidth) */
@@ -44,7 +43,7 @@ export interface FormatComponentOptions {
44
43
  */
45
44
  export declare function formatComponentData(component: tae.ExportNode & {
46
45
  type: tae.ComponentNode;
47
- }, allExports: tae.ExportNode[], typeNameMap: Record<string, string>, rewriteContext: TypeRewriteContext, options?: FormatComponentOptions): Promise<ComponentTypeMeta>;
46
+ }, typeNameMap: Record<string, string>, rewriteContext: TypeRewriteContext, options?: FormatComponentOptions): Promise<ComponentTypeMeta>;
48
47
  /**
49
48
  * Type guard to check if an export is a public component that should be documented.
50
49
  *
@@ -20,10 +20,8 @@ import * as memberOrder from "../loadServerTypesText/order.mjs";
20
20
  *
21
21
  * The component must be validated with `isPublicComponent()` before calling this function.
22
22
  */
23
- export async function formatComponentData(component, allExports, typeNameMap, rewriteContext, options = {}) {
23
+ export async function formatComponentData(component, typeNameMap, rewriteContext, options = {}) {
24
24
  const {
25
- dataAttributesSuffix = 'DataAttributes',
26
- cssVariablesSuffix = 'CssVars',
27
25
  descriptionReplacements,
28
26
  formatting,
29
27
  externalTypes
@@ -34,91 +32,11 @@ export async function formatComponentData(component, allExports, typeNameMap, re
34
32
  const descriptionText = component.documentation?.description ? applyDescriptionReplacements(component.documentation.description, descriptionReplacements) : undefined;
35
33
  const description = descriptionText ? await parseMarkdownToHast(descriptionText) : undefined;
36
34
 
37
- // Find data attributes and CSS variables in a single loop
38
- let dataAttributes;
39
- let cssVariables;
40
-
41
- // For DataAttributes/CssVars lookup, use the originalName (before transformations).
42
- // Example for re-exported component:
43
- // - Component: ContextMenu.Backdrop (transformed from MenuBackdrop)
44
- // - originalName: MenuBackdrop
45
- // - We look for: MenuBackdropDataAttributes (using originalName + suffix)
46
- // - That export's originalName will also be MenuBackdropDataAttributes
47
- const originalName = component.originalName;
48
- const componentNameForLookup = originalName || component.name.replace(/\./g, '');
49
- const dataAttributesName = `${componentNameForLookup}${dataAttributesSuffix}`;
50
- const cssVariablesName = `${componentNameForLookup}${cssVariablesSuffix}`;
51
-
52
- // Get the component's short name (e.g., "Trigger" from "AlertDialog.Trigger")
53
- const componentShortName = component.name.split('.').pop() || component.name;
54
- const dataAttributesSuffixWithShortName = `${componentShortName}${dataAttributesSuffix}`;
55
- const cssVariablesSuffixWithShortName = `${componentShortName}${cssVariablesSuffix}`;
56
-
57
- // Look for DataAttributes/CssVars by checking originalName on each export
58
- // First pass: exact match
59
- for (const node of allExports) {
60
- const nodeOriginalName = node.originalName;
61
- const nodeName = nodeOriginalName || node.name;
62
- if (nodeName === dataAttributesName) {
63
- dataAttributes = node;
64
- } else if (nodeName === cssVariablesName) {
65
- cssVariables = node;
66
- }
67
-
68
- // Early exit if we found both
69
- if (dataAttributes && cssVariables) {
70
- break;
71
- }
72
- }
73
-
74
- // Fallback: For re-exported components (like AlertDialog.Trigger which re-exports DialogTrigger),
75
- // the DataAttributes file uses the original component name (DialogTriggerDataAttributes).
76
- // If we didn't find an exact match, look for any DataAttributes ending with the component's
77
- // short name (e.g., "TriggerDataAttributes").
78
- // Priority: prefer DataAttributes whose prefix is contained in the component's namespace
79
- // (e.g., for AlertDialog.Trigger, prefer DialogTriggerDataAttributes over MenuTriggerDataAttributes)
80
- if (!dataAttributes || !cssVariables) {
81
- // Get the component's namespace (e.g., "AlertDialog" from "AlertDialog.Trigger")
82
- const componentNamespace = component.name.includes('.') ? component.name.substring(0, component.name.lastIndexOf('.')) : '';
83
-
84
- // Collect all matching candidates
85
- const dataAttributesCandidates = [];
86
- const cssVariablesCandidates = [];
87
- for (const node of allExports) {
88
- const nodeOriginalName = node.originalName;
89
- const nodeName = nodeOriginalName || node.name;
90
-
91
- // Check if this export ends with the component's short name + suffix
92
- if (!dataAttributes && nodeName.endsWith(dataAttributesSuffixWithShortName)) {
93
- // Extract the prefix (e.g., "Dialog" from "DialogTriggerDataAttributes")
94
- const prefix = nodeName.slice(0, -dataAttributesSuffixWithShortName.length);
95
- // Priority: 2 if prefix is contained in namespace (related component), 1 otherwise
96
- const priority = componentNamespace.includes(prefix) ? 2 : 1;
97
- dataAttributesCandidates.push({
98
- node,
99
- priority
100
- });
101
- }
102
- if (!cssVariables && nodeName.endsWith(cssVariablesSuffixWithShortName)) {
103
- const prefix = nodeName.slice(0, -cssVariablesSuffixWithShortName.length);
104
- const priority = componentNamespace.includes(prefix) ? 2 : 1;
105
- cssVariablesCandidates.push({
106
- node,
107
- priority
108
- });
109
- }
110
- }
111
-
112
- // Select the highest priority candidate
113
- if (!dataAttributes && dataAttributesCandidates.length > 0) {
114
- dataAttributesCandidates.sort((a, b) => b.priority - a.priority);
115
- dataAttributes = dataAttributesCandidates[0].node;
116
- }
117
- if (!cssVariables && cssVariablesCandidates.length > 0) {
118
- cssVariablesCandidates.sort((a, b) => b.priority - a.priority);
119
- cssVariables = cssVariablesCandidates[0].node;
120
- }
121
- }
35
+ // The component's data attributes and CSS variables are the constant groups matched to it.
36
+ const {
37
+ dataAttributes,
38
+ cssVariables
39
+ } = options.constantGroups ?? {};
122
40
  const raw = {
123
41
  name: component.name,
124
42
  description,
@@ -131,8 +49,8 @@ export async function formatComponentData(component, allExports, typeNameMap, re
131
49
  externalTypes,
132
50
  descriptionReplacements
133
51
  }), options.ordering?.props ?? memberOrder.props),
134
- dataAttributes: dataAttributes && dataAttributes.type.kind === 'enum' ? sortObjectByKeys(await formatEnum(dataAttributes.type, descriptionReplacements), options.ordering?.dataAttributes ?? memberOrder.dataAttributes) : {},
135
- cssVariables: cssVariables && cssVariables.type.kind === 'enum' ? sortObjectByKeys(await formatEnum(cssVariables.type, descriptionReplacements), options.ordering?.cssVariables ?? memberOrder.cssVariables) : {}
52
+ dataAttributes: dataAttributes ? sortObjectByKeys(await formatEnum(dataAttributes, descriptionReplacements), options.ordering?.dataAttributes ?? memberOrder.dataAttributes) : {},
53
+ cssVariables: cssVariables ? sortObjectByKeys(await formatEnum(cssVariables, descriptionReplacements), options.ordering?.cssVariables ?? memberOrder.cssVariables) : {}
136
54
  };
137
55
 
138
56
  // Post-process type strings to align naming across re-exports and hide internal suffixes.
@@ -1,5 +1,6 @@
1
1
  import type * as tae from 'typescript-api-extractor';
2
2
  import type { FormattedProperty, FormatInlineTypeOptions, DescriptionReplacement } from "./format.mjs";
3
+ import type { ConstantGroupTarget } from "./constantGroups.mjs";
3
4
  import type { TypeRewriteContext } from "./rewriteTypes.mjs";
4
5
  import type { ExternalTypesCollector } from "./externalTypes.mjs";
5
6
  import type { HastRoot } from "../../CodeHighlighter/types.mjs";
@@ -12,7 +13,7 @@ export interface ReExportInfo {
12
13
  /** Anchor slug for linking (e.g., "#trigger") */
13
14
  slug: string;
14
15
  /** What kind of type this re-exports */
15
- suffix: 'props' | 'css-variables' | 'data-attributes';
16
+ suffix: 'props';
16
17
  }
17
18
  /**
18
19
  * Formatted raw type metadata with the type declaration as a formatted code string.
@@ -46,11 +47,11 @@ export type RawTypeMeta = {
46
47
  */
47
48
  reExportOf?: ReExportInfo;
48
49
  /**
49
- * For DataAttributes types, the component name this type belongs to.
50
+ * For constant groups holding a component's data attributes, that component's name.
50
51
  */
51
52
  dataAttributesOf?: string;
52
53
  /**
53
- * For CssVars types, the component name this type belongs to.
54
+ * For constant groups holding a component's CSS variables, that component's name.
54
55
  */
55
56
  cssVarsOf?: string;
56
57
  /**
@@ -75,6 +76,10 @@ export interface FormatRawOptions {
75
76
  externalTypes?: ExternalTypesCollector;
76
77
  /** Pattern/replacement pairs to apply to descriptions */
77
78
  descriptionReplacements?: DescriptionReplacement[];
79
+ /** The component table this constant group holds (see `matchConstantGroups`) */
80
+ constantGroup?: ConstantGroupTarget;
81
+ /** Whether the constant group was exported as a namespace of constants rather than an enum */
82
+ constantNamespace?: boolean;
78
83
  }
79
84
  /**
80
85
  * Formats a raw type export into a structured metadata object with formatted code.
@@ -1,6 +1,7 @@
1
1
  import { prettyFormat, parseMarkdownToHast, applyDescriptionReplacements, formatProperties, extractTypeParameters } from "./format.mjs";
2
2
  import { formatType } from "./formatType.mjs";
3
3
  import { isEnumType, isObjectType } from "./typeGuards.mjs";
4
+ import { formatConstantGroupDeclaration } from "./constantGroups.mjs";
4
5
  import { rewriteTypeStringsDeep } from "./rewriteTypes.mjs";
5
6
 
6
7
  /**
@@ -33,7 +34,9 @@ import { rewriteTypeStringsDeep } from "./rewriteTypes.mjs";
33
34
  */
34
35
  export async function formatRawData(exportNode, displayName, typeNameMap, rewriteContext, _options = {}) {
35
36
  const {
36
- descriptionReplacements
37
+ descriptionReplacements,
38
+ constantGroup,
39
+ constantNamespace
37
40
  } = _options;
38
41
  const descriptionText = exportNode.documentation?.description ? applyDescriptionReplacements(exportNode.documentation.description, descriptionReplacements) : undefined;
39
42
  const description = descriptionText ? await parseMarkdownToHast(descriptionText) : undefined;
@@ -50,8 +53,9 @@ export async function formatRawData(exportNode, displayName, typeNameMap, rewrit
50
53
  };
51
54
  }));
52
55
 
53
- // For enums, still generate the code block but also include members
54
- const formattedCode = await generateFormattedCode(exportNode, displayName, typeNameMap);
56
+ // For enums, still generate the code block but also include members. The declaration
57
+ // follows how the group is exported: an enum, or a namespace of constants.
58
+ const formattedCode = await prettyFormat(formatConstantGroupDeclaration(displayName.replace(/\./g, ''), exportNode.type, Boolean(constantNamespace)), null);
55
59
 
56
60
  // Rewrite type names in descriptions (but NOT in formattedCode which is valid TypeScript syntax)
57
61
  const rewrittenDescriptionText = descriptionText ? rewriteTypeStringsDeep(descriptionText, rewriteContext) : undefined;
@@ -62,36 +66,11 @@ export async function formatRawData(exportNode, displayName, typeNameMap, rewrit
62
66
  formattedCode,
63
67
  enumMembers
64
68
  };
65
- return raw;
66
- }
67
-
68
- // Handle DataAttributes types
69
- if (displayName.endsWith('.DataAttributes')) {
70
- const componentName = displayName.replace('.DataAttributes', '');
71
- const formattedCode = await generateFormattedCode(exportNode, displayName, typeNameMap);
72
- const rewrittenDescriptionText = descriptionText ? rewriteTypeStringsDeep(descriptionText, rewriteContext) : undefined;
73
- const raw = {
74
- name: displayName,
75
- description,
76
- descriptionText: rewrittenDescriptionText,
77
- formattedCode,
78
- dataAttributesOf: componentName
79
- };
80
- return raw;
81
- }
82
-
83
- // Handle CssVars types
84
- if (displayName.endsWith('.CssVars')) {
85
- const componentName = displayName.replace('.CssVars', '');
86
- const formattedCode = await generateFormattedCode(exportNode, displayName, typeNameMap);
87
- const rewrittenDescriptionText = descriptionText ? rewriteTypeStringsDeep(descriptionText, rewriteContext) : undefined;
88
- const raw = {
89
- name: displayName,
90
- description,
91
- descriptionText: rewrittenDescriptionText,
92
- formattedCode,
93
- cssVarsOf: componentName
94
- };
69
+ if (constantGroup?.kind === 'dataAttributes') {
70
+ raw.dataAttributesOf = constantGroup.component;
71
+ } else if (constantGroup?.kind === 'cssVariables') {
72
+ raw.cssVarsOf = constantGroup.component;
73
+ }
95
74
  return raw;
96
75
  }
97
76
 
@@ -20,4 +20,9 @@ export declare function formatType(type: tae.AnyType, options: FormatTypeOptions
20
20
  * processing, then runs the output through `prettyFormat()` for consistent styling.
21
21
  */
22
22
  export declare function prettyFormatType(type: tae.AnyType, options: FormatTypeOptions): Promise<string>;
23
- export declare function getFullyQualifiedName(typeName: tae.TypeName, exportNames: string[], typeNameMap: Record<string, string>, preserveTypeParameters?: boolean): string;
23
+ export declare function getFullyQualifiedName(typeName: tae.TypeName, exportNames: string[], typeNameMap: Record<string, string>, preserveTypeParameters?: boolean): string;
24
+ /**
25
+ * Formats a JSDoc comment block from property documentation.
26
+ * Returns undefined if no meaningful content to document.
27
+ */
28
+ export declare function formatPropertyComment(documentation: tae.Documentation): string | undefined;
@@ -818,7 +818,7 @@ function normalizeQuotes(str) {
818
818
  * Formats a JSDoc comment block from property documentation.
819
819
  * Returns undefined if no meaningful content to document.
820
820
  */
821
- function formatPropertyComment(documentation) {
821
+ export function formatPropertyComment(documentation) {
822
822
  const lines = [];
823
823
  if (documentation.description) {
824
824
  lines.push(...documentation.description.split('\n'));
@@ -119,7 +119,6 @@ export interface LoadServerTypesMetaResult extends OrganizeTypesResult<TypesMeta
119
119
  * This function handles:
120
120
  * - Loading TypeScript configuration
121
121
  * - Resolving library source files and variants
122
- * - Finding meta files (DataAttributes, CssVars)
123
122
  * - Processing types via worker thread
124
123
  * - Formatting component, hook, function, and raw types
125
124
  * - Collecting external types referenced in props/params