diff --git a/packages/compat/plugin-swc/src/minimizer.ts b/packages/compat/plugin-swc/src/minimizer.ts index ed671ff0bb..b97ae79e69 100644 --- a/packages/compat/plugin-swc/src/minimizer.ts +++ b/packages/compat/plugin-swc/src/minimizer.ts @@ -1,4 +1,8 @@ -import { type NormalizedConfig, __internalHelper, logger } from '@rsbuild/core'; +import { + type NormalizedEnvironmentConfig, + __internalHelper, + logger, +} from '@rsbuild/core'; import { color, deepmerge } from '@rsbuild/shared'; import type { webpack } from '@rsbuild/webpack'; import { minify, minifyCss } from './binding'; @@ -33,7 +37,7 @@ export class SwcMinimizerPlugin { constructor(options: { jsMinify?: boolean | JsMinifyOptions; cssMinify?: boolean | CssMinifyOptions; - rsbuildConfig: NormalizedConfig; + rsbuildConfig: NormalizedEnvironmentConfig; }) { this.minifyOptions = { jsMinify: options.jsMinify @@ -48,7 +52,9 @@ export class SwcMinimizerPlugin { }; } - getDefaultJsMinifyOptions(rsbuildConfig: NormalizedConfig): JsMinifyOptions { + getDefaultJsMinifyOptions( + rsbuildConfig: NormalizedEnvironmentConfig, + ): JsMinifyOptions { const options = { ...__internalHelper.getSwcMinimizerOptions(rsbuildConfig), mangle: true, diff --git a/packages/compat/plugin-swc/src/plugin.ts b/packages/compat/plugin-swc/src/plugin.ts index 4235d26406..12e4059d52 100644 --- a/packages/compat/plugin-swc/src/plugin.ts +++ b/packages/compat/plugin-swc/src/plugin.ts @@ -39,11 +39,11 @@ export const pluginSwc = (options: PluginSwcOptions = {}): RsbuildPlugin => ({ // loader should be applied in the pre stage for customizing order: 'pre', handler: async (chain, utils) => { - const { CHAIN_ID } = utils; - const rsbuildConfig = api.getNormalizedConfig(); + const { CHAIN_ID, environment } = utils; + const rsbuildConfig = api.getNormalizedConfig({ environment }); const { rootPath } = api.context; - const { browserslist } = api.context.environments[utils.environment]; + const { browserslist } = api.context.environments[environment]; const swcConfigs = await applyPluginConfig( options, utils, @@ -119,8 +119,8 @@ export const pluginSwc = (options: PluginSwcOptions = {}): RsbuildPlugin => ({ }, }); - api.modifyBundlerChain((chain, { CHAIN_ID, isProd }) => { - const rsbuildConfig = api.getNormalizedConfig(); + api.modifyBundlerChain((chain, { CHAIN_ID, isProd, environment }) => { + const rsbuildConfig = api.getNormalizedConfig({ environment }); if (checkUseMinify(mainConfig, rsbuildConfig, isProd)) { const { minify } = rsbuildConfig.output; diff --git a/packages/compat/plugin-swc/src/utils.ts b/packages/compat/plugin-swc/src/utils.ts index e904a9a96e..2663a427ec 100644 --- a/packages/compat/plugin-swc/src/utils.ts +++ b/packages/compat/plugin-swc/src/utils.ts @@ -1,7 +1,10 @@ import fs from 'node:fs'; import path from 'node:path'; import { __internalHelper } from '@rsbuild/core'; -import type { ModifyChainUtils, NormalizedConfig } from '@rsbuild/core'; +import type { + ModifyChainUtils, + NormalizedEnvironmentConfig, +} from '@rsbuild/core'; import { getCoreJsVersion } from '@rsbuild/shared'; import _ from 'lodash'; import semver from 'semver'; @@ -74,7 +77,7 @@ export async function determinePresetReact( export function checkUseMinify( options: ObjPluginSwcOptions, - config: NormalizedConfig, + config: NormalizedEnvironmentConfig, isProd: boolean, ) { return ( @@ -174,7 +177,7 @@ const getDefaultStyledComponentsConfig = (isProd: boolean, ssr: boolean) => { export async function applyPluginConfig( rawOptions: PluginSwcOptions, utils: ModifyChainUtils, - rsbuildConfig: NormalizedConfig, + rsbuildConfig: NormalizedEnvironmentConfig, rootPath: string, browserslist?: string[], ): Promise { diff --git a/packages/compat/plugin-swc/tests/__snapshots__/plugin.test.ts.snap b/packages/compat/plugin-swc/tests/__snapshots__/plugin.test.ts.snap index 1f3384ad9f..89c5502c14 100644 --- a/packages/compat/plugin-swc/tests/__snapshots__/plugin.test.ts.snap +++ b/packages/compat/plugin-swc/tests/__snapshots__/plugin.test.ts.snap @@ -1,5 +1,260 @@ // Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html +exports[`plugin-swc > should apply multiply environment configs correctly 1`] = ` +[ + { + "module": { + "rules": [ + { + "include": [ + { + "and": [ + "/packages/compat/plugin-swc/tests", + { + "not": /\\[\\\\\\\\/\\]node_modules\\[\\\\\\\\/\\]/, + }, + ], + }, + /\\\\\\.\\(\\?:ts\\|tsx\\|jsx\\|mts\\|cts\\)\\$/, + ], + "test": /\\\\\\.\\(\\?:js\\|jsx\\|mjs\\|cjs\\|ts\\|tsx\\|mts\\|cts\\)\\$/, + "use": [ + { + "loader": "/packages/compat/plugin-swc/src/loader.cjs", + "options": { + "cwd": "/packages/compat/plugin-swc/tests", + "env": { + "coreJs": "3.36", + "mode": "usage", + "targets": [ + "chrome >= 87", + "edge >= 88", + "firefox >= 78", + "safari >= 14", + ], + }, + "extensions": { + "lockCorejsVersion": { + "corejs": "/node_modules//core-js", + "swcHelpers": "/node_modules//@swc/helpers", + }, + "lodash": { + "cwd": "/packages/compat/plugin-swc/tests", + "ids": [ + "lodash", + "lodash-es", + ], + }, + }, + "jsc": { + "externalHelpers": true, + "parser": { + "decorators": true, + "syntax": "typescript", + "tsx": true, + }, + "preserveAllComments": true, + "transform": { + "decoratorVersion": "2022-03", + "legacyDecorator": false, + "react": { + "refresh": true, + "runtime": "classic", + }, + }, + }, + }, + }, + ], + }, + { + "mimetype": { + "or": [ + "text/javascript", + "application/javascript", + ], + }, + "resolve": { + "fullySpecified": false, + }, + "use": [ + { + "loader": "/packages/compat/plugin-swc/src/loader.cjs", + "options": { + "cwd": "/packages/compat/plugin-swc/tests", + "env": { + "coreJs": "3.36", + "mode": "usage", + "targets": [ + "chrome >= 87", + "edge >= 88", + "firefox >= 78", + "safari >= 14", + ], + }, + "extensions": { + "lockCorejsVersion": { + "corejs": "/node_modules//core-js", + "swcHelpers": "/node_modules//@swc/helpers", + }, + "lodash": { + "cwd": "/packages/compat/plugin-swc/tests", + "ids": [ + "lodash", + "lodash-es", + ], + }, + }, + "jsc": { + "externalHelpers": true, + "parser": { + "decorators": true, + "syntax": "typescript", + "tsx": true, + }, + "preserveAllComments": true, + "transform": { + "decoratorVersion": "2022-03", + "legacyDecorator": false, + "react": { + "refresh": true, + "runtime": "classic", + }, + }, + }, + }, + }, + ], + }, + ], + }, + }, + { + "module": { + "rules": [ + { + "include": [ + { + "and": [ + "/packages/compat/plugin-swc/tests", + { + "not": /\\[\\\\\\\\/\\]node_modules\\[\\\\\\\\/\\]/, + }, + ], + }, + /\\\\\\.\\(\\?:ts\\|tsx\\|jsx\\|mts\\|cts\\)\\$/, + ], + "test": /\\\\\\.\\(\\?:js\\|jsx\\|mjs\\|cjs\\|ts\\|tsx\\|mts\\|cts\\)\\$/, + "use": [ + { + "loader": "/packages/compat/plugin-swc/src/loader.cjs", + "options": { + "cwd": "/packages/compat/plugin-swc/tests", + "env": { + "coreJs": "3.36", + "mode": "usage", + "targets": [ + "node >= 16", + ], + }, + "extensions": { + "lockCorejsVersion": { + "corejs": "/node_modules//core-js", + "swcHelpers": "/node_modules//@swc/helpers", + }, + "lodash": { + "cwd": "/packages/compat/plugin-swc/tests", + "ids": [ + "lodash", + "lodash-es", + ], + }, + }, + "jsc": { + "externalHelpers": true, + "parser": { + "decorators": true, + "syntax": "typescript", + "tsx": true, + }, + "preserveAllComments": true, + "transform": { + "decoratorMetadata": true, + "legacyDecorator": true, + "react": { + "refresh": false, + "runtime": "classic", + }, + "useDefineForClassFields": false, + }, + }, + }, + }, + ], + }, + { + "mimetype": { + "or": [ + "text/javascript", + "application/javascript", + ], + }, + "resolve": { + "fullySpecified": false, + }, + "use": [ + { + "loader": "/packages/compat/plugin-swc/src/loader.cjs", + "options": { + "cwd": "/packages/compat/plugin-swc/tests", + "env": { + "coreJs": "3.36", + "mode": "usage", + "targets": [ + "node >= 16", + ], + }, + "extensions": { + "lockCorejsVersion": { + "corejs": "/node_modules//core-js", + "swcHelpers": "/node_modules//@swc/helpers", + }, + "lodash": { + "cwd": "/packages/compat/plugin-swc/tests", + "ids": [ + "lodash", + "lodash-es", + ], + }, + }, + "jsc": { + "externalHelpers": true, + "parser": { + "decorators": true, + "syntax": "typescript", + "tsx": true, + }, + "preserveAllComments": true, + "transform": { + "decoratorMetadata": true, + "legacyDecorator": true, + "react": { + "refresh": false, + "runtime": "classic", + }, + "useDefineForClassFields": false, + }, + }, + }, + }, + ], + }, + ], + }, + }, +] +`; + exports[`plugin-swc > should apply source.include and source.exclude correctly 1`] = ` { "module": { diff --git a/packages/compat/plugin-swc/tests/plugin.test.ts b/packages/compat/plugin-swc/tests/plugin.test.ts index 4c8464b63a..5df143dbb9 100644 --- a/packages/compat/plugin-swc/tests/plugin.test.ts +++ b/packages/compat/plugin-swc/tests/plugin.test.ts @@ -29,6 +29,40 @@ describe('plugin-swc', () => { expect(config).toMatchSnapshot(); }); + it('should apply multiply environment configs correctly', async () => { + const rsbuild = await createStubRsbuild({ + plugins: [pluginSwc()], + rsbuildConfig: { + provider: webpackProvider, + environments: { + web: { + output: { + target: 'web', + }, + source: { + decorators: { + version: '2022-03', + }, + }, + }, + ssr: { + output: { + target: 'node', + }, + source: { + decorators: { + version: 'legacy', + }, + }, + }, + }, + }, + }); + const configs = await rsbuild.initConfigs(); + + expect(configs).toMatchSnapshot(); + }); + it('should set swc minimizer in production', async () => { process.env.NODE_ENV = 'production'; const rsbuild = await createStubRsbuild({ @@ -234,7 +268,7 @@ describe('plugin-swc', () => { target: 'node', }, }, - 'serviceWorker': { + serviceWorker: { output: { target: 'service-worker', }, @@ -244,7 +278,7 @@ describe('plugin-swc', () => { target: 'web', }, }, - 'webWorker': { + webWorker: { output: { target: 'web-worker', }, diff --git a/packages/core/src/index.ts b/packages/core/src/index.ts index 9d2dc44ef5..bf44074372 100644 --- a/packages/core/src/index.ts +++ b/packages/core/src/index.ts @@ -51,6 +51,7 @@ export type { NormalizedSecurityConfig, NormalizedPerformanceConfig, NormalizedModuleFederationConfig, + NormalizedEnvironmentConfig, // Plugin Types RsbuildPlugin, RsbuildPlugins, diff --git a/packages/core/src/plugins/define.ts b/packages/core/src/plugins/define.ts index e82977395f..210d30ca17 100644 --- a/packages/core/src/plugins/define.ts +++ b/packages/core/src/plugins/define.ts @@ -6,8 +6,8 @@ export const pluginDefine = (): RsbuildPlugin => ({ name: 'rsbuild:define', setup(api) { - api.modifyBundlerChain((chain, { CHAIN_ID, bundler }) => { - const config = api.getNormalizedConfig(); + api.modifyBundlerChain((chain, { CHAIN_ID, bundler, environment }) => { + const config = api.getNormalizedConfig({ environment }); const builtinVars: Define = { 'process.env.NODE_ENV': JSON.stringify(getNodeEnv()), 'process.env.ASSET_PREFIX': JSON.stringify( diff --git a/packages/core/src/plugins/externals.ts b/packages/core/src/plugins/externals.ts index 644ded01fe..383b134745 100644 --- a/packages/core/src/plugins/externals.ts +++ b/packages/core/src/plugins/externals.ts @@ -4,8 +4,8 @@ export function pluginExternals(): RsbuildPlugin { return { name: 'rsbuild:externals', setup(api) { - api.modifyBundlerChain((chain) => { - const { externals } = api.getNormalizedConfig().output; + api.modifyBundlerChain((chain, { environment }) => { + const { externals } = api.getNormalizedConfig({ environment }).output; if (externals) { chain.externals(externals); } diff --git a/packages/core/src/plugins/lazyCompilation.ts b/packages/core/src/plugins/lazyCompilation.ts index 41e326e8fb..0376bb47ba 100644 --- a/packages/core/src/plugins/lazyCompilation.ts +++ b/packages/core/src/plugins/lazyCompilation.ts @@ -5,13 +5,14 @@ export const pluginLazyCompilation = (): RsbuildPlugin => ({ name: 'rsbuild:lazy-compilation', setup(api) { - api.modifyBundlerChain((chain, { isProd, target }) => { + api.modifyBundlerChain((chain, { environment, isProd, target }) => { if (isProd || target !== 'web') { return; } - const config = api.getNormalizedConfig(); + const config = api.getNormalizedConfig({ environment }); + // TODO: support dev.lazyCompilation option in environment config const options = config.dev?.lazyCompilation; if (!options) { return; diff --git a/packages/core/src/plugins/manifest.ts b/packages/core/src/plugins/manifest.ts index d0fe2517e3..e40f1b32c3 100644 --- a/packages/core/src/plugins/manifest.ts +++ b/packages/core/src/plugins/manifest.ts @@ -146,7 +146,7 @@ export const pluginManifest = (): RsbuildPlugin => ({ api.modifyBundlerChain(async (chain, { CHAIN_ID, environment }) => { const { output: { manifest }, - } = api.getNormalizedConfig(); + } = api.getNormalizedConfig({ environment }); if (manifest === false) { return; diff --git a/packages/core/src/plugins/minimize.ts b/packages/core/src/plugins/minimize.ts index 563bbe7718..db1eef6144 100644 --- a/packages/core/src/plugins/minimize.ts +++ b/packages/core/src/plugins/minimize.ts @@ -1,7 +1,6 @@ import { CHAIN_ID, type HTMLPluginOptions, - type NormalizedConfig, type NormalizedEnvironmentConfig, deepmerge, isObject, @@ -11,7 +10,7 @@ import type { SwcJsMinimizerRspackPluginOptions } from '@rspack/core'; import type { RsbuildPlugin } from '../types'; export const getSwcMinimizerOptions = ( - config: NormalizedConfig, + config: NormalizedEnvironmentConfig, ): SwcJsMinimizerRspackPluginOptions => { const options: SwcJsMinimizerRspackPluginOptions = {}; @@ -108,8 +107,8 @@ export const pluginMinimize = (): RsbuildPlugin => ({ return; } - api.modifyBundlerChain(async (chain, { isProd }) => { - const config = api.getNormalizedConfig(); + api.modifyBundlerChain(async (chain, { isProd, environment }) => { + const config = api.getNormalizedConfig({ environment }); const isMinimize = isProd && config.output.minify !== false; if (!isMinimize) { diff --git a/packages/core/src/plugins/moment.ts b/packages/core/src/plugins/moment.ts index 900d05f1a5..e43a1e3175 100644 --- a/packages/core/src/plugins/moment.ts +++ b/packages/core/src/plugins/moment.ts @@ -4,8 +4,8 @@ export const pluginMoment = (): RsbuildPlugin => ({ name: 'rsbuild:moment', setup(api) { - api.modifyBundlerChain(async (chain, { bundler }) => { - const config = api.getNormalizedConfig(); + api.modifyBundlerChain(async (chain, { environment, bundler }) => { + const config = api.getNormalizedConfig({ environment }); if (config.performance.removeMomentLocale) { // Moment.js includes a lots of locale data by default. diff --git a/packages/core/src/plugins/output.ts b/packages/core/src/plugins/output.ts index 2b7e45aa69..ea06353c23 100644 --- a/packages/core/src/plugins/output.ts +++ b/packages/core/src/plugins/output.ts @@ -1,7 +1,7 @@ import { posix } from 'node:path'; import { DEFAULT_ASSET_PREFIX, - type NormalizedConfig, + type NormalizedEnvironmentConfig, type RsbuildContext, } from '@rsbuild/shared'; import { rspack } from '@rspack/core'; @@ -17,7 +17,7 @@ function getPublicPath({ context, }: { isProd: boolean; - config: NormalizedConfig; + config: NormalizedEnvironmentConfig; context: RsbuildContext; }) { const { dev, output } = config; @@ -56,9 +56,9 @@ export const pluginOutput = (): RsbuildPlugin => ({ api.modifyBundlerChain( async ( chain, - { CHAIN_ID, target, isProd, isServer, isServiceWorker }, + { CHAIN_ID, target, isProd, isServer, isServiceWorker, environment }, ) => { - const config = api.getNormalizedConfig(); + const config = api.getNormalizedConfig({ environment }); const publicPath = getPublicPath({ config, diff --git a/packages/core/src/plugins/resourceHints.ts b/packages/core/src/plugins/resourceHints.ts index 86e9dfe38e..26959bd594 100644 --- a/packages/core/src/plugins/resourceHints.ts +++ b/packages/core/src/plugins/resourceHints.ts @@ -21,8 +21,8 @@ export const pluginResourceHints = (): RsbuildPlugin => ({ name: 'rsbuild:resource-hints', setup(api) { - api.modifyHTMLTags(({ headTags, bodyTags }) => { - const config = api.getNormalizedConfig(); + api.modifyHTMLTags(({ headTags, bodyTags }, { environment }) => { + const config = api.getNormalizedConfig({ environment }); const { dnsPrefetch, preconnect } = config.performance; if (dnsPrefetch) { diff --git a/packages/core/src/plugins/sri.ts b/packages/core/src/plugins/sri.ts index 9f08ff97f3..091a386d86 100644 --- a/packages/core/src/plugins/sri.ts +++ b/packages/core/src/plugins/sri.ts @@ -19,8 +19,8 @@ export const pluginSri = (): RsbuildPlugin => ({ setup(api) { const placeholder = 'RSBUILD_INTEGRITY_PLACEHOLDER:'; - const getAlgorithm = () => { - const config = api.getNormalizedConfig(); + const getAlgorithm = (environment: string) => { + const config = api.getNormalizedConfig({ environment }); const { sri } = config.security; const enable = sri.enable === 'auto' ? isProd() : sri.enable; @@ -35,8 +35,8 @@ export const pluginSri = (): RsbuildPlugin => ({ api.modifyHTMLTags({ // ensure `sri` can be applied to all tags order: 'post', - handler(tags, { assetPrefix }) { - const algorithm = getAlgorithm(); + handler(tags, { assetPrefix, environment }) { + const algorithm = getAlgorithm(environment); if (!algorithm) { return tags; @@ -193,7 +193,7 @@ export const pluginSri = (): RsbuildPlugin => ({ return; } - const algorithm = getAlgorithm(); + const algorithm = getAlgorithm(environment); if (!algorithm) { return; } diff --git a/packages/core/src/plugins/swc.ts b/packages/core/src/plugins/swc.ts index 8334853354..068d567f0e 100644 --- a/packages/core/src/plugins/swc.ts +++ b/packages/core/src/plugins/swc.ts @@ -12,7 +12,7 @@ import { PLUGIN_SWC_NAME } from '../constants'; import { isWebTarget } from '../helpers'; import { reduceConfigs } from '../reduceConfigs'; import type { - NormalizedConfig, + NormalizedEnvironmentConfig, NormalizedSourceConfig, RsbuildPlugin, } from '../types'; @@ -54,7 +54,7 @@ export const pluginSwc = (): RsbuildPlugin => ({ api.modifyBundlerChain({ order: 'pre', handler: async (chain, { CHAIN_ID, target, environment }) => { - const config = api.getNormalizedConfig(); + const config = api.getNormalizedConfig({ environment }); const rule = chain.module .rule(CHAIN_ID.RULE.JS) @@ -169,7 +169,7 @@ function applyTransformImport( export function applySwcDecoratorConfig( swcConfig: SwcLoaderOptions, - config: NormalizedConfig, + config: NormalizedEnvironmentConfig, ) { swcConfig.jsc ||= {}; swcConfig.jsc.transform ||= {}; diff --git a/packages/core/src/plugins/wasm.ts b/packages/core/src/plugins/wasm.ts index ebe2f4d4ac..3d900bf999 100644 --- a/packages/core/src/plugins/wasm.ts +++ b/packages/core/src/plugins/wasm.ts @@ -5,8 +5,8 @@ export const pluginWasm = (): RsbuildPlugin => ({ name: 'rsbuild:wasm', setup(api) { - api.modifyBundlerChain(async (chain, { CHAIN_ID }) => { - const config = api.getNormalizedConfig(); + api.modifyBundlerChain(async (chain, { CHAIN_ID, environment }) => { + const config = api.getNormalizedConfig({ environment }); const distPath = config.output.distPath.wasm; chain.experiments({ diff --git a/packages/core/tests/__snapshots__/swc.test.ts.snap b/packages/core/tests/__snapshots__/swc.test.ts.snap index 589a4cc2dd..acb4a51980 100644 --- a/packages/core/tests/__snapshots__/swc.test.ts.snap +++ b/packages/core/tests/__snapshots__/swc.test.ts.snap @@ -533,6 +533,230 @@ exports[`plugin-swc > should allow to use \`tools.swc\` to configure swc-loader ] `; +exports[`plugin-swc > should apply environment config correctly 1`] = ` +[ + { + "exclude": [ + "src/example", + ], + "include": [ + { + "and": [ + "/packages/core/tests", + { + "not": /\\[\\\\\\\\/\\]node_modules\\[\\\\\\\\/\\]/, + }, + ], + }, + /\\\\\\.\\(\\?:ts\\|tsx\\|jsx\\|mts\\|cts\\)\\$/, + ], + "resolve": { + "alias": { + "core-js": "/node_modules//core-js", + }, + }, + "test": /\\\\\\.\\(\\?:js\\|jsx\\|mjs\\|cjs\\|ts\\|tsx\\|mts\\|cts\\)\\$/, + "type": "javascript/auto", + "use": [ + { + "loader": "builtin:swc-loader", + "options": { + "env": { + "coreJs": "3.36", + "mode": "usage", + "shippedProposals": true, + "targets": [ + "chrome >= 87", + "edge >= 88", + "firefox >= 78", + "safari >= 14", + ], + }, + "isModule": "unknown", + "jsc": { + "externalHelpers": true, + "parser": { + "decorators": true, + "syntax": "typescript", + "tsx": false, + }, + "preserveAllComments": true, + "transform": { + "decoratorMetadata": true, + "legacyDecorator": true, + "useDefineForClassFields": false, + }, + }, + "rspackExperiments": { + "import": [ + { + "libraryName": "foo", + }, + ], + }, + }, + }, + ], + }, + { + "mimetype": { + "or": [ + "text/javascript", + "application/javascript", + ], + }, + "resolve": { + "alias": { + "core-js": "/node_modules//core-js", + }, + "fullySpecified": false, + }, + "use": [ + { + "loader": "builtin:swc-loader", + "options": { + "env": { + "coreJs": "3.36", + "mode": "usage", + "shippedProposals": true, + "targets": [ + "chrome >= 87", + "edge >= 88", + "firefox >= 78", + "safari >= 14", + ], + }, + "isModule": "unknown", + "jsc": { + "externalHelpers": true, + "parser": { + "decorators": true, + "syntax": "typescript", + "tsx": false, + }, + "preserveAllComments": true, + "transform": { + "decoratorMetadata": true, + "legacyDecorator": true, + "useDefineForClassFields": false, + }, + }, + "rspackExperiments": { + "import": [ + { + "libraryName": "foo", + }, + ], + }, + }, + }, + ], + }, +] +`; + +exports[`plugin-swc > should apply environment config correctly 2`] = ` +[ + { + "exclude": [ + "src/example1", + ], + "include": [ + { + "and": [ + "/packages/core/tests", + { + "not": /\\[\\\\\\\\/\\]node_modules\\[\\\\\\\\/\\]/, + }, + ], + }, + /\\\\\\.\\(\\?:ts\\|tsx\\|jsx\\|mts\\|cts\\)\\$/, + ], + "test": /\\\\\\.\\(\\?:js\\|jsx\\|mjs\\|cjs\\|ts\\|tsx\\|mts\\|cts\\)\\$/, + "type": "javascript/auto", + "use": [ + { + "loader": "builtin:swc-loader", + "options": { + "env": { + "targets": [ + "node >= 16", + ], + }, + "isModule": "unknown", + "jsc": { + "externalHelpers": true, + "parser": { + "decorators": true, + "syntax": "typescript", + "tsx": false, + }, + "preserveAllComments": true, + "transform": { + "decoratorMetadata": true, + "legacyDecorator": true, + "useDefineForClassFields": false, + }, + }, + "rspackExperiments": { + "import": [ + { + "libraryName": "bar", + }, + ], + }, + }, + }, + ], + }, + { + "mimetype": { + "or": [ + "text/javascript", + "application/javascript", + ], + }, + "resolve": { + "fullySpecified": false, + }, + "use": [ + { + "loader": "builtin:swc-loader", + "options": { + "env": { + "targets": [ + "node >= 16", + ], + }, + "isModule": "unknown", + "jsc": { + "externalHelpers": true, + "parser": { + "decorators": true, + "syntax": "typescript", + "tsx": false, + }, + "preserveAllComments": true, + "transform": { + "decoratorMetadata": true, + "legacyDecorator": true, + "useDefineForClassFields": false, + }, + }, + "rspackExperiments": { + "import": [ + { + "libraryName": "bar", + }, + ], + }, + }, + }, + ], + }, +] +`; + exports[`plugin-swc > should disable all pluginImport 1`] = ` { "entry": { diff --git a/packages/core/tests/external.test.ts b/packages/core/tests/external.test.ts index 1073881c3a..6b99498eee 100644 --- a/packages/core/tests/external.test.ts +++ b/packages/core/tests/external.test.ts @@ -24,7 +24,7 @@ describe('plugin-external', () => { const chain = await getBundlerChain(); - await modifyBundlerChainCb(chain); + await modifyBundlerChainCb(chain, { environment: 'client' }); const bundlerConfigs = [ { diff --git a/packages/core/tests/swc.test.ts b/packages/core/tests/swc.test.ts index e1d162a42c..f60e4c19c0 100644 --- a/packages/core/tests/swc.test.ts +++ b/packages/core/tests/swc.test.ts @@ -154,6 +154,50 @@ describe('plugin-swc', () => { expect(bundlerConfig.module?.rules).toMatchSnapshot(); } }); + + it('should apply environment config correctly', async () => { + const rsbuild = await createStubRsbuild({ + rsbuildConfig: { + environments: { + web: { + source: { + exclude: ['src/example'], + transformImport: [ + { + libraryName: 'foo', + }, + ], + }, + output: { + polyfill: 'usage', + target: 'web', + }, + }, + node: { + source: { + exclude: ['src/example1'], + transformImport: [ + { + libraryName: 'bar', + }, + ], + }, + output: { + polyfill: 'usage', + target: 'node', + }, + }, + }, + }, + plugins: [pluginSwc()], + }); + + const bundlerConfigs = await rsbuild.initConfigs(); + + for (const bundlerConfig of bundlerConfigs) { + expect(bundlerConfig.module?.rules).toMatchSnapshot(); + } + }); }); async function matchConfigSnapshot(rsbuildConfig: RsbuildConfig) { diff --git a/packages/plugin-babel/src/plugin.ts b/packages/plugin-babel/src/plugin.ts index 6690c0b660..4804c74974 100644 --- a/packages/plugin-babel/src/plugin.ts +++ b/packages/plugin-babel/src/plugin.ts @@ -1,7 +1,7 @@ import fs from 'node:fs'; import path, { isAbsolute, join } from 'node:path'; import type { - NormalizedConfig, + NormalizedEnvironmentConfig, RsbuildContext, RsbuildPlugin, } from '@rsbuild/core'; @@ -50,7 +50,7 @@ async function getCacheIdentifier(options: BabelLoaderOptions) { } export const getDefaultBabelOptions = ( - config: NormalizedConfig, + config: NormalizedEnvironmentConfig, context: RsbuildContext, ): BabelLoaderOptions => { const isLegacyDecorators = config.source.decorators.version === 'legacy'; @@ -103,8 +103,8 @@ export const pluginBabel = ( name: PLUGIN_BABEL_NAME, setup(api) { - const getBabelOptions = async () => { - const config = api.getNormalizedConfig(); + const getBabelOptions = async (environment: string) => { + const config = api.getNormalizedConfig({ environment }); const baseOptions = getDefaultBabelOptions(config, api.context); const mergedOptions = applyUserBabelConfig( @@ -122,8 +122,8 @@ export const pluginBabel = ( api.modifyBundlerChain({ order: 'pre', - handler: async (chain, { CHAIN_ID }) => { - const babelOptions = await getBabelOptions(); + handler: async (chain, { CHAIN_ID, environment }) => { + const babelOptions = await getBabelOptions(environment); const babelLoader = path.resolve( __dirname, '../compiled/babel-loader/index.js', diff --git a/packages/plugin-babel/tests/__snapshots__/index.test.ts.snap b/packages/plugin-babel/tests/__snapshots__/index.test.ts.snap index 02f6d07a70..23a202393d 100644 --- a/packages/plugin-babel/tests/__snapshots__/index.test.ts.snap +++ b/packages/plugin-babel/tests/__snapshots__/index.test.ts.snap @@ -89,6 +89,169 @@ exports[`plugins/babel > babel-loader should works with builtin:swc-loader 1`] = } `; +exports[`plugins/babel > should apply environment config correctly 1`] = ` +{ + "exclude": [ + "src/example", + ], + "include": [ + { + "and": [ + "/packages/plugin-babel/tests", + { + "not": /\\[\\\\\\\\/\\]node_modules\\[\\\\\\\\/\\]/, + }, + ], + }, + /\\\\\\.\\(\\?:ts\\|tsx\\|jsx\\|mts\\|cts\\)\\$/, + ], + "resolve": { + "alias": { + "core-js": "/node_modules//core-js", + }, + }, + "test": /\\\\\\.\\(\\?:js\\|jsx\\|mjs\\|cjs\\|ts\\|tsx\\|mts\\|cts\\)\\$/, + "type": "javascript/auto", + "use": [ + { + "loader": "builtin:swc-loader", + "options": { + "env": { + "coreJs": "3.36", + "mode": "usage", + "shippedProposals": true, + "targets": [ + "chrome >= 87", + "edge >= 88", + "firefox >= 78", + "safari >= 14", + ], + }, + "isModule": "unknown", + "jsc": { + "externalHelpers": true, + "parser": { + "decorators": true, + "syntax": "typescript", + "tsx": false, + }, + "preserveAllComments": true, + "transform": { + "decoratorVersion": "2022-03", + "legacyDecorator": false, + }, + }, + }, + }, + { + "loader": "/packages/plugin-babel/compiled/babel-loader/index.js", + "options": { + "babelrc": false, + "compact": false, + "configFile": false, + "plugins": [ + [ + "/node_modules//@babel/plugin-proposal-decorators/lib/index.js", + { + "version": "2022-03", + }, + ], + ], + "presets": [ + [ + "/node_modules//@babel/preset-typescript/lib/index.js", + { + "allExtensions": true, + "allowDeclareFields": true, + "allowNamespaces": true, + "isTSX": true, + "optimizeConstEnums": true, + }, + ], + ], + }, + }, + ], +} +`; + +exports[`plugins/babel > should apply environment config correctly 2`] = ` +{ + "exclude": [ + "src/example1", + ], + "include": [ + { + "and": [ + "/packages/plugin-babel/tests", + { + "not": /\\[\\\\\\\\/\\]node_modules\\[\\\\\\\\/\\]/, + }, + ], + }, + /\\\\\\.\\(\\?:ts\\|tsx\\|jsx\\|mts\\|cts\\)\\$/, + ], + "test": /\\\\\\.\\(\\?:js\\|jsx\\|mjs\\|cjs\\|ts\\|tsx\\|mts\\|cts\\)\\$/, + "type": "javascript/auto", + "use": [ + { + "loader": "builtin:swc-loader", + "options": { + "env": { + "targets": [ + "node >= 16", + ], + }, + "isModule": "unknown", + "jsc": { + "externalHelpers": true, + "parser": { + "decorators": true, + "syntax": "typescript", + "tsx": false, + }, + "preserveAllComments": true, + "transform": { + "decoratorMetadata": true, + "legacyDecorator": true, + "useDefineForClassFields": false, + }, + }, + }, + }, + { + "loader": "/packages/plugin-babel/compiled/babel-loader/index.js", + "options": { + "babelrc": false, + "compact": false, + "configFile": false, + "plugins": [ + [ + "/node_modules//@babel/plugin-proposal-decorators/lib/index.js", + { + "version": "legacy", + }, + ], + "/node_modules//@babel/plugin-transform-class-properties/lib/index.js", + ], + "presets": [ + [ + "/node_modules//@babel/preset-typescript/lib/index.js", + { + "allExtensions": true, + "allowDeclareFields": true, + "allowNamespaces": true, + "isTSX": true, + "optimizeConstEnums": true, + }, + ], + ], + }, + }, + ], +} +`; + exports[`plugins/babel > should set babel-loader 1`] = ` { "module": { diff --git a/packages/plugin-babel/tests/index.test.ts b/packages/plugin-babel/tests/index.test.ts index bd4575016f..1513773ea4 100644 --- a/packages/plugin-babel/tests/index.test.ts +++ b/packages/plugin-babel/tests/index.test.ts @@ -28,6 +28,53 @@ describe('plugins/babel', () => { ).toMatchSnapshot(); }); + it('should apply environment config correctly', async () => { + const rsbuild = await createStubRsbuild({ + rsbuildConfig: { + environments: { + web: { + source: { + exclude: ['src/example'], + decorators: { + version: '2022-03', + }, + }, + performance: { + buildCache: false, + }, + }, + ssr: { + source: { + exclude: ['src/example1'], + decorators: { + version: 'legacy', + }, + }, + performance: { + buildCache: false, + }, + output: { + target: 'node', + }, + }, + }, + }, + }); + + rsbuild.addPlugins([pluginBabel()]); + + const bundlerConfigs = await rsbuild.initConfigs(); + + for (const bundlerConfig of bundlerConfigs) { + const rules = bundlerConfig.module?.rules?.find( + (r) => + (typeof r === 'object' ? r?.test?.toString() : '') === + SCRIPT_REGEX.toString(), + ); + expect(rules).toMatchSnapshot(); + } + }); + it('should set babel-loader', async () => { const rsbuild = await createStubRsbuild({ plugins: [pluginBabel()], diff --git a/packages/shared/src/chain.ts b/packages/shared/src/chain.ts index 40779c17b6..69a029d826 100644 --- a/packages/shared/src/chain.ts +++ b/packages/shared/src/chain.ts @@ -1,6 +1,6 @@ import RspackChain from '../compiled/rspack-chain/index.js'; import { NODE_MODULES_REGEX, TS_AND_JSX_REGEX } from './constants'; -import type { NormalizedConfig, RsbuildContext } from './types'; +import type { NormalizedEnvironmentConfig, RsbuildContext } from './types'; import { castArray } from './utils'; export { RspackChain }; @@ -173,7 +173,7 @@ export function applyScriptCondition({ }: { rule: RspackChain.Rule; chain: RspackChain; - config: NormalizedConfig; + config: NormalizedEnvironmentConfig; context: RsbuildContext; includes: (string | RegExp)[]; excludes: (string | RegExp)[]; diff --git a/website/docs/en/config/environments.mdx b/website/docs/en/config/environments.mdx index 7f8ff1bb3a..adfe797c55 100644 --- a/website/docs/en/config/environments.mdx +++ b/website/docs/en/config/environments.mdx @@ -96,15 +96,15 @@ For the `web` environment, the merged Rsbuild configuration is: const webConfig = { source: { alias: { - "@common": "./src/common", - "@common1": "./src/web/common1", + '@common': './src/common', + '@common1': './src/web/common1', }, entry: { - index: "./src/index.client.js", + index: './src/index.client.js', }, }, output: { - target: "web", + target: 'web', }, }; ``` @@ -115,15 +115,15 @@ For the `node` environment, the merged Rsbuild configuration is: const nodeConfig = { source: { alias: { - "@common": "./src/common", - "@common1": "./src/ssr/common1", + '@common': './src/common', + '@common1': './src/ssr/common1', }, entry: { - index: "./src/index.server.js", + index: './src/index.server.js', }, }, output: { - target: "node", + target: 'node', }, }; ``` diff --git a/website/docs/zh/config/environments.mdx b/website/docs/zh/config/environments.mdx index 0a1c83b61e..a736931f94 100644 --- a/website/docs/zh/config/environments.mdx +++ b/website/docs/zh/config/environments.mdx @@ -1,6 +1,6 @@ # environments -Rsbuild 支持同时为多个环境构建产物。你可以使用 `environments` 为每个环境定义不同的 Rsbuild 配置。 +Rsbuild 支持同时为多个环境构建产物。你可以使用 `environments` 为每个环境定义不同的 Rsbuild 配置。 定义在 `environments` 中的配置会覆盖外部的 Rsbuild 基础配置。 @@ -96,17 +96,17 @@ export default { const webConfig = { source: { alias: { - "@common": "./src/common", - "@common1": "./src/web/common1", + '@common': './src/common', + '@common1': './src/web/common1', }, entry: { - index: "./src/index.client.js", + index: './src/index.client.js', }, }, output: { - target: "web", + target: 'web', }, -} +}; ``` 对于 `node` 环境,合并后的 Rsbuild 配置为: @@ -115,15 +115,15 @@ const webConfig = { const nodeConfig = { source: { alias: { - "@common": "./src/common", - "@common1": "./src/ssr/common1", + '@common': './src/common', + '@common1': './src/ssr/common1', }, entry: { - index: "./src/index.server.js", + index: './src/index.server.js', }, }, output: { - target: "node", + target: 'node', }, -} +}; ```