rsbuild-plugin-react-router 0.3.0 → 0.3.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.
Files changed (56) hide show
  1. package/README.md +55 -24
  2. package/dist/451.js +36 -20
  3. package/dist/build-manifest.d.ts +6 -3
  4. package/dist/concurrency.d.ts +2 -1
  5. package/dist/config-imports.d.ts +7 -0
  6. package/dist/dev-background-resources.d.ts +38 -0
  7. package/dist/dev-generation.d.ts +2 -2
  8. package/dist/dev-runtime-artifacts.d.ts +9 -1
  9. package/dist/dev-runtime-compilation.d.ts +17 -2
  10. package/dist/dev-server.d.ts +5 -0
  11. package/dist/effect-runtime.d.ts +18 -0
  12. package/dist/export-utils.d.ts +2 -2
  13. package/dist/index.cjs +11448 -891
  14. package/dist/index.d.ts +4 -0
  15. package/dist/index.js +7950 -758
  16. package/dist/lazy-compilation-prewarm.d.ts +25 -0
  17. package/dist/lazy-compilation.d.ts +2 -1
  18. package/dist/manifest.d.ts +19 -4
  19. package/dist/parallel-route-transforms.d.ts +17 -2
  20. package/dist/prerender-build.d.ts +4 -0
  21. package/dist/prerender.d.ts +0 -1
  22. package/dist/react-router-config.d.ts +8 -5
  23. package/dist/server-build-resolution.d.ts +3 -0
  24. package/dist/ssr-externals.d.ts +1 -0
  25. package/dist/typegen.d.ts +14 -1
  26. package/dist/types.d.ts +15 -6
  27. package/package.json +11 -9
  28. package/src/build-manifest.ts +110 -73
  29. package/src/concurrency.ts +3 -22
  30. package/src/config-imports.ts +38 -0
  31. package/src/dev-background-resources.ts +255 -0
  32. package/src/dev-generation.ts +43 -53
  33. package/src/dev-runtime-artifacts.ts +50 -18
  34. package/src/dev-runtime-compilation.ts +80 -1
  35. package/src/dev-runtime-controller.ts +111 -31
  36. package/src/dev-runtime-session.ts +18 -11
  37. package/src/dev-server.ts +31 -1
  38. package/src/effect-runtime.ts +130 -0
  39. package/src/export-utils.ts +82 -23
  40. package/src/index.ts +118 -153
  41. package/src/lazy-compilation-prewarm.ts +279 -0
  42. package/src/lazy-compilation.ts +12 -5
  43. package/src/manifest.ts +366 -255
  44. package/src/modify-browser-manifest.ts +2 -1
  45. package/src/parallel-route-transforms.ts +195 -69
  46. package/src/prerender-build.ts +125 -61
  47. package/src/prerender.ts +0 -18
  48. package/src/react-router-config.ts +98 -73
  49. package/src/route-artifacts.ts +2 -2
  50. package/src/route-export-resolution.ts +3 -3
  51. package/src/route-watch.ts +119 -84
  52. package/src/server-build-resolution.ts +131 -0
  53. package/src/server-utils.ts +7 -106
  54. package/src/ssr-externals.ts +1 -1
  55. package/src/typegen.ts +162 -33
  56. package/src/types.ts +16 -6
package/README.md CHANGED
@@ -70,24 +70,23 @@ plugin only needs options for Rsbuild-specific behavior.
70
70
  ```ts
71
71
  pluginReactRouter({
72
72
  customServer: false,
73
- serverOutput: 'module',
74
- lazyCompilation: undefined,
73
+ lazyCompilation: true,
74
+ unstableLazyCompilationPrewarm: false,
75
75
  logPerformance: false,
76
- parallelRouteTransform: undefined,
77
- onRouteTopologyChange: undefined,
78
76
  federation: false,
79
77
  });
80
78
  ```
81
79
 
