diff --git a/packages/rspack/src/utils/lib/apply-react-config.ts b/packages/rspack/src/utils/lib/apply-react-config.ts new file mode 100644 index 0000000000000..04bf5b67455e2 --- /dev/null +++ b/packages/rspack/src/utils/lib/apply-react-config.ts @@ -0,0 +1,65 @@ +import { Configuration, RspackOptionsNormalized } from '@rspack/core'; +import { SvgrOptions } from '../model'; + +export function applyReactConfig( + options: { svgr?: boolean | SvgrOptions }, + config: Partial<RspackOptionsNormalized | Configuration> = {} +): void { + if (global.NX_GRAPH_CREATION) return; + + addHotReload(config); + + if (options.svgr !== false || typeof options.svgr === 'object') { + removeSvgLoaderIfPresent(config); + + const defaultSvgrOptions = { + svgo: false, + titleProp: true, + ref: true, + }; + + const svgrOptions = + typeof options.svgr === 'object' ? options.svgr : defaultSvgrOptions; + + config.module.rules.push( + { + test: /\.svg$/i, + type: 'asset', + resourceQuery: /react/, // *.svg?react + }, + { + test: /\.svg$/i, + issuer: /\.[jt]sx?$/, + resourceQuery: { not: [/react/] }, // exclude react component if *.svg?react + use: [{ loader: '@svgr/webpack', options: svgrOptions }], + } + ); + } + + // enable rspack node api + config.node = { + __dirname: true, + __filename: true, + }; +} + +function removeSvgLoaderIfPresent( + config: Partial<RspackOptionsNormalized | Configuration> +) { + const svgLoaderIdx = config.module.rules.findIndex( + (rule) => typeof rule === 'object' && rule.test.toString().includes('svg') + ); + if (svgLoaderIdx === -1) return; + config.module.rules.splice(svgLoaderIdx, 1); +} + +function addHotReload( + config: Partial<RspackOptionsNormalized | Configuration> +) { + const ReactRefreshPlugin = require('@rspack/plugin-react-refresh'); + const isDev = + process.env.NODE_ENV === 'development' || config.mode === 'development'; + if (isDev) { + config.plugins.push(new ReactRefreshPlugin({ overlay: false })); + } +} diff --git a/packages/rspack/src/utils/model.ts b/packages/rspack/src/utils/model.ts index 0b518d3bc6f5d..b5b2dd18c7f8b 100644 --- a/packages/rspack/src/utils/model.ts +++ b/packages/rspack/src/utils/model.ts @@ -17,3 +17,9 @@ export interface EmittedFile { initial: boolean; asset?: boolean; } + +export interface SvgrOptions { + svgo?: boolean; + titleProp?: boolean; + ref?: boolean; +} diff --git a/packages/rspack/src/utils/with-react.ts b/packages/rspack/src/utils/with-react.ts index 396a7c5379d34..c11e9456c4e9d 100644 --- a/packages/rspack/src/utils/with-react.ts +++ b/packages/rspack/src/utils/with-react.ts @@ -1,77 +1,24 @@ import { Configuration } from '@rspack/core'; -import { withWeb } from './with-web'; import { NxRspackExecutionContext } from './config'; +import { withWeb, WithWebOptions } from './with-web'; +import { applyReactConfig } from './lib/apply-react-config'; +import { SvgrOptions } from './model'; -export function withReact(opts = {}) { +export interface WithReactOptions extends WithWebOptions { + svgr?: boolean | SvgrOptions; +} + +export function withReact(opts: WithReactOptions = {}) { return function makeConfig( config: Configuration, { options, context }: NxRspackExecutionContext ): Configuration { - const isDev = - process.env.NODE_ENV === 'development' || options.mode === 'development'; - config = withWeb({ ...opts, cssModules: true })(config, { options, context, }); - // eslint-disable-next-line @typescript-eslint/no-var-requires - const ReactRefreshPlugin = require('@rspack/plugin-react-refresh'); - - const react = { - runtime: 'automatic', - development: isDev, - refresh: isDev, - }; - - return { - ...config, - plugins: [ - ...(config.plugins || []), - isDev && new ReactRefreshPlugin(), - ].filter(Boolean), - module: { - ...config.module, - rules: [ - ...(config.module.rules || []), - { - test: /\.jsx$/, - loader: 'builtin:swc-loader', - exclude: /node_modules/, - options: { - jsc: { - parser: { - syntax: 'ecmascript', - jsx: true, - }, - transform: { - react, - }, - externalHelpers: true, - }, - }, - type: 'javascript/auto', - }, - { - test: /\.tsx$/, - loader: 'builtin:swc-loader', - exclude: /node_modules/, - options: { - jsc: { - parser: { - syntax: 'typescript', - tsx: true, - }, - transform: { - react, - }, - externalHelpers: true, - }, - }, - type: 'javascript/auto', - }, - ], - }, - }; + applyReactConfig(opts, config); + return config; }; }