PostCSS 插件指南
PostCSS 插件是一个函数,它接收并通常转换来自 PostCSS 解析器的 CSS AST。
以下规则对所有 PostCSS 插件都是强制性的。
另请参阅 ClojureWerkz 对开源项目的建议。
1. API
1.1 使用 postcss-
前缀的清晰名称
仅通过阅读插件的名称,其目的就应该很清楚。如果你为 CSS 4 自定义媒体编写了转换器,那么 postcss-custom-media
将是一个好名称。如果你编写了一个支持 mixin 的插件,那么 postcss-mixins
将是一个好名称。
前缀 postcss-
表示该插件是 PostCSS 生态系统的一部分。
对于可以作为独立工具运行的插件,此规则不是强制性的,而用户不必知道它是由 PostCSS 提供支持的,例如 RTLCSS 和 Autoprefixer。
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.log
或 console.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.md
、History.md
或 GitHub Releases。访问 Keep A Changelog 以获取有关如何撰写其中之一的更多信息。
当然,你应该使用 SemVer。
5.4. 在 package.json
中包含 postcss-plugin
关键字
为 npm 编写的 PostCSS 插件必须在其 package.json
中包含 postcss-plugin
关键字。此特殊关键字将有助于提供有关 PostCSS 生态系统的反馈。
对于未发布到 npm 的包,这不是强制性的,但如果包格式可以包含关键字,则建议这样做。