@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
@@ -0,0 +1,220 @@
1
+ import { Node, SyntaxKind } from 'ts-morph';
2
+ import { getJsxTagNameNode, getNodeDefinitions, getValueTargetDeclarations, isIntrinsicJsxElement, } from '../support/tsx-shared.js';
3
+ import { resolveFunctionNode } from './function-resolution.js';
4
+ function getDeclarationIdentity(node) {
5
+ if (Node.isIdentifier(node)) {
6
+ const parent = node.getParent();
7
+ if ((Node.isVariableDeclaration(parent) || Node.isBindingElement(parent) || Node.isParameterDeclaration(parent)) &&
8
+ parent.getNameNode() === node) {
9
+ return parent;
10
+ }
11
+ }
12
+ return getNodeDefinitions(node)[0]?.getDeclarationNode() ?? node;
13
+ }
14
+ export function getComponentIdentity(func) {
15
+ return `${func.getSourceFile().getFilePath()}:${func.getStart()}`;
16
+ }
17
+ function resolveForwardedComponentIdentity(tagNameNode) {
18
+ if (!Node.isIdentifier(tagNameNode))
19
+ return undefined;
20
+ for (const declaration of getValueTargetDeclarations(tagNameNode)) {
21
+ const func = resolveFunctionNode(declaration);
22
+ if (func)
23
+ return getComponentIdentity(func);
24
+ }
25
+ return undefined;
26
+ }
27
+ /**
28
+ * Declarations that are rebound somewhere in the body, so their initializer no
29
+ * longer describes the value that reaches JSX.
30
+ */
31
+ function collectReboundDeclarations(body) {
32
+ const rebound = new Set();
33
+ const recordTarget = (target) => {
34
+ if (Node.isIdentifier(target)) {
35
+ rebound.add(getDeclarationIdentity(target));
36
+ return;
37
+ }
38
+ if ((Node.isPropertyAccessExpression(target) || Node.isElementAccessExpression(target)) &&
39
+ Node.isIdentifier(target.getExpression())) {
40
+ rebound.add(getDeclarationIdentity(target.getExpression()));
41
+ }
42
+ };
43
+ for (const assignment of body?.getDescendantsOfKind(SyntaxKind.BinaryExpression) ?? []) {
44
+ // Covers `=` and every compound form (`+=`, `??=`, `>>>=`, ...).
45
+ const operator = assignment.getOperatorToken().getKind();
46
+ if (operator < SyntaxKind.FirstAssignment || operator > SyntaxKind.LastAssignment)
47
+ continue;
48
+ recordTarget(assignment.getLeft());
49
+ }
50
+ // Only `++`/`--` rebind the operand. Reading a prop through `!`, `-`, `+`, or
51
+ // `~` leaves the alias intact, so those operators must not invalidate it.
52
+ for (const update of body?.getDescendantsOfKind(SyntaxKind.PrefixUnaryExpression) ?? []) {
53
+ const operator = update.getOperatorToken();
54
+ if (operator !== SyntaxKind.PlusPlusToken && operator !== SyntaxKind.MinusMinusToken)
55
+ continue;
56
+ recordTarget(update.getOperand());
57
+ }
58
+ for (const update of body?.getDescendantsOfKind(SyntaxKind.PostfixUnaryExpression) ?? []) {
59
+ recordTarget(update.getOperand());
60
+ }
61
+ return rebound;
62
+ }
63
+ /** Trace only statically resolvable, component-local prop dataflow into JSX. */
64
+ export function collectPropDataflow(func, param, availablePropNames) {
65
+ const valueAliases = new Map();
66
+ const objectAliases = new Map();
67
+ const fullPropsShape = new Map([...availablePropNames].map((name) => [name, name]));
68
+ const belongsToComponentFunction = (node) => node.getFirstAncestor((ancestor) => Node.isFunctionDeclaration(ancestor) || Node.isArrowFunction(ancestor) || Node.isFunctionExpression(ancestor)) === func;
69
+ const resolveValue = (expression) => {
70
+ if (!expression)
71
+ return undefined;
72
+ if (Node.isParenthesizedExpression(expression))
73
+ return resolveValue(expression.getExpression());
74
+ if (Node.isIdentifier(expression))
75
+ return valueAliases.get(getDeclarationIdentity(expression));
76
+ if (Node.isPropertyAccessExpression(expression)) {
77
+ const object = expression.getExpression();
78
+ return Node.isIdentifier(object)
79
+ ? objectAliases.get(getDeclarationIdentity(object))?.get(expression.getName())
80
+ : undefined;
81
+ }
82
+ if (Node.isElementAccessExpression(expression)) {
83
+ const argument = expression.getArgumentExpression();
84
+ if (!argument || (!Node.isStringLiteral(argument) && !Node.isNoSubstitutionTemplateLiteral(argument))) {
85
+ return undefined;
86
+ }
87
+ const object = expression.getExpression();
88
+ return Node.isIdentifier(object)
89
+ ? objectAliases.get(getDeclarationIdentity(object))?.get(argument.getLiteralText())
90
+ : undefined;
91
+ }
92
+ return undefined;
93
+ };
94
+ const resolveObject = (expression) => {
95
+ if (!expression)
96
+ return undefined;
97
+ if (Node.isParenthesizedExpression(expression))
98
+ return resolveObject(expression.getExpression());
99
+ if (Node.isIdentifier(expression))
100
+ return objectAliases.get(getDeclarationIdentity(expression));
101
+ if (!Node.isObjectLiteralExpression(expression))
102
+ return undefined;
103
+ const shape = new Map();
104
+ for (const property of expression.getProperties()) {
105
+ if (Node.isSpreadAssignment(property)) {
106
+ const spreadShape = resolveObject(property.getExpression());
107
+ for (const [targetProp, sourceProp] of spreadShape ?? [])
108
+ shape.set(targetProp, sourceProp);
109
+ }
110
+ else if (Node.isShorthandPropertyAssignment(property)) {
111
+ const sourceProp = resolveValue(property.getNameNode());
112
+ if (sourceProp)
113
+ shape.set(property.getName(), sourceProp);
114
+ }
115
+ else if (Node.isPropertyAssignment(property)) {
116
+ const propertyNameNode = property.getNameNode();
117
+ if (!Node.isIdentifier(propertyNameNode) && !Node.isStringLiteral(propertyNameNode))
118
+ continue;
119
+ const sourceProp = resolveValue(property.getInitializer());
120
+ if (sourceProp)
121
+ shape.set(property.getName(), sourceProp);
122
+ }
123
+ }
124
+ return shape;
125
+ };
126
+ const addBindings = (pattern, sourceShape) => {
127
+ const omittedProps = new Set(pattern
128
+ .getElements()
129
+ .filter((element) => !element.getDotDotDotToken())
130
+ .map((element) => element.getPropertyNameNode()?.getText() ?? element.getNameNode().getText()));
131
+ for (const element of pattern.getElements()) {
132
+ const localName = element.getNameNode();
133
+ if (!Node.isIdentifier(localName))
134
+ continue;
135
+ if (element.getDotDotDotToken()) {
136
+ objectAliases.set(getDeclarationIdentity(localName), new Map([...sourceShape].filter(([targetProp]) => !omittedProps.has(targetProp))));
137
+ continue;
138
+ }
139
+ const targetProp = element.getPropertyNameNode()?.getText() ?? localName.getText();
140
+ const sourceProp = sourceShape.get(targetProp);
141
+ if (sourceProp)
142
+ valueAliases.set(getDeclarationIdentity(localName), sourceProp);
143
+ }
144
+ };
145
+ const paramNameNode = param.getNameNode();
146
+ if (Node.isIdentifier(paramNameNode))
147
+ objectAliases.set(getDeclarationIdentity(paramNameNode), fullPropsShape);
148
+ else if (Node.isObjectBindingPattern(paramNameNode))
149
+ addBindings(paramNameNode, fullPropsShape);
150
+ const body = func.getBody();
151
+ const reboundDeclarations = collectReboundDeclarations(body);
152
+ for (const declaration of reboundDeclarations) {
153
+ valueAliases.delete(declaration);
154
+ objectAliases.delete(declaration);
155
+ }
156
+ for (const declaration of body?.getDescendantsOfKind(SyntaxKind.VariableDeclaration) ?? []) {
157
+ if (!belongsToComponentFunction(declaration))
158
+ continue;
159
+ const initializer = declaration.getInitializer();
160
+ const declarationName = declaration.getNameNode();
161
+ if (Node.isIdentifier(declarationName)) {
162
+ const aliasDeclaration = getDeclarationIdentity(declarationName);
163
+ if (reboundDeclarations.has(aliasDeclaration))
164
+ continue;
165
+ const sourceProp = resolveValue(initializer);
166
+ if (sourceProp)
167
+ valueAliases.set(aliasDeclaration, sourceProp);
168
+ const sourceShape = resolveObject(initializer);
169
+ if (sourceShape)
170
+ objectAliases.set(aliasDeclaration, new Map(sourceShape));
171
+ }
172
+ else if (Node.isObjectBindingPattern(declarationName)) {
173
+ const sourceShape = resolveObject(initializer);
174
+ if (sourceShape) {
175
+ const hasInvalidatedBinding = declarationName
176
+ .getElements()
177
+ .some((element) => reboundDeclarations.has(getDeclarationIdentity(element.getNameNode())));
178
+ if (!hasInvalidatedBinding)
179
+ addBindings(declarationName, sourceShape);
180
+ }
181
+ }
182
+ }
183
+ const intrinsicDomProps = new Set();
184
+ const componentEdges = [];
185
+ const recordForwarding = (tagNameNode, targetProp, sourceProp) => {
186
+ const tagName = tagNameNode.getText();
187
+ if (isIntrinsicJsxElement(tagName)) {
188
+ if (targetProp === sourceProp)
189
+ intrinsicDomProps.add(sourceProp);
190
+ return;
191
+ }
192
+ const targetComponentIdentity = resolveForwardedComponentIdentity(tagNameNode);
193
+ if (targetComponentIdentity)
194
+ componentEdges.push({ sourceProp, targetComponentIdentity, targetProp });
195
+ };
196
+ for (const attribute of func.getDescendantsOfKind(SyntaxKind.JsxAttribute)) {
197
+ if (!belongsToComponentFunction(attribute))
198
+ continue;
199
+ const tagNameNode = getJsxTagNameNode(attribute);
200
+ if (!tagNameNode)
201
+ continue;
202
+ const initializer = attribute.getInitializer();
203
+ if (!initializer || !Node.isJsxExpression(initializer))
204
+ continue;
205
+ const sourceProp = resolveValue(initializer.getExpression());
206
+ if (sourceProp)
207
+ recordForwarding(tagNameNode, attribute.getNameNode().getText(), sourceProp);
208
+ }
209
+ for (const spread of func.getDescendantsOfKind(SyntaxKind.JsxSpreadAttribute)) {
210
+ if (!belongsToComponentFunction(spread))
211
+ continue;
212
+ const tagNameNode = getJsxTagNameNode(spread);
213
+ if (!tagNameNode)
214
+ continue;
215
+ const shape = resolveObject(spread.getExpression());
216
+ for (const [targetProp, sourceProp] of shape ?? [])
217
+ recordForwarding(tagNameNode, targetProp, sourceProp);
218
+ }
219
+ return { intrinsicDomProps, componentEdges };
220
+ }
@@ -1,2 +1,2 @@
1
- import type { ComponentExtractionResult } from '../types.js';
1
+ import type { ComponentExtractionResult } from '../../model/component.js';
2
2
  export declare function extractStencilComponents(filePaths: string[]): Promise<ComponentExtractionResult>;
