vite-plugin-react-server 0.1.0

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 (158) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +289 -0
  3. package/dist/build/createBuildConfig.d.ts +12 -0
  4. package/dist/build/createBuildConfig.d.ts.map +1 -0
  5. package/dist/build/createBuildConfig.js +55 -0
  6. package/dist/build/createBuildConfig.js.map +1 -0
  7. package/dist/checkFilesExist.d.ts +8 -0
  8. package/dist/checkFilesExist.d.ts.map +1 -0
  9. package/dist/checkFilesExist.js +61 -0
  10. package/dist/checkFilesExist.js.map +1 -0
  11. package/dist/collect-css-manifest.d.ts +4 -0
  12. package/dist/collect-css-manifest.d.ts.map +1 -0
  13. package/dist/collect-css-manifest.js +57 -0
  14. package/dist/collect-css-manifest.js.map +1 -0
  15. package/dist/components.d.ts +13 -0
  16. package/dist/components.d.ts.map +1 -0
  17. package/dist/components.js +13 -0
  18. package/dist/components.js.map +1 -0
  19. package/dist/copy-dir.d.ts +4 -0
  20. package/dist/copy-dir.d.ts.map +1 -0
  21. package/dist/getEnv.d.ts +19 -0
  22. package/dist/getEnv.d.ts.map +1 -0
  23. package/dist/getEnv.js +76 -0
  24. package/dist/getEnv.js.map +1 -0
  25. package/dist/helpers/normalizedRelativePath.d.ts +9 -0
  26. package/dist/helpers/normalizedRelativePath.d.ts.map +1 -0
  27. package/dist/helpers/normalizedRelativePath.js +31 -0
  28. package/dist/helpers/normalizedRelativePath.js.map +1 -0
  29. package/dist/helpers/tryManifest.d.ts +8 -0
  30. package/dist/helpers/tryManifest.d.ts.map +1 -0
  31. package/dist/html/createPageLoader.d.ts +26 -0
  32. package/dist/html/createPageLoader.d.ts.map +1 -0
  33. package/dist/html/createPageLoader.js +70 -0
  34. package/dist/html/createPageLoader.js.map +1 -0
  35. package/dist/index.d.ts +3 -0
  36. package/dist/index.d.ts.map +1 -0
  37. package/dist/index.js +5 -0
  38. package/dist/index.js.map +1 -0
  39. package/dist/manifest.d.ts +6 -0
  40. package/dist/manifest.d.ts.map +1 -0
  41. package/dist/module-graph.d.ts +10 -0
  42. package/dist/module-graph.d.ts.map +1 -0
  43. package/dist/options.d.ts +86 -0
  44. package/dist/options.d.ts.map +1 -0
  45. package/dist/options.js +251 -0
  46. package/dist/options.js.map +1 -0
  47. package/dist/plugin.d.ts +8 -0
  48. package/dist/plugin.d.ts.map +1 -0
  49. package/dist/plugin.js +31 -0
  50. package/dist/plugin.js.map +1 -0
  51. package/dist/react-client/plugin.d.ts +4 -0
  52. package/dist/react-client/plugin.d.ts.map +1 -0
  53. package/dist/react-client/plugin.js +28 -0
  54. package/dist/react-client/plugin.js.map +1 -0
  55. package/dist/react-server/createDevMiddleware.d.ts +8 -0
  56. package/dist/react-server/createDevMiddleware.d.ts.map +1 -0
  57. package/dist/react-server/createDevServer.d.ts +4 -0
  58. package/dist/react-server/createDevServer.d.ts.map +1 -0
  59. package/dist/react-server/createHandler.d.ts +23 -0
  60. package/dist/react-server/createHandler.d.ts.map +1 -0
  61. package/dist/react-server/createHandler.js +110 -0
  62. package/dist/react-server/createHandler.js.map +1 -0
  63. package/dist/react-server/createReactNodeStreamer.d.ts +10 -0
  64. package/dist/react-server/createReactNodeStreamer.d.ts.map +1 -0
  65. package/dist/react-server/createRscStream.d.ts +4 -0
  66. package/dist/react-server/createRscStream.d.ts.map +1 -0
  67. package/dist/react-server/createRscStream.js +47 -0
  68. package/dist/react-server/createRscStream.js.map +1 -0
  69. package/dist/react-server/createSsrHandler.d.ts +4 -0
  70. package/dist/react-server/createSsrHandler.d.ts.map +1 -0
  71. package/dist/react-server/plugin.d.ts +8 -0
  72. package/dist/react-server/plugin.d.ts.map +1 -0
  73. package/dist/react-server/plugin.js +298 -0
  74. package/dist/react-server/plugin.js.map +1 -0
  75. package/dist/resolvePage.d.ts +19 -0
  76. package/dist/resolvePage.d.ts.map +1 -0
  77. package/dist/resolvePage.js +44 -0
  78. package/dist/resolvePage.js.map +1 -0
  79. package/dist/resolveProps.d.ts +19 -0
  80. package/dist/resolveProps.d.ts.map +1 -0
  81. package/dist/resolveProps.js +90 -0
  82. package/dist/resolveProps.js.map +1 -0
  83. package/dist/server.d.ts +2 -0
  84. package/dist/server.d.ts.map +1 -0
  85. package/dist/transformer/index.d.ts +28 -0
  86. package/dist/transformer/index.d.ts.map +1 -0
  87. package/dist/transformer/index.js +54 -0
  88. package/dist/transformer/index.js.map +1 -0
  89. package/dist/transformer/preserveDirectives.d.ts +4 -0
  90. package/dist/transformer/preserveDirectives.d.ts.map +1 -0
  91. package/dist/transformer/preserveDirectives.js +72 -0
  92. package/dist/transformer/preserveDirectives.js.map +1 -0
  93. package/dist/transformer/preserver.d.ts +2 -0
  94. package/dist/transformer/preserver.d.ts.map +1 -0
  95. package/dist/transformer/transformer.d.ts +30 -0
  96. package/dist/transformer/transformer.d.ts.map +1 -0
  97. package/dist/transformer/transformer.js +80 -0
  98. package/dist/transformer/transformer.js.map +1 -0
  99. package/dist/transformer/types.d.ts +15 -0
  100. package/dist/transformer/types.d.ts.map +1 -0
  101. package/dist/types.d.ts +197 -0
  102. package/dist/types.d.ts.map +1 -0
  103. package/dist/worker/createHtmlStream.d.ts +7 -0
  104. package/dist/worker/createHtmlStream.d.ts.map +1 -0
  105. package/dist/worker/createWorker.d.ts +3 -0
  106. package/dist/worker/createWorker.d.ts.map +1 -0
  107. package/dist/worker/createWorker.js +33 -0
  108. package/dist/worker/createWorker.js.map +1 -0
  109. package/dist/worker/loader.d.ts +15 -0
  110. package/dist/worker/loader.d.ts.map +1 -0
  111. package/dist/worker/renderPages.d.ts +18 -0
  112. package/dist/worker/renderPages.d.ts.map +1 -0
  113. package/dist/worker/renderPages.js +99 -0
  114. package/dist/worker/renderPages.js.map +1 -0
  115. package/dist/worker/types.d.ts +31 -0
  116. package/dist/worker/types.d.ts.map +1 -0
  117. package/dist/worker/worker.d.ts +7 -0
  118. package/dist/worker/worker.d.ts.map +1 -0
  119. package/package.json +116 -0
  120. package/src/build/createBuildConfig.ts +74 -0
  121. package/src/checkFilesExist.ts +67 -0
  122. package/src/collect-css-manifest.ts +76 -0
  123. package/src/components.tsx +14 -0
  124. package/src/copy-dir.ts +27 -0
  125. package/src/getEnv.ts +135 -0
  126. package/src/helpers/normalizedRelativePath.ts +59 -0
  127. package/src/helpers/tryManifest.ts +23 -0
  128. package/src/html/createPageLoader.ts +99 -0
  129. package/src/index.ts +4 -0
  130. package/src/manifest.ts +24 -0
  131. package/src/module-graph.ts +48 -0
  132. package/src/options.ts +351 -0
  133. package/src/plugin.ts +31 -0
  134. package/src/react-client/plugin.ts +34 -0
  135. package/src/react-server/createDevMiddleware.ts +75 -0
  136. package/src/react-server/createDevServer.ts +10 -0
  137. package/src/react-server/createHandler.ts +144 -0
  138. package/src/react-server/createReactNodeStreamer.ts +25 -0
  139. package/src/react-server/createRscStream.ts +52 -0
  140. package/src/react-server/createSsrHandler.ts +147 -0
  141. package/src/react-server/plugin.ts +349 -0
  142. package/src/resolvePage.ts +65 -0
  143. package/src/resolveProps.ts +122 -0
  144. package/src/server.tsx +0 -0
  145. package/src/transformer/README.md +44 -0
  146. package/src/transformer/index.ts +112 -0
  147. package/src/transformer/preserveDirectives.ts +100 -0
  148. package/src/transformer/preserver.ts +47 -0
  149. package/src/transformer/transformer.ts +123 -0
  150. package/src/transformer/types.ts +15 -0
  151. package/src/types.ts +245 -0
  152. package/src/worker/createHtmlStream.ts +76 -0
  153. package/src/worker/createWorker.ts +39 -0
  154. package/src/worker/loader.ts +16 -0
  155. package/src/worker/renderPages.ts +144 -0
  156. package/src/worker/types.ts +38 -0
  157. package/src/worker/worker.tsx +136 -0
  158. package/tsconfig.json +79 -0
