How to add PostCSS plugins to Bud?

nice! thank you for the repo. i was able to reproduce your issue and i have a fix. allow me to walk you through the steps i took.

Check .budfiles/bud/webpack.config.js

If bud made it all the way to the dashboard it means bud has created a diagnostic file at this location. The file has a representation of the configuration bud is giving to webpack.

The first example

When you set your plugin with setPlugin in the first example:

.tap(bud => {
  bud.postcss
    .setPlugin('postcss-inline-svg', [
      require.resolve('postcss-inline-svg'),
      {},
    ])
})

this is the output you’ll find in the postcss rule:

Object {
  "loader": "[modules]/postcss-loader/dist/cjs.js",
  "options": Object {
    "postcssOptions": Object {
      "plugins": Array [
        "[modules]/postcss-import/index.js",
        "[modules]/tailwindcss/nesting/index.js",
        "[modules]/tailwindcss/lib/index.js",
        Array [
          "[modules]/postcss-inline-svg/lib/index.js",
          Object {},
        ],
        Array [
          "[modules]/postcss-preset-env/dist/index.js",
          Object {
            "features": Object {
              "focus-within-pseudo-class": false,
            },
            "stage": 1,
          },
        ],
      ],
    },
    "sourceMap": true,
  },
}

This is correct so we know postcss.setPlugins works.

The second example

Running your second example

bud.tap(bud => {
  bud.postcss
    .setPlugins({
      'postcss-import': require.resolve('postcss-import'),
      'tailwindcss': require.resolve('tailwindcss'),
      'postcss-nested': require.resolve('postcss-nested'),
      'postcss-inline-svg': [
        require.resolve('postcss-inline-svg'),
        {},
      ],
      'postcss-preset-env': [
        require.resolve('postcss-preset-env'),
        {
          stage: 1,
          features: {
            'focus-within-pseudo-class': false,
          },
        },
      ],
    });
})

also gives us what we ask for.

Both seem to be what was specified. So, bud is doing its job so far but we aren’t getting what we want. I think there is an issue combining tailwindcss-nesting with postcss-inline-svg. Fixing this feels like an issue for one of those two repositories.

Removing an extension

I noticed you aren’t using tailwindcss in your stylesheet. We can remove it:

bud.tap(({postcss}) => postcss
  .remove('tailwindcss')
  .remove('tailwindcss-nesting')
)

And now it’s a matter of keeping postcss-import and adding postcss-nested back in.

Inserting it into the right position is tricky, but we can do it with a reducer (it’s basically like dealing with state in redux). Here is the function I came up with:

/**
 * @param {Bud} instance
 */
const mergePlugin = ({postcss}) => {
  const reducer = (a, [k, v]) => ({...a, [k]: v});

  postcss.remove('tailwindcss').remove('tailwindcss-nesting');

  const plugins = {
    ...postcss.getEntries().slice(0, 1).reduce(reducer, {}),
    ['postcss-nested']: ['postcss-nested', {}],
    ['postcss-inline-svg']: ['postcss-inline-svg', {}],
    ...postcss.getEntries().slice(2).reduce(reducer, {}),
  };

  postcss.setPlugins(plugins);
};

This works and can be applied with .tap(mergePlugin) You could also do what you’re doing in the first example and overwrite everything. Just leave out tailwindcss and its nesting plugin.

I don’t know what’s up with tailwindcss and postcss-inline-svg. Postcss doesn’t have the best reporting, and so it’s hard to say what is going on.

Of note, in 5.1.0 svg will automatically be converted to data urls if you install @roots/bud-imagemin. I wouldn’t install it until 5.1.0, though.

2 Likes