diff --git a/packages/core/src/config.ts b/packages/core/src/config.ts index 3e3242436..30f019b71 100644 --- a/packages/core/src/config.ts +++ b/packages/core/src/config.ts @@ -58,6 +58,7 @@ import { color, getAbsolutePath, isEmptyObject, + isIntermediateOutputFormat, isObject, nodeBuiltInModules, omit, @@ -234,14 +235,27 @@ const composeExternalsWarnConfig = ( }; }; +const getAutoExternalDefaultValue = ( + format: Format, + autoExternal?: AutoExternal, +): AutoExternal => { + return autoExternal ?? isIntermediateOutputFormat(format); +}; + export const composeAutoExternalConfig = (options: { - autoExternal: AutoExternal; + format: Format; + autoExternal?: AutoExternal; pkgJson?: PkgJson; userExternals?: NonNullable['externals']; }): RsbuildConfig => { - const { autoExternal, pkgJson, userExternals } = options; + const { format, pkgJson, userExternals } = options; - if (!autoExternal) { + const autoExternal = getAutoExternalDefaultValue( + format, + options.autoExternal, + ); + + if (autoExternal === false) { return {}; } @@ -1005,7 +1019,7 @@ const composeDtsConfig = async ( libConfig: LibConfig, dtsExtension: string, ): Promise => { - const { autoExternal, banner, footer } = libConfig; + const { format, autoExternal, banner, footer } = libConfig; let { dts } = libConfig; @@ -1028,7 +1042,7 @@ const composeDtsConfig = async ( build: dts?.build, abortOnError: dts?.abortOnError, dtsExtension: dts?.autoExtension ? dtsExtension : '.d.ts', - autoExternal, + autoExternal: getAutoExternalDefaultValue(format!, autoExternal), banner: banner?.dts, footer: footer?.dts, }), @@ -1150,7 +1164,7 @@ async function composeLibRsbuildConfig(config: LibConfig) { banner = {}, footer = {}, autoExtension = true, - autoExternal = true, + autoExternal, externalHelpers = false, redirect = {}, umdName, @@ -1190,6 +1204,7 @@ async function composeLibRsbuildConfig(config: LibConfig) { ); const syntaxConfig = composeSyntaxConfig(target, config?.syntax); const autoExternalConfig = composeAutoExternalConfig({ + format: format!, autoExternal, pkgJson, userExternals: config.output?.externals, diff --git a/packages/core/src/types/config/index.ts b/packages/core/src/types/config/index.ts index 5ae702680..1c0d7839d 100644 --- a/packages/core/src/types/config/index.ts +++ b/packages/core/src/types/config/index.ts @@ -109,7 +109,7 @@ export interface LibConfig extends RsbuildConfig { autoExtension?: boolean; /** * Whether to automatically externalize dependencies of different dependency types and do not bundle them. - * @defaultValue `true` + * @defaultValue `true` when {@link format} is `cjs` or `esm`, `false` when {@link format} is `umd` or `mf`. * @see {@link https://lib.rsbuild.dev/config/lib/auto-external} */ autoExternal?: AutoExternal; diff --git a/packages/core/src/utils/helper.ts b/packages/core/src/utils/helper.ts index 7a061039e..e5c3805e8 100644 --- a/packages/core/src/utils/helper.ts +++ b/packages/core/src/utils/helper.ts @@ -4,7 +4,7 @@ import path, { isAbsolute, join } from 'node:path'; import type { RsbuildPlugins } from '@rsbuild/core'; import color from 'picocolors'; -import type { LibConfig, PkgJson } from '../types'; +import type { Format, LibConfig, PkgJson } from '../types'; import { logger } from './logger'; /** @@ -232,4 +232,8 @@ export const isTTY = (type: 'stdin' | 'stdout' = 'stdout'): boolean => { ); }; +export const isIntermediateOutputFormat = (format: Format): boolean => { + return format === 'cjs' || format === 'esm'; +}; + export { color }; diff --git a/packages/core/tests/external.test.ts b/packages/core/tests/external.test.ts index 343b35d71..4dada5d99 100644 --- a/packages/core/tests/external.test.ts +++ b/packages/core/tests/external.test.ts @@ -4,10 +4,83 @@ import { composeAutoExternalConfig } from '../src/config'; vi.mock('rslog'); describe('should composeAutoExternalConfig correctly', () => { + it('autoExternal default value', () => { + const esmResult = composeAutoExternalConfig({ + format: 'esm', + autoExternal: undefined, + pkgJson: { + name: 'esm', + dependencies: { + foo: '1.0.0', + }, + }, + }); + + const cjsResult = composeAutoExternalConfig({ + format: 'cjs', + autoExternal: undefined, + pkgJson: { + name: 'cjs', + dependencies: { + foo: '1.0.0', + }, + }, + }); + + const umdResult = composeAutoExternalConfig({ + format: 'umd', + autoExternal: undefined, + pkgJson: { + name: 'umd', + dependencies: { + foo: '1.0.0', + }, + }, + }); + + const mfResult = composeAutoExternalConfig({ + 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({ + format: 'esm', autoExternal: true, pkgJson: { + name: 'esm', dependencies: { foo: '1.0.0', foo1: '1.0.0', @@ -35,10 +108,58 @@ describe('should composeAutoExternalConfig correctly', () => { }); }); + it('autoExternal is true when format is umd or mf', () => { + const umdResult = composeAutoExternalConfig({ + format: 'umd', + autoExternal: true, + pkgJson: { + name: 'umd', + dependencies: { + foo: '1.0.0', + }, + }, + }); + + expect(umdResult).toMatchInlineSnapshot(` + { + "output": { + "externals": [ + /\\^foo\\(\\$\\|\\\\/\\|\\\\\\\\\\)/, + "foo", + ], + }, + } + `); + + const mfResult = composeAutoExternalConfig({ + 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({ + format: 'esm', autoExternal: true, pkgJson: { + name: 'esm', dependencies: { foo: '1.0.0', foo1: '1.0.0', @@ -70,11 +191,13 @@ describe('should composeAutoExternalConfig correctly', () => { it('autoExternal is object', () => { const result = composeAutoExternalConfig({ + format: 'esm', autoExternal: { peerDependencies: false, devDependencies: true, }, pkgJson: { + name: 'esm', dependencies: { foo: '1.0.0', }, @@ -96,8 +219,10 @@ describe('should composeAutoExternalConfig correctly', () => { it('autoExternal is false', () => { const result = composeAutoExternalConfig({ + format: 'esm', autoExternal: false, pkgJson: { + name: 'esm', dependencies: { foo: '1.0.0', }, @@ -109,8 +234,10 @@ describe('should composeAutoExternalConfig correctly', () => { it('autoExternal with user externals object', () => { const result = composeAutoExternalConfig({ + format: 'esm', autoExternal: true, pkgJson: { + name: 'esm', dependencies: { foo: '1.0.0', bar: '1.0.0', @@ -130,6 +257,7 @@ describe('should composeAutoExternalConfig correctly', () => { it('read package.json failed', () => { const result = composeAutoExternalConfig({ + format: 'esm', autoExternal: true, }); diff --git a/tests/integration/umd-library-name/rslib.config.ts b/tests/integration/umd-library-name/rslib.config.ts index a293c4d1b..88876214f 100644 --- a/tests/integration/umd-library-name/rslib.config.ts +++ b/tests/integration/umd-library-name/rslib.config.ts @@ -14,5 +14,8 @@ export default defineConfig({ }, output: { target: 'web', + externals: { + react: 'react', + }, }, }); diff --git a/website/docs/en/config/lib/auto-external.mdx b/website/docs/en/config/lib/auto-external.mdx index 7660db9e0..5b14fe0d5 100644 --- a/website/docs/en/config/lib/auto-external.mdx +++ b/website/docs/en/config/lib/auto-external.mdx @@ -17,7 +17,9 @@ type AutoExternal = }; ``` -- **Default:** `true` +- **Default:** + - `true` when [format](/config/lib/format) is `cjs` or `esm` + - `false` when [format](/config/lib/format) is `umd` or `mf` Whether to automatically externalize dependencies of different dependency types and do not bundle them. diff --git a/website/docs/en/guide/advanced/third-party-deps.mdx b/website/docs/en/guide/advanced/third-party-deps.mdx index 6773c439c..52534593e 100644 --- a/website/docs/en/guide/advanced/third-party-deps.mdx +++ b/website/docs/en/guide/advanced/third-party-deps.mdx @@ -15,7 +15,7 @@ In addition to `"dependencies"`, `"peerDependencies"`can also declare dependenci ## Default handling of third-party dependencies -By default, third-party dependencies under `"dependencies"`, `"optionalDependencies"` and `"peerDependencies"` are not bundled by Rslib. +By default, when generating CJS or ESM outputs, third-party dependencies under `"dependencies"`, `"optionalDependencies"` and `"peerDependencies"` are not bundled by Rslib. This is because when the npm package is installed, its `"dependencies"` will also be installed. By not packaging `"dependencies"`, you can reduce the size of the package product. diff --git a/website/docs/zh/config/lib/auto-external.mdx b/website/docs/zh/config/lib/auto-external.mdx index 9950146b0..9ed6e75ac 100644 --- a/website/docs/zh/config/lib/auto-external.mdx +++ b/website/docs/zh/config/lib/auto-external.mdx @@ -17,7 +17,9 @@ type AutoExternal = }; ``` -- **默认值:** `true` +- **默认值:** + - 当 [format](/config/lib/format) 为 `cjs` 或 `esm` 时为 `true` + - 当 [format](/config/lib/format) 为 `umd` 或 `mf` 时为 `false` 是否自动对不同依赖类型的依赖进行外部化处理,不将其打包。 diff --git a/website/docs/zh/guide/advanced/third-party-deps.mdx b/website/docs/zh/guide/advanced/third-party-deps.mdx index ff74ceeb0..b133f7e2c 100644 --- a/website/docs/zh/guide/advanced/third-party-deps.mdx +++ b/website/docs/zh/guide/advanced/third-party-deps.mdx @@ -15,7 +15,7 @@ ## 三方依赖的默认处理 -默认情况下,`dependencies`、`optionalDependencies` 和 `peerDependencies` 字段下的三方依赖不会被 Rslib 打包。 +默认情况下,当生成 CJS 或 ESM 产物时,`dependencies`、`optionalDependencies` 和 `peerDependencies` 字段下的三方依赖不会被 Rslib 打包。 这是因为在 npm 包安装时,其 `dependencies` 也会被安装。通过不打包 `dependencies`,可以减少包的体积。