@contentful/experience-design-system-cli 2.2.1
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/README.md +532 -0
- package/bin/cli.js +58 -0
- package/dist/package.json +56 -0
- package/dist/src/analyze/command.d.ts +3 -0
- package/dist/src/analyze/command.js +175 -0
- package/dist/src/analyze/extract/astro.d.ts +5 -0
- package/dist/src/analyze/extract/astro.js +280 -0
- package/dist/src/analyze/extract/pipeline.d.ts +6 -0
- package/dist/src/analyze/extract/pipeline.js +298 -0
- package/dist/src/analyze/extract/react.d.ts +2 -0
- package/dist/src/analyze/extract/react.js +1949 -0
- package/dist/src/analyze/extract/slot-detection.d.ts +35 -0
- package/dist/src/analyze/extract/slot-detection.js +101 -0
- package/dist/src/analyze/extract/stencil.d.ts +2 -0
- package/dist/src/analyze/extract/stencil.js +293 -0
- package/dist/src/analyze/extract/tsx-shared.d.ts +8 -0
- package/dist/src/analyze/extract/tsx-shared.js +263 -0
- package/dist/src/analyze/extract/vue-tsx.d.ts +2 -0
- package/dist/src/analyze/extract/vue-tsx.js +498 -0
- package/dist/src/analyze/extract/vue.d.ts +5 -0
- package/dist/src/analyze/extract/vue.js +647 -0
- package/dist/src/analyze/extract/web-components.d.ts +2 -0
- package/dist/src/analyze/extract/web-components.js +866 -0
- package/dist/src/analyze/pre-classify.d.ts +17 -0
- package/dist/src/analyze/pre-classify.js +144 -0
- package/dist/src/analyze/select/command.d.ts +2 -0
- package/dist/src/analyze/select/command.js +256 -0
- package/dist/src/analyze/select/index.d.ts +6 -0
- package/dist/src/analyze/select/index.js +5 -0
- package/dist/src/analyze/select/parser.d.ts +6 -0
- package/dist/src/analyze/select/parser.js +53 -0
- package/dist/src/analyze/select/persistence.d.ts +9 -0
- package/dist/src/analyze/select/persistence.js +42 -0
- package/dist/src/analyze/select/stdout.d.ts +7 -0
- package/dist/src/analyze/select/stdout.js +3 -0
- package/dist/src/analyze/select/tui/App.d.ts +8 -0
- package/dist/src/analyze/select/tui/App.js +491 -0
- package/dist/src/analyze/select/tui/components/ComponentDetail.d.ts +20 -0
- package/dist/src/analyze/select/tui/components/ComponentDetail.js +43 -0
- package/dist/src/analyze/select/tui/components/FieldEditor.d.ts +11 -0
- package/dist/src/analyze/select/tui/components/FieldEditor.js +531 -0
- package/dist/src/analyze/select/tui/components/FinalizeDialog.d.ts +10 -0
- package/dist/src/analyze/select/tui/components/FinalizeDialog.js +15 -0
- package/dist/src/analyze/select/tui/components/HelpOverlay.d.ts +7 -0
- package/dist/src/analyze/select/tui/components/HelpOverlay.js +11 -0
- package/dist/src/analyze/select/tui/components/JsonEditor.d.ts +11 -0
- package/dist/src/analyze/select/tui/components/JsonEditor.js +154 -0
- package/dist/src/analyze/select/tui/components/JsonPanel.d.ts +11 -0
- package/dist/src/analyze/select/tui/components/JsonPanel.js +62 -0
- package/dist/src/analyze/select/tui/components/PreviewSummaryBar.d.ts +8 -0
- package/dist/src/analyze/select/tui/components/PreviewSummaryBar.js +29 -0
- package/dist/src/analyze/select/tui/components/QuitDialog.d.ts +8 -0
- package/dist/src/analyze/select/tui/components/QuitDialog.js +14 -0
- package/dist/src/analyze/select/tui/components/Sidebar.d.ts +15 -0
- package/dist/src/analyze/select/tui/components/Sidebar.js +48 -0
- package/dist/src/analyze/select/tui/components/SourcePanel.d.ts +11 -0
- package/dist/src/analyze/select/tui/components/SourcePanel.js +52 -0
- package/dist/src/analyze/select/tui/components/StatusBar.d.ts +11 -0
- package/dist/src/analyze/select/tui/components/StatusBar.js +6 -0
- package/dist/src/analyze/select/tui/components/TopBar.d.ts +10 -0
- package/dist/src/analyze/select/tui/components/TopBar.js +5 -0
- package/dist/src/analyze/select/tui/hooks/useImmediateInput.d.ts +24 -0
- package/dist/src/analyze/select/tui/hooks/useImmediateInput.js +68 -0
- package/dist/src/analyze/select/tui/hooks/useKeymap.d.ts +24 -0
- package/dist/src/analyze/select/tui/hooks/useKeymap.js +67 -0
- package/dist/src/analyze/select/tui/hooks/useSession.d.ts +19 -0
- package/dist/src/analyze/select/tui/hooks/useSession.js +52 -0
- package/dist/src/analyze/select/tui/hooks/useUndo.d.ts +8 -0
- package/dist/src/analyze/select/tui/hooks/useUndo.js +26 -0
- package/dist/src/analyze/select/types.d.ts +46 -0
- package/dist/src/analyze/select/types.js +20 -0
- package/dist/src/analyze/select-agent/command.d.ts +2 -0
- package/dist/src/analyze/select-agent/command.js +208 -0
- package/dist/src/analyze/tui/AnalyzeView.d.ts +24 -0
- package/dist/src/analyze/tui/AnalyzeView.js +38 -0
- package/dist/src/apply/api-client.d.ts +35 -0
- package/dist/src/apply/api-client.js +143 -0
- package/dist/src/apply/command.d.ts +6 -0
- package/dist/src/apply/command.js +787 -0
- package/dist/src/apply/manifest.d.ts +1 -0
- package/dist/src/apply/manifest.js +1 -0
- package/dist/src/apply/tui/SelectView.d.ts +18 -0
- package/dist/src/apply/tui/SelectView.js +34 -0
- package/dist/src/apply/tui/ServerApplyView.d.ts +32 -0
- package/dist/src/apply/tui/ServerApplyView.js +42 -0
- package/dist/src/apply/tui/ServerPreviewView.d.ts +9 -0
- package/dist/src/apply/tui/ServerPreviewView.js +21 -0
- package/dist/src/credentials-store.d.ts +8 -0
- package/dist/src/credentials-store.js +30 -0
- package/dist/src/generate/agent-runner.d.ts +86 -0
- package/dist/src/generate/agent-runner.js +314 -0
- package/dist/src/generate/command.d.ts +2 -0
- package/dist/src/generate/command.js +545 -0
- package/dist/src/generate/edit/command.d.ts +2 -0
- package/dist/src/generate/edit/command.js +126 -0
- package/dist/src/generate/prompt-builder.d.ts +18 -0
- package/dist/src/generate/prompt-builder.js +202 -0
- package/dist/src/generate/tui/GenerateView.d.ts +12 -0
- package/dist/src/generate/tui/GenerateView.js +10 -0
- package/dist/src/import/command.d.ts +2 -0
- package/dist/src/import/command.js +96 -0
- package/dist/src/import/orchestrator.d.ts +37 -0
- package/dist/src/import/orchestrator.js +374 -0
- package/dist/src/import/path-utils.d.ts +15 -0
- package/dist/src/import/path-utils.js +30 -0
- package/dist/src/import/tui/WizardApp.d.ts +10 -0
- package/dist/src/import/tui/WizardApp.js +906 -0
- package/dist/src/import/tui/steps/CredentialsStep.d.ts +15 -0
- package/dist/src/import/tui/steps/CredentialsStep.js +79 -0
- package/dist/src/import/tui/steps/DoneStep.d.ts +20 -0
- package/dist/src/import/tui/steps/DoneStep.js +17 -0
- package/dist/src/import/tui/steps/ErrorStep.d.ts +8 -0
- package/dist/src/import/tui/steps/ErrorStep.js +11 -0
- package/dist/src/import/tui/steps/GateStep.d.ts +14 -0
- package/dist/src/import/tui/steps/GateStep.js +20 -0
- package/dist/src/import/tui/steps/GenerateReviewStep.d.ts +8 -0
- package/dist/src/import/tui/steps/GenerateReviewStep.js +208 -0
- package/dist/src/import/tui/steps/PathValidationStep.d.ts +10 -0
- package/dist/src/import/tui/steps/PathValidationStep.js +151 -0
- package/dist/src/import/tui/steps/PreviewStep.d.ts +21 -0
- package/dist/src/import/tui/steps/PreviewStep.js +36 -0
- package/dist/src/import/tui/steps/RunningStep.d.ts +10 -0
- package/dist/src/import/tui/steps/RunningStep.js +20 -0
- package/dist/src/import/tui/steps/TokenInputStep.d.ts +8 -0
- package/dist/src/import/tui/steps/TokenInputStep.js +70 -0
- package/dist/src/import/tui/steps/WelcomeStep.d.ts +7 -0
- package/dist/src/import/tui/steps/WelcomeStep.js +33 -0
- package/dist/src/import/tui/steps/WizardPreviewStep.d.ts +15 -0
- package/dist/src/import/tui/steps/WizardPreviewStep.js +121 -0
- package/dist/src/import/tui/steps/preview-diff.d.ts +10 -0
- package/dist/src/import/tui/steps/preview-diff.js +132 -0
- package/dist/src/index.d.ts +1 -0
- package/dist/src/index.js +2 -0
- package/dist/src/output/format.d.ts +23 -0
- package/dist/src/output/format.js +110 -0
- package/dist/src/print/command.d.ts +2 -0
- package/dist/src/print/command.js +199 -0
- package/dist/src/print/validate/tui/ValidateView.d.ts +15 -0
- package/dist/src/print/validate/tui/ValidateView.js +37 -0
- package/dist/src/print/validate/validators/cdf-validator.d.ts +2 -0
- package/dist/src/print/validate/validators/cdf-validator.js +104 -0
- package/dist/src/print/validate/validators/dtcg-validator.d.ts +2 -0
- package/dist/src/print/validate/validators/dtcg-validator.js +110 -0
- package/dist/src/print/validate/validators/format-errors.d.ts +12 -0
- package/dist/src/print/validate/validators/format-errors.js +18 -0
- package/dist/src/program.d.ts +2 -0
- package/dist/src/program.js +25 -0
- package/dist/src/session/command.d.ts +2 -0
- package/dist/src/session/command.js +261 -0
- package/dist/src/session/db.d.ts +111 -0
- package/dist/src/session/db.js +1114 -0
- package/dist/src/session/migration.d.ts +4 -0
- package/dist/src/session/migration.js +117 -0
- package/dist/src/session/session-id.d.ts +1 -0
- package/dist/src/session/session-id.js +212 -0
- package/dist/src/session/stats.d.ts +27 -0
- package/dist/src/session/stats.js +89 -0
- package/dist/src/setup/command.d.ts +2 -0
- package/dist/src/setup/command.js +765 -0
- package/dist/src/types.d.ts +48 -0
- package/dist/src/types.js +1 -0
- package/package.json +55 -0
- package/skills/generate-components.md +361 -0
- package/skills/generate-tokens.md +194 -0
- package/skills/select-components.md +180 -0
|
@@ -0,0 +1,374 @@
|
|
|
1
|
+
import { mkdir } from 'node:fs/promises';
|
|
2
|
+
import { join, resolve } from 'node:path';
|
|
3
|
+
import { fileURLToPath } from 'node:url';
|
|
4
|
+
import { execFile } from 'node:child_process';
|
|
5
|
+
import { openPipelineDb, getOrCreateSession, createStep, updateStep, findLatestSessionForCommand, } from '../session/db.js';
|
|
6
|
+
function findCliPath() {
|
|
7
|
+
return join(fileURLToPath(import.meta.url), '..', '..', '..', '..', 'bin', 'cli.js');
|
|
8
|
+
}
|
|
9
|
+
async function runStep(args, cliPath, env = {}, streamStderr = false) {
|
|
10
|
+
return new Promise((res) => {
|
|
11
|
+
const child = execFile('node', [cliPath, ...args], {
|
|
12
|
+
env: { ...process.env, ...env },
|
|
13
|
+
});
|
|
14
|
+
let stdout = '';
|
|
15
|
+
let stderr = '';
|
|
16
|
+
child.stdout?.on('data', (chunk) => {
|
|
17
|
+
stdout += chunk.toString();
|
|
18
|
+
});
|
|
19
|
+
child.stderr?.on('data', (chunk) => {
|
|
20
|
+
const text = chunk.toString();
|
|
21
|
+
stderr += text;
|
|
22
|
+
if (streamStderr)
|
|
23
|
+
process.stderr.write(text);
|
|
24
|
+
});
|
|
25
|
+
child.on('close', (code) => {
|
|
26
|
+
res({ exitCode: code ?? 0, stdout, stderr });
|
|
27
|
+
});
|
|
28
|
+
});
|
|
29
|
+
}
|
|
30
|
+
export async function runPipeline(opts, progressWriter, cliPathOverride) {
|
|
31
|
+
const projectRoot = resolve(opts.project);
|
|
32
|
+
const outDir = resolve(opts.out);
|
|
33
|
+
const componentsPath = join(outDir, 'components.json');
|
|
34
|
+
const cliPath = cliPathOverride ?? findCliPath();
|
|
35
|
+
const db = openPipelineDb();
|
|
36
|
+
const { sessionId } = getOrCreateSession(db, undefined, undefined, {
|
|
37
|
+
command: 'import',
|
|
38
|
+
inputPath: projectRoot,
|
|
39
|
+
outDir,
|
|
40
|
+
});
|
|
41
|
+
progressWriter(`Experience Design System CLI — Pipeline Import`);
|
|
42
|
+
progressWriter(`Project: ${projectRoot}`);
|
|
43
|
+
progressWriter(`Output: ${outDir}`);
|
|
44
|
+
if (opts.spaceId)
|
|
45
|
+
progressWriter(`Space: ${opts.spaceId} (${opts.environmentId})`);
|
|
46
|
+
progressWriter(`Session: ${sessionId}`);
|
|
47
|
+
progressWriter('');
|
|
48
|
+
await mkdir(outDir, { recursive: true });
|
|
49
|
+
const steps = [];
|
|
50
|
+
let stepNum = 0;
|
|
51
|
+
// print components is an optional step; adjust total accordingly
|
|
52
|
+
const totalSteps = 4 + (opts.print ? 1 : 0);
|
|
53
|
+
function stepLabel(name) {
|
|
54
|
+
stepNum++;
|
|
55
|
+
return ` Step ${stepNum}/${totalSteps} ${name} `;
|
|
56
|
+
}
|
|
57
|
+
// ── Step 1: analyze extract ──────────────────────────────────────────────
|
|
58
|
+
const analyzeLabel = stepLabel('Statically analyzing project');
|
|
59
|
+
const analyzeSkipped = !opts.noCache && opts.skipAnalyze;
|
|
60
|
+
let extractSessionId = null;
|
|
61
|
+
if (analyzeSkipped) {
|
|
62
|
+
progressWriter(`${analyzeLabel}– skipped (--skip-analyze)`);
|
|
63
|
+
steps.push({
|
|
64
|
+
step: 'analyze extract',
|
|
65
|
+
status: 'skipped',
|
|
66
|
+
reason: '--skip-analyze',
|
|
67
|
+
});
|
|
68
|
+
// Try to find a prior extract session to hand to downstream steps
|
|
69
|
+
const prior = findLatestSessionForCommand(db, 'analyze extract');
|
|
70
|
+
extractSessionId = prior ?? null;
|
|
71
|
+
}
|
|
72
|
+
else {
|
|
73
|
+
const stepId = createStep(db, sessionId, 'analyze extract', {
|
|
74
|
+
project: projectRoot,
|
|
75
|
+
});
|
|
76
|
+
const t0 = Date.now();
|
|
77
|
+
const analyzeArgs = ['analyze', 'extract', '--project', projectRoot];
|
|
78
|
+
const r = await runStep(analyzeArgs, cliPath);
|
|
79
|
+
const durationMs = Date.now() - t0;
|
|
80
|
+
if (r.exitCode !== 0) {
|
|
81
|
+
if (r.stderr)
|
|
82
|
+
process.stderr.write(r.stderr);
|
|
83
|
+
updateStep(db, stepId, 'failed', {}, r.stderr);
|
|
84
|
+
progressWriter(`${analyzeLabel}✗ failed (${(durationMs / 1000).toFixed(1)}s)`);
|
|
85
|
+
steps.push({
|
|
86
|
+
step: 'analyze extract',
|
|
87
|
+
status: 'failed',
|
|
88
|
+
durationMs,
|
|
89
|
+
error: r.stderr,
|
|
90
|
+
});
|
|
91
|
+
db.close();
|
|
92
|
+
return { session: sessionId, project: projectRoot, steps };
|
|
93
|
+
}
|
|
94
|
+
// Read session ID from DB — avoids dependence on subprocess stdout format.
|
|
95
|
+
extractSessionId = findLatestSessionForCommand(db, 'analyze extract') ?? null;
|
|
96
|
+
const componentMatch = /Extracted (\d+) component/.exec(r.stderr);
|
|
97
|
+
const componentCount = componentMatch ? Number(componentMatch[1]) : 0;
|
|
98
|
+
updateStep(db, stepId, 'complete', {
|
|
99
|
+
extractSessionId: extractSessionId ?? '',
|
|
100
|
+
});
|
|
101
|
+
progressWriter(`${analyzeLabel}✓ ${componentCount} component${componentCount === 1 ? '' : 's'} found (${(durationMs / 1000).toFixed(1)}s)`);
|
|
102
|
+
steps.push({
|
|
103
|
+
step: 'analyze extract',
|
|
104
|
+
status: 'complete',
|
|
105
|
+
durationMs,
|
|
106
|
+
detail: { components: componentCount },
|
|
107
|
+
});
|
|
108
|
+
}
|
|
109
|
+
// ── Step 2: analyze select ───────────────────────────────────────────────
|
|
110
|
+
const editLabel = stepLabel('Filtering components');
|
|
111
|
+
if (analyzeSkipped) {
|
|
112
|
+
progressWriter(`${editLabel}– skipped (--skip-analyze)`);
|
|
113
|
+
steps.push({
|
|
114
|
+
step: 'analyze select',
|
|
115
|
+
status: 'skipped',
|
|
116
|
+
reason: '--skip-analyze',
|
|
117
|
+
});
|
|
118
|
+
}
|
|
119
|
+
else if (extractSessionId) {
|
|
120
|
+
const editStepId = createStep(db, sessionId, 'analyze select', {
|
|
121
|
+
extractSession: extractSessionId,
|
|
122
|
+
});
|
|
123
|
+
const t0Edit = Date.now();
|
|
124
|
+
// Use agentic select when an agent is available and no manual select/deselect patterns are given.
|
|
125
|
+
const useAgentSelect = !opts.selectAll && (!opts.select || opts.select.length === 0) && (!opts.deselect || opts.deselect.length === 0);
|
|
126
|
+
let editArgs;
|
|
127
|
+
if (useAgentSelect) {
|
|
128
|
+
editArgs = ['analyze', 'select-agent', '--session', extractSessionId, '--agent', opts.agent];
|
|
129
|
+
if (opts.model)
|
|
130
|
+
editArgs.push('--model', opts.model);
|
|
131
|
+
}
|
|
132
|
+
else {
|
|
133
|
+
editArgs = ['analyze', 'select', '--session', extractSessionId];
|
|
134
|
+
if (opts.select && opts.select.length > 0) {
|
|
135
|
+
for (const p of opts.select)
|
|
136
|
+
editArgs.push('--select', p);
|
|
137
|
+
}
|
|
138
|
+
else if (opts.deselect && opts.deselect.length > 0) {
|
|
139
|
+
for (const p of opts.deselect)
|
|
140
|
+
editArgs.push('--deselect', p);
|
|
141
|
+
editArgs.push('--select-all'); // select-all with deselect patterns = select all except matches
|
|
142
|
+
}
|
|
143
|
+
else {
|
|
144
|
+
editArgs.push('--select-all');
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
const rEdit = await runStep(editArgs, cliPath, { FORCE_COLOR: '1' }, useAgentSelect);
|
|
148
|
+
const editDurationMs = Date.now() - t0Edit;
|
|
149
|
+
if (rEdit.exitCode !== 0) {
|
|
150
|
+
if (rEdit.stderr && !useAgentSelect)
|
|
151
|
+
process.stderr.write(rEdit.stderr);
|
|
152
|
+
updateStep(db, editStepId, 'failed', {}, rEdit.stderr);
|
|
153
|
+
progressWriter(`${editLabel}✗ failed (${(editDurationMs / 1000).toFixed(1)}s)`);
|
|
154
|
+
steps.push({
|
|
155
|
+
step: 'analyze select',
|
|
156
|
+
status: 'failed',
|
|
157
|
+
durationMs: editDurationMs,
|
|
158
|
+
error: rEdit.stderr,
|
|
159
|
+
});
|
|
160
|
+
db.close();
|
|
161
|
+
return { session: sessionId, project: projectRoot, steps };
|
|
162
|
+
}
|
|
163
|
+
const acceptedMatch = /Accepted: (\d+)/.exec(rEdit.stderr);
|
|
164
|
+
const acceptedCount = acceptedMatch ? Number(acceptedMatch[1]) : 0;
|
|
165
|
+
updateStep(db, editStepId, 'complete', {
|
|
166
|
+
extractSession: extractSessionId,
|
|
167
|
+
});
|
|
168
|
+
progressWriter(`${editLabel}✓ ${acceptedCount} accepted (${(editDurationMs / 1000).toFixed(1)}s)`);
|
|
169
|
+
steps.push({
|
|
170
|
+
step: 'analyze select',
|
|
171
|
+
status: 'complete',
|
|
172
|
+
durationMs: editDurationMs,
|
|
173
|
+
detail: { accepted: acceptedCount },
|
|
174
|
+
});
|
|
175
|
+
}
|
|
176
|
+
else {
|
|
177
|
+
progressWriter(`${editLabel}– skipped (no extract session)`);
|
|
178
|
+
steps.push({
|
|
179
|
+
step: 'analyze select',
|
|
180
|
+
status: 'skipped',
|
|
181
|
+
reason: 'no extract session',
|
|
182
|
+
});
|
|
183
|
+
}
|
|
184
|
+
// ── Step 3: generate components ──────────────────────────────────────────
|
|
185
|
+
if (opts.skipGenerate) {
|
|
186
|
+
const generateLabel = stepLabel('Categorizing component props');
|
|
187
|
+
progressWriter(`${generateLabel}– skipped (--skip-generate)`);
|
|
188
|
+
steps.push({
|
|
189
|
+
step: 'generate components',
|
|
190
|
+
status: 'skipped',
|
|
191
|
+
reason: '--skip-generate',
|
|
192
|
+
});
|
|
193
|
+
}
|
|
194
|
+
else {
|
|
195
|
+
const generateLabel = stepLabel('Categorizing component props');
|
|
196
|
+
const generateArgs = ['generate', 'components', '--agent', opts.agent];
|
|
197
|
+
if (opts.model)
|
|
198
|
+
generateArgs.push('--model', opts.model);
|
|
199
|
+
if (extractSessionId)
|
|
200
|
+
generateArgs.push('--session', extractSessionId);
|
|
201
|
+
if (opts.dryRun)
|
|
202
|
+
generateArgs.push('--dry-run');
|
|
203
|
+
if (opts.verbose)
|
|
204
|
+
generateArgs.push('--verbose');
|
|
205
|
+
const stepId = createStep(db, sessionId, 'generate components', {
|
|
206
|
+
extractSession: extractSessionId ?? '',
|
|
207
|
+
});
|
|
208
|
+
const t0 = Date.now();
|
|
209
|
+
const r = await runStep(generateArgs, cliPath, { FORCE_COLOR: '1' }, true);
|
|
210
|
+
const durationMs = Date.now() - t0;
|
|
211
|
+
if (r.exitCode !== 0) {
|
|
212
|
+
updateStep(db, stepId, 'failed', {}, r.stderr);
|
|
213
|
+
progressWriter(`${generateLabel}✗ failed (${(durationMs / 1000).toFixed(1)}s)`);
|
|
214
|
+
steps.push({
|
|
215
|
+
step: 'generate components',
|
|
216
|
+
status: 'failed',
|
|
217
|
+
durationMs,
|
|
218
|
+
error: r.stderr,
|
|
219
|
+
});
|
|
220
|
+
db.close();
|
|
221
|
+
return { session: sessionId, project: projectRoot, steps };
|
|
222
|
+
}
|
|
223
|
+
if (opts.dryRun) {
|
|
224
|
+
updateStep(db, stepId, 'complete', { dryRun: 'true' });
|
|
225
|
+
progressWriter(`${generateLabel}✓ prompt printed (${(durationMs / 1000).toFixed(1)}s)`);
|
|
226
|
+
steps.push({
|
|
227
|
+
step: 'generate components',
|
|
228
|
+
status: 'complete',
|
|
229
|
+
durationMs,
|
|
230
|
+
});
|
|
231
|
+
db.close();
|
|
232
|
+
return { session: sessionId, project: projectRoot, steps };
|
|
233
|
+
}
|
|
234
|
+
updateStep(db, stepId, 'complete', {
|
|
235
|
+
extractSession: extractSessionId ?? '',
|
|
236
|
+
});
|
|
237
|
+
progressWriter(`${generateLabel}✓ components stored locally (${(durationMs / 1000).toFixed(1)}s)`);
|
|
238
|
+
steps.push({ step: 'generate components', status: 'complete', durationMs });
|
|
239
|
+
}
|
|
240
|
+
// ── Step 4 (optional): print components ─────────────────────────────────
|
|
241
|
+
if (opts.print) {
|
|
242
|
+
const printLabel = stepLabel('Writing components.json');
|
|
243
|
+
const printArgs = ['print', 'components', '--out', componentsPath];
|
|
244
|
+
if (extractSessionId)
|
|
245
|
+
printArgs.push('--session', extractSessionId);
|
|
246
|
+
const stepId = createStep(db, sessionId, 'print components', {
|
|
247
|
+
out: componentsPath,
|
|
248
|
+
});
|
|
249
|
+
const t0 = Date.now();
|
|
250
|
+
const r = await runStep(printArgs, cliPath);
|
|
251
|
+
const durationMs = Date.now() - t0;
|
|
252
|
+
if (r.exitCode !== 0) {
|
|
253
|
+
if (r.stderr)
|
|
254
|
+
process.stderr.write(r.stderr);
|
|
255
|
+
updateStep(db, stepId, 'failed', {}, r.stderr);
|
|
256
|
+
progressWriter(`${printLabel}✗ failed (${(durationMs / 1000).toFixed(1)}s)`);
|
|
257
|
+
steps.push({
|
|
258
|
+
step: 'print components',
|
|
259
|
+
status: 'failed',
|
|
260
|
+
durationMs,
|
|
261
|
+
error: r.stderr,
|
|
262
|
+
});
|
|
263
|
+
db.close();
|
|
264
|
+
return { session: sessionId, project: projectRoot, steps };
|
|
265
|
+
}
|
|
266
|
+
updateStep(db, stepId, 'complete', { components: componentsPath });
|
|
267
|
+
progressWriter(`${printLabel}✓ components.json written (${(durationMs / 1000).toFixed(1)}s)`);
|
|
268
|
+
steps.push({ step: 'print components', status: 'complete', durationMs });
|
|
269
|
+
}
|
|
270
|
+
// ── Step 4/5: apply push ─────────────────────────────────────────────────
|
|
271
|
+
const applyLabelText = opts.spaceId && opts.environmentId
|
|
272
|
+
? `Applying changes to Space: ${opts.spaceId} Environment: ${opts.environmentId}`
|
|
273
|
+
: 'Applying changes to Contentful';
|
|
274
|
+
if (opts.skipApply) {
|
|
275
|
+
const pushLabel = stepLabel(applyLabelText);
|
|
276
|
+
progressWriter(`${pushLabel}– skipped (--skip-apply)`);
|
|
277
|
+
steps.push({
|
|
278
|
+
step: 'apply push',
|
|
279
|
+
status: 'skipped',
|
|
280
|
+
reason: '--skip-apply',
|
|
281
|
+
});
|
|
282
|
+
}
|
|
283
|
+
else {
|
|
284
|
+
const pushLabel = stepLabel(applyLabelText);
|
|
285
|
+
if (!opts.spaceId || !opts.environmentId || !opts.cmaToken) {
|
|
286
|
+
process.stderr.write('Error: --space-id, --environment-id, and --cma-token are required for apply push. Use --skip-apply to skip.\n');
|
|
287
|
+
db.close();
|
|
288
|
+
process.exit(1);
|
|
289
|
+
}
|
|
290
|
+
const pushArgs = [
|
|
291
|
+
'apply',
|
|
292
|
+
'push',
|
|
293
|
+
'--space-id',
|
|
294
|
+
opts.spaceId,
|
|
295
|
+
'--environment-id',
|
|
296
|
+
opts.environmentId,
|
|
297
|
+
'--cma-token',
|
|
298
|
+
opts.cmaToken,
|
|
299
|
+
];
|
|
300
|
+
// Components are stored under the extract session ID (generate command uses resolveSessionId
|
|
301
|
+
// which returns the passed --session value, i.e. the extract session). Pass that directly so
|
|
302
|
+
// apply push reads from the DB without needing a components.json file.
|
|
303
|
+
// Fall back to --components so the step fails with a clear error rather than a generic one.
|
|
304
|
+
if (extractSessionId) {
|
|
305
|
+
pushArgs.push('--session', extractSessionId);
|
|
306
|
+
}
|
|
307
|
+
else {
|
|
308
|
+
pushArgs.push('--components', componentsPath);
|
|
309
|
+
}
|
|
310
|
+
if (opts.tokens)
|
|
311
|
+
pushArgs.push('--tokens', opts.tokens);
|
|
312
|
+
if (opts.viewports)
|
|
313
|
+
pushArgs.push('--viewports', opts.viewports);
|
|
314
|
+
if (opts.host)
|
|
315
|
+
pushArgs.push('--host', opts.host);
|
|
316
|
+
if (opts.verbose)
|
|
317
|
+
pushArgs.push('--verbose');
|
|
318
|
+
pushArgs.push('--yes'); // always non-interactive in subprocess context
|
|
319
|
+
const pushStepId = createStep(db, sessionId, 'apply push', {
|
|
320
|
+
components: componentsPath,
|
|
321
|
+
});
|
|
322
|
+
const t0 = Date.now();
|
|
323
|
+
const r = await runStep(pushArgs, cliPath, { FORCE_COLOR: '1' }, true);
|
|
324
|
+
const durationMs = Date.now() - t0;
|
|
325
|
+
let pushResult = null;
|
|
326
|
+
try {
|
|
327
|
+
pushResult = JSON.parse(r.stdout);
|
|
328
|
+
}
|
|
329
|
+
catch {
|
|
330
|
+
// stdout wasn't JSON — fall back to regex parsing
|
|
331
|
+
}
|
|
332
|
+
const created = pushResult
|
|
333
|
+
? (pushResult.componentTypes?.created ?? 0) + (pushResult.designTokens?.created ?? 0)
|
|
334
|
+
: Number(/(\d+) created/.exec(r.stdout + r.stderr)?.[1] ?? 0);
|
|
335
|
+
const updated = pushResult
|
|
336
|
+
? (pushResult.componentTypes?.updated ?? 0) + (pushResult.designTokens?.updated ?? 0)
|
|
337
|
+
: Number(/(\d+) updated/.exec(r.stdout + r.stderr)?.[1] ?? 0);
|
|
338
|
+
const failed = pushResult
|
|
339
|
+
? (pushResult.componentTypes?.failed ?? 0) + (pushResult.designTokens?.failed ?? 0)
|
|
340
|
+
: Number(/(\d+) failed/.exec(r.stdout + r.stderr)?.[1] ?? 0);
|
|
341
|
+
const totalPushed = created + updated + failed;
|
|
342
|
+
if (r.exitCode !== 0 && (totalPushed === 0 || failed === totalPushed)) {
|
|
343
|
+
// Total failure — nothing was pushed
|
|
344
|
+
updateStep(db, pushStepId, 'failed', {}, r.stderr);
|
|
345
|
+
progressWriter(`${pushLabel}✗ failed (${(durationMs / 1000).toFixed(1)}s)`);
|
|
346
|
+
steps.push({
|
|
347
|
+
step: 'apply push',
|
|
348
|
+
status: 'failed',
|
|
349
|
+
durationMs,
|
|
350
|
+
error: r.stderr,
|
|
351
|
+
});
|
|
352
|
+
db.close();
|
|
353
|
+
return { session: sessionId, project: projectRoot, steps };
|
|
354
|
+
}
|
|
355
|
+
const stepStatus = failed > 0 ? 'failed' : 'complete';
|
|
356
|
+
updateStep(db, pushStepId, stepStatus === 'complete' ? 'complete' : 'failed', { components: componentsPath });
|
|
357
|
+
const statusIcon = failed > 0 ? '⚠' : '✓';
|
|
358
|
+
progressWriter(`${pushLabel}${statusIcon} ${created} created, ${updated} updated, ${failed} failed (${(durationMs / 1000).toFixed(1)}s)`);
|
|
359
|
+
steps.push({
|
|
360
|
+
step: 'apply push',
|
|
361
|
+
status: stepStatus,
|
|
362
|
+
durationMs,
|
|
363
|
+
detail: { created, updated, failed },
|
|
364
|
+
});
|
|
365
|
+
}
|
|
366
|
+
progressWriter('');
|
|
367
|
+
progressWriter(`Pipeline complete. Session: ${sessionId}`);
|
|
368
|
+
if (opts.spaceId && opts.environmentId && !opts.skipApply) {
|
|
369
|
+
progressWriter('');
|
|
370
|
+
progressWriter(`View your design system: https://app.contentful.com/spaces/${opts.spaceId}/environments/${opts.environmentId}/exo/components`);
|
|
371
|
+
}
|
|
372
|
+
db.close();
|
|
373
|
+
return { session: sessionId, project: projectRoot, steps };
|
|
374
|
+
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Expands a leading `~` to the user's home directory.
|
|
3
|
+
* Exported separately so it can be unit-tested and reused.
|
|
4
|
+
*/
|
|
5
|
+
export declare function expandTilde(input: string): string;
|
|
6
|
+
/**
|
|
7
|
+
* Normalizes a user-entered path to an absolute path, handling all common
|
|
8
|
+
* terminal path styles:
|
|
9
|
+
* - surrounding quotes ("~/path" or '~/path') → stripped
|
|
10
|
+
* - tilde prefix (~/projects/mylib) → expanded to $HOME
|
|
11
|
+
* - relative paths (../mylib, ./src, mylib) → resolved against CWD
|
|
12
|
+
* - absolute paths (/Users/ryun/projects/mylib) → passed through
|
|
13
|
+
* - trailing slashes, repeated slashes → normalized by path.resolve
|
|
14
|
+
*/
|
|
15
|
+
export declare function normalizePath(input: string): string;
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import { resolve } from 'node:path';
|
|
2
|
+
import { homedir } from 'node:os';
|
|
3
|
+
/**
|
|
4
|
+
* Expands a leading `~` to the user's home directory.
|
|
5
|
+
* Exported separately so it can be unit-tested and reused.
|
|
6
|
+
*/
|
|
7
|
+
export function expandTilde(input) {
|
|
8
|
+
if (input === '~' || input.startsWith('~/') || input.startsWith('~\\')) {
|
|
9
|
+
return homedir() + input.slice(1);
|
|
10
|
+
}
|
|
11
|
+
return input;
|
|
12
|
+
}
|
|
13
|
+
/**
|
|
14
|
+
* Normalizes a user-entered path to an absolute path, handling all common
|
|
15
|
+
* terminal path styles:
|
|
16
|
+
* - surrounding quotes ("~/path" or '~/path') → stripped
|
|
17
|
+
* - tilde prefix (~/projects/mylib) → expanded to $HOME
|
|
18
|
+
* - relative paths (../mylib, ./src, mylib) → resolved against CWD
|
|
19
|
+
* - absolute paths (/Users/ryun/projects/mylib) → passed through
|
|
20
|
+
* - trailing slashes, repeated slashes → normalized by path.resolve
|
|
21
|
+
*/
|
|
22
|
+
export function normalizePath(input) {
|
|
23
|
+
let p = input.trim();
|
|
24
|
+
// strip surrounding single or double quotes
|
|
25
|
+
if (p.length >= 2 && ((p.startsWith('"') && p.endsWith('"')) || (p.startsWith("'") && p.endsWith("'")))) {
|
|
26
|
+
p = p.slice(1, -1);
|
|
27
|
+
}
|
|
28
|
+
p = expandTilde(p);
|
|
29
|
+
return resolve(p);
|
|
30
|
+
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
export type WizardAppProps = {
|
|
3
|
+
initialSpaceId?: string;
|
|
4
|
+
initialEnvironmentId?: string;
|
|
5
|
+
initialCmaToken?: string;
|
|
6
|
+
initialAgent?: string;
|
|
7
|
+
initialProjectPath?: string;
|
|
8
|
+
host?: string;
|
|
9
|
+
};
|
|
10
|
+
export declare function WizardApp({ initialSpaceId, initialEnvironmentId, initialCmaToken, initialAgent, initialProjectPath, host, }?: WizardAppProps): React.ReactElement;
|