@contentful/experience-design-system-cli 2.12.4-dev-build-ff57340.0 → 2.13.1-dev-build-847dead.0

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 (55) hide show
  1. package/dist/package.json +3 -6
  2. package/dist/src/analyze/command.js +2 -22
  3. package/dist/src/analyze/select/command.js +1 -1
  4. package/dist/src/analyze/select/tui/components/FieldEditor.d.ts +1 -24
  5. package/dist/src/analyze/select/tui/components/FieldEditor.js +10 -137
  6. package/dist/src/analyze/select-agent/command.js +1 -1
  7. package/dist/src/apply/command.d.ts +1 -34
  8. package/dist/src/apply/command.js +0 -81
  9. package/dist/src/import/tui/WizardApp.js +10 -59
  10. package/dist/src/import/tui/steps/GenerateReviewStep.d.ts +1 -1
  11. package/dist/src/import/tui/steps/GenerateReviewStep.js +23 -213
  12. package/dist/src/import/tui/steps/WizardPreviewStep.d.ts +0 -10
  13. package/dist/src/import/tui/steps/WizardPreviewStep.js +51 -95
  14. package/dist/src/import/tui/steps/preview-diff.js +0 -48
  15. package/dist/src/session/db.d.ts +0 -9
  16. package/dist/src/session/db.js +0 -46
  17. package/dist/src/types.d.ts +2 -76
  18. package/dist/src/types.js +1 -4
  19. package/package.json +3 -6
  20. package/dist/src/analyze/cycle-detection.d.ts +0 -93
  21. package/dist/src/analyze/cycle-detection.js +0 -326
  22. package/dist/src/analyze/extract/astro.d.ts +0 -5
  23. package/dist/src/analyze/extract/astro.js +0 -289
  24. package/dist/src/analyze/extract/non-authorable-filter.d.ts +0 -16
  25. package/dist/src/analyze/extract/non-authorable-filter.js +0 -60
  26. package/dist/src/analyze/extract/pipeline.d.ts +0 -6
  27. package/dist/src/analyze/extract/pipeline.js +0 -314
  28. package/dist/src/analyze/extract/react.d.ts +0 -2
  29. package/dist/src/analyze/extract/react.js +0 -2041
  30. package/dist/src/analyze/extract/scoring.d.ts +0 -12
  31. package/dist/src/analyze/extract/scoring.js +0 -108
  32. package/dist/src/analyze/extract/slot-allowed-components.d.ts +0 -8
  33. package/dist/src/analyze/extract/slot-allowed-components.js +0 -40
  34. package/dist/src/analyze/extract/slot-detection.d.ts +0 -35
  35. package/dist/src/analyze/extract/slot-detection.js +0 -101
  36. package/dist/src/analyze/extract/source-inspection.d.ts +0 -13
  37. package/dist/src/analyze/extract/source-inspection.js +0 -189
  38. package/dist/src/analyze/extract/stencil.d.ts +0 -2
  39. package/dist/src/analyze/extract/stencil.js +0 -296
  40. package/dist/src/analyze/extract/svelte.d.ts +0 -5
  41. package/dist/src/analyze/extract/svelte.js +0 -1626
  42. package/dist/src/analyze/extract/tsx-shared.d.ts +0 -8
  43. package/dist/src/analyze/extract/tsx-shared.js +0 -263
  44. package/dist/src/analyze/extract/validate.d.ts +0 -16
  45. package/dist/src/analyze/extract/validate.js +0 -94
  46. package/dist/src/analyze/extract/vue-tsx.d.ts +0 -2
  47. package/dist/src/analyze/extract/vue-tsx.js +0 -498
  48. package/dist/src/analyze/extract/vue.d.ts +0 -5
  49. package/dist/src/analyze/extract/vue.js +0 -653
  50. package/dist/src/analyze/extract/web-components.d.ts +0 -2
  51. package/dist/src/analyze/extract/web-components.js +0 -876
  52. package/dist/src/analyze/pre-classify.d.ts +0 -17
  53. package/dist/src/analyze/pre-classify.js +0 -211
  54. package/dist/src/apply/error-parser.d.ts +0 -43
  55. package/dist/src/apply/error-parser.js +0 -163
