dinou 6.1.0 → 7.0.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/CHANGELOG.md +99 -0
- package/README.md +9461 -1080
- package/cli.js +392 -169
- package/dinou/adapters/bun.js +62 -0
- package/dinou/adapters/cloudflare.js +65 -0
- package/dinou/adapters/deno.js +48 -0
- package/dinou/adapters/netlify.js +85 -0
- package/dinou/adapters/node.mjs +31 -0
- package/dinou/bun/build.mjs +1077 -0
- package/dinou/cloudflare/build.mjs +1121 -0
- package/dinou/cloudflare/shims/child_process.js +14 -0
- package/dinou/cloudflare/shims/empty.js +2 -0
- package/dinou/cloudflare/shims/fs.js +99 -0
- package/dinou/cloudflare/shims/os.js +29 -0
- package/dinou/config.js +1 -0
- package/dinou/config.mjs +2 -0
- package/dinou/constants.js +29 -4
- package/dinou/core/babel-esm-loader.js +66 -37
- package/dinou/core/bailout-proxy.js +61 -0
- package/dinou/core/build-static-pages.js +309 -157
- package/dinou/core/cache-revalidate.js +471 -144
- package/dinou/core/cache-slot.js +387 -0
- package/dinou/core/client-redirect.jsx +12 -7
- package/dinou/core/client-webpack.jsx +457 -82
- package/dinou/core/client.jsx +452 -82
- package/dinou/core/config.js +11 -0
- package/dinou/core/dinou-paths.js +52 -0
- package/dinou/core/edge-ssr.js +122 -0
- package/dinou/core/export-static.mjs +64 -0
- package/dinou/core/generate-route-types.js +252 -0
- package/dinou/core/generate-static-page.js +55 -94
- package/dinou/core/generate-static-rsc.js +47 -15
- package/dinou/core/generate-static.js +212 -10
- package/dinou/core/generating-isg.js +5 -0
- package/dinou/core/get-asset-from-manifest.js +9 -3
- package/dinou/core/get-error-jsx.js +221 -41
- package/dinou/core/get-file-path-and-dynamic-params.js +9 -13
- package/dinou/core/get-jsx.js +172 -95
- package/dinou/core/handler.js +2296 -0
- package/dinou/core/http-adapter.js +115 -0
- package/dinou/core/import-module.js +83 -53
- package/dinou/core/layout-functions.js +181 -0
- package/dinou/core/link.jsx +18 -6
- package/dinou/core/load-dinou-config.js +60 -0
- package/dinou/core/manifest-provider.js +261 -0
- package/dinou/core/navigation-utils.js +4 -0
- package/dinou/core/navigation.js +50 -39
- package/dinou/core/parse-exports.js +207 -2
- package/dinou/core/ppr-context.js +105 -0
- package/dinou/core/ppr-runtime.js +263 -0
- package/dinou/core/redirect.jsx +20 -19
- package/dinou/core/render-jsx-to-client-jsx.js +133 -15
- package/dinou/core/request-context.js +47 -13
- package/dinou/core/resolve-module-ppr.js +32 -0
- package/dinou/core/revalidating.js +28 -9
- package/dinou/core/route-generator.js +107 -0
- package/dinou/core/rsc-renderer.js +90 -0
- package/dinou/core/run-ssg.js +19 -0
- package/dinou/core/scan-dependency-components.js +218 -0
- package/dinou/core/server-function-proxy-webpack.js +3 -0
- package/dinou/core/server-function-proxy.js +3 -0
- package/dinou/core/slot.js +302 -0
- package/dinou/core/status-manifest.js +41 -1
- package/dinou/core/storage-adapter.js +522 -0
- package/dinou/core/url-resolver.js +16 -16
- package/dinou/core/vfs.js +139 -68
- package/dinou/deno/build.mjs +1112 -0
- package/dinou/deno/seed-kv.js +115 -0
- package/dinou/esbuild/build.mjs +51 -1
- package/dinou/esbuild/dev.mjs +290 -224
- package/dinou/esbuild/helpers-esbuild/get-config-esbuild-prod.mjs +3 -2
- package/dinou/esbuild/helpers-esbuild/get-config-esbuild.mjs +104 -16
- package/dinou/esbuild/helpers-esbuild/get-esbuild-entries.mjs +31 -19
- package/dinou/esbuild/helpers-esbuild/strip-namespace.mjs +9 -0
- package/dinou/esbuild/helpers-esbuild/swc-disk-cache.mjs +225 -0
- package/dinou/esbuild/helpers-esbuild/update-manifest-for-module.mjs +7 -0
- package/dinou/esbuild/helpers-esbuild/write.mjs +111 -2
- package/dinou/esbuild/plugins-esbuild/assets-plugin.mjs +236 -161
- package/dinou/esbuild/plugins-esbuild/css-processor-plugin.mjs +101 -5
- package/dinou/esbuild/plugins-esbuild/react-client-manifest-plugin.mjs +63 -24
- package/dinou/esbuild/plugins-esbuild/server-functions-plugin.mjs +177 -56
- package/dinou/esbuild/plugins-esbuild/skip-missing-entry-points-plugin.mjs +3 -0
- package/dinou/esbuild/plugins-esbuild/stable-chunk-names-and-maps-plugin.mjs +182 -137
- package/dinou/esbuild/plugins-postcss/postcss-extract-plugin.js +38 -11
- package/dinou/esbuild/react-refresh/esm-hmr/client.mjs +55 -8
- package/dinou/esbuild/react-refresh/esm-hmr/server.js +1 -1
- package/dinou/esbuild/react-refresh/esm-hmr-plugin.mjs +238 -101
- package/dinou/esbuild/react-refresh/is-react-refresh-boundary.mjs +18 -4
- package/dinou/index.d.ts +315 -11
- package/dinou/index.js +12 -6
- package/dinou/index.mjs +9 -0
- package/dinou/node/build.mjs +195 -0
- package/dinou/node/bundle-dual-engine.mjs +1012 -0
- package/dinou/node/dev.mjs +2589 -0
- package/dinou/node/port-selector.mjs +164 -0
- package/dinou/node/terminal-status.mjs +345 -0
- package/dinou/package.json +17 -14
- package/dinou/rollup/react-refresh/esm-hmr/client.js +66 -10
- package/dinou/rollup/react-refresh/esm-hmr/server.js +1 -1
- package/dinou/rollup/react-refresh/is-react-refresh-boundary.js +24 -5
- package/dinou/rollup/react-refresh/react-refresh-runtime.js +13 -6
- package/dinou/rollup/react-refresh/react-refresh-wrap-modules.js +56 -11
- package/dinou/rollup/react-refresh/rollup-plugin-esm-hmr.js +176 -48
- package/dinou/rollup/rollup-plugins/dinou-asset-plugin.js +9 -5
- package/dinou/rollup/rollup-plugins/rollup-plugin-memory.js +66 -0
- package/dinou/rollup/rollup-plugins/rollup-plugin-react-client-manifest.js +468 -123
- package/dinou/rollup/rollup-plugins/rollup-plugin-server-functions.js +44 -12
- package/dinou/rollup/rollup-plugins/rollup-plugin-swc.js +100 -0
- package/dinou/rollup/rollup.config.js +155 -43
- package/dinou/server.d.ts +114 -12
- package/dinou/server.js +35 -10
- package/dinou/server.mjs +30 -9
- package/dinou/webpack/helpers/get-webpack-entries.js +14 -19
- package/dinou/webpack/loaders/server-functions-loader.js +22 -1
- package/dinou/webpack/loaders/swc-loader.js +76 -0
- package/dinou/webpack/plugins/manifest-generator-plugin.js +1 -0
- package/dinou/webpack/plugins/server-functions-plugin.js +75 -19
- package/dinou/webpack/plugins/webpack-memory-plugin.js +66 -0
- package/dinou/webpack/webpack.config.js +227 -29
- package/eject.js +173 -94
- package/package.json +110 -107
- package/dinou/core/asset-require-hook.js +0 -58
- package/dinou/core/client-error-webpack.jsx +0 -432
- package/dinou/core/client-error.jsx +0 -432
- package/dinou/core/concurrency-manager.js +0 -48
- package/dinou/core/context-proxy.js +0 -68
- package/dinou/core/generate-static-pages.js +0 -130
- package/dinou/core/generate-static-rscs.js +0 -151
- package/dinou/core/get-ssg-metadata.js +0 -31
- package/dinou/core/register-loader.mjs +0 -11
- package/dinou/core/register-paths.js +0 -27
- package/dinou/core/render-app-to-html.js +0 -430
- package/dinou/core/render-html.js +0 -407
- package/dinou/core/server.js +0 -1542
- package/dinou/esbuild/plugins-esbuild/write-metafile-plugin.mjs +0 -16
|
@@ -0,0 +1,1012 @@
|
|
|
1
|
+
// dinou/node/bundle-dual-engine.mjs
|
|
2
|
+
// Reusable Dual-Bundle Compiler for Dinou:
|
|
3
|
+
// Compiles Pass A (RSC Engine) and Pass B (SSR Engine) using esbuild
|
|
4
|
+
// in a single process without React collisions.
|
|
5
|
+
|
|
6
|
+
import fs from "node:fs";
|
|
7
|
+
import path from "node:path";
|
|
8
|
+
import esbuild from "esbuild";
|
|
9
|
+
import { createRequire } from "node:module";
|
|
10
|
+
import { fileURLToPath, pathToFileURL } from "node:url";
|
|
11
|
+
|
|
12
|
+
const require = createRequire(import.meta.url);
|
|
13
|
+
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
|
14
|
+
const { generateRouteModulesCode, generateRouteTypes } = require("../core/route-generator.js");
|
|
15
|
+
const parseExports = require("../core/parse-exports.js");
|
|
16
|
+
const { useClientRegex, useServerRegex } = require("../constants.js");
|
|
17
|
+
const createScopedName = require("../core/createScopedName.js");
|
|
18
|
+
const { regex: assetRegex } = require("../core/asset-extensions.js");
|
|
19
|
+
const {
|
|
20
|
+
isSupportedClientModule,
|
|
21
|
+
scanProjectDependenciesForClientComponents,
|
|
22
|
+
} = require("../core/scan-dependency-components.js");
|
|
23
|
+
|
|
24
|
+
export async function bundleDualEngine(options = {}) {
|
|
25
|
+
const isDev = Boolean(options.isDev);
|
|
26
|
+
const projectRoot = path.resolve(options.projectRoot || process.cwd());
|
|
27
|
+
const nodeDir = path.resolve(projectRoot, options.outDir || ".dinou/node");
|
|
28
|
+
fs.mkdirSync(nodeDir, { recursive: true });
|
|
29
|
+
|
|
30
|
+
try {
|
|
31
|
+
generateRouteTypes(projectRoot);
|
|
32
|
+
} catch (e) {}
|
|
33
|
+
|
|
34
|
+
// Locate dinou root
|
|
35
|
+
const dinouDir = fs.existsSync(path.resolve(projectRoot, "dinou"))
|
|
36
|
+
? path.resolve(projectRoot, "dinou")
|
|
37
|
+
: path.resolve(__dirname, "..");
|
|
38
|
+
const dinouDirSlash = dinouDir.replace(/\\/g, "/");
|
|
39
|
+
|
|
40
|
+
const candidateDinouRoots = [
|
|
41
|
+
dinouDir,
|
|
42
|
+
path.resolve(projectRoot, "dinou"),
|
|
43
|
+
path.resolve(projectRoot, "node_modules/dinou/dinou"),
|
|
44
|
+
path.resolve(projectRoot, "node_modules/dinou"),
|
|
45
|
+
path.resolve(__dirname, ".."),
|
|
46
|
+
];
|
|
47
|
+
|
|
48
|
+
const candidateLinkPaths = new Set();
|
|
49
|
+
const candidateRedirectPaths = new Set();
|
|
50
|
+
const candidateSlotPaths = new Set();
|
|
51
|
+
for (const r of candidateDinouRoots) {
|
|
52
|
+
candidateLinkPaths.add(path.resolve(r, "core/link.jsx"));
|
|
53
|
+
candidateRedirectPaths.add(path.resolve(r, "core/client-redirect.jsx"));
|
|
54
|
+
candidateSlotPaths.add(path.resolve(r, "core/slot.js"));
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
// 1. Generate route modules
|
|
58
|
+
const routeModulesCode = generateRouteModulesCode(projectRoot, "../..");
|
|
59
|
+
const routeModulesPath = path.join(nodeDir, "route-modules.mjs");
|
|
60
|
+
fs.writeFileSync(routeModulesPath, routeModulesCode, "utf8");
|
|
61
|
+
|
|
62
|
+
// 2. Discover manifests
|
|
63
|
+
const isWebpackBuild = process.env.DINOU_BUILD_TOOL === "webpack";
|
|
64
|
+
|
|
65
|
+
function findManifest(filename, fallbackFolder) {
|
|
66
|
+
const candidates = isWebpackBuild
|
|
67
|
+
? [
|
|
68
|
+
path.resolve(projectRoot, ".dinou/dist3", filename),
|
|
69
|
+
path.resolve(projectRoot, ".dinou/public", filename),
|
|
70
|
+
path.resolve(projectRoot, ".dinou", fallbackFolder, filename),
|
|
71
|
+
]
|
|
72
|
+
: [
|
|
73
|
+
path.resolve(projectRoot, ".dinou", fallbackFolder, filename),
|
|
74
|
+
path.resolve(projectRoot, ".dinou/dist3", filename),
|
|
75
|
+
path.resolve(projectRoot, ".dinou/public", filename),
|
|
76
|
+
];
|
|
77
|
+
for (const c of candidates) {
|
|
78
|
+
if (fs.existsSync(c)) return c;
|
|
79
|
+
}
|
|
80
|
+
return null;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
const clientManifestPath = findManifest("react-client-manifest.json", "react_client_manifest");
|
|
84
|
+
const sfManifestPath = findManifest("server-functions-manifest.json", "server_functions_manifest");
|
|
85
|
+
|
|
86
|
+
let parsedClientManifest = {};
|
|
87
|
+
if (clientManifestPath && fs.existsSync(clientManifestPath)) {
|
|
88
|
+
try {
|
|
89
|
+
parsedClientManifest = JSON.parse(fs.readFileSync(clientManifestPath, "utf8"));
|
|
90
|
+
} catch (e) {}
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
let parsedServerFunctionsManifest = {};
|
|
94
|
+
if (sfManifestPath && fs.existsSync(sfManifestPath)) {
|
|
95
|
+
try {
|
|
96
|
+
parsedServerFunctionsManifest = JSON.parse(fs.readFileSync(sfManifestPath, "utf8"));
|
|
97
|
+
} catch (e) {}
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
// 3. Client components discovery
|
|
101
|
+
const srcDir = path.resolve(projectRoot, "src");
|
|
102
|
+
|
|
103
|
+
|
|
104
|
+
function findClientComponents() {
|
|
105
|
+
const clientFiles = new Set();
|
|
106
|
+
function walk(dir) {
|
|
107
|
+
if (!fs.existsSync(dir)) return;
|
|
108
|
+
const entries = fs.readdirSync(dir, { withFileTypes: true });
|
|
109
|
+
for (const entry of entries) {
|
|
110
|
+
const full = path.join(dir, entry.name);
|
|
111
|
+
if (entry.isDirectory()) {
|
|
112
|
+
walk(full);
|
|
113
|
+
} else if (/\.[jt]sx?$/.test(entry.name)) {
|
|
114
|
+
try {
|
|
115
|
+
const content = fs.readFileSync(full, "utf8");
|
|
116
|
+
if (useClientRegex.test(content.trim()) && isSupportedClientModule(full, content)) {
|
|
117
|
+
clientFiles.add(path.resolve(full));
|
|
118
|
+
}
|
|
119
|
+
} catch (e) {}
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
walk(srcDir);
|
|
124
|
+
|
|
125
|
+
const coreCandidates = [
|
|
126
|
+
...Array.from(candidateLinkPaths),
|
|
127
|
+
...Array.from(candidateRedirectPaths),
|
|
128
|
+
...Array.from(candidateSlotPaths),
|
|
129
|
+
];
|
|
130
|
+
for (const f of coreCandidates) {
|
|
131
|
+
if (fs.existsSync(f)) {
|
|
132
|
+
clientFiles.add(path.resolve(f));
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
scanProjectDependenciesForClientComponents(projectRoot, clientFiles, useClientRegex);
|
|
137
|
+
|
|
138
|
+
for (const k of Object.keys(parsedClientManifest)) {
|
|
139
|
+
const fileUrl = k.split("#")[0];
|
|
140
|
+
if (fileUrl.startsWith("file:///")) {
|
|
141
|
+
try {
|
|
142
|
+
const filePath = fileURLToPath(fileUrl);
|
|
143
|
+
const baseName = path.basename(filePath);
|
|
144
|
+
if (
|
|
145
|
+
baseName === "client.jsx" ||
|
|
146
|
+
baseName === "client-webpack.jsx"
|
|
147
|
+
) {
|
|
148
|
+
continue;
|
|
149
|
+
}
|
|
150
|
+
if (fs.existsSync(filePath) && isSupportedClientModule(filePath)) {
|
|
151
|
+
clientFiles.add(path.resolve(filePath));
|
|
152
|
+
}
|
|
153
|
+
} catch (e) {}
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
return Array.from(clientFiles);
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
const clientComponents = findClientComponents();
|
|
161
|
+
|
|
162
|
+
let manifestInlines = "";
|
|
163
|
+
const normalizedManifest = { ...parsedClientManifest };
|
|
164
|
+
for (const [k, v] of Object.entries(parsedClientManifest)) {
|
|
165
|
+
if (k.startsWith("file:///c:/")) {
|
|
166
|
+
normalizedManifest["file:///C:/" + k.slice(11)] = v;
|
|
167
|
+
} else if (k.startsWith("file:///C:/")) {
|
|
168
|
+
normalizedManifest["file:///c:/" + k.slice(11)] = v;
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
function generateAllUrlVariants(absPath) {
|
|
173
|
+
const norm = absPath.replace(/\\/g, "/");
|
|
174
|
+
const fileUrl = pathToFileURL(absPath).href;
|
|
175
|
+
const urls = new Set([fileUrl]);
|
|
176
|
+
urls.add(fileUrl.replace(/file:\/\/\/([a-zA-Z]):/, (m, d) => "file:///" + d.toLowerCase() + ":"));
|
|
177
|
+
urls.add(fileUrl.replace(/file:\/\/\/([a-zA-Z]):/, (m, d) => "file:///" + d.toUpperCase() + ":"));
|
|
178
|
+
urls.add("file:///" + norm);
|
|
179
|
+
urls.add("file://" + norm);
|
|
180
|
+
return Array.from(urls);
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
let linkChunkId = null;
|
|
184
|
+
let redirectChunkId = null;
|
|
185
|
+
let linkEntry = null;
|
|
186
|
+
let redirectEntry = null;
|
|
187
|
+
let slotChunkId = null;
|
|
188
|
+
let slotEntry = null;
|
|
189
|
+
|
|
190
|
+
for (const [k, v] of Object.entries(parsedClientManifest)) {
|
|
191
|
+
if (k.includes("/core/link.jsx") || k.includes("dinouLink")) {
|
|
192
|
+
if (v && v.id) {
|
|
193
|
+
linkChunkId = v.id;
|
|
194
|
+
linkEntry = v;
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
if (k.includes("/core/client-redirect.jsx") || k.includes("dinouClientRedirect")) {
|
|
198
|
+
if (v && v.id) {
|
|
199
|
+
redirectChunkId = v.id;
|
|
200
|
+
redirectEntry = v;
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
if (k.includes("/core/slot.") || k.includes("dinouSlot")) {
|
|
204
|
+
if (v && v.id) {
|
|
205
|
+
slotChunkId = v.id;
|
|
206
|
+
slotEntry = v;
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
if (linkChunkId) {
|
|
212
|
+
const linkChunks = isWebpackBuild ? linkEntry?.chunks || [linkChunkId] : "Link";
|
|
213
|
+
const linkDefaultChunks = isWebpackBuild ? linkEntry?.chunks || [linkChunkId] : "default";
|
|
214
|
+
|
|
215
|
+
for (const lp of candidateLinkPaths) {
|
|
216
|
+
for (const url of generateAllUrlVariants(lp)) {
|
|
217
|
+
normalizedManifest[`${url}#Link`] = { id: linkChunkId, chunks: linkChunks, name: "Link" };
|
|
218
|
+
normalizedManifest[`${url}#default`] = { id: linkChunkId, chunks: linkDefaultChunks, name: "default" };
|
|
219
|
+
normalizedManifest[url] = { id: linkChunkId, chunks: linkDefaultChunks, name: "default" };
|
|
220
|
+
}
|
|
221
|
+
}
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
if (redirectChunkId) {
|
|
225
|
+
const redirectChunks = isWebpackBuild ? redirectEntry?.chunks || [redirectChunkId] : "ClientRedirect";
|
|
226
|
+
const redirectDefaultChunks = isWebpackBuild ? redirectEntry?.chunks || [redirectChunkId] : "default";
|
|
227
|
+
|
|
228
|
+
for (const rp of candidateRedirectPaths) {
|
|
229
|
+
for (const url of generateAllUrlVariants(rp)) {
|
|
230
|
+
normalizedManifest[`${url}#ClientRedirect`] = { id: redirectChunkId, chunks: redirectChunks, name: "ClientRedirect" };
|
|
231
|
+
normalizedManifest[`${url}#default`] = { id: redirectChunkId, chunks: redirectDefaultChunks, name: "default" };
|
|
232
|
+
normalizedManifest[url] = { id: redirectChunkId, chunks: redirectDefaultChunks, name: "default" };
|
|
233
|
+
}
|
|
234
|
+
}
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
if (slotChunkId) {
|
|
238
|
+
const slotChunks = isWebpackBuild ? slotEntry?.chunks || [slotChunkId] : "DinouPageSlot";
|
|
239
|
+
const slotBoundaryChunks = isWebpackBuild ? slotEntry?.chunks || [slotChunkId] : "DinouCacheSlotBoundary";
|
|
240
|
+
const slotDefaultChunks = isWebpackBuild ? slotEntry?.chunks || [slotChunkId] : "default";
|
|
241
|
+
|
|
242
|
+
for (const sp of candidateSlotPaths) {
|
|
243
|
+
for (const url of generateAllUrlVariants(sp)) {
|
|
244
|
+
normalizedManifest[`${url}#DinouPageSlot`] = { id: slotChunkId, chunks: slotChunks, name: "DinouPageSlot" };
|
|
245
|
+
normalizedManifest[`${url}#DinouCacheSlotBoundary`] = { id: slotChunkId, chunks: slotBoundaryChunks, name: "DinouCacheSlotBoundary" };
|
|
246
|
+
normalizedManifest[`${url}#default`] = { id: slotChunkId, chunks: slotDefaultChunks, name: "default" };
|
|
247
|
+
normalizedManifest[url] = { id: slotChunkId, chunks: slotDefaultChunks, name: "default" };
|
|
248
|
+
}
|
|
249
|
+
}
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
for (const comp of clientComponents) {
|
|
253
|
+
const fileUrl = pathToFileURL(comp).href;
|
|
254
|
+
const fileUrlLower = fileUrl.replace(/file:\/\/\/([a-zA-Z]):/, (m, d) => "file:///" + d.toLowerCase() + ":");
|
|
255
|
+
const fileUrlUpper = fileUrl.replace(/file:\/\/\/([a-zA-Z]):/, (m, d) => "file:///" + d.toUpperCase() + ":");
|
|
256
|
+
const compResolved = path.resolve(comp);
|
|
257
|
+
const isCoreLink = candidateLinkPaths.has(compResolved);
|
|
258
|
+
const isCoreRedirect = candidateRedirectPaths.has(compResolved);
|
|
259
|
+
const isCoreSlot = candidateSlotPaths.has(compResolved);
|
|
260
|
+
|
|
261
|
+
if (!normalizedManifest[fileUrlLower]) {
|
|
262
|
+
if (isCoreLink && linkChunkId) {
|
|
263
|
+
normalizedManifest[fileUrlLower] = {
|
|
264
|
+
id: linkChunkId,
|
|
265
|
+
chunks: isWebpackBuild ? linkEntry?.chunks || [linkChunkId] : "default",
|
|
266
|
+
name: "default",
|
|
267
|
+
};
|
|
268
|
+
} else if (isCoreRedirect && redirectChunkId) {
|
|
269
|
+
normalizedManifest[fileUrlLower] = {
|
|
270
|
+
id: redirectChunkId,
|
|
271
|
+
chunks: isWebpackBuild ? redirectEntry?.chunks || [redirectChunkId] : "default",
|
|
272
|
+
name: "default",
|
|
273
|
+
};
|
|
274
|
+
} else if (isCoreSlot && slotChunkId) {
|
|
275
|
+
normalizedManifest[fileUrlLower] = {
|
|
276
|
+
id: slotChunkId,
|
|
277
|
+
chunks: isWebpackBuild ? slotEntry?.chunks || [slotChunkId] : "default",
|
|
278
|
+
name: "default",
|
|
279
|
+
};
|
|
280
|
+
} else {
|
|
281
|
+
normalizedManifest[fileUrlLower] = { id: fileUrlLower, chunks: [], name: "*" };
|
|
282
|
+
}
|
|
283
|
+
}
|
|
284
|
+
if (!normalizedManifest[fileUrlUpper]) {
|
|
285
|
+
if (isCoreLink && linkChunkId) {
|
|
286
|
+
normalizedManifest[fileUrlUpper] = {
|
|
287
|
+
id: linkChunkId,
|
|
288
|
+
chunks: isWebpackBuild ? linkEntry?.chunks || [linkChunkId] : "default",
|
|
289
|
+
name: "default",
|
|
290
|
+
};
|
|
291
|
+
} else if (isCoreRedirect && redirectChunkId) {
|
|
292
|
+
normalizedManifest[fileUrlUpper] = {
|
|
293
|
+
id: redirectChunkId,
|
|
294
|
+
chunks: isWebpackBuild ? redirectEntry?.chunks || [redirectChunkId] : "default",
|
|
295
|
+
name: "default",
|
|
296
|
+
};
|
|
297
|
+
} else if (isCoreSlot && slotChunkId) {
|
|
298
|
+
normalizedManifest[fileUrlUpper] = {
|
|
299
|
+
id: slotChunkId,
|
|
300
|
+
chunks: isWebpackBuild ? slotEntry?.chunks || [slotChunkId] : "default",
|
|
301
|
+
name: "default",
|
|
302
|
+
};
|
|
303
|
+
} else {
|
|
304
|
+
normalizedManifest[fileUrlUpper] = { id: fileUrlLower, chunks: [], name: "*" };
|
|
305
|
+
}
|
|
306
|
+
}
|
|
307
|
+
}
|
|
308
|
+
|
|
309
|
+
manifestInlines += `\nglobalThis.__DINOU_CLIENT_MANIFEST__ = ${JSON.stringify(normalizedManifest)};\n`;
|
|
310
|
+
|
|
311
|
+
if (sfManifestPath && fs.existsSync(sfManifestPath)) {
|
|
312
|
+
const content = fs.readFileSync(sfManifestPath, "utf8");
|
|
313
|
+
manifestInlines += `\nglobalThis.__DINOU_SERVER_FUNCTIONS_MANIFEST__ = ${content};\n`;
|
|
314
|
+
} else {
|
|
315
|
+
manifestInlines += `\nglobalThis.__DINOU_SERVER_FUNCTIONS_MANIFEST__ = {};\n`;
|
|
316
|
+
}
|
|
317
|
+
|
|
318
|
+
const assetManifestPath = findManifest("manifest.json", "dist3");
|
|
319
|
+
if (assetManifestPath && fs.existsSync(assetManifestPath)) {
|
|
320
|
+
const content = fs.readFileSync(assetManifestPath, "utf8");
|
|
321
|
+
manifestInlines += `\nglobalThis.__DINOU_ASSET_MANIFEST__ = ${content};\n`;
|
|
322
|
+
} else {
|
|
323
|
+
manifestInlines += `\nglobalThis.__DINOU_ASSET_MANIFEST__ = {};\n`;
|
|
324
|
+
}
|
|
325
|
+
|
|
326
|
+
let importMapHtml = "";
|
|
327
|
+
if (!isWebpackBuild && Object.keys(parsedClientManifest).length > 0) {
|
|
328
|
+
const imports = {};
|
|
329
|
+
const moduleBasePath = pathToFileURL(projectRoot).href + "/";
|
|
330
|
+
|
|
331
|
+
for (const [key, val] of Object.entries(parsedClientManifest)) {
|
|
332
|
+
let specifier = key;
|
|
333
|
+
if (specifier.startsWith(moduleBasePath)) {
|
|
334
|
+
specifier = specifier.slice(moduleBasePath.length);
|
|
335
|
+
}
|
|
336
|
+
const hashIdx = specifier.indexOf("#");
|
|
337
|
+
if (hashIdx !== -1) {
|
|
338
|
+
specifier = specifier.slice(0, hashIdx);
|
|
339
|
+
}
|
|
340
|
+
imports[specifier] = val.id;
|
|
341
|
+
|
|
342
|
+
const srcIdx = key.indexOf("/src/");
|
|
343
|
+
if (srcIdx !== -1) {
|
|
344
|
+
const srcRel = "src/" + key.slice(srcIdx + 5).split("#")[0];
|
|
345
|
+
imports[srcRel] = val.id;
|
|
346
|
+
imports["/" + srcRel] = val.id;
|
|
347
|
+
imports["./" + srcRel] = val.id;
|
|
348
|
+
}
|
|
349
|
+
const dinouIdx = key.lastIndexOf("/dinou/");
|
|
350
|
+
if (dinouIdx !== -1) {
|
|
351
|
+
const dinouRel = "dinou/" + key.slice(dinouIdx + 7).split("#")[0];
|
|
352
|
+
imports[dinouRel] = val.id;
|
|
353
|
+
imports["/" + dinouRel] = val.id;
|
|
354
|
+
imports["./" + dinouRel] = val.id;
|
|
355
|
+
}
|
|
356
|
+
}
|
|
357
|
+
|
|
358
|
+
if (linkChunkId) {
|
|
359
|
+
imports["dinou/core/link.jsx"] = linkChunkId;
|
|
360
|
+
imports["dinou/core/link"] = linkChunkId;
|
|
361
|
+
imports["/dinou/core/link.jsx"] = linkChunkId;
|
|
362
|
+
imports["./dinou/core/link.jsx"] = linkChunkId;
|
|
363
|
+
for (const lp of candidateLinkPaths) {
|
|
364
|
+
for (const u of generateAllUrlVariants(lp)) {
|
|
365
|
+
imports[u] = linkChunkId;
|
|
366
|
+
}
|
|
367
|
+
}
|
|
368
|
+
}
|
|
369
|
+
if (redirectChunkId) {
|
|
370
|
+
imports["dinou/core/client-redirect.jsx"] = redirectChunkId;
|
|
371
|
+
imports["dinou/core/client-redirect"] = redirectChunkId;
|
|
372
|
+
imports["/dinou/core/client-redirect.jsx"] = redirectChunkId;
|
|
373
|
+
imports["./dinou/core/client-redirect.jsx"] = redirectChunkId;
|
|
374
|
+
for (const rp of candidateRedirectPaths) {
|
|
375
|
+
for (const u of generateAllUrlVariants(rp)) {
|
|
376
|
+
imports[u] = redirectChunkId;
|
|
377
|
+
}
|
|
378
|
+
}
|
|
379
|
+
}
|
|
380
|
+
if (slotChunkId) {
|
|
381
|
+
imports["dinou/core/slot.js"] = slotChunkId;
|
|
382
|
+
imports["dinou/core/slot"] = slotChunkId;
|
|
383
|
+
imports["/dinou/core/slot.js"] = slotChunkId;
|
|
384
|
+
imports["./dinou/core/slot.js"] = slotChunkId;
|
|
385
|
+
for (const sp of candidateSlotPaths) {
|
|
386
|
+
for (const u of generateAllUrlVariants(sp)) {
|
|
387
|
+
imports[u] = slotChunkId;
|
|
388
|
+
}
|
|
389
|
+
}
|
|
390
|
+
}
|
|
391
|
+
|
|
392
|
+
importMapHtml = `<script type="importmap">{"imports":${JSON.stringify(imports)}}</script>`;
|
|
393
|
+
}
|
|
394
|
+
|
|
395
|
+
manifestInlines += `\nglobalThis.__DINOU_IMPORT_MAP_HTML__ = ${JSON.stringify(importMapHtml)};\n`;
|
|
396
|
+
|
|
397
|
+
const discoveredLayouts = [];
|
|
398
|
+
function findLayoutRoutes(dir, rel = "") {
|
|
399
|
+
if (!fs.existsSync(dir)) return;
|
|
400
|
+
const entries = fs.readdirSync(dir, { withFileTypes: true });
|
|
401
|
+
for (const entry of entries) {
|
|
402
|
+
if (entry.isDirectory()) {
|
|
403
|
+
findLayoutRoutes(path.join(dir, entry.name), rel ? `${rel}/${entry.name}` : entry.name);
|
|
404
|
+
} else if (/^layout\.[jt]sx?$/.test(entry.name)) {
|
|
405
|
+
discoveredLayouts.push(rel);
|
|
406
|
+
}
|
|
407
|
+
}
|
|
408
|
+
}
|
|
409
|
+
findLayoutRoutes(srcDir);
|
|
410
|
+
manifestInlines += `\nglobalThis.__DINOU_LAYOUTS__ = ${JSON.stringify(discoveredLayouts)};\n`;
|
|
411
|
+
|
|
412
|
+
// 4. In-memory VFS snapshot for routing
|
|
413
|
+
const vfsSnapshot = {};
|
|
414
|
+
function walkVfs(dir) {
|
|
415
|
+
if (!fs.existsSync(dir)) return;
|
|
416
|
+
const entries = fs.readdirSync(dir, { withFileTypes: true });
|
|
417
|
+
const children = [];
|
|
418
|
+
for (const entry of entries) {
|
|
419
|
+
const isDir = entry.isDirectory();
|
|
420
|
+
children.push({ name: entry.name, isDirectory: isDir });
|
|
421
|
+
const fullPath = path.join(dir, entry.name);
|
|
422
|
+
const relFromSrc = path.relative(srcDir, fullPath).replace(/\\/g, "/");
|
|
423
|
+
const edgePath = "/src/" + relFromSrc;
|
|
424
|
+
const slashPath = fullPath.replace(/\\/g, "/");
|
|
425
|
+
if (isDir) {
|
|
426
|
+
walkVfs(fullPath);
|
|
427
|
+
} else {
|
|
428
|
+
vfsSnapshot[edgePath] = { type: "file" };
|
|
429
|
+
vfsSnapshot[slashPath] = { type: "file" };
|
|
430
|
+
if (slashPath.length > 2 && slashPath[1] === ":") {
|
|
431
|
+
vfsSnapshot[slashPath.slice(2)] = { type: "file" };
|
|
432
|
+
}
|
|
433
|
+
vfsSnapshot["src/" + relFromSrc] = { type: "file" };
|
|
434
|
+
}
|
|
435
|
+
}
|
|
436
|
+
const dirRel = path.relative(srcDir, dir).replace(/\\/g, "/");
|
|
437
|
+
const edgeDir = dirRel ? "/src/" + dirRel : "/src";
|
|
438
|
+
const dirSlash = dir.replace(/\\/g, "/");
|
|
439
|
+
vfsSnapshot[edgeDir] = { type: "directory", children };
|
|
440
|
+
vfsSnapshot[dirSlash] = { type: "directory", children };
|
|
441
|
+
if (dirSlash.length > 2 && dirSlash[1] === ":") {
|
|
442
|
+
vfsSnapshot[dirSlash.slice(2)] = { type: "directory", children };
|
|
443
|
+
}
|
|
444
|
+
vfsSnapshot[dirRel ? "src/" + dirRel : "src"] = { type: "directory", children };
|
|
445
|
+
}
|
|
446
|
+
walkVfs(srcDir);
|
|
447
|
+
|
|
448
|
+
const envSetupContent = `// Auto-generated environment setup
|
|
449
|
+
if (typeof globalThis.__webpack_require__ === 'undefined') {
|
|
450
|
+
globalThis.__webpack_require__ = function(id) {
|
|
451
|
+
if (globalThis.__webpack_modules__ && globalThis.__webpack_modules__[id]) {
|
|
452
|
+
return globalThis.__webpack_modules__[id];
|
|
453
|
+
}
|
|
454
|
+
return {};
|
|
455
|
+
};
|
|
456
|
+
}
|
|
457
|
+
if (typeof globalThis.__webpack_require__.u === 'undefined') {
|
|
458
|
+
globalThis.__webpack_require__.u = function(chunkId) { return '' + chunkId + '.js'; };
|
|
459
|
+
}
|
|
460
|
+
if (typeof globalThis.__webpack_chunk_load__ === 'undefined') {
|
|
461
|
+
globalThis.__webpack_chunk_load__ = () => Promise.resolve();
|
|
462
|
+
}
|
|
463
|
+
${manifestInlines}
|
|
464
|
+
globalThis.__DINOU_VFS__ = ${JSON.stringify(vfsSnapshot)};
|
|
465
|
+
`;
|
|
466
|
+
const envSetupPath = path.join(nodeDir, "env-setup.mjs");
|
|
467
|
+
fs.writeFileSync(envSetupPath, envSetupContent, "utf8");
|
|
468
|
+
|
|
469
|
+
// 5. Generate ssr-client-manifest.mjs
|
|
470
|
+
let ssrManifestCode = `// Auto-generated client modules registry for Native Node SSR\n`;
|
|
471
|
+
clientComponents.forEach((compPath, index) => {
|
|
472
|
+
const normPath = compPath.replace(/\\/g, "/");
|
|
473
|
+
ssrManifestCode += `import * as mod_${index} from ${JSON.stringify(normPath)};\n`;
|
|
474
|
+
});
|
|
475
|
+
|
|
476
|
+
const serverFunctionFiles = Object.keys(parsedServerFunctionsManifest);
|
|
477
|
+
serverFunctionFiles.forEach((relPath, index) => {
|
|
478
|
+
const absPath = path.resolve(projectRoot, relPath).replace(/\\/g, "/");
|
|
479
|
+
ssrManifestCode += `import * as sf_${index} from ${JSON.stringify(absPath)};\n`;
|
|
480
|
+
});
|
|
481
|
+
|
|
482
|
+
ssrManifestCode += `\nexport const clientModules = {\n`;
|
|
483
|
+
const emittedClientModules = new Set();
|
|
484
|
+
function addClientModule(key, valueExpr) {
|
|
485
|
+
if (!emittedClientModules.has(key)) {
|
|
486
|
+
emittedClientModules.add(key);
|
|
487
|
+
ssrManifestCode += ` ${JSON.stringify(key)}: ${valueExpr},\n`;
|
|
488
|
+
}
|
|
489
|
+
}
|
|
490
|
+
|
|
491
|
+
clientComponents.forEach((compPath, index) => {
|
|
492
|
+
const fileUrl = pathToFileURL(compPath).href;
|
|
493
|
+
const altFileUrl = fileUrl.replace(
|
|
494
|
+
/file:\/\/\/([a-zA-Z]):/,
|
|
495
|
+
(m, d) => "file:///" + (d === d.toLowerCase() ? d.toUpperCase() : d.toLowerCase()) + ":"
|
|
496
|
+
);
|
|
497
|
+
addClientModule(fileUrl, `mod_${index}`);
|
|
498
|
+
if (altFileUrl !== fileUrl) {
|
|
499
|
+
addClientModule(altFileUrl, `mod_${index}`);
|
|
500
|
+
}
|
|
501
|
+
});
|
|
502
|
+
|
|
503
|
+
for (const [k, v] of Object.entries(parsedClientManifest)) {
|
|
504
|
+
const fileUrl = k.split("#")[0];
|
|
505
|
+
if (fileUrl.startsWith("file:///")) {
|
|
506
|
+
try {
|
|
507
|
+
const filePath = fileURLToPath(fileUrl);
|
|
508
|
+
if (fs.existsSync(filePath) && !isSupportedClientModule(filePath)) continue;
|
|
509
|
+
} catch (e) {}
|
|
510
|
+
}
|
|
511
|
+
const compIndex = clientComponents.findIndex(
|
|
512
|
+
(c) => pathToFileURL(c).href === fileUrl || pathToFileURL(c).href.toLowerCase() === fileUrl.toLowerCase()
|
|
513
|
+
);
|
|
514
|
+
if (compIndex !== -1) {
|
|
515
|
+
if (v && v.id) addClientModule(v.id, `mod_${compIndex}`);
|
|
516
|
+
addClientModule(fileUrl, `mod_${compIndex}`);
|
|
517
|
+
}
|
|
518
|
+
}
|
|
519
|
+
|
|
520
|
+
if (linkChunkId) {
|
|
521
|
+
const linkCompIndex = clientComponents.findIndex((c) => candidateLinkPaths.has(path.resolve(c)));
|
|
522
|
+
if (linkCompIndex !== -1) {
|
|
523
|
+
addClientModule(linkChunkId, `mod_${linkCompIndex}`);
|
|
524
|
+
for (const lp of candidateLinkPaths) {
|
|
525
|
+
for (const u of generateAllUrlVariants(lp)) {
|
|
526
|
+
addClientModule(u, `mod_${linkCompIndex}`);
|
|
527
|
+
}
|
|
528
|
+
}
|
|
529
|
+
}
|
|
530
|
+
}
|
|
531
|
+
|
|
532
|
+
if (redirectChunkId) {
|
|
533
|
+
const redirectCompIndex = clientComponents.findIndex((c) => candidateRedirectPaths.has(path.resolve(c)));
|
|
534
|
+
if (redirectCompIndex !== -1) {
|
|
535
|
+
addClientModule(redirectChunkId, `mod_${redirectCompIndex}`);
|
|
536
|
+
for (const rp of candidateRedirectPaths) {
|
|
537
|
+
for (const u of generateAllUrlVariants(rp)) {
|
|
538
|
+
addClientModule(u, `mod_${redirectCompIndex}`);
|
|
539
|
+
}
|
|
540
|
+
}
|
|
541
|
+
}
|
|
542
|
+
}
|
|
543
|
+
|
|
544
|
+
if (slotChunkId) {
|
|
545
|
+
const slotCompIndex = clientComponents.findIndex((c) => candidateSlotPaths.has(path.resolve(c)));
|
|
546
|
+
if (slotCompIndex !== -1) {
|
|
547
|
+
addClientModule(slotChunkId, `mod_${slotCompIndex}`);
|
|
548
|
+
for (const sp of candidateSlotPaths) {
|
|
549
|
+
for (const u of generateAllUrlVariants(sp)) {
|
|
550
|
+
addClientModule(u, `mod_${slotCompIndex}`);
|
|
551
|
+
}
|
|
552
|
+
}
|
|
553
|
+
}
|
|
554
|
+
}
|
|
555
|
+
|
|
556
|
+
serverFunctionFiles.forEach((relPath, index) => {
|
|
557
|
+
const normRel = relPath.replace(/\\/g, "/");
|
|
558
|
+
const relFileUrl = "file:///" + normRel;
|
|
559
|
+
const absPath = path.resolve(projectRoot, relPath);
|
|
560
|
+
const fullFileUrl = pathToFileURL(absPath).href;
|
|
561
|
+
const altFullFileUrl = fullFileUrl.replace(
|
|
562
|
+
/file:\/\/\/([a-zA-Z]):/,
|
|
563
|
+
(m, d) => "file:///" + (d === d.toLowerCase() ? d.toUpperCase() : d.toLowerCase()) + ":"
|
|
564
|
+
);
|
|
565
|
+
|
|
566
|
+
addClientModule(relFileUrl, `sf_${index}`);
|
|
567
|
+
addClientModule(fullFileUrl, `sf_${index}`);
|
|
568
|
+
if (altFullFileUrl !== fullFileUrl) {
|
|
569
|
+
addClientModule(altFullFileUrl, `sf_${index}`);
|
|
570
|
+
}
|
|
571
|
+
});
|
|
572
|
+
ssrManifestCode += `};\n\n`;
|
|
573
|
+
|
|
574
|
+
ssrManifestCode += `export const ssrConsumerManifest = {\n moduleMap: {\n`;
|
|
575
|
+
const emittedModuleMap = new Set();
|
|
576
|
+
function addModuleMap(key, valueObjStr) {
|
|
577
|
+
if (!emittedModuleMap.has(key)) {
|
|
578
|
+
emittedModuleMap.add(key);
|
|
579
|
+
ssrManifestCode += ` ${JSON.stringify(key)}: ${valueObjStr},\n`;
|
|
580
|
+
}
|
|
581
|
+
}
|
|
582
|
+
|
|
583
|
+
clientComponents.forEach((compPath) => {
|
|
584
|
+
const fileUrl = pathToFileURL(compPath).href;
|
|
585
|
+
const altFileUrl = fileUrl.replace(
|
|
586
|
+
/file:\/\/\/([a-zA-Z]):/,
|
|
587
|
+
(m, d) => "file:///" + (d === d.toLowerCase() ? d.toUpperCase() : d.toLowerCase()) + ":"
|
|
588
|
+
);
|
|
589
|
+
addModuleMap(fileUrl, `{ "*": { id: ${JSON.stringify(fileUrl)}, chunks: [], name: "*" } }`);
|
|
590
|
+
if (altFileUrl !== fileUrl) {
|
|
591
|
+
addModuleMap(altFileUrl, `{ "*": { id: ${JSON.stringify(fileUrl)}, chunks: [], name: "*" } }`);
|
|
592
|
+
}
|
|
593
|
+
});
|
|
594
|
+
|
|
595
|
+
for (const [k, v] of Object.entries(parsedClientManifest)) {
|
|
596
|
+
const fileUrl = k.split("#")[0];
|
|
597
|
+
if (fileUrl.startsWith("file:///")) {
|
|
598
|
+
try {
|
|
599
|
+
const filePath = fileURLToPath(fileUrl);
|
|
600
|
+
if (fs.existsSync(filePath) && !isSupportedClientModule(filePath)) continue;
|
|
601
|
+
} catch (e) {}
|
|
602
|
+
}
|
|
603
|
+
if (v && v.id) {
|
|
604
|
+
addModuleMap(v.id, `{ "*": { id: ${JSON.stringify(fileUrl)}, chunks: [], name: "*" } }`);
|
|
605
|
+
}
|
|
606
|
+
}
|
|
607
|
+
|
|
608
|
+
if (linkChunkId) {
|
|
609
|
+
const linkCompIndex = clientComponents.findIndex((c) => candidateLinkPaths.has(path.resolve(c)));
|
|
610
|
+
if (linkCompIndex !== -1) {
|
|
611
|
+
const fileUrl = pathToFileURL(clientComponents[linkCompIndex]).href;
|
|
612
|
+
addModuleMap(linkChunkId, `{ "*": { id: ${JSON.stringify(fileUrl)}, chunks: [], name: "*" } }`);
|
|
613
|
+
for (const lp of candidateLinkPaths) {
|
|
614
|
+
for (const u of generateAllUrlVariants(lp)) {
|
|
615
|
+
addModuleMap(u, `{ "*": { id: ${JSON.stringify(fileUrl)}, chunks: [], name: "*" } }`);
|
|
616
|
+
}
|
|
617
|
+
}
|
|
618
|
+
}
|
|
619
|
+
}
|
|
620
|
+
|
|
621
|
+
if (redirectChunkId) {
|
|
622
|
+
const redirectCompIndex = clientComponents.findIndex((c) => candidateRedirectPaths.has(path.resolve(c)));
|
|
623
|
+
if (redirectCompIndex !== -1) {
|
|
624
|
+
const fileUrl = pathToFileURL(clientComponents[redirectCompIndex]).href;
|
|
625
|
+
addModuleMap(redirectChunkId, `{ "*": { id: ${JSON.stringify(fileUrl)}, chunks: [], name: "*" } }`);
|
|
626
|
+
for (const rp of candidateRedirectPaths) {
|
|
627
|
+
for (const u of generateAllUrlVariants(rp)) {
|
|
628
|
+
addModuleMap(u, `{ "*": { id: ${JSON.stringify(fileUrl)}, chunks: [], name: "*" } }`);
|
|
629
|
+
}
|
|
630
|
+
}
|
|
631
|
+
}
|
|
632
|
+
}
|
|
633
|
+
|
|
634
|
+
if (slotChunkId) {
|
|
635
|
+
const slotCompIndex = clientComponents.findIndex((c) => candidateSlotPaths.has(path.resolve(c)));
|
|
636
|
+
if (slotCompIndex !== -1) {
|
|
637
|
+
const fileUrl = pathToFileURL(clientComponents[slotCompIndex]).href;
|
|
638
|
+
addModuleMap(slotChunkId, `{ "*": { id: ${JSON.stringify(fileUrl)}, chunks: [], name: "*" } }`);
|
|
639
|
+
for (const sp of candidateSlotPaths) {
|
|
640
|
+
for (const u of generateAllUrlVariants(sp)) {
|
|
641
|
+
addModuleMap(u, `{ "*": { id: ${JSON.stringify(fileUrl)}, chunks: [], name: "*" } }`);
|
|
642
|
+
}
|
|
643
|
+
}
|
|
644
|
+
}
|
|
645
|
+
}
|
|
646
|
+
|
|
647
|
+
ssrManifestCode += ` },\n serverModuleMap: {\n`;
|
|
648
|
+
const emittedServerModuleMap = new Set();
|
|
649
|
+
function addServerModuleMap(key, valueObjStr) {
|
|
650
|
+
if (!emittedServerModuleMap.has(key)) {
|
|
651
|
+
emittedServerModuleMap.add(key);
|
|
652
|
+
ssrManifestCode += ` ${JSON.stringify(key)}: ${valueObjStr},\n`;
|
|
653
|
+
}
|
|
654
|
+
}
|
|
655
|
+
|
|
656
|
+
serverFunctionFiles.forEach((relPath) => {
|
|
657
|
+
const normRel = relPath.replace(/\\/g, "/");
|
|
658
|
+
const relFileUrl = "file:///" + normRel;
|
|
659
|
+
const absPath = path.resolve(projectRoot, relPath);
|
|
660
|
+
const fullFileUrl = pathToFileURL(absPath).href;
|
|
661
|
+
const altFullFileUrl = fullFileUrl.replace(
|
|
662
|
+
/file:\/\/\/([a-zA-Z]):/,
|
|
663
|
+
(m, d) => "file:///" + (d === d.toLowerCase() ? d.toUpperCase() : d.toLowerCase()) + ":"
|
|
664
|
+
);
|
|
665
|
+
|
|
666
|
+
addServerModuleMap(relFileUrl, `{ id: ${JSON.stringify(relFileUrl)}, chunks: [], name: "*" }`);
|
|
667
|
+
addServerModuleMap(fullFileUrl, `{ id: ${JSON.stringify(relFileUrl)}, chunks: [], name: "*" }`);
|
|
668
|
+
if (altFullFileUrl !== fullFileUrl) {
|
|
669
|
+
addServerModuleMap(altFullFileUrl, `{ id: ${JSON.stringify(relFileUrl)}, chunks: [], name: "*" }`);
|
|
670
|
+
}
|
|
671
|
+
|
|
672
|
+
const fns = parsedServerFunctionsManifest[relPath] || [];
|
|
673
|
+
for (const fn of fns) {
|
|
674
|
+
addServerModuleMap(relFileUrl + "#" + fn, `{ id: ${JSON.stringify(relFileUrl)}, chunks: [], name: ${JSON.stringify(fn)} }`);
|
|
675
|
+
addServerModuleMap(fullFileUrl + "#" + fn, `{ id: ${JSON.stringify(relFileUrl)}, chunks: [], name: ${JSON.stringify(fn)} }`);
|
|
676
|
+
if (altFullFileUrl !== fullFileUrl) {
|
|
677
|
+
addServerModuleMap(altFullFileUrl + "#" + fn, `{ id: ${JSON.stringify(relFileUrl)}, chunks: [], name: ${JSON.stringify(fn)} }`);
|
|
678
|
+
}
|
|
679
|
+
}
|
|
680
|
+
});
|
|
681
|
+
|
|
682
|
+
ssrManifestCode += ` },\n moduleLoading: null,\n};\n`;
|
|
683
|
+
const ssrClientManifestPath = path.join(nodeDir, "ssr-client-manifest.mjs");
|
|
684
|
+
fs.writeFileSync(ssrClientManifestPath, ssrManifestCode, "utf8");
|
|
685
|
+
|
|
686
|
+
// 6. Entry Files Generation
|
|
687
|
+
const rscEntryContent = `// Auto-generated RSC Engine entry for Node.js (Dual-Bundle)
|
|
688
|
+
import "./env-setup.mjs";
|
|
689
|
+
import "./route-modules.mjs";
|
|
690
|
+
export { handleRequest } from "${dinouDirSlash}/core/handler.js";
|
|
691
|
+
export { buildStaticPages, getStaticPaths, getStaticLayouts, getStaticMetadata } from "${dinouDirSlash}/core/build-static-pages.js";
|
|
692
|
+
`;
|
|
693
|
+
const rscEntryPath = path.join(nodeDir, "rsc-entry.mjs");
|
|
694
|
+
fs.writeFileSync(rscEntryPath, rscEntryContent, "utf8");
|
|
695
|
+
|
|
696
|
+
const ssrEntryContent = `// Auto-generated SSR Engine entry for Node.js (Dual-Bundle)
|
|
697
|
+
import { renderRscStreamToHtmlStream } from "${dinouDirSlash}/core/edge-ssr.js";
|
|
698
|
+
import { clientModules, ssrConsumerManifest } from "./ssr-client-manifest.mjs";
|
|
699
|
+
|
|
700
|
+
const lowerMap = new Map();
|
|
701
|
+
globalThis.__webpack_require__ = (id) => {
|
|
702
|
+
if (clientModules[id]) return clientModules[id];
|
|
703
|
+
if (typeof id === "string") {
|
|
704
|
+
if (lowerMap.size === 0) {
|
|
705
|
+
for (const k of Object.keys(clientModules)) lowerMap.set(k.toLowerCase(), clientModules[k]);
|
|
706
|
+
}
|
|
707
|
+
const lowerVal = lowerMap.get(id.toLowerCase());
|
|
708
|
+
if (lowerVal) return lowerVal;
|
|
709
|
+
}
|
|
710
|
+
console.error("[SSR Engine] Module not found in __webpack_require__:", id);
|
|
711
|
+
return {};
|
|
712
|
+
};
|
|
713
|
+
globalThis.__webpack_require__.u = (chunkId) => "" + chunkId + ".js";
|
|
714
|
+
globalThis.__webpack_chunk_load__ = () => Promise.resolve();
|
|
715
|
+
|
|
716
|
+
export async function renderHtml(rscStream, options = {}) {
|
|
717
|
+
return renderRscStreamToHtmlStream(rscStream, ssrConsumerManifest, options);
|
|
718
|
+
}
|
|
719
|
+
`;
|
|
720
|
+
const ssrEntryPath = path.join(nodeDir, "ssr-entry.mjs");
|
|
721
|
+
fs.writeFileSync(ssrEntryPath, ssrEntryContent, "utf8");
|
|
722
|
+
|
|
723
|
+
// 7. Esbuild Configuration & Compilation
|
|
724
|
+
const nodeBuiltins = [
|
|
725
|
+
"assert", "async_hooks", "buffer", "child_process", "cluster", "console",
|
|
726
|
+
"constants", "crypto", "dgram", "diagnostics_channel", "dns", "domain",
|
|
727
|
+
"events", "fs", "fs/promises", "http", "http2", "https", "inspector",
|
|
728
|
+
"module", "net", "os", "path", "path/posix", "path/win32", "perf_hooks",
|
|
729
|
+
"process", "punycode", "querystring", "readline", "repl", "stream",
|
|
730
|
+
"stream/consumers", "stream/promises", "stream/web", "string_decoder",
|
|
731
|
+
"timers", "timers/promises", "tls", "trace_events", "tty", "url",
|
|
732
|
+
"util", "util/types", "v8", "vm", "wasi", "worker_threads", "zlib"
|
|
733
|
+
];
|
|
734
|
+
const externalList = [...nodeBuiltins, ...nodeBuiltins.map((b) => "node:" + b)];
|
|
735
|
+
|
|
736
|
+
const commonAlias = {
|
|
737
|
+
"@": path.resolve(projectRoot, "src"),
|
|
738
|
+
"dinou/config": path.resolve(dinouDir, "core/config.js"),
|
|
739
|
+
dinou: dinouDir,
|
|
740
|
+
};
|
|
741
|
+
const commonLoader = {
|
|
742
|
+
".js": "jsx", ".jsx": "jsx", ".ts": "ts", ".tsx": "tsx",
|
|
743
|
+
".json": "json", ".css": "empty",
|
|
744
|
+
};
|
|
745
|
+
|
|
746
|
+
const serverAssetPlugin = {
|
|
747
|
+
name: "dinou-server-asset-plugin",
|
|
748
|
+
setup(build) {
|
|
749
|
+
build.onResolve({ filter: assetRegex }, (args) => {
|
|
750
|
+
let resolvedPath;
|
|
751
|
+
if (args.path.startsWith("@/")) {
|
|
752
|
+
resolvedPath = path.resolve(projectRoot, "src", args.path.slice(2));
|
|
753
|
+
} else if (path.isAbsolute(args.path)) {
|
|
754
|
+
resolvedPath = args.path;
|
|
755
|
+
} else {
|
|
756
|
+
resolvedPath = path.resolve(args.resolveDir, args.path);
|
|
757
|
+
}
|
|
758
|
+
return { path: resolvedPath, namespace: "dinou-server-asset" };
|
|
759
|
+
});
|
|
760
|
+
|
|
761
|
+
build.onLoad({ filter: /.*/, namespace: "dinou-server-asset" }, (args) => {
|
|
762
|
+
const ext = path.extname(args.path);
|
|
763
|
+
const base = path.basename(args.path, ext);
|
|
764
|
+
const scoped = createScopedName(base, args.path);
|
|
765
|
+
const assetUrl = `/assets/${scoped}${ext}`;
|
|
766
|
+
|
|
767
|
+
return {
|
|
768
|
+
contents: `export default ${JSON.stringify(assetUrl)};`,
|
|
769
|
+
loader: "js",
|
|
770
|
+
};
|
|
771
|
+
});
|
|
772
|
+
},
|
|
773
|
+
};
|
|
774
|
+
|
|
775
|
+
const banner = {
|
|
776
|
+
js: `import { createRequire as ___createRequire } from 'node:module';
|
|
777
|
+
import { AsyncLocalStorage as ___AsyncLocalStorage } from 'node:async_hooks';
|
|
778
|
+
const require = ___createRequire(import.meta.url || 'file:///server.js');
|
|
779
|
+
const __dirname = '';
|
|
780
|
+
const __filename = '';
|
|
781
|
+
globalThis.__dinou_require__ = require;
|
|
782
|
+
if (typeof globalThis.AsyncLocalStorage === 'undefined' && typeof ___AsyncLocalStorage !== 'undefined') {
|
|
783
|
+
globalThis.AsyncLocalStorage = ___AsyncLocalStorage;
|
|
784
|
+
}
|
|
785
|
+
if (typeof globalThis.__webpack_require__ === 'undefined') {
|
|
786
|
+
globalThis.__webpack_require__ = function(id) {
|
|
787
|
+
if (globalThis.__webpack_modules__ && globalThis.__webpack_modules__[id]) {
|
|
788
|
+
return globalThis.__webpack_modules__[id];
|
|
789
|
+
}
|
|
790
|
+
return {};
|
|
791
|
+
};
|
|
792
|
+
}
|
|
793
|
+
if (typeof globalThis.__webpack_require__.u === 'undefined') {
|
|
794
|
+
globalThis.__webpack_require__.u = function(chunkId) { return '' + chunkId + '.js'; };
|
|
795
|
+
}
|
|
796
|
+
if (typeof globalThis.__webpack_chunk_load__ === 'undefined') {
|
|
797
|
+
globalThis.__webpack_chunk_load__ = () => Promise.resolve();
|
|
798
|
+
}
|
|
799
|
+
var __webpack_require__ = function(id) {
|
|
800
|
+
return globalThis.__webpack_require__ ? globalThis.__webpack_require__(id) : {};
|
|
801
|
+
};
|
|
802
|
+
__webpack_require__.u = function(chunkId) {
|
|
803
|
+
return (globalThis.__webpack_require__ && globalThis.__webpack_require__.u)
|
|
804
|
+
? globalThis.__webpack_require__.u(chunkId)
|
|
805
|
+
: '' + chunkId + '.js';
|
|
806
|
+
};
|
|
807
|
+
var __webpack_chunk_load__ = function(chunkId) {
|
|
808
|
+
return globalThis.__webpack_chunk_load__ ? globalThis.__webpack_chunk_load__(chunkId) : Promise.resolve();
|
|
809
|
+
};
|
|
810
|
+
`,
|
|
811
|
+
};
|
|
812
|
+
|
|
813
|
+
const clientReferencesPlugin = {
|
|
814
|
+
name: "dinou-client-references",
|
|
815
|
+
setup(build) {
|
|
816
|
+
build.onLoad({ filter: /\.[jt]sx?$/ }, async (args) => {
|
|
817
|
+
if (args.path.includes("node_modules")) {
|
|
818
|
+
if (
|
|
819
|
+
args.path.includes("react-server-dom") ||
|
|
820
|
+
args.path.includes("@roggc/react-server-dom-esm") ||
|
|
821
|
+
args.path.includes("node_modules/react/") ||
|
|
822
|
+
args.path.includes("node_modules\\react\\") ||
|
|
823
|
+
args.path.includes("node_modules/react-dom/") ||
|
|
824
|
+
args.path.includes("node_modules\\react-dom\\")
|
|
825
|
+
) {
|
|
826
|
+
return null;
|
|
827
|
+
}
|
|
828
|
+
}
|
|
829
|
+
const normalizedPath = args.path.replace(/\\/g, "/");
|
|
830
|
+
if (normalizedPath.includes("dinou/core/navigation")) return null;
|
|
831
|
+
|
|
832
|
+
let code;
|
|
833
|
+
try { code = fs.readFileSync(args.path, "utf8"); } catch (e) { return null; }
|
|
834
|
+
if (!isSupportedClientModule(args.path, code)) return null;
|
|
835
|
+
if (!useClientRegex.test(code.trim())) return null;
|
|
836
|
+
|
|
837
|
+
const exports = parseExports(code);
|
|
838
|
+
const absPath = path.resolve(args.path);
|
|
839
|
+
const fileUrl = pathToFileURL(absPath).href;
|
|
840
|
+
|
|
841
|
+
let proxyCode = `import { createClientModuleProxy } from "react-server-dom-webpack/server.edge";\n`;
|
|
842
|
+
proxyCode += `const proxy = createClientModuleProxy(${JSON.stringify(fileUrl)});\n`;
|
|
843
|
+
|
|
844
|
+
for (const name of exports) {
|
|
845
|
+
if (name === "default") {
|
|
846
|
+
proxyCode += `export default proxy.default;\n`;
|
|
847
|
+
} else {
|
|
848
|
+
proxyCode += `export const ${name} = proxy[${JSON.stringify(name)}];\n`;
|
|
849
|
+
}
|
|
850
|
+
}
|
|
851
|
+
|
|
852
|
+
if (!exports.includes("default")) {
|
|
853
|
+
proxyCode += `export default proxy.default;\n`;
|
|
854
|
+
}
|
|
855
|
+
|
|
856
|
+
return { contents: proxyCode, loader: "js" };
|
|
857
|
+
});
|
|
858
|
+
},
|
|
859
|
+
};
|
|
860
|
+
|
|
861
|
+
const serverReferencesPlugin = {
|
|
862
|
+
name: "dinou-server-references",
|
|
863
|
+
setup(build) {
|
|
864
|
+
build.onLoad({ filter: /\.[jt]sx?$/ }, async (args) => {
|
|
865
|
+
if (args.path.includes("node_modules")) {
|
|
866
|
+
if (
|
|
867
|
+
args.path.includes("react-server-dom") ||
|
|
868
|
+
args.path.includes("@roggc/react-server-dom-esm") ||
|
|
869
|
+
args.path.includes("node_modules/react/") ||
|
|
870
|
+
args.path.includes("node_modules\\react\\") ||
|
|
871
|
+
args.path.includes("node_modules/react-dom/") ||
|
|
872
|
+
args.path.includes("node_modules\\react-dom\\")
|
|
873
|
+
) {
|
|
874
|
+
return null;
|
|
875
|
+
}
|
|
876
|
+
}
|
|
877
|
+
let code;
|
|
878
|
+
try { code = fs.readFileSync(args.path, "utf8"); } catch (e) { return null; }
|
|
879
|
+
if (!useServerRegex.test(code.trim())) return null;
|
|
880
|
+
|
|
881
|
+
const exports = parseExports(code);
|
|
882
|
+
const absPath = path.resolve(args.path);
|
|
883
|
+
const relPath = path.relative(projectRoot, absPath).replace(/\\/g, "/");
|
|
884
|
+
const relativeFileUrl = "file:///" + relPath;
|
|
885
|
+
|
|
886
|
+
let transformed = code + "\n\n";
|
|
887
|
+
transformed += `import { registerServerReference } from "react-server-dom-webpack/server.edge";\n`;
|
|
888
|
+
for (const name of exports) {
|
|
889
|
+
if (name !== "default") {
|
|
890
|
+
transformed += `registerServerReference(${name}, ${JSON.stringify(relativeFileUrl)}, ${JSON.stringify(name)});\n`;
|
|
891
|
+
}
|
|
892
|
+
}
|
|
893
|
+
|
|
894
|
+
const ext = path.extname(args.path);
|
|
895
|
+
const loader = ext === ".ts" ? "ts" : ext === ".tsx" ? "tsx" : ext === ".jsx" ? "jsx" : "js";
|
|
896
|
+
return { contents: transformed, loader };
|
|
897
|
+
});
|
|
898
|
+
},
|
|
899
|
+
};
|
|
900
|
+
|
|
901
|
+
const serverReferencesPluginSsr = {
|
|
902
|
+
name: "dinou-server-references-ssr",
|
|
903
|
+
setup(build) {
|
|
904
|
+
build.onLoad({ filter: /\.[jt]sx?$/ }, async (args) => {
|
|
905
|
+
if (args.path.includes("node_modules")) {
|
|
906
|
+
if (
|
|
907
|
+
args.path.includes("react-server-dom") ||
|
|
908
|
+
args.path.includes("@roggc/react-server-dom-esm") ||
|
|
909
|
+
args.path.includes("node_modules/react/") ||
|
|
910
|
+
args.path.includes("node_modules\\react\\") ||
|
|
911
|
+
args.path.includes("node_modules/react-dom/") ||
|
|
912
|
+
args.path.includes("node_modules\\react-dom\\")
|
|
913
|
+
) {
|
|
914
|
+
return null;
|
|
915
|
+
}
|
|
916
|
+
}
|
|
917
|
+
let code;
|
|
918
|
+
try { code = fs.readFileSync(args.path, "utf8"); } catch (e) { return null; }
|
|
919
|
+
if (!useServerRegex.test(code.trim())) return null;
|
|
920
|
+
|
|
921
|
+
const exports = parseExports(code);
|
|
922
|
+
const absPath = path.resolve(args.path);
|
|
923
|
+
const relPath = path.relative(projectRoot, absPath).replace(/\\/g, "/");
|
|
924
|
+
const relativeFileUrl = "file:///" + relPath;
|
|
925
|
+
|
|
926
|
+
let transformed = code + "\n\n";
|
|
927
|
+
transformed += `import { registerServerReference } from "react-server-dom-webpack/client.edge";\n`;
|
|
928
|
+
for (const name of exports) {
|
|
929
|
+
if (name !== "default") {
|
|
930
|
+
transformed += `registerServerReference(${name}, ${JSON.stringify(relativeFileUrl + "#" + name)});\n`;
|
|
931
|
+
transformed += `registerServerReference(${name}, ${JSON.stringify(pathToFileURL(absPath).href + "#" + name)});\n`;
|
|
932
|
+
}
|
|
933
|
+
}
|
|
934
|
+
|
|
935
|
+
const ext = path.extname(args.path);
|
|
936
|
+
const loader = ext === ".ts" ? "ts" : ext === ".tsx" ? "tsx" : ext === ".jsx" ? "jsx" : "js";
|
|
937
|
+
return { contents: transformed, loader };
|
|
938
|
+
});
|
|
939
|
+
},
|
|
940
|
+
};
|
|
941
|
+
|
|
942
|
+
const rscOutfile = path.join(nodeDir, "rsc-engine.mjs");
|
|
943
|
+
const ssrOutfile = path.join(nodeDir, "ssr-engine.mjs");
|
|
944
|
+
|
|
945
|
+
const buildEnv = isDev ? "development" : "production";
|
|
946
|
+
|
|
947
|
+
// Pass A: RSC Engine
|
|
948
|
+
await esbuild.build({
|
|
949
|
+
entryPoints: [rscEntryPath],
|
|
950
|
+
outfile: rscOutfile,
|
|
951
|
+
bundle: true,
|
|
952
|
+
format: "esm",
|
|
953
|
+
target: "node20",
|
|
954
|
+
platform: "node",
|
|
955
|
+
mainFields: ["module", "main"],
|
|
956
|
+
conditions: ["node", "worker", "react-server"],
|
|
957
|
+
external: externalList,
|
|
958
|
+
banner,
|
|
959
|
+
plugins: [clientReferencesPlugin, serverReferencesPlugin, serverAssetPlugin],
|
|
960
|
+
alias: commonAlias,
|
|
961
|
+
loader: commonLoader,
|
|
962
|
+
jsx: "automatic",
|
|
963
|
+
define: {
|
|
964
|
+
"process.env.NODE_ENV": JSON.stringify(buildEnv),
|
|
965
|
+
"process.env.DINOU_RUNTIME": '"node-bundle"',
|
|
966
|
+
"process.env.DINOU_BUILD_TOOL": JSON.stringify(isWebpackBuild ? "webpack" : (process.env.DINOU_BUILD_TOOL || "esbuild")),
|
|
967
|
+
},
|
|
968
|
+
logLevel: "warning",
|
|
969
|
+
});
|
|
970
|
+
|
|
971
|
+
// Pass B: SSR Engine
|
|
972
|
+
await esbuild.build({
|
|
973
|
+
entryPoints: [ssrEntryPath],
|
|
974
|
+
outfile: ssrOutfile,
|
|
975
|
+
bundle: true,
|
|
976
|
+
format: "esm",
|
|
977
|
+
target: "node20",
|
|
978
|
+
platform: "node",
|
|
979
|
+
mainFields: ["module", "main"],
|
|
980
|
+
conditions: ["node", "worker", "browser"],
|
|
981
|
+
external: externalList,
|
|
982
|
+
banner,
|
|
983
|
+
plugins: [serverReferencesPluginSsr, serverAssetPlugin],
|
|
984
|
+
alias: commonAlias,
|
|
985
|
+
loader: commonLoader,
|
|
986
|
+
jsx: "automatic",
|
|
987
|
+
define: {
|
|
988
|
+
"process.env.NODE_ENV": JSON.stringify(buildEnv),
|
|
989
|
+
"process.env.DINOU_RUNTIME": '"node-bundle"',
|
|
990
|
+
"process.env.DINOU_BUILD_TOOL": JSON.stringify(isWebpackBuild ? "webpack" : (process.env.DINOU_BUILD_TOOL || "esbuild")),
|
|
991
|
+
},
|
|
992
|
+
logLevel: "warning",
|
|
993
|
+
});
|
|
994
|
+
|
|
995
|
+
return {
|
|
996
|
+
nodeDir,
|
|
997
|
+
rscEnginePath: rscOutfile,
|
|
998
|
+
ssrEnginePath: ssrOutfile,
|
|
999
|
+
rscEntryPath,
|
|
1000
|
+
ssrEntryPath,
|
|
1001
|
+
parsedClientManifest,
|
|
1002
|
+
clientComponents,
|
|
1003
|
+
dinouDirSlash,
|
|
1004
|
+
externalList,
|
|
1005
|
+
banner,
|
|
1006
|
+
commonAlias,
|
|
1007
|
+
commonLoader,
|
|
1008
|
+
isWebpackBuild,
|
|
1009
|
+
};
|
|
1010
|
+
}
|
|
1011
|
+
|
|
1012
|
+
export default { bundleDualEngine };
|