@decocms/start 2.1.3 → 2.2.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/package.json
CHANGED
|
@@ -19,7 +19,14 @@ import path from "node:path";
|
|
|
19
19
|
* --out Output file (default: "src/server/admin/meta.gen.json")
|
|
20
20
|
* --platform Platform name (default: "cloudflare")
|
|
21
21
|
*/
|
|
22
|
-
import {
|
|
22
|
+
import {
|
|
23
|
+
type Symbol as MorphSymbol,
|
|
24
|
+
Node,
|
|
25
|
+
Project,
|
|
26
|
+
type SourceFile,
|
|
27
|
+
SyntaxKind,
|
|
28
|
+
type Type,
|
|
29
|
+
} from "ts-morph";
|
|
23
30
|
|
|
24
31
|
// ---------------------------------------------------------------------------
|
|
25
32
|
// CLI arg parsing
|
|
@@ -448,6 +455,39 @@ function resolveModulePath(
|
|
|
448
455
|
return fs.existsSync(target) ? target : null;
|
|
449
456
|
}
|
|
450
457
|
|
|
458
|
+
type SourceFileCache = Map<string, SourceFile>;
|
|
459
|
+
type ModuleResolutionCache = Map<string, string | null>;
|
|
460
|
+
type PropsSchemaCache = Map<string, any>;
|
|
461
|
+
|
|
462
|
+
function getSourceFile(
|
|
463
|
+
project: import("ts-morph").Project,
|
|
464
|
+
filePath: string,
|
|
465
|
+
cache: SourceFileCache,
|
|
466
|
+
): SourceFile {
|
|
467
|
+
const normalizedPath = path.resolve(filePath);
|
|
468
|
+
const cached = cache.get(normalizedPath);
|
|
469
|
+
if (cached) return cached;
|
|
470
|
+
|
|
471
|
+
const sourceFile =
|
|
472
|
+
project.getSourceFile(normalizedPath) ?? project.addSourceFileAtPath(normalizedPath);
|
|
473
|
+
cache.set(normalizedPath, sourceFile);
|
|
474
|
+
return sourceFile;
|
|
475
|
+
}
|
|
476
|
+
|
|
477
|
+
function resolveModulePathCached(
|
|
478
|
+
moduleSpec: string,
|
|
479
|
+
fromFile: string,
|
|
480
|
+
projectRoot: string,
|
|
481
|
+
cache: ModuleResolutionCache,
|
|
482
|
+
): string | null {
|
|
483
|
+
const key = `${fromFile}\0${moduleSpec}`;
|
|
484
|
+
if (cache.has(key)) return cache.get(key) ?? null;
|
|
485
|
+
|
|
486
|
+
const resolved = resolveModulePath(moduleSpec, fromFile, projectRoot);
|
|
487
|
+
cache.set(key, resolved);
|
|
488
|
+
return resolved;
|
|
489
|
+
}
|
|
490
|
+
|
|
451
491
|
/**
|
|
452
492
|
* Recursively follow `export { default } from "..."` chains (up to maxDepth hops)
|
|
453
493
|
* and try to extract Props from each target file.
|
|
@@ -458,6 +498,9 @@ function resolvePropsViaReExport(
|
|
|
458
498
|
filePath: string,
|
|
459
499
|
projectRoot: string,
|
|
460
500
|
maxDepth: number,
|
|
501
|
+
sourceFileCache: SourceFileCache,
|
|
502
|
+
moduleResolutionCache: ModuleResolutionCache,
|
|
503
|
+
propsSchemaCache: PropsSchemaCache,
|
|
461
504
|
): any | null {
|
|
462
505
|
if (maxDepth <= 0) return null;
|
|
463
506
|
|
|
@@ -471,21 +514,41 @@ function resolvePropsViaReExport(
|
|
|
471
514
|
});
|
|
472
515
|
if (!hasDefault) continue;
|
|
473
516
|
|
|
474
|
-
const targetPath =
|
|
517
|
+
const targetPath = resolveModulePathCached(
|
|
518
|
+
moduleSpec,
|
|
519
|
+
filePath,
|
|
520
|
+
projectRoot,
|
|
521
|
+
moduleResolutionCache,
|
|
522
|
+
);
|
|
475
523
|
if (!targetPath) continue;
|
|
476
524
|
|
|
525
|
+
const cachedProps = propsSchemaCache.get(targetPath);
|
|
526
|
+
if (cachedProps) return cachedProps;
|
|
527
|
+
|
|
477
528
|
try {
|
|
478
|
-
const targetFile = project
|
|
529
|
+
const targetFile = getSourceFile(project, targetPath, sourceFileCache);
|
|
479
530
|
|
|
480
531
|
const targetProps = targetFile.getInterface("Props");
|
|
481
|
-
if (targetProps)
|
|
532
|
+
if (targetProps) {
|
|
533
|
+
const schema = typeToJsonSchema(targetProps.getType());
|
|
534
|
+
propsSchemaCache.set(targetPath, schema);
|
|
535
|
+
return schema;
|
|
536
|
+
}
|
|
482
537
|
|
|
483
538
|
const targetAlias = targetFile.getTypeAlias("Props");
|
|
484
|
-
if (targetAlias)
|
|
539
|
+
if (targetAlias) {
|
|
540
|
+
const schema = typeToJsonSchema(targetAlias.getType());
|
|
541
|
+
propsSchemaCache.set(targetPath, schema);
|
|
542
|
+
return schema;
|
|
543
|
+
}
|
|
485
544
|
|
|
486
545
|
// Type-checker approach: extract from default export call signature
|
|
487
546
|
const propsType = extractDefaultExportPropsType(targetFile);
|
|
488
|
-
if (propsType)
|
|
547
|
+
if (propsType) {
|
|
548
|
+
const schema = typeToJsonSchema(propsType);
|
|
549
|
+
propsSchemaCache.set(targetPath, schema);
|
|
550
|
+
return schema;
|
|
551
|
+
}
|
|
489
552
|
|
|
490
553
|
// Recurse: target might also re-export from another file
|
|
491
554
|
const deeper = resolvePropsViaReExport(
|
|
@@ -494,8 +557,14 @@ function resolvePropsViaReExport(
|
|
|
494
557
|
targetPath,
|
|
495
558
|
projectRoot,
|
|
496
559
|
maxDepth - 1,
|
|
560
|
+
sourceFileCache,
|
|
561
|
+
moduleResolutionCache,
|
|
562
|
+
propsSchemaCache,
|
|
497
563
|
);
|
|
498
|
-
if (deeper)
|
|
564
|
+
if (deeper) {
|
|
565
|
+
propsSchemaCache.set(targetPath, deeper);
|
|
566
|
+
return deeper;
|
|
567
|
+
}
|
|
499
568
|
} catch {
|
|
500
569
|
// Target file couldn't be parsed
|
|
501
570
|
}
|
|
@@ -530,6 +599,9 @@ function generateMeta(): MetaResponse {
|
|
|
530
599
|
const definitions: Record<string, any> = {};
|
|
531
600
|
const sectionBlocks: Record<string, any> = {};
|
|
532
601
|
const sectionRootAnyOf: any[] = [];
|
|
602
|
+
const sourceFileCache: SourceFileCache = new Map();
|
|
603
|
+
const moduleResolutionCache: ModuleResolutionCache = new Map();
|
|
604
|
+
const propsSchemaCache: PropsSchemaCache = new Map();
|
|
533
605
|
|
|
534
606
|
// Resolvable: the admin's deRefUntil expects the LITERAL key "Resolvable",
|
|
535
607
|
// not a base64-encoded version. We store both for compatibility.
|
|
@@ -553,13 +625,16 @@ function generateMeta(): MetaResponse {
|
|
|
553
625
|
|
|
554
626
|
const sectionFiles = findTsxFiles(sectionsDir);
|
|
555
627
|
console.log(`Found ${sectionFiles.length} section files`);
|
|
628
|
+
for (const filePath of sectionFiles) {
|
|
629
|
+
getSourceFile(project, filePath, sourceFileCache);
|
|
630
|
+
}
|
|
556
631
|
|
|
557
632
|
for (const filePath of sectionFiles) {
|
|
558
633
|
const relativePath = path.relative(srcDir, filePath);
|
|
559
634
|
const blockKey = `${SITE_NAMESPACE}/${relativePath}`;
|
|
560
635
|
|
|
561
636
|
try {
|
|
562
|
-
const sourceFile = project
|
|
637
|
+
const sourceFile = getSourceFile(project, filePath, sourceFileCache);
|
|
563
638
|
|
|
564
639
|
let propsSchema: any = null;
|
|
565
640
|
|
|
@@ -573,7 +648,16 @@ function generateMeta(): MetaResponse {
|
|
|
573
648
|
// Strategy 2: Follow re-exports recursively (up to 3 hops)
|
|
574
649
|
// Handles: section → island → component chains
|
|
575
650
|
if (!propsSchema) {
|
|
576
|
-
propsSchema = resolvePropsViaReExport(
|
|
651
|
+
propsSchema = resolvePropsViaReExport(
|
|
652
|
+
project,
|
|
653
|
+
sourceFile,
|
|
654
|
+
filePath,
|
|
655
|
+
root,
|
|
656
|
+
3,
|
|
657
|
+
sourceFileCache,
|
|
658
|
+
moduleResolutionCache,
|
|
659
|
+
propsSchemaCache,
|
|
660
|
+
);
|
|
577
661
|
}
|
|
578
662
|
|
|
579
663
|
// Strategy 4: Default export call signature in the section file via type checker
|
|
@@ -46,10 +46,36 @@ const IMPORT_RULES: Array<[RegExp, string | null]> = [
|
|
|
46
46
|
[/^"apps\/vtex\/hooks\/useWishlist(?:\.ts)?"$/, `"~/hooks/useWishlist"`],
|
|
47
47
|
[/^"apps\/vtex\/hooks\/([^"]+?)(?:\.ts)?"$/, `"@decocms/apps/vtex/hooks/$1"`],
|
|
48
48
|
// Specific VTEX utils that moved to different paths in @decocms/apps
|
|
49
|
-
|
|
49
|
+
// fetchVTEX (generic fetchSafe + QS sanitization) lives at vtex/utils/fetch in apps-start.
|
|
50
|
+
[/^"apps\/vtex\/utils\/fetchVTEX(?:\.ts)?"$/, `"@decocms/apps/vtex/utils/fetch"`],
|
|
50
51
|
[/^"apps\/vtex\/utils\/client(?:\.ts)?"$/, `"@decocms/apps/vtex/client"`],
|
|
51
52
|
[/^"apps\/vtex\/utils\/([^"]+?)(?:\.ts)?"$/, `"@decocms/apps/vtex/utils/$1"`],
|
|
52
53
|
[/^"apps\/vtex\/actions\/([^"]+?)(?:\.ts)?"$/, `"@decocms/apps/vtex/actions/$1"`],
|
|
54
|
+
// Tier B loader path rewrites (apps-start has no `intelligentSearch/`, `legacy/<file>`, or `paths/` subdirs).
|
|
55
|
+
// Intelligent Search loaders moved to inline-loaders/.
|
|
56
|
+
[
|
|
57
|
+
/^"apps\/vtex\/loaders\/intelligentSearch\/productList(?:\.ts)?"$/,
|
|
58
|
+
`"@decocms/apps/vtex/inline-loaders/productList"`,
|
|
59
|
+
],
|
|
60
|
+
[
|
|
61
|
+
/^"apps\/vtex\/loaders\/intelligentSearch\/productListingPage(?:\.ts)?"$/,
|
|
62
|
+
`"@decocms/apps/vtex/inline-loaders/productListingPage"`,
|
|
63
|
+
],
|
|
64
|
+
[
|
|
65
|
+
/^"apps\/vtex\/loaders\/intelligentSearch\/productDetailsPage(?:\.ts)?"$/,
|
|
66
|
+
`"@decocms/apps/vtex/inline-loaders/productDetailsPage"`,
|
|
67
|
+
],
|
|
68
|
+
[
|
|
69
|
+
/^"apps\/vtex\/loaders\/intelligentSearch\/suggestions(?:\.ts)?"$/,
|
|
70
|
+
`"@decocms/apps/vtex/inline-loaders/suggestions"`,
|
|
71
|
+
],
|
|
72
|
+
// Legacy product loaders are consolidated into a single file (named exports).
|
|
73
|
+
[
|
|
74
|
+
/^"apps\/vtex\/loaders\/legacy\/(?:productList|productListingPage|productDetailsPage|search|category)(?:\.ts)?"$/,
|
|
75
|
+
`"@decocms/apps/vtex/loaders/legacy"`,
|
|
76
|
+
],
|
|
77
|
+
// Path-default loaders (sitemap seeds) don't exist in TanStack Start — paths resolve at request time.
|
|
78
|
+
[/^"apps\/vtex\/loaders\/paths\/(?:[^"]+)(?:\.ts)?"$/, null],
|
|
53
79
|
[/^"apps\/vtex\/loaders\/([^"]+?)(?:\.ts)?"$/, `"@decocms/apps/vtex/loaders/$1"`],
|
|
54
80
|
[/^"apps\/vtex\/types(?:\.ts)?"$/, `"@decocms/apps/vtex/types"`],
|
|
55
81
|
[/^"apps\/vtex\/mod(?:\.ts)?"$/, `"~/types/vtex-app"`],
|