dinou 6.1.1 → 7.1.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 +106 -0
- package/README.md +9352 -1081
- package/cli.js +314 -91
- 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 +322 -165
- 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 +175 -96
- package/dinou/core/handler.js +2303 -0
- package/dinou/core/http-adapter.js +115 -0
- package/dinou/core/import-module.js +83 -53
- package/dinou/core/layout-functions.js +183 -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 +252 -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 +91 -87
- package/dinou/core/request-context.js +47 -13
- package/dinou/core/resolve-module-ppr.js +34 -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 +16 -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 +113 -34
- package/package.json +18 -16
- 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
|
@@ -3,16 +3,49 @@ const path = require("path");
|
|
|
3
3
|
const { dirname } = require("path");
|
|
4
4
|
const glob = require("fast-glob");
|
|
5
5
|
const { pathToFileURL } = require("url");
|
|
6
|
+
let swc = null;
|
|
7
|
+
try {
|
|
8
|
+
swc = require("@swc/core");
|
|
9
|
+
} catch (e) {}
|
|
6
10
|
const parser = require("@babel/parser");
|
|
7
|
-
const
|
|
11
|
+
const _traverseRaw = require("@babel/traverse");
|
|
12
|
+
const traverse = typeof _traverseRaw === "function" ? _traverseRaw : (_traverseRaw.default || _traverseRaw);
|
|
8
13
|
const { regex } = require("../../core/asset-extensions.js");
|
|
9
14
|
const createScopedName = require("../../core/createScopedName.js");
|
|
10
15
|
const { getAbsPathWithExt } = require("../../core/get-abs-path-with-ext.js");
|
|
11
|
-
const { useClientRegex } = require("../../constants.js");
|
|
16
|
+
const { useClientRegex, useServerRegex } = require("../../constants.js");
|
|
12
17
|
const parseExports = require("../../core/parse-exports.js");
|
|
13
18
|
|
|
14
19
|
function getDefaultExportName(code) {
|
|
15
20
|
let name = null;
|
|
21
|
+
if (swc && typeof swc.parseSync === "function") {
|
|
22
|
+
try {
|
|
23
|
+
const ast = swc.parseSync(code, { syntax: "typescript", tsx: true });
|
|
24
|
+
for (const item of ast.body) {
|
|
25
|
+
if (item.type === "ExportDefaultDeclaration") {
|
|
26
|
+
const d = item.decl;
|
|
27
|
+
if (d) {
|
|
28
|
+
if (d.type === "Identifier") {
|
|
29
|
+
name = d.value;
|
|
30
|
+
} else if (
|
|
31
|
+
(d.type === "FunctionDeclaration" || d.type === "ClassDeclaration") &&
|
|
32
|
+
d.identifier?.value
|
|
33
|
+
) {
|
|
34
|
+
name = d.identifier.value;
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
break;
|
|
38
|
+
} else if (item.type === "ExportDefaultExpression") {
|
|
39
|
+
if (item.expression?.type === "Identifier") {
|
|
40
|
+
name = item.expression.value;
|
|
41
|
+
break;
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
if (name) return name;
|
|
46
|
+
} catch (e) {}
|
|
47
|
+
}
|
|
48
|
+
|
|
16
49
|
try {
|
|
17
50
|
const ast = parser.parse(code, {
|
|
18
51
|
sourceType: "module",
|
|
@@ -41,32 +74,121 @@ function reactClientManifestPlugin({
|
|
|
41
74
|
srcDir = path.resolve("src"),
|
|
42
75
|
manifestPath = ".dinou/react_client_manifest/react-client-manifest.json",
|
|
43
76
|
assetInclude = regex,
|
|
77
|
+
serverFiles = new Set(),
|
|
44
78
|
} = {}) {
|
|
45
79
|
const manifest = {};
|
|
46
80
|
const clientModules = new Set();
|
|
47
81
|
const serverModules = new Set();
|
|
82
|
+
let lastManifest = null;
|
|
83
|
+
|
|
84
|
+
const urlToManifestKeys = new Map();
|
|
85
|
+
const defaultExportCache = new Map();
|
|
86
|
+
|
|
87
|
+
function normalizeFsPath(p) {
|
|
88
|
+
return path.resolve(p).replace(/\\/g, "/").toLowerCase();
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
function alignDrive(p) {
|
|
92
|
+
const resolved = path.resolve(p);
|
|
93
|
+
const cwd = process.cwd();
|
|
94
|
+
if (process.platform === "win32" && resolved.length > 2 && resolved[1] === ":" && cwd.length > 2 && cwd[1] === ":") {
|
|
95
|
+
return cwd[0] + resolved.slice(1);
|
|
96
|
+
}
|
|
97
|
+
return resolved;
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
function getStableChunkName(absPath) {
|
|
101
|
+
const norm = absPath.replace(/\\/g, "/");
|
|
102
|
+
if (norm.endsWith("/core/client-redirect.jsx") || norm.endsWith("/core/client-redirect.js")) {
|
|
103
|
+
return "dinouClientRedirect";
|
|
104
|
+
}
|
|
105
|
+
if (norm.endsWith("/core/link.jsx") || norm.endsWith("/core/link.js")) {
|
|
106
|
+
return "dinouLink";
|
|
107
|
+
}
|
|
108
|
+
if (norm.endsWith("/core/slot.js")) {
|
|
109
|
+
return "dinouSlot";
|
|
110
|
+
}
|
|
111
|
+
const rel = path.relative(process.cwd(), absPath).replace(/\\/g, "/");
|
|
112
|
+
const clean = rel
|
|
113
|
+
.replace(/\.[jt]sx?$/, "")
|
|
114
|
+
.replace(/^src\//, "")
|
|
115
|
+
.replace(/[^a-zA-Z0-9_-]/g, "_");
|
|
116
|
+
return clean || path.basename(absPath, path.extname(absPath));
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
function normalizeFileUrl(p) {
|
|
120
|
+
const abs = path.resolve(p);
|
|
121
|
+
const norm =
|
|
122
|
+
abs.length > 1 && abs[1] === ":"
|
|
123
|
+
? abs[0].toUpperCase() + abs.slice(1)
|
|
124
|
+
: abs;
|
|
125
|
+
return pathToFileURL(norm).href;
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
function setManifestEntry(fileUrl, expName, entry) {
|
|
129
|
+
const keys = [expName === "default" ? fileUrl : `${fileUrl}#${expName}`];
|
|
130
|
+
if (process.platform === "win32") {
|
|
131
|
+
const winMatch = fileUrl.match(/^file:\/\/\/([a-zA-Z]):(\/.*)$/);
|
|
132
|
+
if (winMatch) {
|
|
133
|
+
const drive = winMatch[1];
|
|
134
|
+
const altDrive = drive === drive.toLowerCase() ? drive.toUpperCase() : drive.toLowerCase();
|
|
135
|
+
const altUrl = `file:///${altDrive}:${winMatch[2]}`;
|
|
136
|
+
keys.push(expName === "default" ? altUrl : `${altUrl}#${expName}`);
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
const fileUrlLower = fileUrl.toLowerCase();
|
|
140
|
+
let keySet = urlToManifestKeys.get(fileUrlLower);
|
|
141
|
+
if (!keySet) {
|
|
142
|
+
keySet = new Set();
|
|
143
|
+
urlToManifestKeys.set(fileUrlLower, keySet);
|
|
144
|
+
}
|
|
145
|
+
for (const k of keys) {
|
|
146
|
+
manifest[k] = { ...entry };
|
|
147
|
+
keySet.add(k);
|
|
148
|
+
}
|
|
149
|
+
}
|
|
48
150
|
|
|
49
151
|
function updateManifestForModule(absPath, code, isClientModule) {
|
|
50
|
-
const fileUrl =
|
|
51
|
-
const
|
|
52
|
-
|
|
152
|
+
const fileUrl = normalizeFileUrl(absPath);
|
|
153
|
+
const fileUrlLower = fileUrl.toLowerCase();
|
|
154
|
+
const stableChunkName = getStableChunkName(absPath);
|
|
155
|
+
const stableChunkUrl = "/" + stableChunkName + ".js";
|
|
53
156
|
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
157
|
+
// Delete previous keys from manifest and determine if an existing chunk ID should be kept
|
|
158
|
+
const oldKeys = urlToManifestKeys.get(fileUrlLower);
|
|
159
|
+
let previousChunkId = null;
|
|
160
|
+
if (oldKeys) {
|
|
161
|
+
for (const k of oldKeys) {
|
|
162
|
+
if (!previousChunkId && manifest[k]?.id && manifest[k].id.startsWith("/")) {
|
|
163
|
+
previousChunkId = manifest[k].id;
|
|
164
|
+
}
|
|
165
|
+
delete manifest[k];
|
|
166
|
+
}
|
|
167
|
+
oldKeys.clear();
|
|
168
|
+
} else {
|
|
169
|
+
for (const key in manifest) {
|
|
170
|
+
if (key.toLowerCase().startsWith(fileUrlLower)) {
|
|
171
|
+
if (!previousChunkId && manifest[key]?.id && manifest[key].id.startsWith("/")) {
|
|
172
|
+
previousChunkId = manifest[key].id;
|
|
173
|
+
}
|
|
174
|
+
delete manifest[key];
|
|
175
|
+
}
|
|
57
176
|
}
|
|
58
177
|
}
|
|
59
178
|
|
|
60
179
|
if (isClientModule) {
|
|
61
180
|
const exports = parseExports(code);
|
|
181
|
+
const defaultName = getDefaultExportName(code);
|
|
182
|
+
if (defaultName) {
|
|
183
|
+
defaultExportCache.set(path.resolve(absPath), defaultName);
|
|
184
|
+
}
|
|
185
|
+
const chunkIdToUse = previousChunkId || stableChunkUrl;
|
|
62
186
|
for (const expName of exports) {
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
manifest[manifestKey] = {
|
|
66
|
-
id: relPath,
|
|
187
|
+
setManifestEntry(fileUrl, expName, {
|
|
188
|
+
id: chunkIdToUse,
|
|
67
189
|
chunks: expName,
|
|
68
190
|
name: expName,
|
|
69
|
-
};
|
|
191
|
+
});
|
|
70
192
|
}
|
|
71
193
|
}
|
|
72
194
|
}
|
|
@@ -76,31 +198,51 @@ function reactClientManifestPlugin({
|
|
|
76
198
|
code,
|
|
77
199
|
baseFilePath,
|
|
78
200
|
visited = new Set(),
|
|
79
|
-
pluginContext
|
|
201
|
+
pluginContext,
|
|
202
|
+
sharedCache = null
|
|
80
203
|
) {
|
|
81
204
|
if (visited.has(baseFilePath)) {
|
|
82
205
|
return { imports: [], assets: [], csss: [] };
|
|
83
206
|
}
|
|
84
207
|
visited.add(baseFilePath);
|
|
85
208
|
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
209
|
+
if (sharedCache && sharedCache.has(baseFilePath)) {
|
|
210
|
+
return sharedCache.get(baseFilePath);
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
const importSources = [];
|
|
214
|
+
if (swc && typeof swc.parseSync === "function") {
|
|
215
|
+
try {
|
|
216
|
+
const ast = swc.parseSync(code, { syntax: "typescript", tsx: true });
|
|
217
|
+
for (const item of ast.body) {
|
|
218
|
+
if (item.type === "ImportDeclaration" && item.source?.value) {
|
|
219
|
+
importSources.push(item.source.value);
|
|
220
|
+
}
|
|
221
|
+
}
|
|
222
|
+
} catch (e) {}
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
if (importSources.length === 0 && (!swc || typeof swc.parseSync !== "function")) {
|
|
226
|
+
try {
|
|
227
|
+
const ast = parser.parse(code, {
|
|
228
|
+
sourceType: "module",
|
|
229
|
+
plugins: ["jsx", "typescript"],
|
|
230
|
+
});
|
|
231
|
+
traverse(ast, {
|
|
232
|
+
ImportDeclaration(nodePath) {
|
|
233
|
+
if (nodePath.node?.source?.value) {
|
|
234
|
+
importSources.push(nodePath.node.source.value);
|
|
235
|
+
}
|
|
236
|
+
},
|
|
237
|
+
});
|
|
238
|
+
} catch (e) {}
|
|
239
|
+
}
|
|
240
|
+
|
|
90
241
|
const imports = new Set();
|
|
91
242
|
const assets = new Set();
|
|
92
243
|
const csss = new Set();
|
|
93
244
|
|
|
94
|
-
|
|
95
|
-
const importNodes = [];
|
|
96
|
-
traverse(ast, {
|
|
97
|
-
ImportDeclaration(nodePath) {
|
|
98
|
-
importNodes.push(nodePath);
|
|
99
|
-
},
|
|
100
|
-
});
|
|
101
|
-
|
|
102
|
-
for (const nodePath of importNodes) {
|
|
103
|
-
const source = nodePath.node.source.value;
|
|
245
|
+
for (const source of importSources) {
|
|
104
246
|
// console.log("source", source);
|
|
105
247
|
|
|
106
248
|
// Resolve the import
|
|
@@ -128,16 +270,29 @@ function reactClientManifestPlugin({
|
|
|
128
270
|
continue; // Don't recurse for assets
|
|
129
271
|
}
|
|
130
272
|
|
|
273
|
+
// Do not recurse into third-party libraries in node_modules for server asset discovery
|
|
274
|
+
if (absImportPathWithExt.includes("node_modules")) {
|
|
275
|
+
continue;
|
|
276
|
+
}
|
|
277
|
+
|
|
131
278
|
// Otherwise, it's a code import
|
|
132
279
|
imports.add(absImportPathWithExt);
|
|
133
280
|
|
|
134
281
|
try {
|
|
135
282
|
const importCode = readFileSync(absImportPathWithExt, "utf8");
|
|
283
|
+
// Optimization: stop recursing at Client Component boundaries.
|
|
284
|
+
// Everything inside a "use client" module is bundled by Rollup's client pipeline,
|
|
285
|
+
// and dinou-asset-plugin already emits all assets referenced by client modules.
|
|
286
|
+
if (useClientRegex.test(importCode.trim())) {
|
|
287
|
+
continue;
|
|
288
|
+
}
|
|
289
|
+
|
|
136
290
|
const nested = await getImportsAndAssetsAndCsss(
|
|
137
291
|
importCode,
|
|
138
292
|
absImportPathWithExt,
|
|
139
293
|
visited,
|
|
140
|
-
pluginContext
|
|
294
|
+
pluginContext,
|
|
295
|
+
sharedCache
|
|
141
296
|
);
|
|
142
297
|
nested.imports.forEach((nestedPath) => imports.add(nestedPath));
|
|
143
298
|
nested.assets.forEach((nestedPath) => assets.add(nestedPath));
|
|
@@ -150,20 +305,27 @@ function reactClientManifestPlugin({
|
|
|
150
305
|
}
|
|
151
306
|
}
|
|
152
307
|
|
|
153
|
-
|
|
308
|
+
const result = {
|
|
154
309
|
imports: Array.from(imports),
|
|
155
310
|
assets: Array.from(assets),
|
|
156
311
|
csss: Array.from(csss),
|
|
157
312
|
};
|
|
313
|
+
if (sharedCache) {
|
|
314
|
+
sharedCache.set(baseFilePath, result);
|
|
315
|
+
}
|
|
316
|
+
return result;
|
|
158
317
|
}
|
|
159
318
|
|
|
160
319
|
// New helper to emit a single asset (used in buildStart and watchChange)
|
|
161
320
|
function emitAsset(absAssetPath, pluginContext) {
|
|
162
|
-
const source = readFileSync(absAssetPath);
|
|
163
321
|
const base = path.basename(absAssetPath, path.extname(absAssetPath));
|
|
164
322
|
const scoped = createScopedName(base, absAssetPath);
|
|
165
323
|
const ext = path.extname(absAssetPath);
|
|
166
324
|
const fileName = `assets/${scoped}${ext}`;
|
|
325
|
+
const emitted = globalThis.__DINOU_EMITTED_ASSETS__ || (globalThis.__DINOU_EMITTED_ASSETS__ = new Set());
|
|
326
|
+
if (emitted.has(fileName)) return;
|
|
327
|
+
emitted.add(fileName);
|
|
328
|
+
const source = readFileSync(absAssetPath);
|
|
167
329
|
pluginContext.emitFile({
|
|
168
330
|
type: "asset",
|
|
169
331
|
fileName,
|
|
@@ -176,10 +338,68 @@ function reactClientManifestPlugin({
|
|
|
176
338
|
return fileName.startsWith("page.") || fileName.startsWith("layout.");
|
|
177
339
|
}
|
|
178
340
|
|
|
341
|
+
let isInitial = true;
|
|
342
|
+
const knownClientChunks = new Map();
|
|
343
|
+
const knownCssChunks = new Map();
|
|
344
|
+
let hasPendingCssChange = false;
|
|
179
345
|
|
|
180
346
|
return {
|
|
181
347
|
name: "react-client-manifest",
|
|
182
348
|
async buildStart(options) {
|
|
349
|
+
if (globalThis.__DINOU_EMITTED_ASSETS__) {
|
|
350
|
+
globalThis.__DINOU_EMITTED_ASSETS__.clear();
|
|
351
|
+
}
|
|
352
|
+
|
|
353
|
+
if (!isInitial) {
|
|
354
|
+
for (const entry of knownClientChunks.values()) {
|
|
355
|
+
this.emitFile({
|
|
356
|
+
type: "chunk",
|
|
357
|
+
id: entry.id,
|
|
358
|
+
name: entry.name,
|
|
359
|
+
});
|
|
360
|
+
}
|
|
361
|
+
if (hasPendingCssChange) {
|
|
362
|
+
for (const entry of knownCssChunks.values()) {
|
|
363
|
+
this.emitFile({
|
|
364
|
+
type: "chunk",
|
|
365
|
+
id: entry.id,
|
|
366
|
+
name: entry.name,
|
|
367
|
+
});
|
|
368
|
+
}
|
|
369
|
+
hasPendingCssChange = false;
|
|
370
|
+
}
|
|
371
|
+
|
|
372
|
+
// Quick scan for newly added client files in incremental builds
|
|
373
|
+
try {
|
|
374
|
+
const freshSrcFiles = await glob(["**/*.{js,jsx,ts,tsx}"], {
|
|
375
|
+
cwd: srcDir,
|
|
376
|
+
absolute: true,
|
|
377
|
+
});
|
|
378
|
+
for (const absPath of freshSrcFiles) {
|
|
379
|
+
const normAbs = alignDrive(absPath);
|
|
380
|
+
const normKey = normalizeFsPath(normAbs);
|
|
381
|
+
if (knownClientChunks.has(normKey)) continue;
|
|
382
|
+
let code = "";
|
|
383
|
+
try { code = readFileSync(normAbs, "utf8"); } catch (e) { continue; }
|
|
384
|
+
if (useClientRegex.test(code.trim())) {
|
|
385
|
+
clientModules.add(normKey);
|
|
386
|
+
updateManifestForModule(normAbs, code, true);
|
|
387
|
+
this.addWatchFile(normAbs);
|
|
388
|
+
const chunkName = getStableChunkName(normAbs);
|
|
389
|
+
knownClientChunks.set(normKey, { id: normAbs, name: chunkName });
|
|
390
|
+
this.emitFile({
|
|
391
|
+
type: "chunk",
|
|
392
|
+
id: normAbs,
|
|
393
|
+
name: chunkName,
|
|
394
|
+
});
|
|
395
|
+
}
|
|
396
|
+
}
|
|
397
|
+
} catch (e) {}
|
|
398
|
+
return;
|
|
399
|
+
}
|
|
400
|
+
isInitial = false;
|
|
401
|
+
const tManifest0 = Date.now();
|
|
402
|
+
|
|
183
403
|
const srcFiles = await glob(["**/*.{js,jsx,ts,tsx}"], {
|
|
184
404
|
cwd: srcDir,
|
|
185
405
|
absolute: true,
|
|
@@ -200,155 +420,224 @@ function reactClientManifestPlugin({
|
|
|
200
420
|
entryPoints = Object.values(inputOption);
|
|
201
421
|
}
|
|
202
422
|
const uniqueFiles = new Set([...srcFiles, ...entryPoints]);
|
|
423
|
+
const emittedChunks = new Set();
|
|
424
|
+
const emittedAssets = new Set();
|
|
425
|
+
const sharedAstCache = new Map();
|
|
203
426
|
|
|
427
|
+
let fileScanIdx = 0;
|
|
204
428
|
for (const absPath of uniqueFiles) {
|
|
205
|
-
|
|
206
|
-
|
|
429
|
+
if (++fileScanIdx % 50 === 0) {
|
|
430
|
+
await new Promise((resolve) => setImmediate(resolve));
|
|
431
|
+
}
|
|
432
|
+
const normAbs = alignDrive(absPath);
|
|
433
|
+
const normKey = normalizeFsPath(normAbs);
|
|
434
|
+
const code = readFileSync(normAbs, "utf8");
|
|
207
435
|
const isClientModule = useClientRegex.test(code.trim());
|
|
436
|
+
const isServerModule = useServerRegex.test(code.trim());
|
|
437
|
+
if (isServerModule) {
|
|
438
|
+
serverFiles.add(normKey);
|
|
439
|
+
}
|
|
208
440
|
|
|
209
441
|
if (isClientModule) {
|
|
210
|
-
clientModules.add(
|
|
211
|
-
updateManifestForModule(
|
|
212
|
-
this.
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
442
|
+
clientModules.add(normKey);
|
|
443
|
+
updateManifestForModule(normAbs, code, true);
|
|
444
|
+
this.addWatchFile(normAbs);
|
|
445
|
+
const chunkName = getStableChunkName(normAbs);
|
|
446
|
+
knownClientChunks.set(normKey, { id: normAbs, name: chunkName });
|
|
447
|
+
if (!emittedChunks.has(normKey)) {
|
|
448
|
+
emittedChunks.add(normKey);
|
|
449
|
+
this.emitFile({
|
|
450
|
+
type: "chunk",
|
|
451
|
+
id: normAbs,
|
|
452
|
+
name: chunkName,
|
|
453
|
+
});
|
|
454
|
+
}
|
|
455
|
+
} else if (isPageOrLayout(normAbs)) {
|
|
456
|
+
serverModules.add(normKey);
|
|
457
|
+
this.addWatchFile(normAbs);
|
|
220
458
|
const { imports, assets, csss } = await getImportsAndAssetsAndCsss(
|
|
221
459
|
code,
|
|
222
|
-
|
|
460
|
+
normAbs,
|
|
223
461
|
new Set(),
|
|
224
|
-
this
|
|
462
|
+
this,
|
|
463
|
+
sharedAstCache
|
|
225
464
|
);
|
|
226
|
-
// console.log("assets", assets);
|
|
227
465
|
for (const importPath of imports) {
|
|
228
466
|
this.addWatchFile(importPath);
|
|
229
467
|
}
|
|
230
468
|
// Emit assets for server components (replicate dinouAssetPlugin logic)
|
|
231
469
|
for (const assetPath of assets) {
|
|
232
470
|
this.addWatchFile(assetPath);
|
|
233
|
-
|
|
471
|
+
if (!emittedAssets.has(assetPath)) {
|
|
472
|
+
emittedAssets.add(assetPath);
|
|
473
|
+
emitAsset(assetPath, this);
|
|
474
|
+
}
|
|
234
475
|
}
|
|
235
476
|
for (const cssPath of csss) {
|
|
236
477
|
this.addWatchFile(cssPath);
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
478
|
+
const normCssAbs = alignDrive(cssPath);
|
|
479
|
+
const normCssKey = normalizeFsPath(normCssAbs);
|
|
480
|
+
const chunkName = path.basename(normCssAbs, path.extname(normCssAbs));
|
|
481
|
+
knownCssChunks.set(normCssKey, { id: normCssAbs, name: chunkName });
|
|
482
|
+
if (!emittedChunks.has(normCssKey)) {
|
|
483
|
+
emittedChunks.add(normCssKey);
|
|
484
|
+
// Emit CSS as a Rollup asset so postcss() processes it
|
|
485
|
+
this.emitFile({
|
|
486
|
+
type: "chunk",
|
|
487
|
+
id: normCssAbs,
|
|
488
|
+
name: chunkName,
|
|
489
|
+
});
|
|
490
|
+
}
|
|
243
491
|
}
|
|
244
492
|
}
|
|
245
493
|
}
|
|
494
|
+
globalThis.__DINOU_ROLLUP_MANIFEST_TIME__ = Date.now() - tManifest0;
|
|
246
495
|
},
|
|
247
496
|
async transform(code, id) {
|
|
248
|
-
if (
|
|
249
|
-
|
|
250
|
-
|
|
497
|
+
if (
|
|
498
|
+
id.includes("\0") ||
|
|
499
|
+
(id.includes("node_modules") && !id.includes("dinou")) ||
|
|
500
|
+
id.startsWith("commonjsHelpers") ||
|
|
501
|
+
id.includes("react-refresh")
|
|
502
|
+
)
|
|
503
|
+
return;
|
|
504
|
+
if (!useClientRegex.test(code)) return;
|
|
505
|
+
const normAbs = alignDrive(id);
|
|
506
|
+
const normId = normalizeFsPath(normAbs);
|
|
251
507
|
|
|
252
|
-
if (
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
}
|
|
508
|
+
if (!clientModules.has(normId)) {
|
|
509
|
+
clientModules.add(normId);
|
|
510
|
+
updateManifestForModule(normAbs, code, true);
|
|
511
|
+
const chunkName = getStableChunkName(normAbs);
|
|
512
|
+
knownClientChunks.set(normId, { id: normAbs, name: chunkName });
|
|
513
|
+
this.emitFile({
|
|
514
|
+
type: "chunk",
|
|
515
|
+
id: normAbs,
|
|
516
|
+
name: chunkName,
|
|
517
|
+
});
|
|
263
518
|
}
|
|
264
519
|
},
|
|
265
520
|
async watchChange(id) {
|
|
521
|
+
const lower = id.toLowerCase();
|
|
522
|
+
if (lower.endsWith(".css") || lower.endsWith(".scss") || lower.endsWith(".less")) {
|
|
523
|
+
hasPendingCssChange = true;
|
|
524
|
+
return;
|
|
525
|
+
}
|
|
266
526
|
if (
|
|
267
|
-
!
|
|
268
|
-
!
|
|
269
|
-
!
|
|
270
|
-
!
|
|
527
|
+
!lower.endsWith(".tsx") &&
|
|
528
|
+
!lower.endsWith(".jsx") &&
|
|
529
|
+
!lower.endsWith(".js") &&
|
|
530
|
+
!lower.endsWith(".ts")
|
|
271
531
|
)
|
|
272
532
|
return;
|
|
273
|
-
const
|
|
274
|
-
|
|
275
|
-
|
|
533
|
+
const normAbs = alignDrive(id);
|
|
534
|
+
const normId = normalizeFsPath(normAbs);
|
|
535
|
+
if (!existsSync(normAbs)) {
|
|
536
|
+
const fileUrl = normalizeFileUrl(normAbs);
|
|
537
|
+
const fileUrlLower = fileUrl.toLowerCase();
|
|
276
538
|
for (const key in manifest) {
|
|
277
|
-
if (key.startsWith(
|
|
539
|
+
if (key.toLowerCase().startsWith(fileUrlLower)) {
|
|
278
540
|
delete manifest[key];
|
|
279
541
|
}
|
|
280
542
|
}
|
|
281
|
-
clientModules.delete(
|
|
282
|
-
serverModules.delete(
|
|
543
|
+
clientModules.delete(normId);
|
|
544
|
+
serverModules.delete(normId);
|
|
545
|
+
serverFiles.delete(normId);
|
|
546
|
+
knownClientChunks.delete(normId);
|
|
283
547
|
return;
|
|
284
548
|
}
|
|
285
|
-
const code = readFileSync(
|
|
549
|
+
const code = readFileSync(normAbs, "utf8");
|
|
286
550
|
const isClientModule = useClientRegex.test(code.trim());
|
|
551
|
+
const isServerModule = useServerRegex.test(code.trim());
|
|
552
|
+
if (isServerModule) {
|
|
553
|
+
serverFiles.add(normId);
|
|
554
|
+
} else {
|
|
555
|
+
serverFiles.delete(normId);
|
|
556
|
+
}
|
|
287
557
|
|
|
288
|
-
updateManifestForModule(
|
|
558
|
+
updateManifestForModule(normAbs, code, isClientModule);
|
|
289
559
|
|
|
290
560
|
if (isClientModule) {
|
|
291
|
-
clientModules.add(
|
|
292
|
-
|
|
293
|
-
|
|
561
|
+
clientModules.add(normId);
|
|
562
|
+
const chunkName = getStableChunkName(normAbs);
|
|
563
|
+
knownClientChunks.set(normId, { id: normAbs, name: chunkName });
|
|
564
|
+
serverModules.delete(normId);
|
|
565
|
+
this.addWatchFile(normAbs);
|
|
294
566
|
} else {
|
|
295
|
-
clientModules.delete(
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
567
|
+
clientModules.delete(normId);
|
|
568
|
+
knownClientChunks.delete(normId);
|
|
569
|
+
if (isPageOrLayout(normAbs)) {
|
|
570
|
+
serverModules.add(normId);
|
|
571
|
+
this.addWatchFile(normAbs);
|
|
299
572
|
const { imports, assets, csss } = await getImportsAndAssetsAndCsss(
|
|
300
573
|
code,
|
|
301
|
-
|
|
574
|
+
normAbs,
|
|
302
575
|
new Set(),
|
|
303
576
|
this
|
|
304
577
|
);
|
|
305
578
|
for (const importPath of imports) {
|
|
306
579
|
this.addWatchFile(importPath);
|
|
307
580
|
}
|
|
308
|
-
// console.log("assets", assets);
|
|
309
581
|
for (const assetPath of assets) {
|
|
310
582
|
this.addWatchFile(assetPath);
|
|
311
|
-
// emitAsset(assetPath, this); // Re-emit assets on server file change
|
|
312
583
|
}
|
|
313
584
|
for (const cssPath of csss) {
|
|
314
585
|
this.addWatchFile(cssPath);
|
|
586
|
+
const normCssAbs = alignDrive(cssPath);
|
|
587
|
+
const normCssKey = normalizeFsPath(normCssAbs);
|
|
588
|
+
const chunkName = path.basename(normCssAbs, path.extname(normCssAbs));
|
|
589
|
+
knownCssChunks.set(normCssKey, { id: normCssAbs, name: chunkName });
|
|
315
590
|
}
|
|
316
591
|
} else {
|
|
317
|
-
serverModules.delete(
|
|
592
|
+
serverModules.delete(normId);
|
|
318
593
|
}
|
|
319
594
|
}
|
|
320
595
|
},
|
|
321
596
|
generateBundle(outputOptions, bundle) {
|
|
597
|
+
const normFsPathCache = new Map();
|
|
598
|
+
function getNormFsPath(p) {
|
|
599
|
+
let n = normFsPathCache.get(p);
|
|
600
|
+
if (!n) {
|
|
601
|
+
n = normalizeFsPath(p);
|
|
602
|
+
normFsPathCache.set(p, n);
|
|
603
|
+
}
|
|
604
|
+
return n;
|
|
605
|
+
}
|
|
606
|
+
|
|
322
607
|
for (const [fileName, chunk] of Object.entries(bundle)) {
|
|
323
608
|
if (chunk.type !== "chunk") continue;
|
|
609
|
+
const chunkUrl = "/" + fileName;
|
|
324
610
|
|
|
325
611
|
// Process entry point facadeModuleId for default export preservation
|
|
326
612
|
if (chunk.facadeModuleId) {
|
|
327
613
|
const absModulePath = path.resolve(chunk.facadeModuleId);
|
|
328
|
-
if (
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
614
|
+
if (
|
|
615
|
+
!absModulePath.includes("\0") &&
|
|
616
|
+
!absModulePath.startsWith("commonjsHelpers") &&
|
|
617
|
+
(!absModulePath.includes("node_modules") || absModulePath.includes("dinou"))
|
|
618
|
+
) {
|
|
619
|
+
const normPath = getNormFsPath(absModulePath);
|
|
620
|
+
if (clientModules.has(normPath)) {
|
|
621
|
+
const fileUrl = normalizeFileUrl(absModulePath);
|
|
622
|
+
const fileUrlLower = fileUrl.toLowerCase();
|
|
623
|
+
const keys = urlToManifestKeys.get(fileUrlLower);
|
|
624
|
+
if (keys) {
|
|
625
|
+
for (const k of keys) {
|
|
626
|
+
if (manifest[k]) manifest[k].id = chunkUrl;
|
|
627
|
+
}
|
|
628
|
+
}
|
|
629
|
+
setManifestEntry(fileUrl, "default", {
|
|
630
|
+
id: chunkUrl,
|
|
631
|
+
chunks: "default",
|
|
632
|
+
name: "default",
|
|
633
|
+
});
|
|
634
|
+
|
|
635
|
+
if (!chunk.exports.includes("default")) {
|
|
636
|
+
const defaultName = defaultExportCache.get(absModulePath);
|
|
345
637
|
if (defaultName && chunk.exports.includes(defaultName)) {
|
|
346
638
|
chunk.code += `\nexport { ${defaultName} as default };\n`;
|
|
347
639
|
chunk.exports.push("default");
|
|
348
|
-
// console.log(`👉 [react-client-manifest] Appended default export alias to chunk ${fileName}: export { ${defaultName} as default };`);
|
|
349
640
|
}
|
|
350
|
-
} catch (err) {
|
|
351
|
-
// Ignore errors
|
|
352
641
|
}
|
|
353
642
|
}
|
|
354
643
|
}
|
|
@@ -356,26 +645,82 @@ function reactClientManifestPlugin({
|
|
|
356
645
|
|
|
357
646
|
// Map all modules in the chunk to this chunk in the manifest (only if they are client modules)
|
|
358
647
|
for (const modulePath of Object.keys(chunk.modules)) {
|
|
359
|
-
if (
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
648
|
+
if (
|
|
649
|
+
modulePath.includes("\0") ||
|
|
650
|
+
modulePath.startsWith("commonjsHelpers") ||
|
|
651
|
+
(modulePath.includes("node_modules") && !modulePath.includes("dinou"))
|
|
652
|
+
) {
|
|
653
|
+
continue;
|
|
654
|
+
}
|
|
655
|
+
const normPath = getNormFsPath(modulePath);
|
|
656
|
+
|
|
657
|
+
if (clientModules.has(normPath)) {
|
|
658
|
+
const fileUrl = normalizeFileUrl(modulePath);
|
|
659
|
+
const fileUrlLower = fileUrl.toLowerCase();
|
|
660
|
+
const keys = urlToManifestKeys.get(fileUrlLower);
|
|
661
|
+
if (keys) {
|
|
662
|
+
for (const k of keys) {
|
|
663
|
+
if (manifest[k]) manifest[k].id = chunkUrl;
|
|
369
664
|
}
|
|
370
665
|
}
|
|
371
666
|
}
|
|
372
667
|
}
|
|
373
668
|
}
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
669
|
+
|
|
670
|
+
function areManifestsSemanticallyEqual(m1, m2) {
|
|
671
|
+
if (!m1 || !m2) return false;
|
|
672
|
+
const k1 = Object.keys(m1);
|
|
673
|
+
const k2 = Object.keys(m2);
|
|
674
|
+
if (k1.length !== k2.length) return false;
|
|
675
|
+
for (const k of k1) {
|
|
676
|
+
const v1 = m1[k];
|
|
677
|
+
const v2 = m2[k];
|
|
678
|
+
if (!v2) return false;
|
|
679
|
+
if (
|
|
680
|
+
v1.id !== v2.id ||
|
|
681
|
+
v1.name !== v2.name ||
|
|
682
|
+
v1.chunks !== v2.chunks
|
|
683
|
+
) {
|
|
684
|
+
return false;
|
|
685
|
+
}
|
|
686
|
+
}
|
|
687
|
+
return true;
|
|
688
|
+
}
|
|
689
|
+
|
|
690
|
+
if (typeof globalThis !== "undefined") {
|
|
691
|
+
globalThis.__DINOU_RAW_CLIENT_MANIFEST__ = { ...manifest };
|
|
692
|
+
}
|
|
693
|
+
|
|
694
|
+
const isDev = process.env.NODE_ENV !== "production" || process.env.DINOU_DEV === "true";
|
|
695
|
+
const shouldWriteToDisk = process.env.DINOU_WRITE_TO_DISK === "true" || !isDev;
|
|
696
|
+
|
|
697
|
+
let existingManifest = null;
|
|
698
|
+
if (shouldWriteToDisk && existsSync(manifestPath)) {
|
|
699
|
+
try {
|
|
700
|
+
existingManifest = JSON.parse(readFileSync(manifestPath, "utf8"));
|
|
701
|
+
} catch (e) {}
|
|
702
|
+
}
|
|
703
|
+
|
|
704
|
+
if (!lastManifest || !areManifestsSemanticallyEqual(lastManifest, manifest)) {
|
|
705
|
+
lastManifest = JSON.parse(JSON.stringify(manifest));
|
|
706
|
+
if (shouldWriteToDisk) {
|
|
707
|
+
const sortedManifest = {};
|
|
708
|
+
for (const k of Object.keys(manifest).sort()) {
|
|
709
|
+
sortedManifest[k] = manifest[k];
|
|
710
|
+
}
|
|
711
|
+
mkdirSync(dirname(manifestPath), { recursive: true });
|
|
712
|
+
writeFileSync(manifestPath, JSON.stringify(sortedManifest, null, 2));
|
|
713
|
+
}
|
|
714
|
+
manifestUpdatedCallback?.();
|
|
715
|
+
}
|
|
377
716
|
},
|
|
378
717
|
};
|
|
379
718
|
}
|
|
380
719
|
|
|
720
|
+
let manifestUpdatedCallback = null;
|
|
721
|
+
function setOnManifestUpdated(cb) {
|
|
722
|
+
manifestUpdatedCallback = cb;
|
|
723
|
+
}
|
|
724
|
+
reactClientManifestPlugin.setOnManifestUpdated = setOnManifestUpdated;
|
|
725
|
+
|
|
381
726
|
module.exports = reactClientManifestPlugin;
|