@ivogt/rsc-router 0.0.0-experimental.3 → 0.0.0-experimental.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.
Files changed (2) hide show
  1. package/package.json +1 -1
  2. package/src/vite/index.ts +126 -8
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ivogt/rsc-router",
3
- "version": "0.0.0-experimental.3",
3
+ "version": "0.0.0-experimental.6",
4
4
  "type": "module",
5
5
  "description": "Type-safe RSC router with partial rendering support",
6
6
  "author": "Ivo Todorov",
package/src/vite/index.ts CHANGED
@@ -37,33 +37,80 @@ export { exposeLocationStateId } from "./expose-location-state-id.ts";
37
37
  * exists in the tree.
38
38
  *
39
39
  * By excluding these modules, we ensure a single module instance is used everywhere.
40
+ *
41
+ * We include both the scoped package name (@ivogt/rsc-router) and the aliased paths
42
+ * (rsc-router) because Vite's optimizer runs before alias resolution.
43
+ */
44
+ /**
45
+ * esbuild plugin to provide rsc-router:version virtual module during optimization.
46
+ * This is needed because esbuild runs during Vite's dependency optimization phase,
47
+ * before Vite's plugin system can handle virtual modules.
40
48
  */
49
+ const versionEsbuildPlugin = {
50
+ name: "rsc-router-version",
51
+ setup(build: any) {
52
+ build.onResolve({ filter: /^rsc-router:version$/ }, (args: any) => ({
53
+ path: args.path,
54
+ namespace: "rsc-router-virtual",
55
+ }));
56
+ build.onLoad({ filter: /.*/, namespace: "rsc-router-virtual" }, () => ({
57
+ contents: `export const VERSION = "dev";`,
58
+ loader: "js",
59
+ }));
60
+ },
61
+ };
62
+
63
+ /**
64
+ * Shared esbuild options for dependency optimization.
65
+ * Includes the version stub plugin for all environments.
66
+ */
67
+ const sharedEsbuildOptions = {
68
+ plugins: [versionEsbuildPlugin],
69
+ };
70
+
41
71
  const RSC_ROUTER_EXCLUDE_DEPS = [
72
+ // Scoped package paths
42
73
  "@ivogt/rsc-router",
43
74
  "@ivogt/rsc-router/browser",
44
75
  "@ivogt/rsc-router/client",
76
+ "@ivogt/rsc-router/server",
77
+ "@ivogt/rsc-router/rsc",
78
+ "@ivogt/rsc-router/ssr",
45
79
  "@ivogt/rsc-router/internal/deps/browser",
46
80
  "@ivogt/rsc-router/internal/deps/html-stream-client",
81
+ "@ivogt/rsc-router/internal/deps/ssr",
82
+ "@ivogt/rsc-router/internal/deps/rsc",
83
+ // Aliased paths (before alias resolution)
84
+ "rsc-router/browser",
85
+ "rsc-router/client",
86
+ "rsc-router/server",
87
+ "rsc-router/rsc",
88
+ "rsc-router/ssr",
89
+ "rsc-router/internal/deps/browser",
90
+ "rsc-router/internal/deps/html-stream-client",
91
+ "rsc-router/internal/deps/ssr",
92
+ "rsc-router/internal/deps/rsc",
47
93
  ];
48
94
 
49
95
  /**
50
- * Plugin to transform CJS react-server-dom vendor file to ESM.
96
+ * Plugin to transform CJS react-server-dom vendor files to ESM.
51
97
  * 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.
98
+ * which doesn't work in the browser. This transforms both:
99
+ * 1. The entry point (client.browser.js) to re-export from the CJS file
100
+ * 2. The actual CJS file content to ESM syntax
53
101
  */
