@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.
- package/CodeHighlighter/buildStringFallback.mjs +1 -1
- package/package.json +2 -2
- package/pipeline/loadServerTypesMeta/constantGroups.d.mts +48 -0
- package/pipeline/loadServerTypesMeta/constantGroups.mjs +202 -0
- package/pipeline/loadServerTypesMeta/formatComponent.d.mts +4 -5
- package/pipeline/loadServerTypesMeta/formatComponent.mjs +8 -90
- package/pipeline/loadServerTypesMeta/formatRaw.d.mts +8 -3
- package/pipeline/loadServerTypesMeta/formatRaw.mjs +12 -33
- package/pipeline/loadServerTypesMeta/formatType.d.mts +6 -1
- package/pipeline/loadServerTypesMeta/formatType.mjs +1 -1
- package/pipeline/loadServerTypesMeta/loadServerTypesMeta.d.mts +0 -1
- package/pipeline/loadServerTypesMeta/loadServerTypesMeta.mjs +15 -136
- package/pipeline/loadServerTypesMeta/parseTestSources.d.mts +11 -1
- package/pipeline/loadServerTypesMeta/parseTestSources.mjs +19 -7
- package/pipeline/loadServerTypesMeta/processTypes.d.mts +2 -8
- package/pipeline/loadServerTypesMeta/processTypes.mjs +14 -49
- package/pipeline/parseSource/createPlainTextRoot.d.mts +18 -0
- package/pipeline/parseSource/createPlainTextRoot.mjs +38 -0
- package/pipeline/parseSource/parseSource.d.mts +2 -18
- package/pipeline/parseSource/parseSource.mjs +2 -33
- package/pipeline/syncTypes/generateTypesMarkdown.mjs +10 -14
- package/pipeline/loadServerTypesMeta/findMetaFiles.d.mts +0 -9
- package/pipeline/loadServerTypesMeta/findMetaFiles.mjs +0 -45
- package/pipeline/loadServerTypesMeta/transformConstantGroup.d.mts +0 -14
- package/pipeline/loadServerTypesMeta/transformConstantGroup.mjs +0 -80
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { buildRootFallback } from "./fallbackFormat.mjs";
|
|
2
|
-
import { parsePlainText } from "../pipeline/parseSource/
|
|
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.
|
|
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": "
|
|
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
|
-
/**
|
|
24
|
-
|
|
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
|
-
},
|
|
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,
|
|
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
|
-
//
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
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
|
|
135
|
-
cssVariables: 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'
|
|
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
|
|
50
|
+
* For constant groups holding a component's data attributes, that component's name.
|
|
50
51
|
*/
|
|
51
52
|
dataAttributesOf?: string;
|
|
52
53
|
/**
|
|
53
|
-
* For
|
|
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
|
-
|
|
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
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
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
|