@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.
- package/README.md +0 -3
- package/dist/{ai-client-I6MDWNYA.js → ai-client-LSLQGOMM.js} +1 -2
- package/dist/bin.js +4745 -3817
- package/dist/bin.js.map +1 -1
- package/dist/{chunk-TXFCEDOC.js → chunk-2WXKALIG.js} +2 -2
- package/dist/{chunk-I34BC3CU.js → chunk-32LIWN2P.js} +1006 -3
- package/dist/chunk-32LIWN2P.js.map +1 -0
- package/dist/chunk-5JF26E55.js +1255 -0
- package/dist/chunk-5JF26E55.js.map +1 -0
- package/dist/{chunk-APTQIBS5.js → chunk-6SQPP47U.js} +153 -1342
- package/dist/chunk-6SQPP47U.js.map +1 -0
- package/dist/chunk-7DZC4YEV.js +294 -0
- package/dist/chunk-7DZC4YEV.js.map +1 -0
- package/dist/{chunk-PJT5IZ37.js → chunk-BJE3425I.js} +19 -52
- package/dist/{chunk-PJT5IZ37.js.map → chunk-BJE3425I.js.map} +1 -1
- package/dist/{chunk-55KERLWL.js → chunk-HQ6A6DTV.js} +1587 -1073
- package/dist/chunk-HQ6A6DTV.js.map +1 -0
- package/dist/chunk-MHIBEEW4.js +511 -0
- package/dist/chunk-MHIBEEW4.js.map +1 -0
- package/dist/{chunk-5A6X2Y73.js → chunk-ONUP6Z4W.js} +25 -13
- package/dist/chunk-ONUP6Z4W.js.map +1 -0
- package/dist/chunk-QCN35LJU.js +630 -0
- package/dist/chunk-QCN35LJU.js.map +1 -0
- package/dist/chunk-T47OLCSF.js +36 -0
- package/dist/chunk-T47OLCSF.js.map +1 -0
- package/dist/codebase-scanner-MQHUZC2G.js +21 -0
- package/dist/converter-7XM3Y6NJ.js +33 -0
- package/dist/converter-7XM3Y6NJ.js.map +1 -0
- package/dist/core/index.js +43 -2
- package/dist/create-IH4R45GE.js +806 -0
- package/dist/create-IH4R45GE.js.map +1 -0
- package/dist/{generate-RYWIPDN2.js → generate-PVOLUAAC.js} +4 -6
- package/dist/{generate-RYWIPDN2.js.map → generate-PVOLUAAC.js.map} +1 -1
- package/dist/govern-scan-OYFZYOQW.js +413 -0
- package/dist/govern-scan-OYFZYOQW.js.map +1 -0
- package/dist/index.d.ts +4 -23
- package/dist/index.js +15 -14
- package/dist/index.js.map +1 -1
- package/dist/{init-WRUSW7R5.js → init-SSGUSP7Z.js} +131 -129
- package/dist/init-SSGUSP7Z.js.map +1 -0
- package/dist/{init-cloud-REQ3XLHO.js → init-cloud-3DNKPWFB.js} +30 -5
- package/dist/{init-cloud-REQ3XLHO.js.map → init-cloud-3DNKPWFB.js.map} +1 -1
- package/dist/mcp-bin.js +5 -37
- package/dist/mcp-bin.js.map +1 -1
- package/dist/node-37AUE74M.js +65 -0
- package/dist/push-contracts-WY32TFP6.js +84 -0
- package/dist/push-contracts-WY32TFP6.js.map +1 -0
- package/dist/scan-PKSYSTRR.js +15 -0
- package/dist/{scan-generate-TFZVL3BT.js → scan-generate-VY27PIOX.js} +340 -52
- package/dist/scan-generate-VY27PIOX.js.map +1 -0
- package/dist/scanner-4KZNOXAK.js +12 -0
- package/dist/{service-HKJ6B7P7.js → service-QJGWUIVL.js} +41 -30
- package/dist/{snapshot-C5DYIGIV.js → snapshot-WIJMEIFT.js} +2 -3
- package/dist/{snapshot-C5DYIGIV.js.map → snapshot-WIJMEIFT.js.map} +1 -1
- package/dist/{static-viewer-DUVC4UIM.js → static-viewer-7QIBQZRC.js} +3 -4
- package/dist/static-viewer-7QIBQZRC.js.map +1 -0
- package/dist/{test-JW7JIDFG.js → test-64Z5BKBA.js} +4 -7
- package/dist/{test-JW7JIDFG.js.map → test-64Z5BKBA.js.map} +1 -1
- package/dist/token-normalizer-TEPOVBPV.js +312 -0
- package/dist/token-normalizer-TEPOVBPV.js.map +1 -0
- package/dist/token-parser-32KOIOFN.js +22 -0
- package/dist/token-parser-32KOIOFN.js.map +1 -0
- package/dist/{tokens-KE73G5JC.js → tokens-NZWFQIAB.js} +10 -9
- package/dist/{tokens-KE73G5JC.js.map → tokens-NZWFQIAB.js.map} +1 -1
- package/dist/tokens-generate-5JQSJ27E.js +85 -0
- package/dist/tokens-generate-5JQSJ27E.js.map +1 -0
- package/dist/tokens-push-HY3KO36V.js +148 -0
- package/dist/tokens-push-HY3KO36V.js.map +1 -0
- package/package.json +8 -6
- package/src/bin.ts +300 -48
- package/src/commands/__fixtures__/shadcn-label-wrapper/package.json +7 -0
- package/src/commands/__fixtures__/shadcn-label-wrapper/src/components/ui/label.contract.json +42 -0
- package/src/commands/__fixtures__/shadcn-label-wrapper/src/components/ui/label.tsx +11 -0
- package/src/commands/__fixtures__/shadcn-label-wrapper/src/components/ui/primitive.contract.json +20 -0
- package/src/commands/__fixtures__/shadcn-label-wrapper/src/components/ui/primitive.tsx +14 -0
- package/src/commands/__fixtures__/shadcn-label-wrapper/tsconfig.app.json +23 -0
- package/src/commands/__tests__/build-freshness.test.ts +231 -0
- package/src/commands/__tests__/create.test.ts +71 -0
- package/src/commands/__tests__/drift-sync.test.ts +1 -1
- package/src/commands/__tests__/govern.test.ts +258 -0
- package/src/commands/__tests__/init.test.ts +113 -0
- package/src/commands/__tests__/scan-generate.test.ts +189 -70
- package/src/commands/__tests__/verify.test.ts +91 -0
- package/src/commands/build.ts +54 -1
- package/src/commands/context.ts +1 -1
- package/src/commands/create.ts +536 -0
- package/src/commands/discover.ts +151 -0
- package/src/commands/doctor.ts +3 -2
- package/src/commands/enhance.ts +3 -1
- package/src/commands/govern-scan.ts +565 -0
- package/src/commands/govern.ts +67 -4
- package/src/commands/init-cloud.ts +32 -4
- package/src/commands/init.ts +152 -28
- package/src/commands/inspect.ts +290 -0
- package/src/commands/migrate-contract.ts +85 -0
- package/src/commands/push-contracts.ts +112 -0
- package/src/commands/scan-generate.ts +439 -51
- package/src/commands/scan.ts +14 -0
- package/src/commands/setup.ts +27 -50
- package/src/commands/sync.ts +2 -2
- package/src/commands/tokens-generate.ts +113 -0
- package/src/commands/tokens-push.ts +199 -0
- package/src/commands/verify.ts +195 -1
- package/src/core/__fixtures__/shadcn-input/input.tsx +7 -0
- package/src/core/__fixtures__/shadcn-input/tsconfig.json +14 -0
- package/src/core/__fixtures__/shadcn-label/label.tsx +11 -0
- package/src/core/__fixtures__/shadcn-label/primitive.tsx +14 -0
- package/src/core/__fixtures__/shadcn-label/tsconfig.json +14 -0
- package/src/core/__fixtures__/shadcn-radix-label/label.tsx +11 -0
- package/src/core/__fixtures__/shadcn-radix-label/node_modules/radix-ui/index.d.ts +12 -0
- package/src/core/__fixtures__/shadcn-radix-label/tsconfig.json +14 -0
- package/src/core/__tests__/contract-parity.test.ts +316 -0
- package/src/core/__tests__/token-resolver.test.ts +1 -1
- package/src/core/component-extractor.test.ts +40 -1
- package/src/core/config.ts +2 -1
- package/src/core/discovery.ts +13 -2
- package/src/core/drift-verifier.ts +123 -0
- package/src/core/extractor-adapter.ts +80 -0
- package/src/index.ts +3 -3
- package/src/mcp/__tests__/projectFields.test.ts +1 -1
- package/src/mcp/utils.ts +1 -50
- package/src/migrate/converter.ts +3 -3
- package/src/migrate/fragment-to-contract.ts +253 -0
- package/src/migrate/report.ts +1 -1
- package/src/scripts/token-benchmark.ts +121 -0
- package/src/service/__tests__/props-extractor.test.ts +94 -0
- package/src/service/__tests__/token-normalizer.test.ts +690 -0
- package/src/service/ast-utils.ts +4 -23
- package/src/service/babel-config.ts +23 -0
- package/src/service/enhance/converter.ts +61 -0
- package/src/service/enhance/props-extractor.ts +25 -8
- package/src/service/enhance/scanner.ts +5 -24
- package/src/service/index.ts +8 -0
- package/src/service/snippet-validation.ts +9 -3
- package/src/service/tailwind-v4-parser.ts +314 -0
- package/src/service/token-normalizer.ts +510 -0
- package/src/service/token-parser.ts +56 -0
- package/src/setup.ts +10 -39
- package/src/shared/index.ts +1 -0
- package/src/shared/project-fields.ts +46 -0
- package/src/theme/__tests__/component-contrast.test.ts +2 -2
- package/src/theme/__tests__/serializer.test.ts +1 -1
- package/src/theme/generator.ts +16 -1
- package/src/theme/schema.ts +8 -0
- package/src/theme/serializer.ts +13 -9
- package/src/theme/types.ts +8 -0
- package/src/validators.ts +1 -2
- package/src/viewer/__tests__/viewer-integration.test.ts +8 -8
- package/src/viewer/style-utils.ts +27 -412
- package/src/viewer/vite-plugin.ts +2 -2
- package/dist/chunk-55KERLWL.js.map +0 -1
- package/dist/chunk-5A6X2Y73.js.map +0 -1
- package/dist/chunk-APTQIBS5.js.map +0 -1
- package/dist/chunk-EYXVAMEX.js +0 -626
- package/dist/chunk-EYXVAMEX.js.map +0 -1
- package/dist/chunk-I34BC3CU.js.map +0 -1
- package/dist/chunk-LOYS64QS.js +0 -2453
- package/dist/chunk-LOYS64QS.js.map +0 -1
- package/dist/chunk-Z7EY4VHE.js +0 -50
- package/dist/chunk-ZKTFKHWN.js +0 -324
- package/dist/chunk-ZKTFKHWN.js.map +0 -1
- package/dist/discovery-VDANZAJ2.js +0 -28
- package/dist/init-WRUSW7R5.js.map +0 -1
- package/dist/sass.node-4XJK6YBF.js +0 -130708
- package/dist/sass.node-4XJK6YBF.js.map +0 -1
- package/dist/scan-YJHQIRKG.js +0 -14
- package/dist/scan-generate-TFZVL3BT.js.map +0 -1
- package/dist/viewer-2TZS3NDL.js +0 -2730
- package/dist/viewer-2TZS3NDL.js.map +0 -1
- package/src/build.ts +0 -612
- package/src/commands/dev.ts +0 -107
- package/src/core/auto-props.ts +0 -464
- package/src/core/component-extractor.ts +0 -1030
- package/src/core/token-resolver.ts +0 -155
- /package/dist/{ai-client-I6MDWNYA.js.map → ai-client-LSLQGOMM.js.map} +0 -0
- /package/dist/{chunk-TXFCEDOC.js.map → chunk-2WXKALIG.js.map} +0 -0
- /package/dist/{chunk-Z7EY4VHE.js.map → codebase-scanner-MQHUZC2G.js.map} +0 -0
- /package/dist/{discovery-VDANZAJ2.js.map → node-37AUE74M.js.map} +0 -0
- /package/dist/{scan-YJHQIRKG.js.map → scan-PKSYSTRR.js.map} +0 -0
- /package/dist/{service-HKJ6B7P7.js.map → scanner-4KZNOXAK.js.map} +0 -0
- /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-
|
|
9
|
-
import "./chunk-D2CDBRNU.js";
|
|
5
|
+
} from "./chunk-HQ6A6DTV.js";
|
|
10
6
|
import {
|
|
11
7
|
BRAND
|
|
12
|
-
} from "./chunk-
|
|
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-
|
|
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
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
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
|
|
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(
|
|
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 (
|
|
606
|
+
if (fragmentProps.length > 0) {
|
|
468
607
|
lines.push("");
|
|
469
608
|
lines.push("Props:");
|
|
470
|
-
for (const [propName, prop] of
|
|
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-
|
|
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
|
|
610
|
-
const hasProps =
|
|
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
|
|
620
|
-
|
|
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 =
|
|
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 =
|
|
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
|
-
|
|
796
|
-
|
|
797
|
-
|
|
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
|
|
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
|
|
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,
|
|
1117
|
+
const description = data.meta?.description || inferDescriptionFromMeta(componentName, inferenceProps);
|
|
919
1118
|
const descriptionTodo = data.meta?.description ? "" : " // TODO: Review description";
|
|
920
|
-
const category = inferCategoryFromMeta(componentName,
|
|
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(
|
|
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(
|
|
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 { ${
|
|
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 =
|
|
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
|
|
1046
|
-
const innerJsx =
|
|
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
|
|
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
|
-
${
|
|
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-
|
|
1403
|
+
//# sourceMappingURL=scan-generate-VY27PIOX.js.map
|