5
5
type RsbuildConfig ,
6
6
type RsbuildPlugin ,
7
7
type RsbuildPlugins ,
8
+ type Rspack ,
8
9
defineConfig as defineRsbuildConfig ,
9
10
loadConfig as loadRsbuildConfig ,
10
11
mergeRsbuildConfig ,
@@ -38,6 +39,7 @@ import type {
38
39
DeepRequired ,
39
40
ExcludesFalse ,
40
41
Format ,
42
+ JsRedirect ,
41
43
LibConfig ,
42
44
LibOnlyConfig ,
43
45
PkgJson ,
@@ -50,6 +52,7 @@ import type {
50
52
RslibConfigAsyncFn ,
51
53
RslibConfigExport ,
52
54
RslibConfigSyncFn ,
55
+ RspackResolver ,
53
56
Shims ,
54
57
Syntax ,
55
58
} from './types' ;
@@ -63,6 +66,7 @@ import {
63
66
isIntermediateOutputFormat ,
64
67
isObject ,
65
68
nodeBuiltInModules ,
69
+ normalizeSlash ,
66
70
omit ,
67
71
pick ,
68
72
readPackageJson ,
@@ -956,64 +960,113 @@ const composeEntryConfig = async (
956
960
} ;
957
961
} ;
958
962
959
- const composeBundleConfig = (
963
+ const composeBundlelessExternalConfig = (
960
964
jsExtension : string ,
961
965
redirect : Redirect ,
962
966
cssModulesAuto : CssLoaderOptionsAuto ,
963
967
bundle : boolean ,
964
- ) : RsbuildConfig => {
965
- if ( bundle ) return { } ;
968
+ ) : {
969
+ config : RsbuildConfig ;
970
+ resolvedJsRedirect ?: DeepRequired < JsRedirect > ;
971
+ } => {
972
+ if ( bundle ) return { config : { } } ;
973
+
974
+ const isStyleRedirected = redirect . style ?? true ;
975
+ const jsRedirectPath = redirect . js ?. path ?? true ;
976
+ const jsRedirectExtension = redirect . js ?. extension ?? true ;
966
977
967
- const isStyleRedirect = redirect . style ?? true ;
978
+ let resolver : RspackResolver | undefined ;
968
979
969
980
return {
970
- output : {
971
- externals : [
972
- ( data : any , callback : any ) => {
973
- // Issuer is not empty string when the module is imported by another module.
974
- // Prevent from externalizing entry modules here.
975
- if ( data . 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 request : string = data . request ;
983
-
984
- const cssExternal = cssExternalHandler (
985
- request ,
986
- callback ,
987
- jsExtension ,
988
- cssModulesAuto ,
989
- isStyleRedirect ,
990
- ) ;
991
-
992
- if ( cssExternal !== false ) {
993
- return cssExternal ;
981
+ resolvedJsRedirect : {
982
+ path : jsRedirectPath ,
983
+ extension : jsRedirectExtension ,
984
+ } ,
985
+ config : {
986
+ output : {
987
+ externals : [
988
+ async ( data , callback ) => {
989
+ const { request, getResolve, context, contextInfo } = data ;
990
+ if ( ! request || ! getResolve || ! context || ! contextInfo ) {
991
+ return callback ( ) ;
992
+ }
993
+
994
+ if ( ! resolver ) {
995
+ resolver = ( await getResolve ( ) ) as RspackResolver ;
994
996
}
995
997
996
- if ( request [ 0 ] === '.' ) {
997
- const ext = extname ( request ) ;
998
+ // Issuer is not empty string when the module is imported by another module.
999
+ // Prevent from externalizing entry modules here.
1000
+ if ( contextInfo . issuer ) {
1001
+ let resolvedRequest : string = request ;
1002
+
1003
+ const cssExternal = cssExternalHandler (
1004
+ resolvedRequest ,
1005
+ callback ,
1006
+ jsExtension ,
1007
+ cssModulesAuto ,
1008
+ isStyleRedirected ,
1009
+ ) ;
1010
+
1011
+ if ( cssExternal !== false ) {
1012
+ return cssExternal ;
1013
+ }
1014
+
1015
+ if ( jsRedirectPath ) {
1016
+ try {
1017
+ resolvedRequest = await resolver ( context , resolvedRequest ) ;
1018
+ } catch ( e ) {
1019
+ // Do nothing, fallthrough to other external matches.
1020
+ logger . debug (
1021
+ `Failed to resolve ${ resolvedRequest } with resolver` ,
1022
+ ) ;
1023
+ }
1024
+
1025
+ resolvedRequest = normalizeSlash (
1026
+ path . relative (
1027
+ path . dirname ( contextInfo . issuer ) ,
1028
+ resolvedRequest ,
1029
+ ) ,
1030
+ ) ;
1031
+
1032
+ // Requests that fall through here cannot be matched by any other externals config ahead.
1033
+ // Treat all these requests as relative import of source code. Node.js won't add the
1034
+ // leading './' to the relative path resolved by `path.relative`. So add manually it here.
1035
+ if ( resolvedRequest [ 0 ] !== '.' ) {
1036
+ resolvedRequest = `./${ resolvedRequest } ` ;
1037
+ }
1038
+ }
998
1039
999
- if ( ext ) {
1000
- if ( JS_EXTENSIONS_PATTERN . test ( request ) ) {
1001
- request = request . replace ( / \. [ ^ . ] + $ / , jsExtension ) ;
1040
+ // Node.js ECMAScript module loader does no extension searching.
1041
+ // Add a file extension according to autoExtension config
1042
+ // when data.request is a relative path and do not have an extension.
1043
+ // If data.request already have an extension, we replace it with new extension
1044
+ // This may result in a change in semantics,
1045
+ // user should use copy to keep origin file or use another separate entry to deal this
1046
+ if ( jsRedirectExtension ) {
1047
+ const ext = extname ( resolvedRequest ) ;
1048
+ if ( ext ) {
1049
+ if ( JS_EXTENSIONS_PATTERN . test ( resolvedRequest ) ) {
1050
+ resolvedRequest = resolvedRequest . replace (
1051
+ / \. [ ^ . ] + $ / ,
1052
+ jsExtension ,
1053
+ ) ;
1054
+ } else {
1055
+ // If it does not match jsExtensionsPattern, we should do nothing, eg: ./foo.png
1056
+ return callback ( ) ;
1057
+ }
1002
1058
} else {
1003
- // If it does not match jsExtensionsPattern, we should do nothing, eg: ./foo.png
1004
- return callback ( ) ;
1059
+ resolvedRequest = `${ resolvedRequest } ${ jsExtension } ` ;
1005
1060
}
1006
- } else {
1007
- // TODO: add redirect.extension option
1008
- request = `${ request } ${ jsExtension } ` ;
1009
1061
}
1062
+
1063
+ return callback ( undefined , resolvedRequest ) ;
1010
1064
}
1011
1065
1012
- return callback ( null , request ) ;
1013
- }
1014
- callback ( ) ;
1015
- } ,
1016
- ] ,
1066
+ callback ( ) ;
1067
+ } ,
1068
+ ] as Rspack . ExternalItem [ ] ,
1069
+ } ,
1017
1070
} ,
1018
1071
} ;
1019
1072
} ;
@@ -1054,17 +1107,15 @@ const composeDtsConfig = async (
1054
1107
} ;
1055
1108
1056
1109
const composeTargetConfig = (
1057
- target : RsbuildConfigOutputTarget ,
1110
+ userTarget : RsbuildConfigOutputTarget ,
1058
1111
format : Format ,
1059
1112
) : {
1060
1113
config : RsbuildConfig ;
1114
+ externalsConfig : RsbuildConfig ;
1061
1115
target : RsbuildConfigOutputTarget ;
1062
1116
} => {
1063
- let defaultTarget = target ;
1064
- if ( ! defaultTarget ) {
1065
- defaultTarget = format === 'mf' ? 'web' : 'node' ;
1066
- }
1067
- switch ( defaultTarget ) {
1117
+ const target = userTarget ?? ( format === 'mf' ? 'web' : 'node' ) ;
1118
+ switch ( target ) {
1068
1119
case 'web' :
1069
1120
return {
1070
1121
config : {
@@ -1075,6 +1126,7 @@ const composeTargetConfig = (
1075
1126
} ,
1076
1127
} ,
1077
1128
target : 'web' ,
1129
+ externalsConfig : { } ,
1078
1130
} ;
1079
1131
case 'node' :
1080
1132
return {
@@ -1084,15 +1136,19 @@ const composeTargetConfig = (
1084
1136
target : [ 'node' ] ,
1085
1137
} ,
1086
1138
} ,
1139
+ output : {
1140
+ target : 'node' ,
1141
+ } ,
1142
+ } ,
1143
+ target : 'node' ,
1144
+ externalsConfig : {
1087
1145
output : {
1088
1146
// When output.target is 'node', Node.js's built-in will be treated as externals of type `node-commonjs`.
1089
1147
// Simply override the built-in modules to make them external.
1090
1148
// https://github.com/webpack/webpack/blob/dd44b206a9c50f4b4cb4d134e1a0bd0387b159a3/lib/node/NodeTargetPlugin.js#L81
1091
1149
externals : nodeBuiltInModules ,
1092
- target : 'node' ,
1093
1150
} ,
1094
1151
} ,
1095
- target : 'node' ,
1096
1152
} ;
1097
1153
// TODO: Support `neutral` target, however Rsbuild don't list it as an option in the target field.
1098
1154
// case 'neutral':
@@ -1104,7 +1160,7 @@ const composeTargetConfig = (
1104
1160
// },
1105
1161
// };
1106
1162
default :
1107
- throw new Error ( `Unsupported platform: ${ defaultTarget } ` ) ;
1163
+ throw new Error ( `Unsupported platform: ${ target } ` ) ;
1108
1164
}
1109
1165
} ;
1110
1166
@@ -1189,7 +1245,7 @@ async function composeLibRsbuildConfig(
1189
1245
externalHelpers ,
1190
1246
pkgJson ,
1191
1247
) ;
1192
- const externalsConfig = composeExternalsConfig (
1248
+ const userExternalsConfig = composeExternalsConfig (
1193
1249
format ! ,
1194
1250
config . output ?. externals ,
1195
1251
) ;
@@ -1198,16 +1254,17 @@ async function composeLibRsbuildConfig(
1198
1254
jsExtension,
1199
1255
dtsExtension,
1200
1256
} = composeAutoExtensionConfig ( config , autoExtension , pkgJson ) ;
1201
- const bundleConfig = composeBundleConfig (
1257
+ const { config : bundlelessExternalConfig } = composeBundlelessExternalConfig (
1202
1258
jsExtension ,
1203
1259
redirect ,
1204
1260
cssModulesAuto ,
1205
1261
bundle ,
1206
1262
) ;
1207
- const { config : targetConfig , target } = composeTargetConfig (
1208
- config . output ?. target ,
1209
- format ! ,
1210
- ) ;
1263
+ const {
1264
+ config : targetConfig ,
1265
+ externalsConfig : targetExternalsConfig ,
1266
+ target,
1267
+ } = composeTargetConfig ( config . output ?. target , format ! ) ;
1211
1268
const syntaxConfig = composeSyntaxConfig ( target , config ?. syntax ) ;
1212
1269
const autoExternalConfig = composeAutoExternalConfig ( {
1213
1270
format : format ! ,
@@ -1231,7 +1288,7 @@ async function composeLibRsbuildConfig(
1231
1288
const externalsWarnConfig = composeExternalsWarnConfig (
1232
1289
format ! ,
1233
1290
autoExternalConfig ?. output ?. externals ,
1234
- externalsConfig ?. output ?. externals ,
1291
+ userExternalsConfig ?. output ?. externals ,
1235
1292
) ;
1236
1293
const minifyConfig = composeMinifyConfig ( config ) ;
1237
1294
const bannerFooterConfig = composeBannerFooterConfig ( banner , footer ) ;
@@ -1243,15 +1300,23 @@ async function composeLibRsbuildConfig(
1243
1300
return mergeRsbuildConfig (
1244
1301
formatConfig ,
1245
1302
shimsConfig ,
1303
+ syntaxConfig ,
1246
1304
externalHelpersConfig ,
1247
- // externalsWarnConfig should before other externals config
1248
- externalsWarnConfig ,
1249
- externalsConfig ,
1250
- autoExternalConfig ,
1251
1305
autoExtensionConfig ,
1252
- syntaxConfig ,
1253
- bundleConfig ,
1254
1306
targetConfig ,
1307
+ // #region Externals configs
1308
+ // The order of the externals config should come in the following order:
1309
+ // 1. `externalsWarnConfig` should come before other externals config to touch the externalized modules first.
1310
+ // 2. The externals config in `bundlelessExternalConfig` should present after other externals config as
1311
+ // it relies on other externals config to bail out the externalized modules first then resolve
1312
+ // the correct path for relative imports.
1313
+ // 3. `userExternalsConfig` should present later to override the externals config of the ahead ones.
1314
+ externalsWarnConfig ,
1315
+ autoExternalConfig ,
1316
+ targetExternalsConfig ,
1317
+ userExternalsConfig ,
1318
+ bundlelessExternalConfig ,
1319
+ // #endregion
1255
1320
entryConfig ,
1256
1321
cssConfig ,
1257
1322
assetConfig ,
0 commit comments