@buoy-design/scanners 0.2.1 → 0.2.26
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/design-systems/index.d.ts +6 -0
- package/dist/design-systems/index.d.ts.map +1 -0
- package/dist/design-systems/index.js +6 -0
- package/dist/design-systems/index.js.map +1 -0
- package/dist/design-systems/library-detector.d.ts +53 -0
- package/dist/design-systems/library-detector.d.ts.map +1 -0
- package/dist/design-systems/library-detector.js +183 -0
- package/dist/design-systems/library-detector.js.map +1 -0
- package/dist/extractors/css-in-js.d.ts +20 -0
- package/dist/extractors/css-in-js.d.ts.map +1 -0
- package/dist/extractors/css-in-js.js +284 -0
- package/dist/extractors/css-in-js.js.map +1 -0
- package/dist/extractors/html-style.d.ts +1 -1
- package/dist/extractors/html-style.d.ts.map +1 -1
- package/dist/extractors/index.d.ts +1 -0
- package/dist/extractors/index.d.ts.map +1 -1
- package/dist/extractors/index.js +1 -0
- package/dist/extractors/index.js.map +1 -1
- package/dist/figma/client.d.ts +4 -0
- package/dist/figma/client.d.ts.map +1 -1
- package/dist/figma/client.js.map +1 -1
- package/dist/figma/component-scanner.d.ts +67 -1
- package/dist/figma/component-scanner.d.ts.map +1 -1
- package/dist/figma/component-scanner.js +162 -1
- package/dist/figma/component-scanner.js.map +1 -1
- package/dist/figma/index.d.ts +1 -1
- package/dist/figma/index.d.ts.map +1 -1
- package/dist/figma/index.js.map +1 -1
- package/dist/git/angular-scanner.d.ts +78 -1
- package/dist/git/angular-scanner.d.ts.map +1 -1
- package/dist/git/angular-scanner.js +249 -6
- package/dist/git/angular-scanner.js.map +1 -1
- package/dist/git/index.d.ts +4 -3
- package/dist/git/index.d.ts.map +1 -1
- package/dist/git/index.js +1 -0
- package/dist/git/index.js.map +1 -1
- package/dist/git/nextjs-scanner.d.ts +190 -0
- package/dist/git/nextjs-scanner.d.ts.map +1 -0
- package/dist/git/nextjs-scanner.js +979 -0
- package/dist/git/nextjs-scanner.js.map +1 -0
- package/dist/git/react-scanner.d.ts +74 -1
- package/dist/git/react-scanner.d.ts.map +1 -1
- package/dist/git/react-scanner.js +174 -5
- package/dist/git/react-scanner.js.map +1 -1
- package/dist/git/token-scanner.d.ts.map +1 -1
- package/dist/git/token-scanner.js +24 -2
- package/dist/git/token-scanner.js.map +1 -1
- package/dist/git/vue-scanner.d.ts +43 -1
- package/dist/git/vue-scanner.d.ts.map +1 -1
- package/dist/git/vue-scanner.js +130 -4
- package/dist/git/vue-scanner.js.map +1 -1
- package/dist/index.d.ts +2 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +6 -2
- package/dist/index.js.map +1 -1
- package/dist/signals/extractors/arbitrary-value.d.ts +7 -0
- package/dist/signals/extractors/arbitrary-value.d.ts.map +1 -0
- package/dist/signals/extractors/arbitrary-value.js +57 -0
- package/dist/signals/extractors/arbitrary-value.js.map +1 -0
- package/dist/signals/extractors/breakpoint.d.ts +6 -0
- package/dist/signals/extractors/breakpoint.d.ts.map +1 -0
- package/dist/signals/extractors/breakpoint.js +41 -0
- package/dist/signals/extractors/breakpoint.js.map +1 -0
- package/dist/signals/extractors/index.d.ts +7 -0
- package/dist/signals/extractors/index.d.ts.map +1 -1
- package/dist/signals/extractors/index.js +7 -0
- package/dist/signals/extractors/index.js.map +1 -1
- package/dist/signals/extractors/inline-style.d.ts +6 -0
- package/dist/signals/extractors/inline-style.d.ts.map +1 -0
- package/dist/signals/extractors/inline-style.js +60 -0
- package/dist/signals/extractors/inline-style.js.map +1 -0
- package/dist/signals/extractors/radius.d.ts +3 -0
- package/dist/signals/extractors/radius.d.ts.map +1 -0
- package/dist/signals/extractors/radius.js +32 -0
- package/dist/signals/extractors/radius.js.map +1 -0
- package/dist/signals/extractors/shadow.d.ts +3 -0
- package/dist/signals/extractors/shadow.d.ts.map +1 -0
- package/dist/signals/extractors/shadow.js +41 -0
- package/dist/signals/extractors/shadow.js.map +1 -0
- package/dist/signals/extractors/sizing.d.ts +6 -0
- package/dist/signals/extractors/sizing.d.ts.map +1 -0
- package/dist/signals/extractors/sizing.js +60 -0
- package/dist/signals/extractors/sizing.js.map +1 -0
- package/dist/signals/extractors/z-index.d.ts +3 -0
- package/dist/signals/extractors/z-index.d.ts.map +1 -0
- package/dist/signals/extractors/z-index.js +30 -0
- package/dist/signals/extractors/z-index.js.map +1 -0
- package/dist/signals/scanner-integration.d.ts.map +1 -1
- package/dist/signals/scanner-integration.js +35 -0
- package/dist/signals/scanner-integration.js.map +1 -1
- package/dist/signals/types.d.ts +4 -4
- package/dist/signals/types.d.ts.map +1 -1
- package/dist/signals/types.js +4 -0
- package/dist/signals/types.js.map +1 -1
- package/dist/storybook/extractor.d.ts +106 -1
- package/dist/storybook/extractor.d.ts.map +1 -1
- package/dist/storybook/extractor.js +325 -5
- package/dist/storybook/extractor.js.map +1 -1
- package/dist/storybook/index.d.ts +1 -1
- package/dist/storybook/index.d.ts.map +1 -1
- package/dist/storybook/index.js.map +1 -1
- package/dist/tailwind/index.d.ts +1 -1
- package/dist/tailwind/index.d.ts.map +1 -1
- package/dist/tailwind/index.js.map +1 -1
- package/dist/tailwind/scanner.d.ts +32 -0
- package/dist/tailwind/scanner.d.ts.map +1 -1
- package/dist/tailwind/scanner.js +105 -0
- package/dist/tailwind/scanner.js.map +1 -1
- package/package.json +2 -2
package/dist/signals/types.d.ts
CHANGED
|
@@ -2,7 +2,7 @@ import { z } from 'zod';
|
|
|
2
2
|
/**
|
|
3
3
|
* Signal types representing atomic facts extracted from code
|
|
4
4
|
*/
|
|
5
|
-
export declare const SignalTypeSchema: z.ZodEnum<["color-value", "spacing-value", "font-size", "font-family", "font-weight", "radius-value", "shadow-value", "breakpoint", "token-definition", "token-usage", "component-def", "component-usage", "prop-pattern", "class-pattern"]>;
|
|
5
|
+
export declare const SignalTypeSchema: z.ZodEnum<["color-value", "spacing-value", "font-size", "font-family", "font-weight", "radius-value", "shadow-value", "breakpoint", "z-index", "sizing-value", "inline-style", "arbitrary-value", "token-definition", "token-usage", "component-def", "component-usage", "prop-pattern", "class-pattern"]>;
|
|
6
6
|
export type SignalType = z.infer<typeof SignalTypeSchema>;
|
|
7
7
|
/**
|
|
8
8
|
* Source location for a signal
|
|
@@ -64,7 +64,7 @@ export type SignalContext = z.infer<typeof SignalContextSchema>;
|
|
|
64
64
|
*/
|
|
65
65
|
export declare const RawSignalSchema: z.ZodObject<{
|
|
66
66
|
id: z.ZodString;
|
|
67
|
-
type: z.ZodEnum<["color-value", "spacing-value", "font-size", "font-family", "font-weight", "radius-value", "shadow-value", "breakpoint", "token-definition", "token-usage", "component-def", "component-usage", "prop-pattern", "class-pattern"]>;
|
|
67
|
+
type: z.ZodEnum<["color-value", "spacing-value", "font-size", "font-family", "font-weight", "radius-value", "shadow-value", "breakpoint", "z-index", "sizing-value", "inline-style", "arbitrary-value", "token-definition", "token-usage", "component-def", "component-usage", "prop-pattern", "class-pattern"]>;
|
|
68
68
|
value: z.ZodUnknown;
|
|
69
69
|
location: z.ZodObject<{
|
|
70
70
|
path: z.ZodString;
|
|
@@ -100,7 +100,7 @@ export declare const RawSignalSchema: z.ZodObject<{
|
|
|
100
100
|
}>;
|
|
101
101
|
metadata: z.ZodRecord<z.ZodString, z.ZodUnknown>;
|
|
102
102
|
}, "strip", z.ZodTypeAny, {
|
|
103
|
-
type: "color-value" | "spacing-value" | "font-size" | "font-family" | "font-weight" | "radius-value" | "shadow-value" | "breakpoint" | "token-definition" | "token-usage" | "component-def" | "component-usage" | "prop-pattern" | "class-pattern";
|
|
103
|
+
type: "color-value" | "spacing-value" | "font-size" | "font-family" | "font-weight" | "radius-value" | "shadow-value" | "breakpoint" | "z-index" | "sizing-value" | "inline-style" | "arbitrary-value" | "token-definition" | "token-usage" | "component-def" | "component-usage" | "prop-pattern" | "class-pattern";
|
|
104
104
|
id: string;
|
|
105
105
|
location: {
|
|
106
106
|
path: string;
|
|
@@ -117,7 +117,7 @@ export declare const RawSignalSchema: z.ZodObject<{
|
|
|
117
117
|
metadata: Record<string, unknown>;
|
|
118
118
|
value?: unknown;
|
|
119
119
|
}, {
|
|
120
|
-
type: "color-value" | "spacing-value" | "font-size" | "font-family" | "font-weight" | "radius-value" | "shadow-value" | "breakpoint" | "token-definition" | "token-usage" | "component-def" | "component-usage" | "prop-pattern" | "class-pattern";
|
|
120
|
+
type: "color-value" | "spacing-value" | "font-size" | "font-family" | "font-weight" | "radius-value" | "shadow-value" | "breakpoint" | "z-index" | "sizing-value" | "inline-style" | "arbitrary-value" | "token-definition" | "token-usage" | "component-def" | "component-usage" | "prop-pattern" | "class-pattern";
|
|
121
121
|
id: string;
|
|
122
122
|
location: {
|
|
123
123
|
path: string;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/signals/types.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAExB;;GAEG;AACH,eAAO,MAAM,gBAAgB,
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/signals/types.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAExB;;GAEG;AACH,eAAO,MAAM,gBAAgB,4SAuB3B,CAAC;AAEH,MAAM,MAAM,UAAU,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,gBAAgB,CAAC,CAAC;AAE1D;;GAEG;AACH,eAAO,MAAM,oBAAoB;;;;;;;;;;;;;;;EAK/B,CAAC;AAEH,MAAM,MAAM,cAAc,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,oBAAoB,CAAC,CAAC;AAElE;;GAEG;AACH,eAAO,MAAM,cAAc,qHAczB,CAAC;AAEH,MAAM,MAAM,QAAQ,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,cAAc,CAAC,CAAC;AAEtD;;GAEG;AACH,eAAO,MAAM,eAAe,+FAQf,CAAC;AAEd,MAAM,MAAM,SAAS,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,eAAe,CAAC,CAAC;AAExD;;GAEG;AACH,eAAO,MAAM,WAAW,8CAItB,CAAC;AAEH,MAAM,MAAM,KAAK,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,WAAW,CAAC,CAAC;AAEhD;;GAEG;AACH,eAAO,MAAM,mBAAmB;;;;;;;;;;;;;;;EAK9B,CAAC;AAEH,MAAM,MAAM,aAAa,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,mBAAmB,CAAC,CAAC;AAEhE;;GAEG;AACH,eAAO,MAAM,eAAe;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAO1B,CAAC;AAEH,MAAM,MAAM,SAAS,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,eAAe,CAAC,CAAC;AAExD;;GAEG;AACH,wBAAgB,cAAc,CAC5B,IAAI,EAAE,UAAU,EAChB,IAAI,EAAE,MAAM,EACZ,IAAI,EAAE,MAAM,EACZ,KAAK,EAAE,OAAO,GACb,MAAM,CAKR"}
|
package/dist/signals/types.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"types.js","sourceRoot":"","sources":["../../src/signals/types.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAExB;;GAEG;AACH,MAAM,CAAC,MAAM,gBAAgB,GAAG,CAAC,CAAC,IAAI,CAAC;IACrC,sBAAsB;IACtB,aAAa;IACb,eAAe;IACf,WAAW;IACX,aAAa;IACb,aAAa;IACb,cAAc;IACd,cAAc;IACd,YAAY;IACZ,gBAAgB;IAChB,kBAAkB;IAClB,aAAa;IACb,oBAAoB;IACpB,eAAe;IACf,iBAAiB;IACjB,kBAAkB;IAClB,cAAc;IACd,eAAe;CAChB,CAAC,CAAC;AAIH;;GAEG;AACH,MAAM,CAAC,MAAM,oBAAoB,GAAG,CAAC,CAAC,MAAM,CAAC;IAC3C,IAAI,EAAE,CAAC,CAAC,MAAM,EAAE;IAChB,IAAI,EAAE,CAAC,CAAC,MAAM,EAAE;IAChB,MAAM,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE;IAC7B,OAAO,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE;CAC/B,CAAC,CAAC;AAIH;;GAEG;AACH,MAAM,CAAC,MAAM,cAAc,GAAG,CAAC,CAAC,IAAI,CAAC;IACnC,KAAK;IACL,KAAK;IACL,IAAI;IACJ,IAAI;IACJ,KAAK;IACL,QAAQ;IACR,KAAK;IACL,MAAM;IACN,MAAM;IACN,MAAM;IACN,QAAQ;IACR,MAAM;IACN,UAAU;CACX,CAAC,CAAC;AAIH;;GAEG;AACH,MAAM,CAAC,MAAM,eAAe,GAAG,CAAC,CAAC,IAAI,CAAC;IACpC,OAAO;IACP,KAAK;IACL,QAAQ;IACR,SAAS;IACT,UAAU;IACV,SAAS;IACT,KAAK;CACN,CAAC,CAAC,QAAQ,EAAE,CAAC;AAId;;GAEG;AACH,MAAM,CAAC,MAAM,WAAW,GAAG,CAAC,CAAC,IAAI,CAAC;IAChC,QAAQ;IACR,WAAW;IACX,QAAQ;CACT,CAAC,CAAC;AAIH;;GAEG;AACH,MAAM,CAAC,MAAM,mBAAmB,GAAG,CAAC,CAAC,MAAM,CAAC;IAC1C,QAAQ,EAAE,cAAc;IACxB,SAAS,EAAE,eAAe;IAC1B,KAAK,EAAE,WAAW;IAClB,WAAW,EAAE,CAAC,CAAC,OAAO,EAAE;CACzB,CAAC,CAAC;AAIH;;GAEG;AACH,MAAM,CAAC,MAAM,eAAe,GAAG,CAAC,CAAC,MAAM,CAAC;IACtC,EAAE,EAAE,CAAC,CAAC,MAAM,EAAE;IACd,IAAI,EAAE,gBAAgB;IACtB,KAAK,EAAE,CAAC,CAAC,OAAO,EAAE;IAClB,QAAQ,EAAE,oBAAoB;IAC9B,OAAO,EAAE,mBAAmB;IAC5B,QAAQ,EAAE,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,OAAO,EAAE,CAAC;CAChC,CAAC,CAAC;AAIH;;GAEG;AACH,MAAM,UAAU,cAAc,CAC5B,IAAgB,EAChB,IAAY,EACZ,IAAY,EACZ,KAAc;IAEd,MAAM,SAAS,GAAG,OAAO,KAAK,KAAK,QAAQ;QACzC,CAAC,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC,EAAE,EAAE,CAAC;QACpB,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,KAAK,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;IAC/B,OAAO,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,SAAS,EAAE,CAAC;AAChD,CAAC"}
|
|
1
|
+
{"version":3,"file":"types.js","sourceRoot":"","sources":["../../src/signals/types.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAExB;;GAEG;AACH,MAAM,CAAC,MAAM,gBAAgB,GAAG,CAAC,CAAC,IAAI,CAAC;IACrC,sBAAsB;IACtB,aAAa;IACb,eAAe;IACf,WAAW;IACX,aAAa;IACb,aAAa;IACb,cAAc;IACd,cAAc;IACd,YAAY;IACZ,SAAS;IACT,cAAc;IACd,cAAc;IACd,iBAAiB;IACjB,gBAAgB;IAChB,kBAAkB;IAClB,aAAa;IACb,oBAAoB;IACpB,eAAe;IACf,iBAAiB;IACjB,kBAAkB;IAClB,cAAc;IACd,eAAe;CAChB,CAAC,CAAC;AAIH;;GAEG;AACH,MAAM,CAAC,MAAM,oBAAoB,GAAG,CAAC,CAAC,MAAM,CAAC;IAC3C,IAAI,EAAE,CAAC,CAAC,MAAM,EAAE;IAChB,IAAI,EAAE,CAAC,CAAC,MAAM,EAAE;IAChB,MAAM,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE;IAC7B,OAAO,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE;CAC/B,CAAC,CAAC;AAIH;;GAEG;AACH,MAAM,CAAC,MAAM,cAAc,GAAG,CAAC,CAAC,IAAI,CAAC;IACnC,KAAK;IACL,KAAK;IACL,IAAI;IACJ,IAAI;IACJ,KAAK;IACL,QAAQ;IACR,KAAK;IACL,MAAM;IACN,MAAM;IACN,MAAM;IACN,QAAQ;IACR,MAAM;IACN,UAAU;CACX,CAAC,CAAC;AAIH;;GAEG;AACH,MAAM,CAAC,MAAM,eAAe,GAAG,CAAC,CAAC,IAAI,CAAC;IACpC,OAAO;IACP,KAAK;IACL,QAAQ;IACR,SAAS;IACT,UAAU;IACV,SAAS;IACT,KAAK;CACN,CAAC,CAAC,QAAQ,EAAE,CAAC;AAId;;GAEG;AACH,MAAM,CAAC,MAAM,WAAW,GAAG,CAAC,CAAC,IAAI,CAAC;IAChC,QAAQ;IACR,WAAW;IACX,QAAQ;CACT,CAAC,CAAC;AAIH;;GAEG;AACH,MAAM,CAAC,MAAM,mBAAmB,GAAG,CAAC,CAAC,MAAM,CAAC;IAC1C,QAAQ,EAAE,cAAc;IACxB,SAAS,EAAE,eAAe;IAC1B,KAAK,EAAE,WAAW;IAClB,WAAW,EAAE,CAAC,CAAC,OAAO,EAAE;CACzB,CAAC,CAAC;AAIH;;GAEG;AACH,MAAM,CAAC,MAAM,eAAe,GAAG,CAAC,CAAC,MAAM,CAAC;IACtC,EAAE,EAAE,CAAC,CAAC,MAAM,EAAE;IACd,IAAI,EAAE,gBAAgB;IACtB,KAAK,EAAE,CAAC,CAAC,OAAO,EAAE;IAClB,QAAQ,EAAE,oBAAoB;IAC9B,OAAO,EAAE,mBAAmB;IAC5B,QAAQ,EAAE,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,OAAO,EAAE,CAAC;CAChC,CAAC,CAAC;AAIH;;GAEG;AACH,MAAM,UAAU,cAAc,CAC5B,IAAgB,EAChB,IAAY,EACZ,IAAY,EACZ,KAAc;IAEd,MAAM,SAAS,GAAG,OAAO,KAAK,KAAK,QAAQ;QACzC,CAAC,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC,EAAE,EAAE,CAAC;QACpB,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,KAAK,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;IAC/B,OAAO,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,SAAS,EAAE,CAAC;AAChD,CAAC"}
|
|
@@ -13,22 +13,103 @@ export interface StoryFileScannerConfig extends ScannerConfig {
|
|
|
13
13
|
include?: string[];
|
|
14
14
|
/** Patterns to exclude */
|
|
15
15
|
exclude?: string[];
|
|
16
|
+
/** Whether to scan MDX files (default: true) */
|
|
17
|
+
scanMdx?: boolean;
|
|
18
|
+
/** Whether to validate story args against prop types (default: false) */
|
|
19
|
+
validateArgs?: boolean;
|
|
20
|
+
}
|
|
21
|
+
/**
|
|
22
|
+
* MDX story information
|
|
23
|
+
*/
|
|
24
|
+
export interface MdxStoryInfo {
|
|
25
|
+
/** File path */
|
|
26
|
+
file: string;
|
|
27
|
+
/** Title extracted from Meta */
|
|
28
|
+
title?: string;
|
|
29
|
+
/** Component referenced in Meta */
|
|
30
|
+
component?: string;
|
|
31
|
+
/** Story names found in the MDX */
|
|
32
|
+
stories: string[];
|
|
33
|
+
/** Whether it uses Canvas blocks */
|
|
34
|
+
hasCanvas: boolean;
|
|
35
|
+
/** Whether it uses Story blocks */
|
|
36
|
+
hasStoryBlocks: boolean;
|
|
37
|
+
/** Whether it has prose documentation */
|
|
38
|
+
hasDocumentation: boolean;
|
|
39
|
+
}
|
|
40
|
+
/**
|
|
41
|
+
* Args validation issue found in a story
|
|
42
|
+
*/
|
|
43
|
+
export interface ArgsValidationIssue {
|
|
44
|
+
/** Story name where the issue was found */
|
|
45
|
+
storyName: string;
|
|
46
|
+
/** Property name with the issue */
|
|
47
|
+
propName: string;
|
|
48
|
+
/** Type of validation issue */
|
|
49
|
+
issueType: 'type-mismatch' | 'missing-required' | 'unknown-prop' | 'invalid-option';
|
|
50
|
+
/** Description of the issue */
|
|
51
|
+
message: string;
|
|
52
|
+
/** Expected type (for type mismatches) */
|
|
53
|
+
expectedType?: string;
|
|
54
|
+
/** Actual type found */
|
|
55
|
+
actualType?: string;
|
|
56
|
+
/** Value that caused the issue */
|
|
57
|
+
actualValue?: unknown;
|
|
58
|
+
}
|
|
59
|
+
/**
|
|
60
|
+
* Validation result for a story file
|
|
61
|
+
*/
|
|
62
|
+
export interface StoryArgsValidation {
|
|
63
|
+
/** File path of the story */
|
|
64
|
+
file: string;
|
|
65
|
+
/** Component name */
|
|
66
|
+
componentName: string;
|
|
67
|
+
/** Issues found */
|
|
68
|
+
issues: ArgsValidationIssue[];
|
|
69
|
+
/** Total args validated */
|
|
70
|
+
argsValidated: number;
|
|
71
|
+
/** Whether all args are valid */
|
|
72
|
+
isValid: boolean;
|
|
16
73
|
}
|
|
17
74
|
/**
|
|
18
75
|
* Scans .stories.{ts,tsx,js,jsx} files to extract component metadata.
|
|
19
76
|
* This scanner parses Component Story Format (CSF) files directly from source code.
|
|
20
77
|
*/
|
|
78
|
+
/**
|
|
79
|
+
* Extended scan result with MDX info
|
|
80
|
+
*/
|
|
81
|
+
export interface StoryFileScanResult extends ScanResult<Component> {
|
|
82
|
+
/** MDX story files found */
|
|
83
|
+
mdxStories: MdxStoryInfo[];
|
|
84
|
+
/** Components with stories */
|
|
85
|
+
documentedComponents: string[];
|
|
86
|
+
/** Total story count */
|
|
87
|
+
storyCount: number;
|
|
88
|
+
/** Args validation results (when validateArgs is enabled) */
|
|
89
|
+
argsValidation?: StoryArgsValidation[];
|
|
90
|
+
}
|
|
21
91
|
export declare class StoryFileScanner extends Scanner<Component, StoryFileScannerConfig> {
|
|
22
92
|
/** Default file patterns for story files */
|
|
23
93
|
private static readonly DEFAULT_PATTERNS;
|
|
94
|
+
/** MDX file patterns */
|
|
95
|
+
private static readonly MDX_PATTERNS;
|
|
24
96
|
constructor(config: StoryFileScannerConfig);
|
|
25
|
-
|
|
97
|
+
private parsedMetas;
|
|
98
|
+
scan(): Promise<StoryFileScanResult>;
|
|
26
99
|
getSourceType(): string;
|
|
27
100
|
/**
|
|
28
101
|
* Get story patterns from .storybook/main.ts config file
|
|
29
102
|
*/
|
|
30
103
|
getStoryPatternsFromConfig(): Promise<string[]>;
|
|
31
104
|
private extractStoriesPatterns;
|
|
105
|
+
/**
|
|
106
|
+
* Scan MDX story files
|
|
107
|
+
*/
|
|
108
|
+
private scanMdxFiles;
|
|
109
|
+
/**
|
|
110
|
+
* Parse an MDX file for story information
|
|
111
|
+
*/
|
|
112
|
+
private parseMdxFile;
|
|
32
113
|
private parseStoryFile;
|
|
33
114
|
private extractMeta;
|
|
34
115
|
/**
|
|
@@ -85,6 +166,30 @@ export declare class StoryFileScanner extends Scanner<Component, StoryFileScanne
|
|
|
85
166
|
private extractComponentName;
|
|
86
167
|
private getComponentNameFromTitle;
|
|
87
168
|
private createStoryId;
|
|
169
|
+
/**
|
|
170
|
+
* Validate all story args against their argTypes
|
|
171
|
+
*/
|
|
172
|
+
private validateAllStoryArgs;
|
|
173
|
+
/**
|
|
174
|
+
* Validate args for a single story file
|
|
175
|
+
*/
|
|
176
|
+
private validateStoryArgs;
|
|
177
|
+
/**
|
|
178
|
+
* Get the expected type string from an argType
|
|
179
|
+
*/
|
|
180
|
+
private getExpectedType;
|
|
181
|
+
/**
|
|
182
|
+
* Map Storybook control types to basic types
|
|
183
|
+
*/
|
|
184
|
+
private controlToType;
|
|
185
|
+
/**
|
|
186
|
+
* Get the actual type of a value
|
|
187
|
+
*/
|
|
188
|
+
private getActualType;
|
|
189
|
+
/**
|
|
190
|
+
* Check if a value is compatible with an expected type
|
|
191
|
+
*/
|
|
192
|
+
private isTypeCompatible;
|
|
88
193
|
}
|
|
89
194
|
/**
|
|
90
195
|
* Scans Storybook static builds (index.json/stories.json) or running servers.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"extractor.d.ts","sourceRoot":"","sources":["../../src/storybook/extractor.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,UAAU,EAAE,aAAa,EAAwB,MAAM,oBAAoB,CAAC;AAC9F,OAAO,KAAK,EAAE,SAAS,EAAmC,MAAM,mBAAmB,CAAC;AAOpF,MAAM,WAAW,sBAAuB,SAAQ,aAAa;IAC3D,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,sCAAsC;IACtC,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,gEAAgE;IAChE,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB;AAiBD,MAAM,WAAW,sBAAuB,SAAQ,aAAa;IAC3D,qCAAqC;IACrC,OAAO,CAAC,EAAE,MAAM,EAAE,CAAC;IACnB,0BAA0B;IAC1B,OAAO,CAAC,EAAE,MAAM,EAAE,CAAC;
|
|
1
|
+
{"version":3,"file":"extractor.d.ts","sourceRoot":"","sources":["../../src/storybook/extractor.ts"],"names":[],"mappings":"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"}
|
|
@@ -12,10 +12,6 @@ const STORY_SCANNER_EXCLUDES = [
|
|
|
12
12
|
'**/.next/**',
|
|
13
13
|
'**/coverage/**',
|
|
14
14
|
];
|
|
15
|
-
/**
|
|
16
|
-
* Scans .stories.{ts,tsx,js,jsx} files to extract component metadata.
|
|
17
|
-
* This scanner parses Component Story Format (CSF) files directly from source code.
|
|
18
|
-
*/
|
|
19
15
|
export class StoryFileScanner extends Scanner {
|
|
20
16
|
/** Default file patterns for story files */
|
|
21
17
|
static DEFAULT_PATTERNS = [
|
|
@@ -24,15 +20,46 @@ export class StoryFileScanner extends Scanner {
|
|
|
24
20
|
'**/*.stories.jsx',
|
|
25
21
|
'**/*.stories.js',
|
|
26
22
|
];
|
|
23
|
+
/** MDX file patterns */
|
|
24
|
+
static MDX_PATTERNS = [
|
|
25
|
+
'**/*.stories.mdx',
|
|
26
|
+
'**/*.mdx',
|
|
27
|
+
];
|
|
27
28
|
constructor(config) {
|
|
28
29
|
// Override exclude patterns to not exclude story files
|
|
29
30
|
super({
|
|
30
31
|
...config,
|
|
32
|
+
scanMdx: config.scanMdx !== false,
|
|
31
33
|
exclude: config.exclude ?? STORY_SCANNER_EXCLUDES,
|
|
32
34
|
});
|
|
33
35
|
}
|
|
36
|
+
// Store parsed meta for validation
|
|
37
|
+
parsedMetas = new Map();
|
|
34
38
|
async scan() {
|
|
35
|
-
|
|
39
|
+
// Clear the metas map for each scan
|
|
40
|
+
this.parsedMetas.clear();
|
|
41
|
+
const baseResult = await this.runScan((file) => this.parseStoryFile(file), StoryFileScanner.DEFAULT_PATTERNS);
|
|
42
|
+
// Scan MDX files if enabled
|
|
43
|
+
let mdxStories = [];
|
|
44
|
+
if (this.config.scanMdx !== false) {
|
|
45
|
+
mdxStories = await this.scanMdxFiles();
|
|
46
|
+
}
|
|
47
|
+
// Calculate documented components
|
|
48
|
+
const documentedComponents = baseResult.items.map(c => c.name);
|
|
49
|
+
// Calculate total story count
|
|
50
|
+
const storyCount = baseResult.items.reduce((sum, c) => sum + c.variants.length, 0) + mdxStories.reduce((sum, m) => sum + m.stories.length, 0);
|
|
51
|
+
// Validate args if enabled
|
|
52
|
+
let argsValidation;
|
|
53
|
+
if (this.config.validateArgs) {
|
|
54
|
+
argsValidation = this.validateAllStoryArgs();
|
|
55
|
+
}
|
|
56
|
+
return {
|
|
57
|
+
...baseResult,
|
|
58
|
+
mdxStories,
|
|
59
|
+
documentedComponents,
|
|
60
|
+
storyCount,
|
|
61
|
+
argsValidation,
|
|
62
|
+
};
|
|
36
63
|
}
|
|
37
64
|
getSourceType() {
|
|
38
65
|
return 'storybook';
|
|
@@ -80,6 +107,102 @@ export class StoryFileScanner extends Scanner {
|
|
|
80
107
|
ts.forEachChild(sourceFile, visit);
|
|
81
108
|
return patterns;
|
|
82
109
|
}
|
|
110
|
+
/**
|
|
111
|
+
* Scan MDX story files
|
|
112
|
+
*/
|
|
113
|
+
async scanMdxFiles() {
|
|
114
|
+
const mdxStories = [];
|
|
115
|
+
const { glob } = await import('glob');
|
|
116
|
+
for (const pattern of StoryFileScanner.MDX_PATTERNS) {
|
|
117
|
+
try {
|
|
118
|
+
const files = await glob(pattern, {
|
|
119
|
+
cwd: this.config.projectRoot,
|
|
120
|
+
ignore: this.config.exclude || STORY_SCANNER_EXCLUDES,
|
|
121
|
+
absolute: true,
|
|
122
|
+
});
|
|
123
|
+
for (const file of files) {
|
|
124
|
+
try {
|
|
125
|
+
const content = await readFile(file, 'utf-8');
|
|
126
|
+
const relativePath = relative(this.config.projectRoot, file);
|
|
127
|
+
const mdxInfo = this.parseMdxFile(content, relativePath);
|
|
128
|
+
if (mdxInfo) {
|
|
129
|
+
mdxStories.push(mdxInfo);
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
catch {
|
|
133
|
+
// Continue on read errors
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
catch {
|
|
138
|
+
// Continue to next pattern
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
return mdxStories;
|
|
142
|
+
}
|
|
143
|
+
/**
|
|
144
|
+
* Parse an MDX file for story information
|
|
145
|
+
*/
|
|
146
|
+
parseMdxFile(content, filePath) {
|
|
147
|
+
const info = {
|
|
148
|
+
file: filePath,
|
|
149
|
+
stories: [],
|
|
150
|
+
hasCanvas: false,
|
|
151
|
+
hasStoryBlocks: false,
|
|
152
|
+
hasDocumentation: false,
|
|
153
|
+
};
|
|
154
|
+
// Check for Meta component (required for story MDX)
|
|
155
|
+
const metaMatch = content.match(/<Meta\s+([^>]+)>/);
|
|
156
|
+
if (metaMatch) {
|
|
157
|
+
const metaProps = metaMatch[1];
|
|
158
|
+
// Extract title
|
|
159
|
+
const titleMatch = metaProps.match(/title\s*=\s*["']([^"']+)["']/);
|
|
160
|
+
if (titleMatch) {
|
|
161
|
+
info.title = titleMatch[1];
|
|
162
|
+
}
|
|
163
|
+
// Extract component (either as string or JSX reference)
|
|
164
|
+
const componentMatch = metaProps.match(/component\s*=\s*\{?([A-Za-z]+)\}?/);
|
|
165
|
+
if (componentMatch) {
|
|
166
|
+
info.component = componentMatch[1];
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
// Check for Canvas blocks
|
|
170
|
+
if (/<Canvas\s*[^>]*>/.test(content)) {
|
|
171
|
+
info.hasCanvas = true;
|
|
172
|
+
}
|
|
173
|
+
// Check for Story blocks and extract names
|
|
174
|
+
const storyRegex = /<Story\s+(?:[^>]*?)name\s*=\s*["']([^"']+)["'](?:[^>]*?)>/g;
|
|
175
|
+
let storyMatch;
|
|
176
|
+
while ((storyMatch = storyRegex.exec(content)) !== null) {
|
|
177
|
+
info.stories.push(storyMatch[1]);
|
|
178
|
+
info.hasStoryBlocks = true;
|
|
179
|
+
}
|
|
180
|
+
// Also check for export const story pattern in MDX2
|
|
181
|
+
const exportStoryRegex = /export\s+const\s+([A-Z][a-zA-Z0-9]*)\s*=/g;
|
|
182
|
+
while ((storyMatch = exportStoryRegex.exec(content)) !== null) {
|
|
183
|
+
const storyName = storyMatch[1];
|
|
184
|
+
if (!info.stories.includes(storyName)) {
|
|
185
|
+
info.stories.push(storyName);
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
// Check for prose documentation (markdown content outside of code blocks)
|
|
189
|
+
// Simple heuristic: if there's text content that's not in JSX or code blocks
|
|
190
|
+
const proseContent = content
|
|
191
|
+
.replace(/<[^>]+>/g, '') // Remove JSX tags
|
|
192
|
+
.replace(/```[\s\S]*?```/g, '') // Remove code blocks
|
|
193
|
+
.replace(/`[^`]+`/g, '') // Remove inline code
|
|
194
|
+
.replace(/import\s+.+;?/g, '') // Remove imports
|
|
195
|
+
.replace(/export\s+.+;?/g, '') // Remove exports
|
|
196
|
+
.trim();
|
|
197
|
+
if (proseContent.length > 50) {
|
|
198
|
+
info.hasDocumentation = true;
|
|
199
|
+
}
|
|
200
|
+
// Only return if this looks like a story MDX (has Meta, Canvas, or Story blocks)
|
|
201
|
+
if (info.title || info.component || info.hasCanvas || info.hasStoryBlocks || info.stories.length > 0) {
|
|
202
|
+
return info;
|
|
203
|
+
}
|
|
204
|
+
return null;
|
|
205
|
+
}
|
|
83
206
|
async parseStoryFile(filePath) {
|
|
84
207
|
const content = await readFile(filePath, 'utf-8');
|
|
85
208
|
const isTypeScript = filePath.endsWith('.tsx') || filePath.endsWith('.ts');
|
|
@@ -185,6 +308,13 @@ export class StoryFileScanner extends Scanner {
|
|
|
185
308
|
},
|
|
186
309
|
scannedAt: new Date(),
|
|
187
310
|
};
|
|
311
|
+
// Store meta for validation
|
|
312
|
+
this.parsedMetas.set(relativePath, {
|
|
313
|
+
meta,
|
|
314
|
+
variants,
|
|
315
|
+
file: relativePath,
|
|
316
|
+
componentName,
|
|
317
|
+
});
|
|
188
318
|
return [component];
|
|
189
319
|
}
|
|
190
320
|
extractMeta(sourceFile, relativePath) {
|
|
@@ -687,6 +817,26 @@ export class StoryFileScanner extends Scanner {
|
|
|
687
817
|
}
|
|
688
818
|
}
|
|
689
819
|
}
|
|
820
|
+
// Parse type: { name: 'string', required: true }
|
|
821
|
+
if (argPropName === 'type' && ts.isObjectLiteralExpression(argProp.initializer)) {
|
|
822
|
+
argInfo.type = { name: '' };
|
|
823
|
+
for (const typeProp of argProp.initializer.properties) {
|
|
824
|
+
if (!ts.isPropertyAssignment(typeProp) || !typeProp.name)
|
|
825
|
+
continue;
|
|
826
|
+
const typePropName = typeProp.name.getText(sourceFile);
|
|
827
|
+
if (typePropName === 'name' && ts.isStringLiteral(typeProp.initializer)) {
|
|
828
|
+
argInfo.type.name = typeProp.initializer.text;
|
|
829
|
+
}
|
|
830
|
+
if (typePropName === 'required') {
|
|
831
|
+
if (typeProp.initializer.kind === ts.SyntaxKind.TrueKeyword) {
|
|
832
|
+
argInfo.type.required = true;
|
|
833
|
+
}
|
|
834
|
+
else if (typeProp.initializer.kind === ts.SyntaxKind.FalseKeyword) {
|
|
835
|
+
argInfo.type.required = false;
|
|
836
|
+
}
|
|
837
|
+
}
|
|
838
|
+
}
|
|
839
|
+
}
|
|
690
840
|
}
|
|
691
841
|
}
|
|
692
842
|
argTypes[argName] = argInfo;
|
|
@@ -938,6 +1088,176 @@ export class StoryFileScanner extends Scanner {
|
|
|
938
1088
|
const normalizedName = storyName.toLowerCase().replace(/[^a-z0-9]+/g, '-');
|
|
939
1089
|
return `${normalizedTitle}--${normalizedName}`;
|
|
940
1090
|
}
|
|
1091
|
+
/**
|
|
1092
|
+
* Validate all story args against their argTypes
|
|
1093
|
+
*/
|
|
1094
|
+
validateAllStoryArgs() {
|
|
1095
|
+
const validations = [];
|
|
1096
|
+
for (const [, data] of this.parsedMetas) {
|
|
1097
|
+
const validation = this.validateStoryArgs(data);
|
|
1098
|
+
validations.push(validation);
|
|
1099
|
+
}
|
|
1100
|
+
return validations;
|
|
1101
|
+
}
|
|
1102
|
+
/**
|
|
1103
|
+
* Validate args for a single story file
|
|
1104
|
+
*/
|
|
1105
|
+
validateStoryArgs(data) {
|
|
1106
|
+
const issues = [];
|
|
1107
|
+
let argsValidated = 0;
|
|
1108
|
+
const { meta, variants, file, componentName } = data;
|
|
1109
|
+
const argTypes = meta.argTypes || {};
|
|
1110
|
+
for (const variant of variants) {
|
|
1111
|
+
if (!variant.args)
|
|
1112
|
+
continue;
|
|
1113
|
+
for (const [propName, value] of Object.entries(variant.args)) {
|
|
1114
|
+
argsValidated++;
|
|
1115
|
+
// Check if prop is defined in argTypes
|
|
1116
|
+
const argType = argTypes[propName];
|
|
1117
|
+
if (!argType) {
|
|
1118
|
+
// Unknown prop - might be intentional but worth noting
|
|
1119
|
+
issues.push({
|
|
1120
|
+
storyName: variant.name,
|
|
1121
|
+
propName,
|
|
1122
|
+
issueType: 'unknown-prop',
|
|
1123
|
+
message: `Prop '${propName}' is not defined in argTypes`,
|
|
1124
|
+
actualValue: value,
|
|
1125
|
+
});
|
|
1126
|
+
continue;
|
|
1127
|
+
}
|
|
1128
|
+
// Check type if defined
|
|
1129
|
+
const expectedType = this.getExpectedType(argType);
|
|
1130
|
+
if (expectedType) {
|
|
1131
|
+
const actualType = this.getActualType(value);
|
|
1132
|
+
const isValid = this.isTypeCompatible(expectedType, actualType, value);
|
|
1133
|
+
if (!isValid) {
|
|
1134
|
+
issues.push({
|
|
1135
|
+
storyName: variant.name,
|
|
1136
|
+
propName,
|
|
1137
|
+
issueType: 'type-mismatch',
|
|
1138
|
+
message: `Type mismatch for '${propName}': expected ${expectedType}, got ${actualType}`,
|
|
1139
|
+
expectedType,
|
|
1140
|
+
actualType,
|
|
1141
|
+
actualValue: value,
|
|
1142
|
+
});
|
|
1143
|
+
}
|
|
1144
|
+
}
|
|
1145
|
+
// Check options constraint
|
|
1146
|
+
if (argType.options && argType.options.length > 0) {
|
|
1147
|
+
if (typeof value === 'string' && !argType.options.includes(value)) {
|
|
1148
|
+
issues.push({
|
|
1149
|
+
storyName: variant.name,
|
|
1150
|
+
propName,
|
|
1151
|
+
issueType: 'invalid-option',
|
|
1152
|
+
message: `Invalid option for '${propName}': '${value}' is not in [${argType.options.join(', ')}]`,
|
|
1153
|
+
actualValue: value,
|
|
1154
|
+
});
|
|
1155
|
+
}
|
|
1156
|
+
}
|
|
1157
|
+
}
|
|
1158
|
+
// Check for missing required props
|
|
1159
|
+
for (const [propName, argType] of Object.entries(argTypes)) {
|
|
1160
|
+
if (argType.type?.required && (!variant.args || !(propName in variant.args))) {
|
|
1161
|
+
issues.push({
|
|
1162
|
+
storyName: variant.name,
|
|
1163
|
+
propName,
|
|
1164
|
+
issueType: 'missing-required',
|
|
1165
|
+
message: `Required prop '${propName}' is missing in story '${variant.name}'`,
|
|
1166
|
+
});
|
|
1167
|
+
}
|
|
1168
|
+
}
|
|
1169
|
+
}
|
|
1170
|
+
return {
|
|
1171
|
+
file,
|
|
1172
|
+
componentName,
|
|
1173
|
+
issues,
|
|
1174
|
+
argsValidated,
|
|
1175
|
+
isValid: issues.length === 0,
|
|
1176
|
+
};
|
|
1177
|
+
}
|
|
1178
|
+
/**
|
|
1179
|
+
* Get the expected type string from an argType
|
|
1180
|
+
*/
|
|
1181
|
+
getExpectedType(argType) {
|
|
1182
|
+
// Check explicit type
|
|
1183
|
+
if (argType.type?.name) {
|
|
1184
|
+
return argType.type.name;
|
|
1185
|
+
}
|
|
1186
|
+
// Infer from control
|
|
1187
|
+
if (argType.control) {
|
|
1188
|
+
if (typeof argType.control === 'string') {
|
|
1189
|
+
return this.controlToType(argType.control);
|
|
1190
|
+
}
|
|
1191
|
+
if (typeof argType.control === 'object' && argType.control.type) {
|
|
1192
|
+
return this.controlToType(argType.control.type);
|
|
1193
|
+
}
|
|
1194
|
+
}
|
|
1195
|
+
// Infer from options
|
|
1196
|
+
if (argType.options && argType.options.length > 0) {
|
|
1197
|
+
return 'string';
|
|
1198
|
+
}
|
|
1199
|
+
return undefined;
|
|
1200
|
+
}
|
|
1201
|
+
/**
|
|
1202
|
+
* Map Storybook control types to basic types
|
|
1203
|
+
*/
|
|
1204
|
+
controlToType(control) {
|
|
1205
|
+
switch (control) {
|
|
1206
|
+
case 'text':
|
|
1207
|
+
case 'color':
|
|
1208
|
+
case 'date':
|
|
1209
|
+
case 'select':
|
|
1210
|
+
case 'radio':
|
|
1211
|
+
return 'string';
|
|
1212
|
+
case 'number':
|
|
1213
|
+
case 'range':
|
|
1214
|
+
return 'number';
|
|
1215
|
+
case 'boolean':
|
|
1216
|
+
return 'boolean';
|
|
1217
|
+
case 'object':
|
|
1218
|
+
return 'object';
|
|
1219
|
+
case 'array':
|
|
1220
|
+
return 'array';
|
|
1221
|
+
case 'file':
|
|
1222
|
+
return 'object';
|
|
1223
|
+
default:
|
|
1224
|
+
return control;
|
|
1225
|
+
}
|
|
1226
|
+
}
|
|
1227
|
+
/**
|
|
1228
|
+
* Get the actual type of a value
|
|
1229
|
+
*/
|
|
1230
|
+
getActualType(value) {
|
|
1231
|
+
if (value === null)
|
|
1232
|
+
return 'null';
|
|
1233
|
+
if (value === undefined)
|
|
1234
|
+
return 'undefined';
|
|
1235
|
+
if (Array.isArray(value))
|
|
1236
|
+
return 'array';
|
|
1237
|
+
return typeof value;
|
|
1238
|
+
}
|
|
1239
|
+
/**
|
|
1240
|
+
* Check if a value is compatible with an expected type
|
|
1241
|
+
*/
|
|
1242
|
+
isTypeCompatible(expectedType, actualType, value) {
|
|
1243
|
+
// Direct type match
|
|
1244
|
+
if (expectedType === actualType)
|
|
1245
|
+
return true;
|
|
1246
|
+
// Allow string for enum types (quoted union types)
|
|
1247
|
+
if (expectedType.includes("'") && actualType === 'string')
|
|
1248
|
+
return true;
|
|
1249
|
+
// Allow number coercion
|
|
1250
|
+
if (expectedType === 'number' && actualType === 'string') {
|
|
1251
|
+
return !isNaN(Number(value));
|
|
1252
|
+
}
|
|
1253
|
+
// Allow any for object types
|
|
1254
|
+
if (expectedType === 'object' && actualType === 'object')
|
|
1255
|
+
return true;
|
|
1256
|
+
// Allow function references (string in AST parsed form)
|
|
1257
|
+
if (expectedType === 'function' && actualType === 'string')
|
|
1258
|
+
return true;
|
|
1259
|
+
return false;
|
|
1260
|
+
}
|
|
941
1261
|
}
|
|
942
1262
|
/**
|
|
943
1263
|
* Scans Storybook static builds (index.json/stories.json) or running servers.
|