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 @@ + + + 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 @@ + + + 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')