@fragments-sdk/cli 0.14.3 → 0.15.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (181) hide show
  1. package/README.md +0 -3
  2. package/dist/{ai-client-I6MDWNYA.js → ai-client-LSLQGOMM.js} +1 -2
  3. package/dist/bin.js +4745 -3817
  4. package/dist/bin.js.map +1 -1
  5. package/dist/{chunk-TXFCEDOC.js → chunk-2WXKALIG.js} +2 -2
  6. package/dist/{chunk-I34BC3CU.js → chunk-32LIWN2P.js} +1006 -3
  7. package/dist/chunk-32LIWN2P.js.map +1 -0
  8. package/dist/chunk-5JF26E55.js +1255 -0
  9. package/dist/chunk-5JF26E55.js.map +1 -0
  10. package/dist/{chunk-APTQIBS5.js → chunk-6SQPP47U.js} +153 -1342
  11. package/dist/chunk-6SQPP47U.js.map +1 -0
  12. package/dist/chunk-7DZC4YEV.js +294 -0
  13. package/dist/chunk-7DZC4YEV.js.map +1 -0
  14. package/dist/{chunk-PJT5IZ37.js → chunk-BJE3425I.js} +19 -52
  15. package/dist/{chunk-PJT5IZ37.js.map → chunk-BJE3425I.js.map} +1 -1
  16. package/dist/{chunk-55KERLWL.js → chunk-HQ6A6DTV.js} +1587 -1073
  17. package/dist/chunk-HQ6A6DTV.js.map +1 -0
  18. package/dist/chunk-MHIBEEW4.js +511 -0
  19. package/dist/chunk-MHIBEEW4.js.map +1 -0
  20. package/dist/{chunk-5A6X2Y73.js → chunk-ONUP6Z4W.js} +25 -13
  21. package/dist/chunk-ONUP6Z4W.js.map +1 -0
  22. package/dist/chunk-QCN35LJU.js +630 -0
  23. package/dist/chunk-QCN35LJU.js.map +1 -0
  24. package/dist/chunk-T47OLCSF.js +36 -0
  25. package/dist/chunk-T47OLCSF.js.map +1 -0
  26. package/dist/codebase-scanner-MQHUZC2G.js +21 -0
  27. package/dist/converter-7XM3Y6NJ.js +33 -0
  28. package/dist/converter-7XM3Y6NJ.js.map +1 -0
  29. package/dist/core/index.js +43 -2
  30. package/dist/create-IH4R45GE.js +806 -0
  31. package/dist/create-IH4R45GE.js.map +1 -0
  32. package/dist/{generate-RYWIPDN2.js → generate-PVOLUAAC.js} +4 -6
  33. package/dist/{generate-RYWIPDN2.js.map → generate-PVOLUAAC.js.map} +1 -1
  34. package/dist/govern-scan-OYFZYOQW.js +413 -0
  35. package/dist/govern-scan-OYFZYOQW.js.map +1 -0
  36. package/dist/index.d.ts +4 -23
  37. package/dist/index.js +15 -14
  38. package/dist/index.js.map +1 -1
  39. package/dist/{init-WRUSW7R5.js → init-SSGUSP7Z.js} +131 -129
  40. package/dist/init-SSGUSP7Z.js.map +1 -0
  41. package/dist/{init-cloud-REQ3XLHO.js → init-cloud-3DNKPWFB.js} +30 -5
  42. package/dist/{init-cloud-REQ3XLHO.js.map → init-cloud-3DNKPWFB.js.map} +1 -1
  43. package/dist/mcp-bin.js +5 -37
  44. package/dist/mcp-bin.js.map +1 -1
  45. package/dist/node-37AUE74M.js +65 -0
  46. package/dist/push-contracts-WY32TFP6.js +84 -0
  47. package/dist/push-contracts-WY32TFP6.js.map +1 -0
  48. package/dist/scan-PKSYSTRR.js +15 -0
  49. package/dist/{scan-generate-TFZVL3BT.js → scan-generate-VY27PIOX.js} +340 -52
  50. package/dist/scan-generate-VY27PIOX.js.map +1 -0
  51. package/dist/scanner-4KZNOXAK.js +12 -0
  52. package/dist/{service-HKJ6B7P7.js → service-QJGWUIVL.js} +41 -30
  53. package/dist/{snapshot-C5DYIGIV.js → snapshot-WIJMEIFT.js} +2 -3
  54. package/dist/{snapshot-C5DYIGIV.js.map → snapshot-WIJMEIFT.js.map} +1 -1
  55. package/dist/{static-viewer-DUVC4UIM.js → static-viewer-7QIBQZRC.js} +3 -4
  56. package/dist/static-viewer-7QIBQZRC.js.map +1 -0
  57. package/dist/{test-JW7JIDFG.js → test-64Z5BKBA.js} +4 -7
  58. package/dist/{test-JW7JIDFG.js.map → test-64Z5BKBA.js.map} +1 -1
  59. package/dist/token-normalizer-TEPOVBPV.js +312 -0
  60. package/dist/token-normalizer-TEPOVBPV.js.map +1 -0
  61. package/dist/token-parser-32KOIOFN.js +22 -0
  62. package/dist/token-parser-32KOIOFN.js.map +1 -0
  63. package/dist/{tokens-KE73G5JC.js → tokens-NZWFQIAB.js} +10 -9
  64. package/dist/{tokens-KE73G5JC.js.map → tokens-NZWFQIAB.js.map} +1 -1
  65. package/dist/tokens-generate-5JQSJ27E.js +85 -0
  66. package/dist/tokens-generate-5JQSJ27E.js.map +1 -0
  67. package/dist/tokens-push-HY3KO36V.js +148 -0
  68. package/dist/tokens-push-HY3KO36V.js.map +1 -0
  69. package/package.json +8 -6
  70. package/src/bin.ts +300 -48
  71. package/src/commands/__fixtures__/shadcn-label-wrapper/package.json +7 -0
  72. package/src/commands/__fixtures__/shadcn-label-wrapper/src/components/ui/label.contract.json +42 -0
  73. package/src/commands/__fixtures__/shadcn-label-wrapper/src/components/ui/label.tsx +11 -0
  74. package/src/commands/__fixtures__/shadcn-label-wrapper/src/components/ui/primitive.contract.json +20 -0
  75. package/src/commands/__fixtures__/shadcn-label-wrapper/src/components/ui/primitive.tsx +14 -0
  76. package/src/commands/__fixtures__/shadcn-label-wrapper/tsconfig.app.json +23 -0
  77. package/src/commands/__tests__/build-freshness.test.ts +231 -0
  78. package/src/commands/__tests__/create.test.ts +71 -0
  79. package/src/commands/__tests__/drift-sync.test.ts +1 -1
  80. package/src/commands/__tests__/govern.test.ts +258 -0
  81. package/src/commands/__tests__/init.test.ts +113 -0
  82. package/src/commands/__tests__/scan-generate.test.ts +189 -70
  83. package/src/commands/__tests__/verify.test.ts +91 -0
  84. package/src/commands/build.ts +54 -1
  85. package/src/commands/context.ts +1 -1
  86. package/src/commands/create.ts +536 -0
  87. package/src/commands/discover.ts +151 -0
  88. package/src/commands/doctor.ts +3 -2
  89. package/src/commands/enhance.ts +3 -1
  90. package/src/commands/govern-scan.ts +565 -0
  91. package/src/commands/govern.ts +67 -4
  92. package/src/commands/init-cloud.ts +32 -4
  93. package/src/commands/init.ts +152 -28
  94. package/src/commands/inspect.ts +290 -0
  95. package/src/commands/migrate-contract.ts +85 -0
  96. package/src/commands/push-contracts.ts +112 -0
  97. package/src/commands/scan-generate.ts +439 -51
  98. package/src/commands/scan.ts +14 -0
  99. package/src/commands/setup.ts +27 -50
  100. package/src/commands/sync.ts +2 -2
  101. package/src/commands/tokens-generate.ts +113 -0
  102. package/src/commands/tokens-push.ts +199 -0
  103. package/src/commands/verify.ts +195 -1
  104. package/src/core/__fixtures__/shadcn-input/input.tsx +7 -0
  105. package/src/core/__fixtures__/shadcn-input/tsconfig.json +14 -0
  106. package/src/core/__fixtures__/shadcn-label/label.tsx +11 -0
  107. package/src/core/__fixtures__/shadcn-label/primitive.tsx +14 -0
  108. package/src/core/__fixtures__/shadcn-label/tsconfig.json +14 -0
  109. package/src/core/__fixtures__/shadcn-radix-label/label.tsx +11 -0
  110. package/src/core/__fixtures__/shadcn-radix-label/node_modules/radix-ui/index.d.ts +12 -0
  111. package/src/core/__fixtures__/shadcn-radix-label/tsconfig.json +14 -0
  112. package/src/core/__tests__/contract-parity.test.ts +316 -0
  113. package/src/core/__tests__/token-resolver.test.ts +1 -1
  114. package/src/core/component-extractor.test.ts +40 -1
  115. package/src/core/config.ts +2 -1
  116. package/src/core/discovery.ts +13 -2
  117. package/src/core/drift-verifier.ts +123 -0
  118. package/src/core/extractor-adapter.ts +80 -0
  119. package/src/index.ts +3 -3
  120. package/src/mcp/__tests__/projectFields.test.ts +1 -1
  121. package/src/mcp/utils.ts +1 -50
  122. package/src/migrate/converter.ts +3 -3
  123. package/src/migrate/fragment-to-contract.ts +253 -0
  124. package/src/migrate/report.ts +1 -1
  125. package/src/scripts/token-benchmark.ts +121 -0
  126. package/src/service/__tests__/props-extractor.test.ts +94 -0
  127. package/src/service/__tests__/token-normalizer.test.ts +690 -0
  128. package/src/service/ast-utils.ts +4 -23
  129. package/src/service/babel-config.ts +23 -0
  130. package/src/service/enhance/converter.ts +61 -0
  131. package/src/service/enhance/props-extractor.ts +25 -8
  132. package/src/service/enhance/scanner.ts +5 -24
  133. package/src/service/index.ts +8 -0
  134. package/src/service/snippet-validation.ts +9 -3
  135. package/src/service/tailwind-v4-parser.ts +314 -0
  136. package/src/service/token-normalizer.ts +510 -0
  137. package/src/service/token-parser.ts +56 -0
  138. package/src/setup.ts +10 -39
  139. package/src/shared/index.ts +1 -0
  140. package/src/shared/project-fields.ts +46 -0
  141. package/src/theme/__tests__/component-contrast.test.ts +2 -2
  142. package/src/theme/__tests__/serializer.test.ts +1 -1
  143. package/src/theme/generator.ts +16 -1
  144. package/src/theme/schema.ts +8 -0
  145. package/src/theme/serializer.ts +13 -9
  146. package/src/theme/types.ts +8 -0
  147. package/src/validators.ts +1 -2
  148. package/src/viewer/__tests__/viewer-integration.test.ts +8 -8
  149. package/src/viewer/style-utils.ts +27 -412
  150. package/src/viewer/vite-plugin.ts +2 -2
  151. package/dist/chunk-55KERLWL.js.map +0 -1
  152. package/dist/chunk-5A6X2Y73.js.map +0 -1
  153. package/dist/chunk-APTQIBS5.js.map +0 -1
  154. package/dist/chunk-EYXVAMEX.js +0 -626
  155. package/dist/chunk-EYXVAMEX.js.map +0 -1
  156. package/dist/chunk-I34BC3CU.js.map +0 -1
  157. package/dist/chunk-LOYS64QS.js +0 -2453
  158. package/dist/chunk-LOYS64QS.js.map +0 -1
  159. package/dist/chunk-Z7EY4VHE.js +0 -50
  160. package/dist/chunk-ZKTFKHWN.js +0 -324
  161. package/dist/chunk-ZKTFKHWN.js.map +0 -1
  162. package/dist/discovery-VDANZAJ2.js +0 -28
  163. package/dist/init-WRUSW7R5.js.map +0 -1
  164. package/dist/sass.node-4XJK6YBF.js +0 -130708
  165. package/dist/sass.node-4XJK6YBF.js.map +0 -1
  166. package/dist/scan-YJHQIRKG.js +0 -14
  167. package/dist/scan-generate-TFZVL3BT.js.map +0 -1
  168. package/dist/viewer-2TZS3NDL.js +0 -2730
  169. package/dist/viewer-2TZS3NDL.js.map +0 -1
  170. package/src/build.ts +0 -612
  171. package/src/commands/dev.ts +0 -107
  172. package/src/core/auto-props.ts +0 -464
  173. package/src/core/component-extractor.ts +0 -1030
  174. package/src/core/token-resolver.ts +0 -155
  175. /package/dist/{ai-client-I6MDWNYA.js.map → ai-client-LSLQGOMM.js.map} +0 -0
  176. /package/dist/{chunk-TXFCEDOC.js.map → chunk-2WXKALIG.js.map} +0 -0
  177. /package/dist/{chunk-Z7EY4VHE.js.map → codebase-scanner-MQHUZC2G.js.map} +0 -0
  178. /package/dist/{discovery-VDANZAJ2.js.map → node-37AUE74M.js.map} +0 -0
  179. /package/dist/{scan-YJHQIRKG.js.map → scan-PKSYSTRR.js.map} +0 -0
  180. /package/dist/{service-HKJ6B7P7.js.map → scanner-4KZNOXAK.js.map} +0 -0
  181. /package/dist/{static-viewer-DUVC4UIM.js.map → service-QJGWUIVL.js.map} +0 -0
