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
|
@@ -1,158 +1,203 @@
|
|
|
1
1
|
import path from "node:path";
|
|
2
|
+
import stripNamespace from "../helpers-esbuild/strip-namespace.mjs";
|
|
3
|
+
|
|
4
|
+
const normKey = (p) => {
|
|
5
|
+
if (!p) return "";
|
|
6
|
+
let s = path.resolve(p).replace(/\\/g, "/");
|
|
7
|
+
if (process.platform === "win32") {
|
|
8
|
+
s = s.replace(/^([a-zA-Z]):/, (_, d) => d.toLowerCase() + ":");
|
|
9
|
+
}
|
|
10
|
+
return s;
|
|
11
|
+
};
|
|
12
|
+
|
|
13
|
+
export default function stableChunkNamesAndMapsPlugin({ dev = true, changedIds } = {}) {
|
|
14
|
+
let isInitial = true;
|
|
2
15
|
|
|
3
|
-
export default function stableChunkNamesAndMapsPlugin({ dev = true } = {}) {
|
|
4
16
|
return {
|
|
5
17
|
name: "stable-chunk-names",
|
|
6
18
|
setup(build) {
|
|
7
19
|
build.onEnd(async (result) => {
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
const
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
20
|
+
const tStable0 = Date.now();
|
|
21
|
+
try {
|
|
22
|
+
if (!result.metafile || !result.outputFiles?.length) return;
|
|
23
|
+
const outdir = build.initialOptions.outdir;
|
|
24
|
+
if (!outdir) return;
|
|
25
|
+
|
|
26
|
+
const normalizeRel = (p) => p.replace(/\\/g, "/");
|
|
27
|
+
const isIncremental = !isInitial && changedIds && changedIds.size > 0;
|
|
28
|
+
const renames = new Map(); // oldFilename → newFilename (e.g., 'chunk-ABC.js' → 'chunk-stable.js')
|
|
29
|
+
|
|
30
|
+
// First, rename chunks
|
|
31
|
+
for (const [oldRelPath, info] of Object.entries(
|
|
32
|
+
result.metafile.outputs
|
|
33
|
+
)) {
|
|
34
|
+
if (info.entryPoint || !oldRelPath.endsWith(".js")) continue;
|
|
35
|
+
const inputs = Object.keys(info.inputs || {});
|
|
36
|
+
const sourceFile = inputs.find(
|
|
37
|
+
(f) =>
|
|
38
|
+
(f.startsWith("src/") ||
|
|
39
|
+
f.includes(".dinou/swc/src/") ||
|
|
40
|
+
f.includes(".dinou\\swc\\src\\")) &&
|
|
41
|
+
/\.(js|jsx|ts|tsx)$/.test(f)
|
|
42
|
+
);
|
|
43
|
+
if (!sourceFile) continue;
|
|
44
|
+
// Clean source file if it was loaded from .dinou/swc/
|
|
45
|
+
const cleanSourceFile = sourceFile.replace(/^.*\.dinou[/\\]swc[/\\]/, "");
|
|
46
|
+
// Stable name based on the source file (always the same)
|
|
47
|
+
const rel = path.relative("src", cleanSourceFile);
|
|
48
|
+
const normalizedRel = rel.replace(/\\/g, "/");
|
|
49
|
+
const dir = path.dirname(normalizedRel);
|
|
50
|
+
const base = path.basename(
|
|
51
|
+
normalizedRel,
|
|
52
|
+
path.extname(normalizedRel)
|
|
53
|
+
);
|
|
54
|
+
const stableName =
|
|
55
|
+
dir === "." ? base : `${dir.replace(/\//g, "-")}-${base}`;
|
|
38
56
|
const hash = oldRelPath.match(/-([A-Z0-9]+)\./)?.[1] || "";
|
|
39
|
-
finalName = `${stableName}-${hash}.js
|
|
57
|
+
const finalName = dev ? `${stableName}.js` : (hash ? `${stableName}-${hash}.js` : `${stableName}.js`);
|
|
58
|
+
const finalRelPath = `chunk-${finalName}`;
|
|
59
|
+
const oldLocal = path.basename(oldRelPath);
|
|
60
|
+
if (Array.from(renames.values()).includes(finalRelPath)) {
|
|
61
|
+
renames.set(oldLocal, oldLocal);
|
|
62
|
+
} else {
|
|
63
|
+
renames.set(oldLocal, finalRelPath);
|
|
64
|
+
}
|
|
40
65
|
}
|
|
41
|
-
|
|
42
|
-
const
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
const newMapLocal = newJsLocal.replace(/\.js$/, ".js.map");
|
|
55
|
-
const oldMapLocal = path.basename(oldRelPath);
|
|
56
|
-
renames.set(oldMapLocal, newMapLocal);
|
|
66
|
+
// Second, rename maps corresponding to the chunks
|
|
67
|
+
for (const [oldRelPath, info] of Object.entries(
|
|
68
|
+
result.metafile.outputs
|
|
69
|
+
)) {
|
|
70
|
+
if (!oldRelPath.endsWith(".js.map")) continue;
|
|
71
|
+
const jsRelPath = oldRelPath.replace(".map", "");
|
|
72
|
+
const jsLocal = path.basename(jsRelPath);
|
|
73
|
+
if (renames.has(jsLocal)) {
|
|
74
|
+
const newJsLocal = renames.get(jsLocal);
|
|
75
|
+
const newMapLocal = newJsLocal.replace(/\.js$/, ".js.map");
|
|
76
|
+
const oldMapLocal = path.basename(oldRelPath);
|
|
77
|
+
renames.set(oldMapLocal, newMapLocal);
|
|
78
|
+
}
|
|
57
79
|
}
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
const
|
|
67
|
-
(
|
|
68
|
-
|
|
69
|
-
)
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
const
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
const
|
|
80
|
+
|
|
81
|
+
if (renames.size === 0) return;
|
|
82
|
+
if (typeof globalThis !== "undefined") {
|
|
83
|
+
globalThis.__DINOU_CHUNK_RENAMES__ = renames;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
// Step 3: Update references in importers (imports in .js)
|
|
87
|
+
const outputs = result.metafile.outputs;
|
|
88
|
+
const escapeRegExp = (string) =>
|
|
89
|
+
string.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
90
|
+
|
|
91
|
+
// Pre-index outputFiles for O(1) lookup
|
|
92
|
+
const outputFilesMap = new Map();
|
|
93
|
+
for (const f of result.outputFiles) {
|
|
94
|
+
outputFilesMap.set(normalizeRel(path.relative(process.cwd(), f.path)), f);
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
for (const relPath in outputs) {
|
|
98
|
+
const output = outputs[relPath];
|
|
99
|
+
if (!output.imports || !relPath.endsWith(".js")) continue;
|
|
100
|
+
|
|
101
|
+
const importerFile = outputFilesMap.get(relPath);
|
|
102
|
+
if (!importerFile) continue;
|
|
103
|
+
|
|
104
|
+
// Only decode and regex-replace if this output imports any renamed chunk
|
|
105
|
+
const hasRenamedChunk = output.imports.some((imp) => renames.has(path.basename(imp.path)));
|
|
106
|
+
if (!hasRenamedChunk) continue;
|
|
107
|
+
|
|
108
|
+
let content = new TextDecoder().decode(importerFile.contents);
|
|
109
|
+
for (const imp of output.imports) {
|
|
110
|
+
const importedRelPath = imp.path;
|
|
111
|
+
const oldImportedLocal = path.basename(importedRelPath);
|
|
112
|
+
const newImportedLocal = renames.get(oldImportedLocal);
|
|
113
|
+
if (!newImportedLocal) continue;
|
|
114
|
+
// Replace for double quotes with ./
|
|
115
|
+
const oldDouble = `"./${escapeRegExp(oldImportedLocal)}"`;
|
|
116
|
+
const newDouble = `"./${newImportedLocal}"`;
|
|
117
|
+
content = content.replace(new RegExp(oldDouble, "g"), newDouble);
|
|
118
|
+
// Replace for double quotes without ./
|
|
119
|
+
const oldDoubleNoDot = `"${escapeRegExp(oldImportedLocal)}"`;
|
|
120
|
+
const newDoubleNoDot = `"${newImportedLocal}"`;
|
|
121
|
+
content = content.replace(
|
|
122
|
+
new RegExp(oldDoubleNoDot, "g"),
|
|
123
|
+
newDoubleNoDot
|
|
124
|
+
);
|
|
125
|
+
// Replace for single quotes with ./
|
|
126
|
+
const oldSingle = `'./${escapeRegExp(oldImportedLocal)}'`;
|
|
127
|
+
const newSingle = `'./${newImportedLocal}'`;
|
|
128
|
+
content = content.replace(new RegExp(oldSingle, "g"), newSingle);
|
|
129
|
+
// Replace for single quotes without ./
|
|
130
|
+
const oldSingleNoDot = `'${escapeRegExp(oldImportedLocal)}'`;
|
|
131
|
+
const newSingleNoDot = `'${newImportedLocal}'`;
|
|
132
|
+
content = content.replace(
|
|
133
|
+
new RegExp(oldSingleNoDot, "g"),
|
|
134
|
+
newSingleNoDot
|
|
135
|
+
);
|
|
136
|
+
}
|
|
137
|
+
importerFile.contents = new TextEncoder().encode(content);
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
// Step 4: Update sourceMappingURL in the .js files being renamed
|
|
141
|
+
for (const file of result.outputFiles) {
|
|
142
|
+
if (!file.path.endsWith(".js")) continue;
|
|
143
|
+
const oldRelPath = normalizeRel(
|
|
144
|
+
path.relative(process.cwd(), file.path)
|
|
145
|
+
);
|
|
146
|
+
const oldLocal = path.basename(oldRelPath);
|
|
147
|
+
if (!renames.has(oldLocal)) continue;
|
|
148
|
+
|
|
149
|
+
const newLocal = renames.get(oldLocal);
|
|
150
|
+
const oldMapLocal = oldLocal.replace(/\.js$/, ".js.map");
|
|
151
|
+
const newMapLocal = newLocal.replace(/\.js$/, ".js.map");
|
|
152
|
+
let content = new TextDecoder().decode(file.contents);
|
|
153
|
+
const escOld = escapeRegExp(oldMapLocal);
|
|
154
|
+
const escNew = newMapLocal;
|
|
84
155
|
content = content.replace(
|
|
85
|
-
new RegExp(
|
|
86
|
-
|
|
156
|
+
new RegExp(`sourceMappingURL=${escOld}`, "g"),
|
|
157
|
+
`sourceMappingURL=${escNew}`
|
|
87
158
|
);
|
|
88
|
-
// Replace for single quotes with ./
|
|
89
|
-
const oldSingle = `'./${escapeRegExp(oldImportedLocal)}'`;
|
|
90
|
-
const newSingle = `'./${newImportedLocal}'`;
|
|
91
|
-
content = content.replace(new RegExp(oldSingle, "g"), newSingle);
|
|
92
|
-
// Replace for single quotes without ./
|
|
93
|
-
const oldSingleNoDot = `'${escapeRegExp(oldImportedLocal)}'`;
|
|
94
|
-
const newSingleNoDot = `'${newImportedLocal}'`;
|
|
95
159
|
content = content.replace(
|
|
96
|
-
new RegExp(
|
|
97
|
-
|
|
160
|
+
new RegExp(`sourceMappingURL=\\./${escOld}`, "g"),
|
|
161
|
+
`sourceMappingURL=./${escNew}`
|
|
98
162
|
);
|
|
163
|
+
file.contents = new TextEncoder().encode(content);
|
|
99
164
|
}
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
if (!renames.has(oldLocal)) continue;
|
|
110
|
-
const newLocal = renames.get(oldLocal);
|
|
111
|
-
const oldMapLocal = oldLocal.replace(/\.js$/, ".js.map");
|
|
112
|
-
const newMapLocal = newLocal.replace(/\.js$/, ".js.map");
|
|
113
|
-
let content = new TextDecoder().decode(file.contents);
|
|
114
|
-
const escOld = escapeRegExp(oldMapLocal);
|
|
115
|
-
const escNew = newMapLocal;
|
|
116
|
-
content = content.replace(
|
|
117
|
-
new RegExp(`sourceMappingURL=${escOld}`, "g"),
|
|
118
|
-
`sourceMappingURL=${escNew}`
|
|
119
|
-
);
|
|
120
|
-
content = content.replace(
|
|
121
|
-
new RegExp(`sourceMappingURL=\\./${escOld}`, "g"),
|
|
122
|
-
`sourceMappingURL=./${escNew}`
|
|
123
|
-
);
|
|
124
|
-
file.contents = new TextEncoder().encode(content);
|
|
125
|
-
}
|
|
126
|
-
// Update paths in outputFiles for chunks and maps
|
|
127
|
-
for (const file of result.outputFiles) {
|
|
128
|
-
const relPath = normalizeRel(path.relative(process.cwd(), file.path));
|
|
129
|
-
const oldLocal = path.basename(relPath);
|
|
130
|
-
const newLocal = renames.get(oldLocal);
|
|
131
|
-
if (newLocal) {
|
|
132
|
-
file.path = path.join(path.dirname(file.path), newLocal);
|
|
165
|
+
|
|
166
|
+
// Step 5: Update paths in outputFiles for chunks and maps
|
|
167
|
+
for (const file of result.outputFiles) {
|
|
168
|
+
const relPath = normalizeRel(path.relative(process.cwd(), file.path));
|
|
169
|
+
const oldLocal = path.basename(relPath);
|
|
170
|
+
const newLocal = renames.get(oldLocal);
|
|
171
|
+
if (newLocal) {
|
|
172
|
+
file.path = path.join(path.dirname(file.path), newLocal);
|
|
173
|
+
}
|
|
133
174
|
}
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
175
|
+
|
|
176
|
+
// Step 6: Update metafile for consistency
|
|
177
|
+
const newOutputs = {};
|
|
178
|
+
for (const oldRelPath in outputs) {
|
|
179
|
+
const oldLocal = path.basename(oldRelPath);
|
|
180
|
+
const newLocal = renames.get(oldLocal);
|
|
181
|
+
const newRelPath = newLocal
|
|
182
|
+
? normalizeRel(path.join(path.dirname(oldRelPath), newLocal))
|
|
183
|
+
: oldRelPath;
|
|
184
|
+
newOutputs[newRelPath] = outputs[oldRelPath];
|
|
185
|
+
if (newOutputs[newRelPath].imports) {
|
|
186
|
+
for (let i = 0; i < newOutputs[newRelPath].imports.length; i++) {
|
|
187
|
+
const imp = newOutputs[newRelPath].imports[i];
|
|
188
|
+
const oldImpLocal = path.basename(imp.path);
|
|
189
|
+
const newImpLocal = renames.get(oldImpLocal) || oldImpLocal;
|
|
190
|
+
imp.path = normalizeRel(
|
|
191
|
+
path.join(path.dirname(imp.path), newImpLocal)
|
|
192
|
+
);
|
|
193
|
+
}
|
|
152
194
|
}
|
|
153
195
|
}
|
|
196
|
+
result.metafile.outputs = newOutputs;
|
|
197
|
+
} finally {
|
|
198
|
+
isInitial = false;
|
|
199
|
+
globalThis.__DINOU_STABLE_TIME__ = Date.now() - tStable0;
|
|
154
200
|
}
|
|
155
|
-
result.metafile.outputs = newOutputs;
|
|
156
201
|
});
|
|
157
202
|
},
|
|
158
203
|
};
|
|
@@ -1,11 +1,14 @@
|
|
|
1
|
-
//
|
|
1
|
+
// postcss-extract-plugin.js
|
|
2
2
|
const fs = require("fs");
|
|
3
3
|
const path = require("path");
|
|
4
4
|
|
|
5
|
+
let cachedExtractedCSS = "";
|
|
6
|
+
let cachedCSSBuffer = null;
|
|
7
|
+
|
|
5
8
|
const createPostCSSExtractPlugin = (options = {}) => {
|
|
6
9
|
const { outputFile = "styles.css", shouldExtract = () => true } = options;
|
|
7
10
|
|
|
8
|
-
let
|
|
11
|
+
let currentExtractedCSS = "";
|
|
9
12
|
|
|
10
13
|
// Define the PostCSS plugin to intercept and extract CSS rules
|
|
11
14
|
const postcssPlugin = {
|
|
@@ -15,8 +18,8 @@ const createPostCSSExtractPlugin = (options = {}) => {
|
|
|
15
18
|
const filePath = result.opts.from;
|
|
16
19
|
|
|
17
20
|
if (shouldExtract(filePath, root)) {
|
|
18
|
-
|
|
19
|
-
|
|
21
|
+
currentExtractedCSS += root.toString();
|
|
22
|
+
currentExtractedCSS += "\n";
|
|
20
23
|
|
|
21
24
|
// Remove all CSS rules from the original file to prevent duplicate injection
|
|
22
25
|
root.removeAll();
|
|
@@ -24,23 +27,47 @@ const createPostCSSExtractPlugin = (options = {}) => {
|
|
|
24
27
|
},
|
|
25
28
|
};
|
|
26
29
|
|
|
27
|
-
// Write the collected CSS contents to the final output file on disk
|
|
30
|
+
// Write the collected CSS contents to the final output file on disk or memory
|
|
28
31
|
const finalize = () => {
|
|
29
|
-
if (
|
|
30
|
-
|
|
32
|
+
if (currentExtractedCSS) {
|
|
33
|
+
cachedExtractedCSS = currentExtractedCSS;
|
|
34
|
+
cachedCSSBuffer = Buffer.from(cachedExtractedCSS);
|
|
35
|
+
currentExtractedCSS = "";
|
|
36
|
+
}
|
|
31
37
|
|
|
32
|
-
if (!
|
|
33
|
-
|
|
38
|
+
if (!cachedCSSBuffer) return;
|
|
39
|
+
|
|
40
|
+
// 1. Ensure it's ALWAYS stored in globalThis.__DINOU_MEM_FILES__
|
|
41
|
+
if (typeof globalThis !== "undefined") {
|
|
42
|
+
if (!globalThis.__DINOU_MEM_FILES__) {
|
|
43
|
+
globalThis.__DINOU_MEM_FILES__ = new Map();
|
|
44
|
+
}
|
|
45
|
+
globalThis.__DINOU_MEM_FILES__.set("styles.css", cachedCSSBuffer);
|
|
46
|
+
globalThis.__DINOU_MEM_FILES__.set("/styles.css", cachedCSSBuffer);
|
|
34
47
|
}
|
|
35
48
|
|
|
36
|
-
|
|
49
|
+
// 2. Write to disk if in production OR if DINOU_WRITE_TO_DISK is requested in dev
|
|
50
|
+
const isProduction = process.env.NODE_ENV === "production";
|
|
51
|
+
const shouldWriteToDisk = process.env.DINOU_WRITE_TO_DISK === "true" || isProduction;
|
|
52
|
+
if (shouldWriteToDisk && cachedExtractedCSS) {
|
|
53
|
+
const outputDir = path.dirname(outputFile);
|
|
54
|
+
if (!fs.existsSync(outputDir)) {
|
|
55
|
+
fs.mkdirSync(outputDir, { recursive: true });
|
|
56
|
+
}
|
|
57
|
+
fs.writeFileSync(outputFile, cachedExtractedCSS);
|
|
58
|
+
}
|
|
59
|
+
};
|
|
37
60
|
|
|
38
|
-
|
|
61
|
+
const addExtractedCss = (cssText) => {
|
|
62
|
+
if (cssText) {
|
|
63
|
+
currentExtractedCSS += cssText + "\n";
|
|
64
|
+
}
|
|
39
65
|
};
|
|
40
66
|
|
|
41
67
|
return {
|
|
42
68
|
plugin: postcssPlugin,
|
|
43
69
|
finalize,
|
|
70
|
+
addExtractedCss,
|
|
44
71
|
};
|
|
45
72
|
};
|
|
46
73
|
|
|
@@ -95,18 +95,21 @@ class HotModuleState {
|
|
|
95
95
|
}
|
|
96
96
|
|
|
97
97
|
export function createHotContext(id) {
|
|
98
|
-
const
|
|
98
|
+
const normId = id.startsWith("/") ? id : "/" + id;
|
|
99
|
+
const existing = REGISTERED_MODULES[normId] || REGISTERED_MODULES[id];
|
|
99
100
|
if (existing) {
|
|
100
|
-
existing.
|
|
101
|
+
existing.isLocked = false;
|
|
101
102
|
return existing;
|
|
102
103
|
}
|
|
103
|
-
const state = new HotModuleState(
|
|
104
|
+
const state = new HotModuleState(normId);
|
|
105
|
+
REGISTERED_MODULES[normId] = state;
|
|
104
106
|
REGISTERED_MODULES[id] = state;
|
|
105
107
|
return state;
|
|
106
108
|
}
|
|
107
109
|
|
|
108
110
|
async function applyUpdate(id) {
|
|
109
|
-
const
|
|
111
|
+
const normId = id.startsWith("/") ? id : "/" + id;
|
|
112
|
+
const state = REGISTERED_MODULES[normId] || REGISTERED_MODULES[id];
|
|
110
113
|
if (!state || state.isDeclined) {
|
|
111
114
|
return false;
|
|
112
115
|
}
|
|
@@ -120,10 +123,12 @@ async function applyUpdate(id) {
|
|
|
120
123
|
|
|
121
124
|
const updateID = Date.now();
|
|
122
125
|
for (const { deps, callback: acceptCallback } of acceptCallbacks) {
|
|
123
|
-
const url = `/${id.replace(/^\/+/, "")}?mtime=${updateID}`;
|
|
124
126
|
const [module, ...depModules] = await Promise.all([
|
|
125
|
-
import(
|
|
126
|
-
...deps.map((d) =>
|
|
127
|
+
import(normId + `?mtime=${updateID}`),
|
|
128
|
+
...deps.map((d) => {
|
|
129
|
+
const depId = d.startsWith("/") ? d : "/" + d;
|
|
130
|
+
return import(depId + `?mtime=${updateID}`);
|
|
131
|
+
}),
|
|
127
132
|
]);
|
|
128
133
|
acceptCallback({ module, deps: depModules });
|
|
129
134
|
}
|
|
@@ -140,18 +145,60 @@ socket.addEventListener("message", ({ data: _data }) => {
|
|
|
140
145
|
return;
|
|
141
146
|
}
|
|
142
147
|
|
|
148
|
+
if (data.type === "rsc-update") {
|
|
149
|
+
if (typeof window !== "undefined" && typeof window.__DINOU_ROUTER_REFRESH__ === "function") {
|
|
150
|
+
window.__DINOU_ROUTER_REFRESH__();
|
|
151
|
+
return;
|
|
152
|
+
}
|
|
153
|
+
reload();
|
|
154
|
+
return;
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
if (
|
|
158
|
+
data.type === "style-update" ||
|
|
159
|
+
(data.type === "update" && data.url && (data.url.endsWith(".css") || data.url.includes("styles.css")))
|
|
160
|
+
) {
|
|
161
|
+
const targetUrl = data.url ? data.url.split("?")[0] : "";
|
|
162
|
+
const links = document.querySelectorAll('link[rel="stylesheet"]');
|
|
163
|
+
for (const link of links) {
|
|
164
|
+
const rawHref = link.getAttribute("href");
|
|
165
|
+
if (!rawHref) continue;
|
|
166
|
+
const cleanHref = rawHref.split("?")[0];
|
|
167
|
+
if (!targetUrl || cleanHref.endsWith(targetUrl) || targetUrl.endsWith(cleanHref) || cleanHref.includes("styles.css")) {
|
|
168
|
+
const nextUrl = new URL(link.href, window.location.origin);
|
|
169
|
+
nextUrl.searchParams.set("t", String(Date.now()));
|
|
170
|
+
link.href = nextUrl.href;
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
return;
|
|
174
|
+
}
|
|
175
|
+
|
|
143
176
|
if (data.type !== "update") {
|
|
144
177
|
return;
|
|
145
178
|
}
|
|
146
179
|
|
|
180
|
+
const tRecv = Date.now();
|
|
181
|
+
const isDebug = typeof window !== "undefined" && Boolean(window.__DINOU_DEBUG__);
|
|
182
|
+
|
|
183
|
+
if (isDebug) {
|
|
184
|
+
console.log(`⏱️ [BROWSER HMR] Received update for ${data.url} at ${new Date().toTimeString().slice(0, 8)}.${String(Date.now() % 1000).padStart(3, '0')}`);
|
|
185
|
+
}
|
|
186
|
+
|
|
147
187
|
applyUpdate(data.url)
|
|
148
188
|
.then((ok) => {
|
|
149
189
|
if (!ok) {
|
|
190
|
+
if (isDebug) console.warn(`⏱️ [BROWSER HMR] applyUpdate returned false, falling back to full reload!`);
|
|
150
191
|
reload();
|
|
192
|
+
} else {
|
|
193
|
+
if (isDebug) {
|
|
194
|
+
console.log(`⏱️ [BROWSER HMR] Fast Refresh finished applying in ${Date.now() - tRecv}ms!`);
|
|
195
|
+
} else {
|
|
196
|
+
console.log(`[ESM-HMR] hot updated: ${data.url}`);
|
|
197
|
+
}
|
|
151
198
|
}
|
|
152
199
|
})
|
|
153
200
|
.catch((err) => {
|
|
154
|
-
console.error(err);
|
|
201
|
+
console.error(`[ESM-HMR] applyUpdate error:`, err);
|
|
155
202
|
reload();
|
|
156
203
|
});
|
|
157
204
|
});
|
|
@@ -16,7 +16,7 @@ class EsmHmrEngine {
|
|
|
16
16
|
this.dependencyTree = map;
|
|
17
17
|
const wss = options.server
|
|
18
18
|
? new WebSocket.Server({ noServer: true })
|
|
19
|
-
: new WebSocket.Server({ port: 3001 });
|
|
19
|
+
: new WebSocket.Server({ port: options.port || Number(process.env.HMR_PORT) || 3001 });
|
|
20
20
|
|
|
21
21
|
if (options.server) {
|
|
22
22
|
options.server.on("upgrade", (req, socket, head) => {
|