@ivogt/rsc-router 0.0.0-experimental.1 → 0.0.0-experimental.3

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ivogt/rsc-router",
3
- "version": "0.0.0-experimental.1",
3
+ "version": "0.0.0-experimental.3",
4
4
  "type": "module",
5
5
  "description": "Type-safe RSC router with partial rendering support",
6
6
  "author": "Ivo Todorov",
@@ -1,5 +1,5 @@
1
1
  import type { ReactNode } from "react";
2
- import { Outlet } from "rsc-router/client";
2
+ import { Outlet } from "../client.js";
3
3
 
4
4
  const MapRootLayout = (
5
5
  <>
package/src/vite/index.ts CHANGED
@@ -21,6 +21,64 @@ export { exposeLocationStateId } from "./expose-location-state-id.ts";
21
21
 
22
22
  // Virtual module type declarations in ./version.d.ts
23
23
 
24
+ /**
25
+ * Modules that must be excluded from Vite's dependency optimization.
26
+ *
27
+ * When rsc-router is installed from npm, Vite's dep optimizer bundles these modules
28
+ * into separate chunks. However, @vitejs/plugin-rsc creates virtual proxy modules
29
+ * for client components that import from the original source paths.
30
+ *
31
+ * This creates two different module instances:
32
+ * 1. Bundled version in /node_modules/.vite/deps/
33
+ * 2. Original source via plugin-rsc proxy
34
+ *
35
+ * When both instances create React Contexts (e.g., OutletContext), React sees them
36
+ * as different contexts, causing useContext to return undefined even when a Provider
37
+ * exists in the tree.
38
+ *
39
+ * By excluding these modules, we ensure a single module instance is used everywhere.
40
+ */
41
+ const RSC_ROUTER_EXCLUDE_DEPS = [
42
+ "@ivogt/rsc-router",
43
+ "@ivogt/rsc-router/browser",
44
+ "@ivogt/rsc-router/client",
45
+ "@ivogt/rsc-router/internal/deps/browser",
46
+ "@ivogt/rsc-router/internal/deps/html-stream-client",
47
+ ];
48
+
49
+ /**
50
+ * Plugin to transform CJS react-server-dom vendor file to ESM.
51
+ * The @vitejs/plugin-rsc package ships client.browser.js as CommonJS
52
+ * which doesn't work in the browser. This transforms it to ESM re-exports.
53
+ */
54
+ function createCjsToEsmPlugin(): Plugin {
55
+ return {
56
+ name: "rsc-router:cjs-to-esm",
57
+ enforce: "pre",
58
+ transform(code, id) {
59
+ // Transform the client.browser.js file from CJS to ESM
60
+ // Match any path containing vendor/react-server-dom/client.browser.js
61
+ if (
62
+ id.includes("vendor/react-server-dom/client.browser.js") ||
63
+ id.includes("vendor\\react-server-dom\\client.browser.js")
64
+ ) {
65
+ // The original file uses: module.exports = require('./cjs/...')
66
+ // Transform to ESM re-export from the development or production CJS file
67
+ const isProd = process.env.NODE_ENV === "production";
68
+ const cjsFile = isProd
69
+ ? "./cjs/react-server-dom-webpack-client.browser.production.js"
70
+ : "./cjs/react-server-dom-webpack-client.browser.development.js";
71
+
72
+ return {
73
+ code: `export * from "${cjsFile}";`,
74
+ map: null,
75
+ };
76
+ }
77
+ return null;
78
+ },
79
+ };
80
+ }
81
+
24
82
  /**
25
83
  * RSC plugin entry points configuration.
26
84
  * All entries use virtual modules by default. Specify a path to use a custom entry file.
@@ -422,6 +480,32 @@ export async function rscRouter(
422
480
  config() {
423
481
  // Configure environments for cloudflare deployment
424
482
  return {
483
+ // Exclude rsc-router modules from optimization to prevent module duplication
484
+ // This ensures the same Context instance is used by both browser entry and RSC proxy modules
485
+ optimizeDeps: {
486
+ exclude: RSC_ROUTER_EXCLUDE_DEPS,
487
+ },
488
+ resolve: {
489
+ alias: {
490
+ // Map rsc-router/* to @ivogt/rsc-router/* for virtual entries
491
+ // This allows the package to work when published under a scoped name
492
+ "rsc-router/internal/deps/browser":
493
+ "@ivogt/rsc-router/internal/deps/browser",
494
+ "rsc-router/internal/deps/ssr":
495
+ "@ivogt/rsc-router/internal/deps/ssr",
496
+ "rsc-router/internal/deps/rsc":
497
+ "@ivogt/rsc-router/internal/deps/rsc",
498
+ "rsc-router/internal/deps/html-stream-client":
499
+ "@ivogt/rsc-router/internal/deps/html-stream-client",
500
+ "rsc-router/internal/deps/html-stream-server":
501
+ "@ivogt/rsc-router/internal/deps/html-stream-server",
502
+ "rsc-router/browser": "@ivogt/rsc-router/browser",
503
+ "rsc-router/client": "@ivogt/rsc-router/client",
504
+ "rsc-router/server": "@ivogt/rsc-router/server",
505
+ "rsc-router/rsc": "@ivogt/rsc-router/rsc",
506
+ "rsc-router/ssr": "@ivogt/rsc-router/ssr",
507
+ },
508
+ },
425
509
  environments: {
426
510
  client: {
427
511
  build: {
@@ -432,8 +516,10 @@ export async function rscRouter(
432
516
  },
433
517
  },
434
518
  // Pre-bundle rsc-html-stream to prevent discovery during first request
519
+ // Exclude rsc-router modules to ensure same Context instance
435
520
  optimizeDeps: {
436
521
  include: ["rsc-html-stream/client"],
522
+ exclude: RSC_ROUTER_EXCLUDE_DEPS,
437
523
  },
438
524
  },
439
525
  ssr: {
@@ -515,6 +601,32 @@ export async function rscRouter(
515
601
  const useVirtualRSC = finalEntries.rsc === VIRTUAL_IDS.rsc;
516
602
 
517
603
  return {
604
+ // Exclude rsc-router modules from optimization to prevent module duplication
605
+ // This ensures the same Context instance is used by both browser entry and RSC proxy modules
606
+ optimizeDeps: {
607
+ exclude: RSC_ROUTER_EXCLUDE_DEPS,
608
+ },
609
+ resolve: {
610
+ alias: {
611
+ // Map rsc-router/* to @ivogt/rsc-router/* for virtual entries
612
+ // This allows the package to work when published under a scoped name
613
+ "rsc-router/internal/deps/browser":
614
+ "@ivogt/rsc-router/internal/deps/browser",
615
+ "rsc-router/internal/deps/ssr":
616
+ "@ivogt/rsc-router/internal/deps/ssr",
617
+ "rsc-router/internal/deps/rsc":
618
+ "@ivogt/rsc-router/internal/deps/rsc",
619
+ "rsc-router/internal/deps/html-stream-client":
620
+ "@ivogt/rsc-router/internal/deps/html-stream-client",
621
+ "rsc-router/internal/deps/html-stream-server":
622
+ "@ivogt/rsc-router/internal/deps/html-stream-server",
623
+ "rsc-router/browser": "@ivogt/rsc-router/browser",
624
+ "rsc-router/client": "@ivogt/rsc-router/client",
625
+ "rsc-router/server": "@ivogt/rsc-router/server",
626
+ "rsc-router/rsc": "@ivogt/rsc-router/rsc",
627
+ "rsc-router/ssr": "@ivogt/rsc-router/ssr",
628
+ },
629
+ },
518
630
  environments: {
519
631
  client: {
520
632
  build: {
@@ -524,12 +636,14 @@ export async function rscRouter(
524
636
  },
525
637
  },
526
638
  },
527
- ...(useVirtualClient && {
528
- optimizeDeps: {
639
+ // Always exclude rsc-router modules, conditionally add virtual entry
640
+ optimizeDeps: {
641
+ exclude: RSC_ROUTER_EXCLUDE_DEPS,
642
+ ...(useVirtualClient && {
529
643
  // Tell Vite to scan the virtual entry for dependencies
530
644
  entries: [VIRTUAL_IDS.browser],
531
- },
532
- }),
645
+ }),
646
+ },
533
647
  },
534
648
  ...(useVirtualSSR && {
535
649
  ssr: {
@@ -537,6 +651,7 @@ export async function rscRouter(
537
651
  entries: [VIRTUAL_IDS.ssr],
538
652
  // Pre-bundle React for SSR to ensure single instance
539
653
  include: ["react", "react-dom/server.edge", "react/jsx-runtime"],
654
+ exclude: RSC_ROUTER_EXCLUDE_DEPS,
540
655
  },
541
656
  },
542
657
  }),
@@ -603,6 +718,10 @@ export async function rscRouter(
603
718
  plugins.push(createVersionInjectorPlugin(rscEntryPath));
604
719
  }
605
720
 
721
+ // Add CJS-to-ESM transform for @vitejs/plugin-rsc vendor files
722
+ // This must be added to transform the CommonJS client.browser.js to ESM
723
+ plugins.push(createCjsToEsmPlugin());
724
+
606
725
  return plugins;
607
726
  }
608
727