Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
180 changes: 5 additions & 175 deletions packages/core/src/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -71,159 +71,6 @@ import {
} from './utils/syntax';
import { loadTsconfig } from './utils/tsconfig';

// Match logic is derived from https://github.com/webpack/webpack/blob/94aba382eccf3de1004d235045d4462918dfdbb7/lib/ExternalModuleFactoryPlugin.js#L89-L158
const handleMatchedExternal = (
value: string | string[] | boolean | Record<string, string | string[]>,
request: string,
): boolean => {
if (typeof value === 'boolean') {
return value;
}

if (typeof value === 'string') {
const [first, second] = value.split(' ');
const hasType = !!second;
const _request = second ? second : first;

// Don't need to warn explicit declared external type.
if (!hasType) {
return request === _request;
}

return false;
}

if (Array.isArray(value)) {
return handleMatchedExternal(value[0] ?? '', request);
}

if (typeof value === 'object') {
return false;
}

return false;
};

const composeExternalsWarnConfig = (
format: Format,
...externalsArray: NonNullable<EnvironmentConfig['output']>['externals'][]
): EnvironmentConfig => {
if (format !== 'esm') {
return {};
}

const externals: NonNullable<EnvironmentConfig['output']>['externals'] = [];
for (const e of externalsArray.filter(Boolean)) {
if (Array.isArray(e)) {
externals.push(...e);
} else {
// @ts-expect-error
externals.push(e);
}
}

// Match logic is derived from https://github.com/webpack/webpack/blob/94aba382eccf3de1004d235045d4462918dfdbb7/lib/ExternalModuleFactoryPlugin.js#L166-L293.
const matchUserExternals = (
externals: NonNullable<EnvironmentConfig['output']>['externals'],
request: string,
callback: (matched: boolean, shouldWarn?: boolean) => void,
) => {
// string
if (typeof externals === 'string') {
if (handleMatchedExternal(externals, request)) {
callback(true, true);
return;
}
}
// array
if (Array.isArray(externals)) {
let i = 0;
const next = () => {
let asyncFlag: boolean;
const handleExternalsAndCallback = (
matched: boolean,
shouldWarn?: boolean,
) => {
if (!matched) {
if (asyncFlag) {
asyncFlag = false;
return;
}
next();
return;
}

callback(matched, shouldWarn);
};

do {
asyncFlag = true;
if (i >= externals.length) {
callback(false);
return;
}
matchUserExternals(
externals[i++],
request,
handleExternalsAndCallback,
);
} while (!asyncFlag);
asyncFlag = false;
};

next();
return;
}
// regexp
if (externals instanceof RegExp) {
if (externals.test(request)) {
callback(true, true);
return;
}
}
// function
else if (typeof externals === 'function') {
// TODO: Support function
}
// object
else if (typeof externals === 'object') {
if (Object.hasOwn(externals, request)) {
if (handleMatchedExternal(externals[request]!, request)) {
callback(true, true);
} else {
callback(true);
}
return;
}
}

callback(false);
};

return {
output: {
externals: [
({ request, dependencyType, contextInfo }: any, callback: any) => {
let shouldWarn = false;
const _callback = (_matched: boolean, _shouldWarn?: boolean) => {
if (_shouldWarn) {
shouldWarn = true;
}
};

if (contextInfo.issuer && dependencyType === 'commonjs') {
matchUserExternals(externals, request, _callback);
if (shouldWarn) {
logger.warn(composeModuleImportWarn(request, contextInfo.issuer));
}
}
callback();
},
],
},
};
};