@@ -0,0 +1,144 @@
1
+ import { join, resolve as resolvePath } from "node:path";
2
+ import { Transform } from "node:stream";
3
+ import type { Worker } from "node:worker_threads";
4
+ import type { PipeableStreamOptions } from "react-dom/server.node";
5
+ import { createHandler } from "../react-server/createHandler.js";
6
+ import type { StreamPluginOptions } from "../types.js";
7
+ import type {
8
+ WorkerRscChunkMessage,
9
+ WorkerRscEndMessage,
10
+ } from "./types.js";
11
+
12
+ type RenderPagesOptions = {
13
+ pluginOptions: Required<
14
+ Pick<
15
+ StreamPluginOptions,
16
+ "moduleBase" | "moduleBasePath" | "moduleBaseURL" | "projectRoot"
17
+ >
18
+ > &
19
+ Pick<
20
+ StreamPluginOptions,
21
+ "Page" | "props" | "build" | "Html" | "pageExportName" | "propsExportName"
22
+ >;
23
+ outDir: string;
24
+ manifest: Record<string, { file: string }>;
25
+ worker: Worker;
26
+ pipableStreamOptions?: PipeableStreamOptions;
27
+ loader: (id: string) => Promise<Record<string, any>>;
28
+ onCssFile?: (path: string) => void;
29
+ clientCss?: string[];
30
+ };
31
+
32
+ export async function renderPages(
33
+ routes: string[],
34
+ options: RenderPagesOptions
35
+ ) {
36
+ const destinationRoot = resolvePath(
37
+ options.pluginOptions.projectRoot,
38
+ options.outDir
39
+ );
40
+ const failedRoutes = new Map<string, Error>();
41
+ const moduleBasePath = join(
42
+ destinationRoot,
43
+ options.pluginOptions.moduleBasePath
44
+ );
45
+ const moduleBaseURL = options.pluginOptions.moduleBaseURL;
46
+
47
+ const htmlRoot = resolvePath(
48
+ options.pluginOptions.projectRoot,
49
+ options.pluginOptions.build?.client ?? options.outDir
50
+ );
51
+ const filesOutputted: string[] = [];
52
+
53
+ options.worker.on("message", (msg) => {
54
+ switch (msg.type) {
55
+ case "ERROR":
56
+ console.error("[RenderPages] Worker error:", msg.error);
57
+ break;
58
+ case "HTML":
59
+ filesOutputted.push(msg.outputPath);
60
+ if (filesOutputted.length === routes.length) {
61
+ renderPromises.push(
62
+ new Promise<void>((resolve) => {
63
+ options.worker.removeAllListeners();
64
+ options.worker.terminate();
65
+ resolve();
66
+ })
67
+ );
68
+ }
69
+ break;
70
+ default:
71
+ break;
72
+ }
73
+ });
74
+ const pipableStreamOptions = options.pipableStreamOptions ?? {};
75
+
76
+ // Create promises for each route in the batch
77
+ const renderPromises = routes.map(async (route) => {
78
+ try {
79
+ // Wait for handler creation
80
+ const result = await createHandler(route, options.pluginOptions, {
81
+ loader: options.loader,
82
+ manifest: options.manifest,
83
+ });
84
+
85
+ if (result.type !== "success") {
86
+ return;
87
+ }
88
+ const htmlOutputPath = join(htmlRoot, route, "index.html");
89
+
90
+ // Create a promise that resolves when the worker completes
91
+ await new Promise<void>((resolve, reject) => {
92
+ // Pipe RSC stream to worker
93
+ const transform = new Transform({
94
+ transform(chunk, _encoding, callback) {
95
+ // Send raw chunk
96
+ options.worker.postMessage({
97
+ type: "RSC_CHUNK",
98
+ id: route,
99
+ chunk: chunk,
100
+ moduleBasePath,
101
+ moduleBaseURL,
102
+ htmlOutputPath: htmlOutputPath,
103
+ outDir: options.outDir,
104
+ pipableStreamOptions,
105
+ } satisfies WorkerRscChunkMessage);
106
+ callback();
107
+ },
108
+ flush(callback) {
109
+ options.worker.postMessage({
110
+ type: "RSC_END",
111
+ id: route,
112
+ } satisfies WorkerRscEndMessage);
113
+ callback();
114
+ },
115
+ });
116
+
117
+ // Listen for worker response for this route
118
+ const messageHandler = (msg: any) => {
119
+ if (msg.route === route) {
120
+ if (msg.type === "ERROR") {
121
+ options.worker.removeListener("message", messageHandler);
122
+ reject(new Error(msg.error));
123
+ } else if (msg.type === "HTML") {
124
+ options.worker.removeListener("message", messageHandler);
125
+ resolve();
126
+ }
127
+ }
128
+ };
129
+
130
+ options.worker.on("message", messageHandler);
131
+ result.stream?.pipe(transform);
132
+ });
133
+ } catch (error) {
134
+ failedRoutes.set(route, error as Error);
135
+ }
136
+ });
137
+
138
+ // Wait for all routes to complete
139
+ await Promise.all(renderPromises);
140
+
141
+ if (failedRoutes.size > 0) {
142
+ console.error("[vite-react-stream] Failed routes:", failedRoutes);
143
+ }
144
+ }
@@ -0,0 +1,38 @@
1
+ import type { PipeableStreamOptions } from "react-dom/server.node";
2
+
3
+ export interface RenderState {
4
+ chunks: string[];
5
+ complete: boolean;
6
+ rendered: boolean;
7
+ outDir: string;
8
+ moduleBasePath: string;
9
+ moduleBaseURL: string;
10
+ htmlOutputPath: string;
11
+ id: string;
12
+ pipableStreamOptions: PipeableStreamOptions;
13
+ }
14
+
15
+ export interface WorkerRscChunkMessage {
16
+ type: "RSC_CHUNK";
17
+ id: string;
18
+ chunk: string;
19
+ moduleBasePath: string;
20
+ moduleBaseURL: string;
21
+ outDir: string;
22
+ htmlOutputPath: string;
23
+ pipableStreamOptions: PipeableStreamOptions;
24
+ }
25
+
26
+ export interface WorkerRscEndMessage {
27
+ type: "RSC_END";
28
+ id: string;
29
+ }
30
+
31
+ export interface WorkerShutdownMessage {
32
+ type: "SHUTDOWN";
33
+ }
34
+
35
+ export type WorkerMessage =
36
+ | WorkerRscChunkMessage
37
+ | WorkerRscEndMessage
38
+ | WorkerShutdownMessage;
@@ -0,0 +1,136 @@
1
+ import { createWriteStream } from "node:fs";
2
+ import { mkdir } from "node:fs/promises";
3
+ import { dirname } from "node:path";
4
+ import { Writable } from "node:stream";
5
+ import { parentPort } from "node:worker_threads";
6
+ import type { PipeableStream } from "react-server-dom-esm/server.node";
7
+ import { createHtmlStream } from "./createHtmlStream.js";
8
+ import type { RenderState, WorkerMessage } from "./types.js";
9
+
10
+ if (!parentPort) {
11
+ throw new Error("This module must be run as a worker");
12
+ }
13
+
14
+ declare global {
15
+ interface Window {
16
+ href: string;
17
+ }
18
+ }
19
+ // Initialize happy-dom window
20
+ (global as any).window = {
21
+ href: undefined,
22
+ pathname: undefined,
23
+ }
24
+
25
+ // Track active renders
26
+ const activeRenders = new Map<string, RenderState>();
27
+ const activeStreams = new Map<string, PipeableStream>();
28
+ const activeWrites = new Map<string, Writable>();
29
+
30
+ async function shutdown() {
31
+ console.log("[Worker] Shutting down forcefully");
32
+ while (activeRenders.size > 0) {
33
+ await new Promise((resolve) => setTimeout(resolve, 100));
34
+ }
35
+ for (const stream of activeStreams.values()) {
36
+ stream.abort();
37
+ }
38
+ for (const writeStream of activeWrites.values()) {
39
+ writeStream.destroy();
40
+ }
41
+ process.exit(0);
42
+ }
43
+ // Handle incoming messages
44
+ parentPort.on("message", async (message: WorkerMessage) => {
45
+ if (message.type === "SHUTDOWN") {
46
+ await shutdown();
47
+ }
48
+ if (!parentPort) {
49
+ throw new Error("No parent port available");
50
+ }
51
+ try {
52
+ switch (message.type) {
53
+ case "RSC_CHUNK": {
54
+ const { chunk, id, ...rest } = message;
55
+ (global as any).window.pathname = id;
56
+ // Skip if already rendered
57
+ let renderState = activeRenders.get(id);
58
+ if (renderState?.rendered) {
59
+ return;
60
+ }
61
+ // Initialize render state
62
+ if (!renderState) {
63
+ renderState = {
64
+ chunks: [],
65
+ complete: false,
66
+ rendered: false,
67
+ id: id,
68
+ ...rest,
69
+ };
70
+ activeRenders.set(id, renderState);
71
+ }
72
+ // Add chunk
73
+ if (chunk) renderState.chunks.push(chunk);
74
+ break;
75
+ }
76
+
77
+ case "RSC_END": {
78
+ const { id } = message;
79
+ const render = activeRenders.get(id);
80
+
81
+ if (!render || !parentPort || render.rendered) return;
82
+ try {
83
+ const writeToFile = render.outDir && render.htmlOutputPath;
84
+ // Write RSC content
85
+ if (writeToFile) {
86
+ await mkdir(dirname(render.htmlOutputPath), { recursive: true });
87
+ }
88
+ const { stream, writeStream } = createHtmlStream(
89
+ render,
90
+ writeToFile
91
+ ? createWriteStream(render.htmlOutputPath)
92
+ : new Writable({
93
+ write(chunk, _, callback) {
94
+ parentPort?.postMessage({
95
+ type: "HTML",
96
+ route: render.id,
97
+ content: chunk.toString(),
98
+ });
99
+ callback();
100
+ },
101
+ })
102
+ );
103
+ activeStreams.set(id, stream);
104
+ activeWrites.set(id, writeStream);
105
+ writeStream.on("finish", () => {
106
+ activeStreams.delete(id);
107
+ activeWrites.delete(id);
108
+ });
109
+ writeStream.on("error", () => {
110
+ activeStreams.delete(id);
111
+ activeWrites.delete(id);
112
+ stream.abort();
113
+ });
114
+ } catch (error) {
115
+ activeRenders.delete(id);
116
+ activeStreams.delete(id);
117
+ activeWrites.delete(id);
118
+ throw error;
119
+ } finally {
120
+ activeRenders.delete(id);
121
+ }
122
+ break;
123
+ }
124
+ }
125
+ } catch (error) {
126
+ const errorMessage = error instanceof Error ? error.message : String(error);
127
+ parentPort?.postMessage({
128
+ type: "ERROR",
129
+ error: errorMessage,
130
+ });
131
+ }
132
+ });
133
+
134
+ // Signal ready only after loader is registered
135
+ parentPort.postMessage({ type: "READY" });
136
+
package/tsconfig.json ADDED
@@ -0,0 +1,79 @@
1
+ {
2
+ // when we extend a tsconfig.json file, it will merge and override the compilerOptions but not the arrays like lib, rootDirs, etc.
3
+ "compilerOptions": {
4
+ "composite": true,
5
+ "declaration": true,
6
+ "declarationMap": true,
7
+ // idk whatever this does but strict sounds like a good idea. (https://www.typescriptlang.org/tsconfig/#strict)
8
+ "strict": true,
9
+ // Should you want to just import a js file anyway. You can use type comments in a .js file like: /* @type {string} */
10
+ "allowJs": true,
11
+ // prevents issues when renaming files only in casing
12
+ "forceConsistentCasingInFileNames": true,
13
+ // our jsx type's will be configured for react.
14
+ "jsx": "react",
15
+ // RSC portability - JSX will be transpiled to React.createElement
16
+ "jsxFactory": "React.createElement",
17
+ // The fragment refers to the empty component, like <></>
18
+ "jsxFragmentFactory": "React.Fragment",
19
+ // We force module detection to be sure that the module system is detected correctly.
20
+ "moduleDetection": "force",
21
+ // To indicate that we are NOT going to be emitting files
22
+ // This just means that when you write a switch statement, you will get an error once you forget to add a break statement.
23
+ "noFallthroughCasesInSwitch": true,
24
+ // We don't allow property access from index signatures. In this case we prefer to write record['images'] instead of record.images
25
+ "noPropertyAccessFromIndexSignature": true,
26
+ // You'll get an error if you forget to remove a variable. If you intended not to use it, you can prefix the variable with an underscore or change to a underscore variable to indicate unused variables.
27
+ "noUnusedLocals": false,
28
+ // Same as above, but for function parameters.
29
+ "noUnusedParameters": false,
30
+ // We allow JSON imports. In fact we promote using them. We need to use the `with {type: 'json'}` which is the most recent way to do it.
31
+ "resolveJsonModule": true,
32
+ // If you are having issues with libraries, you can disable this and it won't check the types of the libraries.
33
+ "skipLibCheck": false,
34
+ // The source folder is intended for polymorphic code that can work on the client, server, or during development. By default new code should go here, and should follow the types defined here.
35
+ "rootDir": "src",
36
+ // We collect all of our compiled files into the dist folder so that we do not clutter our source directories with distilled files.
37
+ "outDir": "dist",
38
+ // We tell the transpiler that we are using vite and that we want to use the client types. This makes sure that our import.meta.env types are available, as well as the experimental react types.
39
+ "types": [
40
+ "vite/client",
41
+ ],
42
+ "typeRoots": [
43
+ "./node_modules/@types",
44
+ "./node_modules",
45
+ "./types",
46
+ ],
47
+ "resolvePackageJsonImports": true,
48
+ "resolvePackageJsonExports": true,
49
+ "emitDeclarationOnly": true,
50
+ "verbatimModuleSyntax": true,
51
+ // The very latest ES features
52
+ "target": "es2022",
53
+ "module": "nodenext",
54
+ "moduleResolution": "nodenext",
55
+ "lib": [
56
+ "es2023",
57
+ "dom",
58
+ "dom.iterable",
59
+ ]
60
+ },
61
+ "include": [
62
+ // support just normal ts
63
+ "src/**/*.ts",
64
+ // support tsx
65
+ "src/**/*.tsx",
66
+ // support mts
67
+ "src/**/*.mts",
68
+ // support json
69
+ "src/**/*.json",
70
+ // package.json
71
+ "package.json",
72
+ // types
73
+ "types/**/*.d.ts",
74
+ ],
75
+ "exclude": [
76
+ "dist",
77
+ "node_modules"
78
+ ]
79
+ }