@@ -38,6 +38,7 @@ import type {
38
38
ExcludesFalse ,
39
39
Format ,
40
40
GetAsyncFunctionFromUnion ,
41
+ JsRedirect ,
41
42
LibConfig ,
42
43
LibOnlyConfig ,
43
44
PkgJson ,
@@ -942,90 +943,112 @@ const composeEntryConfig = async (
942
943
} ;
943
944
} ;
944
945
945
- const composeBundleConfig = (
946
+ const composeBundlelessExternalConfig = (
946
947
jsExtension : string ,
947
948
redirect : Redirect ,
948
949
cssModulesAuto : CssLoaderOptionsAuto ,
949
950
bundle : boolean ,
950
- ) : RsbuildConfig => {
951
- if ( bundle ) return { } ;
951
+ ) : {
952
+ config : RsbuildConfig ;
953
+ resolvedJsRedirect ?: DeepRequired < JsRedirect > ;
954
+ } => {
955
+ if ( bundle ) return { config : { } } ;
952
956
953
- const isStyleRedirect = redirect . style ?? true ;
957
+ const doesRedirectStyle = redirect . style ?? true ;
958
+ const jsRedirectPath = redirect . js ?. path ?? true ;
959
+ const jsRedirectExtension = redirect . js ?. extension ?? true ;
954
960
955
961
type Resolver = GetAsyncFunctionFromUnion <
956
962
ReturnType < NonNullable < Rspack . ExternalItemFunctionData [ 'getResolve' ] > >
957
963
> ;
958
964
let resolver : Resolver | undefined ;
959
965
960
966
return {
961
- output : {
962
- externals : [
963
- async ( data , callback ) => {
964
- const { request, getResolve, context, contextInfo } = data ;
965
- if ( ! request || ! getResolve || ! context || ! contextInfo ) {
966
- return callback ( ) ;
967
- }
968
-
969
- if ( ! resolver ) {
970
- resolver = ( await getResolve ( ) ) as Resolver ;
971
- }
967
+ resolvedJsRedirect : {
968
+ path : jsRedirectPath ,
969
+ extension : jsRedirectExtension ,
970
+ } ,
971
+ config : {
972
+ output : {
973
+ externals : [
974
+ async ( data , callback ) => {
975
+ const { request, getResolve, context, contextInfo } = data ;
976
+ if ( ! request || ! getResolve || ! context || ! contextInfo ) {
977
+ return callback ( ) ;
978
+ }
972
979
973
- // Issuer is not empty string when the module is imported by another module.
974
- // Prevent from externalizing entry modules here.
975
- if ( contextInfo . issuer ) {
976
- // Node.js ECMAScript module loader does no extension searching.
977
- // Add a file extension according to autoExtension config
978
- // when data.request is a relative path and do not have an extension.
979
- // If data.request already have an extension, we replace it with new extension
980
- // This may result in a change in semantics,
981
- // user should use copy to keep origin file or use another separate entry to deal this
982
- let resolvedRequest : string = request ;
983
-
984
- const cssExternal = cssExternalHandler (
985
- resolvedRequest ,
986
- callback ,
987
- jsExtension ,
988
- cssModulesAuto ,
989
- isStyleRedirect ,
990
- ) ;
991
-
992
- if ( cssExternal !== false ) {
993
- return cssExternal ;
980
+ if ( ! resolver ) {
981
+ resolver = ( await getResolve ( ) ) as Resolver ;
994
982
}
995
983
996
- if ( resolvedRequest [ 0 ] === '.' ) {
997
- const resolved = await resolver ( context , resolvedRequest ) ;
998
- resolvedRequest = normalizeSlash (
999
- path . relative ( path . dirname ( contextInfo . issuer ) , resolved ) ,
984
+ // Issuer is not empty string when the module is imported by another module.
985
+ // Prevent from externalizing entry modules here.
986
+ if ( contextInfo . issuer ) {
987
+ let resolvedRequest : string = request ;
988
+
989
+ const cssExternal = cssExternalHandler (
990
+ resolvedRequest ,
991
+ callback ,
992
+ jsExtension ,
993
+ cssModulesAuto ,
994
+ doesRedirectStyle ,
1000
995
) ;
1001
996
1002
- if ( resolvedRequest [ 0 ] !== '.' ) {
1003
- resolvedRequest = `./ ${ resolvedRequest } ` ;
997
+ if ( cssExternal !== false ) {
998
+ return cssExternal ;
1004
999
}
1005
1000
1006
- const ext = extname ( resolvedRequest ) ;
1001
+ // Node.js ECMAScript module loader does no extension searching.
1002
+ // Add a file extension according to autoExtension config
1003
+ // when data.request is a relative path and do not have an extension.
1004
+ // If data.request already have an extension, we replace it with new extension
1005
+ // This may result in a change in semantics,
1006
+ // user should use copy to keep origin file or use another separate entry to deal this
1007
+
1008
+ if ( jsRedirectPath ) {
1009
+ try {
1010
+ resolvedRequest = await resolver ( context , resolvedRequest ) ;
1011
+ } catch ( e ) {
1012
+ // Do nothing, fallthrough to other external matches.
1013
+ }
1014
+
1015
+ resolvedRequest = normalizeSlash (
1016
+ path . relative (
1017
+ path . dirname ( contextInfo . issuer ) ,
1018
+ resolvedRequest ,
1019
+ ) ,
1020
+ ) ;
1007
1021
1008
- if ( ext ) {
1009
- if ( JS_EXTENSIONS_PATTERN . test ( resolvedRequest ) ) {
1010
- resolvedRequest = resolvedRequest . replace (
1011
- / \. [ ^ . ] + $ / ,
1012
- jsExtension ,
1013
- ) ;
1022
+ if ( resolvedRequest [ 0 ] !== '.' ) {
1023
+ resolvedRequest = `./${ resolvedRequest } ` ;
1024
+ }
1025
+ }
1026
+
1027
+ if ( jsRedirectExtension ) {
1028
+ const ext = extname ( resolvedRequest ) ;
1029
+ if ( ext ) {
1030
+ if ( JS_EXTENSIONS_PATTERN . test ( resolvedRequest ) ) {
1031
+ resolvedRequest = resolvedRequest . replace (
1032
+ / \. [ ^ . ] + $ / ,
1033
+ jsExtension ,
1034
+ ) ;
1035
+ } else {
1036
+ // If it does not match jsExtensionsPattern, we should do nothing, eg: ./foo.png
1037
+ return callback ( ) ;
1038
+ }
1014
1039
} else {
1015
- // If it does not match jsExtensionsPattern, we should do nothing, eg: ./foo.png
1016
- return callback ( ) ;
1040
+ // TODO: add redirect.extension option
1041
+ resolvedRequest = ` ${ resolvedRequest } ${ jsExtension } ` ;
1017
1042
}
1018
- } else {
1019
- // TODO: add redirect.extension option
1020
- resolvedRequest = `${ resolvedRequest } ${ jsExtension } ` ;
1021
1043
}
1044
+
1045
+ return callback ( undefined , resolvedRequest ) ;
1022
1046
}
1023
1047
1024
- return callback ( undefined , resolvedRequest ) ;
1025
- }
1026
- callback ( ) ;
1027
- } ,
1028
- ] as Rspack . ExternalItem [ ] ,
1048
+ callback ( ) ;
1049
+ } ,
1050
+ ] as Rspack . ExternalItem [ ] ,
1051
+ } ,
1029
1052
} ,
1030
1053
} ;
1031
1054
} ;
@@ -1198,7 +1221,7 @@ async function composeLibRsbuildConfig(config: LibConfig) {
1198
1221
externalHelpers ,
1199
1222
pkgJson ,
1200
1223
) ;
1201
- const externalsConfig = composeExternalsConfig (
1224
+ const userExternalConfig = composeExternalsConfig (
1202
1225
format ! ,
1203
1226
config . output ?. externals ,
1204
1227
) ;
@@ -1207,7 +1230,7 @@ async function composeLibRsbuildConfig(config: LibConfig) {
1207
1230
jsExtension,
1208
1231
dtsExtension,
1209
1232
} = composeAutoExtensionConfig ( config , autoExtension , pkgJson ) ;
1210
- const bundleConfig = composeBundleConfig (
1233
+ const { config : bundlelessExternalConfig } = composeBundlelessExternalConfig (
1211
1234
jsExtension ,
1212
1235
redirect ,
1213
1236
cssModulesAuto ,
@@ -1237,7 +1260,7 @@ async function composeLibRsbuildConfig(config: LibConfig) {
1237
1260
const externalsWarnConfig = composeExternalsWarnConfig (
1238
1261
format ! ,
1239
1262
autoExternalConfig ?. output ?. externals ,
1240
- externalsConfig ?. output ?. externals ,
1263
+ userExternalConfig ?. output ?. externals ,
1241
1264
) ;
1242
1265
const minifyConfig = composeMinifyConfig ( config ) ;
1243
1266
const bannerFooterConfig = composeBannerFooterConfig ( banner , footer ) ;
@@ -1249,15 +1272,20 @@ async function composeLibRsbuildConfig(config: LibConfig) {
1249
1272
return mergeRsbuildConfig (
1250
1273
formatConfig ,
1251
1274
shimsConfig ,
1275
+ syntaxConfig ,
1252
1276
externalHelpersConfig ,
1253
- // externalsWarnConfig should before other externals config
1277
+ autoExtensionConfig ,
1278
+
1279
+ // `externalsWarnConfig` should before other externals config.
1254
1280
externalsWarnConfig ,
1255
- externalsConfig ,
1256
1281
autoExternalConfig ,
1257
- autoExtensionConfig ,
1258
- syntaxConfig ,
1259
- bundleConfig ,
1260
1282
targetConfig ,
1283
+ // The externals config in `bundleConfig` should present after all externals config as
1284
+ // it relies on other externals config to bail out the externalized modules first then resolve
1285
+ // the correct path for relative imports.
1286
+ userExternalConfig ,
1287
+ bundlelessExternalConfig ,
1288
+
1261
1289
entryConfig ,
1262
1290
cssConfig ,
1263
1291
entryChunkConfig ,
0 commit comments