Skip to content

Conversation

@adamwathan
Copy link
Member

This PR adds support for a new way of creating plugins using a new plugin function exposed at tailwindcss/plugin.

It allows you to create plugins like this:

// my-plugin.js
const plugin = require('tailwindcss/plugin')

module.exports = plugin(function({ addUtilities, theme, variants }) {
  // Plugin work goes here
}, {
  theme: {
    // ...
  },
  variants: {
    // ...
  },
})

The first argument to plugin is the traditional Tailwind plugin function. The second argument is your plugin's config object that should be merged with the user's config.

This API replaces the unstable API introduced in #1162 that allowed you to write your plugins as an object with a config and handler property:

module.exports = {
  config: {
    theme: {
      // ...
    },
    variants: {
      // ...
    },
  },
  handler({ addUtilities, theme, variants }) {
    // ...
  }
}

This PR also introduces a plugin.withOption function that allows you to author plugins that accept top-level non-config options:

const plugin = require('tailwindcss/plugin')

module.exports = plugin.withOptions(function(options) {
  return function({ addUtilities, theme, variants }) {
    // ...
  }
}, function(options) {
  return {
    theme: {
      // ...
    },
    variants: {
      // ...
    },
  }
})

These sorts of plugins are used by end-users like this:

// tailwind.config.js
module.exports = {
  plugins: [
    require('some-fancy-plugin')({ optionA: 'foo', optionB: 'bar' })
  ]
}

This is implemented in such a way that if your plugin takes top-level options but those options are optional, the end-user doesn't need to invoke your plugin with no arguments when adding it to their Tailwind config:

// tailwind.config.js
module.exports = {
  plugins: [
    require('some-fancy-plugin')
  ]
}

It's worth noting that under the hood we are still using the { config: ..., handler: .... } format (that's what this helper function spits out) so functions authored that way will continue to work, although you're encouraged to migrate to this new API.

@adamwathan adamwathan merged commit 293900a into master Dec 20, 2019
@hacknug
Copy link
Contributor

hacknug commented Dec 23, 2019

This is implemented in such a way that if your plugin takes top-level options but those options are optional, the end-user doesn't need to invoke your plugin with no arguments when adding it to their Tailwind config

I love this ❤️

@qntndev
Copy link

qntndev commented Jan 7, 2020

Hey @adamwathan, I use this feature to add plugins to my Vue & Tailwind project and I have a question related to the vue-cli/webpack config on this. Is there a way to use aliases defined in webpack config in tailwind.config.js to require a plugin as require('alias/plugin')?

//vue.config.js
module.exports = {
  configureWebpack: {
    resolve: {
      alias: {
        TailwindPlugins: 'src/assets/styles/plugins'
      }
    }
  }
};

Neither the base webpack alias for src @ nor the one I have defined can resolve the path correctly. I get an Cannot find module error. Did you get this to works on some of your projects ?

//tailwind.config.js
module.exports = {
  //...
  plugins: [
    require('src/assets/styles/plugins/reset'),   //✅
    require('@/assets/styles/plugins/reset'),     //❌
    require('TailwindPlugins/reset')              //❌
  ]
}

@adamwathan
Copy link
Member Author

My guess is that that file isn't processed by Webpack in the same way since it's not part of the actual modules being bundled, it's just a config file written in CommonJS format that is part of configuring the build rather than being part of the JS that is output.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants