@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.
Files changed (135) hide show
  1. package/README.md +0 -3
  2. package/dist/bin.js +4290 -3754
  3. package/dist/bin.js.map +1 -1
  4. package/dist/{chunk-TXFCEDOC.js → chunk-2WXKALIG.js} +2 -2
  5. package/dist/{chunk-I34BC3CU.js → chunk-32LIWN2P.js} +1006 -3
  6. package/dist/chunk-32LIWN2P.js.map +1 -0
  7. package/dist/{chunk-55KERLWL.js → chunk-65WSVDV5.js} +314 -89
  8. package/dist/chunk-65WSVDV5.js.map +1 -0
  9. package/dist/chunk-7DZC4YEV.js +294 -0
  10. package/dist/chunk-7DZC4YEV.js.map +1 -0
  11. package/dist/{chunk-LOYS64QS.js → chunk-7WHVW72L.js} +230 -19
  12. package/dist/chunk-7WHVW72L.js.map +1 -0
  13. package/dist/{chunk-PJT5IZ37.js → chunk-BJE3425I.js} +19 -52
  14. package/dist/{chunk-PJT5IZ37.js.map → chunk-BJE3425I.js.map} +1 -1
  15. package/dist/{chunk-5A6X2Y73.js → chunk-CZD3AD4Q.js} +12 -11
  16. package/dist/chunk-CZD3AD4Q.js.map +1 -0
  17. package/dist/{chunk-EYXVAMEX.js → chunk-MN3TJ3D5.js} +72 -3
  18. package/dist/chunk-MN3TJ3D5.js.map +1 -0
  19. package/dist/chunk-QCN35LJU.js +630 -0
  20. package/dist/chunk-QCN35LJU.js.map +1 -0
  21. package/dist/chunk-T47OLCSF.js +36 -0
  22. package/dist/chunk-T47OLCSF.js.map +1 -0
  23. package/dist/{chunk-APTQIBS5.js → chunk-XJQ5BIWI.js} +144 -1049
  24. package/dist/chunk-XJQ5BIWI.js.map +1 -0
  25. package/dist/codebase-scanner-VOTPXRYW.js +22 -0
  26. package/dist/converter-JLINP7CJ.js +34 -0
  27. package/dist/converter-JLINP7CJ.js.map +1 -0
  28. package/dist/core/index.js +43 -1
  29. package/dist/{generate-RYWIPDN2.js → generate-A4FP5426.js} +3 -4
  30. package/dist/{generate-RYWIPDN2.js.map → generate-A4FP5426.js.map} +1 -1
  31. package/dist/govern-scan-UCBZR6D6.js +280 -0
  32. package/dist/govern-scan-UCBZR6D6.js.map +1 -0
  33. package/dist/index.d.ts +2 -1
  34. package/dist/index.js +11 -11
  35. package/dist/{init-WRUSW7R5.js → init-HGSM35XA.js} +131 -128
  36. package/dist/init-HGSM35XA.js.map +1 -0
  37. package/dist/{init-cloud-REQ3XLHO.js → init-cloud-MQ6GRJAZ.js} +2 -2
  38. package/dist/mcp-bin.js +5 -36
  39. package/dist/mcp-bin.js.map +1 -1
  40. package/dist/scan-VNNKACG2.js +15 -0
  41. package/dist/{scan-generate-TFZVL3BT.js → scan-generate-TWRHNU5M.js} +335 -46
  42. package/dist/scan-generate-TWRHNU5M.js.map +1 -0
  43. package/dist/scanner-7LAZYPWZ.js +13 -0
  44. package/dist/{service-HKJ6B7P7.js → service-FHQU7YS7.js} +27 -23
  45. package/dist/{snapshot-C5DYIGIV.js → snapshot-KQEQ6XHL.js} +2 -2
  46. package/dist/{static-viewer-DUVC4UIM.js → static-viewer-63PG6FWY.js} +3 -3
  47. package/dist/static-viewer-63PG6FWY.js.map +1 -0
  48. package/dist/{test-JW7JIDFG.js → test-UQYUCZIS.js} +4 -6
  49. package/dist/{test-JW7JIDFG.js.map → test-UQYUCZIS.js.map} +1 -1
  50. package/dist/{tokens-KE73G5JC.js → tokens-6GYKDV6U.js} +6 -5
  51. package/dist/{tokens-KE73G5JC.js.map → tokens-6GYKDV6U.js.map} +1 -1
  52. package/dist/tokens-generate-VTZV5EEW.js +86 -0
  53. package/dist/tokens-generate-VTZV5EEW.js.map +1 -0
  54. package/package.json +6 -6
  55. package/src/bin.ts +210 -48
  56. package/src/build.ts +130 -6
  57. package/src/commands/__fixtures__/shadcn-label-wrapper/package.json +7 -0
  58. package/src/commands/__fixtures__/shadcn-label-wrapper/src/components/ui/label.contract.json +42 -0
  59. package/src/commands/__fixtures__/shadcn-label-wrapper/src/components/ui/label.tsx +11 -0
  60. package/src/commands/__fixtures__/shadcn-label-wrapper/src/components/ui/primitive.contract.json +20 -0
  61. package/src/commands/__fixtures__/shadcn-label-wrapper/src/components/ui/primitive.tsx +14 -0
  62. package/src/commands/__fixtures__/shadcn-label-wrapper/tsconfig.app.json +23 -0
  63. package/src/commands/__tests__/init.test.ts +113 -0
  64. package/src/commands/__tests__/scan-generate.test.ts +188 -69
  65. package/src/commands/__tests__/verify.test.ts +91 -0
  66. package/src/commands/discover.ts +151 -0
  67. package/src/commands/enhance.ts +3 -1
  68. package/src/commands/govern-scan.ts +386 -0
  69. package/src/commands/govern.ts +2 -2
  70. package/src/commands/init.ts +152 -28
  71. package/src/commands/inspect.ts +290 -0
  72. package/src/commands/migrate-contract.ts +85 -0
  73. package/src/commands/scan-generate.ts +438 -50
  74. package/src/commands/scan.ts +1 -0
  75. package/src/commands/setup.ts +27 -50
  76. package/src/commands/tokens-generate.ts +113 -0
  77. package/src/commands/verify.ts +195 -1
  78. package/src/core/__fixtures__/shadcn-input/input.tsx +7 -0
  79. package/src/core/__fixtures__/shadcn-input/tsconfig.json +14 -0
  80. package/src/core/__fixtures__/shadcn-label/label.tsx +11 -0
  81. package/src/core/__fixtures__/shadcn-label/primitive.tsx +14 -0
  82. package/src/core/__fixtures__/shadcn-label/tsconfig.json +14 -0
  83. package/src/core/__fixtures__/shadcn-radix-label/label.tsx +11 -0
  84. package/src/core/__fixtures__/shadcn-radix-label/node_modules/radix-ui/index.d.ts +12 -0
  85. package/src/core/__fixtures__/shadcn-radix-label/tsconfig.json +14 -0
  86. package/src/core/__tests__/contract-parity.test.ts +316 -0
  87. package/src/core/component-extractor.test.ts +39 -0
  88. package/src/core/component-extractor.ts +92 -1
  89. package/src/core/config.ts +2 -1
  90. package/src/core/discovery.ts +13 -2
  91. package/src/core/drift-verifier.ts +123 -0
  92. package/src/core/extractor-adapter.ts +80 -0
  93. package/src/mcp/__tests__/projectFields.test.ts +1 -1
  94. package/src/mcp/utils.ts +1 -50
  95. package/src/migrate/converter.ts +3 -3
  96. package/src/migrate/fragment-to-contract.ts +253 -0
  97. package/src/migrate/report.ts +1 -1
  98. package/src/scripts/token-benchmark.ts +121 -0
  99. package/src/service/__tests__/props-extractor.test.ts +94 -0
  100. package/src/service/__tests__/token-normalizer.test.ts +690 -0
  101. package/src/service/ast-utils.ts +4 -23
  102. package/src/service/babel-config.ts +23 -0
  103. package/src/service/enhance/converter.ts +61 -0
  104. package/src/service/enhance/props-extractor.ts +25 -8
  105. package/src/service/enhance/scanner.ts +5 -24
  106. package/src/service/snippet-validation.ts +9 -3
  107. package/src/service/token-normalizer.ts +510 -0
  108. package/src/shared/index.ts +1 -0
  109. package/src/shared/project-fields.ts +46 -0
  110. package/src/viewer/__tests__/viewer-integration.test.ts +8 -8
  111. package/src/viewer/preview-adapter.ts +116 -0
  112. package/src/viewer/style-utils.ts +27 -412
  113. package/src/viewer/vite-plugin.ts +2 -2
  114. package/dist/chunk-55KERLWL.js.map +0 -1
  115. package/dist/chunk-5A6X2Y73.js.map +0 -1
  116. package/dist/chunk-APTQIBS5.js.map +0 -1
  117. package/dist/chunk-EYXVAMEX.js.map +0 -1
  118. package/dist/chunk-I34BC3CU.js.map +0 -1
  119. package/dist/chunk-LOYS64QS.js.map +0 -1
  120. package/dist/chunk-ZKTFKHWN.js +0 -324
  121. package/dist/chunk-ZKTFKHWN.js.map +0 -1
  122. package/dist/discovery-VDANZAJ2.js +0 -28
  123. package/dist/init-WRUSW7R5.js.map +0 -1
  124. package/dist/scan-YJHQIRKG.js +0 -14
  125. package/dist/scan-generate-TFZVL3BT.js.map +0 -1
  126. package/dist/viewer-2TZS3NDL.js +0 -2730
  127. package/dist/viewer-2TZS3NDL.js.map +0 -1
  128. package/src/commands/dev.ts +0 -107
  129. /package/dist/{chunk-TXFCEDOC.js.map → chunk-2WXKALIG.js.map} +0 -0
  130. /package/dist/{discovery-VDANZAJ2.js.map → codebase-scanner-VOTPXRYW.js.map} +0 -0
  131. /package/dist/{init-cloud-REQ3XLHO.js.map → init-cloud-MQ6GRJAZ.js.map} +0 -0
  132. /package/dist/{scan-YJHQIRKG.js.map → scan-VNNKACG2.js.map} +0 -0
  133. /package/dist/{service-HKJ6B7P7.js.map → scanner-7LAZYPWZ.js.map} +0 -0
  134. /package/dist/{static-viewer-DUVC4UIM.js.map → service-FHQU7YS7.js.map} +0 -0
  135. /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-I34BC3CU.js";
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 resolve5 = this.waitingQueue.shift();
76
- resolve5(context);
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 resolve5 of this.waitingQueue) {
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((resolve5, reject) => {
193
+ return new Promise((resolve4, reject) => {
191
194
  const timeout = setTimeout(() => {
192
- const index = this.waitingQueue.indexOf(resolve5);
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
- resolve5(ctx);
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 resolve5;
291
+ let resolve4;
289
292
  let reject;
290
293
  const promise = new Promise((res, rej) => {
291
- resolve5 = res;
294
+ resolve4 = res;
292
295
  reject = rej;
293
296
  });
294
- return { promise, resolve: resolve5, reject };
297
+ return { promise, resolve: resolve4, reject };
295
298
  }
296
299
  function sleep(ms) {
297
- return new Promise((resolve5) => setTimeout(resolve5, ms));
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((resolve5) => setTimeout(resolve5, timeoutMs))
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
- (t5) => t5.theme === theme || t5.theme === "default"
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, PARSER_OPTIONS);
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, PARSER_OPTIONS);
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/scanner.ts
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 { createHash as createHash2 } from "crypto";
4601
- import { join as join3, dirname as dirname3 } from "path";
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 readFile7(filePath, "utf-8");
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 = parse3(source, {
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
- traverse3(ast, {
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 (t3.isTSPropertySignature(member) && t3.isIdentifier(member.key)) {
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 (t3.isTSTypeLiteral(node.typeAnnotation)) {
4219
+ if (t2.isTSTypeLiteral(node.typeAnnotation)) {
5119
4220
  for (const member of node.typeAnnotation.members) {
5120
- if (t3.isTSPropertySignature(member) && t3.isIdentifier(member.key)) {
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 (t3.isTSStringKeyword(node)) return "string";
5137
- if (t3.isTSNumberKeyword(node)) return "number";
5138
- if (t3.isTSBooleanKeyword(node)) return "boolean";
5139
- if (t3.isTSNullKeyword(node)) return "null";
5140
- if (t3.isTSUndefinedKeyword(node)) return "undefined";
5141
- if (t3.isTSVoidKeyword(node)) return "void";
5142
- if (t3.isTSAnyKeyword(node)) return "any";
5143
- if (t3.isTSNeverKeyword(node)) return "never";
5144
- if (t3.isTSUnknownKeyword(node)) return "unknown";
5145
- if (t3.isTSLiteralType(node)) {
5146
- if (t3.isStringLiteral(node.literal)) return `"${node.literal.value}"`;
5147
- if (t3.isNumericLiteral(node.literal)) return String(node.literal.value);
5148
- if (t3.isBooleanLiteral(node.literal)) return String(node.literal.value);
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 (t3.isTSTypeReference(node)) {
5152
- if (t3.isIdentifier(node.typeName)) {
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 (t3.isTSUnionType(node)) {
4262
+ if (t2.isTSUnionType(node)) {
5162
4263
  return node.types.map(typeAnnotationToString).join(" | ");
5163
4264
  }
5164
- if (t3.isTSIntersectionType(node)) {
4265
+ if (t2.isTSIntersectionType(node)) {
5165
4266
  return node.types.map(typeAnnotationToString).join(" & ");
5166
4267
  }
5167
- if (t3.isTSArrayType(node)) {
4268
+ if (t2.isTSArrayType(node)) {
5168
4269
  return `${typeAnnotationToString(node.elementType)}[]`;
5169
4270
  }
5170
- if (t3.isTSFunctionType(node)) {
4271
+ if (t2.isTSFunctionType(node)) {
5171
4272
  return "(...args: any[]) => any";
5172
4273
  }
5173
- if (t3.isTSTupleType(node)) {
4274
+ if (t2.isTSTupleType(node)) {
5174
4275
  const elements = node.elementTypes.map((el) => {
5175
- if (t3.isTSNamedTupleMember(el)) {
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 (t3.isTSTypeLiteral(node)) {
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 (t3.isExportDefaultDeclaration(parent)) return true;
5193
- if (t3.isExportNamedDeclaration(parent)) return true;
5194
- if (t3.isProgram(parent) && path.node.id && /^[A-Z]/.test(path.node.id.name)) {
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 (!t3.isExportNamedDeclaration(parent)) return false;
4302
+ if (!t2.isExportNamedDeclaration(parent)) return false;
5202
4303
  for (const declarator of path.node.declarations) {
5203
- if (t3.isIdentifier(declarator.id) && /^[A-Z]/.test(declarator.id.name) && (t3.isArrowFunctionExpression(declarator.init) || t3.isFunctionExpression(declarator.init))) {
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 = basename2(filePath);
4311
+ const fileName = basename(filePath);
5211
4312
  let name = fileName.replace(/\.(tsx?|jsx?)$/, "");
5212
4313
  if (name === "index") {
5213
- name = basename2(dirname5(filePath));
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 = join4(dir, pattern);
5253
- if (existsSync3(fullPath)) {
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 parse4 } from "@babel/parser";
5279
- import _traverse3 from "@babel/traverse";
5280
- import * as t4 from "@babel/types";
5281
- import { readFile as readFile8 } from "fs/promises";
5282
- import fg2 from "fast-glob";
5283
- import { basename as basename3, relative as relative3 } from "path";
5284
- var traverse4 = _traverse3.default || _traverse3;
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 readFile8(filePath, "utf-8");
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 = parse4(source, {
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
- traverse4(ast, {
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
- traverse4(ast, {
4420
+ traverse3(ast, {
5320
4421
  ExportNamedDeclaration(path) {
5321
4422
  const declaration = path.node.declaration;
5322
- if (t4.isVariableDeclaration(declaration)) {
4423
+ if (t3.isVariableDeclaration(declaration)) {
5323
4424
  for (const declarator of declaration.declarations) {
5324
- if (!t4.isIdentifier(declarator.id)) continue;
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 (t4.isObjectExpression(node)) {
4450
+ if (t3.isObjectExpression(node)) {
5350
4451
  return node;
5351
4452
  }
5352
- if (t4.isIdentifier(node)) {
4453
+ if (t3.isIdentifier(node)) {
5353
4454
  const metaName = node.name;
5354
4455
  let foundObject = null;
5355
- traverse4(ast, {
4456
+ traverse3(ast, {
5356
4457
  VariableDeclarator(path) {
5357
- if (t4.isIdentifier(path.node.id) && path.node.id.name === metaName && t4.isObjectExpression(path.node.init)) {
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 (t4.isTSAsExpression(node) && t4.isObjectExpression(node.expression)) {
4465
+ if (t3.isTSAsExpression(node) && t3.isObjectExpression(node.expression)) {
5365
4466
  return node.expression;
5366
4467
  }
5367
- if (t4.isTSSatisfiesExpression && t4.isTSSatisfiesExpression(node) && t4.isObjectExpression(node.expression)) {
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 (!t4.isObjectProperty(prop) || !t4.isIdentifier(prop.key)) continue;
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 (t4.isStringLiteral(prop.value)) {
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 (t4.isIdentifier(prop.value)) {
4489
+ if (t3.isIdentifier(prop.value)) {
5389
4490
  meta.componentName = prop.value.name;
5390
4491
  }
5391
4492
  break;
5392
4493
  case "parameters":
5393
- if (t4.isObjectExpression(prop.value)) {
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 (t4.isObjectExpression(prop.value)) {
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 (!t4.isObjectProperty(prop) || !t4.isIdentifier(prop.key)) continue;
4517
+ if (!t3.isObjectProperty(prop) || !t3.isIdentifier(prop.key)) continue;
5417
4518
  const propName = prop.key.name;
5418
4519
  const argType = {};
5419
- if (t4.isObjectExpression(prop.value)) {
4520
+ if (t3.isObjectExpression(prop.value)) {
5420
4521
  for (const inner of prop.value.properties) {
5421
- if (!t4.isObjectProperty(inner) || !t4.isIdentifier(inner.key)) continue;
4522
+ if (!t3.isObjectProperty(inner) || !t3.isIdentifier(inner.key)) continue;
5422
4523
  const innerKey = inner.key.name;
5423
- if (innerKey === "description" && t4.isStringLiteral(inner.value)) {
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 (t4.isStringLiteral(inner.value)) {
4531
+ if (t3.isStringLiteral(inner.value)) {
5431
4532
  argType.control = inner.value.value;
5432
- } else if (t4.isObjectExpression(inner.value)) {
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" && t4.isArrayExpression(inner.value)) {
5438
- argType.options = inner.value.elements.filter((el) => t4.isStringLiteral(el)).map((el) => el.value);
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 (t4.isObjectExpression(init)) {
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 (t4.isTSAsExpression(init) && t4.isObjectExpression(init.expression)) {
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 (t4.isArrowFunctionExpression(init) || t4.isFunctionExpression(init)) {
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 (t4.isTSSatisfiesExpression && t4.isTSSatisfiesExpression(init)) {
5474
- if (t4.isObjectExpression(init.expression)) {
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 (!t4.isObjectProperty(prop) || !t4.isIdentifier(prop.key)) continue;
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 (t4.isStringLiteral(prop.value)) {
4589
+ if (t3.isStringLiteral(prop.value)) {
5489
4590
  story.displayName = prop.value.value;
5490
4591
  }
5491
4592
  break;
5492
4593
  case "args":
5493
- if (t4.isObjectExpression(prop.value)) {
4594
+ if (t3.isObjectExpression(prop.value)) {
5494
4595
  story.args = extractObjectLiteral(prop.value);
5495
4596
  }
5496
4597
  break;
5497
4598
  case "parameters":
5498
- if (t4.isObjectExpression(prop.value)) {
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 (t4.isStringLiteral(node)) return node.value;
5514
- if (t4.isNumericLiteral(node)) return node.value;
5515
- if (t4.isBooleanLiteral(node)) return node.value;
5516
- if (t4.isNullLiteral(node)) return null;
5517
- if (t4.isIdentifier(node) && node.name === "undefined") return void 0;
5518
- if (t4.isArrayExpression(node)) {
5519
- return node.elements.filter((el) => el !== null && t4.isExpression(el)).map(extractLiteralValue);
5520
- }
5521
- if (t4.isObjectExpression(node)) {
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 (t4.isSpreadElement(prop)) continue;
5530
- if (!t4.isObjectProperty(prop)) continue;
4630
+ if (t3.isSpreadElement(prop)) continue;
4631
+ if (!t3.isObjectProperty(prop)) continue;
5531
4632
  let key;
5532
- if (t4.isIdentifier(prop.key)) {
4633
+ if (t3.isIdentifier(prop.key)) {
5533
4634
  key = prop.key.name;
5534
- } else if (t4.isStringLiteral(prop.key)) {
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 = basename3(filePath);
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 fg2(include, {
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 ${relative3(rootDir, filePath)}:`,
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 readFile9 } from "fs/promises";
5922
- import { existsSync as existsSync4 } from "fs";
5923
- import { basename as basename4, dirname as dirname7, join as join6, resolve as resolve4 } from "path";
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 = resolve4(filePath);
5926
- if (!existsSync4(absPath)) {
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 readFile9(absPath, "utf-8");
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 && (ts.isArrowFunction(decl.initializer) || ts.isFunctionExpression(decl.initializer))) {
6225
- targetFunc = decl.initializer;
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 = basename4(filePath);
5455
+ const fileName = basename3(filePath);
6344
5456
  let name = fileName.replace(/\.(tsx?|jsx?)$/, "");
6345
5457
  if (name === "index") {
6346
- name = basename4(dirname7(filePath));
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 = join6(dir, pattern);
6395
- if (existsSync4(fullPath)) {
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-APTQIBS5.js.map
5874
+ //# sourceMappingURL=chunk-XJQ5BIWI.js.map