@contentful/experience-design-system-extraction 2.34.5-dev-build-4a4b9e84.0 → 2.34.5-dev-build-9dff4380.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/{adapters/astro/extractor.d.ts → astro.d.ts} +1 -1
  3. package/dist/src/extract/{adapters/astro/extractor.js → astro.js} +3 -4
  4. package/dist/src/extract/{adapters/support/file-processing/file-workers.d.ts → file-extraction-workers.d.ts} +13 -1
  5. package/dist/src/extract/{adapters/support/file-processing/file-workers.js → file-extraction-workers.js} +19 -0
  6. package/dist/src/extract/{policies/quality/authorability.d.ts → non-authorable-filter.d.ts} +1 -1
  7. package/dist/src/extract/pipeline.d.ts +7 -0
  8. package/dist/src/extract/{services/component-deduplicator.js → pipeline.js} +137 -13
  9. package/dist/src/extract/{adapters/react/extractor.d.ts → react.d.ts} +1 -1
  10. package/dist/src/extract/{adapters/react/extractor.js → react.js} +353 -9
  11. package/dist/src/extract/scoring.d.ts +12 -0
  12. package/dist/src/extract/{policies/quality/scoring.js → scoring.js} +0 -1
  13. package/dist/src/extract/{policies/quality/source-inspection.d.ts → source-inspection.d.ts} +1 -1
  14. package/dist/src/extract/{policies/quality/source-inspection.js → source-inspection.js} +1 -1
  15. package/dist/src/extract/{adapters/support/resolution/source-location.d.ts → source-line-metadata.d.ts} +1 -1
  16. package/dist/src/extract/{adapters/stencil/extractor.d.ts → stencil.d.ts} +1 -1
  17. package/dist/src/extract/{adapters/stencil/extractor.js → stencil.js} +2 -3
  18. package/dist/src/extract/{evidence/structural-slot-evidence.d.ts → structural-slot-evidence.d.ts} +1 -1
  19. package/dist/src/extract/{evidence/structural-slot-evidence.js → structural-slot-evidence.js} +2 -2
  20. package/dist/src/extract/{adapters/svelte/extractor.d.ts → svelte.d.ts} +1 -2
  21. package/dist/src/extract/{adapters/svelte/extractor.js → svelte.js} +91 -7
  22. package/dist/src/extract/{adapters/support/tsx-shared.d.ts → tsx-shared.d.ts} +1 -1
  23. package/dist/src/extract/{policies/quality/validation.d.ts → validate.d.ts} +1 -1
  24. package/dist/src/extract/{adapters/vue-tsx/extractor.d.ts → vue-tsx.d.ts} +1 -1
  25. package/dist/src/extract/{adapters/vue-tsx/extractor.js → vue-tsx.js} +1 -1
  26. package/dist/src/extract/{adapters/vue/extractor.d.ts → vue.d.ts} +1 -1
  27. package/dist/src/extract/{adapters/vue/extractor.js → vue.js} +3 -4
  28. package/dist/src/extract/{adapters/web-components/extractor.d.ts → web-components.d.ts} +1 -1
  29. package/dist/src/extract/{adapters/web-components/extractor.js → web-components.js} +213 -4
  30. package/dist/src/index.d.ts +21 -25
  31. package/dist/src/index.js +17 -18
  32. package/dist/src/{extract/services/classification-service.d.ts → pre-classify.d.ts} +1 -1
  33. package/dist/src/{extract/model/component.d.ts → types.d.ts} +20 -0
  34. package/package.json +1 -1
  35. package/dist/src/extract/adapters/react/function-resolution.d.ts +0 -4
  36. package/dist/src/extract/adapters/react/function-resolution.js +0 -131
  37. package/dist/src/extract/adapters/react/prop-dataflow.d.ts +0 -14
  38. package/dist/src/extract/adapters/react/prop-dataflow.js +0 -220
  39. package/dist/src/extract/adapters/support/file-processing/project-source-files.d.ts +0 -5
  40. package/dist/src/extract/adapters/support/file-processing/project-source-files.js +0 -13
  41. package/dist/src/extract/adapters/support/file-processing/result-normalizer.d.ts +0 -6
  42. package/dist/src/extract/adapters/support/file-processing/result-normalizer.js +0 -6
  43. package/dist/src/extract/adapters/svelte/ast.d.ts +0 -12
  44. package/dist/src/extract/adapters/svelte/ast.js +0 -1
  45. package/dist/src/extract/adapters/svelte/identity.d.ts +0 -1
  46. package/dist/src/extract/adapters/svelte/identity.js +0 -26
  47. package/dist/src/extract/adapters/svelte/slots.d.ts +0 -7
  48. package/dist/src/extract/adapters/svelte/slots.js +0 -63
  49. package/dist/src/extract/adapters/web-components/properties.d.ts +0 -9
  50. package/dist/src/extract/adapters/web-components/properties.js +0 -212
  51. package/dist/src/extract/controller/extract-controller.d.ts +0 -10
  52. package/dist/src/extract/controller/extract-controller.js +0 -56
  53. package/dist/src/extract/model/contract.d.ts +0 -16
  54. package/dist/src/extract/model/contract.js +0 -1
  55. package/dist/src/extract/model/options.d.ts +0 -15
  56. package/dist/src/extract/model/options.js +0 -1
  57. package/dist/src/extract/model/scoring.d.ts +0 -9
  58. package/dist/src/extract/model/scoring.js +0 -1
  59. package/dist/src/extract/policies/quality/scoring.d.ts +0 -5
  60. package/dist/src/extract/services/component-deduplicator.d.ts +0 -2
  61. package/dist/src/extract/services/extraction-pipeline.d.ts +0 -3
  62. package/dist/src/extract/services/extraction-pipeline.js +0 -43
  63. package/dist/src/extract/services/extraction-runner.d.ts +0 -4
  64. package/dist/src/extract/services/extraction-runner.js +0 -21
  65. package/dist/src/extract/services/extraction-service.d.ts +0 -13
  66. package/dist/src/extract/services/extraction-service.js +0 -17
  67. package/dist/src/extract/services/extractor-registry.d.ts +0 -8
  68. package/dist/src/extract/services/extractor-registry.js +0 -68
  69. package/dist/src/extract/services/ports/component-extractor.d.ts +0 -7
  70. package/dist/src/extract/services/ports/component-extractor.js +0 -1
  71. package/dist/src/extract/services/quality-service.d.ts +0 -4
  72. package/dist/src/extract/services/quality-service.js +0 -61
  73. /package/dist/src/extract/{policies/quality/authorability.js → non-authorable-filter.js} +0 -0
  74. /package/dist/src/extract/{evidence/source-evidence.d.ts → parse-imported-names.d.ts} +0 -0
  75. /package/dist/src/extract/{evidence/source-evidence.js → parse-imported-names.js} +0 -0
  76. /package/dist/src/extract/{adapters/support/resolution/local-module.d.ts → resolve-local-module.d.ts} +0 -0
  77. /package/dist/src/extract/{adapters/support/resolution/local-module.js → resolve-local-module.js} +0 -0
  78. /package/dist/src/extract/{adapters/support/resolution/type-property.d.ts → resolve-type-property.d.ts} +0 -0
  79. /package/dist/src/extract/{adapters/support/resolution/type-property.js → resolve-type-property.js} +0 -0
  80. /package/dist/src/extract/{evidence/allowed-components.d.ts → slot-allowed-components.d.ts} +0 -0
  81. /package/dist/src/extract/{evidence/allowed-components.js → slot-allowed-components.js} +0 -0
  82. /package/dist/src/extract/{evidence/slot-evidence.d.ts → slot-detection.d.ts} +0 -0
  83. /package/dist/src/extract/{evidence/slot-evidence.js → slot-detection.js} +0 -0
  84. /package/dist/src/extract/{adapters/support/resolution/source-location.js → source-line-metadata.js} +0 -0
  85. /package/dist/src/extract/{adapters/support/tsx-shared.js → tsx-shared.js} +0 -0
  86. /package/dist/src/extract/{policies/quality/validation.js → validate.js} +0 -0
  87. /package/dist/src/{extract/services/classification-service.js → pre-classify.js} +0 -0
  88. /package/dist/src/{extract/model/component.js → types.js} +0 -0
