@contentful/experience-design-system-extraction 2.34.5-dev-build-ec126bf0.0 → 2.34.5-dev-build-4a4b9e84.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 (88) hide show
  1. package/dist/package.json +1 -1
  2. package/dist/src/extract/{astro.d.ts → adapters/astro/extractor.d.ts} +1 -1
  3. package/dist/src/extract/{astro.js → adapters/astro/extractor.js} +4 -3
  4. package/dist/src/extract/{react.d.ts → adapters/react/extractor.d.ts} +1 -1
  5. package/dist/src/extract/{react.js → adapters/react/extractor.js} +9 -353
  6. package/dist/src/extract/adapters/react/function-resolution.d.ts +4 -0
  7. package/dist/src/extract/adapters/react/function-resolution.js +131 -0
  8. package/dist/src/extract/adapters/react/prop-dataflow.d.ts +14 -0
  9. package/dist/src/extract/adapters/react/prop-dataflow.js +220 -0
  10. package/dist/src/extract/{stencil.d.ts → adapters/stencil/extractor.d.ts} +1 -1
  11. package/dist/src/extract/{stencil.js → adapters/stencil/extractor.js} +3 -2
  12. package/dist/src/extract/{file-extraction-workers.d.ts → adapters/support/file-processing/file-workers.d.ts} +1 -13
  13. package/dist/src/extract/{file-extraction-workers.js → adapters/support/file-processing/file-workers.js} +0 -19
  14. package/dist/src/extract/adapters/support/file-processing/project-source-files.d.ts +5 -0
  15. package/dist/src/extract/adapters/support/file-processing/project-source-files.js +13 -0
  16. package/dist/src/extract/adapters/support/file-processing/result-normalizer.d.ts +6 -0
  17. package/dist/src/extract/adapters/support/file-processing/result-normalizer.js +6 -0
  18. package/dist/src/extract/{source-line-metadata.d.ts → adapters/support/resolution/source-location.d.ts} +1 -1
  19. package/dist/src/extract/{tsx-shared.d.ts → adapters/support/tsx-shared.d.ts} +1 -1
  20. package/dist/src/extract/adapters/svelte/ast.d.ts +12 -0
  21. package/dist/src/extract/adapters/svelte/ast.js +1 -0
  22. package/dist/src/extract/{svelte.d.ts → adapters/svelte/extractor.d.ts} +2 -1
  23. package/dist/src/extract/{svelte.js → adapters/svelte/extractor.js} +7 -91
  24. package/dist/src/extract/adapters/svelte/identity.d.ts +1 -0
  25. package/dist/src/extract/adapters/svelte/identity.js +26 -0
  26. package/dist/src/extract/adapters/svelte/slots.d.ts +7 -0
  27. package/dist/src/extract/adapters/svelte/slots.js +63 -0
  28. package/dist/src/extract/{vue.d.ts → adapters/vue/extractor.d.ts} +1 -1
  29. package/dist/src/extract/{vue.js → adapters/vue/extractor.js} +4 -3
  30. package/dist/src/extract/{vue-tsx.d.ts → adapters/vue-tsx/extractor.d.ts} +1 -1
  31. package/dist/src/extract/{vue-tsx.js → adapters/vue-tsx/extractor.js} +1 -1
  32. package/dist/src/extract/{web-components.d.ts → adapters/web-components/extractor.d.ts} +1 -1
  33. package/dist/src/extract/{web-components.js → adapters/web-components/extractor.js} +4 -213
  34. package/dist/src/extract/adapters/web-components/properties.d.ts +9 -0
  35. package/dist/src/extract/adapters/web-components/properties.js +212 -0
  36. package/dist/src/extract/controller/extract-controller.d.ts +10 -0
  37. package/dist/src/extract/controller/extract-controller.js +56 -0
  38. package/dist/src/extract/{structural-slot-evidence.d.ts → evidence/structural-slot-evidence.d.ts} +1 -1
  39. package/dist/src/extract/{structural-slot-evidence.js → evidence/structural-slot-evidence.js} +2 -2
  40. package/dist/src/{types.d.ts → extract/model/component.d.ts} +0 -20
  41. package/dist/src/extract/model/contract.d.ts +16 -0
  42. package/dist/src/extract/model/contract.js +1 -0
  43. package/dist/src/extract/model/options.d.ts +15 -0
  44. package/dist/src/extract/model/options.js +1 -0
  45. package/dist/src/extract/model/scoring.d.ts +9 -0
  46. package/dist/src/extract/model/scoring.js +1 -0
  47. package/dist/src/extract/{non-authorable-filter.d.ts → policies/quality/authorability.d.ts} +1 -1
  48. package/dist/src/extract/policies/quality/scoring.d.ts +5 -0
  49. package/dist/src/extract/{scoring.js → policies/quality/scoring.js} +1 -0
  50. package/dist/src/extract/{source-inspection.d.ts → policies/quality/source-inspection.d.ts} +1 -1
  51. package/dist/src/extract/{source-inspection.js → policies/quality/source-inspection.js} +1 -1
  52. package/dist/src/extract/{validate.d.ts → policies/quality/validation.d.ts} +1 -1
  53. package/dist/src/{pre-classify.d.ts → extract/services/classification-service.d.ts} +1 -1
  54. package/dist/src/extract/services/component-deduplicator.d.ts +2 -0
  55. package/dist/src/extract/{pipeline.js → services/component-deduplicator.js} +13 -137
  56. package/dist/src/extract/services/extraction-pipeline.d.ts +3 -0
  57. package/dist/src/extract/services/extraction-pipeline.js +43 -0
  58. package/dist/src/extract/services/extraction-runner.d.ts +4 -0
  59. package/dist/src/extract/services/extraction-runner.js +21 -0
  60. package/dist/src/extract/services/extraction-service.d.ts +13 -0
  61. package/dist/src/extract/services/extraction-service.js +17 -0
  62. package/dist/src/extract/services/extractor-registry.d.ts +8 -0
  63. package/dist/src/extract/services/extractor-registry.js +68 -0
  64. package/dist/src/extract/services/ports/component-extractor.d.ts +7 -0
  65. package/dist/src/extract/services/ports/component-extractor.js +1 -0
  66. package/dist/src/extract/services/quality-service.d.ts +4 -0
  67. package/dist/src/extract/services/quality-service.js +61 -0
  68. package/dist/src/index.d.ts +25 -21
  69. package/dist/src/index.js +18 -17
  70. package/package.json +1 -1
  71. package/dist/src/extract/pipeline.d.ts +0 -7
  72. package/dist/src/extract/scoring.d.ts +0 -12
  73. /package/dist/src/extract/{resolve-local-module.d.ts → adapters/support/resolution/local-module.d.ts} +0 -0
  74. /package/dist/src/extract/{resolve-local-module.js → adapters/support/resolution/local-module.js} +0 -0
  75. /package/dist/src/extract/{source-line-metadata.js → adapters/support/resolution/source-location.js} +0 -0
  76. /package/dist/src/extract/{resolve-type-property.d.ts → adapters/support/resolution/type-property.d.ts} +0 -0
  77. /package/dist/src/extract/{resolve-type-property.js → adapters/support/resolution/type-property.js} +0 -0
  78. /package/dist/src/extract/{tsx-shared.js → adapters/support/tsx-shared.js} +0 -0
  79. /package/dist/src/extract/{slot-allowed-components.d.ts → evidence/allowed-components.d.ts} +0 -0
  80. /package/dist/src/extract/{slot-allowed-components.js → evidence/allowed-components.js} +0 -0
  81. /package/dist/src/extract/{slot-detection.d.ts → evidence/slot-evidence.d.ts} +0 -0
  82. /package/dist/src/extract/{slot-detection.js → evidence/slot-evidence.js} +0 -0
  83. /package/dist/src/extract/{parse-imported-names.d.ts → evidence/source-evidence.d.ts} +0 -0
  84. /package/dist/src/extract/{parse-imported-names.js → evidence/source-evidence.js} +0 -0
  85. /package/dist/src/{types.js → extract/model/component.js} +0 -0
  86. /package/dist/src/extract/{non-authorable-filter.js → policies/quality/authorability.js} +0 -0
  87. /package/dist/src/extract/{validate.js → policies/quality/validation.js} +0 -0
  88. /package/dist/src/{pre-classify.js → extract/services/classification-service.js} +0 -0
