rollup-plugin-react-scoped-css
Version:
A rollup plugin designed to allow scoped css to be run in react (Compatible with vite and rollup)
88 lines (87 loc) • 3.47 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", { value: true });
exports.scopeCss = scopeCss;
const css_tree_1 = require("css-tree");
const getSelectors = (items) => {
const selectorLists = items.reduce((acc, node) => {
if (node.type === "Rule" && node?.prelude?.type === "SelectorList") {
acc.push(node.prelude.children);
}
else if (node.type === "Atrule" && node.name === "media") {
node.block?.children.forEach((rule) => {
if (rule.type === "Atrule" && rule.name === "media") {
const selectors = getSelectors(rule.block?.children);
acc.push(...selectors);
}
if (rule.type === "Rule" && rule.prelude.type === "SelectorList") {
acc.push(rule.prelude.children);
}
});
}
return acc;
}, []);
return selectorLists;
};
const isScopePiercingPseudoSelector = (item) => {
return (item?.data?.type === "PseudoElementSelector" &&
(item?.data?.name === "v-deep" || item?.data?.name === "deep"));
};
const isPseudoSelector = (item) => {
return (item?.data?.type === "PseudoClassSelector" ||
item?.data?.type === "PseudoElementSelector");
};
const isCombinator = (item) => {
return item?.data?.type === "Combinator";
};
function scopeCss(css, filename, hash) {
try {
const ast = (0, css_tree_1.parse)(css);
const selectorLists = getSelectors(ast.children);
const attributeSelector = {
type: "AttributeSelector",
name: { type: "Identifier", name: hash },
matcher: null,
value: null,
flags: null,
};
selectorLists.forEach((selectorList) => {
selectorList.forEach((selector) => {
if (selector.type !== "Selector") {
return;
}
let item = selector.children.head;
const itemsToInsertScopeBefore = [];
let currentChunkIndex = 0;
while (item !== null) {
if (isScopePiercingPseudoSelector(item)) {
Object.assign(item.data, attributeSelector);
break;
}
else if (isCombinator(item) || isPseudoSelector(item)) {
currentChunkIndex += 1;
}
else if (item.next) {
itemsToInsertScopeBefore[currentChunkIndex] = item.next;
}
else {
selector.children.appendData(attributeSelector);
break;
}
item = item?.next ?? null;
}
// This is true if one of the two break statements above was run. Which means we don't want to add to the last one.
if (itemsToInsertScopeBefore[currentChunkIndex]) {
itemsToInsertScopeBefore.pop();
}
itemsToInsertScopeBefore.forEach((scopePositionItem) => {
selector.children.insertData(attributeSelector, scopePositionItem);
});
});
});
return (0, css_tree_1.generate)(ast);
}
catch (e) {
console.log(`Failed scoping css of file: ${filename}\n\nError:\n`, e);
return css;
}
}