dinou 4.0.2 → 4.0.4

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.
@@ -38,7 +38,8 @@ const isHashChangeOnly = (finalPath) => {
38
38
  );
39
39
  };
40
40
 
41
- const getRSCPayload = (url) => {
41
+ const getRSCPayload = (rscKey) => {
42
+ const url = rscKey.split("::")[0];
42
43
  // Important: url must already be normalized here
43
44
  if (cache.has(url)) return cache.get(url);
44
45
 
@@ -55,7 +56,7 @@ const getRSCPayload = (url) => {
55
56
  name: window.__DINOU_ERROR_NAME__,
56
57
  },
57
58
  }),
58
- })
59
+ }),
59
60
  );
60
61
  cache.set(url, content);
61
62
  return content;
@@ -113,7 +114,7 @@ function Router() {
113
114
  // Normal RSC Navigation
114
115
  scrollCache.set(
115
116
  window.location.pathname + window.location.search,
116
- window.scrollY
117
+ window.scrollY,
117
118
  );
118
119
  // cache.delete(finalPath);
119
120
  if (options.replace) {
@@ -231,7 +232,8 @@ function Router() {
231
232
  }, [route]);
232
233
 
233
234
  // RSC Logic
234
- const content = getRSCPayload(route);
235
+ const rscKey = route + "::" + version;
236
+ const content = getRSCPayload(rscKey);
235
237
 
236
238
  const contextValue = useMemo(
237
239
  () => ({
@@ -242,7 +244,7 @@ function Router() {
242
244
  refresh,
243
245
  isPending,
244
246
  }),
245
- [route, isPending]
247
+ [route, isPending],
246
248
  );
247
249
 
248
250
  return (
@@ -38,7 +38,8 @@ const isHashChangeOnly = (finalPath) => {
38
38
  );
39
39
  };
40
40
 
41
- const getRSCPayload = (url) => {
41
+ const getRSCPayload = (rscKey) => {
42
+ const url = rscKey.split("::")[0];
42
43
  // Important: url must already be normalized here
43
44
  if (cache.has(url)) return cache.get(url);
44
45
 
@@ -55,7 +56,7 @@ const getRSCPayload = (url) => {
55
56
  name: window.__DINOU_ERROR_NAME__,
56
57
  },
57
58
  }),
58
- })
59
+ }),
59
60
  );
60
61
  cache.set(url, content);
61
62
  return content;
