survivejs / webpack-book

From apprentice to master (CC BY-NC-ND)
https://survivejs.com/webpack/
2.42k stars 319 forks source link

PostCSS: Multiple instances of postcsss-loader #129

Closed Velenir closed 7 years ago

Velenir commented 7 years ago

I think it would be a good idea to cover the case of including postcss-loader multiple times in the rules -- e.g. with stylelint plugin as a preloader and with autoprefixer in css/sass/less processing rule. To illustrate, given:

// webpack.config.js
// ...
rules: [
  {
    test: /\.s?css$/,
    loader: 'postcss-loader',
    enforce: 'pre'
  },
  {
    test: /\.scss$/,
    use: ['style-loader', 'css-loader', 'postcss-loader', 'sass-loader']
  }
]

and

// postcss.config.js
module.exports = {
  plugins: {
    autoprefixer: {},
    stylelint: {
      configFile: '.stylelintrc',
      ignoreFiles: 'node_modules/**/*.css'
    }
  }
};

Both autoprefixer and stylelint plugins get applied on each postcss-loader use with unpredictable results (mostly just warnings from stylelint for compressed .scss files).

Do you solve this somehow? In webpack 1 I used to include postcss config directly in the module and specialize in the loader: 'postcss?pack=lint', but it's now deprecated.

In webpack 2 I specialize like this:

// webpack.config.js
// ...
rules: [
  {
    test: /\.s?css$/,
    loader: 'postcss-loader?lint',
    enforce: 'pre'
  },
  {
    test: /\.scss$/,
    use: ['style-loader', 'css-loader', 'postcss-loader', 'sass-loader']
  }
]

and return different plugins depending on context passed to postcss:

// postcss.config.js
const autoprefixer = require('autoprefixer');
const stylelint = require('stylelint');

module.exports = function(ctx) {
  // extract options from 'postcss-loader?options'
  // or stringified options object from { loader: 'postcss-loader', options: {} }
  const { loaderIndex, loaders } = ctx.webpack;
  const { options } = loaders[loaderIndex];

  if (options === "lint") {
    return {
      plugins: [
        stylelint({
          configFile: '.stylelintrc',
          ignoreFiles: 'node_modules/**/*.css'
        })
      ]
    };
  }

  return {
    plugins: [autoprefixer]
    // or
    // plugins: { autoprefixer: {} }
  };
};

I'm not sure if this is the most efficient way to do it but it works.

bebraw commented 7 years ago

Yeah, great point. If I remember right, postcss.config.js supports a function format. If we can get enough context information through that, we could handle specialization there. If the data we need for that isn't there yet, I assume it would be possible to add.

bebraw commented 7 years ago

You can see the format here. Here's the relevant portion:

module.exports = (ctx) => ({
  parser: ctx.sugar ? 'sugarss' : false,
  map: ctx.env === 'development' ? ctx.map : false,
  from: ctx.from
  to: ctx.to
  plugins: {
    'postcss-plugin': ctx.plugin
  }
})

So this could be it.

Velenir commented 7 years ago

Thank you for the link. I've looked through it, and it seems to me that we can't pass parameters directly to context as it is handled by webpack. Maybe we could use LoaderOptionsPlugin but we really shouldn't. Adding something like

rules: [
  {
    test: /\.s?css$/,
    loader: 'postcss-loader',
    lint: true,
    enforce: 'pre'
  }
]

breaks validation. So the only way to pass parameter is through ?params or options: {}. Then we can access query and options on the loader in context (where query === (options ? "?"+options : "")). At least I can't think of another way.

bebraw commented 7 years ago

Yeah, validation is very strict. We would need a smart way to tell the loaders apart somehow without relying on custom fields as webpack chokes on those. options sounds like a potential way out to me.

I guess we need to escalate this issue to postcss-loader as it's quite possible this hasn't been solved properly yet.

bebraw commented 7 years ago

@Velenir I set up https://github.com/postcss/postcss-loader/issues/158 if you want to follow/comment.

bebraw commented 7 years ago

Looks like this will be solved by postcss-loader once they implement config option. So give it a week. 👍

Velenir commented 7 years ago

Glad to hear it. Thanks for all your help!

bebraw commented 7 years ago

This should be resolved at dev now. I ended up going through options and plugins (it's important to set ident at the moment for this to work!).

Velenir commented 7 years ago

Thank you very much for that! I would have preferred some kind of simpler specialization in postcss.config.js, but this way is good too.

bebraw commented 7 years ago

postcss.config.js might be possible too. It depends on what kind of context information we have available there. There could be more than there used to be. That would allow branching.