Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

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

Closed
wants to merge 4 commits into from

Conversation

simllll
Copy link

@simllll simllll commented Feb 24, 2021

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

  • 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:

  • 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?
  • All new and existing tests are passing.

in postcss@v8 "PostcssPlugin" is not a generic, but in postcss@v7 it is.
As postcss@v7 is the default right now, but postcss@8 is supported,
we need to ignore this "type conflict" right now
this.postcssOptions(loaderContext)
)
this.loadPlugins(postcssOptions)

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It seems we are losing part of features comparted to non function mode (preset, execute) (and it might be source of more inconsistent behavior in the future)

Can we maybe convert L200:221 (isPureObject(postcssOptions))) into an inline function to immediately call and return or delay to use by function wrapper to get context?

Copy link
Author

@simllll simllll Feb 24, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

.. we could wrap some parts in a function wrapper, but the main "problem" here is that preset and execute needs to be evaluated "at beginning" and not when the function itself is executed. The only return type the function can give is the "postcssOptions" itself as an object.
Therefore I would propose to allow a config like this:

postcss: {
  execute: true,
  order: 'presetEnvAndCssnanoLast',
  preset: {
    stage: 2
  },
  postcssOptions: // config or function.. well of course old plugins can still be used, but to keep it easier I would suggest also allowing setting configs inside this object too.
}

I will commit my idea, let me know what you think of it

Copy link
Member

@pi0 pi0 Feb 24, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Seems a nice idea just maybe we can use postcss.options or postcss.postcssOptions for new options passing/function possibility? (avoid two top level options for postcss)

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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

Could be possible, but it needs postcss-loader >= 4.x, see release notes https://github.com/webpack-contrib/postcss-loader/releases/tag/v4.0.0
and I couldn't run nuxt with postcss-loader 4.x without postcss8. Therefore I guess it's not really necessary.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Seems a nice idea just maybe we can use postcss.options or postcss.postcssOptions for new options passing/function possibility? (avoid two top level options for postcss)

Not quite sure what you mean, is postcssOptions good or should I go with options instead?
I just finished some small tests, looks good to me. Fully backwards compatible and no duplicate code anymore :)

@pi0
Copy link
Member

pi0 commented Feb 24, 2021

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

@simllll simllll requested a review from pi0 February 24, 2021 21:42
Copy link
Member

@clarkdo clarkdo left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actually we did some nomralization logic here for backward compatibility, the build.postcss will be passed as postcssOptions of postcss-loader v4, so we won't have postcssOptions.postcssOptions.

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

// Will be

{
  loader: "postcss-loader",
  options: {
    postcssOptions: {
      plugins: [
        ['postcss-short', { prefix: 'x' }],
      ],
    },
  },
},

Based on the above, I think we can just support build.postcss as function which is same as postcss-loader.

export default {
  build: {
    postcss() {
      // ...
    }
  }
}

// Will be

{
  loader: "postcss-loader",
  options: {
    postcssOptions() {
      // ...
    }
  },
},

@simllll
Copy link
Author

simllll commented Feb 25, 2021

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.

@clarkdo
Copy link
Member

clarkdo commented Feb 25, 2021

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

@simllll
Copy link
Author

simllll commented Feb 25, 2021

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.

@pi0
Copy link
Member

pi0 commented Feb 25, 2021

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)
}

@clarkdo
Copy link
Member

clarkdo commented Feb 25, 2021

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.

@simllll
Copy link
Author

simllll commented Feb 25, 2021

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
Copy link
Author

simllll commented Mar 15, 2021

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

@simllll
Copy link
Author

simllll commented Apr 2, 2021

any news on this?

@clarkdo
Copy link
Member

clarkdo commented Apr 2, 2021

@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?

@danielroe danielroe linked an issue Apr 14, 2021 that may be closed by this pull request
@simllll
Copy link
Author

simllll commented Apr 18, 2021

@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.

@codecov-commenter
Copy link

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%) ⬇️

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%) ⬇️

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.

@simllll
Copy link
Author

simllll commented Apr 30, 2021

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...

@clarkdo
Copy link
Member

clarkdo commented May 3, 2021

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.

@pi0 pi0 mentioned this pull request Aug 13, 2021
5 tasks
@web-apply
Copy link

i love this

@blooddrunk
Copy link

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

@danielroe
Copy link
Member

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

@obulat
Copy link

obulat commented Mar 6, 2023

@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

@danielroe
Copy link
Member

danielroe commented Mar 6, 2023

@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.

@simllll
Copy link
Author

simllll commented Mar 6, 2023

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... ;-)

obulat added a commit to obulat/nuxt that referenced this pull request Mar 7, 2023
This PR allows to use a function in `postcssOptions`
obulat added a commit to obulat/nuxt that referenced this pull request Mar 7, 2023
@danielroe
Copy link
Member

implemented in #19495

@danielroe danielroe closed this Jan 17, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

postcss-px-to-viewport这个插件不能配置参数传入? using function for build.postcss config option
9 participants