@contentful/experience-design-system-cli 2.7.4 → 2.11.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/analyze/build-analyze-view-rows.d.ts +23 -0
- package/dist/src/analyze/build-analyze-view-rows.js +39 -0
- package/dist/src/analyze/command.js +16 -11
- package/dist/src/analyze/extract/validate.d.ts +16 -0
- package/dist/src/analyze/extract/validate.js +94 -0
- package/dist/src/analyze/select/command.d.ts +47 -0
- package/dist/src/analyze/select/command.js +238 -14
- package/dist/src/analyze/select/tui/App.js +15 -17
- package/dist/src/analyze/select/tui/components/Sidebar.d.ts +4 -1
- package/dist/src/analyze/select/tui/components/Sidebar.js +42 -6
- package/dist/src/analyze/select/types.d.ts +6 -0
- package/dist/src/analyze/select/types.js +24 -7
- package/dist/src/analyze/select-agent/command.js +43 -3
- package/dist/src/analyze/tui/AnalyzeView.d.ts +8 -0
- package/dist/src/analyze/tui/AnalyzeView.js +5 -2
- package/dist/src/apply/api-client.d.ts +20 -0
- package/dist/src/apply/api-client.js +71 -5
- package/dist/src/generate/command.js +21 -2
- package/dist/src/import/command.js +2 -0
- package/dist/src/import/orchestrator.d.ts +22 -0
- package/dist/src/import/orchestrator.js +97 -14
- package/dist/src/import/tui/WizardApp.d.ts +13 -0
- package/dist/src/import/tui/WizardApp.js +224 -52
- package/dist/src/import/tui/steps/GateStep.d.ts +2 -1
- package/dist/src/import/tui/steps/GateStep.js +4 -2
- package/dist/src/import/tui/steps/GenerateReviewStep.js +2 -0
- package/dist/src/import/tui/steps/PreviewValidationErrorStep.d.ts +11 -0
- package/dist/src/import/tui/steps/PreviewValidationErrorStep.js +14 -0
- package/dist/src/import/tui/wizard-422-helpers.d.ts +48 -0
- package/dist/src/import/tui/wizard-422-helpers.js +60 -0
- package/dist/src/program.d.ts +17 -0
- package/dist/src/program.js +27 -4
- package/dist/src/session/db.d.ts +13 -0
- package/dist/src/session/db.js +50 -0
- package/dist/src/types.d.ts +8 -0
- package/package.json +2 -2
- package/skills/generate-components.md +2 -0
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { jsxs as _jsxs, jsx as _jsx } from "react/jsx-runtime";
|
|
2
2
|
import { Box, Text } from 'ink';
|
|
3
3
|
import { useImmediateInput } from '../../../analyze/select/tui/hooks/useImmediateInput.js';
|
|
4
|
-
export function GateStep({ successMessage, summary, context, continueLabel = 'Continue', skipLabel = 'Approve all and skip', onContinue, onSkip, onQuit, showSkip = true, }) {
|
|
4
|
+
export function GateStep({ successMessage, summary, context, continueLabel = 'Continue', skipLabel = 'Approve all and skip', onContinue, onSkip, onQuit, showSkip = true, intent = 'success', }) {
|
|
5
5
|
useImmediateInput((input, key) => {
|
|
6
6
|
if (key.return) {
|
|
7
7
|
onContinue();
|
|
@@ -16,5 +16,7 @@ export function GateStep({ successMessage, summary, context, continueLabel = 'Co
|
|
|
16
16
|
return;
|
|
17
17
|
}
|
|
18
18
|
});
|
|
19
|
-
|
|
19
|
+
const headerColor = intent === 'error' ? 'red' : 'green';
|
|
20
|
+
const headerIcon = intent === 'error' ? '✗' : '✓';
|
|
21
|
+
return (_jsxs(Box, { flexDirection: "column", gap: 1, paddingX: 2, paddingY: 1, children: [_jsxs(Text, { color: headerColor, children: [headerIcon, " ", successMessage] }), summary && _jsx(Text, { dimColor: true, children: summary }), _jsx(Box, { marginTop: 1, children: _jsx(Text, { children: context }) }), _jsxs(Box, { gap: 3, marginTop: 1, children: [_jsxs(Text, { dimColor: true, children: ["[Enter] ", continueLabel] }), showSkip && onSkip && _jsxs(Text, { dimColor: true, children: ["[a] ", skipLabel] }), _jsx(Text, { dimColor: true, children: "[q] Quit" })] })] }));
|
|
20
22
|
}
|
|
@@ -187,6 +187,8 @@ export function GenerateReviewStep({ extractSessionId, onFinalize, onQuit, }) {
|
|
|
187
187
|
status: c.status,
|
|
188
188
|
extractionConfidence: null,
|
|
189
189
|
needsReview: false,
|
|
190
|
+
validationErrorCount: 0,
|
|
191
|
+
validationWarningCount: 0,
|
|
190
192
|
}));
|
|
191
193
|
const longestName = components.reduce((m, c) => Math.max(m, c.key.length), 0);
|
|
192
194
|
const sidebarWidth = Math.min(Math.max(longestName + 4, 14), 22);
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import type { PreviewValidationError } from '../../../apply/api-client.js';
|
|
3
|
+
type PreviewValidationErrorStepProps = {
|
|
4
|
+
errors: PreviewValidationError[];
|
|
5
|
+
missingNames: string[];
|
|
6
|
+
onEdit: () => void;
|
|
7
|
+
onSkip: () => void;
|
|
8
|
+
onQuit: () => void;
|
|
9
|
+
};
|
|
10
|
+
export declare function PreviewValidationErrorStep({ errors, missingNames, onEdit, onSkip, onQuit, }: PreviewValidationErrorStepProps): React.ReactElement;
|
|
11
|
+
export {};
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
+
import { GateStep } from './GateStep.js';
|
|
3
|
+
export function PreviewValidationErrorStep({ errors, missingNames, onEdit, onSkip, onQuit, }) {
|
|
4
|
+
const uniqueNames = [...new Set(errors.map((e) => e.componentName))];
|
|
5
|
+
const matchedNames = uniqueNames.filter((n) => !missingNames.includes(n));
|
|
6
|
+
const errorLines = errors.map((e) => ` ${e.componentName}: ${e.message}`).join('\n');
|
|
7
|
+
const missingNote = missingNames.length > 0
|
|
8
|
+
? `\n\nNote: ${missingNames.length} component name${missingNames.length === 1 ? '' : 's'} from the server (${missingNames.join(', ')}) ${missingNames.length === 1 ? 'does' : 'do'} not match anything in this session — they cannot be edited or skipped from here.`
|
|
9
|
+
: '';
|
|
10
|
+
const skipLabel = matchedNames.length === 0
|
|
11
|
+
? 'No matching components to skip'
|
|
12
|
+
: `Skip ${matchedNames.length === 1 ? matchedNames[0] : `${matchedNames.length} components`} and retry`;
|
|
13
|
+
return (_jsx(GateStep, { intent: "error", successMessage: "Preview validation failed", summary: errorLines, context: `${uniqueNames.length} component${uniqueNames.length === 1 ? '' : 's'} failed server validation. Edit their definitions in the review TUI, or skip them and retry preview without them.${missingNote}`, continueLabel: "Edit definitions", skipLabel: skipLabel, showSkip: matchedNames.length > 0, onContinue: onEdit, onSkip: onSkip, onQuit: onQuit }));
|
|
14
|
+
}
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import { ApiError, type PreviewValidationError } from '../../apply/api-client.js';
|
|
2
|
+
import { patchReviewStateWithValidationErrors, rejectComponentsByName } from '../../analyze/select/command.js';
|
|
3
|
+
export type Preview422Outcome = {
|
|
4
|
+
kind: 'validation-error';
|
|
5
|
+
errors: PreviewValidationError[];
|
|
6
|
+
missingNames: string[];
|
|
7
|
+
} | {
|
|
8
|
+
kind: 'unparseable';
|
|
9
|
+
message: string;
|
|
10
|
+
} | {
|
|
11
|
+
kind: 'not-422';
|
|
12
|
+
};
|
|
13
|
+
/**
|
|
14
|
+
* Decide what to do with an `ApiError` raised by `previewImport()`.
|
|
15
|
+
* Returns `not-422` if the caller should fall through to its other branches
|
|
16
|
+
* (404 / 401 / 403 / generic), `unparseable` if the body cannot be parsed
|
|
17
|
+
* into structured component-level errors (caller should route to the generic
|
|
18
|
+
* error step), or `validation-error` with the patched-state side effects
|
|
19
|
+
* already applied.
|
|
20
|
+
*
|
|
21
|
+
* Pure decision logic + a single delegated side effect to
|
|
22
|
+
* `patchReviewStateWithValidationErrors`. Extracted from `WizardApp.runPreview`
|
|
23
|
+
* so the routing is unit-testable without driving the full TUI.
|
|
24
|
+
*/
|
|
25
|
+
export declare function handlePreview422(err: ApiError, extractSessionId: string | null, patchFn?: typeof patchReviewStateWithValidationErrors): Promise<Preview422Outcome>;
|
|
26
|
+
/**
|
|
27
|
+
* The WizardState patch the wizard should spread into any update that
|
|
28
|
+
* transitions OUT of the preview-validation-error step (e.g. into
|
|
29
|
+
* preview-gate after a successful retry). Without this, the validation-error
|
|
30
|
+
* arrays linger in state and any future code path that reads them sees stale
|
|
31
|
+
* data from a prior failed attempt.
|
|
32
|
+
*/
|
|
33
|
+
export declare function clearedValidationErrorState(): {
|
|
34
|
+
previewValidationErrors: PreviewValidationError[];
|
|
35
|
+
previewValidationMissingNames: string[];
|
|
36
|
+
};
|
|
37
|
+
/**
|
|
38
|
+
* Run the "skip and retry" side effect: dedup the offending component
|
|
39
|
+
* names from a list of errors and call `rejectComponentsByName`. Returns
|
|
40
|
+
* the deduped names so the caller can log them or include them in test
|
|
41
|
+
* assertions. No-op when sessionId is null or errors are empty.
|
|
42
|
+
*
|
|
43
|
+
* The DB update inside `rejectComponentsByName` is what makes the next
|
|
44
|
+
* preview see a manifest with the offenders excluded — see SP-3 retro
|
|
45
|
+
* Bug 1: writing to the JSON state file alone is not enough because
|
|
46
|
+
* `loadCDFComponents` reads from the pipeline DB.
|
|
47
|
+
*/
|
|
48
|
+
export declare function applySkipValidationErrors(sessionId: string | null, errors: PreviewValidationError[], rejectFn?: typeof rejectComponentsByName): Promise<string[]>;
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
import { parsePreviewValidationErrors } from '../../apply/api-client.js';
|
|
2
|
+
import { patchReviewStateWithValidationErrors, rejectComponentsByName } from '../../analyze/select/command.js';
|
|
3
|
+
/**
|
|
4
|
+
* Decide what to do with an `ApiError` raised by `previewImport()`.
|
|
5
|
+
* Returns `not-422` if the caller should fall through to its other branches
|
|
6
|
+
* (404 / 401 / 403 / generic), `unparseable` if the body cannot be parsed
|
|
7
|
+
* into structured component-level errors (caller should route to the generic
|
|
8
|
+
* error step), or `validation-error` with the patched-state side effects
|
|
9
|
+
* already applied.
|
|
10
|
+
*
|
|
11
|
+
* Pure decision logic + a single delegated side effect to
|
|
12
|
+
* `patchReviewStateWithValidationErrors`. Extracted from `WizardApp.runPreview`
|
|
13
|
+
* so the routing is unit-testable without driving the full TUI.
|
|
14
|
+
*/
|
|
15
|
+
export async function handlePreview422(err, extractSessionId, patchFn = patchReviewStateWithValidationErrors) {
|
|
16
|
+
if (err.status !== 422)
|
|
17
|
+
return { kind: 'not-422' };
|
|
18
|
+
const errors = parsePreviewValidationErrors(err.body);
|
|
19
|
+
if (errors.length === 0)
|
|
20
|
+
return { kind: 'unparseable', message: err.message };
|
|
21
|
+
let missingNames = [];
|
|
22
|
+
if (extractSessionId) {
|
|
23
|
+
const result = await patchFn(extractSessionId, errors);
|
|
24
|
+
missingNames = result.missingNames;
|
|
25
|
+
}
|
|
26
|
+
return { kind: 'validation-error', errors, missingNames };
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* The WizardState patch the wizard should spread into any update that
|
|
30
|
+
* transitions OUT of the preview-validation-error step (e.g. into
|
|
31
|
+
* preview-gate after a successful retry). Without this, the validation-error
|
|
32
|
+
* arrays linger in state and any future code path that reads them sees stale
|
|
33
|
+
* data from a prior failed attempt.
|
|
34
|
+
*/
|
|
35
|
+
export function clearedValidationErrorState() {
|
|
36
|
+
return {
|
|
37
|
+
previewValidationErrors: [],
|
|
38
|
+
previewValidationMissingNames: [],
|
|
39
|
+
};
|
|
40
|
+
}
|
|
41
|
+
/**
|
|
42
|
+
* Run the "skip and retry" side effect: dedup the offending component
|
|
43
|
+
* names from a list of errors and call `rejectComponentsByName`. Returns
|
|
44
|
+
* the deduped names so the caller can log them or include them in test
|
|
45
|
+
* assertions. No-op when sessionId is null or errors are empty.
|
|
46
|
+
*
|
|
47
|
+
* The DB update inside `rejectComponentsByName` is what makes the next
|
|
48
|
+
* preview see a manifest with the offenders excluded — see SP-3 retro
|
|
49
|
+
* Bug 1: writing to the JSON state file alone is not enough because
|
|
50
|
+
* `loadCDFComponents` reads from the pipeline DB.
|
|
51
|
+
*/
|
|
52
|
+
export async function applySkipValidationErrors(sessionId, errors, rejectFn = rejectComponentsByName) {
|
|
53
|
+
if (!sessionId)
|
|
54
|
+
return [];
|
|
55
|
+
const names = [...new Set(errors.map((e) => e.componentName))];
|
|
56
|
+
if (names.length === 0)
|
|
57
|
+
return [];
|
|
58
|
+
await rejectFn(sessionId, names);
|
|
59
|
+
return names;
|
|
60
|
+
}
|
package/dist/src/program.d.ts
CHANGED
|
@@ -1,2 +1,19 @@
|
|
|
1
1
|
import { Command } from 'commander';
|
|
2
|
+
type SpawnedChild = {
|
|
3
|
+
on(event: 'error', listener: (err: unknown) => void): unknown;
|
|
4
|
+
on(event: 'exit', listener: (code: number | null) => void): unknown;
|
|
5
|
+
};
|
|
6
|
+
/**
|
|
7
|
+
* Run the spawn lifecycle for the build command. Extracted so the
|
|
8
|
+
* error-surfacing path is testable: previously `child.on('error', ...)`
|
|
9
|
+
* silently returned exit code 1 with no context, leaving the user
|
|
10
|
+
* staring at a generic non-zero exit when `pnpm` wasn't on PATH.
|
|
11
|
+
*/
|
|
12
|
+
export declare function runBuild(opts: {
|
|
13
|
+
spawnFn: () => SpawnedChild;
|
|
14
|
+
stderrWrite: (chunk: string) => void;
|
|
15
|
+
}): Promise<{
|
|
16
|
+
exitCode: number;
|
|
17
|
+
}>;
|
|
2
18
|
export declare function createProgram(): Command;
|
|
19
|
+
export {};
|
package/dist/src/program.js
CHANGED
|
@@ -12,6 +12,30 @@ import { registerImportCommand } from './import/command.js';
|
|
|
12
12
|
import { registerSetupCommand } from './setup/command.js';
|
|
13
13
|
const require = createRequire(import.meta.url);
|
|
14
14
|
const pkg = require('../package.json');
|
|
15
|
+
/**
|
|
16
|
+
* Run the spawn lifecycle for the build command. Extracted so the
|
|
17
|
+
* error-surfacing path is testable: previously `child.on('error', ...)`
|
|
18
|
+
* silently returned exit code 1 with no context, leaving the user
|
|
19
|
+
* staring at a generic non-zero exit when `pnpm` wasn't on PATH.
|
|
20
|
+
*/
|
|
21
|
+
export async function runBuild(opts) {
|
|
22
|
+
return new Promise((resolvePromise) => {
|
|
23
|
+
const child = opts.spawnFn();
|
|
24
|
+
let settled = false;
|
|
25
|
+
const settle = (exitCode) => {
|
|
26
|
+
if (settled)
|
|
27
|
+
return;
|
|
28
|
+
settled = true;
|
|
29
|
+
resolvePromise({ exitCode });
|
|
30
|
+
};
|
|
31
|
+
child.on('error', (err) => {
|
|
32
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
33
|
+
opts.stderrWrite(`Error: failed to spawn build subprocess: ${message}\n`);
|
|
34
|
+
settle(1);
|
|
35
|
+
});
|
|
36
|
+
child.on('exit', (code) => settle(code ?? 1));
|
|
37
|
+
});
|
|
38
|
+
}
|
|
15
39
|
function registerBuildCommand(program) {
|
|
16
40
|
program
|
|
17
41
|
.command('build')
|
|
@@ -19,10 +43,9 @@ function registerBuildCommand(program) {
|
|
|
19
43
|
.action(async () => {
|
|
20
44
|
const pkgRoot = resolve(dirname(fileURLToPath(import.meta.url)), '..', '..');
|
|
21
45
|
process.stderr.write('⚙ Building from source...\n');
|
|
22
|
-
const exitCode = await
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
child.on('exit', (code) => resolvePromise(code ?? 1));
|
|
46
|
+
const { exitCode } = await runBuild({
|
|
47
|
+
spawnFn: () => spawn('pnpm', ['build'], { cwd: pkgRoot, stdio: 'inherit' }),
|
|
48
|
+
stderrWrite: (s) => process.stderr.write(s),
|
|
26
49
|
});
|
|
27
50
|
process.exit(exitCode);
|
|
28
51
|
});
|
package/dist/src/session/db.d.ts
CHANGED
|
@@ -53,6 +53,19 @@ export type RawComponentWithId = RawComponentDefinition & {
|
|
|
53
53
|
component_id: string;
|
|
54
54
|
};
|
|
55
55
|
export declare function loadRawComponents(db: DatabaseSync, sessionId: string, allowedNames?: Set<string>): RawComponentWithId[];
|
|
56
|
+
/**
|
|
57
|
+
* Rename empty-named slots in the DB to heuristic names before generation.
|
|
58
|
+
* The LLM classify_slot tool call is matched back to the DB row by name, so a
|
|
59
|
+
* row with name="" can never be updated by the LLM — renaming it here makes it
|
|
60
|
+
* classifiable. Returns one warning string per renamed slot.
|
|
61
|
+
*/
|
|
62
|
+
export declare function renameEmptySlots(db: DatabaseSync, sessionId: string, componentId: string, componentName: string, slotCount: number): {
|
|
63
|
+
renames: Array<{
|
|
64
|
+
oldName: string;
|
|
65
|
+
newName: string;
|
|
66
|
+
}>;
|
|
67
|
+
warnings: string[];
|
|
68
|
+
};
|
|
56
69
|
export declare function storeCDFComponents(db: DatabaseSync, sessionId: string, components: Array<{
|
|
57
70
|
key: string;
|
|
58
71
|
entry: CDFComponentEntry;
|
package/dist/src/session/db.js
CHANGED
|
@@ -563,6 +563,45 @@ export function loadRawComponents(db, sessionId, allowedNames) {
|
|
|
563
563
|
}),
|
|
564
564
|
}));
|
|
565
565
|
}
|
|
566
|
+
/**
|
|
567
|
+
* Rename empty-named slots in the DB to heuristic names before generation.
|
|
568
|
+
* The LLM classify_slot tool call is matched back to the DB row by name, so a
|
|
569
|
+
* row with name="" can never be updated by the LLM — renaming it here makes it
|
|
570
|
+
* classifiable. Returns one warning string per renamed slot.
|
|
571
|
+
*/
|
|
572
|
+
export function renameEmptySlots(db, sessionId, componentId, componentName, slotCount) {
|
|
573
|
+
const emptySlots = db
|
|
574
|
+
.prepare(`SELECT name, position FROM raw_slots
|
|
575
|
+
WHERE session_id = ? AND component_id = ? AND trim(name) = ''
|
|
576
|
+
ORDER BY position`)
|
|
577
|
+
.all(sessionId, componentId);
|
|
578
|
+
if (emptySlots.length === 0)
|
|
579
|
+
return { renames: [], warnings: [] };
|
|
580
|
+
const renames = [];
|
|
581
|
+
const warnings = [];
|
|
582
|
+
const rename = db.prepare(`UPDATE raw_slots SET name = ? WHERE session_id = ? AND component_id = ? AND name = ? AND position = ?`);
|
|
583
|
+
// Multi-statement write — wrap in a transaction so a SIGINT or driver error
|
|
584
|
+
// mid-loop leaves raw_slots either fully renamed or fully untouched, never
|
|
585
|
+
// half-renamed (which would corrupt the prompt-vs-DB invariant the LLM
|
|
586
|
+
// relies on for classify_slot). Matches the existing BEGIN/COMMIT pattern
|
|
587
|
+
// throughout this file (see storeRawComponents, createStep, etc.).
|
|
588
|
+
db.exec('BEGIN');
|
|
589
|
+
try {
|
|
590
|
+
for (const slot of emptySlots) {
|
|
591
|
+
// Single unnamed slot → "children". Multiple → positional names.
|
|
592
|
+
const newName = slotCount === 1 ? 'children' : `slot_${slot.position}`;
|
|
593
|
+
rename.run(newName, sessionId, componentId, slot.name, slot.position);
|
|
594
|
+
renames.push({ oldName: slot.name, newName });
|
|
595
|
+
warnings.push(`${componentName}: slot at position ${slot.position} had empty name — renamed to "${newName}" for classification`);
|
|
596
|
+
}
|
|
597
|
+
db.exec('COMMIT');
|
|
598
|
+
}
|
|
599
|
+
catch (e) {
|
|
600
|
+
db.exec('ROLLBACK');
|
|
601
|
+
throw e;
|
|
602
|
+
}
|
|
603
|
+
return { renames, warnings };
|
|
604
|
+
}
|
|
566
605
|
function groupBy(items, key) {
|
|
567
606
|
const map = new Map();
|
|
568
607
|
for (const item of items) {
|
|
@@ -676,6 +715,12 @@ export function loadCDFComponents(db, sessionId) {
|
|
|
676
715
|
return null;
|
|
677
716
|
const $properties = {};
|
|
678
717
|
for (const p of compProps) {
|
|
718
|
+
// Hallucination insurance: drop any prop whose name didn't survive trim.
|
|
719
|
+
// The pre-generate rename guard catches empty-named slots, but if the LLM
|
|
720
|
+
// hallucinated a classify_prop with an empty name into the DB, surface
|
|
721
|
+
// nothing to the manifest builder rather than emitting "$properties: { '': ... }".
|
|
722
|
+
if (!p.name.trim())
|
|
723
|
+
continue;
|
|
679
724
|
const av = allowedValuesByProp.get(`${component_id}::${p.name}`);
|
|
680
725
|
const propDef = {
|
|
681
726
|
$type: p.cdf_type,
|
|
@@ -702,6 +747,11 @@ export function loadCDFComponents(db, sessionId) {
|
|
|
702
747
|
const compSlots = slotsByComponent.get(component_id) ?? [];
|
|
703
748
|
const $slots = {};
|
|
704
749
|
for (const s of compSlots) {
|
|
750
|
+
// Hallucination insurance: same as $properties — drop empty-named slots
|
|
751
|
+
// before they reach buildManifest, which faithfully passes empty keys
|
|
752
|
+
// through and triggers a 422 from the preview API.
|
|
753
|
+
if (!s.name.trim())
|
|
754
|
+
continue;
|
|
705
755
|
const ac = allowedComponentsBySlot.get(`${component_id}::${s.name}`);
|
|
706
756
|
const slotDef = {};
|
|
707
757
|
if (s.description !== null)
|
package/dist/src/types.d.ts
CHANGED
|
@@ -1,4 +1,11 @@
|
|
|
1
1
|
import type { DesignTokenType } from '@contentful/experience-design-system-types';
|
|
2
|
+
export type ExtractionValidationIssueCode = 'EMPTY_COMPONENT_NAME' | 'EMPTY_PROP_NAME' | 'EMPTY_SLOT_NAME' | 'PROP_SLOT_NAME_COLLISION' | 'DUPLICATE_COMPONENT_NAME' | 'EMPTY_COMPONENT' | 'SERVER_VALIDATION_FAILED';
|
|
3
|
+
export type ExtractionValidationIssue = {
|
|
4
|
+
severity: 'error' | 'warning';
|
|
5
|
+
code: ExtractionValidationIssueCode;
|
|
6
|
+
message: string;
|
|
7
|
+
field?: string;
|
|
8
|
+
};
|
|
2
9
|
export interface RawTokenDefinition {
|
|
3
10
|
name: string;
|
|
4
11
|
value: string;
|
|
@@ -37,6 +44,7 @@ export interface RawComponentDefinition {
|
|
|
37
44
|
extractionConfidence?: number | null;
|
|
38
45
|
reviewReasons?: string[];
|
|
39
46
|
needsReview?: boolean;
|
|
47
|
+
validationIssues?: ExtractionValidationIssue[];
|
|
40
48
|
}
|
|
41
49
|
export interface ComponentExtractionResult {
|
|
42
50
|
components: RawComponentDefinition[];
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@contentful/experience-design-system-cli",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.11.0",
|
|
4
4
|
"description": "Contentful Experiences design system import CLI",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"type": "module",
|
|
@@ -36,7 +36,7 @@
|
|
|
36
36
|
"react-dom": "^18.3.1",
|
|
37
37
|
"ts-morph": "^27.0.2",
|
|
38
38
|
"typescript": "^5.9.3",
|
|
39
|
-
"@contentful/experience-design-system-types": "2.
|
|
39
|
+
"@contentful/experience-design-system-types": "2.11.0"
|
|
40
40
|
},
|
|
41
41
|
"devDependencies": {
|
|
42
42
|
"@tsconfig/node24": "^24.0.3",
|
|
@@ -251,6 +251,8 @@ For each `RawSlotDefinition`:
|
|
|
251
251
|
- `false` if clearly optional (icon slot, footer slot with a default, decorative slot)
|
|
252
252
|
- Default to `true` when the source gives no signal
|
|
253
253
|
|
|
254
|
+
**Pre-named slots:** If the input contains a slot whose `name` was already inferred by the pipeline (e.g. `"children"`, `"slot_0"`), treat it as you would any named slot — classify it normally. The pipeline renames empty-named slots to heuristic names before passing them to you; your job is to confirm or enrich the classification (set `required`, `description`, `allowed_components`), not to rename again.
|
|
255
|
+
|
|
254
256
|
---
|
|
255
257
|
|
|
256
258
|
## Examples
|