@decocms/start 1.2.5 → 1.2.7
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 +1 -1
- package/scripts/deco-migrate-cli.ts +444 -0
- package/scripts/migrate/analyzers/island-classifier.ts +73 -0
- package/scripts/migrate/analyzers/loader-inventory.ts +63 -0
- package/scripts/migrate/analyzers/section-metadata.ts +91 -0
- package/scripts/migrate/analyzers/theme-extractor.ts +122 -0
- package/scripts/migrate/phase-analyze.ts +147 -17
- package/scripts/migrate/phase-cleanup.ts +124 -2
- package/scripts/migrate/phase-report.ts +44 -16
- package/scripts/migrate/phase-scaffold.ts +38 -132
- package/scripts/migrate/phase-transform.ts +28 -3
- package/scripts/migrate/phase-verify.ts +127 -5
- package/scripts/migrate/templates/app-css.ts +204 -0
- package/scripts/migrate/templates/cache-config.ts +26 -0
- package/scripts/migrate/templates/commerce-loaders.ts +124 -0
- package/scripts/migrate/templates/hooks.ts +358 -0
- package/scripts/migrate/templates/package-json.ts +29 -6
- package/scripts/migrate/templates/routes.ts +41 -136
- package/scripts/migrate/templates/sdk-gen.ts +59 -0
- package/scripts/migrate/templates/section-loaders.ts +108 -0
- package/scripts/migrate/templates/server-entry.ts +174 -67
- package/scripts/migrate/templates/setup.ts +64 -55
- package/scripts/migrate/templates/types-gen.ts +119 -0
- package/scripts/migrate/templates/ui-components.ts +113 -0
- package/scripts/migrate/templates/vite-config.ts +18 -1
- package/scripts/migrate/templates/wrangler.ts +4 -1
- package/scripts/migrate/transforms/dead-code.ts +23 -2
- package/scripts/migrate/transforms/imports.ts +40 -10
- package/scripts/migrate/transforms/jsx.ts +9 -0
- package/scripts/migrate/transforms/section-conventions.ts +83 -0
- package/scripts/migrate/types.ts +74 -0
- package/src/cms/resolve.ts +10 -0
- package/src/routes/cmsRoute.ts +13 -0
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
import type { MigrationContext } from "../types.ts";
|
|
2
2
|
|
|
3
3
|
export function generateWrangler(ctx: MigrationContext): string {
|
|
4
|
-
// Sanitize site name for worker name (lowercase, hyphens only)
|
|
5
4
|
const workerName = ctx.siteName
|
|
6
5
|
.toLowerCase()
|
|
7
6
|
.replace(/[^a-z0-9-]/g, "-")
|
|
@@ -14,6 +13,10 @@ export function generateWrangler(ctx: MigrationContext): string {
|
|
|
14
13
|
"main": "./src/worker-entry.ts",
|
|
15
14
|
"workers_dev": true,
|
|
16
15
|
"preview_urls": true,
|
|
16
|
+
// Uncomment for redirect/AB testing support via KV:
|
|
17
|
+
// "kv_namespaces": [
|
|
18
|
+
// { "binding": "SITES_KV", "id": "YOUR_KV_NAMESPACE_ID" }
|
|
19
|
+
// ],
|
|
17
20
|
"observability": {
|
|
18
21
|
"logs": {
|
|
19
22
|
"enabled": true,
|
|
@@ -62,12 +62,22 @@ export function transformDeadCode(content: string): TransformResult {
|
|
|
62
62
|
let changed = false;
|
|
63
63
|
let result = content;
|
|
64
64
|
|
|
65
|
-
// Remove old cache export: export const cache = "stale-while-revalidate"
|
|
66
|
-
if (/^export\s+const\s+cache\s
|
|
65
|
+
// Remove old cache export: export const cache = "stale-while-revalidate" or { maxAge: ... }
|
|
66
|
+
if (/^export\s+const\s+cache\s*=/m.test(result)) {
|
|
67
|
+
// String form: export const cache = "stale-while-revalidate";
|
|
67
68
|
result = result.replace(
|
|
68
69
|
/^export\s+const\s+cache\s*=\s*["'][^"']*["'];?\s*\n?/gm,
|
|
69
70
|
"",
|
|
70
71
|
);
|
|
72
|
+
// Object form: export const cache = { maxAge: 60 * 10 };
|
|
73
|
+
result = result.replace(
|
|
74
|
+
/^export\s+const\s+cache\s*=\s*\{[^}]*\};?\s*\n?/gm,
|
|
75
|
+
"",
|
|
76
|
+
);
|
|
77
|
+
// Multiline object form (use brace-counting)
|
|
78
|
+
if (/^export\s+const\s+cache\s*=/m.test(result)) {
|
|
79
|
+
result = removeExportConstBlock(result, "cache");
|
|
80
|
+
}
|
|
71
81
|
changed = true;
|
|
72
82
|
notes.push("Removed dead `export const cache` (old caching system)");
|
|
73
83
|
}
|
|
@@ -97,6 +107,17 @@ export function transformDeadCode(content: string): TransformResult {
|
|
|
97
107
|
notes.push("MANUAL: crypto.subtle.digestSync is Deno-only — replaced with crypto.subtle.digest (needs await)");
|
|
98
108
|
}
|
|
99
109
|
|
|
110
|
+
// Replace logger usage from @deco/deco/o11y with console
|
|
111
|
+
if (result.includes("logger.")) {
|
|
112
|
+
result = result.replace(/\blogger\.error\b/g, "console.error");
|
|
113
|
+
result = result.replace(/\blogger\.warn\b/g, "console.warn");
|
|
114
|
+
result = result.replace(/\blogger\.info\b/g, "console.info");
|
|
115
|
+
result = result.replace(/\blogger\.debug\b/g, "console.debug");
|
|
116
|
+
result = result.replace(/\blogger\.log\b/g, "console.log");
|
|
117
|
+
changed = true;
|
|
118
|
+
notes.push("Replaced logger.* with console.* (logger from @deco/deco/o11y removed)");
|
|
119
|
+
}
|
|
120
|
+
|
|
100
121
|
// invoke.* calls are server RPC via runtime.ts proxy → keep as-is
|
|
101
122
|
// The runtime.ts scaffolded file creates a proxy that routes to /deco/invoke/*
|
|
102
123
|
// where the CMS config (API keys, tokens) is available server-side.
|
|
@@ -9,6 +9,7 @@ const IMPORT_RULES: Array<[RegExp, string | null]> = [
|
|
|
9
9
|
// Fresh — remove entirely (handled by fresh-apis transform)
|
|
10
10
|
[/^"\$fresh\/runtime\.ts"/, null],
|
|
11
11
|
[/^"\$fresh\/server\.ts"/, null],
|
|
12
|
+
[/^"\$fresh\//, null], // catch-all for any $fresh/* import
|
|
12
13
|
|
|
13
14
|
// Preact → React
|
|
14
15
|
[/^"preact\/hooks"$/, `"react"`],
|
|
@@ -18,19 +19,25 @@ const IMPORT_RULES: Array<[RegExp, string | null]> = [
|
|
|
18
19
|
[/^"@preact\/signals-core"$/, `"~/sdk/signal"`],
|
|
19
20
|
[/^"@preact\/signals"$/, `"~/sdk/signal"`],
|
|
20
21
|
|
|
21
|
-
// Deco framework
|
|
22
|
+
// Deco framework — hooks need splitting (useDevice, useScript, useSection)
|
|
22
23
|
[/^"@deco\/deco\/hooks"$/, `"@decocms/start/sdk/useScript"`],
|
|
23
|
-
[/^"@deco\/deco\/blocks"$/, `"
|
|
24
|
+
[/^"@deco\/deco\/blocks"$/, `"~/types/deco"`],
|
|
25
|
+
[/^"@deco\/deco\/o11y"$/, null], // logger — use console.log/warn/error instead
|
|
24
26
|
[/^"@deco\/deco\/web"$/, null], // runtime.ts is rewritten
|
|
25
|
-
[/^"@deco\/deco"$/,
|
|
27
|
+
[/^"@deco\/deco\/utils\/invoke\.types\.ts"$/, null],
|
|
28
|
+
[/^"@deco\/deco\/utils\/([^"]+)"$/, null],
|
|
29
|
+
[/^"@deco\/deco"$/, `"~/types/deco"`],
|
|
26
30
|
|
|
27
31
|
// Apps — widgets & components
|
|
28
|
-
[/^"apps\/admin\/widgets\.ts"$/, `"
|
|
29
|
-
[/^"apps\/website\/components\/Image\.tsx"$/, `"
|
|
30
|
-
[/^"apps\/website\/components\/Picture\.tsx"$/, `"
|
|
31
|
-
[/^"apps\/website\/components\/Video\.tsx"$/, `"
|
|
32
|
+
[/^"apps\/admin\/widgets\.ts"$/, `"~/types/widgets"`],
|
|
33
|
+
[/^"apps\/website\/components\/Image\.tsx"$/, `"~/components/ui/Image"`],
|
|
34
|
+
[/^"apps\/website\/components\/Picture\.tsx"$/, `"~/components/ui/Picture"`],
|
|
35
|
+
[/^"apps\/website\/components\/Video\.tsx"$/, `"~/components/ui/Video"`],
|
|
32
36
|
[/^"apps\/website\/components\/Theme\.tsx"$/, `"~/components/ui/Theme"`],
|
|
37
|
+
[/^"apps\/website\/components\/([^"]+?)(?:\.tsx?)?"$/, `"~/components/ui/$1"`],
|
|
33
38
|
[/^"apps\/commerce\/types\.ts"$/, `"@decocms/apps/commerce/types"`],
|
|
39
|
+
[/^"apps\/commerce\/mod\.ts"$/, `"~/types/commerce-app"`],
|
|
40
|
+
[/^"apps\/commerce\/types"$/, `"@decocms/apps/commerce/types"`],
|
|
34
41
|
|
|
35
42
|
// Apps — VTEX (hooks, utils, actions, loaders, types)
|
|
36
43
|
[/^"apps\/vtex\/hooks\/([^"]+?)(?:\.ts)?"$/, `"@decocms/apps/vtex/hooks/$1"`],
|
|
@@ -65,15 +72,29 @@ const IMPORT_RULES: Array<[RegExp, string | null]> = [
|
|
|
65
72
|
[/^"site\/sdk\/useVariantPossiblities(?:\.tsx?)?.*"$/, `"@decocms/apps/commerce/sdk/useVariantPossibilities"`],
|
|
66
73
|
[/^"site\/sdk\/usePlatform(?:\.tsx?)?.*"$/, null],
|
|
67
74
|
|
|
75
|
+
// $store/account.json → ~/account.json (JSON import with assertion)
|
|
76
|
+
[/^"\$store\/account\.json"$/, `"~/account.json"`],
|
|
77
|
+
[/^"site\/account\.json"$/, `"~/account.json"`],
|
|
78
|
+
|
|
68
79
|
// $store/ → ~/ (common Deno import map alias for project root)
|
|
69
80
|
[/^"\$store\/sdk\/clx(?:\.tsx?)?.*"$/, `"~/sdk/clx"`],
|
|
70
81
|
[/^"\$store\/sdk\/useId(?:\.tsx?)?.*"$/, `"react"`],
|
|
71
82
|
[/^"\$store\/sdk\/useOffer(?:\.tsx?)?.*"$/, `"@decocms/apps/commerce/sdk/useOffer"`],
|
|
83
|
+
[/^"\$store\/sdk\/format(?:\.tsx?)?.*"$/, `"@decocms/apps/commerce/sdk/formatPrice"`],
|
|
72
84
|
[/^"\$store\/sdk\/useVariantPossiblities(?:\.tsx?)?.*"$/, `"@decocms/apps/commerce/sdk/useVariantPossibilities"`],
|
|
73
85
|
[/^"\$store\/sdk\/usePlatform(?:\.tsx?)?.*"$/, null],
|
|
74
86
|
[/^"\$store\/(.+)"$/, `"~/$1"`],
|
|
75
87
|
|
|
88
|
+
// $home/ → ~/ (another common alias)
|
|
89
|
+
[/^"\$home\/(.+)"$/, `"~/$1"`],
|
|
90
|
+
|
|
76
91
|
// site/ → ~/
|
|
92
|
+
[/^"site\/sdk\/clx(?:\.tsx?)?.*"$/, `"~/sdk/clx"`],
|
|
93
|
+
[/^"site\/sdk\/useId(?:\.tsx?)?.*"$/, `"react"`],
|
|
94
|
+
[/^"site\/sdk\/useOffer(?:\.tsx?)?.*"$/, `"@decocms/apps/commerce/sdk/useOffer"`],
|
|
95
|
+
[/^"site\/sdk\/format(?:\.tsx?)?.*"$/, `"@decocms/apps/commerce/sdk/formatPrice"`],
|
|
96
|
+
[/^"site\/sdk\/useVariantPossiblities(?:\.tsx?)?.*"$/, `"@decocms/apps/commerce/sdk/useVariantPossibilities"`],
|
|
97
|
+
[/^"site\/sdk\/usePlatform(?:\.tsx?)?.*"$/, null],
|
|
77
98
|
[/^"site\/(.+)"$/, `"~/$1"`],
|
|
78
99
|
];
|
|
79
100
|
|
|
@@ -89,6 +110,8 @@ const RELATIVE_SDK_REWRITES: Array<[RegExp, string]> = [
|
|
|
89
110
|
[/(?:\.\.\/)*sdk\/useId(?:\.tsx?)?$/, "react"],
|
|
90
111
|
// sdk/useOffer → @decocms/apps/commerce/sdk/useOffer
|
|
91
112
|
[/(?:\.\.\/)*sdk\/useOffer(?:\.tsx?)?$/, "@decocms/apps/commerce/sdk/useOffer"],
|
|
113
|
+
// sdk/format → @decocms/apps/commerce/sdk/formatPrice
|
|
114
|
+
[/(?:\.\.\/)*sdk\/format(?:\.tsx?)?$/, "@decocms/apps/commerce/sdk/formatPrice"],
|
|
92
115
|
// sdk/useVariantPossiblities → @decocms/apps/commerce/sdk/useVariantPossibilities
|
|
93
116
|
[/(?:\.\.\/)*sdk\/useVariantPossiblities(?:\.tsx?)?$/, "@decocms/apps/commerce/sdk/useVariantPossibilities"],
|
|
94
117
|
// sdk/usePlatform → remove entirely
|
|
@@ -113,12 +136,19 @@ export function transformImports(content: string): TransformResult {
|
|
|
113
136
|
const notes: string[] = [];
|
|
114
137
|
let changed = false;
|
|
115
138
|
|
|
139
|
+
// Strip BOM that prevents ^ matching on the first line
|
|
140
|
+
if (content.charCodeAt(0) === 0xfeff) {
|
|
141
|
+
content = content.slice(1);
|
|
142
|
+
changed = true;
|
|
143
|
+
}
|
|
144
|
+
|
|
116
145
|
// Match import/export lines with their specifiers
|
|
146
|
+
// The suffix group also captures import assertions (with { type: "json" }) and assert syntax
|
|
117
147
|
const importLineRegex =
|
|
118
|
-
/^(import\s+(?:type\s+)?(?:\{[^}]*\}|[\w*]+(?:\s*,\s*\{[^}]*\})?)\s+from\s+)("[^"]+"|'[^']+')(
|
|
148
|
+
/^(import\s+(?:type\s+)?(?:\{[^}]*\}|[\w*]+(?:\s*,\s*\{[^}]*\})?)\s+from\s+)("[^"]+"|'[^']+')((?:\s+(?:with|assert)\s+\{[^}]*\})?;?\s*)$/gm;
|
|
119
149
|
const reExportLineRegex =
|
|
120
|
-
/^(export\s+(?:type\s+)?\{[^}]*\}\s+from\s+)("[^"]+"|'[^']+')(
|
|
121
|
-
const sideEffectImportRegex = /^(import\s+)("[^"]+"|'[^']+')(
|
|
150
|
+
/^(export\s+(?:type\s+)?\{[^}]*\}\s+from\s+)("[^"]+"|'[^']+')((?:\s+(?:with|assert)\s+\{[^}]*\})?;?\s*)$/gm;
|
|
151
|
+
const sideEffectImportRegex = /^(import\s+)("[^"]+"|'[^']+')((?:\s+(?:with|assert)\s+\{[^}]*\})?;?\s*)$/gm;
|
|
122
152
|
|
|
123
153
|
/**
|
|
124
154
|
* Post-process: split @deco/deco/hooks imports.
|
|
@@ -220,6 +220,15 @@ export function transformJsx(content: string): TransformResult {
|
|
|
220
220
|
notes.push("Prefixed setTimeout/setInterval with window. for correct typing");
|
|
221
221
|
}
|
|
222
222
|
|
|
223
|
+
// Strip data-fresh-disable-lock (Fresh-specific, not needed in React/TanStack)
|
|
224
|
+
if (result.includes("data-fresh-disable-lock")) {
|
|
225
|
+
result = result.replace(/\s*data-fresh-disable-lock=\{[^}]*\}/g, "");
|
|
226
|
+
result = result.replace(/\s*data-fresh-disable-lock="[^"]*"/g, "");
|
|
227
|
+
result = result.replace(/\s*data-fresh-disable-lock/g, "");
|
|
228
|
+
changed = true;
|
|
229
|
+
notes.push("Removed data-fresh-disable-lock attribute (Fresh-specific)");
|
|
230
|
+
}
|
|
231
|
+
|
|
223
232
|
// Ensure React import exists if we introduced React.* references
|
|
224
233
|
if (
|
|
225
234
|
(result.includes("React.") || result.includes("React,")) &&
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
import type { TransformResult, SectionMeta } from "../types.ts";
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Adds section convention exports (sync, eager, layout, cache)
|
|
5
|
+
* to section files based on metadata extracted during analysis.
|
|
6
|
+
*
|
|
7
|
+
* These exports are read by generate-sections.ts in @decocms/start
|
|
8
|
+
* to build the sections.gen.ts registry.
|
|
9
|
+
*/
|
|
10
|
+
export function transformSectionConventions(
|
|
11
|
+
content: string,
|
|
12
|
+
sectionMeta: SectionMeta | undefined,
|
|
13
|
+
): TransformResult {
|
|
14
|
+
if (!sectionMeta) {
|
|
15
|
+
return { content, changed: false, notes: [] };
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
const notes: string[] = [];
|
|
19
|
+
let result = content;
|
|
20
|
+
let changed = false;
|
|
21
|
+
|
|
22
|
+
// Header, footer, theme → eager + sync + layout
|
|
23
|
+
if (sectionMeta.isHeader || sectionMeta.isFooter || sectionMeta.isTheme) {
|
|
24
|
+
if (!result.includes("export const eager")) {
|
|
25
|
+
result += "\nexport const eager = true;\n";
|
|
26
|
+
notes.push("Added: export const eager = true");
|
|
27
|
+
changed = true;
|
|
28
|
+
}
|
|
29
|
+
if (!result.includes("export const sync")) {
|
|
30
|
+
result += "export const sync = true;\n";
|
|
31
|
+
notes.push("Added: export const sync = true");
|
|
32
|
+
changed = true;
|
|
33
|
+
}
|
|
34
|
+
if (!result.includes("export const layout")) {
|
|
35
|
+
result += "export const layout = true;\n";
|
|
36
|
+
notes.push("Added: export const layout = true");
|
|
37
|
+
changed = true;
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
// Listing sections → cache = "listing"
|
|
42
|
+
if (sectionMeta.isListing && !result.includes("export const cache")) {
|
|
43
|
+
result += '\nexport const cache = "listing";\n';
|
|
44
|
+
notes.push('Added: export const cache = "listing"');
|
|
45
|
+
changed = true;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
// Sections with loaders that use device → add sync (needs SSR device detection)
|
|
49
|
+
if (sectionMeta.hasLoader && sectionMeta.loaderUsesDevice && !result.includes("export const sync")) {
|
|
50
|
+
result += "\nexport const sync = true;\n";
|
|
51
|
+
notes.push("Added: export const sync = true (loader uses device)");
|
|
52
|
+
changed = true;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
// Generate a basic LoadingFallback if the section doesn't have one
|
|
56
|
+
// and it's a listing section (visible skeleton improvement)
|
|
57
|
+
if (sectionMeta.isListing && !sectionMeta.hasLoadingFallback) {
|
|
58
|
+
result += `
|
|
59
|
+
export function LoadingFallback() {
|
|
60
|
+
return (
|
|
61
|
+
<div className="w-full py-8">
|
|
62
|
+
<div className="container mx-auto px-4">
|
|
63
|
+
<div className="h-6 w-48 bg-base-200 animate-pulse rounded mb-4" />
|
|
64
|
+
<div className="grid grid-cols-2 md:grid-cols-4 gap-4">
|
|
65
|
+
{Array.from({ length: 4 }).map((_, i) => (
|
|
66
|
+
<div key={i} className="flex flex-col gap-2">
|
|
67
|
+
<div className="aspect-square bg-base-200 animate-pulse rounded" />
|
|
68
|
+
<div className="h-4 bg-base-200 animate-pulse rounded w-3/4" />
|
|
69
|
+
<div className="h-4 bg-base-200 animate-pulse rounded w-1/2" />
|
|
70
|
+
</div>
|
|
71
|
+
))}
|
|
72
|
+
</div>
|
|
73
|
+
</div>
|
|
74
|
+
</div>
|
|
75
|
+
);
|
|
76
|
+
}
|
|
77
|
+
`;
|
|
78
|
+
notes.push("Added: LoadingFallback skeleton for listing section");
|
|
79
|
+
changed = true;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
return { content: result, changed, notes };
|
|
83
|
+
}
|
package/scripts/migrate/types.ts
CHANGED
|
@@ -63,6 +63,70 @@ export type DetectedPattern =
|
|
|
63
63
|
| "define-app"
|
|
64
64
|
| "invoke-proxy";
|
|
65
65
|
|
|
66
|
+
/** Metadata extracted from a section file during analysis */
|
|
67
|
+
export interface SectionMeta {
|
|
68
|
+
/** Relative path from source root (e.g. "sections/Header/Header.tsx") */
|
|
69
|
+
path: string;
|
|
70
|
+
/** Has export const loader or export function loader */
|
|
71
|
+
hasLoader: boolean;
|
|
72
|
+
/** Loader is async */
|
|
73
|
+
loaderIsAsync: boolean;
|
|
74
|
+
/** Has export function LoadingFallback */
|
|
75
|
+
hasLoadingFallback: boolean;
|
|
76
|
+
/** Is a header section (by filename) */
|
|
77
|
+
isHeader: boolean;
|
|
78
|
+
/** Is a footer section (by filename) */
|
|
79
|
+
isFooter: boolean;
|
|
80
|
+
/** Is a theme section (by filename) */
|
|
81
|
+
isTheme: boolean;
|
|
82
|
+
/** Is a shelf/carousel/listing section (by filename or content) */
|
|
83
|
+
isListing: boolean;
|
|
84
|
+
/** Has JSDoc @title */
|
|
85
|
+
hasTitle: boolean;
|
|
86
|
+
/** Has JSDoc @description */
|
|
87
|
+
hasDescription: boolean;
|
|
88
|
+
/** Loader uses ctx.device or similar device detection */
|
|
89
|
+
loaderUsesDevice: boolean;
|
|
90
|
+
/** Loader uses request URL / search params */
|
|
91
|
+
loaderUsesUrl: boolean;
|
|
92
|
+
/** Loader is an Account section (sections/Account/*) */
|
|
93
|
+
isAccountSection: boolean;
|
|
94
|
+
/** Loader only sets ctx.response.status (no real prop enrichment) */
|
|
95
|
+
isStatusOnly: boolean;
|
|
96
|
+
/** Loader sets isMobile (boolean) rather than device (string) */
|
|
97
|
+
usesMobileBoolean: boolean;
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
/** Classification of an island file */
|
|
101
|
+
export interface IslandClassification {
|
|
102
|
+
/** Relative path from source root */
|
|
103
|
+
path: string;
|
|
104
|
+
/** "wrapper" = thin re-export/bridge, "standalone" = has real logic */
|
|
105
|
+
type: "wrapper" | "standalone";
|
|
106
|
+
/** If wrapper, the target component path */
|
|
107
|
+
wrapsComponent?: string;
|
|
108
|
+
/** If standalone, the suggested target path under src/ */
|
|
109
|
+
suggestedTarget: string;
|
|
110
|
+
/** Line count (used as heuristic) */
|
|
111
|
+
lineCount: number;
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
/** Information about a loader file */
|
|
115
|
+
export interface LoaderInfo {
|
|
116
|
+
/** Relative path from source root */
|
|
117
|
+
path: string;
|
|
118
|
+
/** Has export const cache (SWR) */
|
|
119
|
+
hasCache: boolean;
|
|
120
|
+
/** Has export const cacheKey */
|
|
121
|
+
hasCacheKey: boolean;
|
|
122
|
+
/** Maps to a known @decocms/apps equivalent */
|
|
123
|
+
appsEquivalent: string | null;
|
|
124
|
+
/** Is a custom loader that needs dynamic import in commerce-loaders */
|
|
125
|
+
isCustom: boolean;
|
|
126
|
+
/** Detected platform relevance (vtex, shopify, etc.) */
|
|
127
|
+
platformRelevance: Platform | null;
|
|
128
|
+
}
|
|
129
|
+
|
|
66
130
|
export interface MigrationContext {
|
|
67
131
|
sourceDir: string;
|
|
68
132
|
siteName: string;
|
|
@@ -83,6 +147,13 @@ export interface MigrationContext {
|
|
|
83
147
|
/** All categorized source files */
|
|
84
148
|
files: FileRecord[];
|
|
85
149
|
|
|
150
|
+
/** Section metadata extracted during analysis */
|
|
151
|
+
sectionMetas: SectionMeta[];
|
|
152
|
+
/** Island classifications */
|
|
153
|
+
islandClassifications: IslandClassification[];
|
|
154
|
+
/** Loader inventory */
|
|
155
|
+
loaderInventory: LoaderInfo[];
|
|
156
|
+
|
|
86
157
|
/** Files created by scaffold phase */
|
|
87
158
|
scaffoldedFiles: string[];
|
|
88
159
|
/** Files transformed */
|
|
@@ -126,6 +197,9 @@ export function createContext(
|
|
|
126
197
|
themeColors: {},
|
|
127
198
|
fontFamily: null,
|
|
128
199
|
files: [],
|
|
200
|
+
sectionMetas: [],
|
|
201
|
+
islandClassifications: [],
|
|
202
|
+
loaderInventory: [],
|
|
129
203
|
scaffoldedFiles: [],
|
|
130
204
|
transformedFiles: [],
|
|
131
205
|
deletedFiles: [],
|
package/src/cms/resolve.ts
CHANGED
|
@@ -627,7 +627,17 @@ async function resolveRawSection(
|
|
|
627
627
|
section: unknown,
|
|
628
628
|
rctx: ResolveContext,
|
|
629
629
|
): Promise<ResolvedSection[]> {
|
|
630
|
+
const sectionRt = (section as any)?.__resolveType;
|
|
631
|
+
if (String(sectionRt).includes("eader")) {
|
|
632
|
+
console.log(`[RAW-SECTION-ENTER] sectionRt="${sectionRt}"`);
|
|
633
|
+
}
|
|
634
|
+
|
|
630
635
|
const resolved = await internalResolve(section, rctx);
|
|
636
|
+
|
|
637
|
+
if (String(sectionRt).includes("eader")) {
|
|
638
|
+
console.log(`[RAW-SECTION-RESOLVED] resolved type=${typeof resolved} isObj=${resolved && typeof resolved === 'object'} keys=${resolved && typeof resolved === 'object' ? Object.keys(resolved as any).join(',') : 'N/A'}`);
|
|
639
|
+
}
|
|
640
|
+
|
|
631
641
|
if (!resolved || typeof resolved !== "object") return [];
|
|
632
642
|
|
|
633
643
|
const items = Array.isArray(resolved) ? resolved : [resolved];
|
package/src/routes/cmsRoute.ts
CHANGED
|
@@ -99,8 +99,21 @@ async function loadCmsPageInternal(fullPath: string) {
|
|
|
99
99
|
const request = new Request(urlWithSearch, {
|
|
100
100
|
headers: originRequest.headers,
|
|
101
101
|
});
|
|
102
|
+
|
|
103
|
+
for (const s of page.resolvedSections) {
|
|
104
|
+
if (s.component?.includes("eader")) {
|
|
105
|
+
console.log(`[CMS-ROUTE] BEFORE loaders: "${s.component}" propKeys=[${Object.keys(s.props || {}).join(",")}]`);
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
|
|
102
109
|
const enrichedSections = await runSectionLoaders(page.resolvedSections, request);
|
|
103
110
|
|
|
111
|
+
for (const s of enrichedSections) {
|
|
112
|
+
if (s.component?.includes("eader")) {
|
|
113
|
+
console.log(`[CMS-ROUTE] AFTER loaders: "${s.component}" propKeys=[${Object.keys(s.props || {}).join(",")}]`);
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
|
|
104
117
|
// Pre-import eager section modules so their default exports are cached
|
|
105
118
|
// in resolvedComponents. This ensures SSR renders with direct component
|
|
106
119
|
// refs, and the client hydration can skip React.lazy/Suspense.
|