diff --git a/packages/next/build/webpack-config.ts b/packages/next/build/webpack-config.ts index 33becc9406aae..e476fb90c2d45 100644 --- a/packages/next/build/webpack-config.ts +++ b/packages/next/build/webpack-config.ts @@ -1867,14 +1867,14 @@ export default async function getBaseWebpackConfig( } if (webpackConfig.module?.rules.length) { - // Remove default CSS Loader - webpackConfig.module.rules = webpackConfig.module.rules.filter( - (r) => - !( - typeof r.oneOf?.[0]?.options === 'object' && - r.oneOf[0].options.__next_css_remove === true + // Remove default CSS Loaders + webpackConfig.module.rules.forEach((r) => { + if (Array.isArray(r.oneOf)) { + r.oneOf = r.oneOf.filter( + (o) => (o as any)[Symbol.for('__next_css_remove')] !== true ) - ) + } + }) } if (webpackConfig.plugins?.length) { // Disable CSS Extraction Plugin diff --git a/packages/next/build/webpack/config/blocks/css/index.ts b/packages/next/build/webpack/config/blocks/css/index.ts index 4b0219dbaeb7b..65bf3d8e11d16 100644 --- a/packages/next/build/webpack/config/blocks/css/index.ts +++ b/packages/next/build/webpack/config/blocks/css/index.ts @@ -23,6 +23,21 @@ const regexCssModules = /\.module\.css$/ const regexSassGlobal = /(? export async function lazyPostCSS( rootDirectory: string, @@ -162,25 +177,14 @@ export const css = curry(async function css( }, ] - const fns: ConfigurationFn[] = [ - loader({ - oneOf: [ - { - // Impossible regex expression - test: /a^/, - loader: 'noop-loader', - options: { __next_css_remove: true }, - }, - ], - }), - ] + const fns: ConfigurationFn[] = [] // CSS cannot be imported in _document. This comes before everything because // global CSS nor CSS modules work in said file. fns.push( loader({ oneOf: [ - { + markRemovable({ test: regexLikeCss, // Use a loose regex so we don't have to crawl the file system to // find the real file name (if present). @@ -191,7 +195,7 @@ export const css = curry(async function css( reason: getCustomDocumentError(), }, }, - }, + }), ], }) ) @@ -201,7 +205,7 @@ export const css = curry(async function css( fns.push( loader({ oneOf: [ - { + markRemovable({ // CSS Modules should never have side effects. This setting will // allow unused CSS to be removed from the production build. // We ensure this by disallowing `:global()` CSS at the top-level @@ -216,7 +220,7 @@ export const css = curry(async function css( not: [/node_modules/], }, use: getCssModuleLoader(ctx, lazyPostCSSInitalizer), - }, + }), ], }) ) @@ -224,7 +228,7 @@ export const css = curry(async function css( loader({ oneOf: [ // Opt-in support for Sass (using .scss or .sass extensions). - { + markRemovable({ // Sass Modules should never have side effects. This setting will // allow unused Sass to be removed from the production build. // We ensure this by disallowing `:global()` Sass at the top-level @@ -243,7 +247,7 @@ export const css = curry(async function css( lazyPostCSSInitalizer, sassPreprocessors ), - }, + }), ], }) ) @@ -252,7 +256,7 @@ export const css = curry(async function css( fns.push( loader({ oneOf: [ - { + markRemovable({ test: [regexCssModules, regexSassModules], use: { loader: 'error-loader', @@ -260,7 +264,7 @@ export const css = curry(async function css( reason: getLocalModuleImportError(), }, }, - }, + }), ], }) ) @@ -269,10 +273,10 @@ export const css = curry(async function css( fns.push( loader({ oneOf: [ - { + markRemovable({ test: [regexCssGlobal, regexSassGlobal], use: require.resolve('next/dist/compiled/ignore-loader'), - }, + }), ], }) ) @@ -280,7 +284,7 @@ export const css = curry(async function css( fns.push( loader({ oneOf: [ - { + markRemovable({ // A global CSS import always has side effects. Webpack will tree // shake the CSS without this option if the issuer claims to have // no side-effects. @@ -303,7 +307,7 @@ export const css = curry(async function css( not: [/node_modules/], }, use: getGlobalCssLoader(ctx, lazyPostCSSInitalizer), - }, + }), ], }) ) @@ -312,7 +316,7 @@ export const css = curry(async function css( fns.push( loader({ oneOf: [ - { + markRemovable({ // A global CSS import always has side effects. Webpack will tree // shake the CSS without this option if the issuer claims to have // no side-effects. @@ -321,14 +325,14 @@ export const css = curry(async function css( test: regexCssGlobal, issuer: { and: [ctx.customAppFile] }, use: getGlobalCssLoader(ctx, lazyPostCSSInitalizer), - }, + }), ], }) ) fns.push( loader({ oneOf: [ - { + markRemovable({ // A global Sass import always has side effects. Webpack will tree // shake the Sass without this option if the issuer claims to have // no side-effects. @@ -341,7 +345,7 @@ export const css = curry(async function css( lazyPostCSSInitalizer, sassPreprocessors ), - }, + }), ], }) ) @@ -353,7 +357,7 @@ export const css = curry(async function css( fns.push( loader({ oneOf: [ - { + markRemovable({ test: [regexCssGlobal, regexSassGlobal], issuer: { and: [/node_modules/] }, use: { @@ -362,7 +366,7 @@ export const css = curry(async function css( reason: getGlobalModuleImportError(), }, }, - }, + }), ], }) ) @@ -372,7 +376,7 @@ export const css = curry(async function css( fns.push( loader({ oneOf: [ - { + markRemovable({ test: [regexCssGlobal, regexSassGlobal], use: { loader: 'error-loader', @@ -380,7 +384,7 @@ export const css = curry(async function css( reason: getGlobalImportError(), }, }, - }, + }), ], }) ) @@ -391,7 +395,7 @@ export const css = curry(async function css( fns.push( loader({ oneOf: [ - { + markRemovable({ // This should only be applied to CSS files issuer: regexLikeCss, // Exclude extensions that webpack handles by default @@ -404,7 +408,7 @@ export const css = curry(async function css( // `asset/resource` always emits a URL reference, where `asset` // might inline the asset as a data URI type: 'asset/resource', - }, + }), ], }) ) diff --git a/test/integration/css-customization/test/index.test.js b/test/integration/css-customization/test/index.test.js index 0468d5e4da3f4..6d90eee87b788 100644 --- a/test/integration/css-customization/test/index.test.js +++ b/test/integration/css-customization/test/index.test.js @@ -142,6 +142,38 @@ describe('CSS Customization Array', () => { }) }) +describe('CSS Customization custom loader', () => { + const appDir = join(fixturesDir, 'custom-configuration-loader') + + beforeAll(async () => { + await remove(join(appDir, '.next')) + }) + + it('should compile successfully', async () => { + const { code, stdout, stderr } = await nextBuild(appDir, [], { + stdout: true, + stderr: true, + }) + expect(code).toBe(0) + expect(stderr).toMatch(/Built-in CSS support is being disabled/) + expect(stdout).toMatch(/Compiled successfully/) + }) + + it(`should've applied style`, async () => { + const pagesFolder = join(appDir, '.next/static/chunks/pages') + + const files = await readdir(pagesFolder) + const indexFiles = files.filter((f) => /^index.+\.js$/.test(f)) + + expect(indexFiles.length).toBe(1) + const indexContent = await readFile( + join(pagesFolder, indexFiles[0]), + 'utf8' + ) + expect(indexContent).toMatch(/\.my-text\.jsx-[0-9a-z]+ {color:red}/) + }) +}) + describe('Bad CSS Customization', () => { const appDir = join(fixturesDir, 'bad-custom-configuration') diff --git a/test/integration/css-fixtures/custom-configuration-loader/next.config.js b/test/integration/css-fixtures/custom-configuration-loader/next.config.js new file mode 100644 index 0000000000000..c2286a6e1d124 --- /dev/null +++ b/test/integration/css-fixtures/custom-configuration-loader/next.config.js @@ -0,0 +1,20 @@ +const config = { + webpack: (config, { defaultLoaders }) => { + config.module.rules.push({ + test: /\.css$/, + use: [ + defaultLoaders.babel, + { + loader: require('styled-jsx/webpack').loader, + options: { + type: (fileName, options) => options.query.type || 'scoped', + }, + }, + ], + }) + + return config + }, +} + +module.exports = config diff --git a/test/integration/css-fixtures/custom-configuration-loader/pages/index.js b/test/integration/css-fixtures/custom-configuration-loader/pages/index.js new file mode 100644 index 0000000000000..e86f5eb6905f0 --- /dev/null +++ b/test/integration/css-fixtures/custom-configuration-loader/pages/index.js @@ -0,0 +1,10 @@ +import styles from '../styles/index.css' + +export default function Home() { + return ( + <> +
This text should be red.
+ + + ) +} diff --git a/test/integration/css-fixtures/custom-configuration-loader/styles/index.css b/test/integration/css-fixtures/custom-configuration-loader/styles/index.css new file mode 100644 index 0000000000000..167caa35ea86e --- /dev/null +++ b/test/integration/css-fixtures/custom-configuration-loader/styles/index.css @@ -0,0 +1,3 @@ +.my-text { + color: red; +}