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,99 @@
1
+ import { resolve, join } from "node:path";
2
+ import { Transform } from "node:stream";
3
+ import { createHandler } from "../react-server/createHandler.js";
4
+ async function renderPages(routes, options) {
5
+ const destinationRoot = resolve(
6
+ options.pluginOptions.projectRoot,
7
+ options.outDir
8
+ );
9
+ const failedRoutes = /* @__PURE__ */ new Map();
10
+ const moduleBasePath = join(
11
+ destinationRoot,
12
+ options.pluginOptions.moduleBasePath
13
+ );
14
+ const moduleBaseURL = options.pluginOptions.moduleBaseURL;
15
+ const htmlRoot = resolve(
16
+ options.pluginOptions.projectRoot,
17
+ options.pluginOptions.build?.client ?? options.outDir
18
+ );
19
+ const filesOutputted = [];
20
+ options.worker.on("message", (msg) => {
21
+ switch (msg.type) {
22
+ case "ERROR":
23
+ console.error("[RenderPages] Worker error:", msg.error);
24
+ break;
25
+ case "HTML":
26
+ filesOutputted.push(msg.outputPath);
27
+ if (filesOutputted.length === routes.length) {
28
+ renderPromises.push(
29
+ new Promise((resolve2) => {
30
+ options.worker.removeAllListeners();
31
+ options.worker.terminate();
32
+ resolve2();
33
+ })
34
+ );
35
+ }
36
+ break;
37
+ }
38
+ });
39
+ const pipableStreamOptions = options.pipableStreamOptions ?? {};
40
+ const renderPromises = routes.map(async (route) => {
41
+ try {
42
+ const result = await createHandler(route, options.pluginOptions, {
43
+ loader: options.loader,
44
+ manifest: options.manifest
45
+ });
46
+ if (result.type !== "success") {
47
+ return;
48
+ }
49
+ const htmlOutputPath = join(htmlRoot, route, "index.html");
50
+ await new Promise((resolve2, reject) => {
51
+ const transform = new Transform({
52
+ transform(chunk, _encoding, callback) {
53
+ options.worker.postMessage({
54
+ type: "RSC_CHUNK",
55
+ id: route,
56
+ chunk,
57
+ moduleBasePath,
58
+ moduleBaseURL,
59
+ htmlOutputPath,
60
+ outDir: options.outDir,
61
+ pipableStreamOptions
62
+ });
63
+ callback();
64
+ },
65
+ flush(callback) {
66
+ options.worker.postMessage({
67
+ type: "RSC_END",
68
+ id: route
69
+ });
70
+ callback();
71
+ }
72
+ });
73
+ const messageHandler = (msg) => {
74
+ if (msg.route === route) {
75
+ if (msg.type === "ERROR") {
76
+ options.worker.removeListener("message", messageHandler);
77
+ reject(new Error(msg.error));
78
+ } else if (msg.type === "HTML") {
79
+ options.worker.removeListener("message", messageHandler);
80
+ resolve2();
81
+ }
82
+ }
83
+ };
84
+ options.worker.on("message", messageHandler);
85
+ result.stream?.pipe(transform);
86
+ });
87
+ } catch (error) {
88
+ failedRoutes.set(route, error);
89
+ }
90
+ });
91
+ await Promise.all(renderPromises);
92
+ if (failedRoutes.size > 0) {
93
+ console.error("[vite-react-stream] Failed routes:", failedRoutes);
94
+ }
95
+ }
96
+ export {
97
+ renderPages
98
+ };
99
+ //# sourceMappingURL=renderPages.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"renderPages.js","sources":["../../src/worker/renderPages.ts"],"sourcesContent":["import { join, resolve as resolvePath } from \"node:path\";\nimport { Transform } from \"node:stream\";\nimport type { Worker } from \"node:worker_threads\";\nimport type { PipeableStreamOptions } from \"react-dom/server.node\";\nimport { createHandler } from \"../react-server/createHandler.js\";\nimport type { StreamPluginOptions } from \"../types.js\";\nimport type {\n WorkerRscChunkMessage,\n WorkerRscEndMessage,\n} from \"./types.js\";\n\ntype RenderPagesOptions = {\n pluginOptions: Required<\n Pick<\n StreamPluginOptions,\n \"moduleBase\" | \"moduleBasePath\" | \"moduleBaseURL\" | \"projectRoot\"\n >\n > &\n Pick<\n StreamPluginOptions,\n \"Page\" | \"props\" | \"build\" | \"Html\" | \"pageExportName\" | \"propsExportName\"\n >;\n outDir: string;\n manifest: Record<string, { file: string }>;\n worker: Worker;\n pipableStreamOptions?: PipeableStreamOptions;\n loader: (id: string) => Promise<Record<string, any>>;\n onCssFile?: (path: string) => void;\n clientCss?: string[];\n};\n\nexport async function renderPages(\n routes: string[],\n options: RenderPagesOptions\n) {\n const destinationRoot = resolvePath(\n options.pluginOptions.projectRoot,\n options.outDir\n );\n const failedRoutes = new Map<string, Error>();\n const moduleBasePath = join(\n destinationRoot,\n options.pluginOptions.moduleBasePath\n );\n const moduleBaseURL = options.pluginOptions.moduleBaseURL;\n\n const htmlRoot = resolvePath(\n options.pluginOptions.projectRoot,\n options.pluginOptions.build?.client ?? options.outDir\n );\n const filesOutputted: string[] = [];\n\n options.worker.on(\"message\", (msg) => {\n switch (msg.type) {\n case \"ERROR\":\n console.error(\"[RenderPages] Worker error:\", msg.error);\n break;\n case \"HTML\":\n filesOutputted.push(msg.outputPath);\n if (filesOutputted.length === routes.length) {\n renderPromises.push(\n new Promise<void>((resolve) => {\n options.worker.removeAllListeners();\n options.worker.terminate();\n resolve();\n })\n );\n }\n break;\n default:\n break;\n }\n });\n const pipableStreamOptions = options.pipableStreamOptions ?? {};\n\n // Create promises for each route in the batch\n const renderPromises = routes.map(async (route) => {\n try {\n // Wait for handler creation\n const result = await createHandler(route, options.pluginOptions, {\n loader: options.loader,\n manifest: options.manifest,\n });\n\n if (result.type !== \"success\") {\n return;\n }\n const htmlOutputPath = join(htmlRoot, route, \"index.html\");\n\n // Create a promise that resolves when the worker completes\n await new Promise<void>((resolve, reject) => {\n // Pipe RSC stream to worker\n const transform = new Transform({\n transform(chunk, _encoding, callback) {\n // Send raw chunk\n options.worker.postMessage({\n type: \"RSC_CHUNK\",\n id: route,\n chunk: chunk,\n moduleBasePath,\n moduleBaseURL,\n htmlOutputPath: htmlOutputPath,\n outDir: options.outDir,\n pipableStreamOptions,\n } satisfies WorkerRscChunkMessage);\n callback();\n },\n flush(callback) {\n options.worker.postMessage({\n type: \"RSC_END\",\n id: route,\n } satisfies WorkerRscEndMessage);\n callback();\n },\n });\n\n // Listen for worker response for this route\n const messageHandler = (msg: any) => {\n if (msg.route === route) {\n if (msg.type === \"ERROR\") {\n options.worker.removeListener(\"message\", messageHandler);\n reject(new Error(msg.error));\n } else if (msg.type === \"HTML\") {\n options.worker.removeListener(\"message\", messageHandler);\n resolve();\n }\n }\n };\n\n options.worker.on(\"message\", messageHandler);\n result.stream?.pipe(transform);\n });\n } catch (error) {\n failedRoutes.set(route, error as Error);\n }\n });\n\n // Wait for all routes to complete\n await Promise.all(renderPromises);\n\n if (failedRoutes.size > 0) {\n console.error(\"[vite-react-stream] Failed routes:\", failedRoutes);\n }\n}\n"],"names":["resolvePath","resolve"],"mappings":";;;AA+BsB,eAAA,YACpB,QACA,SACA;AACA,QAAM,kBAAkBA;AAAAA,IACtB,QAAQ,cAAc;AAAA,IACtB,QAAQ;AAAA,EACV;AACM,QAAA,mCAAmB,IAAmB;AAC5C,QAAM,iBAAiB;AAAA,IACrB;AAAA,IACA,QAAQ,cAAc;AAAA,EACxB;AACM,QAAA,gBAAgB,QAAQ,cAAc;AAE5C,QAAM,WAAWA;AAAAA,IACf,QAAQ,cAAc;AAAA,IACtB,QAAQ,cAAc,OAAO,UAAU,QAAQ;AAAA,EACjD;AACA,QAAM,iBAA2B,CAAC;AAElC,UAAQ,OAAO,GAAG,WAAW,CAAC,QAAQ;AACpC,YAAQ,IAAI,MAAM;AAAA,MAChB,KAAK;AACK,gBAAA,MAAM,+BAA+B,IAAI,KAAK;AACtD;AAAA,MACF,KAAK;AACY,uBAAA,KAAK,IAAI,UAAU;AAC9B,YAAA,eAAe,WAAW,OAAO,QAAQ;AAC5B,yBAAA;AAAA,YACb,IAAI,QAAc,CAACC,aAAY;AAC7B,sBAAQ,OAAO,mBAAmB;AAClC,sBAAQ,OAAO,UAAU;AACjB,cAAAA,SAAA;AAAA,YACT,CAAA;AAAA,UACH;AAAA,QAAA;AAEF;AAAA,IAEA;AAAA,EACJ,CACD;AACK,QAAA,uBAAuB,QAAQ,wBAAwB,CAAC;AAG9D,QAAM,iBAAiB,OAAO,IAAI,OAAO,UAAU;AAC7C,QAAA;AAEF,YAAM,SAAS,MAAM,cAAc,OAAO,QAAQ,eAAe;AAAA,QAC/D,QAAQ,QAAQ;AAAA,QAChB,UAAU,QAAQ;AAAA,MAAA,CACnB;AAEG,UAAA,OAAO,SAAS,WAAW;AAC7B;AAAA,MAAA;AAEF,YAAM,iBAAiB,KAAK,UAAU,OAAO,YAAY;AAGzD,YAAM,IAAI,QAAc,CAACA,UAAS,WAAW;AAErC,cAAA,YAAY,IAAI,UAAU;AAAA,UAC9B,UAAU,OAAO,WAAW,UAAU;AAEpC,oBAAQ,OAAO,YAAY;AAAA,cACzB,MAAM;AAAA,cACN,IAAI;AAAA,cACJ;AAAA,cACA;AAAA,cACA;AAAA,cACA;AAAA,cACA,QAAQ,QAAQ;AAAA,cAChB;AAAA,YAAA,CAC+B;AACxB,qBAAA;AAAA,UACX;AAAA,UACA,MAAM,UAAU;AACd,oBAAQ,OAAO,YAAY;AAAA,cACzB,MAAM;AAAA,cACN,IAAI;AAAA,YAAA,CACyB;AACtB,qBAAA;AAAA,UAAA;AAAA,QACX,CACD;AAGK,cAAA,iBAAiB,CAAC,QAAa;AAC/B,cAAA,IAAI,UAAU,OAAO;AACnB,gBAAA,IAAI,SAAS,SAAS;AAChB,sBAAA,OAAO,eAAe,WAAW,cAAc;AACvD,qBAAO,IAAI,MAAM,IAAI,KAAK,CAAC;AAAA,YAAA,WAClB,IAAI,SAAS,QAAQ;AACtB,sBAAA,OAAO,eAAe,WAAW,cAAc;AAC/C,cAAAA,SAAA;AAAA,YAAA;AAAA,UACV;AAAA,QAEJ;AAEQ,gBAAA,OAAO,GAAG,WAAW,cAAc;AACpC,eAAA,QAAQ,KAAK,SAAS;AAAA,MAAA,CAC9B;AAAA,aACM,OAAO;AACD,mBAAA,IAAI,OAAO,KAAc;AAAA,IAAA;AAAA,EACxC,CACD;AAGK,QAAA,QAAQ,IAAI,cAAc;AAE5B,MAAA,aAAa,OAAO,GAAG;AACjB,YAAA,MAAM,sCAAsC,YAAY;AAAA,EAAA;AAEpE;"}
@@ -0,0 +1,31 @@
1
+ import type { PipeableStreamOptions } from "react-dom/server.node";
2
+ export interface RenderState {
3
+ chunks: string[];
4
+ complete: boolean;
5
+ rendered: boolean;
6
+ outDir: string;
7
+ moduleBasePath: string;
8
+ moduleBaseURL: string;
9
+ htmlOutputPath: string;
10
+ id: string;
11
+ pipableStreamOptions: PipeableStreamOptions;
12
+ }
13
+ export interface WorkerRscChunkMessage {
14
+ type: "RSC_CHUNK";
15
+ id: string;
16
+ chunk: string;
17
+ moduleBasePath: string;
18
+ moduleBaseURL: string;
19
+ outDir: string;
20
+ htmlOutputPath: string;
21
+ pipableStreamOptions: PipeableStreamOptions;
22
+ }
23
+ export interface WorkerRscEndMessage {
24
+ type: "RSC_END";
25
+ id: string;
26
+ }
27
+ export interface WorkerShutdownMessage {
28
+ type: "SHUTDOWN";
29
+ }
30
+ export type WorkerMessage = WorkerRscChunkMessage | WorkerRscEndMessage | WorkerShutdownMessage;
31
+ //# sourceMappingURL=types.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/worker/types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,uBAAuB,CAAC;AAEnE,MAAM,WAAW,WAAW;IAC1B,MAAM,EAAE,MAAM,EAAE,CAAC;IACjB,QAAQ,EAAE,OAAO,CAAC;IAClB,QAAQ,EAAE,OAAO,CAAC;IAClB,MAAM,EAAE,MAAM,CAAC;IACf,cAAc,EAAE,MAAM,CAAC;IACvB,aAAa,EAAE,MAAM,CAAC;IACtB,cAAc,EAAE,MAAM,CAAC;IACvB,EAAE,EAAE,MAAM,CAAC;IACX,oBAAoB,EAAE,qBAAqB,CAAC;CAC7C;AAED,MAAM,WAAW,qBAAqB;IACpC,IAAI,EAAE,WAAW,CAAC;IAClB,EAAE,EAAE,MAAM,CAAC;IACX,KAAK,EAAE,MAAM,CAAC;IACd,cAAc,EAAE,MAAM,CAAC;IACvB,aAAa,EAAE,MAAM,CAAC;IACtB,MAAM,EAAE,MAAM,CAAC;IACf,cAAc,EAAE,MAAM,CAAC;IACvB,oBAAoB,EAAE,qBAAqB,CAAC;CAC7C;AAED,MAAM,WAAW,mBAAmB;IAClC,IAAI,EAAE,SAAS,CAAC;IAChB,EAAE,EAAE,MAAM,CAAC;CACZ;AAED,MAAM,WAAW,qBAAqB;IACpC,IAAI,EAAE,UAAU,CAAC;CAClB;AAED,MAAM,MAAM,aAAa,GACrB,qBAAqB,GACrB,mBAAmB,GACnB,qBAAqB,CAAC"}
@@ -0,0 +1,7 @@
1
+ declare global {
2
+ interface Window {
3
+ href: string;
4
+ }
5
+ }
6
+ export {};
7
+ //# sourceMappingURL=worker.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"worker.d.ts","sourceRoot":"","sources":["../../src/worker/worker.tsx"],"names":[],"mappings":"AAaA,OAAO,CAAC,MAAM,CAAC;IACb,UAAU,MAAM;QACd,IAAI,EAAE,MAAM,CAAC;KACd;CACF"}
package/package.json ADDED
@@ -0,0 +1,116 @@
1
+ {
2
+ "name": "vite-plugin-react-server",
3
+ "version": "0.1.0",
4
+ "description": "Vite plugin for React Server Components (RSC)",
5
+ "type": "module",
6
+ "main": "./dist/index.js",
7
+ "module": "./dist/index.js",
8
+ "types": "./dist/index.d.ts",
9
+ "files": [
10
+ "dist",
11
+ "src",
12
+ "README.md",
13
+ "LICENSE",
14
+ "tsconfig.json"
15
+ ],
16
+ "exports": {
17
+ ".": {
18
+ "types": "./dist/index.d.ts",
19
+ "import": "./dist/index.js",
20
+ "react-server": "./dist/react-server/plugin.js",
21
+ "default": "./dist/react-client/plugin.js"
22
+ },
23
+ "./client": {
24
+ "types": "./dist/react-client/plugin.d.ts",
25
+ "import": "./dist/react-client/plugin.js",
26
+ "default": "./dist/react-client/plugin.js"
27
+ },
28
+ "./server": {
29
+ "types": "./dist/react-server/plugin.d.ts",
30
+ "import": "./dist/react-server/plugin.js",
31
+ "react-server": "./dist/react-server/plugin.js",
32
+ "default": "./dist/react-server/plugin.js"
33
+ },
34
+ "./package.json": "./package.json"
35
+ },
36
+ "typesVersions": {
37
+ "*": {
38
+ ".": [
39
+ "./dist/index.d.ts"
40
+ ],
41
+ "client": [
42
+ "./dist/react-client/plugin.d.ts"
43
+ ],
44
+ "server": [
45
+ "./dist/react-server/plugin.d.ts"
46
+ ]
47
+ }
48
+ },
49
+ "sideEffects": false,
50
+ "scripts": {
51
+ "build": "npm run clean && npm run build:types && npm run build:vite",
52
+ "build:types": "tsc --build --force",
53
+ "build:vite": "vite build",
54
+ "clean": "rm -rf dist",
55
+ "prepublishOnly": "npm run build",
56
+ "lint": "eslint ./src --fix",
57
+ "test:server": "NODE_OPTIONS='--conditions react-server' vitest run",
58
+ "test:client": "vitest run",
59
+ "test": "npm run test:client && npm run test:server",
60
+ "test:coverage": "vitest run --coverage",
61
+ "test:ui": "vitest --ui",
62
+ "copy-oss": "cp -r ./oss-experimental/* ./node_modules/",
63
+ "patch-oss": "npx patch-package react-server-dom-esm --exclude 'nothing' && npx patch-package react react-dom",
64
+ "apply-patch": "npm run copy-oss && npm run patch-oss",
65
+ "postinstall": "patch-package",
66
+ "publish": "npm run postinstall && npm run build && npm publish"
67
+ },
68
+ "keywords": [
69
+ "vite",
70
+ "vite-plugin",
71
+ "react",
72
+ "rsc",
73
+ "server-components"
74
+ ],
75
+ "author": "Nico Brinkkemper",
76
+ "license": "MIT",
77
+ "engines": {
78
+ "node": "^20.0.0 || >=21.0.0"
79
+ },
80
+ "repository": {
81
+ "type": "git",
82
+ "url": "git+https://github.com/nicobrinkkemper/vite-plugin-react-server.git"
83
+ },
84
+ "bugs": {
85
+ "url": "https://github.com/nicobrinkkemper/vite-plugin-react-server/issues"
86
+ },
87
+ "homepage": "https://github.com/nicobrinkkemper/vite-plugin-react-server#readme",
88
+ "peerDependencies": {
89
+ "react": "^19.0.0",
90
+ "react-dom": "^19.0.0",
91
+ "vite": "^5.0.0"
92
+ },
93
+ "peerDependenciesMeta": {
94
+ "react": {
95
+ "optional": true
96
+ },
97
+ "react-dom": {
98
+ "optional": true
99
+ }
100
+ },
101
+ "devDependencies": {
102
+ "@types/node": "^20.17.16",
103
+ "@types/react": "^19.0.8",
104
+ "@types/react-dom": "^19.0.3",
105
+ "acorn-loose": "^8.3.0",
106
+ "patch-package": "^8.0.0",
107
+ "react": "^19.0.0",
108
+ "react-dom": "^19.0.0",
109
+ "react-server-dom-esm": "^0.0.1",
110
+ "source-map": "^0.7.4",
111
+ "typescript": "^5.7.3",
112
+ "vite": "^5.4.14",
113
+ "vitest": "^3.0.4",
114
+ "webpack-sources": "^3.2.0"
115
+ }
116
+ }
@@ -0,0 +1,74 @@
1
+ import { dirname, resolve } from "node:path";
2
+ import { fileURLToPath } from "node:url";
3
+ import type { InlineConfig } from "vite";
4
+ import { DEFAULT_CONFIG } from "../options.js";
5
+ import type { StreamPluginOptions } from "../types.js";
6
+ const __dirname = dirname(fileURLToPath(import.meta.url));
7
+
8
+ type CreateBuildConfigOptions = {
9
+ root: string;
10
+ base: string;
11
+ outDir: string;
12
+ entries: string[];
13
+ options?: StreamPluginOptions;
14
+ };
15
+
16
+ export function createBuildConfig({
17
+ root,
18
+ base,
19
+ outDir,
20
+ entries,
21
+ options,
22
+ }: CreateBuildConfigOptions) {
23
+ // Transform entries into proper input format with unique keys
24
+ const entryInputs = Object.fromEntries(
25
+ entries.map((entry) => {
26
+ // Get the path relative to root
27
+ const relativePath = entry.replace(root + "/", "");
28
+ // Create a unique key based on the full path
29
+ const key = relativePath.replace(/\.[^/.]+$/, ""); // Remove extension
30
+
31
+ return [key, entry];
32
+ })
33
+ );
34
+
35
+ const workerPath = options?.workerPath
36
+ ? resolve(root, options.workerPath)
37
+ : resolve(__dirname, "..", DEFAULT_CONFIG.WORKER_PATH);
38
+
39
+ const loaderPath = options?.loaderPath
40
+ ? resolve(root, options.loaderPath)
41
+ : resolve(__dirname, "..", DEFAULT_CONFIG.LOADER_PATH);
42
+
43
+ const config: InlineConfig = {
44
+ configFile: false,
45
+ root,
46
+ base,
47
+ build: {
48
+ target: "node18",
49
+ ssr: true,
50
+ ssrEmitAssets: false,
51
+ manifest: true,
52
+ ssrManifest: true,
53
+ outDir,
54
+ rollupOptions: {
55
+ input: {
56
+ ...entryInputs,
57
+ worker: workerPath,
58
+ loader: loaderPath,
59
+ },
60
+ output: {
61
+ format: "esm",
62
+ preserveModules: true,
63
+ hoistTransitiveImports: false,
64
+ esModule: true,
65
+ entryFileNames: "[name].js",
66
+ chunkFileNames: "[name].js",
67
+ assetFileNames: "[name][extname]",
68
+ },
69
+ },
70
+ },
71
+ };
72
+
73
+ return config;
74
+ }
@@ -0,0 +1,67 @@
1
+ import { existsSync } from "node:fs";
2
+ import { resolve } from "node:path";
3
+ import type { StreamPluginOptions } from "./types.js";
4
+
5
+ export async function checkFilesExist(
6
+ pages: string[],
7
+ options: Pick<StreamPluginOptions, "Page" | "props">,
8
+ root: string
9
+ ) {
10
+ const errors: string[] = [];
11
+ const pageSet = new Set<string>();
12
+ const pageMap = new Map<string, string>();
13
+ // Check if files exist when string paths are provided
14
+ if (typeof options.Page === "string") {
15
+ const pagePath = resolve(root, options.Page);
16
+ pageMap.set(options.Page, pagePath);
17
+ if (!pageSet.has(pagePath)) {
18
+ if (!existsSync(pagePath)) {
19
+ errors.push(`Page file not found: ${pagePath}`);
20
+ }
21
+ pageSet.add(pagePath);
22
+ }
23
+ } else if (typeof options.Page === "function" && pages) {
24
+ for (const page of pages) {
25
+ const pagePath = options.Page(resolve(root, page));
26
+ pageMap.set(page, pagePath);
27
+ if (pageSet.has(pagePath)) {
28
+ continue;
29
+ }
30
+ if (!existsSync(pagePath)) {
31
+ errors.push(`Page file not found: ${pagePath}`);
32
+ }
33
+ pageSet.add(pagePath);
34
+ }
35
+ }
36
+
37
+ const propsSet = new Set<string>();
38
+ const propsMap = new Map<string, string>();
39
+ if (typeof options.props === "string") {
40
+ const propsPath = resolve(root, options.props);
41
+ propsMap.set(options.props, propsPath);
42
+ if (!propsSet.has(propsPath)) {
43
+ if (!existsSync(propsPath)) {
44
+ errors.push(`Props file not found: ${propsPath}`);
45
+ }
46
+ propsSet.add(propsPath);
47
+ }
48
+ } else if (typeof options.props === "function" && pages) {
49
+ for (const page of pages) {
50
+ const propsPath = options.props(resolve(root, page));
51
+ propsMap.set(page, propsPath);
52
+ if (propsSet.has(propsPath)) {
53
+ continue;
54
+ }
55
+ if (!existsSync(propsPath)) {
56
+ errors.push(`Props file not found: ${propsPath}`);
57
+ }
58
+ propsSet.add(propsPath);
59
+ }
60
+ }
61
+
62
+ if (errors.length) {
63
+ throw new Error("React Stream Plugin Validation:\n" + errors.join("\n"));
64
+ }
65
+
66
+ return { pageMap, pageSet, propsMap, propsSet };
67
+ }
@@ -0,0 +1,76 @@
1
+ import type { Manifest, ModuleGraph } from "vite";
2
+
3
+ export async function collectModuleGraphCss(
4
+ moduleGraph: ModuleGraph,
5
+ pagePath: string
6
+ ) {
7
+ if (!pagePath) return new Map<string, string>();
8
+
9
+ const cssFiles = new Map<string, string>();
10
+ const pageModule = await moduleGraph.getModuleByUrl(pagePath, true);
11
+ if (!pageModule) {
12
+ return new Map<string, string>();
13
+ }
14
+ const seen = new Set<string>();
15
+ const walkModule = (mod: any) => {
16
+ if (!mod?.id || seen.has(mod.id)) return;
17
+ seen.add(mod.id);
18
+ if (mod?.id?.endsWith(".css")) {
19
+ cssFiles.set(mod?.url, mod?.id);
20
+ }
21
+ mod?.importedModules?.forEach((imp: any) => walkModule(imp));
22
+ };
23
+ walkModule(pageModule);
24
+ return cssFiles;
25
+ }
26
+
27
+ export function collectManifestCss(
28
+ manifest: Manifest,
29
+ root: string,
30
+ pagePath: string,
31
+ onCss?: (path: string) => void
32
+ ) {
33
+ const relativePagePath = pagePath.startsWith(root + "/")
34
+ ? pagePath.slice(root.length + 1)
35
+ : pagePath;
36
+ if (!relativePagePath) return new Map<string, string>();
37
+ const cssFiles = new Map<string, string>();
38
+ const seen = new Set<string>();
39
+
40
+ const walkManifestEntry = (id: string) => {
41
+ if (seen.has(id)) return;
42
+ seen.add(id);
43
+ if (id.endsWith(".css")) {
44
+ cssFiles.set(id, id);
45
+ onCss?.(id);
46
+ return;
47
+ }
48
+ // Get the manifest entry
49
+ const entry = manifest[id];
50
+ if (!entry) return;
51
+
52
+ // Add direct CSS
53
+ if (entry.css) {
54
+ entry.css.forEach((css: string) => {
55
+ cssFiles.set(entry.file, css);
56
+ onCss?.(css);
57
+ });
58
+ }
59
+
60
+ // Walk imports recursively
61
+ if (entry.imports) {
62
+ entry.imports.forEach((imp: string) => walkManifestEntry(imp));
63
+ }
64
+
65
+ // Also check dynamicImports
66
+ if (entry.dynamicImports) {
67
+ entry.dynamicImports.forEach((imp: string) => walkManifestEntry(imp));
68
+ }
69
+ };
70
+
71
+ if (manifest[relativePagePath]) {
72
+ walkManifestEntry(relativePagePath);
73
+ }
74
+
75
+ return cssFiles;
76
+ }
@@ -0,0 +1,14 @@
1
+ import { createElement } from 'react';
2
+
3
+ /**
4
+ * A component that emits <link> tags for CSS files during streaming.
5
+ * The high precedence ensures they bubble up to the document head.
6
+ */
7
+ export function CssCollector({ url }: { url: string }) {
8
+ return createElement('link', {
9
+ key: url,
10
+ rel: 'stylesheet',
11
+ href: url,
12
+ precedence: 'high'
13
+ });
14
+ }
@@ -0,0 +1,27 @@
1
+ import { copyFileSync, mkdirSync, readdirSync, statSync } from "node:fs";
2
+ import { join } from "node:path";
3
+
4
+ export async function copyDir(src: string, dest: string, options?: {
5
+ filter?: (file: string) => boolean
6
+ }) {
7
+ const entries = readdirSync(src);
8
+
9
+ mkdirSync(dest, { recursive: true });
10
+
11
+ for (const entry of entries) {
12
+ const srcPath = join(src, entry);
13
+ const destPath = join(dest, entry);
14
+
15
+ const stat = statSync(srcPath);
16
+
17
+ if (options?.filter && !options.filter(srcPath)) {
18
+ continue;
19
+ }
20
+
21
+ if (stat.isDirectory()) {
22
+ await copyDir(srcPath, destPath, options);
23
+ } else {
24
+ copyFileSync(srcPath, destPath);
25
+ }
26
+ }
27
+ }
package/src/getEnv.ts ADDED
@@ -0,0 +1,135 @@
1
+ import { readFileSync } from "node:fs";
2
+ import { resolve } from "node:path";
3
+ import type { ConfigEnv, UserConfig } from "vite";
4
+ import { loadEnv } from "vite";
5
+ import { DEFAULT_CONFIG } from "./options.js";
6
+
7
+ /**
8
+ * Get environment variables for Vite, sets defaults to ensure the server can start with BASE_URL and PUBLIC_URL
9
+ *
10
+ * @param config - Vite configuration object
11
+ * @param { isPreview: boolean } - Object containing a boolean indicating if the environment is for preview
12
+ * @returns An object containing the environment variables
13
+ */
14
+ export function getEnv(config: UserConfig, configEnv: ConfigEnv) {
15
+ const isLocal =
16
+ config.mode === "development"
17
+
18
+ const envName = isLocal
19
+ ? `${config.mode}.local`
20
+ : config.mode
21
+ ? config.mode
22
+ : "production";
23
+ const environmentName = config.mode ?? envName ?? "production";
24
+
25
+ const env = loadEnv(
26
+ environmentName,
27
+ config.envDir ?? config.root ?? process.cwd(),
28
+ config.envPrefix ?? DEFAULT_CONFIG.ENV_PREFIX ?? "VITE_"
29
+ );
30
+
31
+ // Get server config
32
+ const serverConfig = config.server || {};
33
+ const previewConfig = config.preview || {};
34
+ const host = configEnv.isPreview
35
+ ? previewConfig.host ?? DEFAULT_CONFIG.PREVIEW_HOST
36
+ : serverConfig.host ?? DEFAULT_CONFIG.DEV_HOST;
37
+ let previewPort = previewConfig.port ?? DEFAULT_CONFIG.PREVIEW_PORT;
38
+ let devPort = serverConfig.port ?? DEFAULT_CONFIG.DEV_PORT;
39
+
40
+ let homepage = env["VITE_BASE_URL"]
41
+ if (configEnv.command === "build" && (!homepage || homepage === "")) {
42
+ try {
43
+ const packageJson = JSON.parse(
44
+ readFileSync(resolve(config.root ?? "", "package.json"), "utf-8")
45
+ );
46
+ homepage = packageJson.homepage ?? "";
47
+ if (!homepage || homepage === "") {
48
+ console.warn(
49
+ "[RSC] 🔧 For production builds, please set 'homepage' in package.json, or set VITE_BASE_URL in your environment"
50
+ );
51
+ }
52
+ } catch (e) {
53
+ console.error(e);
54
+ }
55
+ }
56
+
57
+ let baseUrl =
58
+ env["VITE_BASE_URL"] && env["VITE_PUBLIC_URL"] !== ""
59
+ ? env["VITE_BASE_URL"]
60
+ : configEnv.isPreview
61
+ ? `http://${host}:${previewPort}`
62
+ : configEnv.command === "serve"
63
+ ? `http://${host}:${devPort}`
64
+ : homepage;
65
+
66
+ let publicUrl =
67
+ env["VITE_PUBLIC_URL"] && env["VITE_PUBLIC_URL"] !== ""
68
+ ? env["VITE_PUBLIC_URL"]
69
+ : "";
70
+
71
+ // Determine port and host based on mode
72
+ const port = configEnv.isPreview
73
+ ? previewConfig.port || DEFAULT_CONFIG.PREVIEW_PORT // Preview server
74
+ : serverConfig.port || DEFAULT_CONFIG.DEV_PORT; // Dev server
75
+
76
+ // Build base URL
77
+ if (configEnv.isPreview && `http://${host}:${port}` !== baseUrl) {
78
+ console.log(
79
+ `VITE_BASE_URL: \"${baseUrl}\" wasn't configured correctly for this server, overriding to: \"http://${host}:${port}\"`
80
+ );
81
+ baseUrl = `http://${host}:${port}`;
82
+ }
83
+
84
+ const envPrefix =
85
+ typeof config.envPrefix === "string"
86
+ ? config.envPrefix
87
+ : Array.isArray(config.envPrefix)
88
+ ? config.envPrefix[0]
89
+ : DEFAULT_CONFIG.ENV_PREFIX;
90
+
91
+ const nodeProcessEnv = {
92
+ NODE_ENV: configEnv.command === "build" ? "production" : "development",
93
+ };
94
+ const defineProcess = Object.entries(nodeProcessEnv)
95
+ .map(([key, value]) => {
96
+ switch (key) {
97
+ case "NODE_ENV":
98
+ const isDev =
99
+ value === ""
100
+ ? configEnv.command === "build"
101
+ ? false
102
+ : true
103
+ : value === "development";
104
+ return [`import.meta.env.DEV`, JSON.stringify(isDev)];
105
+ default:
106
+ return null;
107
+ }
108
+ })
109
+ .filter(Array.isArray);
110
+
111
+ const defineImportMeta = Object.entries(env).map(([key, value]) => [
112
+ `import.meta.env.${key}`,
113
+ key === "VITE_BASE_URL"
114
+ ? value
115
+ ? JSON.stringify(value)
116
+ : JSON.stringify(baseUrl)
117
+ : key === "VITE_PUBLIC_URL"
118
+ ? value
119
+ ? JSON.stringify(value)
120
+ : JSON.stringify(publicUrl)
121
+ : JSON.stringify(value),
122
+ ]);
123
+ const define = Object.fromEntries([...defineProcess, ...defineImportMeta]);
124
+
125
+ return {
126
+ baseUrl,
127
+ publicUrl,
128
+ port,
129
+ host,
130
+ envPrefix,
131
+ environmentName,
132
+ env,
133
+ define,
134
+ };
135
+ }