@react-router/dev 0.0.0-experimental-c0856287f

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 (68) hide show
  1. package/CHANGELOG.md +1773 -0
  2. package/LICENSE.md +23 -0
  3. package/README.md +13 -0
  4. package/dist/cli/commands.d.ts +12 -0
  5. package/dist/cli/commands.js +174 -0
  6. package/dist/cli/detectPackageManager.d.ts +10 -0
  7. package/dist/cli/detectPackageManager.js +39 -0
  8. package/dist/cli/index.d.ts +1 -0
  9. package/dist/cli/index.js +19 -0
  10. package/dist/cli/run.d.ts +5 -0
  11. package/dist/cli/run.js +180 -0
  12. package/dist/cli/useJavascript.d.ts +4 -0
  13. package/dist/cli/useJavascript.js +66 -0
  14. package/dist/cli.d.ts +1 -0
  15. package/dist/cli.js +21 -0
  16. package/dist/colors.d.ts +17 -0
  17. package/dist/colors.js +49 -0
  18. package/dist/config/defaults/entry.client.tsx +12 -0
  19. package/dist/config/defaults/entry.dev.d.ts +2 -0
  20. package/dist/config/defaults/entry.dev.ts +13 -0
  21. package/dist/config/defaults/entry.server.cloudflare.tsx +55 -0
  22. package/dist/config/defaults/entry.server.deno.tsx +55 -0
  23. package/dist/config/defaults/entry.server.node.tsx +155 -0
  24. package/dist/config/defaults/entry.server.spa.tsx +20 -0
  25. package/dist/config/flat-routes.d.ts +14 -0
  26. package/dist/config/flat-routes.js +418 -0
  27. package/dist/config/format.d.ts +5 -0
  28. package/dist/config/format.js +68 -0
  29. package/dist/config/routes.d.ts +98 -0
  30. package/dist/config/routes.js +93 -0
  31. package/dist/config/serverModes.d.ts +9 -0
  32. package/dist/config/serverModes.js +28 -0
  33. package/dist/config.d.ts +75 -0
  34. package/dist/config.js +152 -0
  35. package/dist/index.d.ts +4 -0
  36. package/dist/index.js +23 -0
  37. package/dist/invariant.d.ts +2 -0
  38. package/dist/invariant.js +22 -0
  39. package/dist/manifest.d.ts +28 -0
  40. package/dist/vite/babel.d.ts +20 -0
  41. package/dist/vite/babel.js +49 -0
  42. package/dist/vite/build.d.ts +15 -0
  43. package/dist/vite/build.js +271 -0
  44. package/dist/vite/cloudflare-proxy-plugin.d.ts +15 -0
  45. package/dist/vite/cloudflare-proxy-plugin.js +82 -0
  46. package/dist/vite/dev.d.ts +15 -0
  47. package/dist/vite/dev.js +81 -0
  48. package/dist/vite/import-vite-esm-sync.d.ts +4 -0
  49. package/dist/vite/import-vite-esm-sync.js +28 -0
  50. package/dist/vite/index.d.ts +4 -0
  51. package/dist/vite/index.js +30 -0
  52. package/dist/vite/node-adapter.d.ts +6 -0
  53. package/dist/vite/node-adapter.js +78 -0
  54. package/dist/vite/plugin.d.ts +165 -0
  55. package/dist/vite/plugin.js +1178 -0
  56. package/dist/vite/profiler.d.ts +5 -0
  57. package/dist/vite/profiler.js +55 -0
  58. package/dist/vite/remove-exports-test.d.ts +1 -0
  59. package/dist/vite/remove-exports.d.ts +2 -0
  60. package/dist/vite/remove-exports.js +278 -0
  61. package/dist/vite/resolve-file-url.d.ts +3 -0
  62. package/dist/vite/resolve-file-url.js +53 -0
  63. package/dist/vite/static/refresh-utils.cjs +185 -0
  64. package/dist/vite/styles.d.ts +13 -0
  65. package/dist/vite/styles.js +176 -0
  66. package/dist/vite/vmod.d.ts +3 -0
  67. package/dist/vite/vmod.js +21 -0
  68. package/package.json +107 -0
