@catchdrift/cli 0.1.5 → 0.1.7
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/package.json +1 -1
- package/src/commands/init.mjs +67 -8
- package/src/lib/writers.mjs +2 -2
package/package.json
CHANGED
package/src/commands/init.mjs
CHANGED
|
@@ -102,8 +102,35 @@ export async function init(argv) {
|
|
|
102
102
|
|
|
103
103
|
const sources = Array.isArray(dsSources) ? dsSources : []
|
|
104
104
|
|
|
105
|
+
// ── Storybook nudge — needed to close the Figma → code loop ─────────────────
|
|
106
|
+
if (sources.includes('figma') && !sources.includes('storybook') && !storybook.found) {
|
|
107
|
+
p.log.warn(
|
|
108
|
+
'Storybook is needed to complete the loop.\n' +
|
|
109
|
+
' Without it, the overlay has no way to identify which components are from your DS.\n' +
|
|
110
|
+
' Figma tells Drift what exists in design — Storybook tells it what exists in code.'
|
|
111
|
+
)
|
|
112
|
+
const setupSB = await p.confirm({
|
|
113
|
+
message: 'Set up Storybook now? (runs npx storybook@latest init)',
|
|
114
|
+
initialValue: true,
|
|
115
|
+
})
|
|
116
|
+
if (p.isCancel(setupSB)) { p.cancel('Setup cancelled.'); process.exit(EXIT_CANCELED) }
|
|
117
|
+
|
|
118
|
+
if (setupSB) {
|
|
119
|
+
spinner.start('Running npx storybook@latest init...')
|
|
120
|
+
try {
|
|
121
|
+
execSync('npx storybook@latest init --yes', { cwd, stdio: 'ignore' })
|
|
122
|
+
spinner.stop('Storybook installed — it will be available at http://localhost:6006')
|
|
123
|
+
sources.push('storybook')
|
|
124
|
+
} catch {
|
|
125
|
+
spinner.stop('Storybook install failed — run `npx storybook@latest init` manually, then re-run `npx catchdrift init`')
|
|
126
|
+
}
|
|
127
|
+
} else {
|
|
128
|
+
p.log.info('Continuing without Storybook. Add it later and re-run `npx catchdrift init` to complete the loop.')
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
|
|
105
132
|
// ── Step 3a: Figma ───────────────────────────────────────────────────────────
|
|
106
|
-
let figmaFileKey,
|
|
133
|
+
let figmaFileKey, figmaToken, figmaWIPPages
|
|
107
134
|
if (sources.includes('figma')) {
|
|
108
135
|
figmaFileKey = await p.text({
|
|
109
136
|
message: 'Figma file key',
|
|
@@ -113,13 +140,30 @@ export async function init(argv) {
|
|
|
113
140
|
if (p.isCancel(figmaFileKey)) { p.cancel('Setup cancelled.'); process.exit(EXIT_CANCELED) }
|
|
114
141
|
figmaFileKey = figmaFileKey?.trim() || undefined
|
|
115
142
|
|
|
116
|
-
|
|
117
|
-
message: '
|
|
118
|
-
placeholder: '
|
|
119
|
-
hint: '
|
|
143
|
+
figmaToken = await p.text({
|
|
144
|
+
message: 'Figma personal access token',
|
|
145
|
+
placeholder: 'figd_... (figma.com → Profile → Settings → Security → Personal access tokens)',
|
|
146
|
+
hint: 'Used to fetch your real page list. Store in FIGMA_API_TOKEN env var — not committed to git.',
|
|
120
147
|
})
|
|
121
|
-
if (p.isCancel(
|
|
122
|
-
|
|
148
|
+
if (p.isCancel(figmaToken)) { p.cancel('Setup cancelled.'); process.exit(EXIT_CANCELED) }
|
|
149
|
+
figmaToken = figmaToken?.trim() || undefined
|
|
150
|
+
|
|
151
|
+
// Fetch actual pages from Figma so the user picks from real names
|
|
152
|
+
if (figmaToken && figmaFileKey) {
|
|
153
|
+
spinner.start('Fetching pages from Figma...')
|
|
154
|
+
const pages = await fetchFigmaPages(figmaFileKey, figmaToken)
|
|
155
|
+
spinner.stop(pages ? `Found ${pages.length} pages` : 'Could not reach Figma — skipping page selection')
|
|
156
|
+
|
|
157
|
+
if (pages?.length) {
|
|
158
|
+
const selected = await p.multiselect({
|
|
159
|
+
message: 'Which pages hold in-progress / not-yet-ready components? (drafts — won\'t be added to registry)',
|
|
160
|
+
options: pages.map(name => ({ value: name, label: name })),
|
|
161
|
+
required: false,
|
|
162
|
+
})
|
|
163
|
+
if (p.isCancel(selected)) { p.cancel('Setup cancelled.'); process.exit(EXIT_CANCELED) }
|
|
164
|
+
figmaWIPPages = Array.isArray(selected) && selected.length ? selected : undefined
|
|
165
|
+
}
|
|
166
|
+
}
|
|
123
167
|
}
|
|
124
168
|
|
|
125
169
|
// ── Step 3b: Storybook ───────────────────────────────────────────────────────
|
|
@@ -202,7 +246,7 @@ export async function init(argv) {
|
|
|
202
246
|
storybookUrl: storybookUrl || (storybook.found ? storybook.url : undefined),
|
|
203
247
|
chromaticUrl,
|
|
204
248
|
figmaFileKey,
|
|
205
|
-
|
|
249
|
+
figmaWIPPages,
|
|
206
250
|
dsPackages,
|
|
207
251
|
threshold: Number(threshold) || 80,
|
|
208
252
|
components,
|
|
@@ -292,3 +336,18 @@ ${pc.dim('Docs: https://catchdrift.ai · Issues: https://github.com/dyoon92/de
|
|
|
292
336
|
console.log('')
|
|
293
337
|
}
|
|
294
338
|
}
|
|
339
|
+
|
|
340
|
+
// ── Helpers ───────────────────────────────────────────────────────────────────
|
|
341
|
+
|
|
342
|
+
async function fetchFigmaPages(fileKey, token) {
|
|
343
|
+
try {
|
|
344
|
+
const res = await fetch(`https://api.figma.com/v1/files/${fileKey}?depth=1`, {
|
|
345
|
+
headers: { 'X-Figma-Token': token },
|
|
346
|
+
})
|
|
347
|
+
if (!res.ok) return null
|
|
348
|
+
const data = await res.json()
|
|
349
|
+
return data.document?.children?.map(page => page.name) ?? null
|
|
350
|
+
} catch {
|
|
351
|
+
return null
|
|
352
|
+
}
|
|
353
|
+
}
|
package/src/lib/writers.mjs
CHANGED
|
@@ -10,7 +10,7 @@ import { findAppEntry } from './detect.mjs'
|
|
|
10
10
|
|
|
11
11
|
// ── drift.config.ts ───────────────────────────────────────────────────────────
|
|
12
12
|
|
|
13
|
-
export function writeDriftConfig(cwd, { storybookUrl, chromaticUrl, figmaFileKey,
|
|
13
|
+
export function writeDriftConfig(cwd, { storybookUrl, chromaticUrl, figmaFileKey, figmaWIPPages, dsPackages, threshold, components }) {
|
|
14
14
|
const registry = buildComponentRegistry(components)
|
|
15
15
|
|
|
16
16
|
const dsPackagesLine = dsPackages?.length
|
|
@@ -24,7 +24,7 @@ export function writeDriftConfig(cwd, { storybookUrl, chromaticUrl, figmaFileKey
|
|
|
24
24
|
storybookUrl ? ` storybookUrl: '${storybookUrl}',` : null,
|
|
25
25
|
chromaticUrl ? ` chromaticUrl: '${chromaticUrl}',` : null,
|
|
26
26
|
figmaFileKey ? ` figmaFileKey: '${figmaFileKey}',` : null,
|
|
27
|
-
|
|
27
|
+
figmaWIPPages?.length ? ` figmaWIPPages: [${figmaWIPPages.map(p => `'${p}'`).join(', ')}], // components on these pages are drafts — not added to registry` : null,
|
|
28
28
|
` threshold: ${threshold},`,
|
|
29
29
|
dsPackagesLine,
|
|
30
30
|
` components: {`,
|