vite-plugin-react-server 0.3.19 → 1.0.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 (108) hide show
  1. package/README.md +219 -141
  2. package/dist/package.json +13 -6
  3. package/dist/plugin/collect-manifest-client-files.d.ts.map +1 -1
  4. package/dist/plugin/collect-manifest-client-files.js +25 -11
  5. package/dist/plugin/collect-manifest-client-files.js.map +1 -1
  6. package/dist/plugin/components.d.ts +3 -13
  7. package/dist/plugin/components.d.ts.map +1 -1
  8. package/dist/plugin/components.js +3 -54
  9. package/dist/plugin/config/defaults.d.ts +9 -2
  10. package/dist/plugin/config/defaults.d.ts.map +1 -1
  11. package/dist/plugin/config/defaults.js +3 -2
  12. package/dist/plugin/config/defaults.js.map +1 -1
  13. package/dist/plugin/config/resolveOptions.d.ts +2 -2
  14. package/dist/plugin/config/resolveOptions.d.ts.map +1 -1
  15. package/dist/plugin/config/resolveOptions.js +6 -0
  16. package/dist/plugin/config/resolveOptions.js.map +1 -1
  17. package/dist/plugin/config/resolveUserConfig.d.ts.map +1 -1
  18. package/dist/plugin/config/resolveUserConfig.js +178 -149
  19. package/dist/plugin/config/resolveUserConfig.js.map +1 -1
  20. package/dist/plugin/css-collector-inline.d.ts +10 -0
  21. package/dist/plugin/css-collector-inline.d.ts.map +1 -0
  22. package/dist/plugin/css-collector-inline.js +55 -0
  23. package/dist/plugin/css-collector-inline.js.map +1 -0
  24. package/dist/plugin/css-collector.d.ts +14 -0
  25. package/dist/plugin/css-collector.d.ts.map +1 -0
  26. package/dist/plugin/css-collector.js +49 -0
  27. package/dist/plugin/css-collector.js.map +1 -0
  28. package/dist/plugin/helpers/createHandler.d.ts +3 -8
  29. package/dist/plugin/helpers/createHandler.d.ts.map +1 -1
  30. package/dist/plugin/helpers/createHandler.js +52 -55
  31. package/dist/plugin/helpers/createHandler.js.map +1 -1
  32. package/dist/plugin/helpers/createRscStream.d.ts +14 -4
  33. package/dist/plugin/helpers/createRscStream.d.ts.map +1 -1
  34. package/dist/plugin/helpers/createRscStream.js +25 -16
  35. package/dist/plugin/helpers/createRscStream.js.map +1 -1
  36. package/dist/plugin/html.d.ts +5 -0
  37. package/dist/plugin/html.d.ts.map +1 -0
  38. package/dist/plugin/html.js +11 -0
  39. package/dist/plugin/html.js.map +1 -0
  40. package/dist/plugin/react-client/plugin.d.ts.map +1 -1
  41. package/dist/plugin/react-client/plugin.js +3 -2
  42. package/dist/plugin/react-client/plugin.js.map +1 -1
  43. package/dist/plugin/react-server/plugin.d.ts.map +1 -1
  44. package/dist/plugin/react-server/plugin.js +47 -16
  45. package/dist/plugin/react-server/plugin.js.map +1 -1
  46. package/dist/plugin/react-static/plugin.d.ts.map +1 -1
  47. package/dist/plugin/react-static/plugin.js +20 -12
  48. package/dist/plugin/react-static/plugin.js.map +1 -1
  49. package/dist/plugin/react-static/types.d.ts +2 -0
  50. package/dist/plugin/react-static/types.d.ts.map +1 -0
  51. package/dist/plugin/react-static/types.js +1 -0
  52. package/dist/plugin/types.d.ts +48 -11
  53. package/dist/plugin/types.d.ts.map +1 -1
  54. package/dist/plugin/worker/html/messageHandler.d.ts.map +1 -1
  55. package/dist/plugin/worker/html/messageHandler.js +4 -1
  56. package/dist/plugin/worker/html/messageHandler.js.map +1 -1
  57. package/dist/plugin/worker/html/renderPages.d.ts +8 -25
  58. package/dist/plugin/worker/html/renderPages.d.ts.map +1 -1
  59. package/dist/plugin/worker/html/renderPages.js +61 -44
  60. package/dist/plugin/worker/html/renderPages.js.map +1 -1
  61. package/dist/plugin/worker/rsc/messageHandler.d.ts.map +1 -1
  62. package/dist/plugin/worker/rsc/messageHandler.js +37 -22
  63. package/dist/plugin/worker/rsc/messageHandler.js.map +1 -1
  64. package/dist/plugin/worker/types.d.ts +3 -0
  65. package/dist/plugin/worker/types.d.ts.map +1 -1
  66. package/dist/tsconfig.tsbuildinfo +1 -1
  67. package/package.json +12 -4
  68. package/plugin/collect-manifest-client-files.ts +25 -10
  69. package/plugin/components.ts +3 -0
  70. package/plugin/config/defaults.tsx +10 -9
  71. package/plugin/config/resolveOptions.ts +10 -5
  72. package/plugin/config/resolveUserConfig.ts +218 -177
  73. package/plugin/css-collector-inline.tsx +60 -0
  74. package/plugin/css-collector.tsx +62 -0
  75. package/plugin/helpers/createHandler.ts +73 -83
  76. package/plugin/helpers/createRscStream.ts +49 -21
  77. package/plugin/html.tsx +9 -0
  78. package/plugin/react-client/plugin.ts +3 -2
  79. package/plugin/react-server/plugin.ts +58 -22
  80. package/plugin/react-static/plugin.ts +20 -11
  81. package/plugin/react-static/types.ts +3 -0
  82. package/plugin/types.ts +53 -11
  83. package/plugin/worker/html/messageHandler.ts +5 -2
  84. package/plugin/worker/html/renderPages.ts +82 -78
  85. package/plugin/worker/rsc/messageHandler.tsx +41 -26
  86. package/plugin/worker/types.ts +3 -0
  87. package/dist/plugin/components.js.map +0 -1
  88. package/dist/plugin/getEnv.d.ts +0 -19
  89. package/dist/plugin/getEnv.d.ts.map +0 -1
  90. package/dist/plugin/getEnv.js +0 -107
  91. package/dist/plugin/module-graph.d.ts +0 -10
  92. package/dist/plugin/module-graph.d.ts.map +0 -1
  93. package/dist/plugin/module-graph.js +0 -35
  94. package/dist/plugin/worker/html/plugin.d.ts +0 -4
  95. package/dist/plugin/worker/html/plugin.d.ts.map +0 -1
  96. package/dist/plugin/worker/html/plugin.js +0 -93
  97. package/dist/plugin/worker/plugin.d.ts +0 -19
  98. package/dist/plugin/worker/plugin.d.ts.map +0 -1
  99. package/dist/plugin/worker/plugin.js +0 -23
  100. package/dist/plugin/worker/rsc/plugin.d.ts +0 -4
  101. package/dist/plugin/worker/rsc/plugin.d.ts.map +0 -1
  102. package/dist/plugin/worker/rsc/plugin.js +0 -75
  103. package/plugin/components.tsx +0 -59
  104. package/plugin/getEnv.ts +0 -135
  105. package/plugin/module-graph.ts +0 -48
  106. package/plugin/worker/html/plugin.ts +0 -100
  107. package/plugin/worker/plugin.ts +0 -26
  108. package/plugin/worker/rsc/plugin.ts +0 -83
