dinou 3.0.5 → 3.0.6
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 +6 -0
- package/dinou/core/parse-exports.js +40 -0
- package/dinou/core/server-function-proxy-webpack.js +12 -2
- package/dinou/core/server-function-proxy.js +4 -1
- package/dinou/core/server.js +186 -35
- package/dinou/esbuild/build.mjs +1 -0
- package/dinou/esbuild/dev.mjs +1 -0
- package/dinou/esbuild/plugins-esbuild/server-functions-plugin.mjs +27 -47
- package/dinou/rollup/rollup-plugins/rollup-plugin-server-functions.js +29 -38
- package/dinou/rollup/rollup.config.js +5 -1
- package/dinou/webpack/loaders/server-functions-loader.js +62 -53
- package/dinou/webpack/plugins/server-functions-plugin.js +74 -36
- package/dinou/webpack/webpack.config.js +36 -16
- package/package.json +5 -5
package/CHANGELOG.md
CHANGED
|
@@ -5,6 +5,12 @@ All notable changes to this project will be documented in this file.
|
|
|
5
5
|
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
|
6
6
|
and this project adheres to [Semantic Versioning](https://semver.org/).
|
|
7
7
|
|
|
8
|
+
## [3.0.6]
|
|
9
|
+
|
|
10
|
+
## Security
|
|
11
|
+
|
|
12
|
+
- Fix: Use fixes from React versions 19.2.3 and improve security of server function endpoint.
|
|
13
|
+
|
|
8
14
|
## [3.0.5]
|
|
9
15
|
|
|
10
16
|
## Security
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
const parser = require("@babel/parser");
|
|
2
|
+
const traverse = require("@babel/traverse");
|
|
3
|
+
|
|
4
|
+
function parseExports(code) {
|
|
5
|
+
const ast = parser.parse(code, {
|
|
6
|
+
sourceType: "module",
|
|
7
|
+
plugins: ["jsx", "typescript"],
|
|
8
|
+
});
|
|
9
|
+
|
|
10
|
+
const exports = new Set();
|
|
11
|
+
|
|
12
|
+
traverse.default(ast, {
|
|
13
|
+
ExportDefaultDeclaration() {
|
|
14
|
+
exports.add("default");
|
|
15
|
+
},
|
|
16
|
+
ExportNamedDeclaration(p) {
|
|
17
|
+
if (p.node.declaration) {
|
|
18
|
+
if (p.node.declaration.type === "FunctionDeclaration") {
|
|
19
|
+
exports.add(p.node.declaration.id.name);
|
|
20
|
+
} else if (p.node.declaration.type === "VariableDeclaration") {
|
|
21
|
+
p.node.declaration.declarations.forEach((d) => {
|
|
22
|
+
if (d.id.type === "Identifier") {
|
|
23
|
+
exports.add(d.id.name);
|
|
24
|
+
}
|
|
25
|
+
});
|
|
26
|
+
}
|
|
27
|
+
} else if (p.node.specifiers) {
|
|
28
|
+
p.node.specifiers.forEach((s) => {
|
|
29
|
+
if (s.type === "ExportSpecifier") {
|
|
30
|
+
exports.add(s.exported.name);
|
|
31
|
+
}
|
|
32
|
+
});
|
|
33
|
+
}
|
|
34
|
+
},
|
|
35
|
+
});
|
|
36
|
+
|
|
37
|
+
return [...exports];
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
module.exports = parseExports;
|
|
@@ -1,12 +1,15 @@
|
|
|
1
1
|
// public/server-function-proxy.js
|
|
2
2
|
import { createFromFetch } from "react-server-dom-webpack/client";
|
|
3
3
|
|
|
4
|
-
|
|
4
|
+
function createServerFunctionProxy(id) {
|
|
5
5
|
return new Proxy(() => {}, {
|
|
6
6
|
apply: async (_target, _thisArg, args) => {
|
|
7
7
|
const res = await fetch("/____server_function____", {
|
|
8
8
|
method: "POST",
|
|
9
|
-
headers: {
|
|
9
|
+
headers: {
|
|
10
|
+
"Content-Type": "application/json",
|
|
11
|
+
"x-server-function-call": "1",
|
|
12
|
+
},
|
|
10
13
|
body: JSON.stringify({ id, args }),
|
|
11
14
|
});
|
|
12
15
|
if (!res.ok) throw new Error("Server function failed");
|
|
@@ -21,3 +24,10 @@ export function createServerFunctionProxy(id) {
|
|
|
21
24
|
},
|
|
22
25
|
});
|
|
23
26
|
}
|
|
27
|
+
|
|
28
|
+
if (typeof window !== "undefined") {
|
|
29
|
+
window.__SERVER_FUNCTION_PROXY_LIB__ = { createServerFunctionProxy };
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
export { createServerFunctionProxy };
|
|
33
|
+
export default { createServerFunctionProxy };
|
|
@@ -6,7 +6,10 @@ export function createServerFunctionProxy(id) {
|
|
|
6
6
|
apply: async (_target, _thisArg, args) => {
|
|
7
7
|
const res = await fetch("/____server_function____", {
|
|
8
8
|
method: "POST",
|
|
9
|
-
headers: {
|
|
9
|
+
headers: {
|
|
10
|
+
"Content-Type": "application/json",
|
|
11
|
+
"x-server-function-call": "1",
|
|
12
|
+
},
|
|
10
13
|
body: JSON.stringify({ id, args }),
|
|
11
14
|
});
|
|
12
15
|
if (!res.ok) throw new Error("Server function failed");
|
package/dinou/core/server.js
CHANGED
|
@@ -41,6 +41,7 @@ const outputFolder = isDevelopment ? "public" : "dist3";
|
|
|
41
41
|
const chokidar = require("chokidar");
|
|
42
42
|
const { fileURLToPath } = require("url");
|
|
43
43
|
const isWebpack = process.env.DINOU_BUILD_TOOL === "webpack";
|
|
44
|
+
const parseExports = require("./parse-exports.js");
|
|
44
45
|
if (isDevelopment) {
|
|
45
46
|
const manifestPath = path.resolve(
|
|
46
47
|
process.cwd(),
|
|
@@ -217,6 +218,28 @@ if (isDevelopment) {
|
|
|
217
218
|
}
|
|
218
219
|
startManifestWatcher();
|
|
219
220
|
}
|
|
221
|
+
let serverFunctionsManifest = null;
|
|
222
|
+
// const devCache = new Map(); // Para dev: Map<absolutePath, Set<exports>>
|
|
223
|
+
|
|
224
|
+
if (!isDevelopment) {
|
|
225
|
+
// En prod/build: cargar manifest generado
|
|
226
|
+
const manifestPath = path.resolve(
|
|
227
|
+
process.cwd(),
|
|
228
|
+
isWebpack
|
|
229
|
+
? `${outputFolder}/server-functions-manifest.json`
|
|
230
|
+
: `server_functions_manifest/server-functions-manifest.json`
|
|
231
|
+
); // Ajusta 'dist/' a tu outdir
|
|
232
|
+
if (existsSync(manifestPath)) {
|
|
233
|
+
serverFunctionsManifest = JSON.parse(readFileSync(manifestPath, "utf8"));
|
|
234
|
+
// Convertir arrays a Sets para lookups rápidos
|
|
235
|
+
for (const key in serverFunctionsManifest) {
|
|
236
|
+
serverFunctionsManifest[key] = new Set(serverFunctionsManifest[key]);
|
|
237
|
+
}
|
|
238
|
+
console.log("[server] Loaded server functions manifest");
|
|
239
|
+
} else {
|
|
240
|
+
console.error("[server] Manifest not found - falling back to file reads");
|
|
241
|
+
}
|
|
242
|
+
}
|
|
220
243
|
const cookieParser = require("cookie-parser");
|
|
221
244
|
const appUseCookieParser = cookieParser();
|
|
222
245
|
const app = express();
|
|
@@ -235,6 +258,22 @@ app.get("/.well-known/appspecific/com.chrome.devtools.json", (req, res) => {
|
|
|
235
258
|
});
|
|
236
259
|
});
|
|
237
260
|
|
|
261
|
+
let cachedClientManifest = null;
|
|
262
|
+
if (!isDevelopment) {
|
|
263
|
+
// Carga inicial
|
|
264
|
+
cachedClientManifest = JSON.parse(
|
|
265
|
+
readFileSync(
|
|
266
|
+
path.resolve(
|
|
267
|
+
process.cwd(),
|
|
268
|
+
isWebpack
|
|
269
|
+
? `${outputFolder}/react-client-manifest.json`
|
|
270
|
+
: `react_client_manifest/react-client-manifest.json`
|
|
271
|
+
),
|
|
272
|
+
"utf8"
|
|
273
|
+
)
|
|
274
|
+
);
|
|
275
|
+
}
|
|
276
|
+
|
|
238
277
|
app.get(/^\/____rsc_payload____\/.*\/?$/, async (req, res) => {
|
|
239
278
|
try {
|
|
240
279
|
const reqPath = (
|
|
@@ -242,7 +281,17 @@ app.get(/^\/____rsc_payload____\/.*\/?$/, async (req, res) => {
|
|
|
242
281
|
).replace("/____rsc_payload____", "");
|
|
243
282
|
|
|
244
283
|
if (!isDevelopment && Object.keys({ ...req.query }).length === 0) {
|
|
245
|
-
const payloadPath = path.join("dist2", reqPath, "rsc.rsc");
|
|
284
|
+
// const payloadPath = path.join("dist2", reqPath, "rsc.rsc");
|
|
285
|
+
const payloadPath = path.resolve(
|
|
286
|
+
"dist2",
|
|
287
|
+
reqPath.replace(/^\//, ""),
|
|
288
|
+
"rsc.rsc"
|
|
289
|
+
);
|
|
290
|
+
const distDir = path.resolve("dist2");
|
|
291
|
+
|
|
292
|
+
if (!payloadPath.startsWith(distDir)) {
|
|
293
|
+
return res.status(403).end();
|
|
294
|
+
}
|
|
246
295
|
if (existsSync(payloadPath)) {
|
|
247
296
|
res.setHeader("Content-Type", "application/octet-stream");
|
|
248
297
|
const readStream = createReadStream(payloadPath);
|
|
@@ -259,16 +308,19 @@ app.get(/^\/____rsc_payload____\/.*\/?$/, async (req, res) => {
|
|
|
259
308
|
{ ...req.cookies },
|
|
260
309
|
isDevelopment
|
|
261
310
|
);
|
|
262
|
-
const manifest =
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
311
|
+
const manifest = isDevelopment
|
|
312
|
+
? JSON.parse(
|
|
313
|
+
readFileSync(
|
|
314
|
+
path.resolve(
|
|
315
|
+
process.cwd(),
|
|
316
|
+
isWebpack
|
|
317
|
+
? `${outputFolder}/react-client-manifest.json`
|
|
318
|
+
: `react_client_manifest/react-client-manifest.json`
|
|
319
|
+
),
|
|
320
|
+
"utf8"
|
|
321
|
+
)
|
|
322
|
+
)
|
|
323
|
+
: cachedClientManifest;
|
|
272
324
|
|
|
273
325
|
const { pipe } = renderToPipeableStream(jsx, manifest);
|
|
274
326
|
pipe(res);
|
|
@@ -284,17 +336,20 @@ app.post(/^\/____rsc_payload_error____\/.*\/?$/, async (req, res) => {
|
|
|
284
336
|
req.path.endsWith("/") ? req.path : req.path + "/"
|
|
285
337
|
).replace("/____rsc_payload_error____", "");
|
|
286
338
|
const jsx = await getErrorJSX(reqPath, { ...req.query }, req.body.error);
|
|
287
|
-
const manifest =
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
339
|
+
const manifest = isDevelopment
|
|
340
|
+
? JSON.parse(
|
|
341
|
+
readFileSync(
|
|
342
|
+
path.resolve(
|
|
343
|
+
process.cwd(),
|
|
344
|
+
isWebpack
|
|
345
|
+
? `${outputFolder}/react-client-manifest.json`
|
|
346
|
+
: `react_client_manifest/react-client-manifest.json`
|
|
347
|
+
),
|
|
348
|
+
"utf8"
|
|
349
|
+
)
|
|
350
|
+
)
|
|
351
|
+
: cachedClientManifest;
|
|
352
|
+
const { pipe } = renderToPipeableStream(jsx, manifest);
|
|
298
353
|
pipe(res);
|
|
299
354
|
} catch (error) {
|
|
300
355
|
console.error("Error rendering RSC:", error);
|
|
@@ -337,47 +392,143 @@ app.get(/^\/.*\/?$/, (req, res) => {
|
|
|
337
392
|
|
|
338
393
|
app.post("/____server_function____", async (req, res) => {
|
|
339
394
|
try {
|
|
395
|
+
// 1. Verificar Origin (Prevenir llamadas desde otros dominios)
|
|
396
|
+
const origin = req.headers.origin;
|
|
397
|
+
const host = req.headers.host;
|
|
398
|
+
|
|
399
|
+
// Nota: En local a veces origin es undefined o null, permitirlo en dev si es necesario
|
|
400
|
+
if (!isDevelopment && origin && !origin.includes(host)) {
|
|
401
|
+
return res.status(403).json({ error: "Invalid Origin" });
|
|
402
|
+
}
|
|
403
|
+
|
|
404
|
+
// 2. Verificar Header Personalizado (Defensa CSRF robusta)
|
|
405
|
+
// Asegúrate de que tu cliente (server-function-proxy.js) envíe este header
|
|
406
|
+
if (!req.headers["x-server-function-call"]) {
|
|
407
|
+
return res.status(403).json({ error: "Missing security header" });
|
|
408
|
+
}
|
|
340
409
|
const { id, args } = req.body;
|
|
410
|
+
|
|
411
|
+
// Validación básica de inputs: id debe ser string, args un array
|
|
412
|
+
if (typeof id !== "string" || !Array.isArray(args)) {
|
|
413
|
+
return res.status(400).json({ error: "Invalid request body" });
|
|
414
|
+
}
|
|
415
|
+
|
|
341
416
|
const [fileUrl, exportName] = id.split("#");
|
|
342
417
|
|
|
343
|
-
|
|
418
|
+
// Validar fileUrl: debe empezar con 'file://' y no contener caracteres sospechosos
|
|
419
|
+
if (!fileUrl.startsWith("file://")) {
|
|
420
|
+
return res.status(400).json({ error: "Invalid file URL format" });
|
|
421
|
+
}
|
|
422
|
+
|
|
423
|
+
// Extraer relativePath y normalizarlo (elimina 'file://' y posibles '/')
|
|
424
|
+
let relativePath = fileUrl.replace(/^file:\/\/\/?/, "").trim();
|
|
425
|
+
if (relativePath.startsWith("/") || relativePath.includes("..")) {
|
|
426
|
+
return res
|
|
427
|
+
.status(400)
|
|
428
|
+
.json({ error: "Invalid path: no absolute or traversal allowed" });
|
|
429
|
+
}
|
|
430
|
+
// console.log("relPath", relativePath);
|
|
431
|
+
// Restringir a carpeta 'src/': prepend 'src/' si no está, y resolver absolutePath
|
|
432
|
+
if (!relativePath.startsWith("src/") && !relativePath.startsWith("src\\")) {
|
|
433
|
+
relativePath = path.join("src", relativePath);
|
|
434
|
+
}
|
|
344
435
|
const absolutePath = path.resolve(process.cwd(), relativePath);
|
|
345
436
|
|
|
437
|
+
// Verificar que absolutePath esté estrictamente dentro de 'src/'
|
|
438
|
+
const srcDir = path.resolve(process.cwd(), "src");
|
|
439
|
+
if (!absolutePath.startsWith(srcDir + path.sep)) {
|
|
440
|
+
return res
|
|
441
|
+
.status(403)
|
|
442
|
+
.json({ error: "Access denied: file outside src directory" });
|
|
443
|
+
}
|
|
444
|
+
// console.log("absPath", absolutePath);
|
|
445
|
+
// Verificar que el archivo exista
|
|
446
|
+
if (!existsSync(absolutePath)) {
|
|
447
|
+
return res.status(404).json({ error: "File not found" });
|
|
448
|
+
}
|
|
449
|
+
|
|
450
|
+
let allowedExports;
|
|
451
|
+
if (serverFunctionsManifest) {
|
|
452
|
+
// Prod: usar manifest (relativePath ya está normalizado)
|
|
453
|
+
allowedExports = serverFunctionsManifest[relativePath];
|
|
454
|
+
} else {
|
|
455
|
+
// Dev: usar cache o verificar archivo
|
|
456
|
+
// allowedExports = devCache.get(absolutePath);
|
|
457
|
+
// if (!allowedExports) {
|
|
458
|
+
const fileContent = readFileSync(absolutePath, "utf8"); // Solo lee una vez
|
|
459
|
+
const firstLine = fileContent.trim().split("\n")[0].trim();
|
|
460
|
+
if (
|
|
461
|
+
!firstLine.startsWith('"use server"') &&
|
|
462
|
+
!firstLine.startsWith("'use server'")
|
|
463
|
+
) {
|
|
464
|
+
return res
|
|
465
|
+
.status(403)
|
|
466
|
+
.json({ error: "Not a valid server function file" });
|
|
467
|
+
}
|
|
468
|
+
// Parsear exports (necesitas implementar parseExports en server si no lo tienes)
|
|
469
|
+
const exports = parseExports(fileContent); // Asume que mueves parseExports a un util compartido
|
|
470
|
+
allowedExports = new Set(exports);
|
|
471
|
+
// devCache.set(absolutePath, allowedExports);
|
|
472
|
+
// }
|
|
473
|
+
}
|
|
474
|
+
|
|
475
|
+
// Validar exportName contra allowedExports
|
|
476
|
+
if (
|
|
477
|
+
!exportName ||
|
|
478
|
+
(exportName !== "default" && !allowedExports.has(exportName))
|
|
479
|
+
) {
|
|
480
|
+
return res.status(400).json({ error: "Invalid export name" });
|
|
481
|
+
}
|
|
482
|
+
|
|
483
|
+
// Proceder con la importación (usando tu importModule)
|
|
346
484
|
const mod = await importModule(absolutePath);
|
|
347
485
|
|
|
486
|
+
// Validar exportName: solo permitir 'default' u otros si defines una whitelist
|
|
487
|
+
if (!exportName || (exportName !== "default" && !mod[exportName])) {
|
|
488
|
+
return res.status(400).json({ error: "Invalid export name" });
|
|
489
|
+
}
|
|
348
490
|
const fn = exportName === "default" ? mod.default : mod[exportName];
|
|
349
491
|
|
|
350
492
|
if (typeof fn !== "function") {
|
|
351
493
|
return res.status(400).json({ error: "Export is not a function" });
|
|
352
494
|
}
|
|
353
495
|
|
|
496
|
+
// Ejecutar la función con context
|
|
354
497
|
const context = { req, res };
|
|
355
498
|
args.push(context);
|
|
499
|
+
if (args.length > fn.length + 1) {
|
|
500
|
+
return res.status(400).json({ error: "Invalid args length" });
|
|
501
|
+
}
|
|
356
502
|
const result = await fn(...args);
|
|
357
503
|
|
|
504
|
+
// Manejo del resultado (igual que antes, pero con chequeos extras si es necesario)
|
|
358
505
|
if (
|
|
359
506
|
result &&
|
|
360
507
|
result.$$typeof === Symbol.for("react.transitional.element")
|
|
361
508
|
) {
|
|
362
509
|
res.setHeader("Content-Type", "text/x-component");
|
|
363
|
-
const
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
: `react_client_manifest/react-client-manifest.json`
|
|
369
|
-
),
|
|
370
|
-
"utf8"
|
|
510
|
+
const manifestPath = path.resolve(
|
|
511
|
+
process.cwd(),
|
|
512
|
+
isWebpack
|
|
513
|
+
? `${outputFolder}/react-client-manifest.json`
|
|
514
|
+
: `react_client_manifest/react-client-manifest.json`
|
|
371
515
|
);
|
|
372
|
-
|
|
373
|
-
|
|
516
|
+
// Verificar que el manifest exista para evitar errores
|
|
517
|
+
if (!existsSync(manifestPath)) {
|
|
518
|
+
return res.status(500).json({ error: "Manifest not found" });
|
|
519
|
+
}
|
|
520
|
+
const manifest = isDevelopment
|
|
521
|
+
? JSON.parse(readFileSync(manifestPath, "utf8"))
|
|
522
|
+
: cachedClientManifest;
|
|
523
|
+
const { pipe } = renderToPipeableStream(result, manifest);
|
|
374
524
|
pipe(res);
|
|
375
525
|
} else {
|
|
376
526
|
res.json(result);
|
|
377
527
|
}
|
|
378
528
|
} catch (err) {
|
|
379
|
-
console.error(`Server function error [${req.body
|
|
380
|
-
|
|
529
|
+
console.error(`Server function error [${req.body?.id}]:`, err);
|
|
530
|
+
// En producción, no envíes err.message completo para evitar leaks
|
|
531
|
+
res.status(500).json({ error: "Internal server error" });
|
|
381
532
|
}
|
|
382
533
|
});
|
|
383
534
|
|
package/dinou/esbuild/build.mjs
CHANGED
|
@@ -11,6 +11,7 @@ const __dirname = path.dirname(__filename);
|
|
|
11
11
|
const outdir = "dist3";
|
|
12
12
|
await fs.rm(outdir, { recursive: true, force: true });
|
|
13
13
|
await fs.rm("react_client_manifest", { recursive: true, force: true });
|
|
14
|
+
await fs.rm("server_functions_manifest", { recursive: true, force: true });
|
|
14
15
|
|
|
15
16
|
const frameworkEntryPoints = {
|
|
16
17
|
main: path.resolve(__dirname, "../core/client.jsx"),
|
package/dinou/esbuild/dev.mjs
CHANGED
|
@@ -14,6 +14,7 @@ const __dirname = path.dirname(__filename);
|
|
|
14
14
|
const outdir = "public";
|
|
15
15
|
await fs.rm(outdir, { recursive: true, force: true });
|
|
16
16
|
await fs.rm("react_client_manifest", { recursive: true, force: true });
|
|
17
|
+
await fs.rm("server_functions_manifest", { recursive: true, force: true });
|
|
17
18
|
|
|
18
19
|
let currentCtx = null; // Track the active esbuild context
|
|
19
20
|
let debounceTimer = null; // For debouncing recreations
|
|
@@ -1,62 +1,29 @@
|
|
|
1
1
|
import path from "path";
|
|
2
|
-
import
|
|
3
|
-
import
|
|
4
|
-
import fs from "fs";
|
|
5
|
-
|
|
6
|
-
function parseExports(code) {
|
|
7
|
-
const ast = parser.parse(code, {
|
|
8
|
-
sourceType: "module",
|
|
9
|
-
plugins: ["jsx", "typescript"],
|
|
10
|
-
});
|
|
11
|
-
|
|
12
|
-
const exports = new Set();
|
|
13
|
-
|
|
14
|
-
traverse.default(ast, {
|
|
15
|
-
ExportDefaultDeclaration() {
|
|
16
|
-
exports.add("default");
|
|
17
|
-
},
|
|
18
|
-
ExportNamedDeclaration(p) {
|
|
19
|
-
if (p.node.declaration) {
|
|
20
|
-
if (p.node.declaration.type === "FunctionDeclaration") {
|
|
21
|
-
exports.add(p.node.declaration.id.name);
|
|
22
|
-
} else if (p.node.declaration.type === "VariableDeclaration") {
|
|
23
|
-
p.node.declaration.declarations.forEach((d) => {
|
|
24
|
-
if (d.id.type === "Identifier") {
|
|
25
|
-
exports.add(d.id.name);
|
|
26
|
-
}
|
|
27
|
-
});
|
|
28
|
-
}
|
|
29
|
-
} else if (p.node.specifiers) {
|
|
30
|
-
p.node.specifiers.forEach((s) => {
|
|
31
|
-
if (s.type === "ExportSpecifier") {
|
|
32
|
-
exports.add(s.exported.name);
|
|
33
|
-
}
|
|
34
|
-
});
|
|
35
|
-
}
|
|
36
|
-
},
|
|
37
|
-
});
|
|
38
|
-
|
|
39
|
-
return [...exports];
|
|
40
|
-
}
|
|
2
|
+
import fs from "node:fs/promises";
|
|
3
|
+
import parseExports from "../../core/parse-exports.js";
|
|
41
4
|
|
|
42
5
|
export default function serverFunctionsPlugin(manifestData = {}) {
|
|
43
6
|
return {
|
|
44
7
|
name: "server-functions-proxy",
|
|
45
8
|
setup(build) {
|
|
46
9
|
const root = process.cwd();
|
|
10
|
+
const serverFunctions = new Map(); // Recolectar aquí: Map<relativePath, Set<exports>>
|
|
47
11
|
|
|
48
12
|
// 1. TRANSFORM FILES DURING BUILD
|
|
49
13
|
build.onLoad({ filter: /\.[jt]sx?$/ }, async (args) => {
|
|
50
|
-
const code = await fs.
|
|
14
|
+
const code = await fs.readFile(args.path, "utf8");
|
|
51
15
|
|
|
52
16
|
if (!code.trim().startsWith('"use server"')) return null;
|
|
53
17
|
|
|
54
18
|
const exports = parseExports(code);
|
|
55
19
|
if (exports.length === 0) return null;
|
|
56
20
|
|
|
57
|
-
const
|
|
21
|
+
const relativePath = path.relative(root, args.path);
|
|
22
|
+
serverFunctions.set(relativePath, new Set(exports)); // Guardar exports como Set para uniqueness
|
|
23
|
+
|
|
24
|
+
const fileUrl = `file:///${relativePath}`;
|
|
58
25
|
|
|
59
|
-
// Proxy code
|
|
26
|
+
// Proxy code (igual que antes)
|
|
60
27
|
let proxyCode = `
|
|
61
28
|
import { createServerFunctionProxy } from "/__SERVER_FUNCTION_PROXY__";
|
|
62
29
|
`;
|
|
@@ -82,9 +49,8 @@ export default function serverFunctionsPlugin(manifestData = {}) {
|
|
|
82
49
|
};
|
|
83
50
|
});
|
|
84
51
|
|
|
85
|
-
// 2. REPLACE PLACEHOLDER AFTER BUILD
|
|
52
|
+
// 2. REPLACE PLACEHOLDER AND GENERATE MANIFEST AFTER BUILD
|
|
86
53
|
build.onEnd(async (result) => {
|
|
87
|
-
// console.log("[server-functions-proxy] manifest:", manifestData);
|
|
88
54
|
const hashedProxy =
|
|
89
55
|
"/" +
|
|
90
56
|
(manifestData["serverFunctionProxy.js"] || "serverFunctionProxy.js");
|
|
@@ -98,12 +64,26 @@ export default function serverFunctionsPlugin(manifestData = {}) {
|
|
|
98
64
|
/\/__SERVER_FUNCTION_PROXY__/g,
|
|
99
65
|
hashedProxy
|
|
100
66
|
);
|
|
101
|
-
// console.log(
|
|
102
|
-
// `[server-functions-proxy] Replaced __SERVER_FUNCTION_PROXY__ in ${outputFile.path}`
|
|
103
|
-
// );
|
|
104
67
|
outputFile.contents = new TextEncoder().encode(newCode);
|
|
105
68
|
}
|
|
106
69
|
}
|
|
70
|
+
|
|
71
|
+
// Generar manifest: convertir Map a objeto simple
|
|
72
|
+
const manifestObj = {};
|
|
73
|
+
for (const [path, exportsSet] of serverFunctions.entries()) {
|
|
74
|
+
manifestObj[path] = Array.from(exportsSet);
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
// Escribir el manifest en el output dir (ej. mismo lugar que otros assets)
|
|
78
|
+
const manifestPath = path.join(
|
|
79
|
+
"server_functions_manifest",
|
|
80
|
+
"server-functions-manifest.json"
|
|
81
|
+
);
|
|
82
|
+
await fs.mkdir(path.dirname(manifestPath), { recursive: true });
|
|
83
|
+
await fs.writeFile(manifestPath, JSON.stringify(manifestObj, null, 2));
|
|
84
|
+
// console.log(
|
|
85
|
+
// `[server-functions-proxy] Generated manifest at ${manifestPath}`
|
|
86
|
+
// );
|
|
107
87
|
});
|
|
108
88
|
},
|
|
109
89
|
};
|
|
@@ -1,45 +1,13 @@
|
|
|
1
1
|
// rollup-plugin-server-functions.js
|
|
2
2
|
const path = require("path");
|
|
3
|
-
const
|
|
4
|
-
const traverse = require("@babel/traverse").default;
|
|
3
|
+
const fs = require("fs/promises");
|
|
5
4
|
const manifestGeneratorPlugin = require("./manifest-generator-plugin");
|
|
6
|
-
|
|
7
|
-
function parseExports(code) {
|
|
8
|
-
const ast = parser.parse(code, {
|
|
9
|
-
sourceType: "module",
|
|
10
|
-
plugins: ["jsx", "typescript"],
|
|
11
|
-
});
|
|
12
|
-
const exports = new Set();
|
|
13
|
-
|
|
14
|
-
traverse(ast, {
|
|
15
|
-
ExportDefaultDeclaration() {
|
|
16
|
-
exports.add("default");
|
|
17
|
-
},
|
|
18
|
-
ExportNamedDeclaration(p) {
|
|
19
|
-
if (p.node.declaration) {
|
|
20
|
-
if (p.node.declaration.type === "FunctionDeclaration") {
|
|
21
|
-
exports.add(p.node.declaration.id.name);
|
|
22
|
-
} else if (p.node.declaration.type === "VariableDeclaration") {
|
|
23
|
-
p.node.declaration.declarations.forEach((d) => {
|
|
24
|
-
if (d.id.type === "Identifier") {
|
|
25
|
-
exports.add(d.id.name);
|
|
26
|
-
}
|
|
27
|
-
});
|
|
28
|
-
}
|
|
29
|
-
} else if (p.node.specifiers) {
|
|
30
|
-
p.node.specifiers.forEach((s) => {
|
|
31
|
-
if (s.type === "ExportSpecifier") {
|
|
32
|
-
exports.add(s.exported.name);
|
|
33
|
-
}
|
|
34
|
-
});
|
|
35
|
-
}
|
|
36
|
-
},
|
|
37
|
-
});
|
|
38
|
-
|
|
39
|
-
return Array.from(exports);
|
|
40
|
-
}
|
|
5
|
+
const parseExports = require("../../core/parse-exports.js");
|
|
41
6
|
|
|
42
7
|
function serverFunctionsPlugin() {
|
|
8
|
+
const root = process.cwd();
|
|
9
|
+
const serverFunctions = new Map(); // Recolectar aquí: Map<relativePath, Set<exports>>
|
|
10
|
+
|
|
43
11
|
return {
|
|
44
12
|
name: "server-functions-proxy",
|
|
45
13
|
transform(code, id) {
|
|
@@ -48,7 +16,10 @@ function serverFunctionsPlugin() {
|
|
|
48
16
|
const exports = parseExports(code);
|
|
49
17
|
if (exports.length === 0) return null;
|
|
50
18
|
|
|
51
|
-
const
|
|
19
|
+
const relativePath = path.relative(root, id);
|
|
20
|
+
serverFunctions.set(relativePath, new Set(exports)); // Guardar exports como Set para uniqueness
|
|
21
|
+
|
|
22
|
+
const fileUrl = `file:///${relativePath}`;
|
|
52
23
|
|
|
53
24
|
// Generamos un módulo que exporta proxies en lugar del código real
|
|
54
25
|
let proxyCode = `
|
|
@@ -90,6 +61,26 @@ function serverFunctionsPlugin() {
|
|
|
90
61
|
);
|
|
91
62
|
}
|
|
92
63
|
}
|
|
64
|
+
|
|
65
|
+
// Generar manifest: convertir Map a objeto simple
|
|
66
|
+
const manifestObj = {};
|
|
67
|
+
for (const [relPath, exportsSet] of serverFunctions.entries()) {
|
|
68
|
+
manifestObj[relPath] = Array.from(exportsSet);
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
// Escribir el manifest en la carpeta especificada (ej. mismo lugar que otros assets)
|
|
72
|
+
const manifestPath = path.join(
|
|
73
|
+
"server_functions_manifest",
|
|
74
|
+
"server-functions-manifest.json"
|
|
75
|
+
);
|
|
76
|
+
fs.mkdir(path.dirname(manifestPath), { recursive: true })
|
|
77
|
+
.then(() =>
|
|
78
|
+
fs.writeFile(manifestPath, JSON.stringify(manifestObj, null, 2))
|
|
79
|
+
)
|
|
80
|
+
.then(() => {
|
|
81
|
+
// console.log(`[rollup-server-functions] Generated manifest at ${manifestPath}`);
|
|
82
|
+
})
|
|
83
|
+
.catch(console.error);
|
|
93
84
|
},
|
|
94
85
|
};
|
|
95
86
|
}
|
|
@@ -60,7 +60,11 @@ module.exports = async function () {
|
|
|
60
60
|
],
|
|
61
61
|
plugins: [
|
|
62
62
|
del({
|
|
63
|
-
targets: [
|
|
63
|
+
targets: [
|
|
64
|
+
`${outputDirectory}/*`,
|
|
65
|
+
"react_client_manifest/*",
|
|
66
|
+
"server_functions_manifest/*",
|
|
67
|
+
],
|
|
64
68
|
runOnce: true,
|
|
65
69
|
hook: "buildStart",
|
|
66
70
|
}),
|
|
@@ -1,69 +1,78 @@
|
|
|
1
|
-
// server-functions-loader.js
|
|
2
|
-
const parser = require("@babel/parser");
|
|
3
|
-
const traverse = require("@babel/traverse").default;
|
|
1
|
+
// server-functions-loader-simple.js
|
|
4
2
|
const path = require("path");
|
|
3
|
+
const parseExports = require("../../core/parse-exports.js");
|
|
5
4
|
|
|
6
|
-
function
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
});
|
|
5
|
+
module.exports = function (source) {
|
|
6
|
+
// Detect "use server"
|
|
7
|
+
const lines = source.split("\n");
|
|
8
|
+
let hasUseServer = false;
|
|
11
9
|
|
|
12
|
-
const
|
|
10
|
+
for (const line of lines) {
|
|
11
|
+
const trimmed = line.trim();
|
|
12
|
+
if (
|
|
13
|
+
trimmed.startsWith('"use server"') ||
|
|
14
|
+
trimmed.startsWith("'use server'")
|
|
15
|
+
) {
|
|
16
|
+
hasUseServer = true;
|
|
17
|
+
break;
|
|
18
|
+
}
|
|
19
|
+
}
|
|
13
20
|
|
|
14
|
-
|
|
15
|
-
ExportDefaultDeclaration() {
|
|
16
|
-
exports.add("default");
|
|
17
|
-
},
|
|
18
|
-
ExportNamedDeclaration(p) {
|
|
19
|
-
if (p.node.declaration) {
|
|
20
|
-
if (p.node.declaration.type === "FunctionDeclaration") {
|
|
21
|
-
exports.add(p.node.declaration.id.name);
|
|
22
|
-
} else if (p.node.declaration.type === "VariableDeclaration") {
|
|
23
|
-
p.node.declaration.declarations.forEach((d) => {
|
|
24
|
-
if (d.id.type === "Identifier") {
|
|
25
|
-
exports.add(d.id.name);
|
|
26
|
-
}
|
|
27
|
-
});
|
|
28
|
-
}
|
|
29
|
-
} else if (p.node.specifiers) {
|
|
30
|
-
p.node.specifiers.forEach((s) => {
|
|
31
|
-
if (s.type === "ExportSpecifier") {
|
|
32
|
-
exports.add(s.exported.name);
|
|
33
|
-
}
|
|
34
|
-
});
|
|
35
|
-
}
|
|
36
|
-
},
|
|
37
|
-
});
|
|
21
|
+
if (!hasUseServer) return source;
|
|
38
22
|
|
|
39
|
-
|
|
40
|
-
|
|
23
|
+
const exports = parseExports(source);
|
|
24
|
+
if (exports.length === 0) return source;
|
|
41
25
|
|
|
42
|
-
|
|
43
|
-
|
|
26
|
+
// Build IDs
|
|
27
|
+
const moduleId = this.resourcePath;
|
|
28
|
+
const relativePath = path.relative(process.cwd(), moduleId);
|
|
29
|
+
const normalizedPath = relativePath.replace(/\\/g, "/");
|
|
44
30
|
|
|
45
|
-
const
|
|
46
|
-
const exports = parseExports(source);
|
|
47
|
-
if (exports.length === 0) return callback(null, source);
|
|
31
|
+
const fileUrl = `file:///${normalizedPath}`;
|
|
48
32
|
|
|
49
|
-
|
|
33
|
+
//
|
|
34
|
+
// IMPORTANT: dynamic import instead of static import
|
|
35
|
+
//
|
|
36
|
+
// Webpack will NOT try to resolve "__SERVER_FUNCTION_PROXY__"
|
|
37
|
+
// as a module → it will remain a string → replaced later → browser loads it.
|
|
50
38
|
|
|
51
|
-
let
|
|
39
|
+
let proxyCode = `
|
|
40
|
+
const loadProxy = new Function('return import("/"+"__SERVER_FUNCTION_PROXY__")');
|
|
41
|
+
`;
|
|
52
42
|
|
|
53
|
-
for (const
|
|
54
|
-
const key =
|
|
55
|
-
name === "default" ? `${fileUrl}#default` : `${fileUrl}#${name}`;
|
|
43
|
+
for (const exp of exports) {
|
|
44
|
+
const key = exp === "default" ? `${fileUrl}#default` : `${fileUrl}#${exp}`;
|
|
56
45
|
|
|
57
|
-
if (
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
46
|
+
if (exp === "default") {
|
|
47
|
+
proxyCode += `
|
|
48
|
+
export default (...args) =>
|
|
49
|
+
loadProxy().then(mod =>
|
|
50
|
+
(mod.default ?? mod ?? window.__SERVER_FUNCTION_PROXY_LIB__).createServerFunctionProxy(${JSON.stringify(
|
|
51
|
+
key
|
|
52
|
+
)})(...args)
|
|
53
|
+
);
|
|
54
|
+
`;
|
|
61
55
|
} else {
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
56
|
+
proxyCode += `
|
|
57
|
+
export const ${exp} = (...args) =>
|
|
58
|
+
loadProxy().then(mod => (mod.default ?? mod ?? window.__SERVER_FUNCTION_PROXY_LIB__).createServerFunctionProxy(${JSON.stringify(
|
|
59
|
+
key
|
|
60
|
+
)})(...args)
|
|
61
|
+
);
|
|
62
|
+
`;
|
|
65
63
|
}
|
|
66
64
|
}
|
|
67
65
|
|
|
68
|
-
|
|
66
|
+
// Emit manifest entry
|
|
67
|
+
const manifestEntry = {
|
|
68
|
+
path: normalizedPath,
|
|
69
|
+
exports: exports,
|
|
70
|
+
};
|
|
71
|
+
|
|
72
|
+
this.emitFile(
|
|
73
|
+
`server-functions/${normalizedPath}.json`,
|
|
74
|
+
JSON.stringify(manifestEntry, null, 2)
|
|
75
|
+
);
|
|
76
|
+
|
|
77
|
+
return proxyCode;
|
|
69
78
|
};
|
|
@@ -1,44 +1,82 @@
|
|
|
1
|
-
//
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
1
|
+
// webpack-server-functions-plugin-simple.js
|
|
2
|
+
const path = require("path");
|
|
3
|
+
const fs = require("fs");
|
|
4
|
+
const manifestGeneratorPlugin = require("./manifest-generator-plugin");
|
|
5
|
+
|
|
6
|
+
class WebpackServerFunctionsPluginSimple {
|
|
7
|
+
constructor() {
|
|
8
|
+
this.serverFunctions = new Map();
|
|
5
9
|
}
|
|
6
10
|
|
|
7
11
|
apply(compiler) {
|
|
8
|
-
const
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
12
|
+
const { webpack } = compiler;
|
|
13
|
+
const { Compilation, sources } = webpack;
|
|
14
|
+
|
|
15
|
+
// Recopilar archivos generados por el loader
|
|
16
|
+
compiler.hooks.thisCompilation.tap(
|
|
17
|
+
"WebpackServerFunctionsPluginSimple",
|
|
18
|
+
(compilation) => {
|
|
19
|
+
compilation.hooks.processAssets.tap(
|
|
20
|
+
{
|
|
21
|
+
name: "WebpackServerFunctionsPluginSimple",
|
|
22
|
+
stage: Compilation.PROCESS_ASSETS_STAGE_REPORT,
|
|
23
|
+
},
|
|
24
|
+
(assets) => {
|
|
25
|
+
// 1. Reemplazar placeholder
|
|
26
|
+
const manifest = manifestGeneratorPlugin.manifestData;
|
|
27
|
+
const hashedPath =
|
|
28
|
+
// "/" +
|
|
29
|
+
manifest["serverFunctionProxy.js"] || "serverFunctionProxy.js";
|
|
30
|
+
|
|
31
|
+
for (const [filename, asset] of Object.entries(assets)) {
|
|
32
|
+
if (filename.endsWith(".js")) {
|
|
33
|
+
let code = asset.source();
|
|
34
|
+
if (code.includes("__SERVER_FUNCTION_PROXY__")) {
|
|
35
|
+
code = code.replace(/__SERVER_FUNCTION_PROXY__/g, hashedPath);
|
|
36
|
+
compilation.updateAsset(
|
|
37
|
+
filename,
|
|
38
|
+
new sources.RawSource(code)
|
|
39
|
+
);
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
// 2. Recopilar todos los archivos de server functions
|
|
45
|
+
const serverFunctionsManifest = {};
|
|
32
46
|
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
47
|
+
for (const [filename, asset] of Object.entries(assets)) {
|
|
48
|
+
if (
|
|
49
|
+
filename.startsWith("server-functions/") &&
|
|
50
|
+
filename.endsWith(".json")
|
|
51
|
+
) {
|
|
52
|
+
try {
|
|
53
|
+
const content = asset.source();
|
|
54
|
+
const entry = JSON.parse(content);
|
|
55
|
+
serverFunctionsManifest[entry.path] = entry.exports;
|
|
56
|
+
|
|
57
|
+
// Eliminar este archivo temporal
|
|
58
|
+
delete assets[filename];
|
|
59
|
+
} catch (e) {
|
|
60
|
+
// Ignorar errores de parsing
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
// 3. Generar manifest final
|
|
66
|
+
const manifestContent = JSON.stringify(
|
|
67
|
+
serverFunctionsManifest,
|
|
68
|
+
null,
|
|
69
|
+
2
|
|
70
|
+
);
|
|
71
|
+
compilation.emitAsset(
|
|
72
|
+
"server-functions-manifest.json",
|
|
73
|
+
new sources.RawSource(manifestContent)
|
|
74
|
+
);
|
|
37
75
|
}
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
76
|
+
);
|
|
77
|
+
}
|
|
78
|
+
);
|
|
41
79
|
}
|
|
42
80
|
}
|
|
43
81
|
|
|
44
|
-
module.exports =
|
|
82
|
+
module.exports = WebpackServerFunctionsPluginSimple;
|
|
@@ -51,11 +51,24 @@ module.exports = async () => {
|
|
|
51
51
|
{}
|
|
52
52
|
),
|
|
53
53
|
},
|
|
54
|
+
experiments: {
|
|
55
|
+
outputModule: true,
|
|
56
|
+
},
|
|
54
57
|
output: {
|
|
55
58
|
path: path.resolve(process.cwd(), outputDirectory),
|
|
56
59
|
filename: "[name]-[contenthash].js",
|
|
57
60
|
publicPath: "/",
|
|
58
61
|
clean: true,
|
|
62
|
+
library: {
|
|
63
|
+
type: "module",
|
|
64
|
+
},
|
|
65
|
+
environment: {
|
|
66
|
+
module: true,
|
|
67
|
+
},
|
|
68
|
+
// module: true,
|
|
69
|
+
chunkFormat: "module", // Ensures non-entry chunks (like serverFunctionProxy) output as ESM
|
|
70
|
+
// // Optional: If webpack renames to .mjs, force .js
|
|
71
|
+
// chunkFilename: "[name]-[contenthash].js",
|
|
59
72
|
},
|
|
60
73
|
module: {
|
|
61
74
|
noParse: [/dist3/, /public/],
|
|
@@ -65,18 +78,7 @@ module.exports = async () => {
|
|
|
65
78
|
// include: path.resolve(process.cwd(), "dist3"),
|
|
66
79
|
// use: "null-loader",
|
|
67
80
|
// },
|
|
68
|
-
|
|
69
|
-
test: /\.[jt]sx?$/,
|
|
70
|
-
include: path.resolve(process.cwd(), "src"),
|
|
71
|
-
use: [
|
|
72
|
-
{
|
|
73
|
-
loader: path.resolve(
|
|
74
|
-
__dirname,
|
|
75
|
-
"./loaders/server-functions-loader.js"
|
|
76
|
-
),
|
|
77
|
-
},
|
|
78
|
-
],
|
|
79
|
-
},
|
|
81
|
+
|
|
80
82
|
{
|
|
81
83
|
test: /\.(js|jsx|ts|tsx)$/,
|
|
82
84
|
include: [
|
|
@@ -91,7 +93,6 @@ module.exports = async () => {
|
|
|
91
93
|
"@babel/preset-typescript",
|
|
92
94
|
],
|
|
93
95
|
plugins: [
|
|
94
|
-
"@babel/plugin-transform-modules-commonjs",
|
|
95
96
|
"@babel/plugin-syntax-import-meta",
|
|
96
97
|
// isDevelopment && require.resolve("react-refresh/babel"),
|
|
97
98
|
].filter(Boolean),
|
|
@@ -103,6 +104,18 @@ module.exports = async () => {
|
|
|
103
104
|
path.resolve(process.cwd(), "public"),
|
|
104
105
|
],
|
|
105
106
|
},
|
|
107
|
+
{
|
|
108
|
+
test: /\.[jt]sx?$/,
|
|
109
|
+
include: path.resolve(process.cwd(), "src"),
|
|
110
|
+
use: [
|
|
111
|
+
{
|
|
112
|
+
loader: path.resolve(
|
|
113
|
+
__dirname,
|
|
114
|
+
"./loaders/server-functions-loader.js"
|
|
115
|
+
),
|
|
116
|
+
},
|
|
117
|
+
],
|
|
118
|
+
},
|
|
106
119
|
{
|
|
107
120
|
test: /\.module\.css$/,
|
|
108
121
|
use: [
|
|
@@ -198,9 +211,11 @@ module.exports = async () => {
|
|
|
198
211
|
]
|
|
199
212
|
: [],
|
|
200
213
|
},
|
|
201
|
-
externals: {
|
|
202
|
-
|
|
203
|
-
|
|
214
|
+
// externals: {
|
|
215
|
+
// // __SERVER_FUNCTION_PROXY__: "__SERVER_FUNCTION_PROXY__",
|
|
216
|
+
// // "/__SERVER_FUNCTION_PROXY__": "/__SERVER_FUNCTION_PROXY__",
|
|
217
|
+
// // serverFunctionProxy: "/serverFunctionProxy.js",
|
|
218
|
+
// },
|
|
204
219
|
optimization: {
|
|
205
220
|
splitChunks: {
|
|
206
221
|
cacheGroups: {
|
|
@@ -216,6 +231,10 @@ module.exports = async () => {
|
|
|
216
231
|
watchOptions: {
|
|
217
232
|
ignored: ["public/", "dist3/"],
|
|
218
233
|
},
|
|
234
|
+
stats: "normal", // o 'verbose' en dev
|
|
235
|
+
infrastructureLogging: {
|
|
236
|
+
level: "info",
|
|
237
|
+
},
|
|
219
238
|
...(isDevelopment
|
|
220
239
|
? {
|
|
221
240
|
devServer: {
|
|
@@ -232,6 +251,7 @@ module.exports = async () => {
|
|
|
232
251
|
changeOrigin: true,
|
|
233
252
|
},
|
|
234
253
|
],
|
|
254
|
+
client: false,
|
|
235
255
|
},
|
|
236
256
|
}
|
|
237
257
|
: {}),
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "dinou",
|
|
3
|
-
"version": "3.0.
|
|
3
|
+
"version": "3.0.6",
|
|
4
4
|
"description": "Dinou is a modern React 19 framework with React Server Components, Server Functions, and streaming SSR.",
|
|
5
5
|
"main": "index.js",
|
|
6
6
|
"bin": {
|
|
@@ -41,7 +41,7 @@
|
|
|
41
41
|
"@babel/traverse": "^7.28.3",
|
|
42
42
|
"@esbuild-plugins/tsconfig-paths": "^0.1.2",
|
|
43
43
|
"@pmmmwh/react-refresh-webpack-plugin": "^0.6.2",
|
|
44
|
-
"@roggc/react-server-dom-esm": "^0.0.0-
|
|
44
|
+
"@roggc/react-server-dom-esm": "^0.0.0-b061b597-20251212",
|
|
45
45
|
"@rollup/plugin-babel": "^6.0.4",
|
|
46
46
|
"@rollup/plugin-commonjs": "^28.0.6",
|
|
47
47
|
"@rollup/plugin-json": "^6.1.0",
|
|
@@ -70,7 +70,7 @@
|
|
|
70
70
|
"postcss-loader": "^8.2.0",
|
|
71
71
|
"postcss-modules": "^6.0.1",
|
|
72
72
|
"react-refresh": "^0.17.0",
|
|
73
|
-
"react-server-dom-webpack": "^19.2.
|
|
73
|
+
"react-server-dom-webpack": "^19.2.3",
|
|
74
74
|
"rollup": "^4.46.2",
|
|
75
75
|
"rollup-plugin-copy": "^3.5.0",
|
|
76
76
|
"rollup-plugin-delete": "^3.0.1",
|
|
@@ -85,7 +85,7 @@
|
|
|
85
85
|
"ws": "^8.18.3"
|
|
86
86
|
},
|
|
87
87
|
"peerDependencies": {
|
|
88
|
-
"react": ">=19.2.
|
|
89
|
-
"react-dom": ">=19.2.
|
|
88
|
+
"react": ">=19.2.3",
|
|
89
|
+
"react-dom": ">=19.2.3"
|
|
90
90
|
}
|
|
91
91
|
}
|