|
|
- # 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.
|