feat(css): add strictPostcssConfiguration flag (#20914)

This pull request adds `future.strictPostcssConfiguration`, allowing users to opt-into the more strict PostCSS configuration loading.

This stricter PostCSS configuration loading ensures that CSS can be cached across builds.
This commit is contained in:
Joe Haddad 2021-01-11 12:19:56 -05:00 committed by GitHub
parent 16ff4390a0
commit d0e28549af
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
10 changed files with 111 additions and 5 deletions

View file

@ -1208,6 +1208,7 @@ export default async function getBaseWebpackConfig(
assetPrefix: config.assetPrefix || '',
sassOptions: config.sassOptions,
productionBrowserSourceMaps: config.productionBrowserSourceMaps,
future: config.future,
})
let originalDevtool = webpackConfig.devtool
@ -1330,7 +1331,7 @@ export default async function getBaseWebpackConfig(
(e) => (e as any).__next_css_remove !== true
)
}
} else {
} else if (!config.future.strictPostcssConfiguration) {
await __overrideCssConfiguration(dir, !dev, webpackConfig)
}

View file

@ -78,10 +78,7 @@ export const css = curry(async function css(
const postCssPlugins = await getPostCssPlugins(
ctx.rootDirectory,
ctx.isProduction,
// TODO: In the future, we should stop supporting old CSS setups and
// unconditionally inject ours. When that happens, we should remove this
// function argument.
true
!ctx.future.strictPostcssConfiguration
)
// CSS cannot be imported in _document. This comes before everything because

View file

@ -1,4 +1,5 @@
import webpack from 'webpack'
import { NextConfig } from '../../../next-server/server/config'
import { base } from './blocks/base'
import { css } from './blocks/css'
import { ConfigurationContext, pipe } from './utils'
@ -13,6 +14,7 @@ export async function build(
assetPrefix,
sassOptions,
productionBrowserSourceMaps,
future,
}: {
rootDirectory: string
customAppFile: string | null
@ -21,6 +23,7 @@ export async function build(
assetPrefix: string
sassOptions: any
productionBrowserSourceMaps: boolean
future: NextConfig['future']
}
): Promise<webpack.Configuration> {
const ctx: ConfigurationContext = {
@ -37,6 +40,7 @@ export async function build(
: '',
sassOptions,
productionBrowserSourceMaps,
future,
}
const fn = pipe(base(ctx), css(ctx))

View file

@ -1,4 +1,5 @@
import webpack from 'webpack'
import { NextConfig } from '../../../next-server/server/config'
export type ConfigurationContext = {
rootDirectory: string
@ -14,6 +15,8 @@ export type ConfigurationContext = {
sassOptions: any
productionBrowserSourceMaps: boolean
future: NextConfig['future']
}
export type ConfigurationFn = (

View file

@ -31,6 +31,11 @@ export type NextConfig = { [key: string]: any } & {
redirects?: () => Promise<Redirect[]>
trailingSlash?: boolean
future: {
strictPostcssConfiguration: boolean
excludeDefaultMomentLocales: boolean
}
}
const defaultConfig: NextConfig = {
@ -83,6 +88,7 @@ const defaultConfig: NextConfig = {
scriptLoader: false,
},
future: {
strictPostcssConfiguration: false,
excludeDefaultMomentLocales: false,
},
serverRuntimeConfig: {},

View file

@ -108,6 +108,39 @@ describe('Legacy Next-CSS Customization', () => {
})
})
describe('Custom CSS Customization via Webpack', () => {
const appDir = join(fixturesDir, 'custom-configuration-webpack')
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(stdout).toMatch(/Compiled successfully/)
expect(stderr).not.toMatch(
/Built-in CSS support is being disabled due to custom CSS configuration being detected/
)
})
it(`should've compiled and prefixed`, async () => {
const cssFolder = join(appDir, '.next/static/css')
const files = await readdir(cssFolder)
const cssFiles = files.filter((f) => /\.css$/.test(f))
expect(cssFiles.length).toBe(1)
const cssContent = await readFile(join(cssFolder, cssFiles[0]), 'utf8')
expect(cssContent.replace(/\/\*.*?\*\//g, '').trim()).toMatchInlineSnapshot(
`"@media (480px <= width < 768px){::placeholder{color:green}}.video{max-width:400px;max-height:300px}"`
)
})
})
describe('CSS Customization Array', () => {
const appDir = join(fixturesDir, 'custom-configuration-arr')

View file

@ -0,0 +1,36 @@
module.exports = {
onDemandEntries: {
maxInactiveAge: 1000 * 60 * 60,
},
webpack(config) {
modifyLoaderConfig(
config.module.rules,
[/(?<!\.module)\.css$/, /\.module\.css$/],
(rule) => {
if (!Array.isArray(rule.use)) return
rule.use.forEach((u) => {
if (u.options.postcssOptions) {
u.options.postcssOptions.plugins = [
require('postcss-short-size')({
// Add a prefix to test that configuration is passed
prefix: 'xyz',
}),
]
}
})
}
)
return config
},
future: { strictPostcssConfiguration: true },
}
function modifyLoaderConfig(rules, regexes, cb) {
rules.forEach((rule) => {
if (rule.oneOf) return modifyLoaderConfig(rule.oneOf, regexes, cb)
regexes.forEach((regex) => {
if (rule.test && rule.test.toString() === regex.toString()) cb(rule)
})
})
}

View file

@ -0,0 +1,12 @@
import React from 'react'
import App from 'next/app'
import '../styles/global.css'
class MyApp extends App {
render() {
const { Component, pageProps } = this.props
return <Component {...pageProps} />
}
}
export default MyApp

View file

@ -0,0 +1,3 @@
export default function Home() {
return <div />
}

View file

@ -0,0 +1,11 @@
/* this should pass through untransformed */
@media (480px <= width < 768px) {
::placeholder {
color: green;
}
}
/* this should be transformed to width/height */
.video {
-xyz-max-size: 400px 300px;
}