@contentful/experience-design-system-extraction 2.33.1-dev-build-3d84cf3.0 → 2.33.1-dev-build-c4ed223.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.
@@ -1,7 +1,8 @@
1
- import { Project, Node, SyntaxKind, } from 'ts-morph';
2
- import { extractAllowedValues, getNodeDefinitions, getTypeReferenceName, getTypeTargetDeclarations, getValueTargetDeclarations, getJsxTagNameNode, isIntrinsicJsxElement, } from './tsx-shared.js';
1
+ import { Node, SyntaxKind, } from 'ts-morph';
2
+ import { extractAllowedValues, getNodeDefinitions, getTypeReferenceName, getTypeTargetDeclarations, getTsxExtractionContext, getValueTargetDeclarations, getJsxTagNameNode, isIntrinsicJsxElement, resolveDefaultExportName, } from './tsx-shared.js';
3
3
  import { shouldBeSlot } from './slot-detection.js';
4
4
  import { extractAllowedComponentsFromTypeText, extractAllowedComponentsFromJsdoc } from './slot-allowed-components.js';
5
+ import { getSourceLineMetadata } from './source-line-metadata.js';
5
6
  import { collectTypePredicateComponentReferences, collectRuntimeTypeCheckComponentReferences, collectRenderedComponentReferences, } from './structural-slot-evidence.js';
6
7
  const REACT_ELEMENT_GENERIC_TEST = /(?:React\.)?ReactElement\s*<\s*[A-Za-z_$][\w$.]*/;
7
8
  function isReactElementGenericSlotType(typeText) {
@@ -26,6 +27,23 @@ const EXPANDABLE_DOM_ATTRIBUTE_TYPE_NAMES = new Set([
26
27
  'TextareaHTMLAttributes',
27
28
  'TdHTMLAttributes',
28
29
  ]);
30
+ function createTextControlSurface(onChangeType, specificProps) {
31
+ return [
32
+ { name: 'autoComplete', type: 'string', required: false },
33
+ ...specificProps,
34
+ { name: 'disabled', type: 'boolean', required: false },
35
+ { name: 'name', type: 'string', required: false },
36
+ { name: 'onChange', type: onChangeType, required: false },
37
+ { name: 'placeholder', type: 'string', required: false },
38
+ { name: 'readOnly', type: 'boolean', required: false },
39
+ { name: 'required', type: 'boolean', required: false },
40
+ {
41
+ name: 'value',
42
+ type: 'string | number | readonly string[]',
43
+ required: false,
44
+ },
45
+ ];
46
+ }
29
47
  const DOM_ATTRIBUTE_PROP_SURFACES = {
30
48
  HTMLProps: [],
31
49
  HTMLAttributes: [
@@ -91,30 +109,14 @@ const DOM_ATTRIBUTE_PROP_SURFACES = {
91
109
  required: false,
92
110
  },
93
111
  ],
94
- InputHTMLAttributes: [
95
- { name: 'autoComplete', type: 'string', required: false },
112
+ InputHTMLAttributes: createTextControlSurface('ChangeEventHandler<HTMLInputElement>', [
96
113
  { name: 'checked', type: 'boolean', required: false },
97
- { name: 'disabled', type: 'boolean', required: false },
98
114
  { name: 'max', type: 'number | string', required: false },
99
115
  { name: 'maxLength', type: 'number', required: false },
100
116
  { name: 'min', type: 'number | string', required: false },
101
117
  { name: 'minLength', type: 'number', required: false },
102
- { name: 'name', type: 'string', required: false },
103
- {
104
- name: 'onChange',
105
- type: 'ChangeEventHandler<HTMLInputElement>',
106
- required: false,
107
- },
108
- { name: 'placeholder', type: 'string', required: false },
109
- { name: 'readOnly', type: 'boolean', required: false },
110
- { name: 'required', type: 'boolean', required: false },
111
118
  { name: 'type', type: 'string', required: false },
112
- {
113
- name: 'value',
114
- type: 'string | number | readonly string[]',
115
- required: false,
116
- },
117
- ],
119
+ ]),
118
120
  FieldsetHTMLAttributes: [
119
121
  { name: 'disabled', type: 'boolean', required: false },
120
122
  { name: 'form', type: 'string', required: false },
@@ -155,29 +157,11 @@ const DOM_ATTRIBUTE_PROP_SURFACES = {
155
157
  { name: 'width', type: 'number | string', required: false },
156
158
  ],
157
159
  SVGProps: [],
158
- TextareaHTMLAttributes: [
159
- { name: 'autoComplete', type: 'string', required: false },
160
+ TextareaHTMLAttributes: createTextControlSurface('ChangeEventHandler<HTMLTextAreaElement>', [
160
161
  { name: 'cols', type: 'number', required: false },
161
- { name: 'disabled', type: 'boolean', required: false },
162
- { name: 'maxLength', type: 'number', required: false },
163
- { name: 'minLength', type: 'number', required: false },
164
- { name: 'name', type: 'string', required: false },
165
- {
166
- name: 'onChange',
167
- type: 'ChangeEventHandler<HTMLTextAreaElement>',
168
- required: false,
169
- },
170
- { name: 'placeholder', type: 'string', required: false },
171
- { name: 'readOnly', type: 'boolean', required: false },
172
- { name: 'required', type: 'boolean', required: false },
173
162
  { name: 'rows', type: 'number', required: false },
174
- {
175
- name: 'value',
176
- type: 'string | number | readonly string[]',
177
- required: false,
178
- },
179
163
  { name: 'wrap', type: 'string', required: false },
180
- ],
164
+ ]),
181
165
  TdHTMLAttributes: [
182
166
  {
183
167
  name: 'align',
@@ -489,12 +473,7 @@ function extractPropsFromTypeSymbols(type, slotNames, suppressNeverChildrenSlot
489
473
  type: typeText,
490
474
  required,
491
475
  ...(allowedValues && { allowedValues }),
492
- ...(typeof declaration.getStartLineNumber === 'function'
493
- ? {
494
- sourceStartLine: declaration.getStartLineNumber(),
495
- sourceEndLine: declaration.getEndLineNumber(),
496
- }
497
- : {}),
476
+ ...getSourceLineMetadata(declaration),
498
477
  });
499
478
  }
500
479
  return {
@@ -549,12 +528,7 @@ function extractPropsFromInterfaceDeclaration(declaration, slotNames, seen, excl
549
528
  type: typeText,
550
529
  required,
551
530
  ...(allowedValues && { allowedValues }),
552
- ...(typeof decl.getStartLineNumber === 'function'
553
- ? {
554
- sourceStartLine: decl.getStartLineNumber(),
555
- sourceEndLine: decl.getEndLineNumber(),
556
- }
557
- : {}),
531
+ ...getSourceLineMetadata(decl),
558
532
  });
559
533
  return acc;
560
534
  }, { props: [], hasChildren: false })
@@ -617,6 +591,19 @@ function getMappedOmitEquivalentArgs(typeNode) {
617
591
  }
618
592
  return undefined;
619
593
  }
