@contentful/experience-design-system-extraction 2.33.1-dev-build-d7fc540.0 → 2.33.1-dev-build-1b6f259.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 +1 -1
- package/dist/src/extract/astro.js +40 -102
- package/dist/src/extract/file-extraction-workers.d.ts +25 -0
- package/dist/src/extract/file-extraction-workers.js +48 -0
- package/dist/src/extract/non-authorable-filter.d.ts +2 -1
- package/dist/src/extract/parse-imported-names.d.ts +1 -0
- package/dist/src/extract/parse-imported-names.js +27 -0
- package/dist/src/extract/pipeline.d.ts +2 -1
- package/dist/src/extract/react.js +156 -392
- package/dist/src/extract/resolve-local-module.d.ts +3 -0
- package/dist/src/extract/resolve-local-module.js +26 -0
- package/dist/src/extract/resolve-type-property.d.ts +16 -0
- package/dist/src/extract/resolve-type-property.js +11 -0
- package/dist/src/extract/slot-detection.js +11 -14
- package/dist/src/extract/source-inspection.js +1 -27
- package/dist/src/extract/source-line-metadata.d.ts +3 -0
- package/dist/src/extract/source-line-metadata.js +13 -0
- package/dist/src/extract/stencil.js +10 -26
- package/dist/src/extract/svelte.js +11 -58
- package/dist/src/extract/tsx-shared.d.ts +9 -1
- package/dist/src/extract/tsx-shared.js +48 -1
- package/dist/src/extract/validate.d.ts +4 -1
- package/dist/src/extract/validate.js +10 -5
- package/dist/src/extract/vue-tsx.js +20 -43
- package/dist/src/extract/vue.js +17 -64
- package/dist/src/extract/web-components.js +26 -39
- package/dist/src/index.d.ts +2 -1
- package/dist/src/index.js +2 -1
- package/package.json +1 -1
package/dist/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@contentful/experience-design-system-extraction",
|
|
3
|
-
"version": "2.33.1-dev-build-
|
|
3
|
+
"version": "2.33.1-dev-build-1b6f259.0",
|
|
4
4
|
"description": "Component extraction engine for the Contentful Experience Design System SDK",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"type": "module",
|
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
import { basename } from 'node:path';
|
|
2
|
-
import { readFile } from 'node:fs/promises';
|
|
3
2
|
import os from 'node:os';
|
|
4
3
|
import { Project, Node } from 'ts-morph';
|
|
4
|
+
import { createSortedExtractionResult, runFileExtractionWorkers } from './file-extraction-workers.js';
|
|
5
|
+
import { resolveTypeProperty } from './resolve-type-property.js';
|
|
6
|
+
import { getSourceLineMetadata } from './source-line-metadata.js';
|
|
5
7
|
function extractAllowedValues(typeText) {
|
|
6
8
|
// Check if the type is a union of string literals like 'a' | 'b' | 'c'
|
|
7
9
|
const parts = typeText.split('|').map((p) => p.trim());
|
|
@@ -30,8 +32,8 @@ function extractBindingPropName(element) {
|
|
|
30
32
|
return null;
|
|
31
33
|
return element.getPropertyNameNode()?.getText() ?? element.getNameNode().getText();
|
|
32
34
|
}
|
|
33
|
-
function
|
|
34
|
-
|
|
35
|
+
function createAstroFrontmatterProject() {
|
|
36
|
+
return new Project({
|
|
35
37
|
compilerOptions: {
|
|
36
38
|
strict: false,
|
|
37
39
|
target: 99,
|
|
@@ -41,27 +43,33 @@ function extractFallbackPropsFromFrontmatter(frontmatter) {
|
|
|
41
43
|
useInMemoryFileSystem: true,
|
|
42
44
|
skipAddingFilesFromTsConfig: true,
|
|
43
45
|
});
|
|
44
|
-
|
|
45
|
-
|
|
46
|
+
}
|
|
47
|
+
function forEachAstroPropsBinding(frontmatter, visit) {
|
|
48
|
+
const sf = createAstroFrontmatterProject().createSourceFile('__frontmatter__.ts', frontmatter);
|
|
46
49
|
sf.forEachDescendant((node) => {
|
|
47
50
|
if (!Node.isVariableDeclaration(node))
|
|
48
51
|
return;
|
|
49
52
|
const initializer = node.getInitializer();
|
|
50
|
-
if (!usesAstroProps(initializer))
|
|
53
|
+
if (!initializer || !usesAstroProps(initializer))
|
|
51
54
|
return;
|
|
52
55
|
const nameNode = node.getNameNode();
|
|
53
56
|
if (!Node.isObjectBindingPattern(nameNode))
|
|
54
57
|
return;
|
|
55
|
-
for (const element of nameNode.getElements())
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
58
|
+
for (const element of nameNode.getElements())
|
|
59
|
+
visit(element);
|
|
60
|
+
});
|
|
61
|
+
}
|
|
62
|
+
function extractFallbackPropsFromFrontmatter(frontmatter) {
|
|
63
|
+
const props = new Map();
|
|
64
|
+
forEachAstroPropsBinding(frontmatter, (element) => {
|
|
65
|
+
const propName = extractBindingPropName(element);
|
|
66
|
+
if (!propName)
|
|
67
|
+
return;
|
|
68
|
+
props.set(propName, {
|
|
69
|
+
name: propName,
|
|
70
|
+
type: 'any',
|
|
71
|
+
required: !element.getInitializer(),
|
|
72
|
+
});
|
|
65
73
|
});
|
|
66
74
|
return [...props.values()].sort((a, b) => a.name.localeCompare(b.name));
|
|
67
75
|
}
|
|
@@ -82,17 +90,7 @@ function mergeProps(...propGroups) {
|
|
|
82
90
|
return [...merged.values()].sort((a, b) => a.name.localeCompare(b.name));
|
|
83
91
|
}
|
|
84
92
|
function extractPropsFromFrontmatter(frontmatter) {
|
|
85
|
-
const
|
|
86
|
-
compilerOptions: {
|
|
87
|
-
strict: false,
|
|
88
|
-
target: 99, // ESNext
|
|
89
|
-
module: 99, // ESNext
|
|
90
|
-
allowJs: true,
|
|
91
|
-
},
|
|
92
|
-
useInMemoryFileSystem: true,
|
|
93
|
-
skipAddingFilesFromTsConfig: true,
|
|
94
|
-
});
|
|
95
|
-
const sf = project.createSourceFile('__frontmatter__.ts', frontmatter);
|
|
93
|
+
const sf = createAstroFrontmatterProject().createSourceFile('__frontmatter__.ts', frontmatter);
|
|
96
94
|
const props = [];
|
|
97
95
|
// Find `interface Props` or `type Props = ...`
|
|
98
96
|
const propsInterface = sf.getInterface('Props');
|
|
@@ -116,25 +114,17 @@ function extractPropsFromFrontmatter(frontmatter) {
|
|
|
116
114
|
else if (propsTypeAlias) {
|
|
117
115
|
const type = propsTypeAlias.getType();
|
|
118
116
|
for (const property of type.getProperties()) {
|
|
119
|
-
const
|
|
120
|
-
|
|
121
|
-
if (!decl)
|
|
117
|
+
const resolved = resolveTypeProperty(property);
|
|
118
|
+
if (!resolved)
|
|
122
119
|
continue;
|
|
123
|
-
const
|
|
124
|
-
const typeText = propType.getText(decl);
|
|
125
|
-
const required = !property.isOptional();
|
|
120
|
+
const { name, declaration: decl, typeText, required } = resolved;
|
|
126
121
|
const allowedValues = extractAllowedValues(typeText);
|
|
127
122
|
props.push({
|
|
128
123
|
name,
|
|
129
124
|
type: typeText,
|
|
130
125
|
required,
|
|
131
126
|
...(allowedValues && { allowedValues }),
|
|
132
|
-
...(
|
|
133
|
-
? {
|
|
134
|
-
sourceStartLine: decl.getStartLineNumber(),
|
|
135
|
-
sourceEndLine: decl.getEndLineNumber(),
|
|
136
|
-
}
|
|
137
|
-
: {}),
|
|
127
|
+
...getSourceLineMetadata(decl),
|
|
138
128
|
});
|
|
139
129
|
}
|
|
140
130
|
}
|
|
@@ -142,38 +132,15 @@ function extractPropsFromFrontmatter(frontmatter) {
|
|
|
142
132
|
}
|
|
143
133
|
function extractDefaultsFromFrontmatter(frontmatter) {
|
|
144
134
|
const defaults = new Map();
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
target: 99,
|
|
149
|
-
module: 99,
|
|
150
|
-
allowJs: true,
|
|
151
|
-
},
|
|
152
|
-
useInMemoryFileSystem: true,
|
|
153
|
-
skipAddingFilesFromTsConfig: true,
|
|
154
|
-
});
|
|
155
|
-
const sf = project.createSourceFile('__frontmatter__.ts', frontmatter);
|
|
156
|
-
sf.forEachDescendant((node) => {
|
|
157
|
-
if (!Node.isVariableDeclaration(node))
|
|
158
|
-
return;
|
|
159
|
-
const initializer = node.getInitializer();
|
|
160
|
-
if (!initializer)
|
|
135
|
+
forEachAstroPropsBinding(frontmatter, (element) => {
|
|
136
|
+
const propName = extractBindingPropName(element);
|
|
137
|
+
if (!propName)
|
|
161
138
|
return;
|
|
162
|
-
|
|
139
|
+
const elementInitializer = element.getInitializer();
|
|
140
|
+
if (!elementInitializer)
|
|
163
141
|
return;
|
|
164
|
-
const
|
|
165
|
-
|
|
166
|
-
return;
|
|
167
|
-
for (const element of nameNode.getElements()) {
|
|
168
|
-
const propName = extractBindingPropName(element);
|
|
169
|
-
if (!propName)
|
|
170
|
-
continue;
|
|
171
|
-
const elementInitializer = element.getInitializer();
|
|
172
|
-
if (!elementInitializer)
|
|
173
|
-
continue;
|
|
174
|
-
const value = elementInitializer.getText().replace(/^['"]|['"]$/g, '');
|
|
175
|
-
defaults.set(propName, value);
|
|
176
|
-
}
|
|
142
|
+
const value = elementInitializer.getText().replace(/^['"]|['"]$/g, '');
|
|
143
|
+
defaults.set(propName, value);
|
|
177
144
|
});
|
|
178
145
|
return defaults;
|
|
179
146
|
}
|
|
@@ -253,37 +220,8 @@ function extractFromAstroFile(filePath, source) {
|
|
|
253
220
|
const ASTRO_EXTRACT_CONCURRENCY = Number(process.env['EDS_EXTRACT_CONCURRENCY'] ?? 0) || os.cpus().length;
|
|
254
221
|
export async function extractAstroComponents(filePaths, onProgress) {
|
|
255
222
|
const astroFiles = filePaths.filter((f) => f.endsWith('.astro'));
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
}
|
|
259
|
-
|
|
260
|
-
const components = [];
|
|
261
|
-
let filesProcessed = 0;
|
|
262
|
-
let componentsFound = 0;
|
|
263
|
-
const queue = [...astroFiles];
|
|
264
|
-
async function worker() {
|
|
265
|
-
while (queue.length > 0) {
|
|
266
|
-
const filePath = queue.shift();
|
|
267
|
-
if (!filePath)
|
|
268
|
-
break;
|
|
269
|
-
try {
|
|
270
|
-
const source = await readFile(filePath, 'utf-8');
|
|
271
|
-
const component = extractFromAstroFile(filePath, source);
|
|
272
|
-
if (component) {
|
|
273
|
-
components.push(component);
|
|
274
|
-
componentsFound++;
|
|
275
|
-
}
|
|
276
|
-
}
|
|
277
|
-
catch (e) {
|
|
278
|
-
warnings.push(`Failed to extract from ${filePath}: ${e instanceof Error ? e.message : String(e)}`);
|
|
279
|
-
}
|
|
280
|
-
filesProcessed++;
|
|
281
|
-
onProgress?.({ filesProcessed, componentsFound });
|
|
282
|
-
}
|
|
283
|
-
}
|
|
284
|
-
await Promise.all(Array.from({ length: Math.min(ASTRO_EXTRACT_CONCURRENCY, astroFiles.length) }, worker));
|
|
285
|
-
return {
|
|
286
|
-
components: components.sort((a, b) => a.name.localeCompare(b.name)),
|
|
287
|
-
warnings,
|
|
288
|
-
};
|
|
223
|
+
const { items: components, warnings } = await runFileExtractionWorkers(astroFiles, ASTRO_EXTRACT_CONCURRENCY, async (filePath, source) => ({
|
|
224
|
+
item: extractFromAstroFile(filePath, source),
|
|
225
|
+
}), (filePath, error) => `Failed to extract from ${filePath}: ${error instanceof Error ? error.message : String(error)}`, onProgress);
|
|
226
|
+
return createSortedExtractionResult(components, warnings);
|
|
289
227
|
}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import type { Project, SourceFile } from 'ts-morph';
|
|
2
|
+
export type FileExtractionOutcome<T, M = undefined> = {
|
|
3
|
+
item: T | null;
|
|
4
|
+
warnings?: string[];
|
|
5
|
+
metadata?: M;
|
|
6
|
+
};
|
|
7
|
+
type FileExtractionProgress = {
|
|
8
|
+
filesProcessed: number;
|
|
9
|
+
componentsFound: number;
|
|
10
|
+
};
|
|
11
|
+
export declare function runFileExtractionWorkers<T, M = undefined>(filePaths: string[], concurrency: number, extractFile: (filePath: string, source: string) => Promise<FileExtractionOutcome<T, M>>, formatError: (filePath: string, error: unknown) => string, onProgress?: (progress: FileExtractionProgress) => void, onResult?: (filePath: string, outcome: FileExtractionOutcome<T, M>) => void): Promise<{
|
|
12
|
+
items: T[];
|
|
13
|
+
warnings: string[];
|
|
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 {};
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import { readFile } from 'node:fs/promises';
|
|
2
|
+
export async function runFileExtractionWorkers(filePaths, concurrency, extractFile, formatError, onProgress, onResult) {
|
|
3
|
+
const warnings = [];
|
|
4
|
+
const items = [];
|
|
5
|
+
let filesProcessed = 0;
|
|
6
|
+
const queue = [...filePaths];
|
|
7
|
+
async function worker() {
|
|
8
|
+
while (queue.length > 0) {
|
|
9
|
+
const filePath = queue.shift();
|
|
10
|
+
if (!filePath)
|
|
11
|
+
break;
|
|
12
|
+
try {
|
|
13
|
+
const source = await readFile(filePath, 'utf-8');
|
|
14
|
+
const outcome = await extractFile(filePath, source);
|
|
15
|
+
warnings.push(...(outcome.warnings ?? []));
|
|
16
|
+
if (outcome.item)
|
|
17
|
+
items.push(outcome.item);
|
|
18
|
+
onResult?.(filePath, outcome);
|
|
19
|
+
}
|
|
20
|
+
catch (error) {
|
|
21
|
+
warnings.push(formatError(filePath, error));
|
|
22
|
+
}
|
|
23
|
+
filesProcessed++;
|
|
24
|
+
onProgress?.({ filesProcessed, componentsFound: items.length });
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
await Promise.all(Array.from({ length: Math.min(concurrency, filePaths.length) }, worker));
|
|
28
|
+
return { items, warnings };
|
|
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,5 +1,5 @@
|
|
|
1
1
|
import type { RawComponentDefinition } from '../types.js';
|
|
2
|
-
|
|
2
|
+
interface NonAuthorableResult {
|
|
3
3
|
skip: boolean;
|
|
4
4
|
reason?: string;
|
|
5
5
|
}
|
|
@@ -14,3 +14,4 @@ export interface NonAuthorableResult {
|
|
|
14
14
|
* other repos.
|
|
15
15
|
*/
|
|
16
16
|
export declare function isNonAuthorableComponent(component: RawComponentDefinition): NonAuthorableResult;
|
|
17
|
+
export {};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function parseImportedNames(importClause: string): string[];
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
export function parseImportedNames(importClause) {
|
|
2
|
+
const trimmed = importClause.trim();
|
|
3
|
+
if (!trimmed)
|
|
4
|
+
return [];
|
|
5
|
+
const names = [];
|
|
6
|
+
const defaultAndNamed = trimmed.split(',').map((part) => part.trim());
|
|
7
|
+
for (const part of defaultAndNamed) {
|
|
8
|
+
if (!part)
|
|
9
|
+
continue;
|
|
10
|
+
if (part.startsWith('{') && part.endsWith('}')) {
|
|
11
|
+
for (const named of part
|
|
12
|
+
.slice(1, -1)
|
|
13
|
+
.split(',')
|
|
14
|
+
.map((item) => item.trim())
|
|
15
|
+
.filter(Boolean)) {
|
|
16
|
+
const [local] = named.split(/\s+as\s+/i);
|
|
17
|
+
if (local)
|
|
18
|
+
names.push(local.trim());
|
|
19
|
+
}
|
|
20
|
+
continue;
|
|
21
|
+
}
|
|
22
|
+
const [local] = part.split(/\s+as\s+/i);
|
|
23
|
+
if (local)
|
|
24
|
+
names.push(local.trim());
|
|
25
|
+
}
|
|
26
|
+
return names;
|
|
27
|
+
}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import type { ComponentExtractionResult, ExtractorOptions } from '../types.js';
|
|
2
|
-
|
|
2
|
+
type ExtractProgress = {
|
|
3
3
|
filesProcessed: number;
|
|
4
4
|
componentsFound: number;
|
|
5
5
|
};
|
|
6
6
|
export declare function extractComponents(filePaths: string[], onProgress?: (progress: ExtractProgress) => void, opts?: ExtractorOptions): Promise<ComponentExtractionResult>;
|
|
7
|
+
export {};
|