Skip to content

Commit a69fe4d

Browse files
committed
js redirect
1 parent 91ab67b commit a69fe4d

File tree

27 files changed

+423
-79
lines changed

27 files changed

+423
-79
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,
@@ -942,90 +943,112 @@ const composeEntryConfig = async (
942943
};
943944
};
944945

945-
const composeBundleConfig = (
946+
const composeBundlelessExternalConfig = (
946947
jsExtension: string,
947948
redirect: Redirect,
948949
cssModulesAuto: CssLoaderOptionsAuto,
949950
bundle: boolean,
950-
): RsbuildConfig => {
951-
if (bundle) return {};
951+
): {
952+
config: RsbuildConfig;
953+
resolvedJsRedirect?: DeepRequired<JsRedirect>;
954+
} => {
955+
if (bundle) return { config: {} };
952956

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;
954960

955961
type Resolver = GetAsyncFunctionFromUnion<
956962
ReturnType<NonNullable<Rspack.ExternalItemFunctionData['getResolve']>>
957963
>;
958964
let resolver: Resolver | undefined;
959965

960966
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+
}
972979

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;
994982
}
995983

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,
1000995
);
1001996

1002-
if (resolvedRequest[0] !== '.') {
1003-
resolvedRequest = `./${resolvedRequest}`;
997+
if (cssExternal !== false) {
998+
return cssExternal;
1004999
}
10051000

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+
);
10071021

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+
}
10141039
} 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}`;
10171042
}
1018-
} else {
1019-
// TODO: add redirect.extension option
1020-
resolvedRequest = `${resolvedRequest}${jsExtension}`;
10211043
}
1044+
1045+
return callback(undefined, resolvedRequest);
10221046
}
10231047

1024-
return callback(undefined, resolvedRequest);
1025-
}
1026-
callback();
1027-
},
1028-
] as Rspack.ExternalItem[],
1048+
callback();
1049+
},
1050+
] as Rspack.ExternalItem[],
1051+
},
10291052
},
10301053
};
10311054
};
@@ -1198,7 +1221,7 @@ async function composeLibRsbuildConfig(config: LibConfig) {
11981221
externalHelpers,
11991222
pkgJson,
12001223
);
1201-
const externalsConfig = composeExternalsConfig(
1224+
const userExternalConfig = composeExternalsConfig(
12021225
format!,
12031226
config.output?.externals,
12041227
);
@@ -1207,7 +1230,7 @@ async function composeLibRsbuildConfig(config: LibConfig) {
12071230
jsExtension,
12081231
dtsExtension,
12091232
} = composeAutoExtensionConfig(config, autoExtension, pkgJson);
1210-
const bundleConfig = composeBundleConfig(
1233+
const { config: bundlelessExternalConfig } = composeBundlelessExternalConfig(
12111234
jsExtension,
12121235
redirect,
12131236
cssModulesAuto,
@@ -1237,7 +1260,7 @@ async function composeLibRsbuildConfig(config: LibConfig) {
12371260
const externalsWarnConfig = composeExternalsWarnConfig(
12381261
format!,
12391262
autoExternalConfig?.output?.externals,
1240-
externalsConfig?.output?.externals,
1263+
userExternalConfig?.output?.externals,
12411264
);
12421265
const minifyConfig = composeMinifyConfig(config);
12431266
const bannerFooterConfig = composeBannerFooterConfig(banner, footer);
@@ -1249,15 +1272,20 @@ async function composeLibRsbuildConfig(config: LibConfig) {
12491272
return mergeRsbuildConfig(
12501273
formatConfig,
12511274
shimsConfig,
1275+
syntaxConfig,
12521276
externalHelpersConfig,
1253-
// externalsWarnConfig should before other externals config
1277+
autoExtensionConfig,
1278+
1279+
// `externalsWarnConfig` should before other externals config.
12541280
externalsWarnConfig,
1255-
externalsConfig,
12561281
autoExternalConfig,
1257-
autoExtensionConfig,
1258-
syntaxConfig,
1259-
bundleConfig,
12601282
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+
12611289
entryConfig,
12621290
cssConfig,
12631291
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 {

pnpm-lock.yaml

Lines changed: 11 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

scripts/dictionary.txt

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,7 @@ jfif
5151
jiti
5252
jscpuprofile
5353
jsesc
54+
jsxs
5455
koppers
5556
lightningcss
5657
liyincode
@@ -135,10 +136,10 @@ unpatch
135136
unplugin
136137
unpredictibly
137138
unshift
139+
unstubAllEnvs
138140
upath
139141
vitest
140142
vnode
141143
watchpack
142144
webm
143145
webp
144-
unstubAllEnvs
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
import { value } from '../constant';
2+
3+
export const bar = 'bar' + value;
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
export const value = 1;
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
export const foo = 'foo';
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
import lodash from 'lodash';
2+
3+
import { bar as bar2 } from '@/bar';
4+
import { foo as foo2 } from '@/foo';
5+
import { bar } from './bar';
6+
import { foo } from './foo.ts';
7+
8+
export default function (): string {
9+
return lodash.toUpper(foo + bar + foo2 + bar2);
10+
}

0 commit comments

Comments
 (0)