@@ -1,220 +0,0 @@
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,5 +0,0 @@
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
- };
@@ -1,13 +0,0 @@
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
- }
@@ -1,6 +0,0 @@
1
- export declare function createSortedExtractionResult<T extends {
2
- name: string;
3
- }>(components: T[], warnings: string[]): {
4
- components: T[];
5
- warnings: string[];
6
- };
@@ -1,6 +0,0 @@
1
- export function createSortedExtractionResult(components, warnings) {
2
- return {
3
- components: components.sort((a, b) => a.name.localeCompare(b.name)),
4
- warnings,
5
- };
6
- }
@@ -1,12 +0,0 @@
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
- }
@@ -1 +0,0 @@
1
- export {};
@@ -1 +0,0 @@
1
- export declare function getSvelteComponentName(filePath: string): string;
@@ -1,26 +0,0 @@
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
- }
@@ -1,7 +0,0 @@
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
- };
@@ -1,63 +0,0 @@
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,9 +0,0 @@
1
- import { type ClassDeclaration } from 'ts-morph';
2
- import type { RawPropDefinition } from '../../model/component.js';
3
- export declare function extractObservedAttributes(classDecl: ClassDeclaration): RawPropDefinition[];
4
- export declare function hasShoelaceRuntimeBookkeepingField(classDecl: ClassDeclaration): boolean;
5
- export declare function extractClassProperties(classDecl: ClassDeclaration, applyRuntimeFieldDenylist?: boolean): RawPropDefinition[];
6
- export declare function extractAccessorProperties(classDecl: ClassDeclaration, applyRuntimeFieldDenylist?: boolean): RawPropDefinition[];
7
- export declare function extractJsDocAttributeProps(classDecl: ClassDeclaration): RawPropDefinition[];
8
- export declare function mergePropLists(...propLists: RawPropDefinition[][]): RawPropDefinition[];
9
- export declare function mergeProps(observed: RawPropDefinition[], classProps: RawPropDefinition[]): RawPropDefinition[];
@@ -1,212 +0,0 @@
1
- import { Node } from 'ts-morph';
2
- function kebabToCamel(input) {
3
- return input.replace(/-([a-z0-9])/gi, (_, char) => char.toUpperCase());
4
- }
5
- export function extractObservedAttributes(classDecl) {
6
- const props = [];
7
- const getter = classDecl.getGetAccessor('observedAttributes');
8
- if (!getter || !getter.isStatic())
9
- return props;
10
- const body = getter.getBody();
11
- if (!body)
12
- return props;
13
- // Find the return statement containing an array literal
14
- body.forEachDescendant((node) => {
15
- if (!Node.isReturnStatement(node))
16
- return;
17
- const expression = node.getExpression();
18
- if (!expression || !Node.isArrayLiteralExpression(expression))
19
- return;
20
- for (const element of expression.getElements()) {
21
- if (Node.isStringLiteral(element)) {
22
- const attrName = element.getLiteralValue();
23
- props.push({
24
- name: attrName,
25
- type: 'string',
26
- required: false,
27
- });
28
- }
29
- }
30
- });
31
- return props;
32
- }
33
- function hasInternalJsDocTag(member) {
34
- return member.getJsDocs().some((doc) => doc.getTags().some((tag) => tag.getTagName() === 'internal'));
35
- }
36
- const NON_PUBLIC_LIT_DECORATORS = new Set(['consume', 'provide', 'query', 'queryAsync', 'state']);
37
- const INTERNAL_RUNTIME_FIELD_NAMES = new Set(['dir', 'initialReflectedProperties', 'lang']);
38
- function isNonPublicLitMember(member) {
39
- return member.getDecorators().some((decorator) => NON_PUBLIC_LIT_DECORATORS.has(decorator.getName()));
40
- }
41
- export function hasShoelaceRuntimeBookkeepingField(classDecl) {
42
- return classDecl.getProperties().some((property) => property.getName() === 'initialReflectedProperties');
43
- }
44
- function isInternalRuntimeField(name, applyRuntimeFieldDenylist) {
45
- return applyRuntimeFieldDenylist && INTERNAL_RUNTIME_FIELD_NAMES.has(name);
46
- }
47
- function getExplicitLitPropertyAttributeName(property) {
48
- const decorator = property.getDecorators().find((candidate) => candidate.getName() === 'property');
49
- const firstArg = decorator?.getArguments()[0];
50
- if (!firstArg || !Node.isObjectLiteralExpression(firstArg)) {
51
- return null;
52
- }
53
- const attributeProp = firstArg.getProperty('attribute');
54
- if (!attributeProp || !Node.isPropertyAssignment(attributeProp)) {
55
- return null;
56
- }
57
- const initializer = attributeProp.getInitializer();
58
- if (!initializer || !Node.isStringLiteral(initializer)) {
59
- return null;
60
- }
61
- return initializer.getLiteralValue();
62
- }
63
- export function extractClassProperties(classDecl, applyRuntimeFieldDenylist = false) {
64
- const props = [];
65
- for (const property of classDecl.getProperties()) {
66
- if (property.isStatic())
67
- continue;
68
- if (hasInternalJsDocTag(property))
69
- continue;
70
- if (isNonPublicLitMember(property))
71
- continue;
72
- const name = property.getName();
73
- if (name.startsWith('#') || isInternalRuntimeField(name, applyRuntimeFieldDenylist))
74
- continue;
75
- const scope = property.getScope();
76
- if (scope === 'private' || scope === 'protected')
77
- continue;
78
- const initializer = property.getInitializer();
79
- const hasDecorators = property.getDecorators().length > 0;
80
- // Skip undecorated arrow-function properties (internal event handlers / callbacks)
81
- if (!hasDecorators && initializer && Node.isArrowFunction(initializer)) {
82
- continue;
83
- }
84
- // Skip properties initialized with this.attachInternals() (ElementInternals)
85
- if (initializer &&
86
- Node.isCallExpression(initializer) &&
87
- initializer.getExpression().getText() === 'this.attachInternals') {
88
- continue;
89
- }
90
- const typeNode = property.getTypeNode();
91
- const type = typeNode ? typeNode.getText() : 'any';
92
- const publicName = name.startsWith('_') && hasDecorators ? getExplicitLitPropertyAttributeName(property) : null;
93
- let defaultValue;
94
- if (initializer) {
95
- if (Node.isStringLiteral(initializer)) {
96
- defaultValue = initializer.getLiteralValue();
97
- }
98
- else {
99
- defaultValue = initializer.getText();
100
- }
101
- }
102
- props.push({
103
- name: publicName ? kebabToCamel(publicName) : name,
104
- type,
105
- required: false,
106
- ...(defaultValue !== undefined && { defaultValue }),
107
- sourceStartLine: property.getStartLineNumber(),
108
- sourceEndLine: property.getEndLineNumber(),
109
- });
110
- }
111
- return props;
112
- }
113
- export function extractAccessorProperties(classDecl, applyRuntimeFieldDenylist = false) {
114
- const props = [];
115
- const accessors = new Map();
116
- for (const getter of classDecl.getGetAccessors()) {
117
- if (getter.isStatic())
118
- continue;
119
- if (hasInternalJsDocTag(getter))
120
- continue;
121
- if (isNonPublicLitMember(getter))
122
- continue;
123
- const name = getter.getName();
124
- if (name === 'observedAttributes' ||
125
- name === 'template' ||
126
- name.startsWith('#') ||
127
- isInternalRuntimeField(name, applyRuntimeFieldDenylist))
128
- continue;
129
- const scope = getter.getScope();
130
- if (scope === 'private' || scope === 'protected')
131
- continue;
132
- const entry = accessors.get(name) ?? {};
133
- entry.getter = getter;
134
- accessors.set(name, entry);
135
- }
136
- for (const setter of classDecl.getSetAccessors()) {
137
- if (setter.isStatic())
138
- continue;
139
- if (hasInternalJsDocTag(setter))
140
- continue;
141
- if (isNonPublicLitMember(setter))
142
- continue;
143
- const name = setter.getName();
144
- if (name === 'observedAttributes' ||
145
- name === 'template' ||
146
- name.startsWith('#') ||
147
- isInternalRuntimeField(name, applyRuntimeFieldDenylist))
148
- continue;
149
- const scope = setter.getScope();
150
- if (scope === 'private' || scope === 'protected')
151
- continue;
152
- const entry = accessors.get(name) ?? {};
153
- entry.setter = setter;
154
- accessors.set(name, entry);
155
- }
156
- for (const [name, { getter, setter }] of accessors) {
157
- const hasPropertyDecorator = getter?.getDecorators().some((decorator) => decorator.getName() === 'property') ||
158
- setter?.getDecorators().some((decorator) => decorator.getName() === 'property');
159
- if (!hasPropertyDecorator && !(getter && setter))
160
- continue;
161
- const type = getter?.getReturnTypeNode()?.getText() ?? setter?.getParameters()[0]?.getTypeNode()?.getText() ?? 'any';
162
- const accessorNode = getter ?? setter;
163
- props.push({
164
- name,
165
- type,
166
- required: false,
167
- ...(accessorNode
168
- ? {
169
- sourceStartLine: accessorNode.getStartLineNumber(),
170
- sourceEndLine: accessorNode.getEndLineNumber(),
171
- }
172
- : {}),
173
- });
174
- }
175
- return props;
176
- }
177
- export function extractJsDocAttributeProps(classDecl) {
178
- const props = [];
179
- for (const doc of classDecl.getJsDocs()) {
180
- for (const tag of doc.getTags()) {
181
- if (tag.getTagName() !== 'attribute')
182
- continue;
183
- const tagComment = tag.getCommentText();
184
- const normalizedComment = Array.isArray(tagComment) ? tagComment.join(' ') : tagComment;
185
- const match = normalizedComment?.match(/(?:\{([^}]+)\}\s+)?([^\s]+)(?:\s*-\s*([\s\S]*))?/);
186
- if (!match)
187
- continue;
188
- const [, type, name, description] = match;
189
- if (!name || name.startsWith('#'))
190
- continue;
191
- props.push({
192
- name,
193
- type: type?.trim() || 'any',
194
- required: false,
195
- ...(description ? { description: description.replace(/\s+/g, ' ').trim() } : {}),
196
- });
197
- }
198
- }
199
- return props;
200
- }
201
- export function mergePropLists(...propLists) {
202
- const merged = new Map();
203
- for (const propList of propLists) {
204
- for (const prop of propList) {
205
- merged.set(prop.name, prop);
206
- }
207
- }
208
- return [...merged.values()].sort((a, b) => a.name.localeCompare(b.name));
209
- }
210
- export function mergeProps(observed, classProps) {
211
- return mergePropLists(observed, classProps);
212
- }
@@ -1,10 +0,0 @@
1
- import type { ExtractionEndpointRequest, ExtractionEndpointResponse } from '../model/contract.js';
2
- export type { ExtractionEndpointProgress, ExtractionEndpointRequest, ExtractionEndpointResponse, } from '../model/contract.js';
3
- /**
4
- * Stable in-process extraction contract.
5
- *
6
- * The controller owns public request validation and progress translation. The
7
- * extraction service owns source analysis and quality policy; neither layer
8
- * scans directories, invokes agents, or persists sessions.
9
- */
10
- export declare function extractEndpoint(request: ExtractionEndpointRequest): Promise<ExtractionEndpointResponse>;
@@ -1,56 +0,0 @@
1
- import { runExtractionService } from '../services/extraction-service.js';
2
- function validateRequest(request) {
3
- if (request === null || typeof request !== 'object') {
4
- throw new TypeError('extractEndpoint requires a request object');
5
- }
6
- if (!Array.isArray(request.filePaths)) {
7
- throw new TypeError('extractEndpoint requires filePaths to be an array');
8
- }
9
- if (request.filePaths.some((filePath) => typeof filePath !== 'string' || filePath.trim().length === 0)) {
10
- throw new TypeError('extractEndpoint requires filePaths to contain non-empty strings');
11
- }
12
- if (request.projectRoot !== undefined &&
13
- (typeof request.projectRoot !== 'string' || request.projectRoot.trim().length === 0)) {
14
- throw new TypeError('extractEndpoint requires projectRoot to be a non-empty string when provided');
15
- }
16
- if (request.resolveUnreachable !== undefined &&
17
- request.resolveUnreachable !== 'auto' &&
18
- request.resolveUnreachable !== 'always' &&
19
- request.resolveUnreachable !== 'never') {
20
- throw new TypeError("extractEndpoint requires resolveUnreachable to be 'auto', 'always', or 'never'");
21
- }
22
- if (request.onProgress !== undefined && typeof request.onProgress !== 'function') {
23
- throw new TypeError('extractEndpoint requires onProgress to be a function when provided');
24
- }
25
- }
26
- /**
27
- * Stable in-process extraction contract.
28
- *
29
- * The controller owns public request validation and progress translation. The
30
- * extraction service owns source analysis and quality policy; neither layer
31
- * scans directories, invokes agents, or persists sessions.
32
- */
33
- export async function extractEndpoint(request) {
34
- validateRequest(request);
35
- const filePaths = [...request.filePaths];
36
- const result = await runExtractionService({
37
- filePaths,
38
- ...(request.resolveUnreachable !== undefined ? { resolveUnreachable: request.resolveUnreachable } : {}),
39
- ...(request.projectRoot !== undefined ? { projectRoot: request.projectRoot } : {}),
40
- onProgress: ({ filesProcessed, componentsFound }) => {
41
- request.onProgress?.({
42
- phase: 'extract',
43
- filesProcessed,
44
- totalFiles: filePaths.length,
45
- componentsFound,
46
- });
47
- },
48
- });
49
- request.onProgress?.({
50
- phase: 'extract',
51
- filesProcessed: filePaths.length,
52
- totalFiles: filePaths.length,
53
- componentsFound: result.components.length,
54
- });
55
- return result;
56
- }