105 lines
2.4 KiB
JavaScript
105 lines
2.4 KiB
JavaScript
const Tokenizer = require("css-selector-tokenizer")
|
|
|
|
function itMatchesOne(arr, term) {
|
|
return arr.some((i) => term.search(i) >= 0)
|
|
}
|
|
|
|
function parseAttrSelector(node) {
|
|
const { content } = node
|
|
const regex =
|
|
/(^class|^id)([*^?~|$=]*)+(?:("\s*)([^"\\]*?(?:\\.[^"\\]*)*?)(\s*")|('\s*)([^'\\]*?(?:\\.[^'\\]*)*?)(\s*'))/i
|
|
|
|
const [type, operator, head, classes, foot] = content.split(regex).filter((part) => part)
|
|
|
|
return {
|
|
type,
|
|
operator,
|
|
head,
|
|
classes: classes ? classes.split(" ").map((c) => c.replace(/"|'/g, "")) : [],
|
|
foot,
|
|
}
|
|
}
|
|
|
|
function attrStringify({ type, operator, head, classes, foot }) {
|
|
return `${type}${operator || ""}${head || ""}${classes.join(" ")}${foot || ""}`
|
|
}
|
|
|
|
function prefixNode(node, prefix) {
|
|
if (["class", "id"].includes(node.type)) {
|
|
return {
|
|
...node,
|
|
name: `${prefix}${node.name}`,
|
|
}
|
|
}
|
|
|
|
if (["attribute"].includes(node.type) && node.content) {
|
|
const { type, operator, head, classes, foot } = parseAttrSelector(node)
|
|
|
|
if (!["class", "id"].includes(type)) return node
|
|
|
|
return {
|
|
...node,
|
|
content: attrStringify({
|
|
type,
|
|
operator,
|
|
head,
|
|
classes: classes.map((cls) => `${prefix}${cls}`),
|
|
foot,
|
|
}),
|
|
}
|
|
}
|
|
|
|
return node
|
|
}
|
|
|
|
function iterateSelectorNodes(selector, options) {
|
|
const { prefix, ignore } = options
|
|
return {
|
|
...selector,
|
|
nodes: selector.nodes.map((node) => {
|
|
if (["selector", "nested-pseudo-class"].includes(node.type)) {
|
|
return iterateSelectorNodes(node, options)
|
|
}
|
|
|
|
if (itMatchesOne(ignore, Tokenizer.stringify(node))) return node
|
|
|
|
return prefixNode(node, prefix)
|
|
}),
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @type {import('postcss').PluginCreator}
|
|
*/
|
|
module.exports = (opts = {}) => {
|
|
const { prefix, ignore } = {
|
|
prefix: "",
|
|
ignore: [],
|
|
...opts,
|
|
}
|
|
|
|
if (typeof prefix !== "string") {
|
|
throw new Error("prefix option should be of type string.")
|
|
}
|
|
|
|
if (!Array.isArray(ignore)) {
|
|
throw new Error("ignore options should be an Array.")
|
|
}
|
|
|
|
if (!prefix.length) return
|
|
|
|
return {
|
|
postcssPlugin: "addprefix",
|
|
Root(root, postcss) {
|
|
root.walkRules((rule) => {
|
|
const parsed = Tokenizer.parse(rule.selector)
|
|
const selector = iterateSelectorNodes(parsed, { prefix, ignore })
|
|
|
|
rule.selector = Tokenizer.stringify(selector)
|
|
})
|
|
},
|
|
}
|
|
}
|
|
|
|
module.exports.postcss = true
|