@effinrich/forgekit-storybook-plugin 2.0.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/cli.js ADDED
@@ -0,0 +1,523 @@
1
+ "use strict"; function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } else { var newObj = {}; if (obj != null) { for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) { newObj[key] = obj[key]; } } } newObj.default = obj; return newObj; } } function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
2
+
3
+
4
+
5
+
6
+
7
+ var _chunkT4UFXGMCjs = require('./chunk-T4UFXGMC.js');
8
+
9
+
10
+ var _chunkWUKJNZOFjs = require('./chunk-WUKJNZOF.js');
11
+
12
+ // src/cli/index.ts
13
+ var _yargs = require('yargs'); var _yargs2 = _interopRequireDefault(_yargs);
14
+ var _helpers = require('yargs/helpers');
15
+
16
+ // src/cli/commands/story.ts
17
+ var _path = require('path'); var path = _interopRequireWildcard(_path); var path2 = _interopRequireWildcard(_path); var path3 = _interopRequireWildcard(_path); var path4 = _interopRequireWildcard(_path); var path5 = _interopRequireWildcard(_path);
18
+
19
+ // src/cli/ui.ts
20
+ var _chalk = require('chalk'); var _chalk2 = _interopRequireDefault(_chalk);
21
+ var BRAND = _chalk2.default.hex("#6C5CE7");
22
+ var BRAND_BG = _chalk2.default.bgHex("#6C5CE7").white.bold;
23
+ var ui = {
24
+ banner() {
25
+ console.log("");
26
+ console.log(BRAND_BG(" "));
27
+ console.log(BRAND_BG(" \u26A1 ForgeKit Storybook Plugin "));
28
+ console.log(BRAND_BG(" "));
29
+ console.log("");
30
+ },
31
+ success(msg) {
32
+ console.log(_chalk2.default.green(" \u2714 ") + msg);
33
+ },
34
+ warn(msg) {
35
+ console.log(_chalk2.default.yellow(" \u26A0 ") + msg);
36
+ },
37
+ error(msg) {
38
+ console.log(_chalk2.default.red(" \u2716 ") + msg);
39
+ },
40
+ info(msg) {
41
+ console.log(_chalk2.default.cyan(" \u2139 ") + msg);
42
+ },
43
+ skip(msg) {
44
+ console.log(_chalk2.default.dim(" \u2023 ") + _chalk2.default.dim(msg));
45
+ },
46
+ step(number, total, label) {
47
+ console.log("");
48
+ console.log(
49
+ _chalk2.default.bold(BRAND(` [${number}/${total}]`)) + _chalk2.default.bold(` ${label}`)
50
+ );
51
+ },
52
+ fileCreated(filePath) {
53
+ console.log(_chalk2.default.green(" CREATE ") + _chalk2.default.white(filePath));
54
+ },
55
+ fileUpdated(filePath) {
56
+ console.log(_chalk2.default.yellow(" UPDATE ") + _chalk2.default.white(filePath));
57
+ },
58
+ fileSkipped(filePath) {
59
+ console.log(_chalk2.default.dim(" SKIP ") + _chalk2.default.dim(filePath));
60
+ },
61
+ analysisReport(report) {
62
+ console.log("");
63
+ console.log(_chalk2.default.bold(" Component: ") + BRAND(report.name));
64
+ console.log(_chalk2.default.dim(" \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500"));
65
+ console.log(
66
+ _chalk2.default.bold(" Props: ") + (report.propsCount > 0 ? _chalk2.default.white(`${report.propsCount} detected`) : _chalk2.default.dim("none"))
67
+ );
68
+ const features = [];
69
+ if (report.hasChildren) features.push("children");
70
+ if (report.usesRouter) features.push(_chalk2.default.blue("router"));
71
+ if (report.usesChakra) features.push(_chalk2.default.magenta("chakra"));
72
+ if (report.usesReactQuery) features.push(_chalk2.default.cyan("react-query"));
73
+ if (features.length > 0) {
74
+ console.log(
75
+ _chalk2.default.bold(" Features: ") + features.join(_chalk2.default.dim(" \xB7 "))
76
+ );
77
+ }
78
+ console.log(_chalk2.default.bold(" Export: ") + _chalk2.default.white(report.exportType));
79
+ if (report.storiesGenerated.length > 0) {
80
+ console.log(
81
+ _chalk2.default.bold(" Stories: ") + _chalk2.default.green(report.storiesGenerated.join(_chalk2.default.dim(", ")))
82
+ );
83
+ }
84
+ console.log("");
85
+ },
86
+ done(msg) {
87
+ console.log("");
88
+ console.log(_chalk2.default.green.bold(" Done! ") + (msg ? _chalk2.default.white(msg) : ""));
89
+ console.log("");
90
+ },
91
+ separator() {
92
+ console.log(_chalk2.default.dim(" \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500"));
93
+ },
94
+ hint(msg) {
95
+ console.log(_chalk2.default.dim(` \u{1F4A1} ${msg}`));
96
+ },
97
+ command(cmd) {
98
+ return _chalk2.default.cyan.bold(cmd);
99
+ }
100
+ };
101
+
102
+ // src/cli/commands/story.ts
103
+ var storyCommand = {
104
+ command: "story <path>",
105
+ describe: "Generate a Storybook story for a React component",
106
+ builder: (yargs2) => yargs2.positional("path", {
107
+ describe: "Path to the component file",
108
+ type: "string",
109
+ demandOption: true
110
+ }).option("story-title", {
111
+ describe: "Custom Storybook title",
112
+ type: "string"
113
+ }).option("skip-interaction-tests", {
114
+ describe: "Skip generating play functions",
115
+ type: "boolean",
116
+ default: false
117
+ }).option("overwrite", {
118
+ describe: "Overwrite existing story file",
119
+ type: "boolean",
120
+ default: false
121
+ }).option("dry-run", {
122
+ describe: "Preview without writing files",
123
+ type: "boolean",
124
+ default: false
125
+ }),
126
+ handler: async (argv) => {
127
+ ui.banner();
128
+ try {
129
+ ui.step(1, 3, "Analyzing component");
130
+ const result = await _chunkT4UFXGMCjs.forgeStory.call(void 0, {
131
+ componentPath: argv.path,
132
+ storyTitle: argv["story-title"],
133
+ skipInteractionTests: argv["skip-interaction-tests"],
134
+ overwrite: argv.overwrite,
135
+ dryRun: argv["dry-run"]
136
+ });
137
+ if (argv["dry-run"]) {
138
+ ui.info("Dry run \u2014 no files will be written.");
139
+ ui.separator();
140
+ console.log(result.content);
141
+ ui.separator();
142
+ return;
143
+ }
144
+ ui.step(2, 3, "Generating story");
145
+ ui.step(3, 3, "Writing file");
146
+ const relPath = path.relative(process.cwd(), result.storyPath);
147
+ ui.fileCreated(relPath);
148
+ ui.analysisReport({
149
+ name: result.analysis.name,
150
+ propsCount: result.analysis.props.length,
151
+ hasChildren: result.analysis.hasChildren,
152
+ usesRouter: result.analysis.usesRouter,
153
+ usesChakra: result.analysis.usesChakra,
154
+ usesReactQuery: result.analysis.usesReactQuery,
155
+ exportType: result.analysis.exportType,
156
+ storiesGenerated: result.storiesGenerated
157
+ });
158
+ ui.done(`Story generated at ${relPath}`);
159
+ } catch (err) {
160
+ ui.error(err.message);
161
+ process.exit(1);
162
+ }
163
+ }
164
+ };
165
+
166
+ // src/cli/commands/stories.ts
167
+
168
+ var storiesCommand = {
169
+ command: "stories <dir>",
170
+ describe: "Bulk generate stories for all components in a directory",
171
+ builder: (yargs2) => yargs2.positional("dir", {
172
+ describe: "Directory to scan for components",
173
+ type: "string",
174
+ demandOption: true
175
+ }).option("skip-interaction-tests", {
176
+ describe: "Skip generating play functions",
177
+ type: "boolean",
178
+ default: false
179
+ }).option("overwrite", {
180
+ describe: "Overwrite existing story files",
181
+ type: "boolean",
182
+ default: false
183
+ }).option("dry-run", {
184
+ describe: "Preview without writing files",
185
+ type: "boolean",
186
+ default: false
187
+ }).option("include-tests", {
188
+ describe: "Also generate Playwright component tests",
189
+ type: "boolean",
190
+ default: false
191
+ }),
192
+ handler: async (argv) => {
193
+ ui.banner();
194
+ try {
195
+ ui.step(1, 3, "Scanning for components");
196
+ const result = await _chunkT4UFXGMCjs.forgeStories.call(void 0, {
197
+ dir: argv.dir,
198
+ skipInteractionTests: argv["skip-interaction-tests"],
199
+ overwrite: argv.overwrite,
200
+ dryRun: argv["dry-run"],
201
+ includeComponentTests: argv["include-tests"]
202
+ });
203
+ ui.step(2, 3, `Generating ${result.generated} stories`);
204
+ ui.info(`Found ${result.total} analyzable components`);
205
+ ui.success(`${result.alreadyCovered} already have stories`);
206
+ if (result.generated > 0) {
207
+ ui.success(`${result.generated} stories generated`);
208
+ }
209
+ if (result.failed > 0) {
210
+ ui.warn(`${result.failed} failed`);
211
+ for (const err of result.errors) {
212
+ const rel = path2.relative(process.cwd(), err.file);
213
+ ui.error(` ${rel}: ${err.error}`);
214
+ }
215
+ }
216
+ if (result.notAnalyzable > 0) {
217
+ ui.skip(`${result.notAnalyzable} could not be analyzed`);
218
+ }
219
+ ui.step(3, 3, "Coverage report");
220
+ ui.separator();
221
+ console.log("");
222
+ const { coverage } = result;
223
+ const method = ["A", "B"].includes(coverage.grade) ? "success" : ["C", "D"].includes(coverage.grade) ? "warn" : "error";
224
+ ui[method](
225
+ `Story Coverage: ${coverage.percentage}% (${coverage.covered}/${coverage.total}) \u2014 Grade: ${coverage.grade}`
226
+ );
227
+ console.log("");
228
+ ui.info(`Generated: ${result.generated}`);
229
+ if (result.failed > 0) ui.warn(`Failed: ${result.failed}`);
230
+ ui.info(`Already covered: ${result.alreadyCovered}`);
231
+ ui.info(`Total components: ${result.total}`);
232
+ ui.done(
233
+ argv["dry-run"] ? "Dry run complete \u2014 no files were written." : `Generated ${result.generated} story files.`
234
+ );
235
+ } catch (err) {
236
+ ui.error(err.message);
237
+ process.exit(1);
238
+ }
239
+ }
240
+ };
241
+
242
+ // src/cli/commands/test.ts
243
+
244
+ var testCommand = {
245
+ command: "test <path>",
246
+ describe: "Generate a Playwright component test for a React component",
247
+ builder: (yargs2) => yargs2.positional("path", {
248
+ describe: "Path to the component file",
249
+ type: "string",
250
+ demandOption: true
251
+ }).option("overwrite", {
252
+ describe: "Overwrite existing test file",
253
+ type: "boolean",
254
+ default: false
255
+ }).option("dry-run", {
256
+ describe: "Preview without writing files",
257
+ type: "boolean",
258
+ default: false
259
+ }),
260
+ handler: async (argv) => {
261
+ ui.banner();
262
+ try {
263
+ ui.step(1, 3, "Analyzing component");
264
+ const result = await _chunkWUKJNZOFjs.forgeTest.call(void 0, {
265
+ componentPath: argv.path,
266
+ overwrite: argv.overwrite,
267
+ dryRun: argv["dry-run"]
268
+ });
269
+ if (argv["dry-run"]) {
270
+ ui.info("Dry run \u2014 no files will be written.");
271
+ ui.separator();
272
+ console.log(result.content);
273
+ ui.separator();
274
+ return;
275
+ }
276
+ ui.step(2, 3, "Generating Playwright component test");
277
+ ui.step(3, 3, "Writing file");
278
+ const relPath = path3.relative(process.cwd(), result.testPath);
279
+ ui.fileCreated(relPath);
280
+ ui.analysisReport({
281
+ name: result.analysis.name,
282
+ propsCount: result.analysis.props.length,
283
+ hasChildren: result.analysis.hasChildren,
284
+ usesRouter: result.analysis.usesRouter,
285
+ usesChakra: result.analysis.usesChakra,
286
+ usesReactQuery: result.analysis.usesReactQuery,
287
+ exportType: result.analysis.exportType,
288
+ storiesGenerated: [
289
+ "mounts and renders",
290
+ "matches screenshot",
291
+ ...result.analysis.props.some((p) => p.isCallback) ? ["handles interactions"] : [],
292
+ ...result.analysis.props.some(
293
+ (p) => p.name === "disabled" || p.name === "isDisabled"
294
+ ) ? ["disabled state"] : [],
295
+ "meets a11y standards"
296
+ ]
297
+ });
298
+ ui.done(`Playwright test generated at ${relPath}`);
299
+ } catch (err) {
300
+ ui.error(err.message);
301
+ process.exit(1);
302
+ }
303
+ }
304
+ };
305
+
306
+ // src/cli/commands/watch.ts
307
+
308
+ var watchCommand = {
309
+ command: "watch <dir>",
310
+ describe: "Watch for component changes and auto-generate stories",
311
+ builder: (yargs2) => yargs2.positional("dir", {
312
+ describe: "Directory to watch",
313
+ type: "string",
314
+ demandOption: true
315
+ }).option("skip-interaction-tests", {
316
+ describe: "Skip generating play functions",
317
+ type: "boolean",
318
+ default: false
319
+ }).option("debounce", {
320
+ describe: "Debounce interval in milliseconds",
321
+ type: "number",
322
+ default: 300
323
+ }),
324
+ handler: async (argv) => {
325
+ ui.banner();
326
+ ui.info("Watching for component changes in:");
327
+ console.log(` ${ui.command(path4.resolve(argv.dir))}`);
328
+ console.log("");
329
+ ui.hint("Press Ctrl+C to stop.");
330
+ console.log("");
331
+ try {
332
+ const handle = _chunkT4UFXGMCjs.watchDirectory.call(void 0,
333
+ {
334
+ dir: argv.dir,
335
+ debounceMs: argv.debounce,
336
+ skipInteractionTests: argv["skip-interaction-tests"]
337
+ },
338
+ (event) => {
339
+ switch (event.type) {
340
+ case "ready":
341
+ ui.success("Watching for changes...");
342
+ break;
343
+ case "generate":
344
+ ui.separator();
345
+ ui.fileCreated(path4.relative(process.cwd(), event.storyPath));
346
+ break;
347
+ case "update":
348
+ ui.separator();
349
+ ui.fileUpdated(path4.relative(process.cwd(), event.storyPath));
350
+ break;
351
+ case "error":
352
+ ui.separator();
353
+ ui.error(
354
+ `Failed: ${path4.relative(process.cwd(), event.file)} \u2014 ${event.error.message}`
355
+ );
356
+ break;
357
+ }
358
+ }
359
+ );
360
+ await new Promise((resolve2) => {
361
+ const cleanup = async () => {
362
+ await handle.close();
363
+ console.log("");
364
+ ui.info("Stopped watching for changes.");
365
+ resolve2();
366
+ };
367
+ process.on("SIGINT", cleanup);
368
+ process.on("SIGTERM", cleanup);
369
+ });
370
+ } catch (err) {
371
+ ui.error(err.message);
372
+ process.exit(1);
373
+ }
374
+ }
375
+ };
376
+
377
+ // src/cli/commands/coverage.ts
378
+ var coverageCommand = {
379
+ command: "coverage <dir>",
380
+ describe: "Report story coverage without generating any files",
381
+ builder: (yargs2) => yargs2.positional("dir", {
382
+ describe: "Directory to scan for components",
383
+ type: "string",
384
+ demandOption: true
385
+ }).option("json", {
386
+ describe: "Output results as JSON",
387
+ type: "boolean",
388
+ default: false
389
+ }),
390
+ handler: async (argv) => {
391
+ try {
392
+ const scan = await _chunkT4UFXGMCjs.scanDirectory.call(void 0, argv.dir);
393
+ const totalAnalyzable = scan.total - scan.notAnalyzable.length;
394
+ const coverage = _chunkT4UFXGMCjs.scoreCoverage.call(void 0, scan.withStories.length, totalAnalyzable);
395
+ if (argv.json) {
396
+ console.log(
397
+ JSON.stringify(
398
+ {
399
+ covered: coverage.covered,
400
+ total: coverage.total,
401
+ percentage: coverage.percentage,
402
+ grade: coverage.grade,
403
+ withStories: scan.withStories.length,
404
+ withoutStories: scan.withoutStories.length,
405
+ notAnalyzable: scan.notAnalyzable.length
406
+ },
407
+ null,
408
+ 2
409
+ )
410
+ );
411
+ return;
412
+ }
413
+ ui.banner();
414
+ ui.info(`Scanned ${scan.total} component files`);
415
+ ui.success(`${scan.withStories.length} have stories`);
416
+ if (scan.withoutStories.length > 0) {
417
+ ui.warn(`${scan.withoutStories.length} missing stories`);
418
+ }
419
+ if (scan.notAnalyzable.length > 0) {
420
+ ui.skip(`${scan.notAnalyzable.length} could not be analyzed`);
421
+ }
422
+ ui.separator();
423
+ console.log("");
424
+ const method = ["A", "B"].includes(coverage.grade) ? "success" : ["C", "D"].includes(coverage.grade) ? "warn" : "error";
425
+ ui[method](
426
+ `Story Coverage: ${coverage.percentage}% (${coverage.covered}/${coverage.total}) \u2014 Grade: ${coverage.grade}`
427
+ );
428
+ ui.done();
429
+ } catch (err) {
430
+ ui.error(err.message);
431
+ process.exit(1);
432
+ }
433
+ }
434
+ };
435
+
436
+ // src/cli/commands/init.ts
437
+ var _fs = require('fs'); var fs = _interopRequireWildcard(_fs);
438
+
439
+ var initCommand = {
440
+ command: "init",
441
+ describe: "Check prerequisites and show setup guide",
442
+ handler: async () => {
443
+ ui.banner();
444
+ ui.step(1, 2, "Checking prerequisites");
445
+ const cwd = process.cwd();
446
+ const pkgPath = path5.join(cwd, "package.json");
447
+ if (!fs.existsSync(pkgPath)) {
448
+ ui.error("No package.json found in current directory.");
449
+ ui.hint("Run this command from your project root.");
450
+ process.exit(1);
451
+ }
452
+ const pkg = JSON.parse(fs.readFileSync(pkgPath, "utf-8"));
453
+ const allDeps = {
454
+ ...pkg.dependencies,
455
+ ...pkg.devDependencies
456
+ };
457
+ const checks = [
458
+ { name: "react", label: "React" },
459
+ { name: "storybook", label: "Storybook" },
460
+ { name: "@storybook/react-vite", label: "@storybook/react-vite" },
461
+ { name: "typescript", label: "TypeScript" }
462
+ ];
463
+ let allGood = true;
464
+ for (const check of checks) {
465
+ if (allDeps[check.name]) {
466
+ ui.success(`${check.label} \u2014 ${allDeps[check.name]}`);
467
+ } else {
468
+ ui.warn(`${check.label} \u2014 not found`);
469
+ allGood = false;
470
+ }
471
+ }
472
+ const optionalChecks = [
473
+ { name: "@storybook/test", label: "@storybook/test (interaction tests)" },
474
+ {
475
+ name: "@storybook/addon-a11y",
476
+ label: "@storybook/addon-a11y (accessibility)"
477
+ },
478
+ {
479
+ name: "@playwright/experimental-ct-react",
480
+ label: "Playwright CT (component tests)"
481
+ },
482
+ {
483
+ name: "@axe-core/playwright",
484
+ label: "axe-core Playwright (a11y in CT)"
485
+ }
486
+ ];
487
+ console.log("");
488
+ ui.info("Optional dependencies:");
489
+ for (const check of optionalChecks) {
490
+ if (allDeps[check.name]) {
491
+ ui.success(`${check.label}`);
492
+ } else {
493
+ ui.skip(`${check.label} \u2014 not installed`);
494
+ }
495
+ }
496
+ ui.step(2, 2, "Available commands");
497
+ console.log("");
498
+ console.log(
499
+ ` ${ui.command("forgekit-storybook-plugin story <path>")} Generate a story for one component`
500
+ );
501
+ console.log(
502
+ ` ${ui.command("forgekit-storybook-plugin stories <dir>")} Bulk generate stories for a directory`
503
+ );
504
+ console.log(
505
+ ` ${ui.command("forgekit-storybook-plugin test <path>")} Generate a Playwright component test`
506
+ );
507
+ console.log(
508
+ ` ${ui.command("forgekit-storybook-plugin watch <dir>")} Watch and auto-generate on changes`
509
+ );
510
+ console.log(
511
+ ` ${ui.command("forgekit-storybook-plugin coverage <dir>")} Report story coverage`
512
+ );
513
+ console.log("");
514
+ if (!allGood) {
515
+ ui.hint("Install missing dependencies before generating stories.");
516
+ }
517
+ ui.done("ForgeKit Storybook Plugin is ready!");
518
+ }
519
+ };
520
+
521
+ // src/cli/index.ts
522
+ _yargs2.default.call(void 0, _helpers.hideBin.call(void 0, process.argv)).scriptName("forgekit-storybook-plugin").usage("$0 <command> [options]").command(storyCommand).command(storiesCommand).command(testCommand).command(watchCommand).command(coverageCommand).command(initCommand).demandCommand(1, "Please specify a command.").strict().alias("h", "help").alias("v", "version").parse();
523
+ //# sourceMappingURL=cli.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["/Users/richtillman/Documents/GitHub/forgekit-storybook-plugin-1/dist/cli.js","../src/cli/index.ts","../src/cli/commands/story.ts","../src/cli/ui.ts","../src/cli/commands/stories.ts","../src/cli/commands/test.ts","../src/cli/commands/watch.ts","../src/cli/commands/coverage.ts","../src/cli/commands/init.ts"],"names":[],"mappings":"AAAA;AACE;AACA;AACA;AACA;AACA;AACF,sDAA4B;AAC5B;AACE;AACF,sDAA4B;AAC5B;AACA;ACXA,4EAAkB;AAClB,wCAAwB;ADaxB;AACA;AEdA,uPAAsB;AFgBtB;AACA;AGlBA,4EAAkB;AAElB,IAAM,MAAA,EAAQ,eAAA,CAAM,GAAA,CAAI,SAAS,CAAA;AACjC,IAAM,SAAA,EAAW,eAAA,CAAM,KAAA,CAAM,SAAS,CAAA,CAAE,KAAA,CAAM,IAAA;AAEvC,IAAM,GAAA,EAAK;AAAA,EAChB,MAAA,CAAA,EAAe;AACb,IAAA,OAAA,CAAQ,GAAA,CAAI,EAAE,CAAA;AACd,IAAA,OAAA,CAAQ,GAAA,CAAI,QAAA,CAAS,wCAAwC,CAAC,CAAA;AAC9D,IAAA,OAAA,CAAQ,GAAA,CAAI,QAAA,CAAS,6CAAwC,CAAC,CAAA;AAC9D,IAAA,OAAA,CAAQ,GAAA,CAAI,QAAA,CAAS,wCAAwC,CAAC,CAAA;AAC9D,IAAA,OAAA,CAAQ,GAAA,CAAI,EAAE,CAAA;AAAA,EAChB,CAAA;AAAA,EAEA,OAAA,CAAQ,GAAA,EAAmB;AACzB,IAAA,OAAA,CAAQ,GAAA,CAAI,eAAA,CAAM,KAAA,CAAM,WAAM,EAAA,EAAI,GAAG,CAAA;AAAA,EACvC,CAAA;AAAA,EAEA,IAAA,CAAK,GAAA,EAAmB;AACtB,IAAA,OAAA,CAAQ,GAAA,CAAI,eAAA,CAAM,MAAA,CAAO,WAAM,EAAA,EAAI,GAAG,CAAA;AAAA,EACxC,CAAA;AAAA,EAEA,KAAA,CAAM,GAAA,EAAmB;AACvB,IAAA,OAAA,CAAQ,GAAA,CAAI,eAAA,CAAM,GAAA,CAAI,WAAM,EAAA,EAAI,GAAG,CAAA;AAAA,EACrC,CAAA;AAAA,EAEA,IAAA,CAAK,GAAA,EAAmB;AACtB,IAAA,OAAA,CAAQ,GAAA,CAAI,eAAA,CAAM,IAAA,CAAK,WAAM,EAAA,EAAI,GAAG,CAAA;AAAA,EACtC,CAAA;AAAA,EAEA,IAAA,CAAK,GAAA,EAAmB;AACtB,IAAA,OAAA,CAAQ,GAAA,CAAI,eAAA,CAAM,GAAA,CAAI,WAAM,EAAA,EAAI,eAAA,CAAM,GAAA,CAAI,GAAG,CAAC,CAAA;AAAA,EAChD,CAAA;AAAA,EAEA,IAAA,CAAK,MAAA,EAAgB,KAAA,EAAe,KAAA,EAAqB;AACvD,IAAA,OAAA,CAAQ,GAAA,CAAI,EAAE,CAAA;AACd,IAAA,OAAA,CAAQ,GAAA;AAAA,MACN,eAAA,CAAM,IAAA,CAAK,KAAA,CAAM,CAAA,GAAA,EAAM,MAAM,CAAA,CAAA,EAAI,KAAK,CAAA,CAAA,CAAG,CAAC,EAAA,EAAI,eAAA,CAAM,IAAA,CAAK,CAAA,CAAA,EAAI,KAAK,CAAA,CAAA;AACpE,IAAA;AACF,EAAA;AAEoC,EAAA;AAC0B,IAAA;AAC9D,EAAA;AAEoC,EAAA;AAC2B,IAAA;AAC/D,EAAA;AAEoC,EAAA;AACsB,IAAA;AAC1D,EAAA;AAWS,EAAA;AACO,IAAA;AAC8C,IAAA;AACtC,IAAA;AACd,IAAA;AAGM,MAAA;AAEd,IAAA;AAE4B,IAAA;AACoB,IAAA;AACS,IAAA;AACG,IAAA;AACM,IAAA;AAEzC,IAAA;AACf,MAAA;AACwD,QAAA;AAChE,MAAA;AACF,IAAA;AAE+D,IAAA;AAEvB,IAAA;AAC9B,MAAA;AAEe,QAAA;AACvB,MAAA;AACF,IAAA;AACc,IAAA;AAChB,EAAA;AAEyB,EAAA;AACT,IAAA;AACuD,IAAA;AACvD,IAAA;AAChB,EAAA;AAEkB,EAAA;AACM,IAAA;AACxB,EAAA;AAEwB,EAAA;AACc,IAAA;AACtC,EAAA;AAE6B,EAAA;AACD,IAAA;AAC5B,EAAA;AACF;AHbyE;AACA;AEvFX;AACnD,EAAA;AACC,EAAA;AAGc,EAAA;AACR,IAAA;AACJ,IAAA;AACQ,IAAA;AAEO,EAAA;AACX,IAAA;AACJ,IAAA;AAE0B,EAAA;AACtB,IAAA;AACJ,IAAA;AACG,IAAA;AAEU,EAAA;AACT,IAAA;AACJ,IAAA;AACG,IAAA;AAEQ,EAAA;AACP,IAAA;AACJ,IAAA;AACG,IAAA;AACV,EAAA;AACoB,EAAA;AACb,IAAA;AAEN,IAAA;AACiC,MAAA;AAEH,MAAA;AACV,QAAA;AACU,QAAA;AACqB,QAAA;AACnC,QAAA;AACM,QAAA;AACvB,MAAA;AAEoB,MAAA;AAC0B,QAAA;AAChC,QAAA;AACa,QAAA;AACb,QAAA;AACb,QAAA;AACF,MAAA;AAEgC,MAAA;AACJ,MAAA;AAEiC,MAAA;AACvC,MAAA;AAEJ,MAAA;AACM,QAAA;AACY,QAAA;AACL,QAAA;AACD,QAAA;AACA,QAAA;AACI,QAAA;AACJ,QAAA;AACH,QAAA;AAC1B,MAAA;AAEsC,MAAA;AAC3B,IAAA;AACmB,MAAA;AACjB,MAAA;AAChB,IAAA;AACF,EAAA;AACF;AF4EyE;AACA;AIpKnD;AAa4C;AACvD,EAAA;AACC,EAAA;AAGa,EAAA;AACP,IAAA;AACJ,IAAA;AACQ,IAAA;AAEkB,EAAA;AACtB,IAAA;AACJ,IAAA;AACG,IAAA;AAEU,EAAA;AACT,IAAA;AACJ,IAAA;AACG,IAAA;AAEQ,EAAA;AACP,IAAA;AACJ,IAAA;AACG,IAAA;AAEc,EAAA;AACb,IAAA;AACJ,IAAA;AACG,IAAA;AACV,EAAA;AACoB,EAAA;AACb,IAAA;AAEN,IAAA;AACqC,MAAA;AAEL,MAAA;AACtB,QAAA;AACyC,QAAA;AACnC,QAAA;AACM,QAAA;AACqB,QAAA;AAC5C,MAAA;AAEqD,MAAA;AAED,MAAA;AACK,MAAA;AAEhC,MAAA;AAC0B,QAAA;AACpD,MAAA;AACuB,MAAA;AACY,QAAA;AACA,QAAA;AACkB,UAAA;AAChB,UAAA;AACnC,QAAA;AACF,MAAA;AAC8B,MAAA;AAC2B,QAAA;AACzD,MAAA;AAG+B,MAAA;AAClB,MAAA;AACC,MAAA;AAEO,MAAA;AAEhB,MAAA;AAEI,MAAA;AACyD,QAAA;AAClE,MAAA;AAEc,MAAA;AAC0B,MAAA;AACiB,MAAA;AACN,MAAA;AACR,MAAA;AAExC,MAAA;AAEG,QAAA;AAEN,MAAA;AACY,IAAA;AACmB,MAAA;AACjB,MAAA;AAChB,IAAA;AACF,EAAA;AACF;AJsIyE;AACA;AKhPnD;AAWsC;AACjD,EAAA;AACC,EAAA;AAGc,EAAA;AACR,IAAA;AACJ,IAAA;AACQ,IAAA;AAEK,EAAA;AACT,IAAA;AACJ,IAAA;AACG,IAAA;AAEQ,EAAA;AACP,IAAA;AACJ,IAAA;AACG,IAAA;AACV,EAAA;AACoB,EAAA;AACb,IAAA;AAEN,IAAA;AACiC,MAAA;AAEJ,MAAA;AACT,QAAA;AACJ,QAAA;AACM,QAAA;AACvB,MAAA;AAEoB,MAAA;AAC0B,QAAA;AAChC,QAAA;AACa,QAAA;AACb,QAAA;AACb,QAAA;AACF,MAAA;AAEoD,MAAA;AACxB,MAAA;AAEgC,MAAA;AACtC,MAAA;AAEJ,MAAA;AACM,QAAA;AACY,QAAA;AACL,QAAA;AACD,QAAA;AACA,QAAA;AACI,QAAA;AACJ,QAAA;AACV,QAAA;AAChB,UAAA;AACA,UAAA;AAEK,UAAA;AAEqB,UAAA;AACmB,YAAA;AAGxC,UAAA;AACL,UAAA;AACF,QAAA;AACD,MAAA;AAEgD,MAAA;AACrC,IAAA;AACmB,MAAA;AACjB,MAAA;AAChB,IAAA;AACF,EAAA;AACF;ALyNyE;AACA;AMhTnD;AAWwC;AACnD,EAAA;AACC,EAAA;AAGa,EAAA;AACP,IAAA;AACJ,IAAA;AACQ,IAAA;AAEkB,EAAA;AACtB,IAAA;AACJ,IAAA;AACG,IAAA;AAES,EAAA;AACR,IAAA;AACJ,IAAA;AACG,IAAA;AACV,EAAA;AACoB,EAAA;AACb,IAAA;AAEkC,IAAA;AACa,IAAA;AAC3C,IAAA;AACiB,IAAA;AACjB,IAAA;AAEV,IAAA;AACa,MAAA;AACb,QAAA;AACY,UAAA;AACO,UAAA;AACkC,UAAA;AACrD,QAAA;AACW,QAAA;AACW,UAAA;AACb,YAAA;AACiC,cAAA;AACpC,cAAA;AACG,YAAA;AACU,cAAA;AACqC,cAAA;AAClD,cAAA;AACG,YAAA;AACU,cAAA;AACqC,cAAA;AAClD,cAAA;AACG,YAAA;AACU,cAAA;AACV,cAAA;AACkD,gBAAA;AACrD,cAAA;AACA,cAAA;AACJ,UAAA;AACF,QAAA;AACF,MAAA;AAGqC,MAAA;AACP,QAAA;AACP,UAAA;AACL,UAAA;AACyB,UAAA;AAC/B,UAAA;AACV,QAAA;AAE4B,QAAA;AACC,QAAA;AAC9B,MAAA;AACW,IAAA;AACmB,MAAA;AACjB,MAAA;AAChB,IAAA;AACF,EAAA;AACF;AN+RyE;AACA;AO7WL;AACzD,EAAA;AACC,EAAA;AAGa,EAAA;AACP,IAAA;AACJ,IAAA;AACQ,IAAA;AAEA,EAAA;AACJ,IAAA;AACJ,IAAA;AACG,IAAA;AACV,EAAA;AACoB,EAAA;AACnB,IAAA;AACuC,MAAA;AACe,MAAA;AACA,MAAA;AAEzC,MAAA;AACL,QAAA;AACD,UAAA;AACH,YAAA;AACoB,cAAA;AACF,cAAA;AACK,cAAA;AACL,cAAA;AACc,cAAA;AACM,cAAA;AACF,cAAA;AACpC,YAAA;AACA,YAAA;AACA,YAAA;AACF,UAAA;AACF,QAAA;AACA,QAAA;AACF,MAAA;AAEU,MAAA;AAEqC,MAAA;AACK,MAAA;AAChB,MAAA;AACqB,QAAA;AACzD,MAAA;AACmC,MAAA;AAC2B,QAAA;AAC9D,MAAA;AAEa,MAAA;AACC,MAAA;AAGT,MAAA;AAEI,MAAA;AACyD,QAAA;AAClE,MAAA;AAEQ,MAAA;AACI,IAAA;AACmB,MAAA;AACjB,MAAA;AAChB,IAAA;AACF,EAAA;AACF;APoWyE;AACA;AQlbrD;AACE;AAIoB;AAC/B,EAAA;AACC,EAAA;AACW,EAAA;AACT,IAAA;AAC4B,IAAA;AAEd,IAAA;AACqB,IAAA;AAEhB,IAAA;AAC2B,MAAA;AACJ,MAAA;AACpC,MAAA;AAChB,IAAA;AAEwD,IAAA;AACxC,IAAA;AACP,MAAA;AACA,MAAA;AACT,IAAA;AAEe,IAAA;AACmB,MAAA;AACQ,MAAA;AACwB,MAAA;AACtB,MAAA;AAC5C,IAAA;AAEc,IAAA;AACc,IAAA;AACD,MAAA;AAC6B,QAAA;AAC/C,MAAA;AAC+B,QAAA;AAC1B,QAAA;AACZ,MAAA;AACF,IAAA;AAGuB,IAAA;AACa,MAAA;AAClC,MAAA;AACQ,QAAA;AACC,QAAA;AACT,MAAA;AACA,MAAA;AACQ,QAAA;AACC,QAAA;AACT,MAAA;AACA,MAAA;AACQ,QAAA;AACC,QAAA;AACT,MAAA;AACF,IAAA;AAEc,IAAA;AACkB,IAAA;AACI,IAAA;AACT,MAAA;AACI,QAAA;AACtB,MAAA;AACmC,QAAA;AAC1C,MAAA;AACF,IAAA;AAGkC,IAAA;AACpB,IAAA;AACN,IAAA;AACmD,MAAA;AAC3D,IAAA;AACQ,IAAA;AACoD,MAAA;AAC5D,IAAA;AACQ,IAAA;AACkD,MAAA;AAC1D,IAAA;AACQ,IAAA;AACkD,MAAA;AAC1D,IAAA;AACQ,IAAA;AACqD,MAAA;AAC7D,IAAA;AACc,IAAA;AAEA,IAAA;AACqD,MAAA;AACnE,IAAA;AAE6C,IAAA;AAC/C,EAAA;AACF;ARqayE;AACA;AC5ftE","file":"/Users/richtillman/Documents/GitHub/forgekit-storybook-plugin-1/dist/cli.js","sourcesContent":[null,"import yargs from 'yargs'\nimport { hideBin } from 'yargs/helpers'\n\nimport { storyCommand } from './commands/story'\nimport { storiesCommand } from './commands/stories'\nimport { testCommand } from './commands/test'\nimport { watchCommand } from './commands/watch'\nimport { coverageCommand } from './commands/coverage'\nimport { initCommand } from './commands/init'\n\nyargs(hideBin(process.argv))\n .scriptName('forgekit-storybook-plugin')\n .usage('$0 <command> [options]')\n .command(storyCommand)\n .command(storiesCommand)\n .command(testCommand)\n .command(watchCommand)\n .command(coverageCommand)\n .command(initCommand)\n .demandCommand(1, 'Please specify a command.')\n .strict()\n .alias('h', 'help')\n .alias('v', 'version')\n .parse()\n","import type { CommandModule } from 'yargs';\nimport * as path from 'node:path';\n\nimport { ui } from '../ui';\nimport { forgeStory } from '../../api/forge-story';\n\ninterface StoryArgs {\n path: string;\n 'story-title'?: string;\n 'skip-interaction-tests': boolean;\n overwrite: boolean;\n 'dry-run': boolean;\n}\n\nexport const storyCommand: CommandModule<object, StoryArgs> = {\n command: 'story <path>',\n describe: 'Generate a Storybook story for a React component',\n builder: (yargs) =>\n yargs\n .positional('path', {\n describe: 'Path to the component file',\n type: 'string',\n demandOption: true,\n })\n .option('story-title', {\n describe: 'Custom Storybook title',\n type: 'string',\n })\n .option('skip-interaction-tests', {\n describe: 'Skip generating play functions',\n type: 'boolean',\n default: false,\n })\n .option('overwrite', {\n describe: 'Overwrite existing story file',\n type: 'boolean',\n default: false,\n })\n .option('dry-run', {\n describe: 'Preview without writing files',\n type: 'boolean',\n default: false,\n }),\n handler: async (argv) => {\n ui.banner();\n\n try {\n ui.step(1, 3, 'Analyzing component');\n\n const result = await forgeStory({\n componentPath: argv.path,\n storyTitle: argv['story-title'],\n skipInteractionTests: argv['skip-interaction-tests'],\n overwrite: argv.overwrite,\n dryRun: argv['dry-run'],\n });\n\n if (argv['dry-run']) {\n ui.info('Dry run — no files will be written.');\n ui.separator();\n console.log(result.content);\n ui.separator();\n return;\n }\n\n ui.step(2, 3, 'Generating story');\n ui.step(3, 3, 'Writing file');\n\n const relPath = path.relative(process.cwd(), result.storyPath);\n ui.fileCreated(relPath);\n\n ui.analysisReport({\n name: result.analysis.name,\n propsCount: result.analysis.props.length,\n hasChildren: result.analysis.hasChildren,\n usesRouter: result.analysis.usesRouter,\n usesChakra: result.analysis.usesChakra,\n usesReactQuery: result.analysis.usesReactQuery,\n exportType: result.analysis.exportType,\n storiesGenerated: result.storiesGenerated,\n });\n\n ui.done(`Story generated at ${relPath}`);\n } catch (err) {\n ui.error((err as Error).message);\n process.exit(1);\n }\n },\n};\n","import chalk from 'chalk'\n\nconst BRAND = chalk.hex('#6C5CE7')\nconst BRAND_BG = chalk.bgHex('#6C5CE7').white.bold\n\nexport const ui = {\n banner(): void {\n console.log('')\n console.log(BRAND_BG(' '))\n console.log(BRAND_BG(' ⚡ ForgeKit Storybook Plugin '))\n console.log(BRAND_BG(' '))\n console.log('')\n },\n\n success(msg: string): void {\n console.log(chalk.green(' ✔ ') + msg)\n },\n\n warn(msg: string): void {\n console.log(chalk.yellow(' ⚠ ') + msg)\n },\n\n error(msg: string): void {\n console.log(chalk.red(' ✖ ') + msg)\n },\n\n info(msg: string): void {\n console.log(chalk.cyan(' ℹ ') + msg)\n },\n\n skip(msg: string): void {\n console.log(chalk.dim(' ‣ ') + chalk.dim(msg))\n },\n\n step(number: number, total: number, label: string): void {\n console.log('')\n console.log(\n chalk.bold(BRAND(` [${number}/${total}]`)) + chalk.bold(` ${label}`)\n )\n },\n\n fileCreated(filePath: string): void {\n console.log(chalk.green(' CREATE ') + chalk.white(filePath))\n },\n\n fileUpdated(filePath: string): void {\n console.log(chalk.yellow(' UPDATE ') + chalk.white(filePath))\n },\n\n fileSkipped(filePath: string): void {\n console.log(chalk.dim(' SKIP ') + chalk.dim(filePath))\n },\n\n analysisReport(report: {\n name: string\n propsCount: number\n hasChildren: boolean\n usesRouter: boolean\n usesChakra: boolean\n usesReactQuery: boolean\n exportType: string\n storiesGenerated: string[]\n }): void {\n console.log('')\n console.log(chalk.bold(' Component: ') + BRAND(report.name))\n console.log(chalk.dim(' ─────────────────────────────────────'))\n console.log(\n chalk.bold(' Props: ') +\n (report.propsCount > 0\n ? chalk.white(`${report.propsCount} detected`)\n : chalk.dim('none'))\n )\n\n const features: string[] = []\n if (report.hasChildren) features.push('children')\n if (report.usesRouter) features.push(chalk.blue('router'))\n if (report.usesChakra) features.push(chalk.magenta('chakra'))\n if (report.usesReactQuery) features.push(chalk.cyan('react-query'))\n\n if (features.length > 0) {\n console.log(\n chalk.bold(' Features: ') + features.join(chalk.dim(' · '))\n )\n }\n\n console.log(chalk.bold(' Export: ') + chalk.white(report.exportType))\n\n if (report.storiesGenerated.length > 0) {\n console.log(\n chalk.bold(' Stories: ') +\n chalk.green(report.storiesGenerated.join(chalk.dim(', ')))\n )\n }\n console.log('')\n },\n\n done(msg?: string): void {\n console.log('')\n console.log(chalk.green.bold(' Done! ') + (msg ? chalk.white(msg) : ''))\n console.log('')\n },\n\n separator(): void {\n console.log(chalk.dim(' ─────────────────────────────────────'))\n },\n\n hint(msg: string): void {\n console.log(chalk.dim(` 💡 ${msg}`))\n },\n\n command(cmd: string): string {\n return chalk.cyan.bold(cmd)\n }\n}\n","import type { CommandModule } from 'yargs';\nimport * as path from 'node:path';\n\nimport { ui } from '../ui';\nimport { forgeStories } from '../../api/forge-stories';\n\ninterface StoriesArgs {\n dir: string;\n 'skip-interaction-tests': boolean;\n overwrite: boolean;\n 'dry-run': boolean;\n 'include-tests': boolean;\n}\n\nexport const storiesCommand: CommandModule<object, StoriesArgs> = {\n command: 'stories <dir>',\n describe: 'Bulk generate stories for all components in a directory',\n builder: (yargs) =>\n yargs\n .positional('dir', {\n describe: 'Directory to scan for components',\n type: 'string',\n demandOption: true,\n })\n .option('skip-interaction-tests', {\n describe: 'Skip generating play functions',\n type: 'boolean',\n default: false,\n })\n .option('overwrite', {\n describe: 'Overwrite existing story files',\n type: 'boolean',\n default: false,\n })\n .option('dry-run', {\n describe: 'Preview without writing files',\n type: 'boolean',\n default: false,\n })\n .option('include-tests', {\n describe: 'Also generate Playwright component tests',\n type: 'boolean',\n default: false,\n }),\n handler: async (argv) => {\n ui.banner();\n\n try {\n ui.step(1, 3, 'Scanning for components');\n\n const result = await forgeStories({\n dir: argv.dir,\n skipInteractionTests: argv['skip-interaction-tests'],\n overwrite: argv.overwrite,\n dryRun: argv['dry-run'],\n includeComponentTests: argv['include-tests'],\n });\n\n ui.step(2, 3, `Generating ${result.generated} stories`);\n\n ui.info(`Found ${result.total} analyzable components`);\n ui.success(`${result.alreadyCovered} already have stories`);\n\n if (result.generated > 0) {\n ui.success(`${result.generated} stories generated`);\n }\n if (result.failed > 0) {\n ui.warn(`${result.failed} failed`);\n for (const err of result.errors) {\n const rel = path.relative(process.cwd(), err.file);\n ui.error(` ${rel}: ${err.error}`);\n }\n }\n if (result.notAnalyzable > 0) {\n ui.skip(`${result.notAnalyzable} could not be analyzed`);\n }\n\n // Coverage report\n ui.step(3, 3, 'Coverage report');\n ui.separator();\n console.log('');\n\n const { coverage } = result;\n const method = ['A', 'B'].includes(coverage.grade) ? 'success'\n : ['C', 'D'].includes(coverage.grade) ? 'warn'\n : 'error';\n ui[method](\n `Story Coverage: ${coverage.percentage}% (${coverage.covered}/${coverage.total}) — Grade: ${coverage.grade}`\n );\n\n console.log('');\n ui.info(`Generated: ${result.generated}`);\n if (result.failed > 0) ui.warn(`Failed: ${result.failed}`);\n ui.info(`Already covered: ${result.alreadyCovered}`);\n ui.info(`Total components: ${result.total}`);\n\n ui.done(\n argv['dry-run']\n ? 'Dry run complete — no files were written.'\n : `Generated ${result.generated} story files.`\n );\n } catch (err) {\n ui.error((err as Error).message);\n process.exit(1);\n }\n },\n};\n","import type { CommandModule } from 'yargs';\nimport * as path from 'node:path';\n\nimport { ui } from '../ui';\nimport { forgeTest } from '../../api/forge-test';\n\ninterface TestArgs {\n path: string;\n overwrite: boolean;\n 'dry-run': boolean;\n}\n\nexport const testCommand: CommandModule<object, TestArgs> = {\n command: 'test <path>',\n describe: 'Generate a Playwright component test for a React component',\n builder: (yargs) =>\n yargs\n .positional('path', {\n describe: 'Path to the component file',\n type: 'string',\n demandOption: true,\n })\n .option('overwrite', {\n describe: 'Overwrite existing test file',\n type: 'boolean',\n default: false,\n })\n .option('dry-run', {\n describe: 'Preview without writing files',\n type: 'boolean',\n default: false,\n }),\n handler: async (argv) => {\n ui.banner();\n\n try {\n ui.step(1, 3, 'Analyzing component');\n\n const result = await forgeTest({\n componentPath: argv.path,\n overwrite: argv.overwrite,\n dryRun: argv['dry-run'],\n });\n\n if (argv['dry-run']) {\n ui.info('Dry run — no files will be written.');\n ui.separator();\n console.log(result.content);\n ui.separator();\n return;\n }\n\n ui.step(2, 3, 'Generating Playwright component test');\n ui.step(3, 3, 'Writing file');\n\n const relPath = path.relative(process.cwd(), result.testPath);\n ui.fileCreated(relPath);\n\n ui.analysisReport({\n name: result.analysis.name,\n propsCount: result.analysis.props.length,\n hasChildren: result.analysis.hasChildren,\n usesRouter: result.analysis.usesRouter,\n usesChakra: result.analysis.usesChakra,\n usesReactQuery: result.analysis.usesReactQuery,\n exportType: result.analysis.exportType,\n storiesGenerated: [\n 'mounts and renders',\n 'matches screenshot',\n ...(result.analysis.props.some((p) => p.isCallback)\n ? ['handles interactions']\n : []),\n ...(result.analysis.props.some(\n (p) => p.name === 'disabled' || p.name === 'isDisabled'\n )\n ? ['disabled state']\n : []),\n 'meets a11y standards',\n ],\n });\n\n ui.done(`Playwright test generated at ${relPath}`);\n } catch (err) {\n ui.error((err as Error).message);\n process.exit(1);\n }\n },\n};\n","import type { CommandModule } from 'yargs';\nimport * as path from 'node:path';\n\nimport { ui } from '../ui';\nimport { watchDirectory } from '../../core/watch';\n\ninterface WatchArgs {\n dir: string;\n 'skip-interaction-tests': boolean;\n debounce: number;\n}\n\nexport const watchCommand: CommandModule<object, WatchArgs> = {\n command: 'watch <dir>',\n describe: 'Watch for component changes and auto-generate stories',\n builder: (yargs) =>\n yargs\n .positional('dir', {\n describe: 'Directory to watch',\n type: 'string',\n demandOption: true,\n })\n .option('skip-interaction-tests', {\n describe: 'Skip generating play functions',\n type: 'boolean',\n default: false,\n })\n .option('debounce', {\n describe: 'Debounce interval in milliseconds',\n type: 'number',\n default: 300,\n }),\n handler: async (argv) => {\n ui.banner();\n\n ui.info('Watching for component changes in:');\n console.log(` ${ui.command(path.resolve(argv.dir))}`);\n console.log('');\n ui.hint('Press Ctrl+C to stop.');\n console.log('');\n\n try {\n const handle = watchDirectory(\n {\n dir: argv.dir,\n debounceMs: argv.debounce,\n skipInteractionTests: argv['skip-interaction-tests'],\n },\n (event) => {\n switch (event.type) {\n case 'ready':\n ui.success('Watching for changes...');\n break;\n case 'generate':\n ui.separator();\n ui.fileCreated(path.relative(process.cwd(), event.storyPath));\n break;\n case 'update':\n ui.separator();\n ui.fileUpdated(path.relative(process.cwd(), event.storyPath));\n break;\n case 'error':\n ui.separator();\n ui.error(\n `Failed: ${path.relative(process.cwd(), event.file)} — ${event.error.message}`\n );\n break;\n }\n },\n );\n\n // Keep alive until SIGINT/SIGTERM\n await new Promise<void>((resolve) => {\n const cleanup = async () => {\n await handle.close();\n console.log('');\n ui.info('Stopped watching for changes.');\n resolve();\n };\n\n process.on('SIGINT', cleanup);\n process.on('SIGTERM', cleanup);\n });\n } catch (err) {\n ui.error((err as Error).message);\n process.exit(1);\n }\n },\n};\n","import type { CommandModule } from 'yargs';\n\nimport { ui } from '../ui';\nimport { scanDirectory } from '../../core/scan-directory';\nimport { scoreCoverage } from '../../core/score-coverage';\n\ninterface CoverageArgs {\n dir: string;\n json: boolean;\n}\n\nexport const coverageCommand: CommandModule<object, CoverageArgs> = {\n command: 'coverage <dir>',\n describe: 'Report story coverage without generating any files',\n builder: (yargs) =>\n yargs\n .positional('dir', {\n describe: 'Directory to scan for components',\n type: 'string',\n demandOption: true,\n })\n .option('json', {\n describe: 'Output results as JSON',\n type: 'boolean',\n default: false,\n }),\n handler: async (argv) => {\n try {\n const scan = await scanDirectory(argv.dir);\n const totalAnalyzable = scan.total - scan.notAnalyzable.length;\n const coverage = scoreCoverage(scan.withStories.length, totalAnalyzable);\n\n if (argv.json) {\n console.log(\n JSON.stringify(\n {\n covered: coverage.covered,\n total: coverage.total,\n percentage: coverage.percentage,\n grade: coverage.grade,\n withStories: scan.withStories.length,\n withoutStories: scan.withoutStories.length,\n notAnalyzable: scan.notAnalyzable.length,\n },\n null,\n 2\n )\n );\n return;\n }\n\n ui.banner();\n\n ui.info(`Scanned ${scan.total} component files`);\n ui.success(`${scan.withStories.length} have stories`);\n if (scan.withoutStories.length > 0) {\n ui.warn(`${scan.withoutStories.length} missing stories`);\n }\n if (scan.notAnalyzable.length > 0) {\n ui.skip(`${scan.notAnalyzable.length} could not be analyzed`);\n }\n\n ui.separator();\n console.log('');\n\n const method = ['A', 'B'].includes(coverage.grade) ? 'success'\n : ['C', 'D'].includes(coverage.grade) ? 'warn'\n : 'error';\n ui[method](\n `Story Coverage: ${coverage.percentage}% (${coverage.covered}/${coverage.total}) — Grade: ${coverage.grade}`\n );\n\n ui.done();\n } catch (err) {\n ui.error((err as Error).message);\n process.exit(1);\n }\n },\n};\n","import type { CommandModule } from 'yargs'\nimport * as fs from 'node:fs'\nimport * as path from 'node:path'\n\nimport { ui } from '../ui'\n\nexport const initCommand: CommandModule = {\n command: 'init',\n describe: 'Check prerequisites and show setup guide',\n handler: async () => {\n ui.banner()\n ui.step(1, 2, 'Checking prerequisites')\n\n const cwd = process.cwd()\n const pkgPath = path.join(cwd, 'package.json')\n\n if (!fs.existsSync(pkgPath)) {\n ui.error('No package.json found in current directory.')\n ui.hint('Run this command from your project root.')\n process.exit(1)\n }\n\n const pkg = JSON.parse(fs.readFileSync(pkgPath, 'utf-8'))\n const allDeps = {\n ...pkg.dependencies,\n ...pkg.devDependencies\n }\n\n const checks = [\n { name: 'react', label: 'React' },\n { name: 'storybook', label: 'Storybook' },\n { name: '@storybook/react-vite', label: '@storybook/react-vite' },\n { name: 'typescript', label: 'TypeScript' }\n ]\n\n let allGood = true\n for (const check of checks) {\n if (allDeps[check.name]) {\n ui.success(`${check.label} — ${allDeps[check.name]}`)\n } else {\n ui.warn(`${check.label} — not found`)\n allGood = false\n }\n }\n\n // Optional deps\n const optionalChecks = [\n { name: '@storybook/test', label: '@storybook/test (interaction tests)' },\n {\n name: '@storybook/addon-a11y',\n label: '@storybook/addon-a11y (accessibility)'\n },\n {\n name: '@playwright/experimental-ct-react',\n label: 'Playwright CT (component tests)'\n },\n {\n name: '@axe-core/playwright',\n label: 'axe-core Playwright (a11y in CT)'\n }\n ]\n\n console.log('')\n ui.info('Optional dependencies:')\n for (const check of optionalChecks) {\n if (allDeps[check.name]) {\n ui.success(`${check.label}`)\n } else {\n ui.skip(`${check.label} — not installed`)\n }\n }\n\n // Show available commands\n ui.step(2, 2, 'Available commands')\n console.log('')\n console.log(\n ` ${ui.command('forgekit-storybook-plugin story <path>')} Generate a story for one component`\n )\n console.log(\n ` ${ui.command('forgekit-storybook-plugin stories <dir>')} Bulk generate stories for a directory`\n )\n console.log(\n ` ${ui.command('forgekit-storybook-plugin test <path>')} Generate a Playwright component test`\n )\n console.log(\n ` ${ui.command('forgekit-storybook-plugin watch <dir>')} Watch and auto-generate on changes`\n )\n console.log(\n ` ${ui.command('forgekit-storybook-plugin coverage <dir>')} Report story coverage`\n )\n console.log('')\n\n if (!allGood) {\n ui.hint('Install missing dependencies before generating stories.')\n }\n\n ui.done('ForgeKit Storybook Plugin is ready!')\n }\n}\n"]}