diff --git a/packages/webpack/src/utils/postcss.js b/packages/webpack/src/utils/postcss.js
index ccc535965a..874ddd8596 100644
--- a/packages/webpack/src/utils/postcss.js
+++ b/packages/webpack/src/utils/postcss.js
@@ -154,7 +154,7 @@ export default class PostcssConfig {
* @param postcssOptions
* @returns {{ postcssOptions: { plugins?: unknown, order?: string, preset?: any} }}
*/
- normalize (postcssOptions) {
+ normalize (postcssOptions, warnAboutTopLevelDeprecation = true) {
// 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' +
@@ -168,12 +168,19 @@ export default class PostcssConfig {
return { postcssOptions: {} }
}
if (postcssOptions.postcssOptions && typeof postcssOptions.postcssOptions === 'function') {
- consola.warn('Using a Function as `build.postcss.postcssOptions` is not yet supported in Nuxt 2.16.2')
- return { postcssOptions: {} }
+ const postcssOptionsFn = postcssOptions.postcssOptions
+ return {
+ postcssOptions: (loaderContext) => {
+ const result = this.normalize(postcssOptionsFn(loaderContext), false)
+ if (result) {
+ return result.postcssOptions
+ }
+ }
+ }
}
if (!('postcssOptions' in postcssOptions)) {
- if (Object.keys(postcssOptions).length > 0) {
- consola.warn('Using the top-level properties in `build.postcss` will be deprecated in Nuxt 3. Please move' +
+ if (Object.keys(postcssOptions).length > 0 && warnAboutTopLevelDeprecation) {
+ consola.warn('Using the top-level properties in `build.postcss` will be deprecated in Nuxt 3. Please move ' +
'the settings to `postcss.postcssOptions`')
}
postcssOptions = { postcssOptions }
@@ -211,7 +218,7 @@ export default class PostcssConfig {
/**
* Load plugins from postcssOptions
- * @param {{ postcssOptions: {plugins?: unknown, order?: string | function}}} postcssOptions
+ * @param {{ postcssOptions: {plugins?: unknown, order?: string | function }}} postcssOptions
*/
loadPlugins (postcssOptions) {
const { plugins, order } = postcssOptions.postcssOptions
@@ -252,7 +259,7 @@ export default class PostcssConfig {
if (Array.isArray(postcssOptions.postcssOptions.plugins)) {
defaults(postcssOptions.postcssOptions.plugins, this.defaultPostcssOptions.plugins)
- } else {
+ } else if (typeof postcssOptions.postcssOptions !== 'function') {
// Merge all plugins and use preset for setting up postcss-preset-env
if (postcssOptions.postcssOptions.preset) {
if (!postcssOptions.postcssOptions.plugins) {
diff --git a/packages/webpack/test/postcss.test.js b/packages/webpack/test/postcss.test.js
new file mode 100644
index 0000000000..e92fadf3a7
--- /dev/null
+++ b/packages/webpack/test/postcss.test.js
@@ -0,0 +1,55 @@
+import { join } from 'node:path'
+import PostcssConfig from '../src/utils/postcss'
+
+describe('webpack: postcss', () => {
+ const getConfigWithPostcssConfig = config =>
+ new PostcssConfig({
+ options: {
+ dev: false,
+ srcDir: join(__dirname),
+ rootDir: join(__dirname),
+ modulesDir: []
+ },
+ nuxt: {
+ resolver: {
+ requireModule: plugin => opts => [plugin, opts]
+ }
+ },
+ buildOptions: {
+ postcss: config
+ }
+ })
+
+ test('should have the right default configuration', () => {
+ // Use the default postcss config: stage 2
+ // https://cssdb.org/#staging-process
+ const pluginConfig = Object.fromEntries(
+ getConfigWithPostcssConfig({ postcssOptions: {} }).config().postcssOptions.plugins
+ )
+ expect(pluginConfig).toMatchInlineSnapshot(`
+ {
+ "cssnano": {
+ "preset": [
+ "default",
+ {
+ "minifyFontValues": {
+ "removeQuotes": false,
+ },
+ },
+ ],
+ },
+ "postcss-import": {
+ "resolve": [Function],
+ },
+ "postcss-preset-env": {},
+ "postcss-url": {},
+ }
+ `)
+ })
+ test('can pass a function through', () => {
+ // Use the default postcss config: stage 2
+ // https://cssdb.org/#staging-process
+ const options = getConfigWithPostcssConfig({ postcssOptions: () => ({ preset: { stage: 2 } }) }).config().postcssOptions
+ expect(typeof options).toBe('function')
+ })
+})
diff --git a/test/dev/postcss-function.test.js b/test/dev/postcss-function.test.js
new file mode 100644
index 0000000000..d7137ef9d3
--- /dev/null
+++ b/test/dev/postcss-function.test.js
@@ -0,0 +1,32 @@
+import { getPort, loadFixture, Nuxt } from '../utils'
+
+let port
+const url = route => 'http://localhost:' + port + route
+
+let nuxt = null
+
+describe('postcss configuration as function', () => {
+ beforeAll(async () => {
+ const options = await loadFixture('postcss-function')
+ nuxt = new Nuxt(options)
+ await nuxt.ready()
+
+ port = await getPort()
+ await nuxt.server.listen(port, '0.0.0.0')
+ })
+
+ for (const path of ['/css', '/postcss']) {
+ test(path, async () => {
+ const window = await nuxt.server.renderAndGetWindow(url(path))
+
+ const headHtml = window.document.head.innerHTML
+ expect(headHtml.replace(/\s+/g, '').replace(/;}/g, '}')).toContain('div.red{background-color:blue}.red{color:red}')
+
+ const element = window.document.querySelector('.red')
+ expect(element).not.toBe(null)
+ expect(element.textContent).toContain('This is red')
+ expect(element.className).toBe('red')
+ // t.is(window.getComputedStyle(element).color, 'red')
+ })
+ }
+})
diff --git a/test/fixtures/postcss-function/nuxt.config.js b/test/fixtures/postcss-function/nuxt.config.js
new file mode 100644
index 0000000000..7ee7c7fa37
--- /dev/null
+++ b/test/fixtures/postcss-function/nuxt.config.js
@@ -0,0 +1,16 @@
+const createData = async () => {
+ await new Promise(resolve => setTimeout(resolve, 500))
+ return {
+ build: {
+ postcss: {
+ postcssOptions: () => ({
+ plugins: [
+ ['postcss-preset-env', { features: { 'custom-selectors': true } }]
+ ]
+ })
+ }
+ }
+ }
+}
+
+export default createData
diff --git a/test/fixtures/postcss-function/pages/css.vue b/test/fixtures/postcss-function/pages/css.vue
new file mode 100644
index 0000000000..8bf4d9da7d
--- /dev/null
+++ b/test/fixtures/postcss-function/pages/css.vue
@@ -0,0 +1,15 @@
+
+
+ This is red
+
+
+
+
diff --git a/test/fixtures/postcss-function/pages/postcss.vue b/test/fixtures/postcss-function/pages/postcss.vue
new file mode 100644
index 0000000000..976a4f653b
--- /dev/null
+++ b/test/fixtures/postcss-function/pages/postcss.vue
@@ -0,0 +1,15 @@
+
+
+ This is red
+
+
+
+
diff --git a/test/fixtures/postcss-function/postcss-function.test.js b/test/fixtures/postcss-function/postcss-function.test.js
new file mode 100644
index 0000000000..1ec7857263
--- /dev/null
+++ b/test/fixtures/postcss-function/postcss-function.test.js
@@ -0,0 +1,3 @@
+import { buildFixture } from '../../utils/build'
+
+buildFixture('postcss-function')