const getAutoExternalDefaultValue = (
format: Format,
autoExternal?: AutoExternal,
Expand Down Expand Up @@ -511,7 +358,6 @@ export async function createConstantRsbuildConfig(): Promise<EnvironmentConfig>

const composeFormatConfig = ({
format,
target,
bundle = true,
umdName,
pkgJson,
Expand All @@ -520,7 +366,6 @@ const composeFormatConfig = ({
sourceEntry,
}: {
format: Format;
target: RsbuildConfigOutputTarget;
pkgJson: PkgJson;
bundle?: boolean;
umdName?: Rspack.LibraryName;
Expand Down Expand Up @@ -552,7 +397,6 @@ const composeFormatConfig = ({
new rspack.experiments.RslibPlugin({
interceptApiPlugin: true,
forceNodeShims: enabledShims.esm.__dirname || enabledShims.esm.__filename,
autoCjsNodeBuiltin: format === 'esm' && target === 'node',
}),
].filter(Boolean);

Expand Down Expand Up @@ -940,13 +784,6 @@ const composeShimsConfig = (
return { rsbuildConfig, enabledShims };
};

export const composeModuleImportWarn = (
request: string,
issuer: string,
): string => {
return `The externalized commonjs request ${color.green(`"${request}"`)} from ${color.green(issuer)} will use ${color.blue('"module"')} external type in ESM format. If you want to specify other external type, consider setting the request and type with ${color.blue('"output.externals"')}.`;
};

const composeExternalsConfig = (
format: Format,
externals: NonNullable<EnvironmentConfig['output']>['externals'],
Expand All @@ -956,7 +793,7 @@ const composeExternalsConfig = (
// should to be unified and merged together in the future.

const externalsTypeMap: Record<Format, Rspack.ExternalsType> = {
esm: 'module-import',
esm: 'modern-module',

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Gate modern-module default to Node targets

For format: 'esm' with output.target: 'web', this default now applies modern-module to auto/user externals as well; the current patch’s own docs (website/docs/en/config/rsbuild/output.mdx line 90) confirm that CommonJS require() externals on non-Node targets are emitted as bare require() calls. That regresses web-target ESM bundles that externalize a dependency used via require() (including auto-external dependencies), because browsers do not provide require, whereas the previous module-import fallback emitted ESM imports for these cases. Please keep module-import for web targets or make this mapping target-aware.

Useful? React with 👍 / 👎.

cjs: 'commonjs-import',
umd: 'umd',
// If use 'var', when projects import an external package like '@pkg', this will cause a syntax error such as 'var pkg = @pkg'.
Expand Down Expand Up @@ -1684,8 +1521,9 @@ const composeTargetConfig = (
target: 'node',
externalsConfig: {
output: {
// When output.target is 'node', Node.js's built-in will be treated as externals of type `node-commonjs`.
// Keep Node.js built-in modules externalized for all Node.js targets.
// The generated code follows the current format's externalsType,
// such as `modern-module` for ESM and `commonjs-import` for CJS.
// https://github.com/webpack/webpack/blob/dd44b206a9c50f4b4cb4d134e1a0bd0387b159a3/lib/node/NodeTargetPlugin.js#L81
externals: nodeBuiltInModules,
},
Expand Down Expand Up @@ -1784,7 +1622,6 @@ async function composeLibRsbuildConfig(
} = composeTargetConfig(config.output?.target, format);
const formatConfig = composeFormatConfig({
format,
target,
pkgJson: pkgJson!,
bundle,
umdName,
Expand Down Expand Up @@ -1855,11 +1692,6 @@ async function composeLibRsbuildConfig(
contextToWatch: outBase,
});
const dtsConfig = await composeDtsConfig(config, format, dtsExtension);
const externalsWarnConfig = composeExternalsWarnConfig(
format,
userExternalsConfig?.output?.externals,
autoExternalConfig?.output?.externals,
);
const minifyConfig = composeMinifyConfig(config);
const bannerFooterConfig = composeBannerFooterConfig(banner, footer);
const decoratorsConfig = composeDecoratorsConfig(
Expand All @@ -1879,12 +1711,10 @@ async function composeLibRsbuildConfig(
targetConfig,
// #region Externals configs
// The order of the externals config should come in the following order:
// 1. `externalsWarnConfig` should come before other externals config to touch the externalized modules first.
// 2. `userExternalsConfig` should present at first to takes effect earlier than others.
// 3. The externals config in `bundlelessExternalConfig` should present after other externals config as
// 1. `userExternalsConfig` should present at first to takes effect earlier than others.
// 2. The externals config in `bundlelessExternalConfig` should present after other externals config as
// it relies on other externals config to bail out the externalized modules first then resolve
// the correct path for relative imports.
externalsWarnConfig,
userExternalsConfig,
autoExternalConfig,
targetExternalsConfig,
Expand Down
22 changes: 6 additions & 16 deletions packages/core/tests/__snapshots__/config.test.ts.snap
Original file line number Diff line number Diff line change
Expand Up @@ -321,7 +321,6 @@ exports[`Should compose create Rsbuild config correctly > Merge Rsbuild config i
]
},
externals: [
function () { /* omitted long function */ },
/^@rsbuild\\/core($|\\/|\\\\)/,
/^rsbuild-plugin-dts($|\\/|\\\\)/,
/^@microsoft\\/api-extractor($|\\/|\\\\)/,
Expand Down Expand Up @@ -1036,8 +1035,7 @@ exports[`Should compose create Rsbuild config correctly > Merge Rsbuild config i
_args: [
{
interceptApiPlugin: true,
forceNodeShims: false,
autoCjsNodeBuiltin: true
forceNodeShims: false
}
]
},
Expand All @@ -1059,7 +1057,7 @@ exports[`Should compose create Rsbuild config correctly > Merge Rsbuild config i
externalsPresets: {
node: false
},
externalsType: 'module-import'
externalsType: 'modern-module'
}"
`;

Expand Down Expand Up @@ -1801,8 +1799,7 @@ exports[`Should compose create Rsbuild config correctly > Merge Rsbuild config i
_args: [
{
interceptApiPlugin: true,
forceNodeShims: false,
autoCjsNodeBuiltin: false
forceNodeShims: false
}
]
},
Expand Down Expand Up @@ -2471,8 +2468,7 @@ exports[`Should compose create Rsbuild config correctly > Merge Rsbuild config i
_args: [
{
interceptApiPlugin: true,
forceNodeShims: false,
autoCjsNodeBuiltin: false
forceNodeShims: false
}
]
},
Expand Down Expand Up @@ -3144,8 +3140,7 @@ exports[`Should compose create Rsbuild config correctly > Merge Rsbuild config i
_args: [
{
interceptApiPlugin: true,
forceNodeShims: false,
autoCjsNodeBuiltin: false
forceNodeShims: false
}
]
},
Expand Down Expand Up @@ -3797,7 +3792,6 @@ exports[`Should compose create Rsbuild config correctly > Merge Rsbuild config i
"jsAsync": "./",
},
"externals": [
[Function],
/\\^@rsbuild\\\\/core\\(\\$\\|\\\\/\\|\\\\\\\\\\)/,
/\\^rsbuild-plugin-dts\\(\\$\\|\\\\/\\|\\\\\\\\\\)/,
/\\^@microsoft\\\\/api-extractor\\(\\$\\|\\\\/\\|\\\\\\\\\\)/,
Expand Down Expand Up @@ -4004,7 +3998,6 @@ exports[`Should compose create Rsbuild config correctly > Merge Rsbuild config i
RslibPlugin {
"_args": [
{
"autoCjsNodeBuiltin": true,
"forceNodeShims": false,
"interceptApiPlugin": true,
},
Expand All @@ -4029,7 +4022,7 @@ exports[`Should compose create Rsbuild config correctly > Merge Rsbuild config i
],
},
{
"externalsType": "module-import",
"externalsType": "modern-module",
},
{
"plugins": [
Expand Down Expand Up @@ -4282,7 +4275,6 @@ exports[`Should compose create Rsbuild config correctly > Merge Rsbuild config i
RslibPlugin {
"_args": [
{
"autoCjsNodeBuiltin": false,
"forceNodeShims": false,
"interceptApiPlugin": true,
},
Expand Down Expand Up @@ -4519,7 +4511,6 @@ exports[`Should compose create Rsbuild config correctly > Merge Rsbuild config i
RslibPlugin {
"_args": [
{
"autoCjsNodeBuiltin": false,
"forceNodeShims": false,
"interceptApiPlugin": true,
},
Expand Down Expand Up @@ -4760,7 +4751,6 @@ exports[`Should compose create Rsbuild config correctly > Merge Rsbuild config i
RslibPlugin {
"_args": [
{
"autoCjsNodeBuiltin": false,
"forceNodeShims": false,
"interceptApiPlugin": true,
},
Expand Down
15 changes: 5 additions & 10 deletions pnpm-lock.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading
Loading