@@ -0,0 +1,84 @@
1
+ import { createRequire as __banner_createRequire } from 'module'; const require = __banner_createRequire(import.meta.url);
2
+ import "./chunk-D2CDBRNU.js";
3
+ import {
4
+ BRAND
5
+ } from "./chunk-32LIWN2P.js";
6
+
7
+ // src/commands/push-contracts.ts
8
+ import { readFileSync, existsSync } from "fs";
9
+ import { resolve } from "path";
10
+ import pc from "picocolors";
11
+ async function pushContracts(options = {}) {
12
+ const quiet = options.quiet ?? false;
13
+ const apiKey = options.apiKey ?? process.env.FRAGMENTS_API_KEY;
14
+ if (!apiKey) {
15
+ console.error(
16
+ pc.red("No API key found. Set FRAGMENTS_API_KEY or pass --api-key.")
17
+ );
18
+ return { exitCode: 1 };
19
+ }
20
+ if (!quiet) {
21
+ console.log(pc.cyan(`
22
+ ${BRAND.name} Push Contracts
23
+ `));
24
+ }
25
+ const inputPath = options.input ? resolve(options.input) : resolve("fragments.json");
26
+ if (!existsSync(inputPath)) {
27
+ console.error(
28
+ pc.red(`Contract registry not found at ${inputPath}`)
29
+ );
30
+ console.error(
31
+ pc.dim("Run `fragments build` or `fragments scan` to generate fragments.json first.")
32
+ );
33
+ return { exitCode: 1 };
34
+ }
35
+ let contractRegistry;
36
+ let componentCount = 0;
37
+ try {
38
+ const raw = readFileSync(inputPath, "utf-8");
39
+ const parsed = JSON.parse(raw);
40
+ const fragments = parsed.fragments ?? parsed;
41
+ if (!Array.isArray(fragments)) {
42
+ console.error(pc.red("Invalid contract registry: expected fragments array"));
43
+ return { exitCode: 1 };
44
+ }
45
+ componentCount = fragments.length;
46
+ contractRegistry = JSON.stringify({ fragments });
47
+ } catch (error) {
48
+ console.error(
49
+ pc.red("Failed to parse contract registry:"),
50
+ error instanceof Error ? error.message : "unknown error"
51
+ );
52
+ return { exitCode: 1 };
53
+ }
54
+ if (componentCount === 0) {
55
+ console.warn(pc.yellow("No components found in contract registry. Nothing to push."));
56
+ return { exitCode: 0 };
57
+ }
58
+ if (!quiet) {
59
+ console.log(pc.dim(` Found ${componentCount} component contracts
60
+ `));
61
+ console.log(pc.dim(" Pushing to Fragments Cloud...\n"));
62
+ }
63
+ const { pushContracts: push } = await import("@fragments-sdk/govern");
64
+ const result = await push({
65
+ contractRegistry,
66
+ url: options.url,
67
+ apiKey
68
+ });
69
+ if (!result.ok) {
70
+ console.error(pc.red(`Failed to push contracts: ${result.error}`));
71
+ return { exitCode: 1 };
72
+ }
73
+ if (!quiet) {
74
+ console.log(
75
+ pc.green(` \u2713 Pushed ${componentCount} component contracts to Fragments Cloud
76
+ `)
77
+ );
78
+ }
79
+ return { exitCode: 0 };
80
+ }
81
+ export {
82
+ pushContracts
83
+ };
84
+ //# sourceMappingURL=push-contracts-WY32TFP6.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/commands/push-contracts.ts"],"sourcesContent":["/**\n * fragments push-contracts\n *\n * Push compiled component contracts to Fragments Cloud.\n * Reads fragments.json from the project root (or builds it from .contract.json files)\n * and posts the contract registry to the Cloud ingest endpoint.\n */\n\nimport { readFileSync, existsSync } from 'node:fs';\nimport { resolve } from 'node:path';\nimport pc from 'picocolors';\nimport { BRAND } from '../core/index.js';\n\nexport interface PushContractsOptions {\n /** Path to fragments.json or directory containing .contract.json files */\n input?: string;\n /** Fragments Cloud URL */\n url?: string;\n /** API key (defaults to FRAGMENTS_API_KEY env var) */\n apiKey?: string;\n /** Suppress output */\n quiet?: boolean;\n}\n\nexport async function pushContracts(\n options: PushContractsOptions = {},\n): Promise<{ exitCode: number }> {\n const quiet = options.quiet ?? false;\n const apiKey = options.apiKey ?? process.env.FRAGMENTS_API_KEY;\n\n if (!apiKey) {\n console.error(\n pc.red('No API key found. Set FRAGMENTS_API_KEY or pass --api-key.'),\n );\n return { exitCode: 1 };\n }\n\n if (!quiet) {\n console.log(pc.cyan(`\\n${BRAND.name} Push Contracts\\n`));\n }\n\n // 1. Find contract registry\n const inputPath = options.input\n ? resolve(options.input)\n : resolve('fragments.json');\n\n if (!existsSync(inputPath)) {\n console.error(\n pc.red(`Contract registry not found at ${inputPath}`),\n );\n console.error(\n pc.dim('Run `fragments build` or `fragments scan` to generate fragments.json first.'),\n );\n return { exitCode: 1 };\n }\n\n // 2. Parse and validate\n let contractRegistry: string;\n let componentCount = 0;\n\n try {\n const raw = readFileSync(inputPath, 'utf-8');\n const parsed = JSON.parse(raw);\n const fragments = parsed.fragments ?? parsed;\n\n if (!Array.isArray(fragments)) {\n console.error(pc.red('Invalid contract registry: expected fragments array'));\n return { exitCode: 1 };\n }\n\n componentCount = fragments.length;\n contractRegistry = JSON.stringify({ fragments });\n } catch (error) {\n console.error(\n pc.red('Failed to parse contract registry:'),\n error instanceof Error ? error.message : 'unknown error',\n );\n return { exitCode: 1 };\n }\n\n if (componentCount === 0) {\n console.warn(pc.yellow('No components found in contract registry. Nothing to push.'));\n return { exitCode: 0 };\n }\n\n if (!quiet) {\n console.log(pc.dim(` Found ${componentCount} component contracts\\n`));\n console.log(pc.dim(' Pushing to Fragments Cloud...\\n'));\n }\n\n // 3. Push to Cloud\n const { pushContracts: push } = await import('@fragments-sdk/govern');\n\n const result = await push({\n contractRegistry,\n url: options.url,\n apiKey,\n });\n\n if (!result.ok) {\n console.error(pc.red(`Failed to push contracts: ${result.error}`));\n return { exitCode: 1 };\n }\n\n if (!quiet) {\n console.log(\n pc.green(` ✓ Pushed ${componentCount} component contracts to Fragments Cloud\\n`),\n );\n }\n\n return { exitCode: 0 };\n}\n"],"mappings":";;;;;;;AAQA,SAAS,cAAc,kBAAkB;AACzC,SAAS,eAAe;AACxB,OAAO,QAAQ;AAcf,eAAsB,cACpB,UAAgC,CAAC,GACF;AAC/B,QAAM,QAAQ,QAAQ,SAAS;AAC/B,QAAM,SAAS,QAAQ,UAAU,QAAQ,IAAI;AAE7C,MAAI,CAAC,QAAQ;AACX,YAAQ;AAAA,MACN,GAAG,IAAI,4DAA4D;AAAA,IACrE;AACA,WAAO,EAAE,UAAU,EAAE;AAAA,EACvB;AAEA,MAAI,CAAC,OAAO;AACV,YAAQ,IAAI,GAAG,KAAK;AAAA,EAAK,MAAM,IAAI;AAAA,CAAmB,CAAC;AAAA,EACzD;AAGA,QAAM,YAAY,QAAQ,QACtB,QAAQ,QAAQ,KAAK,IACrB,QAAQ,gBAAgB;AAE5B,MAAI,CAAC,WAAW,SAAS,GAAG;AAC1B,YAAQ;AAAA,MACN,GAAG,IAAI,kCAAkC,SAAS,EAAE;AAAA,IACtD;AACA,YAAQ;AAAA,MACN,GAAG,IAAI,6EAA6E;AAAA,IACtF;AACA,WAAO,EAAE,UAAU,EAAE;AAAA,EACvB;AAGA,MAAI;AACJ,MAAI,iBAAiB;AAErB,MAAI;AACF,UAAM,MAAM,aAAa,WAAW,OAAO;AAC3C,UAAM,SAAS,KAAK,MAAM,GAAG;AAC7B,UAAM,YAAY,OAAO,aAAa;AAEtC,QAAI,CAAC,MAAM,QAAQ,SAAS,GAAG;AAC7B,cAAQ,MAAM,GAAG,IAAI,qDAAqD,CAAC;AAC3E,aAAO,EAAE,UAAU,EAAE;AAAA,IACvB;AAEA,qBAAiB,UAAU;AAC3B,uBAAmB,KAAK,UAAU,EAAE,UAAU,CAAC;AAAA,EACjD,SAAS,OAAO;AACd,YAAQ;AAAA,MACN,GAAG,IAAI,oCAAoC;AAAA,MAC3C,iBAAiB,QAAQ,MAAM,UAAU;AAAA,IAC3C;AACA,WAAO,EAAE,UAAU,EAAE;AAAA,EACvB;AAEA,MAAI,mBAAmB,GAAG;AACxB,YAAQ,KAAK,GAAG,OAAO,4DAA4D,CAAC;AACpF,WAAO,EAAE,UAAU,EAAE;AAAA,EACvB;AAEA,MAAI,CAAC,OAAO;AACV,YAAQ,IAAI,GAAG,IAAI,WAAW,cAAc;AAAA,CAAwB,CAAC;AACrE,YAAQ,IAAI,GAAG,IAAI,mCAAmC,CAAC;AAAA,EACzD;AAGA,QAAM,EAAE,eAAe,KAAK,IAAI,MAAM,OAAO,uBAAuB;AAEpE,QAAM,SAAS,MAAM,KAAK;AAAA,IACxB;AAAA,IACA,KAAK,QAAQ;AAAA,IACb;AAAA,EACF,CAAC;AAED,MAAI,CAAC,OAAO,IAAI;AACd,YAAQ,MAAM,GAAG,IAAI,6BAA6B,OAAO,KAAK,EAAE,CAAC;AACjE,WAAO,EAAE,UAAU,EAAE;AAAA,EACvB;AAEA,MAAI,CAAC,OAAO;AACV,YAAQ;AAAA,MACN,GAAG,MAAM,mBAAc,cAAc;AAAA,CAA2C;AAAA,IAClF;AAAA,EACF;AAEA,SAAO,EAAE,UAAU,EAAE;AACvB;","names":[]}
@@ -0,0 +1,15 @@
1
+ import { createRequire as __banner_createRequire } from 'module'; const require = __banner_createRequire(import.meta.url);
2
+ import {
3
+ scan
4
+ } from "./chunk-ONUP6Z4W.js";
5
+ import "./chunk-6SQPP47U.js";
6
+ import "./chunk-D2CDBRNU.js";
7
+ import "./chunk-HQ6A6DTV.js";
8
+ import "./chunk-32LIWN2P.js";
9
+ import "./chunk-MHIBEEW4.js";
10
+ import "./chunk-QCN35LJU.js";
11
+ import "./chunk-7DZC4YEV.js";
12
+ export {
13
+ scan
14
+ };
15
+ //# sourceMappingURL=scan-PKSYSTRR.js.map
@@ -1,22 +1,144 @@
1
1
  import { createRequire as __banner_createRequire } from 'module'; const require = __banner_createRequire(import.meta.url);