package/dist/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@contentful/experience-design-system-extraction",
3
- "version": "2.34.5-dev-build-ec126bf0.0",
3
+ "version": "2.34.5-dev-build-4a4b9e84.0",
4
4
  "description": "Component extraction engine for the Contentful Experience Design System SDK",
5
5
  "license": "MIT",
6
6
  "type": "module",
@@ -1,4 +1,4 @@
1
- import type { ComponentExtractionResult } from '../types.js';
1
+ import type { ComponentExtractionResult } from '../../model/component.js';
2
2
  export declare function extractAstroComponents(filePaths: string[], onProgress?: (p: {
3
3
  filesProcessed: number;
4
4
  componentsFound: number;
@@ -1,9 +1,10 @@
1
1
  import os from 'node:os';
2
2
  import { basename } from 'node:path';
3
3
  import { Project, Node } from 'ts-morph';
4
- import { createSortedExtractionResult, runFileExtractionWorkers } from './file-extraction-workers.js';
5
- import { resolveTypeProperty } from './resolve-type-property.js';
6
- import { getSourceLineMetadata } from './source-line-metadata.js';
4
+ import { runFileExtractionWorkers } from '../support/file-processing/file-workers.js';
5
+ import { createSortedExtractionResult } from '../support/file-processing/result-normalizer.js';
6
+ import { resolveTypeProperty } from '../support/resolution/type-property.js';
7
+ import { getSourceLineMetadata } from '../support/resolution/source-location.js';
7
8
  function extractAllowedValues(typeText) {
8
9
  // Check if the type is a union of string literals like 'a' | 'b' | 'c'
9
10
  const parts = typeText.split('|').map((p) => p.trim());
@@ -1,2 +1,2 @@
1
- import type { ComponentExtractionResult } from '../types.js';
1
+ import type { ComponentExtractionResult } from '../../model/component.js';
2
2
  export declare function extractReactComponents(filePaths: string[]): Promise<ComponentExtractionResult>;
@@ -1,9 +1,11 @@
1
1
  import { Node, SyntaxKind, } from 'ts-morph';
2
- import { extractAllowedValues, getNodeDefinitions, getTypeReferenceName, getTypeTargetDeclarations, getValueTargetDeclarations, getJsxTagNameNode, extractTsxComponents, getRenderableExports, isIntrinsicJsxElement, } from './tsx-shared.js';
3
- import { shouldBeSlot } from './slot-detection.js';
4
- import { extractAllowedComponentsFromTypeText, extractAllowedComponentsFromJsdoc } from './slot-allowed-components.js';
5
- import { getSourceLineMetadata } from './source-line-metadata.js';
6
- import { collectTypePredicateComponentReferences, collectRuntimeTypeCheckComponentReferences, collectRenderedComponentReferences, collectArrayMapRenderComponentReferences, } from './structural-slot-evidence.js';
2
+ import { extractAllowedValues, getNodeDefinitions, getTypeReferenceName, getTypeTargetDeclarations, getJsxTagNameNode, extractTsxComponents, getRenderableExports, } from '../support/tsx-shared.js';
3
+ import { shouldBeSlot } from '../../evidence/slot-evidence.js';
4
+ import { extractAllowedComponentsFromTypeText, extractAllowedComponentsFromJsdoc, } from '../../evidence/allowed-components.js';
5
+ import { getSourceLineMetadata } from '../support/resolution/source-location.js';
6
+ import { collectTypePredicateComponentReferences, collectRuntimeTypeCheckComponentReferences, collectRenderedComponentReferences, collectArrayMapRenderComponentReferences, } from '../../evidence/structural-slot-evidence.js';
7
+ import { collectPropDataflow, getComponentIdentity } from './prop-dataflow.js';
8
+ import { resolveBestFunctionNode } from './function-resolution.js';
7
9
  const REACT_ELEMENT_GENERIC_TEST = /(?:React\.)?ReactElement\s*<\s*[A-Za-z_$][\w$.]*/;
8
10
  function isReactElementGenericSlotType(typeText) {
9
11
  return REACT_ELEMENT_GENERIC_TEST.test(typeText);
@@ -817,223 +819,6 @@ function filterImplementationOnlyAliasProps(props, func) {
817
819
  return !isImplementationOnlyAliasProp(func, prop.name);
818
820
  });
819
821
  }
820
- function getDeclarationIdentity(node) {
821
- if (Node.isIdentifier(node)) {
822
- const parent = node.getParent();
823
- if ((Node.isVariableDeclaration(parent) || Node.isBindingElement(parent) || Node.isParameterDeclaration(parent)) &&
824
- parent.getNameNode() === node) {
825
- return parent;
826
- }
827
- }
828
- return getNodeDefinitions(node)[0]?.getDeclarationNode() ?? node;
829
- }
830
- function getComponentIdentity(func) {
831
- return `${func.getSourceFile().getFilePath()}:${func.getStart()}`;
832
- }
833
- function resolveForwardedComponentIdentity(tagNameNode) {
834
- if (!Node.isIdentifier(tagNameNode))
835
- return undefined;
836
- for (const declaration of getValueTargetDeclarations(tagNameNode)) {
837
- const func = resolveFunctionNode(declaration);
838
- if (func)
839
- return getComponentIdentity(func);
840
- }
841
- return undefined;
842
- }
843
- /**
844
- * Declarations that are rebound somewhere in the body, so their initializer no
845
- * longer describes the value that reaches JSX.
846
- */
847
- function collectReboundDeclarations(body) {
848
- const rebound = new Set();
849
- const recordTarget = (target) => {
850
- if (Node.isIdentifier(target)) {
851
- rebound.add(getDeclarationIdentity(target));
852
- return;
853
- }
854
- if ((Node.isPropertyAccessExpression(target) || Node.isElementAccessExpression(target)) &&
855
- Node.isIdentifier(target.getExpression())) {
856
- rebound.add(getDeclarationIdentity(target.getExpression()));
857
- }
858
- };
859
- for (const assignment of body?.getDescendantsOfKind(SyntaxKind.BinaryExpression) ?? []) {
860
- // Covers `=` and every compound form (`+=`, `??=`, `>>>=`, ...).
861
- const operator = assignment.getOperatorToken().getKind();
862
- if (operator < SyntaxKind.FirstAssignment || operator > SyntaxKind.LastAssignment)
863
- continue;
864
- recordTarget(assignment.getLeft());
865
- }
866
- // Only `++`/`--` rebind the operand. Reading a prop through `!`, `-`, `+`, or
867
- // `~` leaves the alias intact, so those operators must not invalidate it.
868
- for (const update of body?.getDescendantsOfKind(SyntaxKind.PrefixUnaryExpression) ?? []) {
869
- const operator = update.getOperatorToken();
870
- if (operator !== SyntaxKind.PlusPlusToken && operator !== SyntaxKind.MinusMinusToken)
871
- continue;
872
- recordTarget(update.getOperand());
873
- }
874
- for (const update of body?.getDescendantsOfKind(SyntaxKind.PostfixUnaryExpression) ?? []) {
875
- recordTarget(update.getOperand());
876
- }
877
- return rebound;
878
- }
879
- /** Trace only statically resolvable, component-local prop dataflow into JSX. */
880
- function collectPropDataflow(func, param, availablePropNames) {
881
- const valueAliases = new Map();
882
- const objectAliases = new Map();
883
- const fullPropsShape = new Map([...availablePropNames].map((name) => [name, name]));
884
- const belongsToComponentFunction = (node) => node.getFirstAncestor((ancestor) => Node.isFunctionDeclaration(ancestor) || Node.isArrowFunction(ancestor) || Node.isFunctionExpression(ancestor)) === func;
885
- const resolveValue = (expression) => {
886
- if (!expression)
887
- return undefined;
888
- if (Node.isParenthesizedExpression(expression))
889
- return resolveValue(expression.getExpression());
890
- if (Node.isIdentifier(expression))
891
- return valueAliases.get(getDeclarationIdentity(expression));
892
- if (Node.isPropertyAccessExpression(expression)) {
893
- const object = expression.getExpression();
894
- return Node.isIdentifier(object)
895
- ? objectAliases.get(getDeclarationIdentity(object))?.get(expression.getName())
896
- : undefined;
897
- }
898
- if (Node.isElementAccessExpression(expression)) {
899
- const argument = expression.getArgumentExpression();
900
- if (!argument || (!Node.isStringLiteral(argument) && !Node.isNoSubstitutionTemplateLiteral(argument))) {
901
- return undefined;
902
- }
903
- const object = expression.getExpression();
904
- return Node.isIdentifier(object)
905
- ? objectAliases.get(getDeclarationIdentity(object))?.get(argument.getLiteralText())
906
- : undefined;
907
- }
908
- return undefined;
909
- };
910
- const resolveObject = (expression) => {
911
- if (!expression)
912
- return undefined;
913
- if (Node.isParenthesizedExpression(expression))
914
- return resolveObject(expression.getExpression());
915
- if (Node.isIdentifier(expression))
916
- return objectAliases.get(getDeclarationIdentity(expression));
917
- if (!Node.isObjectLiteralExpression(expression))
918
- return undefined;
919
- const shape = new Map();
920
- for (const property of expression.getProperties()) {
921
- if (Node.isSpreadAssignment(property)) {
922
- const spreadShape = resolveObject(property.getExpression());
923
- for (const [targetProp, sourceProp] of spreadShape ?? [])
924
- shape.set(targetProp, sourceProp);
925
- }
926
- else if (Node.isShorthandPropertyAssignment(property)) {
927
- const sourceProp = resolveValue(property.getNameNode());
928
- if (sourceProp)
929
- shape.set(property.getName(), sourceProp);
930
- }
931
- else if (Node.isPropertyAssignment(property)) {
932
- const propertyNameNode = property.getNameNode();
933
- if (!Node.isIdentifier(propertyNameNode) && !Node.isStringLiteral(propertyNameNode))
934
- continue;
935
- const sourceProp = resolveValue(property.getInitializer());
936
- if (sourceProp)
937
- shape.set(property.getName(), sourceProp);
938
- }
939
- }
940
- return shape;
941
- };
942
- const addBindings = (pattern, sourceShape) => {
943
- const omittedProps = new Set(pattern
944
- .getElements()
945
- .filter((element) => !element.getDotDotDotToken())
946
- .map((element) => element.getPropertyNameNode()?.getText() ?? element.getNameNode().getText()));
947
- for (const element of pattern.getElements()) {
948
- const localName = element.getNameNode();
949
- if (!Node.isIdentifier(localName))
950
- continue;
951
- if (element.getDotDotDotToken()) {
952
- objectAliases.set(getDeclarationIdentity(localName), new Map([...sourceShape].filter(([targetProp]) => !omittedProps.has(targetProp))));
953
- continue;
954
- }
955
- const targetProp = element.getPropertyNameNode()?.getText() ?? localName.getText();
956
- const sourceProp = sourceShape.get(targetProp);
957
- if (sourceProp)
958
- valueAliases.set(getDeclarationIdentity(localName), sourceProp);
959
- }
960
- };
961
- const paramNameNode = param.getNameNode();
962
- if (Node.isIdentifier(paramNameNode))
963
- objectAliases.set(getDeclarationIdentity(paramNameNode), fullPropsShape);
964
- else if (Node.isObjectBindingPattern(paramNameNode))
965
- addBindings(paramNameNode, fullPropsShape);
966
- const body = func.getBody();
967
- const reboundDeclarations = collectReboundDeclarations(body);
968
- for (const declaration of reboundDeclarations) {
969
- valueAliases.delete(declaration);
970
- objectAliases.delete(declaration);
971
- }
972
- for (const declaration of body?.getDescendantsOfKind(SyntaxKind.VariableDeclaration) ?? []) {
973
- if (!belongsToComponentFunction(declaration))
974
- continue;
975
- const initializer = declaration.getInitializer();
976
- const declarationName = declaration.getNameNode();
977
- if (Node.isIdentifier(declarationName)) {
978
- const aliasDeclaration = getDeclarationIdentity(declarationName);
979
- if (reboundDeclarations.has(aliasDeclaration))
980
- continue;
981
- const sourceProp = resolveValue(initializer);
982
- if (sourceProp)
983
- valueAliases.set(aliasDeclaration, sourceProp);
984
- const sourceShape = resolveObject(initializer);
985
- if (sourceShape)
986
- objectAliases.set(aliasDeclaration, new Map(sourceShape));
987
- }
988
- else if (Node.isObjectBindingPattern(declarationName)) {
989
- const sourceShape = resolveObject(initializer);
990
- if (sourceShape) {
991
- const hasInvalidatedBinding = declarationName
992
- .getElements()
993
- .some((element) => reboundDeclarations.has(getDeclarationIdentity(element.getNameNode())));
994
- if (!hasInvalidatedBinding)
995
- addBindings(declarationName, sourceShape);
996
- }
997
- }
998
- }
999
- const intrinsicDomProps = new Set();
1000
- const componentEdges = [];
1001
- const recordForwarding = (tagNameNode, targetProp, sourceProp) => {
1002
- const tagName = tagNameNode.getText();
1003
- if (isIntrinsicJsxElement(tagName)) {
1004
- if (targetProp === sourceProp)
1005
- intrinsicDomProps.add(sourceProp);
1006
- return;
1007
- }
1008
- const targetComponentIdentity = resolveForwardedComponentIdentity(tagNameNode);
1009
- if (targetComponentIdentity)
1010
- componentEdges.push({ sourceProp, targetComponentIdentity, targetProp });
1011
- };
1012
- for (const attribute of func.getDescendantsOfKind(SyntaxKind.JsxAttribute)) {
1013
- if (!belongsToComponentFunction(attribute))
1014
- continue;
1015
- const tagNameNode = getJsxTagNameNode(attribute);
1016
- if (!tagNameNode)
1017
- continue;
1018
- const initializer = attribute.getInitializer();
1019
- if (!initializer || !Node.isJsxExpression(initializer))
1020
- continue;
1021
- const sourceProp = resolveValue(initializer.getExpression());
1022
- if (sourceProp)
1023
- recordForwarding(tagNameNode, attribute.getNameNode().getText(), sourceProp);
1024
- }
1025
- for (const spread of func.getDescendantsOfKind(SyntaxKind.JsxSpreadAttribute)) {
1026
- if (!belongsToComponentFunction(spread))
1027
- continue;
1028
- const tagNameNode = getJsxTagNameNode(spread);
1029
- if (!tagNameNode)
1030
- continue;
1031
- const shape = resolveObject(spread.getExpression());
1032
- for (const [targetProp, sourceProp] of shape ?? [])
1033
- recordForwarding(tagNameNode, targetProp, sourceProp);
1034
- }
1035
- return { intrinsicDomProps, componentEdges };
1036
- }
1037
822
  function extractDestructuredBindingFallbackProps(func, param, existingPropNames, slotNames) {
1038
823
  const nameNode = param.getNameNode();
1039
824
  const bindingPatterns = [];
@@ -1663,8 +1448,8 @@ export async function extractReactComponents(filePaths) {
1663
1448
  }
1664
1449
  }
1665
1450
  }
1666
- // Structural evidence (usage, not a declared slot contract) — see
1667
- // structural-slot-evidence.ts. Only fills slots the typed-slot pass above
1451
+ // Structural evidence (usage, not a declared slot contract) — see the
1452
+ // structural slot evidence pass. Only fills slots the typed-slot pass above
1668
1453
  // found nothing for; a declared contract always takes precedence. File-wide
1669
1454
  // signals (type-predicate functions, `.type === Component` identity checks)
1670
1455
  // are computed once per file and shared across every component in it, since
@@ -1938,132 +1723,3 @@ function extractPropTypes(sourceFile, componentName) {
1938
1723
  }
1939
1724
  return undefined;
1940
1725
  }
1941
- function resolveFunctionNode(decl) {
1942
- if (Node.isFunctionDeclaration(decl))
1943
- return decl;
1944
- if (Node.isArrowFunction(decl))
1945
- return decl;
1946
- if (Node.isFunctionExpression(decl))
1947
- return decl;
1948
- if (Node.isVariableDeclaration(decl)) {
1949
- const init = decl.getInitializer();
1950
- if (init && Node.isArrowFunction(init))
1951
- return init;
1952
- if (init && Node.isFunctionExpression(init))
1953
- return init;
1954
- if (init && Node.isCallExpression(init))
1955
- return resolveFunctionFromCallExpression(init, new Set());
1956
- }
1957
- return undefined;
1958
- }
1959
- function resolveBestFunctionNode(declarations) {
1960
- const candidates = declarations
1961
- .map((decl) => resolveFunctionNode(decl))
1962
- .filter((candidate) => candidate !== undefined);
1963
- const implementation = candidates.find((candidate) => !Node.isFunctionDeclaration(candidate) || candidate.getBody() !== undefined);
1964
- return implementation ?? candidates[0];
1965
- }
1966
- function resolveForwardRefFunction(callExpr) {
1967
- if (!Node.isCallExpression(callExpr))
1968
- return undefined;
1969
- const expressionText = callExpr.getExpression().getText();
1970
- if (!/(^|\.)forwardRef$/.test(expressionText))
1971
- return undefined;
1972
- const firstArg = callExpr.getArguments()[0];
1973
- if (!firstArg)
1974
- return undefined;
1975
- if (Node.isArrowFunction(firstArg))
1976
- return firstArg;
1977
- if (Node.isFunctionExpression(firstArg))
1978
- return firstArg;
1979
- if (Node.isIdentifier(firstArg)) {
1980
- const declarations = getNodeDefinitions(firstArg).flatMap((definition) => {
1981
- const declarationNode = definition.getDeclarationNode();
1982
- return declarationNode ? [declarationNode] : [];
1983
- });
1984
- for (const declaration of declarations) {
1985
- const resolved = resolveFunctionNode(declaration);
1986
- if (resolved)
1987
- return resolved;
1988
- }
1989
- }
1990
- return undefined;
1991
- }
1992
- function resolveFunctionFromCallExpression(callExpr, seen) {
1993
- return resolveForwardRefFunction(callExpr) ?? resolveReturnedFunctionFromFactoryCall(callExpr, seen);
1994
- }
1995
- function resolveReturnedFunctionFromFactoryCall(callExpr, seen) {
1996
- const expression = callExpr.getExpression();
1997
- if (!Node.isIdentifier(expression))
1998
- return undefined;
1999
- for (const definition of getNodeDefinitions(expression)) {
2000
- const declaration = definition.getDeclarationNode();
2001
- if (!declaration)
2002
- continue;
2003
- const resolved = resolveReturnedFunctionFromDeclaration(declaration, seen);
2004
- if (resolved)
2005
- return resolved;
2006
- }
2007
- return undefined;
2008
- }
2009
- function resolveReturnedFunctionFromDeclaration(node, seen) {
2010
- if (seen.has(node))
2011
- return undefined;
2012
- seen.add(node);
2013
- if (Node.isVariableDeclaration(node)) {
2014
- const initializer = node.getInitializer();
2015
- if (!initializer)
2016
- return undefined;
2017
- if (Node.isArrowFunction(initializer) || Node.isFunctionExpression(initializer)) {
2018
- return resolveReturnedFunctionFromCallable(initializer, seen);
2019
- }
2020
- if (Node.isCallExpression(initializer)) {
2021
- return resolveFunctionFromCallExpression(initializer, seen);
2022
- }
2023
- return undefined;
2024
- }
2025
- if (Node.isFunctionDeclaration(node) || Node.isFunctionExpression(node) || Node.isArrowFunction(node)) {
2026
- return resolveReturnedFunctionFromCallable(node, seen);
2027
- }
2028
- return undefined;
2029
- }
2030
- function resolveReturnedFunctionFromCallable(fn, seen) {
2031
- const body = fn.getBody();
2032
- if (!body)
2033
- return undefined;
2034
- if (!Node.isBlock(body)) {
2035
- return resolveReturnedFunctionFromExpression(body, seen);
2036
- }
2037
- for (const statement of body.getStatements()) {
2038
- if (!Node.isReturnStatement(statement))
2039
- continue;
2040
- const expression = statement.getExpression();
2041
- if (!expression)
2042
- continue;
2043
- const resolved = resolveReturnedFunctionFromExpression(expression, seen);
2044
- if (resolved)
2045
- return resolved;
2046
- }
2047
- return undefined;
2048
- }
2049
- function resolveReturnedFunctionFromExpression(node, seen) {
2050
- if (Node.isArrowFunction(node) || Node.isFunctionExpression(node))
2051
- return node;
2052
- if (Node.isParenthesizedExpression(node)) {
2053
- return resolveReturnedFunctionFromExpression(node.getExpression(), seen);
2054
- }
2055
- if (Node.isCallExpression(node)) {
2056
- return resolveFunctionFromCallExpression(node, seen);
2057
- }
2058
- if (Node.isIdentifier(node)) {
2059
- for (const definition of getNodeDefinitions(node)) {
2060
- const declaration = definition.getDeclarationNode();
2061
- if (!declaration)
2062
- continue;
2063
- const resolved = resolveReturnedFunctionFromDeclaration(declaration, seen);
2064
- if (resolved)
2065
- return resolved;
2066
- }
2067
- }
2068
- return undefined;
2069
- }
@@ -0,0 +1,4 @@
1
+ import { Node, type ArrowFunction, type FunctionDeclaration, type FunctionExpression } from 'ts-morph';
2
+ export type FunctionLike = FunctionDeclaration | ArrowFunction | FunctionExpression;
3
+ export declare function resolveFunctionNode(decl: Node): FunctionLike | undefined;
4
+ export declare function resolveBestFunctionNode(declarations: Node[]): FunctionLike | undefined;
@@ -0,0 +1,131 @@
1
+ import { Node, } from 'ts-morph';
2
+ import { getNodeDefinitions } from '../support/tsx-shared.js';
3
+ export function resolveFunctionNode(decl) {
4
+ if (Node.isFunctionDeclaration(decl))
5
+ return decl;
6
+ if (Node.isArrowFunction(decl))
7
+ return decl;
8
+ if (Node.isFunctionExpression(decl))
9
+ return decl;
10
+ if (Node.isVariableDeclaration(decl)) {
11
+ const init = decl.getInitializer();
12
+ if (init && Node.isArrowFunction(init))
13
+ return init;
14
+ if (init && Node.isFunctionExpression(init))
15
+ return init;
16
+ if (init && Node.isCallExpression(init))
17
+ return resolveFunctionFromCallExpression(init, new Set());
18
+ }
19
+ return undefined;
20
+ }
21
+ export function resolveBestFunctionNode(declarations) {
22
+ const candidates = declarations
23
+ .map((decl) => resolveFunctionNode(decl))
24
+ .filter((candidate) => candidate !== undefined);
25
+ const implementation = candidates.find((candidate) => !Node.isFunctionDeclaration(candidate) || candidate.getBody() !== undefined);
26
+ return implementation ?? candidates[0];
27
+ }
28
+ function resolveForwardRefFunction(callExpr) {
29
+ if (!Node.isCallExpression(callExpr))
30
+ return undefined;
31
+ const expressionText = callExpr.getExpression().getText();
32
+ if (!/(^|\.)forwardRef$/.test(expressionText))
33
+ return undefined;
34
+ const firstArg = callExpr.getArguments()[0];
35
+ if (!firstArg)
36
+ return undefined;
37
+ if (Node.isArrowFunction(firstArg))
38
+ return firstArg;
39
+ if (Node.isFunctionExpression(firstArg))
40
+ return firstArg;
41
+ if (Node.isIdentifier(firstArg)) {
42
+ const declarations = getNodeDefinitions(firstArg).flatMap((definition) => {
43
+ const declarationNode = definition.getDeclarationNode();
44
+ return declarationNode ? [declarationNode] : [];
45
+ });
46
+ for (const declaration of declarations) {
47
+ const resolved = resolveFunctionNode(declaration);
48
+ if (resolved)
49
+ return resolved;
50
+ }
51
+ }
52
+ return undefined;
53
+ }
54
+ function resolveFunctionFromCallExpression(callExpr, seen) {
55
+ return resolveForwardRefFunction(callExpr) ?? resolveReturnedFunctionFromFactoryCall(callExpr, seen);
56
+ }
57
+ function resolveReturnedFunctionFromFactoryCall(callExpr, seen) {
58
+ const expression = callExpr.getExpression();
59
+ if (!Node.isIdentifier(expression))
60
+ return undefined;
61
+ for (const definition of getNodeDefinitions(expression)) {
62
+ const declaration = definition.getDeclarationNode();
63
+ if (!declaration)
64
+ continue;
65
+ const resolved = resolveReturnedFunctionFromDeclaration(declaration, seen);
66
+ if (resolved)
67
+ return resolved;
68
+ }
69
+ return undefined;
70
+ }
71
+ function resolveReturnedFunctionFromDeclaration(node, seen) {
72
+ if (seen.has(node))
73
+ return undefined;
74
+ seen.add(node);
75
+ if (Node.isVariableDeclaration(node)) {
76
+ const initializer = node.getInitializer();
77
+ if (!initializer)
78
+ return undefined;
79
+ if (Node.isArrowFunction(initializer) || Node.isFunctionExpression(initializer)) {
80
+ return resolveReturnedFunctionFromCallable(initializer, seen);
81
+ }
82
+ if (Node.isCallExpression(initializer)) {
83
+ return resolveFunctionFromCallExpression(initializer, seen);
84
+ }
85
+ return undefined;
86
+ }
87
+ if (Node.isFunctionDeclaration(node) || Node.isFunctionExpression(node) || Node.isArrowFunction(node)) {
88
+ return resolveReturnedFunctionFromCallable(node, seen);
89
+ }
90
+ return undefined;
91
+ }
92
+ function resolveReturnedFunctionFromCallable(fn, seen) {
93
+ const body = fn.getBody();
94
+ if (!body)
95
+ return undefined;
96
+ if (!Node.isBlock(body)) {
97
+ return resolveReturnedFunctionFromExpression(body, seen);
98
+ }
99
+ for (const statement of body.getStatements()) {
100
+ if (!Node.isReturnStatement(statement))
101
+ continue;
102
+ const expression = statement.getExpression();
103
+ if (!expression)
104
+ continue;
105
+ const resolved = resolveReturnedFunctionFromExpression(expression, seen);
106
+ if (resolved)
107
+ return resolved;
108
+ }
109
+ return undefined;
110
+ }
111
+ function resolveReturnedFunctionFromExpression(node, seen) {
112
+ if (Node.isArrowFunction(node) || Node.isFunctionExpression(node))
113
+ return node;
114
+ if (Node.isParenthesizedExpression(node)) {
115
+ return resolveReturnedFunctionFromExpression(node.getExpression(), seen);
116
+ }
117
+ if (Node.isCallExpression(node)) {
118
+ return resolveFunctionFromCallExpression(node, seen);
119
+ }
120
+ if (Node.isIdentifier(node)) {
121
+ for (const definition of getNodeDefinitions(node)) {
122
+ const declaration = definition.getDeclarationNode();
123
+ if (!declaration)
124
+ continue;
125
+ const resolved = resolveReturnedFunctionFromDeclaration(declaration, seen);
126
+ if (resolved)
127
+ return resolved;
128
+ }
129
+ }
130
+ return undefined;
131
+ }
@@ -0,0 +1,14 @@
1
+ import { type ParameterDeclaration } from 'ts-morph';
2
+ import { type FunctionLike } from './function-resolution.js';
3
+ export type PropForwardingEdge = {
4
+ sourceProp: string;
5
+ targetComponentIdentity: string;
6
+ targetProp: string;
7
+ };
8
+ export type PropDataflow = {
9
+ intrinsicDomProps: Set<string>;
10
+ componentEdges: PropForwardingEdge[];
11
+ };
12
+ export declare function getComponentIdentity(func: FunctionLike): string;
13
+ /** Trace only statically resolvable, component-local prop dataflow into JSX. */
14
+ export declare function collectPropDataflow(func: FunctionLike, param: ParameterDeclaration, availablePropNames: Set<string>): PropDataflow;