nuxt icon indicating copy to clipboard operation
nuxt copied to clipboard

feat(postcss-loader): support passing in a function for postcss definition

Open simllll opened this issue 3 years ago • 16 comments

Issue

Postcssconfig can either be an object (already supported) or a function which gets passed in the loadercontext. But nuxt does not support passing in a function currently.

Why

Passing in a function allows more complex uses cases, e.g. by applying different postcss steps for different ressources. (see also https://github.com/webpack-contrib/postcss-loader#function)

Solution

This approach adds a new "postcssConfig" option to the "postcss" nuxt config. Either this is the regular postcssConfig, or it is a function. see https://webpack.js.org/loaders/postcss-loader/#postcssoptions (only works with postcss@8, because postcss-loader >= 4.0 is required, and postcss7 nuxt setup conflicts with this version of postcss-loader)

e.g. nuxt.config.js.

module.exports = {
  build: {
    postcss: {
      postcssOptions: loaderContext => {
        // e.g. do something else with ".sss" files
        if (/\.sss$/.test(loaderContext.resourcePath)) {
          return {
            parser: 'sugarss',
            plugins: [['postcss-short', { prefix: 'x' }], 'postcss-preset-env']
          };
        }

        return {
          plugins: [['postcss-short', { prefix: 'x' }], 'postcss-preset-env']
        };
      }
    }
  }
};

this closes #7015

Types of changes

  • [x] New feature (a non-breaking change which adds functionality)

Description

If postcss config is a function, we execute the custom function, but we also apply the default config of nuxt. Therefore we can assure that the config has a minimum default.

Checklist:

  • [x] My change requires a change to the documentation. The postcss option can also have a function.
  • [ ] I have updated the documentation accordingly. (PR: #)
  • [ ] I have added tests to cover my changes (if not applicable, please state why) nope, as this is a advanced use case, I'm not quite sure where to add a appropiat test or if there is even a test really needed?
  • [x] All new and existing tests are passing.

simllll avatar Feb 24 '21 17:02 simllll

@simllll Since linked issue is related to postcss < 8 (current) shall we support same feature in postcss.js util?

pi0 avatar Feb 24 '21 21:02 pi0

hi @clarkdo, thanks for your review. Actually your comment was pretty much the same as my first implementation, but as @pi0 stated correctly, this will loose some magic nuxt can do for us. E.g. execute, preset,.. because "postcss" is defined with a function, and you cannot define additional properties to it.

Therefore I was thinking how we could make this "better". By adding a new config option, which is used as postcssOptions if defined. (either a functino or an object), but everything stays the same when this option is not defined.

export default {
  build: {
    postcss: {
      ... all nuxt options like before,
      postcssOptions: {} <-- optional a postcssOptions, which is then passed to postcss-loader
    }
  }
}

the nice thing on this is, that it's also easy possible to describe what are nuxt special options, and what are postcss "native" options, and they also can not conflict.

simllll avatar Feb 25 '21 16:02 simllll

Sorry, I did't follow all of your discussions carefully, I'm just thinking build.postcs and build.postcss.postcssOptions are having same functionalities and conflicts like:

export default {
  build: {
    postcss: {
      plugins: {
        'postcss-short': { prefix: 'x' }
      },
      postcssOptions: {
        plugins: [
          ['postcss-short': { prefix: 'x' }]
        ]
      }
    }
  }

And it will be much harder to deal with things like order and options of plugins:

export default {
  build: {
    postcss: {
      preset: {
        stage: 2
      },
      postcssOptions: {
        plugins: [
          ['postcss-preset-env': { stage: 1 }]
        ]
      }
    }
  }

And also postcss-loader and postcss are all only suporting either funciton or object of postcssOptions, I think funciton postcssOptions is for advanced usage which user will set up everything inside the function, it may be better to align to their official solution.

cc @pi0

clarkdo avatar Feb 25 '21 16:02 clarkdo

As far as I understand, there are two concercn about using the "postcss" parameter as a function.

First, we are actually talking aobut the postcss-loader here, which has an object as input, see: image (https://www.npmjs.com/package/postcss-loader)

one of the options is postcssOptions.

so putting a function here directly, would break two things: 1.) you cannot pass anything to the loader anymore 2.) custom options for nuxt can not be handled anymore.

therefore I would still suggest adding an additional "options" property, and would make them actually the same like postcss-loader has it already.

simllll avatar Feb 25 '21 17:02 simllll

I see both points :)

  • To ensure extendibility with nuxt/loader-specific options, we need a namespace like postcss
  • For DX and not breaking current config, we should support options.build.postcss.plugins and options.build.postcssOptions().plugins

We can always return a function to pass to loader for postcssOptions:

  • When called, if user provided postcssOptions is function, call it with context we have
  • Merge new postcssOptions with top level options and applying defaults/normalization

Config types would be like this:

// type for build.postcss
interface NuxtPostcssOptions extends PostCSSOptions {
  execute: Boolean
  sourceMap: Boolean
  postcssOptions: PostCSSOptions | ((ctx: any) => PostCSSOptions)
}

pi0 avatar Feb 25 '21 17:02 pi0

Just to clarify, are we gonna support ?

export default {
  build: {
    postcss: {
      // puglins and preset here can be from user or our default config
      plugins: {
        'postcss-import': {},
        'postcss-url': {},
        cssnano: true
      },
      preset: {
        stage: 2,
        ...
      },
      postcssOptions: {
        plugins: [
          ['postcss-import': { prefix: 'x' }],
          ['postcss-short': { prefix: 'x' }],
          ['postcss-url': {}],
          ['postcss-preset-env': { ... }]
        ]
      }
    }
  }
}

What the finial config will be like in this case ?

therefore I would still suggest adding an additional "options" property, and would make them actually the same like postcss-loader has it already.

I agree with the additional "options" property, what I concern is we should remove postcss.plugins and postcss.preset for avoiding too much confusion and conflicts like above code.

clarkdo avatar Feb 25 '21 17:02 clarkdo

I agree with @clarkdo , to make things future proof I would suggest deprecating the old "root" options for plugins at least. Right now I've implemented followign logic:

  • everything works like before
  • except if you set "postcssOptions", it is used instead of the "root" for the "postcssOption" that is passed to the loader.

see here https://github.com/nuxt/nuxt.js/pull/8899/files#diff-9613feda5d5173514c3656904bb88e5a8545c01dcacdc2e6c74cf476435a60e3R175 for the function method and here https://github.com/nuxt/nuxt.js/pull/8899/files#diff-9613feda5d5173514c3656904bb88e5a8545c01dcacdc2e6c74cf476435a60e3R185 for the object variant

Following up on @clarkdo's example, this means:

export default {
  build: {
    postcss: {
      // puglins and preset here can be from user or our default config
      plugins: { // <-- this is NOT used in this case
        'postcss-import': {},
        'postcss-url': {},
        cssnano: true
      },
      preset: {
        stage: 2,
        ...
      },
      postcssOptions: {
        plugins: [ // <-- ONLY this one is used, but regardless if function or object, defaults are applied
          ['postcss-import': { prefix: 'x' }],
          ['postcss-short': { prefix: 'x' }],
          ['postcss-url': {}],
          ['postcss-preset-env': { ... }]
        ]
      }
    }
  }
}

simllll avatar Feb 25 '21 20:02 simllll

As this still blocks us from upgrading to postcss8, is there anything to help to get this thing going? :)

simllll avatar Mar 15 '21 22:03 simllll

any news on this?

simllll avatar Apr 02 '21 14:04 simllll

@pi0 hs created a module for postcss 8 https://github.com/nuxt/postcss8 , what do you think moving the logic to the module as a new feature?

clarkdo avatar Apr 02 '21 15:04 clarkdo

@clarkdo I was just looking into the postcss8 module, but it only adapts some configs, it hasn't included the webpack (webpack/src/utils/postcss-v8.js) configuration, therefore adding it to the module is not possible in my opinion, it needs to be part of the webpack postcss-v8.js implementation.

simllll avatar Apr 18 '21 14:04 simllll

Codecov Report

Merging #8899 (61753fd) into dev (88ea02c) will decrease coverage by 0.12%. The diff coverage is 0.00%.

Impacted file tree graph

@@            Coverage Diff             @@
##              dev    #8899      +/-   ##
==========================================
- Coverage   65.10%   64.97%   -0.13%     
==========================================
  Files          94       94              
  Lines        4098     4106       +8     
  Branches     1121     1124       +3     
==========================================
  Hits         2668     2668              
- Misses       1152     1157       +5     
- Partials      278      281       +3     
Flag Coverage Δ
unittests 64.97% <0.00%> (-0.13%) :arrow_down:

Flags with carried forward coverage won't be shown. Click here to find out more.

Impacted Files Coverage Δ
packages/webpack/src/utils/postcss-v8.js 1.03% <0.00%> (-0.10%) :arrow_down:

Continue to review full report at Codecov.

Legend - Click here to learn more Δ = absolute <relative> (impact), ø = not affected, ? = missing data Powered by Codecov. Last update 88ea02c...61753fd. Read the comment docs.

codecov-commenter avatar Apr 18 '21 14:04 codecov-commenter

Any chance to get this into nuxt2 or/and nuxt 3? :) I'm eager to find out due to the reason that the approach we are using right now (external postcss file) produces a warning that this will not be possible in nuxt 3...

