Skip to content

Commit 059c9de

Browse files
committed
js redirect
1 parent cf81ff4 commit 059c9de

File tree

27 files changed

+461
-79
lines changed

27 files changed

+461
-79
lines changed

packages/core/src/config.ts

Lines changed: 95 additions & 67 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@ import type {
3939
ExcludesFalse,
4040
Format,
4141
GetAsyncFunctionFromUnion,
42+
JsRedirect,
4243
LibConfig,
4344
LibOnlyConfig,
4445
PkgJson,
@@ -958,90 +959,112 @@ const composeEntryConfig = async (
958959
};
959960
};
960961

961-
const composeBundleConfig = (
962+
const composeBundlelessExternalConfig = (
962963
jsExtension: string,
963964
redirect: Redirect,
964965
cssModulesAuto: CssLoaderOptionsAuto,
965966
bundle: boolean,
966-
): RsbuildConfig => {
967-
if (bundle) return {};
967+
): {
968+
config: RsbuildConfig;
969+
resolvedJsRedirect?: DeepRequired<JsRedirect>;
970+
} => {
971+
if (bundle) return { config: {} };
968972

969-
const isStyleRedirect = redirect.style ?? true;
973+
const doesRedirectStyle = redirect.style ?? true;
974+
const jsRedirectPath = redirect.js?.path ?? true;
975+
const jsRedirectExtension = redirect.js?.extension ?? true;
970976

971977
type Resolver = GetAsyncFunctionFromUnion<
972978
ReturnType<NonNullable<Rspack.ExternalItemFunctionData['getResolve']>>
973979
>;
974980
let resolver: Resolver | undefined;
975981

976982
return {
977-
output: {
978-
externals: [
979-
async (data, callback) => {
980-
const { request, getResolve, context, contextInfo } = data;
981-
if (!request || !getResolve || !context || !contextInfo) {
982-
return callback();
983-
}
984-
985-
if (!resolver) {
986-
resolver = (await getResolve()) as Resolver;
987-
}
983+
resolvedJsRedirect: {
984+
path: jsRedirectPath,
985+
extension: jsRedirectExtension,
986+
},
987+
config: {
988+
output: {
989+
externals: [
990+
async (data, callback) => {
991+
const { request, getResolve, context, contextInfo } = data;
992+
if (!request || !getResolve || !context || !contextInfo) {
993+
return callback();
994+
}
988995

989-
// Issuer is not empty string when the module is imported by another module.
990-
// Prevent from externalizing entry modules here.
991-
if (contextInfo.issuer) {
992-
// Node.js ECMAScript module loader does no extension searching.
993-
// Add a file extension according to autoExtension config
994-
// when data.request is a relative path and do not have an extension.
995-
// If data.request already have an extension, we replace it with new extension
996-
// This may result in a change in semantics,
997-
// user should use copy to keep origin file or use another separate entry to deal this
998-
let resolvedRequest: string = request;
999-
1000-
const cssExternal = cssExternalHandler(
1001-
resolvedRequest,
1002-
callback,
1003-
jsExtension,
1004-
cssModulesAuto,
1005-
isStyleRedirect,
1006-
);
1007-
1008-
if (cssExternal !== false) {
1009-
return cssExternal;
996+
if (!resolver) {
997+
resolver = (await getResolve()) as Resolver;
1010998
}
1011999

1012-
if (resolvedRequest[0] === '.') {
1013-
const resolved = await resolver(context, resolvedRequest);
1014-
resolvedRequest = normalizeSlash(
1015-
path.relative(path.dirname(contextInfo.issuer), resolved),
1000+
// Issuer is not empty string when the module is imported by another module.
1001+
// Prevent from externalizing entry modules here.
1002+
if (contextInfo.issuer) {
1003+
let resolvedRequest: string = request;
1004+
1005+
const cssExternal = cssExternalHandler(
1006+
resolvedRequest,
1007+
callback,
1008+
jsExtension,
1009+
cssModulesAuto,
1010+
doesRedirectStyle,
10161011
);
10171012

1018-
if (resolvedRequest[0] !== '.') {
1019-
resolvedRequest = `./${resolvedRequest}`;
1013+
if (cssExternal !== false) {
1014+
return cssExternal;
10201015
}
10211016

1022-
const ext = extname(resolvedRequest);
1017+
// Node.js ECMAScript module loader does no extension searching.
1018+
// Add a file extension according to autoExtension config
1019+
// when data.request is a relative path and do not have an extension.
1020+
// If data.request already have an extension, we replace it with new extension
1021+
// This may result in a change in semantics,
1022+
// user should use copy to keep origin file or use another separate entry to deal this
1023+
1024+
if (jsRedirectPath) {
1025+
try {
1026+
resolvedRequest = await resolver(context, resolvedRequest);
1027+
} catch (e) {
1028+
// Do nothing, fallthrough to other external matches.
1029+
}
1030+
1031+
resolvedRequest = normalizeSlash(
1032+
path.relative(
1033+
path.dirname(contextInfo.issuer),
1034+
resolvedRequest,
1035+
),
1036+
);
10231037

1024-
if (ext) {
1025-
if (JS_EXTENSIONS_PATTERN.test(resolvedRequest)) {
1026-
resolvedRequest = resolvedRequest.replace(
1027-
/\.[^.]+$/,
1028-
jsExtension,
1029-
);
1038+
if (resolvedRequest[0] !== '.') {
1039+
resolvedRequest = `./${resolvedRequest}`;
1040+
}
1041+
}
1042+
1043+
if (jsRedirectExtension) {
1044+
const ext = extname(resolvedRequest);
1045+
if (ext) {
1046+
if (JS_EXTENSIONS_PATTERN.test(resolvedRequest)) {
1047+
resolvedRequest = resolvedRequest.replace(
1048+
/\.[^.]+$/,
1049+
jsExtension,
1050+
);
1051+
} else {
1052+
// If it does not match jsExtensionsPattern, we should do nothing, eg: ./foo.png
1053+
return callback();
1054+
}
10301055
} else {
1031-
// If it does not match jsExtensionsPattern, we should do nothing, eg: ./foo.png
1032-
return callback();
1056+
// TODO: add redirect.extension option
1057+
resolvedRequest = `${resolvedRequest}${jsExtension}`;
10331058
}
1034-
} else {
1035-
// TODO: add redirect.extension option
1036-
resolvedRequest = `${resolvedRequest}${jsExtension}`;
10371059
}
1060+
1061+
return callback(undefined, resolvedRequest);
10381062
}
10391063

1040-
return callback(undefined, resolvedRequest);
1041-
}
1042-
callback();
1043-
},
1044-
] as Rspack.ExternalItem[],
1064+
callback();
1065+
},
1066+
] as Rspack.ExternalItem[],
1067+
},
10451068
},
10461069
};
10471070
};
@@ -1217,7 +1240,7 @@ async function composeLibRsbuildConfig(
12171240
externalHelpers,
12181241
pkgJson,
12191242
);
1220-
const externalsConfig = composeExternalsConfig(
1243+
const userExternalConfig = composeExternalsConfig(
12211244
format!,
12221245
config.output?.externals,
12231246
);
@@ -1226,7 +1249,7 @@ async function composeLibRsbuildConfig(
12261249
jsExtension,
12271250
dtsExtension,
12281251
} = composeAutoExtensionConfig(config, autoExtension, pkgJson);
1229-
const bundleConfig = composeBundleConfig(
1252+
const { config: bundlelessExternalConfig } = composeBundlelessExternalConfig(
12301253
jsExtension,
12311254
redirect,
12321255
cssModulesAuto,
@@ -1257,7 +1280,7 @@ async function composeLibRsbuildConfig(
12571280
const externalsWarnConfig = composeExternalsWarnConfig(
12581281
format!,
12591282
autoExternalConfig?.output?.externals,
1260-
externalsConfig?.output?.externals,
1283+
userExternalConfig?.output?.externals,
12611284
);
12621285
const minifyConfig = composeMinifyConfig(config);
12631286
const bannerFooterConfig = composeBannerFooterConfig(banner, footer);
@@ -1269,15 +1292,20 @@ async function composeLibRsbuildConfig(
12691292
return mergeRsbuildConfig(
12701293
formatConfig,
12711294
shimsConfig,
1295+
syntaxConfig,
12721296
externalHelpersConfig,
1273-
// externalsWarnConfig should before other externals config
1297+
autoExtensionConfig,
1298+
1299+
// `externalsWarnConfig` should before other externals config.
12741300
externalsWarnConfig,
1275-
externalsConfig,
12761301
autoExternalConfig,
1277-
autoExtensionConfig,
1278-
syntaxConfig,
1279-
bundleConfig,
12801302
targetConfig,
1303+
// The externals config in `bundleConfig` should present after all externals config as
1304+
// it relies on other externals config to bail out the externalized modules first then resolve
1305+
// the correct path for relative imports.
1306+
userExternalConfig,
1307+
bundlelessExternalConfig,
1308+
12811309
entryConfig,
12821310
cssConfig,
12831311
entryChunkConfig,

packages/core/src/types/config/index.ts renamed to packages/core/src/types/config.ts

Lines changed: 24 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -74,12 +74,33 @@ export type Shims = {
7474
};
7575
};
7676

77+
export type JsRedirect = {
78+
/**
79+
* Whether to automatically redirect the import paths of JavaScript output files,
80+
* compilerOptions.paths in tsconfig.json will be applied by default.
81+
* @defaultValue `true`
82+
*/
83+
path?: boolean;
84+
/**
85+
* Whether to automatically add the file extension based on the JavaScript output files.
86+
* @defaultValue `true`
87+
*/
88+
extension?: boolean;
89+
};
90+
91+
// @ts-expect-error TODO: support dts redirect in the future
92+
type DtsRedirect = {
93+
path?: boolean;
94+
extension?: boolean;
95+
};
96+
7797
export type Redirect = {
78-
// TODO: support other redirects
79-
// alias?: boolean;
98+
/** Controls the redirect of the import paths of JavaScript output files. */
99+
js?: JsRedirect;
80100
style?: boolean;
101+
// TODO: support other redirects
81102
// asset?: boolean;
82-
// autoExtension?: boolean;
103+
// dts?: DtsRedirect;
83104
};
84105

85106
export interface LibConfig extends RsbuildConfig {

packages/core/tests/__snapshots__/config.test.ts.snap

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -173,7 +173,6 @@ exports[`Should compose create Rsbuild config correctly > Merge Rsbuild config 1
173173
"experiments": {
174174
"outputModule": true,
175175
},
176-
"externalsType": "module-import",
177176
"module": {
178177
"parser": {
179178
"javascript": {
@@ -210,6 +209,9 @@ exports[`Should compose create Rsbuild config correctly > Merge Rsbuild config 1
210209
"node",
211210
],
212211
},
212+
{
213+
"externalsType": "module-import",
214+
},
213215
{
214216
"plugins": [
215217
EntryChunkPlugin {
@@ -412,7 +414,6 @@ exports[`Should compose create Rsbuild config correctly > Merge Rsbuild config 1
412414
},
413415
},
414416
{
415-
"externalsType": "commonjs-import",
416417
"module": {
417418
"parser": {
418419
"javascript": {
@@ -441,6 +442,9 @@ exports[`Should compose create Rsbuild config correctly > Merge Rsbuild config 1
441442
"node",
442443
],
443444
},
445+
{
446+
"externalsType": "commonjs-import",
447+
},
444448
{
445449
"plugins": [
446450
EntryChunkPlugin {
@@ -635,7 +639,6 @@ exports[`Should compose create Rsbuild config correctly > Merge Rsbuild config 1
635639
},
636640
},
637641
{
638-
"externalsType": "umd",
639642
"module": {
640643
"parser": {
641644
"javascript": {
@@ -659,6 +662,9 @@ exports[`Should compose create Rsbuild config correctly > Merge Rsbuild config 1
659662
"node",
660663
],
661664
},
665+
{
666+
"externalsType": "umd",
667+
},
662668
{
663669
"plugins": [
664670
EntryChunkPlugin {

0 commit comments

Comments
 (0)