@mui/internal-docs-infra 0.12.1-canary.22 → 0.12.1-canary.24

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.
@@ -1,4 +1,5 @@
1
1
  import type { DescriptionReplacement } from "../pipeline/loadServerTypesMeta/format.mjs";
2
+ import type { InheritedExternalPropsConfig } from "../pipeline/loadServerTypesMeta/inheritedExternalProps.mjs";
2
3
  import type { OrderingConfig } from "../pipeline/loadServerTypesText/order.mjs";
3
4
  export interface DemoClientRequirement {
4
5
  /**
@@ -21,6 +22,8 @@ export interface DemoPageRequirement {
21
22
  export type ExtractedNextConfigOptions = {
22
23
  ordering?: OrderingConfig;
23
24
  descriptionReplacements?: DescriptionReplacement[];
25
+ /** Props to re-include when inherited from these externally declared types. */
26
+ inheritedExternalProps?: InheritedExternalPropsConfig;
24
27
  useVisibleDescription?: boolean;
25
28
  socketDir?: string;
26
29
  /** Page-index cache directory configured on the sitemap loader. */
@@ -47,6 +47,9 @@ function extractOptionsFromLoaderEntries(loaders) {
47
47
  if (!result.descriptionReplacements && loader.loader === TYPES_LOADER && loader.options?.descriptionReplacements) {
48
48
  result.descriptionReplacements = loader.options.descriptionReplacements;
49
49
  }
50
+ if (!result.inheritedExternalProps && loader.loader === TYPES_LOADER && loader.options?.inheritedExternalProps) {
51
+ result.inheritedExternalProps = loader.options.inheritedExternalProps;
52
+ }
50
53
  if (!result.socketDir && loader.loader === TYPES_LOADER && typeof loader.options?.socketDir === 'string') {
51
54
  result.socketDir = loader.options.socketDir;
52
55
  }
@@ -83,6 +86,7 @@ export function extractOptionsFromTurbopack(config) {
83
86
  const extracted = extractOptionsFromLoaderEntries(loaders);
84
87
  merged.ordering ??= extracted.ordering;
85
88
  merged.descriptionReplacements ??= extracted.descriptionReplacements;
89
+ merged.inheritedExternalProps ??= extracted.inheritedExternalProps;
86
90
  merged.useVisibleDescription ??= extracted.useVisibleDescription;
87
91
  merged.socketDir ??= extracted.socketDir;
88
92
  merged.cacheDir ??= extracted.cacheDir;
@@ -171,6 +175,7 @@ function extractOptionsFromWebpackResult(result) {
171
175
  const extracted = extractOptionsFromLoaderEntries(useEntries);
172
176
  merged.ordering ??= extracted.ordering;
173
177
  merged.descriptionReplacements ??= extracted.descriptionReplacements;
178
+ merged.inheritedExternalProps ??= extracted.inheritedExternalProps;
174
179
  merged.useVisibleDescription ??= extracted.useVisibleDescription;
175
180
  merged.socketDir ??= extracted.socketDir;
176
181
  merged.cacheDir ??= extracted.cacheDir;
@@ -329,6 +334,7 @@ export async function extractDocsInfraOptionsFromNextConfig(dir) {
329
334
  return {
330
335
  ordering: turbopack.ordering ?? webpack.ordering,
331
336
  descriptionReplacements: turbopack.descriptionReplacements ?? webpack.descriptionReplacements,
337
+ inheritedExternalProps: turbopack.inheritedExternalProps ?? webpack.inheritedExternalProps,
332
338
  useVisibleDescription: turbopack.useVisibleDescription ?? webpack.useVisibleDescription,
333
339
  socketDir: turbopack.socketDir ?? webpack.socketDir,
334
340
  cacheDir: turbopack.cacheDir ?? webpack.cacheDir,
@@ -88,6 +88,7 @@ const runValidate = {
88
88
  const {
89
89
  ordering,
90
90
  descriptionReplacements,
91
+ inheritedExternalProps,
91
92
  useVisibleDescription = false,
92
93
  socketDir: configSocketDir,
93
94
  cacheDir: configCacheDir,
@@ -300,6 +301,7 @@ const runValidate = {
300
301
  },
301
302
  ordering,
302
303
  descriptionReplacements,
304
+ inheritedExternalProps,
303
305
  socketDir,
304
306
  cacheDir
305
307
  }
@@ -74,6 +74,7 @@ if (parentPort) {
74
74
  updateParentIndex: excludeFromIndex ? undefined : task.syncTypesOptions.updateParentIndex,
75
75
  ordering: task.syncTypesOptions.ordering,
76
76
  descriptionReplacements: task.syncTypesOptions.descriptionReplacements,
77
+ inheritedExternalProps: task.syncTypesOptions.inheritedExternalProps,
77
78
  socketDir: task.syncTypesOptions.socketDir,
78
79
  cacheDir: task.syncTypesOptions.cacheDir
79
80
  });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mui/internal-docs-infra",
3
- "version": "0.12.1-canary.22",
3
+ "version": "0.12.1-canary.24",
4
4
  "author": "MUI Team",
5
5
  "description": "MUI Infra - internal documentation creation tools.",
6
6
  "license": "MIT",
@@ -30,15 +30,15 @@
30
30
  "homepage": "https://github.com/mui/mui-public/tree/master/packages/docs-infra",
31
31
  "dependencies": {
32
32
  "@babel/runtime": "^7.29.7",
33
- "@csstools/postcss-light-dark-function": "^3.0.1",
34
- "@csstools/postcss-relative-color-syntax": "^4.0.5",
35
- "@csstools/postcss-stepped-value-functions": "^5.0.3",
33
+ "@csstools/postcss-light-dark-function": "^3.0.2",
34
+ "@csstools/postcss-relative-color-syntax": "^4.0.7",
35
+ "@csstools/postcss-stepped-value-functions": "^5.0.4",
36
36
  "@orama/orama": "^3.1.18",
37
37
  "@orama/plugin-qps": "^3.1.18",
38
38
  "@orama/stemmers": "^3.1.18",
39
39
  "@orama/stopwords": "^3.1.18",
40
- "@wooorm/starry-night": "^3.9.0",
41
- "autoprefixer": "^10.5.0",
40
+ "@wooorm/starry-night": "^3.10.0",
41
+ "autoprefixer": "^10.5.4",
42
42
  "chalk": "^5.6.2",
43
43
  "clipboard-copy": "^4.0.1",
44
44
  "es-toolkit": "^1.49.0",
@@ -48,11 +48,11 @@
48
48
  "icss-utils": "^5.1.0",
49
49
  "import-meta-resolve": "^4.2.0",
50
50
  "jiti": "^2.7.0",
51
- "jsondiffpatch": "^0.7.3",
51
+ "jsondiffpatch": "^0.7.6",
52
52
  "kebab-case": "^2.0.2",
53
53
  "lz-string": "^1.5.0",
54
54
  "path-module": "^0.1.2",
55
- "postcss": "^8.5.15",
55
+ "postcss": "^8.5.22",
56
56
  "postcss-modules-extract-imports": "^3.1.0",
57
57
  "postcss-modules-local-by-default": "^4.2.0",
58
58
  "postcss-modules-scope": "^3.2.1",
@@ -64,7 +64,7 @@
64
64
  "remark-stringify": "^11.0.0",
65
65
  "remark-typography": "^0.7.3",
66
66
  "sucrase": "^3.35.1",
67
- "typescript-api-extractor": "1.0.0-beta.3",
67
+ "typescript-api-extractor": "1.0.0-beta.4",
68
68
  "uint8-to-base64": "^0.2.1",
69
69
  "unified": "^11.0.5",
70
70
  "unist-util-visit": "^5.1.0",
@@ -804,5 +804,5 @@
804
804
  "bin": {
805
805
  "docs-infra": "./cli/index.mjs"
806
806
  },
807
- "gitSha": "5a40e5ed0fc8d5ff358463279c75f69b33062d9f"
807
+ "gitSha": "da1a18c9574a4a0c716b373ec6169e6ab47abc62"
808
808
  }
@@ -2,6 +2,7 @@ import type { LoaderContext } from 'webpack';
2
2
  import type { FormatInlineTypeOptions, DescriptionReplacement } from "../loadServerTypesMeta/format.mjs";
3
3
  import type { SyncPageIndexBaseOptions } from "../transformMarkdownMetadata/types.mjs";
4
4
  import type { OrderingConfig } from "../loadServerTypesText/order.mjs";
5
+ import type { InheritedExternalPropsConfig } from "../loadServerTypesMeta/inheritedExternalProps.mjs";
5
6
  import type { TransformHtmlCodeBlockOptions } from "../transformHtmlCodeBlock/transformHtmlCodeBlock.mjs";
6
7
  export type LoaderOptions = {
7
8
  /** Performance tracking and logging options */
@@ -54,6 +55,11 @@ export type LoaderOptions = {
54
55
  * Each entry has a `pattern` (regex string) and `replacement` string.
55
56
  */
56
57
  descriptionReplacements?: DescriptionReplacement[];
58
+ /**
59
+ * Props to re-include when inherited from these externally declared types,
60
+ * keyed by the declaring type's name.
61
+ */
62
+ inheritedExternalProps?: InheritedExternalPropsConfig;
57
63
  /** Options for code blocks highlighted inside generated type metadata */
58
64
  codeBlockEmphasisOptions?: TransformHtmlCodeBlockOptions;
59
65
  /**
@@ -92,6 +92,7 @@ export async function loadPrecomputedTypes(source) {
92
92
  externalTypesPattern: options.externalTypesPattern,
93
93
  ordering: options.ordering,
94
94
  descriptionReplacements: options.descriptionReplacements,
95
+ inheritedExternalProps: options.inheritedExternalProps,
95
96
  codeBlockEmphasisOptions: options.codeBlockEmphasisOptions,
96
97
  cacheDir: options.cacheDir,
97
98
  sync: true,
@@ -0,0 +1,34 @@
1
+ import ts from 'typescript';
2
+ import type { ExportNode } from 'typescript-api-extractor';
3
+ /**
4
+ * Props to re-include for a single externally declared type. Either a list of
5
+ * prop names, or an object that additionally pins the package the type must be
6
+ * declared in — use the object form when another installed package could
7
+ * declare a type with the same name.
8
+ */
9
+ export type InheritedExternalPropsEntry = string[] | {
10
+ /** Name of the package the type must be declared in (e.g. `@base-ui/react`). */
11
+ from: string;
12
+ props: string[];
13
+ };
14
+ /**
15
+ * Props to re-include when they are inherited from externally declared types,
16
+ * keyed by the name of the type (interface or type alias) that declares them.
17
+ *
18
+ * @example { BaseUIComponentProps: ['className', 'render', 'style'] }
19
+ * @example { BaseUIComponentProps: { from: '@base-ui/react', props: ['className'] } }
20
+ */
21
+ export interface InheritedExternalPropsConfig {
22
+ [typeName: string]: InheritedExternalPropsEntry;
23
+ }
24
+ /**
25
+ * Re-adds configured props to parsed component exports when they are inherited
26
+ * from a type declared in an installed package. typescript-api-extractor skips
27
+ * properties that are only declared inside `node_modules` (which is what keeps
28
+ * native DOM attributes out of the docs), so shared props that a library
29
+ * inherits from another package's base props type silently disappear from its
30
+ * prop tables. The synthesized nodes are built from the checker's resolved
31
+ * types, so the output matches what the extractor produces when the same props
32
+ * are declared locally.
33
+ */
34
+ export declare function augmentComponentsWithInheritedProps(exports: ExportNode[], program: ts.Program, sourceFile: ts.SourceFile, config: InheritedExternalPropsConfig | undefined): void;
@@ -0,0 +1,286 @@
1
+ import ts from 'typescript';
2
+ import { CallSignature, Documentation, ExternalTypeNode, FunctionNode, IntrinsicNode, LiteralNode, Parameter, PropertyNode, TypeName, UnionNode } from 'typescript-api-extractor';
3
+ import { isComponentType, isObjectType } from "./typeGuards.mjs";
4
+
5
+ /**
6
+ * Props to re-include for a single externally declared type. Either a list of
7
+ * prop names, or an object that additionally pins the package the type must be
8
+ * declared in — use the object form when another installed package could
9
+ * declare a type with the same name.
10
+ */
11
+
12
+ /**
13
+ * Props to re-include when they are inherited from externally declared types,
14
+ * keyed by the name of the type (interface or type alias) that declares them.
15
+ *
16
+ * @example { BaseUIComponentProps: ['className', 'render', 'style'] }
17
+ * @example { BaseUIComponentProps: { from: '@base-ui/react', props: ['className'] } }
18
+ */
19
+
20
+ function resolveConfig(config) {
21
+ return new Map(Object.entries(config).map(([typeName, entry]) => [typeName, Array.isArray(entry) ? {
22
+ props: entry
23
+ } : entry]));
24
+ }
25
+
26
+ /** Bail out of type conversion once nesting gets deeper than any expected prop shape. */
27
+ const MAX_CONVERSION_DEPTH = 8;
28
+
29
+ /**
30
+ * Re-adds configured props to parsed component exports when they are inherited
31
+ * from a type declared in an installed package. typescript-api-extractor skips
32
+ * properties that are only declared inside `node_modules` (which is what keeps
33
+ * native DOM attributes out of the docs), so shared props that a library
34
+ * inherits from another package's base props type silently disappear from its
35
+ * prop tables. The synthesized nodes are built from the checker's resolved
36
+ * types, so the output matches what the extractor produces when the same props
37
+ * are declared locally.
38
+ */
39
+ export function augmentComponentsWithInheritedProps(exports, program, sourceFile, config) {
40
+ if (!config || Object.keys(config).length === 0) {
41
+ return;
42
+ }
43
+ const checker = program.getTypeChecker();
44
+ const moduleSymbol = checker.getSymbolAtLocation(sourceFile);
45
+ if (!moduleSymbol) {
46
+ return;
47
+ }
48
+ const symbolsByExportName = new Map();
49
+ collectExportSymbols(checker, moduleSymbol, [], symbolsByExportName, new Set());
50
+ const exportNames = exports.map(exportNode => exportNode.name);
51
+ const resolvedConfig = resolveConfig(config);
52
+ const propNames = new Set(Array.from(resolvedConfig.values()).flatMap(entry => entry.props));
53
+ for (const exportNode of exports) {
54
+ if (!isComponentType(exportNode.type)) {
55
+ continue;
56
+ }
57
+ const symbol = symbolsByExportName.get(exportNode.name);
58
+ if (!symbol) {
59
+ continue;
60
+ }
61
+ const propsType = getComponentPropsType(checker, symbol);
62
+ if (!propsType) {
63
+ continue;
64
+ }
65
+ const component = exportNode.type;
66
+ const existingNames = new Set(component.props.map(prop => prop.name));
67
+ const addedProps = [];
68
+ for (const propName of propNames) {
69
+ if (existingNames.has(propName)) {
70
+ continue;
71
+ }
72
+ const propSymbol = propsType.getProperty(propName);
73
+ if (!propSymbol || !isInheritedExternalProp(propSymbol, propName, resolvedConfig)) {
74
+ continue;
75
+ }
76
+ const propNode = createPropNode(propSymbol, checker, exportNames);
77
+ if (propNode) {
78
+ addedProps.push(propNode);
79
+ }
80
+ }
81
+ if (addedProps.length === 0) {
82
+ continue;
83
+ }
84
+ component.props.push(...addedProps);
85
+ augmentPropsTypeExports(exports, exportNode, addedProps);
86
+ }
87
+ }
88
+
89
+ /**
90
+ * Mirrors the synthesized props onto the exported props type of the component
91
+ * (both the namespaced `Component.Props` alias and the flat interface export),
92
+ * so their rendered type definitions match the component's prop table.
93
+ */
94
+ function augmentPropsTypeExports(exports, componentExport, addedProps) {
95
+ const componentName = componentExport.name;
96
+ const propsExportNames = new Set([`${componentName}.Props`, `${componentName.replaceAll('.', '')}Props`]);
97
+ if (componentExport.reexportedFrom) {
98
+ propsExportNames.add(`${componentExport.reexportedFrom}Props`);
99
+ }
100
+ for (const exportNode of exports) {
101
+ if (!propsExportNames.has(exportNode.name) || !isObjectType(exportNode.type)) {
102
+ continue;
103
+ }
104
+ const existingNames = new Set(exportNode.type.properties.map(prop => prop.name));
105
+ exportNode.type.properties.push(...addedProps.filter(prop => !existingNames.has(prop.name)));
106
+ }
107
+ }
108
+
109
+ /**
110
+ * Maps parsed export names to their TypeScript symbols, using the same naming
111
+ * scheme as typescript-api-extractor (namespace members become dotted names,
112
+ * e.g. `Menu.Root`).
113
+ */
114
+ function collectExportSymbols(checker, moduleSymbol, namespaces, result, visited) {
115
+ if (namespaces.length > 2 || visited.has(moduleSymbol)) {
116
+ return;
117
+ }
118
+ visited.add(moduleSymbol);
119
+ for (const exportSymbol of checker.getExportsOfModule(moduleSymbol)) {
120
+ const name = [...namespaces, exportSymbol.name].join('.');
121
+ result.set(name, exportSymbol);
122
+
123
+ // Recurse into `export * as Namespace from '...'` re-exports.
124
+ const declaration = exportSymbol.declarations?.[0];
125
+ if (declaration && ts.isNamespaceExport(declaration)) {
126
+ const aliased = checker.getAliasedSymbol(exportSymbol);
127
+ if (aliased) {
128
+ collectExportSymbols(checker, aliased, [...namespaces, exportSymbol.name], result, visited);
129
+ }
130
+ }
131
+ }
132
+ }
133
+
134
+ /**
135
+ * Resolves the props type of a component export from its first call signature.
136
+ */
137
+ function getComponentPropsType(checker, symbol) {
138
+ // eslint-disable-next-line no-bitwise
139
+ const isAlias = (symbol.flags & ts.SymbolFlags.Alias) !== 0;
140
+ const resolvedSymbol = isAlias ? checker.getAliasedSymbol(symbol) : symbol;
141
+ const componentType = checker.getTypeOfSymbol(resolvedSymbol);
142
+ for (const signature of componentType.getCallSignatures()) {
143
+ const propsParameter = signature.parameters[0];
144
+ const declaration = propsParameter?.valueDeclaration ?? propsParameter?.declarations?.[0];
145
+ if (declaration) {
146
+ return checker.getTypeOfSymbolAtLocation(propsParameter, declaration);
147
+ }
148
+ }
149
+ return undefined;
150
+ }
151
+
152
+ /**
153
+ * Checks that a prop is declared in an installed package on one of the
154
+ * configured types (and, when configured, in the expected package). Locally
155
+ * declared props are already handled by the extractor, and external props of
156
+ * any other origin (e.g. native DOM attributes) must stay excluded from the
157
+ * docs.
158
+ */
159
+ function isInheritedExternalProp(propSymbol, propName, config) {
160
+ const declarations = propSymbol.declarations ?? [];
161
+ if (declarations.length === 0) {
162
+ return false;
163
+ }
164
+ return declarations.every(declaration => {
165
+ const fileName = declaration.getSourceFile().fileName;
166
+ if (!fileName.includes('node_modules')) {
167
+ return false;
168
+ }
169
+ const owner = ts.findAncestor(declaration, node => ts.isTypeAliasDeclaration(node) || ts.isInterfaceDeclaration(node));
170
+ const entry = owner && config.get(owner.name.text);
171
+ if (!entry || !entry.props.includes(propName)) {
172
+ return false;
173
+ }
174
+ return !entry.from || getPackageName(fileName) === entry.from;
175
+ });
176
+ }
177
+
178
+ /**
179
+ * Extracts the package name from a file path inside `node_modules`,
180
+ * e.g. `.../node_modules/@base-ui/react/types.d.ts` → `@base-ui/react`.
181
+ * Uses the last `node_modules` segment to handle nested installations.
182
+ */
183
+ function getPackageName(fileName) {
184
+ const segments = fileName.split(/[\\/]/);
185
+ const index = segments.lastIndexOf('node_modules');
186
+ if (index === -1) {
187
+ return undefined;
188
+ }
189
+ const first = segments[index + 1];
190
+ if (!first) {
191
+ return undefined;
192
+ }
193
+ if (first.startsWith('@')) {
194
+ const second = segments[index + 2];
195
+ return second ? `${first}/${second}` : undefined;
196
+ }
197
+ return first;
198
+ }
199
+ function createPropNode(propSymbol, checker, exportNames) {
200
+ const declaration = propSymbol.valueDeclaration ?? propSymbol.declarations?.[0];
201
+ if (!declaration) {
202
+ return undefined;
203
+ }
204
+ const propType = checker.getTypeOfSymbolAtLocation(propSymbol, declaration);
205
+ const description = ts.displayPartsToString(propSymbol.getDocumentationComment(checker));
206
+ // eslint-disable-next-line no-bitwise
207
+ const optional = (propSymbol.flags & ts.SymbolFlags.Optional) !== 0;
208
+ return new PropertyNode(propSymbol.name, convertType(propType, checker, exportNames, 0), description ? new Documentation(description) : undefined, optional);
209
+ }
210
+
211
+ /**
212
+ * Converts a checker-resolved type into the extractor's type model, mirroring
213
+ * how typescript-api-extractor represents locally declared props: intrinsics
214
+ * and unions are expanded, anonymous function types are expanded into call
215
+ * signatures, and named types become references.
216
+ */
217
+ function convertType(type, checker, exportNames, depth) {
218
+ if (depth > MAX_CONVERSION_DEPTH) {
219
+ return new ExternalTypeNode(new TypeName(checker.typeToString(type)));
220
+ }
221
+ const intrinsic = getIntrinsicName(type);
222
+ if (intrinsic) {
223
+ return new IntrinsicNode(intrinsic);
224
+ }
225
+
226
+ // eslint-disable-next-line no-bitwise
227
+ if ((type.flags & ts.TypeFlags.Literal) !== 0) {
228
+ return new LiteralNode(checker.typeToString(type));
229
+ }
230
+ if (type.isUnion()) {
231
+ return new UnionNode(undefined, type.types.map(member => convertType(member, checker, exportNames, depth + 1)));
232
+ }
233
+ const typeName = getNamedTypeReference(type, exportNames);
234
+ const callSignatures = type.getCallSignatures();
235
+ if (callSignatures.length > 0) {
236
+ return new FunctionNode(typeName, callSignatures.map(signature => convertSignature(signature, checker, exportNames, depth)));
237
+ }
238
+ if (typeName) {
239
+ return new ExternalTypeNode(typeName);
240
+ }
241
+ return new ExternalTypeNode(new TypeName(checker.typeToString(type)));
242
+ }
243
+ function convertSignature(signature, checker, exportNames, depth) {
244
+ const parameters = signature.parameters.map(parameter => {
245
+ const declaration = parameter.valueDeclaration ?? parameter.declarations?.[0];
246
+ const parameterType = declaration ? checker.getTypeOfSymbolAtLocation(parameter, declaration) : checker.getTypeOfSymbol(parameter);
247
+ const optional = Boolean(declaration && ts.isParameter(declaration) && (declaration.questionToken || declaration.initializer));
248
+ return new Parameter(convertType(parameterType, checker, exportNames, depth + 1), parameter.name, undefined, optional, undefined);
249
+ });
250
+ return new CallSignature(parameters, convertType(signature.getReturnType(), checker, exportNames, depth + 1));
251
+ }
252
+ const INTRINSIC_FLAGS = [[ts.TypeFlags.String, 'string'], [ts.TypeFlags.Number, 'number'], [ts.TypeFlags.Boolean, 'boolean'], [ts.TypeFlags.BigInt, 'bigint'], [ts.TypeFlags.Undefined, 'undefined'], [ts.TypeFlags.Null, 'null'], [ts.TypeFlags.Void, 'void'], [ts.TypeFlags.Any, 'any'], [ts.TypeFlags.Unknown, 'unknown'], [ts.TypeFlags.Never, 'never']];
253
+ function getIntrinsicName(type) {
254
+ // `boolean` is represented as a union of literals, so check it before unions.
255
+ // eslint-disable-next-line no-bitwise
256
+ const match = INTRINSIC_FLAGS.find(([flag]) => (type.flags & flag) !== 0);
257
+ return match?.[1];
258
+ }
259
+
260
+ /**
261
+ * Builds a reference for a named type, preferring the module's dotted export
262
+ * name (e.g. `Menu.Root.State`) when one exists for the flat name.
263
+ */
264
+ function getNamedTypeReference(type, exportNames) {
265
+ const symbol = type.aliasSymbol ?? type.getSymbol();
266
+ const name = symbol?.name;
267
+ if (!symbol || !name || name.startsWith('__')) {
268
+ return undefined;
269
+ }
270
+
271
+ // Collect enclosing namespaces (e.g. `React` for `React.CSSProperties`),
272
+ // stopping at module/source-file symbols whose names are quoted paths.
273
+ const namespaces = [];
274
+ let parent = symbol.parent;
275
+ while (parent && /^[A-Za-z_$][\w$]*$/.test(parent.name)) {
276
+ namespaces.unshift(parent.name);
277
+ parent = parent.parent;
278
+ }
279
+ const flatName = [...namespaces, name].join('');
280
+ const dottedName = exportNames.find(exportName => exportName.includes('.') && exportName.replaceAll('.', '') === flatName);
281
+ if (dottedName) {
282
+ const parts = dottedName.split('.');
283
+ return new TypeName(parts[parts.length - 1], parts.slice(0, -1));
284
+ }
285
+ return new TypeName(name, namespaces.length > 0 ? namespaces : undefined);
286
+ }
@@ -4,6 +4,7 @@ import type { HookTypeMeta as HookType } from "./formatHook.mjs";
4
4
  import type { FunctionTypeMeta as FunctionType } from "./formatFunction.mjs";
5
5
  import type { RawTypeMeta as RawType, ReExportInfo } from "./formatRaw.mjs";
6
6
  import type { FormattedProperty, FormattedEnumMember, FormattedParameter, FormatInlineTypeOptions, DescriptionReplacement } from "./format.mjs";
7
+ import type { InheritedExternalPropsConfig } from "./inheritedExternalProps.mjs";
7
8
  import type { OrderingConfig } from "../loadServerTypesText/order.mjs";
8
9
  import type { OrganizeTypesResult } from "../loadServerTypesText/organizeTypesByExport.mjs";
9
10
  export type ClassTypeMeta = ClassType;
@@ -93,6 +94,14 @@ export interface LoadServerTypesMetaOptions {
93
94
  * Each entry has a `pattern` (regex string) and `replacement` string.
94
95
  */
95
96
  descriptionReplacements?: DescriptionReplacement[];
97
+ /**
98
+ * Props to re-include when inherited from these externally declared types,
99
+ * keyed by the declaring type's name. The parser normally drops props that are
100
+ * only declared inside `node_modules`; this re-adds the configured ones.
101
+ *
102
+ * @example { BaseUIComponentProps: ['className', 'render', 'style'] }
103
+ */
104
+ inheritedExternalProps?: InheritedExternalPropsConfig;
96
105
  }
97
106
  export interface LoadServerTypesMetaResult extends OrganizeTypesResult<TypesMeta> {
98
107
  /** All dependencies that should be watched for changes */
@@ -166,7 +166,8 @@ export async function loadServerTypesMeta(options) {
166
166
  resolvedVariantMap: Array.from(resolvedVariantMap.entries()),
167
167
  dependencies: config.dependencies,
168
168
  rootContextDir,
169
- relativePath
169
+ relativePath,
170
+ inheritedExternalProps: options.inheritedExternalProps
170
171
  });
171
172
  if (!workerResult.success) {
172
173
  throw new Error(workerResult.error || 'Worker failed to process types');
@@ -1,5 +1,6 @@
1
1
  import type { CompilerOptions } from 'typescript';
2
2
  import type { ExportNode } from 'typescript-api-extractor';
3
+ import type { InheritedExternalPropsConfig } from "./inheritedExternalProps.mjs";
3
4
  import type { PerformanceLog } from "./performanceTracking.mjs";
4
5
  export interface VariantResult {
5
6
  exports: ExportNode[];
@@ -22,6 +23,11 @@ export interface WorkerRequest {
22
23
  /** Root context directory path (must end with /) */
23
24
  rootContextDir: string;
24
25
  relativePath: string;
26
+ /**
27
+ * Props to re-include when inherited from these externally declared types,
28
+ * keyed by the declaring type's name.
29
+ */
30
+ inheritedExternalProps?: InheritedExternalPropsConfig;
25
31
  }
26
32
  export interface WorkerResponse {
27
33
  requestId?: number;
@@ -3,6 +3,7 @@ import { fileURLToPath } from 'url';
3
3
  import { parseFromProgram } from 'typescript-api-extractor';
4
4
  import ts from 'typescript';
5
5
  import { createOptimizedProgram } from "./createOptimizedProgram.mjs";
6
+ import { augmentComponentsWithInheritedProps } from "./inheritedExternalProps.mjs";
6
7
  import { extractJSDocText, isJSDocNodeArray } from "./extractJSDocText.mjs";
7
8
  import { PerformanceTracker } from "./performanceTracking.mjs";
8
9
  import { nameMark } from "../loadPrecomputedCodeHighlighter/performanceLogger.mjs";
@@ -299,6 +300,10 @@ export async function processTypes(request) {
299
300
  exports
300
301
  } = parseFromProgram(entrypoint, program, parserOptions);
301
302
 
303
+ // Re-add configured props that the parser dropped because they are
304
+ // inherited from an externally declared type in node_modules
305
+ augmentComponentsWithInheritedProps(exports, program, sourceFile, request.inheritedExternalProps);
306
+
302
307
  // Extract namespaces from the exports (e.g., "Menu" from "Menu.Root")
303
308
  const namespaces = extractNamespaces(exports);
304
309
 
@@ -1,3 +1,4 @@
1
+ import type { InheritedExternalPropsConfig } from "../loadServerTypesMeta/inheritedExternalProps.mjs";
1
2
  import type { FormatInlineTypeOptions, DescriptionReplacement } from "../loadServerTypesMeta/format.mjs";
2
3
  import type { OrderingConfig } from "../loadServerTypesText/order.mjs";
3
4
  import type { SyncPageIndexBaseOptions } from "../transformMarkdownMetadata/types.mjs";
@@ -62,6 +63,11 @@ export interface SyncTypesOptions {
62
63
  * Each entry has a `pattern` (regex string) and `replacement` string.
63
64
  */
64
65
  descriptionReplacements?: DescriptionReplacement[];
66
+ /**
67
+ * Props to re-include when inherited from these externally declared types,
68
+ * keyed by the declaring type's name.
69
+ */
70
+ inheritedExternalProps?: InheritedExternalPropsConfig;
65
71
  /**
66
72
  * Directory for the sha256-validated JSON cache of the types pipeline. When set, after
67
73
  * writing types.md syncTypes pre-populates `{cacheDir}/types-text/{route}.json` with the
@@ -196,7 +196,8 @@ export async function syncTypes(options) {
196
196
  socketDir: options.socketDir,
197
197
  externalTypesPattern: options.externalTypesPattern,
198
198
  ordering: options.ordering,
199
- descriptionReplacements: options.descriptionReplacements
199
+ descriptionReplacements: options.descriptionReplacements,
200
+ inheritedExternalProps: options.inheritedExternalProps
200
201
  });
201
202
  const {
202
203
  allDependencies,
@@ -68,8 +68,8 @@ export declare function useDemo<T extends {} = {}>(contentProps: ContentProps<T>
68
68
  expanded: boolean;
69
69
  expand: () => void;
70
70
  setExpanded: (expanded: boolean) => void;
71
- copy: (event: React.MouseEvent<Element, MouseEvent>) => Promise<void>;
72
- copyMarkdown: (event: React.MouseEvent<Element, MouseEvent>) => Promise<void>;
71
+ copy: (event: React.MouseEvent<Element>) => Promise<void>;
72
+ copyMarkdown: (event: React.MouseEvent<Element>) => Promise<void>;
73
73
  availableTransforms: string[];
74
74
  selectedTransform: string | null | undefined;
75
75
  selectTransform: (transformName: string | null) => void;
@@ -2,6 +2,7 @@ import type { NextConfig } from 'next';
2
2
  import type { RuleSetRule } from 'webpack';
3
3
  import type { OrderingConfig } from "../pipeline/loadServerTypesText/order.mjs";
4
4
  import type { DescriptionReplacement } from "../pipeline/loadServerTypesMeta/format.mjs";
5
+ import type { InheritedExternalPropsConfig } from "../pipeline/loadServerTypesMeta/inheritedExternalProps.mjs";
5
6
  import type { EnhanceCodeEmphasisOptions } from "../pipeline/parseSource/calculateFrameRanges.mjs";
6
7
  import type { TransformHtmlCodeBlockOptions } from "../pipeline/transformHtmlCodeBlock/transformHtmlCodeBlock.mjs";
7
8
  export interface WebpackOptions {
@@ -165,6 +166,27 @@ export interface WithDocsInfraOptions {
165
166
  * ```
166
167
  */
167
168
  descriptionReplacements?: DescriptionReplacement[];
169
+ /**
170
+ * Props to re-include in component docs when they are inherited from these
171
+ * externally declared types, keyed by the declaring type's name. Type
172
+ * extraction normally drops props that are only declared inside
173
+ * `node_modules` (which keeps native DOM attributes out of the docs); this
174
+ * option re-adds the configured props when a component's props type extends
175
+ * one of the listed types from an installed package.
176
+ *
177
+ * Entries can also pin the package the type must come from, to disambiguate
178
+ * same-named types from different packages.
179
+ *
180
+ * @example
181
+ * ```js
182
+ * { BaseUIComponentProps: ['className', 'render', 'style'] }
183
+ * ```
184
+ * @example
185
+ * ```js
186
+ * { BaseUIComponentProps: { from: '@base-ui/react', props: ['className', 'render', 'style'] } }
187
+ * ```
188
+ */
189
+ inheritedExternalProps?: InheritedExternalPropsConfig;
168
190
  /**
169
191
  * Directory rooting docs-infra's build caches and coordination state — relocate it (or point it
170
192
  * at a persistent cache) and everything under it moves together: the sha256-validated JSON caches
@@ -93,6 +93,7 @@ export function withDocsInfra(options = {}) {
93
93
  // Only include ordering in loader options if explicitly provided
94
94
  const ordering = options.ordering;
95
95
  const descriptionReplacements = options.descriptionReplacements;
96
+ const inheritedExternalProps = options.inheritedExternalProps;
96
97
  const demoEmphasisOptions = options.demoEmphasisOptions;
97
98
  const codeBlockEmphasisOptions = options.codeBlockEmphasisOptions;
98
99
 
@@ -183,6 +184,9 @@ export function withDocsInfra(options = {}) {
183
184
  } : {}),
184
185
  ...(descriptionReplacements ? {
185
186
  descriptionReplacements: descriptionReplacements
187
+ } : {}),
188
+ ...(inheritedExternalProps ? {
189
+ inheritedExternalProps: inheritedExternalProps
186
190
  } : {})
187
191
  }
188
192
  }]
@@ -313,6 +317,9 @@ export function withDocsInfra(options = {}) {
313
317
  } : {}),
314
318
  ...(descriptionReplacements ? {
315
319
  descriptionReplacements
320
+ } : {}),
321
+ ...(inheritedExternalProps ? {
322
+ inheritedExternalProps
316
323
  } : {})
317
324
  }
318
325
  }]