@@ -5,18 +5,23 @@ import type { RsbuildPlugin, Rspack } from '@rsbuild/core';
55import * as esbuild from 'esbuild' ;
66import { createJiti } from 'jiti' ;
77import jsesc from 'jsesc' ;
8- import { isAbsolute , relative , resolve } from 'pathe' ;
8+ import { relative , resolve } from 'pathe' ;
99import { RspackVirtualModulePlugin } from 'rspack-plugin-virtual-module' ;
1010import { generate , parse } from './babel.js' ;
11- import { PLUGIN_NAME , SERVER_EXPORTS , CLIENT_EXPORTS , SERVER_ONLY_ROUTE_EXPORTS } from './constants.js' ;
11+ import { PLUGIN_NAME , SERVER_ONLY_ROUTE_EXPORTS } from './constants.js' ;
1212import { createDevServerMiddleware } from './dev-server.js' ;
1313import {
14- combineURLs ,
15- createRouteId ,
16- generateWithProps ,
17- removeExports ,
18- transformRoute ,
14+ generateWithProps ,
15+ removeExports ,
16+ transformRoute ,
17+ findEntryFile
1918} from './plugin-utils.js' ;
19+ import {
20+ configRoutesToRouteManifest ,
21+ getReactRouterManifestForDev
22+ } from './manifest.js' ;
23+ import { generateServerBuild } from './server-utils.js' ;
24+ import { createModifyBrowserManifestPlugin } from './modify-browser-manifest.js' ;
2025
2126export type PluginOptions = {
2227 /**
@@ -140,18 +145,9 @@ export const pluginReactRouter = (
140145 return [ ] as RouteConfigEntry [ ] ;
141146 } ) ;
142147
143- const jsExtensions = [ '.tsx' , '.ts' , '.jsx' , '.js' , '.mjs' ] ;
144-
145- const findEntryFile = ( basePath : string ) => {
146- for ( const ext of jsExtensions ) {
147- const filePath = `${ basePath } ${ ext } ` ;
148- if ( existsSync ( filePath ) ) {
149- return filePath ;
150- }
151- }
152- return `${ basePath } .tsx` ; // Default to .tsx if no file exists
153- } ;
154-
148+ // Remove local JS_EXTENSIONS definition - use the imported one instead
149+
150+ // Remove duplicate findEntryFile implementation
155151 const entryClientPath = findEntryFile (
156152 resolve ( appDirectory , 'entry.client' ) ,
157153 ) ;
@@ -208,6 +204,7 @@ export const pluginReactRouter = (
208204 basename,
209205 appDirectory,
210206 ssr,
207+ federation : false ,
211208 } ) ,
212209 'virtual/react-router/with-props' : generateWithProps ( ) ,
213210 } ) ;
@@ -329,61 +326,9 @@ export const pluginReactRouter = (
329326 tools : {
330327 rspack : ( rspackConfig ) => {
331328 if ( rspackConfig . plugins ) {
332- rspackConfig . plugins . push ( {
333- apply ( compiler : Rspack . Compiler ) {
334- compiler . hooks . emit . tapAsync (
335- 'ModifyBrowserManifest' ,
336- async ( compilation : Rspack . Compilation , callback ) => {
337- const manifest = await getReactRouterManifestForDev (
338- routes ,
339- pluginOptions ,
340- compilation . getStats ( ) . toJson ( ) ,
341- appDirectory ,
342- ) ;
343-
344- const manifestPath =
345- 'static/js/virtual/react-router/browser-manifest.js' ;
346- if ( compilation . assets [ manifestPath ] ) {
347- const originalSource = compilation . assets [
348- manifestPath
349- ]
350- . source ( )
351- . toString ( ) ;
352- const newSource = originalSource . replace (
353- / [ " ' ` ] P L A C E H O L D E R [ " ' ` ] / ,
354- jsesc ( manifest , { es6 : true } ) ,
355- ) ;
356- compilation . assets [ manifestPath ] = {
357- source : ( ) => newSource ,
358- size : ( ) => newSource . length ,
359- map : ( ) => ( {
360- version : 3 ,
361- sources : [ manifestPath ] ,
362- names : [ ] ,
363- mappings : '' ,
364- file : manifestPath ,
365- sourcesContent : [ newSource ] ,
366- } ) ,
367- sourceAndMap : ( ) => ( {
368- source : newSource ,
369- map : {
370- version : 3 ,
371- sources : [ manifestPath ] ,
372- names : [ ] ,
373- mappings : '' ,
374- file : manifestPath ,
375- sourcesContent : [ newSource ] ,
376- } ,
377- } ) ,
378- updateHash : ( hash ) => hash . update ( newSource ) ,
379- buffer : ( ) => Buffer . from ( newSource ) ,
380- } ;
381- }
382- callback ( ) ;
383- } ,
384- ) ;
385- } ,
386- } ) ;
329+ rspackConfig . plugins . push (
330+ createModifyBrowserManifestPlugin ( routes , pluginOptions , appDirectory )
331+ ) ;
387332 }
388333 return rspackConfig ;
389334 } ,
@@ -478,187 +423,3 @@ export const pluginReactRouter = (
478423 ) ;
479424 } ,
480425} ) ;
481-
482- // Helper functions
483- function configRoutesToRouteManifest (
484- appDirectory : string ,
485- routes : RouteConfigEntry [ ] ,
486- rootId = 'root' ,
487- ) {
488- const routeManifest : Record < string , Route > = { } ;
489-
490- function walk ( route : RouteConfigEntry , parentId : string ) {
491- const id = route . id || createRouteId ( route . file ) ;
492- const manifestItem = {
493- id,
494- parentId,
495- file : isAbsolute ( route . file )
496- ? relative ( appDirectory , route . file )
497- : route . file ,
498- path : route . path ,
499- index : route . index ,
500- caseSensitive : route . caseSensitive ,
501- } ;
502-
503- if ( Object . prototype . hasOwnProperty . call ( routeManifest , id ) ) {
504- throw new Error (
505- `Unable to define routes with duplicate route id: "${ id } "` ,
506- ) ;
507- }
508- routeManifest [ id ] = manifestItem ;
509-
510- if ( route . children ) {
511- for ( const child of route . children ) {
512- walk ( child , id ) ;
513- }
514- }
515- }
516-
517- for ( const route of routes ) {
518- walk ( route , rootId ) ;
519- }
520-
521- return routeManifest ;
522- }
523-
524- async function getReactRouterManifestForDev (
525- routes : Record < string , Route > ,
526- //@ts -ignore
527- options : PluginOptions ,
528- clientStats : Rspack . StatsCompilation | undefined ,
529- context : string ,
530- ) : Promise < {
531- version : string ;
532- url : string ;
533- entry : {
534- module : string ;
535- imports : string [ ] ;
536- css : string [ ] ;
537- } ;
538- routes : Record < string , RouteManifestItem > ;
539- } > {
540- const result : Record < string , RouteManifestItem > = { } ;
541-
542- for ( const [ key , route ] of Object . entries ( routes ) ) {
543- const assets = clientStats ?. assetsByChunkName ?. [ route . id ] ;
544- const jsAssets = assets ?. filter ( ( asset ) => asset . endsWith ( '.js' ) ) || [ ] ;
545- const cssAssets = assets ?. filter ( ( asset ) => asset . endsWith ( '.css' ) ) || [ ] ;
546- // Read and analyze the route file to check for exports
547- const routeFilePath = resolve ( context , route . file ) ;
548- let exports = new Set < string > ( ) ;
549-
550- try {
551- const buildResult = await esbuild . build ( {
552- entryPoints : [ routeFilePath ] ,
553- bundle : false ,
554- write : false ,
555- metafile : true ,
556- jsx : 'automatic' ,
557- format : 'esm' ,
558- platform : 'neutral' ,
559- loader : {
560- '.ts' : 'ts' ,
561- '.tsx' : 'tsx' ,
562- '.js' : 'js' ,
563- '.jsx' : 'jsx'
564- } ,
565- } ) ;
566-
567- // Get exports from the metafile
568- const entryPoint = Object . values ( buildResult . metafile . outputs ) [ 0 ] ;
569- if ( entryPoint ?. exports ) {
570- exports = new Set ( entryPoint . exports ) ;
571- }
572- } catch ( error ) {
573- console . error ( `Failed to analyze route file ${ routeFilePath } :` , error ) ;
574- }
575-
576- result [ key ] = {
577- id : route . id ,
578- parentId : route . parentId ,
579- path : route . path ,
580- index : route . index ,
581- caseSensitive : route . caseSensitive ,
582- module : combineURLs ( '/' , jsAssets [ 0 ] || '' ) ,
583- hasAction : exports . has ( SERVER_EXPORTS . action ) ,
584- hasLoader : exports . has ( SERVER_EXPORTS . loader ) ,
585- hasClientAction : exports . has ( CLIENT_EXPORTS . clientAction ) ,
586- hasClientLoader : exports . has ( CLIENT_EXPORTS . clientLoader ) ,
587- hasErrorBoundary : exports . has ( CLIENT_EXPORTS . ErrorBoundary ) ,
588- imports : jsAssets . map ( ( asset ) => combineURLs ( '/' , asset ) ) ,
589- css : cssAssets . map ( ( asset ) => combineURLs ( '/' , asset ) ) ,
590- } ;
591- }
592-
593- const entryAssets = clientStats ?. assetsByChunkName ?. [ 'entry.client' ] ;
594- const entryJsAssets =
595- entryAssets ?. filter ( ( asset ) => asset . endsWith ( '.js' ) ) || [ ] ;
596- const entryCssAssets =
597- entryAssets ?. filter ( ( asset ) => asset . endsWith ( '.css' ) ) || [ ] ;
598-
599- return {
600- version : String ( Math . random ( ) ) ,
601- url : '/static/js/virtual/react-router/browser-manifest.js' ,
602- entry : {
603- module : combineURLs ( '/' , entryJsAssets [ 0 ] || '' ) ,
604- imports : entryJsAssets . map ( ( asset ) => combineURLs ( '/' , asset ) ) ,
605- css : entryCssAssets . map ( ( asset ) => combineURLs ( '/' , asset ) ) ,
606- } ,
607- routes : result ,
608- } ;
609- }
610-
611- /**
612- * Generates the server build module content
613- * @param routes The route manifest
614- * @param options Build options
615- * @returns The generated module content as a string
616- */
617- function generateServerBuild (
618- routes : Record < string , Route > ,
619- options : {
620- entryServerPath : string ;
621- assetsBuildDirectory : string ;
622- basename : string ;
623- appDirectory : string ;
624- ssr : boolean ;
625- } ,
626- ) : string {
627- return `
628- import * as entryServer from ${ JSON . stringify ( options . entryServerPath ) } ;
629- ${ Object . keys ( routes )
630- . map ( ( key , index ) => {
631- const route = routes [ key ] ;
632- return `import * as route${ index } from ${ JSON . stringify (
633- `${ resolve ( options . appDirectory , route . file ) } ?react-router-route` ,
634- ) } ;`;
635- } )
636- . join ( '\n' ) }
637-
638- export { default as assets } from "virtual/react-router/server-manifest";
639- export const assetsBuildDirectory = ${ JSON . stringify (
640- options . assetsBuildDirectory ,
641- ) } ;
642- export const basename = ${ JSON . stringify ( options . basename ) } ;
643- export const future = ${ JSON . stringify ( { } ) } ;
644- export const isSpaMode = ${ ! options . ssr } ;
645- export const ssr = ${ options . ssr } ;
646- export const publicPath = "/";
647- export const entry = { module: entryServer };
648- export const routes = {
649- ${ Object . keys ( routes )
650- . map ( ( key , index ) => {
651- const route = routes [ key ] ;
652- return `${ JSON . stringify ( key ) } : {
653- id: ${ JSON . stringify ( route . id ) } ,
654- parentId: ${ JSON . stringify ( route . parentId ) } ,
655- path: ${ JSON . stringify ( route . path ) } ,
656- index: ${ JSON . stringify ( route . index ) } ,
657- caseSensitive: ${ JSON . stringify ( route . caseSensitive ) } ,
658- module: route${ index }
659- }` ;
660- } )
661- . join ( ',\n ' ) }
662- };
663- ` ;
664- }
0 commit comments