@@ -0,0 +1,1178 @@
1
+ /**
2
+ * @react-router/dev v0.0.0-experimental-c0856287f
3
+ *
4
+ * Copyright (c) Remix Software Inc.
5
+ *
6
+ * This source code is licensed under the MIT license found in the
7
+ * LICENSE.md file in the root directory of this source tree.
8
+ *
9
+ * @license MIT
10
+ */
11
+ 'use strict';
12
+
13
+ Object.defineProperty(exports, '__esModule', { value: true });
14
+
15
+ var node_crypto = require('node:crypto');
16
+ var path = require('node:path');
17
+ var url = require('node:url');
18
+ var fse = require('fs-extra');
19
+ var babel = require('@babel/core');
20
+ var serverRuntime = require('@react-router/server-runtime');
21
+ var esModuleLexer = require('es-module-lexer');
22
+ var jsesc = require('jsesc');
23
+ var pick = require('lodash/pick');
24
+ var omit = require('lodash/omit');
25
+ var colors = require('picocolors');
26
+ var config = require('../config.js');
27
+ var invariant = require('../invariant.js');
28
+ var nodeAdapter = require('./node-adapter.js');
29
+ var styles = require('./styles.js');
30
+ var vmod = require('./vmod.js');
31
+ var resolveFileUrl = require('./resolve-file-url.js');
32
+ var removeExports = require('./remove-exports.js');
33
+ var importViteEsmSync = require('./import-vite-esm-sync.js');
34
+
35
+ function _interopDefaultLegacy (e) { return e && typeof e === 'object' && 'default' in e ? e : { 'default': e }; }
36
+
37
+ function _interopNamespace(e) {
38
+ if (e && e.__esModule) return e;
39
+ var n = Object.create(null);
40
+ if (e) {
41
+ Object.keys(e).forEach(function (k) {
42
+ if (k !== 'default') {
43
+ var d = Object.getOwnPropertyDescriptor(e, k);
44
+ Object.defineProperty(n, k, d.get ? d : {
45
+ enumerable: true,
46
+ get: function () { return e[k]; }
47
+ });
48
+ }
49
+ });
50
+ }
51
+ n["default"] = e;
52
+ return Object.freeze(n);
53
+ }
54
+
55
+ var path__namespace = /*#__PURE__*/_interopNamespace(path);
56
+ var url__namespace = /*#__PURE__*/_interopNamespace(url);
57
+ var fse__namespace = /*#__PURE__*/_interopNamespace(fse);
58
+ var babel__default = /*#__PURE__*/_interopDefaultLegacy(babel);
59
+ var jsesc__default = /*#__PURE__*/_interopDefaultLegacy(jsesc);
60
+ var pick__default = /*#__PURE__*/_interopDefaultLegacy(pick);
61
+ var omit__default = /*#__PURE__*/_interopDefaultLegacy(omit);
62
+ var colors__default = /*#__PURE__*/_interopDefaultLegacy(colors);
63
+
64
+ async function resolveViteConfig({
65
+ configFile,
66
+ mode,
67
+ root
68
+ }) {
69
+ let vite = await import('vite');
70
+ let viteConfig = await vite.resolveConfig({
71
+ mode,
72
+ configFile,
73
+ root
74
+ }, "build",
75
+ // command
76
+ "production",
77
+ // default mode
78
+ "production" // default NODE_ENV
79
+ );
80
+ if (typeof viteConfig.build.manifest === "string") {
81
+ throw new Error("Custom Vite manifest paths are not supported");
82
+ }
83
+ return viteConfig;
84
+ }
85
+ async function extractPluginContext(viteConfig) {
86
+ return viteConfig["__reactRouterPluginContext"];
87
+ }
88
+ async function loadPluginContext({
89
+ configFile,
90
+ root
91
+ }) {
92
+ if (!root) {
93
+ root = process.env.REACT_ROUTER_ROOT || process.cwd();
94
+ }
95
+ configFile = configFile ?? config.findConfig(root, "vite.config", [".ts", ".cts", ".mts", ".js", ".cjs", ".mjs"]);
96
+ if (!configFile) {
97
+ console.error(colors__default["default"].red("Vite config file not found"));
98
+ process.exit(1);
99
+ }
100
+ let viteConfig = await resolveViteConfig({
101
+ configFile,
102
+ root
103
+ });
104
+ let ctx = await extractPluginContext(viteConfig);
105
+ if (!ctx) {
106
+ console.error(colors__default["default"].red("React Router Vite plugin not found in Vite config"));
107
+ process.exit(1);
108
+ }
109
+ return ctx;
110
+ }
111
+ const SERVER_ONLY_ROUTE_EXPORTS = ["loader", "action", "headers"];
112
+ const CLIENT_ROUTE_EXPORTS = ["clientAction", "clientLoader", "default", "ErrorBoundary", "handle", "HydrateFallback", "Layout", "links", "meta", "shouldRevalidate"];
113
+ // The "=1" suffix ensures client route requests can be processed before hitting
114
+ // the Vite plugin since "?client-route" can be serialized as "?client-route="
115
+ const CLIENT_ROUTE_QUERY_STRING = "?client-route=1";
116
+ // Only expose a subset of route properties to the "serverBundles" function
117
+ const branchRouteProperties = ["id", "path", "file", "index"];
118
+ const configRouteToBranchRoute = configRoute => pick__default["default"](configRoute, branchRouteProperties);
119
+ const excludedConfigPresetKeys = ["presets"];
120
+ let serverBuildId = vmod.id("server-build");
121
+ let serverManifestId = vmod.id("server-manifest");
122
+ let browserManifestId = vmod.id("browser-manifest");
123
+ let hmrRuntimeId = vmod.id("hmr-runtime");
124
+ let injectHmrRuntimeId = vmod.id("inject-hmr-runtime");
125
+ const resolveRelativeRouteFilePath = (route, reactRouterConfig) => {
126
+ let vite = importViteEsmSync.importViteEsmSync();
127
+ let file = route.file;
128
+ let fullPath = path__namespace.resolve(reactRouterConfig.appDirectory, file);
129
+ return vite.normalizePath(fullPath);
130
+ };
131
+ let vmods = [serverBuildId, serverManifestId, browserManifestId];
132
+ const invalidateVirtualModules = viteDevServer => {
133
+ vmods.forEach(vmod$1 => {
134
+ let mod = viteDevServer.moduleGraph.getModuleById(vmod.resolve(vmod$1));
135
+ if (mod) {
136
+ viteDevServer.moduleGraph.invalidateModule(mod);
137
+ }
138
+ });
139
+ };
140
+ const getHash = (source, maxLength) => {
141
+ let hash = node_crypto.createHash("sha256").update(source).digest("hex");
142
+ return typeof maxLength === "number" ? hash.slice(0, maxLength) : hash;
143
+ };
144
+ const isClientRoute = id => {
145
+ return id.endsWith(CLIENT_ROUTE_QUERY_STRING);
146
+ };
147
+ const resolveChunk = (ctx, viteManifest, absoluteFilePath) => {
148
+ let vite = importViteEsmSync.importViteEsmSync();
149
+ let rootRelativeFilePath = vite.normalizePath(path__namespace.relative(ctx.rootDirectory, absoluteFilePath));
150
+ let entryChunk = viteManifest[rootRelativeFilePath + CLIENT_ROUTE_QUERY_STRING] ?? viteManifest[rootRelativeFilePath];
151
+ if (!entryChunk) {
152
+ let knownManifestKeys = Object.keys(viteManifest).map(key => '"' + key + '"').join(", ");
153
+ throw new Error(`No manifest entry found for "${rootRelativeFilePath}". Known manifest keys: ${knownManifestKeys}`);
154
+ }
155
+ return entryChunk;
156
+ };
157
+ const getReactRouterManifestBuildAssets = (ctx, viteManifest, entryFilePath, prependedAssetFilePaths = []) => {
158
+ let entryChunk = resolveChunk(ctx, viteManifest, entryFilePath);
159
+ // This is here to support prepending client entry assets to the root route
160
+ let prependedAssetChunks = prependedAssetFilePaths.map(filePath => resolveChunk(ctx, viteManifest, filePath));
161
+ let chunks = resolveDependantChunks(viteManifest, [...prependedAssetChunks, entryChunk]);
162
+ return {
163
+ module: `${ctx.reactRouterConfig.publicPath}${entryChunk.file}`,
164
+ imports: dedupe(chunks.flatMap(e => e.imports ?? [])).map(imported => {
165
+ return `${ctx.reactRouterConfig.publicPath}${viteManifest[imported].file}`;
166
+ }) ?? [],
167
+ css: dedupe(chunks.flatMap(e => e.css ?? [])).map(href => {
168
+ return `${ctx.reactRouterConfig.publicPath}${href}`;
169
+ }) ?? []
170
+ };
171
+ };
172
+ function resolveDependantChunks(viteManifest, entryChunks) {
173
+ let chunks = new Set();
174
+ function walk(chunk) {
175
+ if (chunks.has(chunk)) {
176
+ return;
177
+ }
178
+ if (chunk.imports) {
179
+ for (let importKey of chunk.imports) {
180
+ walk(viteManifest[importKey]);
181
+ }
182
+ }
183
+ chunks.add(chunk);
184
+ }
185
+ for (let entryChunk of entryChunks) {
186
+ walk(entryChunk);
187
+ }
188
+ return Array.from(chunks);
189
+ }
190
+ function dedupe(array) {
191
+ return [...new Set(array)];
192
+ }
193
+ const writeFileSafe = async (file, contents) => {
194
+ await fse__namespace.ensureDir(path__namespace.dirname(file));
195
+ await fse__namespace.writeFile(file, contents);
196
+ };
197
+ const getRouteManifestModuleExports = async (viteChildCompiler, ctx) => {
198
+ let entries = await Promise.all(Object.entries(ctx.reactRouterConfig.routes).map(async ([key, route]) => {
199
+ let sourceExports = await getRouteModuleExports(viteChildCompiler, ctx, route.file);
200
+ return [key, sourceExports];
201
+ }));
202
+ return Object.fromEntries(entries);
203
+ };
204
+ const getRouteModuleExports = async (viteChildCompiler, ctx, routeFile, readRouteFile) => {
205
+ if (!viteChildCompiler) {
206
+ throw new Error("Vite child compiler not found");
207
+ }
208
+ // We transform the route module code with the Vite child compiler so that we
209
+ // can parse the exports from non-JS files like MDX. This ensures that we can
210
+ // understand the exports from anything that Vite can compile to JS, not just
211
+ // the route file formats that the Remix compiler historically supported.
212
+ let ssr = true;
213
+ let {
214
+ pluginContainer,
215
+ moduleGraph
216
+ } = viteChildCompiler;
217
+ let routePath = path__namespace.resolve(ctx.reactRouterConfig.appDirectory, routeFile);
218
+ let url = resolveFileUrl.resolveFileUrl(ctx, routePath);
219
+ let resolveId = async () => {
220
+ let result = await pluginContainer.resolveId(url, undefined, {
221
+ ssr
222
+ });
223
+ if (!result) throw new Error(`Could not resolve module ID for ${url}`);
224
+ return result.id;
225
+ };
226
+ let [id, code] = await Promise.all([resolveId(), (readRouteFile === null || readRouteFile === void 0 ? void 0 : readRouteFile()) ?? fse__namespace.readFile(routePath, "utf-8"),
227
+ // pluginContainer.transform(...) fails if we don't do this first:
228
+ moduleGraph.ensureEntryFromUrl(url, ssr)]);
229
+ let transformed = await pluginContainer.transform(code, id, {
230
+ ssr
231
+ });
232
+ let [, exports] = esModuleLexer.parse(transformed.code);
233
+ let exportNames = exports.map(e => e.n);
234
+ return exportNames;
235
+ };
236
+ const getServerBundleBuildConfig = viteUserConfig => {
237
+ if (!("__reactRouterServerBundleBuildConfig" in viteUserConfig) || !viteUserConfig.__reactRouterServerBundleBuildConfig) {
238
+ return null;
239
+ }
240
+ return viteUserConfig.__reactRouterServerBundleBuildConfig;
241
+ };
242
+ let getServerBuildDirectory = ctx => path__namespace.join(ctx.reactRouterConfig.buildDirectory, "server", ...(ctx.serverBundleBuildConfig ? [ctx.serverBundleBuildConfig.serverBundleId] : []));
243
+ let getClientBuildDirectory = reactRouterConfig => path__namespace.join(reactRouterConfig.buildDirectory, "client");
244
+ let defaultEntriesDir = path__namespace.resolve(__dirname, "..", "config", "defaults");
245
+ let defaultEntries = fse__namespace.readdirSync(defaultEntriesDir).map(filename => path__namespace.join(defaultEntriesDir, filename));
246
+ invariant["default"](defaultEntries.length > 0, "No default entries found");
247
+ let mergeReactRouterConfig = (...configs) => {
248
+ let reducer = (configA, configB) => {
249
+ let mergeRequired = key => configA[key] !== undefined && configB[key] !== undefined;
250
+ return {
251
+ ...configA,
252
+ ...configB,
253
+ ...(mergeRequired("buildEnd") ? {
254
+ buildEnd: async (...args) => {
255
+ var _configA$buildEnd, _configB$buildEnd;
256
+ await Promise.all([(_configA$buildEnd = configA.buildEnd) === null || _configA$buildEnd === void 0 ? void 0 : _configA$buildEnd.call(configA, ...args), (_configB$buildEnd = configB.buildEnd) === null || _configB$buildEnd === void 0 ? void 0 : _configB$buildEnd.call(configB, ...args)]);
257
+ }
258
+ } : {}),
259
+ ...(mergeRequired("future") ? {
260
+ future: {
261
+ ...configA.future,
262
+ ...configB.future
263
+ }
264
+ } : {}),
265
+ ...(mergeRequired("ignoredRouteFiles") ? {
266
+ ignoredRouteFiles: Array.from(new Set([...(configA.ignoredRouteFiles ?? []), ...(configB.ignoredRouteFiles ?? [])]))
267
+ } : {}),
268
+ ...(mergeRequired("presets") ? {
269
+ presets: [...(configA.presets ?? []), ...(configB.presets ?? [])]
270
+ } : {}),
271
+ ...(mergeRequired("routes") ? {
272
+ routes: async (...args) => {
273
+ var _configA$routes, _configB$routes;
274
+ let [routesA, routesB] = await Promise.all([(_configA$routes = configA.routes) === null || _configA$routes === void 0 ? void 0 : _configA$routes.call(configA, ...args), (_configB$routes = configB.routes) === null || _configB$routes === void 0 ? void 0 : _configB$routes.call(configB, ...args)]);
275
+ return {
276
+ ...routesA,
277
+ ...routesB
278
+ };
279
+ }
280
+ } : {})
281
+ };
282
+ };
283
+ return configs.reduce(reducer, {});
284
+ };
285
+ let reactRouterDevLoadContext = () => ({});
286
+ let setReactRouterDevLoadContext = loadContext => {
287
+ reactRouterDevLoadContext = loadContext;
288
+ };
289
+ // Inlined from https://github.com/jsdf/deep-freeze
290
+ let deepFreeze = o => {
291
+ Object.freeze(o);
292
+ let oIsFunction = typeof o === "function";
293
+ let hasOwnProp = Object.prototype.hasOwnProperty;
294
+ Object.getOwnPropertyNames(o).forEach(function (prop) {
295
+ if (hasOwnProp.call(o, prop) && (oIsFunction ? prop !== "caller" && prop !== "callee" && prop !== "arguments" : true) && o[prop] !== null && (typeof o[prop] === "object" || typeof o[prop] === "function") && !Object.isFrozen(o[prop])) {
296
+ deepFreeze(o[prop]);
297
+ }
298
+ });
299
+ return o;
300
+ };
301
+ const reactRouterVitePlugin = _config => {
302
+ let reactRouterUserConfig = _config ?? {};
303
+ // Prevent mutations to the user config
304
+ reactRouterUserConfig = deepFreeze(reactRouterUserConfig);
305
+ let viteCommand;
306
+ let viteUserConfig;
307
+ let viteConfigEnv;
308
+ let viteConfig;
309
+ let cssModulesManifest = {};
310
+ let viteChildCompiler = null;
311
+ // This is initialized by `updatePluginContext` during Vite's `config`
312
+ // hook, so most of the code can assume this defined without null check.
313
+ // During dev, `updatePluginContext` is called again on every config file
314
+ // change or route file addition/removal.
315
+ let ctx;
316
+ /** Mutates `ctx` as a side-effect */
317
+ let updatePluginContext = async () => {
318
+ var _viteUserConfig$serve, _viteUserConfig$build;
319
+ let presets = (await Promise.all((reactRouterUserConfig.presets ?? []).map(async preset => {
320
+ if (!preset.name) {
321
+ throw new Error("React Router presets must have a `name` property defined.");
322
+ }
323
+ if (!preset.reactRouterConfig) {
324
+ return null;
325
+ }
326
+ let configPreset = omit__default["default"](await preset.reactRouterConfig({
327
+ reactRouterUserConfig
328
+ }), excludedConfigPresetKeys);
329
+ return configPreset;
330
+ }))).filter(function isNotNull(value) {
331
+ return value !== null;
332
+ });
333
+ let defaults = {
334
+ basename: "/",
335
+ buildDirectory: "build",
336
+ manifest: false,
337
+ serverBuildFile: "index.js",
338
+ ssr: true
339
+ };
340
+ let resolvedReactRouterUserConfig = {
341
+ ...defaults,
342
+ // Default values should be completely overridden by user/preset config, not merged
343
+ ...mergeReactRouterConfig(...presets, reactRouterUserConfig)
344
+ };
345
+ let rootDirectory = viteUserConfig.root ?? process.env.REACT_ROUTER_ROOT ?? process.cwd();
346
+ let {
347
+ basename,
348
+ buildEnd,
349
+ manifest,
350
+ ssr
351
+ } = resolvedReactRouterUserConfig;
352
+ let isSpaMode = !ssr;
353
+ let {
354
+ appDirectory,
355
+ entryClientFilePath,
356
+ entryServerFilePath,
357
+ future,
358
+ routes,
359
+ serverModuleFormat
360
+ } = await config.resolveConfig(resolvedReactRouterUserConfig, {
361
+ rootDirectory,
362
+ isSpaMode
363
+ });
364
+ let buildDirectory = path__namespace.resolve(rootDirectory, resolvedReactRouterUserConfig.buildDirectory);
365
+ let {
366
+ serverBuildFile,
367
+ serverBundles
368
+ } = resolvedReactRouterUserConfig;
369
+ let publicPath = viteUserConfig.base ?? "/";
370
+ if (basename !== "/" && viteCommand === "serve" && !((_viteUserConfig$serve = viteUserConfig.server) !== null && _viteUserConfig$serve !== void 0 && _viteUserConfig$serve.middlewareMode) && !basename.startsWith(publicPath)) {
371
+ throw new Error("When using the React Router `basename` and the Vite `base` config, " + "the `basename` config must begin with `base` for the default " + "Vite dev server.");
372
+ }
373
+ // Log warning for incompatible vite config flags
374
+ if (isSpaMode && serverBundles) {
375
+ console.warn(colors__default["default"].yellow(colors__default["default"].bold("⚠️ SPA Mode: ") + "the `serverBundles` config is invalid with " + "`ssr:false` and will be ignored`"));
376
+ serverBundles = undefined;
377
+ }
378
+ let reactRouterConfig = deepFreeze({
379
+ appDirectory,
380
+ basename,
381
+ buildDirectory,
382
+ buildEnd,
383
+ future,
384
+ manifest,
385
+ publicPath,
386
+ routes,
387
+ serverBuildFile,
388
+ serverBundles,
389
+ serverModuleFormat,
390
+ ssr
391
+ });
392
+ for (let preset of reactRouterUserConfig.presets ?? []) {
393
+ var _preset$reactRouterCo;
394
+ await ((_preset$reactRouterCo = preset.reactRouterConfigResolved) === null || _preset$reactRouterCo === void 0 ? void 0 : _preset$reactRouterCo.call(preset, {
395
+ reactRouterConfig
396
+ }));
397
+ }
398
+ let viteManifestEnabled = ((_viteUserConfig$build = viteUserConfig.build) === null || _viteUserConfig$build === void 0 ? void 0 : _viteUserConfig$build.manifest) === true;
399
+ let ssrBuildCtx = viteConfigEnv.isSsrBuild && viteCommand === "build" ? {
400
+ isSsrBuild: true,
401
+ getReactRouterServerManifest: async () => (await generateReactRouterManifestsForBuild()).reactRouterServerManifest,
402
+ serverBundleBuildConfig: getServerBundleBuildConfig(viteUserConfig)
403
+ } : {
404
+ isSsrBuild: false
405
+ };
406
+ ctx = {
407
+ reactRouterConfig,
408
+ rootDirectory,
409
+ entryClientFilePath,
410
+ entryServerFilePath,
411
+ viteManifestEnabled,
412
+ ...ssrBuildCtx
413
+ };
414
+ };
415
+ let pluginIndex = pluginName => {
416
+ invariant["default"](viteConfig);
417
+ return viteConfig.plugins.findIndex(plugin => plugin.name === pluginName);
418
+ };
419
+ let getServerEntry = async () => {
420
+ invariant["default"](viteConfig, "viteconfig required to generate the server entry");
421
+ let routes = ctx.serverBundleBuildConfig ?
422
+ // For server bundle builds, the server build should only import the
423
+ // routes for this bundle rather than importing all routes
424
+ ctx.serverBundleBuildConfig.routes :
425
+ // Otherwise, all routes are imported as usual
426
+ ctx.reactRouterConfig.routes;
427
+ return `
428
+ import * as entryServer from ${JSON.stringify(resolveFileUrl.resolveFileUrl(ctx, ctx.entryServerFilePath))};
429
+ ${Object.keys(routes).map((key, index) => {
430
+ let route = routes[key];
431
+ return `import * as route${index} from ${JSON.stringify(resolveFileUrl.resolveFileUrl(ctx, resolveRelativeRouteFilePath(route, ctx.reactRouterConfig)))};`;
432
+ }).join("\n")}
433
+ export { default as assets } from ${JSON.stringify(serverManifestId)};
434
+ export const assetsBuildDirectory = ${JSON.stringify(path__namespace.relative(ctx.rootDirectory, getClientBuildDirectory(ctx.reactRouterConfig)))};
435
+ export const basename = ${JSON.stringify(ctx.reactRouterConfig.basename)};
436
+ export const future = ${JSON.stringify(ctx.reactRouterConfig.future)};
437
+ export const isSpaMode = ${!ctx.reactRouterConfig.ssr};
438
+ export const publicPath = ${JSON.stringify(ctx.reactRouterConfig.publicPath)};
439
+ export const entry = { module: entryServer };
440
+ export const routes = {
441
+ ${Object.keys(routes).map((key, index) => {
442
+ let route = routes[key];
443
+ return `${JSON.stringify(key)}: {
444
+ id: ${JSON.stringify(route.id)},
445
+ parentId: ${JSON.stringify(route.parentId)},
446
+ path: ${JSON.stringify(route.path)},
447
+ index: ${JSON.stringify(route.index)},
448
+ caseSensitive: ${JSON.stringify(route.caseSensitive)},
449
+ module: route${index}
450
+ }`;
451
+ }).join(",\n ")}
452
+ };`;
453
+ };
454
+ let loadViteManifest = async directory => {
455
+ let manifestContents = await fse__namespace.readFile(path__namespace.resolve(directory, ".vite", "manifest.json"), "utf-8");
456
+ return JSON.parse(manifestContents);
457
+ };
458
+ let getViteManifestAssetPaths = viteManifest => {
459
+ // Get .css?url imports and CSS entry points
460
+ let cssUrlPaths = Object.values(viteManifest).filter(chunk => chunk.file.endsWith(".css")).map(chunk => chunk.file);
461
+ // Get bundled CSS files and generic asset types
462
+ let chunkAssetPaths = Object.values(viteManifest).flatMap(chunk => chunk.assets ?? []);
463
+ return new Set([...cssUrlPaths, ...chunkAssetPaths]);
464
+ };
465
+ let generateReactRouterManifestsForBuild = async () => {
466
+ invariant["default"](viteConfig);
467
+ let viteManifest = await loadViteManifest(getClientBuildDirectory(ctx.reactRouterConfig));
468
+ let entry = getReactRouterManifestBuildAssets(ctx, viteManifest, ctx.entryClientFilePath);
469
+ let browserRoutes = {};
470
+ let serverRoutes = {};
471
+ let routeManifestExports = await getRouteManifestModuleExports(viteChildCompiler, ctx);
472
+ for (let [key, route] of Object.entries(ctx.reactRouterConfig.routes)) {
473
+ var _ctx$serverBundleBuil;
474
+ let routeFilePath = path__namespace.join(ctx.reactRouterConfig.appDirectory, route.file);
475
+ let sourceExports = routeManifestExports[key];
476
+ let isRootRoute = route.parentId === undefined;
477
+ let routeManifestEntry = {
478
+ id: route.id,
479
+ parentId: route.parentId,
480
+ path: route.path,
481
+ index: route.index,
482
+ caseSensitive: route.caseSensitive,
483
+ hasAction: sourceExports.includes("action"),
484
+ hasLoader: sourceExports.includes("loader"),
485
+ hasClientAction: sourceExports.includes("clientAction"),
486
+ hasClientLoader: sourceExports.includes("clientLoader"),
487
+ hasErrorBoundary: sourceExports.includes("ErrorBoundary"),
488
+ ...getReactRouterManifestBuildAssets(ctx, viteManifest, routeFilePath,
489
+ // If this is the root route, we also need to include assets from the
490
+ // client entry file as this is a common way for consumers to import
491
+ // global reset styles, etc.
492
+ isRootRoute ? [ctx.entryClientFilePath] : [])
493
+ };
494
+ browserRoutes[key] = routeManifestEntry;
495
+ let serverBundleRoutes = (_ctx$serverBundleBuil = ctx.serverBundleBuildConfig) === null || _ctx$serverBundleBuil === void 0 ? void 0 : _ctx$serverBundleBuil.routes;
496
+ if (!serverBundleRoutes || serverBundleRoutes[key]) {
497
+ serverRoutes[key] = routeManifestEntry;
498
+ }
499
+ }
500
+ let fingerprintedValues = {
501
+ entry,
502
+ routes: browserRoutes
503
+ };
504
+ let version = getHash(JSON.stringify(fingerprintedValues), 8);
505
+ let manifestPath = path__namespace.posix.join(viteConfig.build.assetsDir, `manifest-${version}.js`);
506
+ let url = `${ctx.reactRouterConfig.publicPath}${manifestPath}`;
507
+ let nonFingerprintedValues = {
508
+ url,
509
+ version
510
+ };
511
+ let reactRouterBrowserManifest = {
512
+ ...fingerprintedValues,
513
+ ...nonFingerprintedValues
514
+ };
515
+ // Write the browser manifest to disk as part of the build process
516
+ await writeFileSafe(path__namespace.join(getClientBuildDirectory(ctx.reactRouterConfig), manifestPath), `window.__remixManifest=${JSON.stringify(reactRouterBrowserManifest)};`);
517
+ // The server manifest is the same as the browser manifest, except for
518
+ // server bundle builds which only includes routes for the current bundle,
519
+ // otherwise the server and client have the same routes
520
+ let reactRouterServerManifest = {
521
+ ...reactRouterBrowserManifest,
522
+ routes: serverRoutes
523
+ };
524
+ return {
525
+ reactRouterBrowserManifest,
526
+ reactRouterServerManifest
527
+ };
528
+ };
529
+ // In dev, the server and browser manifests are the same
530
+ let getReactRouterManifestForDev = async () => {
531
+ let routes = {};
532
+ let routeManifestExports = await getRouteManifestModuleExports(viteChildCompiler, ctx);
533
+ for (let [key, route] of Object.entries(ctx.reactRouterConfig.routes)) {
534
+ let sourceExports = routeManifestExports[key];
535
+ routes[key] = {
536
+ id: route.id,
537
+ parentId: route.parentId,
538
+ path: route.path,
539
+ index: route.index,
540
+ caseSensitive: route.caseSensitive,
541
+ module: path__namespace.posix.join(ctx.reactRouterConfig.publicPath, `${resolveFileUrl.resolveFileUrl(ctx, resolveRelativeRouteFilePath(route, ctx.reactRouterConfig))}${CLIENT_ROUTE_QUERY_STRING}`),
542
+ hasAction: sourceExports.includes("action"),
543
+ hasLoader: sourceExports.includes("loader"),
544
+ hasClientAction: sourceExports.includes("clientAction"),
545
+ hasClientLoader: sourceExports.includes("clientLoader"),
546
+ hasErrorBoundary: sourceExports.includes("ErrorBoundary"),
547
+ imports: []
548
+ };
549
+ }
550
+ return {
551
+ version: String(Math.random()),
552
+ url: path__namespace.posix.join(ctx.reactRouterConfig.publicPath, vmod.url(browserManifestId)),
553
+ hmr: {
554
+ runtime: path__namespace.posix.join(ctx.reactRouterConfig.publicPath, vmod.url(injectHmrRuntimeId))
555
+ },
556
+ entry: {
557
+ module: path__namespace.posix.join(ctx.reactRouterConfig.publicPath, resolveFileUrl.resolveFileUrl(ctx, ctx.entryClientFilePath)),
558
+ imports: []
559
+ },
560
+ routes
561
+ };
562
+ };
563
+ return [{
564
+ name: "react-router",
565
+ config: async (_viteUserConfig, _viteConfigEnv) => {
566
+ var _viteUserConfig$serve2, _viteUserConfig$serve3, _viteUserConfig$build4;
567
+ // Preload Vite's ESM build up-front as soon as we're in an async context
568
+ await importViteEsmSync.preloadViteEsm();
569
+ // Ensure sync import of Vite works after async preload
570
+ let vite = importViteEsmSync.importViteEsmSync();
571
+ viteUserConfig = _viteUserConfig;
572
+ viteConfigEnv = _viteConfigEnv;
573
+ viteCommand = viteConfigEnv.command;
574
+ await updatePluginContext();
575
+ Object.assign(process.env, vite.loadEnv(viteConfigEnv.mode, ctx.rootDirectory,
576
+ // We override default prefix of "VITE_" with a blank string since
577
+ // we're targeting the server, so we want to load all environment
578
+ // variables, not just those explicitly marked for the client
579
+ ""));
580
+ let baseRollupOptions = {
581
+ // Silence Rollup "use client" warnings
582
+ // Adapted from https://github.com/vitejs/vite-plugin-react/pull/144
583
+ onwarn(warning, defaultHandler) {
584
+ var _viteUserConfig$build2, _viteUserConfig$build3;
585
+ if (warning.code === "MODULE_LEVEL_DIRECTIVE" && warning.message.includes("use client")) {
586
+ return;
587
+ }
588
+ if ((_viteUserConfig$build2 = viteUserConfig.build) !== null && _viteUserConfig$build2 !== void 0 && (_viteUserConfig$build3 = _viteUserConfig$build2.rollupOptions) !== null && _viteUserConfig$build3 !== void 0 && _viteUserConfig$build3.onwarn) {
589
+ viteUserConfig.build.rollupOptions.onwarn(warning, defaultHandler);
590
+ } else {
591
+ defaultHandler(warning);
592
+ }
593
+ }
594
+ };
595
+ return {
596
+ __reactRouterPluginContext: ctx,
597
+ appType: viteCommand === "serve" && viteConfigEnv.mode === "production" && ctx.reactRouterConfig.ssr === false ? "spa" : "custom",
598
+ ssr: {
599
+ external: [
600
+ // This is only necessary for development within this repo
601
+ // because these packages are symlinked and Vite treats them as
602
+ // internal source code. For consumers this is a no-op.
603
+ "react-router", "react-router-dom", "@react-router/architect", "@react-router/cloudflare-pages", "@react-router/cloudflare-workers", "@react-router/cloudflare", "@react-router/deno", "@react-router/dev", "@react-router/express", "@react-router/netlify", "@react-router/node", "@react-router/serve", "@react-router/server-runtime"]
604
+ },
605
+ optimizeDeps: {
606
+ include: [
607
+ // Pre-bundle React dependencies to avoid React duplicates,
608
+ // even if React dependencies are not direct dependencies.
609
+ // https://react.dev/warnings/invalid-hook-call-warning#duplicate-react
610
+ "react", "react/jsx-runtime", "react/jsx-dev-runtime", "react-dom/client",
611
+ // Pre-bundle router dependencies to avoid router duplicates.
612
+ // Mismatching routers cause `Error: You must render this element inside a <Remix> element`.
613
+ "react-router", "react-router-dom",
614
+ // For some reason, the `vite-dotenv` integration test consistently fails on webkit
615
+ // with `504 (Outdated Optimize Dep)` from Vite unless `@react-router/node` is included
616
+ // in `optimizeDeps.include`. 🤷
617
+ // This could be caused by how we copy `node_modules/` into integration test fixtures,
618
+ // so maybe this will be unnecessary once we switch to pnpm
619
+ "@react-router/node"]
620
+ },
621
+ esbuild: {
622
+ jsx: "automatic",
623
+ jsxDev: viteCommand !== "build"
624
+ },
625
+ resolve: {
626
+ dedupe: [
627
+ // https://react.dev/warnings/invalid-hook-call-warning#duplicate-react
628
+ "react", "react-dom",
629
+ // see description for `optimizeDeps.include`
630
+ "react-router", "react-router-dom"]
631
+ },
632
+ base: viteUserConfig.base,
633
+ // When consumer provides an allow list for files that can be read by
634
+ // the server, ensure that the default entry files are included.
635
+ // If we don't do this and a default entry file is used, the server
636
+ // will throw an error that the file is not allowed to be read.
637
+ // https://vitejs.dev/config/server-options#server-fs-allow
638
+ server: (_viteUserConfig$serve2 = viteUserConfig.server) !== null && _viteUserConfig$serve2 !== void 0 && (_viteUserConfig$serve3 = _viteUserConfig$serve2.fs) !== null && _viteUserConfig$serve3 !== void 0 && _viteUserConfig$serve3.allow ? {
639
+ fs: {
640
+ allow: defaultEntries
641
+ }
642
+ } : undefined,
643
+ // Vite config options for building
644
+ ...(viteCommand === "build" ? {
645
+ build: {
646
+ cssMinify: ((_viteUserConfig$build4 = viteUserConfig.build) === null || _viteUserConfig$build4 === void 0 ? void 0 : _viteUserConfig$build4.cssMinify) ?? true,
647
+ ...(!viteConfigEnv.isSsrBuild ? {
648
+ manifest: true,
649
+ outDir: getClientBuildDirectory(ctx.reactRouterConfig),
650
+ rollupOptions: {
651
+ ...baseRollupOptions,
652
+ preserveEntrySignatures: "exports-only",
653
+ input: [ctx.entryClientFilePath, ...Object.values(ctx.reactRouterConfig.routes).map(route => `${path__namespace.resolve(ctx.reactRouterConfig.appDirectory, route.file)}${CLIENT_ROUTE_QUERY_STRING}`)]
654
+ }
655
+ } : {
656
+ // We move SSR-only assets to client assets. Note that the
657
+ // SSR build can also emit code-split JS files (e.g. by
658
+ // dynamic import) under the same assets directory
659
+ // regardless of "ssrEmitAssets" option, so we also need to
660
+ // keep these JS files have to be kept as-is.
661
+ ssrEmitAssets: true,
662
+ copyPublicDir: false,
663
+ // Assets in the public directory are only used by the client
664
+ manifest: true,
665
+ // We need the manifest to detect SSR-only assets
666
+ outDir: getServerBuildDirectory(ctx),
667
+ rollupOptions: {
668
+ ...baseRollupOptions,
669
+ preserveEntrySignatures: "exports-only",
670
+ input: serverBuildId,
671
+ output: {
672
+ entryFileNames: ctx.reactRouterConfig.serverBuildFile,
673
+ format: ctx.reactRouterConfig.serverModuleFormat
674
+ }
675
+ }
676
+ })
677
+ }
678
+ } : undefined),
679
+ // Vite config options for SPA preview mode
680
+ ...(viteCommand === "serve" && ctx.reactRouterConfig.ssr === false ? {
681
+ build: {
682
+ manifest: true,
683
+ outDir: getClientBuildDirectory(ctx.reactRouterConfig)
684
+ }
685
+ } : undefined)
686
+ };
687
+ },
688
+ async configResolved(resolvedViteConfig) {
689
+ await esModuleLexer.init;
690
+ viteConfig = resolvedViteConfig;
691
+ invariant["default"](viteConfig);
692
+ // We load the same Vite config file again for the child compiler so
693
+ // that both parent and child compiler's plugins have independent state.
694
+ // If we re-used the `viteUserConfig.plugins` array for the child
695
+ // compiler, it could lead to mutating shared state between plugin
696
+ // instances in unexpected ways, e.g. during `vite build` the
697
+ // `configResolved` plugin hook would be called with `command = "build"`
698
+ // by parent and then `command = "serve"` by child, which some plugins
699
+ // may respond to by updating state referenced by the parent.
700
+ if (!viteConfig.configFile) {
701
+ throw new Error("The React Router Vite plugin requires the use of a Vite config file");
702
+ }
703
+ let vite = importViteEsmSync.importViteEsmSync();
704
+ let childCompilerConfigFile = await vite.loadConfigFromFile({
705
+ command: viteConfig.command,
706
+ mode: viteConfig.mode,
707
+ isSsrBuild: ctx.isSsrBuild
708
+ }, viteConfig.configFile);
709
+ invariant["default"](childCompilerConfigFile, "Vite config file was unable to be resolved for React Router child compiler");
710
+ // Validate that commonly used Rollup plugins that need to run before
711
+ // ours are in the correct order. This is because Rollup plugins can't
712
+ // set `enforce: "pre"` like Vite plugins can. Explicitly validating
713
+ // this provides a much nicer developer experience.
714
+ let rollupPrePlugins = [{
715
+ pluginName: "@mdx-js/rollup",
716
+ displayName: "@mdx-js/rollup"
717
+ }];
718
+ for (let prePlugin of rollupPrePlugins) {
719
+ let prePluginIndex = pluginIndex(prePlugin.pluginName);
720
+ if (prePluginIndex >= 0 && prePluginIndex > pluginIndex("react-router")) {
721
+ throw new Error(`The "${prePlugin.displayName}" plugin should be placed before the React Router plugin in your Vite config file`);
722
+ }
723
+ }
724
+ viteChildCompiler = await vite.createServer({
725
+ ...viteUserConfig,
726
+ mode: viteConfig.mode,
727
+ server: {
728
+ watch: viteConfig.command === "build" ? null : undefined,
729
+ preTransformRequests: false,
730
+ hmr: false
731
+ },
732
+ configFile: false,
733
+ envFile: false,
734
+ plugins: [...(childCompilerConfigFile.config.plugins ?? []).flat()
735
+ // Exclude this plugin from the child compiler to prevent an
736
+ // infinite loop (plugin creates a child compiler with the same
737
+ // plugin that creates another child compiler, repeat ad
738
+ // infinitum), and to prevent the manifest from being written to
739
+ // disk from the child compiler. This is important in the
740
+ // production build because the child compiler is a Vite dev
741
+ // server and will generate incorrect manifests.
742
+ .filter(plugin => typeof plugin === "object" && plugin !== null && "name" in plugin && plugin.name !== "react-router" && plugin.name !== "react-router-hmr-updates")]
743
+ });
744
+ await viteChildCompiler.pluginContainer.buildStart({});
745
+ },
746
+ async transform(code, id) {
747
+ if (styles.isCssModulesFile(id)) {
748
+ cssModulesManifest[id] = code;
749
+ }
750
+ if (isClientRoute(id)) {
751
+ let routeModuleId = id.replace(CLIENT_ROUTE_QUERY_STRING, "");
752
+ let sourceExports = await getRouteModuleExports(viteChildCompiler, ctx, routeModuleId);
753
+ let routeFileName = path__namespace.basename(routeModuleId);
754
+ let clientExports = sourceExports.filter(exportName => CLIENT_ROUTE_EXPORTS.includes(exportName)).join(", ");
755
+ return `export { ${clientExports} } from "./${routeFileName}";`;
756
+ }
757
+ },
758
+ buildStart() {
759
+ invariant["default"](viteConfig);
760
+ if (viteCommand === "build" && viteConfig.mode === "production" && !viteConfig.build.ssr && viteConfig.build.sourcemap) {
761
+ viteConfig.logger.warn(colors__default["default"].yellow("\n" + colors__default["default"].bold(" ⚠️ Source maps are enabled in production\n") + ["This makes your server code publicly", "visible in the browser. This is highly", "discouraged! If you insist, ensure that", "you are using environment variables for", "secrets and not hard-coding them in", "your source code."].map(line => " " + line).join("\n") + "\n"));
762
+ }
763
+ },
764
+ async configureServer(viteDevServer) {
765
+ serverRuntime.unstable_setDevServerHooks({
766
+ // Give the request handler access to the critical CSS in dev to avoid a
767
+ // flash of unstyled content since Vite injects CSS file contents via JS
768
+ getCriticalCss: async (build, url) => {
769
+ return styles.getStylesForUrl({
770
+ rootDirectory: ctx.rootDirectory,
771
+ entryClientFilePath: ctx.entryClientFilePath,
772
+ reactRouterConfig: ctx.reactRouterConfig,
773
+ viteDevServer,
774
+ cssModulesManifest,
775
+ build,
776
+ url
777
+ });
778
+ },
779
+ // If an error is caught within the request handler, let Vite fix the
780
+ // stack trace so it maps back to the actual source code
781
+ processRequestError: error => {
782
+ if (error instanceof Error) {
783
+ viteDevServer.ssrFixStacktrace(error);
784
+ }
785
+ }
786
+ });
787
+ // Invalidate virtual modules and update cached plugin config via file watcher
788
+ viteDevServer.watcher.on("all", async (eventName, filepath) => {
789
+ var _viteConfig;
790
+ let {
791
+ normalizePath
792
+ } = importViteEsmSync.importViteEsmSync();
793
+ let appFileAddedOrRemoved = (eventName === "add" || eventName === "unlink") && normalizePath(filepath).startsWith(normalizePath(ctx.reactRouterConfig.appDirectory));
794
+ invariant["default"]((_viteConfig = viteConfig) === null || _viteConfig === void 0 ? void 0 : _viteConfig.configFile);
795
+ let viteConfigChanged = eventName === "change" && normalizePath(filepath) === normalizePath(viteConfig.configFile);
796
+ if (appFileAddedOrRemoved || viteConfigChanged) {
797
+ let lastReactRouterConfig = ctx.reactRouterConfig;
798
+ await updatePluginContext();
799
+ if (!isEqualJson(lastReactRouterConfig, ctx.reactRouterConfig)) {
800
+ invalidateVirtualModules(viteDevServer);
801
+ }
802
+ }
803
+ });
804
+ return () => {
805
+ // Let user servers handle SSR requests in middleware mode,
806
+ // otherwise the Vite plugin will handle the request
807
+ if (!viteDevServer.config.server.middlewareMode) {
808
+ viteDevServer.middlewares.use(async (req, res, next) => {
809
+ try {
810
+ let build = await viteDevServer.ssrLoadModule(serverBuildId);
811
+ let handler = serverRuntime.createRequestHandler(build, "development");
812
+ let nodeHandler = async (nodeReq, nodeRes) => {
813
+ let req = nodeAdapter.fromNodeRequest(nodeReq);
814
+ let res = await handler(req, await reactRouterDevLoadContext(req));
815
+ await nodeAdapter.toNodeRequest(res, nodeRes);
816
+ };
817
+ await nodeHandler(req, res);
818
+ } catch (error) {
819
+ next(error);
820
+ }
821
+ });
822
+ }
823
+ };
824
+ },
825
+ writeBundle: {
826
+ // After the SSR build is finished, we inspect the Vite manifest for
827
+ // the SSR build and move server-only assets to client assets directory
828
+ async handler() {
829
+ if (!ctx.isSsrBuild) {
830
+ return;
831
+ }
832
+ invariant["default"](viteConfig);
833
+ let clientBuildDirectory = getClientBuildDirectory(ctx.reactRouterConfig);
834
+ let serverBuildDirectory = getServerBuildDirectory(ctx);
835
+ let ssrViteManifest = await loadViteManifest(serverBuildDirectory);
836
+ let clientViteManifest = await loadViteManifest(clientBuildDirectory);
837
+ let clientAssetPaths = getViteManifestAssetPaths(clientViteManifest);
838
+ let ssrAssetPaths = getViteManifestAssetPaths(ssrViteManifest);
839
+ // We only move assets that aren't in the client build, otherwise we
840
+ // remove them. These assets only exist because we explicitly set
841
+ // `ssrEmitAssets: true` in the SSR Vite config. These assets
842
+ // typically wouldn't exist by default, which is why we assume it's
843
+ // safe to remove them. We're aiming for a clean build output so that
844
+ // unnecessary assets don't get deployed alongside the server code.
845
+ let movedAssetPaths = [];
846
+ for (let ssrAssetPath of ssrAssetPaths) {
847
+ let src = path__namespace.join(serverBuildDirectory, ssrAssetPath);
848
+ if (!clientAssetPaths.has(ssrAssetPath)) {
849
+ let dest = path__namespace.join(clientBuildDirectory, ssrAssetPath);
850
+ await fse__namespace.move(src, dest);
851
+ movedAssetPaths.push(dest);
852
+ } else {
853
+ await fse__namespace.remove(src);
854
+ }
855
+ }
856
+ // We assume CSS assets from the SSR build are unnecessary and remove
857
+ // them for the same reasons as above.
858
+ let ssrCssPaths = Object.values(ssrViteManifest).flatMap(chunk => chunk.css ?? []);
859
+ await Promise.all(ssrCssPaths.map(cssPath => fse__namespace.remove(path__namespace.join(serverBuildDirectory, cssPath))));
860
+ if (movedAssetPaths.length) {
861
+ viteConfig.logger.info(["", `${colors__default["default"].green("✓")} ${movedAssetPaths.length} asset${movedAssetPaths.length > 1 ? "s" : ""} moved from React Router server build to client assets.`, ...movedAssetPaths.map(movedAssetPath => colors__default["default"].dim(path__namespace.relative(ctx.rootDirectory, movedAssetPath))), ""].join("\n"));
862
+ }
863
+ if (!ctx.reactRouterConfig.ssr) {
864
+ await handleSpaMode(serverBuildDirectory, ctx.reactRouterConfig.serverBuildFile, clientBuildDirectory, viteConfig, ctx.reactRouterConfig.basename);
865
+ }
866
+ }
867
+ },
868
+ async buildEnd() {
869
+ var _viteChildCompiler;
870
+ await ((_viteChildCompiler = viteChildCompiler) === null || _viteChildCompiler === void 0 ? void 0 : _viteChildCompiler.close());
871
+ }
872
+ }, {
873
+ name: "react-router-virtual-modules",
874
+ enforce: "pre",
875
+ resolveId(id) {
876
+ if (vmods.includes(id)) return vmod.resolve(id);
877
+ },
878
+ async load(id) {
879
+ switch (id) {
880
+ case vmod.resolve(serverBuildId):
881
+ {
882
+ return await getServerEntry();
883
+ }
884
+ case vmod.resolve(serverManifestId):
885
+ {
886
+ let reactRouterManifest = ctx.isSsrBuild ? await ctx.getReactRouterServerManifest() : await getReactRouterManifestForDev();
887
+ return `export default ${jsesc__default["default"](reactRouterManifest, {
888
+ es6: true
889
+ })};`;
890
+ }
891
+ case vmod.resolve(browserManifestId):
892
+ {
893
+ if (viteCommand === "build") {
894
+ throw new Error("This module only exists in development");
895
+ }
896
+ let reactRouterManifest = await getReactRouterManifestForDev();
897
+ let reactRouterManifestString = jsesc__default["default"](reactRouterManifest, {
898
+ es6: true
899
+ });
900
+ return `window.__remixManifest=${reactRouterManifestString};`;
901
+ }
902
+ }
903
+ }
904
+ }, {
905
+ name: "react-router-dot-server",
906
+ enforce: "pre",
907
+ async resolveId(id, importer, options) {
908
+ var _options$custom;
909
+ if (options !== null && options !== void 0 && options.ssr) return;
910
+ let isResolving = (options === null || options === void 0 ? void 0 : (_options$custom = options.custom) === null || _options$custom === void 0 ? void 0 : _options$custom["react-router-dot-server"]) ?? false;
911
+ if (isResolving) return;
912
+ options.custom = {
913
+ ...options.custom,
914
+ "react-router-dot-server": true
915
+ };
916
+ let resolved = await this.resolve(id, importer, options);
917
+ if (!resolved) return;
918
+ let serverFileRE = /\.server(\.[cm]?[jt]sx?)?$/;
919
+ let serverDirRE = /\/\.server\//;
920
+ let isDotServer = serverFileRE.test(resolved.id) || serverDirRE.test(resolved.id);
921
+ if (!isDotServer) return;
922
+ if (!importer) return;
923
+ if (viteCommand !== "build" && importer.endsWith(".html")) {
924
+ // Vite has a special `index.html` importer for `resolveId` within `transformRequest`
925
+ // https://github.com/vitejs/vite/blob/5684fcd8d27110d098b3e1c19d851f44251588f1/packages/vite/src/node/server/transformRequest.ts#L158
926
+ // https://github.com/vitejs/vite/blob/5684fcd8d27110d098b3e1c19d851f44251588f1/packages/vite/src/node/server/pluginContainer.ts#L668
927
+ return;
928
+ }
929
+ let vite = importViteEsmSync.importViteEsmSync();
930
+ let importerShort = vite.normalizePath(path__namespace.relative(ctx.rootDirectory, importer));
931
+ let isRoute = getRoute(ctx.reactRouterConfig, importer);
932
+ if (isRoute) {
933
+ let serverOnlyExports = SERVER_ONLY_ROUTE_EXPORTS.map(xport => `\`${xport}\``).join(", ");
934
+ throw Error([colors__default["default"].red(`Server-only module referenced by client`), "", ` '${id}' imported by route '${importerShort}'`, "", ` React Router automatically removes server-code from these exports:`, ` ${serverOnlyExports}`, "", ` But other route exports in '${importerShort}' depend on '${id}'.`, "", " See https://remix.run/docs/en/main/guides/vite#splitting-up-client-and-server-code", ""].join("\n"));
935
+ }
936
+ throw Error([colors__default["default"].red(`Server-only module referenced by client`), "", ` '${id}' imported by '${importerShort}'`, "", " See https://remix.run/docs/en/main/guides/vite#splitting-up-client-and-server-code", ""].join("\n"));
937
+ }
938
+ }, {
939
+ name: "react-router-dot-client",
940
+ async transform(code, id, options) {
941
+ if (!(options !== null && options !== void 0 && options.ssr)) return;
942
+ let clientFileRE = /\.client(\.[cm]?[jt]sx?)?$/;
943
+ let clientDirRE = /\/\.client\//;
944
+ if (clientFileRE.test(id) || clientDirRE.test(id)) {
945
+ let exports = esModuleLexer.parse(code)[1];
946
+ return {
947
+ code: exports.map(({
948
+ n: name
949
+ }) => name === "default" ? "export default undefined;" : `export const ${name} = undefined;`).join("\n"),
950
+ map: null
951
+ };
952
+ }
953
+ }
954
+ }, {
955
+ name: "react-router-route-exports",
956
+ async transform(code, id, options) {
957
+ if (options !== null && options !== void 0 && options.ssr) return;
958
+ let route = getRoute(ctx.reactRouterConfig, id);
959
+ if (!route) return;
960
+ if (!ctx.reactRouterConfig.ssr) {
961
+ let serverOnlyExports = esModuleLexer.parse(code)[1].map(exp => exp.n).filter(exp => SERVER_ONLY_ROUTE_EXPORTS.includes(exp));
962
+ if (serverOnlyExports.length > 0) {
963
+ let str = serverOnlyExports.map(e => `\`${e}\``).join(", ");
964
+ let message = `SPA Mode: ${serverOnlyExports.length} invalid route export(s) in ` + `\`${route.file}\`: ${str}. See https://remix.run/guides/spa-mode ` + `for more information.`;
965
+ throw Error(message);
966
+ }
967
+ if (route.id !== "root") {
968
+ let hasHydrateFallback = esModuleLexer.parse(code)[1].map(exp => exp.n).some(exp => exp === "HydrateFallback");
969
+ if (hasHydrateFallback) {
970
+ let message = `SPA Mode: Invalid \`HydrateFallback\` export found in ` + `\`${route.file}\`. \`HydrateFallback\` is only permitted on ` + `the root route in SPA Mode. See https://remix.run/guides/spa-mode ` + `for more information.`;
971
+ throw Error(message);
972
+ }
973
+ }
974
+ }
975
+ let [filepath] = id.split("?");
976
+ return removeExports.removeExports(code, SERVER_ONLY_ROUTE_EXPORTS, {
977
+ sourceMaps: true,
978
+ filename: id,
979
+ sourceFileName: filepath
980
+ });
981
+ }
982
+ }, {
983
+ name: "react-router-inject-hmr-runtime",
984
+ enforce: "pre",
985
+ resolveId(id) {
986
+ if (id === injectHmrRuntimeId) return vmod.resolve(injectHmrRuntimeId);
987
+ },
988
+ async load(id) {
989
+ if (id !== vmod.resolve(injectHmrRuntimeId)) return;
990
+ return [`import RefreshRuntime from "${hmrRuntimeId}"`, "RefreshRuntime.injectIntoGlobalHook(window)", "window.$RefreshReg$ = () => {}", "window.$RefreshSig$ = () => (type) => type", "window.__vite_plugin_react_preamble_installed__ = true"].join("\n");
991
+ }
992
+ }, {
993
+ name: "react-router-hmr-runtime",
994
+ enforce: "pre",
995
+ resolveId(id) {
996
+ if (id === hmrRuntimeId) return vmod.resolve(hmrRuntimeId);
997
+ },
998
+ async load(id) {
999
+ if (id !== vmod.resolve(hmrRuntimeId)) return;
1000
+ let reactRefreshDir = path__namespace.dirname(require.resolve("react-refresh/package.json"));
1001
+ let reactRefreshRuntimePath = path__namespace.join(reactRefreshDir, "cjs/react-refresh-runtime.development.js");
1002
+ return ["const exports = {}", await fse__namespace.readFile(reactRefreshRuntimePath, "utf8"), await fse__namespace.readFile(require.resolve("./static/refresh-utils.cjs"), "utf8"), "export default exports"].join("\n");
1003
+ }
1004
+ }, {
1005
+ name: "react-router-react-refresh-babel",
1006
+ async transform(code, id, options) {
1007
+ if (viteCommand !== "serve") return;
1008
+ if (id.includes("/node_modules/")) return;
1009
+ let [filepath] = id.split("?");
1010
+ let extensionsRE = /\.(jsx?|tsx?|mdx?)$/;
1011
+ if (!extensionsRE.test(filepath)) return;
1012
+ let devRuntime = "react/jsx-dev-runtime";
1013
+ let ssr = (options === null || options === void 0 ? void 0 : options.ssr) === true;
1014
+ let isJSX = filepath.endsWith("x");
1015
+ let useFastRefresh = !ssr && (isJSX || code.includes(devRuntime));
1016
+ if (!useFastRefresh) return;
1017
+ if (isClientRoute(id)) {
1018
+ return {
1019
+ code: addRefreshWrapper(ctx.reactRouterConfig, code, id)
1020
+ };
1021
+ }
1022
+ let result = await babel__default["default"].transformAsync(code, {
1023
+ babelrc: false,
1024
+ configFile: false,
1025
+ filename: id,
1026
+ sourceFileName: filepath,
1027
+ parserOpts: {
1028
+ sourceType: "module",
1029
+ allowAwaitOutsideFunction: true
1030
+ },
1031
+ plugins: [[require("react-refresh/babel"), {
1032
+ skipEnvCheck: true
1033
+ }]],
1034
+ sourceMaps: true
1035
+ });
1036
+ if (result === null) return;
1037
+ code = result.code;
1038
+ let refreshContentRE = /\$Refresh(?:Reg|Sig)\$\(/;
1039
+ if (refreshContentRE.test(code)) {
1040
+ code = addRefreshWrapper(ctx.reactRouterConfig, code, id);
1041
+ }
1042
+ return {
1043
+ code,
1044
+ map: result.map
1045
+ };
1046
+ }
1047
+ }, {
1048
+ name: "react-router-hmr-updates",
1049
+ async handleHotUpdate({
1050
+ server,
1051
+ file,
1052
+ modules,
1053
+ read
1054
+ }) {
1055
+ let route = getRoute(ctx.reactRouterConfig, file);
1056
+ let hmrEventData = {
1057
+ route: null
1058
+ };
1059
+ if (route) {
1060
+ // invalidate manifest on route exports change
1061
+ let serverManifest = (await server.ssrLoadModule(serverManifestId)).default;
1062
+ let oldRouteMetadata = serverManifest.routes[route.id];
1063
+ let newRouteMetadata = await getRouteMetadata(ctx, viteChildCompiler, route, read);
1064
+ hmrEventData.route = newRouteMetadata;
1065
+ if (!oldRouteMetadata || ["hasLoader", "hasClientLoader", "hasAction", "hasClientAction", "hasErrorBoundary"].some(key => oldRouteMetadata[key] !== newRouteMetadata[key])) {
1066
+ invalidateVirtualModules(server);
1067
+ }
1068
+ }
1069
+ server.ws.send({
1070
+ type: "custom",
1071
+ event: "react-router:hmr",
1072
+ data: hmrEventData
1073
+ });
1074
+ return modules;
1075
+ }
1076
+ }];
1077
+ };
1078
+ function isEqualJson(v1, v2) {
1079
+ return JSON.stringify(v1) === JSON.stringify(v2);
1080
+ }
1081
+ function addRefreshWrapper(reactRouterConfig, code, id) {
1082
+ let route = getRoute(reactRouterConfig, id);
1083
+ let acceptExports = route || isClientRoute(id) ? ["clientAction", "clientLoader", "handle", "meta", "links", "shouldRevalidate"] : [];
1084
+ return REACT_REFRESH_HEADER.replaceAll("__SOURCE__", JSON.stringify(id)) + code + REACT_REFRESH_FOOTER.replaceAll("__SOURCE__", JSON.stringify(id)).replaceAll("__ACCEPT_EXPORTS__", JSON.stringify(acceptExports)).replaceAll("__ROUTE_ID__", JSON.stringify(route === null || route === void 0 ? void 0 : route.id));
1085
+ }
1086
+ const REACT_REFRESH_HEADER = `
1087
+ import RefreshRuntime from "${hmrRuntimeId}";
1088
+
1089
+ const inWebWorker = typeof WorkerGlobalScope !== 'undefined' && self instanceof WorkerGlobalScope;
1090
+ let prevRefreshReg;
1091
+ let prevRefreshSig;
1092
+
1093
+ if (import.meta.hot && !inWebWorker) {
1094
+ if (!window.__vite_plugin_react_preamble_installed__) {
1095
+ throw new Error(
1096
+ "React Router Vite plugin can't detect preamble. Something is wrong."
1097
+ );
1098
+ }
1099
+
1100
+ prevRefreshReg = window.$RefreshReg$;
1101
+ prevRefreshSig = window.$RefreshSig$;
1102
+ window.$RefreshReg$ = (type, id) => {
1103
+ RefreshRuntime.register(type, __SOURCE__ + " " + id)
1104
+ };
1105
+ window.$RefreshSig$ = RefreshRuntime.createSignatureFunctionForTransform;
1106
+ }`.replace(/\n+/g, "");
1107
+ const REACT_REFRESH_FOOTER = `
1108
+ if (import.meta.hot && !inWebWorker) {
1109
+ window.$RefreshReg$ = prevRefreshReg;
1110
+ window.$RefreshSig$ = prevRefreshSig;
1111
+ RefreshRuntime.__hmr_import(import.meta.url).then((currentExports) => {
1112
+ RefreshRuntime.registerExportsForReactRefresh(__SOURCE__, currentExports);
1113
+ import.meta.hot.accept((nextExports) => {
1114
+ if (!nextExports) return;
1115
+ __ROUTE_ID__ && window.__reactRouterRouteModuleUpdates.set(__ROUTE_ID__, nextExports);
1116
+ const invalidateMessage = RefreshRuntime.validateRefreshBoundaryAndEnqueueUpdate(currentExports, nextExports, __ACCEPT_EXPORTS__);
1117
+ if (invalidateMessage) import.meta.hot.invalidate(invalidateMessage);
1118
+ });
1119
+ });
1120
+ }`;
1121
+ function getRoute(pluginConfig, file) {
1122
+ let vite = importViteEsmSync.importViteEsmSync();
1123
+ let routePath = vite.normalizePath(path__namespace.relative(pluginConfig.appDirectory, file));
1124
+ let route = Object.values(pluginConfig.routes).find(r => vite.normalizePath(r.file) === routePath);
1125
+ return route;
1126
+ }
1127
+ async function getRouteMetadata(ctx, viteChildCompiler, route, readRouteFile) {
1128
+ let sourceExports = await getRouteModuleExports(viteChildCompiler, ctx, route.file, readRouteFile);
1129
+ let info = {
1130
+ id: route.id,
1131
+ parentId: route.parentId,
1132
+ path: route.path,
1133
+ index: route.index,
1134
+ caseSensitive: route.caseSensitive,
1135
+ url: path__namespace.posix.join(ctx.reactRouterConfig.publicPath, "/" + path__namespace.relative(ctx.rootDirectory, resolveRelativeRouteFilePath(route, ctx.reactRouterConfig))),
1136
+ module: path__namespace.posix.join(ctx.reactRouterConfig.publicPath, `${resolveFileUrl.resolveFileUrl(ctx, resolveRelativeRouteFilePath(route, ctx.reactRouterConfig))}?import`),
1137
+ // Ensure the Vite dev server responds with a JS module
1138
+ hasAction: sourceExports.includes("action"),
1139
+ hasClientAction: sourceExports.includes("clientAction"),
1140
+ hasLoader: sourceExports.includes("loader"),
1141
+ hasClientLoader: sourceExports.includes("clientLoader"),
1142
+ hasErrorBoundary: sourceExports.includes("ErrorBoundary"),
1143
+ imports: []
1144
+ };
1145
+ return info;
1146
+ }
1147
+ async function handleSpaMode(serverBuildDirectoryPath, serverBuildFile, clientBuildDirectory, viteConfig, basename) {
1148
+ // Create a handler and call it for the `/` path - rendering down to the
1149
+ // proper HydrateFallback ... or not! Maybe they have a static landing page
1150
+ // generated from routes/_index.tsx.
1151
+ let serverBuildPath = path__namespace.join(serverBuildDirectoryPath, serverBuildFile);
1152
+ let build = await import(url__namespace.pathToFileURL(serverBuildPath).toString());
1153
+ let {
1154
+ createRequestHandler: createHandler
1155
+ } = await import('@react-router/node');
1156
+ let handler = createHandler(build, viteConfig.mode);
1157
+ let response = await handler(new Request(`http://localhost${basename}`));
1158
+ let html = await response.text();
1159
+ if (response.status !== 200) {
1160
+ throw new Error(`SPA Mode: Received a ${response.status} status code from ` + `\`entry.server.tsx\` while generating the \`index.html\` file.\n${html}`);
1161
+ }
1162
+ if (!html.includes("window.__remixContext =") || !html.includes("window.__remixRouteModules =")) {
1163
+ throw new Error("SPA Mode: Did you forget to include <Scripts/> in your `root.tsx` " + "`HydrateFallback` component? Your `index.html` file cannot hydrate " + "into a SPA without `<Scripts />`.");
1164
+ }
1165
+ // Write out the index.html file for the SPA
1166
+ await fse__namespace.writeFile(path__namespace.join(clientBuildDirectory, "index.html"), html);
1167
+ viteConfig.logger.info("SPA Mode: index.html has been written to your " + colors__default["default"].bold(path__namespace.relative(process.cwd(), clientBuildDirectory)) + " directory");
1168
+ // Cleanup - we no longer need the server build assets
1169
+ fse__namespace.removeSync(serverBuildDirectoryPath);
1170
+ }
1171
+
1172
+ exports.configRouteToBranchRoute = configRouteToBranchRoute;
1173
+ exports.extractPluginContext = extractPluginContext;
1174
+ exports.getServerBuildDirectory = getServerBuildDirectory;
1175
+ exports.loadPluginContext = loadPluginContext;
1176
+ exports.reactRouterVitePlugin = reactRouterVitePlugin;
1177
+ exports.resolveViteConfig = resolveViteConfig;
1178
+ exports.setReactRouterDevLoadContext = setReactRouterDevLoadContext;