@@ -1,6 +1,7 @@
1
1
  import { Project, Node, SyntaxKind } from 'ts-morph';
2
- import { getJsxTagNameNode, isIntrinsicJsxElement, kebabToPascal } from './tsx-shared.js';
3
- import { createSortedExtractionResult, extractProjectSourceFiles } from './file-extraction-workers.js';
2
+ import { getJsxTagNameNode, isIntrinsicJsxElement, kebabToPascal } from '../support/tsx-shared.js';
3
+ import { createSortedExtractionResult } from '../support/file-processing/result-normalizer.js';
4
+ import { extractProjectSourceFiles } from '../support/file-processing/project-source-files.js';
4
5
  function isStencilFile(sourceFile) {
5
6
  return sourceFile.getImportDeclarations().some((imp) => imp.getModuleSpecifierValue() === '@stencil/core');
6
7
  }
@@ -1,10 +1,9 @@
1
- import type { Project, SourceFile } from 'ts-morph';
2
1
  export type FileExtractionOutcome<T, M = undefined> = {
3
2
  item: T | null;
4
3
  warnings?: string[];
5
4
  metadata?: M;
6
5
  };
7
- type FileExtractionProgress = {
6
+ export type FileExtractionProgress = {
8
7
  filesProcessed: number;
9
8
  componentsFound: number;
10
9
  };
@@ -12,14 +11,3 @@ export declare function runFileExtractionWorkers<T, M = undefined>(filePaths: st
12
11
  items: T[];
13
12
  warnings: string[];
14
13
  }>;
15
- export declare function createSortedExtractionResult<T extends {
16
- name: string;
17
- }>(components: T[], warnings: string[]): {
18
- components: T[];
19
- warnings: string[];
20
- };
21
- export declare function extractProjectSourceFiles<T>(project: Project, extractFile: (sourceFile: SourceFile, warnings: string[]) => T[]): {
22
- items: T[];
23
- warnings: string[];
24
- };
25
- export {};
@@ -27,22 +27,3 @@ export async function runFileExtractionWorkers(filePaths, concurrency, extractFi
27
27
  await Promise.all(Array.from({ length: Math.min(concurrency, filePaths.length) }, worker));
28
28
  return { items, warnings };
29
29
  }
30
- export function createSortedExtractionResult(components, warnings) {
31
- return {
32
- components: components.sort((a, b) => a.name.localeCompare(b.name)),
33
- warnings,
34
- };
35
- }
36
- export function extractProjectSourceFiles(project, extractFile) {
37
- const warnings = [];
38
- const items = [];
39
- for (const sourceFile of project.getSourceFiles()) {
40
- try {
41
- items.push(...extractFile(sourceFile, warnings));
42
- }
43
- catch (error) {
44
- warnings.push(`Failed to extract from ${sourceFile.getFilePath()}: ${error instanceof Error ? error.message : String(error)}`);
45
- }
46
- }
47
- return { items, warnings };
48
- }
@@ -0,0 +1,5 @@
1
+ import type { Project, SourceFile } from 'ts-morph';
2
+ export declare function extractProjectSourceFiles<T>(project: Project, extractFile: (sourceFile: SourceFile, warnings: string[]) => T[]): {
3
+ items: T[];
4
+ warnings: string[];
5
+ };
@@ -0,0 +1,13 @@
1
+ export function extractProjectSourceFiles(project, extractFile) {
2
+ const warnings = [];
3
+ const items = [];
4
+ for (const sourceFile of project.getSourceFiles()) {
5
+ try {
6
+ items.push(...extractFile(sourceFile, warnings));
7
+ }
8
+ catch (error) {
9
+ warnings.push(`Failed to extract from ${sourceFile.getFilePath()}: ${error instanceof Error ? error.message : String(error)}`);
10
+ }
11
+ }
12
+ return { items, warnings };
13
+ }
@@ -0,0 +1,6 @@
1
+ export declare function createSortedExtractionResult<T extends {
2
+ name: string;
3
+ }>(components: T[], warnings: string[]): {
4
+ components: T[];
5
+ warnings: string[];
6
+ };
@@ -0,0 +1,6 @@
1
+ export function createSortedExtractionResult(components, warnings) {
2
+ return {
3
+ components: components.sort((a, b) => a.name.localeCompare(b.name)),
4
+ warnings,
5
+ };
6
+ }
@@ -1,3 +1,3 @@
1
1
  import type { Node } from 'ts-morph';
2
- import type { RawPropDefinition } from '../types.js';
2
+ import type { RawPropDefinition } from '../../../model/component.js';
3
3
  export declare function getSourceLineMetadata(declaration: Node | undefined): Pick<RawPropDefinition, 'sourceStartLine' | 'sourceEndLine'>;
@@ -1,5 +1,5 @@
1
1
  import { Node, Project, type SourceFile, type Type } from 'ts-morph';
2
- import type { ExtractionExclusion } from '../types.js';
2
+ import type { ExtractionExclusion } from '../../model/component.js';
3
3
  export declare function getTsxExtractionContext(filePaths: string[], componentFilePattern: RegExp): {
4
4
  componentFiles: string[];
5
5
  project: Project;
@@ -0,0 +1,12 @@
1
+ export interface AstNode {
2
+ type: string;
3
+ loc?: {
4
+ start?: {
5
+ line?: number;
6
+ };
7
+ end?: {
8
+ line?: number;
9
+ };
10
+ };
11
+ [key: string]: unknown;
12
+ }
@@ -0,0 +1 @@
1
+ export {};
@@ -1,4 +1,5 @@
1
- import type { ComponentExtractionResult, ExtractorOptions } from '../types.js';
1
+ import type { ComponentExtractionResult } from '../../model/component.js';
2
+ import type { ExtractorOptions } from '../../model/options.js';
2
3
  export declare function extractSvelteComponents(filePaths: string[], onProgress?: (p: {
3
4
  filesProcessed: number;
4
5
  componentsFound: number;
@@ -1,13 +1,15 @@
1
1
  import os from 'node:os';
2
- import { basename, dirname, resolve, join } from 'node:path';
2
+ import { dirname, resolve, join } from 'node:path';
3
3
  import { existsSync, readFileSync } from 'node:fs';
4
4
  import { createRequire } from 'node:module';
5
5
  import { parse as parseSvelte } from 'svelte/compiler';
6
6
  import { Project, Node, ScriptTarget, ModuleKind, ts } from 'ts-morph';
7
- import { computeExtractionScore, deriveNeedsReview } from './scoring.js';
8
- import { extractAllowedComponentsFromTypeText } from './slot-allowed-components.js';
9
- import { runFileExtractionWorkers } from './file-extraction-workers.js';
10
- import { resolveLocalModule } from './resolve-local-module.js';
7
+ import { computeExtractionScore, deriveNeedsReview } from '../../policies/quality/scoring.js';
8
+ import { extractAllowedComponentsFromTypeText } from '../../evidence/allowed-components.js';
9
+ import { runFileExtractionWorkers } from '../support/file-processing/file-workers.js';
10
+ import { resolveLocalModule } from '../support/resolution/local-module.js';
11
+ import { getSvelteComponentName } from './identity.js';
12
+ import { extractTemplateSlots, mergeSlots } from './slots.js';
11
13
  export async function extractSvelteComponents(filePaths, onProgress, opts) {
12
14
  const svelteFiles = filePaths.filter((f) => f.endsWith('.svelte'));
13
15
  const retryContexts = new Map();
@@ -466,31 +468,6 @@ async function extractFromSvelteFile(filePath, source) {
466
468
  }
467
469
  return { component, warnings, ...(retryContext ? { retryContext } : {}) };
468
470
  }
469
- const ANATOMY_FOLDERS = new Set(['anatomy', 'parts']);
470
- function getSvelteComponentName(filePath) {
471
- const file = basename(filePath, '.svelte');
472
- const parentDir = basename(dirname(filePath));
473
- if (file === 'index')
474
- return toPascalCase(parentDir);
475
- if (ANATOMY_FOLDERS.has(parentDir)) {
476
- const grandparent = basename(dirname(dirname(filePath)));
477
- if (grandparent && grandparent !== '.' && grandparent !== '/') {
478
- return `${toPascalCase(grandparent)}${toPascalCase(file)}`;
479
- }
480
- }
481
- return toPascalCase(file);
482
- }
483
- function toPascalCase(s) {
484
- if (!s)
485
- return s;
486
- if (/^[A-Z]/.test(s) && !s.includes('-') && !s.includes('_'))
487
- return s;
488
- return s
489
- .split(/[-_]+/)
490
- .filter(Boolean)
491
- .map((part) => part[0].toUpperCase() + part.slice(1))
492
- .join('');
493
- }
494
471
  function hasV4ExportLetProps(instance) {
495
472
  const body = instance['content']?.['body'];
496
473
  if (!body)
@@ -1246,64 +1223,3 @@ function extractAllowedValuesFromText(typeText) {
1246
1223
  }
1247
1224
  return out.length > 0 ? out : undefined;
1248
1225
  }
1249
- function extractTemplateSlots(fragment) {
1250
- const slots = [];
1251
- const seen = new Set();
1252
- walk(fragment);
1253
- return slots;
1254
- function walk(node) {
1255
- if (!node)
1256
- return;
1257
- if (node.type === 'SlotElement') {
1258
- const name = readSlotName(node);
1259
- if (!seen.has(name)) {
1260
- seen.add(name);
1261
- slots.push({ name, isDefault: name === 'default' });
1262
- }
1263
- }
1264
- const children = node['nodes'] ?? node['children'] ?? [];
1265
- for (const c of children)
1266
- walk(c);
1267
- const fragChild = node['fragment'];
1268
- if (fragChild)
1269
- walk(fragChild);
1270
- }
1271
- }
1272
- function readSlotName(slotEl) {
1273
- const attrs = slotEl['attributes'] ?? [];
1274
- for (const attr of attrs) {
1275
- if (attr['name'] !== 'name')
1276
- continue;
1277
- const value = attr['value'];
1278
- if (Array.isArray(value)) {
1279
- for (const v of value) {
1280
- if (v.type === 'Text' && typeof v['data'] === 'string')
1281
- return v['data'];
1282
- }
1283
- }
1284
- }
1285
- return 'default';
1286
- }
1287
- function mergeSlots(fromSnippetProps, fromTemplate) {
1288
- if (fromSnippetProps.length === 0)
1289
- return { slots: fromTemplate, mixedWarning: false };
1290
- if (fromTemplate.length === 0)
1291
- return { slots: fromSnippetProps, mixedWarning: false };
1292
- const byName = new Map();
1293
- for (const s of fromSnippetProps)
1294
- byName.set(s.name, s);
1295
- const snippetHasDefault = fromSnippetProps.some((s) => s.isDefault);
1296
- let mixed = false;
1297
- for (const s of fromTemplate) {
1298
- if (s.isDefault && snippetHasDefault) {
1299
- mixed = true;
1300
- continue;
1301
- }
1302
- if (byName.has(s.name)) {
1303
- mixed = true;
1304
- continue;
1305
- }
1306
- byName.set(s.name, s);
1307
- }
1308
- return { slots: [...byName.values()], mixedWarning: mixed };
1309
- }
@@ -0,0 +1 @@
1
+ export declare function getSvelteComponentName(filePath: string): string;
@@ -0,0 +1,26 @@
1
+ import { basename, dirname } from 'node:path';
2
+ const ANATOMY_FOLDERS = new Set(['anatomy', 'parts']);
3
+ export function getSvelteComponentName(filePath) {
4
+ const file = basename(filePath, '.svelte');
5
+ const parentDir = basename(dirname(filePath));
6
+ if (file === 'index')
7
+ return toPascalCase(parentDir);
8
+ if (ANATOMY_FOLDERS.has(parentDir)) {
9
+ const grandparent = basename(dirname(dirname(filePath)));
10
+ if (grandparent && grandparent !== '.' && grandparent !== '/') {
11
+ return `${toPascalCase(grandparent)}${toPascalCase(file)}`;
12
+ }
13
+ }
14
+ return toPascalCase(file);
15
+ }
16
+ function toPascalCase(s) {
17
+ if (!s)
18
+ return s;
19
+ if (/^[A-Z]/.test(s) && !s.includes('-') && !s.includes('_'))
20
+ return s;
21
+ return s
22
+ .split(/[-_]+/)
23
+ .filter(Boolean)
24
+ .map((part) => part[0].toUpperCase() + part.slice(1))
25
+ .join('');
26
+ }
@@ -0,0 +1,7 @@
1
+ import type { RawSlotDefinition } from '../../model/component.js';
2
+ import type { AstNode } from './ast.js';
3
+ export declare function extractTemplateSlots(fragment: AstNode): RawSlotDefinition[];
4
+ export declare function mergeSlots(fromSnippetProps: RawSlotDefinition[], fromTemplate: RawSlotDefinition[]): {
5
+ slots: RawSlotDefinition[];
6
+ mixedWarning: boolean;
7
+ };
@@ -0,0 +1,63 @@
1
+ export function extractTemplateSlots(fragment) {
2
+ const slots = new Map();
3
+ const seen = new Set();
4
+ walk(fragment);
5
+ return [...slots.entries()]
6
+ .map(([name, isDefault]) => ({ name, isDefault }))
7
+ .sort((a, b) => a.name.localeCompare(b.name));
8
+ function walk(node) {
9
+ if (!node)
10
+ return;
11
+ if (node.type === 'SlotElement') {
12
+ const name = readSlotName(node);
13
+ if (!seen.has(name)) {
14
+ seen.add(name);
15
+ slots.set(name, name === 'default');
16
+ }
17
+ }
18
+ const children = node['nodes'] ?? node['children'] ?? [];
19
+ for (const child of children)
20
+ walk(child);
21
+ const fragmentChild = node['fragment'];
22
+ if (fragmentChild)
23
+ walk(fragmentChild);
24
+ }
25
+ }
26
+ function readSlotName(slotElement) {
27
+ const attrs = slotElement['attributes'] ?? [];
28
+ for (const attr of attrs) {
29
+ if (attr['name'] !== 'name')
30
+ continue;
31
+ const value = attr['value'];
32
+ if (Array.isArray(value)) {
33
+ for (const item of value) {
34
+ if (item.type === 'Text' && typeof item['data'] === 'string')
35
+ return item['data'];
36
+ }
37
+ }
38
+ }
39
+ return 'default';
40
+ }
41
+ export function mergeSlots(fromSnippetProps, fromTemplate) {
42
+ if (fromSnippetProps.length === 0)
43
+ return { slots: fromTemplate, mixedWarning: false };
44
+ if (fromTemplate.length === 0)
45
+ return { slots: fromSnippetProps, mixedWarning: false };
46
+ const byName = new Map();
47
+ for (const slot of fromSnippetProps)
48
+ byName.set(slot.name, slot);
49
+ const snippetHasDefault = fromSnippetProps.some((slot) => slot.isDefault);
50
+ let mixed = false;
51
+ for (const slot of fromTemplate) {
52
+ if (slot.isDefault && snippetHasDefault) {
53
+ mixed = true;
54
+ continue;
55
+ }
56
+ if (byName.has(slot.name)) {
57
+ mixed = true;
58
+ continue;
59
+ }
60
+ byName.set(slot.name, slot);
61
+ }
62
+ return { slots: [...byName.values()], mixedWarning: mixed };
63
+ }
@@ -1,4 +1,4 @@
1
- import type { ComponentExtractionResult } from '../types.js';
1
+ import type { ComponentExtractionResult } from '../../model/component.js';
2
2
  export declare function extractVueComponents(filePaths: string[], onProgress?: (p: {
3
3
  filesProcessed: number;
4
4
  componentsFound: number;
@@ -4,9 +4,10 @@ import { readFile } from 'node:fs/promises';
4
4
  import { existsSync, readFileSync, readdirSync, statSync } from 'node:fs';
5
5
  import { parse as parseSFC } from '@vue/compiler-sfc';
6
6
  import { Project, Node } from 'ts-morph';
7
- import { createSortedExtractionResult, runFileExtractionWorkers } from './file-extraction-workers.js';
8
- import { resolveLocalModule } from './resolve-local-module.js';
9
- import { resolveTypeProperty } from './resolve-type-property.js';
7
+ import { runFileExtractionWorkers } from '../support/file-processing/file-workers.js';
8
+ import { createSortedExtractionResult } from '../support/file-processing/result-normalizer.js';
9
+ import { resolveLocalModule } from '../support/resolution/local-module.js';
10
+ import { resolveTypeProperty } from '../support/resolution/type-property.js';
10
11
  // @vue/compiler-core NodeTypes enum values (stable since Vue 3.0)
11
12
  const ELEMENT_TYPE = 1;
12
13
  const ATTRIBUTE_TYPE = 6;
@@ -1,2 +1,2 @@
1
- import type { ComponentExtractionResult } from '../types.js';
1
+ import type { ComponentExtractionResult } from '../../model/component.js';
2
2
  export declare function extractVueTsxComponents(filePaths: string[]): Promise<ComponentExtractionResult>;
@@ -1,5 +1,5 @@
1
1
  import { Node, SyntaxKind } from 'ts-morph';
2
- import { extractAllowedValues, extractTsxComponents, getTypeReferenceName, getTypeTargetDeclarations, getValueTargetDeclarations, getRenderableExports, } from './tsx-shared.js';
2
+ import { extractAllowedValues, extractTsxComponents, getTypeReferenceName, getTypeTargetDeclarations, getValueTargetDeclarations, getRenderableExports, } from '../support/tsx-shared.js';
3
3
  export async function extractVueTsxComponents(filePaths) {
4
4
  const { components, warnings, exclusions } = extractTsxComponents(filePaths, /\.tsx$/, extractFromSourceFile);
5
5
  return {
@@ -1,2 +1,2 @@
1
- import type { ComponentExtractionResult } from '../types.js';
1
+ import type { ComponentExtractionResult } from '../../model/component.js';
2
2
  export declare function extractWebComponentDefinitions(filePaths: string[]): Promise<ComponentExtractionResult>;