@react-router/dev 7.1.5 → 7.2.0-pre.1

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.
package/dist/config.d.ts CHANGED
@@ -38,6 +38,14 @@ type ServerBundlesBuildManifest = BaseBuildManifest & {
38
38
  type ServerModuleFormat = "esm" | "cjs";
39
39
  interface FutureConfig {
40
40
  unstable_optimizeDeps: boolean;
41
+ /**
42
+ * Automatically split route modules into multiple chunks when possible.
43
+ */
44
+ unstable_splitRouteModules?: boolean | "enforce";
45
+ /**
46
+ * Use Vite Environment API (experimental)
47
+ */
48
+ unstable_viteEnvironmentApi?: boolean;
41
49
  }
42
50
  type BuildManifest = DefaultBuildManifest | ServerBundlesBuildManifest;
43
51
  type BuildEndHook = (args: {
package/dist/config.js CHANGED
@@ -1,5 +1,5 @@
1
1
  /**
2
- * @react-router/dev v7.1.5
2
+ * @react-router/dev v7.2.0-pre.1
3
3
  *
4
4
  * Copyright (c) Remix Software Inc.
5
5
  *
package/dist/routes.js CHANGED
@@ -1,5 +1,5 @@
1
1
  /**
2
- * @react-router/dev v7.1.5
2
+ * @react-router/dev v7.2.0-pre.1
3
3
  *
4
4
  * Copyright (c) Remix Software Inc.
5
5
  *
@@ -53,7 +53,7 @@ const enqueueUpdate = debounce(async () => {
53
53
  needsRevalidation,
54
54
  manifest.routes,
55
55
  window.__reactRouterRouteModules,
56
- window.__reactRouterContext.future,
56
+ window.__reactRouterContext.ssr,
57
57
  window.__reactRouterContext.isSpaMode
58
58
  );
59
59
  __reactRouterDataRouter._internalSetRoutes(routes);
@@ -1,5 +1,5 @@
1
1
  /**
2
- * @react-router/dev v7.1.5
2
+ * @react-router/dev v7.2.0-pre.1
3
3
  *
4
4
  * Copyright (c) Remix Software Inc.
5
5
  *
@@ -156,6 +156,469 @@ function getVite() {
156
156
  return vite;
157
157
  }
158
158
 
159
+ // config/config.ts
160
+ var import_node_fs = __toESM(require("fs"));
161
+ var import_node_child_process = require("child_process");
162
+ var import_package_json = __toESM(require("@npmcli/package-json"));
163
+
164
+ // vite/vite-node.ts
165
+ var import_server = require("vite-node/server");
166
+ var import_client = require("vite-node/client");
167
+ var import_source_map = require("vite-node/source-map");
168
+ async function createContext(viteConfig = {}) {
169
+ await preloadVite();
170
+ const vite2 = getVite();
171
+ const devServer = await vite2.createServer(
172
+ vite2.mergeConfig(
173
+ {
174
+ server: {
175
+ preTransformRequests: false,
176
+ hmr: false
177
+ },
178
+ optimizeDeps: {
179
+ noDiscovery: true
180
+ },
181
+ configFile: false,
182
+ envFile: false,
183
+ plugins: []
184
+ },
185
+ viteConfig
186
+ )
187
+ );
188
+ await devServer.pluginContainer.buildStart({});
189
+ const server = new import_server.ViteNodeServer(devServer);
190
+ (0, import_source_map.installSourcemapsSupport)({
191
+ getSourceMap: (source) => server.getSourceMap(source)
192
+ });
193
+ const runner = new import_client.ViteNodeRunner({
194
+ root: devServer.config.root,
195
+ base: devServer.config.base,
196
+ fetchModule(id) {
197
+ return server.fetchModule(id);
198
+ },
199
+ resolveId(id, importer) {
200
+ return server.resolveId(id, importer);
201
+ }
202
+ });
203
+ return { devServer, server, runner };
204
+ }
205
+
206
+ // config/config.ts
207
+ var import_pathe3 = __toESM(require("pathe"));
208
+ var import_chokidar = __toESM(require("chokidar"));
209
+ var import_picocolors = __toESM(require("picocolors"));
210
+ var import_pick2 = __toESM(require("lodash/pick"));
211
+ var import_omit = __toESM(require("lodash/omit"));
212
+ var import_cloneDeep = __toESM(require("lodash/cloneDeep"));
213
+ var import_isEqual = __toESM(require("lodash/isEqual"));
214
+
215
+ // config/routes.ts
216
+ var Path = __toESM(require("pathe"));
217
+ var v = __toESM(require("valibot"));
218
+ var import_pick = __toESM(require("lodash/pick"));
219
+ function setAppDirectory(directory) {
220
+ globalThis.__reactRouterAppDirectory = directory;
221
+ }
222
+ var routeConfigEntrySchema = v.pipe(
223
+ v.custom((value) => {
224
+ return !(typeof value === "object" && value !== null && "then" in value && "catch" in value);
225
+ }, "Invalid type: Expected object but received a promise. Did you forget to await?"),
226
+ v.object({
227
+ id: v.optional(v.string()),
228
+ path: v.optional(v.string()),
229
+ index: v.optional(v.boolean()),
230
+ caseSensitive: v.optional(v.boolean()),
231
+ file: v.string(),
232
+ children: v.optional(v.array(v.lazy(() => routeConfigEntrySchema)))
233
+ })
234
+ );
235
+ var resolvedRouteConfigSchema = v.array(routeConfigEntrySchema);
236
+ function validateRouteConfig({
237
+ routeConfigFile,
238
+ routeConfig
239
+ }) {
240
+ if (!routeConfig) {
241
+ return {
242
+ valid: false,
243
+ message: `Route config must be the default export in "${routeConfigFile}".`
244
+ };
245
+ }
246
+ if (!Array.isArray(routeConfig)) {
247
+ return {
248
+ valid: false,
249
+ message: `Route config in "${routeConfigFile}" must be an array.`
250
+ };
251
+ }
252
+ let { issues } = v.safeParse(resolvedRouteConfigSchema, routeConfig);
253
+ if (issues?.length) {
254
+ let { root, nested } = v.flatten(issues);
255
+ return {
256
+ valid: false,
257
+ message: [
258
+ `Route config in "${routeConfigFile}" is invalid.`,
259
+ root ? `${root}` : [],
260
+ nested ? Object.entries(nested).map(
261
+ ([path4, message]) => `Path: routes.${path4}
262
+ ${message}`
263
+ ) : []
264
+ ].flat().join("\n\n")
265
+ };
266
+ }
267
+ return { valid: true };
268
+ }
269
+ function configRoutesToRouteManifest(appDirectory, routes, rootId = "root") {
270
+ let routeManifest = {};
271
+ function walk(route, parentId) {
272
+ let id = route.id || createRouteId(route.file);
273
+ let manifestItem = {
274
+ id,
275
+ parentId,
276
+ file: Path.isAbsolute(route.file) ? Path.relative(appDirectory, route.file) : route.file,
277
+ path: route.path,
278
+ index: route.index,
279
+ caseSensitive: route.caseSensitive
280
+ };
281
+ if (routeManifest.hasOwnProperty(id)) {
282
+ throw new Error(
283
+ `Unable to define routes with duplicate route id: "${id}"`
284
+ );
285
+ }
286
+ routeManifest[id] = manifestItem;
287
+ if (route.children) {
288
+ for (let child of route.children) {
289
+ walk(child, id);
290
+ }
291
+ }
292
+ }
293
+ for (let route of routes) {
294
+ walk(route, rootId);
295
+ }
296
+ return routeManifest;
297
+ }
298
+ function createRouteId(file) {
299
+ return Path.normalize(stripFileExtension(file));
300
+ }
301
+ function stripFileExtension(file) {
302
+ return file.replace(/\.[a-z0-9]+$/i, "");
303
+ }
304
+
305
+ // config/config.ts
306
+ var excludedConfigPresetKeys = ["presets"];
307
+ var mergeReactRouterConfig = (...configs) => {
308
+ let reducer = (configA, configB) => {
309
+ let mergeRequired = (key) => configA[key] !== void 0 && configB[key] !== void 0;
310
+ return {
311
+ ...configA,
312
+ ...configB,
313
+ ...mergeRequired("buildEnd") ? {
314
+ buildEnd: async (...args) => {
315
+ await Promise.all([
316
+ configA.buildEnd?.(...args),
317
+ configB.buildEnd?.(...args)
318
+ ]);
319
+ }
320
+ } : {},
321
+ ...mergeRequired("future") ? {
322
+ future: {
323
+ ...configA.future,
324
+ ...configB.future
325
+ }
326
+ } : {},
327
+ ...mergeRequired("presets") ? {
328
+ presets: [...configA.presets ?? [], ...configB.presets ?? []]
329
+ } : {}
330
+ };
331
+ };
332
+ return configs.reduce(reducer, {});
333
+ };
334
+ var deepFreeze = (o) => {
335
+ Object.freeze(o);
336
+ let oIsFunction = typeof o === "function";
337
+ let hasOwnProp = Object.prototype.hasOwnProperty;
338
+ Object.getOwnPropertyNames(o).forEach(function(prop) {
339
+ 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])) {
340
+ deepFreeze(o[prop]);
341
+ }
342
+ });
343
+ return o;
344
+ };
345
+ function ok(value) {
346
+ return { ok: true, value };
347
+ }
348
+ function err(error) {
349
+ return { ok: false, error };
350
+ }
351
+ async function resolveConfig({
352
+ root,
353
+ viteNodeContext,
354
+ reactRouterConfigFile
355
+ }) {
356
+ let reactRouterUserConfig = {};
357
+ if (reactRouterConfigFile) {
358
+ try {
359
+ if (!import_node_fs.default.existsSync(reactRouterConfigFile)) {
360
+ return err(`${reactRouterConfigFile} no longer exists`);
361
+ }
362
+ let configModule = await viteNodeContext.runner.executeFile(
363
+ reactRouterConfigFile
364
+ );
365
+ if (configModule.default === void 0) {
366
+ return err(`${reactRouterConfigFile} must provide a default export`);
367
+ }
368
+ if (typeof configModule.default !== "object") {
369
+ return err(`${reactRouterConfigFile} must export a config`);
370
+ }
371
+ reactRouterUserConfig = configModule.default;
372
+ } catch (error) {
373
+ return err(`Error loading ${reactRouterConfigFile}: ${error}`);
374
+ }
375
+ }
376
+ reactRouterUserConfig = deepFreeze((0, import_cloneDeep.default)(reactRouterUserConfig));
377
+ let presets = (await Promise.all(
378
+ (reactRouterUserConfig.presets ?? []).map(async (preset) => {
379
+ if (!preset.name) {
380
+ throw new Error(
381
+ "React Router presets must have a `name` property defined."
382
+ );
383
+ }
384
+ if (!preset.reactRouterConfig) {
385
+ return null;
386
+ }
387
+ let configPreset = (0, import_omit.default)(
388
+ await preset.reactRouterConfig({ reactRouterUserConfig }),
389
+ excludedConfigPresetKeys
390
+ );
391
+ return configPreset;
392
+ })
393
+ )).filter(function isNotNull(value) {
394
+ return value !== null;
395
+ });
396
+ let defaults = {
397
+ basename: "/",
398
+ buildDirectory: "build",
399
+ serverBuildFile: "index.js",
400
+ serverModuleFormat: "esm",
401
+ ssr: true
402
+ };
403
+ let {
404
+ appDirectory: userAppDirectory,
405
+ basename,
406
+ buildDirectory: userBuildDirectory,
407
+ buildEnd,
408
+ prerender,
409
+ serverBuildFile,
410
+ serverBundles,
411
+ serverModuleFormat,
412
+ ssr
413
+ } = {
414
+ ...defaults,
415
+ // Default values should be completely overridden by user/preset config, not merged
416
+ ...mergeReactRouterConfig(...presets, reactRouterUserConfig)
417
+ };
418
+ if (!ssr && serverBundles) {
419
+ serverBundles = void 0;
420
+ }
421
+ let isValidPrerenderConfig = prerender == null || typeof prerender === "boolean" || Array.isArray(prerender) || typeof prerender === "function";
422
+ if (!isValidPrerenderConfig) {
423
+ return err(
424
+ "The `prerender` config must be a boolean, an array of string paths, or a function returning a boolean or array of string paths"
425
+ );
426
+ }
427
+ let appDirectory = import_pathe3.default.resolve(root, userAppDirectory || "app");
428
+ let buildDirectory = import_pathe3.default.resolve(root, userBuildDirectory);
429
+ let rootRouteFile = findEntry(appDirectory, "root");
430
+ if (!rootRouteFile) {
431
+ let rootRouteDisplayPath = import_pathe3.default.relative(
432
+ root,
433
+ import_pathe3.default.join(appDirectory, "root.tsx")
434
+ );
435
+ return err(
436
+ `Could not find a root route module in the app directory as "${rootRouteDisplayPath}"`
437
+ );
438
+ }
439
+ let routes = {
440
+ root: { path: "", id: "root", file: rootRouteFile }
441
+ };
442
+ let routeConfigFile = findEntry(appDirectory, "routes");
443
+ try {
444
+ if (!routeConfigFile) {
445
+ let routeConfigDisplayPath = import_pathe3.default.relative(
446
+ root,
447
+ import_pathe3.default.join(appDirectory, "routes.ts")
448
+ );
449
+ return err(`Route config file not found at "${routeConfigDisplayPath}".`);
450
+ }
451
+ setAppDirectory(appDirectory);
452
+ let routeConfigExport = (await viteNodeContext.runner.executeFile(
453
+ import_pathe3.default.join(appDirectory, routeConfigFile)
454
+ )).default;
455
+ let routeConfig = await routeConfigExport;
456
+ let result = validateRouteConfig({
457
+ routeConfigFile,
458
+ routeConfig
459
+ });
460
+ if (!result.valid) {
461
+ return err(result.message);
462
+ }
463
+ routes = {
464
+ ...routes,
465
+ ...configRoutesToRouteManifest(appDirectory, routeConfig)
466
+ };
467
+ } catch (error) {
468
+ return err(
469
+ [
470
+ import_picocolors.default.red(`Route config in "${routeConfigFile}" is invalid.`),
471
+ "",
472
+ error.loc?.file && error.loc?.column && error.frame ? [
473
+ import_pathe3.default.relative(appDirectory, error.loc.file) + ":" + error.loc.line + ":" + error.loc.column,
474
+ error.frame.trim?.()
475
+ ] : error.stack
476
+ ].flat().join("\n")
477
+ );
478
+ }
479
+ let future = {
480
+ unstable_optimizeDeps: reactRouterUserConfig.future?.unstable_optimizeDeps ?? false,
481
+ unstable_splitRouteModules: reactRouterUserConfig.future?.unstable_splitRouteModules ?? false,
482
+ unstable_viteEnvironmentApi: reactRouterUserConfig.future?.unstable_viteEnvironmentApi ?? false
483
+ };
484
+ let reactRouterConfig = deepFreeze({
485
+ appDirectory,
486
+ basename,
487
+ buildDirectory,
488
+ buildEnd,
489
+ future,
490
+ prerender,
491
+ routes,
492
+ serverBuildFile,
493
+ serverBundles,
494
+ serverModuleFormat,
495
+ ssr
496
+ });
497
+ for (let preset of reactRouterUserConfig.presets ?? []) {
498
+ await preset.reactRouterConfigResolved?.({ reactRouterConfig });
499
+ }
500
+ return ok(reactRouterConfig);
501
+ }
502
+ async function createConfigLoader({
503
+ rootDirectory: root,
504
+ watch
505
+ }) {
506
+ root = root ?? process.env.REACT_ROUTER_ROOT ?? process.cwd();
507
+ let viteNodeContext = await createContext({
508
+ root,
509
+ mode: watch ? "development" : "production",
510
+ server: !watch ? { watch: null } : {},
511
+ ssr: {
512
+ external: ssrExternals
513
+ }
514
+ });
515
+ let reactRouterConfigFile = findEntry(root, "react-router.config", {
516
+ absolute: true
517
+ });
518
+ let getConfig = () => resolveConfig({ root, viteNodeContext, reactRouterConfigFile });
519
+ let appDirectory;
520
+ let initialConfigResult = await getConfig();
521
+ if (!initialConfigResult.ok) {
522
+ throw new Error(initialConfigResult.error);
523
+ }
524
+ appDirectory = initialConfigResult.value.appDirectory;
525
+ let lastConfig = initialConfigResult.value;
526
+ let fsWatcher;
527
+ let changeHandlers = [];
528
+ return {
529
+ getConfig,
530
+ onChange: (handler) => {
531
+ if (!watch) {
532
+ throw new Error(
533
+ "onChange is not supported when watch mode is disabled"
534
+ );
535
+ }
536
+ changeHandlers.push(handler);
537
+ if (!fsWatcher) {
538
+ fsWatcher = import_chokidar.default.watch(
539
+ [
540
+ ...reactRouterConfigFile ? [reactRouterConfigFile] : [],
541
+ appDirectory
542
+ ],
543
+ { ignoreInitial: true }
544
+ );
545
+ fsWatcher.on("all", async (...args) => {
546
+ let [event, rawFilepath] = args;
547
+ let filepath = import_pathe3.default.normalize(rawFilepath);
548
+ let appFileAddedOrRemoved = appDirectory && (event === "add" || event === "unlink") && filepath.startsWith(import_pathe3.default.normalize(appDirectory));
549
+ let configCodeUpdated = Boolean(
550
+ viteNodeContext.devServer?.moduleGraph.getModuleById(filepath)
551
+ );
552
+ if (configCodeUpdated || appFileAddedOrRemoved) {
553
+ viteNodeContext.devServer?.moduleGraph.invalidateAll();
554
+ viteNodeContext.runner?.moduleCache.clear();
555
+ }
556
+ if (appFileAddedOrRemoved || configCodeUpdated) {
557
+ let result = await getConfig();
558
+ let configChanged = result.ok && !(0, import_isEqual.default)(lastConfig, result.value);
559
+ let routeConfigChanged = result.ok && !(0, import_isEqual.default)(lastConfig?.routes, result.value.routes);
560
+ for (let handler2 of changeHandlers) {
561
+ handler2({
562
+ result,
563
+ configCodeUpdated,
564
+ configChanged,
565
+ routeConfigChanged,
566
+ path: filepath,
567
+ event
568
+ });
569
+ }
570
+ if (result.ok) {
571
+ lastConfig = result.value;
572
+ }
573
+ }
574
+ });
575
+ }
576
+ return () => {
577
+ changeHandlers = changeHandlers.filter(
578
+ (changeHandler) => changeHandler !== handler
579
+ );
580
+ };
581
+ },
582
+ close: async () => {
583
+ changeHandlers = [];
584
+ await viteNodeContext.devServer.close();
585
+ await fsWatcher?.close();
586
+ }
587
+ };
588
+ }
589
+ async function loadConfig({ rootDirectory }) {
590
+ let configLoader = await createConfigLoader({
591
+ rootDirectory,
592
+ watch: false
593
+ });
594
+ let config = await configLoader.getConfig();
595
+ await configLoader.close();
596
+ return config;
597
+ }
598
+ var ssrExternals = isReactRouterRepo() ? [
599
+ // This is only needed within this repo because these packages
600
+ // are linked to a directory outside of node_modules so Vite
601
+ // treats them as internal code by default.
602
+ "react-router",
603
+ "react-router-dom",
604
+ "@react-router/architect",
605
+ "@react-router/cloudflare",
606
+ "@react-router/dev",
607
+ "@react-router/express",
608
+ "@react-router/node",
609
+ "@react-router/serve"
610
+ ] : void 0;
611
+ var entryExts = [".js", ".jsx", ".ts", ".tsx"];
612
+ function findEntry(dir, basename, options) {
613
+ for (let ext of entryExts) {
614
+ let file = import_pathe3.default.resolve(dir, basename + ext);
615
+ if (import_node_fs.default.existsSync(file)) {
616
+ return options?.absolute ?? false ? file : import_pathe3.default.relative(dir, file);
617
+ }
618
+ }
619
+ return void 0;
620
+ }
621
+
159
622
  // vite/cloudflare-dev-proxy.ts
160
623
  var serverBuildId = "virtual:react-router/server-build";
161
624
  function importWrangler() {
@@ -168,22 +631,43 @@ function importWrangler() {
168
631
  var PLUGIN_NAME = "react-router-cloudflare-vite-dev-proxy";
169
632
  var cloudflareDevProxyVitePlugin = (options = {}) => {
170
633
  let { getLoadContext, ...restOptions } = options;
634
+ const workerdConditions = ["workerd", "worker"];
635
+ let future;
171
636
  return {
172
637
  name: PLUGIN_NAME,
173
- config: async () => {
638
+ config: async (config) => {
174
639
  await preloadVite();
175
640
  const vite2 = getVite();
176
641
  const serverConditions = [
177
642
  ...vite2.defaultServerConditions ?? []
178
643
  ];
644
+ let configResult = await loadConfig({
645
+ rootDirectory: config.root ?? process.cwd()
646
+ });
647
+ if (!configResult.ok) {
648
+ throw new Error(configResult.error);
649
+ }
650
+ future = configResult.value.future;
179
651
  return {
180
652
  ssr: {
181
653
  resolve: {
182
- externalConditions: ["workerd", "worker", ...serverConditions]
654
+ externalConditions: [...workerdConditions, ...serverConditions]
183
655
  }
184
656
  }
185
657
  };
186
658
  },
659
+ configEnvironment: async (name, options2) => {
660
+ if (!future.unstable_viteEnvironmentApi) {
661
+ return;
662
+ }
663
+ if (name !== "client") {
664
+ options2.resolve = options2.resolve ?? {};
665
+ options2.resolve.externalConditions = [
666
+ ...workerdConditions,
667
+ ...options2.resolve?.externalConditions ?? []
668
+ ];
669
+ }
670
+ },
187
671
  configResolved: (viteConfig) => {
188
672
  let pluginIndex = (name) => viteConfig.plugins.findIndex((plugin) => plugin.name === name);
189
673
  let reactRouterPluginIndex = pluginIndex("react-router");