diff --git a/e2e/react/src/react.test.ts b/e2e/react/src/react.test.ts index 671fa16c3ae289..96f2e1a78c9ba4 100644 --- a/e2e/react/src/react.test.ts +++ b/e2e/react/src/react.test.ts @@ -50,6 +50,17 @@ describe('React Applications', () => { ` ); + // Make sure global stylesheets are properly processed. + const stylesPath = `apps/${appName}/src/styles.css`; + updateFile( + stylesPath, + ` + .foobar { + background-image: url('/bg.png'); + } + ` + ); + const libTestResults = await runCLIAsync(`test ${libName}`); expect(libTestResults.combinedOutput).toContain( 'Test Suites: 1 passed, 1 total' diff --git a/packages/webpack/src/utils/with-web.ts b/packages/webpack/src/utils/with-web.ts index 586891251038d7..bad0e5080cebeb 100644 --- a/packages/webpack/src/utils/with-web.ts +++ b/packages/webpack/src/utils/with-web.ts @@ -214,35 +214,78 @@ export function withWeb() { }, ]; - const rules: RuleSetRule[] = [ + const globalStyleRules: RuleSetRule[] = [ { - test: /\.css$|\.scss$|\.sass$|\.less$|\.styl$/, - oneOf: [ - ...cssModuleRules, - ...globalCssRules, - // load global css as css files + test: /\.css$/, + use: getCommonLoadersForGlobalStyle(options, includePaths), + }, + { + test: /\.scss$|\.sass$/, + use: [ + ...getCommonLoadersForGlobalStyle(options, includePaths), + { + loader: require.resolve('sass-loader'), + options: { + implementation: require('sass'), + sourceMap: options.sourceMap, + sassOptions: { + fiber: false, + // bootstrap-sass requires a minimum precision of 8 + precision: 8, + includePaths, + }, + }, + }, + ], + }, + { + test: /\.less$/, + use: [ + ...getCommonLoadersForGlobalStyle(options, includePaths), { - test: /\.css$|\.scss$|\.sass$|\.less$|\.styl$/, - include: globalStylePaths, - use: [ - { - loader: MiniCssExtractPlugin.loader, - options: { esModule: true }, + loader: require.resolve('less-loader'), + options: { + sourceMap: options.sourceMap, + lessOptions: { + javascriptEnabled: true, + ...lessPathOptions, }, - { loader: require.resolve('css-loader') }, - { - loader: require.resolve('postcss-loader'), - options: { - implementation: require('postcss'), - postcssOptions: postcssOptionsCreator(options, includePaths), - }, + }, + }, + ], + }, + { + test: /\.styl$/, + use: [ + ...getCommonLoadersForGlobalStyle(options, includePaths), + { + loader: require.resolve('stylus-loader'), + options: { + sourceMap: options.sourceMap, + stylusOptions: { + include: includePaths, }, - ], + }, }, ], }, ]; + const rules: RuleSetRule[] = [ + // CSS Module and global CSS stylesheets used in .ts, .tsx files. + { + test: /\.css$|\.scss$|\.sass$|\.less$|\.styl$/, + exclude: globalStylePaths, + oneOf: [...cssModuleRules, ...globalCssRules], + }, + // Global stylesheets included in `styles` option of webpack executor. + { + test: /\.css$|\.scss$|\.sass$|\.less$|\.styl$/, + include: globalStylePaths, + oneOf: globalStyleRules, + }, + ]; + plugins.push( // extract global css from js files into own css file new MiniCssExtractPlugin({ @@ -305,7 +348,6 @@ export function withWeb() { ...config.module, rules: [ ...(config.module.rules ?? []), - ...rules, { test: /\.(bmp|png|jpe?g|gif|webp|avif)$/, type: 'asset', @@ -322,6 +364,7 @@ export function withWeb() { name: `[name]${hashFormat.file}.[ext]`, }, }, + ...rules, ], }; processed.add(config); @@ -432,6 +475,26 @@ function getCommonLoadersForGlobalCss( ]; } +function getCommonLoadersForGlobalStyle( + options: NormalizedWebpackExecutorOptions, + includePaths: string[] +) { + return [ + { + loader: MiniCssExtractPlugin.loader, + options: { esModule: true }, + }, + { loader: require.resolve('css-loader'), options: { url: false } }, + { + loader: require.resolve('postcss-loader'), + options: { + implementation: require('postcss'), + postcssOptions: postcssOptionsCreator(options, includePaths), + }, + }, + ]; +} + function postcssOptionsCreator( options: NormalizedWebpackExecutorOptions, includePaths: string[]