594
+ function getTypeReferenceTargetNode(typeNode) {
595
+ if (Node.isTypeReference(typeNode))
596
+ return typeNode.getTypeName();
597
+ if (Node.isExpressionWithTypeArguments(typeNode))
598
+ return typeNode.getExpression();
599
+ return undefined;
600
+ }
601
+ function getTypeReferenceArguments(typeNode) {
602
+ if (Node.isTypeReference(typeNode) || Node.isExpressionWithTypeArguments(typeNode)) {
603
+ return typeNode.getTypeArguments();
604
+ }
605
+ return [];
606
+ }
620
607
  function extractPropsFromTypeNode(typeNode, slotNames, seen = new Set(), excludedProps = new Set(), allowImportedOmitWorkspaceFallback = false, suppressNeverChildrenSlot = false) {
621
608
  if (seen.has(typeNode))
622
609
  return { props: [], hasChildren: false };
@@ -642,12 +629,14 @@ function extractPropsFromTypeNode(typeNode, slotNames, seen = new Set(), exclude
642
629
  if (Node.isInterfaceDeclaration(typeNode)) {
643
630
  return extractPropsFromInterfaceDeclaration(typeNode, slotNames, seen, excludedProps, allowImportedOmitWorkspaceFallback, suppressNeverChildrenSlot);
644
631
  }
645
- if (Node.isTypeReference(typeNode)) {
632
+ const typeReferenceTarget = getTypeReferenceTargetNode(typeNode);
633
+ if (typeReferenceTarget) {
646
634
  const typeName = getTypeReferenceName(typeNode);
647
635
  if (!typeName)
648
636
  return { props: [], hasChildren: false };
637
+ const typeArguments = getTypeReferenceArguments(typeNode);
649
638
  if (typeName === 'PropsWithChildren') {
650
- const wrappedType = typeNode.getTypeArguments()[0];
639
+ const wrappedType = typeArguments[0];
651
640
  if (!wrappedType)
652
641
  return { props: [], hasChildren: true };
653
642
  const extracted = extractPropsFromTypeNode(wrappedType, slotNames, seen, excludedProps, allowImportedOmitWorkspaceFallback, suppressNeverChildrenSlot);
@@ -666,12 +655,12 @@ function extractPropsFromTypeNode(typeNode, slotNames, seen = new Set(), exclude
666
655
  typeName === 'Readonly' ||
667
656
  typeName === 'Required' ||
668
657
  typeName === 'NonNullable') {
669
- const wrappedType = typeNode.getTypeArguments()[0];
658
+ const wrappedType = typeArguments[0];
670
659
  if (!wrappedType)
671
660
  return { props: [], hasChildren: false };
672
661
  const nextExcludedProps = new Set(excludedProps);
673
662
  if (typeName === 'Omit') {
674
- const omittedProps = typeNode.getTypeArguments()[1];
663
+ const omittedProps = typeArguments[1];
675
664
  if (omittedProps) {
676
665
  for (const value of getStringLiteralTypeValues(omittedProps)) {
677
666
  nextExcludedProps.add(value);
@@ -696,75 +685,7 @@ function extractPropsFromTypeNode(typeNode, slotNames, seen = new Set(), exclude
696
685
  if (TRANSPARENT_POLYMORPHIC_TYPE_NAMES.has(typeName)) {
697
686
  return { props: [], hasChildren: false };
698
687
  }
699
- for (const declaration of getTypeTargetDeclarations(typeNode.getTypeName(), allowImportedOmitWorkspaceFallback)) {
700
- if (!declaration)
701
- continue;
702
- if (Node.isInterfaceDeclaration(declaration)) {
703
- return extractPropsFromInterfaceDeclaration(declaration, slotNames, seen, excludedProps, allowImportedOmitWorkspaceFallback, suppressNeverChildrenSlot);
704
- }
705
- if (Node.isTypeAliasDeclaration(declaration)) {
706
- const aliasedTypeNode = declaration.getTypeNode();
707
- if (!aliasedTypeNode)
708
- continue;
709
- return extractPropsFromTypeNode(aliasedTypeNode, slotNames, seen, excludedProps, allowImportedOmitWorkspaceFallback, suppressNeverChildrenSlot);
710
- }
711
- }
712
- }
713
- if (Node.isExpressionWithTypeArguments(typeNode)) {
714
- const typeName = getTypeReferenceName(typeNode);
715
- if (!typeName)
716
- return { props: [], hasChildren: false };
717
- if (typeName === 'PropsWithChildren') {
718
- const wrappedType = typeNode.getTypeArguments()[0];
719
- if (!wrappedType)
720
- return { props: [], hasChildren: true };
721
- const extracted = extractPropsFromTypeNode(wrappedType, slotNames, seen, excludedProps, allowImportedOmitWorkspaceFallback, suppressNeverChildrenSlot);
722
- return { props: extracted.props, hasChildren: true };
723
- }
724
- if (isExpandableDomAttributeWrapperName(typeName) || typeName === 'Pick') {
725
- return typeName === 'Pick'
726
- ? {
727
- props: extractPickedPropsFromTypeNode(typeNode, slotNames, excludedProps),
728
- hasChildren: false,
729
- }
730
- : { props: [], hasChildren: false };
731
- }
732
- if (typeName === 'Omit' ||
733
- typeName === 'Partial' ||
734
- typeName === 'Readonly' ||
735
- typeName === 'Required' ||
736
- typeName === 'NonNullable') {
737
- const wrappedType = typeNode.getTypeArguments()[0];
738
- if (!wrappedType)
739
- return { props: [], hasChildren: false };
740
- const nextExcludedProps = new Set(excludedProps);
741
- if (typeName === 'Omit') {
742
- const omittedProps = typeNode.getTypeArguments()[1];
743
- if (omittedProps) {
744
- for (const value of getStringLiteralTypeValues(omittedProps)) {
745
- nextExcludedProps.add(value);
746
- }
747
- }
748
- }
749
- return extractPropsFromTypeNode(wrappedType, slotNames, seen, nextExcludedProps, allowImportedOmitWorkspaceFallback || typeName === 'Omit', suppressNeverChildrenSlot);
750
- }
751
- const mappedOmitArgs = allowImportedOmitWorkspaceFallback ? getMappedOmitEquivalentArgs(typeNode) : undefined;
752
- if (mappedOmitArgs) {
753
- const nextExcludedProps = new Set(excludedProps);
754
- for (const value of getStringLiteralTypeValues(mappedOmitArgs.omittedProps)) {
755
- nextExcludedProps.add(value);
756
- }
757
- return extractPropsFromTypeNode(mappedOmitArgs.wrappedType, slotNames, seen, nextExcludedProps, true, suppressNeverChildrenSlot);
758
- }
759
- const wrappedTypeNode = unwrapRepoLocalTransparentPolymorphicWrapper(typeNode, allowImportedOmitWorkspaceFallback) ??
760
- (!allowImportedOmitWorkspaceFallback ? unwrapRepoLocalTransparentPolymorphicWrapper(typeNode, true) : undefined);
761
- if (wrappedTypeNode) {
762
- return extractPropsFromTypeNode(wrappedTypeNode, slotNames, seen, excludedProps, allowImportedOmitWorkspaceFallback, true);
763
- }
764
- if (TRANSPARENT_POLYMORPHIC_TYPE_NAMES.has(typeName)) {
765
- return { props: [], hasChildren: false };
766
- }
767
- for (const declaration of getTypeTargetDeclarations(typeNode.getExpression(), allowImportedOmitWorkspaceFallback)) {
688
+ for (const declaration of getTypeTargetDeclarations(typeReferenceTarget, allowImportedOmitWorkspaceFallback)) {
768
689
  if (!declaration)
769
690
  continue;
770
691
  if (Node.isInterfaceDeclaration(declaration)) {
@@ -783,19 +704,20 @@ function extractPropsFromTypeNode(typeNode, slotNames, seen = new Set(), exclude
783
704
  function collectRenderPropSlotNames(type) {
784
705
  const names = new Set();
785
706
  for (const property of type.getProperties()) {
786
- const name = property.getName();
787
- if (!name.startsWith('render'))
788
- continue;
789
- const declaration = property.getValueDeclaration() ?? property.getDeclarations()[0];
790
- if (!declaration)
791
- continue;
792
- const propType = property.getTypeAtLocation(declaration);
793
- if (isRenderPropType(propType)) {
794
- names.add(name);
795
- }
707
+ if (getRenderPropType(property))
708
+ names.add(property.getName());
796
709
  }
797
710
  return names;
798
711
  }
712
+ function getRenderPropType(property) {
713
+ if (!property.getName().startsWith('render'))
714
+ return undefined;
715
+ const declaration = property.getValueDeclaration() ?? property.getDeclarations()[0];
716
+ if (!declaration)
717
+ return undefined;
718
+ const propType = property.getTypeAtLocation(declaration);
719
+ return isRenderPropType(propType) ? propType : undefined;
720
+ }
799
721
  function recordBindingPatternDefaults(nameNode, defaults) {
800
722
  if (!Node.isObjectBindingPattern(nameNode))
801
723
  return;
@@ -1154,13 +1076,7 @@ function extractDestructuredBindingFallbackProps(func, param, existingPropNames,
1154
1076
  name: propName,
1155
1077
  type: propertyType === 'unknown' ? 'any' : propertyType,
1156
1078
  required: property ? !property.isOptional() : false,
1157
- ...(declaration &&
1158
- typeof declaration.getStartLineNumber === 'function'
1159
- ? {
1160
- sourceStartLine: declaration.getStartLineNumber(),
1161
- sourceEndLine: declaration.getEndLineNumber(),
1162
- }
1163
- : {}),
1079
+ ...getSourceLineMetadata(declaration),
1164
1080
  });
1165
1081
  }
1166
1082
  }
@@ -1175,13 +1091,7 @@ function extractSlots(type, hasChildren) {
1175
1091
  const name = property.getName();
1176
1092
  if (name === 'children')
1177
1093
  continue;
1178
- if (!name.startsWith('render'))
1179
- continue;
1180
- const declaration = property.getValueDeclaration() ?? property.getDeclarations()[0];
1181
- if (!declaration)
1182
- continue;
1183
- const propType = property.getTypeAtLocation(declaration);
1184
- if (!isRenderPropType(propType))
1094
+ if (!getRenderPropType(property))
1185
1095
  continue;
1186
1096
  const slotName = name.replace(/^render/, '');
1187
1097
  slots.push({
@@ -1268,104 +1178,64 @@ function containsImportedOmitWrappedCustomProps(typeNode, seen = new Set(), orig
1268
1178
  }
1269
1179
  return false;
1270
1180
  }
1271
- function containsSupportedDomPickType(typeNode, seen = new Set()) {
1181
+ function containsPickType(typeNode, seen, mode) {
1272
1182
  if (seen.has(typeNode))
1273
1183
  return false;
1274
1184
  seen.add(typeNode);
1275
1185
  if (Node.isParenthesizedTypeNode(typeNode) || Node.isTypeOperatorTypeNode(typeNode)) {
1276
- return containsSupportedDomPickType(typeNode.getTypeNode(), seen);
1186
+ return containsPickType(typeNode.getTypeNode(), seen, mode);
1277
1187
  }
1278
1188
  if (Node.isIntersectionTypeNode(typeNode) || Node.isUnionTypeNode(typeNode)) {
1279
- return typeNode.getTypeNodes().some((child) => containsSupportedDomPickType(child, seen));
1189
+ return typeNode.getTypeNodes().some((child) => containsPickType(child, seen, mode));
1280
1190
  }
1281
- if (Node.isTypeReference(typeNode) || Node.isExpressionWithTypeArguments(typeNode)) {
1282
- const typeName = getTypeReferenceName(typeNode);
1283
- if (!typeName)
1284
- return false;
1285
- if (typeName === 'Pick') {
1286
- const sourceTypeNode = typeNode.getTypeArguments()[0];
1287
- return sourceTypeNode
1288
- ? collectExpandableDomAttributeWrapperContexts(sourceTypeNode).length > 0 ||
1289
- containsSupportedDomPickType(sourceTypeNode, seen)
1290
- : false;
1291
- }
1292
- if (typeName === 'Omit' ||
1293
- typeName === 'Partial' ||
1294
- typeName === 'Readonly' ||
1295
- typeName === 'Required' ||
1296
- typeName === 'NonNullable') {
1297
- const wrappedType = typeNode.getTypeArguments()[0];
1298
- return wrappedType ? containsSupportedDomPickType(wrappedType, seen) : false;
1299
- }
1300
- const targetNode = Node.isTypeReference(typeNode) ? typeNode.getTypeName() : typeNode.getExpression();
1301
- for (const declaration of getTypeTargetDeclarations(targetNode)) {
1302
- if (!declaration)
1303
- continue;
1304
- if (Node.isInterfaceDeclaration(declaration)) {
1305
- if (declaration
1306
- .getHeritageClauses()
1307
- .some((clause) => clause.getTypeNodes().some((heritageTypeNode) => containsSupportedDomPickType(heritageTypeNode, seen)))) {
1308
- return true;
1309
- }
1310
- }
1311
- if (Node.isTypeAliasDeclaration(declaration)) {
1312
- const aliasedTypeNode = declaration.getTypeNode();
1313
- if (aliasedTypeNode && containsSupportedDomPickType(aliasedTypeNode, seen)) {
1314
- return true;
1315
- }
1316
- }
1317
- }
1318
- }
1319
- return false;
1320
- }
1321
- function containsAnyPickType(typeNode, seen = new Set()) {
1322
- if (seen.has(typeNode))
1191
+ const typeReferenceTarget = getTypeReferenceTargetNode(typeNode);
1192
+ if (!typeReferenceTarget)
1323
1193
  return false;
1324
- seen.add(typeNode);
1325
- if (Node.isParenthesizedTypeNode(typeNode) || Node.isTypeOperatorTypeNode(typeNode)) {
1326
- return containsAnyPickType(typeNode.getTypeNode(), seen);
1194
+ const typeName = getTypeReferenceName(typeNode);
1195
+ if (!typeName)
1196
+ return false;
1197
+ const typeArguments = getTypeReferenceArguments(typeNode);
1198
+ if (typeName === 'Pick') {
1199
+ if (mode === 'any')
1200
+ return true;
1201
+ const sourceTypeNode = typeArguments[0];
1202
+ return sourceTypeNode
1203
+ ? collectExpandableDomAttributeWrapperContexts(sourceTypeNode).length > 0 ||
1204
+ containsPickType(sourceTypeNode, seen, mode)
1205
+ : false;
1327
1206
  }
1328
- if (Node.isIntersectionTypeNode(typeNode) || Node.isUnionTypeNode(typeNode)) {
1329
- return typeNode.getTypeNodes().some((child) => containsAnyPickType(child, seen));
1207
+ const wrappedTypeNames = mode === 'any'
1208
+ ? ['PropsWithChildren', 'Omit', 'Partial', 'Readonly', 'Required', 'NonNullable']
1209
+ : ['Omit', 'Partial', 'Readonly', 'Required', 'NonNullable'];
1210
+ if (wrappedTypeNames.includes(typeName)) {
1211
+ const wrappedType = typeArguments[0];
1212
+ return wrappedType ? containsPickType(wrappedType, seen, mode) : false;
1330
1213
  }
1331
- if (Node.isTypeReference(typeNode) || Node.isExpressionWithTypeArguments(typeNode)) {
1332
- const typeName = getTypeReferenceName(typeNode);
1333
- if (!typeName)
1334
- return false;
1335
- if (typeName === 'Pick') {
1336
- return true;
1337
- }
1338
- const wrappedType = typeNode.getTypeArguments()[0];
1339
- if ((typeName === 'PropsWithChildren' ||
1340
- typeName === 'Omit' ||
1341
- typeName === 'Partial' ||
1342
- typeName === 'Readonly' ||
1343
- typeName === 'Required' ||
1344
- typeName === 'NonNullable') &&
1345
- wrappedType) {
1346
- return containsAnyPickType(wrappedType, seen);
1347
- }
1348
- const targetNode = Node.isTypeReference(typeNode) ? typeNode.getTypeName() : typeNode.getExpression();
1349
- for (const declaration of getTypeTargetDeclarations(targetNode, true)) {
1350
- if (!declaration)
1351
- continue;
1352
- if (Node.isInterfaceDeclaration(declaration)) {
1353
- if (declaration
1354
- .getHeritageClauses()
1355
- .some((clause) => clause.getTypeNodes().some((heritageTypeNode) => containsAnyPickType(heritageTypeNode, seen)))) {
1356
- return true;
1357
- }
1214
+ for (const declaration of getTypeTargetDeclarations(typeReferenceTarget, mode === 'any')) {
1215
+ if (!declaration)
1216
+ continue;
1217
+ if (Node.isInterfaceDeclaration(declaration)) {
1218
+ if (declaration
1219
+ .getHeritageClauses()
1220
+ .some((clause) => clause.getTypeNodes().some((heritageTypeNode) => containsPickType(heritageTypeNode, seen, mode)))) {
1221
+ return true;
1358
1222
  }
1359
- if (Node.isTypeAliasDeclaration(declaration)) {
1360
- const aliasedTypeNode = declaration.getTypeNode();
1361
- if (aliasedTypeNode && containsAnyPickType(aliasedTypeNode, seen)) {
1362
- return true;
1363
- }
1223
+ }
1224
+ if (Node.isTypeAliasDeclaration(declaration)) {
1225
+ const aliasedTypeNode = declaration.getTypeNode();
1226
+ if (aliasedTypeNode && containsPickType(aliasedTypeNode, seen, mode)) {
1227
+ return true;
1364
1228
  }
1365
1229
  }
1366
1230
  }
1367
1231
  return false;
1368
1232
  }
1233
+ function containsSupportedDomPickType(typeNode, seen = new Set()) {
1234
+ return containsPickType(typeNode, seen, 'supported');
1235
+ }
1236
+ function containsAnyPickType(typeNode, seen = new Set()) {
1237
+ return containsPickType(typeNode, seen, 'any');
1238
+ }
1369
1239
  function collectExpandableDomAttributeWrapperContexts(typeNode, seen = new Set(), excludedProps = new Set()) {
1370
1240
  if (seen.has(typeNode))
1371
1241
  return [];
@@ -1381,59 +1251,17 @@ function collectExpandableDomAttributeWrapperContexts(typeNode, seen = new Set()
1381
1251
  .getTypeNodes()
1382
1252
  .flatMap((child) => collectExpandableDomAttributeWrapperContexts(child, seen, excludedProps));
1383
1253
  }
1384
- if (Node.isTypeReference(typeNode)) {
1385
- const typeName = getTypeReferenceName(typeNode);
1386
- if (!typeName)
1387
- return [];
1388
- if (isExpandableDomAttributeWrapperName(typeName)) {
1389
- return [{ name: typeName, excludedProps: new Set(excludedProps) }];
1390
- }
1391
- if (typeName === 'Omit') {
1392
- const [wrappedType, omittedProps] = typeNode.getTypeArguments();
1393
- if (!wrappedType)
1394
- return [];
1395
- const nextExcludedProps = new Set(excludedProps);
1396
- if (omittedProps) {
1397
- for (const value of getStringLiteralTypeValues(omittedProps)) {
1398
- nextExcludedProps.add(value);
1399
- }
1400
- }
1401
- return collectExpandableDomAttributeWrapperContexts(wrappedType, seen, nextExcludedProps);
1402
- }
1403
- if (typeName === 'Partial' || typeName === 'Readonly' || typeName === 'Required' || typeName === 'NonNullable') {
1404
- const wrappedType = typeNode.getTypeArguments()[0];
1405
- if (!wrappedType)
1406
- return [];
1407
- return collectExpandableDomAttributeWrapperContexts(wrappedType, seen, excludedProps);
1408
- }
1409
- for (const definition of getNodeDefinitions(typeNode.getTypeName())) {
1410
- const declaration = definition.getDeclarationNode();
1411
- if (!declaration)
1412
- continue;
1413
- if (Node.isInterfaceDeclaration(declaration)) {
1414
- return declaration
1415
- .getHeritageClauses()
1416
- .flatMap((clause) => clause
1417
- .getTypeNodes()
1418
- .flatMap((heritageTypeNode) => collectExpandableDomAttributeWrapperContexts(heritageTypeNode, seen, excludedProps)));
1419
- }
1420
- if (Node.isTypeAliasDeclaration(declaration)) {
1421
- const aliasedTypeNode = declaration.getTypeNode();
1422
- if (!aliasedTypeNode)
1423
- return [];
1424
- return collectExpandableDomAttributeWrapperContexts(aliasedTypeNode, seen, excludedProps);
1425
- }
1426
- }
1427
- }
1428
- if (Node.isExpressionWithTypeArguments(typeNode)) {
1254
+ const typeReferenceTarget = getTypeReferenceTargetNode(typeNode);
1255
+ if (typeReferenceTarget) {
1429
1256
  const typeName = getTypeReferenceName(typeNode);
1430
1257
  if (!typeName)
1431
1258
  return [];
1259
+ const typeArguments = getTypeReferenceArguments(typeNode);
1432
1260
  if (isExpandableDomAttributeWrapperName(typeName)) {
1433
1261
  return [{ name: typeName, excludedProps: new Set(excludedProps) }];
1434
1262
  }
1435
1263
  if (typeName === 'Omit') {
1436
- const [wrappedType, omittedProps] = typeNode.getTypeArguments();
1264
+ const [wrappedType, omittedProps] = typeArguments;
1437
1265
  if (!wrappedType)
1438
1266
  return [];
1439
1267
  const nextExcludedProps = new Set(excludedProps);
@@ -1445,12 +1273,12 @@ function collectExpandableDomAttributeWrapperContexts(typeNode, seen = new Set()
1445
1273
  return collectExpandableDomAttributeWrapperContexts(wrappedType, seen, nextExcludedProps);
1446
1274
  }
1447
1275
  if (typeName === 'Partial' || typeName === 'Readonly' || typeName === 'Required' || typeName === 'NonNullable') {
1448
- const wrappedType = typeNode.getTypeArguments()[0];
1276
+ const wrappedType = typeArguments[0];
1449
1277
  if (!wrappedType)
1450
1278
  return [];
1451
1279
  return collectExpandableDomAttributeWrapperContexts(wrappedType, seen, excludedProps);
1452
1280
  }
1453
- for (const definition of getNodeDefinitions(typeNode.getExpression())) {
1281
+ for (const definition of getNodeDefinitions(typeReferenceTarget)) {
1454
1282
  const declaration = definition.getDeclarationNode();
1455
1283
  if (!declaration)
1456
1284
  continue;
@@ -1488,10 +1316,12 @@ function isPureExpandableDomAttributeWrapperTypeNode(typeNode, seen = new Set())
1488
1316
  const childTypes = typeNode.getTypeNodes();
1489
1317
  return (childTypes.length > 0 && childTypes.every((child) => isPureExpandableDomAttributeWrapperTypeNode(child, seen)));
1490
1318
  }
1491
- if (Node.isTypeReference(typeNode)) {
1319
+ const typeReferenceTarget = getTypeReferenceTargetNode(typeNode);
1320
+ if (typeReferenceTarget) {
1492
1321
  const typeName = getTypeReferenceName(typeNode);
1493
1322
  if (!typeName)
1494
1323
  return false;
1324
+ const typeArguments = getTypeReferenceArguments(typeNode);
1495
1325
  if (isExpandableDomAttributeWrapperName(typeName)) {
1496
1326
  return true;
1497
1327
  }
@@ -1500,49 +1330,12 @@ function isPureExpandableDomAttributeWrapperTypeNode(typeNode, seen = new Set())
1500
1330
  typeName === 'Readonly' ||
1501
1331
  typeName === 'Required' ||
1502
1332
  typeName === 'NonNullable') {
1503
- const wrappedType = typeNode.getTypeArguments()[0];
1333
+ const wrappedType = typeArguments[0];
1504
1334
  return wrappedType ? isPureExpandableDomAttributeWrapperTypeNode(wrappedType, seen) : false;
1505
1335
  }
1506
- if (typeName === 'Pick') {
1507
- return false;
1508
- }
1509
- for (const definition of getNodeDefinitions(typeNode.getTypeName())) {
1510
- const declaration = definition.getDeclarationNode();
1511
- if (!declaration)
1512
- continue;
1513
- if (Node.isInterfaceDeclaration(declaration)) {
1514
- if (declaration.getMembers().length > 0)
1515
- return false;
1516
- const heritageClauses = declaration.getHeritageClauses();
1517
- return (heritageClauses.length > 0 &&
1518
- heritageClauses.every((clause) => clause
1519
- .getTypeNodes()
1520
- .every((heritageTypeNode) => isPureExpandableDomAttributeWrapperTypeNode(heritageTypeNode, seen))));
1521
- }
1522
- if (Node.isTypeAliasDeclaration(declaration)) {
1523
- const aliasedTypeNode = declaration.getTypeNode();
1524
- if (!aliasedTypeNode)
1525
- return false;
1526
- return isPureExpandableDomAttributeWrapperTypeNode(aliasedTypeNode, seen);
1527
- }
1528
- }
1529
- }
1530
- if (Node.isExpressionWithTypeArguments(typeNode)) {
1531
- const typeName = getTypeReferenceName(typeNode);
1532
- if (!typeName)
1336
+ if (typeName === 'Pick')
1533
1337
  return false;
1534
- if (isExpandableDomAttributeWrapperName(typeName)) {
1535
- return true;
1536
- }
1537
- if (typeName === 'Omit' ||
1538
- typeName === 'Partial' ||
1539
- typeName === 'Readonly' ||
1540
- typeName === 'Required' ||
1541
- typeName === 'NonNullable') {
1542
- const wrappedType = typeNode.getTypeArguments()[0];
1543
- return wrappedType ? isPureExpandableDomAttributeWrapperTypeNode(wrappedType, seen) : false;
1544
- }
1545
- for (const definition of getNodeDefinitions(typeNode.getExpression())) {
1338
+ for (const definition of getNodeDefinitions(typeReferenceTarget)) {
1546
1339
  const declaration = definition.getDeclarationNode();
1547
1340
  if (!declaration)
1548
1341
  continue;
@@ -1599,29 +1392,46 @@ function getDomAttributeSurface(name, seen = new Set()) {
1599
1392
  // Every prop on these surfaces is a DOM attribute by construction, so the
1600
1393
  // provenance is known here without tracing the implementation. Copy each prop
1601
1394
  // so that later provenance propagation cannot mutate the shared surface table.
1602
- return [...propsByName.values()].map((prop) => ({ ...prop, domAttribute: true }));
1395
+ return [...propsByName.values()].map((prop) => ({
1396
+ ...prop,
1397
+ domAttribute: true,
1398
+ }));
1603
1399
  }
1604
1400
  function hasSyntheticDomChildren(typeNode) {
1605
1401
  if (!typeNode)
1606
1402
  return false;
1607
1403
  return collectExpandableDomAttributeWrapperContexts(typeNode).some((context) => DOM_ATTRIBUTE_WRAPPERS_WITH_SYNTHETIC_CHILDREN.has(context.name));
1608
1404
  }
1405
+ function collectRestBindingNames(nameNode) {
1406
+ const names = new Set();
1407
+ if (!Node.isObjectBindingPattern(nameNode))
1408
+ return names;
1409
+ for (const element of nameNode.getElements()) {
1410
+ if (!element.getDotDotDotToken())
1411
+ continue;
1412
+ const restNameNode = element.getNameNode();
1413
+ if (Node.isIdentifier(restNameNode))
1414
+ names.add(restNameNode.getText());
1415
+ }
1416
+ return names;
1417
+ }
1418
+ function isComponentSpreadAttribute(attribute, bindingNames) {
1419
+ if (!Node.isJsxSpreadAttribute(attribute))
1420
+ return false;
1421
+ const expression = attribute.getExpression();
1422
+ if (!Node.isIdentifier(expression) || !bindingNames.has(expression.getText()))
1423
+ return false;
1424
+ const tagName = getJsxTagNameNode(attribute)?.getText();
1425
+ return tagName ? /^[A-Z]/.test(tagName) : false;
1426
+ }
1609
1427
  function inferPrimitiveDomPropsFromImplementation(funcNode, propsParam) {
1610
1428
  const candidatePropNames = new Set();
1611
1429
  const propsParamNameNode = propsParam.getNameNode();
1612
1430
  if (Node.isIdentifier(propsParamNameNode)) {
1613
1431
  candidatePropNames.add(propsParam.getName());
1614
1432
  }
1615
- if (Node.isObjectBindingPattern(propsParamNameNode)) {
1616
- for (const element of propsParamNameNode.getElements()) {
1617
- if (!element.getDotDotDotToken())
1618
- continue;
1619
- const restNameNode = element.getNameNode();
1620
- if (Node.isIdentifier(restNameNode)) {
1621
- candidatePropNames.add(restNameNode.getText());
1622
- }
1623
- }
1624
- }
1433
+ for (const restName of collectRestBindingNames(propsParamNameNode))
1434
+ candidatePropNames.add(restName);
1625
1435
  for (const variableDeclaration of funcNode.getDescendantsOfKind(SyntaxKind.VariableDeclaration)) {
1626
1436
  const initializer = variableDeclaration.getInitializer();
1627
1437
  if (!initializer || !Node.isIdentifier(initializer) || !candidatePropNames.has(initializer.getText())) {
@@ -1630,14 +1440,8 @@ function inferPrimitiveDomPropsFromImplementation(funcNode, propsParam) {
1630
1440
  const nameNode = variableDeclaration.getNameNode();
1631
1441
  if (!Node.isObjectBindingPattern(nameNode))
1632
1442
  continue;
1633
- for (const element of nameNode.getElements()) {
1634
- if (!element.getDotDotDotToken())
1635
- continue;
1636
- const restNameNode = element.getNameNode();
1637
- if (Node.isIdentifier(restNameNode)) {
1638
- candidatePropNames.add(restNameNode.getText());
1639
- }
1640
- }
1443
+ for (const restName of collectRestBindingNames(nameNode))
1444
+ candidatePropNames.add(restName);
1641
1445
  }
1642
1446
  if (candidatePropNames.size === 0)
1643
1447
  return [];
@@ -1797,28 +1601,17 @@ function hasImplementationChildrenHint(funcNode, param) {
1797
1601
  if (!body)
1798
1602
  return false;
1799
1603
  if (Node.isObjectBindingPattern(nameNode)) {
1800
- const restBindingNames = new Set();
1801
1604
  for (const element of nameNode.getElements()) {
1802
1605
  if (element.getNameNode().getText() === 'children') {
1803
1606
  return true;
1804
1607
  }
1805
- if (element.getDotDotDotToken()) {
1806
- restBindingNames.add(element.getNameNode().getText());
1807
- }
1808
1608
  }
1609
+ const restBindingNames = collectRestBindingNames(nameNode);
1809
1610
  if (restBindingNames.size === 0)
1810
1611
  return false;
1811
- return body.getDescendantsOfKind(SyntaxKind.JsxSpreadAttribute).some((attr) => {
1812
- const expression = attr.getExpression();
1813
- if (!expression || !Node.isIdentifier(expression))
1814
- return false;
1815
- if (!restBindingNames.has(expression.getText()))
1816
- return false;
1817
- const openingElement = attr.getFirstAncestorByKind(SyntaxKind.JsxOpeningElement);
1818
- const selfClosingElement = attr.getFirstAncestorByKind(SyntaxKind.JsxSelfClosingElement);
1819
- const tagName = openingElement?.getTagNameNode().getText() ?? selfClosingElement?.getTagNameNode().getText();
1820
- return tagName ? /^[A-Z]/.test(tagName) : false;
1821
- });
1612
+ return body
1613
+ .getDescendantsOfKind(SyntaxKind.JsxSpreadAttribute)
1614
+ .some((attr) => isComponentSpreadAttribute(attr, restBindingNames));
1822
1615
  }
1823
1616
  if (!Node.isIdentifier(nameNode))
1824
1617
  return false;
@@ -1828,36 +1621,16 @@ function hasImplementationChildrenHint(funcNode, param) {
1828
1621
  .some((expr) => expr.getExpression().getText() === paramName && expr.getName() === 'children')) {
1829
1622
  return true;
1830
1623
  }
1831
- return body.getDescendantsOfKind(SyntaxKind.JsxSpreadAttribute).some((attr) => {
1832
- const expression = attr.getExpression();
1833
- if (!expression || !Node.isIdentifier(expression) || expression.getText() !== paramName)
1834
- return false;
1835
- const openingElement = attr.getFirstAncestorByKind(SyntaxKind.JsxOpeningElement);
1836
- const selfClosingElement = attr.getFirstAncestorByKind(SyntaxKind.JsxSelfClosingElement);
1837
- const tagName = openingElement?.getTagNameNode().getText() ?? selfClosingElement?.getTagNameNode().getText();
1838
- return tagName ? /^[A-Z]/.test(tagName) : false;
1839
- });
1624
+ return body
1625
+ .getDescendantsOfKind(SyntaxKind.JsxSpreadAttribute)
1626
+ .some((attr) => isComponentSpreadAttribute(attr, new Set([paramName])));
1840
1627
  }
1841
1628
  export async function extractReactComponents(filePaths) {
1842
- const componentFiles = filePaths.filter((f) => /\.[jt]sx$/.test(f));
1843
- if (componentFiles.length === 0) {
1629
+ const extractionContext = getTsxExtractionContext(filePaths, /\.[jt]sx$/);
1630
+ if (!extractionContext) {
1844
1631
  return { components: [], warnings: [] };
1845
1632
  }
1846
- const projectFiles = filePaths.filter((f) => /\.[jt]sx?$/.test(f) && !f.endsWith('.d.ts'));
1847
- const project = new Project({
1848
- compilerOptions: {
1849
- jsx: 1, // JsxEmit.Preserve
1850
- target: 99, // ScriptTarget.ESNext
1851
- module: 99, // ModuleKind.ESNext
1852
- moduleResolution: 100, // ModuleResolutionKind.Bundler
1853
- skipLibCheck: true,
1854
- allowJs: true,
1855
- },
1856
- skipAddingFilesFromTsConfig: true,
1857
- });
1858
- for (const filePath of projectFiles) {
1859
- project.addSourceFileAtPath(filePath);
1860
- }
1633
+ const { componentFiles, project } = extractionContext;
1861
1634
  const warnings = [];
1862
1635
  const components = [];
1863
1636
  for (const filePath of componentFiles) {
@@ -1981,19 +1754,10 @@ function extractFromSourceFile(sourceFile, isNext) {
1981
1754
  for (const [exportKey, declarations] of exported) {
1982
1755
  let name = exportKey;
1983
1756
  if (exportKey === 'default') {
1984
- const decl = declarations[0];
1985
- let declName;
1986
- if (Node.isFunctionDeclaration(decl)) {
1987
- declName = decl.getName();
1988
- }
1989
- else if (Node.isVariableDeclaration(decl)) {
1990
- declName = decl.getName();
1991
- }
1992
- if (!declName || !/^[A-Z]/.test(declName))
1993
- continue;
1994
- if (exported.has(declName))
1757
+ const defaultExportName = resolveDefaultExportName(declarations, exported, true);
1758
+ if (!defaultExportName)
1995
1759
  continue;
1996
- name = declName;
1760
+ name = defaultExportName;
1997
1761
  }
1998
1762
  if (!/^[A-Z]/.test(name))
1999
1763
  continue;