@fragments-sdk/cli 0.14.3 → 0.15.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/README.md +0 -3
- package/dist/bin.js +4290 -3754
- package/dist/bin.js.map +1 -1
- package/dist/{chunk-TXFCEDOC.js → chunk-2WXKALIG.js} +2 -2
- package/dist/{chunk-I34BC3CU.js → chunk-32LIWN2P.js} +1006 -3
- package/dist/chunk-32LIWN2P.js.map +1 -0
- package/dist/{chunk-55KERLWL.js → chunk-65WSVDV5.js} +314 -89
- package/dist/chunk-65WSVDV5.js.map +1 -0
- package/dist/chunk-7DZC4YEV.js +294 -0
- package/dist/chunk-7DZC4YEV.js.map +1 -0
- package/dist/{chunk-LOYS64QS.js → chunk-7WHVW72L.js} +230 -19
- package/dist/chunk-7WHVW72L.js.map +1 -0
- package/dist/{chunk-PJT5IZ37.js → chunk-BJE3425I.js} +19 -52
- package/dist/{chunk-PJT5IZ37.js.map → chunk-BJE3425I.js.map} +1 -1
- package/dist/{chunk-5A6X2Y73.js → chunk-CZD3AD4Q.js} +12 -11
- package/dist/chunk-CZD3AD4Q.js.map +1 -0
- package/dist/{chunk-EYXVAMEX.js → chunk-MN3TJ3D5.js} +72 -3
- package/dist/chunk-MN3TJ3D5.js.map +1 -0
- package/dist/chunk-QCN35LJU.js +630 -0
- package/dist/chunk-QCN35LJU.js.map +1 -0
- package/dist/chunk-T47OLCSF.js +36 -0
- package/dist/chunk-T47OLCSF.js.map +1 -0
- package/dist/{chunk-APTQIBS5.js → chunk-XJQ5BIWI.js} +144 -1049
- package/dist/chunk-XJQ5BIWI.js.map +1 -0
- package/dist/codebase-scanner-VOTPXRYW.js +22 -0
- package/dist/converter-JLINP7CJ.js +34 -0
- package/dist/converter-JLINP7CJ.js.map +1 -0
- package/dist/core/index.js +43 -1
- package/dist/{generate-RYWIPDN2.js → generate-A4FP5426.js} +3 -4
- package/dist/{generate-RYWIPDN2.js.map → generate-A4FP5426.js.map} +1 -1
- package/dist/govern-scan-UCBZR6D6.js +280 -0
- package/dist/govern-scan-UCBZR6D6.js.map +1 -0
- package/dist/index.d.ts +2 -1
- package/dist/index.js +11 -11
- package/dist/{init-WRUSW7R5.js → init-HGSM35XA.js} +131 -128
- package/dist/init-HGSM35XA.js.map +1 -0
- package/dist/{init-cloud-REQ3XLHO.js → init-cloud-MQ6GRJAZ.js} +2 -2
- package/dist/mcp-bin.js +5 -36
- package/dist/mcp-bin.js.map +1 -1
- package/dist/scan-VNNKACG2.js +15 -0
- package/dist/{scan-generate-TFZVL3BT.js → scan-generate-TWRHNU5M.js} +335 -46
- package/dist/scan-generate-TWRHNU5M.js.map +1 -0
- package/dist/scanner-7LAZYPWZ.js +13 -0
- package/dist/{service-HKJ6B7P7.js → service-FHQU7YS7.js} +27 -23
- package/dist/{snapshot-C5DYIGIV.js → snapshot-KQEQ6XHL.js} +2 -2
- package/dist/{static-viewer-DUVC4UIM.js → static-viewer-63PG6FWY.js} +3 -3
- package/dist/static-viewer-63PG6FWY.js.map +1 -0
- package/dist/{test-JW7JIDFG.js → test-UQYUCZIS.js} +4 -6
- package/dist/{test-JW7JIDFG.js.map → test-UQYUCZIS.js.map} +1 -1
- package/dist/{tokens-KE73G5JC.js → tokens-6GYKDV6U.js} +6 -5
- package/dist/{tokens-KE73G5JC.js.map → tokens-6GYKDV6U.js.map} +1 -1
- package/dist/tokens-generate-VTZV5EEW.js +86 -0
- package/dist/tokens-generate-VTZV5EEW.js.map +1 -0
- package/package.json +6 -6
- package/src/bin.ts +210 -48
- package/src/build.ts +130 -6
- package/src/commands/__fixtures__/shadcn-label-wrapper/package.json +7 -0
- package/src/commands/__fixtures__/shadcn-label-wrapper/src/components/ui/label.contract.json +42 -0
- package/src/commands/__fixtures__/shadcn-label-wrapper/src/components/ui/label.tsx +11 -0
- package/src/commands/__fixtures__/shadcn-label-wrapper/src/components/ui/primitive.contract.json +20 -0
- package/src/commands/__fixtures__/shadcn-label-wrapper/src/components/ui/primitive.tsx +14 -0
- package/src/commands/__fixtures__/shadcn-label-wrapper/tsconfig.app.json +23 -0
- package/src/commands/__tests__/init.test.ts +113 -0
- package/src/commands/__tests__/scan-generate.test.ts +188 -69
- package/src/commands/__tests__/verify.test.ts +91 -0
- package/src/commands/discover.ts +151 -0
- package/src/commands/enhance.ts +3 -1
- package/src/commands/govern-scan.ts +386 -0
- package/src/commands/govern.ts +2 -2
- package/src/commands/init.ts +152 -28
- package/src/commands/inspect.ts +290 -0
- package/src/commands/migrate-contract.ts +85 -0
- package/src/commands/scan-generate.ts +438 -50
- package/src/commands/scan.ts +1 -0
- package/src/commands/setup.ts +27 -50
- package/src/commands/tokens-generate.ts +113 -0
- package/src/commands/verify.ts +195 -1
- package/src/core/__fixtures__/shadcn-input/input.tsx +7 -0
- package/src/core/__fixtures__/shadcn-input/tsconfig.json +14 -0
- package/src/core/__fixtures__/shadcn-label/label.tsx +11 -0
- package/src/core/__fixtures__/shadcn-label/primitive.tsx +14 -0
- package/src/core/__fixtures__/shadcn-label/tsconfig.json +14 -0
- package/src/core/__fixtures__/shadcn-radix-label/label.tsx +11 -0
- package/src/core/__fixtures__/shadcn-radix-label/node_modules/radix-ui/index.d.ts +12 -0
- package/src/core/__fixtures__/shadcn-radix-label/tsconfig.json +14 -0
- package/src/core/__tests__/contract-parity.test.ts +316 -0
- package/src/core/component-extractor.test.ts +39 -0
- package/src/core/component-extractor.ts +92 -1
- package/src/core/config.ts +2 -1
- package/src/core/discovery.ts +13 -2
- package/src/core/drift-verifier.ts +123 -0
- package/src/core/extractor-adapter.ts +80 -0
- package/src/mcp/__tests__/projectFields.test.ts +1 -1
- package/src/mcp/utils.ts +1 -50
- package/src/migrate/converter.ts +3 -3
- package/src/migrate/fragment-to-contract.ts +253 -0
- package/src/migrate/report.ts +1 -1
- package/src/scripts/token-benchmark.ts +121 -0
- package/src/service/__tests__/props-extractor.test.ts +94 -0
- package/src/service/__tests__/token-normalizer.test.ts +690 -0
- package/src/service/ast-utils.ts +4 -23
- package/src/service/babel-config.ts +23 -0
- package/src/service/enhance/converter.ts +61 -0
- package/src/service/enhance/props-extractor.ts +25 -8
- package/src/service/enhance/scanner.ts +5 -24
- package/src/service/snippet-validation.ts +9 -3
- package/src/service/token-normalizer.ts +510 -0
- package/src/shared/index.ts +1 -0
- package/src/shared/project-fields.ts +46 -0
- package/src/viewer/__tests__/viewer-integration.test.ts +8 -8
- package/src/viewer/preview-adapter.ts +116 -0
- package/src/viewer/style-utils.ts +27 -412
- package/src/viewer/vite-plugin.ts +2 -2
- package/dist/chunk-55KERLWL.js.map +0 -1
- package/dist/chunk-5A6X2Y73.js.map +0 -1
- package/dist/chunk-APTQIBS5.js.map +0 -1
- package/dist/chunk-EYXVAMEX.js.map +0 -1
- package/dist/chunk-I34BC3CU.js.map +0 -1
- package/dist/chunk-LOYS64QS.js.map +0 -1
- package/dist/chunk-ZKTFKHWN.js +0 -324
- package/dist/chunk-ZKTFKHWN.js.map +0 -1
- package/dist/discovery-VDANZAJ2.js +0 -28
- package/dist/init-WRUSW7R5.js.map +0 -1
- package/dist/scan-YJHQIRKG.js +0 -14
- package/dist/scan-generate-TFZVL3BT.js.map +0 -1
- package/dist/viewer-2TZS3NDL.js +0 -2730
- package/dist/viewer-2TZS3NDL.js.map +0 -1
- package/src/commands/dev.ts +0 -107
- /package/dist/{chunk-TXFCEDOC.js.map → chunk-2WXKALIG.js.map} +0 -0
- /package/dist/{discovery-VDANZAJ2.js.map → codebase-scanner-VOTPXRYW.js.map} +0 -0
- /package/dist/{init-cloud-REQ3XLHO.js.map → init-cloud-MQ6GRJAZ.js.map} +0 -0
- /package/dist/{scan-YJHQIRKG.js.map → scan-VNNKACG2.js.map} +0 -0
- /package/dist/{service-HKJ6B7P7.js.map → scanner-7LAZYPWZ.js.map} +0 -0
- /package/dist/{static-viewer-DUVC4UIM.js.map → service-FHQU7YS7.js.map} +0 -0
- /package/dist/{snapshot-C5DYIGIV.js.map → snapshot-KQEQ6XHL.js.map} +0 -0
|
@@ -2,7 +2,10 @@ import { createRequire as __banner_createRequire } from 'module'; const require
|
|
|
2
2
|
import {
|
|
3
3
|
BRAND,
|
|
4
4
|
DEFAULTS
|
|
5
|
-
} from "./chunk-
|
|
5
|
+
} from "./chunk-32LIWN2P.js";
|
|
6
|
+
import {
|
|
7
|
+
BABEL_PARSER_OPTIONS
|
|
8
|
+
} from "./chunk-7DZC4YEV.js";
|
|
6
9
|
|
|
7
10
|
// src/service/browser-pool.ts
|
|
8
11
|
var BrowserPool = class {
|
|
@@ -72,8 +75,8 @@ var BrowserPool = class {
|
|
|
72
75
|
}
|
|
73
76
|
await this.clearContext(context);
|
|
74
77
|
if (this.waitingQueue.length > 0) {
|
|
75
|
-
const
|
|
76
|
-
|
|
78
|
+
const resolve4 = this.waitingQueue.shift();
|
|
79
|
+
resolve4(context);
|
|
77
80
|
return;
|
|
78
81
|
}
|
|
79
82
|
this.available.push(context);
|
|
@@ -100,7 +103,7 @@ var BrowserPool = class {
|
|
|
100
103
|
clearTimeout(this.idleTimeout);
|
|
101
104
|
this.idleTimeout = null;
|
|
102
105
|
}
|
|
103
|
-
for (const
|
|
106
|
+
for (const resolve4 of this.waitingQueue) {
|
|
104
107
|
}
|
|
105
108
|
this.waitingQueue = [];
|
|
106
109
|
for (const context of this.contexts) {
|
|
@@ -187,9 +190,9 @@ var BrowserPool = class {
|
|
|
187
190
|
* Wait for an available context.
|
|
188
191
|
*/
|
|
189
192
|
waitForAvailable() {
|
|
190
|
-
return new Promise((
|
|
193
|
+
return new Promise((resolve4, reject) => {
|
|
191
194
|
const timeout = setTimeout(() => {
|
|
192
|
-
const index = this.waitingQueue.indexOf(
|
|
195
|
+
const index = this.waitingQueue.indexOf(resolve4);
|
|
193
196
|
if (index > -1) {
|
|
194
197
|
this.waitingQueue.splice(index, 1);
|
|
195
198
|
}
|
|
@@ -202,7 +205,7 @@ var BrowserPool = class {
|
|
|
202
205
|
}, 3e4);
|
|
203
206
|
const wrappedResolve = (ctx) => {
|
|
204
207
|
clearTimeout(timeout);
|
|
205
|
-
|
|
208
|
+
resolve4(ctx);
|
|
206
209
|
};
|
|
207
210
|
this.waitingQueue.push(wrappedResolve);
|
|
208
211
|
});
|
|
@@ -285,16 +288,16 @@ var Timer = class {
|
|
|
285
288
|
}
|
|
286
289
|
};
|
|
287
290
|
function createDeferred() {
|
|
288
|
-
let
|
|
291
|
+
let resolve4;
|
|
289
292
|
let reject;
|
|
290
293
|
const promise = new Promise((res, rej) => {
|
|
291
|
-
|
|
294
|
+
resolve4 = res;
|
|
292
295
|
reject = rej;
|
|
293
296
|
});
|
|
294
|
-
return { promise, resolve:
|
|
297
|
+
return { promise, resolve: resolve4, reject };
|
|
295
298
|
}
|
|
296
299
|
function sleep(ms) {
|
|
297
|
-
return new Promise((
|
|
300
|
+
return new Promise((resolve4) => setTimeout(resolve4, ms));
|
|
298
301
|
}
|
|
299
302
|
function bufferToBase64Url(buffer, mimeType = "image/png") {
|
|
300
303
|
return `data:${mimeType};base64,${buffer.toString("base64")}`;
|
|
@@ -450,7 +453,7 @@ var CaptureEngine = class {
|
|
|
450
453
|
await page.evaluate(async (timeoutMs) => {
|
|
451
454
|
await Promise.race([
|
|
452
455
|
document.fonts.ready,
|
|
453
|
-
new Promise((
|
|
456
|
+
new Promise((resolve4) => setTimeout(resolve4, timeoutMs))
|
|
454
457
|
]);
|
|
455
458
|
}, timeout);
|
|
456
459
|
} catch {
|
|
@@ -3116,7 +3119,7 @@ var TokenRegistryManager = class {
|
|
|
3116
3119
|
}
|
|
3117
3120
|
if (theme) {
|
|
3118
3121
|
candidates = candidates.filter(
|
|
3119
|
-
(
|
|
3122
|
+
(t4) => t4.theme === theme || t4.theme === "default"
|
|
3120
3123
|
);
|
|
3121
3124
|
}
|
|
3122
3125
|
if (isColor) {
|
|
@@ -3633,28 +3636,11 @@ import { parse } from "@babel/parser";
|
|
|
3633
3636
|
import traverse from "@babel/traverse";
|
|
3634
3637
|
import generate from "@babel/generator";
|
|
3635
3638
|
import * as t from "@babel/types";
|
|
3636
|
-
var PARSER_OPTIONS = {
|
|
3637
|
-
sourceType: "module",
|
|
3638
|
-
plugins: [
|
|
3639
|
-
"jsx",
|
|
3640
|
-
"typescript",
|
|
3641
|
-
["decorators", { decoratorsBeforeExport: true }],
|
|
3642
|
-
"classProperties",
|
|
3643
|
-
"classPrivateProperties",
|
|
3644
|
-
"classPrivateMethods",
|
|
3645
|
-
"exportDefaultFrom",
|
|
3646
|
-
"exportNamespaceFrom",
|
|
3647
|
-
"dynamicImport",
|
|
3648
|
-
"nullishCoalescingOperator",
|
|
3649
|
-
"optionalChaining",
|
|
3650
|
-
"objectRestSpread"
|
|
3651
|
-
]
|
|
3652
|
-
};
|
|
3653
3639
|
function extractStyleLocations(sourceCode, filePath) {
|
|
3654
3640
|
const locations = [];
|
|
3655
3641
|
let ast;
|
|
3656
3642
|
try {
|
|
3657
|
-
ast = parse(sourceCode,
|
|
3643
|
+
ast = parse(sourceCode, BABEL_PARSER_OPTIONS);
|
|
3658
3644
|
} catch (error) {
|
|
3659
3645
|
console.error(`Failed to parse ${filePath}:`, error);
|
|
3660
3646
|
return locations;
|
|
@@ -3760,7 +3746,7 @@ function extractFromTemplateLiteral(template, path, filePath, type, locations) {
|
|
|
3760
3746
|
function applyPatch(sourceCode, styleLocation, oldValue, newValue) {
|
|
3761
3747
|
let ast;
|
|
3762
3748
|
try {
|
|
3763
|
-
ast = parse(sourceCode,
|
|
3749
|
+
ast = parse(sourceCode, BABEL_PARSER_OPTIONS);
|
|
3764
3750
|
} catch (error) {
|
|
3765
3751
|
return {
|
|
3766
3752
|
success: false,
|
|
@@ -4042,907 +4028,22 @@ function createMetricsStore(projectRoot) {
|
|
|
4042
4028
|
return new MetricsStore(projectRoot);
|
|
4043
4029
|
}
|
|
4044
4030
|
|
|
4045
|
-
// src/service/enhance/
|
|
4031
|
+
// src/service/enhance/doc-extractor.ts
|
|
4046
4032
|
import { parse as parse2 } from "@babel/parser";
|
|
4047
4033
|
import _traverse from "@babel/traverse";
|
|
4048
4034
|
import * as t2 from "@babel/types";
|
|
4049
4035
|
import { readFile as readFile4 } from "fs/promises";
|
|
4050
|
-
var traverse2 = _traverse.default || _traverse;
|
|
4051
|
-
var PARSER_OPTIONS2 = {
|
|
4052
|
-
sourceType: "module",
|
|
4053
|
-
plugins: [
|
|
4054
|
-
"jsx",
|
|
4055
|
-
"typescript",
|
|
4056
|
-
["decorators", { decoratorsBeforeExport: true }],
|
|
4057
|
-
"classProperties",
|
|
4058
|
-
"classPrivateProperties",
|
|
4059
|
-
"classPrivateMethods",
|
|
4060
|
-
"exportDefaultFrom",
|
|
4061
|
-
"exportNamespaceFrom",
|
|
4062
|
-
"dynamicImport",
|
|
4063
|
-
"nullishCoalescingOperator",
|
|
4064
|
-
"optionalChaining",
|
|
4065
|
-
"objectRestSpread"
|
|
4066
|
-
]
|
|
4067
|
-
};
|
|
4068
|
-
async function scanFileForImports(filePath) {
|
|
4069
|
-
const imports = [];
|
|
4070
|
-
try {
|
|
4071
|
-
const sourceCode = await readFile4(filePath, "utf-8");
|
|
4072
|
-
const ast = parse2(sourceCode, PARSER_OPTIONS2);
|
|
4073
|
-
traverse2(ast, {
|
|
4074
|
-
ImportDeclaration(path) {
|
|
4075
|
-
const source = path.node.source.value;
|
|
4076
|
-
const line = path.node.loc?.start.line ?? 0;
|
|
4077
|
-
for (const specifier of path.node.specifiers) {
|
|
4078
|
-
if (t2.isImportSpecifier(specifier)) {
|
|
4079
|
-
const importedName = t2.isIdentifier(specifier.imported) ? specifier.imported.name : specifier.imported.value;
|
|
4080
|
-
const localName = specifier.local.name;
|
|
4081
|
-
if (isPascalCase(importedName)) {
|
|
4082
|
-
imports.push({
|
|
4083
|
-
componentName: importedName,
|
|
4084
|
-
localName,
|
|
4085
|
-
source,
|
|
4086
|
-
isDefault: false,
|
|
4087
|
-
filePath,
|
|
4088
|
-
line
|
|
4089
|
-
});
|
|
4090
|
-
}
|
|
4091
|
-
} else if (t2.isImportDefaultSpecifier(specifier)) {
|
|
4092
|
-
const localName = specifier.local.name;
|
|
4093
|
-
if (isPascalCase(localName)) {
|
|
4094
|
-
imports.push({
|
|
4095
|
-
componentName: localName,
|
|
4096
|
-
localName,
|
|
4097
|
-
source,
|
|
4098
|
-
isDefault: true,
|
|
4099
|
-
filePath,
|
|
4100
|
-
line
|
|
4101
|
-
});
|
|
4102
|
-
}
|
|
4103
|
-
}
|
|
4104
|
-
}
|
|
4105
|
-
}
|
|
4106
|
-
});
|
|
4107
|
-
} catch (error) {
|
|
4108
|
-
console.warn(`Failed to parse ${filePath}:`, error.message);
|
|
4109
|
-
return [];
|
|
4110
|
-
}
|
|
4111
|
-
return imports;
|
|
4112
|
-
}
|
|
4113
|
-
async function scanFileForUsages(filePath, componentNames) {
|
|
4114
|
-
const usages = [];
|
|
4115
|
-
try {
|
|
4116
|
-
const sourceCode = await readFile4(filePath, "utf-8");
|
|
4117
|
-
const lines = sourceCode.split("\n");
|
|
4118
|
-
const ast = parse2(sourceCode, PARSER_OPTIONS2);
|
|
4119
|
-
traverse2(ast, {
|
|
4120
|
-
JSXOpeningElement(path) {
|
|
4121
|
-
const elementName = getJSXElementName(path.node.name);
|
|
4122
|
-
if (!elementName) return;
|
|
4123
|
-
const componentName = componentNames.get(elementName);
|
|
4124
|
-
if (!componentName) return;
|
|
4125
|
-
const loc = path.node.loc;
|
|
4126
|
-
if (!loc) return;
|
|
4127
|
-
const props = extractProps(path.node.attributes);
|
|
4128
|
-
const contextLines = getContextLines(lines, loc.start.line - 1, 3);
|
|
4129
|
-
const isConditional = checkIfConditional(path);
|
|
4130
|
-
const parentElement = getParentElementName(path);
|
|
4131
|
-
usages.push({
|
|
4132
|
-
componentName,
|
|
4133
|
-
filePath,
|
|
4134
|
-
line: loc.start.line,
|
|
4135
|
-
column: loc.start.column,
|
|
4136
|
-
props,
|
|
4137
|
-
context: contextLines,
|
|
4138
|
-
parentElement,
|
|
4139
|
-
hasSpreadProps: props.spreads.length > 0,
|
|
4140
|
-
isConditional
|
|
4141
|
-
});
|
|
4142
|
-
}
|
|
4143
|
-
});
|
|
4144
|
-
} catch (error) {
|
|
4145
|
-
console.warn(`Failed to parse ${filePath}:`, error.message);
|
|
4146
|
-
return [];
|
|
4147
|
-
}
|
|
4148
|
-
return usages;
|
|
4149
|
-
}
|
|
4150
|
-
async function scanFile(filePath, trackedComponents) {
|
|
4151
|
-
const imports = [];
|
|
4152
|
-
const usages = [];
|
|
4153
|
-
try {
|
|
4154
|
-
const sourceCode = await readFile4(filePath, "utf-8");
|
|
4155
|
-
const lines = sourceCode.split("\n");
|
|
4156
|
-
const ast = parse2(sourceCode, PARSER_OPTIONS2);
|
|
4157
|
-
const localToComponent = /* @__PURE__ */ new Map();
|
|
4158
|
-
traverse2(ast, {
|
|
4159
|
-
ImportDeclaration(path) {
|
|
4160
|
-
const source = path.node.source.value;
|
|
4161
|
-
const line = path.node.loc?.start.line ?? 0;
|
|
4162
|
-
for (const specifier of path.node.specifiers) {
|
|
4163
|
-
if (t2.isImportSpecifier(specifier)) {
|
|
4164
|
-
const importedName = t2.isIdentifier(specifier.imported) ? specifier.imported.name : specifier.imported.value;
|
|
4165
|
-
const localName = specifier.local.name;
|
|
4166
|
-
if (isPascalCase(importedName)) {
|
|
4167
|
-
if (!trackedComponents || trackedComponents.has(importedName)) {
|
|
4168
|
-
imports.push({
|
|
4169
|
-
componentName: importedName,
|
|
4170
|
-
localName,
|
|
4171
|
-
source,
|
|
4172
|
-
isDefault: false,
|
|
4173
|
-
filePath,
|
|
4174
|
-
line
|
|
4175
|
-
});
|
|
4176
|
-
localToComponent.set(localName, importedName);
|
|
4177
|
-
}
|
|
4178
|
-
}
|
|
4179
|
-
} else if (t2.isImportDefaultSpecifier(specifier)) {
|
|
4180
|
-
const localName = specifier.local.name;
|
|
4181
|
-
if (isPascalCase(localName)) {
|
|
4182
|
-
if (!trackedComponents || trackedComponents.has(localName)) {
|
|
4183
|
-
imports.push({
|
|
4184
|
-
componentName: localName,
|
|
4185
|
-
localName,
|
|
4186
|
-
source,
|
|
4187
|
-
isDefault: true,
|
|
4188
|
-
filePath,
|
|
4189
|
-
line
|
|
4190
|
-
});
|
|
4191
|
-
localToComponent.set(localName, localName);
|
|
4192
|
-
}
|
|
4193
|
-
}
|
|
4194
|
-
}
|
|
4195
|
-
}
|
|
4196
|
-
},
|
|
4197
|
-
JSXOpeningElement(path) {
|
|
4198
|
-
const elementName = getJSXElementName(path.node.name);
|
|
4199
|
-
if (!elementName) return;
|
|
4200
|
-
const componentName = localToComponent.get(elementName);
|
|
4201
|
-
if (!componentName) return;
|
|
4202
|
-
const loc = path.node.loc;
|
|
4203
|
-
if (!loc) return;
|
|
4204
|
-
const props = extractProps(path.node.attributes);
|
|
4205
|
-
const contextLines = getContextLines(lines, loc.start.line - 1, 3);
|
|
4206
|
-
const isConditional = checkIfConditional(path);
|
|
4207
|
-
const parentElement = getParentElementName(path);
|
|
4208
|
-
usages.push({
|
|
4209
|
-
componentName,
|
|
4210
|
-
filePath,
|
|
4211
|
-
line: loc.start.line,
|
|
4212
|
-
column: loc.start.column,
|
|
4213
|
-
props,
|
|
4214
|
-
context: contextLines,
|
|
4215
|
-
parentElement,
|
|
4216
|
-
hasSpreadProps: props.spreads.length > 0,
|
|
4217
|
-
isConditional
|
|
4218
|
-
});
|
|
4219
|
-
}
|
|
4220
|
-
});
|
|
4221
|
-
} catch (error) {
|
|
4222
|
-
console.warn(`Failed to parse ${filePath}:`, error.message);
|
|
4223
|
-
return { imports: [], usages: [] };
|
|
4224
|
-
}
|
|
4225
|
-
return { imports, usages };
|
|
4226
|
-
}
|
|
4227
|
-
function isPascalCase(str) {
|
|
4228
|
-
return /^[A-Z][a-zA-Z0-9]*$/.test(str);
|
|
4229
|
-
}
|
|
4230
|
-
function getJSXElementName(name) {
|
|
4231
|
-
if (t2.isJSXIdentifier(name)) {
|
|
4232
|
-
return name.name;
|
|
4233
|
-
}
|
|
4234
|
-
if (t2.isJSXMemberExpression(name)) {
|
|
4235
|
-
const parts = [];
|
|
4236
|
-
let current = name;
|
|
4237
|
-
while (t2.isJSXMemberExpression(current)) {
|
|
4238
|
-
parts.unshift(current.property.name);
|
|
4239
|
-
current = current.object;
|
|
4240
|
-
}
|
|
4241
|
-
if (t2.isJSXIdentifier(current)) {
|
|
4242
|
-
parts.unshift(current.name);
|
|
4243
|
-
}
|
|
4244
|
-
return parts.join(".");
|
|
4245
|
-
}
|
|
4246
|
-
return null;
|
|
4247
|
-
}
|
|
4248
|
-
function extractProps(attributes) {
|
|
4249
|
-
const result = {
|
|
4250
|
-
static: {},
|
|
4251
|
-
dynamic: [],
|
|
4252
|
-
spreads: []
|
|
4253
|
-
};
|
|
4254
|
-
for (const attr of attributes) {
|
|
4255
|
-
if (t2.isJSXSpreadAttribute(attr)) {
|
|
4256
|
-
if (t2.isIdentifier(attr.argument)) {
|
|
4257
|
-
result.spreads.push(attr.argument.name);
|
|
4258
|
-
} else {
|
|
4259
|
-
result.spreads.push("(expression)");
|
|
4260
|
-
}
|
|
4261
|
-
} else if (t2.isJSXAttribute(attr)) {
|
|
4262
|
-
const propName = t2.isJSXIdentifier(attr.name) ? attr.name.name : `${attr.name.namespace.name}:${attr.name.name.name}`;
|
|
4263
|
-
const value = attr.value;
|
|
4264
|
-
if (value === null) {
|
|
4265
|
-
result.static[propName] = true;
|
|
4266
|
-
} else if (t2.isStringLiteral(value)) {
|
|
4267
|
-
result.static[propName] = value.value;
|
|
4268
|
-
} else if (t2.isJSXExpressionContainer(value)) {
|
|
4269
|
-
const expr = value.expression;
|
|
4270
|
-
if (t2.isStringLiteral(expr)) {
|
|
4271
|
-
result.static[propName] = expr.value;
|
|
4272
|
-
} else if (t2.isNumericLiteral(expr)) {
|
|
4273
|
-
result.static[propName] = expr.value;
|
|
4274
|
-
} else if (t2.isBooleanLiteral(expr)) {
|
|
4275
|
-
result.static[propName] = expr.value;
|
|
4276
|
-
} else if (t2.isTemplateLiteral(expr) && expr.expressions.length === 0) {
|
|
4277
|
-
result.static[propName] = expr.quasis.map((q) => q.value.raw).join("");
|
|
4278
|
-
} else {
|
|
4279
|
-
result.dynamic.push(propName);
|
|
4280
|
-
}
|
|
4281
|
-
}
|
|
4282
|
-
}
|
|
4283
|
-
}
|
|
4284
|
-
return result;
|
|
4285
|
-
}
|
|
4286
|
-
function getContextLines(lines, centerLine, radius) {
|
|
4287
|
-
const start = Math.max(0, centerLine - radius);
|
|
4288
|
-
const end = Math.min(lines.length, centerLine + radius + 1);
|
|
4289
|
-
return lines.slice(start, end).join("\n");
|
|
4290
|
-
}
|
|
4291
|
-
function checkIfConditional(path) {
|
|
4292
|
-
let current = path.parentPath;
|
|
4293
|
-
while (current) {
|
|
4294
|
-
if (current.isLogicalExpression() && current.node.operator === "&&") {
|
|
4295
|
-
return true;
|
|
4296
|
-
}
|
|
4297
|
-
if (current.isConditionalExpression()) {
|
|
4298
|
-
return true;
|
|
4299
|
-
}
|
|
4300
|
-
if (current.isJSXExpressionContainer() && current.parentPath?.isJSXElement()) {
|
|
4301
|
-
const expr = current.node.expression;
|
|
4302
|
-
if (t2.isLogicalExpression(expr) || t2.isConditionalExpression(expr)) {
|
|
4303
|
-
return true;
|
|
4304
|
-
}
|
|
4305
|
-
}
|
|
4306
|
-
current = current.parentPath;
|
|
4307
|
-
}
|
|
4308
|
-
return false;
|
|
4309
|
-
}
|
|
4310
|
-
function getParentElementName(path) {
|
|
4311
|
-
let current = path.parentPath;
|
|
4312
|
-
const currentElementName = getJSXElementName(path.node.name);
|
|
4313
|
-
while (current) {
|
|
4314
|
-
if (current.isJSXElement()) {
|
|
4315
|
-
const opening = current.node.openingElement;
|
|
4316
|
-
const name = getJSXElementName(opening.name);
|
|
4317
|
-
if (name && name !== currentElementName) {
|
|
4318
|
-
return name;
|
|
4319
|
-
}
|
|
4320
|
-
}
|
|
4321
|
-
current = current.parentPath;
|
|
4322
|
-
}
|
|
4323
|
-
return void 0;
|
|
4324
|
-
}
|
|
4325
|
-
|
|
4326
|
-
// src/service/enhance/aggregator.ts
|
|
4327
|
-
function aggregateComponentUsages(componentName, sourceFile, usages, imports) {
|
|
4328
|
-
const uniqueFiles = new Set(usages.map((u) => u.filePath));
|
|
4329
|
-
const patternMap = /* @__PURE__ */ new Map();
|
|
4330
|
-
for (const usage of usages) {
|
|
4331
|
-
const propKey = createPropKey(usage.props.static);
|
|
4332
|
-
const existing = patternMap.get(propKey);
|
|
4333
|
-
if (existing) {
|
|
4334
|
-
existing.count++;
|
|
4335
|
-
if (!existing.files.includes(usage.filePath)) {
|
|
4336
|
-
existing.files.push(usage.filePath);
|
|
4337
|
-
}
|
|
4338
|
-
if (existing.sampleContexts.length < 3) {
|
|
4339
|
-
existing.sampleContexts.push(usage.context);
|
|
4340
|
-
}
|
|
4341
|
-
} else {
|
|
4342
|
-
patternMap.set(propKey, {
|
|
4343
|
-
props: { ...usage.props.static },
|
|
4344
|
-
count: 1,
|
|
4345
|
-
files: [usage.filePath],
|
|
4346
|
-
sampleContexts: [usage.context]
|
|
4347
|
-
});
|
|
4348
|
-
}
|
|
4349
|
-
}
|
|
4350
|
-
const patterns = Array.from(patternMap.values()).sort(
|
|
4351
|
-
(a, b) => b.count - a.count
|
|
4352
|
-
);
|
|
4353
|
-
const contextMap = /* @__PURE__ */ new Map();
|
|
4354
|
-
for (const usage of usages) {
|
|
4355
|
-
const context = classifyFileContext(usage.filePath, usage.context);
|
|
4356
|
-
const existing = contextMap.get(context);
|
|
4357
|
-
if (existing) {
|
|
4358
|
-
existing.count++;
|
|
4359
|
-
if (!existing.files.includes(usage.filePath)) {
|
|
4360
|
-
existing.files.push(usage.filePath);
|
|
4361
|
-
}
|
|
4362
|
-
} else {
|
|
4363
|
-
contextMap.set(context, { count: 1, files: [usage.filePath] });
|
|
4364
|
-
}
|
|
4365
|
-
}
|
|
4366
|
-
const contexts = Array.from(contextMap.entries()).map(([type, data]) => ({ type, ...data })).sort((a, b) => b.count - a.count);
|
|
4367
|
-
return {
|
|
4368
|
-
name: componentName,
|
|
4369
|
-
sourceFile,
|
|
4370
|
-
totalUsages: usages.length,
|
|
4371
|
-
uniqueFiles: uniqueFiles.size,
|
|
4372
|
-
patterns,
|
|
4373
|
-
contexts,
|
|
4374
|
-
usages,
|
|
4375
|
-
imports
|
|
4376
|
-
};
|
|
4377
|
-
}
|
|
4378
|
-
function aggregateAllUsages(usages, imports, componentSources) {
|
|
4379
|
-
const usagesByComponent = /* @__PURE__ */ new Map();
|
|
4380
|
-
for (const usage of usages) {
|
|
4381
|
-
const existing = usagesByComponent.get(usage.componentName) ?? [];
|
|
4382
|
-
existing.push(usage);
|
|
4383
|
-
usagesByComponent.set(usage.componentName, existing);
|
|
4384
|
-
}
|
|
4385
|
-
const importsByComponent = /* @__PURE__ */ new Map();
|
|
4386
|
-
for (const imp of imports) {
|
|
4387
|
-
const existing = importsByComponent.get(imp.componentName) ?? [];
|
|
4388
|
-
existing.push(imp);
|
|
4389
|
-
importsByComponent.set(imp.componentName, existing);
|
|
4390
|
-
}
|
|
4391
|
-
const components = {};
|
|
4392
|
-
const allComponentNames = /* @__PURE__ */ new Set([
|
|
4393
|
-
...usagesByComponent.keys(),
|
|
4394
|
-
...importsByComponent.keys()
|
|
4395
|
-
]);
|
|
4396
|
-
for (const componentName of allComponentNames) {
|
|
4397
|
-
const componentUsages = usagesByComponent.get(componentName) ?? [];
|
|
4398
|
-
const componentImports = importsByComponent.get(componentName) ?? [];
|
|
4399
|
-
const sourceFile = componentSources.get(componentName) ?? "unknown";
|
|
4400
|
-
components[componentName] = aggregateComponentUsages(
|
|
4401
|
-
componentName,
|
|
4402
|
-
sourceFile,
|
|
4403
|
-
componentUsages,
|
|
4404
|
-
componentImports
|
|
4405
|
-
);
|
|
4406
|
-
}
|
|
4407
|
-
const allFiles = /* @__PURE__ */ new Set();
|
|
4408
|
-
for (const usage of usages) {
|
|
4409
|
-
allFiles.add(usage.filePath);
|
|
4410
|
-
}
|
|
4411
|
-
for (const imp of imports) {
|
|
4412
|
-
allFiles.add(imp.filePath);
|
|
4413
|
-
}
|
|
4414
|
-
return {
|
|
4415
|
-
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
4416
|
-
rootDir: "",
|
|
4417
|
-
// Will be set by caller
|
|
4418
|
-
totalFiles: allFiles.size,
|
|
4419
|
-
totalComponents: Object.keys(components).length,
|
|
4420
|
-
components,
|
|
4421
|
-
errorFiles: []
|
|
4422
|
-
// Will be populated by scanner
|
|
4423
|
-
};
|
|
4424
|
-
}
|
|
4425
|
-
function createPropKey(props) {
|
|
4426
|
-
const entries = Object.entries(props).sort(([a], [b]) => a.localeCompare(b));
|
|
4427
|
-
return entries.map(([k, v]) => `${k}=${JSON.stringify(v)}`).join("|");
|
|
4428
|
-
}
|
|
4429
|
-
function classifyFileContext(filePath, context) {
|
|
4430
|
-
const lowerPath = filePath.toLowerCase();
|
|
4431
|
-
const lowerContext = context.toLowerCase();
|
|
4432
|
-
if (lowerPath.includes("/modal") || lowerPath.includes("modal.") || lowerContext.includes("<modal")) {
|
|
4433
|
-
return "modal";
|
|
4434
|
-
}
|
|
4435
|
-
if (lowerPath.includes("/dialog") || lowerPath.includes("dialog.") || lowerContext.includes("<dialog")) {
|
|
4436
|
-
return "dialog";
|
|
4437
|
-
}
|
|
4438
|
-
if (lowerPath.includes("/form") || lowerPath.includes("form.") || lowerContext.includes("<form") || lowerContext.includes("onsubmit")) {
|
|
4439
|
-
return "form";
|
|
4440
|
-
}
|
|
4441
|
-
if (lowerPath.includes("/page") || lowerPath.includes("pages/") || lowerPath.includes("/routes/")) {
|
|
4442
|
-
return "page";
|
|
4443
|
-
}
|
|
4444
|
-
if (lowerPath.includes("/layout") || lowerPath.includes("layout.") || lowerPath.includes("/layouts/")) {
|
|
4445
|
-
return "layout";
|
|
4446
|
-
}
|
|
4447
|
-
if (lowerPath.includes("/card") || lowerPath.includes("card.") || lowerContext.includes("<card")) {
|
|
4448
|
-
return "card";
|
|
4449
|
-
}
|
|
4450
|
-
if (lowerPath.includes("/table") || lowerPath.includes("table.") || lowerContext.includes("<table") || lowerContext.includes("<tr")) {
|
|
4451
|
-
return "table";
|
|
4452
|
-
}
|
|
4453
|
-
if (lowerPath.includes("/list") || lowerPath.includes("list.") || lowerContext.includes("<ul") || lowerContext.includes("<ol") || lowerContext.includes("<li")) {
|
|
4454
|
-
return "list";
|
|
4455
|
-
}
|
|
4456
|
-
if (lowerPath.includes("/nav") || lowerPath.includes("navigation") || lowerContext.includes("<nav")) {
|
|
4457
|
-
return "navigation";
|
|
4458
|
-
}
|
|
4459
|
-
if (lowerPath.includes("/header") || lowerPath.includes("header.") || lowerContext.includes("<header")) {
|
|
4460
|
-
return "header";
|
|
4461
|
-
}
|
|
4462
|
-
if (lowerPath.includes("/footer") || lowerPath.includes("footer.") || lowerContext.includes("<footer")) {
|
|
4463
|
-
return "footer";
|
|
4464
|
-
}
|
|
4465
|
-
if (lowerPath.includes("/sidebar") || lowerPath.includes("sidebar.") || lowerContext.includes("<aside")) {
|
|
4466
|
-
return "sidebar";
|
|
4467
|
-
}
|
|
4468
|
-
return "unknown";
|
|
4469
|
-
}
|
|
4470
|
-
function findCommonPropCombinations(usages, minOccurrences = 3) {
|
|
4471
|
-
const combinations = /* @__PURE__ */ new Map();
|
|
4472
|
-
for (const usage of usages) {
|
|
4473
|
-
const allProps = [
|
|
4474
|
-
...Object.keys(usage.props.static),
|
|
4475
|
-
...usage.props.dynamic
|
|
4476
|
-
].sort();
|
|
4477
|
-
for (let size = 2; size <= Math.min(allProps.length, 4); size++) {
|
|
4478
|
-
for (const combo of combinations_k(allProps, size)) {
|
|
4479
|
-
const key = combo.join(",");
|
|
4480
|
-
combinations.set(key, (combinations.get(key) ?? 0) + 1);
|
|
4481
|
-
}
|
|
4482
|
-
}
|
|
4483
|
-
}
|
|
4484
|
-
return Array.from(combinations.entries()).filter(([, count]) => count >= minOccurrences).map(([key, count]) => ({ props: key.split(","), count })).sort((a, b) => b.count - a.count);
|
|
4485
|
-
}
|
|
4486
|
-
function* combinations_k(arr, k) {
|
|
4487
|
-
if (k === 0) {
|
|
4488
|
-
yield [];
|
|
4489
|
-
return;
|
|
4490
|
-
}
|
|
4491
|
-
if (arr.length < k) return;
|
|
4492
|
-
for (let i = 0; i <= arr.length - k; i++) {
|
|
4493
|
-
for (const combo of combinations_k(arr.slice(i + 1), k - 1)) {
|
|
4494
|
-
yield [arr[i], ...combo];
|
|
4495
|
-
}
|
|
4496
|
-
}
|
|
4497
|
-
}
|
|
4498
|
-
function inferRelations(componentName, analysis, allAnalyses) {
|
|
4499
|
-
const relations = [];
|
|
4500
|
-
const parentCounts = /* @__PURE__ */ new Map();
|
|
4501
|
-
const siblingCounts = /* @__PURE__ */ new Map();
|
|
4502
|
-
for (const usage of analysis.usages) {
|
|
4503
|
-
if (usage.parentElement && usage.parentElement !== "div" && usage.parentElement !== "span") {
|
|
4504
|
-
const parent = usage.parentElement;
|
|
4505
|
-
const existing = parentCounts.get(parent);
|
|
4506
|
-
if (existing) {
|
|
4507
|
-
existing.count++;
|
|
4508
|
-
if (!existing.files.includes(usage.filePath)) {
|
|
4509
|
-
existing.files.push(usage.filePath);
|
|
4510
|
-
}
|
|
4511
|
-
} else {
|
|
4512
|
-
parentCounts.set(parent, { count: 1, files: [usage.filePath] });
|
|
4513
|
-
}
|
|
4514
|
-
}
|
|
4515
|
-
}
|
|
4516
|
-
const filesWithThisComponent = new Set(analysis.usages.map((u) => u.filePath));
|
|
4517
|
-
for (const [otherName, otherAnalysis] of Object.entries(allAnalyses)) {
|
|
4518
|
-
if (otherName === componentName) continue;
|
|
4519
|
-
let sharedFiles = 0;
|
|
4520
|
-
const sharedFileList = [];
|
|
4521
|
-
for (const usage of otherAnalysis.usages) {
|
|
4522
|
-
if (filesWithThisComponent.has(usage.filePath)) {
|
|
4523
|
-
if (!sharedFileList.includes(usage.filePath)) {
|
|
4524
|
-
sharedFiles++;
|
|
4525
|
-
sharedFileList.push(usage.filePath);
|
|
4526
|
-
}
|
|
4527
|
-
}
|
|
4528
|
-
}
|
|
4529
|
-
if (sharedFiles >= 3) {
|
|
4530
|
-
siblingCounts.set(otherName, {
|
|
4531
|
-
count: sharedFiles,
|
|
4532
|
-
files: sharedFileList.slice(0, 5)
|
|
4533
|
-
});
|
|
4534
|
-
}
|
|
4535
|
-
}
|
|
4536
|
-
for (const [parent, data] of parentCounts) {
|
|
4537
|
-
if (data.count >= 2) {
|
|
4538
|
-
relations.push({
|
|
4539
|
-
component: parent,
|
|
4540
|
-
relationship: "parent",
|
|
4541
|
-
frequency: data.count,
|
|
4542
|
-
sampleFiles: data.files.slice(0, 3)
|
|
4543
|
-
});
|
|
4544
|
-
}
|
|
4545
|
-
}
|
|
4546
|
-
for (const [sibling, data] of siblingCounts) {
|
|
4547
|
-
relations.push({
|
|
4548
|
-
component: sibling,
|
|
4549
|
-
relationship: "sibling",
|
|
4550
|
-
frequency: data.count,
|
|
4551
|
-
sampleFiles: data.files.slice(0, 3)
|
|
4552
|
-
});
|
|
4553
|
-
}
|
|
4554
|
-
relations.sort((a, b) => b.frequency - a.frequency);
|
|
4555
|
-
return relations;
|
|
4556
|
-
}
|
|
4557
|
-
function inferAllRelations(analysis) {
|
|
4558
|
-
const allRelations = /* @__PURE__ */ new Map();
|
|
4559
|
-
for (const [componentName, componentAnalysis] of Object.entries(analysis.components)) {
|
|
4560
|
-
const relations = inferRelations(componentName, componentAnalysis, analysis.components);
|
|
4561
|
-
if (relations.length > 0) {
|
|
4562
|
-
allRelations.set(componentName, relations);
|
|
4563
|
-
}
|
|
4564
|
-
}
|
|
4565
|
-
return allRelations;
|
|
4566
|
-
}
|
|
4567
|
-
function summarizePatternsForPrompt(analysis, maxPatterns = 10, maxContexts = 5) {
|
|
4568
|
-
const lines = [];
|
|
4569
|
-
lines.push(`## ${analysis.name}`);
|
|
4570
|
-
lines.push(`Total usages: ${analysis.totalUsages} across ${analysis.uniqueFiles} files`);
|
|
4571
|
-
lines.push("");
|
|
4572
|
-
lines.push("### Usage Patterns (by frequency)");
|
|
4573
|
-
const topPatterns = analysis.patterns.slice(0, maxPatterns);
|
|
4574
|
-
for (const pattern of topPatterns) {
|
|
4575
|
-
const propsStr = Object.entries(pattern.props).map(([k, v]) => `${k}=${JSON.stringify(v)}`).join(", ");
|
|
4576
|
-
lines.push(`- ${propsStr || "(no props)"}: ${pattern.count} usages in ${pattern.files.length} files`);
|
|
4577
|
-
}
|
|
4578
|
-
lines.push("");
|
|
4579
|
-
lines.push("### File Contexts");
|
|
4580
|
-
const topContexts = analysis.contexts.slice(0, maxContexts);
|
|
4581
|
-
for (const ctx of topContexts) {
|
|
4582
|
-
lines.push(`- ${ctx.type}: ${ctx.count} usages`);
|
|
4583
|
-
}
|
|
4584
|
-
lines.push("");
|
|
4585
|
-
lines.push("### Sample Usages");
|
|
4586
|
-
for (const pattern of topPatterns.slice(0, 3)) {
|
|
4587
|
-
if (pattern.sampleContexts[0]) {
|
|
4588
|
-
lines.push("```tsx");
|
|
4589
|
-
lines.push(pattern.sampleContexts[0].trim());
|
|
4590
|
-
lines.push("```");
|
|
4591
|
-
lines.push("");
|
|
4592
|
-
}
|
|
4593
|
-
}
|
|
4594
|
-
return lines.join("\n");
|
|
4595
|
-
}
|
|
4596
|
-
|
|
4597
|
-
// src/service/enhance/cache.ts
|
|
4598
|
-
import { readFile as readFile5, writeFile as writeFile3 } from "fs/promises";
|
|
4599
4036
|
import { existsSync as existsSync2 } from "fs";
|
|
4600
|
-
import {
|
|
4601
|
-
|
|
4602
|
-
import { mkdir as mkdir3 } from "fs/promises";
|
|
4603
|
-
var CURRENT_CACHE_VERSION = 1;
|
|
4604
|
-
var CACHE_FILENAME = "analysis-cache.json";
|
|
4605
|
-
function getCachePath(rootDir) {
|
|
4606
|
-
return join3(rootDir, ".fragments", CACHE_FILENAME);
|
|
4607
|
-
}
|
|
4608
|
-
async function loadCache(rootDir) {
|
|
4609
|
-
const cachePath = getCachePath(rootDir);
|
|
4610
|
-
if (!existsSync2(cachePath)) {
|
|
4611
|
-
return null;
|
|
4612
|
-
}
|
|
4613
|
-
try {
|
|
4614
|
-
const content = await readFile5(cachePath, "utf-8");
|
|
4615
|
-
const cache = JSON.parse(content);
|
|
4616
|
-
if (cache.version !== CURRENT_CACHE_VERSION) {
|
|
4617
|
-
console.warn(
|
|
4618
|
-
`Cache version mismatch: expected ${CURRENT_CACHE_VERSION}, got ${cache.version}. Invalidating cache.`
|
|
4619
|
-
);
|
|
4620
|
-
return null;
|
|
4621
|
-
}
|
|
4622
|
-
if (cache.rootDir !== rootDir) {
|
|
4623
|
-
console.warn(`Cache root mismatch. Invalidating cache.`);
|
|
4624
|
-
return null;
|
|
4625
|
-
}
|
|
4626
|
-
return cache;
|
|
4627
|
-
} catch (error) {
|
|
4628
|
-
console.warn(`Failed to load cache: ${error.message}`);
|
|
4629
|
-
return null;
|
|
4630
|
-
}
|
|
4631
|
-
}
|
|
4632
|
-
async function saveCache(rootDir, cache) {
|
|
4633
|
-
const cachePath = getCachePath(rootDir);
|
|
4634
|
-
const cacheDir = dirname3(cachePath);
|
|
4635
|
-
if (!existsSync2(cacheDir)) {
|
|
4636
|
-
await mkdir3(cacheDir, { recursive: true });
|
|
4637
|
-
}
|
|
4638
|
-
cache.updatedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
4639
|
-
await writeFile3(cachePath, JSON.stringify(cache, null, 2));
|
|
4640
|
-
}
|
|
4641
|
-
function createEmptyCache(rootDir) {
|
|
4642
|
-
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
4643
|
-
return {
|
|
4644
|
-
version: CURRENT_CACHE_VERSION,
|
|
4645
|
-
createdAt: now,
|
|
4646
|
-
updatedAt: now,
|
|
4647
|
-
rootDir,
|
|
4648
|
-
files: {}
|
|
4649
|
-
};
|
|
4650
|
-
}
|
|
4651
|
-
function computeFileHash(content) {
|
|
4652
|
-
return createHash2("md5").update(content).digest("hex");
|
|
4653
|
-
}
|
|
4654
|
-
function getCachedFile(cache, filePath) {
|
|
4655
|
-
return cache.files[filePath] ?? null;
|
|
4656
|
-
}
|
|
4657
|
-
function updateCacheFile(cache, filePath, hash, imports, usages) {
|
|
4658
|
-
cache.files[filePath] = {
|
|
4659
|
-
hash,
|
|
4660
|
-
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
4661
|
-
imports,
|
|
4662
|
-
usages
|
|
4663
|
-
};
|
|
4664
|
-
}
|
|
4665
|
-
function removeCacheFile(cache, filePath) {
|
|
4666
|
-
delete cache.files[filePath];
|
|
4667
|
-
}
|
|
4668
|
-
async function detectFileChanges(cache, currentFiles, getFileHash) {
|
|
4669
|
-
const changes = {
|
|
4670
|
-
added: [],
|
|
4671
|
-
modified: [],
|
|
4672
|
-
deleted: [],
|
|
4673
|
-
unchanged: []
|
|
4674
|
-
};
|
|
4675
|
-
const currentFileSet = new Set(currentFiles);
|
|
4676
|
-
const cachedFileSet = new Set(Object.keys(cache.files));
|
|
4677
|
-
for (const filePath of currentFiles) {
|
|
4678
|
-
const entry = cache.files[filePath];
|
|
4679
|
-
if (!entry) {
|
|
4680
|
-
changes.added.push(filePath);
|
|
4681
|
-
} else {
|
|
4682
|
-
const currentHash = await getFileHash(filePath);
|
|
4683
|
-
if (currentHash !== entry.hash) {
|
|
4684
|
-
changes.modified.push(filePath);
|
|
4685
|
-
} else {
|
|
4686
|
-
changes.unchanged.push(filePath);
|
|
4687
|
-
}
|
|
4688
|
-
}
|
|
4689
|
-
}
|
|
4690
|
-
for (const cachedFile of cachedFileSet) {
|
|
4691
|
-
if (!currentFileSet.has(cachedFile)) {
|
|
4692
|
-
changes.deleted.push(cachedFile);
|
|
4693
|
-
}
|
|
4694
|
-
}
|
|
4695
|
-
return changes;
|
|
4696
|
-
}
|
|
4697
|
-
function getCacheStats(cache) {
|
|
4698
|
-
let totalImports = 0;
|
|
4699
|
-
let totalUsages = 0;
|
|
4700
|
-
for (const entry of Object.values(cache.files)) {
|
|
4701
|
-
totalImports += entry.imports.length;
|
|
4702
|
-
totalUsages += entry.usages.length;
|
|
4703
|
-
}
|
|
4704
|
-
const ageMs = Date.now() - new Date(cache.updatedAt).getTime();
|
|
4705
|
-
const ageMinutes = Math.round(ageMs / 6e4);
|
|
4706
|
-
const cacheAge = ageMinutes < 60 ? `${ageMinutes} minutes ago` : ageMinutes < 1440 ? `${Math.round(ageMinutes / 60)} hours ago` : `${Math.round(ageMinutes / 1440)} days ago`;
|
|
4707
|
-
return {
|
|
4708
|
-
totalFiles: Object.keys(cache.files).length,
|
|
4709
|
-
totalImports,
|
|
4710
|
-
totalUsages,
|
|
4711
|
-
cacheAge
|
|
4712
|
-
};
|
|
4713
|
-
}
|
|
4714
|
-
|
|
4715
|
-
// src/service/enhance/codebase-scanner.ts
|
|
4716
|
-
import fg from "fast-glob";
|
|
4717
|
-
import { readFile as readFile6 } from "fs/promises";
|
|
4718
|
-
import { relative as relative2, resolve as resolve3, dirname as dirname4 } from "path";
|
|
4719
|
-
var DEFAULT_INCLUDE = [
|
|
4720
|
-
"**/*.tsx",
|
|
4721
|
-
"**/*.ts",
|
|
4722
|
-
"**/*.jsx",
|
|
4723
|
-
"**/*.js"
|
|
4724
|
-
];
|
|
4725
|
-
var DEFAULT_EXCLUDE = [
|
|
4726
|
-
"**/node_modules/**",
|
|
4727
|
-
"**/dist/**",
|
|
4728
|
-
"**/build/**",
|
|
4729
|
-
"**/.next/**",
|
|
4730
|
-
"**/coverage/**",
|
|
4731
|
-
"**/__tests__/**",
|
|
4732
|
-
"**/*.test.*",
|
|
4733
|
-
"**/*.spec.*",
|
|
4734
|
-
"**/*.stories.*",
|
|
4735
|
-
"**/*.fragment.*",
|
|
4736
|
-
"**/storybook-static/**"
|
|
4737
|
-
];
|
|
4738
|
-
async function scanCodebase(options) {
|
|
4739
|
-
const {
|
|
4740
|
-
rootDir,
|
|
4741
|
-
include = DEFAULT_INCLUDE,
|
|
4742
|
-
exclude = DEFAULT_EXCLUDE,
|
|
4743
|
-
componentNames,
|
|
4744
|
-
useCache = true,
|
|
4745
|
-
onProgress
|
|
4746
|
-
} = options;
|
|
4747
|
-
const absoluteRoot = resolve3(rootDir);
|
|
4748
|
-
let cache = null;
|
|
4749
|
-
if (useCache) {
|
|
4750
|
-
cache = await loadCache(absoluteRoot);
|
|
4751
|
-
}
|
|
4752
|
-
if (!cache) {
|
|
4753
|
-
cache = createEmptyCache(absoluteRoot);
|
|
4754
|
-
}
|
|
4755
|
-
onProgress?.({
|
|
4756
|
-
current: 0,
|
|
4757
|
-
total: 0,
|
|
4758
|
-
currentFile: "",
|
|
4759
|
-
phase: "discovering"
|
|
4760
|
-
});
|
|
4761
|
-
const files = await fg(include, {
|
|
4762
|
-
cwd: absoluteRoot,
|
|
4763
|
-
ignore: exclude,
|
|
4764
|
-
absolute: true,
|
|
4765
|
-
onlyFiles: true
|
|
4766
|
-
});
|
|
4767
|
-
const totalFiles = files.length;
|
|
4768
|
-
const trackedComponents = componentNames ? new Set(componentNames) : void 0;
|
|
4769
|
-
const allImports = [];
|
|
4770
|
-
const allUsages = [];
|
|
4771
|
-
const errorFiles = [];
|
|
4772
|
-
const componentSources = /* @__PURE__ */ new Map();
|
|
4773
|
-
for (let i = 0; i < files.length; i++) {
|
|
4774
|
-
const filePath = files[i];
|
|
4775
|
-
const relativePath = relative2(absoluteRoot, filePath);
|
|
4776
|
-
onProgress?.({
|
|
4777
|
-
current: i + 1,
|
|
4778
|
-
total: totalFiles,
|
|
4779
|
-
currentFile: relativePath,
|
|
4780
|
-
phase: "scanning"
|
|
4781
|
-
});
|
|
4782
|
-
try {
|
|
4783
|
-
const content = await readFile6(filePath, "utf-8");
|
|
4784
|
-
const fileHash = computeFileHash(content);
|
|
4785
|
-
const cachedEntry = getCachedFile(cache, filePath);
|
|
4786
|
-
if (cachedEntry && cachedEntry.hash === fileHash) {
|
|
4787
|
-
allImports.push(...cachedEntry.imports);
|
|
4788
|
-
allUsages.push(...cachedEntry.usages);
|
|
4789
|
-
for (const imp of cachedEntry.imports) {
|
|
4790
|
-
if (!componentSources.has(imp.componentName)) {
|
|
4791
|
-
const sourceFile = resolveImportSource(filePath, imp.source);
|
|
4792
|
-
if (sourceFile) {
|
|
4793
|
-
componentSources.set(imp.componentName, sourceFile);
|
|
4794
|
-
}
|
|
4795
|
-
}
|
|
4796
|
-
}
|
|
4797
|
-
continue;
|
|
4798
|
-
}
|
|
4799
|
-
const { imports, usages } = await scanFile(filePath, trackedComponents);
|
|
4800
|
-
updateCacheFile(cache, filePath, fileHash, imports, usages);
|
|
4801
|
-
allImports.push(...imports);
|
|
4802
|
-
allUsages.push(...usages);
|
|
4803
|
-
for (const imp of imports) {
|
|
4804
|
-
if (!componentSources.has(imp.componentName)) {
|
|
4805
|
-
const sourceFile = resolveImportSource(filePath, imp.source);
|
|
4806
|
-
if (sourceFile) {
|
|
4807
|
-
componentSources.set(imp.componentName, sourceFile);
|
|
4808
|
-
}
|
|
4809
|
-
}
|
|
4810
|
-
}
|
|
4811
|
-
} catch (error) {
|
|
4812
|
-
errorFiles.push(relativePath);
|
|
4813
|
-
console.warn(`Error scanning ${relativePath}:`, error.message);
|
|
4814
|
-
}
|
|
4815
|
-
}
|
|
4816
|
-
if (useCache) {
|
|
4817
|
-
await saveCache(absoluteRoot, cache);
|
|
4818
|
-
}
|
|
4819
|
-
onProgress?.({
|
|
4820
|
-
current: totalFiles,
|
|
4821
|
-
total: totalFiles,
|
|
4822
|
-
currentFile: "",
|
|
4823
|
-
phase: "aggregating"
|
|
4824
|
-
});
|
|
4825
|
-
const analysis = aggregateAllUsages(allUsages, allImports, componentSources);
|
|
4826
|
-
analysis.rootDir = absoluteRoot;
|
|
4827
|
-
analysis.totalFiles = totalFiles;
|
|
4828
|
-
analysis.errorFiles = errorFiles;
|
|
4829
|
-
return analysis;
|
|
4830
|
-
}
|
|
4831
|
-
async function incrementalScan(rootDir, changes, existingCache, onProgress) {
|
|
4832
|
-
const absoluteRoot = resolve3(rootDir);
|
|
4833
|
-
const cache = { ...existingCache, files: { ...existingCache.files } };
|
|
4834
|
-
for (const filePath of changes.deleted) {
|
|
4835
|
-
removeCacheFile(cache, filePath);
|
|
4836
|
-
}
|
|
4837
|
-
const filesToScan = [...changes.added, ...changes.modified];
|
|
4838
|
-
const totalFiles = filesToScan.length;
|
|
4839
|
-
const allImports = [];
|
|
4840
|
-
const allUsages = [];
|
|
4841
|
-
const errorFiles = [];
|
|
4842
|
-
const componentSources = /* @__PURE__ */ new Map();
|
|
4843
|
-
for (const filePath of changes.unchanged) {
|
|
4844
|
-
const cachedEntry = getCachedFile(cache, filePath);
|
|
4845
|
-
if (cachedEntry) {
|
|
4846
|
-
allImports.push(...cachedEntry.imports);
|
|
4847
|
-
allUsages.push(...cachedEntry.usages);
|
|
4848
|
-
for (const imp of cachedEntry.imports) {
|
|
4849
|
-
if (!componentSources.has(imp.componentName)) {
|
|
4850
|
-
const sourceFile = resolveImportSource(filePath, imp.source);
|
|
4851
|
-
if (sourceFile) {
|
|
4852
|
-
componentSources.set(imp.componentName, sourceFile);
|
|
4853
|
-
}
|
|
4854
|
-
}
|
|
4855
|
-
}
|
|
4856
|
-
}
|
|
4857
|
-
}
|
|
4858
|
-
for (let i = 0; i < filesToScan.length; i++) {
|
|
4859
|
-
const filePath = filesToScan[i];
|
|
4860
|
-
const relativePath = relative2(absoluteRoot, filePath);
|
|
4861
|
-
onProgress?.({
|
|
4862
|
-
current: i + 1,
|
|
4863
|
-
total: totalFiles,
|
|
4864
|
-
currentFile: relativePath,
|
|
4865
|
-
phase: "scanning"
|
|
4866
|
-
});
|
|
4867
|
-
try {
|
|
4868
|
-
const content = await readFile6(filePath, "utf-8");
|
|
4869
|
-
const fileHash = computeFileHash(content);
|
|
4870
|
-
const { imports, usages } = await scanFile(filePath);
|
|
4871
|
-
updateCacheFile(cache, filePath, fileHash, imports, usages);
|
|
4872
|
-
allImports.push(...imports);
|
|
4873
|
-
allUsages.push(...usages);
|
|
4874
|
-
for (const imp of imports) {
|
|
4875
|
-
if (!componentSources.has(imp.componentName)) {
|
|
4876
|
-
const sourceFile = resolveImportSource(filePath, imp.source);
|
|
4877
|
-
if (sourceFile) {
|
|
4878
|
-
componentSources.set(imp.componentName, sourceFile);
|
|
4879
|
-
}
|
|
4880
|
-
}
|
|
4881
|
-
}
|
|
4882
|
-
} catch (error) {
|
|
4883
|
-
errorFiles.push(relativePath);
|
|
4884
|
-
}
|
|
4885
|
-
}
|
|
4886
|
-
await saveCache(absoluteRoot, cache);
|
|
4887
|
-
const analysis = aggregateAllUsages(allUsages, allImports, componentSources);
|
|
4888
|
-
analysis.rootDir = absoluteRoot;
|
|
4889
|
-
analysis.totalFiles = changes.unchanged.length + filesToScan.length;
|
|
4890
|
-
analysis.errorFiles = errorFiles;
|
|
4891
|
-
return { analysis, cache };
|
|
4892
|
-
}
|
|
4893
|
-
function resolveImportSource(importingFile, source) {
|
|
4894
|
-
if (!source.startsWith(".") && !source.startsWith("/")) {
|
|
4895
|
-
return null;
|
|
4896
|
-
}
|
|
4897
|
-
const importDir = dirname4(importingFile);
|
|
4898
|
-
const extensions = ["", ".tsx", ".ts", ".jsx", ".js", "/index.tsx", "/index.ts"];
|
|
4899
|
-
for (const ext of extensions) {
|
|
4900
|
-
const fullPath = resolve3(importDir, source + ext);
|
|
4901
|
-
if (ext === "" && source.endsWith(".tsx")) {
|
|
4902
|
-
return fullPath;
|
|
4903
|
-
}
|
|
4904
|
-
if (ext) {
|
|
4905
|
-
return resolve3(importDir, source) + ext;
|
|
4906
|
-
}
|
|
4907
|
-
}
|
|
4908
|
-
return resolve3(importDir, source);
|
|
4909
|
-
}
|
|
4910
|
-
function getScanStats(analysis) {
|
|
4911
|
-
const totalUsages = Object.values(analysis.components).reduce(
|
|
4912
|
-
(sum, c) => sum + c.totalUsages,
|
|
4913
|
-
0
|
|
4914
|
-
);
|
|
4915
|
-
const topComponents = Object.values(analysis.components).map((c) => ({ name: c.name, usages: c.totalUsages })).sort((a, b) => b.usages - a.usages).slice(0, 10);
|
|
4916
|
-
return {
|
|
4917
|
-
totalFiles: analysis.totalFiles,
|
|
4918
|
-
totalComponents: analysis.totalComponents,
|
|
4919
|
-
totalUsages,
|
|
4920
|
-
topComponents
|
|
4921
|
-
};
|
|
4922
|
-
}
|
|
4923
|
-
async function hasCachedAnalysis(rootDir) {
|
|
4924
|
-
const cache = await loadCache(resolve3(rootDir));
|
|
4925
|
-
if (!cache) return false;
|
|
4926
|
-
const stats = getCacheStats(cache);
|
|
4927
|
-
return stats.totalFiles > 0;
|
|
4928
|
-
}
|
|
4929
|
-
|
|
4930
|
-
// src/service/enhance/doc-extractor.ts
|
|
4931
|
-
import { parse as parse3 } from "@babel/parser";
|
|
4932
|
-
import _traverse2 from "@babel/traverse";
|
|
4933
|
-
import * as t3 from "@babel/types";
|
|
4934
|
-
import { readFile as readFile7 } from "fs/promises";
|
|
4935
|
-
import { existsSync as existsSync3 } from "fs";
|
|
4936
|
-
import { basename as basename2, dirname as dirname5, join as join4 } from "path";
|
|
4937
|
-
var traverse3 = _traverse2.default || _traverse2;
|
|
4037
|
+
import { basename, dirname as dirname3, join as join3 } from "path";
|
|
4038
|
+
var traverse2 = _traverse.default || _traverse;
|
|
4938
4039
|
async function extractComponentDocs(filePath) {
|
|
4939
|
-
const content = await
|
|
4040
|
+
const content = await readFile4(filePath, "utf-8");
|
|
4940
4041
|
return extractDocsFromSource(content, filePath);
|
|
4941
4042
|
}
|
|
4942
4043
|
function extractDocsFromSource(source, filePath) {
|
|
4943
4044
|
const isTypeScript = filePath.endsWith(".ts") || filePath.endsWith(".tsx");
|
|
4944
4045
|
const isJSX = filePath.endsWith(".tsx") || filePath.endsWith(".jsx");
|
|
4945
|
-
const ast =
|
|
4046
|
+
const ast = parse2(source, {
|
|
4946
4047
|
sourceType: "module",
|
|
4947
4048
|
plugins: [
|
|
4948
4049
|
...isTypeScript ? ["typescript"] : [],
|
|
@@ -4959,7 +4060,7 @@ function extractDocsFromSource(source, filePath) {
|
|
|
4959
4060
|
tags: {}
|
|
4960
4061
|
};
|
|
4961
4062
|
let mainExportComment;
|
|
4962
|
-
|
|
4063
|
+
traverse2(ast, {
|
|
4963
4064
|
// Extract JSDoc from function declarations
|
|
4964
4065
|
FunctionDeclaration(path) {
|
|
4965
4066
|
const leadingComments = path.node.leadingComments;
|
|
@@ -5095,7 +4196,7 @@ function processTag(docs, tag, content) {
|
|
|
5095
4196
|
function extractPropsFromInterface(node) {
|
|
5096
4197
|
const props = [];
|
|
5097
4198
|
for (const member of node.body.body) {
|
|
5098
|
-
if (
|
|
4199
|
+
if (t2.isTSPropertySignature(member) && t2.isIdentifier(member.key)) {
|
|
5099
4200
|
const name = member.key.name;
|
|
5100
4201
|
const type = member.typeAnnotation ? typeAnnotationToString(member.typeAnnotation.typeAnnotation) : "unknown";
|
|
5101
4202
|
const required = !member.optional;
|
|
@@ -5115,9 +4216,9 @@ function extractPropsFromInterface(node) {
|
|
|
5115
4216
|
}
|
|
5116
4217
|
function extractPropsFromTypeAlias(node) {
|
|
5117
4218
|
const props = [];
|
|
5118
|
-
if (
|
|
4219
|
+
if (t2.isTSTypeLiteral(node.typeAnnotation)) {
|
|
5119
4220
|
for (const member of node.typeAnnotation.members) {
|
|
5120
|
-
if (
|
|
4221
|
+
if (t2.isTSPropertySignature(member) && t2.isIdentifier(member.key)) {
|
|
5121
4222
|
const name = member.key.name;
|
|
5122
4223
|
const type = member.typeAnnotation ? typeAnnotationToString(member.typeAnnotation.typeAnnotation) : "unknown";
|
|
5123
4224
|
const required = !member.optional;
|
|
@@ -5133,23 +4234,23 @@ function extractPropsFromTypeAlias(node) {
|
|
|
5133
4234
|
return props;
|
|
5134
4235
|
}
|
|
5135
4236
|
function typeAnnotationToString(node) {
|
|
5136
|
-
if (
|
|
5137
|
-
if (
|
|
5138
|
-
if (
|
|
5139
|
-
if (
|
|
5140
|
-
if (
|
|
5141
|
-
if (
|
|
5142
|
-
if (
|
|
5143
|
-
if (
|
|
5144
|
-
if (
|
|
5145
|
-
if (
|
|
5146
|
-
if (
|
|
5147
|
-
if (
|
|
5148
|
-
if (
|
|
4237
|
+
if (t2.isTSStringKeyword(node)) return "string";
|
|
4238
|
+
if (t2.isTSNumberKeyword(node)) return "number";
|
|
4239
|
+
if (t2.isTSBooleanKeyword(node)) return "boolean";
|
|
4240
|
+
if (t2.isTSNullKeyword(node)) return "null";
|
|
4241
|
+
if (t2.isTSUndefinedKeyword(node)) return "undefined";
|
|
4242
|
+
if (t2.isTSVoidKeyword(node)) return "void";
|
|
4243
|
+
if (t2.isTSAnyKeyword(node)) return "any";
|
|
4244
|
+
if (t2.isTSNeverKeyword(node)) return "never";
|
|
4245
|
+
if (t2.isTSUnknownKeyword(node)) return "unknown";
|
|
4246
|
+
if (t2.isTSLiteralType(node)) {
|
|
4247
|
+
if (t2.isStringLiteral(node.literal)) return `"${node.literal.value}"`;
|
|
4248
|
+
if (t2.isNumericLiteral(node.literal)) return String(node.literal.value);
|
|
4249
|
+
if (t2.isBooleanLiteral(node.literal)) return String(node.literal.value);
|
|
5149
4250
|
return "literal";
|
|
5150
4251
|
}
|
|
5151
|
-
if (
|
|
5152
|
-
if (
|
|
4252
|
+
if (t2.isTSTypeReference(node)) {
|
|
4253
|
+
if (t2.isIdentifier(node.typeName)) {
|
|
5153
4254
|
const name = node.typeName.name;
|
|
5154
4255
|
if (node.typeParameters) {
|
|
5155
4256
|
const params = node.typeParameters.params.map(typeAnnotationToString).join(", ");
|
|
@@ -5158,28 +4259,28 @@ function typeAnnotationToString(node) {
|
|
|
5158
4259
|
return name;
|
|
5159
4260
|
}
|
|
5160
4261
|
}
|
|
5161
|
-
if (
|
|
4262
|
+
if (t2.isTSUnionType(node)) {
|
|
5162
4263
|
return node.types.map(typeAnnotationToString).join(" | ");
|
|
5163
4264
|
}
|
|
5164
|
-
if (
|
|
4265
|
+
if (t2.isTSIntersectionType(node)) {
|
|
5165
4266
|
return node.types.map(typeAnnotationToString).join(" & ");
|
|
5166
4267
|
}
|
|
5167
|
-
if (
|
|
4268
|
+
if (t2.isTSArrayType(node)) {
|
|
5168
4269
|
return `${typeAnnotationToString(node.elementType)}[]`;
|
|
5169
4270
|
}
|
|
5170
|
-
if (
|
|
4271
|
+
if (t2.isTSFunctionType(node)) {
|
|
5171
4272
|
return "(...args: any[]) => any";
|
|
5172
4273
|
}
|
|
5173
|
-
if (
|
|
4274
|
+
if (t2.isTSTupleType(node)) {
|
|
5174
4275
|
const elements = node.elementTypes.map((el) => {
|
|
5175
|
-
if (
|
|
4276
|
+
if (t2.isTSNamedTupleMember(el)) {
|
|
5176
4277
|
return typeAnnotationToString(el.elementType);
|
|
5177
4278
|
}
|
|
5178
4279
|
return typeAnnotationToString(el);
|
|
5179
4280
|
}).join(", ");
|
|
5180
4281
|
return `[${elements}]`;
|
|
5181
4282
|
}
|
|
5182
|
-
if (
|
|
4283
|
+
if (t2.isTSTypeLiteral(node)) {
|
|
5183
4284
|
return "object";
|
|
5184
4285
|
}
|
|
5185
4286
|
return "unknown";
|
|
@@ -5189,28 +4290,28 @@ function isPropsInterface(name) {
|
|
|
5189
4290
|
}
|
|
5190
4291
|
function isExportedComponent(path) {
|
|
5191
4292
|
const parent = path.parent;
|
|
5192
|
-
if (
|
|
5193
|
-
if (
|
|
5194
|
-
if (
|
|
4293
|
+
if (t2.isExportDefaultDeclaration(parent)) return true;
|
|
4294
|
+
if (t2.isExportNamedDeclaration(parent)) return true;
|
|
4295
|
+
if (t2.isProgram(parent) && path.node.id && /^[A-Z]/.test(path.node.id.name)) {
|
|
5195
4296
|
return true;
|
|
5196
4297
|
}
|
|
5197
4298
|
return false;
|
|
5198
4299
|
}
|
|
5199
4300
|
function hasExportedArrowFunction(path) {
|
|
5200
4301
|
const parent = path.parent;
|
|
5201
|
-
if (!
|
|
4302
|
+
if (!t2.isExportNamedDeclaration(parent)) return false;
|
|
5202
4303
|
for (const declarator of path.node.declarations) {
|
|
5203
|
-
if (
|
|
4304
|
+
if (t2.isIdentifier(declarator.id) && /^[A-Z]/.test(declarator.id.name) && (t2.isArrowFunctionExpression(declarator.init) || t2.isFunctionExpression(declarator.init))) {
|
|
5204
4305
|
return true;
|
|
5205
4306
|
}
|
|
5206
4307
|
}
|
|
5207
4308
|
return false;
|
|
5208
4309
|
}
|
|
5209
4310
|
function inferComponentName(filePath) {
|
|
5210
|
-
const fileName =
|
|
4311
|
+
const fileName = basename(filePath);
|
|
5211
4312
|
let name = fileName.replace(/\.(tsx?|jsx?)$/, "");
|
|
5212
4313
|
if (name === "index") {
|
|
5213
|
-
name =
|
|
4314
|
+
name = basename(dirname3(filePath));
|
|
5214
4315
|
}
|
|
5215
4316
|
return name;
|
|
5216
4317
|
}
|
|
@@ -5249,8 +4350,8 @@ async function findComponentSource(componentName, searchDirs) {
|
|
|
5249
4350
|
];
|
|
5250
4351
|
for (const dir of searchDirs) {
|
|
5251
4352
|
for (const pattern of patterns) {
|
|
5252
|
-
const fullPath =
|
|
5253
|
-
if (
|
|
4353
|
+
const fullPath = join3(dir, pattern);
|
|
4354
|
+
if (existsSync2(fullPath)) {
|
|
5254
4355
|
return fullPath;
|
|
5255
4356
|
}
|
|
5256
4357
|
}
|
|
@@ -5275,21 +4376,21 @@ async function extractAllComponentDocs(componentFiles) {
|
|
|
5275
4376
|
}
|
|
5276
4377
|
|
|
5277
4378
|
// src/service/enhance/storybook-parser.ts
|
|
5278
|
-
import { parse as
|
|
5279
|
-
import
|
|
5280
|
-
import * as
|
|
5281
|
-
import { readFile as
|
|
5282
|
-
import
|
|
5283
|
-
import { basename as
|
|
5284
|
-
var
|
|
4379
|
+
import { parse as parse3 } from "@babel/parser";
|
|
4380
|
+
import _traverse2 from "@babel/traverse";
|
|
4381
|
+
import * as t3 from "@babel/types";
|
|
4382
|
+
import { readFile as readFile5 } from "fs/promises";
|
|
4383
|
+
import fg from "fast-glob";
|
|
4384
|
+
import { basename as basename2, relative as relative2 } from "path";
|
|
4385
|
+
var traverse3 = _traverse2.default || _traverse2;
|
|
5285
4386
|
async function parseStoryFile(filePath) {
|
|
5286
|
-
const content = await
|
|
4387
|
+
const content = await readFile5(filePath, "utf-8");
|
|
5287
4388
|
return parseStorySource(content, filePath);
|
|
5288
4389
|
}
|
|
5289
4390
|
function parseStorySource(source, filePath) {
|
|
5290
4391
|
const isTypeScript = filePath.endsWith(".ts") || filePath.endsWith(".tsx");
|
|
5291
4392
|
const isJSX = filePath.endsWith(".tsx") || filePath.endsWith(".jsx");
|
|
5292
|
-
const ast =
|
|
4393
|
+
const ast = parse3(source, {
|
|
5293
4394
|
sourceType: "module",
|
|
5294
4395
|
plugins: [
|
|
5295
4396
|
...isTypeScript ? ["typescript"] : [],
|
|
@@ -5305,7 +4406,7 @@ function parseStorySource(source, filePath) {
|
|
|
5305
4406
|
stories: []
|
|
5306
4407
|
};
|
|
5307
4408
|
let defaultExportNode = null;
|
|
5308
|
-
|
|
4409
|
+
traverse3(ast, {
|
|
5309
4410
|
ExportDefaultDeclaration(path) {
|
|
5310
4411
|
defaultExportNode = path.node.declaration;
|
|
5311
4412
|
}
|
|
@@ -5316,12 +4417,12 @@ function parseStorySource(source, filePath) {
|
|
|
5316
4417
|
result.meta = parseMetaObject(metaObject, result.meta.componentName);
|
|
5317
4418
|
}
|
|
5318
4419
|
}
|
|
5319
|
-
|
|
4420
|
+
traverse3(ast, {
|
|
5320
4421
|
ExportNamedDeclaration(path) {
|
|
5321
4422
|
const declaration = path.node.declaration;
|
|
5322
|
-
if (
|
|
4423
|
+
if (t3.isVariableDeclaration(declaration)) {
|
|
5323
4424
|
for (const declarator of declaration.declarations) {
|
|
5324
|
-
if (!
|
|
4425
|
+
if (!t3.isIdentifier(declarator.id)) continue;
|
|
5325
4426
|
const storyName = declarator.id.name;
|
|
5326
4427
|
if (!/^[A-Z]/.test(storyName)) continue;
|
|
5327
4428
|
const story = parseStoryDeclarator(
|
|
@@ -5346,25 +4447,25 @@ function parseStorySource(source, filePath) {
|
|
|
5346
4447
|
return result;
|
|
5347
4448
|
}
|
|
5348
4449
|
function extractMetaObject(node, ast) {
|
|
5349
|
-
if (
|
|
4450
|
+
if (t3.isObjectExpression(node)) {
|
|
5350
4451
|
return node;
|
|
5351
4452
|
}
|
|
5352
|
-
if (
|
|
4453
|
+
if (t3.isIdentifier(node)) {
|
|
5353
4454
|
const metaName = node.name;
|
|
5354
4455
|
let foundObject = null;
|
|
5355
|
-
|
|
4456
|
+
traverse3(ast, {
|
|
5356
4457
|
VariableDeclarator(path) {
|
|
5357
|
-
if (
|
|
4458
|
+
if (t3.isIdentifier(path.node.id) && path.node.id.name === metaName && t3.isObjectExpression(path.node.init)) {
|
|
5358
4459
|
foundObject = path.node.init;
|
|
5359
4460
|
}
|
|
5360
4461
|
}
|
|
5361
4462
|
});
|
|
5362
4463
|
return foundObject;
|
|
5363
4464
|
}
|
|
5364
|
-
if (
|
|
4465
|
+
if (t3.isTSAsExpression(node) && t3.isObjectExpression(node.expression)) {
|
|
5365
4466
|
return node.expression;
|
|
5366
4467
|
}
|
|
5367
|
-
if (
|
|
4468
|
+
if (t3.isTSSatisfiesExpression && t3.isTSSatisfiesExpression(node) && t3.isObjectExpression(node.expression)) {
|
|
5368
4469
|
return node.expression;
|
|
5369
4470
|
}
|
|
5370
4471
|
return null;
|
|
@@ -5374,23 +4475,23 @@ function parseMetaObject(obj, fallbackName) {
|
|
|
5374
4475
|
componentName: fallbackName
|
|
5375
4476
|
};
|
|
5376
4477
|
for (const prop of obj.properties) {
|
|
5377
|
-
if (!
|
|
4478
|
+
if (!t3.isObjectProperty(prop) || !t3.isIdentifier(prop.key)) continue;
|
|
5378
4479
|
const key = prop.key.name;
|
|
5379
4480
|
switch (key) {
|
|
5380
4481
|
case "title":
|
|
5381
|
-
if (
|
|
4482
|
+
if (t3.isStringLiteral(prop.value)) {
|
|
5382
4483
|
meta.title = prop.value.value;
|
|
5383
4484
|
const parts = meta.title.split("/");
|
|
5384
4485
|
meta.componentName = parts[parts.length - 1];
|
|
5385
4486
|
}
|
|
5386
4487
|
break;
|
|
5387
4488
|
case "component":
|
|
5388
|
-
if (
|
|
4489
|
+
if (t3.isIdentifier(prop.value)) {
|
|
5389
4490
|
meta.componentName = prop.value.name;
|
|
5390
4491
|
}
|
|
5391
4492
|
break;
|
|
5392
4493
|
case "parameters":
|
|
5393
|
-
if (
|
|
4494
|
+
if (t3.isObjectExpression(prop.value)) {
|
|
5394
4495
|
meta.parameters = extractObjectLiteral(prop.value);
|
|
5395
4496
|
const docs = meta.parameters?.docs;
|
|
5396
4497
|
if (docs?.description) {
|
|
@@ -5402,7 +4503,7 @@ function parseMetaObject(obj, fallbackName) {
|
|
|
5402
4503
|
}
|
|
5403
4504
|
break;
|
|
5404
4505
|
case "argTypes":
|
|
5405
|
-
if (
|
|
4506
|
+
if (t3.isObjectExpression(prop.value)) {
|
|
5406
4507
|
meta.argTypes = parseArgTypes(prop.value);
|
|
5407
4508
|
}
|
|
5408
4509
|
break;
|
|
@@ -5413,29 +4514,29 @@ function parseMetaObject(obj, fallbackName) {
|
|
|
5413
4514
|
function parseArgTypes(obj) {
|
|
5414
4515
|
const argTypes = {};
|
|
5415
4516
|
for (const prop of obj.properties) {
|
|
5416
|
-
if (!
|
|
4517
|
+
if (!t3.isObjectProperty(prop) || !t3.isIdentifier(prop.key)) continue;
|
|
5417
4518
|
const propName = prop.key.name;
|
|
5418
4519
|
const argType = {};
|
|
5419
|
-
if (
|
|
4520
|
+
if (t3.isObjectExpression(prop.value)) {
|
|
5420
4521
|
for (const inner of prop.value.properties) {
|
|
5421
|
-
if (!
|
|
4522
|
+
if (!t3.isObjectProperty(inner) || !t3.isIdentifier(inner.key)) continue;
|
|
5422
4523
|
const innerKey = inner.key.name;
|
|
5423
|
-
if (innerKey === "description" &&
|
|
4524
|
+
if (innerKey === "description" && t3.isStringLiteral(inner.value)) {
|
|
5424
4525
|
argType.description = inner.value.value;
|
|
5425
4526
|
}
|
|
5426
4527
|
if (innerKey === "defaultValue") {
|
|
5427
4528
|
argType.defaultValue = extractLiteralValue(inner.value);
|
|
5428
4529
|
}
|
|
5429
4530
|
if (innerKey === "control") {
|
|
5430
|
-
if (
|
|
4531
|
+
if (t3.isStringLiteral(inner.value)) {
|
|
5431
4532
|
argType.control = inner.value.value;
|
|
5432
|
-
} else if (
|
|
4533
|
+
} else if (t3.isObjectExpression(inner.value)) {
|
|
5433
4534
|
const controlObj = extractObjectLiteral(inner.value);
|
|
5434
4535
|
argType.control = controlObj?.type;
|
|
5435
4536
|
}
|
|
5436
4537
|
}
|
|
5437
|
-
if (innerKey === "options" &&
|
|
5438
|
-
argType.options = inner.value.elements.filter((el) =>
|
|
4538
|
+
if (innerKey === "options" && t3.isArrayExpression(inner.value)) {
|
|
4539
|
+
argType.options = inner.value.elements.filter((el) => t3.isStringLiteral(el)).map((el) => el.value);
|
|
5439
4540
|
}
|
|
5440
4541
|
}
|
|
5441
4542
|
}
|
|
@@ -5453,25 +4554,25 @@ function parseStoryDeclarator(name, declarator, source, componentName) {
|
|
|
5453
4554
|
code: "",
|
|
5454
4555
|
isDefault: false
|
|
5455
4556
|
};
|
|
5456
|
-
if (
|
|
4557
|
+
if (t3.isObjectExpression(init)) {
|
|
5457
4558
|
parseStoryObject(init, story);
|
|
5458
4559
|
story.code = generateStoryCode(componentName, story.args);
|
|
5459
4560
|
return story;
|
|
5460
4561
|
}
|
|
5461
|
-
if (
|
|
4562
|
+
if (t3.isTSAsExpression(init) && t3.isObjectExpression(init.expression)) {
|
|
5462
4563
|
parseStoryObject(init.expression, story);
|
|
5463
4564
|
story.code = generateStoryCode(componentName, story.args);
|
|
5464
4565
|
return story;
|
|
5465
4566
|
}
|
|
5466
|
-
if (
|
|
4567
|
+
if (t3.isArrowFunctionExpression(init) || t3.isFunctionExpression(init)) {
|
|
5467
4568
|
if (init.start != null && init.end != null) {
|
|
5468
4569
|
const fnCode = source.slice(init.start, init.end);
|
|
5469
4570
|
story.code = fnCode;
|
|
5470
4571
|
}
|
|
5471
4572
|
return story;
|
|
5472
4573
|
}
|
|
5473
|
-
if (
|
|
5474
|
-
if (
|
|
4574
|
+
if (t3.isTSSatisfiesExpression && t3.isTSSatisfiesExpression(init)) {
|
|
4575
|
+
if (t3.isObjectExpression(init.expression)) {
|
|
5475
4576
|
parseStoryObject(init.expression, story);
|
|
5476
4577
|
story.code = generateStoryCode(componentName, story.args);
|
|
5477
4578
|
return story;
|
|
@@ -5481,21 +4582,21 @@ function parseStoryDeclarator(name, declarator, source, componentName) {
|
|
|
5481
4582
|
}
|
|
5482
4583
|
function parseStoryObject(obj, story) {
|
|
5483
4584
|
for (const prop of obj.properties) {
|
|
5484
|
-
if (!
|
|
4585
|
+
if (!t3.isObjectProperty(prop) || !t3.isIdentifier(prop.key)) continue;
|
|
5485
4586
|
const key = prop.key.name;
|
|
5486
4587
|
switch (key) {
|
|
5487
4588
|
case "name":
|
|
5488
|
-
if (
|
|
4589
|
+
if (t3.isStringLiteral(prop.value)) {
|
|
5489
4590
|
story.displayName = prop.value.value;
|
|
5490
4591
|
}
|
|
5491
4592
|
break;
|
|
5492
4593
|
case "args":
|
|
5493
|
-
if (
|
|
4594
|
+
if (t3.isObjectExpression(prop.value)) {
|
|
5494
4595
|
story.args = extractObjectLiteral(prop.value);
|
|
5495
4596
|
}
|
|
5496
4597
|
break;
|
|
5497
4598
|
case "parameters":
|
|
5498
|
-
if (
|
|
4599
|
+
if (t3.isObjectExpression(prop.value)) {
|
|
5499
4600
|
const params = extractObjectLiteral(prop.value);
|
|
5500
4601
|
const docs = params?.docs;
|
|
5501
4602
|
if (docs?.description) {
|
|
@@ -5510,15 +4611,15 @@ function parseStoryObject(obj, story) {
|
|
|
5510
4611
|
}
|
|
5511
4612
|
}
|
|
5512
4613
|
function extractLiteralValue(node) {
|
|
5513
|
-
if (
|
|
5514
|
-
if (
|
|
5515
|
-
if (
|
|
5516
|
-
if (
|
|
5517
|
-
if (
|
|
5518
|
-
if (
|
|
5519
|
-
return node.elements.filter((el) => el !== null &&
|
|
5520
|
-
}
|
|
5521
|
-
if (
|
|
4614
|
+
if (t3.isStringLiteral(node)) return node.value;
|
|
4615
|
+
if (t3.isNumericLiteral(node)) return node.value;
|
|
4616
|
+
if (t3.isBooleanLiteral(node)) return node.value;
|
|
4617
|
+
if (t3.isNullLiteral(node)) return null;
|
|
4618
|
+
if (t3.isIdentifier(node) && node.name === "undefined") return void 0;
|
|
4619
|
+
if (t3.isArrayExpression(node)) {
|
|
4620
|
+
return node.elements.filter((el) => el !== null && t3.isExpression(el)).map(extractLiteralValue);
|
|
4621
|
+
}
|
|
4622
|
+
if (t3.isObjectExpression(node)) {
|
|
5522
4623
|
return extractObjectLiteral(node);
|
|
5523
4624
|
}
|
|
5524
4625
|
return void 0;
|
|
@@ -5526,12 +4627,12 @@ function extractLiteralValue(node) {
|
|
|
5526
4627
|
function extractObjectLiteral(node) {
|
|
5527
4628
|
const obj = {};
|
|
5528
4629
|
for (const prop of node.properties) {
|
|
5529
|
-
if (
|
|
5530
|
-
if (!
|
|
4630
|
+
if (t3.isSpreadElement(prop)) continue;
|
|
4631
|
+
if (!t3.isObjectProperty(prop)) continue;
|
|
5531
4632
|
let key;
|
|
5532
|
-
if (
|
|
4633
|
+
if (t3.isIdentifier(prop.key)) {
|
|
5533
4634
|
key = prop.key.name;
|
|
5534
|
-
} else if (
|
|
4635
|
+
} else if (t3.isStringLiteral(prop.key)) {
|
|
5535
4636
|
key = prop.key.value;
|
|
5536
4637
|
} else {
|
|
5537
4638
|
continue;
|
|
@@ -5559,12 +4660,12 @@ function camelToTitle(str) {
|
|
|
5559
4660
|
return str.replace(/([A-Z])/g, " $1").replace(/^./, (s) => s.toUpperCase()).trim();
|
|
5560
4661
|
}
|
|
5561
4662
|
function inferComponentFromPath(filePath) {
|
|
5562
|
-
const fileName =
|
|
4663
|
+
const fileName = basename2(filePath);
|
|
5563
4664
|
const name = fileName.replace(/\.stories\.(tsx?|jsx?|mdx?)$/, "");
|
|
5564
4665
|
return name;
|
|
5565
4666
|
}
|
|
5566
4667
|
async function findStoryFiles(rootDir, include = ["**/*.stories.{tsx,ts,jsx,js}"], exclude = ["**/node_modules/**", "**/dist/**"]) {
|
|
5567
|
-
return
|
|
4668
|
+
return fg(include, {
|
|
5568
4669
|
cwd: rootDir,
|
|
5569
4670
|
ignore: exclude,
|
|
5570
4671
|
absolute: true,
|
|
@@ -5580,7 +4681,7 @@ async function parseAllStories(rootDir) {
|
|
|
5580
4681
|
results.set(parsed.meta.componentName, parsed);
|
|
5581
4682
|
} catch (error) {
|
|
5582
4683
|
console.warn(
|
|
5583
|
-
`Failed to parse story file ${
|
|
4684
|
+
`Failed to parse story file ${relative2(rootDir, filePath)}:`,
|
|
5584
4685
|
error.message
|
|
5585
4686
|
);
|
|
5586
4687
|
}
|
|
@@ -5918,12 +5019,12 @@ If there isn't enough usage data to make confident recommendations, indicate tha
|
|
|
5918
5019
|
|
|
5919
5020
|
// src/service/enhance/props-extractor.ts
|
|
5920
5021
|
import * as ts from "typescript";
|
|
5921
|
-
import { readFile as
|
|
5922
|
-
import { existsSync as
|
|
5923
|
-
import { basename as
|
|
5022
|
+
import { readFile as readFile6 } from "fs/promises";
|
|
5023
|
+
import { existsSync as existsSync3 } from "fs";
|
|
5024
|
+
import { basename as basename3, dirname as dirname5, join as join5, resolve as resolve3 } from "path";
|
|
5924
5025
|
async function extractPropsFromFile(filePath, options = {}) {
|
|
5925
|
-
const absPath =
|
|
5926
|
-
if (!
|
|
5026
|
+
const absPath = resolve3(filePath);
|
|
5027
|
+
if (!existsSync3(absPath)) {
|
|
5927
5028
|
return {
|
|
5928
5029
|
filePath: absPath,
|
|
5929
5030
|
componentName: inferComponentName2(absPath),
|
|
@@ -5932,7 +5033,7 @@ async function extractPropsFromFile(filePath, options = {}) {
|
|
|
5932
5033
|
warnings: [`File not found: ${absPath}`]
|
|
5933
5034
|
};
|
|
5934
5035
|
}
|
|
5935
|
-
const content = await
|
|
5036
|
+
const content = await readFile6(absPath, "utf-8");
|
|
5936
5037
|
return extractPropsFromSource(content, absPath, options);
|
|
5937
5038
|
}
|
|
5938
5039
|
function extractPropsFromSource(source, filePath, options = {}) {
|
|
@@ -6221,8 +5322,19 @@ function extractPropsFromInlineParams(componentName, sourceFile) {
|
|
|
6221
5322
|
}
|
|
6222
5323
|
if (ts.isVariableStatement(node)) {
|
|
6223
5324
|
for (const decl of node.declarationList.declarations) {
|
|
6224
|
-
if (ts.isIdentifier(decl.name) && decl.name.text === componentName && decl.initializer
|
|
6225
|
-
|
|
5325
|
+
if (ts.isIdentifier(decl.name) && decl.name.text === componentName && decl.initializer) {
|
|
5326
|
+
if (ts.isArrowFunction(decl.initializer) || ts.isFunctionExpression(decl.initializer)) {
|
|
5327
|
+
targetFunc = decl.initializer;
|
|
5328
|
+
} else if (ts.isCallExpression(decl.initializer)) {
|
|
5329
|
+
const callee = decl.initializer.expression;
|
|
5330
|
+
const isForwardRef = ts.isPropertyAccessExpression(callee) && callee.name.text === "forwardRef" || ts.isIdentifier(callee) && callee.text === "forwardRef";
|
|
5331
|
+
if (isForwardRef && decl.initializer.arguments.length > 0) {
|
|
5332
|
+
const firstArg = decl.initializer.arguments[0];
|
|
5333
|
+
if (ts.isArrowFunction(firstArg) || ts.isFunctionExpression(firstArg)) {
|
|
5334
|
+
targetFunc = firstArg;
|
|
5335
|
+
}
|
|
5336
|
+
}
|
|
5337
|
+
}
|
|
6226
5338
|
}
|
|
6227
5339
|
}
|
|
6228
5340
|
}
|
|
@@ -6340,12 +5452,12 @@ function extractCvaVariants(sourceFile, props) {
|
|
|
6340
5452
|
}
|
|
6341
5453
|
}
|
|
6342
5454
|
function inferComponentName2(filePath) {
|
|
6343
|
-
const fileName =
|
|
5455
|
+
const fileName = basename3(filePath);
|
|
6344
5456
|
let name = fileName.replace(/\.(tsx?|jsx?)$/, "");
|
|
6345
5457
|
if (name === "index") {
|
|
6346
|
-
name =
|
|
5458
|
+
name = basename3(dirname5(filePath));
|
|
6347
5459
|
}
|
|
6348
|
-
return name;
|
|
5460
|
+
return name.split(/[^a-zA-Z0-9]+/).filter(Boolean).map((segment) => segment.charAt(0).toUpperCase() + segment.slice(1)).join("");
|
|
6349
5461
|
}
|
|
6350
5462
|
function convertToFragmentProps(props) {
|
|
6351
5463
|
const result = {};
|
|
@@ -6391,10 +5503,11 @@ async function extractPropsForComponent(componentName, searchDirs) {
|
|
|
6391
5503
|
];
|
|
6392
5504
|
for (const dir of searchDirs) {
|
|
6393
5505
|
for (const pattern of patterns) {
|
|
6394
|
-
const fullPath =
|
|
6395
|
-
if (
|
|
5506
|
+
const fullPath = join5(dir, pattern);
|
|
5507
|
+
if (existsSync3(fullPath)) {
|
|
6396
5508
|
return extractPropsFromFile(fullPath, {
|
|
6397
|
-
propsTypeName: `${componentName}Props
|
|
5509
|
+
propsTypeName: `${componentName}Props`,
|
|
5510
|
+
componentName
|
|
6398
5511
|
});
|
|
6399
5512
|
}
|
|
6400
5513
|
}
|
|
@@ -6406,7 +5519,8 @@ async function extractAllComponentProps(componentFiles) {
|
|
|
6406
5519
|
for (const [componentName, filePath] of componentFiles) {
|
|
6407
5520
|
try {
|
|
6408
5521
|
const extraction = await extractPropsFromFile(filePath, {
|
|
6409
|
-
propsTypeName: `${componentName}Props
|
|
5522
|
+
propsTypeName: `${componentName}Props`,
|
|
5523
|
+
componentName
|
|
6410
5524
|
});
|
|
6411
5525
|
results.set(componentName, extraction);
|
|
6412
5526
|
} catch (error) {
|
|
@@ -6730,25 +5844,6 @@ export {
|
|
|
6730
5844
|
applyPatches,
|
|
6731
5845
|
MetricsStore,
|
|
6732
5846
|
createMetricsStore,
|
|
6733
|
-
scanFileForImports,
|
|
6734
|
-
scanFileForUsages,
|
|
6735
|
-
scanFile,
|
|
6736
|
-
aggregateComponentUsages,
|
|
6737
|
-
aggregateAllUsages,
|
|
6738
|
-
findCommonPropCombinations,
|
|
6739
|
-
inferRelations,
|
|
6740
|
-
inferAllRelations,
|
|
6741
|
-
summarizePatternsForPrompt,
|
|
6742
|
-
loadCache,
|
|
6743
|
-
saveCache,
|
|
6744
|
-
createEmptyCache,
|
|
6745
|
-
computeFileHash,
|
|
6746
|
-
detectFileChanges,
|
|
6747
|
-
getCacheStats,
|
|
6748
|
-
scanCodebase,
|
|
6749
|
-
incrementalScan,
|
|
6750
|
-
getScanStats,
|
|
6751
|
-
hasCachedAnalysis,
|
|
6752
5847
|
extractComponentDocs,
|
|
6753
5848
|
extractDocsFromSource,
|
|
6754
5849
|
findComponentSource,
|
|
@@ -6776,4 +5871,4 @@ export {
|
|
|
6776
5871
|
getStorybookStoryIds,
|
|
6777
5872
|
renderAllComponentVariants
|
|
6778
5873
|
};
|
|
6779
|
-
//# sourceMappingURL=chunk-
|
|
5874
|
+
//# sourceMappingURL=chunk-XJQ5BIWI.js.map
|