diff --git a/packages/core/src/cli/init.ts b/packages/core/src/cli/init.ts index 4f2ad165d..d6b998e95 100644 --- a/packages/core/src/cli/init.ts +++ b/packages/core/src/cli/init.ts @@ -92,9 +92,9 @@ export const applyCliOptions = ( if (options.dts !== undefined) lib.dts = options.dts; if (options.autoExtension !== undefined) lib.autoExtension = options.autoExtension; - if (options.autoExternal !== undefined) - lib.autoExternal = options.autoExternal; lib.output ??= {}; + if (options.autoExternal !== undefined) + lib.output.autoExternal = options.autoExternal; if (options.target !== undefined) lib.output.target = options.target as RsbuildConfigOutputTarget; if (options.minify !== undefined) lib.output.minify = options.minify; diff --git a/packages/core/src/config.ts b/packages/core/src/config.ts index 1b41cc936..5c6b41e66 100644 --- a/packages/core/src/config.ts +++ b/packages/core/src/config.ts @@ -28,7 +28,6 @@ import { composeExeConfig } from './exe'; import { composeEntryChunkConfig } from './plugins/EntryChunkPlugin'; import { pluginCjsShims, pluginEsmRequireShim } from './plugins/shims'; import type { - AutoExternal, BannerAndFooter, DeepRequired, ExcludesFalse, @@ -56,7 +55,6 @@ import { isDirectory, isEmptyObject, isIntermediateOutputFormat, - isObject, nodeBuiltInModules, normalizeSlash, omit, @@ -71,241 +69,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, - 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['externals'][] -): EnvironmentConfig => { - if (format !== 'esm') { - return {}; - } - - const externals: NonNullable['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['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, -): AutoExternal => { - return autoExternal ?? isIntermediateOutputFormat(format); -}; - -export const composeAutoExternalConfig = (options: { - bundle: boolean; - format: Format; - autoExternal?: AutoExternal; - pkgJson?: PkgJson; - userExternals?: NonNullable['externals']; -}): EnvironmentConfig => { - const { bundle, format, pkgJson, userExternals } = options; - - // If bundle is false, autoExternal will be disabled - if (!bundle) { - return {}; - } - - const autoExternal = getAutoExternalDefaultValue( - format, - options.autoExternal, - ); - - if (autoExternal === false) { - return {}; - } - - if (!pkgJson) { - logger.warn( - 'The `autoExternal` configuration will not be applied due to read package.json failed', - ); - return {}; - } - - // User externals configuration has higher priority than autoExternal - // eg: autoExternal: ['react'], user: output: { externals: { react: 'react-1' } } - // Only handle the case where the externals type is object, string / string[] does not need to be processed, other types are too complex. - const userExternalKeys = - userExternals && isObject(userExternals) ? Object.keys(userExternals) : []; - - const externalOptions = { - dependencies: true, - optionalDependencies: true, - peerDependencies: true, - devDependencies: false, - ...(autoExternal === true ? {} : autoExternal), - }; - - const externals = ( - [ - 'dependencies', - 'peerDependencies', - 'devDependencies', - 'optionalDependencies', - ] as const - ) - .reduce((prev, type) => { - if (externalOptions[type]) { - return pkgJson[type] ? prev.concat(Object.keys(pkgJson[type])) : prev; - } - return prev; - }, []) - .filter((name) => !userExternalKeys.includes(name)); - - const uniqueExternals = Array.from(new Set(externals)); - - return externals.length - ? { - output: { - externals: [ - // Exclude dependencies, e.g. `react`, `react/jsx-runtime` - ...uniqueExternals.map((dep) => new RegExp(`^${dep}($|\\/|\\\\)`)), - ...uniqueExternals, - ], - }, - } - : {}; -}; - export function composeMinifyConfig(config: LibConfig): EnvironmentConfig { const minify = config.output?.minify; const format = config.format; @@ -561,6 +324,7 @@ const composeFormatConfig = ({ return { output: { filenameHash: false, + ...(bundle && { autoExternal: true }), }, tools: { rspack: { @@ -605,6 +369,7 @@ const composeFormatConfig = ({ output: { module: false, filenameHash: false, + ...(bundle && { autoExternal: true }), }, tools: { rspack: { @@ -940,13 +705,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['externals'], @@ -1609,7 +1367,7 @@ const composeDtsConfig = async ( format: Format, dtsExtension: string, ): Promise => { - const { autoExternal, banner, footer, redirect } = libConfig; + const { banner, footer, redirect } = libConfig; let { dts } = libConfig; @@ -1632,7 +1390,10 @@ const composeDtsConfig = async ( build: dts?.build, abortOnError: dts?.abortOnError, dtsExtension: dts?.autoExtension ? dtsExtension : '.d.ts', - autoExternal: getAutoExternalDefaultValue(format, autoExternal), + autoExternal: + libConfig.output?.autoExternal ?? + libConfig.autoExternal ?? + isIntermediateOutputFormat(format), alias: dts?.alias, isolated: dts?.isolated, banner: banner?.dts, @@ -1765,13 +1526,13 @@ async function composeLibRsbuildConfig( banner = {}, footer = {}, autoExtension = true, - autoExternal, externalHelpers = false, redirect = {}, umdName, experiments = {}, } = config; const hasExe = Boolean(experiments.exe); + const { rsbuildConfig: bundleConfig } = composeBundleConfig(bundle); const { rsbuildConfig: shimsConfig, enabledShims } = composeShimsConfig( format, @@ -1834,13 +1595,6 @@ async function composeLibRsbuildConfig( outBase, ); const syntaxConfig = composeSyntaxConfig(target, config?.syntax); - const autoExternalConfig = composeAutoExternalConfig({ - bundle, - format, - autoExternal: hasExe ? false : autoExternal, - pkgJson, - userExternals, - }); const cssConfig = composeCssConfig( outBase, cssModulesAuto, @@ -1855,11 +1609,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( @@ -1879,14 +1628,11 @@ 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, bundlelessExternalConfig, // #endregion @@ -1931,11 +1677,36 @@ export async function composeCreateRsbuildConfig( } const libConfigPromises = libConfigsArray.map(async (libConfig, index) => { + if (libConfig.autoExternal !== undefined) { + logger.warn( + `${color.yellow('lib.autoExternal')} is deprecated, use ${color.yellow('output.autoExternal')} instead.`, + ); + } + + const normalizedLibConfig = + libConfig.autoExternal !== undefined && + libConfig.output?.autoExternal === undefined + ? { + ...libConfig, + output: { + ...libConfig.output, + autoExternal: libConfig.autoExternal, + }, + } + : libConfig; + const userConfig = mergeRsbuildConfig( sharedRsbuildConfig, - libConfig, + normalizedLibConfig, ); + // Exe bundles everything into a single binary, so disable automatic externals + // after user config is merged to prevent users from re-enabling it. + if (userConfig.experiments?.exe) { + userConfig.output ??= {}; + userConfig.output.autoExternal = false; + } + // Merge the configuration of each environment based on the shared Rsbuild // configuration and Lib configuration in the settings. const libRsbuildConfig = await composeLibRsbuildConfig( diff --git a/packages/core/src/types/config.ts b/packages/core/src/types/config.ts index 5ec2a373e..98e7b2577 100644 --- a/packages/core/src/types/config.ts +++ b/packages/core/src/types/config.ts @@ -400,6 +400,7 @@ export interface LibConfig extends EnvironmentConfig { autoExtension?: boolean; /** * Whether to automatically externalize dependencies of different dependency types and do not bundle them. + * @deprecated Use `output.autoExternal` instead. * @defaultValue `true` when {@link format} is `cjs` or `esm`, `false` when {@link format} is `umd` or `mf`. * @see {@link https://rslib.rs/config/lib/auto-external} */ @@ -506,6 +507,11 @@ interface RslibOutputConfig extends OutputConfig { * @see {@link https://rslib.rs/config/rsbuild/output#outputminify} */ minify?: OutputConfig['minify']; + /** + * @override + * @default `true` for ESM/CJS, `false` for UMD/MF/IIFE + */ + autoExternal?: OutputConfig['autoExternal']; } export interface RslibConfig extends RsbuildConfig { diff --git a/packages/core/tests/__snapshots__/config.test.ts.snap b/packages/core/tests/__snapshots__/config.test.ts.snap index c6259a780..2b4c97151 100644 --- a/packages/core/tests/__snapshots__/config.test.ts.snap +++ b/packages/core/tests/__snapshots__/config.test.ts.snap @@ -321,15 +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($|\\/|\\\\)/, - /^typescript($|\\/|\\\\)/, - '@rsbuild/core', - 'rsbuild-plugin-dts', - '@microsoft/api-extractor', - 'typescript', 'assert', 'assert/strict', 'async_hooks', @@ -385,7 +376,15 @@ exports[`Should compose create Rsbuild config correctly > Merge Rsbuild config i 'worker_threads', 'zlib', /^node:/, - 'pnpapi' + 'pnpapi', + /^@rsbuild\\/core($|\\/|\\\\)/, + /^rsbuild-plugin-dts($|\\/|\\\\)/, + /^@microsoft\\/api-extractor($|\\/|\\\\)/, + /^typescript($|\\/|\\\\)/, + '@rsbuild/core', + 'rsbuild-plugin-dts', + '@microsoft/api-extractor', + 'typescript' ], node: { __dirname: false, @@ -1092,14 +1091,6 @@ exports[`Should compose create Rsbuild config correctly > Merge Rsbuild config i ] }, externals: [ - /^@rsbuild\\/core($|\\/|\\\\)/, - /^rsbuild-plugin-dts($|\\/|\\\\)/, - /^@microsoft\\/api-extractor($|\\/|\\\\)/, - /^typescript($|\\/|\\\\)/, - '@rsbuild/core', - 'rsbuild-plugin-dts', - '@microsoft/api-extractor', - 'typescript', 'assert', 'assert/strict', 'async_hooks', @@ -1155,7 +1146,15 @@ exports[`Should compose create Rsbuild config correctly > Merge Rsbuild config i 'worker_threads', 'zlib', /^node:/, - 'pnpapi' + 'pnpapi', + /^@rsbuild\\/core($|\\/|\\\\)/, + /^rsbuild-plugin-dts($|\\/|\\\\)/, + /^@microsoft\\/api-extractor($|\\/|\\\\)/, + /^typescript($|\\/|\\\\)/, + '@rsbuild/core', + 'rsbuild-plugin-dts', + '@microsoft/api-extractor', + 'typescript' ], output: { devtoolModuleFilenameTemplate: '[relative-resource-path]', @@ -3789,6 +3788,7 @@ exports[`Should compose create Rsbuild config correctly > Merge Rsbuild config i "config": { "output": { "assetPrefix": "auto", + "autoExternal": true, "dataUriLimit": 0, "distPath": { "css": "./", @@ -3797,15 +3797,6 @@ exports[`Should compose create Rsbuild config correctly > Merge Rsbuild config i "jsAsync": "./", }, "externals": [ - [Function], - /\\^@rsbuild\\\\/core\\(\\$\\|\\\\/\\|\\\\\\\\\\)/, - /\\^rsbuild-plugin-dts\\(\\$\\|\\\\/\\|\\\\\\\\\\)/, - /\\^@microsoft\\\\/api-extractor\\(\\$\\|\\\\/\\|\\\\\\\\\\)/, - /\\^typescript\\(\\$\\|\\\\/\\|\\\\\\\\\\)/, - "@rsbuild/core", - "rsbuild-plugin-dts", - "@microsoft/api-extractor", - "typescript", "assert", "assert/strict", "async_hooks", @@ -4067,6 +4058,7 @@ exports[`Should compose create Rsbuild config correctly > Merge Rsbuild config i "config": { "output": { "assetPrefix": "auto", + "autoExternal": true, "dataUriLimit": 0, "distPath": { "css": "./", @@ -4075,14 +4067,6 @@ exports[`Should compose create Rsbuild config correctly > Merge Rsbuild config i "jsAsync": "./", }, "externals": [ - /\\^@rsbuild\\\\/core\\(\\$\\|\\\\/\\|\\\\\\\\\\)/, - /\\^rsbuild-plugin-dts\\(\\$\\|\\\\/\\|\\\\\\\\\\)/, - /\\^@microsoft\\\\/api-extractor\\(\\$\\|\\\\/\\|\\\\\\\\\\)/, - /\\^typescript\\(\\$\\|\\\\/\\|\\\\\\\\\\)/, - "@rsbuild/core", - "rsbuild-plugin-dts", - "@microsoft/api-extractor", - "typescript", "assert", "assert/strict", "async_hooks", diff --git a/packages/core/tests/cli.test.ts b/packages/core/tests/cli.test.ts index afa848b3e..e9a868bcf 100644 --- a/packages/core/tests/cli.test.ts +++ b/packages/core/tests/cli.test.ts @@ -130,7 +130,7 @@ describe('applyCliOptions', () => { expect(lib.bundle).toBe(false); expect(lib.dts).toBe(true); expect(lib.autoExtension).toBe(false); - expect(lib.autoExternal).toBe(false); + expect(lib.output?.autoExternal).toBe(false); expect(lib.syntax).toEqual(['node 18']); expect(lib.source?.tsconfigPath).toBe('./tsconfig.build.json'); expect(lib.source?.entry).toEqual({ diff --git a/packages/core/tests/config.test.ts b/packages/core/tests/config.test.ts index 6fc43a4d6..50f4b4aaf 100644 --- a/packages/core/tests/config.test.ts +++ b/packages/core/tests/config.test.ts @@ -16,7 +16,24 @@ import { } from '../src/mergeConfig'; import type { RslibConfig } from '../src/types/config'; -rs.mock('rslog'); +rs.mock('rslog', () => ({ + color: { + blue: (text: string) => text, + cyan: (text: string) => text, + dim: (text: string) => text, + gray: (text: string) => text, + green: (text: string) => text, + magenta: (text: string) => text, + yellow: (text: string) => text, + }, + logger: { + level: 'info', + debug: rs.fn(), + error: rs.fn(), + override: rs.fn(), + warn: rs.fn(), + }, +})); describe('Should load config file correctly', () => { test('Load config.js in cjs project', async () => { @@ -397,10 +414,11 @@ describe('CLI options', () => { "lib": [ { "autoExtension": false, - "autoExternal": false, + "autoExternal": true, "bundle": false, "dts": true, "output": { + "autoExternal": false, "cleanDistPath": true, "distPath": { "root": "build", @@ -563,6 +581,42 @@ describe('Should compose create Rsbuild config correctly', () => { } `); }); + + test('per-lib deprecated autoExternal should override shared output.autoExternal', async () => { + const rslibConfig: RslibConfig = { + output: { + autoExternal: false, + }, + lib: [ + { + format: 'esm', + autoExternal: true, + }, + ], + }; + + const [config] = await composeCreateRsbuildConfig(rslibConfig); + + expect(config?.config.output?.autoExternal).toBe(true); + }); + + test('output.autoExternal should override deprecated autoExternal in the same config', async () => { + const rslibConfig: RslibConfig = { + lib: [ + { + format: 'esm', + autoExternal: true, + output: { + autoExternal: false, + }, + }, + ], + }; + + const [config] = await composeCreateRsbuildConfig(rslibConfig); + + expect(config?.config.output?.autoExternal).toBe(false); + }); }); describe('runtimeChunk', () => { diff --git a/packages/core/tests/exe.test.ts b/packages/core/tests/exe.test.ts index ce02272b8..7931266cc 100644 --- a/packages/core/tests/exe.test.ts +++ b/packages/core/tests/exe.test.ts @@ -175,6 +175,36 @@ describe('experiments.exe', () => { ).resolves.toBeTruthy(); }); + test('should force disable output.autoExternal for executables', async () => { + const [config] = await withSupportedNodeRuntime(() => + composeTestRslibConfig({ + format: 'esm', + output: { + autoExternal: true, + }, + experiments: { + exe: true, + }, + }), + ); + + expect(config?.config.output?.autoExternal).toBe(false); + }); + + test('should force disable deprecated lib.autoExternal for executables', async () => { + const [config] = await withSupportedNodeRuntime(() => + composeTestRslibConfig({ + format: 'esm', + autoExternal: true, + experiments: { + exe: true, + }, + }), + ); + + expect(config?.config.output?.autoExternal).toBe(false); + }); + test('should resolve outputPath separately from fileName', () => { const resolved = resolveExecutableOutputPath({ environment: { diff --git a/packages/core/tests/external.test.ts b/packages/core/tests/external.test.ts deleted file mode 100644 index dacecd499..000000000 --- a/packages/core/tests/external.test.ts +++ /dev/null @@ -1,288 +0,0 @@ -import { describe, expect, it, rs } from '@rstest/core'; -import { composeAutoExternalConfig } from '../src/config'; - -rs.mock('rslog'); - -describe('should composeAutoExternalConfig correctly', () => { - it('autoExternal default value', () => { - const esmResult = composeAutoExternalConfig({ - bundle: true, - format: 'esm', - autoExternal: undefined, - pkgJson: { - name: 'esm', - dependencies: { - foo: '1.0.0', - }, - }, - }); - - const cjsResult = composeAutoExternalConfig({ - bundle: true, - format: 'cjs', - autoExternal: undefined, - pkgJson: { - name: 'cjs', - dependencies: { - foo: '1.0.0', - }, - }, - }); - - const umdResult = composeAutoExternalConfig({ - bundle: true, - format: 'umd', - autoExternal: undefined, - pkgJson: { - name: 'umd', - dependencies: { - foo: '1.0.0', - }, - }, - }); - - const mfResult = composeAutoExternalConfig({ - bundle: true, - format: 'mf', - autoExternal: undefined, - pkgJson: { - name: 'mf', - dependencies: { - foo: '1.0.0', - }, - }, - }); - - expect(esmResult).toMatchInlineSnapshot(` - { - "output": { - "externals": [ - /\\^foo\\(\\$\\|\\\\/\\|\\\\\\\\\\)/, - "foo", - ], - }, - } - `); - - expect(cjsResult).toMatchInlineSnapshot(` - { - "output": { - "externals": [ - /\\^foo\\(\\$\\|\\\\/\\|\\\\\\\\\\)/, - "foo", - ], - }, - } - `); - - expect(umdResult).toMatchInlineSnapshot('{}'); - expect(mfResult).toMatchInlineSnapshot('{}'); - }); - - it('autoExternal is true', () => { - const result = composeAutoExternalConfig({ - bundle: true, - format: 'esm', - autoExternal: true, - pkgJson: { - name: 'esm', - dependencies: { - foo: '1.0.0', - foo1: '1.0.0', - }, - devDependencies: { - bar: '1.0.0', - }, - peerDependencies: { - baz: '1.0.0', - }, - }, - }); - - expect(result).toEqual({ - output: { - externals: [ - /^foo($|\/|\\)/, - /^foo1($|\/|\\)/, - /^baz($|\/|\\)/, - 'foo', - 'foo1', - 'baz', - ], - }, - }); - }); - - it('autoExternal is true when format is umd or mf', () => { - const umdResult = composeAutoExternalConfig({ - bundle: true, - format: 'umd', - autoExternal: true, - pkgJson: { - name: 'umd', - dependencies: { - foo: '1.0.0', - }, - }, - }); - - expect(umdResult).toMatchInlineSnapshot(` - { - "output": { - "externals": [ - /\\^foo\\(\\$\\|\\\\/\\|\\\\\\\\\\)/, - "foo", - ], - }, - } - `); - - const mfResult = composeAutoExternalConfig({ - bundle: true, - format: 'mf', - autoExternal: true, - pkgJson: { - name: 'mf', - dependencies: { - foo: '1.0.0', - }, - }, - }); - - expect(mfResult).toMatchInlineSnapshot(` - { - "output": { - "externals": [ - /\\^foo\\(\\$\\|\\\\/\\|\\\\\\\\\\)/, - "foo", - ], - }, - } - `); - }); - - it('autoExternal will deduplication ', () => { - const result = composeAutoExternalConfig({ - bundle: true, - format: 'esm', - autoExternal: true, - pkgJson: { - name: 'esm', - dependencies: { - foo: '1.0.0', - foo1: '1.0.0', - }, - devDependencies: { - bar: '1.0.0', - }, - peerDependencies: { - baz: '1.0.0', - foo: '1.0.0', - foo1: '1.0.0', - }, - }, - }); - - expect(result).toEqual({ - output: { - externals: [ - /^foo($|\/|\\)/, - /^foo1($|\/|\\)/, - /^baz($|\/|\\)/, - 'foo', - 'foo1', - 'baz', - ], - }, - }); - }); - - it('autoExternal is object', () => { - const result = composeAutoExternalConfig({ - bundle: true, - format: 'esm', - autoExternal: { - peerDependencies: false, - devDependencies: true, - }, - pkgJson: { - name: 'esm', - dependencies: { - foo: '1.0.0', - }, - devDependencies: { - bar: '1.0.0', - }, - peerDependencies: { - baz: '1.0.0', - }, - }, - }); - - expect(result).toEqual({ - output: { - externals: [/^foo($|\/|\\)/, /^bar($|\/|\\)/, 'foo', 'bar'], - }, - }); - }); - - it('autoExternal is false', () => { - const result = composeAutoExternalConfig({ - bundle: true, - format: 'esm', - autoExternal: false, - pkgJson: { - name: 'esm', - dependencies: { - foo: '1.0.0', - }, - }, - }); - - expect(result).toEqual({}); - }); - - it('autoExternal with user externals object', () => { - const result = composeAutoExternalConfig({ - bundle: true, - format: 'esm', - autoExternal: true, - pkgJson: { - name: 'esm', - dependencies: { - foo: '1.0.0', - bar: '1.0.0', - }, - }, - userExternals: { - foo: 'foo-1', - }, - }); - - expect(result).toEqual({ - output: { - externals: [/^bar($|\/|\\)/, 'bar'], - }, - }); - }); - - it('read package.json failed', () => { - const result = composeAutoExternalConfig({ - bundle: true, - format: 'esm', - autoExternal: true, - }); - - expect(result).toEqual({}); - }); - - it('bundleless', () => { - const result = composeAutoExternalConfig({ - bundle: false, - format: 'esm', - autoExternal: true, - }); - - expect(result).toStrictEqual({}); - }); -}); diff --git a/tests/integration/auto-external/index.test.ts b/tests/integration/auto-external/index.test.ts index c4c58b74a..8117f4e93 100644 --- a/tests/integration/auto-external/index.test.ts +++ b/tests/integration/auto-external/index.test.ts @@ -1,9 +1,6 @@ import { join } from 'node:path'; -import { stripVTControlCharacters as stripAnsi } from 'node:util'; import { expect, test } from '@rstest/core'; -import { buildAndGetResults, proxyConsole } from 'test-helper'; - -import { composeModuleImportWarn } from '../../../packages/core/src/config'; +import { buildAndGetResults } from 'test-helper'; test('auto external default should works', async () => { const fixturePath = join(__dirname, 'default'); @@ -73,38 +70,3 @@ test('externals should overrides auto external', async () => { 'const external_react1_namespaceObject = require("react1");', ); }); - -test('should get warn when use require in ESM', async () => { - const { logs, restore } = proxyConsole(); - const fixturePath = join(__dirname, 'module-import-warn'); - const { entries } = await buildAndGetResults({ fixturePath }); - const logStrings = logs.map((log) => stripAnsi(log)); - - const shouldWarn = ['react', 'e2', 'e3', 'e5', 'e6', 'e7']; - const shouldNotWarn = ['e1', 'e4', 'e8', 'lodash/add', 'lodash/drop']; - const issuer = join(fixturePath, 'src/index.ts'); - - for (const item of shouldWarn) { - expect(entries.esm).toContain( - `import * as __rspack_external_${item} from "${item}"`, - ); - } - - for (const request of shouldWarn) { - expect( - logStrings.some((l) => - l.includes(stripAnsi(composeModuleImportWarn(request, issuer))), - ), - ).toBe(true); - } - - for (const request of shouldNotWarn) { - expect( - logStrings.some((l) => - l.includes(stripAnsi(composeModuleImportWarn(request, issuer))), - ), - ).toBe(false); - } - - restore(); -}); diff --git a/tests/integration/externals/index.test.ts b/tests/integration/externals/index.test.ts index a277aaf35..e3086893e 100644 --- a/tests/integration/externals/index.test.ts +++ b/tests/integration/externals/index.test.ts @@ -4,8 +4,6 @@ import { stripVTControlCharacters as stripAnsi } from 'node:util'; import { expect, test } from '@rstest/core'; import { buildAndGetResults, proxyConsole, queryContent } from 'test-helper'; -import { composeModuleImportWarn } from '../../../packages/core/src/config'; - test('should fail to build when `output.target` is not "node"', async () => { const fixturePath = join(__dirname, 'browser'); const { restore } = proxyConsole(); @@ -106,40 +104,6 @@ test('should remap node built-ins with user externals', async () => { expect(typeof esmOutput.loadPathSep).toBe('function'); }); -test('should get warn when use require in ESM', async () => { - const { logs, restore } = proxyConsole(); - const fixturePath = join(__dirname, 'module-import-warn'); - const { entries } = await buildAndGetResults({ fixturePath }); - const logStrings = logs.map((log) => stripAnsi(log)); - const issuer = join(fixturePath, 'src/index.ts'); - - for (const external of [ - 'import * as __rspack_external_bar from "bar";', - 'import * as __rspack_external_foo from "foo";', - 'import * as __rspack_external_qux from "qux";', - ]) { - expect(entries.esm).toContain(external); - } - - for (const external of ['foo', 'bar', 'qux']) { - expect( - logStrings.some((l) => - l.includes(stripAnsi(composeModuleImportWarn(external, issuer))), - ), - ).toBe(true); - } - - for (const external of ['./baz', 'quxx']) { - expect( - logStrings.some((l) => - l.includes(stripAnsi(composeModuleImportWarn(external, issuer))), - ), - ).toBe(false); - } - - restore(); -}); - test('require ESM from CJS', async () => { const fixturePath = join(__dirname, 'node'); const { restore } = proxyConsole(); diff --git a/website/docs/en/config/lib/auto-external.mdx b/website/docs/en/config/lib/auto-external.mdx index c4c0fe33b..c9ee9e45c 100644 --- a/website/docs/en/config/lib/auto-external.mdx +++ b/website/docs/en/config/lib/auto-external.mdx @@ -5,6 +5,10 @@ overviewHeaders: [2, 3] # lib.autoExternal +:::warning +`lib.autoExternal` is deprecated. Please use [output.autoExternal](/config/rsbuild/output#outputautoexternal) instead. +::: + :::info `autoExternal` is a specific configuration for bundle mode. It will not take effect in bundleless mode (set [lib.bundle](/config/lib/bundle) to `false`) since deps will not be bundled in bundleless mode. diff --git a/website/docs/en/config/lib/dts.mdx b/website/docs/en/config/lib/dts.mdx index d1dd6d874..ceb1415c8 100644 --- a/website/docs/en/config/lib/dts.mdx +++ b/website/docs/en/config/lib/dts.mdx @@ -104,7 +104,7 @@ Specifies the dependencies whose declaration files should be bundled. This confi By default, Rslib determines externalized dependencies based on the following configurations. For details, refer to [Handle third-party dependencies](/guide/advanced/third-party-deps). -- [autoExternal](/config/lib/auto-external) configuration +- [output.autoExternal](/config/rsbuild/output#outputautoexternal) configuration - [output.externals](/config/rsbuild/output#outputexternals) configuration Direct dependencies (declared in `package.json`) that are not externalized will be automatically added to `bundledPackages`, and their declaration files will be bundled into the final output. diff --git a/website/docs/en/config/lib/experiments.mdx b/website/docs/en/config/lib/experiments.mdx index 457c8a289..806a36011 100644 --- a/website/docs/en/config/lib/experiments.mdx +++ b/website/docs/en/config/lib/experiments.mdx @@ -107,7 +107,7 @@ To enable `experiments.exe`, make sure that: After enabling `experiments.exe`, Rslib will also: - Disable code splitting -- Ignore [`autoExternal`](/config/lib/auto-external), [`output.externals`](/config/rsbuild/output#outputexternals), [`externalHelpers`](/config/lib/external-helpers), and related options, and bundle all modules directly into the executable +- Ignore [`output.autoExternal`](/config/rsbuild/output#outputautoexternal), [`output.externals`](/config/rsbuild/output#outputexternals), [`externalHelpers`](/config/lib/external-helpers), and related options, and bundle all modules directly into the executable ### Boolean type diff --git a/website/docs/en/config/rsbuild/output.mdx b/website/docs/en/config/rsbuild/output.mdx index 8624f321f..5eb683769 100644 --- a/website/docs/en/config/rsbuild/output.mdx +++ b/website/docs/en/config/rsbuild/output.mdx @@ -21,6 +21,21 @@ When `output.assetPrefix` is set to `"auto"`, Rslib defaults to setting [importM When `output.assetPrefix` is set to a specific path, the static asset import statements in JavaScript files will no longer be preserved and will be replaced with URLs prefixed by that path. Additionally, the static asset paths in CSS files will be substituted with paths that include this prefix. +## output.autoExternal + +- **CLI:** `--auto-external` / `--no-auto-external` + +Whether to automatically externalize dependencies of different dependency types and do not bundle them. This is a specific configuration for bundle mode, it will not take effect in bundleless mode. + +In Rslib, the default value for this option depends on [format](/config/lib/format): + +- When `format` is `cjs` or `esm`, the default value is `true`. +- When `format` is `umd`, `mf`, or `iife`, the default value is `false`. + +When set to `true`, the `dependencies`, `optionalDependencies`, and `peerDependencies` listed in `package.json` will be automatically externalized. + +For more details about handling third-party dependencies, please refer to [Handle Third-party Dependencies](/guide/advanced/third-party-deps). + ## output.charset The `charset` config allows you to specify the [character encoding](https://developer.mozilla.org/en-US/docs/Glossary/Character_encoding) for output files to ensure they are displayed correctly in different environments. @@ -83,7 +98,7 @@ Whether to emit CSS to the output bundles. At build time, prevent some `import` dependencies from being packed into bundles in your code, and instead fetch them externally at runtime. -In bundle mode, Rslib will automatically add the dependencies listed in the `dependencies`, `optionalDependencies`, and `peerDependencies` fields of `package.json` to `output.externals`. See [lib.autoExternal](/config/lib/auto-external) for more information. +In bundle mode, Rslib will automatically externalize the dependencies listed in `package.json` based on [output.autoExternal](#outputautoexternal). See [output.autoExternal](#outputautoexternal) for more information. :::note It is important to note that `output.externals` differs from [resolve.alias](/config/rsbuild/resolve#resolvealias). Check out [resolve.alias](/config/rsbuild/resolve#resolvealias) documentation for more information. diff --git a/website/docs/en/guide/advanced/third-party-deps.mdx b/website/docs/en/guide/advanced/third-party-deps.mdx index 575dc72ff..31eadc07e 100644 --- a/website/docs/en/guide/advanced/third-party-deps.mdx +++ b/website/docs/en/guide/advanced/third-party-deps.mdx @@ -59,30 +59,39 @@ console.info(foo); If you want to modify the default processing, you can use the following API: -- [lib.autoExternal](/config/lib/auto-external) +- [output.autoExternal](/config/rsbuild/output#outputautoexternal) - [output.externals](/config/rsbuild/output#outputexternals) -## Exclude specified third-party dependencies +## Customize third-party dependency handling -The configuration described above allows you to implement more fine-grained handling of third-party dependencies. +You can use the following two options to adjust the default behavior: -For example, when we need to leave only certain dependencies unbundled, we can configure it as follows. +- [output.autoExternal](/config/rsbuild/output#outputautoexternal): automatically externalizes dependencies based on `package.json`. +- [output.externals](/config/rsbuild/output#outputexternals): manually marks matched import requests as external. -:::tip -In this case, some dependencies may not be suitable for bundling. If so, you can handle it as follows. -::: +### Bundle dependencies from package.json + +For ESM and CJS bundle outputs, Rslib enables `output.autoExternal` by default. This means dependencies in `dependencies`, `optionalDependencies`, and `peerDependencies` are externalized automatically. + +If you want these dependencies to be bundled into the output, you can configure [output.autoExternal](/config/rsbuild/output#outputautoexternal) to disable automatic externalization, or enable it only for specific dependency types. + +### Externalize specified imports + +Use [output.externals](/config/rsbuild/output#outputexternals) when you need to manually externalize an import, or change the request path after externalization: ```ts export default defineConfig({ lib: [ { - // ... - autoExternal: true, output: { - externals: ['pkg-1', /pkg-2/], + externals: { + react: 'react', + 'react/jsx-runtime': 'react/jsx-runtime', + }, }, - // ... }, ], }); ``` + +If you need precise control over which imports are externalized, we recommend disabling `output.autoExternal` and explicitly listing them with `output.externals`. If a subpath import such as `react/jsx-runtime` also needs to be externalized or remapped, add the subpath to `output.externals` as well. diff --git a/website/docs/en/guide/faq/features.mdx b/website/docs/en/guide/faq/features.mdx index 87365a4b0..cd66fc0e3 100644 --- a/website/docs/en/guide/faq/features.mdx +++ b/website/docs/en/guide/faq/features.mdx @@ -205,7 +205,7 @@ export default { Rslib uses [rsbuild-plugin-dts](https://github.com/web-infra-dev/rslib/blob/main/packages/plugin-dts/README.md) to generate declaration files, which supports configuration via [output.externals](/config/rsbuild/output#outputexternals) for excluding certain dependencies from bundled declaration files. -For example, if `@types/foo` is only declared in `devDependencies`, according to the dependency handling logic of [autoExternal](/config/lib/auto-external), Rslib will try to bundle `@types/foo` into the declaration output files during the build. In this case, you can exclude `@types/foo` by configuring [output.externals](/config/rsbuild/output#outputexternals). +For example, if `@types/foo` is only declared in `devDependencies`, according to the dependency handling logic of [output.autoExternal](/config/rsbuild/output#outputautoexternal), Rslib will try to bundle `@types/foo` into the declaration output files during the build. In this case, you can exclude `@types/foo` by configuring [output.externals](/config/rsbuild/output#outputexternals). ```ts title="rslib.config.ts" export default { diff --git a/website/docs/en/guide/migration/tsup.mdx b/website/docs/en/guide/migration/tsup.mdx index c495f02cf..0ceff7c8a 100644 --- a/website/docs/en/guide/migration/tsup.mdx +++ b/website/docs/en/guide/migration/tsup.mdx @@ -68,7 +68,7 @@ Here is the corresponding Rslib configuration for tsup configuration: | define | [source.define](/config/rsbuild/source#sourcedefine) | | dts | [lib.dts](/config/lib/dts) | | entry | [source.entry](/config/rsbuild/source#sourceentry) | -| external | [output.externals](/config/rsbuild/output#outputexternals) / [lib.autoExternal](/config/lib/auto-external) | +| external | [output.externals](/config/rsbuild/output#outputexternals) / [output.autoExternal](/config/rsbuild/output#outputautoexternal) | | format | [lib.format](/config/lib/format) | | footer | [lib.footer](/config/lib/footer) | | minify | [output.minify](/config/rsbuild/output#outputminify) | diff --git a/website/docs/zh/config/lib/auto-external.mdx b/website/docs/zh/config/lib/auto-external.mdx index ed381026d..8c0127f68 100644 --- a/website/docs/zh/config/lib/auto-external.mdx +++ b/website/docs/zh/config/lib/auto-external.mdx @@ -5,6 +5,10 @@ overviewHeaders: [2, 3] # lib.autoExternal +:::warning +`lib.autoExternal` 已废弃,请使用 [output.autoExternal](/config/rsbuild/output#outputautoexternal) 代替。 +::: + :::info `autoExternal` 是 bundle 模式的特定配置。在 bundleless 模式(将 [lib.bundle](/config/lib/bundle) 设置为 `false`)下不会生效,因为 bundleless 模式下依赖不会被打包。 diff --git a/website/docs/zh/config/lib/dts.mdx b/website/docs/zh/config/lib/dts.mdx index 0642d4e26..ba25d65ae 100644 --- a/website/docs/zh/config/lib/dts.mdx +++ b/website/docs/zh/config/lib/dts.mdx @@ -104,7 +104,7 @@ Rslib 基于 API Extractor 对类型声明文件进行打包,可能会存在 默认情况下,Rslib 会根据以下配置确定需要外部化的依赖项,详见 [处理第三方依赖](/guide/advanced/third-party-deps)。 -- [autoExternal](/config/lib/auto-external) 配置 +- [output.autoExternal](/config/rsbuild/output#outputautoexternal) 配置 - [output.externals](/config/rsbuild/output#outputexternals) 配置 那些没有被外部化的直接依赖项(在 `package.json` 中声明)会被添加到 `bundledPackages` 中,这些包的类型声明文件将会被打包到最终的产物中。 diff --git a/website/docs/zh/config/lib/experiments.mdx b/website/docs/zh/config/lib/experiments.mdx index 4c72d4403..593b67273 100644 --- a/website/docs/zh/config/lib/experiments.mdx +++ b/website/docs/zh/config/lib/experiments.mdx @@ -107,7 +107,7 @@ type ExeOptions = 开启 `experiments.exe` 后,Rslib 还会: - 禁用代码拆分 -- 忽略 [`autoExternal`](/config/lib/auto-external)、[`output.externals`](/config/rsbuild/output#outputexternals)、[`externalHelpers`](/config/lib/external-helpers) 等配置,将所有模块直接打包进可执行文件 +- 忽略 [`output.autoExternal`](/config/rsbuild/output#outputautoexternal)、[`output.externals`](/config/rsbuild/output#outputexternals)、[`externalHelpers`](/config/lib/external-helpers) 等配置,将所有模块直接打包进可执行文件 ### 布尔类型 diff --git a/website/docs/zh/config/rsbuild/output.mdx b/website/docs/zh/config/rsbuild/output.mdx index 298bb8737..f16b8024f 100644 --- a/website/docs/zh/config/rsbuild/output.mdx +++ b/website/docs/zh/config/rsbuild/output.mdx @@ -21,6 +21,21 @@ import { RsbuildDocBadge } from '@components/RsbuildDocBadge'; 当 `output.assetPrefix` 设置为具体的路径时,JavaScript 文件中引用静态资源的 `import` 或 `require` 语句会被替换为包含该前缀的 URL。同时,CSS 文件中的静态资源会被直接替换为带有该前缀的路径。 +## output.autoExternal + +- **命令行:** `--auto-external` / `--no-auto-external` + +是否自动对不同依赖类型的依赖进行外部化处理,不将其打包。该配置仅在 bundle 模式下生效,bundleless 模式下不会生效。 + +在 Rslib 中,该选项的默认值由 [format](/config/lib/format) 决定: + +- 当 `format` 为 `cjs` 或 `esm` 时,默认值为 `true`。 +- 当 `format` 为 `umd`、`mf` 或 `iife` 时,默认值为 `false`。 + +设置为 `true` 时,`package.json` 中 `dependencies`、`optionalDependencies` 和 `peerDependencies` 字段下的依赖将被自动外部化。 + +关于第三方依赖处理的更多细节,请参考 [处理第三方依赖](/guide/advanced/third-party-deps)。 + ## output.charset 指定输出文件的 [字符编码](https://developer.mozilla.org/en-US/docs/Glossary/Character_encoding),以确保它们在不同的环境中能够正确显示。 @@ -79,7 +94,7 @@ const defaultDistPath = { 在构建时,防止将代码中某些 `import` 的依赖包打包到 bundle 中,而是在运行时再去从外部获取这些依赖。 -在 bundle 模式下,Rslib 会默认将 `package.json` 中 `dependencies`、`optionalDependencies` 和 `peerDependencies` 字段下的三方依赖添加到 `output.externals` 中, 查看 [lib.autoExternal](/config/lib/auto-external) 了解更多信息。 +在 bundle 模式下,Rslib 会根据 [output.autoExternal](#outputautoexternal) 自动将 `package.json` 中的依赖进行外部化处理,查看 [output.autoExternal](#outputautoexternal) 了解更多信息。 :::note 需要注意的是,`output.externals` 与 [resolve.alias](/config/rsbuild/resolve#resolvealias) 有所不同。请查看 [resolve.alias](/config/rsbuild/resolve#resolvealias) 文档以了解更多信息。 diff --git a/website/docs/zh/guide/advanced/third-party-deps.mdx b/website/docs/zh/guide/advanced/third-party-deps.mdx index 81ebe6dc0..9e09af806 100644 --- a/website/docs/zh/guide/advanced/third-party-deps.mdx +++ b/website/docs/zh/guide/advanced/third-party-deps.mdx @@ -57,32 +57,36 @@ import foo from 'foo'; console.info(foo); ``` -如果想要修改默认的处理方式,可以通过下面的 API 进行修改: +## 自定义三方依赖处理 -- [lib.autoExternal](/config/lib/auto-external) -- [output.externals](/config/rsbuild/output#outputexternals) +可以使用下面两个配置调整默认行为: -## 排除指定三方依赖 +- [output.autoExternal](/config/rsbuild/output#outputautoexternal):根据 `package.json` 自动外部化依赖。 +- [output.externals](/config/rsbuild/output#outputexternals):手动将匹配到的 import 请求标记为 external。 -上面介绍的配置可以让你实现对三方依赖更细微的处理。 +### 打包 package.json 中的依赖 -例如当我们需要仅对某些依赖不进行打包处理的时候,可以按照如下方式进行配置: +对于 ESM 和 CJS 的 bundle 产物,Rslib 默认启用 `output.autoExternal`。这意味着 `dependencies`、`optionalDependencies` 和 `peerDependencies` 中的依赖会被自动外部化。 -:::tip -在这种情况下,某些依赖可能不适合打包。如果遇到这种情况,则可以按照下面的方式进行处理。 -::: +如果希望这些依赖被打包进产物,通过配置 [output.autoExternal](/config/rsbuild/output#outputautoexternal) 可以禁用自动外部化,或者只对指定依赖类型启用。 + +### 外部化指定 import + +当你需要手动外部化某个 import,或需要改变 external 后的请求路径时,可以使用 [output.externals](/config/rsbuild/output#outputexternals): ```ts export default defineConfig({ lib: [ { - // ... - autoExternal: true, output: { - externals: ['pkg-1', /pkg-2/], + externals: { + react: 'react', + 'react/jsx-runtime': 'react/jsx-runtime', + }, }, - // ... }, ], }); ``` + +如果你需要精确控制哪些 import 被 external,建议禁用 `output.autoExternal`,并使用 `output.externals` 显式列出它们。如果 `react/jsx-runtime` 这类子路径 import 也需要 external 或改名,也需要把子路径一起添加到 `output.externals` 中。 diff --git a/website/docs/zh/guide/faq/features.mdx b/website/docs/zh/guide/faq/features.mdx index ca2cd713f..fd55db537 100644 --- a/website/docs/zh/guide/faq/features.mdx +++ b/website/docs/zh/guide/faq/features.mdx @@ -205,7 +205,7 @@ export default { Rslib 通过 [rsbuild-plugin-dts](https://github.com/web-infra-dev/rslib/blob/main/packages/plugin-dts/README.md) 生成类型声明文件,该插件支持通过 [output.externals](/config/rsbuild/output#outputexternals) 配置,从打包后的类型声明文件中排除指定的依赖。 -举个例子,若 `@types/foo` 仅声明在 `devDependencies` 中,按照 [autoExternal](/config/lib/auto-external) 处理依赖的逻辑,在打包时 Rslib 会尝试将 `@types/foo` 一同打包进类型声明文件产物中,此时可以通过配置 [output.externals](/config/rsbuild/output#outputexternals) 来排除 `@types/foo`。 +举个例子,若 `@types/foo` 仅声明在 `devDependencies` 中,按照 [output.autoExternal](/config/rsbuild/output#outputautoexternal) 处理依赖的逻辑,在打包时 Rslib 会尝试将 `@types/foo` 一同打包进类型声明文件产物中,此时可以通过配置 [output.externals](/config/rsbuild/output#outputexternals) 来排除 `@types/foo`。 ```ts title="rslib.config.ts" export default { diff --git a/website/docs/zh/guide/migration/tsup.mdx b/website/docs/zh/guide/migration/tsup.mdx index f483cc043..640e1ae05 100644 --- a/website/docs/zh/guide/migration/tsup.mdx +++ b/website/docs/zh/guide/migration/tsup.mdx @@ -68,7 +68,7 @@ export default defineConfig({}); | define | [source.define](/config/rsbuild/source#sourcedefine) | | dts | [lib.dts](/config/lib/dts) | | entry | [source.entry](/config/rsbuild/source#sourceentry) | -| external | [output.externals](/config/rsbuild/output#outputexternals) / [lib.autoExternal](/config/lib/auto-external) | +| external | [output.externals](/config/rsbuild/output#outputexternals) / [output.autoExternal](/config/rsbuild/output#outputautoexternal) | | format | [lib.format](/config/lib/format) | | footer | [lib.footer](/config/lib/footer) | | minify | [output.minify](/config/rsbuild/output#outputminify) |