From 74e5efbd226a47bcc20bd15492057dc2c07d5dd4 Mon Sep 17 00:00:00 2001 From: neverland Date: Wed, 22 May 2024 19:25:23 +0800 Subject: [PATCH] refactor: copy loaders from the builtin CSS rule (#2417) --- packages/core/src/constants.ts | 3 -- packages/core/src/index.ts | 8 +-- packages/core/src/internal.ts | 1 - packages/core/src/plugins/css.ts | 2 +- packages/plugin-less/src/index.ts | 40 ++++++++------ packages/plugin-sass/src/helpers.ts | 1 - packages/plugin-sass/src/index.ts | 42 +++++++++------ packages/plugin-stylus/src/index.ts | 81 ++++++++++++++--------------- packages/shared/src/utils.ts | 7 ++- 9 files changed, 97 insertions(+), 88 deletions(-) diff --git a/packages/core/src/constants.ts b/packages/core/src/constants.ts index 95b7d6bb93..1597921f38 100644 --- a/packages/core/src/constants.ts +++ b/packages/core/src/constants.ts @@ -2,9 +2,6 @@ import { join } from 'node:path'; export const PLUGIN_SWC_NAME = 'rsbuild:swc'; export const PLUGIN_CSS_NAME = 'rsbuild:css'; -export const PLUGIN_LESS_NAME = 'rsbuild:less'; -export const PLUGIN_SASS_NAME = 'rsbuild:sass'; -export const PLUGIN_STYLUS_NAME = 'rsbuild:stylus'; // loaders will be emitted to the same folder of the main bundle export const LOADER_PATH = join(__dirname); diff --git a/packages/core/src/index.ts b/packages/core/src/index.ts index ff5234e17a..ec351a9409 100644 --- a/packages/core/src/index.ts +++ b/packages/core/src/index.ts @@ -23,13 +23,7 @@ export { logger } from '@rsbuild/shared'; export { mergeRsbuildConfig } from './mergeConfig'; // Constants -export { - PLUGIN_SWC_NAME, - PLUGIN_CSS_NAME, - PLUGIN_SASS_NAME, - PLUGIN_LESS_NAME, - PLUGIN_STYLUS_NAME, -} from './constants'; +export { PLUGIN_SWC_NAME, PLUGIN_CSS_NAME } from './constants'; // Types export type { diff --git a/packages/core/src/internal.ts b/packages/core/src/internal.ts index e5ea282786..4515cf4733 100644 --- a/packages/core/src/internal.ts +++ b/packages/core/src/internal.ts @@ -10,7 +10,6 @@ export { createContext, createPublicContext } from './createContext'; export { initPlugins, createPluginManager } from './pluginManager'; export { initHooks, type Hooks } from './initHooks'; export { initRsbuildConfig } from './provider/initConfigs'; -export { applyCSSRule } from './plugins/css'; export { getPluginAPI } from './initPlugins'; export type { InternalContext } from './types'; export { diff --git a/packages/core/src/plugins/css.ts b/packages/core/src/plugins/css.ts index fe6987cf49..9719a23c45 100644 --- a/packages/core/src/plugins/css.ts +++ b/packages/core/src/plugins/css.ts @@ -238,7 +238,7 @@ const getCSSLoaderOptions = ({ return cssLoaderOptions; }; -export async function applyCSSRule({ +async function applyCSSRule({ rule, config, context, diff --git a/packages/plugin-less/src/index.ts b/packages/plugin-less/src/index.ts index 132a47d5cb..e0d7375bb1 100644 --- a/packages/plugin-less/src/index.ts +++ b/packages/plugin-less/src/index.ts @@ -1,18 +1,17 @@ import path from 'node:path'; -import { - type RsbuildPlugin, - type Rspack, - __internalHelper, -} from '@rsbuild/core'; +import type { RsbuildPlugin, Rspack } from '@rsbuild/core'; import { type ChainedConfigWithUtils, type FileFilterUtil, castArray, + cloneDeep, deepmerge, mergeChainedOptions, } from '@rsbuild/shared'; import type Less from '../compiled/less'; +export const PLUGIN_LESS_NAME = 'rsbuild:less'; + export type LessLoaderOptions = { lessOptions?: Less.Options; additionalData?: @@ -93,12 +92,17 @@ const getLessLoaderOptions = ( export const pluginLess = ({ lessLoaderOptions, }: PluginLessOptions = {}): RsbuildPlugin => ({ - name: 'rsbuild:less', + name: PLUGIN_LESS_NAME, setup(api) { - api.modifyBundlerChain(async (chain, utils) => { + api.modifyBundlerChain(async (chain, { CHAIN_ID }) => { const config = api.getNormalizedConfig(); - const rule = chain.module.rule(utils.CHAIN_ID.RULE.LESS).test(/\.less$/); + const rule = chain.module + .rule(CHAIN_ID.RULE.LESS) + .test(/\.less$/) + .merge({ sideEffects: true }) + .resolve.preferRelative(true) + .end(); const { excludes, options } = getLessLoaderOptions( lessLoaderOptions, @@ -110,16 +114,20 @@ export const pluginLess = ({ rule.exclude.add(item); } - await __internalHelper.applyCSSRule({ - rule, - utils, - config, - context: api.context, - importLoaders: 2, - }); + const cssRule = chain.module.rules.get(CHAIN_ID.RULE.CSS); + + // Copy the builtin CSS rules + for (const id of Object.keys(cssRule.uses.entries())) { + const loader = cssRule.uses.get(id); + const options = cloneDeep(loader.get('options')); + if (id === CHAIN_ID.USE.CSS) { + options.importLoaders = 2; + } + rule.use(id).loader(loader.get('loader')).options(options); + } rule - .use(utils.CHAIN_ID.USE.LESS) + .use(CHAIN_ID.USE.LESS) .loader(path.join(__dirname, '../compiled/less-loader/index.js')) .options(options); }); diff --git a/packages/plugin-sass/src/helpers.ts b/packages/plugin-sass/src/helpers.ts index 29694c0f03..6f537dbec2 100644 --- a/packages/plugin-sass/src/helpers.ts +++ b/packages/plugin-sass/src/helpers.ts @@ -1,6 +1,5 @@ import path from 'node:path'; import { pathToFileURL } from 'node:url'; -import { __internalHelper } from '@rsbuild/core'; import type { CompilerTapFn } from '@rsbuild/shared'; const GLOBAL_PATCHED_SYMBOL: unique symbol = Symbol('GLOBAL_PATCHED_SYMBOL'); diff --git a/packages/plugin-sass/src/index.ts b/packages/plugin-sass/src/index.ts index 30054129b0..df7127e66b 100644 --- a/packages/plugin-sass/src/index.ts +++ b/packages/plugin-sass/src/index.ts @@ -1,14 +1,17 @@ import { join } from 'node:path'; -import { type RsbuildPlugin, __internalHelper } from '@rsbuild/core'; +import type { RsbuildPlugin } from '@rsbuild/core'; import { type FileFilterUtil, castArray, + cloneDeep, deepmerge, mergeChainedOptions, } from '@rsbuild/shared'; import { getResolveUrlJoinFn, patchCompilerGlobalLocation } from './helpers'; import type { PluginSassOptions, SassLoaderOptions } from './types'; +export const PLUGIN_SASS_NAME = 'rsbuild:sass'; + const getSassLoaderOptions = ( userOptions: PluginSassOptions['sassLoaderOptions'], isUseCssSourceMap: boolean, @@ -63,16 +66,14 @@ const getSassLoaderOptions = ( export const pluginSass = ( pluginOptions: PluginSassOptions = {}, ): RsbuildPlugin => ({ - name: 'rsbuild:sass', + name: PLUGIN_SASS_NAME, setup(api) { api.onAfterCreateCompiler(({ compiler }) => { patchCompilerGlobalLocation(compiler); }); - api.modifyBundlerChain(async (chain, utils) => { - const config = api.getNormalizedConfig(); - + api.modifyBundlerChain(async (chain, { CHAIN_ID }) => { const { excludes, options } = getSassLoaderOptions( pluginOptions.sassLoaderOptions, // source-maps required for loaders preceding resolve-url-loader @@ -81,24 +82,31 @@ export const pluginSass = ( ); const rule = chain.module - .rule(utils.CHAIN_ID.RULE.SASS) - .test(/\.s(?:a|c)ss$/); + .rule(CHAIN_ID.RULE.SASS) + .test(/\.s(?:a|c)ss$/) + .merge({ sideEffects: true }) + .resolve.preferRelative(true) + .end(); for (const item of excludes) { rule.exclude.add(item); } - await __internalHelper.applyCSSRule({ - rule, - utils, - config, - context: api.context, - // postcss-loader, resolve-url-loader, sass-loader - importLoaders: 3, - }); + const cssRule = chain.module.rules.get(CHAIN_ID.RULE.CSS); + + // Copy the builtin CSS rules + for (const id of Object.keys(cssRule.uses.entries())) { + const loader = cssRule.uses.get(id); + const options = cloneDeep(loader.get('options')); + if (id === CHAIN_ID.USE.CSS) { + // postcss-loader, resolve-url-loader, sass-loader + options.importLoaders = 3; + } + rule.use(id).loader(loader.get('loader')).options(options); + } rule - .use(utils.CHAIN_ID.USE.RESOLVE_URL) + .use(CHAIN_ID.USE.RESOLVE_URL) .loader(join(__dirname, '../compiled/resolve-url-loader/index.js')) .options({ join: await getResolveUrlJoinFn(), @@ -108,7 +116,7 @@ export const pluginSass = ( sourceMap: false, }) .end() - .use(utils.CHAIN_ID.USE.SASS) + .use(CHAIN_ID.USE.SASS) .loader(join(__dirname, '../compiled/sass-loader/index.js')) .options(options); }); diff --git a/packages/plugin-stylus/src/index.ts b/packages/plugin-stylus/src/index.ts index 3b124cf1c3..4bec7afcd5 100644 --- a/packages/plugin-stylus/src/index.ts +++ b/packages/plugin-stylus/src/index.ts @@ -1,9 +1,7 @@ -import { - PLUGIN_STYLUS_NAME, - type RsbuildPlugin, - __internalHelper, -} from '@rsbuild/core'; -import { deepmerge, mergeChainedOptions } from '@rsbuild/shared'; +import type { RsbuildPlugin } from '@rsbuild/core'; +import { cloneDeep, deepmerge, mergeChainedOptions } from '@rsbuild/shared'; + +export const PLUGIN_STYLUS_NAME = 'rsbuild:stylus'; type StylusOptions = { use?: string[]; @@ -14,7 +12,7 @@ type StylusOptions = { hoistAtrules?: boolean; }; -type StylusLoaderOptions = { +export type PluginStylusOptions = { /** * Options passed to Stylus. */ @@ -25,43 +23,44 @@ type StylusLoaderOptions = { sourceMap?: boolean; }; -export type PluginStylusOptions = StylusLoaderOptions; - -export function pluginStylus(options?: PluginStylusOptions): RsbuildPlugin { - return { - name: PLUGIN_STYLUS_NAME, +export const pluginStylus = (options?: PluginStylusOptions): RsbuildPlugin => ({ + name: PLUGIN_STYLUS_NAME, - setup(api) { - const STYLUS_REGEX = /\.styl(us)?$/; + setup(api) { + api.modifyBundlerChain(async (chain, { CHAIN_ID }) => { + const config = api.getNormalizedConfig(); - api.modifyBundlerChain(async (chain, utils) => { - const config = api.getNormalizedConfig(); + const mergedOptions = mergeChainedOptions({ + defaults: { + sourceMap: config.output.sourceMap.css, + }, + options, + mergeFn: deepmerge, + }); - const mergedOptions = mergeChainedOptions({ - defaults: { - sourceMap: config.output.sourceMap.css, - }, - options, - mergeFn: deepmerge, - }); + const rule = chain.module + .rule(CHAIN_ID.RULE.STYLUS) + .test(/\.styl(us)?$/) + .merge({ sideEffects: true }) + .resolve.preferRelative(true) + .end(); - const rule = chain.module - .rule(utils.CHAIN_ID.RULE.STYLUS) - .test(STYLUS_REGEX); + const cssRule = chain.module.rules.get(CHAIN_ID.RULE.CSS); - await __internalHelper.applyCSSRule({ - rule, - config, - context: api.context, - utils, - importLoaders: 2, - }); + // Copy the builtin CSS rules + for (const id of Object.keys(cssRule.uses.entries())) { + const loader = cssRule.uses.get(id); + const options = cloneDeep(loader.get('options')); + if (id === CHAIN_ID.USE.CSS) { + options.importLoaders = 2; + } + rule.use(id).loader(loader.get('loader')).options(options); + } - rule - .use(utils.CHAIN_ID.USE.STYLUS) - .loader(require.resolve('stylus-loader')) - .options(mergedOptions); - }); - }, - }; -} + rule + .use(CHAIN_ID.USE.STYLUS) + .loader(require.resolve('stylus-loader')) + .options(mergedOptions); + }); + }, +}); diff --git a/packages/shared/src/utils.ts b/packages/shared/src/utils.ts index 67c40dec6c..8dd35371d5 100644 --- a/packages/shared/src/utils.ts +++ b/packages/shared/src/utils.ts @@ -125,7 +125,12 @@ export const kebabCase = (str: string) => .toLowerCase() .replace(/^-/, ''); -export const cloneDeep = (value: T): T => deepmerge({}, value); +export const cloneDeep = (value: T): T => { + if (value === null || value === undefined) { + return value; + } + return deepmerge({}, value); +}; const DEP_MATCH_TEMPLATE = /[\\/]node_modules[\\/]()[\\/]/.source;