2
- import {
3
- createComponentExtractor
4
- } from "./chunk-EYXVAMEX.js";
5
- import "./chunk-55KERLWL.js";
2
+ import "./chunk-D2CDBRNU.js";
6
3
  import {
7
4
  discoverAllComponents
8
- } from "./chunk-ZKTFKHWN.js";
9
- import "./chunk-D2CDBRNU.js";
5
+ } from "./chunk-HQ6A6DTV.js";
10
6
  import {
11
7
  BRAND
12
- } from "./chunk-I34BC3CU.js";
13
- import "./chunk-Z7EY4VHE.js";
8
+ } from "./chunk-32LIWN2P.js";
14
9
 
15
10
  // src/commands/scan-generate.ts
16
11
  import { readFile, writeFile, access, mkdir } from "fs/promises";
17
12
  import { resolve, basename, dirname, relative, join } from "path";
13
+ import { execSync } from "child_process";
18
14
  import * as ts from "typescript";
19
15
  import pc from "picocolors";
16
+ import {
17
+ createComponentExtractor
18
+ } from "@fragments-sdk/extract";
19
+ async function findNearestAncestor(scanPath, targetFile) {
20
+ let dir = resolve(scanPath);
21
+ for (let i = 0; i < 20; i++) {
22
+ try {
23
+ await access(join(dir, targetFile));
24
+ return dir;
25
+ } catch {
26
+ const parent = dirname(dir);
27
+ if (parent === dir) break;
28
+ dir = parent;
29
+ }
30
+ }
31
+ return null;
32
+ }
33
+ async function findNearestPackageJsonDir(scanPath) {
34
+ return findNearestAncestor(scanPath, "package.json");
35
+ }
36
+ async function detectPackageManagerForProject(projectDir) {
37
+ let dir = resolve(projectDir);
38
+ for (let i = 0; i < 20; i++) {
39
+ for (const [fileName, manager] of [
40
+ ["pnpm-lock.yaml", "pnpm"],
41
+ ["pnpm-workspace.yaml", "pnpm"],
42
+ ["yarn.lock", "yarn"],
43
+ ["package-lock.json", "npm"]
44
+ ]) {
45
+ try {
46
+ await access(join(dir, fileName));
47
+ return { manager, lockfileDir: dir };
48
+ } catch {
49
+ }
50
+ }
51
+ const parent = dirname(dir);
52
+ if (parent === dir) break;
53
+ dir = parent;
54
+ }
55
+ return { manager: "npm", lockfileDir: null };
56
+ }
57
+ function resolveCoreInstallCommand(manager) {
58
+ switch (manager) {
59
+ case "pnpm":
60
+ return "pnpm add -D @fragments-sdk/core";
61
+ case "yarn":
62
+ return "yarn add -D @fragments-sdk/core";
63
+ default:
64
+ return "npm install -D @fragments-sdk/core";
65
+ }
66
+ }
67
+ async function ensureCoreDependency(scanPath) {
68
+ const projectDir = await findNearestPackageJsonDir(scanPath);
69
+ if (!projectDir) return;
70
+ const pkgJsonPath = join(projectDir, "package.json");
71
+ try {
72
+ const pkgRaw = await readFile(pkgJsonPath, "utf-8");
73
+ const pkg = JSON.parse(pkgRaw);
74
+ const deps = pkg.dependencies || {};
75
+ const devDeps = pkg.devDependencies || {};
76
+ if (deps["@fragments-sdk/core"] || devDeps["@fragments-sdk/core"]) {
77
+ console.log(pc.dim(" \xB7 @fragments-sdk/core already installed"));
78
+ return;
79
+ }
80
+ const { manager } = await detectPackageManagerForProject(projectDir);
81
+ const cmd = resolveCoreInstallCommand(manager);
82
+ execSync(cmd, { cwd: projectDir, stdio: "ignore" });
83
+ console.log(pc.green(" \u2713 Installed @fragments-sdk/core"));
84
+ } catch (e) {
85
+ console.log(
86
+ pc.yellow(
87
+ ` \u26A0 Could not auto-install @fragments-sdk/core: ${e instanceof Error ? e.message : String(e)}`
88
+ )
89
+ );
90
+ }
91
+ }
92
+ async function excludeFragmentsFromTsconfig(scanPath) {
93
+ const projectDir = await findNearestPackageJsonDir(scanPath);
94
+ if (!projectDir) return;
95
+ let tsconfigPath = null;
96
+ let tsconfigName = "";
97
+ for (const name of ["tsconfig.app.json", "tsconfig.json"]) {
98
+ const candidate = join(projectDir, name);
99
+ try {
100
+ await access(candidate);
101
+ tsconfigPath = candidate;
102
+ tsconfigName = name;
103
+ break;
104
+ } catch {
105
+ }
106
+ }
107
+ if (!tsconfigPath) return;
108
+ try {
109
+ const raw = await readFile(tsconfigPath, "utf-8");
110
+ const parsed = ts.parseConfigFileTextToJson(tsconfigPath, raw);
111
+ if (parsed.error || !parsed.config) {
112
+ throw new Error(parsed.error?.messageText?.toString() ?? "Unable to parse tsconfig");
113
+ }
114
+ const config = parsed.config;
115
+ const exclude = Array.isArray(config.exclude) ? [...config.exclude] : [];
116
+ const ext = BRAND.fileExtension;
117
+ const alreadyExcluded = exclude.some(
118
+ (pattern) => pattern.includes(`*${ext}`) || pattern.includes("*.fragment.*") || pattern.includes("*.contract.*")
119
+ );
120
+ if (alreadyExcluded) {
121
+ console.log(
122
+ pc.dim(` \xB7 ${ext} already excluded in ${tsconfigName}`)
123
+ );
124
+ return;
125
+ }
126
+ exclude.push(`**/*${ext}`);
127
+ config.exclude = exclude;
128
+ const updated = `${JSON.stringify(config, null, 2)}
129
+ `;
130
+ await writeFile(tsconfigPath, updated, "utf-8");
131
+ console.log(
132
+ pc.green(` \u2713 Added ${ext} exclusion to ${tsconfigName}`)
133
+ );
134
+ } catch (e) {
135
+ console.log(
136
+ pc.yellow(
137
+ ` \u26A0 Could not update ${tsconfigName}: ${e instanceof Error ? e.message : String(e)}`
138
+ )
139
+ );
140
+ }
141
+ }
20
142
  async function scanGenerate(options) {
21
143
  const scanPath = resolve(options.scanPath);
22
144
  const generated = [];
@@ -122,7 +244,7 @@ ${BRAND.name} Scan \u2192 Generate
122
244
  });
