22// context but want to use Vite's ESM build to avoid deprecation warnings
33import type * as Vite from "vite" ;
44import { type BinaryLike , createHash } from "node:crypto" ;
5+ import * as fs from "node:fs" ;
56import * as path from "node:path" ;
67import * as url from "node:url" ;
78import * as fse from "fs-extra" ;
@@ -805,6 +806,39 @@ export const reactRouterVitePlugin: ReactRouterVitePlugin = () => {
805806 return new Set ( [ ...cssUrlPaths , ...chunkAssetPaths ] ) ;
806807 } ;
807808
809+ let generateSriManifest = async ( ctx : ReactRouterPluginContext ) => {
810+ let clientBuildDirectory = getClientBuildDirectory ( ctx . reactRouterConfig ) ;
811+ // walk the client build directory and generate SRI hashes for all .js files
812+ let entries = fs . readdirSync ( clientBuildDirectory , {
813+ withFileTypes : true ,
814+ recursive : true ,
815+ } ) ;
816+ let sriManifest : ReactRouterManifest [ "sri" ] = { } ;
817+ for ( const entry of entries ) {
818+ if ( entry . isFile ( ) && entry . name . endsWith ( ".js" ) ) {
819+ let contents ;
820+ try {
821+ contents = await fse . readFile (
822+ path . join ( entry . path , entry . name ) ,
823+ "utf-8"
824+ ) ;
825+ } catch ( e ) {
826+ logger . error ( `Failed to read file for SRI generation: ${ entry . name } ` ) ;
827+ throw e ;
828+ }
829+ let hash = createHash ( "sha384" )
830+ . update ( contents )
831+ . digest ( )
832+ . toString ( "base64" ) ;
833+ let filepath = getVite ( ) . normalizePath (
834+ path . relative ( clientBuildDirectory , path . join ( entry . path , entry . name ) )
835+ ) ;
836+ sriManifest [ `${ ctx . publicPath } ${ filepath } ` ] = `sha384-${ hash } ` ;
837+ }
838+ }
839+ return sriManifest ;
840+ } ;
841+
808842 let generateReactRouterManifestsForBuild = async ( {
809843 routeIds,
810844 } : {
@@ -942,6 +976,7 @@ export const reactRouterVitePlugin: ReactRouterVitePlugin = () => {
942976 let reactRouterBrowserManifest : ReactRouterManifest = {
943977 ...fingerprintedValues ,
944978 ...nonFingerprintedValues ,
979+ sri : undefined ,
945980 } ;
946981
947982 // Write the browser manifest to disk as part of the build process
@@ -952,12 +987,18 @@ export const reactRouterVitePlugin: ReactRouterVitePlugin = () => {
952987 ) } ;`
953988 ) ;
954989
990+ let sri : ReactRouterManifest [ "sri" ] = undefined ;
991+ if ( ctx . reactRouterConfig . future . unstable_subResourceIntegrity ) {
992+ sri = await generateSriManifest ( ctx ) ;
993+ }
994+
955995 // The server manifest is the same as the browser manifest, except for
956996 // server bundle builds which only includes routes for the current bundle,
957997 // otherwise the server and client have the same routes
958998 let reactRouterServerManifest : ReactRouterManifest = {
959999 ...reactRouterBrowserManifest ,
9601000 routes : serverRoutes ,
1001+ sri,
9611002 } ;
9621003
9631004 return {
@@ -1043,6 +1084,8 @@ export const reactRouterVitePlugin: ReactRouterVitePlugin = () => {
10431084 } ;
10441085 }
10451086
1087+ let sri : ReactRouterManifest [ "sri" ] = undefined ;
1088+
10461089 let reactRouterManifestForDev = {
10471090 version : String ( Math . random ( ) ) ,
10481091 url : combineURLs ( ctx . publicPath , virtual . browserManifest . url ) ,
@@ -1056,6 +1099,7 @@ export const reactRouterVitePlugin: ReactRouterVitePlugin = () => {
10561099 ) ,
10571100 imports : [ ] ,
10581101 } ,
1102+ sri,
10591103 routes,
10601104 } ;
10611105
0 commit comments