simllll avatar Apr 30 '21 06:04 simllll

I was just looking into the postcss8 module, but it only adapts some configs, it hasn't included the webpack (webpack/src/utils/postcss-v8.js) configuration, therefore adding it to the module is not possible in my opinion, it needs to be part of the webpack postcss-v8.js implementation.

We can change webpack config in module and it's acutally doing by some modules, I think we can leverage build.exntend in nuxt.config to call the customized function you defined for postcss and change the postcss-loader options.

clarkdo avatar May 03 '21 12:05 clarkdo

i love this

web-apply avatar Oct 22 '21 14:10 web-apply

Hi, What's the status of this PR? It's really desired especially after support of external postcss config file is dropped on Nuxt@3

blooddrunk avatar Nov 17 '21 07:11 blooddrunk

@simllll Would you like to update this PR now that we've updated to PostCSS 8 natively in 2.16? 🙏

danielroe avatar Mar 02 '23 13:03 danielroe

@simllll, @danielroe, can we do anything to make this PR land? This would unblock our migration to Nuxt 2.16 (and to Nuxt 3 in the future) by fixing the issue I described in #19482

obulat avatar Mar 06 '23 14:03 obulat

@obulat I suspect @simllll would not mind if you would like to open a new PR incorporating the work here and resolving merge conflicts. I would of course merge it crediting you both.

danielroe avatar Mar 06 '23 16:03 danielroe

Yes, absolute fine to me, no worries :-) i would do it myself, unfortunately couldn't find time yet and the issue itself is not relevant anymore to us... ;-)

simllll avatar Mar 06 '23 17:03 simllll

implemented in https://github.com/nuxt/nuxt/pull/19495

danielroe avatar Jan 17 '24 10:01 danielroe