@@ -113,7 +114,7 @@ function Router() {
113
114
  // Normal RSC Navigation
114
115
  scrollCache.set(
115
116
  window.location.pathname + window.location.search,
116
- window.scrollY
117
+ window.scrollY,
117
118
  );
118
119
  // cache.delete(finalPath);
119
120
  if (options.replace) {
@@ -231,7 +232,8 @@ function Router() {
231
232
  }, [route]);
232
233
 
233
234
  // RSC Logic
234
- const content = getRSCPayload(route);
235
+ const rscKey = route + "::" + version;
236
+ const content = getRSCPayload(rscKey);
235
237
 
236
238
  const contextValue = useMemo(
237
239
  () => ({
@@ -242,7 +244,7 @@ function Router() {
242
244
  refresh,
243
245
  isPending,
244
246
  }),
245
- [route, isPending]
247
+ [route, isPending],
246
248
  );
247
249
 
248
250
  return (
@@ -38,7 +38,8 @@ const isHashChangeOnly = (finalPath) => {
38
38
  );
39
39
  };
40
40
 
41
- const getRSCPayload = (url) => {
41
+ const getRSCPayload = (rscKey) => {
42
+ const url = rscKey.split("::")[0];
42
43
  // 1. Check Idempotence (Avoids the infinite loop of React)
43
44
  if (cache.has(url)) {
44
45
  return cache.get(url);
@@ -52,8 +53,8 @@ const getRSCPayload = (url) => {
52
53
  ? "/____rsc_payload_old_static____" + url
53
54
  : "/____rsc_payload_old____" + url
54
55
  : window.__DINOU_USE_STATIC__
55
- ? "/____rsc_payload_static____" + url
56
- : "/____rsc_payload____" + url;
56
+ ? "/____rsc_payload_static____" + url
57
+ : "/____rsc_payload____" + url;
57
58
 
58
59
  // Clean flags immediately
59
60
  window.__DINOU_USE_OLD_RSC__ = false;
@@ -120,7 +121,7 @@ function Router() {
120
121
  // Normal RSC Navigation
121
122
  scrollCache.set(
122
123
  window.location.pathname + window.location.search,
123
- window.scrollY
124
+ window.scrollY,
124
125
  );
125
126
  // cache.delete(finalPath);
126
127
  if (options.replace) {
@@ -238,7 +239,8 @@ function Router() {
238
239
  }, [route]);
239
240
 
240
241
  // RSC Logic
241
- const content = getRSCPayload(route);
242
+ const rscKey = route + "::" + version;
243
+ const content = getRSCPayload(rscKey);
242
244
 
243
245
  const contextValue = useMemo(
244
246
  () => ({
@@ -249,7 +251,7 @@ function Router() {
249
251
  refresh,
250
252
  isPending,
251
253
  }),
252
- [route, isPending]
254
+ [route, isPending],
253
255
  );
254
256
 
255
257
  return (
@@ -39,7 +39,8 @@ const isHashChangeOnly = (finalPath) => {
39
39
  );
40
40
  };
41
41
 
42
- const getRSCPayload = (url) => {
42
+ const getRSCPayload = (rscKey) => {
43
+ const url = rscKey.split("::")[0];
43
44
  // 1. Check Idempotence (Avoids the infinite loop of React)
44
45
  if (cache.has(url)) {
45
46
  return cache.get(url);
@@ -53,8 +54,8 @@ const getRSCPayload = (url) => {
53
54
  ? "/____rsc_payload_old_static____" + url
54
55
  : "/____rsc_payload_old____" + url
55
56
  : window.__DINOU_USE_STATIC__
56
- ? "/____rsc_payload_static____" + url
57
- : "/____rsc_payload____" + url;
57
+ ? "/____rsc_payload_static____" + url
58
+ : "/____rsc_payload____" + url;
58
59
 
59
60
  // Clean flags immediately
60
61
  window.__DINOU_USE_OLD_RSC__ = false;
@@ -121,7 +122,7 @@ function Router() {
121
122
  // Normal RSC Navigation
122
123
  scrollCache.set(
123
124
  window.location.pathname + window.location.search,
124
- window.scrollY
125
+ window.scrollY,
125
126
  );
126
127
  // cache.delete(finalPath);
127
128
  if (options.replace) {
@@ -239,7 +240,8 @@ function Router() {
239
240
  }, [route]);
240
241
 
241
242
  // RSC Logic
242
- const content = getRSCPayload(route);
243
+ const rscKey = route + "::" + version;
244
+ const content = getRSCPayload(rscKey);
243
245
 
244
246
  const contextValue = useMemo(
245
247
  () => ({
@@ -250,7 +252,7 @@ function Router() {
250
252
  refresh,
251
253
  isPending,
252
254
  }),
253
- [route, isPending]
255
+ [route, isPending],
254
256
  );
255
257
 
256
258
  return (
@@ -4,13 +4,29 @@ import { createFromFetch } from "react-server-dom-webpack/client";
4
4
  function createServerFunctionProxy(id) {
5
5
  return new Proxy(() => {}, {
6
6
  apply: async (_target, _thisArg, args) => {
7
+ let body;
8
+ const headers = {
9
+ "x-server-function-call": "1",
10
+ };
11
+
12
+ if (args[0] instanceof FormData) {
13
+ const formData = args[0];
14
+
15
+ formData.append("__dinou_func_id", id);
16
+
17
+ if (args.length > 1) {
18
+ formData.append("__dinou_args", JSON.stringify(args.slice(1)));
19
+ }
20
+
21
+ body = formData;
22
+ } else {
23
+ headers["Content-Type"] = "application/json";
24
+ body = JSON.stringify({ id, args });
25
+ }
7
26
  const res = await fetch("/____server_function____", {
8
27
  method: "POST",
9
- headers: {
10
- "Content-Type": "application/json",
11
- "x-server-function-call": "1",
12
- },
13
- body: JSON.stringify({ id, args }),
28
+ headers,
29
+ body,
14
30
  });
15
31
 
16
32
  if (!res.ok) throw new Error("Server function failed");
@@ -48,7 +64,7 @@ function createServerFunctionProxy(id) {
48
64
  // CHECK: We search for the tag start, whether it has the closing > or not
49
65
  if (buffer.includes("<script")) {
50
66
  console.warn(
51
- "[Dinou] Stream ended with incomplete script. Discarding tail."
67
+ "[Dinou] Stream ended with incomplete script. Discarding tail.",
52
68
  );
53
69
  // We do not enqueue.
54
70
  } else {
@@ -86,7 +102,7 @@ function createServerFunctionProxy(id) {
86
102
  // Once executed, we remove them so they don't go to React
87
103
  buffer = buffer.replace(scriptRegex, "");
88
104
 
89
- // 4. CALCULATE WHAT IS SAFE TO SEND (Anti-cut logic)
105
+ // 4. CALCULATE WHAT IS SAFE TO SEND (The anti-cut logic)
90
106
  // We need to know if the buffer ends with something that LOOKS like the start of a script
91
107
  // Dangerous patterns at the end: <, <s, <sc, <scr, <scri, <scrip, <script
92
108
 
@@ -4,13 +4,29 @@ import { createFromFetch } from "@roggc/react-server-dom-esm/client";
4
4
  export function createServerFunctionProxy(id) {
5
5
  return new Proxy(() => {}, {
6
6
  apply: async (_target, _thisArg, args) => {
7
+ let body;
8
+ const headers = {
9
+ "x-server-function-call": "1",
10
+ };
11
+
12
+ if (args[0] instanceof FormData) {
13
+ const formData = args[0];
14
+
15
+ formData.append("__dinou_func_id", id);
16
+
17
+ if (args.length > 1) {
18
+ formData.append("__dinou_args", JSON.stringify(args.slice(1)));
19
+ }
20
+
21
+ body = formData;
22
+ } else {
23
+ headers["Content-Type"] = "application/json";
24
+ body = JSON.stringify({ id, args });
25
+ }
7
26
  const res = await fetch("/____server_function____", {
8
27
  method: "POST",
9
- headers: {
10
- "Content-Type": "application/json",
11
- "x-server-function-call": "1",
12
- },
13
- body: JSON.stringify({ id, args }),
28
+ headers,
29
+ body,
14
30
  });
15
31
 
16
32
  if (!res.ok) throw new Error("Server function failed");
@@ -48,7 +64,7 @@ export function createServerFunctionProxy(id) {
48
64
  // CHECK: We search for the tag start, whether it has the closing > or not
49
65
  if (buffer.includes("<script")) {
50
66
  console.warn(
51
- "[Dinou] Stream ended with incomplete script. Discarding tail."
67
+ "[Dinou] Stream ended with incomplete script. Discarding tail.",
52
68
  );
53
69
  // We do not enqueue.
54
70
  } else {
@@ -54,11 +54,11 @@ if (isDevelopment) {
54
54
  process.cwd(),
55
55
  isWebpack
56
56
  ? `${outputFolder}/react-client-manifest.json`
57
- : `react_client_manifest/react-client-manifest.json`
57
+ : `react_client_manifest/react-client-manifest.json`,
58
58
  );
59
59
  const manifestFolderPath = path.resolve(
60
60
  process.cwd(),
61
- isWebpack ? outputFolder : "react_client_manifest"
61
+ isWebpack ? outputFolder : "react_client_manifest",
62
62
  );
63
63
 
64
64
  let manifestWatcher = null;
@@ -139,7 +139,7 @@ if (isDevelopment) {
139
139
  }
140
140
  } catch (err) {
141
141
  console.warn(
142
- `[Server HMR] Could not resolve or clear ${modulePath}: ${err.message}`
142
+ `[Server HMR] Could not resolve or clear ${modulePath}: ${err.message}`,
143
143
  );
144
144
  }
145
145
  }
@@ -234,7 +234,7 @@ if (!isDevelopment) {
234
234
  process.cwd(),
235
235
  isWebpack
236
236
  ? `${outputFolder}/server-functions-manifest.json`
237
- : `server_functions_manifest/server-functions-manifest.json`
237
+ : `server_functions_manifest/server-functions-manifest.json`,
238
238
  ); // Adjust 'dist/' to your outdir
239
239
  if (existsSync(manifestPath)) {
240
240
  serverFunctionsManifest = JSON.parse(readFileSync(manifestPath, "utf8"));
@@ -258,7 +258,7 @@ function getContext(req, res) {
258
258
  const safeResCall = (methodName, ...args) => {
259
259
  if (res.headersSent) {
260
260
  console.log(
261
- `[Dinou] res.${methodName} called but headers already sent. Ignoring.`
261
+ `[Dinou] res.${methodName} called but headers already sent. Ignoring.`,
262
262
  );
263
263
  // console.warn(
264
264
  // `[Dinou Warning] RSC Stream active. Ignoring res.${methodName}() to avoid crash.`
@@ -301,6 +301,12 @@ function getContext(req, res) {
301
301
  cookie: req.headers["cookie"],
302
302
  referer: req.headers["referer"],
303
303
  host: req.headers["host"],
304
+ authorization: req.headers["authorization"],
305
+ "accept-language": req.headers["accept-language"],
306
+ "x-forwarded-for": req.headers["x-forwarded-for"],
307
+ forwarded: req.headers["forwarded"],
308
+ "content-type": req.headers["content-type"],
309
+ origin: req.headers["origin"],
304
310
  },
305
311
  query: { ...req.query },
306
312
  path: req.path,
@@ -344,6 +350,12 @@ function getContextForServerFunctionEndpoint(req, res) {
344
350
  cookie: req.headers["cookie"],
345
351
  referer: req.headers["referer"],
346
352
  host: req.headers["host"],
353
+ authorization: req.headers["authorization"],
354
+ "accept-language": req.headers["accept-language"],
355
+ "x-forwarded-for": req.headers["x-forwarded-for"],
356
+ forwarded: req.headers["forwarded"],
357
+ "content-type": req.headers["content-type"],
358
+ origin: req.headers["origin"],
347
359
  },
348
360
  query: { ...req.query },
349
361
  path: req.path,
@@ -385,7 +397,7 @@ function getContextForServerFunctionEndpoint(req, res) {
385
397
  // 🛑 Security: JS cannot write HttpOnly cookies
386
398
  if (options && options.httpOnly) {
387
399
  console.error(
388
- `[Dinou Error] Cannot set HttpOnly cookie '${name}' in Server Function endpoint because streaming has started.`
400
+ `[Dinou Error] Cannot set HttpOnly cookie '${name}' in Server Function endpoint because streaming has started.`,
389
401
  );
390
402
  return;
391
403
  }
@@ -426,7 +438,7 @@ function getContextForServerFunctionEndpoint(req, res) {
426
438
  const safePath = JSON.stringify(path);
427
439
 
428
440
  res.write(
429
- `<script>document.cookie = ${safeName} + "=; Max-Age=0; path=" + ${safePath} + ";";</script>`
441
+ `<script>document.cookie = ${safeName} + "=; Max-Age=0; path=" + ${safePath} + ";";</script>`,
430
442
  );
431
443
  },
432
444
  },
@@ -465,10 +477,10 @@ if (!isDevelopment) {
465
477
  process.cwd(),
466
478
  isWebpack
467
479
  ? `${outputFolder}/react-client-manifest.json`
468
- : `react_client_manifest/react-client-manifest.json`
480
+ : `react_client_manifest/react-client-manifest.json`,
469
481
  ),
470
- "utf8"
471
- )
482
+ "utf8",
483
+ ),
472
484
  );
473
485
  }
474
486
 
@@ -484,9 +496,9 @@ async function serveRSCPayload(req, res, isOld = false, isStatic = false) {
484
496
  ? "/____rsc_payload_old_static____"
485
497
  : "/____rsc_payload_old____"
486
498
  : isStatic
487
- ? "/____rsc_payload_static____"
488
- : "/____rsc_payload____",
489
- ""
499
+ ? "/____rsc_payload_static____"
500
+ : "/____rsc_payload____",
501
+ "",
490
502
  );
491
503
  // 1. Correct Map initialization
492
504
  if (!isDynamic.has(reqPath)) {
@@ -512,7 +524,7 @@ async function serveRSCPayload(req, res, isOld = false, isStatic = false) {
512
524
  const payloadPath = path.resolve(
513
525
  "dist2",
514
526
  reqPath.replace(/^\//, ""),
515
- isOld || regenerating.has(reqPath) ? "rsc._old.rsc" : "rsc.rsc"
527
+ isOld || regenerating.has(reqPath) ? "rsc._old.rsc" : "rsc.rsc",
516
528
  );
517
529
  const distDir = path.resolve("dist2");
518
530
 
@@ -546,7 +558,7 @@ async function serveRSCPayload(req, res, isOld = false, isStatic = false) {
546
558
  reqPath,
547
559
  { ...req.query },
548
560
  isNotFound,
549
- isDevelopment
561
+ isDevelopment,
550
562
  );
551
563
  const manifest = isDevelopment
552
564
  ? JSON.parse(
@@ -555,10 +567,10 @@ async function serveRSCPayload(req, res, isOld = false, isStatic = false) {
555
567
  process.cwd(),
556
568
  isWebpack
557
569
  ? `${outputFolder}/react-client-manifest.json`
558
- : `react_client_manifest/react-client-manifest.json`
570
+ : `react_client_manifest/react-client-manifest.json`,
559
571
  ),
560
- "utf8"
561
- )
572
+ "utf8",
573
+ ),
562
574
  )
563
575
  : cachedClientManifest;
564
576
 
@@ -596,7 +608,7 @@ app.post(/^\/____rsc_payload_error____\/.*\/?$/, async (req, res) => {
596
608
  reqPath,
597
609
  { ...req.query },
598
610
  req.body.error,
599
- isDevelopment
611
+ isDevelopment,
600
612
  );
601
613
  const manifest = isDevelopment
602
614
  ? JSON.parse(
@@ -605,10 +617,10 @@ app.post(/^\/____rsc_payload_error____\/.*\/?$/, async (req, res) => {
605
617
  process.cwd(),
606
618
  isWebpack
607
619
  ? `${outputFolder}/react-client-manifest.json`
608
- : `react_client_manifest/react-client-manifest.json`
620
+ : `react_client_manifest/react-client-manifest.json`,
609
621
  ),
610
- "utf8"
611
- )
622
+ "utf8",
623
+ ),
612
624
  )
613
625
  : cachedClientManifest;
614
626
  const { pipe } = renderToPipeableStream(jsx, manifest);
@@ -702,6 +714,12 @@ app.get(/^\/.*\/?$/, (req, res) => {
702
714
  cookie: req.headers["cookie"],
703
715
  referer: req.headers["referer"],
704
716
  host: req.headers["host"],
717
+ authorization: req.headers["authorization"],
718
+ "accept-language": req.headers["accept-language"],
719
+ "x-forwarded-for": req.headers["x-forwarded-for"],
720
+ forwarded: req.headers["forwarded"],
721
+ "content-type": req.headers["content-type"],
722
+ origin: req.headers["origin"],
705
723
  },
706
724
  path: req.path,
707
725
  method: req.method,
@@ -718,7 +736,7 @@ app.get(/^\/.*\/?$/, (req, res) => {
718
736
  contextForChild,
719
737
  res,
720
738
  capturedStatus,
721
- isDynamic
739
+ isDynamic,
722
740
  );
723
741
 
724
742
  res.setHeader("Content-Type", "text/html");
@@ -785,8 +803,25 @@ function isOriginAllowed(req) {
785
803
  }
786
804
  }
787
805
 
806
+ const multer = require("multer");
807
+
808
+ const upload = multer().any();
809
+
810
+ const runMiddleware = (req, res, fn) => {
811
+ return new Promise((resolve, reject) => {
812
+ fn(req, res, (result) => {
813
+ if (result instanceof Error) return reject(result);
814
+ return resolve(result);
815
+ });
816
+ });
817
+ };
818
+
788
819
  app.post("/____server_function____", async (req, res) => {
789
820
  try {
821
+ if (req.headers["content-type"]?.includes("multipart/form-data")) {
822
+ await runMiddleware(req, res, upload);
823
+ }
824
+
790
825
  // 1. Check Origin (Prevent calls from other domains)
791
826
  const origin = req.headers.origin;
792
827
  const host = req.headers.host;
@@ -805,11 +840,44 @@ app.post("/____server_function____", async (req, res) => {
805
840
  // 2. Origin Check (NEW)
806
841
  if (!isDevelopment && !isOriginAllowed(req)) {
807
842
  console.error(
808
- `[Security] Blocked request from origin: ${req.headers.origin}`
843
+ `[Security] Blocked request from origin: ${req.headers.origin}`,
809
844
  );
810
845
  return res.status(403).json({ error: "Origin not allowed" });
811
846
  }
812
- const { id, args } = req.body;
847
+
848
+ let id, args;
849
+
850
+ if (req.headers["content-type"]?.includes("multipart/form-data")) {
851
+ id = req.body.__dinou_func_id;
852
+
853
+ const formData = new FormData();
854
+
855
+ for (const key in req.body) {
856
+ if (key === "__dinou_func_id" || key === "__dinou_args") continue;
857
+ formData.append(key, req.body[key]);
858
+ }
859
+
860
+ if (req.files && Array.isArray(req.files)) {
861
+ for (const file of req.files) {
862
+ const blob = new Blob([file.buffer], { type: file.mimetype });
863
+ formData.append(file.fieldname, blob, file.originalname);
864
+ }
865
+ }
866
+
867
+ args = [formData];
868
+
869
+ if (req.body.__dinou_args) {
870
+ try {
871
+ const extraArgs = JSON.parse(req.body.__dinou_args);
872
+ args.push(...extraArgs);
873
+ } catch (e) {
874
+ console.error("Error parsing extra args in multipart request");
875
+ }
876
+ }
877
+ } else {
878
+ id = req.body.id;
879
+ args = req.body.args;
880
+ }
813
881
 
814
882
  // Basic input validation: id must be string, args an array
815
883
  if (typeof id !== "string" || !Array.isArray(args)) {
@@ -932,7 +1000,7 @@ app.post("/____server_function____", async (req, res) => {
932
1000
  process.cwd(),
933
1001
  isWebpack
934
1002
  ? `${outputFolder}/react-client-manifest.json`
935
- : `react_client_manifest/react-client-manifest.json`
1003
+ : `react_client_manifest/react-client-manifest.json`,
936
1004
  );
937
1005
  // Verify that the manifest exists to avoid errors
938
1006
  if (!existsSync(manifestPath)) {
@@ -976,10 +1044,10 @@ const http = require("http");
976
1044
  await new Promise((resolve) => {
977
1045
  server.listen(port, () => {
978
1046
  console.log(
979
- `\n🚀 Dinou Server is ready and listening on http://localhost:${port}`
1047
+ `\n🚀 Dinou Server is ready and listening on http://localhost:${port}`,
980
1048
  );
981
1049
  console.log(
982
- ` Environment: ${isDevelopment ? "Development" : "Production"}`
1050
+ ` Environment: ${isDevelopment ? "Development" : "Production"}`,
983
1051
  );
984
1052
  resolve();
985
1053
  });
@@ -996,7 +1064,7 @@ const http = require("http");
996
1064
  .catch((err) => {
997
1065
  console.error(
998
1066
  "❌ [Background] Static generation failed (App continues in Dynamic Mode):",
999
- err
1067
+ err,
1000
1068
  );
1001
1069
  isReady = true;
1002
1070
  });
@@ -6,6 +6,7 @@ import assetsPlugin from "../plugins-esbuild/assets-plugin.mjs";
6
6
  import copyStaticFiles from "esbuild-copy-static-files";
7
7
  import manifestGeneratorPlugin from "../plugins-esbuild/manifest-generator-plugin.mjs";
8
8
  import writePlugin from "../plugins-esbuild/write-plugin.mjs";
9
+ import babelReactCompilerPlugin from "../plugins-esbuild/babel-react-compiler-plugin.mjs";
9
10
  import { existsSync } from "node:fs";
10
11
 
11
12
  const manifestData = {};
@@ -16,6 +17,7 @@ export default function getConfigEsbuildProd({
16
17
  manifest = {},
17
18
  }) {
18
19
  let plugins = [
20
+ babelReactCompilerPlugin(),
19
21
  TsconfigPathsPlugin({}),
20
22
  cssProcessorPlugin({ outdir }),
21
23
  reactClientManifestPlugin({
@@ -0,0 +1,64 @@
1
+ import babel from "@babel/core";
2
+ import fs from "node:fs/promises";
3
+ import path from "node:path";
4
+ const norm = (p) => path.resolve(p).replace(/\\/g, "/");
5
+ export default function babelReactCompilerPlugin() {
6
+ return {
7
+ name: "babel-react-compiler-bridge",
8
+ setup(build) {
9
+ const entryPoints = build.initialOptions.entryPoints;
10
+ // Interceptamos archivos JS/TS/JSX/TSX
11
+ // OJO: Filtramos node_modules para no ralentizar procesando librerías externas
12
+ build.onLoad({ filter: /\.[jt]sx?$/ }, async (args) => {
13
+ // Doble chequeo de seguridad para evitar node_modules
14
+ if (args.path.includes("node_modules")) return;
15
+ const abs = path.resolve(args.path);
16
+
17
+ const absNorm = norm(abs);
18
+ const isAnEntryPoint = Object.values(entryPoints).some(
19
+ (val) => norm(path.resolve(val)) === absNorm,
20
+ );
21
+ if (!isAnEntryPoint) {
22
+ return;
23
+ }
24
+ try {
25
+ const source = await fs.readFile(args.path, "utf8");
26
+ const filename = args.path;
27
+
28
+ // Transformamos el código con Babel + React Compiler
29
+ const result = await babel.transformAsync(source, {
30
+ filename,
31
+ presets: [
32
+ // Necesitamos decirle a Babel que entienda React y TS antes de compilar
33
+ ["@babel/preset-react", { runtime: "automatic" }],
34
+ "@babel/preset-typescript",
35
+ ],
36
+ plugins: [
37
+ "babel-plugin-react-compiler", // 👈 LA JOYA DE LA CORONA
38
+ ],
39
+ sourceMaps: true, // Vital para que los sourcemaps de esbuild funcionen
40
+ configFile: false, // Ignoramos babel.config.js globales para ir al grano
41
+ });
42
+
43
+ // Si por alguna razón Babel no devuelve código, dejamos a esbuild actuar
44
+ if (!result || !result.code) return;
45
+
46
+ return {
47
+ contents: result.code,
48
+ loader: "js", // Babel devuelve JS estándar
49
+ };
50
+ } catch (error) {
51
+ // Si falla Babel, mostramos error bonito para no romper el build silenciosamente
52
+ return {
53
+ errors: [
54
+ {
55
+ text: error.message,
56
+ detail: error,
57
+ },
58
+ ],
59
+ };
60
+ }
61
+ });
62
+ },
63
+ };
64
+ }