@@ -1,2041 +0,0 @@
1
- import { Project, Node, SyntaxKind, } from 'ts-morph';
2
- import { extractAllowedValues, getNodeDefinitions, getTypeReferenceName, getTypeTargetDeclarations, } from './tsx-shared.js';
3
- import { shouldBeSlot } from './slot-detection.js';
4
- import { extractAllowedComponentsFromTypeText, extractAllowedComponentsFromJsdoc } from './slot-allowed-components.js';
5
- /**
6
- * Matches ReactElement<XProps ...> (with optional `React.` prefix) anywhere in
7
- * a type text. TS often resolves this to ReactElement<XProps, string | JSXElementConstructor<any>>,
8
- * so we allow anything after the first type argument.
9
- */
10
- const REACT_ELEMENT_GENERIC_TEST = /(?:React\.)?ReactElement\s*<\s*[A-Za-z_$][\w$.]*/;
11
- /**
12
- * True when a type text references ReactElement<XProps> — either directly, as a
13
- * union with null/undefined, or as a union of ReactElement<...> types.
14
- */
15
- function isReactElementGenericSlotType(typeText) {
16
- return REACT_ELEMENT_GENERIC_TEST.test(typeText);
17
- }
18
- const PROP_WRAPPER_TYPE_NAMES = new Set(['ExpandProps']);
19
- const CHILD_WRAPPER_TYPE_NAMES = new Set(['PropsWithChildren']);
20
- const TRANSPARENT_POLYMORPHIC_TYPE_NAMES = new Set(['PolymorphicProps', 'PropsWithAs', 'PropsWithHTMLElement']);
21
- const EXPANDABLE_DOM_ATTRIBUTE_TYPE_NAMES = new Set([
22
- 'HTMLProps',
23
- 'HTMLAttributes',
24
- 'ImgHTMLAttributes',
25
- 'LiHTMLAttributes',
26
- 'AnchorHTMLAttributes',
27
- 'ButtonHTMLAttributes',
28
- 'InputHTMLAttributes',
29
- 'FieldsetHTMLAttributes',
30
- 'LabelHTMLAttributes',
31
- 'SelectHTMLAttributes',
32
- 'SVGAttributes',
33
- 'SVGProps',
34
- 'TextareaHTMLAttributes',
35
- 'TdHTMLAttributes',
36
- ]);
37
- const DOM_ATTRIBUTE_PROP_SURFACES = {
38
- HTMLProps: [],
39
- HTMLAttributes: [
40
- { name: 'className', type: 'string', required: false },
41
- { name: 'hidden', type: 'boolean', required: false },
42
- { name: 'id', type: 'string', required: false },
43
- {
44
- name: 'onClick',
45
- type: 'MouseEventHandler<HTMLElement>',
46
- required: false,
47
- },
48
- { name: 'style', type: 'CSSProperties', required: false },
49
- { name: 'tabIndex', type: 'number', required: false },
50
- { name: 'title', type: 'string', required: false },
51
- ],
52
- ImgHTMLAttributes: [
53
- { name: 'alt', type: 'string', required: false },
54
- { name: 'crossOrigin', type: 'string', required: false },
55
- { name: 'height', type: 'number | string', required: false },
56
- {
57
- name: 'loading',
58
- type: 'string',
59
- required: false,
60
- allowedValues: ['eager', 'lazy'],
61
- },
62
- { name: 'sizes', type: 'string', required: false },
63
- { name: 'src', type: 'string', required: false },
64
- { name: 'srcSet', type: 'string', required: false },
65
- { name: 'width', type: 'number | string', required: false },
66
- ],
67
- LiHTMLAttributes: [],
68
- AnchorHTMLAttributes: [
69
- { name: 'download', type: 'boolean | string', required: false },
70
- { name: 'href', type: 'string', required: false },
71
- { name: 'hrefLang', type: 'string', required: false },
72
- { name: 'referrerPolicy', type: 'string', required: false },
73
- { name: 'rel', type: 'string', required: false },
74
- {
75
- name: 'target',
76
- type: 'string',
77
- required: false,
78
- allowedValues: ['_blank', '_parent', '_self', '_top'],
79
- },
80
- ],
81
- ButtonHTMLAttributes: [
82
- { name: 'autoFocus', type: 'boolean', required: false },
83
- { name: 'disabled', type: 'boolean', required: false },
84
- { name: 'form', type: 'string', required: false },
85
- { name: 'formAction', type: 'string', required: false },
86
- { name: 'formMethod', type: 'string', required: false },
87
- { name: 'formNoValidate', type: 'boolean', required: false },
88
- { name: 'formTarget', type: 'string', required: false },
89
- { name: 'name', type: 'string', required: false },
90
- {
91
- name: 'type',
92
- type: 'string',
93
- required: false,
94
- allowedValues: ['button', 'reset', 'submit'],
95
- },
96
- {
97
- name: 'value',
98
- type: 'string | number | readonly string[]',
99
- required: false,
100
- },
101
- ],
102
- InputHTMLAttributes: [
103
- { name: 'autoComplete', type: 'string', required: false },
104
- { name: 'checked', type: 'boolean', required: false },
105
- { name: 'disabled', type: 'boolean', required: false },
106
- { name: 'max', type: 'number | string', required: false },
107
- { name: 'maxLength', type: 'number', required: false },
108
- { name: 'min', type: 'number | string', required: false },
109
- { name: 'minLength', type: 'number', required: false },
110
- { name: 'name', type: 'string', required: false },
111
- {
112
- name: 'onChange',
113
- type: 'ChangeEventHandler<HTMLInputElement>',
114
- required: false,
115
- },
116
- { name: 'placeholder', type: 'string', required: false },
117
- { name: 'readOnly', type: 'boolean', required: false },
118
- { name: 'required', type: 'boolean', required: false },
119
- { name: 'type', type: 'string', required: false },
120
- {
121
- name: 'value',
122
- type: 'string | number | readonly string[]',
123
- required: false,
124
- },
125
- ],
126
- FieldsetHTMLAttributes: [
127
- { name: 'disabled', type: 'boolean', required: false },
128
- { name: 'form', type: 'string', required: false },
129
- { name: 'name', type: 'string', required: false },
130
- ],
131
- LabelHTMLAttributes: [
132
- { name: 'form', type: 'string', required: false },
133
- { name: 'htmlFor', type: 'string', required: false },
134
- ],
135
- SelectHTMLAttributes: [
136
- { name: 'autoComplete', type: 'string', required: false },
137
- { name: 'disabled', type: 'boolean', required: false },
138
- { name: 'form', type: 'string', required: false },
139
- { name: 'multiple', type: 'boolean', required: false },
140
- { name: 'name', type: 'string', required: false },
141
- {
142
- name: 'onChange',
143
- type: 'ChangeEventHandler<HTMLSelectElement>',
144
- required: false,
145
- },
146
- { name: 'required', type: 'boolean', required: false },
147
- { name: 'size', type: 'number', required: false },
148
- {
149
- name: 'value',
150
- type: 'string | number | readonly string[]',
151
- required: false,
152
- },
153
- ],
154
- SVGAttributes: [
155
- {
156
- name: 'focusable',
157
- type: 'boolean | "auto"',
158
- required: false,
159
- allowedValues: ['auto'],
160
- },
161
- { name: 'height', type: 'number | string', required: false },
162
- { name: 'viewBox', type: 'string', required: false },
163
- { name: 'width', type: 'number | string', required: false },
164
- ],
165
- SVGProps: [],
166
- TextareaHTMLAttributes: [
167
- { name: 'autoComplete', type: 'string', required: false },
168
- { name: 'cols', type: 'number', required: false },
169
- { name: 'disabled', type: 'boolean', required: false },
170
- { name: 'maxLength', type: 'number', required: false },
171
- { name: 'minLength', type: 'number', required: false },
172
- { name: 'name', type: 'string', required: false },
173
- {
174
- name: 'onChange',
175
- type: 'ChangeEventHandler<HTMLTextAreaElement>',
176
- required: false,
177
- },
178
- { name: 'placeholder', type: 'string', required: false },
179
- { name: 'readOnly', type: 'boolean', required: false },
180
- { name: 'required', type: 'boolean', required: false },
181
- { name: 'rows', type: 'number', required: false },
182
- {
183
- name: 'value',
184
- type: 'string | number | readonly string[]',
185
- required: false,
186
- },
187
- { name: 'wrap', type: 'string', required: false },
188
- ],
189
- TdHTMLAttributes: [
190
- {
191
- name: 'align',
192
- type: 'string',
193
- required: false,
194
- allowedValues: ['center', 'char', 'justify', 'left', 'right'],
195
- },
196
- { name: 'colSpan', type: 'number', required: false },
197
- { name: 'headers', type: 'string', required: false },
198
- { name: 'rowSpan', type: 'number', required: false },
199
- { name: 'scope', type: 'string', required: false },
200
- ],
201
- };
202
- const DOM_ATTRIBUTE_WRAPPER_PARENTS = {
203
- HTMLProps: ['HTMLAttributes'],
204
- AnchorHTMLAttributes: ['HTMLAttributes'],
205
- ButtonHTMLAttributes: ['HTMLAttributes'],
206
- ImgHTMLAttributes: ['HTMLAttributes'],
207
- InputHTMLAttributes: ['HTMLAttributes'],
208
- FieldsetHTMLAttributes: ['HTMLAttributes'],
209
- LabelHTMLAttributes: ['HTMLAttributes'],
210
- LiHTMLAttributes: ['HTMLAttributes'],
211
- SelectHTMLAttributes: ['HTMLAttributes'],
212
- SVGAttributes: ['HTMLAttributes'],
213
- SVGProps: ['SVGAttributes'],
214
- TdHTMLAttributes: ['HTMLAttributes'],
215
- TextareaHTMLAttributes: ['HTMLAttributes'],
216
- };
217
- const DOM_ATTRIBUTE_WRAPPERS_WITH_SYNTHETIC_CHILDREN = new Set([
218
- 'LabelHTMLAttributes',
219
- ]);
220
- const JSX_PRIMITIVE_DOM_ATTRIBUTE_SURFACES = {
221
- 'Primitive.button': 'ButtonHTMLAttributes',
222
- 'Primitive.input': 'InputHTMLAttributes',
223
- 'Primitive.label': 'LabelHTMLAttributes',
224
- };
225
- function getBoundedImportedJsxDomSurface(tagNameNode) {
226
- if (!Node.isIdentifier(tagNameNode))
227
- return undefined;
228
- const localName = tagNameNode.getText();
229
- if (!/label/i.test(localName))
230
- return undefined;
231
- const declarations = tagNameNode.getSymbol()?.getDeclarations() ?? [];
232
- for (const declaration of declarations) {
233
- if (!Node.isImportSpecifier(declaration))
234
- continue;
235
- const importDeclaration = declaration.getImportDeclaration();
236
- const moduleSpecifier = importDeclaration.getModuleSpecifierValue();
237
- const importedName = declaration.getNameNode().getText();
238
- const aliasName = declaration.getAliasNode()?.getText();
239
- if (/label/i.test(moduleSpecifier) ||
240
- /label/i.test(importedName) ||
241
- (aliasName !== undefined && /label/i.test(aliasName))) {
242
- return 'LabelHTMLAttributes';
243
- }
244
- }
245
- return undefined;
246
- }
247
- function isStencilFile(sourceFile) {
248
- return sourceFile.getImportDeclarations().some((imp) => imp.getModuleSpecifierValue() === '@stencil/core');
249
- }
250
- function sourceFileUsesCreateContext(sourceFile) {
251
- // Match React.createContext(...) and createContext(...) call expressions
252
- return sourceFile.getDescendantsOfKind(SyntaxKind.CallExpression).some((call) => {
253
- const expr = call.getExpression();
254
- const text = expr.getText();
255
- return text === 'createContext' || text === 'React.createContext';
256
- });
257
- }
258
- function isNextJsComponent(filePath, exportedNames) {
259
- const normalized = filePath.replace(/\\/g, '/');
260
- const isAppRouterFile = /\/app\/.*\/(page|layout)\.[jt]sx?$/.test(normalized);
261
- const hasNextExports = exportedNames.some((name) => name === 'generateMetadata' || name === 'generateStaticParams');
262
- return isAppRouterFile || hasNextExports;
263
- }
264
- function isRepoLocalTransparentPolymorphicWrapperDeclaration(declaration, typeName) {
265
- if (!Node.isTypeAliasDeclaration(declaration))
266
- return false;
267
- if (declaration.getSourceFile().getFilePath().includes('/node_modules/'))
268
- return false;
269
- if (!TRANSPARENT_POLYMORPHIC_TYPE_NAMES.has(typeName))
270
- return false;
271
- const typeParameters = declaration.getTypeParameters();
272
- const typeNode = declaration.getTypeNode();
273
- if (!typeNode)
274
- return false;
275
- if (typeName === 'PropsWithAs') {
276
- if (typeParameters.length !== 2 || !Node.isIntersectionTypeNode(typeNode))
277
- return false;
278
- const [propsTypeNode, wrapperTypeNode] = typeNode.getTypeNodes();
279
- if (!propsTypeNode || !wrapperTypeNode)
280
- return false;
281
- if (propsTypeNode.getText() !== typeParameters[0].getName())
282
- return false;
283
- if (!Node.isTypeLiteral(wrapperTypeNode))
284
- return false;
285
- if (wrapperTypeNode.getMembers().length !== 1)
286
- return false;
287
- const asProperty = wrapperTypeNode.getProperty('as');
288
- if (!asProperty || !asProperty.hasQuestionToken())
289
- return false;
290
- return asProperty.getTypeNode()?.getText() === typeParameters[1].getName();
291
- }
292
- if (typeName === 'PropsWithHTMLElement') {
293
- if (typeParameters.length !== 3)
294
- return false;
295
- if (!Node.isTypeReference(typeNode) && !Node.isExpressionWithTypeArguments(typeNode))
296
- return false;
297
- if (getTypeReferenceName(typeNode) !== 'Overwrite')
298
- return false;
299
- const [omittedDomPropsTypeNode, propsTypeNode] = typeNode.getTypeArguments();
300
- if (!omittedDomPropsTypeNode || !propsTypeNode)
301
- return false;
302
- if (!Node.isTypeReference(omittedDomPropsTypeNode) &&
303
- !Node.isExpressionWithTypeArguments(omittedDomPropsTypeNode)) {
304
- return false;
305
- }
306
- if (getTypeReferenceName(omittedDomPropsTypeNode) !== 'Omit')
307
- return false;
308
- const [componentPropsTypeNode, omittedAdditionalPropsTypeNode] = omittedDomPropsTypeNode.getTypeArguments();
309
- if (!componentPropsTypeNode || !omittedAdditionalPropsTypeNode)
310
- return false;
311
- if (getTypeReferenceName(componentPropsTypeNode) !== 'ComponentPropsWithoutRef') {
312
- return false;
313
- }
314
- if (!Node.isTypeReference(componentPropsTypeNode) && !Node.isExpressionWithTypeArguments(componentPropsTypeNode)) {
315
- return false;
316
- }
317
- if (componentPropsTypeNode.getTypeArguments()[0]?.getText() !== typeParameters[1].getName())
318
- return false;
319
- if (omittedAdditionalPropsTypeNode.getText() !== typeParameters[2].getName())
320
- return false;
321
- return propsTypeNode.getText() === typeParameters[0].getName();
322
- }
323
- if (typeName === 'PolymorphicProps') {
324
- if (typeParameters.length !== 3)
325
- return false;
326
- if (!Node.isTypeReference(typeNode) && !Node.isExpressionWithTypeArguments(typeNode))
327
- return false;
328
- if (getTypeReferenceName(typeNode) !== 'PropsWithAs')
329
- return false;
330
- const [propsTypeNode, elementTypeNode] = typeNode.getTypeArguments();
331
- if (!propsTypeNode || !elementTypeNode)
332
- return false;
333
- if (!Node.isTypeReference(propsTypeNode) && !Node.isExpressionWithTypeArguments(propsTypeNode)) {
334
- return false;
335
- }
336
- if (getTypeReferenceName(propsTypeNode) !== 'PropsWithHTMLElement')
337
- return false;
338
- const propsTypeArgs = propsTypeNode.getTypeArguments();
339
- const propsWithAsTargetNode = Node.isTypeReference(typeNode) ? typeNode.getTypeName() : typeNode.getExpression();
340
- const propsWithHTMLElementTargetNode = Node.isTypeReference(propsTypeNode)
341
- ? propsTypeNode.getTypeName()
342
- : propsTypeNode.getExpression();
343
- const hasExactPropsWithAsDeclaration = getTypeTargetDeclarations(propsWithAsTargetNode, true).some((declaration) => isRepoLocalTransparentPolymorphicWrapperDeclaration(declaration, 'PropsWithAs'));
344
- const hasExactPropsWithHTMLElementDeclaration = getTypeTargetDeclarations(propsWithHTMLElementTargetNode, true).some((declaration) => isRepoLocalTransparentPolymorphicWrapperDeclaration(declaration, 'PropsWithHTMLElement'));
345
- return (hasExactPropsWithAsDeclaration &&
346
- hasExactPropsWithHTMLElementDeclaration &&
347
- propsTypeArgs[0]?.getText() === typeParameters[0].getName() &&
348
- propsTypeArgs[1]?.getText() === typeParameters[1].getName() &&
349
- propsTypeArgs[2]?.getText() === typeParameters[2].getName() &&
350
- elementTypeNode.getText() === typeParameters[1].getName());
351
- }
352
- return false;
353
- }
354
- function unwrapRepoLocalTransparentPolymorphicWrapper(typeNode, allowWorkspaceImportFallback) {
355
- if (!Node.isTypeReference(typeNode) && !Node.isExpressionWithTypeArguments(typeNode)) {
356
- return undefined;
357
- }
358
- const typeName = getTypeReferenceName(typeNode);
359
- if (!typeName || !TRANSPARENT_POLYMORPHIC_TYPE_NAMES.has(typeName)) {
360
- return undefined;
361
- }
362
- const firstTypeArg = typeNode.getTypeArguments()[0];
363
- if (!firstTypeArg)
364
- return undefined;
365
- const targetNode = Node.isTypeReference(typeNode) ? typeNode.getTypeName() : typeNode.getExpression();
366
- const declarations = getTypeTargetDeclarations(targetNode, allowWorkspaceImportFallback);
367
- if (!declarations.some((declaration) => isRepoLocalTransparentPolymorphicWrapperDeclaration(declaration, typeName))) {
368
- return undefined;
369
- }
370
- return firstTypeArg;
371
- }
372
- function isRepoLocalSupportedPolymorphicChain(typeNode, seen = new Set(), allowWorkspaceImportFallback = false) {
373
- if (seen.has(typeNode))
374
- return false;
375
- seen.add(typeNode);
376
- if (Node.isParenthesizedTypeNode(typeNode) || Node.isTypeOperatorTypeNode(typeNode)) {
377
- return isRepoLocalSupportedPolymorphicChain(typeNode.getTypeNode(), seen, allowWorkspaceImportFallback);
378
- }
379
- if (Node.isIntersectionTypeNode(typeNode) || Node.isUnionTypeNode(typeNode)) {
380
- const childTypes = typeNode.getTypeNodes();
381
- return (childTypes.length > 0 &&
382
- childTypes.every((child) => isRepoLocalSupportedPolymorphicChain(child, seen, allowWorkspaceImportFallback)));
383
- }
384
- if (Node.isTypeLiteral(typeNode) || Node.isInterfaceDeclaration(typeNode)) {
385
- return true;
386
- }
387
- if (Node.isTypeReference(typeNode) || Node.isExpressionWithTypeArguments(typeNode)) {
388
- const typeName = getTypeReferenceName(typeNode);
389
- if (!typeName)
390
- return false;
391
- if (typeName === 'Omit' ||
392
- typeName === 'Partial' ||
393
- typeName === 'Readonly' ||
394
- typeName === 'Required' ||
395
- typeName === 'NonNullable' ||
396
- typeName === 'MappedOmit') {
397
- const wrappedType = typeNode.getTypeArguments()[0];
398
- return wrappedType
399
- ? isRepoLocalSupportedPolymorphicChain(wrappedType, seen, allowWorkspaceImportFallback)
400
- : false;
401
- }
402
- if (!TRANSPARENT_POLYMORPHIC_TYPE_NAMES.has(typeName)) {
403
- const targetNode = Node.isTypeReference(typeNode) ? typeNode.getTypeName() : typeNode.getExpression();
404
- for (const declaration of getTypeTargetDeclarations(targetNode, allowWorkspaceImportFallback)) {
405
- if (!declaration)
406
- continue;
407
- if (Node.isInterfaceDeclaration(declaration)) {
408
- if (declaration
409
- .getHeritageClauses()
410
- .some((clause) => clause
411
- .getTypeNodes()
412
- .every((heritageTypeNode) => isRepoLocalSupportedPolymorphicChain(heritageTypeNode, seen, allowWorkspaceImportFallback)))) {
413
- return true;
414
- }
415
- }
416
- if (Node.isTypeAliasDeclaration(declaration)) {
417
- const aliasedTypeNode = declaration.getTypeNode();
418
- if (aliasedTypeNode &&
419
- isRepoLocalSupportedPolymorphicChain(aliasedTypeNode, seen, allowWorkspaceImportFallback)) {
420
- return true;
421
- }
422
- }
423
- }
424
- return false;
425
- }
426
- const wrappedTypeNode = unwrapRepoLocalTransparentPolymorphicWrapper(typeNode, allowWorkspaceImportFallback);
427
- return wrappedTypeNode
428
- ? isRepoLocalSupportedPolymorphicChain(wrappedTypeNode, seen, allowWorkspaceImportFallback)
429
- : false;
430
- }
431
- return false;
432
- }
433
- function isRenderPropType(type) {
434
- const callSignatures = type.getCallSignatures();
435
- if (callSignatures.length === 0)
436
- return false;
437
- const returnType = callSignatures[0].getReturnType();
438
- const returnText = returnType.getText();
439
- return /ReactNode|ReactElement|JSX\.Element|Element/.test(returnText);
440
- }
441
- function extractPropsFromType(type, slotNames, typeNode) {
442
- const supportsRepoLocalPolymorphicAlias = typeNode !== undefined && isRepoLocalSupportedPolymorphicChain(typeNode, new Set(), true);
443
- const suppressNeverChildrenSlot = supportsRepoLocalPolymorphicAlias;
444
- const isPureDomWrapper = typeNode !== undefined && isPureExpandableDomAttributeWrapperType(typeNode);
445
- const symbolExtraction = supportsRepoLocalPolymorphicAlias || isPureDomWrapper
446
- ? { props: [], hasChildren: false }
447
- : extractPropsFromTypeSymbols(type, slotNames, suppressNeverChildrenSlot);
448
- const syntaxExtraction = typeNode &&
449
- (symbolExtraction.props.length === 0 ||
450
- shouldMergeDomSyntaxExtraction(typeNode) ||
451
- containsImportedOmitWrappedCustomProps(typeNode))
452
- ? extractPropsFromTypeNode(typeNode, slotNames, undefined, new Set(), false, suppressNeverChildrenSlot)
453
- : { props: [], hasChildren: false };
454
- const propsByName = new Map();
455
- const useSyntaxOnly = typeNode !== undefined && shouldMergeDomSyntaxExtraction(typeNode);
456
- if (!useSyntaxOnly) {
457
- for (const prop of symbolExtraction.props) {
458
- propsByName.set(prop.name, prop);
459
- }
460
- }
461
- for (const prop of syntaxExtraction.props) {
462
- propsByName.set(prop.name, prop);
463
- }
464
- return {
465
- props: [...propsByName.values()].sort((a, b) => a.name.localeCompare(b.name)),
466
- hasChildren: symbolExtraction.hasChildren || syntaxExtraction.hasChildren,
467
- };
468
- }
469
- function extractPropsFromTypeSymbols(type, slotNames, suppressNeverChildrenSlot = false) {
470
- const props = [];
471
- let hasChildren = false;
472
- for (const property of type.getProperties()) {
473
- const name = property.getName();
474
- if (name === 'children') {
475
- if (suppressNeverChildrenSlot) {
476
- const declaration = property.getValueDeclaration() ?? property.getDeclarations()[0];
477
- if (declaration && property.getTypeAtLocation(declaration).isNever()) {
478
- continue;
479
- }
480
- }
481
- hasChildren = true;
482
- continue;
483
- }
484
- // Skip props that are render props — they become slots
485
- if (slotNames.has(name))
486
- continue;
487
- const declaration = property.getValueDeclaration() ?? property.getDeclarations()[0];
488
- if (!declaration)
489
- continue;
490
- const propType = property.getTypeAtLocation(declaration);
491
- const declarationTypeText = 'getTypeNode' in declaration && typeof declaration.getTypeNode === 'function'
492
- ? declaration.getTypeNode()?.getText()
493
- : undefined;
494
- const typeText = propType.isAny() && declarationTypeText ? declarationTypeText : propType.getText(declaration);
495
- const required = !property.isOptional();
496
- const allowedValues = extractAllowedValues(propType);
497
- props.push({
498
- name,
499
- type: typeText,
500
- required,
501
- ...(allowedValues && { allowedValues }),
502
- ...(typeof declaration.getStartLineNumber === 'function'
503
- ? {
504
- sourceStartLine: declaration.getStartLineNumber(),
505
- sourceEndLine: declaration.getEndLineNumber(),
506
- }
507
- : {}),
508
- });
509
- }
510
- return {
511
- props: props.sort((a, b) => a.name.localeCompare(b.name)),
512
- hasChildren,
513
- };
514
- }
515
- function filterExcludedSyntaxProps(extraction, excludedProps) {
516
- if (excludedProps.size === 0)
517
- return extraction;
518
- return {
519
- ...extraction,
520
- props: extraction.props.filter((prop) => !excludedProps.has(prop.name)),
521
- };
522
- }
523
- function extractPropsFromInterfaceDeclaration(declaration, slotNames, seen, excludedProps, allowImportedOmitWorkspaceFallback, suppressNeverChildrenSlot) {
524
- const hasExpandableDomHeritage = declaration
525
- .getHeritageClauses()
526
- .some((clause) => clause.getTypeNodes().some((typeNode) => collectExpandableDomAttributeWrapperContexts(typeNode).length > 0));
527
- const symbolType = hasExpandableDomHeritage
528
- ? declaration
529
- .getType()
530
- .getProperties()
531
- .filter((symbol) => {
532
- const decl = symbol.getValueDeclaration() ?? symbol.getDeclarations()[0];
533
- return decl?.getParent() === declaration;
534
- })
535
- .reduce((acc, symbol) => {
536
- const decl = symbol.getValueDeclaration() ?? symbol.getDeclarations()[0];
537
- if (!decl)
538
- return acc;
539
- const propType = symbol.getTypeAtLocation(decl);
540
- const typeText = propType.isAny()
541
- ? (('getTypeNode' in decl &&
542
- typeof decl.getTypeNode === 'function'
543
- ? decl.getTypeNode()?.getText()
544
- : undefined) ?? propType.getText(decl))
545
- : propType.getText(decl);
546
- const name = symbol.getName();
547
- if (name === 'children') {
548
- if (suppressNeverChildrenSlot && propType.isNever())
549
- return acc;
550
- acc.hasChildren = true;
551
- return acc;
552
- }
553
- if (slotNames.has(name))
554
- return acc;
555
- const required = !symbol.isOptional();
556
- const allowedValues = extractAllowedValues(propType);
557
- acc.props.push({
558
- name,
559
- type: typeText,
560
- required,
561
- ...(allowedValues && { allowedValues }),
562
- ...(typeof decl.getStartLineNumber === 'function'
563
- ? {
564
- sourceStartLine: decl.getStartLineNumber(),
565
- sourceEndLine: decl.getEndLineNumber(),
566
- }
567
- : {}),
568
- });
569
- return acc;
570
- }, { props: [], hasChildren: false })
571
- : extractPropsFromTypeSymbols(declaration.getType(), slotNames, suppressNeverChildrenSlot);
572
- const ownProps = filterExcludedSyntaxProps(symbolType, excludedProps);
573
- const inheritedProps = declaration
574
- .getHeritageClauses()
575
- .flatMap((clause) => clause
576
- .getTypeNodes()
577
- .flatMap((heritageTypeNode) => extractPropsFromTypeNode(heritageTypeNode, slotNames, seen, excludedProps, allowImportedOmitWorkspaceFallback, suppressNeverChildrenSlot).props));
578
- return {
579
- props: [...ownProps.props, ...inheritedProps].sort((a, b) => a.name.localeCompare(b.name)),
580
- hasChildren: ownProps.hasChildren,
581
- };
582
- }
583
- function getMappedOmitEquivalentArgs(typeNode) {
584
- if (!Node.isTypeReference(typeNode) && !Node.isExpressionWithTypeArguments(typeNode)) {
585
- return undefined;
586
- }
587
- const [wrappedType, omittedProps] = typeNode.getTypeArguments();
588
- if (!wrappedType || !omittedProps)
589
- return undefined;
590
- const targetNode = Node.isTypeReference(typeNode) ? typeNode.getTypeName() : typeNode.getExpression();
591
- for (const declaration of getTypeTargetDeclarations(targetNode, true)) {
592
- if (!Node.isTypeAliasDeclaration(declaration))
593
- continue;
594
- if (declaration.getTypeParameters().length !== 2)
595
- continue;
596
- const mappedTypeNode = declaration.getTypeNode();
597
- const [sourceTypeParameter, excludedKeysParameter] = declaration.getTypeParameters();
598
- const sourceTypeParameterName = sourceTypeParameter.getName();
599
- const excludedKeysParameterName = excludedKeysParameter.getName();
600
- if (!mappedTypeNode)
601
- continue;
602
- if (Node.isTypeReference(mappedTypeNode) || Node.isExpressionWithTypeArguments(mappedTypeNode)) {
603
- if (getTypeReferenceName(mappedTypeNode) !== 'Omit')
604
- continue;
605
- const [innerWrappedType, innerOmittedProps] = mappedTypeNode.getTypeArguments();
606
- if (!innerWrappedType || !innerOmittedProps)
607
- continue;
608
- if (innerWrappedType.getText() !== sourceTypeParameterName)
609
- continue;
610
- if (innerOmittedProps.getText() !== excludedKeysParameterName)
611
- continue;
612
- return { wrappedType, omittedProps };
613
- }
614
- if (!Node.isMappedTypeNode(mappedTypeNode))
615
- continue;
616
- const iterationParameter = mappedTypeNode.getTypeParameter();
617
- const iterationParameterName = iterationParameter.getName();
618
- if (iterationParameter.getConstraint()?.getText() !== `keyof ${sourceTypeParameterName}`)
619
- continue;
620
- if (mappedTypeNode.getNameTypeNode()?.getText() !==
621
- `${iterationParameterName} extends ${excludedKeysParameterName} ? never : ${iterationParameterName}`) {
622
- continue;
623
- }
624
- if (mappedTypeNode.getTypeNode()?.getText() !== `${sourceTypeParameterName}[${iterationParameterName}]`)
625
- continue;
626
- return { wrappedType, omittedProps };
627
- }
628
- return undefined;
629
- }
630
- function extractPropsFromTypeNode(typeNode, slotNames, seen = new Set(), excludedProps = new Set(), allowImportedOmitWorkspaceFallback = false, suppressNeverChildrenSlot = false) {
631
- if (seen.has(typeNode))
632
- return { props: [], hasChildren: false };
633
- seen.add(typeNode);
634
- if (Node.isParenthesizedTypeNode(typeNode) || Node.isTypeOperatorTypeNode(typeNode)) {
635
- return extractPropsFromTypeNode(typeNode.getTypeNode(), slotNames, seen, excludedProps, allowImportedOmitWorkspaceFallback, suppressNeverChildrenSlot);
636
- }
637
- if (Node.isIntersectionTypeNode(typeNode) || Node.isUnionTypeNode(typeNode)) {
638
- const propsByName = new Map();
639
- let hasChildren = false;
640
- for (const childTypeNode of typeNode.getTypeNodes()) {
641
- const extracted = extractPropsFromTypeNode(childTypeNode, slotNames, seen, excludedProps, allowImportedOmitWorkspaceFallback, suppressNeverChildrenSlot);
642
- hasChildren ||= extracted.hasChildren;
643
- for (const prop of extracted.props) {
644
- propsByName.set(prop.name, prop);
645
- }
646
- }
647
- return { props: [...propsByName.values()], hasChildren };
648
- }
649
- if (Node.isTypeLiteral(typeNode)) {
650
- return filterExcludedSyntaxProps(extractPropsFromTypeSymbols(typeNode.getType(), slotNames, suppressNeverChildrenSlot), excludedProps);
651
- }
652
- if (Node.isInterfaceDeclaration(typeNode)) {
653
- return extractPropsFromInterfaceDeclaration(typeNode, slotNames, seen, excludedProps, allowImportedOmitWorkspaceFallback, suppressNeverChildrenSlot);
654
- }
655
- if (Node.isTypeReference(typeNode)) {
656
- const typeName = getTypeReferenceName(typeNode);
657
- if (!typeName)
658
- return { props: [], hasChildren: false };
659
- if (typeName === 'PropsWithChildren') {
660
- const wrappedType = typeNode.getTypeArguments()[0];
661
- if (!wrappedType)
662
- return { props: [], hasChildren: true };
663
- const extracted = extractPropsFromTypeNode(wrappedType, slotNames, seen, excludedProps, allowImportedOmitWorkspaceFallback, suppressNeverChildrenSlot);
664
- return { props: extracted.props, hasChildren: true };
665
- }
666
- if (isExpandableDomAttributeWrapperName(typeName) || typeName === 'Pick') {
667
- return typeName === 'Pick'
668
- ? {
669
- props: extractPickedPropsFromTypeNode(typeNode, slotNames, excludedProps),
670
- hasChildren: false,
671
- }
672
- : { props: [], hasChildren: false };
673
- }
674
- if (typeName === 'Omit' ||
675
- typeName === 'Partial' ||
676
- typeName === 'Readonly' ||
677
- typeName === 'Required' ||
678
- typeName === 'NonNullable') {
679
- const wrappedType = typeNode.getTypeArguments()[0];
680
- if (!wrappedType)
681
- return { props: [], hasChildren: false };
682
- const nextExcludedProps = new Set(excludedProps);
683
- if (typeName === 'Omit') {
684
- const omittedProps = typeNode.getTypeArguments()[1];
685
- if (omittedProps) {
686
- for (const value of getStringLiteralTypeValues(omittedProps)) {
687
- nextExcludedProps.add(value);
688
- }
689
- }
690
- }
691
- return extractPropsFromTypeNode(wrappedType, slotNames, seen, nextExcludedProps, allowImportedOmitWorkspaceFallback || typeName === 'Omit', suppressNeverChildrenSlot);
692
- }
693
- const mappedOmitArgs = allowImportedOmitWorkspaceFallback ? getMappedOmitEquivalentArgs(typeNode) : undefined;
694
- if (mappedOmitArgs) {
695
- const nextExcludedProps = new Set(excludedProps);
696
- for (const value of getStringLiteralTypeValues(mappedOmitArgs.omittedProps)) {
697
- nextExcludedProps.add(value);
698
- }
699
- return extractPropsFromTypeNode(mappedOmitArgs.wrappedType, slotNames, seen, nextExcludedProps, true, suppressNeverChildrenSlot);
700
- }
701
- const wrappedTypeNode = unwrapRepoLocalTransparentPolymorphicWrapper(typeNode, allowImportedOmitWorkspaceFallback) ??
702
- (!allowImportedOmitWorkspaceFallback ? unwrapRepoLocalTransparentPolymorphicWrapper(typeNode, true) : undefined);
703
- if (wrappedTypeNode) {
704
- return extractPropsFromTypeNode(wrappedTypeNode, slotNames, seen, excludedProps, allowImportedOmitWorkspaceFallback, true);
705
- }
706
- if (TRANSPARENT_POLYMORPHIC_TYPE_NAMES.has(typeName)) {
707
- return { props: [], hasChildren: false };
708
- }
709
- for (const declaration of getTypeTargetDeclarations(typeNode.getTypeName(), allowImportedOmitWorkspaceFallback)) {
710
- if (!declaration)
711
- continue;
712
- if (Node.isInterfaceDeclaration(declaration)) {
713
- return extractPropsFromInterfaceDeclaration(declaration, slotNames, seen, excludedProps, allowImportedOmitWorkspaceFallback, suppressNeverChildrenSlot);
714
- }
715
- if (Node.isTypeAliasDeclaration(declaration)) {
716
- const aliasedTypeNode = declaration.getTypeNode();
717
- if (!aliasedTypeNode)
718
- continue;
719
- return extractPropsFromTypeNode(aliasedTypeNode, slotNames, seen, excludedProps, allowImportedOmitWorkspaceFallback, suppressNeverChildrenSlot);
720
- }
721
- }
722
- }
723
- if (Node.isExpressionWithTypeArguments(typeNode)) {
724
- const typeName = getTypeReferenceName(typeNode);
725
- if (!typeName)
726
- return { props: [], hasChildren: false };
727
- if (typeName === 'PropsWithChildren') {
728
- const wrappedType = typeNode.getTypeArguments()[0];
729
- if (!wrappedType)
730
- return { props: [], hasChildren: true };
731
- const extracted = extractPropsFromTypeNode(wrappedType, slotNames, seen, excludedProps, allowImportedOmitWorkspaceFallback, suppressNeverChildrenSlot);
732
- return { props: extracted.props, hasChildren: true };
733
- }
734
- if (isExpandableDomAttributeWrapperName(typeName) || typeName === 'Pick') {
735
- return typeName === 'Pick'
736
- ? {
737
- props: extractPickedPropsFromTypeNode(typeNode, slotNames, excludedProps),
738
- hasChildren: false,
739
- }
740
- : { props: [], hasChildren: false };
741
- }
742
- if (typeName === 'Omit' ||
743
- typeName === 'Partial' ||
744
- typeName === 'Readonly' ||
745
- typeName === 'Required' ||
746
- typeName === 'NonNullable') {
747
- const wrappedType = typeNode.getTypeArguments()[0];
748
- if (!wrappedType)
749
- return { props: [], hasChildren: false };
750
- const nextExcludedProps = new Set(excludedProps);
751
- if (typeName === 'Omit') {
752
- const omittedProps = typeNode.getTypeArguments()[1];
753
- if (omittedProps) {
754
- for (const value of getStringLiteralTypeValues(omittedProps)) {
755
- nextExcludedProps.add(value);
756
- }
757
- }
758
- }
759
- return extractPropsFromTypeNode(wrappedType, slotNames, seen, nextExcludedProps, allowImportedOmitWorkspaceFallback || typeName === 'Omit', suppressNeverChildrenSlot);
760
- }
761
- const mappedOmitArgs = allowImportedOmitWorkspaceFallback ? getMappedOmitEquivalentArgs(typeNode) : undefined;
762
- if (mappedOmitArgs) {
763
- const nextExcludedProps = new Set(excludedProps);
764
- for (const value of getStringLiteralTypeValues(mappedOmitArgs.omittedProps)) {
765
- nextExcludedProps.add(value);
766
- }
767
- return extractPropsFromTypeNode(mappedOmitArgs.wrappedType, slotNames, seen, nextExcludedProps, true, suppressNeverChildrenSlot);
768
- }
769
- const wrappedTypeNode = unwrapRepoLocalTransparentPolymorphicWrapper(typeNode, allowImportedOmitWorkspaceFallback) ??
770
- (!allowImportedOmitWorkspaceFallback ? unwrapRepoLocalTransparentPolymorphicWrapper(typeNode, true) : undefined);
771
- if (wrappedTypeNode) {
772
- return extractPropsFromTypeNode(wrappedTypeNode, slotNames, seen, excludedProps, allowImportedOmitWorkspaceFallback, true);
773
- }
774
- if (TRANSPARENT_POLYMORPHIC_TYPE_NAMES.has(typeName)) {
775
- return { props: [], hasChildren: false };
776
- }
777
- for (const declaration of getTypeTargetDeclarations(typeNode.getExpression(), allowImportedOmitWorkspaceFallback)) {
778
- if (!declaration)
779
- continue;
780
- if (Node.isInterfaceDeclaration(declaration)) {
781
- return extractPropsFromInterfaceDeclaration(declaration, slotNames, seen, excludedProps, allowImportedOmitWorkspaceFallback, suppressNeverChildrenSlot);
782
- }
783
- if (Node.isTypeAliasDeclaration(declaration)) {
784
- const aliasedTypeNode = declaration.getTypeNode();
785
- if (!aliasedTypeNode)
786
- continue;
787
- return extractPropsFromTypeNode(aliasedTypeNode, slotNames, seen, excludedProps, allowImportedOmitWorkspaceFallback, suppressNeverChildrenSlot);
788
- }
789
- }
790
- }
791
- return { props: [], hasChildren: false };
792
- }
793
- function collectRenderPropSlotNames(type) {
794
- const names = new Set();
795
- for (const property of type.getProperties()) {
796
- const name = property.getName();
797
- if (!name.startsWith('render'))
798
- continue;
799
- const declaration = property.getValueDeclaration() ?? property.getDeclarations()[0];
800
- if (!declaration)
801
- continue;
802
- const propType = property.getTypeAtLocation(declaration);
803
- if (isRenderPropType(propType)) {
804
- names.add(name);
805
- }
806
- }
807
- return names;
808
- }
809
- function recordBindingPatternDefaults(nameNode, defaults) {
810
- if (!Node.isObjectBindingPattern(nameNode))
811
- return;
812
- for (const element of nameNode.getElements()) {
813
- const initializer = element.getInitializer();
814
- if (!initializer)
815
- continue;
816
- const propertyNameNode = element.getPropertyNameNode();
817
- const propName = propertyNameNode?.getText().replace(/^['"]|['"]$/g, '') ?? element.getNameNode().getText();
818
- const value = initializer.getText().replace(/^['"]|['"]$/g, '');
819
- defaults.set(propName, value);
820
- }
821
- }
822
- function extractDefaultValues(func) {
823
- const defaults = new Map();
824
- const params = func.getParameters();
825
- if (params.length === 0)
826
- return defaults;
827
- const firstParam = params[0];
828
- const nameNode = firstParam.getNameNode();
829
- if (Node.isObjectBindingPattern(nameNode)) {
830
- recordBindingPatternDefaults(nameNode, defaults);
831
- return defaults;
832
- }
833
- if (!Node.isIdentifier(nameNode))
834
- return defaults;
835
- const body = func.getBody();
836
- if (!body || !Node.isBlock(body))
837
- return defaults;
838
- for (const statement of body.getStatements()) {
839
- if (!Node.isVariableStatement(statement))
840
- continue;
841
- for (const declaration of statement.getDeclarationList().getDeclarations()) {
842
- const declarationName = declaration.getNameNode();
843
- const initializer = declaration.getInitializer();
844
- if (!Node.isObjectBindingPattern(declarationName))
845
- continue;
846
- if (!initializer || !Node.isIdentifier(initializer) || initializer.getText() !== nameNode.getText())
847
- continue;
848
- recordBindingPatternDefaults(declarationName, defaults);
849
- return defaults;
850
- }
851
- }
852
- return defaults;
853
- }
854
- function isImplementationOnlyAliasProp(func, propName) {
855
- if (!propName.startsWith('_'))
856
- return false;
857
- const allowedJsxAttributes = new Set([
858
- 'id',
859
- 'htmlFor',
860
- 'aria-activedescendant',
861
- 'aria-controls',
862
- 'aria-describedby',
863
- 'aria-labelledby',
864
- 'aria-owns',
865
- ]);
866
- let sawRuntimeUsage = false;
867
- for (const node of func.getDescendants()) {
868
- if (!Node.isIdentifier(node) || node.getText() !== propName)
869
- continue;
870
- const bindingElement = node.getFirstAncestor((ancestor) => Node.isBindingElement(ancestor));
871
- if (bindingElement?.getNameNode() === node) {
872
- continue;
873
- }
874
- const jsxAttribute = node.getFirstAncestor((ancestor) => Node.isJsxAttribute(ancestor));
875
- if (jsxAttribute) {
876
- const attributeName = jsxAttribute.getNameNode().getText();
877
- if (allowedJsxAttributes.has(attributeName)) {
878
- sawRuntimeUsage = true;
879
- continue;
880
- }
881
- return false;
882
- }
883
- const propertyAssignment = node.getFirstAncestor((ancestor) => Node.isPropertyAssignment(ancestor));
884
- if (propertyAssignment) {
885
- const propertyName = propertyAssignment.getName();
886
- if (propertyName === propName) {
887
- sawRuntimeUsage = true;
888
- continue;
889
- }
890
- return false;
891
- }
892
- return false;
893
- }
894
- return sawRuntimeUsage;
895
- }
896
- function filterImplementationOnlyAliasProps(props, func) {
897
- const publicPropNames = new Set(props.filter((prop) => !prop.name.startsWith('_')).map((prop) => prop.name));
898
- return props.filter((prop) => {
899
- if (/^__scope[A-Z]/.test(prop.name)) {
900
- return false;
901
- }
902
- if (prop.name.startsWith('_') && publicPropNames.has(prop.name.slice(1))) {
903
- return false;
904
- }
905
- return !isImplementationOnlyAliasProp(func, prop.name);
906
- });
907
- }
908
- function extractDestructuredBindingFallbackProps(func, param, existingPropNames, slotNames) {
909
- const nameNode = param.getNameNode();
910
- const bindingPatterns = [];
911
- if (Node.isObjectBindingPattern(nameNode)) {
912
- bindingPatterns.push(nameNode);
913
- }
914
- else if (Node.isIdentifier(nameNode)) {
915
- const body = func.getBody();
916
- const paramName = nameNode.getText();
917
- if (body) {
918
- for (const declaration of body.getDescendantsOfKind(SyntaxKind.VariableDeclaration)) {
919
- const declarationName = declaration.getNameNode();
920
- const initializer = declaration.getInitializer();
921
- if (!Node.isObjectBindingPattern(declarationName))
922
- continue;
923
- if (!initializer || !Node.isIdentifier(initializer) || initializer.getText() !== paramName)
924
- continue;
925
- bindingPatterns.push(declarationName);
926
- }
927
- }
928
- }
929
- else {
930
- return [];
931
- }
932
- const propsByName = new Map();
933
- const paramType = param.getType();
934
- for (const bindingPattern of bindingPatterns) {
935
- for (const element of bindingPattern.getElements()) {
936
- if (element.getDotDotDotToken())
937
- continue;
938
- const propName = element.getPropertyNameNode()?.getText() ?? element.getNameNode().getText();
939
- if (propName === 'children' || existingPropNames.has(propName) || slotNames.has(propName))
940
- continue;
941
- if (propsByName.has(propName))
942
- continue;
943
- const property = paramType.getProperty(propName);
944
- const declaration = property?.getValueDeclaration() ?? property?.getDeclarations()[0];
945
- const propertyType = property && declaration ? property.getTypeAtLocation(declaration).getText(declaration) : 'any';
946
- propsByName.set(propName, {
947
- name: propName,
948
- type: propertyType === 'unknown' ? 'any' : propertyType,
949
- required: property ? !property.isOptional() : false,
950
- ...(declaration &&
951
- typeof declaration.getStartLineNumber === 'function'
952
- ? {
953
- sourceStartLine: declaration.getStartLineNumber(),
954
- sourceEndLine: declaration.getEndLineNumber(),
955
- }
956
- : {}),
957
- });
958
- }
959
- }
960
- return [...propsByName.values()].sort((a, b) => a.name.localeCompare(b.name));
961
- }
962
- function extractSlots(type, hasChildren) {
963
- const slots = [];
964
- if (hasChildren) {
965
- slots.push({ name: 'children', isDefault: true });
966
- }
967
- for (const property of type.getProperties()) {
968
- const name = property.getName();
969
- if (name === 'children')
970
- continue;
971
- if (!name.startsWith('render'))
972
- continue;
973
- const declaration = property.getValueDeclaration() ?? property.getDeclarations()[0];
974
- if (!declaration)
975
- continue;
976
- const propType = property.getTypeAtLocation(declaration);
977
- if (!isRenderPropType(propType))
978
- continue;
979
- const slotName = name.replace(/^render/, '');
980
- slots.push({
981
- name: slotName.charAt(0).toLowerCase() + slotName.slice(1),
982
- isDefault: false,
983
- });
984
- }
985
- return slots.sort((a, b) => a.name.localeCompare(b.name));
986
- }
987
- function isExpandableDomAttributeWrapperName(typeName) {
988
- return EXPANDABLE_DOM_ATTRIBUTE_TYPE_NAMES.has(typeName);
989
- }
990
- function getStringLiteralTypeValues(typeNode) {
991
- if (Node.isLiteralTypeNode(typeNode)) {
992
- const literal = typeNode.getLiteral();
993
- if (Node.isStringLiteral(literal)) {
994
- return [literal.getLiteralText()];
995
- }
996
- }
997
- if (!Node.isUnionTypeNode(typeNode))
998
- return [];
999
- return typeNode.getTypeNodes().flatMap((unionTypeNode) => getStringLiteralTypeValues(unionTypeNode));
1000
- }
1001
- function shouldMergeDomSyntaxExtraction(typeNode) {
1002
- return collectExpandableDomAttributeWrapperContexts(typeNode).length > 0 || containsSupportedDomPickType(typeNode);
1003
- }
1004
- function containsImportedOmitWrappedCustomProps(typeNode, seen = new Set(), originSourceFile = typeNode.getSourceFile(), sawOmit = false) {
1005
- if (seen.has(typeNode))
1006
- return false;
1007
- seen.add(typeNode);
1008
- if (Node.isParenthesizedTypeNode(typeNode) || Node.isTypeOperatorTypeNode(typeNode)) {
1009
- return containsImportedOmitWrappedCustomProps(typeNode.getTypeNode(), seen, originSourceFile, sawOmit);
1010
- }
1011
- if (Node.isIntersectionTypeNode(typeNode) || Node.isUnionTypeNode(typeNode)) {
1012
- return typeNode
1013
- .getTypeNodes()
1014
- .some((child) => containsImportedOmitWrappedCustomProps(child, seen, originSourceFile, sawOmit));
1015
- }
1016
- if (Node.isTypeReference(typeNode) || Node.isExpressionWithTypeArguments(typeNode)) {
1017
- const typeName = getTypeReferenceName(typeNode);
1018
- if (!typeName)
1019
- return false;
1020
- if (typeName === 'PropsWithChildren') {
1021
- const wrappedType = typeNode.getTypeArguments()[0];
1022
- return wrappedType ? containsImportedOmitWrappedCustomProps(wrappedType, seen, originSourceFile, sawOmit) : false;
1023
- }
1024
- if (typeName === 'Omit') {
1025
- const wrappedType = typeNode.getTypeArguments()[0];
1026
- return wrappedType ? containsImportedOmitWrappedCustomProps(wrappedType, seen, originSourceFile, true) : false;
1027
- }
1028
- if (typeName === 'Partial' || typeName === 'Readonly' || typeName === 'Required' || typeName === 'NonNullable') {
1029
- const wrappedType = typeNode.getTypeArguments()[0];
1030
- return wrappedType ? containsImportedOmitWrappedCustomProps(wrappedType, seen, originSourceFile, sawOmit) : false;
1031
- }
1032
- const targetNode = Node.isTypeReference(typeNode) ? typeNode.getTypeName() : typeNode.getExpression();
1033
- for (const declaration of getTypeTargetDeclarations(targetNode, sawOmit)) {
1034
- if (!declaration)
1035
- continue;
1036
- const declarationSourceFile = declaration.getSourceFile();
1037
- const isImportedDeclaration = declarationSourceFile.getFilePath() !== originSourceFile.getFilePath();
1038
- if (sawOmit && isImportedDeclaration) {
1039
- const declarationTypeNode = Node.isTypeAliasDeclaration(declaration) ? declaration.getTypeNode() : declaration;
1040
- if (!declarationTypeNode || !isPureExpandableDomAttributeWrapperType(declarationTypeNode)) {
1041
- return true;
1042
- }
1043
- }
1044
- if (Node.isInterfaceDeclaration(declaration)) {
1045
- if (declaration
1046
- .getHeritageClauses()
1047
- .some((clause) => clause
1048
- .getTypeNodes()
1049
- .some((heritageTypeNode) => containsImportedOmitWrappedCustomProps(heritageTypeNode, seen, originSourceFile, sawOmit)))) {
1050
- return true;
1051
- }
1052
- }
1053
- if (Node.isTypeAliasDeclaration(declaration)) {
1054
- const aliasedTypeNode = declaration.getTypeNode();
1055
- if (aliasedTypeNode &&
1056
- containsImportedOmitWrappedCustomProps(aliasedTypeNode, seen, originSourceFile, sawOmit)) {
1057
- return true;
1058
- }
1059
- }
1060
- }
1061
- }
1062
- return false;
1063
- }
1064
- function containsSupportedDomPickType(typeNode, seen = new Set()) {
1065
- if (seen.has(typeNode))
1066
- return false;
1067
- seen.add(typeNode);
1068
- if (Node.isParenthesizedTypeNode(typeNode) || Node.isTypeOperatorTypeNode(typeNode)) {
1069
- return containsSupportedDomPickType(typeNode.getTypeNode(), seen);
1070
- }
1071
- if (Node.isIntersectionTypeNode(typeNode) || Node.isUnionTypeNode(typeNode)) {
1072
- return typeNode.getTypeNodes().some((child) => containsSupportedDomPickType(child, seen));
1073
- }
1074
- if (Node.isTypeReference(typeNode) || Node.isExpressionWithTypeArguments(typeNode)) {
1075
- const typeName = getTypeReferenceName(typeNode);
1076
- if (!typeName)
1077
- return false;
1078
- if (typeName === 'Pick') {
1079
- const sourceTypeNode = typeNode.getTypeArguments()[0];
1080
- return sourceTypeNode
1081
- ? collectExpandableDomAttributeWrapperContexts(sourceTypeNode).length > 0 ||
1082
- containsSupportedDomPickType(sourceTypeNode, seen)
1083
- : false;
1084
- }
1085
- if (typeName === 'Omit' ||
1086
- typeName === 'Partial' ||
1087
- typeName === 'Readonly' ||
1088
- typeName === 'Required' ||
1089
- typeName === 'NonNullable') {
1090
- const wrappedType = typeNode.getTypeArguments()[0];
1091
- return wrappedType ? containsSupportedDomPickType(wrappedType, seen) : false;
1092
- }
1093
- const targetNode = Node.isTypeReference(typeNode) ? typeNode.getTypeName() : typeNode.getExpression();
1094
- for (const declaration of getTypeTargetDeclarations(targetNode)) {
1095
- if (!declaration)
1096
- continue;
1097
- if (Node.isInterfaceDeclaration(declaration)) {
1098
- if (declaration
1099
- .getHeritageClauses()
1100
- .some((clause) => clause.getTypeNodes().some((heritageTypeNode) => containsSupportedDomPickType(heritageTypeNode, seen)))) {
1101
- return true;
1102
- }
1103
- }
1104
- if (Node.isTypeAliasDeclaration(declaration)) {
1105
- const aliasedTypeNode = declaration.getTypeNode();
1106
- if (aliasedTypeNode && containsSupportedDomPickType(aliasedTypeNode, seen)) {
1107
- return true;
1108
- }
1109
- }
1110
- }
1111
- }
1112
- return false;
1113
- }
1114
- function containsAnyPickType(typeNode, seen = new Set()) {
1115
- if (seen.has(typeNode))
1116
- return false;
1117
- seen.add(typeNode);
1118
- if (Node.isParenthesizedTypeNode(typeNode) || Node.isTypeOperatorTypeNode(typeNode)) {
1119
- return containsAnyPickType(typeNode.getTypeNode(), seen);
1120
- }
1121
- if (Node.isIntersectionTypeNode(typeNode) || Node.isUnionTypeNode(typeNode)) {
1122
- return typeNode.getTypeNodes().some((child) => containsAnyPickType(child, seen));
1123
- }
1124
- if (Node.isTypeReference(typeNode) || Node.isExpressionWithTypeArguments(typeNode)) {
1125
- const typeName = getTypeReferenceName(typeNode);
1126
- if (!typeName)
1127
- return false;
1128
- if (typeName === 'Pick') {
1129
- return true;
1130
- }
1131
- const wrappedType = typeNode.getTypeArguments()[0];
1132
- if ((typeName === 'PropsWithChildren' ||
1133
- typeName === 'Omit' ||
1134
- typeName === 'Partial' ||
1135
- typeName === 'Readonly' ||
1136
- typeName === 'Required' ||
1137
- typeName === 'NonNullable') &&
1138
- wrappedType) {
1139
- return containsAnyPickType(wrappedType, seen);
1140
- }
1141
- const targetNode = Node.isTypeReference(typeNode) ? typeNode.getTypeName() : typeNode.getExpression();
1142
- for (const declaration of getTypeTargetDeclarations(targetNode, true)) {
1143
- if (!declaration)
1144
- continue;
1145
- if (Node.isInterfaceDeclaration(declaration)) {
1146
- if (declaration
1147
- .getHeritageClauses()
1148
- .some((clause) => clause.getTypeNodes().some((heritageTypeNode) => containsAnyPickType(heritageTypeNode, seen)))) {
1149
- return true;
1150
- }
1151
- }
1152
- if (Node.isTypeAliasDeclaration(declaration)) {
1153
- const aliasedTypeNode = declaration.getTypeNode();
1154
- if (aliasedTypeNode && containsAnyPickType(aliasedTypeNode, seen)) {
1155
- return true;
1156
- }
1157
- }
1158
- }
1159
- }
1160
- return false;
1161
- }
1162
- function collectExpandableDomAttributeWrapperContexts(typeNode, seen = new Set(), excludedProps = new Set()) {
1163
- if (seen.has(typeNode))
1164
- return [];
1165
- seen.add(typeNode);
1166
- if (Node.isParenthesizedTypeNode(typeNode)) {
1167
- return collectExpandableDomAttributeWrapperContexts(typeNode.getTypeNode(), seen, excludedProps);
1168
- }
1169
- if (Node.isTypeOperatorTypeNode(typeNode)) {
1170
- return collectExpandableDomAttributeWrapperContexts(typeNode.getTypeNode(), seen, excludedProps);
1171
- }
1172
- if (Node.isIntersectionTypeNode(typeNode) || Node.isUnionTypeNode(typeNode)) {
1173
- return typeNode
1174
- .getTypeNodes()
1175
- .flatMap((child) => collectExpandableDomAttributeWrapperContexts(child, seen, excludedProps));
1176
- }
1177
- if (Node.isTypeReference(typeNode)) {
1178
- const typeName = getTypeReferenceName(typeNode);
1179
- if (!typeName)
1180
- return [];
1181
- if (isExpandableDomAttributeWrapperName(typeName)) {
1182
- return [{ name: typeName, excludedProps: new Set(excludedProps) }];
1183
- }
1184
- if (typeName === 'Omit') {
1185
- const [wrappedType, omittedProps] = typeNode.getTypeArguments();
1186
- if (!wrappedType)
1187
- return [];
1188
- const nextExcludedProps = new Set(excludedProps);
1189
- if (omittedProps) {
1190
- for (const value of getStringLiteralTypeValues(omittedProps)) {
1191
- nextExcludedProps.add(value);
1192
- }
1193
- }
1194
- return collectExpandableDomAttributeWrapperContexts(wrappedType, seen, nextExcludedProps);
1195
- }
1196
- if (typeName === 'Partial' || typeName === 'Readonly' || typeName === 'Required' || typeName === 'NonNullable') {
1197
- const wrappedType = typeNode.getTypeArguments()[0];
1198
- if (!wrappedType)
1199
- return [];
1200
- return collectExpandableDomAttributeWrapperContexts(wrappedType, seen, excludedProps);
1201
- }
1202
- for (const definition of getNodeDefinitions(typeNode.getTypeName())) {
1203
- const declaration = definition.getDeclarationNode();
1204
- if (!declaration)
1205
- continue;
1206
- if (Node.isInterfaceDeclaration(declaration)) {
1207
- return declaration
1208
- .getHeritageClauses()
1209
- .flatMap((clause) => clause
1210
- .getTypeNodes()
1211
- .flatMap((heritageTypeNode) => collectExpandableDomAttributeWrapperContexts(heritageTypeNode, seen, excludedProps)));
1212
- }
1213
- if (Node.isTypeAliasDeclaration(declaration)) {
1214
- const aliasedTypeNode = declaration.getTypeNode();
1215
- if (!aliasedTypeNode)
1216
- return [];
1217
- return collectExpandableDomAttributeWrapperContexts(aliasedTypeNode, seen, excludedProps);
1218
- }
1219
- }
1220
- }
1221
- if (Node.isExpressionWithTypeArguments(typeNode)) {
1222
- const typeName = getTypeReferenceName(typeNode);
1223
- if (!typeName)
1224
- return [];
1225
- if (isExpandableDomAttributeWrapperName(typeName)) {
1226
- return [{ name: typeName, excludedProps: new Set(excludedProps) }];
1227
- }
1228
- if (typeName === 'Omit') {
1229
- const [wrappedType, omittedProps] = typeNode.getTypeArguments();
1230
- if (!wrappedType)
1231
- return [];
1232
- const nextExcludedProps = new Set(excludedProps);
1233
- if (omittedProps) {
1234
- for (const value of getStringLiteralTypeValues(omittedProps)) {
1235
- nextExcludedProps.add(value);
1236
- }
1237
- }
1238
- return collectExpandableDomAttributeWrapperContexts(wrappedType, seen, nextExcludedProps);
1239
- }
1240
- if (typeName === 'Partial' || typeName === 'Readonly' || typeName === 'Required' || typeName === 'NonNullable') {
1241
- const wrappedType = typeNode.getTypeArguments()[0];
1242
- if (!wrappedType)
1243
- return [];
1244
- return collectExpandableDomAttributeWrapperContexts(wrappedType, seen, excludedProps);
1245
- }
1246
- for (const definition of getNodeDefinitions(typeNode.getExpression())) {
1247
- const declaration = definition.getDeclarationNode();
1248
- if (!declaration)
1249
- continue;
1250
- if (Node.isInterfaceDeclaration(declaration)) {
1251
- return declaration
1252
- .getHeritageClauses()
1253
- .flatMap((clause) => clause
1254
- .getTypeNodes()
1255
- .flatMap((heritageTypeNode) => collectExpandableDomAttributeWrapperContexts(heritageTypeNode, seen, excludedProps)));
1256
- }
1257
- if (Node.isTypeAliasDeclaration(declaration)) {
1258
- const aliasedTypeNode = declaration.getTypeNode();
1259
- if (!aliasedTypeNode)
1260
- return [];
1261
- return collectExpandableDomAttributeWrapperContexts(aliasedTypeNode, seen, excludedProps);
1262
- }
1263
- }
1264
- }
1265
- return [];
1266
- }
1267
- function isPureExpandableDomAttributeWrapperType(typeNode) {
1268
- return isPureExpandableDomAttributeWrapperTypeNode(typeNode);
1269
- }
1270
- function isPureExpandableDomAttributeWrapperTypeNode(typeNode, seen = new Set()) {
1271
- if (seen.has(typeNode))
1272
- return false;
1273
- seen.add(typeNode);
1274
- if (Node.isParenthesizedTypeNode(typeNode)) {
1275
- return isPureExpandableDomAttributeWrapperTypeNode(typeNode.getTypeNode(), seen);
1276
- }
1277
- if (Node.isTypeOperatorTypeNode(typeNode)) {
1278
- return isPureExpandableDomAttributeWrapperTypeNode(typeNode.getTypeNode(), seen);
1279
- }
1280
- if (Node.isIntersectionTypeNode(typeNode) || Node.isUnionTypeNode(typeNode)) {
1281
- const childTypes = typeNode.getTypeNodes();
1282
- return (childTypes.length > 0 && childTypes.every((child) => isPureExpandableDomAttributeWrapperTypeNode(child, seen)));
1283
- }
1284
- if (Node.isTypeReference(typeNode)) {
1285
- const typeName = getTypeReferenceName(typeNode);
1286
- if (!typeName)
1287
- return false;
1288
- if (isExpandableDomAttributeWrapperName(typeName)) {
1289
- return true;
1290
- }
1291
- if (typeName === 'Omit' ||
1292
- typeName === 'Partial' ||
1293
- typeName === 'Readonly' ||
1294
- typeName === 'Required' ||
1295
- typeName === 'NonNullable') {
1296
- const wrappedType = typeNode.getTypeArguments()[0];
1297
- return wrappedType ? isPureExpandableDomAttributeWrapperTypeNode(wrappedType, seen) : false;
1298
- }
1299
- if (typeName === 'Pick') {
1300
- // Pick narrows the surface and should not be treated as a transparent wrapper.
1301
- return false;
1302
- }
1303
- for (const definition of getNodeDefinitions(typeNode.getTypeName())) {
1304
- const declaration = definition.getDeclarationNode();
1305
- if (!declaration)
1306
- continue;
1307
- if (Node.isInterfaceDeclaration(declaration)) {
1308
- if (declaration.getMembers().length > 0)
1309
- return false;
1310
- const heritageClauses = declaration.getHeritageClauses();
1311
- return (heritageClauses.length > 0 &&
1312
- heritageClauses.every((clause) => clause
1313
- .getTypeNodes()
1314
- .every((heritageTypeNode) => isPureExpandableDomAttributeWrapperTypeNode(heritageTypeNode, seen))));
1315
- }
1316
- if (Node.isTypeAliasDeclaration(declaration)) {
1317
- const aliasedTypeNode = declaration.getTypeNode();
1318
- if (!aliasedTypeNode)
1319
- return false;
1320
- return isPureExpandableDomAttributeWrapperTypeNode(aliasedTypeNode, seen);
1321
- }
1322
- }
1323
- }
1324
- if (Node.isExpressionWithTypeArguments(typeNode)) {
1325
- const typeName = getTypeReferenceName(typeNode);
1326
- if (!typeName)
1327
- return false;
1328
- if (isExpandableDomAttributeWrapperName(typeName)) {
1329
- return true;
1330
- }
1331
- if (typeName === 'Omit' ||
1332
- typeName === 'Partial' ||
1333
- typeName === 'Readonly' ||
1334
- typeName === 'Required' ||
1335
- typeName === 'NonNullable') {
1336
- const wrappedType = typeNode.getTypeArguments()[0];
1337
- return wrappedType ? isPureExpandableDomAttributeWrapperTypeNode(wrappedType, seen) : false;
1338
- }
1339
- for (const definition of getNodeDefinitions(typeNode.getExpression())) {
1340
- const declaration = definition.getDeclarationNode();
1341
- if (!declaration)
1342
- continue;
1343
- if (Node.isInterfaceDeclaration(declaration)) {
1344
- if (declaration.getMembers().length > 0)
1345
- return false;
1346
- const heritageClauses = declaration.getHeritageClauses();
1347
- return (heritageClauses.length > 0 &&
1348
- heritageClauses.every((clause) => clause
1349
- .getTypeNodes()
1350
- .every((heritageTypeNode) => isPureExpandableDomAttributeWrapperTypeNode(heritageTypeNode, seen))));
1351
- }
1352
- if (Node.isTypeAliasDeclaration(declaration)) {
1353
- const aliasedTypeNode = declaration.getTypeNode();
1354
- if (!aliasedTypeNode)
1355
- return false;
1356
- return isPureExpandableDomAttributeWrapperTypeNode(aliasedTypeNode, seen);
1357
- }
1358
- }
1359
- }
1360
- return false;
1361
- }
1362
- function getSyntheticDomAttributeProps(typeNode) {
1363
- if (!typeNode)
1364
- return [];
1365
- const contexts = collectExpandableDomAttributeWrapperContexts(typeNode);
1366
- if (contexts.length === 0)
1367
- return [];
1368
- const propsByName = new Map();
1369
- for (const context of contexts) {
1370
- for (const prop of getDomAttributeSurface(context.name)) {
1371
- if (context.excludedProps.has(prop.name))
1372
- continue;
1373
- if (propsByName.has(prop.name))
1374
- continue;
1375
- propsByName.set(prop.name, prop);
1376
- }
1377
- }
1378
- return [...propsByName.values()].sort((a, b) => a.name.localeCompare(b.name));
1379
- }
1380
- function getDomAttributeSurface(name, seen = new Set()) {
1381
- if (seen.has(name))
1382
- return [];
1383
- seen.add(name);
1384
- const propsByName = new Map();
1385
- for (const parentName of DOM_ATTRIBUTE_WRAPPER_PARENTS[name] ?? []) {
1386
- for (const prop of getDomAttributeSurface(parentName, seen)) {
1387
- propsByName.set(prop.name, prop);
1388
- }
1389
- }
1390
- for (const prop of DOM_ATTRIBUTE_PROP_SURFACES[name]) {
1391
- propsByName.set(prop.name, prop);
1392
- }
1393
- return [...propsByName.values()];
1394
- }
1395
- function hasSyntheticDomChildren(typeNode) {
1396
- if (!typeNode)
1397
- return false;
1398
- return collectExpandableDomAttributeWrapperContexts(typeNode).some((context) => DOM_ATTRIBUTE_WRAPPERS_WITH_SYNTHETIC_CHILDREN.has(context.name));
1399
- }
1400
- function inferPrimitiveDomPropsFromImplementation(funcNode, propsParam) {
1401
- const candidatePropNames = new Set();
1402
- const propsParamNameNode = propsParam.getNameNode();
1403
- if (Node.isIdentifier(propsParamNameNode)) {
1404
- candidatePropNames.add(propsParam.getName());
1405
- }
1406
- if (Node.isObjectBindingPattern(propsParamNameNode)) {
1407
- for (const element of propsParamNameNode.getElements()) {
1408
- if (!element.getDotDotDotToken())
1409
- continue;
1410
- const restNameNode = element.getNameNode();
1411
- if (Node.isIdentifier(restNameNode)) {
1412
- candidatePropNames.add(restNameNode.getText());
1413
- }
1414
- }
1415
- }
1416
- for (const variableDeclaration of funcNode.getDescendantsOfKind(SyntaxKind.VariableDeclaration)) {
1417
- const initializer = variableDeclaration.getInitializer();
1418
- if (!initializer || !Node.isIdentifier(initializer) || !candidatePropNames.has(initializer.getText())) {
1419
- continue;
1420
- }
1421
- const nameNode = variableDeclaration.getNameNode();
1422
- if (!Node.isObjectBindingPattern(nameNode))
1423
- continue;
1424
- for (const element of nameNode.getElements()) {
1425
- if (!element.getDotDotDotToken())
1426
- continue;
1427
- const restNameNode = element.getNameNode();
1428
- if (Node.isIdentifier(restNameNode)) {
1429
- candidatePropNames.add(restNameNode.getText());
1430
- }
1431
- }
1432
- }
1433
- if (candidatePropNames.size === 0)
1434
- return [];
1435
- const inferredSurfaces = new Set();
1436
- const jsxElements = [
1437
- ...funcNode.getDescendantsOfKind(SyntaxKind.JsxSelfClosingElement),
1438
- ...funcNode.getDescendantsOfKind(SyntaxKind.JsxOpeningElement),
1439
- ];
1440
- for (const jsxElement of jsxElements) {
1441
- const tagNameNode = jsxElement.getTagNameNode();
1442
- const domSurface = JSX_PRIMITIVE_DOM_ATTRIBUTE_SURFACES[tagNameNode.getText()] ?? getBoundedImportedJsxDomSurface(tagNameNode);
1443
- if (!domSurface)
1444
- continue;
1445
- const hasForwardedPropsSpread = jsxElement
1446
- .getAttributes()
1447
- .some((attribute) => Node.isJsxSpreadAttribute(attribute) &&
1448
- Node.isIdentifier(attribute.getExpression()) &&
1449
- candidatePropNames.has(attribute.getExpression().getText()));
1450
- if (hasForwardedPropsSpread) {
1451
- inferredSurfaces.add(domSurface);
1452
- }
1453
- }
1454
- const propsByName = new Map();
1455
- for (const surface of inferredSurfaces) {
1456
- for (const prop of getDomAttributeSurface(surface)) {
1457
- propsByName.set(prop.name, prop);
1458
- }
1459
- }
1460
- return [...propsByName.values()].sort((a, b) => a.name.localeCompare(b.name));
1461
- }
1462
- function extractPickedPropsFromTypeNode(typeNode, slotNames, excludedProps = new Set()) {
1463
- if (!Node.isTypeReference(typeNode) && !Node.isExpressionWithTypeArguments(typeNode)) {
1464
- return [];
1465
- }
1466
- const [sourceTypeNode, pickedKeysNode] = typeNode.getTypeArguments();
1467
- if (!sourceTypeNode || !pickedKeysNode)
1468
- return [];
1469
- const sourcePropsByName = new Map();
1470
- for (const prop of getSyntheticDomAttributeProps(sourceTypeNode)) {
1471
- sourcePropsByName.set(prop.name, prop);
1472
- }
1473
- for (const prop of extractPropsFromTypeNode(sourceTypeNode, slotNames, undefined, excludedProps).props) {
1474
- if (!sourcePropsByName.has(prop.name)) {
1475
- sourcePropsByName.set(prop.name, prop);
1476
- }
1477
- }
1478
- const pickedKeys = getStringLiteralTypeValues(pickedKeysNode);
1479
- const props = [];
1480
- for (const name of pickedKeys) {
1481
- if (slotNames.has(name))
1482
- continue;
1483
- if (excludedProps.has(name))
1484
- continue;
1485
- const pickedProp = sourcePropsByName.get(name);
1486
- if (pickedProp) {
1487
- props.push(pickedProp);
1488
- }
1489
- }
1490
- return props.sort((a, b) => a.name.localeCompare(b.name));
1491
- }
1492
- function normalizePropsTypeNode(typeNode) {
1493
- if (!Node.isTypeReference(typeNode)) {
1494
- return { typeNode, hasWrappedChildren: false, suppressProps: false };
1495
- }
1496
- const typeName = getTypeReferenceName(typeNode);
1497
- if (!typeName) {
1498
- return { typeNode, hasWrappedChildren: false, suppressProps: false };
1499
- }
1500
- if (PROP_WRAPPER_TYPE_NAMES.has(typeName)) {
1501
- const firstTypeArg = typeNode.getTypeArguments()[0];
1502
- if (!firstTypeArg) {
1503
- return { typeNode, hasWrappedChildren: false, suppressProps: false };
1504
- }
1505
- return normalizePropsTypeNode(firstTypeArg);
1506
- }
1507
- if (CHILD_WRAPPER_TYPE_NAMES.has(typeName)) {
1508
- const firstTypeArg = typeNode.getTypeArguments()[0];
1509
- if (!firstTypeArg) {
1510
- return { typeNode, hasWrappedChildren: true, suppressProps: false };
1511
- }
1512
- if (isPureExpandableDomAttributeWrapperType(firstTypeArg)) {
1513
- return {
1514
- typeNode: firstTypeArg,
1515
- hasWrappedChildren: true,
1516
- suppressProps: true,
1517
- };
1518
- }
1519
- const normalized = normalizePropsTypeNode(firstTypeArg);
1520
- return {
1521
- ...normalized,
1522
- hasWrappedChildren: true,
1523
- };
1524
- }
1525
- return { typeNode, hasWrappedChildren: false, suppressProps: false };
1526
- }
1527
- function resolveForwardRefGenericPropsTypeNode(param) {
1528
- const parent = param.getParent();
1529
- if (!Node.isArrowFunction(parent) && !Node.isFunctionExpression(parent)) {
1530
- return undefined;
1531
- }
1532
- const callExpression = parent.getParent();
1533
- if (!Node.isCallExpression(callExpression)) {
1534
- return undefined;
1535
- }
1536
- const expressionText = callExpression.getExpression().getText();
1537
- if (!/(^|\.)forwardRef$/.test(expressionText)) {
1538
- return undefined;
1539
- }
1540
- const [, propsTypeNode] = callExpression.getTypeArguments();
1541
- return propsTypeNode;
1542
- }
1543
- const FC_TYPE_NAMES = new Set(['FC', 'FunctionComponent', 'VFC', 'VoidFunctionComponent']);
1544
- function resolveFCGenericPropsTypeNode(param) {
1545
- const funcNode = param.getParent();
1546
- if (!Node.isArrowFunction(funcNode) && !Node.isFunctionExpression(funcNode)) {
1547
- return undefined;
1548
- }
1549
- const varDecl = funcNode.getParent();
1550
- if (!Node.isVariableDeclaration(varDecl)) {
1551
- return undefined;
1552
- }
1553
- const typeNode = varDecl.getTypeNode();
1554
- if (!typeNode || !Node.isTypeReference(typeNode)) {
1555
- return undefined;
1556
- }
1557
- const typeName = typeNode.getTypeName().getText().split('.').pop();
1558
- if (!typeName || !FC_TYPE_NAMES.has(typeName)) {
1559
- return undefined;
1560
- }
1561
- const typeArgs = typeNode.getTypeArguments();
1562
- return typeArgs[0];
1563
- }
1564
- function resolvePropsType(param) {
1565
- const typeNode = param.getTypeNode();
1566
- const fcGenericPropsTypeNode = resolveFCGenericPropsTypeNode(param);
1567
- const fallbackTypeNode = resolveForwardRefGenericPropsTypeNode(param) ?? fcGenericPropsTypeNode;
1568
- const effectiveTypeNode = typeNode ?? fallbackTypeNode;
1569
- if (!effectiveTypeNode) {
1570
- return {
1571
- type: param.getType(),
1572
- typeNode: param.getTypeNode() ?? param,
1573
- hasWrappedChildren: false,
1574
- suppressProps: false,
1575
- };
1576
- }
1577
- const normalized = normalizePropsTypeNode(effectiveTypeNode);
1578
- return {
1579
- type: normalized.typeNode.getType(),
1580
- typeNode: normalized.typeNode,
1581
- hasWrappedChildren: normalized.hasWrappedChildren || fcGenericPropsTypeNode !== undefined,
1582
- suppressProps: normalized.suppressProps,
1583
- };
1584
- }
1585
- function hasImplementationChildrenHint(funcNode, param) {
1586
- const nameNode = param.getNameNode();
1587
- const body = funcNode.getBody();
1588
- if (!body)
1589
- return false;
1590
- if (Node.isObjectBindingPattern(nameNode)) {
1591
- const restBindingNames = new Set();
1592
- for (const element of nameNode.getElements()) {
1593
- if (element.getNameNode().getText() === 'children') {
1594
- return true;
1595
- }
1596
- if (element.getDotDotDotToken()) {
1597
- restBindingNames.add(element.getNameNode().getText());
1598
- }
1599
- }
1600
- if (restBindingNames.size === 0)
1601
- return false;
1602
- return body.getDescendantsOfKind(SyntaxKind.JsxSpreadAttribute).some((attr) => {
1603
- const expression = attr.getExpression();
1604
- if (!expression || !Node.isIdentifier(expression))
1605
- return false;
1606
- if (!restBindingNames.has(expression.getText()))
1607
- return false;
1608
- const openingElement = attr.getFirstAncestorByKind(SyntaxKind.JsxOpeningElement);
1609
- const selfClosingElement = attr.getFirstAncestorByKind(SyntaxKind.JsxSelfClosingElement);
1610
- const tagName = openingElement?.getTagNameNode().getText() ?? selfClosingElement?.getTagNameNode().getText();
1611
- return tagName ? /^[A-Z]/.test(tagName) : false;
1612
- });
1613
- }
1614
- if (!Node.isIdentifier(nameNode))
1615
- return false;
1616
- const paramName = nameNode.getText();
1617
- if (body
1618
- .getDescendantsOfKind(SyntaxKind.PropertyAccessExpression)
1619
- .some((expr) => expr.getExpression().getText() === paramName && expr.getName() === 'children')) {
1620
- return true;
1621
- }
1622
- return body.getDescendantsOfKind(SyntaxKind.JsxSpreadAttribute).some((attr) => {
1623
- const expression = attr.getExpression();
1624
- if (!expression || !Node.isIdentifier(expression) || expression.getText() !== paramName)
1625
- return false;
1626
- const openingElement = attr.getFirstAncestorByKind(SyntaxKind.JsxOpeningElement);
1627
- const selfClosingElement = attr.getFirstAncestorByKind(SyntaxKind.JsxSelfClosingElement);
1628
- const tagName = openingElement?.getTagNameNode().getText() ?? selfClosingElement?.getTagNameNode().getText();
1629
- return tagName ? /^[A-Z]/.test(tagName) : false;
1630
- });
1631
- }
1632
- export async function extractReactComponents(filePaths) {
1633
- const componentFiles = filePaths.filter((f) => /\.[jt]sx$/.test(f));
1634
- if (componentFiles.length === 0) {
1635
- return { components: [], warnings: [] };
1636
- }
1637
- const projectFiles = filePaths.filter((f) => /\.[jt]sx?$/.test(f) && !f.endsWith('.d.ts'));
1638
- const project = new Project({
1639
- compilerOptions: {
1640
- jsx: 1, // JsxEmit.Preserve
1641
- target: 99, // ScriptTarget.ESNext
1642
- module: 99, // ModuleKind.ESNext
1643
- moduleResolution: 100, // ModuleResolutionKind.Bundler
1644
- skipLibCheck: true,
1645
- allowJs: true,
1646
- },
1647
- skipAddingFilesFromTsConfig: true,
1648
- });
1649
- for (const filePath of projectFiles) {
1650
- project.addSourceFileAtPath(filePath);
1651
- }
1652
- const warnings = [];
1653
- const components = [];
1654
- for (const filePath of componentFiles) {
1655
- try {
1656
- const sourceFile = project.getSourceFile(filePath);
1657
- if (!sourceFile)
1658
- continue;
1659
- if (isStencilFile(sourceFile))
1660
- continue;
1661
- const fileExports = [...sourceFile.getExportedDeclarations().keys()];
1662
- const isNext = isNextJsComponent(sourceFile.getFilePath(), fileExports);
1663
- const extracted = extractFromSourceFile(sourceFile, isNext);
1664
- components.push(...extracted);
1665
- }
1666
- catch (e) {
1667
- warnings.push(`Failed to extract from ${filePath}: ${e instanceof Error ? e.message : String(e)}`);
1668
- }
1669
- }
1670
- // Post-pass: resolve $allowedComponents for slots whose type text referenced
1671
- // ReactElement<XProps>. Build a props-type-name → component-name map from the
1672
- // full run first so cross-file references resolve.
1673
- const propsToComponent = new Map();
1674
- const componentNames = new Set();
1675
- for (const c of components) {
1676
- componentNames.add(c.name);
1677
- if (c._propsTypeName)
1678
- propsToComponent.set(c._propsTypeName, c.name);
1679
- }
1680
- for (const c of components) {
1681
- for (const slot of c.slots) {
1682
- const found = new Set();
1683
- if (slot._rawTypeText) {
1684
- for (const n of extractAllowedComponentsFromTypeText(slot._rawTypeText, {
1685
- propsToComponent,
1686
- componentNames,
1687
- })) {
1688
- found.add(n);
1689
- }
1690
- }
1691
- if (slot._rawJsdoc) {
1692
- for (const n of extractAllowedComponentsFromJsdoc(slot._rawJsdoc, componentNames)) {
1693
- found.add(n);
1694
- }
1695
- }
1696
- delete slot._rawTypeText;
1697
- delete slot._rawJsdoc;
1698
- if (found.size > 0) {
1699
- slot.allowedComponents = [...found].sort();
1700
- }
1701
- }
1702
- delete c._propsTypeName;
1703
- }
1704
- return {
1705
- components: components.sort((a, b) => a.name.localeCompare(b.name)),
1706
- warnings,
1707
- };
1708
- }
1709
- function extractFromSourceFile(sourceFile, isNext) {
1710
- const components = [];
1711
- const exported = sourceFile.getExportedDeclarations();
1712
- const usesCreateContext = sourceFileUsesCreateContext(sourceFile);
1713
- for (const [exportKey, declarations] of exported) {
1714
- let name = exportKey;
1715
- if (exportKey === 'default') {
1716
- const decl = declarations[0];
1717
- let declName;
1718
- if (Node.isFunctionDeclaration(decl)) {
1719
- declName = decl.getName();
1720
- }
1721
- else if (Node.isVariableDeclaration(decl)) {
1722
- declName = decl.getName();
1723
- }
1724
- if (!declName || !/^[A-Z]/.test(declName))
1725
- continue;
1726
- if (exported.has(declName))
1727
- continue;
1728
- name = declName;
1729
- }
1730
- if (!/^[A-Z]/.test(name))
1731
- continue;
1732
- if (name.startsWith('use'))
1733
- continue;
1734
- const funcNode = resolveBestFunctionNode(declarations);
1735
- if (!funcNode)
1736
- continue;
1737
- if (funcNode.getSourceFile().getFilePath() !== sourceFile.getFilePath())
1738
- continue;
1739
- const params = funcNode.getParameters();
1740
- if (params.length === 0) {
1741
- components.push({
1742
- name,
1743
- source: sourceFile.getFilePath(),
1744
- sourcePath: sourceFile.getFilePath(),
1745
- framework: isNext ? 'next' : 'react',
1746
- props: [],
1747
- slots: [],
1748
- ...(usesCreateContext && { usesCreateContext: true }),
1749
- });
1750
- continue;
1751
- }
1752
- const resolvedPropsType = resolvePropsType(params[0]);
1753
- const firstParamType = resolvedPropsType.type;
1754
- const firstParamTypeNode = resolvedPropsType.typeNode;
1755
- const renderPropSlotNames = resolvedPropsType.suppressProps
1756
- ? new Set()
1757
- : collectRenderPropSlotNames(firstParamType);
1758
- const { props, hasChildren } = resolvedPropsType.suppressProps
1759
- ? { props: [], hasChildren: false }
1760
- : extractPropsFromType(firstParamType, renderPropSlotNames, firstParamTypeNode);
1761
- const hasMeaningfulExtractedProps = props.some((prop) => !prop.name.startsWith('__scope'));
1762
- const defaults = extractDefaultValues(funcNode);
1763
- const hasImplementationChildren = hasImplementationChildrenHint(funcNode, params[0]);
1764
- const slots = extractSlots(firstParamType, hasChildren ||
1765
- resolvedPropsType.hasWrappedChildren ||
1766
- hasSyntheticDomChildren(resolvedPropsType.typeNode) ||
1767
- hasImplementationChildren);
1768
- const syntheticDomProps = resolvedPropsType.suppressProps
1769
- ? []
1770
- : getSyntheticDomAttributeProps(resolvedPropsType.typeNode);
1771
- const implementationPrimitiveDomProps = !resolvedPropsType.suppressProps && !hasMeaningfulExtractedProps && syntheticDomProps.length === 0
1772
- ? inferPrimitiveDomPropsFromImplementation(funcNode, params[0])
1773
- : [];
1774
- const shouldTryBindingFallback = !resolvedPropsType.suppressProps &&
1775
- (containsImportedOmitWrappedCustomProps(firstParamTypeNode) ||
1776
- containsAnyPickType(firstParamTypeNode) ||
1777
- (!hasMeaningfulExtractedProps && Node.isObjectBindingPattern(params[0].getNameNode())));
1778
- const bindingFallbackProps = shouldTryBindingFallback
1779
- ? extractDestructuredBindingFallbackProps(funcNode, params[0], new Set(props.map((prop) => prop.name)), renderPropSlotNames)
1780
- : [];
1781
- const mergedPropsByName = new Map();
1782
- for (const prop of props) {
1783
- mergedPropsByName.set(prop.name, prop);
1784
- }
1785
- for (const prop of syntheticDomProps) {
1786
- if (!mergedPropsByName.has(prop.name)) {
1787
- mergedPropsByName.set(prop.name, prop);
1788
- }
1789
- }
1790
- for (const prop of implementationPrimitiveDomProps) {
1791
- if (!mergedPropsByName.has(prop.name)) {
1792
- mergedPropsByName.set(prop.name, prop);
1793
- }
1794
- }
1795
- for (const prop of bindingFallbackProps) {
1796
- if (!mergedPropsByName.has(prop.name)) {
1797
- mergedPropsByName.set(prop.name, prop);
1798
- }
1799
- }
1800
- const resolvedProps = [...mergedPropsByName.values()];
1801
- const noUsefulTypes = resolvedProps.length === 0 || resolvedProps.every((p) => p.type === 'any');
1802
- const finalProps = noUsefulTypes ? (extractPropTypes(sourceFile, name) ?? resolvedProps) : resolvedProps;
1803
- const propsWithDefaults = finalProps.map((p) => {
1804
- if (!defaults.has(p.name))
1805
- return p;
1806
- const defaultValue = defaults.get(p.name);
1807
- return {
1808
- ...p,
1809
- required: false,
1810
- defaultValue,
1811
- };
1812
- });
1813
- const filteredProps = filterImplementationOnlyAliasProps(propsWithDefaults, funcNode);
1814
- // Second pass: expand ReactNode-typed and ReactElement<XProps>-typed props into slots
1815
- const existingSlotNames = new Set(slots.map((s) => s.name));
1816
- const expandedSlots = [];
1817
- const propsAfterSlotExpansion = filteredProps.filter((prop) => {
1818
- if (existingSlotNames.has(prop.name))
1819
- return true; // already handled
1820
- const isElementSlot = isReactElementGenericSlotType(prop.type);
1821
- if (shouldBeSlot(prop.name, prop.type) || isElementSlot) {
1822
- expandedSlots.push({
1823
- name: prop.name,
1824
- isDefault: false,
1825
- ...(isElementSlot ? { _rawTypeText: prop.type } : {}),
1826
- });
1827
- return false;
1828
- }
1829
- return true;
1830
- });
1831
- const finalSlots = [...slots, ...expandedSlots];
1832
- // Capture the props-type-name so the run-level post-pass can build a
1833
- // props-type → component-name map for $allowedComponents resolution.
1834
- const propsTypeName = firstParamTypeNode?.getText?.().trim();
1835
- const propsTypeNameCapture = propsTypeName && /^[A-Za-z_$][\w$]*$/.test(propsTypeName) ? propsTypeName : undefined;
1836
- components.push({
1837
- name,
1838
- source: sourceFile.getFilePath(),
1839
- sourcePath: sourceFile.getFilePath(),
1840
- framework: isNext ? 'next' : 'react',
1841
- props: propsAfterSlotExpansion,
1842
- slots: finalSlots,
1843
- ...(usesCreateContext && { usesCreateContext: true }),
1844
- ...(propsTypeNameCapture ? { _propsTypeName: propsTypeNameCapture } : {}),
1845
- });
1846
- }
1847
- return components;
1848
- }
1849
- const PROP_TYPES_MAP = {
1850
- string: 'string',
1851
- number: 'number',
1852
- bool: 'boolean',
1853
- func: 'function',
1854
- node: 'ReactNode',
1855
- element: 'ReactElement',
1856
- any: 'any',
1857
- array: 'any[]',
1858
- object: 'object',
1859
- symbol: 'symbol',
1860
- };
1861
- function extractPropTypes(sourceFile, componentName) {
1862
- const props = [];
1863
- for (const statement of sourceFile.getStatements()) {
1864
- if (!Node.isExpressionStatement(statement))
1865
- continue;
1866
- const expr = statement.getExpression();
1867
- if (!Node.isBinaryExpression(expr))
1868
- continue;
1869
- const left = expr.getLeft().getText();
1870
- if (left !== `${componentName}.propTypes`)
1871
- continue;
1872
- const right = expr.getRight();
1873
- if (!Node.isObjectLiteralExpression(right))
1874
- continue;
1875
- for (const property of right.getProperties()) {
1876
- if (!Node.isPropertyAssignment(property))
1877
- continue;
1878
- const propName = property.getName();
1879
- const initText = property.getInitializer()?.getText() ?? '';
1880
- let type = 'any';
1881
- let required = false;
1882
- let allowedValues;
1883
- if (initText.includes('.isRequired')) {
1884
- required = true;
1885
- }
1886
- const oneOfMatch = initText.match(/PropTypes\.oneOf\(\[([^\]]+)\]\)/);
1887
- if (oneOfMatch) {
1888
- allowedValues = oneOfMatch[1]
1889
- .split(',')
1890
- .map((v) => v.trim().replace(/^['"]|['"]$/g, ''))
1891
- .filter(Boolean)
1892
- .sort();
1893
- }
1894
- for (const [ptKey, tsType] of Object.entries(PROP_TYPES_MAP)) {
1895
- if (initText.includes(`PropTypes.${ptKey}`)) {
1896
- type = tsType;
1897
- break;
1898
- }
1899
- }
1900
- props.push({
1901
- name: propName,
1902
- type,
1903
- required,
1904
- ...(allowedValues && { allowedValues }),
1905
- sourceStartLine: property.getStartLineNumber(),
1906
- sourceEndLine: property.getEndLineNumber(),
1907
- });
1908
- }
1909
- return props.sort((a, b) => a.name.localeCompare(b.name));
1910
- }
1911
- return undefined;
1912
- }
1913
- function resolveFunctionNode(decl) {
1914
- if (Node.isFunctionDeclaration(decl))
1915
- return decl;
1916
- if (Node.isArrowFunction(decl))
1917
- return decl;
1918
- if (Node.isFunctionExpression(decl))
1919
- return decl;
1920
- if (Node.isVariableDeclaration(decl)) {
1921
- const init = decl.getInitializer();
1922
- if (init && Node.isArrowFunction(init))
1923
- return init;
1924
- if (init && Node.isFunctionExpression(init))
1925
- return init;
1926
- if (init && Node.isCallExpression(init))
1927
- return resolveFunctionFromCallExpression(init, new Set());
1928
- }
1929
- return undefined;
1930
- }
1931
- function resolveBestFunctionNode(declarations) {
1932
- const candidates = declarations
1933
- .map((decl) => resolveFunctionNode(decl))
1934
- .filter((candidate) => candidate !== undefined);
1935
- const implementation = candidates.find((candidate) => !Node.isFunctionDeclaration(candidate) || candidate.getBody() !== undefined);
1936
- return implementation ?? candidates[0];
1937
- }
1938
- function resolveForwardRefFunction(callExpr) {
1939
- if (!Node.isCallExpression(callExpr))
1940
- return undefined;
1941
- const expressionText = callExpr.getExpression().getText();
1942
- if (!/(^|\.)forwardRef$/.test(expressionText))
1943
- return undefined;
1944
- const firstArg = callExpr.getArguments()[0];
1945
- if (!firstArg)
1946
- return undefined;
1947
- if (Node.isArrowFunction(firstArg))
1948
- return firstArg;
1949
- if (Node.isFunctionExpression(firstArg))
1950
- return firstArg;
1951
- if (Node.isIdentifier(firstArg)) {
1952
- const declarations = getNodeDefinitions(firstArg).flatMap((definition) => {
1953
- const declarationNode = definition.getDeclarationNode();
1954
- return declarationNode ? [declarationNode] : [];
1955
- });
1956
- for (const declaration of declarations) {
1957
- const resolved = resolveFunctionNode(declaration);
1958
- if (resolved)
1959
- return resolved;
1960
- }
1961
- }
1962
- return undefined;
1963
- }
1964
- function resolveFunctionFromCallExpression(callExpr, seen) {
1965
- return resolveForwardRefFunction(callExpr) ?? resolveReturnedFunctionFromFactoryCall(callExpr, seen);
1966
- }
1967
- function resolveReturnedFunctionFromFactoryCall(callExpr, seen) {
1968
- const expression = callExpr.getExpression();
1969
- if (!Node.isIdentifier(expression))
1970
- return undefined;
1971
- for (const definition of getNodeDefinitions(expression)) {
1972
- const declaration = definition.getDeclarationNode();
1973
- if (!declaration)
1974
- continue;
1975
- const resolved = resolveReturnedFunctionFromDeclaration(declaration, seen);
1976
- if (resolved)
1977
- return resolved;
1978
- }
1979
- return undefined;
1980
- }
1981
- function resolveReturnedFunctionFromDeclaration(node, seen) {
1982
- if (seen.has(node))
1983
- return undefined;
1984
- seen.add(node);
1985
- if (Node.isVariableDeclaration(node)) {
1986
- const initializer = node.getInitializer();
1987
- if (!initializer)
1988
- return undefined;
1989
- if (Node.isArrowFunction(initializer) || Node.isFunctionExpression(initializer)) {
1990
- return resolveReturnedFunctionFromCallable(initializer, seen);
1991
- }
1992
- if (Node.isCallExpression(initializer)) {
1993
- return resolveFunctionFromCallExpression(initializer, seen);
1994
- }
1995
- return undefined;
1996
- }
1997
- if (Node.isFunctionDeclaration(node) || Node.isFunctionExpression(node) || Node.isArrowFunction(node)) {
1998
- return resolveReturnedFunctionFromCallable(node, seen);
1999
- }
2000
- return undefined;
2001
- }
2002
- function resolveReturnedFunctionFromCallable(fn, seen) {
2003
- const body = fn.getBody();
2004
- if (!body)
2005
- return undefined;
2006
- if (!Node.isBlock(body)) {
2007
- return resolveReturnedFunctionFromExpression(body, seen);
2008
- }
2009
- for (const statement of body.getStatements()) {
2010
- if (!Node.isReturnStatement(statement))
2011
- continue;
2012
- const expression = statement.getExpression();
2013
- if (!expression)
2014
- continue;
2015
- const resolved = resolveReturnedFunctionFromExpression(expression, seen);
2016
- if (resolved)
2017
- return resolved;
2018
- }
2019
- return undefined;
2020
- }
2021
- function resolveReturnedFunctionFromExpression(node, seen) {
2022
- if (Node.isArrowFunction(node) || Node.isFunctionExpression(node))
2023
- return node;
2024
- if (Node.isParenthesizedExpression(node)) {
2025
- return resolveReturnedFunctionFromExpression(node.getExpression(), seen);
2026
- }
2027
- if (Node.isCallExpression(node)) {
2028
- return resolveFunctionFromCallExpression(node, seen);
2029
- }
2030
- if (Node.isIdentifier(node)) {
2031
- for (const definition of getNodeDefinitions(node)) {
2032
- const declaration = definition.getDeclarationNode();
2033
- if (!declaration)
2034
- continue;
2035
- const resolved = resolveReturnedFunctionFromDeclaration(declaration, seen);
2036
- if (resolved)
2037
- return resolved;
2038
- }
2039
- }
2040
- return undefined;
2041
- }