Skip to content

Commit 06ac05a

Browse files
committed
js redirect
1 parent a43e933 commit 06ac05a

File tree

27 files changed

+434
-128
lines changed

27 files changed

+434
-128
lines changed

packages/core/src/config.ts

Lines changed: 95 additions & 67 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@ import type {
3838
ExcludesFalse,
3939
Format,
4040
GetAsyncFunctionFromUnion,
41+
JsRedirect,
4142
LibConfig,
4243
LibOnlyConfig,
4344
PkgJson,
@@ -956,90 +957,112 @@ const composeEntryConfig = async (
956957
};
957958
};
958959

959-
const composeBundleConfig = (
960+
const composeBundlelessExternalConfig = (
960961
jsExtension: string,
961962
redirect: Redirect,
962963
cssModulesAuto: CssLoaderOptionsAuto,
963964
bundle: boolean,
964-
): RsbuildConfig => {
965-
if (bundle) return {};
965+
): {
966+
config: RsbuildConfig;
967+
resolvedJsRedirect?: DeepRequired<JsRedirect>;
968+
} => {
969+
if (bundle) return { config: {} };
966970

967-
const isStyleRedirect = redirect.style ?? true;
971+
const doesRedirectStyle = redirect.style ?? true;
972+
const jsRedirectPath = redirect.js?.path ?? true;
973+
const jsRedirectExtension = redirect.js?.extension ?? true;
968974

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

974980
return {
975-
output: {
976-
externals: [
977-
async (data, callback) => {
978-
const { request, getResolve, context, contextInfo } = data;
979-
if (!request || !getResolve || !context || !contextInfo) {
980-
return callback();
981-
}
982-
983-
if (!resolver) {
984-
resolver = (await getResolve()) as Resolver;
985-
}
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+
}
986993

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

1010-
if (resolvedRequest[0] === '.') {
1011-
const resolved = await resolver(context, resolvedRequest);
1012-
resolvedRequest = normalizeSlash(
1013-
path.relative(path.dirname(contextInfo.issuer), resolved),
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+
doesRedirectStyle,
10141009
);
10151010

1016-
if (resolvedRequest[0] !== '.') {
1017-
resolvedRequest = `./${resolvedRequest}`;
1011+
if (cssExternal !== false) {
1012+
return cssExternal;
10181013
}
10191014

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

1022-
if (ext) {
1023-
if (JS_EXTENSIONS_PATTERN.test(resolvedRequest)) {
1024-
resolvedRequest = resolvedRequest.replace(
1025-
/\.[^.]+$/,
1026-
jsExtension,
1027-
);
1036+
if (resolvedRequest[0] !== '.') {
1037+
resolvedRequest = `./${resolvedRequest}`;
1038+
}
1039+
}
1040+
1041+
if (jsRedirectExtension) {
1042+
const ext = extname(resolvedRequest);
1043+
if (ext) {
1044+
if (JS_EXTENSIONS_PATTERN.test(resolvedRequest)) {
1045+
resolvedRequest = resolvedRequest.replace(
1046+
/\.[^.]+$/,
1047+
jsExtension,
1048+
);
1049+
} else {
1050+
// If it does not match jsExtensionsPattern, we should do nothing, eg: ./foo.png
1051+
return callback();
1052+
}
10281053
} else {
1029-
// If it does not match jsExtensionsPattern, we should do nothing, eg: ./foo.png
1030-
return callback();
1054+
// TODO: add redirect.extension option
1055+
resolvedRequest = `${resolvedRequest}${jsExtension}`;
10311056
}
1032-
} else {
1033-
// TODO: add redirect.extension option
1034-
resolvedRequest = `${resolvedRequest}${jsExtension}`;
10351057
}
1058+
1059+
return callback(undefined, resolvedRequest);
10361060
}
10371061

1038-
return callback(undefined, resolvedRequest);
1039-
}
1040-
callback();
1041-
},
1042-
] as Rspack.ExternalItem[],
1062+
callback();
1063+
},
1064+
] as Rspack.ExternalItem[],
1065+
},
10431066
},
10441067
};
10451068
};
@@ -1212,7 +1235,7 @@ async function composeLibRsbuildConfig(config: LibConfig) {
12121235
externalHelpers,
12131236
pkgJson,
12141237
);
1215-
const externalsConfig = composeExternalsConfig(
1238+
const userExternalConfig = composeExternalsConfig(
12161239
format!,
12171240
config.output?.externals,
12181241
);
@@ -1221,7 +1244,7 @@ async function composeLibRsbuildConfig(config: LibConfig) {
12211244
jsExtension,
12221245
dtsExtension,
12231246
} = composeAutoExtensionConfig(config, autoExtension, pkgJson);
1224-
const bundleConfig = composeBundleConfig(
1247+
const { config: bundlelessExternalConfig } = composeBundlelessExternalConfig(
12251248
jsExtension,
12261249
redirect,
12271250
cssModulesAuto,
@@ -1252,7 +1275,7 @@ async function composeLibRsbuildConfig(config: LibConfig) {
12521275
const externalsWarnConfig = composeExternalsWarnConfig(
12531276
format!,
12541277
autoExternalConfig?.output?.externals,
1255-
externalsConfig?.output?.externals,
1278+
userExternalConfig?.output?.externals,
12561279
);
12571280
const minifyConfig = composeMinifyConfig(config);
12581281
const bannerFooterConfig = composeBannerFooterConfig(banner, footer);
@@ -1264,15 +1287,20 @@ async function composeLibRsbuildConfig(config: LibConfig) {
12641287
return mergeRsbuildConfig(
12651288
formatConfig,
12661289
shimsConfig,
1290+
syntaxConfig,
12671291
externalHelpersConfig,
1268-
// externalsWarnConfig should before other externals config
1292+
autoExtensionConfig,
1293+
1294+
// `externalsWarnConfig` should before other externals config.
12691295
externalsWarnConfig,
1270-
externalsConfig,
12711296
autoExternalConfig,
1272-
autoExtensionConfig,
1273-
syntaxConfig,
1274-
bundleConfig,
12751297
targetConfig,
1298+
// The externals config in `bundleConfig` should present after all externals config as
1299+
// it relies on other externals config to bail out the externalized modules first then resolve
1300+
// the correct path for relative imports.
1301+
userExternalConfig,
1302+
bundlelessExternalConfig,
1303+
12761304
entryConfig,
12771305
cssConfig,
12781306
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
@@ -172,7 +172,6 @@ exports[`Should compose create Rsbuild config correctly > Merge Rsbuild config 1
172172
"experiments": {
173173
"outputModule": true,
174174
},
175-
"externalsType": "module-import",
176175
"module": {
177176
"parser": {
178177
"javascript": {
@@ -209,6 +208,9 @@ exports[`Should compose create Rsbuild config correctly > Merge Rsbuild config 1
209208
"node",
210209
],
211210
},
211+
{
212+
"externalsType": "module-import",
213+
},
212214
{
213215
"plugins": [
214216
EntryChunkPlugin {
@@ -410,7 +412,6 @@ exports[`Should compose create Rsbuild config correctly > Merge Rsbuild config 1
410412
},
411413
},
412414
{
413-
"externalsType": "commonjs-import",
414415
"module": {
415416
"parser": {
416417
"javascript": {
@@ -439,6 +440,9 @@ exports[`Should compose create Rsbuild config correctly > Merge Rsbuild config 1
439440
"node",
440441
],
441442
},
443+
{
444+
"externalsType": "commonjs-import",
445+
},
442446
{
443447
"plugins": [
444448
EntryChunkPlugin {
@@ -632,7 +636,6 @@ exports[`Should compose create Rsbuild config correctly > Merge Rsbuild config 1
632636
},
633637
},
634638
{
635-
"externalsType": "umd",
636639
"module": {
637640
"parser": {
638641
"javascript": {
@@ -656,6 +659,9 @@ exports[`Should compose create Rsbuild config correctly > Merge Rsbuild config 1
656659
"node",
657660
],
658661
},
662+
{
663+
"externalsType": "umd",
664+
},
659665
{
660666
"plugins": [
661667
EntryChunkPlugin {

0 commit comments

Comments
 (0)