PostCSS 插件指南

PostCSS 插件是一个函数,它接收并通常转换来自 PostCSS 解析器的 CSS AST。

以下规则对所有 PostCSS 插件都是强制性的

另请参阅 ClojureWerkz 对开源项目的建议

1. API

1.1 使用 postcss- 前缀的清晰名称

仅通过阅读插件的名称,其目的就应该很清楚。如果你为 CSS 4 自定义媒体编写了转换器,那么 postcss-custom-media 将是一个好名称。如果你编写了一个支持 mixin 的插件,那么 postcss-mixins 将是一个好名称。

前缀 postcss- 表示该插件是 PostCSS 生态系统的一部分。

对于可以作为独立工具运行的插件,此规则不是强制性的,而用户不必知道它是由 PostCSS 提供支持的,例如 RTLCSSAutoprefixer

1.2. 专注于一件事,并做好它

不要创建多工具插件。通常,将几个小型、单一用途的插件捆绑到一个插件包中是一个更好的解决方案。

例如,postcss-preset-env 包含许多小型插件,每个插件对应一个 W3C 规范。而 cssnano 为其每个优化包含一个单独的插件。

1.3. 不要使用 mixin

像 Compass 这样的预处理器库提供了带有 mixin 的 API。

PostCSS 插件不同。插件不能仅仅是 postcss-mixins 的一组 mixin。

为了实现你的目标,请考虑转换有效的 CSS 或使用自定义 at 规则和自定义属性。

1.4. 将 postcss 保持为 peerDependencies

AST 可能会因不同插件中不同的 postcss 版本而中断。不同的插件可以使用不同的节点创建器(如 postcss.decl())。

{
  "peerDependencies": {
    "postcss": "^8.0.0"
  }
}

最好不要导入 postcss

- const { list, decl } = require('postcss')
  module.exports = opts => {
    postcssPlugin: 'postcss-name',
-   Once (root) {
+   Once (root, { list, decl }) {
      // Plugin code
    }
  }
  module.exports.postcss = true

1.5. 使用插件名称设置 plugin.postcssPlugin

插件名称将用于错误消息和警告。

module.exports = opts => {
  return {
    postcssPlugin: 'postcss-name',
    Once (root) {
      // Plugin code
    }
  }
}
module.exports.postcss = true

2. 处理

2.1. 必须测试插件

还建议使用 Travis 等 CI 服务在不同环境中测试代码。你应该在(至少)Node.js 活动 LTS 和当前稳定版本中进行测试。

2.2. 尽可能使用异步方法

例如,使用 fs.writeFile 而不是 fs.writeFileSync

let { readFile } = require('fs').promises

module.exports = opts => {
  return {
    postcssPlugin: 'plugin-inline',
    async Decl (decl) {
      const imagePath = findImage(decl)
      if (imagePath) {
        let imageFile = await readFile(imagePath)
        decl.value = replaceUrl(decl.value, imageFile)
      }
    }
  }
}
module.exports.postcss = true

2.3. 使用快速节点扫描

订阅特定节点类型比调用 walk* 方法快得多

  module.exports = {
    postcssPlugin: 'postcss-example',
-   Once (root) {
-     root.walkDecls(decl => {
-       // Slow
-     })
-   }
+   Declaration (decl) {
+     // Faster
+   }
  }
  module.exports.postcss = true

但是,如果你知道声明的属性或 at 规则的名称,你可以让扫描速度更快

  module.exports = {
    postcssPlugin: 'postcss-example',
-   Declaration (decl) {
-     if (decl.prop === 'color') {
-       // Faster
-     }
-   }
+   Declaration: {
+     color: decl => {
+       // The fastest
+     }
+   }
  }
  module.exports.postcss = true

2.4. 为新节点设置 node.source

每个节点都必须有一个相关的 source,以便 PostCSS 可以生成准确的源映射。

因此,如果你基于某个现有声明添加了一个新声明,你应该克隆现有声明以保存该原始 source

if (needPrefix(decl.prop)) {
  decl.cloneBefore({ prop: '-webkit-' + decl.prop })
}

你还可以直接设置 source,从某个现有节点复制

if (decl.prop === 'animation') {
  const keyframe = createAnimationByName(decl.value)
  keyframes.source = decl.source
  decl.root().append(keyframes)
}

2.5. 仅使用公共 PostCSS API

PostCSS 插件不得依赖于未记录的属性或方法,这些属性或方法可能会在任何次要版本中发生更改。公共 API 在 API 文档 中进行了描述。

3. 依赖项

3.1. 使用消息指定依赖项

如果插件依赖于另一个文件,则应通过将 dependency 消息附加到 result 来指定它

result.messages.push({
  type: 'dependency',
  plugin: 'postcss-import',
  file: '/imported/file.css',
  parent: result.opts.from
})

应使用 dir-dependency 消息类型指定目录依赖项。默认情况下,目录中的所有文件(递归)都被视为依赖项。可以使用可选的 glob 属性来指示仅应考虑与特定 glob 模式匹配的文件。

result.messages.push({
  type: 'dir-dependency',
  plugin: 'postcss-import',
  dir: '/imported',
  glob: '**/*.css', // optional
  parent: result.opts.from
})

4. 错误

4.1. 对与 CSS 相关的错误使用 node.error

如果你因输入 CSS(如混合插件中的未知名称)而出现错误,则应使用 node.error 创建包含源位置的错误

if (typeof mixins[name] === 'undefined') {
  throw node.error('Unknown mixin ' + name)
}

4.2. 对警告使用 result.warn

请勿使用 console.logconsole.warn 打印警告,因为某些 PostCSS 运行程序可能不允许控制台输出。

Declaration (decl, { result }) {
  if (outdated(decl.prop)) {
    result.warn(decl.prop + ' is outdated', { node: decl })
  }
}

如果 CSS 输入是警告的来源,插件必须设置 node 选项。

5. 文档

5.1. 用英语记录你的插件

PostCSS 插件必须用英语撰写其 README.md。不要担心你的英语水平,因为开源社区会帮你纠正错误。

当然,欢迎你用其他语言撰写文档;只要为它们命名得当即可(例如 README.ja.md)。

5.2. 包含输入和输出示例

插件的 README.md 必须包含示例输入和输出 CSS。清晰的示例是描述插件工作原理的最佳方式。

README.md 的第一部分是放置示例的好地方。请参阅 postcss-opacity 以获取示例。

当然,如果你的插件不转换 CSS,则此准则不适用。

5.3. 维护变更日志

PostCSS 插件必须在一个单独的文件中描述其所有版本的更改,例如 CHANGELOG.mdHistory.mdGitHub Releases。访问 Keep A Changelog 以获取有关如何撰写其中之一的更多信息。

当然,你应该使用 SemVer

5.4. 在 package.json 中包含 postcss-plugin 关键字

为 npm 编写的 PostCSS 插件必须在其 package.json 中包含 postcss-plugin 关键字。此特殊关键字将有助于提供有关 PostCSS 生态系统的反馈。

对于未发布到 npm 的包,这不是强制性的,但如果包格式可以包含关键字,则建议这样做。