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:
parent
16ff4390a0
commit
d0e28549af
10 changed files with 111 additions and 5 deletions
|
@ -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)
|
||||
}
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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))
|
||||
|
|
|
@ -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 = (
|
||||
|
|
|
@ -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: {},
|
||||
|
|
|
@ -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')
|
||||
|
||||
|
|
|
@ -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)
|
||||
})
|
||||
})
|
||||
}
|
|
@ -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
|
|
@ -0,0 +1,3 @@
|
|||
export default function Home() {
|
||||
return <div />
|
||||
}
|
|
@ -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;
|
||||
}
|
Loading…
Reference in a new issue