@contentful/experience-design-system-extraction 2.34.5-dev-build-12a524fb.0 → 2.34.5-dev-build-5cd8bcd6.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.
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-12a524fb.0",
3
+ "version": "2.34.5-dev-build-5cd8bcd6.0",
4
4
  "description": "Component extraction engine for the Contentful Experience Design System SDK",
5
5
  "license": "MIT",
6
6
  "type": "module",
@@ -226,6 +226,7 @@ function choosePreferredComponent(existing, candidate) {
226
226
  }
227
227
  export async function extractComponents(filePaths, onProgress, opts) {
228
228
  const filesByExtractor = new Map();
229
+ const exclusions = [];
229
230
  for (const extractor of extractors) {
230
231
  filesByExtractor.set(extractor, []);
231
232
  }
@@ -234,6 +235,18 @@ export async function extractComponents(filePaths, onProgress, opts) {
234
235
  if (extractor.fileFilter(filePath)) {
235
236
  filesByExtractor.get(extractor).push(filePath);
236
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
+ }
237
250
  }
238
251
  }
239
252
  const perExtractorFiles = new Map();
@@ -276,6 +289,7 @@ export async function extractComponents(filePaths, onProgress, opts) {
276
289
  }
277
290
  for (const result of results) {
278
291
  allWarnings.push(...result.warnings);
292
+ exclusions.push(...(result.exclusions ?? []));
279
293
  for (const component of result.components) {
280
294
  const scopeKey = getFamilyScopeKey(component.source, component.name, topLevelFamiliesByRoot);
281
295
  const identityKey = `${component.name}::${scopeKey}`;
@@ -283,6 +297,13 @@ export async function extractComponents(filePaths, onProgress, opts) {
283
297
  if (existing) {
284
298
  const selected = choosePreferredComponent(existing, component);
285
299
  allWarnings.push(`Duplicate component "${component.name}" found in ${component.source} (already seen in ${existing.source}); ${selected.reason}`);
300
+ exclusions.push({
301
+ itemType: 'component',
302
+ name: selected.loser.name,
303
+ source: selected.loser.source,
304
+ reason: `duplicate identity; ${selected.reason}`,
305
+ stage: 'duplicate-filter',
306
+ });
286
307
  componentsByKey.set(identityKey, selected.winner);
287
308
  continue;
288
309
  }
@@ -303,6 +324,13 @@ export async function extractComponents(filePaths, onProgress, opts) {
303
324
  for (const component of allComponents) {
304
325
  if (/^use[A-Z]/.test(component.name)) {
305
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
+ });
306
334
  continue;
307
335
  }
308
336
  filteredComponents.push(component);
@@ -310,5 +338,6 @@ export async function extractComponents(filePaths, onProgress, opts) {
310
338
  return {
311
339
  components: filteredComponents.sort((a, b) => a.name.localeCompare(b.name)),
312
340
  warnings: allWarnings,
341
+ exclusions,
313
342
  };
314
343
  }
@@ -1,5 +1,5 @@
1
1
  import { Node, SyntaxKind, } from 'ts-morph';
2
- import { extractAllowedValues, getNodeDefinitions, getTypeReferenceName, getTypeTargetDeclarations, getTsxExtractionContext, getValueTargetDeclarations, getJsxTagNameNode, isIntrinsicJsxElement, resolveDefaultExportName, } from './tsx-shared.js';
2
+ import { extractAllowedValues, getNodeDefinitions, getTypeReferenceName, getTypeTargetDeclarations, extractTsxComponents, getValueTargetDeclarations, getJsxTagNameNode, isIntrinsicJsxElement, isCompositionHookName, resolveComponentExportName, } from './tsx-shared.js';
3
3
  import { shouldBeSlot } from './slot-detection.js';
4
4
  import { extractAllowedComponentsFromTypeText, extractAllowedComponentsFromJsdoc } from './slot-allowed-components.js';
5
5
  import { getSourceLineMetadata } from './source-line-metadata.js';
@@ -1626,29 +1626,18 @@ function hasImplementationChildrenHint(funcNode, param) {
1626
1626
  .some((attr) => isComponentSpreadAttribute(attr, new Set([paramName])));
1627
1627
  }
1628
1628
  export async function extractReactComponents(filePaths) {
1629
- const extractionContext = getTsxExtractionContext(filePaths, /\.[jt]sx$/);
1629
+ const exclusions = [];
1630
+ const extractionContext = extractTsxComponents(filePaths, /\.[jt]sx$/, (sourceFile) => {
1631
+ if (isStencilFile(sourceFile))
1632
+ return [];
1633
+ const fileExports = [...sourceFile.getExportedDeclarations().keys()];
1634
+ const isNext = isNextJsComponent(sourceFile.getFilePath(), fileExports);
1635
+ return extractFromSourceFile(sourceFile, isNext, exclusions);
1636
+ });
1630
1637
  if (!extractionContext) {
1631
1638
  return { components: [], warnings: [] };
1632
1639
  }
1633
- const { componentFiles, project } = extractionContext;
1634
- const warnings = [];
1635
- const components = [];
1636
- for (const filePath of componentFiles) {
1637
- try {
1638
- const sourceFile = project.getSourceFile(filePath);
1639
- if (!sourceFile)
1640
- continue;
1641
- if (isStencilFile(sourceFile))
1642
- continue;
1643
- const fileExports = [...sourceFile.getExportedDeclarations().keys()];
1644
- const isNext = isNextJsComponent(sourceFile.getFilePath(), fileExports);
1645
- const extracted = extractFromSourceFile(sourceFile, isNext);
1646
- components.push(...extracted);
1647
- }
1648
- catch (e) {
1649
- warnings.push(`Failed to extract from ${filePath}: ${e instanceof Error ? e.message : String(e)}`);
1650
- }
1651
- }
1640
+ const { project, components, warnings } = extractionContext;
1652
1641
  const propsToComponent = new Map();
1653
1642
  const componentNames = new Set();
1654
1643
  for (const c of components) {
@@ -1764,24 +1753,27 @@ export async function extractReactComponents(filePaths) {
1764
1753
  return {
1765
1754
  components: components.sort((a, b) => a.name.localeCompare(b.name)),
1766
1755
  warnings,
1756
+ exclusions,
1767
1757
  };
1768
1758
  }
1769
- function extractFromSourceFile(sourceFile, isNext) {
1759
+ function extractFromSourceFile(sourceFile, isNext, exclusions) {
1770
1760
  const components = [];
1771
1761
  const exported = sourceFile.getExportedDeclarations();
1772
1762
  const usesCreateContext = sourceFileUsesCreateContext(sourceFile);
1773
1763
  for (const [exportKey, declarations] of exported) {
1774
- let name = exportKey;
1775
- if (exportKey === 'default') {
1776
- const defaultExportName = resolveDefaultExportName(declarations, exported, true);
1777
- if (!defaultExportName)
1778
- continue;
1779
- name = defaultExportName;
1780
- }
1781
- if (!/^[A-Z]/.test(name))
1764
+ const name = resolveComponentExportName(exportKey, declarations, exported, true);
1765
+ if (!name)
1782
1766
  continue;
1783
- if (name.startsWith('use'))
1767
+ if (isCompositionHookName(name)) {
1768
+ exclusions.push({
1769
+ itemType: 'component',
1770
+ name,
1771
+ source: sourceFile.getFilePath(),
1772
+ reason: 'React hook names are not renderable components',
1773
+ stage: 'component-filter',
1774
+ });
1784
1775
  continue;
1776
+ }
1785
1777
  const funcNode = resolveBestFunctionNode(declarations);
1786
1778
  if (!funcNode)
1787
1779
  continue;
@@ -1,8 +1,18 @@
1
- import { Node, Project, type Type } from 'ts-morph';
1
+ import { Node, Project, type SourceFile, type Type } from 'ts-morph';
2
2
  export declare function getTsxExtractionContext(filePaths: string[], componentFilePattern: RegExp): {
3
3
  componentFiles: string[];
4
4
  project: Project;
5
5
  } | undefined;
6
+ export declare function extractTsxComponents<T>(filePaths: string[], componentFilePattern: RegExp, extractSourceFile: (sourceFile: SourceFile) => T[]): {
7
+ componentFiles: string[];
8
+ project: Project;
9
+ components: T[];
10
+ warnings: string[];
11
+ } | undefined;
12
+ export declare function resolveComponentExportName(exportKey: string, declarations: Node[], exported: {
13
+ has(name: string): boolean;
14
+ }, allowVariableDeclaration?: boolean): string | undefined;
15
+ export declare function isCompositionHookName(name: string): boolean;
6
16
  export declare function resolveDefaultExportName(declarations: Node[], exported: {
7
17
  has(name: string): boolean;
8
18
  }, allowVariableDeclaration?: boolean): string | undefined;
@@ -35,6 +35,32 @@ export function getTsxExtractionContext(filePaths, componentFilePattern) {
35
35
  project: createTsxProject(getTsxProjectFiles(filePaths)),
36
36
  };
37
37
  }
38
+ export function extractTsxComponents(filePaths, componentFilePattern, extractSourceFile) {
39
+ const extractionContext = getTsxExtractionContext(filePaths, componentFilePattern);
40
+ if (!extractionContext)
41
+ return undefined;
42
+ const warnings = [];
43
+ const components = [];
44
+ for (const filePath of extractionContext.componentFiles) {
45
+ try {
46
+ const sourceFile = extractionContext.project.getSourceFile(filePath);
47
+ if (!sourceFile)
48
+ continue;
49
+ components.push(...extractSourceFile(sourceFile));
50
+ }
51
+ catch (e) {
52
+ warnings.push(`Failed to extract from ${filePath}: ${e instanceof Error ? e.message : String(e)}`);
53
+ }
54
+ }
55
+ return { ...extractionContext, components, warnings };
56
+ }
57
+ export function resolveComponentExportName(exportKey, declarations, exported, allowVariableDeclaration = false) {
58
+ const name = exportKey === 'default' ? resolveDefaultExportName(declarations, exported, allowVariableDeclaration) : exportKey;
59
+ return name && /^[A-Z]/.test(name) ? name : undefined;
60
+ }
61
+ export function isCompositionHookName(name) {
62
+ return name.startsWith('use');
63
+ }
38
64
  export function resolveDefaultExportName(declarations, exported, allowVariableDeclaration = false) {
39
65
  const declaration = declarations[0];
40
66
  const name = Node.isFunctionDeclaration(declaration)
@@ -1,44 +1,35 @@
1
1
  import { Node, SyntaxKind } from 'ts-morph';
2
- import { extractAllowedValues, getTypeReferenceName, getTypeTargetDeclarations, getTsxExtractionContext, getValueTargetDeclarations, resolveDefaultExportName, } from './tsx-shared.js';
2
+ import { extractAllowedValues, extractTsxComponents, getTypeReferenceName, getTypeTargetDeclarations, getValueTargetDeclarations, isCompositionHookName, resolveComponentExportName, } from './tsx-shared.js';
3
3
  export async function extractVueTsxComponents(filePaths) {
4
- const extractionContext = getTsxExtractionContext(filePaths, /\.tsx$/);
4
+ const exclusions = [];
5
+ const extractionContext = extractTsxComponents(filePaths, /\.tsx$/, (sourceFile) => extractFromSourceFile(sourceFile, exclusions));
5
6
  if (!extractionContext) {
6
7
  return { components: [], warnings: [] };
7
8
  }
8
- const { componentFiles, project } = extractionContext;
9
- const warnings = [];
10
- const components = [];
11
- for (const filePath of componentFiles) {
12
- try {
13
- const sourceFile = project.getSourceFile(filePath);
14
- if (!sourceFile)
15
- continue;
16
- components.push(...extractFromSourceFile(sourceFile));
17
- }
18
- catch (e) {
19
- warnings.push(`Failed to extract from ${filePath}: ${e instanceof Error ? e.message : String(e)}`);
20
- }
21
- }
9
+ const { components, warnings } = extractionContext;
22
10
  return {
23
11
  components: components.sort((a, b) => a.name.localeCompare(b.name)),
24
12
  warnings,
13
+ exclusions,
25
14
  };
26
15
  }
27
- function extractFromSourceFile(sourceFile) {
16
+ function extractFromSourceFile(sourceFile, exclusions) {
28
17
  const components = [];
29
18
  const exported = sourceFile.getExportedDeclarations();
30
19
  for (const [exportKey, declarations] of exported) {
31
- let name = exportKey;
32
- if (exportKey === 'default') {
33
- const defaultExportName = resolveDefaultExportName(declarations, exported);
34
- if (!defaultExportName)
35
- continue;
36
- name = defaultExportName;
37
- }
38
- if (!/^[A-Z]/.test(name))
20
+ const name = resolveComponentExportName(exportKey, declarations, exported);
21
+ if (!name)
39
22
  continue;
40
- if (name.startsWith('use'))
23
+ if (isCompositionHookName(name)) {
24
+ exclusions.push({
25
+ itemType: 'component',
26
+ name,
27
+ source: sourceFile.getFilePath(),
28
+ reason: 'Vue composition hook names are not renderable components',
29
+ stage: 'component-filter',
30
+ });
41
31
  continue;
32
+ }
42
33
  const component = extractVueTsxComponent(declarations, name, sourceFile);
43
34
  if (component) {
44
35
  components.push(component);
@@ -1,4 +1,4 @@
1
- export type { RawComponentDefinition, RawPropDefinition, RawSlotDefinition, ComponentExtractionResult, ExtractorProgress, ExtractorOptions, ComponentExtractor, ExtractionValidationIssue, ExtractionValidationIssueCode, } from './types.js';
1
+ export type { RawComponentDefinition, RawPropDefinition, RawSlotDefinition, ComponentExtractionResult, ExtractionExclusion, ExtractorProgress, ExtractorOptions, ComponentExtractor, ExtractionValidationIssue, ExtractionValidationIssueCode, } from './types.js';
2
2
  export { stripScoringFields } from './types.js';
3
3
  export { extractComponents } from './extract/pipeline.js';
4
4
  export { extractReactComponents } from './extract/react.js';
@@ -62,6 +62,15 @@ export interface RawComponentDefinition {
62
62
  export interface ComponentExtractionResult {
63
63
  components: RawComponentDefinition[];
64
64
  warnings: string[];
65
+ exclusions?: ExtractionExclusion[];
66
+ }
67
+ /** A deterministic item removed before the interactive scope gate. */
68
+ export interface ExtractionExclusion {
69
+ itemType: 'component' | 'file' | 'prop';
70
+ name: string;
71
+ source?: string;
72
+ reason: string;
73
+ stage: string;
65
74
  }
66
75
  export type ExtractorProgress = {
67
76
  filesProcessed: number;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@contentful/experience-design-system-extraction",
3
- "version": "2.34.5-dev-build-12a524fb.0",
3
+ "version": "2.34.5-dev-build-5cd8bcd6.0",
4
4
  "description": "Component extraction engine for the Contentful Experience Design System SDK",
5
5
  "license": "MIT",
6
6
  "type": "module",