82
- | Option | Default | Description |
83
- | ------------------------ | ----------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
84
- | `customServer` | `false` | Disables the built-in development SSR middleware. Enable this when an app owns the server with `createDevServer()` or an adapter. |
85
- | `serverOutput` | `'module'` | Emitted Rsbuild server format: `'module'` or `'commonjs'`. When omitted, React Router's `serverModuleFormat` selects the format (`'esm'` -> `'module'`, `'cjs'` -> `'commonjs'`); setting `serverOutput` overrides it. |
86
- | `lazyCompilation` | `undefined` | Optional Rsbuild dev lazy-compilation config. When enabled here or through `dev.lazyCompilation`, React Router hydration-critical modules stay eager so the browser manifest and route modules are not replaced by lazy proxies. |
87
- | `logPerformance` | `false` | Logs structured React Router plugin timing information through the Rsbuild logger. |
88
- | `parallelRouteTransform` | `undefined` | Controls worker-thread route transforms. `undefined` auto-enables workers for 256+ routes, `true` forces the default worker count, a positive integer sets the worker count, and `false` keeps transforms inline. |
89
- | `onRouteTopologyChange` | `undefined` | Notification for programmatic/custom dev servers. Recreate the Rsbuild server when route files are added, removed, or moved. The callback is not awaited. |
90
- | `federation` | `false` | Enables the plugin's experimental Module Federation integration. |
80
+ | Option | Default | Description |
81
+ | -------------------------------- | ----------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
82
+ | `customServer` | `false` | Disables the built-in development SSR middleware. Enable this when an app owns the server with `createDevServer()` or an adapter. |
83
+ | `serverOutput` | Derived | Emitted Rsbuild server format: `'module'` or `'commonjs'`. When omitted, React Router's `serverModuleFormat` selects the format (`'esm'` -> `'module'`, `'cjs'` -> `'commonjs'`); setting `serverOutput` overrides it. |
84
+ | `lazyCompilation` | `true` | Optional Rsbuild dev lazy-compilation config. When enabled here or through `dev.lazyCompilation`, React Router hydration-critical modules stay eager so the browser manifest and route modules are not replaced by lazy proxies. |
85
+ | `unstableLazyCompilationPrewarm` | `false` | Experimental prewarm for emitted Rspack lazy-compilation proxy modules after dev compiles. Enable with `true` when route JS proxy startup should happen shortly after compiler readiness. |
86
+ | `logPerformance` | `false` | Logs structured React Router plugin timing information through the Rsbuild logger. |
87
+ | `parallelRouteTransform` | `undefined` | Controls worker-thread route transforms. `undefined` auto-enables workers for 256+ routes, `true` forces the default worker count (in dev this is 0 on machines with 4 or fewer cores, where workers cost more than they save; production builds always use workers), a positive integer sets the worker count, and `false` keeps transforms inline. |
88
+ | `onRouteTopologyChange` | `undefined` | Notification for programmatic/custom dev servers. Recreate the Rsbuild server when route files are added, removed, or moved. The callback is not awaited. |
89
+ | `federation` | `false` | Enables the plugin's experimental Module Federation integration. |
91
90
 
92
91
  When `federation` is enabled, configure the Module Federation plugin with
93
92
  `experiments.asyncStartup: true`. The dev server resolves async server build
@@ -235,6 +234,20 @@ Route transform source maps are generated in development only. If you enable
235
234
  Rsbuild source maps for faster local debugging, prefer a cheap JS map:
236
235
  `output.sourceMap: { js: 'cheap-module-source-map', css: false }`.
237
236
 
237
+ Lazy compilation prewarming is disabled by default. When enabled alongside
238
+ `lazyCompilation`, the plugin fetches emitted browser entry and route JS assets,
239
+ extracts activation keys from Rspack's generated lazy-compilation client calls,
240
+ and POSTs those keys to Rspack's configured lazy trigger endpoint after dev
241
+ compiles. It does not request application routes or run route loaders. Because
242
+ the key extraction depends on Rspack's generated client code shape, opt in with
243
+ `unstableLazyCompilationPrewarm: true`.
244
+
245
+ Subresource Integrity is disabled by default. Enable it with
246
+ `subResourceIntegrity: true` in `react-router.config.*` when the deployed app
247
+ should emit integrity metadata for browser scripts. The legacy
248
+ `future.unstable_subResourceIntegrity` flag is still accepted and is normalized
249
+ to the stable option.
250
+
238
251
  ### Route Configuration
239
252
 
240
253
  Routes can be defined in `app/routes.ts` using the helper functions from `@react-router/dev/routes`:
@@ -339,6 +352,11 @@ export default defineConfig(() => {
339
352
  plugins: [
340
353
  pluginReactRouter({
341
354
  customServer: true,
355
+ onRouteTopologyChange() {
356
+ console.warn('Route topology changed; restart the dev server.');
357
+ process.exitCode = 75;
358
+ setTimeout(() => process.exit(75), 0);
359
+ },
342
360
  }),
343
361
  pluginReact(),
344
362
  ],
@@ -346,17 +364,14 @@ export default defineConfig(() => {
346
364
  });
347
365
  ```
348
366
 
349
- If the server is created programmatically with `createDevServer()`, pass
350
- `onRouteTopologyChange` and use it to recreate that server. Rsbuild's
351
- `reload-server` watcher is owned by the CLI and is not installed by the
352
- programmatic API. The callback is a notification and is not awaited, so it can
353
- safely start a serialized replacement task. Always `await` the active server's
354
- `close()` before calling `createDevServer()` again; the plugin rejects overlapping
355
- or out-of-order replacement instead of closing one server from inside another
356
- server's startup hooks. If startup fails before returning a server, or if
357
- `close()` rejects, restart the process before retrying unless you can externally
358
- prove and force complete teardown; a fresh Rsbuild instance alone is not
359
- sufficient. Do not launch concurrent `createDevServer()` calls.
367
+ Rsbuild's `reload-server` watcher is owned by the CLI and is not installed by
368
+ the programmatic `createDevServer()` API. The sample below therefore treats
369
+ route topology changes as a full process restart: do not call `startServer()`
370
+ again inside the same process or mount a second dev server on the same Express
371
+ app. If you implement in-process replacement instead, route requests through
372
+ replaceable middleware and request-handler delegates, always `await` the active
373
+ server's `close()` before calling `createDevServer()` again, and do not launch
374
+ concurrent replacements.
360
375
 
361
376
  Create one server entry point (`server.js`) and let it own the React Router
362
377
  request handler in both development and production. Only the build provider
@@ -632,6 +647,22 @@ The plugin automatically:
632
647
  - Handles route-based code splitting
633
648
  - Manages client and server builds
634
649
 
650
+ ### Benchmarking
651
+
652
+ `pnpm bench:large` runs this repository's generated stress fixture for quick
653
+ regression checks. `pnpm bench:synthetic-app` runs the embedded complex Rsbuild
654
+ app under `benchmarks/synthetic-web-bundler-benchmark`, which adds heavier
655
+ loader and transform contention for benchmark coverage closer to a large
656
+ real-world application.
657
+
658
+ ```bash
659
+ pnpm bench:large
660
+ pnpm bench:synthetic-app -- --profile all --runs 2
661
+ ```
662
+
663
+ The PR benchmark workflow reports production build, dev route-load, HMR/update,
664
+ and embedded synthetic app timings in the same benchmark comment.
665
+
635
666
  ## React Router Framework Mode
636
667
 
637
668
  React Router "Framework Mode" wraps Data Mode using a Vite plugin. This Rsbuild
package/dist/451.js CHANGED
@@ -4,6 +4,7 @@ import { langFromPath, parse, walk as external_yuku_parser_walk } from "yuku-par
4
4
  import { Analyzer } from "yuku-analyzer";
5
5
  import { print } from "yuku-codegen";
6
6
  import { readFile, stat } from "node:fs/promises";
7
+ import { rspack } from "@rsbuild/core";
7
8
  import { createRequire } from "node:module";
8
9
  let PLUGIN_NAME = 'rsbuild:react-router', JS_EXTENSIONS = [
9
10
  '.tsx',
@@ -557,16 +558,30 @@ let mightContainRouteChunkExportName = (source)=>routeChunkExportNames.some((exp
557
558
  oldestEntry.done || cache.delete(oldestEntry.value);
558
559
  }
559
560
  cache.set(key, value);
560
- }, exportInfoCache = new Map(), routeModuleAnalysisCache = new Map(), cachePromiseOnReject = (promise, invalidate)=>promise.catch((error)=>{
561
- throw invalidate(), error;
562
- }), parseProgram = (code, resourcePath)=>{
563
- let result = parse(code, {
561
+ }, exportInfoCache = new Map(), routeModuleAnalysisCache = new Map(), getParseErrors = (result)=>result.diagnostics.filter((diagnostic)=>'error' === diagnostic.severity), getParseErrorMessage = (errors)=>errors.map((error)=>error.message).join('\n'), parseProgram = (code, resourcePath)=>{
562
+ let sourcePath = resourcePath ? resourcePath.replace(/[?#].*$/, '') : void 0, lang = sourcePath ? langFromPath(sourcePath) : 'tsx', result = parse(code, {
564
563
  sourceType: 'module',
565
- lang: resourcePath ? langFromPath(resourcePath) : 'tsx'
566
- }), errors = result.diagnostics.filter((diagnostic)=>'error' === diagnostic.severity);
567
- if (errors.length > 0) throw Error(errors.map((error)=>error.message).join('\n'));
568
- return result.program ?? result;
569
- }, isTypeOnlyExport = (node)=>'type' === node.exportKind || 'TSExportAssignment' === node.type || node.declaration?.declare === !0 || 'ExportDefaultDeclaration' === node.type && node.declaration?.type === 'TSInterfaceDeclaration', collectProgramExportNames = (program)=>{
564
+ lang
565
+ }), errors = getParseErrors(result);
566
+ if (0 === errors.length) return result.program ?? result;
567
+ if (!sourcePath || 'ts' !== lang && 'tsx' !== lang) throw Error(getParseErrorMessage(errors));
568
+ let normalizedResult = parse(rspack.experiments.swc.transformSync(code, {
569
+ filename: sourcePath,
570
+ jsc: {
571
+ parser: {
572
+ syntax: "typescript",
573
+ tsx: 'tsx' === lang
574
+ }
575
+ }
576
+ }).code, {
577
+ sourceType: 'module',
578
+ lang: 'js'
579
+ }), normalizedErrors = getParseErrors(normalizedResult);
580
+ if (normalizedErrors.length > 0) throw Error(getParseErrorMessage(normalizedErrors));
581
+ return normalizedResult.program ?? normalizedResult;
582
+ }, cachePromiseOnReject = (promise, invalidate)=>promise.catch((error)=>{
583
+ throw invalidate(), error;
584
+ }), isTypeOnlyExport = (node)=>'type' === node.exportKind || 'TSExportAssignment' === node.type || node.declaration?.declare === !0 || 'ExportDefaultDeclaration' === node.type && node.declaration?.type === 'TSInterfaceDeclaration', collectProgramExportNames = (program)=>{
570
585
  let exportNames = new Set();
571
586
  for (let statement of program.body ?? []){
572
587
  if (isTypeOnlyExport(statement)) continue;
@@ -601,17 +616,18 @@ let mightContainRouteChunkExportName = (source)=>routeChunkExportNames.some((exp
601
616
  'string' == typeof source && modules.push(source);
602
617
  }
603
618
  return modules;
604
- }, getExportNames = async (code)=>(await getExportNamesAndExportAll(code)).exportNames, getExportNamesAndExportAll = async (code)=>{
605
- let trackedExportInfo, cached = exportInfoCache.get(code);
619
+ }, getExportNames = async (code, resourcePath)=>(await getExportNamesAndExportAll(code, resourcePath)).exportNames, getExportNamesAndExportAll = async (code, resourcePath)=>{
620
+ var code1, resourcePath1;
621
+ let lang, trackedExportInfo, cacheKey = (code1 = code, lang = (resourcePath1 = resourcePath) ? langFromPath(resourcePath1.replace(/[?#].*$/, '')) : 'inline', `${lang}\0${code1}`), cached = exportInfoCache.get(cacheKey);
606
622
  return cached || (trackedExportInfo = cachePromiseOnReject((async ()=>{
607
- let program = parseProgram(code);
623
+ let program = parseProgram(code, resourcePath);
608
624
  return {
609
625
  exportNames: collectProgramExportNames(program),
610
626
  exportAllModules: collectExportAllModules(program)
611
627
  };
612
628
  })(), ()=>{
613
- exportInfoCache.get(code) === trackedExportInfo && exportInfoCache.delete(code);
614
- }), setBoundedCacheEntry(exportInfoCache, code, trackedExportInfo, 2048), trackedExportInfo);
629
+ exportInfoCache.get(cacheKey) === trackedExportInfo && exportInfoCache.delete(cacheKey);
630
+ }), setBoundedCacheEntry(exportInfoCache, cacheKey, trackedExportInfo, 2048), trackedExportInfo);
615
631
  }, getRouteModuleAnalysis = async (resourcePath)=>{
616
632
  let trackedAnalysis, stats = await stat(resourcePath), cached = routeModuleAnalysisCache.get(resourcePath);
617
633
  return cached?.mtimeMs === stats.mtimeMs && cached.size === stats.size ? cached.analysis : (trackedAnalysis = cachePromiseOnReject((async ()=>{
@@ -712,12 +728,12 @@ let mightContainRouteChunkExportName = (source)=>routeChunkExportNames.some((exp
712
728
  } catch {
713
729
  return null;
714
730
  }
715
- }, createBundlerRouteExportResolver = (resolveModule)=>(specifier, importerPath)=>new Promise((resolveResolvedPath)=>{
731
+ }, createBundlerRouteExportResolver = (resolveModule)=>(specifier, importerPath)=>new Promise((resolvePromise)=>{
716
732
  resolveModule(dirname(importerPath), specifier, (error, resolved)=>{
717
- resolveResolvedPath(error || !resolved ? null : resolved);
733
+ resolvePromise(error || !resolved ? null : resolved);
718
734
  });
719
735
  }), collectClientOnlyStubExportNames = async (code, resourcePath, resolveModule = resolveExportAllModule)=>{
720
- let { exportNames: directExportNames, exportAllModules } = await getExportNamesAndExportAll(code), exportNames = new Set(directExportNames), unresolvedExportAll = new Set(), visitedModules = new Set(), collectExportNamesFromModule = async (modulePath)=>{
736
+ let { exportNames: directExportNames, exportAllModules } = await getExportNamesAndExportAll(code, resourcePath), exportNames = new Set(directExportNames), unresolvedExportAll = new Set(), visitedModules = new Set(), collectExportNamesFromModule = async (modulePath)=>{
721
737
  if (visitedModules.has(modulePath)) return;
722
738
  visitedModules.add(modulePath);
723
739
  let { exports: moduleExportNames, exportAllModules: moduleExportAll } = await getRouteModuleAnalysis(modulePath);
@@ -748,7 +764,7 @@ let mightContainRouteChunkExportName = (source)=>routeChunkExportNames.some((exp
748
764
  let chunkedExportSet = chunkedExports.length > 0 ? new Set(chunkedExports) : void 0, reexports = exportNames.filter((exp)=>!chunkedExportSet?.has(exp) && (CLIENT_ROUTE_EXPORTS_SET.has(exp) || isServer && SERVER_ONLY_ROUTE_EXPORTS_SET.has(exp))).sort(), target = `${resourcePath}?react-router-route`;
749
765
  return `export { ${reexports.join(', ')} } from ${JSON.stringify(target)};`;
750
766
  })({
751
- exportNames: routeChunkInfo?.exportNames ?? await getExportNames(code),
767
+ exportNames: routeChunkInfo?.exportNames ?? await getExportNames(code, resourcePath),
752
768
  chunkedExports: routeChunkInfo?.chunkedExports ?? [],
753
769
  isServer,
754
770
  resourcePath
@@ -773,11 +789,11 @@ let mightContainRouteChunkExportName = (source)=>routeChunkExportNames.some((exp
773
789
  };
774
790
  let chunk = await getRouteChunkIfEnabled(routeChunkCache, routeChunkConfig, resourcePath, chunkName, code);
775
791
  if ('enforce' === splitRouteModules && 'main' === chunkName && chunk) {
776
- let exportNameSet;
792
+ let exportNameSet, exportNames = await getExportNames(chunk, resourcePath);
777
793
  validateRouteChunks({
778
794
  config: routeChunkConfig,
779
795
  id: resourcePath,
780
- valid: (exportNameSet = new Set(await getExportNames(chunk)), createRouteChunkExportMap((exportName)=>!exportNameSet.has(exportName)))
796
+ valid: (exportNameSet = new Set(exportNames), createRouteChunkExportMap((exportName)=>!exportNameSet.has(exportName)))
781
797
  });
782
798
  }
783
799
  return {
@@ -1,3 +1,4 @@
1
+ import * as Effect from 'effect/Effect';
1
2
  import type { Config } from './react-router-config.js';
2
3
  import type { Route } from './types.js';
3
4
  type BuildManifest = {
@@ -10,10 +11,12 @@ type BuildManifest = {
10
11
  }>;
11
12
  routeIdToServerBundleId: Record<string, string>;
12
13
  };
13
- export declare const getBuildManifest: ({ reactRouterConfig, routes, rootDirectory, }: {
14
- reactRouterConfig: Required<Pick<Config, "appDirectory" | "buildDirectory" | "serverBuildFile" | "future">> & Pick<Config, "serverBundles">;
14
+ type GetBuildManifestOptions = {
15
+ reactRouterConfig: Required<Pick<Config, 'appDirectory' | 'buildDirectory' | 'serverBuildFile' | 'future'>> & Pick<Config, 'serverBundles'>;
15
16
  routes: Record<string, Route>;
16
17
  rootDirectory: string;
17
- }) => Promise<BuildManifest | undefined>;
18
+ };
19
+ export declare const getBuildManifestEffect: ({ reactRouterConfig, routes, rootDirectory, }: GetBuildManifestOptions) => Effect.Effect<BuildManifest | undefined, Error, never>;
20
+ export declare const getBuildManifest: (options: GetBuildManifestOptions) => Promise<BuildManifest | undefined>;
18
21
  export declare const getRoutesByServerBundleId: (buildManifest: BuildManifest | undefined, sourceRoutes: Record<string, Route>) => Record<string, Record<string, Route>>;
19
22
  export {};
@@ -1,2 +1,3 @@
1
+ export declare const getAvailableCpuCount: () => number;
1
2
  export declare const getDefaultConcurrency: (cpuCount?: number) => number;
2
- export declare const mapWithConcurrency: <Item, Result>(items: readonly Item[], concurrency: number, worker: (item: Item, index: number) => Promise<Result>) => Promise<Result[]>;
3
+ export declare const getCappedPluginConcurrency: (cap?: number) => number;
@@ -1,3 +1,10 @@
1
1
  import type { ModuleCache } from 'jiti';
2
+ import { createJiti } from 'jiti';
3
+ type ConfigImporter = Pick<ReturnType<typeof createJiti>, 'import'>;
2
4
  export declare const collectConfigImportWatchPaths: (configPath: string, moduleCache: ModuleCache, previousCacheKeys: ReadonlySet<string>) => string[];
3
5
  export declare const clearConfigImportCache: (moduleCache: ModuleCache, filePaths: readonly string[]) => void;
6
+ export declare const importConfigWithWatchPaths: <T>(configPath: string, load?: (importer: ConfigImporter) => PromiseLike<T> | T) => Promise<{
7
+ value: Awaited<T>;
8
+ watchPaths: string | string[];
9
+ }>;
10
+ export {};
@@ -0,0 +1,38 @@
1
+ import type { RsbuildPluginAPI } from '@rsbuild/core';
2
+ import type { RouteConfigEntry } from '@react-router/dev/routes';
3
+ import { type ReactRouterManifestForDev } from './manifest.js';
4
+ import type { RouteTransformExecutor } from './parallel-route-transforms.js';
5
+ import { type WatchFileConfig } from './route-watch.js';
6
+ import type { PluginOptions } from './types.js';
7
+ type RegisterReactRouterDevBackgroundResourcesOptions = {
8
+ api: RsbuildPluginAPI;
9
+ isBuild: boolean;
10
+ lazyCompilationPrewarm: PluginOptions['unstableLazyCompilationPrewarm'];
11
+ routeTransformExecutor: RouteTransformExecutor;
12
+ routeRestartMarkerPath: string;
13
+ watchDirectory: string;
14
+ getRouteTopology: () => Promise<Set<string>>;
15
+ initialRouteTopology: Set<string>;
16
+ onRouteTopologyChange: PluginOptions['onRouteTopologyChange'];
17
+ };
18
+ type ReactRouterDevBackgroundResources = {
19
+ setManifest(manifest: ReactRouterManifestForDev): void;
20
+ };
21
+ export declare const createReactRouterRouteTopology: ({ appDirectory, rootRouteFile, routeConfig, loadRouteConfig, getRootRoutePath, }: {
22
+ appDirectory: string;
23
+ rootRouteFile: string;
24
+ routeConfig: RouteConfigEntry[];
25
+ loadRouteConfig: () => Promise<RouteConfigEntry[]>;
26
+ getRootRoutePath: () => string;
27
+ }) => {
28
+ initialRouteTopology: Set<string>;
29
+ getRouteTopology: () => Promise<Set<string>>;
30
+ };
31
+ export declare const createReactRouterRouteWatchFiles: ({ configWatchPaths, routeConfigWatchPaths, routeRestartMarkerPath, onRouteTopologyChange, }: {
32
+ configWatchPaths: string | string[];
33
+ routeConfigWatchPaths: string | string[];
34
+ routeRestartMarkerPath: string;
35
+ onRouteTopologyChange: PluginOptions["onRouteTopologyChange"];
36
+ }) => WatchFileConfig[];
37
+ export declare const registerReactRouterDevBackgroundResources: ({ api, isBuild, lazyCompilationPrewarm, routeTransformExecutor, routeRestartMarkerPath, watchDirectory, getRouteTopology, initialRouteTopology, onRouteTopologyChange, }: RegisterReactRouterDevBackgroundResourcesOptions) => ReactRouterDevBackgroundResources;
38
+ export {};
@@ -1,12 +1,12 @@
1
1
  import type { RsbuildDevServer, Rspack } from '@rsbuild/core';
2
2
  import type { ServerBuild } from 'react-router';
3
- import { type DevGraphChanges, type DevGraphIdentity, type ReactRouterDevBuildPlan, type ReactRouterDevManifestSet } from './dev-runtime-artifacts.js';
3
+ import { type DevGraphChanges, type DevGraphIdentity, type DevRuntimeStats, type ReactRouterDevBuildPlan, type ReactRouterDevManifestSet } from './dev-runtime-artifacts.js';
4
4
  export { snapshotDevChangedFiles } from './dev-runtime-artifacts.js';
5
5
  export type { DevChangedFiles, DevGraphChanges, DevGraphIdentity, ReactRouterDevBuildPlan, ReactRouterDevManifest, ReactRouterDevManifestSet, } from './dev-runtime-artifacts.js';
6
6
  export type ReactRouterDevRuntime = {
7
7
  beginAttempt: () => void;
8
8
  captureWeb: (compilation: Rspack.Compilation, manifestsByEntryName: ReactRouterDevManifestSet) => void;
9
- finishAttempt: (stats: Rspack.Stats | Rspack.MultiStats, changes: DevGraphChanges, identity: DevGraphIdentity) => Promise<void>;
9
+ finishAttempt: (stats: DevRuntimeStats, changes: DevGraphChanges, identity: DevGraphIdentity) => Promise<'committed' | 'ignored' | 'retry-node'>;
10
10
  failAttempt: (error: Error) => void;
11
11
  load: (entryName?: string) => Promise<ServerBuild>;
12
12
  close: (error?: Error) => void;
@@ -8,6 +8,11 @@ export type ReactRouterDevBuildPlan = {
8
8
  };
9
9
  export type ReactRouterDevManifestSet = Readonly<Record<string, ReactRouterDevManifest>>;
10
10
  export type ReactRouterServerBuilds = Readonly<Record<string, ServerBuild>>;
11
+ export type PairedDevStats = {
12
+ web: Rspack.Stats;
13
+ node: Rspack.Stats;
14
+ };
15
+ export type DevRuntimeStats = Rspack.Stats | Rspack.MultiStats | PairedDevStats;
11
16
  export type DependencySnapshot = {
12
17
  files: ReadonlySet<string>;
13
18
  contexts: ReadonlySet<string>;
@@ -22,10 +27,12 @@ export type DevGraphChanges = {
22
27
  node: DevChangedFiles;
23
28
  };
24
29
  export type DevCompilationIdentity = symbol;
30
+ export type DevCompileAttemptIdentity = symbol;
25
31
  export type DevGraphIdentity = {
26
32
  web: DevCompilationIdentity | undefined;
27
33
  node: DevCompilationIdentity | undefined;
28
34
  nodeWeb: DevCompilationIdentity | undefined;
35
+ attempt: DevCompileAttemptIdentity | undefined;
29
36
  };
30
37
  export type WebArtifact = {
31
38
  manifestsByEntryName: ReactRouterDevManifestSet;
@@ -34,6 +41,7 @@ export type WebArtifact = {
34
41
  export declare const snapshotDevChangedFiles: (compiler: Pick<Rspack.Compiler, "modifiedFiles" | "removedFiles"> | undefined) => DevChangedFiles;
35
42
  export declare const snapshotDependencies: (compilation: Rspack.Compilation) => DependencySnapshot;
36
43
  export declare const isSafeOneSidedChange: (changes: DevChangedFiles, dependencies: DependencySnapshot) => boolean;
37
- export declare const getEnvironmentStats: (stats: Rspack.Stats | Rspack.MultiStats, name: "web" | "node") => Rspack.Stats | undefined;
44
+ export declare const isPairedDevStats: (stats: DevRuntimeStats) => stats is PairedDevStats;
45
+ export declare const getEnvironmentStats: (stats: DevRuntimeStats, name: "web" | "node") => Rspack.Stats | undefined;
38
46
  export declare const evaluateServerBuilds: (server: RsbuildDevServer, entryNames: readonly string[]) => Promise<ReactRouterServerBuilds>;
39
47
  export declare const pinServerBuildsToManifests: (builds: ReactRouterServerBuilds, entryNames: readonly string[], manifestsByEntryName: ReactRouterDevManifestSet) => ReactRouterServerBuilds;
@@ -1,16 +1,19 @@
1
1
  import type { Rspack } from '@rsbuild/core';
2
- import type { DevCompilationIdentity, DevGraphChanges, DevGraphIdentity } from './dev-runtime-artifacts.js';
2
+ import type { DevCompileAttemptIdentity, DevCompilationIdentity, DevRuntimeStats, DevGraphChanges, DevGraphIdentity } from './dev-runtime-artifacts.js';
3
3
  export type DevCompilerPair = {
4
4
  web: Rspack.Compiler;
5
5
  node: Rspack.Compiler;
6
6
  settledCompilations: WeakSet<Rspack.Compilation>;
7
7
  pendingAttempt?: PendingDevCompilation;
8
+ currentAttemptIdentity?: DevCompileAttemptIdentity;
8
9
  latestCompletedWebIdentity?: DevCompilationIdentity;
10
+ latestCompletedWebStats?: Rspack.Stats;
11
+ latestCompletedNodeStats?: Rspack.Stats;
9
12
  latestWebStart?: CompilationStart;
10
13
  latestNodeStart?: CompilationStart;
11
14
  };
12
15
  export type PendingDevCompilation = {
13
- stats: Rspack.Stats | Rspack.MultiStats;
16
+ stats: DevRuntimeStats;
14
17
  changes: DevGraphChanges;
15
18
  identity: DevGraphIdentity;
16
19
  webCompilation: Rspack.Compilation;
@@ -22,11 +25,23 @@ export type CompilationStart = {
22
25
  status: 'started';
23
26
  identity: DevCompilationIdentity;
24
27
  };
28
+ type CompilerPairStartSide = 'latestWebStart' | 'latestNodeStart';
29
+ export declare const createDevCompilerPair: ({ web, node, }: {
30
+ web: Rspack.Compiler;
31
+ node: Rspack.Compiler;
32
+ }) => DevCompilerPair;
33
+ export declare const resetDevCompilerPair: (pair: DevCompilerPair) => void;
25
34
  export declare const isLatestStartedCompilation: (identity: DevCompilationIdentity | undefined, start: CompilationStart | undefined) => boolean;
26
35
  export declare const hasPendingCompilation: (pair: DevCompilerPair) => boolean;
36
+ export declare const beginDevCompilerAttempt: (pair: DevCompilerPair) => void;
37
+ export declare const markDevCompilerPending: (pair: DevCompilerPair, side: CompilerPairStartSide) => boolean;
38
+ export declare const clearDevCompilerStart: (pair: DevCompilerPair, side: CompilerPairStartSide) => void;
27
39
  export type CompilationIdentityTracker = {
28
40
  getCompilationIdentity(compilation: Rspack.Compilation): DevCompilationIdentity;
29
41
  getWebIdentityForNodeCompilation(compilation: Rspack.Compilation): DevCompilationIdentity | undefined;
42
+ getAttemptIdentityForCompilation(compilation: Rspack.Compilation): DevCompileAttemptIdentity | undefined;
43
+ setAttemptIdentityForCompilation(compilation: Rspack.Compilation, identity: DevCompileAttemptIdentity): void;
30
44
  setWebIdentityForNodeCompilation(compilation: Rspack.Compilation, identity: DevCompilationIdentity): void;
31
45
  };
32
46
  export declare const createCompilationIdentityTracker: () => CompilationIdentityTracker;
47
+ export {};
@@ -1,5 +1,7 @@
1
1
  import type { IncomingMessage, ServerResponse } from 'node:http';
2
+ import type { RsbuildConfig } from '@rsbuild/core';
2
3
  import type { ServerBuild } from 'react-router';
4
+ export type ServerSetup = Exclude<NonNullable<NonNullable<RsbuildConfig['server']>['setup']>, unknown[]>;
3
5
  export type DevServerMiddleware = (req: IncomingMessage, res: ServerResponse, next: (err?: unknown) => void) => Promise<void>;
4
6
  type RequestHandler = (request: Request) => Response | Promise<Response>;
5
7
  type BuildProvider = () => Promise<ServerBuild>;
@@ -9,4 +11,7 @@ export type DevServerMiddlewareDependencies = {
9
11
  createRequestListener?: (handler: RequestHandler) => (req: IncomingMessage, res: ServerResponse) => void | Promise<void>;
10
12
  };
11
13
  export declare const createDevServerMiddleware: (dependencies: DevServerMiddlewareDependencies) => DevServerMiddleware;
14
+ export declare const createReactRouterDevServerSetup: ({ loadBuild, }: {
15
+ loadBuild: BuildProvider;
16
+ }) => ServerSetup;
12
17
  export {};
@@ -0,0 +1,18 @@
1
+ import * as Effect from 'effect/Effect';
2
+ export declare const DEV_BACKGROUND_STARTUP_DELAY_MS = 3000;
3
+ export declare const normalizeEffectError: (cause: unknown) => Error;
4
+ export declare const runPluginEffect: <A, E>(effect: Effect.Effect<A, E, never>) => Promise<A>;
5
+ export declare const tryPluginSync: <A>(evaluate: () => A) => Effect.Effect<A, Error, never>;
6
+ export declare const tryPluginPromise: <A>(evaluate: () => PromiseLike<A> | A) => Effect.Effect<A, Error, never>;
7
+ type DelayedPluginTask = {
8
+ schedule(): void;
9
+ reschedule(): void;
10
+ cancelEffect(): Effect.Effect<void, Error, never>;
11
+ cancel(): Promise<void>;
12
+ };
13
+ export declare const createDelayedPluginTask: ({ delayMs, run, onError, }: {
14
+ delayMs: number;
15
+ run: () => Effect.Effect<void, Error, never>;
16
+ onError: (error: Error) => void;
17
+ }) => DelayedPluginTask;
18
+ export {};
@@ -9,7 +9,7 @@ type RouteModuleAnalysis = {
9
9
  readonly exportAllModules: readonly string[];
10
10
  };
11
11
  export declare const collectProgramExportNames: (program: ProgramNode) => string[];
12
- export declare const getExportNames: (code: string) => Promise<readonly string[]>;
13
- export declare const getExportNamesAndExportAll: (code: string) => Promise<ExportInfo>;
12
+ export declare const getExportNames: (code: string, resourcePath?: string) => Promise<readonly string[]>;
13
+ export declare const getExportNamesAndExportAll: (code: string, resourcePath?: string) => Promise<ExportInfo>;
14
14
  export declare const getRouteModuleAnalysis: (resourcePath: string) => Promise<RouteModuleAnalysis>;
15
15
  export {};