如何使用 postcss 附加 css 中的属性?

How to append attribute in css with postcss?

我有一个关注css:

a, a::after, p + .selector, .selector > .my-selector, .selector::before {

}

我想将 [data-123] 附加到所有选择器。所以像:

a[data-123], a::after[data-123], p[data-123] + .selector[data-123], .selector[data-123] > .my-selector[data-123], .selector::before[data-123] {

}

如何使用 postcss 执行此操作,或者是否有任何其他方法可以在运行时获得结果?

Vue 是这样做的:

  • 创建一个 util 文件:
//css-parser.js

import { Root } from "postcss"
import * as postcss from "postcss"
// postcss-selector-parser does have typings but it's problematic to work with.
const selectorParser = require("postcss-selector-parser")

export default postcss.plugin("add-id", options => root => {
    const id = options
    const keyframes = Object.create(null)

    root.each(function rewriteSelector(node) {
        if (!node.selector) {
            // handle media queries
            if (node.type === "atrule") {
                if (node.name === "media" || node.name === "supports") {
                    node.each(rewriteSelector)
                } else if (/-?keyframes$/.test(node.name)) {
                    // register keyframes
                    keyframes[node.params] = node.params =
                        node.params + "-" + id
                }
            }
            return
        }
        node.selector = selectorParser(selectors => {
            selectors.each(selector => {
                let node = null

                // find the last child node to insert attribute selector
                selector.each(n => {
                    // ">>>" combinator
                    // and /deep/ alias for >>>, since >>> doesn't work in SASS
                    if (
                        n.type === "combinator" &&
                        (n.value === ">>>" || n.value === "/deep/")
                    ) {
                        n.value = " "
                        n.spaces.before = n.spaces.after = ""
                        return false
                    }

                    // in newer versions of sass, /deep/ support is also dropped, so add a ::v-deep alias
                    if (n.type === "pseudo" && n.value === "::v-deep") {
                        n.value = n.spaces.before = n.spaces.after = ""
                        return false
                    }

                    if (n.type !== "pseudo" && n.type !== "combinator") {
                        node = n
                    }
                })

                if (node) {
                    node.spaces.after = ""
                } else {
                    // For deep selectors & standalone pseudo selectors,
                    // the attribute selectors are prepended rather than appended.
                    // So all leading spaces must be eliminated to avoid problems.
                    selector.first.spaces.before = ""
                }

                selector.insertAfter(
                    node,
                    selectorParser.attribute({
                        attribute: id
                    })
                )
            })
        }).processSync(node.selector)
    })

    // If keyframes are found in this <style>, find and rewrite animation names
    // in declarations.
    // Caveat: this only works for keyframes and animation rules in the same
    // <style> element.
    if (Object.keys(keyframes).length) {
        root.walkDecls(decl => {
            // individual animation-name declaration
            if (/^(-\w+-)?animation-name$/.test(decl.prop)) {
                decl.value = decl.value
                    .split(",")
                    .map(v => keyframes[v.trim()] || v.trim())
                    .join(",")
            }
            // shorthand
            if (/^(-\w+-)?animation$/.test(decl.prop)) {
                decl.value = decl.value
                    .split(",")
                    .map(v => {
                        const vals = v.trim().split(/\s+/)
                        const i = vals.findIndex(val => keyframes[val])
                        if (i !== -1) {
                            vals.splice(i, 1, keyframes[vals[i]])
                            return vals.join(" ")
                        } else {
                            return v
                        }
                    })
                    .join(",")
            }
        })
    }
})

  • 然后用作:
import hashsum from "hash-sum"
import cssParser from "@/utils/css-parser"

const uniqueHash = "data-e-" + hashsum("my-random-hash")

const processCss = (css, hash) => {
    const plugins = [cssParser(hash || uniqueHash)]
    const result = postcss(plugins).process(css)

    return result.css
}

完美运行。