Merge pull request #1268 from tailwindcss/plugin-function

Add new `plugin` and `plugin.withOptions` functions for creating plugins
This commit is contained in:
Adam Wathan 2019-12-20 11:09:53 -05:00 committed by GitHub
commit 293900a28c
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 314 additions and 0 deletions

View File

@ -1,6 +1,8 @@
import _ from 'lodash'
import _postcss from 'postcss'
import tailwind from '../src/index'
import processPlugins from '../src/util/processPlugins'
import createPlugin from '../src/util/createPlugin'
function css(nodes) {
return _postcss.root({ nodes }).toString()
@ -1287,3 +1289,284 @@ test('plugins can provide a config but no handler', () => {
}
`)
})
test('plugins can be created using the `createPlugin` function', () => {
const plugin = createPlugin(
function({ addUtilities, theme, variants }) {
const utilities = _.fromPairs(
_.toPairs(theme('testPlugin')).map(([k, v]) => [`.test-${k}`, { testProperty: v }])
)
addUtilities(utilities, variants('testPlugin'))
},
{
theme: {
testPlugin: {
sm: '1rem',
md: '2rem',
lg: '3rem',
},
},
variants: {
testPlugin: ['responsive', 'hover'],
},
}
)
return _postcss([
tailwind({
corePlugins: [],
theme: {
screens: {
sm: '400px',
},
},
plugins: [plugin],
}),
])
.process(
`
@tailwind base;
@tailwind components;
@tailwind utilities;
`,
{ from: undefined }
)
.then(result => {
const expected = `
.test-sm {
test-property: 1rem
}
.test-md {
test-property: 2rem
}
.test-lg {
test-property: 3rem
}
.hover\\:test-sm:hover {
test-property: 1rem
}
.hover\\:test-md:hover {
test-property: 2rem
}
.hover\\:test-lg:hover {
test-property: 3rem
}
@media (min-width: 400px) {
.sm\\:test-sm {
test-property: 1rem
}
.sm\\:test-md {
test-property: 2rem
}
.sm\\:test-lg {
test-property: 3rem
}
.sm\\:hover\\:test-sm:hover {
test-property: 1rem
}
.sm\\:hover\\:test-md:hover {
test-property: 2rem
}
.sm\\:hover\\:test-lg:hover {
test-property: 3rem
}
}
`
expect(result.css).toMatchCss(expected)
})
})
test('plugins with extra options can be created using the `createPlugin.withOptions` function', () => {
const plugin = createPlugin.withOptions(
function({ className }) {
return function({ addUtilities, theme, variants }) {
const utilities = _.fromPairs(
_.toPairs(theme('testPlugin')).map(([k, v]) => [
`.${className}-${k}`,
{ testProperty: v },
])
)
addUtilities(utilities, variants('testPlugin'))
}
},
function() {
return {
theme: {
testPlugin: {
sm: '1rem',
md: '2rem',
lg: '3rem',
},
},
variants: {
testPlugin: ['responsive', 'hover'],
},
}
}
)
return _postcss([
tailwind({
corePlugins: [],
theme: {
screens: {
sm: '400px',
},
},
plugins: [plugin({ className: 'banana' })],
}),
])
.process(
`
@tailwind base;
@tailwind components;
@tailwind utilities;
`,
{ from: undefined }
)
.then(result => {
const expected = `
.banana-sm {
test-property: 1rem
}
.banana-md {
test-property: 2rem
}
.banana-lg {
test-property: 3rem
}
.hover\\:banana-sm:hover {
test-property: 1rem
}
.hover\\:banana-md:hover {
test-property: 2rem
}
.hover\\:banana-lg:hover {
test-property: 3rem
}
@media (min-width: 400px) {
.sm\\:banana-sm {
test-property: 1rem
}
.sm\\:banana-md {
test-property: 2rem
}
.sm\\:banana-lg {
test-property: 3rem
}
.sm\\:hover\\:banana-sm:hover {
test-property: 1rem
}
.sm\\:hover\\:banana-md:hover {
test-property: 2rem
}
.sm\\:hover\\:banana-lg:hover {
test-property: 3rem
}
}
`
expect(result.css).toMatchCss(expected)
})
})
test('plugins created using `createPlugin.withOptions` do not need to be invoked if the user wants to use the default options', () => {
const plugin = createPlugin.withOptions(
function({ className } = { className: 'banana' }) {
return function({ addUtilities, theme, variants }) {
const utilities = _.fromPairs(
_.toPairs(theme('testPlugin')).map(([k, v]) => [
`.${className}-${k}`,
{ testProperty: v },
])
)
addUtilities(utilities, variants('testPlugin'))
}
},
function() {
return {
theme: {
testPlugin: {
sm: '1rem',
md: '2rem',
lg: '3rem',
},
},
variants: {
testPlugin: ['responsive', 'hover'],
},
}
}
)
return _postcss([
tailwind({
corePlugins: [],
theme: {
screens: {
sm: '400px',
},
},
plugins: [plugin],
}),
])
.process(
`
@tailwind base;
@tailwind components;
@tailwind utilities;
`,
{ from: undefined }
)
.then(result => {
const expected = `
.banana-sm {
test-property: 1rem
}
.banana-md {
test-property: 2rem
}
.banana-lg {
test-property: 3rem
}
.hover\\:banana-sm:hover {
test-property: 1rem
}
.hover\\:banana-md:hover {
test-property: 2rem
}
.hover\\:banana-lg:hover {
test-property: 3rem
}
@media (min-width: 400px) {
.sm\\:banana-sm {
test-property: 1rem
}
.sm\\:banana-md {
test-property: 2rem
}
.sm\\:banana-lg {
test-property: 3rem
}
.sm\\:hover\\:banana-sm:hover {
test-property: 1rem
}
.sm\\:hover\\:banana-md:hover {
test-property: 2rem
}
.sm\\:hover\\:banana-lg:hover {
test-property: 3rem
}
}
`
expect(result.css).toMatchCss(expected)
})
})

3
plugin.js Normal file
View File

@ -0,0 +1,3 @@
const createPlugin = require('./lib/util/createPlugin').default
module.exports = createPlugin

21
src/util/createPlugin.js Normal file
View File

@ -0,0 +1,21 @@
function createPlugin(plugin, config) {
return {
handler: plugin,
config,
}
}
createPlugin.withOptions = function(pluginFunction, configFunction) {
const optionsFunction = function(options) {
return {
handler: pluginFunction(options),
config: configFunction(options),
}
}
optionsFunction.__isOptionsFunction = true
return optionsFunction
}
export default createPlugin

View File

@ -29,6 +29,10 @@ export default function(plugins, config) {
const getConfigValue = (path, defaultValue) => _.get(config, path, defaultValue)
plugins.forEach(plugin => {
if (plugin.__isOptionsFunction) {
plugin = plugin()
}
const handler = isFunction(plugin) ? plugin : _.get(plugin, 'handler', () => {})
handler({

View File

@ -107,6 +107,9 @@ function extractPluginConfigs(configs) {
}
plugins.forEach(plugin => {
if (plugin.__isOptionsFunction) {
plugin = plugin()
}
allConfigs = [...allConfigs, ...extractPluginConfigs([get(plugin, 'config', {})])]
})
})