Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix disabling of built-in CSS support if there is a custom loader #31078

Merged
merged 12 commits into from
Nov 29, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 7 additions & 7 deletions packages/next/build/webpack-config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
72 changes: 38 additions & 34 deletions packages/next/build/webpack/config/blocks/css/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,21 @@ const regexCssModules = /\.module\.css$/
const regexSassGlobal = /(?<!\.module)\.(scss|sass)$/
const regexSassModules = /\.module\.(scss|sass)$/

/**
* Mark a rule as removable if built-in CSS support is disabled
*
* @param {webpack.RuleSetRule} r the rule to mark
*
* @returns {webpack.RuleSetRule} the marked rule
*/
function markRemovable(r: webpack.RuleSetRule): webpack.RuleSetRule {
Object.defineProperty(r, Symbol.for('__next_css_remove'), {
enumerable: false,
value: true,
})
return r
}

let postcssInstancePromise: Promise<any>
export async function lazyPostCSS(
rootDirectory: string,
Expand Down Expand Up @@ -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).
Expand All @@ -191,7 +195,7 @@ export const css = curry(async function css(
reason: getCustomDocumentError(),
},
},
},
}),
],
})
)
Expand All @@ -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
Expand All @@ -216,15 +220,15 @@ export const css = curry(async function css(
not: [/node_modules/],
},
use: getCssModuleLoader(ctx, lazyPostCSSInitalizer),
},
}),
],
})
)
fns.push(
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
Expand All @@ -243,7 +247,7 @@ export const css = curry(async function css(
lazyPostCSSInitalizer,
sassPreprocessors
),
},
}),
],
})
)
Expand All @@ -252,15 +256,15 @@ export const css = curry(async function css(
fns.push(
loader({
oneOf: [
{
markRemovable({
test: [regexCssModules, regexSassModules],
use: {
loader: 'error-loader',
options: {
reason: getLocalModuleImportError(),
},
},
},
}),
],
})
)
Expand All @@ -269,18 +273,18 @@ export const css = curry(async function css(
fns.push(
loader({
oneOf: [
{
markRemovable({
test: [regexCssGlobal, regexSassGlobal],
use: require.resolve('next/dist/compiled/ignore-loader'),
},
}),
],
})
)
} else {
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.
Expand All @@ -303,7 +307,7 @@ export const css = curry(async function css(
not: [/node_modules/],
},
use: getGlobalCssLoader(ctx, lazyPostCSSInitalizer),
},
}),
],
})
)
Expand All @@ -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.
Expand All @@ -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.
Expand All @@ -341,7 +345,7 @@ export const css = curry(async function css(
lazyPostCSSInitalizer,
sassPreprocessors
),
},
}),
],
})
)
Expand All @@ -353,7 +357,7 @@ export const css = curry(async function css(
fns.push(
loader({
oneOf: [
{
markRemovable({
test: [regexCssGlobal, regexSassGlobal],
issuer: { and: [/node_modules/] },
use: {
Expand All @@ -362,7 +366,7 @@ export const css = curry(async function css(
reason: getGlobalModuleImportError(),
},
},
},
}),
],
})
)
Expand All @@ -372,15 +376,15 @@ export const css = curry(async function css(
fns.push(
loader({
oneOf: [
{
markRemovable({
test: [regexCssGlobal, regexSassGlobal],
use: {
loader: 'error-loader',
options: {
reason: getGlobalImportError(),
},
},
},
}),
],
})
)
Expand All @@ -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
Expand All @@ -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',
},
}),
],
})
)
Expand Down
32 changes: 32 additions & 0 deletions test/integration/css-customization/test/index.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -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')

Expand Down
Original file line number Diff line number Diff line change
@@ -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
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
import styles from '../styles/index.css'

export default function Home() {
return (
<>
<div className="my-text">This text should be red.</div>
<style jsx>{styles}</style>
</>
)
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
.my-text {
color: red;
}