@fragments-sdk/cli 0.15.10 → 0.16.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/bin.js +896 -787
- package/dist/bin.js.map +1 -1
- package/dist/{chunk-6SQPP47U.js → chunk-77AAP6R6.js} +532 -31
- package/dist/chunk-77AAP6R6.js.map +1 -0
- package/dist/{chunk-5JF26E55.js → chunk-ACFVKMVZ.js} +11 -11
- package/dist/{chunk-BJE3425I.js → chunk-ACX7YWZW.js} +2 -2
- package/dist/{chunk-ONUP6Z4W.js → chunk-G6UVWMFU.js} +8 -8
- package/dist/{chunk-2WXKALIG.js → chunk-OZZ4SVZX.js} +2 -2
- package/dist/{chunk-32LIWN2P.js → chunk-SJFSG7QF.js} +582 -261
- package/dist/chunk-SJFSG7QF.js.map +1 -0
- package/dist/{chunk-HQ6A6DTV.js → chunk-XRADMHMV.js} +315 -1089
- package/dist/chunk-XRADMHMV.js.map +1 -0
- package/dist/core/index.js +53 -1
- package/dist/{create-EXURTBKK.js → create-3ZFYQB3T.js} +2 -2
- package/dist/{doctor-BDPMYYE6.js → doctor-4IDUM7HI.js} +2 -2
- package/dist/{generate-PVOLUAAC.js → generate-VNUUWVWQ.js} +4 -4
- package/dist/{govern-scan-DW4QUAYD.js → govern-scan-HTACKYPF.js} +158 -120
- package/dist/govern-scan-HTACKYPF.js.map +1 -0
- package/dist/index.js +6 -7
- package/dist/index.js.map +1 -1
- package/dist/{init-SSGUSP7Z.js → init-PXFRAQ64.js} +5 -5
- package/dist/mcp-bin.js +2 -2
- package/dist/{scan-PKSYSTRR.js → scan-L4GWGEZX.js} +5 -6
- package/dist/{scan-generate-VY27PIOX.js → scan-generate-74EYSAGH.js} +4 -4
- package/dist/{service-QJGWUIVL.js → service-VELQHEWV.js} +12 -14
- package/dist/{snapshot-WIJMEIFT.js → snapshot-DT4B6DPR.js} +2 -2
- package/dist/{static-viewer-7QIBQZRC.js → static-viewer-E4OJWFDJ.js} +3 -3
- package/dist/{test-64Z5BKBA.js → test-QJY2QO4X.js} +3 -3
- package/dist/{token-normalizer-TEPOVBPV.js → token-normalizer-56H4242J.js} +2 -2
- package/dist/{tokens-NZWFQIAB.js → tokens-K6URXFPK.js} +7 -8
- package/dist/{tokens-NZWFQIAB.js.map → tokens-K6URXFPK.js.map} +1 -1
- package/dist/{tokens-generate-5JQSJ27E.js → tokens-generate-EL6IN536.js} +2 -2
- package/package.json +7 -6
- package/src/bin.ts +49 -88
- package/src/commands/__fixtures__/shadcn-label-wrapper/src/components/ui/label.contract.json +1 -1
- package/src/commands/__fixtures__/shadcn-label-wrapper/src/components/ui/primitive.contract.json +1 -1
- package/src/commands/__tests__/context-cloud.test.ts +291 -0
- package/src/commands/__tests__/govern-scan.test.ts +185 -0
- package/src/commands/__tests__/govern.test.ts +1 -0
- package/src/commands/context-cloud.ts +355 -0
- package/src/commands/govern-scan-report.ts +170 -0
- package/src/commands/govern-scan.ts +42 -147
- package/src/commands/govern.ts +0 -157
- package/dist/chunk-32LIWN2P.js.map +0 -1
- package/dist/chunk-6SQPP47U.js.map +0 -1
- package/dist/chunk-HQ6A6DTV.js.map +0 -1
- package/dist/chunk-MHIBEEW4.js +0 -511
- package/dist/chunk-MHIBEEW4.js.map +0 -1
- package/dist/govern-scan-DW4QUAYD.js.map +0 -1
- package/dist/init-cloud-3DNKPWFB.js +0 -304
- package/dist/init-cloud-3DNKPWFB.js.map +0 -1
- package/dist/node-37AUE74M.js +0 -65
- package/dist/push-contracts-WY32TFP6.js +0 -84
- package/dist/push-contracts-WY32TFP6.js.map +0 -1
- package/dist/static-viewer-7QIBQZRC.js.map +0 -1
- package/dist/token-parser-32KOIOFN.js +0 -22
- package/dist/token-parser-32KOIOFN.js.map +0 -1
- package/dist/tokens-push-HY3KO36V.js +0 -148
- package/dist/tokens-push-HY3KO36V.js.map +0 -1
- package/src/commands/init-cloud.ts +0 -382
- package/src/commands/push-contracts.ts +0 -112
- package/src/commands/tokens-push.ts +0 -199
- /package/dist/{chunk-5JF26E55.js.map → chunk-ACFVKMVZ.js.map} +0 -0
- /package/dist/{chunk-BJE3425I.js.map → chunk-ACX7YWZW.js.map} +0 -0
- /package/dist/{chunk-ONUP6Z4W.js.map → chunk-G6UVWMFU.js.map} +0 -0
- /package/dist/{chunk-2WXKALIG.js.map → chunk-OZZ4SVZX.js.map} +0 -0
- /package/dist/{create-EXURTBKK.js.map → create-3ZFYQB3T.js.map} +0 -0
- /package/dist/{doctor-BDPMYYE6.js.map → doctor-4IDUM7HI.js.map} +0 -0
- /package/dist/{generate-PVOLUAAC.js.map → generate-VNUUWVWQ.js.map} +0 -0
- /package/dist/{init-SSGUSP7Z.js.map → init-PXFRAQ64.js.map} +0 -0
- /package/dist/{node-37AUE74M.js.map → scan-L4GWGEZX.js.map} +0 -0
- /package/dist/{scan-generate-VY27PIOX.js.map → scan-generate-74EYSAGH.js.map} +0 -0
- /package/dist/{scan-PKSYSTRR.js.map → service-VELQHEWV.js.map} +0 -0
- /package/dist/{snapshot-WIJMEIFT.js.map → snapshot-DT4B6DPR.js.map} +0 -0
- /package/dist/{service-QJGWUIVL.js.map → static-viewer-E4OJWFDJ.js.map} +0 -0
- /package/dist/{test-64Z5BKBA.js.map → test-QJY2QO4X.js.map} +0 -0
- /package/dist/{token-normalizer-TEPOVBPV.js.map → token-normalizer-56H4242J.js.map} +0 -0
- /package/dist/{tokens-generate-5JQSJ27E.js.map → tokens-generate-EL6IN536.js.map} +0 -0
|
@@ -2,7 +2,7 @@ import { createRequire as __banner_createRequire } from 'module'; const require
|
|
|
2
2
|
import {
|
|
3
3
|
BRAND,
|
|
4
4
|
fragmentsConfigSchema
|
|
5
|
-
} from "./chunk-
|
|
5
|
+
} from "./chunk-SJFSG7QF.js";
|
|
6
6
|
|
|
7
7
|
// src/core/config.ts
|
|
8
8
|
import { existsSync } from "fs";
|
|
@@ -132,7 +132,6 @@ async function discoverBlockFiles(configDir, exclude) {
|
|
|
132
132
|
absolutePath: resolve2(configDir, relativePath)
|
|
133
133
|
}));
|
|
134
134
|
}
|
|
135
|
-
var discoverRecipeFiles = discoverBlockFiles;
|
|
136
135
|
async function discoverFragmentFiles(config, configDir) {
|
|
137
136
|
const defaultExcludes = [
|
|
138
137
|
"**/*.test.stories.*",
|
|
@@ -323,55 +322,6 @@ async function discoverComponentsFromBarrel(barrelPath, configDir) {
|
|
|
323
322
|
}
|
|
324
323
|
return components;
|
|
325
324
|
}
|
|
326
|
-
var DEFAULT_TOKEN_PATTERNS = [
|
|
327
|
-
"src/**/tokens/**/_variables.scss",
|
|
328
|
-
"src/**/tokens/**/variables.scss",
|
|
329
|
-
"src/**/styles/**/variables.scss",
|
|
330
|
-
"src/**/styles/**/tokens.scss",
|
|
331
|
-
"src/**/styles/**/variables.css",
|
|
332
|
-
"src/**/theme/**/_variables.scss",
|
|
333
|
-
"src/**/theme/**/tokens.css",
|
|
334
|
-
// DTCG token files
|
|
335
|
-
"**/*.tokens.json",
|
|
336
|
-
"**/*.tokens"
|
|
337
|
-
];
|
|
338
|
-
async function discoverTokenFiles(configDir, patterns, exclude) {
|
|
339
|
-
const searchPatterns = patterns && patterns.length > 0 ? patterns : DEFAULT_TOKEN_PATTERNS;
|
|
340
|
-
const files = await fg(searchPatterns, {
|
|
341
|
-
cwd: configDir,
|
|
342
|
-
ignore: exclude ?? ["**/node_modules/**", "**/dist/**"],
|
|
343
|
-
absolute: false
|
|
344
|
-
});
|
|
345
|
-
return files.map((relativePath) => ({
|
|
346
|
-
relativePath,
|
|
347
|
-
absolutePath: resolve2(configDir, relativePath)
|
|
348
|
-
}));
|
|
349
|
-
}
|
|
350
|
-
async function discoverInstalledFragments(projectRoot) {
|
|
351
|
-
const pkgJsonPath = resolve2(projectRoot, "package.json");
|
|
352
|
-
if (!existsSync2(pkgJsonPath)) return [];
|
|
353
|
-
const pkgJson = JSON.parse(await readFile(pkgJsonPath, "utf-8"));
|
|
354
|
-
const allDeps = { ...pkgJson.dependencies, ...pkgJson.devDependencies };
|
|
355
|
-
const results = [];
|
|
356
|
-
for (const depName of Object.keys(allDeps)) {
|
|
357
|
-
const depDir = resolve2(projectRoot, "node_modules", depName);
|
|
358
|
-
const depPkgPath = resolve2(depDir, "package.json");
|
|
359
|
-
if (!existsSync2(depPkgPath)) continue;
|
|
360
|
-
const depPkg = JSON.parse(await readFile(depPkgPath, "utf-8"));
|
|
361
|
-
if (!depPkg.fragments) continue;
|
|
362
|
-
const files = await fg(
|
|
363
|
-
[`src/**/*${BRAND.fileExtension}`, "src/**/*.stories.tsx"],
|
|
364
|
-
{ cwd: depDir, ignore: ["**/node_modules/**"], absolute: false }
|
|
365
|
-
);
|
|
366
|
-
for (const rel of files) {
|
|
367
|
-
results.push({
|
|
368
|
-
relativePath: `${depName}/${rel}`,
|
|
369
|
-
absolutePath: resolve2(depDir, rel)
|
|
370
|
-
});
|
|
371
|
-
}
|
|
372
|
-
}
|
|
373
|
-
return results;
|
|
374
|
-
}
|
|
375
325
|
async function discoverAllComponents(configDir, options = {}) {
|
|
376
326
|
const componentsMap = /* @__PURE__ */ new Map();
|
|
377
327
|
const sourceComponents = await discoverComponentsFromSource(
|
|
@@ -395,9 +345,270 @@ async function discoverAllComponents(configDir, options = {}) {
|
|
|
395
345
|
return Array.from(componentsMap.values()).sort((a, b) => a.name.localeCompare(b.name));
|
|
396
346
|
}
|
|
397
347
|
|
|
348
|
+
// src/core/generators/typescript-extractor.ts
|
|
349
|
+
import ts from "typescript";
|
|
350
|
+
import { readFileSync } from "fs";
|
|
351
|
+
function extractPropsFromFile(filePath) {
|
|
352
|
+
const sourceText = readFileSync(filePath, "utf-8");
|
|
353
|
+
return extractPropsFromSource(sourceText, filePath);
|
|
354
|
+
}
|
|
355
|
+
function extractPropsFromSource(sourceText, fileName = "component.tsx") {
|
|
356
|
+
const sourceFile = ts.createSourceFile(
|
|
357
|
+
fileName,
|
|
358
|
+
sourceText,
|
|
359
|
+
ts.ScriptTarget.Latest,
|
|
360
|
+
true,
|
|
361
|
+
fileName.endsWith(".tsx") ? ts.ScriptKind.TSX : ts.ScriptKind.TS
|
|
362
|
+
);
|
|
363
|
+
const result = {
|
|
364
|
+
componentName: "",
|
|
365
|
+
props: {},
|
|
366
|
+
exports: [],
|
|
367
|
+
imports: []
|
|
368
|
+
};
|
|
369
|
+
const propsInterfaces = /* @__PURE__ */ new Map();
|
|
370
|
+
const componentExports = [];
|
|
371
|
+
const defaultExports = /* @__PURE__ */ new Set();
|
|
372
|
+
const importedModules = [];
|
|
373
|
+
ts.forEachChild(sourceFile, (node) => {
|
|
374
|
+
if (ts.isImportDeclaration(node)) {
|
|
375
|
+
const moduleSpecifier = node.moduleSpecifier;
|
|
376
|
+
if (ts.isStringLiteral(moduleSpecifier)) {
|
|
377
|
+
importedModules.push(moduleSpecifier.text);
|
|
378
|
+
}
|
|
379
|
+
}
|
|
380
|
+
if (ts.isInterfaceDeclaration(node)) {
|
|
381
|
+
const name = node.name.text;
|
|
382
|
+
if (name.endsWith("Props")) {
|
|
383
|
+
propsInterfaces.set(name, node);
|
|
384
|
+
}
|
|
385
|
+
}
|
|
386
|
+
if (ts.isTypeAliasDeclaration(node)) {
|
|
387
|
+
const name = node.name.text;
|
|
388
|
+
if (name.endsWith("Props")) {
|
|
389
|
+
propsInterfaces.set(name, node);
|
|
390
|
+
}
|
|
391
|
+
}
|
|
392
|
+
if (ts.isFunctionDeclaration(node) && node.name) {
|
|
393
|
+
const hasExport = node.modifiers?.some(
|
|
394
|
+
(m) => m.kind === ts.SyntaxKind.ExportKeyword
|
|
395
|
+
);
|
|
396
|
+
const hasDefault = node.modifiers?.some(
|
|
397
|
+
(m) => m.kind === ts.SyntaxKind.DefaultKeyword
|
|
398
|
+
);
|
|
399
|
+
if (hasExport) {
|
|
400
|
+
componentExports.push(node.name.text);
|
|
401
|
+
if (hasDefault) {
|
|
402
|
+
defaultExports.add(node.name.text);
|
|
403
|
+
}
|
|
404
|
+
}
|
|
405
|
+
}
|
|
406
|
+
if (ts.isVariableStatement(node)) {
|
|
407
|
+
const hasExport = node.modifiers?.some(
|
|
408
|
+
(m) => m.kind === ts.SyntaxKind.ExportKeyword
|
|
409
|
+
);
|
|
410
|
+
if (hasExport) {
|
|
411
|
+
for (const decl of node.declarationList.declarations) {
|
|
412
|
+
if (ts.isIdentifier(decl.name)) {
|
|
413
|
+
componentExports.push(decl.name.text);
|
|
414
|
+
}
|
|
415
|
+
}
|
|
416
|
+
}
|
|
417
|
+
}
|
|
418
|
+
if (ts.isExportDeclaration(node) && node.exportClause) {
|
|
419
|
+
if (ts.isNamedExports(node.exportClause)) {
|
|
420
|
+
for (const element of node.exportClause.elements) {
|
|
421
|
+
componentExports.push(element.name.text);
|
|
422
|
+
}
|
|
423
|
+
}
|
|
424
|
+
}
|
|
425
|
+
});
|
|
426
|
+
result.exports = componentExports;
|
|
427
|
+
result.imports = importedModules;
|
|
428
|
+
const mainComponent = componentExports.find(
|
|
429
|
+
(name) => /^[A-Z]/.test(name) && !name.endsWith("Props")
|
|
430
|
+
);
|
|
431
|
+
if (!mainComponent) {
|
|
432
|
+
return null;
|
|
433
|
+
}
|
|
434
|
+
result.componentName = mainComponent;
|
|
435
|
+
result.isDefaultExport = defaultExports.has(mainComponent);
|
|
436
|
+
const propsInterfaceName = `${mainComponent}Props`;
|
|
437
|
+
const propsInterface = propsInterfaces.get(propsInterfaceName);
|
|
438
|
+
if (propsInterface) {
|
|
439
|
+
result.propsInterfaceName = propsInterfaceName;
|
|
440
|
+
result.props = extractPropsFromInterface(propsInterface, sourceFile);
|
|
441
|
+
}
|
|
442
|
+
return result;
|
|
443
|
+
}
|
|
444
|
+
function extractPropsFromInterface(node, sourceFile) {
|
|
445
|
+
const props = {};
|
|
446
|
+
if (ts.isInterfaceDeclaration(node)) {
|
|
447
|
+
for (const member of node.members) {
|
|
448
|
+
if (ts.isPropertySignature(member) && member.name) {
|
|
449
|
+
const propName = member.name.getText(sourceFile);
|
|
450
|
+
const prop = extractPropFromMember(member, sourceFile);
|
|
451
|
+
if (prop) {
|
|
452
|
+
props[propName] = prop;
|
|
453
|
+
}
|
|
454
|
+
}
|
|
455
|
+
}
|
|
456
|
+
}
|
|
457
|
+
if (ts.isTypeAliasDeclaration(node)) {
|
|
458
|
+
const typeNode = node.type;
|
|
459
|
+
if (ts.isTypeLiteralNode(typeNode)) {
|
|
460
|
+
for (const member of typeNode.members) {
|
|
461
|
+
if (ts.isPropertySignature(member) && member.name) {
|
|
462
|
+
const propName = member.name.getText(sourceFile);
|
|
463
|
+
const prop = extractPropFromMember(member, sourceFile);
|
|
464
|
+
if (prop) {
|
|
465
|
+
props[propName] = prop;
|
|
466
|
+
}
|
|
467
|
+
}
|
|
468
|
+
}
|
|
469
|
+
}
|
|
470
|
+
}
|
|
471
|
+
return props;
|
|
472
|
+
}
|
|
473
|
+
function extractPropFromMember(member, sourceFile) {
|
|
474
|
+
const entry = {};
|
|
475
|
+
entry.required = !member.questionToken;
|
|
476
|
+
if (member.type) {
|
|
477
|
+
const typeInfo = parseTypeNode(member.type, sourceFile);
|
|
478
|
+
entry.type = typeInfo.type;
|
|
479
|
+
entry.typeKind = typeInfo.typeKind;
|
|
480
|
+
if (typeInfo.options) {
|
|
481
|
+
entry.options = typeInfo.options;
|
|
482
|
+
}
|
|
483
|
+
}
|
|
484
|
+
const jsDocComment = getJSDocComment(member);
|
|
485
|
+
if (jsDocComment) {
|
|
486
|
+
entry.description = jsDocComment;
|
|
487
|
+
}
|
|
488
|
+
const defaultValue = getJSDocDefault(member);
|
|
489
|
+
if (defaultValue !== void 0) {
|
|
490
|
+
entry.default = defaultValue;
|
|
491
|
+
}
|
|
492
|
+
return entry;
|
|
493
|
+
}
|
|
494
|
+
function parseTypeNode(typeNode, sourceFile) {
|
|
495
|
+
if (typeNode.kind === ts.SyntaxKind.StringKeyword) {
|
|
496
|
+
return { type: "string", typeKind: "string" };
|
|
497
|
+
}
|
|
498
|
+
if (typeNode.kind === ts.SyntaxKind.NumberKeyword) {
|
|
499
|
+
return { type: "number", typeKind: "number" };
|
|
500
|
+
}
|
|
501
|
+
if (typeNode.kind === ts.SyntaxKind.BooleanKeyword) {
|
|
502
|
+
return { type: "boolean", typeKind: "boolean" };
|
|
503
|
+
}
|
|
504
|
+
if (ts.isUnionTypeNode(typeNode)) {
|
|
505
|
+
const options = [];
|
|
506
|
+
let allLiterals = true;
|
|
507
|
+
for (const subType of typeNode.types) {
|
|
508
|
+
if (ts.isLiteralTypeNode(subType)) {
|
|
509
|
+
if (ts.isStringLiteral(subType.literal)) {
|
|
510
|
+
options.push(subType.literal.text);
|
|
511
|
+
} else if (subType.literal.kind === ts.SyntaxKind.TrueKeyword) {
|
|
512
|
+
options.push("true");
|
|
513
|
+
} else if (subType.literal.kind === ts.SyntaxKind.FalseKeyword) {
|
|
514
|
+
options.push("false");
|
|
515
|
+
} else {
|
|
516
|
+
allLiterals = false;
|
|
517
|
+
}
|
|
518
|
+
} else {
|
|
519
|
+
allLiterals = false;
|
|
520
|
+
}
|
|
521
|
+
}
|
|
522
|
+
if (allLiterals && options.length > 0) {
|
|
523
|
+
return {
|
|
524
|
+
type: options.map((o) => `"${o}"`).join(" | "),
|
|
525
|
+
typeKind: "enum",
|
|
526
|
+
options
|
|
527
|
+
};
|
|
528
|
+
}
|
|
529
|
+
return {
|
|
530
|
+
type: typeNode.getText(sourceFile),
|
|
531
|
+
typeKind: "union"
|
|
532
|
+
};
|
|
533
|
+
}
|
|
534
|
+
if (ts.isFunctionTypeNode(typeNode)) {
|
|
535
|
+
return {
|
|
536
|
+
type: typeNode.getText(sourceFile),
|
|
537
|
+
typeKind: "function"
|
|
538
|
+
};
|
|
539
|
+
}
|
|
540
|
+
if (ts.isArrayTypeNode(typeNode)) {
|
|
541
|
+
return {
|
|
542
|
+
type: typeNode.getText(sourceFile),
|
|
543
|
+
typeKind: "array"
|
|
544
|
+
};
|
|
545
|
+
}
|
|
546
|
+
if (ts.isTypeReferenceNode(typeNode)) {
|
|
547
|
+
const typeName = typeNode.typeName.getText(sourceFile);
|
|
548
|
+
if (typeName === "ReactNode" || typeName === "React.ReactNode") {
|
|
549
|
+
return { type: "ReactNode", typeKind: "node" };
|
|
550
|
+
}
|
|
551
|
+
if (typeName === "ReactElement" || typeName === "React.ReactElement") {
|
|
552
|
+
return { type: "ReactElement", typeKind: "element" };
|
|
553
|
+
}
|
|
554
|
+
if (typeName === "JSX.Element") {
|
|
555
|
+
return { type: "JSX.Element", typeKind: "element" };
|
|
556
|
+
}
|
|
557
|
+
return {
|
|
558
|
+
type: typeNode.getText(sourceFile),
|
|
559
|
+
typeKind: "object"
|
|
560
|
+
};
|
|
561
|
+
}
|
|
562
|
+
if (ts.isTypeLiteralNode(typeNode)) {
|
|
563
|
+
return {
|
|
564
|
+
type: typeNode.getText(sourceFile),
|
|
565
|
+
typeKind: "object"
|
|
566
|
+
};
|
|
567
|
+
}
|
|
568
|
+
return {
|
|
569
|
+
type: typeNode.getText(sourceFile),
|
|
570
|
+
typeKind: "unknown"
|
|
571
|
+
};
|
|
572
|
+
}
|
|
573
|
+
function getJSDocComment(node) {
|
|
574
|
+
const jsDocTags = ts.getJSDocTags(node);
|
|
575
|
+
const jsDoc = node.jsDoc;
|
|
576
|
+
if (jsDoc && jsDoc.length > 0) {
|
|
577
|
+
const comment = jsDoc[0].comment;
|
|
578
|
+
if (typeof comment === "string") {
|
|
579
|
+
return comment;
|
|
580
|
+
}
|
|
581
|
+
if (Array.isArray(comment)) {
|
|
582
|
+
return comment.map((c) => typeof c === "string" ? c : c.text).join("");
|
|
583
|
+
}
|
|
584
|
+
}
|
|
585
|
+
return void 0;
|
|
586
|
+
}
|
|
587
|
+
function getJSDocDefault(node) {
|
|
588
|
+
const jsDocTags = ts.getJSDocTags(node);
|
|
589
|
+
for (const tag of jsDocTags) {
|
|
590
|
+
if (tag.tagName.text === "default") {
|
|
591
|
+
const comment = tag.comment;
|
|
592
|
+
if (typeof comment === "string") {
|
|
593
|
+
try {
|
|
594
|
+
return JSON.parse(comment);
|
|
595
|
+
} catch {
|
|
596
|
+
return comment;
|
|
597
|
+
}
|
|
598
|
+
}
|
|
599
|
+
}
|
|
600
|
+
}
|
|
601
|
+
return void 0;
|
|
602
|
+
}
|
|
603
|
+
|
|
604
|
+
// src/core/generators/registry.ts
|
|
605
|
+
import { readFileSync as readFileSync2 } from "fs";
|
|
606
|
+
import { relative, dirname as dirname3, basename as basename2, join } from "path";
|
|
607
|
+
import fg2 from "fast-glob";
|
|
608
|
+
|
|
398
609
|
// src/core/loader.ts
|
|
399
610
|
import { unlink } from "fs/promises";
|
|
400
|
-
import { dirname as
|
|
611
|
+
import { dirname as dirname4, basename as basename3, join as join2 } from "path";
|
|
401
612
|
import { pathToFileURL } from "url";
|
|
402
613
|
import { build } from "esbuild";
|
|
403
614
|
var DEFINE_FRAGMENT_SHIM = `
|
|
@@ -458,9 +669,9 @@ async function loadFragmentFile(absolutePath) {
|
|
|
458
669
|
const module = await import(fileUrl);
|
|
459
670
|
return unwrapFragmentExport(module.default ?? null);
|
|
460
671
|
}
|
|
461
|
-
const sourceDir =
|
|
462
|
-
const baseName =
|
|
463
|
-
const tempFile =
|
|
672
|
+
const sourceDir = dirname4(absolutePath);
|
|
673
|
+
const baseName = basename3(absolutePath, `.${ext}`);
|
|
674
|
+
const tempFile = join2(sourceDir, `.${baseName}.fragments-temp-${Date.now()}.mjs`);
|
|
464
675
|
try {
|
|
465
676
|
await build({
|
|
466
677
|
entryPoints: [absolutePath],
|
|
@@ -510,38 +721,17 @@ async function loadFragmentFile(absolutePath) {
|
|
|
510
721
|
}
|
|
511
722
|
}
|
|
512
723
|
}
|
|
513
|
-
async function loadFragmentFiles(absolutePaths) {
|
|
514
|
-
const results = /* @__PURE__ */ new Map();
|
|
515
|
-
await Promise.all(
|
|
516
|
-
absolutePaths.map(async (path) => {
|
|
517
|
-
try {
|
|
518
|
-
const fragment = await loadFragmentFile(path);
|
|
519
|
-
if (fragment) {
|
|
520
|
-
results.set(path, fragment);
|
|
521
|
-
} else {
|
|
522
|
-
results.set(path, new Error("No default export found"));
|
|
523
|
-
}
|
|
524
|
-
} catch (error) {
|
|
525
|
-
results.set(
|
|
526
|
-
path,
|
|
527
|
-
error instanceof Error ? error : new Error(String(error))
|
|
528
|
-
);
|
|
529
|
-
}
|
|
530
|
-
})
|
|
531
|
-
);
|
|
532
|
-
return results;
|
|
533
|
-
}
|
|
534
724
|
|
|
535
725
|
// src/core/parser.ts
|
|
536
|
-
import
|
|
726
|
+
import ts2 from "typescript";
|
|
537
727
|
function parseFragmentFile(fileContent, filePath) {
|
|
538
728
|
const warnings = [];
|
|
539
|
-
const sourceFile =
|
|
729
|
+
const sourceFile = ts2.createSourceFile(
|
|
540
730
|
filePath ?? "fragment.tsx",
|
|
541
731
|
fileContent,
|
|
542
|
-
|
|
732
|
+
ts2.ScriptTarget.Latest,
|
|
543
733
|
true,
|
|
544
|
-
|
|
734
|
+
ts2.ScriptKind.TSX
|
|
545
735
|
);
|
|
546
736
|
const imports = extractImports(sourceFile);
|
|
547
737
|
const defineFragmentCall = findDefineFragmentCall(sourceFile);
|
|
@@ -559,7 +749,7 @@ function parseFragmentFile(fileContent, filePath) {
|
|
|
559
749
|
};
|
|
560
750
|
}
|
|
561
751
|
const arg = defineFragmentCall.arguments[0];
|
|
562
|
-
if (!arg || !
|
|
752
|
+
if (!arg || !ts2.isObjectLiteralExpression(arg)) {
|
|
563
753
|
warnings.push("defineFragment() argument is not an object literal");
|
|
564
754
|
return {
|
|
565
755
|
componentImport: null,
|
|
@@ -575,7 +765,7 @@ function parseFragmentFile(fileContent, filePath) {
|
|
|
575
765
|
const componentProp = findProperty(arg, "component");
|
|
576
766
|
let componentName = null;
|
|
577
767
|
let componentImport = null;
|
|
578
|
-
if (componentProp &&
|
|
768
|
+
if (componentProp && ts2.isIdentifier(componentProp)) {
|
|
579
769
|
componentName = componentProp.text;
|
|
580
770
|
componentImport = imports.get(componentName) ?? null;
|
|
581
771
|
}
|
|
@@ -601,10 +791,10 @@ function parseFragmentFile(fileContent, filePath) {
|
|
|
601
791
|
}
|
|
602
792
|
function extractImports(sourceFile) {
|
|
603
793
|
const imports = /* @__PURE__ */ new Map();
|
|
604
|
-
|
|
605
|
-
if (
|
|
794
|
+
ts2.forEachChild(sourceFile, (node) => {
|
|
795
|
+
if (ts2.isImportDeclaration(node)) {
|
|
606
796
|
const moduleSpecifier = node.moduleSpecifier;
|
|
607
|
-
if (
|
|
797
|
+
if (ts2.isStringLiteral(moduleSpecifier)) {
|
|
608
798
|
const modulePath = moduleSpecifier.text;
|
|
609
799
|
const importClause = node.importClause;
|
|
610
800
|
if (importClause) {
|
|
@@ -612,7 +802,7 @@ function extractImports(sourceFile) {
|
|
|
612
802
|
imports.set(importClause.name.text, modulePath);
|
|
613
803
|
}
|
|
614
804
|
const namedBindings = importClause.namedBindings;
|
|
615
|
-
if (namedBindings &&
|
|
805
|
+
if (namedBindings && ts2.isNamedImports(namedBindings)) {
|
|
616
806
|
for (const element of namedBindings.elements) {
|
|
617
807
|
imports.set(element.name.text, modulePath);
|
|
618
808
|
}
|
|
@@ -626,23 +816,23 @@ function extractImports(sourceFile) {
|
|
|
626
816
|
function findDefineFragmentCall(sourceFile) {
|
|
627
817
|
let result = null;
|
|
628
818
|
function visit(node) {
|
|
629
|
-
if (
|
|
819
|
+
if (ts2.isCallExpression(node)) {
|
|
630
820
|
const expression = node.expression;
|
|
631
|
-
if (
|
|
821
|
+
if (ts2.isIdentifier(expression) && expression.text === "defineFragment") {
|
|
632
822
|
result = node;
|
|
633
823
|
return;
|
|
634
824
|
}
|
|
635
825
|
}
|
|
636
|
-
|
|
826
|
+
ts2.forEachChild(node, visit);
|
|
637
827
|
}
|
|
638
828
|
visit(sourceFile);
|
|
639
829
|
return result;
|
|
640
830
|
}
|
|
641
831
|
function findProperty(obj, name) {
|
|
642
832
|
for (const prop of obj.properties) {
|
|
643
|
-
if (
|
|
833
|
+
if (ts2.isPropertyAssignment(prop)) {
|
|
644
834
|
const propName = prop.name;
|
|
645
|
-
if (
|
|
835
|
+
if (ts2.isIdentifier(propName) && propName.text === name) {
|
|
646
836
|
return prop.initializer;
|
|
647
837
|
}
|
|
648
838
|
}
|
|
@@ -651,7 +841,7 @@ function findProperty(obj, name) {
|
|
|
651
841
|
}
|
|
652
842
|
function extractMeta(arg, warnings) {
|
|
653
843
|
const metaProp = findProperty(arg, "meta");
|
|
654
|
-
if (!metaProp || !
|
|
844
|
+
if (!metaProp || !ts2.isObjectLiteralExpression(metaProp)) {
|
|
655
845
|
warnings.push("No meta object found");
|
|
656
846
|
return {};
|
|
657
847
|
}
|
|
@@ -671,7 +861,7 @@ function extractMeta(arg, warnings) {
|
|
|
671
861
|
const tags = extractStringArray(metaProp, "tags");
|
|
672
862
|
if (tags.length > 0) meta.tags = tags;
|
|
673
863
|
const depsProp = findProperty(metaProp, "dependencies");
|
|
674
|
-
if (depsProp &&
|
|
864
|
+
if (depsProp && ts2.isArrayLiteralExpression(depsProp)) {
|
|
675
865
|
const deps = extractLiteralValue(depsProp);
|
|
676
866
|
if (Array.isArray(deps) && deps.length > 0) {
|
|
677
867
|
meta.dependencies = deps;
|
|
@@ -681,7 +871,7 @@ function extractMeta(arg, warnings) {
|
|
|
681
871
|
}
|
|
682
872
|
function extractUsage(arg, warnings) {
|
|
683
873
|
const usageProp = findProperty(arg, "usage");
|
|
684
|
-
if (!usageProp || !
|
|
874
|
+
if (!usageProp || !ts2.isObjectLiteralExpression(usageProp)) {
|
|
685
875
|
return { when: [], whenNot: [] };
|
|
686
876
|
}
|
|
687
877
|
return {
|
|
@@ -693,15 +883,15 @@ function extractUsage(arg, warnings) {
|
|
|
693
883
|
}
|
|
694
884
|
function extractProps(arg, warnings) {
|
|
695
885
|
const propsProp = findProperty(arg, "props");
|
|
696
|
-
if (!propsProp || !
|
|
886
|
+
if (!propsProp || !ts2.isObjectLiteralExpression(propsProp)) {
|
|
697
887
|
return {};
|
|
698
888
|
}
|
|
699
889
|
const props = {};
|
|
700
890
|
for (const prop of propsProp.properties) {
|
|
701
|
-
if (
|
|
891
|
+
if (ts2.isPropertyAssignment(prop) && ts2.isIdentifier(prop.name)) {
|
|
702
892
|
const propName = prop.name.text;
|
|
703
893
|
const propValue = prop.initializer;
|
|
704
|
-
if (
|
|
894
|
+
if (ts2.isObjectLiteralExpression(propValue)) {
|
|
705
895
|
props[propName] = extractPropDefinition(propValue);
|
|
706
896
|
}
|
|
707
897
|
}
|
|
@@ -728,12 +918,12 @@ function extractPropDefinition(obj) {
|
|
|
728
918
|
}
|
|
729
919
|
function extractVariants(arg, sourceFile, warnings) {
|
|
730
920
|
const variantsProp = findProperty(arg, "variants");
|
|
731
|
-
if (!variantsProp || !
|
|
921
|
+
if (!variantsProp || !ts2.isArrayLiteralExpression(variantsProp)) {
|
|
732
922
|
return [];
|
|
733
923
|
}
|
|
734
924
|
const variants = [];
|
|
735
925
|
for (const element of variantsProp.elements) {
|
|
736
|
-
if (
|
|
926
|
+
if (ts2.isObjectLiteralExpression(element)) {
|
|
737
927
|
const name = extractStringProperty(element, "name");
|
|
738
928
|
const description = extractStringProperty(element, "description");
|
|
739
929
|
if (name) {
|
|
@@ -742,7 +932,7 @@ function extractVariants(arg, sourceFile, warnings) {
|
|
|
742
932
|
description: description ?? ""
|
|
743
933
|
};
|
|
744
934
|
const codeProp = findProperty(element, "code");
|
|
745
|
-
if (codeProp && (
|
|
935
|
+
if (codeProp && (ts2.isStringLiteral(codeProp) || ts2.isNoSubstitutionTemplateLiteral(codeProp))) {
|
|
746
936
|
variant.code = codeProp.text;
|
|
747
937
|
}
|
|
748
938
|
const renderProp = findProperty(element, "render");
|
|
@@ -754,7 +944,7 @@ function extractVariants(arg, sourceFile, warnings) {
|
|
|
754
944
|
variant.figma = figma;
|
|
755
945
|
}
|
|
756
946
|
const argsProp = findProperty(element, "args");
|
|
757
|
-
if (argsProp &&
|
|
947
|
+
if (argsProp && ts2.isObjectLiteralExpression(argsProp)) {
|
|
758
948
|
const argsValue = extractLiteralValue(argsProp);
|
|
759
949
|
if (argsValue && typeof argsValue === "object" && !Array.isArray(argsValue)) {
|
|
760
950
|
variant.args = argsValue;
|
|
@@ -793,7 +983,7 @@ function dedent(str) {
|
|
|
793
983
|
}).join("\n");
|
|
794
984
|
}
|
|
795
985
|
function extractRenderCode(renderProp, sourceFile) {
|
|
796
|
-
if (
|
|
986
|
+
if (ts2.isArrowFunction(renderProp)) {
|
|
797
987
|
const body = renderProp.body;
|
|
798
988
|
const start = body.getStart(sourceFile);
|
|
799
989
|
const end = body.getEnd();
|
|
@@ -808,12 +998,12 @@ function extractRenderCode(renderProp, sourceFile) {
|
|
|
808
998
|
}
|
|
809
999
|
function extractRelations(arg, warnings) {
|
|
810
1000
|
const relationsProp = findProperty(arg, "relations");
|
|
811
|
-
if (!relationsProp || !
|
|
1001
|
+
if (!relationsProp || !ts2.isArrayLiteralExpression(relationsProp)) {
|
|
812
1002
|
return [];
|
|
813
1003
|
}
|
|
814
1004
|
const relations = [];
|
|
815
1005
|
for (const element of relationsProp.elements) {
|
|
816
|
-
if (
|
|
1006
|
+
if (ts2.isObjectLiteralExpression(element)) {
|
|
817
1007
|
const component = extractStringProperty(element, "component");
|
|
818
1008
|
const relationship = extractStringProperty(element, "relationship");
|
|
819
1009
|
const note = extractStringProperty(element, "note");
|
|
@@ -830,7 +1020,7 @@ function extractRelations(arg, warnings) {
|
|
|
830
1020
|
}
|
|
831
1021
|
function extractAIMetadata(arg, warnings) {
|
|
832
1022
|
const aiProp = findProperty(arg, "ai");
|
|
833
|
-
if (!aiProp || !
|
|
1023
|
+
if (!aiProp || !ts2.isObjectLiteralExpression(aiProp)) {
|
|
834
1024
|
return void 0;
|
|
835
1025
|
}
|
|
836
1026
|
const ai = {};
|
|
@@ -857,7 +1047,7 @@ function extractAIMetadata(arg, warnings) {
|
|
|
857
1047
|
}
|
|
858
1048
|
function extractContractMetadata(arg) {
|
|
859
1049
|
const contractProp = findProperty(arg, "contract");
|
|
860
|
-
if (!contractProp || !
|
|
1050
|
+
if (!contractProp || !ts2.isObjectLiteralExpression(contractProp)) {
|
|
861
1051
|
return void 0;
|
|
862
1052
|
}
|
|
863
1053
|
const contract = {};
|
|
@@ -876,10 +1066,10 @@ function extractContractMetadata(arg) {
|
|
|
876
1066
|
}
|
|
877
1067
|
function extractStringProperty(obj, name) {
|
|
878
1068
|
const prop = findProperty(obj, name);
|
|
879
|
-
if (prop &&
|
|
1069
|
+
if (prop && ts2.isStringLiteral(prop)) {
|
|
880
1070
|
return prop.text;
|
|
881
1071
|
}
|
|
882
|
-
if (prop &&
|
|
1072
|
+
if (prop && ts2.isNoSubstitutionTemplateLiteral(prop)) {
|
|
883
1073
|
return prop.text;
|
|
884
1074
|
}
|
|
885
1075
|
return null;
|
|
@@ -887,49 +1077,49 @@ function extractStringProperty(obj, name) {
|
|
|
887
1077
|
function extractBooleanProperty(obj, name) {
|
|
888
1078
|
const prop = findProperty(obj, name);
|
|
889
1079
|
if (prop) {
|
|
890
|
-
if (prop.kind ===
|
|
891
|
-
if (prop.kind ===
|
|
1080
|
+
if (prop.kind === ts2.SyntaxKind.TrueKeyword) return true;
|
|
1081
|
+
if (prop.kind === ts2.SyntaxKind.FalseKeyword) return false;
|
|
892
1082
|
}
|
|
893
1083
|
return null;
|
|
894
1084
|
}
|
|
895
1085
|
function extractStringArray(obj, name) {
|
|
896
1086
|
const prop = findProperty(obj, name);
|
|
897
|
-
if (!prop || !
|
|
1087
|
+
if (!prop || !ts2.isArrayLiteralExpression(prop)) {
|
|
898
1088
|
return [];
|
|
899
1089
|
}
|
|
900
1090
|
const result = [];
|
|
901
1091
|
for (const element of prop.elements) {
|
|
902
|
-
if (
|
|
1092
|
+
if (ts2.isStringLiteral(element)) {
|
|
903
1093
|
result.push(element.text);
|
|
904
|
-
} else if (
|
|
1094
|
+
} else if (ts2.isNoSubstitutionTemplateLiteral(element)) {
|
|
905
1095
|
result.push(element.text);
|
|
906
1096
|
}
|
|
907
1097
|
}
|
|
908
1098
|
return result;
|
|
909
1099
|
}
|
|
910
1100
|
function extractLiteralValue(expr) {
|
|
911
|
-
if (
|
|
1101
|
+
if (ts2.isStringLiteral(expr)) {
|
|
912
1102
|
return expr.text;
|
|
913
1103
|
}
|
|
914
|
-
if (
|
|
1104
|
+
if (ts2.isNumericLiteral(expr)) {
|
|
915
1105
|
return Number(expr.text);
|
|
916
1106
|
}
|
|
917
|
-
if (expr.kind ===
|
|
1107
|
+
if (expr.kind === ts2.SyntaxKind.TrueKeyword) {
|
|
918
1108
|
return true;
|
|
919
1109
|
}
|
|
920
|
-
if (expr.kind ===
|
|
1110
|
+
if (expr.kind === ts2.SyntaxKind.FalseKeyword) {
|
|
921
1111
|
return false;
|
|
922
1112
|
}
|
|
923
|
-
if (expr.kind ===
|
|
1113
|
+
if (expr.kind === ts2.SyntaxKind.NullKeyword) {
|
|
924
1114
|
return null;
|
|
925
1115
|
}
|
|
926
|
-
if (
|
|
1116
|
+
if (ts2.isArrayLiteralExpression(expr)) {
|
|
927
1117
|
return expr.elements.map(extractLiteralValue);
|
|
928
1118
|
}
|
|
929
|
-
if (
|
|
1119
|
+
if (ts2.isObjectLiteralExpression(expr)) {
|
|
930
1120
|
const obj = {};
|
|
931
1121
|
for (const prop of expr.properties) {
|
|
932
|
-
if (
|
|
1122
|
+
if (ts2.isPropertyAssignment(prop) && ts2.isIdentifier(prop.name)) {
|
|
933
1123
|
obj[prop.name.text] = extractLiteralValue(prop.initializer);
|
|
934
1124
|
}
|
|
935
1125
|
}
|
|
@@ -939,988 +1129,24 @@ function extractLiteralValue(expr) {
|
|
|
939
1129
|
}
|
|
940
1130
|
|
|
941
1131
|
// src/core/previewLoader.ts
|
|
942
|
-
import { existsSync as
|
|
943
|
-
import { join as
|
|
944
|
-
var PREVIEW_FILES = [
|
|
945
|
-
"preview.tsx",
|
|
946
|
-
"preview.ts",
|
|
947
|
-
"preview.jsx",
|
|
948
|
-
"preview.js"
|
|
949
|
-
];
|
|
950
|
-
function findPreviewConfigPath(storybookDir) {
|
|
951
|
-
for (const fileName of PREVIEW_FILES) {
|
|
952
|
-
const filePath = join2(storybookDir, fileName);
|
|
953
|
-
if (existsSync3(filePath)) {
|
|
954
|
-
return filePath;
|
|
955
|
-
}
|
|
956
|
-
}
|
|
957
|
-
return null;
|
|
958
|
-
}
|
|
959
|
-
function findStorybookDir(projectRoot) {
|
|
960
|
-
const possiblePaths = [
|
|
961
|
-
join2(projectRoot, ".storybook"),
|
|
962
|
-
join2(projectRoot, "storybook")
|
|
963
|
-
];
|
|
964
|
-
for (const dir of possiblePaths) {
|
|
965
|
-
if (existsSync3(dir)) {
|
|
966
|
-
return dir;
|
|
967
|
-
}
|
|
968
|
-
}
|
|
969
|
-
return null;
|
|
970
|
-
}
|
|
971
|
-
async function loadPreviewConfig(previewPath) {
|
|
972
|
-
try {
|
|
973
|
-
const fileUrl = new URL(`file://${resolve3(previewPath)}`);
|
|
974
|
-
const module = await import(fileUrl.href);
|
|
975
|
-
const config = {
|
|
976
|
-
decorators: module.decorators ?? module.default?.decorators ?? [],
|
|
977
|
-
parameters: module.parameters ?? module.default?.parameters ?? {},
|
|
978
|
-
globalTypes: module.globalTypes ?? module.default?.globalTypes ?? {},
|
|
979
|
-
args: module.args ?? module.default?.args ?? {},
|
|
980
|
-
argTypes: module.argTypes ?? module.default?.argTypes ?? {},
|
|
981
|
-
loaders: module.loaders ?? module.default?.loaders ?? []
|
|
982
|
-
};
|
|
983
|
-
return config;
|
|
984
|
-
} catch (error) {
|
|
985
|
-
console.warn(
|
|
986
|
-
`[Fragments] Failed to load preview config from ${previewPath}:`,
|
|
987
|
-
error instanceof Error ? error.message : error
|
|
988
|
-
);
|
|
989
|
-
return {};
|
|
990
|
-
}
|
|
991
|
-
}
|
|
992
|
-
async function autoLoadPreviewConfig(projectRoot) {
|
|
993
|
-
const storybookDir = findStorybookDir(projectRoot);
|
|
994
|
-
if (!storybookDir) {
|
|
995
|
-
return {};
|
|
996
|
-
}
|
|
997
|
-
const previewPath = findPreviewConfigPath(storybookDir);
|
|
998
|
-
if (!previewPath) {
|
|
999
|
-
return {};
|
|
1000
|
-
}
|
|
1001
|
-
return loadPreviewConfig(previewPath);
|
|
1002
|
-
}
|
|
1003
|
-
function generatePreviewModule(previewPath) {
|
|
1004
|
-
if (!previewPath) {
|
|
1005
|
-
return `
|
|
1006
|
-
export const decorators = [];
|
|
1007
|
-
export const parameters = {};
|
|
1008
|
-
export const globalTypes = {};
|
|
1009
|
-
export const args = {};
|
|
1010
|
-
export const argTypes = {};
|
|
1011
|
-
export const loaders = [];
|
|
1012
|
-
|
|
1013
|
-
export default {
|
|
1014
|
-
decorators,
|
|
1015
|
-
parameters,
|
|
1016
|
-
globalTypes,
|
|
1017
|
-
args,
|
|
1018
|
-
argTypes,
|
|
1019
|
-
loaders,
|
|
1020
|
-
};
|
|
1021
|
-
`;
|
|
1022
|
-
}
|
|
1023
|
-
return `
|
|
1024
|
-
import * as preview from "${previewPath}";
|
|
1025
|
-
|
|
1026
|
-
export const decorators = preview.decorators ?? preview.default?.decorators ?? [];
|
|
1027
|
-
export const parameters = preview.parameters ?? preview.default?.parameters ?? {};
|
|
1028
|
-
export const globalTypes = preview.globalTypes ?? preview.default?.globalTypes ?? {};
|
|
1029
|
-
export const args = preview.args ?? preview.default?.args ?? {};
|
|
1030
|
-
export const argTypes = preview.argTypes ?? preview.default?.argTypes ?? {};
|
|
1031
|
-
export const loaders = preview.loaders ?? preview.default?.loaders ?? [];
|
|
1032
|
-
|
|
1033
|
-
export default {
|
|
1034
|
-
decorators,
|
|
1035
|
-
parameters,
|
|
1036
|
-
globalTypes,
|
|
1037
|
-
args,
|
|
1038
|
-
argTypes,
|
|
1039
|
-
loaders,
|
|
1040
|
-
};
|
|
1041
|
-
`;
|
|
1042
|
-
}
|
|
1043
|
-
|
|
1044
|
-
// src/core/generators/typescript-extractor.ts
|
|
1045
|
-
import ts2 from "typescript";
|
|
1046
|
-
import { readFileSync } from "fs";
|
|
1047
|
-
function extractPropsFromFile(filePath) {
|
|
1048
|
-
const sourceText = readFileSync(filePath, "utf-8");
|
|
1049
|
-
return extractPropsFromSource(sourceText, filePath);
|
|
1050
|
-
}
|
|
1051
|
-
function extractPropsFromSource(sourceText, fileName = "component.tsx") {
|
|
1052
|
-
const sourceFile = ts2.createSourceFile(
|
|
1053
|
-
fileName,
|
|
1054
|
-
sourceText,
|
|
1055
|
-
ts2.ScriptTarget.Latest,
|
|
1056
|
-
true,
|
|
1057
|
-
fileName.endsWith(".tsx") ? ts2.ScriptKind.TSX : ts2.ScriptKind.TS
|
|
1058
|
-
);
|
|
1059
|
-
const result = {
|
|
1060
|
-
componentName: "",
|
|
1061
|
-
props: {},
|
|
1062
|
-
exports: [],
|
|
1063
|
-
imports: []
|
|
1064
|
-
};
|
|
1065
|
-
const propsInterfaces = /* @__PURE__ */ new Map();
|
|
1066
|
-
const componentExports = [];
|
|
1067
|
-
const defaultExports = /* @__PURE__ */ new Set();
|
|
1068
|
-
const importedModules = [];
|
|
1069
|
-
ts2.forEachChild(sourceFile, (node) => {
|
|
1070
|
-
if (ts2.isImportDeclaration(node)) {
|
|
1071
|
-
const moduleSpecifier = node.moduleSpecifier;
|
|
1072
|
-
if (ts2.isStringLiteral(moduleSpecifier)) {
|
|
1073
|
-
importedModules.push(moduleSpecifier.text);
|
|
1074
|
-
}
|
|
1075
|
-
}
|
|
1076
|
-
if (ts2.isInterfaceDeclaration(node)) {
|
|
1077
|
-
const name = node.name.text;
|
|
1078
|
-
if (name.endsWith("Props")) {
|
|
1079
|
-
propsInterfaces.set(name, node);
|
|
1080
|
-
}
|
|
1081
|
-
}
|
|
1082
|
-
if (ts2.isTypeAliasDeclaration(node)) {
|
|
1083
|
-
const name = node.name.text;
|
|
1084
|
-
if (name.endsWith("Props")) {
|
|
1085
|
-
propsInterfaces.set(name, node);
|
|
1086
|
-
}
|
|
1087
|
-
}
|
|
1088
|
-
if (ts2.isFunctionDeclaration(node) && node.name) {
|
|
1089
|
-
const hasExport = node.modifiers?.some(
|
|
1090
|
-
(m) => m.kind === ts2.SyntaxKind.ExportKeyword
|
|
1091
|
-
);
|
|
1092
|
-
const hasDefault = node.modifiers?.some(
|
|
1093
|
-
(m) => m.kind === ts2.SyntaxKind.DefaultKeyword
|
|
1094
|
-
);
|
|
1095
|
-
if (hasExport) {
|
|
1096
|
-
componentExports.push(node.name.text);
|
|
1097
|
-
if (hasDefault) {
|
|
1098
|
-
defaultExports.add(node.name.text);
|
|
1099
|
-
}
|
|
1100
|
-
}
|
|
1101
|
-
}
|
|
1102
|
-
if (ts2.isVariableStatement(node)) {
|
|
1103
|
-
const hasExport = node.modifiers?.some(
|
|
1104
|
-
(m) => m.kind === ts2.SyntaxKind.ExportKeyword
|
|
1105
|
-
);
|
|
1106
|
-
if (hasExport) {
|
|
1107
|
-
for (const decl of node.declarationList.declarations) {
|
|
1108
|
-
if (ts2.isIdentifier(decl.name)) {
|
|
1109
|
-
componentExports.push(decl.name.text);
|
|
1110
|
-
}
|
|
1111
|
-
}
|
|
1112
|
-
}
|
|
1113
|
-
}
|
|
1114
|
-
if (ts2.isExportDeclaration(node) && node.exportClause) {
|
|
1115
|
-
if (ts2.isNamedExports(node.exportClause)) {
|
|
1116
|
-
for (const element of node.exportClause.elements) {
|
|
1117
|
-
componentExports.push(element.name.text);
|
|
1118
|
-
}
|
|
1119
|
-
}
|
|
1120
|
-
}
|
|
1121
|
-
});
|
|
1122
|
-
result.exports = componentExports;
|
|
1123
|
-
result.imports = importedModules;
|
|
1124
|
-
const mainComponent = componentExports.find(
|
|
1125
|
-
(name) => /^[A-Z]/.test(name) && !name.endsWith("Props")
|
|
1126
|
-
);
|
|
1127
|
-
if (!mainComponent) {
|
|
1128
|
-
return null;
|
|
1129
|
-
}
|
|
1130
|
-
result.componentName = mainComponent;
|
|
1131
|
-
result.isDefaultExport = defaultExports.has(mainComponent);
|
|
1132
|
-
const propsInterfaceName = `${mainComponent}Props`;
|
|
1133
|
-
const propsInterface = propsInterfaces.get(propsInterfaceName);
|
|
1134
|
-
if (propsInterface) {
|
|
1135
|
-
result.propsInterfaceName = propsInterfaceName;
|
|
1136
|
-
result.props = extractPropsFromInterface(propsInterface, sourceFile);
|
|
1137
|
-
}
|
|
1138
|
-
return result;
|
|
1139
|
-
}
|
|
1140
|
-
function extractPropsFromInterface(node, sourceFile) {
|
|
1141
|
-
const props = {};
|
|
1142
|
-
if (ts2.isInterfaceDeclaration(node)) {
|
|
1143
|
-
for (const member of node.members) {
|
|
1144
|
-
if (ts2.isPropertySignature(member) && member.name) {
|
|
1145
|
-
const propName = member.name.getText(sourceFile);
|
|
1146
|
-
const prop = extractPropFromMember(member, sourceFile);
|
|
1147
|
-
if (prop) {
|
|
1148
|
-
props[propName] = prop;
|
|
1149
|
-
}
|
|
1150
|
-
}
|
|
1151
|
-
}
|
|
1152
|
-
}
|
|
1153
|
-
if (ts2.isTypeAliasDeclaration(node)) {
|
|
1154
|
-
const typeNode = node.type;
|
|
1155
|
-
if (ts2.isTypeLiteralNode(typeNode)) {
|
|
1156
|
-
for (const member of typeNode.members) {
|
|
1157
|
-
if (ts2.isPropertySignature(member) && member.name) {
|
|
1158
|
-
const propName = member.name.getText(sourceFile);
|
|
1159
|
-
const prop = extractPropFromMember(member, sourceFile);
|
|
1160
|
-
if (prop) {
|
|
1161
|
-
props[propName] = prop;
|
|
1162
|
-
}
|
|
1163
|
-
}
|
|
1164
|
-
}
|
|
1165
|
-
}
|
|
1166
|
-
}
|
|
1167
|
-
return props;
|
|
1168
|
-
}
|
|
1169
|
-
function extractPropFromMember(member, sourceFile) {
|
|
1170
|
-
const entry = {};
|
|
1171
|
-
entry.required = !member.questionToken;
|
|
1172
|
-
if (member.type) {
|
|
1173
|
-
const typeInfo = parseTypeNode(member.type, sourceFile);
|
|
1174
|
-
entry.type = typeInfo.type;
|
|
1175
|
-
entry.typeKind = typeInfo.typeKind;
|
|
1176
|
-
if (typeInfo.options) {
|
|
1177
|
-
entry.options = typeInfo.options;
|
|
1178
|
-
}
|
|
1179
|
-
}
|
|
1180
|
-
const jsDocComment = getJSDocComment(member);
|
|
1181
|
-
if (jsDocComment) {
|
|
1182
|
-
entry.description = jsDocComment;
|
|
1183
|
-
}
|
|
1184
|
-
const defaultValue = getJSDocDefault(member);
|
|
1185
|
-
if (defaultValue !== void 0) {
|
|
1186
|
-
entry.default = defaultValue;
|
|
1187
|
-
}
|
|
1188
|
-
return entry;
|
|
1189
|
-
}
|
|
1190
|
-
function parseTypeNode(typeNode, sourceFile) {
|
|
1191
|
-
if (typeNode.kind === ts2.SyntaxKind.StringKeyword) {
|
|
1192
|
-
return { type: "string", typeKind: "string" };
|
|
1193
|
-
}
|
|
1194
|
-
if (typeNode.kind === ts2.SyntaxKind.NumberKeyword) {
|
|
1195
|
-
return { type: "number", typeKind: "number" };
|
|
1196
|
-
}
|
|
1197
|
-
if (typeNode.kind === ts2.SyntaxKind.BooleanKeyword) {
|
|
1198
|
-
return { type: "boolean", typeKind: "boolean" };
|
|
1199
|
-
}
|
|
1200
|
-
if (ts2.isUnionTypeNode(typeNode)) {
|
|
1201
|
-
const options = [];
|
|
1202
|
-
let allLiterals = true;
|
|
1203
|
-
for (const subType of typeNode.types) {
|
|
1204
|
-
if (ts2.isLiteralTypeNode(subType)) {
|
|
1205
|
-
if (ts2.isStringLiteral(subType.literal)) {
|
|
1206
|
-
options.push(subType.literal.text);
|
|
1207
|
-
} else if (subType.literal.kind === ts2.SyntaxKind.TrueKeyword) {
|
|
1208
|
-
options.push("true");
|
|
1209
|
-
} else if (subType.literal.kind === ts2.SyntaxKind.FalseKeyword) {
|
|
1210
|
-
options.push("false");
|
|
1211
|
-
} else {
|
|
1212
|
-
allLiterals = false;
|
|
1213
|
-
}
|
|
1214
|
-
} else {
|
|
1215
|
-
allLiterals = false;
|
|
1216
|
-
}
|
|
1217
|
-
}
|
|
1218
|
-
if (allLiterals && options.length > 0) {
|
|
1219
|
-
return {
|
|
1220
|
-
type: options.map((o) => `"${o}"`).join(" | "),
|
|
1221
|
-
typeKind: "enum",
|
|
1222
|
-
options
|
|
1223
|
-
};
|
|
1224
|
-
}
|
|
1225
|
-
return {
|
|
1226
|
-
type: typeNode.getText(sourceFile),
|
|
1227
|
-
typeKind: "union"
|
|
1228
|
-
};
|
|
1229
|
-
}
|
|
1230
|
-
if (ts2.isFunctionTypeNode(typeNode)) {
|
|
1231
|
-
return {
|
|
1232
|
-
type: typeNode.getText(sourceFile),
|
|
1233
|
-
typeKind: "function"
|
|
1234
|
-
};
|
|
1235
|
-
}
|
|
1236
|
-
if (ts2.isArrayTypeNode(typeNode)) {
|
|
1237
|
-
return {
|
|
1238
|
-
type: typeNode.getText(sourceFile),
|
|
1239
|
-
typeKind: "array"
|
|
1240
|
-
};
|
|
1241
|
-
}
|
|
1242
|
-
if (ts2.isTypeReferenceNode(typeNode)) {
|
|
1243
|
-
const typeName = typeNode.typeName.getText(sourceFile);
|
|
1244
|
-
if (typeName === "ReactNode" || typeName === "React.ReactNode") {
|
|
1245
|
-
return { type: "ReactNode", typeKind: "node" };
|
|
1246
|
-
}
|
|
1247
|
-
if (typeName === "ReactElement" || typeName === "React.ReactElement") {
|
|
1248
|
-
return { type: "ReactElement", typeKind: "element" };
|
|
1249
|
-
}
|
|
1250
|
-
if (typeName === "JSX.Element") {
|
|
1251
|
-
return { type: "JSX.Element", typeKind: "element" };
|
|
1252
|
-
}
|
|
1253
|
-
return {
|
|
1254
|
-
type: typeNode.getText(sourceFile),
|
|
1255
|
-
typeKind: "object"
|
|
1256
|
-
};
|
|
1257
|
-
}
|
|
1258
|
-
if (ts2.isTypeLiteralNode(typeNode)) {
|
|
1259
|
-
return {
|
|
1260
|
-
type: typeNode.getText(sourceFile),
|
|
1261
|
-
typeKind: "object"
|
|
1262
|
-
};
|
|
1263
|
-
}
|
|
1264
|
-
return {
|
|
1265
|
-
type: typeNode.getText(sourceFile),
|
|
1266
|
-
typeKind: "unknown"
|
|
1267
|
-
};
|
|
1268
|
-
}
|
|
1269
|
-
function getJSDocComment(node) {
|
|
1270
|
-
const jsDocTags = ts2.getJSDocTags(node);
|
|
1271
|
-
const jsDoc = node.jsDoc;
|
|
1272
|
-
if (jsDoc && jsDoc.length > 0) {
|
|
1273
|
-
const comment = jsDoc[0].comment;
|
|
1274
|
-
if (typeof comment === "string") {
|
|
1275
|
-
return comment;
|
|
1276
|
-
}
|
|
1277
|
-
if (Array.isArray(comment)) {
|
|
1278
|
-
return comment.map((c) => typeof c === "string" ? c : c.text).join("");
|
|
1279
|
-
}
|
|
1280
|
-
}
|
|
1281
|
-
return void 0;
|
|
1282
|
-
}
|
|
1283
|
-
function getJSDocDefault(node) {
|
|
1284
|
-
const jsDocTags = ts2.getJSDocTags(node);
|
|
1285
|
-
for (const tag of jsDocTags) {
|
|
1286
|
-
if (tag.tagName.text === "default") {
|
|
1287
|
-
const comment = tag.comment;
|
|
1288
|
-
if (typeof comment === "string") {
|
|
1289
|
-
try {
|
|
1290
|
-
return JSON.parse(comment);
|
|
1291
|
-
} catch {
|
|
1292
|
-
return comment;
|
|
1293
|
-
}
|
|
1294
|
-
}
|
|
1295
|
-
}
|
|
1296
|
-
}
|
|
1297
|
-
return void 0;
|
|
1298
|
-
}
|
|
1299
|
-
|
|
1300
|
-
// src/core/generators/registry.ts
|
|
1301
|
-
import { readFileSync as readFileSync2 } from "fs";
|
|
1302
|
-
import { relative, dirname as dirname4, basename as basename3, join as join3 } from "path";
|
|
1303
|
-
import fg2 from "fast-glob";
|
|
1304
|
-
async function generateRegistry(options) {
|
|
1305
|
-
const {
|
|
1306
|
-
projectRoot,
|
|
1307
|
-
componentPatterns = ["src/**/*.tsx", "src/**/*.ts"],
|
|
1308
|
-
storyPatterns = ["src/**/*.stories.tsx", "src/**/*.stories.ts"],
|
|
1309
|
-
fragmentsDir = join3(projectRoot, BRAND.dataDir),
|
|
1310
|
-
registryOptions = {}
|
|
1311
|
-
} = options;
|
|
1312
|
-
const {
|
|
1313
|
-
requireStory = false,
|
|
1314
|
-
publicOnly = false,
|
|
1315
|
-
categoryDepth = 1,
|
|
1316
|
-
includeProps = false,
|
|
1317
|
-
embedFragments = false
|
|
1318
|
-
} = registryOptions;
|
|
1319
|
-
const errors = [];
|
|
1320
|
-
const warnings = [];
|
|
1321
|
-
const storyFiles = await fg2(storyPatterns, {
|
|
1322
|
-
cwd: projectRoot,
|
|
1323
|
-
ignore: ["**/node_modules/**"],
|
|
1324
|
-
absolute: true
|
|
1325
|
-
});
|
|
1326
|
-
const storyMap = /* @__PURE__ */ new Map();
|
|
1327
|
-
for (const storyPath of storyFiles) {
|
|
1328
|
-
const storyDir = dirname4(storyPath);
|
|
1329
|
-
const storyBase = basename3(storyPath).replace(/\.stories\.(tsx?|jsx?)$/, "");
|
|
1330
|
-
storyMap.set(`${storyDir}/${storyBase}`, storyPath);
|
|
1331
|
-
}
|
|
1332
|
-
const componentFiles = await fg2(componentPatterns, {
|
|
1333
|
-
cwd: projectRoot,
|
|
1334
|
-
ignore: [
|
|
1335
|
-
"**/node_modules/**",
|
|
1336
|
-
"**/*.stories.*",
|
|
1337
|
-
"**/*.test.*",
|
|
1338
|
-
"**/*.spec.*",
|
|
1339
|
-
"**/*.d.ts"
|
|
1340
|
-
],
|
|
1341
|
-
absolute: true
|
|
1342
|
-
});
|
|
1343
|
-
const fragmentPattern = join3(
|
|
1344
|
-
BRAND.dataDir,
|
|
1345
|
-
BRAND.componentsDir,
|
|
1346
|
-
`*${BRAND.fileExtension}`
|
|
1347
|
-
);
|
|
1348
|
-
const fragmentFiles = await fg2(fragmentPattern, {
|
|
1349
|
-
cwd: projectRoot,
|
|
1350
|
-
absolute: true
|
|
1351
|
-
});
|
|
1352
|
-
const fragmentMap = /* @__PURE__ */ new Map();
|
|
1353
|
-
for (const fragmentPath of fragmentFiles) {
|
|
1354
|
-
const fragmentName = basename3(fragmentPath).replace(BRAND.fileExtension, "");
|
|
1355
|
-
try {
|
|
1356
|
-
const content = readFileSync2(fragmentPath, "utf-8");
|
|
1357
|
-
const fragment = JSON.parse(content);
|
|
1358
|
-
fragmentMap.set(fragmentName, {
|
|
1359
|
-
path: relative(projectRoot, fragmentPath),
|
|
1360
|
-
fragment
|
|
1361
|
-
});
|
|
1362
|
-
} catch (e) {
|
|
1363
|
-
errors.push({
|
|
1364
|
-
file: fragmentPath,
|
|
1365
|
-
error: `Failed to parse fragment: ${e instanceof Error ? e.message : String(e)}`
|
|
1366
|
-
});
|
|
1367
|
-
}
|
|
1368
|
-
}
|
|
1369
|
-
const components = {};
|
|
1370
|
-
const indexComponents = {};
|
|
1371
|
-
const categories = {};
|
|
1372
|
-
for (const filePath of componentFiles) {
|
|
1373
|
-
try {
|
|
1374
|
-
const extracted = extractPropsFromFile(filePath);
|
|
1375
|
-
if (!extracted || !extracted.componentName) {
|
|
1376
|
-
continue;
|
|
1377
|
-
}
|
|
1378
|
-
const componentName = extracted.componentName;
|
|
1379
|
-
const relativePath = relative(projectRoot, filePath);
|
|
1380
|
-
if (publicOnly && !extracted.exports.includes(componentName)) {
|
|
1381
|
-
continue;
|
|
1382
|
-
}
|
|
1383
|
-
const componentDir = dirname4(filePath);
|
|
1384
|
-
const baseNameWithoutExt = basename3(filePath).replace(/\.(tsx?|jsx?)$/, "");
|
|
1385
|
-
const storyPath = storyMap.get(`${componentDir}/${baseNameWithoutExt}`);
|
|
1386
|
-
if (requireStory && !storyPath) {
|
|
1387
|
-
continue;
|
|
1388
|
-
}
|
|
1389
|
-
const fragmentData = fragmentMap.get(componentName);
|
|
1390
|
-
const category = fragmentData?.fragment?.meta?.status ? void 0 : getCategoryFromPath(relativePath, categoryDepth);
|
|
1391
|
-
const hasEnrichment = fragmentData ? hasRealEnrichment(fragmentData.fragment) : false;
|
|
1392
|
-
const entry = {
|
|
1393
|
-
path: relativePath
|
|
1394
|
-
};
|
|
1395
|
-
if (storyPath) {
|
|
1396
|
-
entry.storyPath = relative(projectRoot, storyPath);
|
|
1397
|
-
}
|
|
1398
|
-
if (fragmentData) {
|
|
1399
|
-
entry.fragmentPath = fragmentData.path;
|
|
1400
|
-
if (hasEnrichment) {
|
|
1401
|
-
entry.hasEnrichment = true;
|
|
1402
|
-
}
|
|
1403
|
-
if (fragmentData.fragment.description) {
|
|
1404
|
-
entry.description = fragmentData.fragment.description;
|
|
1405
|
-
}
|
|
1406
|
-
if (fragmentData.fragment.meta?.status) {
|
|
1407
|
-
entry.status = fragmentData.fragment.meta.status;
|
|
1408
|
-
}
|
|
1409
|
-
if (embedFragments) {
|
|
1410
|
-
entry.fragment = fragmentData.fragment;
|
|
1411
|
-
}
|
|
1412
|
-
}
|
|
1413
|
-
if (category) {
|
|
1414
|
-
entry.category = category;
|
|
1415
|
-
if (!categories[category]) {
|
|
1416
|
-
categories[category] = [];
|
|
1417
|
-
}
|
|
1418
|
-
categories[category].push(componentName);
|
|
1419
|
-
}
|
|
1420
|
-
if (includeProps && extracted.props) {
|
|
1421
|
-
entry.props = extracted.props;
|
|
1422
|
-
}
|
|
1423
|
-
if (extracted.exports.length > 1) {
|
|
1424
|
-
entry.exports = extracted.exports;
|
|
1425
|
-
}
|
|
1426
|
-
components[componentName] = entry;
|
|
1427
|
-
indexComponents[componentName] = relativePath;
|
|
1428
|
-
} catch (e) {
|
|
1429
|
-
errors.push({
|
|
1430
|
-
file: filePath,
|
|
1431
|
-
error: `Failed to extract component: ${e instanceof Error ? e.message : String(e)}`
|
|
1432
|
-
});
|
|
1433
|
-
}
|
|
1434
|
-
}
|
|
1435
|
-
const componentCount = Object.keys(components).length;
|
|
1436
|
-
const registry = {
|
|
1437
|
-
$schema: "https://fragments.dev/schema/registry-v1.json",
|
|
1438
|
-
version: "1.0",
|
|
1439
|
-
generatedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
1440
|
-
componentCount,
|
|
1441
|
-
components,
|
|
1442
|
-
categories
|
|
1443
|
-
};
|
|
1444
|
-
const index = {
|
|
1445
|
-
version: "1.0",
|
|
1446
|
-
generatedAt: registry.generatedAt,
|
|
1447
|
-
components: indexComponents,
|
|
1448
|
-
categories
|
|
1449
|
-
};
|
|
1450
|
-
return { registry, index, errors, warnings };
|
|
1451
|
-
}
|
|
1452
|
-
function getCategoryFromPath(relativePath, depth = 1) {
|
|
1453
|
-
const parts = relativePath.split("/");
|
|
1454
|
-
const componentsIndex = parts.findIndex((p) => p === "components");
|
|
1455
|
-
if (componentsIndex === -1) {
|
|
1456
|
-
return void 0;
|
|
1457
|
-
}
|
|
1458
|
-
const afterComponents = parts.slice(componentsIndex + 1);
|
|
1459
|
-
if (afterComponents.length <= 1) {
|
|
1460
|
-
return void 0;
|
|
1461
|
-
}
|
|
1462
|
-
const folderName = afterComponents[0];
|
|
1463
|
-
const fileName = afterComponents[afterComponents.length - 1].replace(/\.(tsx?|jsx?)$/, "");
|
|
1464
|
-
if (afterComponents.length === 2 && folderName === fileName) {
|
|
1465
|
-
return void 0;
|
|
1466
|
-
}
|
|
1467
|
-
const categoryParts = afterComponents.slice(0, Math.min(depth, afterComponents.length - 1));
|
|
1468
|
-
const lastCategoryPart = categoryParts[categoryParts.length - 1];
|
|
1469
|
-
if (lastCategoryPart === fileName) {
|
|
1470
|
-
categoryParts.pop();
|
|
1471
|
-
}
|
|
1472
|
-
if (categoryParts.length === 0) {
|
|
1473
|
-
return void 0;
|
|
1474
|
-
}
|
|
1475
|
-
return categoryParts.join("/");
|
|
1476
|
-
}
|
|
1477
|
-
function hasRealEnrichment(fragment) {
|
|
1478
|
-
if (fragment.description && fragment.description.length > 20) {
|
|
1479
|
-
return true;
|
|
1480
|
-
}
|
|
1481
|
-
if (fragment.usage?.when && fragment.usage.when.length > 0) {
|
|
1482
|
-
return true;
|
|
1483
|
-
}
|
|
1484
|
-
if (fragment.usage?.doNot && fragment.usage.doNot.length > 0) {
|
|
1485
|
-
return true;
|
|
1486
|
-
}
|
|
1487
|
-
if (fragment.usage?.patterns && fragment.usage.patterns.length > 0) {
|
|
1488
|
-
return true;
|
|
1489
|
-
}
|
|
1490
|
-
if (fragment.accessibility?.requirements && fragment.accessibility.requirements.length > 0) {
|
|
1491
|
-
return true;
|
|
1492
|
-
}
|
|
1493
|
-
if (fragment.figma?.nodeId || fragment.figma?.variants) {
|
|
1494
|
-
return true;
|
|
1495
|
-
}
|
|
1496
|
-
if (fragment.related?.similar && fragment.related.similar.length > 0) {
|
|
1497
|
-
return true;
|
|
1498
|
-
}
|
|
1499
|
-
return false;
|
|
1500
|
-
}
|
|
1501
|
-
function resolveComponentPath(componentName, registry) {
|
|
1502
|
-
const entry = registry.components[componentName];
|
|
1503
|
-
return entry?.path;
|
|
1504
|
-
}
|
|
1505
|
-
function getComponentsByCategory(category, registry) {
|
|
1506
|
-
return registry.categories?.[category] || [];
|
|
1507
|
-
}
|
|
1508
|
-
|
|
1509
|
-
// src/core/generators/context.ts
|
|
1510
|
-
function generateContextMd(registry, options = {}) {
|
|
1511
|
-
const {
|
|
1512
|
-
format = "markdown",
|
|
1513
|
-
compact = false,
|
|
1514
|
-
include = { props: true, relations: true, code: false }
|
|
1515
|
-
} = options;
|
|
1516
|
-
if (format === "json") {
|
|
1517
|
-
return generateJsonContext(registry, include, compact);
|
|
1518
|
-
}
|
|
1519
|
-
return generateMarkdownContext(registry, include, compact);
|
|
1520
|
-
}
|
|
1521
|
-
function generateMarkdownContext(registry, include, compact) {
|
|
1522
|
-
const lines = [];
|
|
1523
|
-
const componentNames = Object.keys(registry.components).sort();
|
|
1524
|
-
const componentCount = componentNames.length;
|
|
1525
|
-
lines.push("# Component Library Context");
|
|
1526
|
-
lines.push(`Generated: ${(/* @__PURE__ */ new Date()).toISOString().split("T")[0]} | Components: ${componentCount}`);
|
|
1527
|
-
lines.push("");
|
|
1528
|
-
lines.push("## Quick Reference");
|
|
1529
|
-
lines.push("");
|
|
1530
|
-
lines.push("| Component | Path | Category | Status |");
|
|
1531
|
-
lines.push("|-----------|------|----------|--------|");
|
|
1532
|
-
for (const name of componentNames) {
|
|
1533
|
-
const entry = registry.components[name];
|
|
1534
|
-
const status = entry.status || "stable";
|
|
1535
|
-
const category = entry.category || "-";
|
|
1536
|
-
lines.push(`| ${name} | ${entry.path} | ${category} | ${status} |`);
|
|
1537
|
-
}
|
|
1538
|
-
lines.push("");
|
|
1539
|
-
if (compact) {
|
|
1540
|
-
const content2 = lines.join("\n");
|
|
1541
|
-
return {
|
|
1542
|
-
content: content2,
|
|
1543
|
-
tokenEstimate: estimateTokens(content2),
|
|
1544
|
-
componentCount
|
|
1545
|
-
};
|
|
1546
|
-
}
|
|
1547
|
-
lines.push("---");
|
|
1548
|
-
lines.push("");
|
|
1549
|
-
for (const name of componentNames) {
|
|
1550
|
-
const entry = registry.components[name];
|
|
1551
|
-
const fragment = entry.fragment;
|
|
1552
|
-
lines.push(`## ${name}`);
|
|
1553
|
-
lines.push(`**Path:** \`${entry.path}\``);
|
|
1554
|
-
if (entry.category) {
|
|
1555
|
-
lines.push(`**Category:** ${entry.category} | **Status:** ${entry.status || "stable"}`);
|
|
1556
|
-
}
|
|
1557
|
-
lines.push("");
|
|
1558
|
-
if (entry.description || fragment?.description) {
|
|
1559
|
-
lines.push("### Description");
|
|
1560
|
-
lines.push(entry.description || fragment?.description || "");
|
|
1561
|
-
lines.push("");
|
|
1562
|
-
}
|
|
1563
|
-
if (fragment?.usage) {
|
|
1564
|
-
if (fragment.usage.when && fragment.usage.when.length > 0) {
|
|
1565
|
-
lines.push("### When to Use");
|
|
1566
|
-
for (const when of fragment.usage.when) {
|
|
1567
|
-
lines.push(`- ${when}`);
|
|
1568
|
-
}
|
|
1569
|
-
lines.push("");
|
|
1570
|
-
}
|
|
1571
|
-
if (fragment.usage.doNot && fragment.usage.doNot.length > 0) {
|
|
1572
|
-
lines.push("### Do Not");
|
|
1573
|
-
for (const doNotItem of fragment.usage.doNot) {
|
|
1574
|
-
if (typeof doNotItem === "string") {
|
|
1575
|
-
lines.push(`- ${doNotItem}`);
|
|
1576
|
-
} else {
|
|
1577
|
-
const item = doNotItem;
|
|
1578
|
-
if (item.instead) {
|
|
1579
|
-
const alternativePath = resolveComponentPath2(item.instead, registry);
|
|
1580
|
-
if (alternativePath) {
|
|
1581
|
-
lines.push(`- ${item.text} \u2192 use **${item.instead}** (\`${alternativePath}\`)`);
|
|
1582
|
-
} else {
|
|
1583
|
-
lines.push(`- ${item.text} \u2192 use **${item.instead}**`);
|
|
1584
|
-
}
|
|
1585
|
-
} else {
|
|
1586
|
-
lines.push(`- ${item.text}`);
|
|
1587
|
-
}
|
|
1588
|
-
}
|
|
1589
|
-
}
|
|
1590
|
-
lines.push("");
|
|
1591
|
-
}
|
|
1592
|
-
if (include.code && fragment.usage.patterns && fragment.usage.patterns.length > 0) {
|
|
1593
|
-
lines.push("### Patterns");
|
|
1594
|
-
for (const pattern of fragment.usage.patterns) {
|
|
1595
|
-
lines.push(`**${pattern.name}**`);
|
|
1596
|
-
if (pattern.description) {
|
|
1597
|
-
lines.push(pattern.description);
|
|
1598
|
-
}
|
|
1599
|
-
lines.push("```tsx");
|
|
1600
|
-
lines.push(pattern.code);
|
|
1601
|
-
lines.push("```");
|
|
1602
|
-
lines.push("");
|
|
1603
|
-
}
|
|
1604
|
-
}
|
|
1605
|
-
}
|
|
1606
|
-
if (include.props && entry.props && Object.keys(entry.props).length > 0) {
|
|
1607
|
-
lines.push("### Props");
|
|
1608
|
-
lines.push("| Prop | Type | Default | Description |");
|
|
1609
|
-
lines.push("|------|------|---------|-------------|");
|
|
1610
|
-
for (const [propName, prop] of Object.entries(entry.props)) {
|
|
1611
|
-
const type = formatPropType(prop);
|
|
1612
|
-
const defaultVal = prop.default !== void 0 ? `\`${JSON.stringify(prop.default)}\`` : "-";
|
|
1613
|
-
const desc = prop.description || "-";
|
|
1614
|
-
const required = prop.required ? " (required)" : "";
|
|
1615
|
-
lines.push(`| ${propName}${required} | ${type} | ${defaultVal} | ${desc} |`);
|
|
1616
|
-
}
|
|
1617
|
-
lines.push("");
|
|
1618
|
-
}
|
|
1619
|
-
if (fragment?.accessibility) {
|
|
1620
|
-
lines.push("### Accessibility");
|
|
1621
|
-
if (fragment.accessibility.role) {
|
|
1622
|
-
lines.push(`**Role:** ${fragment.accessibility.role}`);
|
|
1623
|
-
}
|
|
1624
|
-
if (fragment.accessibility.requirements && fragment.accessibility.requirements.length > 0) {
|
|
1625
|
-
for (const req of fragment.accessibility.requirements) {
|
|
1626
|
-
lines.push(`- ${req}`);
|
|
1627
|
-
}
|
|
1628
|
-
}
|
|
1629
|
-
if (fragment.accessibility.keyboard) {
|
|
1630
|
-
lines.push("");
|
|
1631
|
-
lines.push("**Keyboard:**");
|
|
1632
|
-
for (const [key, action] of Object.entries(fragment.accessibility.keyboard)) {
|
|
1633
|
-
lines.push(`- \`${key}\`: ${action}`);
|
|
1634
|
-
}
|
|
1635
|
-
}
|
|
1636
|
-
lines.push("");
|
|
1637
|
-
}
|
|
1638
|
-
if (include.relations && fragment?.related) {
|
|
1639
|
-
lines.push("### Related");
|
|
1640
|
-
if (fragment.related.similar && fragment.related.similar.length > 0) {
|
|
1641
|
-
const resolved = fragment.related.similar.map((comp) => {
|
|
1642
|
-
const path = resolveComponentPath2(comp, registry);
|
|
1643
|
-
return path ? `${comp} (\`${path}\`)` : comp;
|
|
1644
|
-
});
|
|
1645
|
-
lines.push(`- **Similar:** ${resolved.join(", ")}`);
|
|
1646
|
-
}
|
|
1647
|
-
if (fragment.related.composedWith && fragment.related.composedWith.length > 0) {
|
|
1648
|
-
const resolved = fragment.related.composedWith.map((comp) => {
|
|
1649
|
-
const path = resolveComponentPath2(comp, registry);
|
|
1650
|
-
return path ? `${comp} (\`${path}\`)` : comp;
|
|
1651
|
-
});
|
|
1652
|
-
lines.push(`- **Composed with:** ${resolved.join(", ")}`);
|
|
1653
|
-
}
|
|
1654
|
-
if (fragment.related.usedIn && fragment.related.usedIn.length > 0) {
|
|
1655
|
-
const resolved = fragment.related.usedIn.map((comp) => {
|
|
1656
|
-
const path = resolveComponentPath2(comp, registry);
|
|
1657
|
-
return path ? `${comp} (\`${path}\`)` : comp;
|
|
1658
|
-
});
|
|
1659
|
-
lines.push(`- **Used in:** ${resolved.join(", ")}`);
|
|
1660
|
-
}
|
|
1661
|
-
lines.push("");
|
|
1662
|
-
}
|
|
1663
|
-
lines.push("---");
|
|
1664
|
-
lines.push("");
|
|
1665
|
-
}
|
|
1666
|
-
const content = lines.join("\n");
|
|
1667
|
-
return {
|
|
1668
|
-
content,
|
|
1669
|
-
tokenEstimate: estimateTokens(content),
|
|
1670
|
-
componentCount
|
|
1671
|
-
};
|
|
1672
|
-
}
|
|
1673
|
-
function generateJsonContext(registry, include, compact) {
|
|
1674
|
-
const componentNames = Object.keys(registry.components).sort();
|
|
1675
|
-
const componentCount = componentNames.length;
|
|
1676
|
-
const components = {};
|
|
1677
|
-
for (const name of componentNames) {
|
|
1678
|
-
const entry = registry.components[name];
|
|
1679
|
-
const fragment = entry.fragment;
|
|
1680
|
-
const component = {
|
|
1681
|
-
path: entry.path
|
|
1682
|
-
};
|
|
1683
|
-
if (entry.category) component.category = entry.category;
|
|
1684
|
-
if (entry.status) component.status = entry.status;
|
|
1685
|
-
if (entry.description || fragment?.description) {
|
|
1686
|
-
component.description = entry.description || fragment?.description;
|
|
1687
|
-
}
|
|
1688
|
-
if (!compact && fragment?.usage) {
|
|
1689
|
-
if (fragment.usage.when && fragment.usage.when.length > 0) {
|
|
1690
|
-
component.whenToUse = fragment.usage.when;
|
|
1691
|
-
}
|
|
1692
|
-
if (fragment.usage.doNot && fragment.usage.doNot.length > 0) {
|
|
1693
|
-
component.doNot = fragment.usage.doNot.map((item) => {
|
|
1694
|
-
if (typeof item === "string") {
|
|
1695
|
-
return { text: item };
|
|
1696
|
-
}
|
|
1697
|
-
const doNotItem = item;
|
|
1698
|
-
const result = {
|
|
1699
|
-
text: doNotItem.text
|
|
1700
|
-
};
|
|
1701
|
-
if (doNotItem.instead) {
|
|
1702
|
-
result.instead = doNotItem.instead;
|
|
1703
|
-
result.insteadPath = resolveComponentPath2(doNotItem.instead, registry);
|
|
1704
|
-
}
|
|
1705
|
-
return result;
|
|
1706
|
-
});
|
|
1707
|
-
}
|
|
1708
|
-
}
|
|
1709
|
-
if (!compact && include.props && entry.props) {
|
|
1710
|
-
component.props = {};
|
|
1711
|
-
for (const [propName, prop] of Object.entries(entry.props)) {
|
|
1712
|
-
component.props[propName] = {
|
|
1713
|
-
type: formatPropType(prop)
|
|
1714
|
-
};
|
|
1715
|
-
if (prop.required) component.props[propName].required = true;
|
|
1716
|
-
if (prop.default !== void 0) component.props[propName].default = prop.default;
|
|
1717
|
-
if (prop.description) component.props[propName].description = prop.description;
|
|
1718
|
-
}
|
|
1719
|
-
}
|
|
1720
|
-
if (!compact && include.relations && fragment?.related) {
|
|
1721
|
-
component.related = {};
|
|
1722
|
-
if (fragment.related.similar) {
|
|
1723
|
-
component.related.similar = fragment.related.similar.map((name2) => ({
|
|
1724
|
-
name: name2,
|
|
1725
|
-
path: resolveComponentPath2(name2, registry)
|
|
1726
|
-
}));
|
|
1727
|
-
}
|
|
1728
|
-
if (fragment.related.composedWith) {
|
|
1729
|
-
component.related.composedWith = fragment.related.composedWith.map((name2) => ({
|
|
1730
|
-
name: name2,
|
|
1731
|
-
path: resolveComponentPath2(name2, registry)
|
|
1732
|
-
}));
|
|
1733
|
-
}
|
|
1734
|
-
if (fragment.related.usedIn) {
|
|
1735
|
-
component.related.usedIn = fragment.related.usedIn.map((name2) => ({
|
|
1736
|
-
name: name2,
|
|
1737
|
-
path: resolveComponentPath2(name2, registry)
|
|
1738
|
-
}));
|
|
1739
|
-
}
|
|
1740
|
-
}
|
|
1741
|
-
components[name] = component;
|
|
1742
|
-
}
|
|
1743
|
-
const output = {
|
|
1744
|
-
version: "1.0",
|
|
1745
|
-
generatedAt: registry.generatedAt,
|
|
1746
|
-
componentCount,
|
|
1747
|
-
categories: registry.categories,
|
|
1748
|
-
components
|
|
1749
|
-
};
|
|
1750
|
-
const content = JSON.stringify(output, null, 2);
|
|
1751
|
-
return {
|
|
1752
|
-
content,
|
|
1753
|
-
tokenEstimate: estimateTokens(content),
|
|
1754
|
-
componentCount
|
|
1755
|
-
};
|
|
1756
|
-
}
|
|
1757
|
-
function resolveComponentPath2(componentName, registry) {
|
|
1758
|
-
return registry.components[componentName]?.path;
|
|
1759
|
-
}
|
|
1760
|
-
function formatPropType(prop) {
|
|
1761
|
-
if (prop.options && prop.options.length > 0) {
|
|
1762
|
-
return prop.options.map((o) => `"${o}"`).join(" \\| ");
|
|
1763
|
-
}
|
|
1764
|
-
return prop.type || "unknown";
|
|
1765
|
-
}
|
|
1766
|
-
function estimateTokens(text) {
|
|
1767
|
-
return Math.ceil(text.length / 4);
|
|
1768
|
-
}
|
|
1132
|
+
import { existsSync as existsSync4 } from "fs";
|
|
1133
|
+
import { join as join3, resolve as resolve3 } from "path";
|
|
1769
1134
|
|
|
1770
1135
|
// src/core/importAnalyzer.ts
|
|
1771
1136
|
import ts3 from "typescript";
|
|
1772
1137
|
import { readFileSync as readFileSync3 } from "fs";
|
|
1773
1138
|
import { dirname as dirname5, basename as basename4 } from "path";
|
|
1774
|
-
function extractComponentNameFromPath(filePath) {
|
|
1775
|
-
const fileName = basename4(filePath);
|
|
1776
|
-
if (fileName === "index.tsx" || fileName === "index.ts" || fileName === "index.jsx" || fileName === "index.js") {
|
|
1777
|
-
const parentDir = basename4(dirname5(filePath));
|
|
1778
|
-
return parentDir;
|
|
1779
|
-
}
|
|
1780
|
-
return fileName.replace(/\.(tsx?|jsx?)$/, "").replace(/\.stories$/, "").replace(/\.fragment$/, "").replace(/\.fragment$/, "");
|
|
1781
|
-
}
|
|
1782
|
-
function isComponentImportPath(importPath) {
|
|
1783
|
-
if (!importPath.startsWith(".") && !importPath.startsWith("/")) {
|
|
1784
|
-
return false;
|
|
1785
|
-
}
|
|
1786
|
-
const skipPatterns = [
|
|
1787
|
-
/\/hooks\//,
|
|
1788
|
-
/\/utils\//,
|
|
1789
|
-
/\/helpers\//,
|
|
1790
|
-
/\/lib\//,
|
|
1791
|
-
/\/types\//,
|
|
1792
|
-
/\/constants\//,
|
|
1793
|
-
/\/styles\//,
|
|
1794
|
-
/\/context\//,
|
|
1795
|
-
/\.css$/,
|
|
1796
|
-
/\.scss$/,
|
|
1797
|
-
/\.less$/,
|
|
1798
|
-
/\.json$/
|
|
1799
|
-
];
|
|
1800
|
-
for (const pattern of skipPatterns) {
|
|
1801
|
-
if (pattern.test(importPath)) {
|
|
1802
|
-
return false;
|
|
1803
|
-
}
|
|
1804
|
-
}
|
|
1805
|
-
return true;
|
|
1806
|
-
}
|
|
1807
|
-
function isPascalCase(name) {
|
|
1808
|
-
return /^[A-Z][a-zA-Z0-9]*$/.test(name);
|
|
1809
|
-
}
|
|
1810
|
-
function analyzeFileImports(filePath) {
|
|
1811
|
-
const componentName = extractComponentNameFromPath(filePath);
|
|
1812
|
-
const imports = [];
|
|
1813
|
-
let sourceText;
|
|
1814
|
-
try {
|
|
1815
|
-
sourceText = readFileSync3(filePath, "utf-8");
|
|
1816
|
-
} catch {
|
|
1817
|
-
return { filePath, componentName, imports };
|
|
1818
|
-
}
|
|
1819
|
-
const sourceFile = ts3.createSourceFile(
|
|
1820
|
-
filePath,
|
|
1821
|
-
sourceText,
|
|
1822
|
-
ts3.ScriptTarget.Latest,
|
|
1823
|
-
true,
|
|
1824
|
-
filePath.endsWith(".tsx") || filePath.endsWith(".jsx") ? ts3.ScriptKind.TSX : ts3.ScriptKind.TS
|
|
1825
|
-
);
|
|
1826
|
-
ts3.forEachChild(sourceFile, (node) => {
|
|
1827
|
-
if (ts3.isImportDeclaration(node)) {
|
|
1828
|
-
const moduleSpecifier = node.moduleSpecifier;
|
|
1829
|
-
if (!ts3.isStringLiteral(moduleSpecifier)) return;
|
|
1830
|
-
const importPath = moduleSpecifier.text;
|
|
1831
|
-
if (!isComponentImportPath(importPath)) return;
|
|
1832
|
-
const importClause = node.importClause;
|
|
1833
|
-
if (!importClause) return;
|
|
1834
|
-
if (importClause.name) {
|
|
1835
|
-
const name = importClause.name.text;
|
|
1836
|
-
if (isPascalCase(name)) {
|
|
1837
|
-
imports.push({
|
|
1838
|
-
name,
|
|
1839
|
-
path: importPath,
|
|
1840
|
-
isDefault: true,
|
|
1841
|
-
isNamespace: false
|
|
1842
|
-
});
|
|
1843
|
-
}
|
|
1844
|
-
}
|
|
1845
|
-
const namedBindings = importClause.namedBindings;
|
|
1846
|
-
if (namedBindings) {
|
|
1847
|
-
if (ts3.isNamedImports(namedBindings)) {
|
|
1848
|
-
for (const element of namedBindings.elements) {
|
|
1849
|
-
const name = element.name.text;
|
|
1850
|
-
if (isPascalCase(name)) {
|
|
1851
|
-
imports.push({
|
|
1852
|
-
name,
|
|
1853
|
-
path: importPath,
|
|
1854
|
-
isDefault: false,
|
|
1855
|
-
isNamespace: false
|
|
1856
|
-
});
|
|
1857
|
-
}
|
|
1858
|
-
}
|
|
1859
|
-
} else if (ts3.isNamespaceImport(namedBindings)) {
|
|
1860
|
-
const name = namedBindings.name.text;
|
|
1861
|
-
imports.push({
|
|
1862
|
-
name,
|
|
1863
|
-
path: importPath,
|
|
1864
|
-
isDefault: false,
|
|
1865
|
-
isNamespace: true
|
|
1866
|
-
});
|
|
1867
|
-
}
|
|
1868
|
-
}
|
|
1869
|
-
}
|
|
1870
|
-
});
|
|
1871
|
-
return { filePath, componentName, imports };
|
|
1872
|
-
}
|
|
1873
|
-
function buildImportGraph(filePaths) {
|
|
1874
|
-
const importedBy = /* @__PURE__ */ new Map();
|
|
1875
|
-
for (const filePath of filePaths) {
|
|
1876
|
-
const result = analyzeFileImports(filePath);
|
|
1877
|
-
for (const imp of result.imports) {
|
|
1878
|
-
if (imp.isNamespace) continue;
|
|
1879
|
-
const importedComponent = imp.name;
|
|
1880
|
-
const importingComponent = result.componentName;
|
|
1881
|
-
if (importedComponent === importingComponent) continue;
|
|
1882
|
-
const existing = importedBy.get(importedComponent) || [];
|
|
1883
|
-
if (!existing.includes(importingComponent)) {
|
|
1884
|
-
existing.push(importingComponent);
|
|
1885
|
-
importedBy.set(importedComponent, existing);
|
|
1886
|
-
}
|
|
1887
|
-
}
|
|
1888
|
-
}
|
|
1889
|
-
return importedBy;
|
|
1890
|
-
}
|
|
1891
|
-
function getImportedBy(componentName, importGraph) {
|
|
1892
|
-
return importGraph.get(componentName) || [];
|
|
1893
|
-
}
|
|
1894
1139
|
|
|
1895
1140
|
export {
|
|
1896
1141
|
findConfigFile,
|
|
1897
1142
|
loadConfig,
|
|
1898
1143
|
discoverBlockFiles,
|
|
1899
|
-
discoverRecipeFiles,
|
|
1900
1144
|
discoverFragmentFiles,
|
|
1901
1145
|
discoverComponentFiles,
|
|
1902
1146
|
extractComponentName,
|
|
1903
|
-
discoverComponentsFromSource,
|
|
1904
|
-
discoverComponentsFromBarrel,
|
|
1905
|
-
discoverTokenFiles,
|
|
1906
|
-
discoverInstalledFragments,
|
|
1907
1147
|
discoverAllComponents,
|
|
1908
1148
|
loadFragmentFile,
|
|
1909
|
-
loadFragmentFiles,
|
|
1910
1149
|
parseFragmentFile,
|
|
1911
|
-
|
|
1912
|
-
findStorybookDir,
|
|
1913
|
-
loadPreviewConfig,
|
|
1914
|
-
autoLoadPreviewConfig,
|
|
1915
|
-
generatePreviewModule,
|
|
1916
|
-
extractPropsFromFile,
|
|
1917
|
-
extractPropsFromSource,
|
|
1918
|
-
generateRegistry,
|
|
1919
|
-
resolveComponentPath,
|
|
1920
|
-
getComponentsByCategory,
|
|
1921
|
-
generateContextMd,
|
|
1922
|
-
analyzeFileImports,
|
|
1923
|
-
buildImportGraph,
|
|
1924
|
-
getImportedBy
|
|
1150
|
+
extractPropsFromFile
|
|
1925
1151
|
};
|
|
1926
|
-
//# sourceMappingURL=chunk-
|
|
1152
|
+
//# sourceMappingURL=chunk-XRADMHMV.js.map
|