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

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 +124 -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.5",
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,14 @@ 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 also needs esbuild options for version virtual module
653
+ optimizeDeps: {
654
+ esbuildOptions: sharedEsbuildOptions,
543
655
  },
544
656
  },
545
657
  },
@@ -605,6 +717,7 @@ export async function rscRouter(
605
717
  // This ensures the same Context instance is used by both browser entry and RSC proxy modules
606
718
  optimizeDeps: {
607
719
  exclude: RSC_ROUTER_EXCLUDE_DEPS,
720
+ esbuildOptions: sharedEsbuildOptions,
608
721
  },
609
722
  resolve: {
610
723
  alias: {
@@ -639,6 +752,7 @@ export async function rscRouter(
639
752
  // Always exclude rsc-router modules, conditionally add virtual entry
640
753
  optimizeDeps: {
641
754
  exclude: RSC_ROUTER_EXCLUDE_DEPS,
755
+ esbuildOptions: sharedEsbuildOptions,
642
756
  ...(useVirtualClient && {
643
757
  // Tell Vite to scan the virtual entry for dependencies
644
758
  entries: [VIRTUAL_IDS.browser],
@@ -652,6 +766,7 @@ export async function rscRouter(
652
766
  // Pre-bundle React for SSR to ensure single instance
653
767
  include: ["react", "react-dom/server.edge", "react/jsx-runtime"],
654
768
  exclude: RSC_ROUTER_EXCLUDE_DEPS,
769
+ esbuildOptions: sharedEsbuildOptions,
655
770
  },
656
771
  },
657
772
  }),
@@ -661,6 +776,7 @@ export async function rscRouter(
661
776
  entries: [VIRTUAL_IDS.rsc],
662
777
  // Pre-bundle React for RSC to ensure single instance
663
778
  include: ["react", "react/jsx-runtime"],
779
+ esbuildOptions: sharedEsbuildOptions,
664
780
  },
665
781
  },
666
782
  }),