diff --git a/e2e/cases/shims/esm/rslib.config.ts b/e2e/cases/shims/esm/rslib.config.ts index 81545d6da..66ffc4b14 100644 --- a/e2e/cases/shims/esm/rslib.config.ts +++ b/e2e/cases/shims/esm/rslib.config.ts @@ -2,7 +2,23 @@ import { generateBundleEsmConfig } from '@e2e/helper'; import { defineConfig } from '@rslib/core'; export default defineConfig({ - lib: [generateBundleEsmConfig()], + lib: [ + generateBundleEsmConfig({ + output: { + distPath: { + root: './dist/normal', + }, + }, + }), + generateBundleEsmConfig({ + syntax: 'esnext', + output: { + distPath: { + root: './dist/with-syntax', + }, + }, + }), + ], output: { target: 'node', }, diff --git a/e2e/cases/shims/index.test.ts b/e2e/cases/shims/index.test.ts index 3f290a2ab..549851862 100644 --- a/e2e/cases/shims/index.test.ts +++ b/e2e/cases/shims/index.test.ts @@ -5,6 +5,7 @@ import { describe, expect, test } from 'vitest'; test('shims for __dirname and __filename in ESM', async () => { const fixturePath = join(__dirname, 'esm'); const { entries } = await buildAndGetResults(fixturePath); + for (const shim of [ 'import { fileURLToPath as __webpack_fileURLToPath__ } from "url";', 'var src_dirname = __webpack_dirname__(__webpack_fileURLToPath__(import.meta.url));', @@ -12,8 +13,9 @@ test('shims for __dirname and __filename in ESM', async () => { // import.meta.url should not be substituted 'const importMetaUrl = import.meta.url;', ]) { - expect(entries.esm).toContain(shim); + expect(entries.esm0).toContain(shim); } + expect(entries.esm0).toBe(entries.esm1); }); describe('shims for `import.meta.url` in CJS', () => { diff --git a/package.json b/package.json index 226e1a7a1..93be8f6a0 100644 --- a/package.json +++ b/package.json @@ -22,6 +22,7 @@ "test:e2e": "cd e2e && pnpm run test", "test:unit": "vitest run --project unit*", "test:unit:watch": "vitest --project unit*", + "testu": "pnpm run test:unit -u && pnpm run test:artifact -u", "update:rsbuild": "npx taze minor --include /rsbuild/ -w -r -l", "watch": "pnpm build --watch" }, diff --git a/packages/core/src/config.ts b/packages/core/src/config.ts index 29b65f2ed..60e245054 100644 --- a/packages/core/src/config.ts +++ b/packages/core/src/config.ts @@ -46,6 +46,7 @@ import { logger } from './utils/logger'; import { ESX_TO_BROWSERSLIST, transformSyntaxToBrowserslist, + transformSyntaxToRspackTarget, } from './utils/syntax'; import { loadTsconfig } from './utils/tsconfig'; @@ -578,19 +579,15 @@ const composeSyntaxConfig = ( target?: RsbuildConfigOutputTarget, ): RsbuildConfig => { // Defaults to ESNext, Rslib will assume all of the latest JavaScript and CSS features are supported. - if (syntax) { - const resolvedBrowserslist = transformSyntaxToBrowserslist(syntax, target); return { tools: { rspack: (config) => { - config.target = resolvedBrowserslist.map( - (item) => `browserslist:${item}` as const, - ); + config.target = transformSyntaxToRspackTarget(syntax); }, }, output: { - overrideBrowserslist: resolvedBrowserslist, + overrideBrowserslist: transformSyntaxToBrowserslist(syntax, target), }, }; } diff --git a/packages/core/src/utils/syntax.ts b/packages/core/src/utils/syntax.ts index 92b0ffacf..2ac88272e 100644 --- a/packages/core/src/utils/syntax.ts +++ b/packages/core/src/utils/syntax.ts @@ -1,4 +1,4 @@ -import type { RsbuildConfig } from '@rsbuild/core'; +import type { RsbuildConfig, Rspack } from '@rsbuild/core'; import type { EcmaScriptVersion, FixedEcmaVersions, @@ -37,6 +37,12 @@ const calcEsnextBrowserslistByTarget = (target: RsbuildConfigOutputTarget) => { return LATEST_TARGET_VERSIONS.web; }; +const RSPACK_TARGET_UNLISTED_MODERN_ECMA_VERSIONS: EcmaScriptVersion[] = [ + 'es2023', + 'es2024', + 'esnext', +] satisfies EcmaScriptVersion[]; + /** * The esX to browserslist mapping is transformed from esbuild: * https://github.com/evanw/esbuild/blob/main/internal/compat/js_table.go @@ -158,6 +164,40 @@ export const ESX_TO_BROWSERSLIST: Record< }, } as const; +export function transformSyntaxToRspackTarget( + syntax: Syntax, +): Rspack.Configuration['target'] { + const handleSyntaxItem = (syntaxItem: EcmaScriptVersion | string): string => { + const normalizedSyntaxItem = syntaxItem.toLowerCase(); + + if (normalizedSyntaxItem.startsWith('es')) { + if (normalizedSyntaxItem in ESX_TO_BROWSERSLIST) { + // The latest EcmaScript version supported by Rspack's `target` is es2022. + // Higher versions are treated as es2022. + if ( + RSPACK_TARGET_UNLISTED_MODERN_ECMA_VERSIONS.includes( + normalizedSyntaxItem as EcmaScriptVersion, + ) + ) { + return 'es2022'; + } + + return normalizedSyntaxItem; + } + + throw new Error(`Unsupported ES version: ${syntaxItem}`); + } + + return `browserslist:${syntaxItem}`; + }; + + if (Array.isArray(syntax)) { + return syntax.map(handleSyntaxItem) as Rspack.Configuration['target']; + } + + return [handleSyntaxItem(syntax)] as Rspack.Configuration['target']; +} + export function transformSyntaxToBrowserslist( syntax: Syntax, target?: NonNullable['target'], @@ -166,6 +206,7 @@ export function transformSyntaxToBrowserslist( syntaxItem: EcmaScriptVersion | string, ): string[] => { const normalizedSyntaxItem = syntaxItem.toLowerCase(); + if (normalizedSyntaxItem.startsWith('es')) { if (normalizedSyntaxItem in ESX_TO_BROWSERSLIST) { const browserslistItem = diff --git a/packages/core/tests/syntax.test.ts b/packages/core/tests/syntax.test.ts index 7aec58d53..500359308 100644 --- a/packages/core/tests/syntax.test.ts +++ b/packages/core/tests/syntax.test.ts @@ -1,8 +1,11 @@ import { describe, expect, test } from 'vitest'; -import { transformSyntaxToBrowserslist } from '../src/utils/syntax'; +import { + transformSyntaxToBrowserslist, + transformSyntaxToRspackTarget, +} from '../src/utils/syntax'; -describe('Correctly resolve syntax', () => { - test('esX', async () => { +describe('transformSyntaxToBrowserslist', () => { + test('esX', () => { expect(transformSyntaxToBrowserslist('es6')).toMatchInlineSnapshot(` [ "Chrome >= 63.0.0", @@ -56,7 +59,7 @@ describe('Correctly resolve syntax', () => { ); }); - test('browserslist', async () => { + test('browserslist', () => { expect( transformSyntaxToBrowserslist(['fully supports es6-module']), ).toMatchInlineSnapshot(` @@ -75,7 +78,7 @@ describe('Correctly resolve syntax', () => { `); }); - test('combined', async () => { + test('combined', () => { expect( transformSyntaxToBrowserslist(['Chrome 123', 'es5']), ).toMatchInlineSnapshot(` @@ -97,3 +100,41 @@ describe('Correctly resolve syntax', () => { ); }); }); + +describe('transformSyntaxToRspackTarget', () => { + test('esX', () => { + const es2023 = transformSyntaxToRspackTarget('es2023'); + const es2024 = transformSyntaxToRspackTarget('es2024'); + const esnext = transformSyntaxToRspackTarget('esnext'); + + expect(es2023).toEqual(es2024); + expect(es2023).toEqual(esnext); + + expect(es2023).toMatchInlineSnapshot( + ` + [ + "es2022", + ] + `, + ); + + expect(transformSyntaxToRspackTarget('es2015')).toMatchInlineSnapshot( + ` + [ + "es2015", + ] + `, + ); + }); + + test('combined', () => { + expect( + transformSyntaxToRspackTarget(['Chrome 123', 'es2023']), + ).toMatchInlineSnapshot(` + [ + "browserslist:Chrome 123", + "es2022", + ] + `); + }); +});