@fragments-sdk/cli 0.3.3 → 0.4.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/dist/bin.js +18 -13
- package/dist/bin.js.map +1 -1
- package/dist/{chunk-PMGI7ATF.js → chunk-5CKYLCJH.js} +58 -2
- package/dist/chunk-5CKYLCJH.js.map +1 -0
- package/dist/{chunk-MUZ6CM66.js → chunk-5ZYEOHYK.js} +13 -11
- package/dist/chunk-5ZYEOHYK.js.map +1 -0
- package/dist/{chunk-XHNKNI6J.js → chunk-AW7MWOUH.js} +9 -1
- package/dist/chunk-AW7MWOUH.js.map +1 -0
- package/dist/{chunk-LY2CFFPY.js → chunk-G3M3MPQ6.js} +14 -3
- package/dist/chunk-G3M3MPQ6.js.map +1 -0
- package/dist/{chunk-3OTEW66K.js → chunk-J4SI5RIH.js} +4 -4
- package/dist/{chunk-BSCG3IP7.js → chunk-NOTYONHY.js} +2 -2
- package/dist/{chunk-D6VXWI45.js → chunk-ZFKGX3QK.js} +8 -6
- package/dist/chunk-ZFKGX3QK.js.map +1 -0
- package/dist/{core-DWKLGY4N.js → core-LNXDLXDP.js} +5 -3
- package/dist/{generate-3LBZANQ3.js → generate-OIXXHOWR.js} +4 -4
- package/dist/index.d.ts +16 -0
- package/dist/index.js +6 -6
- package/dist/{init-NKIUCYTG.js → init-EVPXIDW4.js} +4 -4
- package/dist/mcp-bin.js +3 -3
- package/dist/mcp-bin.js.map +1 -1
- package/dist/scan-YVYD64GD.js +12 -0
- package/dist/{service-QSZMZJBJ.js → service-K52ORLCJ.js} +4 -4
- package/dist/{static-viewer-MIPGZ4Z7.js → static-viewer-JNQIHA4B.js} +4 -4
- package/dist/{test-ZCTR4LBB.js → test-USARUEFW.js} +9 -5
- package/dist/test-USARUEFW.js.map +1 -0
- package/dist/{tokens-5JQ5IOR2.js → tokens-C6YHBOQE.js} +5 -5
- package/dist/{viewer-D7QC4GM2.js → viewer-H7TVFT4E.js} +15 -15
- package/dist/{viewer-D7QC4GM2.js.map → viewer-H7TVFT4E.js.map} +1 -1
- package/package.json +2 -1
- package/src/bin.ts +7 -1
- package/src/build.ts +2 -0
- package/src/core/index.ts +4 -0
- package/src/core/parser.ts +102 -1
- package/src/core/schema.ts +11 -0
- package/src/core/storyAdapter.ts +1 -1
- package/src/core/storybook-csf.ts +11 -0
- package/src/core/types.ts +25 -1
- package/src/mcp/server.ts +1 -1
- package/src/migrate/bin.ts +7 -1
- package/src/migrate/report.ts +1 -1
- package/src/service/enhance/doc-extractor.ts +2 -2
- package/src/service/enhance/props-extractor.ts +1 -1
- package/src/service/enhance/storybook-parser.ts +1 -1
- package/src/service/figma.ts +2 -2
- package/src/service/metrics-store.ts +2 -1
- package/src/service/patch-generator.ts +2 -1
- package/src/service/report.ts +1 -1
- package/src/test/reporters/junit.ts +7 -3
- package/src/test/runner.ts +4 -4
- package/src/test/watch.ts +2 -2
- package/src/theme/__tests__/generator.test.ts +412 -0
- package/src/theme/__tests__/presets.test.ts +169 -0
- package/src/theme/__tests__/schema.test.ts +463 -0
- package/src/theme/__tests__/serializer.test.ts +326 -0
- package/src/theme/generator.ts +355 -0
- package/src/theme/index.ts +61 -0
- package/src/theme/presets.ts +189 -0
- package/src/theme/schema.ts +193 -0
- package/src/theme/serializer.ts +123 -0
- package/src/theme/types.ts +210 -0
- package/src/viewer/components/CodePanel.tsx +1 -1
- package/src/viewer/components/FigmaEmbed.tsx +1 -1
- package/src/viewer/jsx-parser.ts +2 -1
- package/src/viewer/styles/globals.css +1 -1
- package/src/viewer/utils/colorSchemes.ts +3 -3
- package/dist/chunk-D6VXWI45.js.map +0 -1
- package/dist/chunk-LY2CFFPY.js.map +0 -1
- package/dist/chunk-MUZ6CM66.js.map +0 -1
- package/dist/chunk-PMGI7ATF.js.map +0 -1
- package/dist/chunk-XHNKNI6J.js.map +0 -1
- package/dist/scan-3ZAOVO4U.js +0 -12
- package/dist/test-ZCTR4LBB.js.map +0 -1
- /package/dist/{chunk-3OTEW66K.js.map → chunk-J4SI5RIH.js.map} +0 -0
- /package/dist/{chunk-BSCG3IP7.js.map → chunk-NOTYONHY.js.map} +0 -0
- /package/dist/{core-DWKLGY4N.js.map → core-LNXDLXDP.js.map} +0 -0
- /package/dist/{generate-3LBZANQ3.js.map → generate-OIXXHOWR.js.map} +0 -0
- /package/dist/{init-NKIUCYTG.js.map → init-EVPXIDW4.js.map} +0 -0
- /package/dist/{scan-3ZAOVO4U.js.map → scan-YVYD64GD.js.map} +0 -0
- /package/dist/{service-QSZMZJBJ.js.map → service-K52ORLCJ.js.map} +0 -0
- /package/dist/{static-viewer-MIPGZ4Z7.js.map → static-viewer-JNQIHA4B.js.map} +0 -0
- /package/dist/{tokens-5JQ5IOR2.js.map → tokens-C6YHBOQE.js.map} +0 -0
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@fragments-sdk/cli",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.4.1",
|
|
4
4
|
"description": "CLI, MCP server, and dev tools for Fragments design system",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
@@ -100,6 +100,7 @@
|
|
|
100
100
|
"scripts": {
|
|
101
101
|
"build": "tsup",
|
|
102
102
|
"dev": "tsup --watch",
|
|
103
|
+
"lint": "eslint src",
|
|
103
104
|
"test": "vitest run",
|
|
104
105
|
"typecheck": "tsc --noEmit",
|
|
105
106
|
"clean": "rm -rf dist"
|
package/src/bin.ts
CHANGED
|
@@ -8,9 +8,15 @@
|
|
|
8
8
|
|
|
9
9
|
import { Command } from 'commander';
|
|
10
10
|
import pc from 'picocolors';
|
|
11
|
+
import { readFileSync } from 'node:fs';
|
|
12
|
+
import { fileURLToPath } from 'node:url';
|
|
13
|
+
import { dirname, join } from 'node:path';
|
|
11
14
|
import { BRAND } from './core/index.js';
|
|
12
15
|
import { loadConfig } from './core/node.js';
|
|
13
16
|
|
|
17
|
+
const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
18
|
+
const pkg = JSON.parse(readFileSync(join(__dirname, '../package.json'), 'utf-8')) as { version: string };
|
|
19
|
+
|
|
14
20
|
// Import command implementations
|
|
15
21
|
import { validate } from './commands/validate.js';
|
|
16
22
|
import { build } from './commands/build.js';
|
|
@@ -40,7 +46,7 @@ const program = new Command();
|
|
|
40
46
|
program
|
|
41
47
|
.name(BRAND.cliCommand)
|
|
42
48
|
.description(`${BRAND.name} - Design system documentation and compliance tool`)
|
|
43
|
-
.version(
|
|
49
|
+
.version(pkg.version);
|
|
44
50
|
|
|
45
51
|
// ============================================================================
|
|
46
52
|
// VALIDATE COMMAND
|
package/src/build.ts
CHANGED
|
@@ -111,6 +111,8 @@ export async function buildSegments(
|
|
|
111
111
|
...(v.figma && { figma: v.figma }),
|
|
112
112
|
...(v.args && { args: v.args }),
|
|
113
113
|
})),
|
|
114
|
+
// Include AI metadata if present
|
|
115
|
+
...(parsed.ai && { ai: parsed.ai }),
|
|
114
116
|
};
|
|
115
117
|
|
|
116
118
|
segments[parsed.meta.name] = compiled;
|
package/src/core/index.ts
CHANGED
|
@@ -32,6 +32,8 @@ export type {
|
|
|
32
32
|
// Contract and provenance types
|
|
33
33
|
SegmentContract,
|
|
34
34
|
SegmentGenerated,
|
|
35
|
+
// AI metadata type
|
|
36
|
+
AIMetadata,
|
|
35
37
|
// Screenshot types
|
|
36
38
|
ScreenshotConfig,
|
|
37
39
|
ServiceConfig,
|
|
@@ -87,6 +89,8 @@ export {
|
|
|
87
89
|
segmentGeneratedSchema,
|
|
88
90
|
segmentBanSchema,
|
|
89
91
|
recipeDefinitionSchema,
|
|
92
|
+
// AI metadata schema
|
|
93
|
+
aiMetadataSchema,
|
|
90
94
|
} from "./schema.js";
|
|
91
95
|
|
|
92
96
|
// Main API
|
package/src/core/parser.ts
CHANGED
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
*/
|
|
9
9
|
|
|
10
10
|
import ts from "typescript";
|
|
11
|
-
import type { SegmentMeta, SegmentUsage, PropDefinition } from "./types.js";
|
|
11
|
+
import type { SegmentMeta, SegmentUsage, PropDefinition, AIMetadata } from "./types.js";
|
|
12
12
|
|
|
13
13
|
/**
|
|
14
14
|
* Parsed segment metadata (extracted statically from AST)
|
|
@@ -45,6 +45,9 @@ export interface ParsedSegmentMetadata {
|
|
|
45
45
|
note: string;
|
|
46
46
|
}>;
|
|
47
47
|
|
|
48
|
+
/** AI-specific metadata for playground context generation */
|
|
49
|
+
ai?: AIMetadata;
|
|
50
|
+
|
|
48
51
|
/** Parse warnings */
|
|
49
52
|
warnings: string[];
|
|
50
53
|
}
|
|
@@ -129,6 +132,9 @@ export function parseSegmentFile(
|
|
|
129
132
|
// Extract relations
|
|
130
133
|
const relations = extractRelations(arg, warnings);
|
|
131
134
|
|
|
135
|
+
// Extract AI metadata
|
|
136
|
+
const ai = extractAIMetadata(arg, warnings);
|
|
137
|
+
|
|
132
138
|
return {
|
|
133
139
|
componentImport,
|
|
134
140
|
componentName,
|
|
@@ -137,6 +143,7 @@ export function parseSegmentFile(
|
|
|
137
143
|
props,
|
|
138
144
|
variants,
|
|
139
145
|
relations,
|
|
146
|
+
ai,
|
|
140
147
|
warnings,
|
|
141
148
|
};
|
|
142
149
|
}
|
|
@@ -401,6 +408,51 @@ function extractVariants(
|
|
|
401
408
|
return variants;
|
|
402
409
|
}
|
|
403
410
|
|
|
411
|
+
/**
|
|
412
|
+
* Remove common leading whitespace from all lines (dedent).
|
|
413
|
+
* This handles template literals and JSX that have extra indentation from code formatting.
|
|
414
|
+
*
|
|
415
|
+
* Special handling: If the first line has no indentation (common after .trim()),
|
|
416
|
+
* we calculate minimum indent from subsequent lines only.
|
|
417
|
+
*/
|
|
418
|
+
function dedent(str: string): string {
|
|
419
|
+
const lines = str.split('\n');
|
|
420
|
+
|
|
421
|
+
if (lines.length <= 1) {
|
|
422
|
+
return str;
|
|
423
|
+
}
|
|
424
|
+
|
|
425
|
+
// Check if first line has no indentation
|
|
426
|
+
const firstLineIndent = lines[0].match(/^(\s*)/)?.[1].length ?? 0;
|
|
427
|
+
const startIndex = firstLineIndent === 0 ? 1 : 0;
|
|
428
|
+
|
|
429
|
+
// Find the minimum indentation (ignoring empty lines)
|
|
430
|
+
let minIndent = Infinity;
|
|
431
|
+
for (let i = startIndex; i < lines.length; i++) {
|
|
432
|
+
const line = lines[i];
|
|
433
|
+
if (line.trim() === '') continue;
|
|
434
|
+
const match = line.match(/^(\s*)/);
|
|
435
|
+
if (match) {
|
|
436
|
+
minIndent = Math.min(minIndent, match[1].length);
|
|
437
|
+
}
|
|
438
|
+
}
|
|
439
|
+
|
|
440
|
+
// If no indentation found, return as-is
|
|
441
|
+
if (minIndent === Infinity || minIndent === 0) {
|
|
442
|
+
return str;
|
|
443
|
+
}
|
|
444
|
+
|
|
445
|
+
// Remove the common indentation from all lines (except first if it had no indent)
|
|
446
|
+
return lines
|
|
447
|
+
.map((line, index) => {
|
|
448
|
+
if (index === 0 && firstLineIndent === 0) {
|
|
449
|
+
return line; // Keep first line as-is
|
|
450
|
+
}
|
|
451
|
+
return line.slice(minIndent);
|
|
452
|
+
})
|
|
453
|
+
.join('\n');
|
|
454
|
+
}
|
|
455
|
+
|
|
404
456
|
/**
|
|
405
457
|
* Extract the code from a render function.
|
|
406
458
|
*/
|
|
@@ -421,6 +473,9 @@ function extractRenderCode(
|
|
|
421
473
|
code = code.slice(1, -1).trim();
|
|
422
474
|
}
|
|
423
475
|
|
|
476
|
+
// Dedent the code to remove common leading whitespace
|
|
477
|
+
code = dedent(code);
|
|
478
|
+
|
|
424
479
|
return code;
|
|
425
480
|
}
|
|
426
481
|
|
|
@@ -460,6 +515,52 @@ function extractRelations(
|
|
|
460
515
|
return relations;
|
|
461
516
|
}
|
|
462
517
|
|
|
518
|
+
/**
|
|
519
|
+
* Extract AI metadata from defineSegment call.
|
|
520
|
+
*/
|
|
521
|
+
function extractAIMetadata(
|
|
522
|
+
arg: ts.ObjectLiteralExpression,
|
|
523
|
+
warnings: string[]
|
|
524
|
+
): AIMetadata | undefined {
|
|
525
|
+
const aiProp = findProperty(arg, "ai");
|
|
526
|
+
if (!aiProp || !ts.isObjectLiteralExpression(aiProp)) {
|
|
527
|
+
return undefined;
|
|
528
|
+
}
|
|
529
|
+
|
|
530
|
+
const ai: AIMetadata = {};
|
|
531
|
+
|
|
532
|
+
// Extract compositionPattern
|
|
533
|
+
const compositionPattern = extractStringProperty(aiProp, "compositionPattern");
|
|
534
|
+
if (compositionPattern && ['compound', 'simple', 'controlled'].includes(compositionPattern)) {
|
|
535
|
+
ai.compositionPattern = compositionPattern as AIMetadata['compositionPattern'];
|
|
536
|
+
}
|
|
537
|
+
|
|
538
|
+
// Extract subComponents array
|
|
539
|
+
const subComponents = extractStringArray(aiProp, "subComponents");
|
|
540
|
+
if (subComponents.length > 0) {
|
|
541
|
+
ai.subComponents = subComponents;
|
|
542
|
+
}
|
|
543
|
+
|
|
544
|
+
// Extract requiredChildren array
|
|
545
|
+
const requiredChildren = extractStringArray(aiProp, "requiredChildren");
|
|
546
|
+
if (requiredChildren.length > 0) {
|
|
547
|
+
ai.requiredChildren = requiredChildren;
|
|
548
|
+
}
|
|
549
|
+
|
|
550
|
+
// Extract commonPatterns array
|
|
551
|
+
const commonPatterns = extractStringArray(aiProp, "commonPatterns");
|
|
552
|
+
if (commonPatterns.length > 0) {
|
|
553
|
+
ai.commonPatterns = commonPatterns;
|
|
554
|
+
}
|
|
555
|
+
|
|
556
|
+
// Only return if we have any fields
|
|
557
|
+
if (Object.keys(ai).length > 0) {
|
|
558
|
+
return ai;
|
|
559
|
+
}
|
|
560
|
+
|
|
561
|
+
return undefined;
|
|
562
|
+
}
|
|
563
|
+
|
|
463
564
|
/**
|
|
464
565
|
* Extract a string property from an object literal.
|
|
465
566
|
*/
|
package/src/core/schema.ts
CHANGED
|
@@ -138,6 +138,16 @@ export const segmentGeneratedSchema = z.object({
|
|
|
138
138
|
timestamp: z.string().datetime().optional(),
|
|
139
139
|
});
|
|
140
140
|
|
|
141
|
+
/**
|
|
142
|
+
* Schema for AI-specific metadata for playground context generation
|
|
143
|
+
*/
|
|
144
|
+
export const aiMetadataSchema = z.object({
|
|
145
|
+
compositionPattern: z.enum(['compound', 'simple', 'controlled']).optional(),
|
|
146
|
+
subComponents: z.array(z.string()).optional(),
|
|
147
|
+
requiredChildren: z.array(z.string()).optional(),
|
|
148
|
+
commonPatterns: z.array(z.string()).optional(),
|
|
149
|
+
});
|
|
150
|
+
|
|
141
151
|
/**
|
|
142
152
|
* Schema for recipe definitions
|
|
143
153
|
*/
|
|
@@ -158,6 +168,7 @@ export const segmentDefinitionSchema = z.object({
|
|
|
158
168
|
relations: z.array(componentRelationSchema).optional(),
|
|
159
169
|
variants: z.array(segmentVariantSchema), // Allow empty variants array
|
|
160
170
|
contract: segmentContractSchema.optional(),
|
|
171
|
+
ai: aiMetadataSchema.optional(),
|
|
161
172
|
_generated: segmentGeneratedSchema.optional(),
|
|
162
173
|
});
|
|
163
174
|
|
package/src/core/storyAdapter.ts
CHANGED
|
@@ -9,7 +9,7 @@
|
|
|
9
9
|
*/
|
|
10
10
|
|
|
11
11
|
import { createElement, type ComponentType, type ReactNode } from "react";
|
|
12
|
-
import { toId, storyNameFromExport, isExportStory } from "
|
|
12
|
+
import { toId, storyNameFromExport, isExportStory } from "./storybook-csf.js";
|
|
13
13
|
import type {
|
|
14
14
|
SegmentDefinition,
|
|
15
15
|
SegmentMeta,
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import {
|
|
2
|
+
toId as storybookToId,
|
|
3
|
+
storyNameFromExport as storybookStoryNameFromExport,
|
|
4
|
+
isExportStory as storybookIsExportStory,
|
|
5
|
+
} from "@storybook/csf";
|
|
6
|
+
|
|
7
|
+
export const toId: typeof storybookToId = (...args) => storybookToId(...args);
|
|
8
|
+
export const storyNameFromExport: typeof storybookStoryNameFromExport = (...args) =>
|
|
9
|
+
storybookStoryNameFromExport(...args);
|
|
10
|
+
export const isExportStory: typeof storybookIsExportStory = (...args) =>
|
|
11
|
+
storybookIsExportStory(...args);
|
package/src/core/types.ts
CHANGED
|
@@ -5,7 +5,7 @@ import type { ComponentType, ReactNode, JSX } from "react";
|
|
|
5
5
|
* This type is intentionally broad to support various React component patterns
|
|
6
6
|
* including FC, forwardRef, memo, and class components across different React versions.
|
|
7
7
|
*/
|
|
8
|
-
|
|
8
|
+
|
|
9
9
|
export type SegmentComponent<TProps = any> =
|
|
10
10
|
| ComponentType<TProps>
|
|
11
11
|
| ((props: TProps) => ReactNode | JSX.Element | null);
|
|
@@ -314,6 +314,24 @@ export interface SegmentGenerated {
|
|
|
314
314
|
timestamp?: string;
|
|
315
315
|
}
|
|
316
316
|
|
|
317
|
+
/**
|
|
318
|
+
* AI-specific metadata for playground context generation
|
|
319
|
+
* Provides hints for AI code generation about component composition
|
|
320
|
+
*/
|
|
321
|
+
export interface AIMetadata {
|
|
322
|
+
/** How this component is composed with others */
|
|
323
|
+
compositionPattern?: "compound" | "simple" | "controlled";
|
|
324
|
+
|
|
325
|
+
/** Sub-component names (without parent prefix, e.g., "Header" not "Card.Header") */
|
|
326
|
+
subComponents?: string[];
|
|
327
|
+
|
|
328
|
+
/** Sub-components that must be present for valid composition */
|
|
329
|
+
requiredChildren?: string[];
|
|
330
|
+
|
|
331
|
+
/** Common usage patterns as JSX strings for AI reference */
|
|
332
|
+
commonPatterns?: string[];
|
|
333
|
+
}
|
|
334
|
+
|
|
317
335
|
/**
|
|
318
336
|
* Complete segment definition
|
|
319
337
|
*/
|
|
@@ -339,6 +357,9 @@ export interface SegmentDefinition<TProps = unknown> {
|
|
|
339
357
|
/** Agent-optimized contract metadata */
|
|
340
358
|
contract?: SegmentContract;
|
|
341
359
|
|
|
360
|
+
/** AI-specific metadata for playground context generation */
|
|
361
|
+
ai?: AIMetadata;
|
|
362
|
+
|
|
342
363
|
/** Provenance tracking (for generated segments) */
|
|
343
364
|
_generated?: SegmentGenerated;
|
|
344
365
|
}
|
|
@@ -706,6 +727,9 @@ export interface CompiledSegment {
|
|
|
706
727
|
/** Agent-optimized contract metadata */
|
|
707
728
|
contract?: SegmentContract;
|
|
708
729
|
|
|
730
|
+
/** AI-specific metadata for playground context generation */
|
|
731
|
+
ai?: AIMetadata;
|
|
732
|
+
|
|
709
733
|
/** Provenance tracking (for generated segments) */
|
|
710
734
|
_generated?: SegmentGenerated;
|
|
711
735
|
}
|
package/src/mcp/server.ts
CHANGED
|
@@ -276,7 +276,7 @@ export function createMcpServer(config: McpServerConfig): Server {
|
|
|
276
276
|
// Lazy-loaded resources
|
|
277
277
|
let segmentsData: CompiledSegmentsFile | null = null;
|
|
278
278
|
let packageName: string | null = null;
|
|
279
|
-
|
|
279
|
+
|
|
280
280
|
let browserPool: any = null;
|
|
281
281
|
let storageManager: any = null;
|
|
282
282
|
let diffEngine: any = null;
|
package/src/migrate/bin.ts
CHANGED
|
@@ -7,15 +7,21 @@
|
|
|
7
7
|
|
|
8
8
|
import { Command } from "commander";
|
|
9
9
|
import pc from "picocolors";
|
|
10
|
+
import { readFileSync } from "node:fs";
|
|
11
|
+
import { fileURLToPath } from "node:url";
|
|
12
|
+
import { dirname, join } from "node:path";
|
|
10
13
|
import { BRAND } from "../core/index.js";
|
|
11
14
|
import { migrate } from "./migrate.js";
|
|
12
15
|
|
|
16
|
+
const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
17
|
+
const pkg = JSON.parse(readFileSync(join(__dirname, "../../package.json"), "utf-8")) as { version: string };
|
|
18
|
+
|
|
13
19
|
const program = new Command();
|
|
14
20
|
|
|
15
21
|
program
|
|
16
22
|
.name("segments-migrate")
|
|
17
23
|
.description(`${BRAND.name} Storybook Migration Tool`)
|
|
18
|
-
.version(
|
|
24
|
+
.version(pkg.version);
|
|
19
25
|
|
|
20
26
|
program
|
|
21
27
|
.command("migrate")
|
package/src/migrate/report.ts
CHANGED
|
@@ -139,7 +139,7 @@ function extractJSDoc(
|
|
|
139
139
|
|
|
140
140
|
// Parse JSDoc content
|
|
141
141
|
const lines = content.split("\n");
|
|
142
|
-
|
|
142
|
+
const currentDescription: string[] = [];
|
|
143
143
|
let currentTag: string | null = null;
|
|
144
144
|
let currentTagContent: string[] = [];
|
|
145
145
|
|
|
@@ -197,7 +197,7 @@ function processTag(
|
|
|
197
197
|
if (propMatch) {
|
|
198
198
|
const [, type, nameRaw, description] = propMatch;
|
|
199
199
|
const isOptional = nameRaw.startsWith("[");
|
|
200
|
-
const name = nameRaw.
|
|
200
|
+
const name = nameRaw.replaceAll("[", "").replaceAll("]", "");
|
|
201
201
|
docs.props?.push({
|
|
202
202
|
name,
|
|
203
203
|
type,
|
|
@@ -331,7 +331,7 @@ function parseJSDocContent(
|
|
|
331
331
|
}
|
|
332
332
|
): void {
|
|
333
333
|
const lines = content.split("\n");
|
|
334
|
-
|
|
334
|
+
const currentDescription: string[] = [];
|
|
335
335
|
let currentTag: string | null = null;
|
|
336
336
|
let currentTagContent: string[] = [];
|
|
337
337
|
|
|
@@ -484,7 +484,7 @@ function camelToTitle(str: string): string {
|
|
|
484
484
|
function inferComponentFromPath(filePath: string): string {
|
|
485
485
|
const fileName = basename(filePath);
|
|
486
486
|
// Remove .stories.tsx etc
|
|
487
|
-
|
|
487
|
+
const name = fileName.replace(/\.stories\.(tsx?|jsx?|mdx?)$/, "");
|
|
488
488
|
// Handle patterns like Button.stories.tsx
|
|
489
489
|
return name;
|
|
490
490
|
}
|
package/src/service/figma.ts
CHANGED
|
@@ -313,7 +313,7 @@ export class FigmaClient {
|
|
|
313
313
|
*/
|
|
314
314
|
parseUrl(url: string): FigmaUrlParts {
|
|
315
315
|
// Match both /file/ and /design/ paths
|
|
316
|
-
const urlPattern = /figma\.com\/(?:file|design)\/([
|
|
316
|
+
const urlPattern = /figma\.com\/(?:file|design)\/([^/]+)\/[^?]*\?.*node-id=([^&]+)/i;
|
|
317
317
|
const match = url.match(urlPattern);
|
|
318
318
|
|
|
319
319
|
if (!match) {
|
|
@@ -410,7 +410,7 @@ export class FigmaClient {
|
|
|
410
410
|
*/
|
|
411
411
|
parseFileUrl(url: string): { fileKey: string; nodeId?: string } {
|
|
412
412
|
// Match both /file/ and /design/ paths
|
|
413
|
-
const urlPattern = /figma\.com\/(?:file|design)\/([
|
|
413
|
+
const urlPattern = /figma\.com\/(?:file|design)\/([^/]+)/i;
|
|
414
414
|
const match = url.match(urlPattern);
|
|
415
415
|
|
|
416
416
|
if (!match) {
|
|
@@ -200,12 +200,13 @@ export class MetricsStore {
|
|
|
200
200
|
let key: string;
|
|
201
201
|
|
|
202
202
|
switch (groupBy) {
|
|
203
|
-
case "week":
|
|
203
|
+
case "week": {
|
|
204
204
|
// Get ISO week
|
|
205
205
|
const weekStart = new Date(date);
|
|
206
206
|
weekStart.setDate(date.getDate() - date.getDay());
|
|
207
207
|
key = weekStart.toISOString().split("T")[0];
|
|
208
208
|
break;
|
|
209
|
+
}
|
|
209
210
|
case "month":
|
|
210
211
|
key = `${date.getFullYear()}-${String(date.getMonth() + 1).padStart(2, "0")}`;
|
|
211
212
|
break;
|
|
@@ -189,13 +189,14 @@ function generateHunk(
|
|
|
189
189
|
|
|
190
190
|
// The hunk format depends on the style type
|
|
191
191
|
switch (styleType) {
|
|
192
|
-
case "inline":
|
|
192
|
+
case "inline": {
|
|
193
193
|
// React inline style: { backgroundColor: '#0051c2' }
|
|
194
194
|
const camelProp = toCamelCase(cssProperty);
|
|
195
195
|
lines.push(`@@ -1,1 +1,1 @@ inline style`);
|
|
196
196
|
lines.push(`- ${camelProp}: '${currentValue}',`);
|
|
197
197
|
lines.push(`+ ${camelProp}: 'var(${suggestedFix.tokenName})',`);
|
|
198
198
|
break;
|
|
199
|
+
}
|
|
199
200
|
|
|
200
201
|
case "styled-components":
|
|
201
202
|
case "emotion":
|
package/src/service/report.ts
CHANGED
|
@@ -176,11 +176,15 @@ function generateTestCaseXml(result: TestResult): string {
|
|
|
176
176
|
* Escape special XML characters
|
|
177
177
|
*/
|
|
178
178
|
function escapeXml(str: string): string {
|
|
179
|
-
|
|
179
|
+
const cleaned = Array.from(str).filter((ch) => {
|
|
180
|
+
const code = ch.charCodeAt(0);
|
|
181
|
+
return code === 0x09 || code === 0x0A || code === 0x0D || code >= 0x20;
|
|
182
|
+
}).join('');
|
|
183
|
+
|
|
184
|
+
return cleaned
|
|
180
185
|
.replace(/&/g, '&')
|
|
181
186
|
.replace(/</g, '<')
|
|
182
187
|
.replace(/>/g, '>')
|
|
183
188
|
.replace(/"/g, '"')
|
|
184
|
-
.replace(/'/g, ''')
|
|
185
|
-
.replace(/[\x00-\x08\x0B\x0C\x0E-\x1F]/g, ''); // Remove invalid XML characters
|
|
189
|
+
.replace(/'/g, ''');
|
|
186
190
|
}
|
package/src/test/runner.ts
CHANGED
|
@@ -20,11 +20,11 @@ import type {
|
|
|
20
20
|
import { groupTestsByComponent } from './discovery.js';
|
|
21
21
|
|
|
22
22
|
// Dynamic playwright types (since it's optional)
|
|
23
|
-
|
|
23
|
+
|
|
24
24
|
type Browser = any;
|
|
25
|
-
|
|
25
|
+
|
|
26
26
|
type BrowserContext = any;
|
|
27
|
-
|
|
27
|
+
|
|
28
28
|
type Page = any;
|
|
29
29
|
|
|
30
30
|
/**
|
|
@@ -59,7 +59,7 @@ export async function runTests(
|
|
|
59
59
|
}
|
|
60
60
|
|
|
61
61
|
// Import Playwright dynamically (optional dependency)
|
|
62
|
-
|
|
62
|
+
|
|
63
63
|
let playwright: any;
|
|
64
64
|
try {
|
|
65
65
|
playwright = await import('playwright');
|
package/src/test/watch.ts
CHANGED
|
@@ -34,7 +34,7 @@ export async function startWatchMode(
|
|
|
34
34
|
|
|
35
35
|
let debounceTimer: NodeJS.Timeout | null = null;
|
|
36
36
|
let isRunning = false;
|
|
37
|
-
|
|
37
|
+
const pendingFiles = new Set<string>();
|
|
38
38
|
|
|
39
39
|
// Get files to watch
|
|
40
40
|
const segmentFiles = await discoverSegmentFiles(config, configDir);
|
|
@@ -190,7 +190,7 @@ export async function startInteractiveWatchMode(
|
|
|
190
190
|
runnerOptions: RunnerOptions,
|
|
191
191
|
reporters: TestReporter[]
|
|
192
192
|
): Promise<void> {
|
|
193
|
-
|
|
193
|
+
const lastFailedTests: TestCase[] = [];
|
|
194
194
|
|
|
195
195
|
// Start basic watch mode
|
|
196
196
|
await startWatchMode(config, configDir, runnerOptions, reporters, {
|