@decocms/start 0.29.3 → 0.30.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +1 -1
- package/scripts/generate-blocks.ts +30 -9
- package/src/routes/cmsRoute.ts +27 -0
- package/src/routes/index.ts +1 -0
- package/src/vite/plugin.js +42 -4
package/package.json
CHANGED
|
@@ -1,6 +1,13 @@
|
|
|
1
1
|
#!/usr/bin/env tsx
|
|
2
2
|
/**
|
|
3
|
-
* Reads .deco/blocks/*.json and emits
|
|
3
|
+
* Reads .deco/blocks/*.json and emits:
|
|
4
|
+
* 1. blocks.gen.json — compact JSON data (the source of truth)
|
|
5
|
+
* 2. blocks.gen.ts — thin TypeScript re-export for editor tooling
|
|
6
|
+
*
|
|
7
|
+
* At runtime the Vite plugin (src/vite/plugin.js) intercepts `blocks.gen.ts`
|
|
8
|
+
* imports and replaces them with `JSON.parse(...)` of the .json file. This
|
|
9
|
+
* avoids Vite's SSR module runner hanging on large (13MB+) JS object literals
|
|
10
|
+
* and lets V8 use its fast JSON parser instead of the full JS parser.
|
|
4
11
|
*
|
|
5
12
|
* Usage (from site root):
|
|
6
13
|
* npx tsx node_modules/@decocms/start/scripts/generate-blocks.ts
|
|
@@ -20,6 +27,7 @@ function arg(name: string, fallback: string): string {
|
|
|
20
27
|
|
|
21
28
|
const blocksDir = path.resolve(process.cwd(), arg("blocks-dir", ".deco/blocks"));
|
|
22
29
|
const outFile = path.resolve(process.cwd(), arg("out-file", "src/server/cms/blocks.gen.ts"));
|
|
30
|
+
const jsonFile = outFile.replace(/\.ts$/, ".json");
|
|
23
31
|
|
|
24
32
|
function decodeBlockName(filename: string): string {
|
|
25
33
|
let name = filename.replace(/\.json$/, "");
|
|
@@ -35,13 +43,20 @@ function decodeBlockName(filename: string): string {
|
|
|
35
43
|
return name;
|
|
36
44
|
}
|
|
37
45
|
|
|
46
|
+
const TS_STUB = [
|
|
47
|
+
"// Auto-generated — thin wrapper around blocks.gen.json.",
|
|
48
|
+
"// The Vite plugin replaces this at load time with JSON.parse(...).",
|
|
49
|
+
"// Do not edit manually.",
|
|
50
|
+
"",
|
|
51
|
+
"export const blocks: Record<string, any> = {};",
|
|
52
|
+
"",
|
|
53
|
+
].join("\n");
|
|
54
|
+
|
|
38
55
|
if (!fs.existsSync(blocksDir)) {
|
|
39
56
|
console.warn(`Blocks directory not found: ${blocksDir} — generating empty barrel.`);
|
|
40
57
|
fs.mkdirSync(path.dirname(outFile), { recursive: true });
|
|
41
|
-
fs.writeFileSync(
|
|
42
|
-
|
|
43
|
-
`// Auto-generated — no blocks found\nexport const blocks: Record<string, any> = {};\n`,
|
|
44
|
-
);
|
|
58
|
+
fs.writeFileSync(jsonFile, "{}");
|
|
59
|
+
fs.writeFileSync(outFile, TS_STUB);
|
|
45
60
|
process.exit(0);
|
|
46
61
|
}
|
|
47
62
|
|
|
@@ -70,10 +85,16 @@ for (const [name, file] of Object.entries(blockFiles)) {
|
|
|
70
85
|
}
|
|
71
86
|
}
|
|
72
87
|
|
|
73
|
-
const output = `// Auto-generated from .deco/blocks/*.json\n// Do not edit manually.\n\nexport const blocks: Record<string, any> = ${JSON.stringify(blocks, null, 2)};\n`;
|
|
74
|
-
|
|
75
88
|
fs.mkdirSync(path.dirname(outFile), { recursive: true });
|
|
76
|
-
|
|
89
|
+
|
|
90
|
+
// 1. Compact JSON — the real data (no pretty-printing to save ~40% size)
|
|
91
|
+
const jsonStr = JSON.stringify(blocks);
|
|
92
|
+
fs.writeFileSync(jsonFile, jsonStr);
|
|
93
|
+
|
|
94
|
+
// 2. Thin TS wrapper — just for TypeScript tooling and as a Vite load target
|
|
95
|
+
fs.writeFileSync(outFile, TS_STUB);
|
|
96
|
+
|
|
97
|
+
const jsonSizeMB = (Buffer.byteLength(jsonStr) / 1_048_576).toFixed(1);
|
|
77
98
|
console.log(
|
|
78
|
-
`Generated ${Object.keys(blocks).length} blocks → ${path.relative(process.cwd(),
|
|
99
|
+
`Generated ${Object.keys(blocks).length} blocks → ${path.relative(process.cwd(), jsonFile)} (${jsonSizeMB} MB)`,
|
|
79
100
|
);
|
package/src/routes/cmsRoute.ts
CHANGED
|
@@ -228,6 +228,33 @@ export const loadDeferredSection = createServerFn({ method: "POST" })
|
|
|
228
228
|
return normalizeUrlsInObject(enriched);
|
|
229
229
|
});
|
|
230
230
|
|
|
231
|
+
// ---------------------------------------------------------------------------
|
|
232
|
+
// Pre-wrapped deferred section loader for IntersectionObserver-based rendering
|
|
233
|
+
// ---------------------------------------------------------------------------
|
|
234
|
+
|
|
235
|
+
/**
|
|
236
|
+
* Convenience wrapper around `loadDeferredSection` that matches the
|
|
237
|
+
* `loadDeferredSectionFn` prop signature of `DecoPageRenderer`.
|
|
238
|
+
*
|
|
239
|
+
* Pass this directly to `<DecoPageRenderer loadDeferredSectionFn={deferredSectionLoader} />`
|
|
240
|
+
* to enable IntersectionObserver-based lazy loading of deferred sections.
|
|
241
|
+
*/
|
|
242
|
+
export const deferredSectionLoader = async ({
|
|
243
|
+
component,
|
|
244
|
+
rawProps,
|
|
245
|
+
pagePath,
|
|
246
|
+
pageUrl,
|
|
247
|
+
}: {
|
|
248
|
+
component: string;
|
|
249
|
+
rawProps: Record<string, unknown>;
|
|
250
|
+
pagePath: string;
|
|
251
|
+
pageUrl?: string;
|
|
252
|
+
}): Promise<ResolvedSection | null> => {
|
|
253
|
+
return loadDeferredSection({
|
|
254
|
+
data: { component, rawProps, pagePath, pageUrl },
|
|
255
|
+
});
|
|
256
|
+
};
|
|
257
|
+
|
|
231
258
|
// ---------------------------------------------------------------------------
|
|
232
259
|
// Default pending component — shown during SPA navigation while loader runs
|
|
233
260
|
// ---------------------------------------------------------------------------
|
package/src/routes/index.ts
CHANGED
package/src/vite/plugin.js
CHANGED
|
@@ -5,12 +5,21 @@
|
|
|
5
5
|
* are eliminated from the browser bundle. This consolidates stubs that
|
|
6
6
|
* every Deco site previously had to copy into its own vite.config.ts.
|
|
7
7
|
*
|
|
8
|
+
* blocks.gen.ts handling:
|
|
9
|
+
* The CMS block registry can be 10MB+. Inlining it as a JS object literal
|
|
10
|
+
* causes Vite's SSR module runner to hang on dynamic imports (transport
|
|
11
|
+
* serialization bottleneck) and is slow to parse even with static imports
|
|
12
|
+
* (V8 full JS parser). Instead, generate-blocks.ts writes a .json data
|
|
13
|
+
* file, and this plugin intercepts the .ts import to return JSON.parse(...)
|
|
14
|
+
* — V8's JSON parser is 2-10x faster than the JS parser for large data.
|
|
15
|
+
*
|
|
8
16
|
* Usage:
|
|
9
17
|
* ```ts
|
|
10
18
|
* import { decoVitePlugin } from "@decocms/start/vite";
|
|
11
19
|
* export default defineConfig({ plugins: [decoVitePlugin(), ...] });
|
|
12
20
|
* ```
|
|
13
21
|
*/
|
|
22
|
+
import { readFileSync, existsSync } from "node:fs";
|
|
14
23
|
|
|
15
24
|
// Bare-specifier stubs resolved by ID before Vite touches them.
|
|
16
25
|
/** @type {Record<string, string>} */
|
|
@@ -69,16 +78,45 @@ export function decoVitePlugin() {
|
|
|
69
78
|
},
|
|
70
79
|
|
|
71
80
|
load(id, options) {
|
|
72
|
-
// blocks.gen.ts — the CMS block registry (
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
81
|
+
// blocks.gen.ts — the CMS block registry (can be 10MB+).
|
|
82
|
+
if (id.endsWith("blocks.gen.ts")) {
|
|
83
|
+
// Client: stub — the browser receives pre-resolved sections.
|
|
84
|
+
if (!options?.ssr) {
|
|
85
|
+
return "export const blocks = {};";
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
// SSR: read .json sibling and emit JSON.parse(...) wrapper.
|
|
89
|
+
// This avoids the Vite SSR module runner hanging on large dynamic
|
|
90
|
+
// imports and lets V8 use its fast JSON parser (~2-10x vs object literal).
|
|
91
|
+
const jsonPath = id.replace(/\.ts$/, ".json");
|
|
92
|
+
if (existsSync(jsonPath)) {
|
|
93
|
+
const raw = readFileSync(jsonPath, "utf-8");
|
|
94
|
+
return `export const blocks = JSON.parse(${JSON.stringify(raw)});`;
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
// Fallback: if .json doesn't exist yet (pre-generate-blocks), let
|
|
98
|
+
// Vite load the .ts file normally (may contain inline data for
|
|
99
|
+
// backward-compatible sites that haven't regenerated).
|
|
76
100
|
}
|
|
77
101
|
|
|
78
102
|
// Virtual module stubs.
|
|
79
103
|
return STUB_SOURCE[id];
|
|
80
104
|
},
|
|
81
105
|
|
|
106
|
+
configureServer(server) {
|
|
107
|
+
// When blocks.gen.json changes on disk, invalidate the .ts module
|
|
108
|
+
// so Vite re-runs our load() hook with the fresh data.
|
|
109
|
+
server.watcher.on("change", (file) => {
|
|
110
|
+
if (file.endsWith("blocks.gen.json")) {
|
|
111
|
+
const tsId = file.replace(/\.json$/, ".ts");
|
|
112
|
+
const mod = server.environments?.ssr?.moduleGraph?.getModuleById(tsId);
|
|
113
|
+
if (mod) {
|
|
114
|
+
server.environments.ssr.moduleGraph.invalidateModule(mod);
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
});
|
|
118
|
+
},
|
|
119
|
+
|
|
82
120
|
configEnvironment(name, env) {
|
|
83
121
|
if (name === "ssr" || name === "client") {
|
|
84
122
|
env.optimizeDeps = env.optimizeDeps || {};
|