@@ -9,6 +9,10 @@ import { join } from "path";
9
9
  // @ts-ignore
10
10
  import { globSync } from "fs";
11
11
  import type { OutputOptions } from "rollup";
12
+ import { pluginRoot } from "../root.js";
13
+
14
+ let stashedUserConfig: Record<string, ResolvedUserConfig | null> = {};
15
+
12
16
  export type ResolveUserConfigProps = {
13
17
  isClient?: boolean;
14
18
  isStatic?: boolean;
@@ -30,204 +34,241 @@ export function resolveUserConfig({
30
34
  userOptions,
31
35
  files,
32
36
  }: ResolveUserConfigProps): ResolveUserConfigReturn {
33
- try {
34
- // Get existing inputs
35
- const root = config.root ?? userOptions.projectRoot ?? process.cwd();
37
+ if(isStatic) {
38
+ const serverConfig = stashedUserConfig[`${userOptions.build.server}-ssr`]
39
+ if(!serverConfig) {
40
+ return {
41
+ type: "error",
42
+ error: new Error("Static plugin should run after the server plugin"),
43
+ }
44
+ }
45
+ return {
46
+ type: "success",
47
+ userConfig: serverConfig,
48
+ }
49
+ }
50
+ const envDir = isStatic
51
+ ? userOptions.build.static
52
+ : isClient
53
+ ? userOptions.build.client
54
+ : userOptions.build.server;
55
+ const ssr =
56
+ typeof config.build?.ssr === "boolean"
57
+ ? config.build?.ssr
58
+ : configEnv.isSsrBuild || (!isClient && !isStatic);
59
+ const envId = `${envDir}${ssr ? "-ssr" : ""}`;
60
+ if (stashedUserConfig[envId]) {
61
+ console.log(`[RSC] Using cached config for ${envId}`);
62
+ return {
63
+ type: "success",
64
+ userConfig: stashedUserConfig[envId],
65
+ };
66
+ }
67
+ // Get existing inputs
68
+ const root = config.root ?? userOptions.projectRoot ?? process.cwd();
36
69
 
37
- const normalizer = createInputNormalizer({
38
- root,
39
- preserveModulesRoot: userOptions.build.preserveModulesRoot
40
- ? userOptions.moduleBase
41
- : undefined,
42
- removeExtension: true,
43
- });
70
+ const normalizer = createInputNormalizer({
71
+ root,
72
+ preserveModulesRoot: userOptions.build.preserveModulesRoot
73
+ ? userOptions.moduleBase
74
+ : undefined,
75
+ removeExtension: true,
76
+ });
44
77
 
45
- const serverEntry = userOptions.serverEntry
46
- ? Object.fromEntries([
47
- normalizer([userOptions.serverEntry, userOptions.serverEntry]),
48
- ])
49
- : null;
50
- const clientEntry = userOptions.clientEntry
51
- ? Object.fromEntries(
52
- [
53
- [userOptions.clientEntry, userOptions.clientEntry],
54
- ["index.html", "index.html"],
55
- ].map(normalizer)
56
- )
57
- : { "index.html": "index.html" };
78
+ const serverEntry = userOptions.serverEntry
79
+ ? Object.fromEntries([
80
+ normalizer([userOptions.serverEntry, userOptions.serverEntry]),
81
+ ])
82
+ : null;
83
+ const clientEntry = userOptions.clientEntry
84
+ ? Object.fromEntries(
85
+ [
86
+ [userOptions.clientEntry, userOptions.clientEntry],
87
+ ["index.html", "index.html"],
88
+ ].map(normalizer)
89
+ )
90
+ : { "index.html": "index.html" };
58
91
 
59
- const autoDiscoveredClientFiles = (inputs: Record<string, string>) => {
60
- const allFiles = globSync(`**/*.client.*`, {
61
- cwd: join(root, userOptions.moduleBase),
62
- });
92
+ const autoDiscoveredClientFiles = (inputs: Record<string, string>) => {
93
+ const allFiles = globSync(`**/*.client.*`, {
94
+ cwd: join(root, userOptions.moduleBase),
95
+ });
63
96
 
64
- for (const file of allFiles) {
65
- const [key, value] = normalizer(join(userOptions.moduleBase, file));
66
- if (!inputs[key]) {
67
- inputs[key] = value;
68
- } else {
69
- console.warn(`[RSC] Client file already exists: ${key}`);
70
- }
97
+ for (const file of allFiles) {
98
+ const [key, value] = normalizer(join(userOptions.moduleBase, file));
99
+ if (!inputs[key]) {
100
+ inputs[key] = value;
101
+ } else {
102
+ console.warn(`[RSC] Client file already exists: ${key}`);
71
103
  }
72
- return inputs;
73
- };
74
- const autoDiscoveredServerFiles = (inputs: Record<string, string>) => {
75
- const allFiles = globSync(`${userOptions.moduleBase}/**/*.server.*`, {
76
- cwd: join(root, userOptions.moduleBase),
77
- });
78
- for (const file of allFiles) {
79
- const [key, value] = normalizer(join(userOptions.moduleBase, file));
80
- if (!inputs[key]) {
81
- inputs[key] = value;
82
- } else {
83
- console.warn(`[RSC] Server file already exists: ${key}`);
84
- }
104
+ }
105
+ return inputs;
106
+ };
107
+ const autoDiscoveredServerFiles = (inputs: Record<string, string>) => {
108
+ const allFiles = globSync(`${userOptions.moduleBase}/**/*.server.*`, {
109
+ cwd: join(root, userOptions.moduleBase),
110
+ });
111
+ for (const file of allFiles) {
112
+ const [key, value] = normalizer(join(userOptions.moduleBase, file));
113
+ if (!inputs[key]) {
114
+ inputs[key] = value;
115
+ } else {
116
+ console.warn(`[RSC] Server file already exists: ${key}`);
85
117
  }
86
- return inputs;
87
- };
88
- const autoDiscoveredFiles = (inputs: Record<string, string>) => {
89
- if (!files) return inputs;
118
+ }
119
+ return inputs;
120
+ };
121
+ const customWorkerFiles = (inputs: Record<string, string>) => {
122
+ const customRscWorker = !userOptions.rscWorkerPath.startsWith(pluginRoot)
123
+ const customHtmlWorker = !userOptions.htmlWorkerPath.startsWith(pluginRoot)
124
+ if(customRscWorker && !inputs['rsc-worker']) {
125
+ inputs['rsc-worker'] = userOptions.rscWorkerPath
126
+ }
127
+ if(customHtmlWorker && !inputs['html-worker']) {
128
+ inputs['html-worker'] = userOptions.htmlWorkerPath
129
+ }
130
+ return inputs
131
+ }
132
+ const autoDiscoveredFiles = (inputs: Record<string, string>) => {
133
+ if (!files) return inputs;
90
134
 
91
- // Add page files without extra prefix
92
- for (const [key, value] of files.pageMap) {
93
- if (!inputs[key]) {
94
- inputs[key] = value;
95
- } else {
96
- console.warn(`[RSC] Page file already exists: ${key}`);
97
- }
135
+ // Add page files without extra prefix
136
+ for (const [key, value] of files.pageMap) {
137
+ if (!inputs[key]) {
138
+ inputs[key] = value;
139
+ } else {
140
+ console.warn(`[RSC] Page file already exists: ${key}`);
98
141
  }
99
- // Add props files without extra prefix
100
- for (const [key, value] of files.propsMap) {
101
- if (!inputs[key]) {
102
- inputs[key] = value;
103
- } else {
104
- console.warn(`[RSC] Props file already exists: ${key}`);
105
- }
142
+ }
143
+ // Add props files without extra prefix
144
+ for (const [key, value] of files.propsMap) {
145
+ if (!inputs[key]) {
146
+ inputs[key] = value;
147
+ } else {
148
+ console.warn(`[RSC] Props file already exists: ${key}`);
106
149
  }
107
- return inputs;
108
- };
150
+ }
151
+ return inputs;
152
+ };
109
153
 
110
- // Add inputs based on condition
111
- let inputs = isClient
112
- ? autoDiscoveredClientFiles(clientEntry)
113
- : autoDiscoveredServerFiles(autoDiscoveredFiles(serverEntry ?? {}));
154
+ // Add inputs based on condition
155
+ let inputs = isClient
156
+ ? autoDiscoveredClientFiles(clientEntry)
157
+ : customWorkerFiles(autoDiscoveredServerFiles(autoDiscoveredFiles(serverEntry ?? {})));
114
158
 
115
- const envDir = isStatic ? userOptions.build.static : isClient
116
- ? userOptions.build.client
117
- : userOptions.build.server;
159
+ const pluginOutput = {
160
+ preserveModules: !isClient,
161
+ preserveModulesRoot: userOptions.build.preserveModulesRoot
162
+ ? userOptions.moduleBase
163
+ : undefined,
164
+ entryFileNames: userOptions.build.entryFile,
165
+ assetFileNames: userOptions.build.assetFile,
166
+ chunkFileNames: userOptions.build.chunkFile,
167
+ format: "esm",
168
+ exports: "named",
169
+ hoistTransitiveImports: false,
170
+ generatedCode: {
171
+ constBindings: true,
172
+ objectShorthand: true,
173
+ },
174
+ interop: "auto",
175
+ } satisfies OutputOptions;
118
176
 
119
- const pluginOutput = {
120
- preserveModules: !isClient,
121
- preserveModulesRoot: userOptions.build.preserveModulesRoot
122
- ? userOptions.moduleBase
123
- : undefined,
124
- entryFileNames: userOptions.build.entryFile,
125
- assetFileNames: userOptions.build.assetFile,
126
- chunkFileNames: userOptions.build.chunkFile,
127
- format: "esm",
128
- exports: "named",
129
- hoistTransitiveImports: false,
130
- generatedCode: {
131
- constBindings: true,
132
- objectShorthand: true,
133
- },
134
- interop: "auto",
135
- } satisfies OutputOptions;
136
-
137
- let newOutput = Array.isArray(config.build?.rollupOptions?.output)
138
- ? [...config.build?.rollupOptions?.output, pluginOutput]
139
- : typeof config.build?.rollupOptions?.output === "object" &&
140
- config.build?.rollupOptions?.output !== null
141
- ? [config.build?.rollupOptions?.output, pluginOutput]
142
- : pluginOutput;
177
+ let newOutput = Array.isArray(config.build?.rollupOptions?.output)
178
+ ? [...config.build?.rollupOptions?.output, pluginOutput]
179
+ : typeof config.build?.rollupOptions?.output === "object" &&
180
+ config.build?.rollupOptions?.output !== null
181
+ ? [config.build?.rollupOptions?.output, pluginOutput]
182
+ : pluginOutput;
143
183
 
144
- if (isClient) {
145
- // client plugin build options (client plugin still outputs server files)
146
- return {
147
- type: "success",
148
- userConfig: {
149
- ...config,
150
- root: root,
151
- mode: configEnv.mode ?? configEnv.command === "build" ? "production" : "development",
152
- resolve: {
153
- external: ["react", "react-dom"],
154
- alias: {},
155
- },
156
- ssr: {
157
- target: "node",
158
- external: [
159
- "react",
160
- "react-dom",
161
- "react-server-dom-esm/client.browser",
162
- ],
163
- resolve: {
164
- externalConditions: ["react-server"],
165
- },
166
- },
167
- // client build options
168
- build: {
169
- ...config.build,
170
- emptyOutDir: config.build?.emptyOutDir ?? true,
171
- outDir: join(userOptions.build.outDir, envDir),
172
- assetsDir: config.build?.assetsDir ?? userOptions.build.assetsDir,
173
- copyPublicDir: config.build?.copyPublicDir ?? true,
174
- // modern browsers
175
- target: ["esnext"],
176
- minify: true,
177
- ssr: typeof config.build?.ssr === "boolean" ? config.build?.ssr : configEnv.isSsrBuild ?? false,
178
- manifest: config.build?.manifest ?? `.vite/manifest.json`,
179
- ssrManifest: config.build?.ssrManifest ?? `.vite/ssr-manifest.json`,
180
- ssrEmitAssets: config.build?.ssrEmitAssets ?? true,
181
- rollupOptions: {
182
- ...config.build?.rollupOptions,
183
- input: inputs,
184
- output: newOutput,
185
- preserveEntrySignatures: "exports-only",
186
- },
187
- },
188
- },
189
- };
190
- }
191
- // server build options
192
- if(configEnv.isSsrBuild === false) {
193
- configEnv.isSsrBuild = true
194
- }
195
- return {
196
- type: "success",
197
- userConfig: {
198
- ...config,
199
- root: root,
200
- mode: configEnv.mode ?? configEnv.command === "build" ? "production" : "development",
184
+ if (isClient) {
185
+ // client plugin build options (client plugin still outputs server files)
186
+ stashedUserConfig[envId] = {
187
+ ...config,
188
+ root: root,
189
+ mode:
190
+ configEnv.mode ?? configEnv.command === "build"
191
+ ? "production"
192
+ : "development",
193
+ resolve: {
194
+ external: ["react", "react-dom"],
195
+ alias: {},
196
+ },
197
+ ssr: {
198
+ target: "node",
199
+ external: ["react", "react-dom", "react-server-dom-esm/client.browser"],
201
200
  resolve: {
202
201
  externalConditions: ["react-server"],
203
202
  },
204
- // server build options
205
- build: {
206
- ...config.build,
207
- emptyOutDir: config.build?.emptyOutDir ?? true,
208
- outDir: join(userOptions.build.outDir, envDir),
209
- target: config.build?.target ?? "node18",
210
- minify: config.build?.minify ?? true,
211
- ssr: typeof config.build?.ssr === "boolean" ? config.build?.ssr : configEnv.isSsrBuild ?? true,
212
- manifest: config.build?.manifest ?? `.vite/manifest.json`,
213
- ssrManifest: config.build?.ssrManifest ?? `.vite/ssr-manifest.json`,
214
- ssrEmitAssets: config.build?.ssrEmitAssets ?? true,
215
- copyPublicDir: config.build?.copyPublicDir ?? isStatic,
216
- assetsDir: config.build?.assetsDir ?? userOptions.build.assetsDir,
217
- rollupOptions: {
218
- ...config.build?.rollupOptions,
219
- input: inputs,
220
- preserveEntrySignatures: config.build?.rollupOptions?.preserveEntrySignatures ?? "strict",
221
- output: newOutput,
222
- },
203
+ },
204
+ // client build options
205
+ build: {
206
+ ...config.build,
207
+ emptyOutDir: config.build?.emptyOutDir ?? true,
208
+ outDir: join(userOptions.build.outDir, envDir),
209
+ assetsDir: config.build?.assetsDir ?? userOptions.build.assetsDir,
210
+ copyPublicDir: config.build?.copyPublicDir ?? true,
211
+ // modern browsers
212
+ target: ["esnext"],
213
+ minify: true,
214
+ ssr: ssr,
215
+ manifest: config.build?.manifest ?? `.vite/manifest.json`,
216
+ ssrManifest: config.build?.ssrManifest ?? `.vite/ssr-manifest.json`,
217
+ ssrEmitAssets: config.build?.ssrEmitAssets ?? true,
218
+ rollupOptions: {
219
+ ...config.build?.rollupOptions,
220
+ input: inputs,
221
+ output: newOutput,
222
+ preserveEntrySignatures: "exports-only",
223
+ },
224
+ },
225
+ };
226
+ } else {
227
+ // server build options
228
+ if (configEnv.isSsrBuild === false) {
229
+ configEnv.isSsrBuild = true;
230
+ }
231
+ stashedUserConfig[envId] = {
232
+ ...config,
233
+ root: root,
234
+ mode:
235
+ configEnv.mode ?? configEnv.command === "build"
236
+ ? "production"
237
+ : "development",
238
+ resolve: {
239
+ externalConditions: ["react-server"],
240
+ },
241
+ // server build options
242
+ build: {
243
+ ...config.build,
244
+ emptyOutDir: config.build?.emptyOutDir ?? true,
245
+ outDir: join(userOptions.build.outDir, envDir),
246
+ target: config.build?.target ?? "node18",
247
+ minify: config.build?.minify ?? true,
248
+ ssr: ssr,
249
+ manifest: config.build?.manifest ?? `.vite/manifest.json`,
250
+ ssrManifest: config.build?.ssrManifest ?? `.vite/ssr-manifest.json`,
251
+ ssrEmitAssets: config.build?.ssrEmitAssets ?? true,
252
+ copyPublicDir: config.build?.copyPublicDir ?? isStatic,
253
+ assetsDir: config.build?.assetsDir ?? userOptions.build.assetsDir,
254
+ rollupOptions: {
255
+ ...config.build?.rollupOptions,
256
+ input: inputs,
257
+ preserveEntrySignatures:
258
+ config.build?.rollupOptions?.preserveEntrySignatures ?? "strict",
259
+ output: newOutput,
223
260
  },
224
261
  },
225
262
  };
226
- } catch (error) {
263
+ }
264
+ if (!stashedUserConfig[envId]) {
227
265
  return {
228
266
  type: "error",
229
- error:
230
- error instanceof Error ? error : new Error("Failed to resolve config"),
267
+ error: new Error("Failed to resolve config"),
231
268
  };
232
269
  }
270
+ return {
271
+ type: "success",
272
+ userConfig: stashedUserConfig[envId],
273
+ };
233
274
  }
@@ -0,0 +1,60 @@
1
+ import React from "react";
2
+ import { join } from "node:path";
3
+ import type { InlineCssCollectorProps } from "./types.js";
4
+ import { readFileSync } from "node:fs";
5
+
6
+ interface CssContent {
7
+ type?: string;
8
+ content: string;
9
+ key?: string;
10
+ path: string;
11
+ }
12
+
13
+ /**
14
+ * A component that inlines pre-transformed CSS content.
15
+ * Expects cssFiles to be an array of CssContent objects with the content already loaded.
16
+ */
17
+ export function InlineCssCollector({
18
+ children,
19
+ cssFiles,
20
+ moduleRootPath,
21
+ }: InlineCssCollectorProps) {
22
+ return React.createElement(
23
+ React.Fragment,
24
+ null,
25
+ cssFiles.map((file, index) => {
26
+ if (typeof file === "string") {
27
+ let filePath = file as string;
28
+ if(process.env["NODE_ENV"] === "development") {
29
+ return React.createElement("link", {
30
+ key: file,
31
+ rel: "stylesheet",
32
+ href: filePath,
33
+ });
34
+ }
35
+ return React.createElement(
36
+ "style",
37
+ {
38
+ key: file,
39
+ type: "text/css",
40
+ },
41
+ readFileSync(join(moduleRootPath, filePath), "utf-8")
42
+ );
43
+ }
44
+
45
+ const { type, content, key, path } = file as CssContent;
46
+ return React.createElement(
47
+ "style",
48
+ {
49
+ key: key ?? path ?? index,
50
+ type: type ?? "text/css",
51
+ ...(process.env["NODE_ENV"] === "development" && {
52
+ "data-vite-dev-id": join(moduleRootPath, path),
53
+ }),
54
+ },
55
+ content
56
+ );
57
+ }),
58
+ children
59
+ );
60
+ }
@@ -0,0 +1,62 @@
1
+ import React from "react";
2
+ import { join } from "node:path";
3
+
4
+ /**
5
+ * A component that emits <link> tags for CSS files during streaming.
6
+ * The high precedence ensures they bubble up to the document head.
7
+ */
8
+ export function CssCollector({
9
+ children,
10
+ cssFiles,
11
+ moduleBaseURL,
12
+ route = "/",
13
+ }: {
14
+ children?: React.ReactNode;
15
+ cssFiles: string[];
16
+ moduleBaseURL?: string;
17
+ route?: string;
18
+ }) {
19
+ // Calculate depth and prefix based on route
20
+ const depth = route.split('/').filter(Boolean).length;
21
+ const prefix = depth > 0 ? '../'.repeat(depth) : './';
22
+ const base = typeof moduleBaseURL === 'string' && moduleBaseURL !== '' ? moduleBaseURL : prefix;
23
+
24
+ const elements = cssFiles.map((file) => {
25
+ // Handle pre-processed CSS content - skip in link mode
26
+ if (typeof file === "object" && !React.isValidElement(file)) {
27
+ return null;
28
+ }
29
+ // Handle string paths
30
+ if (typeof file === "string" && file.endsWith('.css')) {
31
+ // Handle different types of paths
32
+ let url = file;
33
+ if (file.startsWith('http') || file.startsWith('data:')) {
34
+ // Keep absolute URLs as is
35
+ url = file;
36
+ } else if (file.startsWith('/')) {
37
+ // Convert absolute paths to relative
38
+ url = base + file.slice(1);
39
+ } else if (!file.startsWith('./') && !file.startsWith('../')) {
40
+ // Add prefix to relative paths that don't start with ./ or ../
41
+ url = join(base, file);
42
+ }
43
+
44
+ return React.createElement("link", {
45
+ key: file,
46
+ rel: "stylesheet",
47
+ href: url,
48
+ precedence: "high",
49
+ });
50
+ }
51
+
52
+ return null;
53
+ }).filter(Boolean);
54
+
55
+ return React.createElement(
56
+ React.Fragment,
57
+ null,
58
+ ...elements,
59
+ children
60
+ );
61
+ }
62
+