123
245
  enrichments = enrichResult.enrichments;
124
246
  if (enrichResult.model && enrichResult.totalInputTokens > 0) {
125
- const { calculateCost } = await import("./ai-client-I6MDWNYA.js");
247
+ const { calculateCost } = await import("./ai-client-LSLQGOMM.js");
126
248
  enrichmentCost = calculateCost(
127
249
  enrichResult.model,
128
250
  enrichResult.totalInputTokens,
@@ -168,13 +290,26 @@ Phase ${options.enrich ? "4" : "3"}: Generating fragment files...`));
168
290
  comp.sourcePath,
169
291
  componentBaseName
170
292
  );
171
- const content = generateFragmentWithTodos(
172
- comp.name,
173
- importPath,
174
- data,
175
- confidence,
176
- enrichment
177
- );
293
+ let content;
294
+ if (fragmentPath.endsWith(".contract.json")) {
295
+ content = generateContractJsonFromScan(
296
+ comp.name,
297
+ componentBaseName,
298
+ data,
299
+ confidence,
300
+ comp.sourcePath,
301
+ scanPath,
302
+ enrichment
303
+ );
304
+ } else {
305
+ content = generateFragmentWithTodos(
306
+ comp.name,
307
+ importPath,
308
+ data,
309
+ confidence,
310
+ enrichment
311
+ );
312
+ }
178
313
  await writeFile(fragmentPath, content, "utf-8");
179
314
  const relPath = relative(process.cwd(), fragmentPath);
180
315
  generated.push({
@@ -196,6 +331,10 @@ Phase ${options.enrich ? "4" : "3"}: Generating fragment files...`));
196
331
  console.log(pc.red(` \u2717 ${comp.name}: ${e instanceof Error ? e.message : String(e)}`));
