feat(webpack)!: update postcss to v8 (#9671)

Co-authored-by: Daniel Roe <daniel@roe.dev>
This commit is contained in:
Xin Du (Clark) 2023-02-02 15:13:28 +00:00 committed by GitHub
parent 9677fbe534
commit 454a9af242
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
20 changed files with 5837 additions and 1314 deletions

1
.iyarc
View File

@ -3,3 +3,4 @@
GHSA-rp65-9cf3-cjxr GHSA-rp65-9cf3-cjxr
GHSA-3j8f-xvm3-ffx4 GHSA-3j8f-xvm3-ffx4
GHSA-f8q6-p94x-37v3

View File

@ -17,9 +17,11 @@ export default {
css: ['~/assets/css/tailwind.css'], css: ['~/assets/css/tailwind.css'],
build: { build: {
postcss: { postcss: {
postcssOptions: {
plugins: { plugins: {
tailwindcss: path.join(__dirname, './tailwind.config.js') tailwindcss: path.join(__dirname, './tailwind.config.js')
} }
} }
} }
} }
}

View File

@ -12,8 +12,10 @@ export default {
build: { build: {
extractCSS: true, extractCSS: true,
postcss: { postcss: {
postcssOptions: {
plugins: { plugins: {
tailwindcss: path.resolve('./tailwind.js') tailwindcss: path.resolve('./tailwind.js')
}
}, },
preset: { autoprefixer: { grid: true } } preset: { autoprefixer: { grid: true } }
}, },

File diff suppressed because it is too large Load Diff

View File

@ -88,7 +88,8 @@ export default () => ({
preset: { preset: {
// https://cssdb.org/#staging-process // https://cssdb.org/#staging-process
stage: 2 stage: 2
} },
postcssOptions: {}
}, },
html: { html: {
minify: { minify: {

View File

@ -440,6 +440,17 @@ export function getNuxtConfig (_options) {
delete options.build.crossorigin delete options.build.crossorigin
} }
if (options.build.postcss.plugins) {
consola.warn('`postcss.plugins` option has been moved to `postcss.postcssOptions.plugins` for aligning `postcss-loader` format.')
options.build.postcss.postcssOptions.plugins = options.build.postcss.plugins
delete options.build.postcss.plugins
}
if (options.buildModules && options.buildModules.includes('@nuxt/postcss8')) {
consola.info('`@nuxt/postcss8` is disabled since nuxt has upgraded to postcss v8.')
options.buildModules = options.buildModules.filter(module => module !== '@nuxt/postcss8')
}
const { timing } = options.server const { timing } = options.server
if (timing) { if (timing) {
options.server.timing = { total: true, ...timing } options.server.timing = { total: true, ...timing }

View File

@ -138,6 +138,7 @@ Object {
"parallel": false, "parallel": false,
"plugins": Array [], "plugins": Array [],
"postcss": Object { "postcss": Object {
"postcssOptions": Object {},
"preset": Object { "preset": Object {
"stage": 2, "stage": 2,
}, },

View File

@ -110,6 +110,7 @@ Object {
"parallel": false, "parallel": false,
"plugins": Array [], "plugins": Array [],
"postcss": Object { "postcss": Object {
"postcssOptions": Object {},
"preset": Object { "preset": Object {
"stage": 2, "stage": 2,
}, },
@ -500,6 +501,7 @@ Object {
"parallel": false, "parallel": false,
"plugins": Array [], "plugins": Array [],
"postcss": Object { "postcss": Object {
"postcssOptions": Object {},
"preset": Object { "preset": Object {
"stage": 2, "stage": 2,
}, },

View File

@ -121,8 +121,8 @@ interface PostcssConfiguration {
order?: PostcssOrderPreset | string[] | ((names: string[], presets: PostcssOrderPresetFunctions) => string[]) order?: PostcssOrderPreset | string[] | ((names: string[], presets: PostcssOrderPresetFunctions) => string[])
plugins?: { plugins?: {
[key: string]: false | { [key: string]: any } [key: string]: false | { [key: string]: any }
} | ((loader: WebpackLoaderNamespace.LoaderContext) => PostcssPlugin<any>[]) | Array<[string | PostcssPlugin<any>, any] | string | PostcssPlugin<any>> } | ((loader: WebpackLoaderNamespace.LoaderContext) => PostcssPlugin[]) | Array<[string | PostcssPlugin, any] | string | PostcssPlugin>
preset?: { readonly preset?: {
autoprefixer?: false | AutoprefixerOptions autoprefixer?: false | AutoprefixerOptions
browsers?: string browsers?: string
exportTo?: string | string[] | Partial<PostcssVariableMap> | ((map: PostcssVariableMap) => Partial<PostcssVariableMap>) exportTo?: string | string[] | Partial<PostcssVariableMap> | ((map: PostcssVariableMap) => Partial<PostcssVariableMap>)
@ -130,8 +130,8 @@ interface PostcssConfiguration {
[key: string]: boolean | { [key: string]: any } [key: string]: boolean | { [key: string]: any }
} }
importFrom?: string | string[] | Partial<PostcssVariableMap> | (() => Partial<PostcssVariableMap>) importFrom?: string | string[] | Partial<PostcssVariableMap> | (() => Partial<PostcssVariableMap>)
insertAfter?: { [key: string]: PostcssPlugin<any> } insertAfter?: { [key: string]: PostcssPlugin }
insertBefore?: { [key: string]: PostcssPlugin<any> } insertBefore?: { [key: string]: PostcssPlugin }
preserve?: boolean preserve?: boolean
stage?: 0 | 1 | 2 | 3 | 4 | false stage?: 0 | 1 | 2 | 3 | 4 | false
} }
@ -166,7 +166,7 @@ export interface NuxtOptionsBuild {
optimizeCSS?: OptimizeCssAssetsWebpackPluginOptions | boolean optimizeCSS?: OptimizeCssAssetsWebpackPluginOptions | boolean
parallel?: boolean parallel?: boolean
plugins?: WebpackPlugin[] plugins?: WebpackPlugin[]
postcss?: string[] | boolean | PostcssConfiguration | (() => PostcssConfiguration) postcss?: string[] | boolean | { postcssOptions: PostcssConfiguration | (() => PostcssConfiguration) }
profile?: boolean profile?: boolean
publicPath?: string publicPath?: string
quiet?: boolean quiet?: boolean

View File

@ -16,8 +16,8 @@
"cache-loader": "^4.1.0", "cache-loader": "^4.1.0",
"caniuse-lite": "^1.0.30001450", "caniuse-lite": "^1.0.30001450",
"consola": "^2.15.3", "consola": "^2.15.3",
"css-loader": "^4.3.0", "css-loader": "^5.2.7",
"cssnano": "^4.1.11", "cssnano": "^5.1.14",
"eventsource-polyfill": "^0.9.6", "eventsource-polyfill": "^0.9.6",
"extract-css-chunks-webpack-plugin": "^4.9.0", "extract-css-chunks-webpack-plugin": "^4.9.0",
"file-loader": "^6.2.0", "file-loader": "^6.2.0",
@ -27,15 +27,15 @@
"html-webpack-plugin": "^4.5.1", "html-webpack-plugin": "^4.5.1",
"lodash": "^4.17.21", "lodash": "^4.17.21",
"memory-fs": "^0.5.0", "memory-fs": "^0.5.0",
"optimize-css-assets-webpack-plugin": "^5.0.8", "optimize-css-assets-webpack-plugin": "^6.0.1",
"pify": "^5.0.0", "pify": "^5.0.0",
"pnp-webpack-plugin": "^1.7.0", "pnp-webpack-plugin": "^1.7.0",
"postcss": "^7.0.32", "postcss": "^8.4.21",
"postcss-import": "^12.0.1", "postcss-import": "^15.1.0",
"postcss-import-resolver": "^2.0.0", "postcss-import-resolver": "^2.0.0",
"postcss-loader": "^3.0.0", "postcss-loader": "^4.3.0",
"postcss-preset-env": "^6.7.1", "postcss-preset-env": "^8.0.1",
"postcss-url": "^8.0.0", "postcss-url": "^10.1.3",
"semver": "^7.3.8", "semver": "^7.3.8",
"std-env": "^3.3.1", "std-env": "^3.3.1",
"style-resources-loader": "^1.5.0", "style-resources-loader": "^1.5.0",

View File

@ -1,208 +0,0 @@
import fs from 'fs'
import path from 'path'
import consola from 'consola'
import { defaults, merge, cloneDeep } from 'lodash'
import createResolver from 'postcss-import-resolver'
import { isPureObject } from '@nuxt/utils'
export const orderPresets = {
cssnanoLast (names) {
const nanoIndex = names.indexOf('cssnano')
if (nanoIndex !== names.length - 1) {
names.push(names.splice(nanoIndex, 1)[0])
}
return names
},
presetEnvLast (names) {
const nanoIndex = names.indexOf('postcss-preset-env')
if (nanoIndex !== names.length - 1) {
names.push(names.splice(nanoIndex, 1)[0])
}
return names
},
presetEnvAndCssnanoLast (names) {
return orderPresets.cssnanoLast(orderPresets.presetEnvLast(names))
}
}
function postcssConfigFileWarning () {
if (postcssConfigFileWarning.executed) {
return
}
consola.warn('Please use `build.postcss` in your nuxt.config.js instead of an external config file. Support for such files will be removed in Nuxt 3 as they remove all defaults set by Nuxt and can cause severe problems with features like alias resolving inside your CSS.')
postcssConfigFileWarning.executed = true
}
export default class PostcssConfig {
constructor (buildContext) {
this.buildContext = buildContext
}
get cssSourceMap () {
return this.buildContext.buildOptions.cssSourceMap
}
get postcssOptions () {
return this.buildContext.buildOptions.postcss
}
get postcssImportAlias () {
const alias = { ...this.buildContext.options.alias }
for (const key in alias) {
if (key.startsWith('~')) {
continue
}
const newKey = '~' + key
if (!alias[newKey]) {
alias[newKey] = alias[key]
}
}
return alias
}
get defaultPostcssOptions () {
const { dev, srcDir, rootDir, modulesDir } = this.buildContext.options
return {
plugins: {
// https://github.com/postcss/postcss-import
'postcss-import': {
resolve: createResolver({
alias: this.postcssImportAlias,
modules: [srcDir, rootDir, ...modulesDir]
})
},
// https://github.com/postcss/postcss-url
'postcss-url': {},
// https://github.com/csstools/postcss-preset-env
// TODO: enable when https://github.com/csstools/postcss-preset-env/issues/191 gets closed
// 'postcss-preset-env': this.preset || {},
cssnano: dev
? false
: {
preset: ['default', {
// Keep quotes in font values to prevent from HEX conversion
// https://github.com/nuxt/nuxt.js/issues/6306
minifyFontValues: { removeQuotes: false }
}]
}
},
// Array, String or Function
order: 'presetEnvAndCssnanoLast'
}
}
searchConfigFile () {
// Search for postCSS config file and use it if exists
// https://github.com/michael-ciniawsky/postcss-load-config
// TODO: Remove in Nuxt 3
const { srcDir, rootDir } = this.buildContext.options
for (const dir of [srcDir, rootDir]) {
for (const file of [
'postcss.config.js',
'.postcssrc.js',
'.postcssrc',
'.postcssrc.json',
'.postcssrc.yaml'
]) {
const configFile = path.resolve(dir, file)
if (fs.existsSync(configFile)) {
postcssConfigFileWarning()
return configFile
}
}
}
}
configFromFile () {
const loaderConfig = (this.postcssOptions && this.postcssOptions.config) || {}
loaderConfig.path = loaderConfig.path || this.searchConfigFile()
if (loaderConfig.path) {
return {
config: loaderConfig
}
}
}
normalize (postcssOptions) {
// TODO: Remove in Nuxt 3
if (Array.isArray(postcssOptions)) {
consola.warn('Using an Array as `build.postcss` will be deprecated in Nuxt 3. Please switch to the object' +
' declaration')
postcssOptions = { plugins: postcssOptions }
}
return postcssOptions
}
sortPlugins ({ plugins, order }) {
const names = Object.keys(plugins)
if (typeof order === 'string') {
order = orderPresets[order]
}
return typeof order === 'function' ? order(names, orderPresets) : (order || names)
}
loadPlugins (postcssOptions) {
const { plugins } = postcssOptions
if (isPureObject(plugins)) {
// Map postcss plugins into instances on object mode once
postcssOptions.plugins = this.sortPlugins(postcssOptions)
.map((p) => {
const plugin = this.buildContext.nuxt.resolver.requireModule(p, { paths: [__dirname] })
const opts = plugins[p]
if (opts === false) {
return false // Disabled
}
return plugin(opts)
})
.filter(Boolean)
}
}
config () {
/* istanbul ignore if */
if (!this.postcssOptions) {
return false
}
let postcssOptions = this.configFromFile()
if (postcssOptions) {
return {
postcssOptions,
sourceMap: this.cssSourceMap
}
}
postcssOptions = this.normalize(cloneDeep(this.postcssOptions))
// Apply default plugins
if (isPureObject(postcssOptions)) {
if (postcssOptions.preset) {
this.preset = postcssOptions.preset
delete postcssOptions.preset
}
if (Array.isArray(postcssOptions.plugins)) {
defaults(postcssOptions, this.defaultPostcssOptions)
} else {
// Keep the order of default plugins
postcssOptions = merge({}, this.defaultPostcssOptions, postcssOptions)
this.loadPlugins(postcssOptions)
}
const { execute } = postcssOptions
delete postcssOptions.execute
delete postcssOptions.order
return {
execute,
postcssOptions,
sourceMap: this.cssSourceMap
}
}
}
}

View File

@ -39,10 +39,18 @@ export default class PostcssConfig {
this.buildContext = buildContext this.buildContext = buildContext
} }
get postcssOptions () { get cssSourceMap () {
return this.buildContext.buildOptions.cssSourceMap
}
get postcssLoaderOptions () {
return this.buildContext.buildOptions.postcss return this.buildContext.buildOptions.postcss
} }
get postcssOptions () {
return this.buildContext.buildOptions.postcss.postcssOptions
}
get postcssImportAlias () { get postcssImportAlias () {
const alias = { ...this.buildContext.options.alias } const alias = { ...this.buildContext.options.alias }
@ -59,10 +67,9 @@ export default class PostcssConfig {
return alias return alias
} }
get defaultConfig () { get defaultPostcssOptions () {
const { dev, srcDir, rootDir, modulesDir } = this.buildContext.options const { dev, srcDir, rootDir, modulesDir } = this.buildContext.options
return { return {
sourceMap: this.buildContext.buildOptions.cssSourceMap,
plugins: { plugins: {
// https://github.com/postcss/postcss-import // https://github.com/postcss/postcss-import
'postcss-import': { 'postcss-import': {
@ -76,7 +83,8 @@ export default class PostcssConfig {
'postcss-url': {}, 'postcss-url': {},
// https://github.com/csstools/postcss-preset-env // https://github.com/csstools/postcss-preset-env
'postcss-preset-env': this.preset || {}, 'postcss-preset-env': this.postcssLoaderOptions.preset || {},
cssnano: dev cssnano: dev
? false ? false
: { : {
@ -116,24 +124,31 @@ export default class PostcssConfig {
configFromFile () { configFromFile () {
const loaderConfig = (this.postcssOptions && this.postcssOptions.config) || {} const loaderConfig = (this.postcssOptions && this.postcssOptions.config) || {}
loaderConfig.path = loaderConfig.path || this.searchConfigFile()
if (loaderConfig.path) { if (loaderConfig.path) {
consola.warn('`postcss-loader` has been removed `config.path` option, please use `config` instead.')
return { return {
sourceMap: this.buildContext.buildOptions.cssSourceMap, config: loaderConfig.path
config: loaderConfig }
}
const postcssConfigFile = this.searchConfigFile()
if (postcssConfigFile) {
return {
config: postcssConfigFile
} }
} }
} }
normalize (config) { normalize (postcssOptions) {
// TODO: Remove in Nuxt 3 // TODO: Remove in Nuxt 3
if (Array.isArray(config)) { if (Array.isArray(postcssOptions)) {
consola.warn('Using an Array as `build.postcss` will be deprecated in Nuxt 3. Please switch to the object' + consola.warn('Using an Array as `build.postcss` will be deprecated in Nuxt 3. Please switch to the object' +
' declaration') ' declaration')
config = { plugins: config } postcssOptions = { plugins: postcssOptions }
} }
return config return postcssOptions
} }
sortPlugins ({ plugins, order }) { sortPlugins ({ plugins, order }) {
@ -144,11 +159,11 @@ export default class PostcssConfig {
return typeof order === 'function' ? order(names, orderPresets) : (order || names) return typeof order === 'function' ? order(names, orderPresets) : (order || names)
} }
loadPlugins (config) { loadPlugins (postcssOptions) {
const { plugins } = config const { plugins } = postcssOptions
if (isPureObject(plugins)) { if (isPureObject(plugins)) {
// Map postcss plugins into instances on object mode once // Map postcss plugins into instances on object mode once
config.plugins = this.sortPlugins(config) postcssOptions.plugins = this.sortPlugins(postcssOptions)
.map((p) => { .map((p) => {
const plugin = this.buildContext.nuxt.resolver.requireModule(p, { paths: [__dirname] }) const plugin = this.buildContext.nuxt.resolver.requireModule(p, { paths: [__dirname] })
const opts = plugins[p] const opts = plugins[p]
@ -167,27 +182,35 @@ export default class PostcssConfig {
return false return false
} }
let config = this.configFromFile() let postcssOptions = this.configFromFile()
if (config) { if (postcssOptions) {
return config return {
postcssOptions,
sourceMap: this.cssSourceMap
}
} }
config = this.normalize(cloneDeep(this.postcssOptions)) postcssOptions = this.normalize(cloneDeep(this.postcssOptions))
// Apply default plugins // Apply default plugins
if (isPureObject(config)) { if (isPureObject(postcssOptions)) {
if (config.preset) { if (Array.isArray(postcssOptions.plugins)) {
this.preset = config.preset defaults(postcssOptions, this.defaultPostcssOptions)
delete config.preset
}
if (Array.isArray(config.plugins)) {
defaults(config, this.defaultConfig)
} else { } else {
// Keep the order of default plugins // Keep the order of default plugins
config = merge({}, this.defaultConfig, config) postcssOptions = merge({}, this.defaultPostcssOptions, postcssOptions)
this.loadPlugins(config) this.loadPlugins(postcssOptions)
}
delete postcssOptions.order
const postcssLoaderOptions = this.postcssLoaderOptions
delete postcssLoaderOptions.preset
return {
sourceMap: this.cssSourceMap,
...postcssLoaderOptions,
postcssOptions
} }
return config
} }
} }
} }

View File

@ -4,7 +4,6 @@ import ExtractCssChunksPlugin from 'extract-css-chunks-webpack-plugin'
import { wrapArray } from '@nuxt/utils' import { wrapArray } from '@nuxt/utils'
import PostcssConfig from './postcss' import PostcssConfig from './postcss'
import PostcssV8Config from './postcss-v8'
export default class StyleLoader { export default class StyleLoader {
constructor (buildContext, { isServer, perfLoader, resolveModule }) { constructor (buildContext, { isServer, perfLoader, resolveModule }) {
@ -13,17 +12,11 @@ export default class StyleLoader {
this.perfLoader = perfLoader this.perfLoader = perfLoader
this.resolveModule = resolveModule this.resolveModule = resolveModule
const { postcss: postcssOptions } = buildContext.options.build const { postcss } = buildContext.options.build
if (postcssOptions) { if (postcss) {
const postcss = require(resolveModule('postcss'))
// postcss >= v8
if (!postcss.vendor) {
this.postcssConfig = new PostcssV8Config(buildContext)
} else {
this.postcssConfig = new PostcssConfig(buildContext) this.postcssConfig = new PostcssConfig(buildContext)
} }
} }
}
get extractCSS () { get extractCSS () {
return this.buildContext.buildOptions.extractCSS return this.buildContext.buildOptions.extractCSS

View File

@ -7,10 +7,6 @@
], ],
"ignoreDeps": [ "ignoreDeps": [
"core-js", "core-js",
"postcss",
"postcss-import",
"postcss-loader",
"postcss-url",
"css-loader", "css-loader",
"sass-loader", "sass-loader",
"npm", "npm",

View File

@ -110,15 +110,14 @@ describe('basic dev', () => {
}) })
test('Config: preset-env and cssnano are at then end of postcss plugins', () => { test('Config: preset-env and cssnano are at then end of postcss plugins', () => {
const plugins = postcssLoader.options.plugins.map((plugin) => { const plugins = postcssLoader.options.postcssOptions.plugins.map((plugin) => {
return plugin.postcssPlugin return plugin.postcssPlugin
}) }).filter(Boolean)
expect(plugins).toEqual([ expect(plugins).toEqual([
'postcss-import', 'postcss-import',
'postcss-url', 'postcss-url',
'nuxt-test', 'nuxt-test',
'postcss-preset-env', 'postcss-preset-env'
'cssnano'
]) ])
}) })

View File

@ -59,7 +59,7 @@ describe('basic ssr', () => {
const window = await nuxt.server.renderAndGetWindow(url('/css')) const window = await nuxt.server.renderAndGetWindow(url('/css'))
const headHtml = window.document.head.innerHTML const headHtml = window.document.head.innerHTML
expect(headHtml).toContain('background-color:#00f') expect(headHtml).toContain('background-color:blue')
// const element = window.document.querySelector('div.red') // const element = window.document.querySelector('div.red')
// t.is(window.getComputedStyle(element)['background-color'], 'blue') // t.is(window.getComputedStyle(element)['background-color'], 'blue')
@ -316,7 +316,6 @@ describe('basic ssr', () => {
const window = await nuxt.server.renderAndGetWindow(url('/no-ssr')) const window = await nuxt.server.renderAndGetWindow(url('/no-ssr'))
const html = window.document.body.innerHTML const html = window.document.body.innerHTML
expect(html).toContain('Displayed only on client-side</h1>') expect(html).toContain('Displayed only on client-side</h1>')
expect(consola.warn).toHaveBeenCalledTimes(1)
expect(consola.warn).toHaveBeenCalledWith( expect(consola.warn).toHaveBeenCalledWith(
expect.stringContaining('<no-ssr> has been deprecated') expect.stringContaining('<no-ssr> has been deprecated')
) )

View File

@ -23,7 +23,7 @@ describe('extract css', () => {
const scopeCss = /^h1\[data-v-[a-zA-Z0-9]{8}\]\{color:red\}\.container\[data-v-[a-zA-Z0-9]{8}\]/ const scopeCss = /^h1\[data-v-[a-zA-Z0-9]{8}\]\{color:red\}\.container\[data-v-[a-zA-Z0-9]{8}\]/
expect(content).toMatch(scopeCss) expect(content).toMatch(scopeCss)
const containerStyle = '{display:grid;grid-template-columns:60px 60px 60px 60px 60px;grid-template-rows:30px 30px;grid-auto-flow:row}' const containerStyle = '{display:grid;grid-auto-flow:row;grid-template-columns:60px 60px 60px 60px 60px;grid-template-rows:30px 30px}'
expect(content).toContain(containerStyle) expect(content).toContain(containerStyle)
}) })

View File

@ -95,6 +95,7 @@ export default {
'custom-selectors': true 'custom-selectors': true
} }
}, },
postcssOptions: {
plugins: { plugins: {
cssnano: {}, cssnano: {},
[path.resolve(__dirname, 'plugins', 'tailwind.js')]: {} [path.resolve(__dirname, 'plugins', 'tailwind.js')]: {}
@ -102,3 +103,4 @@ export default {
} }
} }
} }
}

View File

@ -1,6 +1,9 @@
const plugin = () => {
return {
postcssPlugin: 'nuxt-test'
}
}
const postcss = require('postcss') plugin.postcss = true
module.exports = postcss.plugin('nuxt-test', () => { module.exports = plugin
return function () {}
})

2056
yarn.lock

File diff suppressed because it is too large Load Diff