@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
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-4a4b9e84.0",
3
+ "version": "2.34.5-dev-build-9dff4380.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 '../../model/component.js';
1
+ import type { ComponentExtractionResult } from '../types.js';
2
2
  export declare function extractAstroComponents(filePaths: string[], onProgress?: (p: {
3
3
  filesProcessed: number;
4
4
  componentsFound: number;
@@ -1,10 +1,9 @@
1
1
  import os from 'node:os';
2
2
  import { basename } from 'node:path';
3
3
  import { Project, Node } from 'ts-morph';
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';
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';
8
7
  function extractAllowedValues(typeText) {
9
8
  // Check if the type is a union of string literals like 'a' | 'b' | 'c'
10
9
  const parts = typeText.split('|').map((p) => p.trim());
@@ -1,9 +1,10 @@
1
+ import type { Project, SourceFile } from 'ts-morph';
1
2
  export type FileExtractionOutcome<T, M = undefined> = {
2
3
  item: T | null;
3
4
  warnings?: string[];
4
5
  metadata?: M;
5
6
  };
6
- export type FileExtractionProgress = {
7
+ type FileExtractionProgress = {
7
8
  filesProcessed: number;
8
9
  componentsFound: number;
9
10
  };
@@ -11,3 +12,14 @@ export declare function runFileExtractionWorkers<T, M = undefined>(filePaths: st
11
12
  items: T[];
12
13
  warnings: string[];
13
14
  }>;
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,3 +27,22 @@ 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
+ }
@@ -1,4 +1,4 @@
1
- import type { RawComponentDefinition } from '../../model/component.js';
1
+ import type { RawComponentDefinition } from '../types.js';
2
2
  interface NonAuthorableResult {
3
3
  skip: boolean;
4
4
  reason?: string;
@@ -0,0 +1,7 @@
1
+ import type { ComponentExtractionResult, ExtractorOptions } from '../types.js';
2
+ type ExtractProgress = {
3
+ filesProcessed: number;
4
+ componentsFound: number;
5
+ };
6
+ export declare function extractComponents(filePaths: string[], onProgress?: (progress: ExtractProgress) => void, opts?: ExtractorOptions): Promise<ComponentExtractionResult>;
7
+ export {};
@@ -1,3 +1,57 @@
1
+ import { extractStencilComponents } from './stencil.js';
2
+ import { extractReactComponents } from './react.js';
3
+ import { extractVueTsxComponents } from './vue-tsx.js';
4
+ import { extractVueComponents } from './vue.js';
5
+ import { extractAstroComponents } from './astro.js';
6
+ import { extractWebComponentDefinitions } from './web-components.js';
7
+ import { extractSvelteComponents } from './svelte.js';
8
+ const extractors = [
9
+ {
10
+ name: 'stencil',
11
+ fileFilter: (f) => /\.[jt]sx$/.test(f),
12
+ extract: extractStencilComponents,
13
+ },
14
+ {
15
+ name: 'react',
16
+ fileFilter: (f) => /\.[jt]sx?$/.test(f) && !f.endsWith('.d.ts'),
17
+ extract: extractReactComponents,
18
+ },
19
+ {
20
+ name: 'vue-tsx',
21
+ fileFilter: (f) => /\.[jt]sx?$/.test(f) && !f.endsWith('.d.ts'),
22
+ extract: extractVueTsxComponents,
23
+ },
24
+ {
25
+ name: 'vue',
26
+ fileFilter: (f) => f.endsWith('.vue'),
27
+ extract: extractVueComponents,
28
+ },
29
+ {
30
+ name: 'astro',
31
+ fileFilter: (f) => f.endsWith('.astro'),
32
+ extract: extractAstroComponents,
33
+ },
34
+ {
35
+ name: 'web-components',
36
+ fileFilter: (f) => /\.[jt]s$/.test(f) && !/\.[jt]sx$/.test(f) && !f.endsWith('.d.ts'),
37
+ extract: extractWebComponentDefinitions,
38
+ },
39
+ {
40
+ name: 'svelte',
41
+ fileFilter: (f) => {
42
+ if (!f.endsWith('.svelte'))
43
+ return false;
44
+ // SvelteKit route conventions: +page.svelte, +layout.svelte, +error.svelte
45
+ // are framework-managed entrypoints, not authorable design-system components.
46
+ // Mirrors how Next.js page.tsx / layout.tsx are filtered in pre-classify.
47
+ const filename = f.replace(/\\/g, '/').split('/').pop() ?? '';
48
+ if (/^\+(page|layout|error)\.svelte$/.test(filename))
49
+ return false;
50
+ return true;
51
+ },
52
+ extract: extractSvelteComponents,
53
+ },
54
+ ];
1
55
  function getPathPreferenceScore(filePath) {
2
56
  const normalized = filePath.replace(/\\/g, '/');
3
57
  const segments = normalized.split('/').filter(Boolean);
@@ -36,8 +90,9 @@ function getScopeInfo(filePath) {
36
90
  const normalized = filePath.replace(/\\/g, '/');
37
91
  const segments = normalized.split('/').filter(Boolean);
38
92
  const rootInfo = getPackageRootInfo(segments);
39
- if (!rootInfo)
93
+ if (!rootInfo) {
40
94
  return null;
95
+ }
41
96
  const { rootSegments, relativeSegments } = rootInfo;
42
97
  return {
43
98
  rootKey: rootSegments.join('/'),
@@ -99,35 +154,44 @@ function isWithinTopLevelFamily(relativeSegments, componentName) {
99
154
  if (relativeSegments.length === 1) {
100
155
  return new Set([`${componentName}.tsx`, `${componentName}.ts`, `${componentName}.vue`]).has(first);
101
156
  }
102
- if (first === 'src' && second === 'components' && third === componentName)
157
+ if (first === 'src' && second === 'components' && third === componentName) {
103
158
  return true;
104
- if (first === 'components' && second === componentName)
159
+ }
160
+ if (first === 'components' && second === componentName) {
105
161
  return true;
106
- if (first === 'src' && second === componentName)
162
+ }
163
+ if (first === 'src' && second === componentName) {
107
164
  return true;
108
- if (first === componentName)
165
+ }
166
+ if (first === componentName) {
109
167
  return componentFilenames.has(second) || relativeSegments.length > 2;
168
+ }
110
169
  return false;
111
170
  }
112
171
  function getFamilyScopeKey(filePath, componentName, topLevelFamiliesByRoot) {
113
172
  const normalized = filePath.replace(/\\/g, '/');
114
173
  const scopeInfo = getScopeInfo(filePath);
115
- if (!scopeInfo)
174
+ if (!scopeInfo) {
116
175
  return normalized;
176
+ }
117
177
  const { rootKey, relativeSegments } = scopeInfo;
118
178
  const topLevelFamilies = topLevelFamiliesByRoot.get(rootKey);
119
179
  if (topLevelFamilies?.has(componentName) && isWithinTopLevelFamily(relativeSegments, componentName)) {
120
180
  return `${rootKey}::${componentName}`;
121
181
  }
122
182
  const [first, second, third] = relativeSegments;
123
- if (first === 'src' && second === 'components' && third)
183
+ if (first === 'src' && second === 'components' && third) {
124
184
  return `${rootKey}/src/components/${third}`;
125
- if (first === 'components' && second)
185
+ }
186
+ if (first === 'components' && second) {
126
187
  return `${rootKey}/components/${second}`;
127
- if (first === 'src' && second && relativeSegments.length > 2)
188
+ }
189
+ if (first === 'src' && second && relativeSegments.length > 2) {
128
190
  return `${rootKey}/src/${second}`;
129
- if (first && relativeSegments.length > 1)
191
+ }
192
+ if (first && relativeSegments.length > 1) {
130
193
  return `${rootKey}/${first}`;
194
+ }
131
195
  return rootKey;
132
196
  }
133
197
  function choosePreferredComponent(existing, candidate) {
@@ -160,9 +224,53 @@ function choosePreferredComponent(existing, candidate) {
160
224
  reason: `kept ${existing.source} over ${candidate.source} by stable first-seen order`,
161
225
  };
162
226
  }
163
- export function deduplicateComponents(results) {
164
- const allWarnings = [];
227
+ export async function extractComponents(filePaths, onProgress, opts) {
228
+ const filesByExtractor = new Map();
165
229
  const exclusions = [];
230
+ for (const extractor of extractors) {
231
+ filesByExtractor.set(extractor, []);
232
+ }
233
+ for (const filePath of filePaths) {
234
+ for (const extractor of extractors) {
235
+ if (extractor.fileFilter(filePath)) {
236
+ filesByExtractor.get(extractor).push(filePath);
237
+ }
238
+ else if (extractor.name === 'svelte' && filePath.endsWith('.svelte')) {
239
+ const filename = filePath.replace(/\\/g, '/').split('/').pop() ?? '';
240
+ if (/^\+(page|layout|error)\.svelte$/.test(filename)) {
241
+ exclusions.push({
242
+ itemType: 'file',
243
+ name: filename,
244
+ source: filePath,
245
+ reason: 'SvelteKit route entrypoints are framework-managed, not authorable components',
246
+ stage: 'file-filter',
247
+ });
248
+ }
249
+ }
250
+ }
251
+ }
252
+ const perExtractorFiles = new Map();
253
+ const perExtractorComponents = new Map();
254
+ let totalFilesProcessed = 0;
255
+ let totalComponentsFound = 0;
256
+ const results = await Promise.all(extractors.map(async (extractor) => {
257
+ const files = filesByExtractor.get(extractor);
258
+ if (files.length === 0)
259
+ return { components: [], warnings: [] };
260
+ perExtractorFiles.set(extractor, 0);
261
+ perExtractorComponents.set(extractor, 0);
262
+ const result = await extractor.extract(files, (p) => {
263
+ const prevFiles = perExtractorFiles.get(extractor) ?? 0;
264
+ const prevComponents = perExtractorComponents.get(extractor) ?? 0;
265
+ totalFilesProcessed += p.filesProcessed - prevFiles;
266
+ totalComponentsFound += p.componentsFound - prevComponents;
267
+ perExtractorFiles.set(extractor, p.filesProcessed);
268
+ perExtractorComponents.set(extractor, p.componentsFound);
269
+ onProgress?.({ filesProcessed: totalFilesProcessed, componentsFound: totalComponentsFound });
270
+ }, opts);
271
+ return result;
272
+ }));
273
+ const allWarnings = [];
166
274
  const componentsByKey = new Map();
167
275
  const keysByName = new Map();
168
276
  const topLevelFamiliesByRoot = new Map();
@@ -211,8 +319,24 @@ export function deduplicateComponents(results) {
211
319
  keysByName.set(component.name, [...existingKeys, identityKey]);
212
320
  }
213
321
  }
322
+ const allComponents = [...componentsByKey.values()];
323
+ const filteredComponents = [];
324
+ for (const component of allComponents) {
325
+ if (/^use[A-Z]/.test(component.name)) {
326
+ allWarnings.push(`Skipped hook: ${component.name} (hooks are not renderable components)`);
327
+ exclusions.push({
328
+ itemType: 'component',
329
+ name: component.name,
330
+ source: component.source,
331
+ reason: 'Hook names are not renderable components',
332
+ stage: 'component-filter',
333
+ });
334
+ continue;
335
+ }
336
+ filteredComponents.push(component);
337
+ }
214
338
  return {
215
- components: [...componentsByKey.values()].sort((a, b) => a.name.localeCompare(b.name)),
339
+ components: filteredComponents.sort((a, b) => a.name.localeCompare(b.name)),
216
340
  warnings: allWarnings,
217
341
  exclusions,
218
342
  };
@@ -1,2 +1,2 @@
1
- import type { ComponentExtractionResult } from '../../model/component.js';
1
+ import type { ComponentExtractionResult } from '../types.js';
2
2
  export declare function extractReactComponents(filePaths: string[]): Promise<ComponentExtractionResult>;