54
102
  function createCjsToEsmPlugin(): Plugin {
55
103
  return {
56
104
  name: "rsc-router:cjs-to-esm",
57
105
  enforce: "pre",
58
106
  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
107
+ const cleanId = id.split("?")[0];
108
+
109
+ // Transform the client.browser.js entry point to re-export from CJS
61
110
  if (
62
- id.includes("vendor/react-server-dom/client.browser.js") ||
63
- id.includes("vendor\\react-server-dom\\client.browser.js")
111
+ cleanId.includes("vendor/react-server-dom/client.browser.js") ||
112
+ cleanId.includes("vendor\\react-server-dom\\client.browser.js")
64
113
  ) {
65
- // The original file uses: module.exports = require('./cjs/...')
66
- // Transform to ESM re-export from the development or production CJS file
67
114
  const isProd = process.env.NODE_ENV === "production";
68
115
  const cjsFile = isProd
69
116
  ? "./cjs/react-server-dom-webpack-client.browser.production.js"
@@ -74,6 +121,60 @@ function createCjsToEsmPlugin(): Plugin {
74
121
  map: null,
75
122
  };
76
123
  }
124
+
125
+ // Transform the actual CJS files to ESM
126
+ if (
127
+ (cleanId.includes("vendor/react-server-dom/cjs/") ||
128
+ cleanId.includes("vendor\\react-server-dom\\cjs\\")) &&
129
+ cleanId.includes("client.browser")
130
+ ) {
131
+ let transformed = code;
132
+
133
+ // Extract the license comment to preserve it
134
+ const licenseMatch = transformed.match(/^\/\*\*[\s\S]*?\*\//);
135
+ const license = licenseMatch ? licenseMatch[0] : "";
136
+ if (license) {
137
+ transformed = transformed.slice(license.length);
138
+ }
139
+
140
+ // Remove "use strict" and the conditional IIFE wrapper
141
+ // Pattern: "use strict"; "production" !== process.env.NODE_ENV && (function() { ... })();
142
+ transformed = transformed.replace(
143
+ /^\s*["']use strict["'];\s*["']production["']\s*!==\s*process\.env\.NODE_ENV\s*&&\s*\(function\s*\(\)\s*\{/,
144
+ ""
145
+ );
146
+
147
+ // Remove the closing of the conditional IIFE at the end: })();
148
+ transformed = transformed.replace(/\}\)\(\);?\s*$/, "");
149
+
150
+ // Replace require('react') and require('react-dom') with imports
151
+ // The pattern spans multiple lines with whitespace
152
+ transformed = transformed.replace(
153
+ /var\s+React\s*=\s*require\s*\(\s*["']react["']\s*\)\s*,[\s\n]+ReactDOM\s*=\s*require\s*\(\s*["']react-dom["']\s*\)\s*,/g,
154
+ 'import React from "react";\nimport ReactDOM from "react-dom";\nvar '
155
+ );
156
+
157
+ // Transform exports.xyz = function() to export function xyz()
158
+ transformed = transformed.replace(
159
+ /exports\.(\w+)\s*=\s*function\s*\(/g,
160
+ "export function $1("
161
+ );
162
+
163
+ // Transform exports.xyz = value to export const xyz = value
164
+ transformed = transformed.replace(
165
+ /exports\.(\w+)\s*=/g,
166
+ "export const $1 ="
167
+ );
168
+
169
+ // Reconstruct with license at the top
170
+ transformed = license + "\n" + transformed;
171
+
172
+ return {
173
+ code: transformed,
174
+ map: null,
175
+ };
176
+ }
177
+
77
178
  return null;
78
179
  },
79
180
  };
@@ -484,6 +585,7 @@ export async function rscRouter(
484
585
  // This ensures the same Context instance is used by both browser entry and RSC proxy modules
485
586
  optimizeDeps: {
486
587
  exclude: RSC_ROUTER_EXCLUDE_DEPS,
588
+ esbuildOptions: sharedEsbuildOptions,
487
589
  },
488
590
  resolve: {
489
591
  alias: {
@@ -520,6 +622,7 @@ export async function rscRouter(
520
622
  optimizeDeps: {
521
623
  include: ["rsc-html-stream/client"],
522
624
  exclude: RSC_ROUTER_EXCLUDE_DEPS,
625
+ esbuildOptions: sharedEsbuildOptions,
523
626
  },
524
627
  },
525
628
  ssr: {
@@ -532,6 +635,7 @@ export async function rscRouter(
532
635
  dedupe: ["react", "react-dom"],
533
636
  },
534
637
  // Pre-bundle SSR entry and React for proper module linking with childEnvironments
638
+ // Exclude rsc-router modules to ensure same Context instance
535
639
  optimizeDeps: {
536
640
  entries: [finalEntries.ssr],
537
641
  include: [
@@ -540,6 +644,16 @@ export async function rscRouter(
540
644
  "react/jsx-runtime",
541
645
  "rsc-html-stream/server",
542
646
  ],
647
+ exclude: RSC_ROUTER_EXCLUDE_DEPS,
648
+ esbuildOptions: sharedEsbuildOptions,
649
+ },
650
+ },
651
+ rsc: {
652
+ // RSC environment needs exclude list and esbuild options
653
+ // Exclude rsc-router modules to prevent createContext in RSC environment
654
+ optimizeDeps: {
655
+ exclude: RSC_ROUTER_EXCLUDE_DEPS,
656
+ esbuildOptions: sharedEsbuildOptions,
543
657
  },
544
658
  },
545
659
  },
@@ -605,6 +719,7 @@ export async function rscRouter(
605
719
  // This ensures the same Context instance is used by both browser entry and RSC proxy modules
606
720
  optimizeDeps: {
607
721
  exclude: RSC_ROUTER_EXCLUDE_DEPS,
722
+ esbuildOptions: sharedEsbuildOptions,
608
723
  },
609
724
  resolve: {
610
725
  alias: {
@@ -639,6 +754,7 @@ export async function rscRouter(
639
754
  // Always exclude rsc-router modules, conditionally add virtual entry
640
755
  optimizeDeps: {
641
756
  exclude: RSC_ROUTER_EXCLUDE_DEPS,
757
+ esbuildOptions: sharedEsbuildOptions,
642
758
  ...(useVirtualClient && {
643
759
  // Tell Vite to scan the virtual entry for dependencies
644
760
  entries: [VIRTUAL_IDS.browser],
@@ -652,6 +768,7 @@ export async function rscRouter(
652
768
  // Pre-bundle React for SSR to ensure single instance
653
769
  include: ["react", "react-dom/server.edge", "react/jsx-runtime"],
654
770
  exclude: RSC_ROUTER_EXCLUDE_DEPS,
771
+ esbuildOptions: sharedEsbuildOptions,
655
772
  },
656
773
  },
657
774
  }),
@@ -661,6 +778,7 @@ export async function rscRouter(
661
778
  entries: [VIRTUAL_IDS.rsc],
662
779
  // Pre-bundle React for RSC to ensure single instance
663
780
  include: ["react", "react/jsx-runtime"],
781
+ esbuildOptions: sharedEsbuildOptions,
664
782
  },
665
783
  },
666
784
  }),