dinou 4.0.7 → 4.0.9
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 +14 -0
- package/dinou/core/babel-esm-loader.js +1 -4
- package/dinou/core/build-static-pages.js +37 -40
- package/dinou/core/css-require-hook.js +64 -0
- package/dinou/core/generating-isg.js +2 -3
- package/dinou/core/get-error-jsx.js +1 -3
- package/dinou/core/get-file-path-and-dynamic-params.js +0 -1
- package/dinou/core/get-jsx.js +1 -3
- package/dinou/core/get-ssg-jsx-or-jsx.js +2 -2
- package/dinou/core/render-app-to-html.js +1 -5
- package/dinou/core/render-html.js +24 -29
- package/dinou/core/server-function-proxy-webpack.js +2 -3
- package/dinou/core/server.js +21 -68
- package/dinou/esbuild/helpers-esbuild/get-config-esbuild-prod.mjs +0 -1
- package/dinou/esbuild/helpers-esbuild/get-config-esbuild.mjs +0 -1
- package/dinou/esbuild/helpers-esbuild/get-esbuild-entries.mjs +8 -8
- package/dinou/esbuild/helpers-esbuild/write.mjs +6 -6
- package/dinou/esbuild/plugins-esbuild/assets-plugin.mjs +17 -17
- package/dinou/esbuild/plugins-esbuild/babel-react-compiler-plugin.mjs +11 -11
- package/dinou/esbuild/plugins-esbuild/server-functions-plugin.mjs +5 -5
- package/dinou/esbuild/plugins-esbuild/stable-chunk-names-and-maps-plugin.mjs +12 -12
- package/dinou/esbuild/plugins-postcss/postcss-extract-plugin.js +3 -3
- package/dinou/esbuild/react-refresh/esm-hmr-plugin.mjs +12 -39
- package/dinou/esbuild/react-refresh/react-refresh-runtime.mjs +1 -2
- package/dinou/package.json +4 -6
- package/dinou/rollup/rollup-plugins/rollup-plugin-react-client-manifest.js +4 -4
- package/dinou/rollup/rollup-plugins/rollup-plugin-server-functions.js +5 -5
- package/dinou/rollup/rollup.config.js +30 -58
- package/dinou/webpack/helpers/get-webpack-entries.js +7 -31
- package/dinou/webpack/loaders/server-functions-loader.js +4 -5
- package/dinou/webpack/plugins/manifest-generator-plugin.js +6 -6
- package/dinou/webpack/plugins/server-functions-plugin.js +7 -12
- package/dinou/webpack/webpack.config.js +21 -36
- package/package.json +3 -5
package/dinou/core/server.js
CHANGED
|
@@ -23,9 +23,7 @@ babelRegister({
|
|
|
23
23
|
extensions: [".js", ".jsx", ".ts", ".tsx"],
|
|
24
24
|
});
|
|
25
25
|
const createScopedName = require("./createScopedName");
|
|
26
|
-
require("css-
|
|
27
|
-
generateScopedName: createScopedName,
|
|
28
|
-
});
|
|
26
|
+
require("./css-require-hook.js")();
|
|
29
27
|
addHook({
|
|
30
28
|
extensions,
|
|
31
29
|
name: function (localName, filepath) {
|
|
@@ -220,13 +218,11 @@ if (isDevelopment) {
|
|
|
220
218
|
isInitial = true;
|
|
221
219
|
currentManifest = {};
|
|
222
220
|
});
|
|
223
|
-
// manifestWatcher.on("all", (event) => console.log("event", event));
|
|
224
221
|
manifestWatcher.on("change", onManifestChange);
|
|
225
222
|
}
|
|
226
223
|
startManifestWatcher();
|
|
227
224
|
}
|
|
228
225
|
let serverFunctionsManifest = null;
|
|
229
|
-
// const devCache = new Map(); // For dev: Map<absolutePath, Set<exports>>
|
|
230
226
|
|
|
231
227
|
if (!isDevelopment) {
|
|
232
228
|
// In prod/build: load generated manifest
|
|
@@ -328,14 +324,6 @@ function getContext(req, res) {
|
|
|
328
324
|
|
|
329
325
|
// 4. REDIRECT (Your existing smart wrapper)
|
|
330
326
|
redirect: (...args) => safeResCall("redirect", ...args),
|
|
331
|
-
// redirect: (...args) => {
|
|
332
|
-
// if (res.headersSent) {
|
|
333
|
-
// // Do nothing on res object.
|
|
334
|
-
// // We trust that your Server Function will return <ClientRedirect />
|
|
335
|
-
// return;
|
|
336
|
-
// }
|
|
337
|
-
// res.redirect.apply(res, args);
|
|
338
|
-
// },
|
|
339
327
|
},
|
|
340
328
|
};
|
|
341
329
|
return context;
|
|
@@ -515,12 +503,7 @@ async function serveRSCPayload(req, res, isOld = false, isStatic = false) {
|
|
|
515
503
|
// isStatic,
|
|
516
504
|
// reqPath
|
|
517
505
|
// );
|
|
518
|
-
if (
|
|
519
|
-
(!isDevelopment &&
|
|
520
|
-
/*Object.keys({ ...req.query }).length === 0 &&*/
|
|
521
|
-
!dynamicState.value) ||
|
|
522
|
-
isStatic
|
|
523
|
-
) {
|
|
506
|
+
if ((!isDevelopment && !dynamicState.value) || isStatic) {
|
|
524
507
|
const payloadPath = path.resolve(
|
|
525
508
|
"dist2",
|
|
526
509
|
reqPath.replace(/^\//, ""),
|
|
@@ -538,16 +521,6 @@ async function serveRSCPayload(req, res, isOld = false, isStatic = false) {
|
|
|
538
521
|
console.error("Error reading RSC file:", err);
|
|
539
522
|
res.status(500).end();
|
|
540
523
|
});
|
|
541
|
-
// readStream.on("end", () => {
|
|
542
|
-
// if (!regenerating.has(reqPath)) {
|
|
543
|
-
// // Regeneration has finished, delete the old one
|
|
544
|
-
// try {
|
|
545
|
-
// unlinkSync(payloadPathOld);
|
|
546
|
-
// } catch (err) {
|
|
547
|
-
// console.error("Error deleting old RSC file:", err);
|
|
548
|
-
// }
|
|
549
|
-
// }
|
|
550
|
-
// });
|
|
551
524
|
return readStream.pipe(res);
|
|
552
525
|
}
|
|
553
526
|
}
|
|
@@ -562,16 +535,16 @@ async function serveRSCPayload(req, res, isOld = false, isStatic = false) {
|
|
|
562
535
|
);
|
|
563
536
|
const manifest = isDevelopment
|
|
564
537
|
? JSON.parse(
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
),
|
|
572
|
-
"utf8",
|
|
538
|
+
readFileSync(
|
|
539
|
+
path.resolve(
|
|
540
|
+
process.cwd(),
|
|
541
|
+
isWebpack
|
|
542
|
+
? `${outputFolder}/react-client-manifest.json`
|
|
543
|
+
: `react_client_manifest/react-client-manifest.json`,
|
|
573
544
|
),
|
|
574
|
-
|
|
545
|
+
"utf8",
|
|
546
|
+
),
|
|
547
|
+
)
|
|
575
548
|
: cachedClientManifest;
|
|
576
549
|
|
|
577
550
|
const { pipe } = renderToPipeableStream(jsx, manifest);
|
|
@@ -612,16 +585,16 @@ app.post(/^\/____rsc_payload_error____\/.*\/?$/, async (req, res) => {
|
|
|
612
585
|
);
|
|
613
586
|
const manifest = isDevelopment
|
|
614
587
|
? JSON.parse(
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
),
|
|
622
|
-
"utf8",
|
|
588
|
+
readFileSync(
|
|
589
|
+
path.resolve(
|
|
590
|
+
process.cwd(),
|
|
591
|
+
isWebpack
|
|
592
|
+
? `${outputFolder}/react-client-manifest.json`
|
|
593
|
+
: `react_client_manifest/react-client-manifest.json`,
|
|
623
594
|
),
|
|
624
|
-
|
|
595
|
+
"utf8",
|
|
596
|
+
),
|
|
597
|
+
)
|
|
625
598
|
: cachedClientManifest;
|
|
626
599
|
const { pipe } = renderToPipeableStream(jsx, manifest);
|
|
627
600
|
pipe(res);
|
|
@@ -644,11 +617,7 @@ app.get(/^\/.*\/?$/, (req, res) => {
|
|
|
644
617
|
// Get reference to the mutable object
|
|
645
618
|
const dynamicState = isDynamic.get(reqPath);
|
|
646
619
|
// console.log("dynamicState.value", dynamicState.value);
|
|
647
|
-
if (
|
|
648
|
-
!isDevelopment &&
|
|
649
|
-
/*Object.keys({ ...req.query }).length === 0 &&*/
|
|
650
|
-
!dynamicState.value
|
|
651
|
-
) {
|
|
620
|
+
if (!isDevelopment && !dynamicState.value) {
|
|
652
621
|
revalidating(reqPath, dynamicState);
|
|
653
622
|
let htmlPathOld;
|
|
654
623
|
if (regenerating.has(reqPath)) {
|
|
@@ -688,16 +657,6 @@ app.get(/^\/.*\/?$/, (req, res) => {
|
|
|
688
657
|
|
|
689
658
|
// 4. Manually close the response (now we do)
|
|
690
659
|
res.end();
|
|
691
|
-
|
|
692
|
-
// // 5. Optional cleanup logic (async to avoid blocking)
|
|
693
|
-
// if (htmlPathOld && !regenerating.has(reqPath)) {
|
|
694
|
-
// // It is better to use fs.promises to avoid blocking the event loop
|
|
695
|
-
// require("fs")
|
|
696
|
-
// .promises.unlink(htmlPathOld)
|
|
697
|
-
// .catch((err) => {
|
|
698
|
-
// console.error("Error deleting old HTML file:", err);
|
|
699
|
-
// });
|
|
700
|
-
// }
|
|
701
660
|
});
|
|
702
661
|
|
|
703
662
|
return; // The stream is already flowing
|
|
@@ -763,7 +722,6 @@ app.get(/^\/.*\/?$/, (req, res) => {
|
|
|
763
722
|
appHtmlStream.on("end", resolve);
|
|
764
723
|
appHtmlStream.on("error", (error) => {
|
|
765
724
|
console.error("Stream error:", error);
|
|
766
|
-
// resolve(); // ⚠️ WATCH OUT: If you resolve here and then try to send status 500, it might fail if headers sent
|
|
767
725
|
if (!res.headersSent) res.status(500).send("Internal Server Error");
|
|
768
726
|
resolve(); // Better resolve at the end
|
|
769
727
|
});
|
|
@@ -923,9 +881,6 @@ app.post("/____server_function____", async (req, res) => {
|
|
|
923
881
|
// Prod: use manifest (relativePath is already normalized)
|
|
924
882
|
allowedExports = serverFunctionsManifest[relativePath];
|
|
925
883
|
} else {
|
|
926
|
-
// Dev: use cache or verify file
|
|
927
|
-
// allowedExports = devCache.get(absolutePath);
|
|
928
|
-
// if (!allowedExports) {
|
|
929
884
|
const fileContent = readFileSync(absolutePath, "utf8"); // Reads only once
|
|
930
885
|
if (!useServerRegex.test(fileContent)) {
|
|
931
886
|
return res
|
|
@@ -935,8 +890,6 @@ app.post("/____server_function____", async (req, res) => {
|
|
|
935
890
|
// Parse exports (you need to implement parseExports on server if not present)
|
|
936
891
|
const exports = parseExports(fileContent); // Assume you move parseExports to a shared util
|
|
937
892
|
allowedExports = new Set(exports);
|
|
938
|
-
// devCache.set(absolutePath, allowedExports);
|
|
939
|
-
// }
|
|
940
893
|
}
|
|
941
894
|
|
|
942
895
|
// Validate exportName against allowedExports
|
|
@@ -68,7 +68,7 @@ export default async function getEsbuildEntries({
|
|
|
68
68
|
continue;
|
|
69
69
|
}
|
|
70
70
|
|
|
71
|
-
//
|
|
71
|
+
// Read the file ONLY once
|
|
72
72
|
let importedCode;
|
|
73
73
|
try {
|
|
74
74
|
importedCode = readFileSync(absImportPathWithExt, "utf8");
|
|
@@ -81,12 +81,12 @@ export default async function getEsbuildEntries({
|
|
|
81
81
|
}
|
|
82
82
|
|
|
83
83
|
if (!isTopLevelClientComponent) {
|
|
84
|
-
//
|
|
84
|
+
// Verify if it is a client component
|
|
85
85
|
const isImportedFileClient = useClientRegex.test(importedCode.trim());
|
|
86
86
|
|
|
87
|
-
//
|
|
87
|
+
// If it is a client component, DO NOT process recursively
|
|
88
88
|
if (isImportedFileClient) {
|
|
89
|
-
continue; //
|
|
89
|
+
continue; // Do not recursively process client components
|
|
90
90
|
}
|
|
91
91
|
} else {
|
|
92
92
|
const isImportedFileServer = useServerRegex.test(importedCode.trim());
|
|
@@ -96,7 +96,7 @@ export default async function getEsbuildEntries({
|
|
|
96
96
|
}
|
|
97
97
|
}
|
|
98
98
|
|
|
99
|
-
//
|
|
99
|
+
// For non-client modules, process normally
|
|
100
100
|
if (
|
|
101
101
|
absImportPathWithExt.endsWith(".css") ||
|
|
102
102
|
absImportPathWithExt.endsWith(".scss") ||
|
|
@@ -111,15 +111,15 @@ export default async function getEsbuildEntries({
|
|
|
111
111
|
continue;
|
|
112
112
|
}
|
|
113
113
|
|
|
114
|
-
// 🚨
|
|
114
|
+
// 🚨 THE SOLUTION: If it is a JSON file, add it but DO NOT parse it with Babel
|
|
115
115
|
if (absImportPathWithExt.endsWith(".json")) {
|
|
116
116
|
imports.add(absImportPathWithExt);
|
|
117
|
-
continue; //
|
|
117
|
+
continue; // This prevents it from entering Babel recursion
|
|
118
118
|
}
|
|
119
119
|
|
|
120
120
|
imports.add(absImportPathWithExt);
|
|
121
121
|
|
|
122
|
-
//
|
|
122
|
+
// Process imports recursively for non-client modules
|
|
123
123
|
try {
|
|
124
124
|
const nested = await getImportsAndAssetsAndCsss(
|
|
125
125
|
importedCode,
|
|
@@ -16,31 +16,31 @@ export default async function write(result) {
|
|
|
16
16
|
entryPointNormalized = entryPointNormalized.replace("dinou-asset:", "");
|
|
17
17
|
}
|
|
18
18
|
const inputKeys = Object.keys(info.inputs);
|
|
19
|
-
//
|
|
19
|
+
// Logic for assets
|
|
20
20
|
if (
|
|
21
21
|
regex.test(entryPointNormalized) &&
|
|
22
22
|
inputKeys.length === 1 &&
|
|
23
23
|
inputKeys[0] === info.entryPoint
|
|
24
24
|
) {
|
|
25
|
-
//
|
|
25
|
+
// Skip the useless .js file
|
|
26
26
|
const normalizedPath = normalizeRel(relPath);
|
|
27
27
|
skipSet.add(normalizedPath);
|
|
28
|
-
//
|
|
28
|
+
// Skip the corresponding .map file if it exists
|
|
29
29
|
const mapRelPath = relPath.replace(/\.js$/, ".js.map");
|
|
30
30
|
if (result.metafile.outputs[mapRelPath]) {
|
|
31
31
|
skipSet.add(normalizeRel(mapRelPath));
|
|
32
32
|
}
|
|
33
33
|
}
|
|
34
|
-
//
|
|
34
|
+
// Logic for CSS
|
|
35
35
|
if (
|
|
36
36
|
cssRegex.test(info.entryPoint) &&
|
|
37
37
|
((inputKeys.length === 1 && inputKeys[0] === info.entryPoint) ||
|
|
38
38
|
inputKeys.length === 0)
|
|
39
39
|
) {
|
|
40
|
-
//
|
|
40
|
+
// Skip the useless .js file
|
|
41
41
|
const normalizedPath = normalizeRel(relPath);
|
|
42
42
|
skipSet.add(normalizedPath);
|
|
43
|
-
//
|
|
43
|
+
// Skip the corresponding .map file if it exists
|
|
44
44
|
const mapRelPath = relPath + ".map";
|
|
45
45
|
if (result.metafile.outputs[mapRelPath]) {
|
|
46
46
|
skipSet.add(normalizeRel(mapRelPath));
|
|
@@ -18,7 +18,7 @@ export default function assetsPlugin({ include = regex } = {}) {
|
|
|
18
18
|
|
|
19
19
|
build.initialOptions.assetNames = "assets/[name]-[hash]";
|
|
20
20
|
|
|
21
|
-
//
|
|
21
|
+
// Handle asset loading with different namespaces
|
|
22
22
|
build.onResolve({ filter: include }, (args) => {
|
|
23
23
|
// console.log("[assets-plugin] onResolve", args);
|
|
24
24
|
const resolvedAlias =
|
|
@@ -28,7 +28,7 @@ export default function assetsPlugin({ include = regex } = {}) {
|
|
|
28
28
|
parentURL: pathToFileURL(args.importer).href,
|
|
29
29
|
});
|
|
30
30
|
|
|
31
|
-
// CRUCIAL:
|
|
31
|
+
// CRUCIAL: Different namespace for entry points
|
|
32
32
|
if (args.kind === "entry-point") {
|
|
33
33
|
return {
|
|
34
34
|
path: resolvedAlias,
|
|
@@ -42,13 +42,13 @@ export default function assetsPlugin({ include = regex } = {}) {
|
|
|
42
42
|
};
|
|
43
43
|
});
|
|
44
44
|
|
|
45
|
-
// Loader
|
|
45
|
+
// Loader for normal assets
|
|
46
46
|
build.onLoad({ filter: /.*/, namespace: "dinou-asset" }, async (args) => {
|
|
47
47
|
const contents = await fs.readFile(args.path);
|
|
48
48
|
return { contents, loader: "file" };
|
|
49
49
|
});
|
|
50
50
|
|
|
51
|
-
// Loader
|
|
51
|
+
// Loader for asset entry points
|
|
52
52
|
build.onLoad(
|
|
53
53
|
{ filter: /.*/, namespace: "dinou-asset-entry" },
|
|
54
54
|
async (args) => {
|
|
@@ -64,7 +64,7 @@ export default function assetsPlugin({ include = regex } = {}) {
|
|
|
64
64
|
const normalizeRel = (p) => p.replace(/\\/g, "/");
|
|
65
65
|
const processedSourceFiles = new Set();
|
|
66
66
|
|
|
67
|
-
//
|
|
67
|
+
// First pass: normal assets (individual files)
|
|
68
68
|
for (const [oldRelPath, info] of Object.entries(
|
|
69
69
|
result.metafile.outputs
|
|
70
70
|
)) {
|
|
@@ -88,11 +88,11 @@ export default function assetsPlugin({ include = regex } = {}) {
|
|
|
88
88
|
processedSourceFiles.add(sourceFile);
|
|
89
89
|
}
|
|
90
90
|
|
|
91
|
-
//
|
|
91
|
+
// Second pass: find assets that are inside chunks
|
|
92
92
|
for (const [outputPath, info] of Object.entries(
|
|
93
93
|
result.metafile.outputs
|
|
94
94
|
)) {
|
|
95
|
-
//
|
|
95
|
+
// Only search in JavaScript chunks
|
|
96
96
|
if (!outputPath.endsWith(".js") || info.entryPoint) continue;
|
|
97
97
|
|
|
98
98
|
for (const inputPath of Object.keys(info.inputs)) {
|
|
@@ -111,10 +111,10 @@ export default function assetsPlugin({ include = regex } = {}) {
|
|
|
111
111
|
const scoped = createScopedName(base, sourceFile);
|
|
112
112
|
const newLocal = `assets/${scoped}${ext}`;
|
|
113
113
|
|
|
114
|
-
//
|
|
114
|
+
// Read the original asset
|
|
115
115
|
const assetContent = await fs.readFile(sourceFile);
|
|
116
116
|
|
|
117
|
-
//
|
|
117
|
+
// Create a new output file for this asset
|
|
118
118
|
const newOutputFile = {
|
|
119
119
|
path: path.join(outdir, newLocal),
|
|
120
120
|
contents: assetContent,
|
|
@@ -130,7 +130,7 @@ export default function assetsPlugin({ include = regex } = {}) {
|
|
|
130
130
|
// `Extracted asset from chunk: ${sourceFile} → ${newLocal}`
|
|
131
131
|
// );
|
|
132
132
|
|
|
133
|
-
//
|
|
133
|
+
// Find the chunk that contains this asset
|
|
134
134
|
const chunkFile = result.outputFiles.find(
|
|
135
135
|
(f) =>
|
|
136
136
|
normalizeRel(path.relative(process.cwd(), f.path)) ===
|
|
@@ -140,12 +140,12 @@ export default function assetsPlugin({ include = regex } = {}) {
|
|
|
140
140
|
if (chunkFile) {
|
|
141
141
|
let chunkContent = new TextDecoder().decode(chunkFile.contents);
|
|
142
142
|
|
|
143
|
-
//
|
|
143
|
+
// MORE PRECISE APPROACH: Find the specific comment for this asset
|
|
144
144
|
const assetComment = `// ${inputPath}`;
|
|
145
145
|
const commentIndex = chunkContent.indexOf(assetComment);
|
|
146
146
|
|
|
147
147
|
if (commentIndex !== -1) {
|
|
148
|
-
//
|
|
148
|
+
// Find the next line containing the variable assignment
|
|
149
149
|
const nextLineStart =
|
|
150
150
|
chunkContent.indexOf("\n", commentIndex) + 1;
|
|
151
151
|
const nextLineEnd = chunkContent.indexOf("\n", nextLineStart);
|
|
@@ -154,7 +154,7 @@ export default function assetsPlugin({ include = regex } = {}) {
|
|
|
154
154
|
nextLineEnd
|
|
155
155
|
);
|
|
156
156
|
|
|
157
|
-
//
|
|
157
|
+
// Extract the variable name (e.g., "dinou_default")
|
|
158
158
|
const varMatch = assignmentLine.match(
|
|
159
159
|
/var (\w+)_default = "([^"]+)"/
|
|
160
160
|
);
|
|
@@ -163,7 +163,7 @@ export default function assetsPlugin({ include = regex } = {}) {
|
|
|
163
163
|
const varName = varMatch[1];
|
|
164
164
|
const oldPath = varMatch[2];
|
|
165
165
|
|
|
166
|
-
//
|
|
166
|
+
// Replace ONLY this specific assignment
|
|
167
167
|
const newAssignmentLine = `var ${varName}_default = "/${newLocal}";`;
|
|
168
168
|
chunkContent =
|
|
169
169
|
chunkContent.substring(0, nextLineStart) +
|
|
@@ -175,7 +175,7 @@ export default function assetsPlugin({ include = regex } = {}) {
|
|
|
175
175
|
// );
|
|
176
176
|
}
|
|
177
177
|
} else {
|
|
178
|
-
// Fallback:
|
|
178
|
+
// Fallback: search by filename in the path
|
|
179
179
|
const assetName = path.basename(sourceFile);
|
|
180
180
|
const escapedAssetName = escapeRegExp(assetName);
|
|
181
181
|
const pattern = new RegExp(
|
|
@@ -202,7 +202,7 @@ export default function assetsPlugin({ include = regex } = {}) {
|
|
|
202
202
|
}
|
|
203
203
|
}
|
|
204
204
|
|
|
205
|
-
//
|
|
205
|
+
// Update references in JS/CSS files (existing code)
|
|
206
206
|
for (const file of result.outputFiles) {
|
|
207
207
|
const relPath = normalizeRel(path.relative(process.cwd(), file.path));
|
|
208
208
|
if (!relPath.endsWith(".js") && !relPath.endsWith(".css")) continue;
|
|
@@ -226,7 +226,7 @@ export default function assetsPlugin({ include = regex } = {}) {
|
|
|
226
226
|
file.contents = new TextEncoder().encode(content);
|
|
227
227
|
}
|
|
228
228
|
|
|
229
|
-
//
|
|
229
|
+
// Update paths (existing code)
|
|
230
230
|
for (const file of result.outputFiles) {
|
|
231
231
|
const relPath = normalizeRel(path.relative(process.cwd(), file.path));
|
|
232
232
|
const oldLocal = normalizeRel(path.relative(outdir, relPath));
|
|
@@ -7,10 +7,10 @@ export default function babelReactCompilerPlugin() {
|
|
|
7
7
|
name: "babel-react-compiler-bridge",
|
|
8
8
|
setup(build) {
|
|
9
9
|
const entryPoints = build.initialOptions.entryPoints;
|
|
10
|
-
//
|
|
11
|
-
//
|
|
10
|
+
// Intercept JS/TS/JSX/TSX files
|
|
11
|
+
// NOTE: Filter node_modules to avoid slowing down by processing external libraries
|
|
12
12
|
build.onLoad({ filter: /\.[jt]sx?$/ }, async (args) => {
|
|
13
|
-
//
|
|
13
|
+
// Double check security to avoid node_modules
|
|
14
14
|
if (args.path.includes("node_modules")) return;
|
|
15
15
|
const abs = path.resolve(args.path);
|
|
16
16
|
|
|
@@ -25,30 +25,30 @@ export default function babelReactCompilerPlugin() {
|
|
|
25
25
|
const source = await fs.readFile(args.path, "utf8");
|
|
26
26
|
const filename = args.path;
|
|
27
27
|
|
|
28
|
-
//
|
|
28
|
+
// Transform the code with Babel + React Compiler
|
|
29
29
|
const result = await babel.transformAsync(source, {
|
|
30
30
|
filename,
|
|
31
31
|
presets: [
|
|
32
|
-
//
|
|
32
|
+
// We need to tell Babel to understand React and TS before compiling
|
|
33
33
|
["@babel/preset-react", { runtime: "automatic" }],
|
|
34
34
|
"@babel/preset-typescript",
|
|
35
35
|
],
|
|
36
36
|
plugins: [
|
|
37
|
-
"babel-plugin-react-compiler", // 👈
|
|
37
|
+
"babel-plugin-react-compiler", // 👈 The jewel in the crown
|
|
38
38
|
],
|
|
39
|
-
sourceMaps: true, // Vital
|
|
40
|
-
configFile: false, //
|
|
39
|
+
sourceMaps: true, // Vital for esbuild sourcemaps to work
|
|
40
|
+
configFile: false, // Ignore global babel.config.js to get straight to the point
|
|
41
41
|
});
|
|
42
42
|
|
|
43
|
-
//
|
|
43
|
+
// If for some reason Babel does not return code, let esbuild handle it
|
|
44
44
|
if (!result || !result.code) return;
|
|
45
45
|
|
|
46
46
|
return {
|
|
47
47
|
contents: result.code,
|
|
48
|
-
loader: "js", // Babel
|
|
48
|
+
loader: "js", // Babel returns standard JS
|
|
49
49
|
};
|
|
50
50
|
} catch (error) {
|
|
51
|
-
//
|
|
51
|
+
// If Babel fails, show a clean error to not break the build silently
|
|
52
52
|
return {
|
|
53
53
|
errors: [
|
|
54
54
|
{
|
|
@@ -8,7 +8,7 @@ export default function serverFunctionsPlugin(manifestData = {}) {
|
|
|
8
8
|
name: "server-functions-proxy",
|
|
9
9
|
setup(build) {
|
|
10
10
|
const root = process.cwd();
|
|
11
|
-
const serverFunctions = new Map(); //
|
|
11
|
+
const serverFunctions = new Map(); // Collect server functions here: Map<relativePath, Set<exports>>
|
|
12
12
|
|
|
13
13
|
// 1. TRANSFORM FILES DURING BUILD
|
|
14
14
|
build.onLoad({ filter: /\.[jt]sx?$/ }, async (args) => {
|
|
@@ -20,11 +20,11 @@ export default function serverFunctionsPlugin(manifestData = {}) {
|
|
|
20
20
|
if (exports.length === 0) return null;
|
|
21
21
|
|
|
22
22
|
const relativePath = path.relative(root, args.path);
|
|
23
|
-
serverFunctions.set(relativePath, new Set(exports)); //
|
|
23
|
+
serverFunctions.set(relativePath, new Set(exports)); // Save exports as a Set to guarantee uniqueness
|
|
24
24
|
|
|
25
25
|
const fileUrl = `file:///${relativePath}`;
|
|
26
26
|
|
|
27
|
-
//
|
|
27
|
+
// Generate proxy code that forwards calls to the server instead of executing the actual code
|
|
28
28
|
let proxyCode = `
|
|
29
29
|
import { createServerFunctionProxy } from "/__SERVER_FUNCTION_PROXY__";
|
|
30
30
|
`;
|
|
@@ -69,13 +69,13 @@ export default function serverFunctionsPlugin(manifestData = {}) {
|
|
|
69
69
|
}
|
|
70
70
|
}
|
|
71
71
|
|
|
72
|
-
//
|
|
72
|
+
// Generate the final manifest by converting the Map to a plain object
|
|
73
73
|
const manifestObj = {};
|
|
74
74
|
for (const [path, exportsSet] of serverFunctions.entries()) {
|
|
75
75
|
manifestObj[path] = Array.from(exportsSet);
|
|
76
76
|
}
|
|
77
77
|
|
|
78
|
-
//
|
|
78
|
+
// Write the server functions manifest JSON file to the output directory
|
|
79
79
|
const manifestPath = path.join(
|
|
80
80
|
"server_functions_manifest",
|
|
81
81
|
"server-functions-manifest.json"
|
|
@@ -11,7 +11,7 @@ export default function stableChunkNamesAndMapsPlugin({ dev = true } = {}) {
|
|
|
11
11
|
|
|
12
12
|
const renames = new Map(); // oldFilename → newFilename (e.g., 'chunk-ABC.js' → 'chunk-stable.js')
|
|
13
13
|
const normalizeRel = (p) => p.replace(/\\/g, "/");
|
|
14
|
-
//
|
|
14
|
+
// First, rename chunks
|
|
15
15
|
for (const [oldRelPath, info] of Object.entries(
|
|
16
16
|
result.metafile.outputs
|
|
17
17
|
)) {
|
|
@@ -21,7 +21,7 @@ export default function stableChunkNamesAndMapsPlugin({ dev = true } = {}) {
|
|
|
21
21
|
(f) => f.startsWith("src/") && /\.(js|jsx|ts|tsx)$/.test(f)
|
|
22
22
|
);
|
|
23
23
|
if (!sourceFile) continue;
|
|
24
|
-
//
|
|
24
|
+
// Stable name based on the source file (always the same)
|
|
25
25
|
const rel = path.relative("src", sourceFile);
|
|
26
26
|
const normalizedRel = rel.replace(/\\/g, "/");
|
|
27
27
|
const dir = path.dirname(normalizedRel);
|
|
@@ -33,7 +33,7 @@ export default function stableChunkNamesAndMapsPlugin({ dev = true } = {}) {
|
|
|
33
33
|
dir === "." ? base : `${dir.replace(/\//g, "-")}-${base}`;
|
|
34
34
|
let finalName;
|
|
35
35
|
if (dev) {
|
|
36
|
-
finalName = `${stableName}.js`; // 100%
|
|
36
|
+
finalName = `${stableName}.js`; // 100% stable in dev
|
|
37
37
|
} else {
|
|
38
38
|
const hash = oldRelPath.match(/-([A-Z0-9]+)\./)?.[1] || "";
|
|
39
39
|
finalName = `${stableName}-${hash}.js`;
|
|
@@ -42,7 +42,7 @@ export default function stableChunkNamesAndMapsPlugin({ dev = true } = {}) {
|
|
|
42
42
|
const oldLocal = path.basename(oldRelPath);
|
|
43
43
|
renames.set(oldLocal, finalRelPath);
|
|
44
44
|
}
|
|
45
|
-
//
|
|
45
|
+
// Second, rename maps corresponding to the chunks
|
|
46
46
|
for (const [oldRelPath, info] of Object.entries(
|
|
47
47
|
result.metafile.outputs
|
|
48
48
|
)) {
|
|
@@ -56,7 +56,7 @@ export default function stableChunkNamesAndMapsPlugin({ dev = true } = {}) {
|
|
|
56
56
|
renames.set(oldMapLocal, newMapLocal);
|
|
57
57
|
}
|
|
58
58
|
}
|
|
59
|
-
//
|
|
59
|
+
// Step 3: Update references in importers (imports in .js)
|
|
60
60
|
const outputs = result.metafile.outputs;
|
|
61
61
|
const escapeRegExp = (string) =>
|
|
62
62
|
string.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
@@ -74,22 +74,22 @@ export default function stableChunkNamesAndMapsPlugin({ dev = true } = {}) {
|
|
|
74
74
|
const oldImportedLocal = path.basename(importedRelPath);
|
|
75
75
|
const newImportedLocal = renames.get(oldImportedLocal);
|
|
76
76
|
if (!newImportedLocal) continue;
|
|
77
|
-
//
|
|
77
|
+
// Replace for double quotes with ./
|
|
78
78
|
const oldDouble = `"./${escapeRegExp(oldImportedLocal)}"`;
|
|
79
79
|
const newDouble = `"./${newImportedLocal}"`;
|
|
80
80
|
content = content.replace(new RegExp(oldDouble, "g"), newDouble);
|
|
81
|
-
//
|
|
81
|
+
// Replace for double quotes without ./
|
|
82
82
|
const oldDoubleNoDot = `"${escapeRegExp(oldImportedLocal)}"`;
|
|
83
83
|
const newDoubleNoDot = `"${newImportedLocal}"`;
|
|
84
84
|
content = content.replace(
|
|
85
85
|
new RegExp(oldDoubleNoDot, "g"),
|
|
86
86
|
newDoubleNoDot
|
|
87
87
|
);
|
|
88
|
-
//
|
|
88
|
+
// Replace for single quotes with ./
|
|
89
89
|
const oldSingle = `'./${escapeRegExp(oldImportedLocal)}'`;
|
|
90
90
|
const newSingle = `'./${newImportedLocal}'`;
|
|
91
91
|
content = content.replace(new RegExp(oldSingle, "g"), newSingle);
|
|
92
|
-
//
|
|
92
|
+
// Replace for single quotes without ./
|
|
93
93
|
const oldSingleNoDot = `'${escapeRegExp(oldImportedLocal)}'`;
|
|
94
94
|
const newSingleNoDot = `'${newImportedLocal}'`;
|
|
95
95
|
content = content.replace(
|
|
@@ -99,7 +99,7 @@ export default function stableChunkNamesAndMapsPlugin({ dev = true } = {}) {
|
|
|
99
99
|
}
|
|
100
100
|
importerFile.contents = new TextEncoder().encode(content);
|
|
101
101
|
}
|
|
102
|
-
//
|
|
102
|
+
// New: Update sourceMappingURL in the .js files being renamed
|
|
103
103
|
for (const file of result.outputFiles) {
|
|
104
104
|
if (!file.path.endsWith(".js")) continue;
|
|
105
105
|
const oldRelPath = normalizeRel(
|
|
@@ -123,7 +123,7 @@ export default function stableChunkNamesAndMapsPlugin({ dev = true } = {}) {
|
|
|
123
123
|
);
|
|
124
124
|
file.contents = new TextEncoder().encode(content);
|
|
125
125
|
}
|
|
126
|
-
//
|
|
126
|
+
// Update paths in outputFiles for chunks and maps
|
|
127
127
|
for (const file of result.outputFiles) {
|
|
128
128
|
const relPath = normalizeRel(path.relative(process.cwd(), file.path));
|
|
129
129
|
const oldLocal = path.basename(relPath);
|
|
@@ -132,7 +132,7 @@ export default function stableChunkNamesAndMapsPlugin({ dev = true } = {}) {
|
|
|
132
132
|
file.path = path.join(path.dirname(file.path), newLocal);
|
|
133
133
|
}
|
|
134
134
|
}
|
|
135
|
-
//
|
|
135
|
+
// Update metafile for consistency
|
|
136
136
|
const newOutputs = {};
|
|
137
137
|
for (const oldRelPath in outputs) {
|
|
138
138
|
const oldLocal = path.basename(oldRelPath);
|
|
@@ -7,7 +7,7 @@ const createPostCSSExtractPlugin = (options = {}) => {
|
|
|
7
7
|
|
|
8
8
|
let extractedCSS = "";
|
|
9
9
|
|
|
10
|
-
//
|
|
10
|
+
// Define the PostCSS plugin to intercept and extract CSS rules
|
|
11
11
|
const postcssPlugin = {
|
|
12
12
|
postcssPlugin: "postcss-extract",
|
|
13
13
|
|
|
@@ -18,13 +18,13 @@ const createPostCSSExtractPlugin = (options = {}) => {
|
|
|
18
18
|
extractedCSS += root.toString();
|
|
19
19
|
extractedCSS += "\n";
|
|
20
20
|
|
|
21
|
-
//
|
|
21
|
+
// Remove all CSS rules from the original file to prevent duplicate injection
|
|
22
22
|
root.removeAll();
|
|
23
23
|
}
|
|
24
24
|
},
|
|
25
25
|
};
|
|
26
26
|
|
|
27
|
-
//
|
|
27
|
+
// Write the collected CSS contents to the final output file on disk
|
|
28
28
|
const finalize = () => {
|
|
29
29
|
if (!extractedCSS) return;
|
|
30
30
|
const outputDir = path.dirname(outputFile);
|