197
332
  }
198
333
  }
334
+ if (generated.length > 0) {
335
+ await ensureCoreDependency(scanPath);
336
+ await excludeFragmentsFromTsconfig(scanPath);
337
+ }
199
338
  const avgConfidence = generated.length > 0 ? Math.round(
200
339
  generated.reduce((sum, g) => sum + g.confidence, 0) / generated.length
201
340
  ) : 0;
@@ -450,12 +589,12 @@ Rules:
450
589
  }
451
590
  function buildEnrichmentUserPrompt(name, data) {
452
591
  const props = data.meta?.props ?? {};
453
- const localProps = Object.entries(props).filter(([_, p]) => p.source === "local");
592
+ const fragmentProps = getFragmentPropEntries(props);
454
593
  const composition = data.meta?.composition ?? null;
455
594
  const description = data.meta?.description || "";
456
595
  const category = inferCategoryFromMeta(
457
596
  name,
458
- Object.fromEntries(localProps)
597
+ Object.fromEntries(fragmentProps)
459
598
  );
460
599
  const lines = [
461
600
  `Component: ${name}`,
@@ -464,10 +603,10 @@ function buildEnrichmentUserPrompt(name, data) {
464
603
  if (description) {
465
604
  lines.push(`Description: ${description}`);
466
605
  }
467
- if (localProps.length > 0) {
606
+ if (fragmentProps.length > 0) {
468
607
  lines.push("");
469
608
  lines.push("Props:");
470
- for (const [propName, prop] of localProps) {
609
+ for (const [propName, prop] of fragmentProps) {
471
610
  let propLine = ` - ${propName}: ${prop.typeKind}`;
472
611
  if (prop.values && prop.values.length > 0) {
473
612
  propLine += ` (${prop.values.join(" | ")})`;
@@ -528,7 +667,7 @@ async function enrichComponents(componentDataList, options) {
528
667
  createAIClient,
529
668
  generateCompletion,
530
669
  ENRICHMENT_MODELS
531
- } = await import("./ai-client-I6MDWNYA.js");
670
+ } = await import("./ai-client-LSLQGOMM.js");
532
671
  const provider = detectProvider({ provider: options.provider, apiKey: options.apiKey });
533
672
  if (provider === "none") {
534
673
  console.log(pc.yellow(" No API key found. Set ANTHROPIC_API_KEY or OPENAI_API_KEY, or use --api-key"));
@@ -606,8 +745,8 @@ function calculateFieldConfidence(data, enrichment) {
606
745
  let score = 0;
607
746
  const todoFields = [];
608
747
  const props = data.meta?.props ?? {};
609
- const localEntries = Object.values(props).filter((p) => p.source === "local");
610
- const hasProps = localEntries.length > 0;
748
+ const fragmentPropEntries = getFragmentPropEntries(props);
749
+ const hasProps = fragmentPropEntries.length > 0;
611
750
  if (hasProps) {
612
751
  score += 30;
613
752
  }
@@ -616,10 +755,8 @@ function calculateFieldConfidence(data, enrichment) {
616
755
  } else {
617
756
  todoFields.push("meta.description");
618
757
  }
619
- const localProps = Object.fromEntries(
620
- Object.entries(props).filter(([_, p]) => p.source === "local")
621
- );
622
- const category = inferCategoryFromMeta(data.component.name, localProps);
758
+ const fragmentProps = Object.fromEntries(fragmentPropEntries);
759
+ const category = inferCategoryFromMeta(data.component.name, fragmentProps);
623
760
  if (category !== "Components") {
624
761
  score += 10;
625
762
  } else {
@@ -629,7 +766,7 @@ function calculateFieldConfidence(data, enrichment) {
629
766
  score += 25;
630
767
  }
631
768
  if (hasProps) {
632
- const allResolved = localEntries.every((p) => p.typeKind !== "custom");
769
+ const allResolved = fragmentPropEntries.every(([_, p]) => p.typeKind !== "custom");
633
770
  if (allResolved) {
634
771
  score += 10;
635
772
  }
@@ -638,7 +775,7 @@ function calculateFieldConfidence(data, enrichment) {
638
775
  score += 10;
639
776
  }
640
777
  if (hasProps) {
641
- const hasDefaults = localEntries.some((p) => p.default !== void 0);
778
+ const hasDefaults = fragmentPropEntries.some(([_, p]) => p.default !== void 0);
642
779
  if (hasDefaults) {
643
780
  score += 5;
644
781
  }
@@ -792,13 +929,55 @@ function inferAccessibilityFromMeta(props) {
792
929
  if (requirements.length > 0) accessibility.requirements = requirements;
793
930
  return accessibility;
794
931
  }
795
- function buildContractBlock(componentName, props, composition, accessibility) {
796
- const localEntries = Object.entries(props).filter(([_, p]) => p.source === "local");
797
- if (localEntries.length === 0 && !composition && !accessibility.requirements?.length) {
932
+ var INHERITED_PROP_PRIORITY = [
933
+ "htmlFor",
934
+ "type",
935
+ "value",
936
+ "defaultValue",
937
+ "checked",
938
+ "defaultChecked",
939
+ "disabled",
940
+ "required",
941
+ "placeholder",
942
+ "name",
943
+ "id",
944
+ "form",
945
+ "accept",
946
+ "multiple",
947
+ "min",
948
+ "max",
949
+ "step",
950
+ "pattern",
951
+ "role",
952
+ "children"
953
+ ];
954
+ function getFragmentPropEntries(props) {
955
+ const entries = Object.entries(props);
956
+ const localEntries = entries.filter(([_, prop]) => prop.source === "local");
957
+ if (localEntries.length > 0) {
958
+ return localEntries;
959
+ }
960
+ const prioritized = new Set(INHERITED_PROP_PRIORITY);
961
+ const inheritedEntries = entries.filter(([name, prop]) => {
962
+ if (prop.source !== "inherited") return false;
963
+ if (name === "key" || name === "ref" || name === "className" || name === "style") return false;
964
+ if (name.startsWith("on")) return false;
965
+ if (name.startsWith("aria-") || name.startsWith("data-")) return false;
966
+ return prioritized.has(name);
967
+ }).sort(([nameA], [nameB]) => {
968
+ const rankA = INHERITED_PROP_PRIORITY.indexOf(nameA);
969
+ const rankB = INHERITED_PROP_PRIORITY.indexOf(nameB);
970
+ return rankA - rankB;
971
+ });
972
+ return inheritedEntries.slice(0, 8);
973
+ }
974
+ function buildContractBlock(componentName, props, composition, accessibility, compoundPartRefs = []) {
975
+ const relevantEntries = getFragmentPropEntries(props);
976
+ if (relevantEntries.length === 0 && !composition && !accessibility.requirements?.length) {
798
977
  return null;
799
978
  }
800
979
  const contract = { propsSummary: [] };
801
- for (const [name, prop] of localEntries) {
980
+ for (const [name, prop] of relevantEntries) {
802
981
  let summary = name + ": ";
803
982
  if (prop.typeKind === "enum" && prop.values && prop.values.length > 0) {
804
983
  summary += prop.values.join(" | ");
@@ -825,7 +1004,8 @@ function buildContractBlock(componentName, props, composition, accessibility) {
825
1004
  contract.compoundChildren = children;
826
1005
  }
827
1006
  if (composition && composition.parts.length > 0) {
828
- const innerLines = composition.parts.map((part) => ` <${componentName}.${part.name}>...</${componentName}.${part.name}>`).join("\n");
1007
+ const resolvedParts = compoundPartRefs.length > 0 ? compoundPartRefs : composition.parts.map((part) => ({ partName: part.name, tagName: `${componentName}.${part.name}` }));
1008
+ const innerLines = resolvedParts.map((part) => ` <${part.tagName}>...</${part.tagName}>`).join("\n");
829
1009
  contract.canonicalUsage = [
830
1010
  `<${componentName}>
831
1011
  ${innerLines}
@@ -909,20 +1089,53 @@ function computeImportPath(fragmentDir, sourcePath, componentBaseName) {
909
1089
  function escapeQuotes(str) {
910
1090
  return str.replace(/'/g, "\\'");
911
1091
  }
1092
+ function resolveCompoundPartReferences(componentName, composition, exportNames) {
1093
+ if (!composition || composition.parts.length === 0) return [];
1094
+ return composition.parts.map((part) => {
1095
+ const directExportName = `${componentName}${part.name}`;
1096
+ if (exportNames.includes(directExportName)) {
1097
+ return {
1098
+ partName: part.name,
1099
+ tagName: directExportName,
1100
+ importName: directExportName
1101
+ };
1102
+ }
1103
+ return {
1104
+ partName: part.name,
1105
+ tagName: `${componentName}.${part.name}`
1106
+ };
1107
+ });
1108
+ }
912
1109
  function generateFragmentWithTodos(componentName, importPath, data, confidence, enrichment) {
913
1110
  const props = data.meta?.props ?? {};
914
1111
  const localProps = Object.fromEntries(
915
1112
  Object.entries(props).filter(([_, p]) => p.source === "local")
916
1113
  );
1114
+ const fragmentProps = Object.fromEntries(getFragmentPropEntries(props));
1115
+ const inferenceProps = Object.keys(localProps).length > 0 ? localProps : fragmentProps;
917
1116
  const composition = data.meta?.composition ?? null;
918
- const description = data.meta?.description || inferDescriptionFromMeta(componentName, localProps);
1117
+ const description = data.meta?.description || inferDescriptionFromMeta(componentName, inferenceProps);
919
1118
  const descriptionTodo = data.meta?.description ? "" : " // TODO: Review description";
920
- const category = inferCategoryFromMeta(componentName, localProps);
1119
+ const category = inferCategoryFromMeta(componentName, inferenceProps);
921
1120
  const categoryTodo = category === "Components" ? " // TODO: Set correct category" : "";
922
1121
  const status = inferStatus(data.component.sourcePath);
923
- const accessibility = inferAccessibilityFromMeta(localProps);
1122
+ const accessibility = inferAccessibilityFromMeta(inferenceProps);
1123
+ const exportNames = data.meta?.exports ?? [];
1124
+ const compoundPartRefs = resolveCompoundPartReferences(componentName, composition, exportNames);
1125
+ const importNames = Array.from(
1126
+ /* @__PURE__ */ new Set([
1127
+ componentName,
1128
+ ...compoundPartRefs.flatMap((part) => part.importName ? [part.importName] : [])
1129
+ ])
1130
+ );
924
1131
  const propsBlock = buildPropsBlockFromMeta(props);
925
- const contract = buildContractBlock(componentName, props, composition, accessibility);
1132
+ const contract = buildContractBlock(
1133
+ componentName,
1134
+ props,
1135
+ composition,
1136
+ accessibility,
1137
+ compoundPartRefs
1138
+ );
926
1139
  if (contract && enrichment) {
927
1140
  if (enrichment.a11yRules.length > 0) {
928
1141
  contract.a11yRules = enrichment.a11yRules;
@@ -950,17 +1163,17 @@ function generateFragmentWithTodos(componentName, importPath, data, confidence,
950
1163
  const variantsBlock = buildVariantsBlock(
951
1164
  componentName,
952
1165
  data.storyVariants,
953
- composition
1166
+ composition,
1167
+ compoundPartRefs
954
1168
  );
955
- const aiBlock = buildAIBlock(composition);
1169
+ const aiBlock = buildAIBlock(composition, compoundPartRefs);
956
1170
  const provenanceBlock = buildProvenanceBlock(confidence, props);
957
1171
  const compoundComment = composition && composition.parts.length > 0 ? `
958
1172
  // Compound sub-components detected: ${composition.parts.map((p) => p.name).join(", ")}` : "";
959
1173
  return `// Auto-generated by fragments init --scan | Confidence: ${confidence.score}/100
960
1174
  // ${confidence.todoFields.length} TODO(s) \u2014 search for "TODO:" and fill in human knowledge${compoundComment}
961
- import React from 'react';
962
1175
  import { defineFragment } from '@fragments-sdk/core';
963
- import { ${componentName} } from '${importPath}';
1176
+ import { ${importNames.join(", ")} } from '${importPath}';
964
1177
 
965
1178
  export default defineFragment({
966
1179
  component: ${componentName},
@@ -983,7 +1196,7 @@ ${provenanceBlock}});
983
1196
  `;
984
1197
  }
985
1198
  function buildPropsBlockFromMeta(props) {
986
- const entries = Object.entries(props).filter(([_, p]) => p.source === "local");
1199
+ const entries = getFragmentPropEntries(props);
987
1200
  if (entries.length === 0) return "{}";
988
1201
  const lines = entries.map(([name, prop]) => {
989
1202
  const type = prop.typeKind;
@@ -1009,12 +1222,12 @@ ${parts.join(",\n")},
1009
1222
  ${lines.join("\n")}
1010
1223
  }`;
1011
1224
  }
1012
- function buildVariantsBlock(componentName, storyVariants, composition) {
1225
+ function buildVariantsBlock(componentName, storyVariants, composition, compoundPartRefs = []) {
1013
1226
  const entries = [];
1014
1227
  const hasDefault = storyVariants.some((v) => v.name === "Default");
1015
1228
  if (!hasDefault) {
1016
1229
  if (composition && composition.pattern === "compound" && composition.parts.length > 0) {
1017
- entries.push(formatCompoundVariantEntry(componentName, composition));
1230
+ entries.push(formatCompoundVariantEntry(componentName, composition, compoundPartRefs));
1018
1231
  } else {
1019
1232
  entries.push(formatVariantEntry(componentName, "Default", `Default ${componentName}`, {}));
1020
1233
  }
@@ -1041,9 +1254,9 @@ function formatVariantEntry(componentName, name, description, args) {
1041
1254
  render: () => ${jsxCode},
1042
1255
  },`;
1043
1256
  }
1044
- function formatCompoundVariantEntry(componentName, composition) {
1045
- const parts = composition.parts;
1046
- const innerJsx = parts.map((part) => ` <${componentName}.${part.name}>...</${componentName}.${part.name}>`).join("\n");
1257
+ function formatCompoundVariantEntry(componentName, composition, compoundPartRefs = []) {
1258
+ const resolvedParts = compoundPartRefs.length > 0 ? compoundPartRefs : composition.parts.map((part) => ({ partName: part.name, tagName: `${componentName}.${part.name}` }));
1259
+ const innerJsx = resolvedParts.map((part) => ` <${part.tagName}>...</${part.tagName}>`).join("\n");
1047
1260
  const jsxCode = `<${componentName}>
1048
1261
  ${innerJsx}
1049
1262
  </${componentName}>`;
@@ -1056,12 +1269,16 @@ ${innerJsx}
1056
1269
  ),
1057
1270
  },`;
1058
1271
  }
1059
- function buildAIBlock(composition) {
1272
+ function buildAIBlock(composition, compoundPartRefs = []) {
1060
1273
  if (!composition || composition.parts.length === 0) return "";
1061
- const subComponents = composition.parts.map((p) => `'${p.name}'`).join(", ");
1274
+ const resolvedParts = compoundPartRefs.length > 0 ? compoundPartRefs : composition.parts.map((part) => ({
1275
+ partName: part.name,
1276
+ tagName: `Component.${part.name}`
1277
+ }));
1278
+ const subComponents = resolvedParts.map((part) => `'${part.importName ?? part.partName}'`).join(", ");
1062
1279
  const pattern = `
1063
1280
  <Component>
1064
- ${composition.parts.map((p) => ` <Component.${p.name}>...</Component.${p.name}>`).join("\n")}
1281
+ ${resolvedParts.map((part) => ` <${part.tagName}>...</${part.tagName}>`).join("\n")}
1065
1282
  </Component>`;
1066
1283
  return `
1067
1284
 
@@ -1100,6 +1317,73 @@ function buildJsxString(componentName, args) {
1100
1317
  }
1101
1318
  return `<${componentName}${propsStr} />`;
1102
1319
  }
1320
+ function generateContractJsonFromScan(componentName, _baseName, data, confidence, sourcePath, scanPath, enrichment) {
1321
+ const props = data.meta?.props ?? {};
1322
+ const localProps = Object.fromEntries(
1323
+ Object.entries(props).filter(([, p]) => p.source === "local")
1324
+ );
1325
+ const effectiveProps = Object.keys(localProps).length > 0 ? localProps : props;
1326
+ const composition = data.meta?.composition ?? null;
1327
+ const description = data.meta?.description || `${componentName} component`;
1328
+ const category = inferCategoryFromMeta(componentName, effectiveProps);
1329
+ const propsSchema = {};
1330
+ for (const [name, prop] of Object.entries(effectiveProps)) {
1331
+ propsSchema[name] = {
1332
+ type: prop.typeKind,
1333
+ description: prop.description ?? "",
1334
+ ...prop.values?.length && { values: prop.values },
1335
+ ...prop.default !== void 0 && { default: prop.default },
1336
+ ...prop.required && { required: true }
1337
+ };
1338
+ }
1339
+ const propsSummary = Object.entries(effectiveProps).map(([name, prop]) => {
1340
+ let summary = `${name}: `;
1341
+ if (prop.values?.length) {
1342
+ summary += prop.values.join("|");
1343
+ } else {
1344
+ summary += prop.typeKind;
1345
+ }
1346
+ if (prop.default !== void 0) summary += ` (default: ${prop.default})`;
1347
+ if (prop.required) summary += " (required)";
1348
+ return summary;
1349
+ });
1350
+ let ai;
1351
+ if (composition) {
1352
+ ai = {
1353
+ compositionPattern: composition.pattern,
1354
+ subComponents: composition.parts.map((p) => p.name),
1355
+ ...composition.required.length > 0 && { requiredChildren: composition.required }
1356
+ };
1357
+ }
1358
+ const relativeSourcePath = relative(scanPath, sourcePath);
1359
+ const usage = enrichment ? {
1360
+ when: enrichment.when ?? [],
1361
+ whenNot: enrichment.whenNot ?? [],
1362
+ ...enrichment.guidelines?.length && { guidelines: enrichment.guidelines }
1363
+ } : {
1364
+ when: [],
1365
+ whenNot: []
1366
+ };
1367
+ const contract = {
1368
+ $schema: "https://usefragments.com/schemas/contract.v1.json",
1369
+ name: componentName,
1370
+ description,
1371
+ category,
1372
+ sourcePath: relativeSourcePath,
1373
+ exportName: componentName,
1374
+ propsSummary,
1375
+ props: propsSchema,
1376
+ usage,
1377
+ ...ai && { ai },
1378
+ provenance: {
1379
+ source: "extracted",
1380
+ verified: confidence.score >= 70,
1381
+ frameworkSupport: "native",
1382
+ extractedAt: (/* @__PURE__ */ new Date()).toISOString()
1383
+ }
1384
+ };
1385
+ return JSON.stringify(contract, null, 2) + "\n";
1386
+ }
1103
1387
  export {
1104
1388
  buildEnrichedUsageBlock,
1105
1389
  buildEnrichmentSystemPrompt,
@@ -1107,9 +1391,13 @@ export {
1107
1391
  calculateFieldConfidence,
1108
1392
  detectCompoundComponents,
1109
1393
  detectCompoundComponentsFromSource,
1394
+ detectPackageManagerForProject,
1395
+ ensureCoreDependency,
1396
+ excludeFragmentsFromTsconfig,
1110
1397
  extractComponentJSDoc,
1111
1398
  extractComponentJSDocFromSource,
1112
1399
  parseEnrichmentResponse,
1400
+ resolveCoreInstallCommand,
1113
1401
  scanGenerate
1114
1402
  };
1115
- //# sourceMappingURL=scan-generate-TFZVL3BT.js.map
1403
+ //# sourceMappingURL=scan-generate-VY27PIOX.js.map