| # PostCSS Plugin Guidelines |
| |
| A PostCSS plugin is a function that receives and, usually, |
| transforms a CSS AST from the PostCSS parser. |
| |
| The rules below are *mandatory* for all PostCSS plugins. |
| |
| See also [ClojureWerkz’s recommendations] for open source projects. |
| |
| [ClojureWerkz’s recommendations]: http://blog.clojurewerkz.org/blog/2013/04/20/how-to-make-your-open-source-project-really-awesome/ |
| |
| ## 1. API |
| |
| ### 1.1 Clear name with `postcss-` prefix |
| |
| The plugin’s purpose should be clear just by reading its name. |
| If you wrote a transpiler for CSS 4 Custom Media, `postcss-custom-media` |
| would be a good name. If you wrote a plugin to support mixins, |
| `postcss-mixins` would be a good name. |
| |
| The prefix `postcss-` shows that the plugin is part of the PostCSS ecosystem. |
| |
| This rule is not mandatory for plugins that can run as independent tools, |
| without the user necessarily knowing that it is powered by |
| PostCSS — for example, [RTLCSS] and [Autoprefixer]. |
| |
| [Autoprefixer]: https://github.com/postcss/autoprefixer |
| [RTLCSS]: https://rtlcss.com/ |
| |
| ### 1.2. Do one thing, and do it well |
| |
| Do not create multitool plugins. Several small, one-purpose plugins bundled into |
| a plugin pack is usually a better solution. |
| |
| For example, [`postcss-preset-env`] contains many small plugins, |
| one for each W3C specification. And [`cssnano`] contains a separate plugin |
| for each of its optimization. |
| |
| [`postcss-preset-env`]: https://preset-env.cssdb.org/ |
| [`cssnano`]: https://github.com/ben-eb/cssnano |
| |
| ### 1.3. Do not use mixins |
| |
| Preprocessors libraries like Compass provide an API with mixins. |
| |
| PostCSS plugins are different. |
| A plugin cannot be just a set of mixins for [`postcss-mixins`]. |
| |
| To achieve your goal, consider transforming valid CSS |
| or using custom at-rules and custom properties. |
| |
| [`postcss-mixins`]: https://github.com/postcss/postcss-mixins |
| |
| ### 1.4. Create plugin by `postcss.plugin` |
| |
| By wrapping your function in this method, |
| you are hooking into a common plugin API: |
| |
| ```js |
| module.exports = postcss.plugin('plugin-name', opts => { |
| return (root, result) => { |
| // Plugin code |
| } |
| }) |
| ``` |
| |
| ## 2. Processing |
| |
| ### 2.1. Plugin must be tested |
| |
| A CI service like [Travis] is also recommended for testing code in |
| different environments. You should test in (at least) Node.js [active LTS](https://github.com/nodejs/LTS) and current stable version. |
| |
| [Travis]: https://travis-ci.org/ |
| |
| ### 2.2. Use asynchronous methods whenever possible |
| |
| For example, use `fs.writeFile` instead of `fs.writeFileSync`: |
| |
| ```js |
| postcss.plugin('plugin-sprite', opts => { |
| return (root, result) => { |
| |
| return new Promise((resolve, reject) => { |
| const sprite = makeSprite() |
| fs.writeFile(opts.file, sprite, err => { |
| if (err) return reject(err) |
| resolve() |
| }) |
| }) |
| |
| } |
| }) |
| ``` |
| |
| ### 2.3. Set `node.source` for new nodes |
| |
| Every node must have a relevant `source` so PostCSS can generate |
| an accurate source map. |
| |
| So if you add a new declaration based on some existing declaration, you should |
| clone the existing declaration in order to save that original `source`. |
| |
| ```js |
| if (needPrefix(decl.prop)) { |
| decl.cloneBefore({ prop: '-webkit-' + decl.prop }) |
| } |
| ``` |
| |
| You can also set `source` directly, copying from some existing node: |
| |
| ```js |
| if (decl.prop === 'animation') { |
| const keyframe = createAnimationByName(decl.value) |
| keyframes.source = decl.source |
| decl.root().append(keyframes) |
| } |
| ``` |
| |
| ### 2.4. Use only the public PostCSS API |
| |
| PostCSS plugins must not rely on undocumented properties or methods, |
| which may be subject to change in any minor release. The public API |
| is described in [API docs]. |
| |
| [API docs]: http://api.postcss.org/ |
| |
| ## 3. Errors |
| |
| ### 3.1. Use `node.error` on CSS relevant errors |
| |
| If you have an error because of input CSS (like an unknown name |
| in a mixin plugin) you should use `node.error` to create an error |
| that includes source position: |
| |
| ```js |
| if (typeof mixins[name] === 'undefined') { |
| throw decl.error('Unknown mixin ' + name, { plugin: 'postcss-mixins' }) |
| } |
| ``` |
| |
| ### 3.2. Use `result.warn` for warnings |
| |
| Do not print warnings with `console.log` or `console.warn`, |
| because some PostCSS runner may not allow console output. |
| |
| ```js |
| if (outdated(decl.prop)) { |
| result.warn(decl.prop + ' is outdated', { node: decl }) |
| } |
| ``` |
| |
| If CSS input is a source of the warning, the plugin must set the `node` option. |
| |
| ## 4. Documentation |
| |
| ### 4.1. Document your plugin in English |
| |
| PostCSS plugins must have their `README.md` wrote in English. Do not be afraid |
| of your English skills, as the open source community will fix your errors. |
| |
| Of course, you are welcome to write documentation in other languages; |
| just name them appropriately (e.g. `README.ja.md`). |
| |
| ### 4.2. Include input and output examples |
| |
| The plugin's `README.md` must contain example input and output CSS. |
| A clear example is the best way to describe how your plugin works. |
| |
| The first section of the `README.md` is a good place to put examples. |
| See [postcss-opacity](https://github.com/iamvdo/postcss-opacity) for an example. |
| |
| Of course, this guideline does not apply if your plugin does not |
| transform the CSS. |
| |
| ### 4.3. Maintain a changelog |
| |
| PostCSS plugins must describe the changes of all their releases |
| in a separate file, such as `CHANGELOG.md`, `History.md`, or [GitHub Releases]. |
| Visit [Keep A Changelog] for more information about how to write one of these. |
| |
| Of course, you should be using [SemVer]. |
| |
| [Keep A Changelog]: http://keepachangelog.com/ |
| [GitHub Releases]: https://help.github.com/articles/creating-releases/ |
| [SemVer]: http://semver.org/ |
| |
| ### 4.4. Include `postcss-plugin` keyword in `package.json` |
| |
| PostCSS plugins written for npm must have the `postcss-plugin` keyword |
| in their `package.json`. This special keyword will be useful for feedback about |
| the PostCSS ecosystem. |
| |
| For packages not published to npm, this is not mandatory, but is recommended |
| if the package format can contain keywords. |