Press n or j to go to the next uncovered block, b, p or k for the previous block.
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 | 8x 8x 8x 8x 18x 9x 8x 18x 15x 15x 4x 4x 4x 4x 11x 8x 2x 5x 5x 5x 2x 3x 1x | /**
* postcss-merge-layers
*
* Merges multiple `@layer <name> { ... }` blocks that share the same layer name
* into a single `@layer <name> { ... }` block per unique name.
*
* The merged block is placed at the position of the first occurrence.
* The `@layer` order declaration (e.g. `@layer deprecated, theme, customization;`)
* is left untouched.
*
* This is useful for theme-layer outputs where postcss-scope-tokens and source
* files each produce their own `@layer theme` blocks, resulting in many repeated
* wrappers in the final output.
*/
/**
* @type {import('postcss').PluginCreator}
*/
const postcssMergeLayers = () => ({
postcssPlugin: 'postcss-merge-layers',
OnceExit(root) {
/** @type {Map<string, import('postcss').AtRule>} */
const targets = new Map();
// Find the layer order declaration (e.g. `@layer deprecated, theme, customization;`)
// to use as the canonical sort order for merged blocks.
/** @type {string[]} */
let declaredOrder = [];
root.walkAtRules('layer', (atRule) => {
if (!atRule.nodes && declaredOrder.length === 0) {
declaredOrder = atRule.params.split(',').map((s) => s.trim());
}
});
root.walkAtRules('layer', (atRule) => {
// Skip order declarations
if (!atRule.nodes) return;
const name = atRule.params.trim();
if (targets.has(name)) {
// Move all children into the first occurrence, then remove the empty shell
const target = targets.get(name);
atRule.each((node) => {
target.append(node.clone());
});
atRule.remove();
} else {
// First occurrence becomes the merge target
targets.set(name, atRule);
}
});
// Reorder merged blocks to match the declared layer order so output is
// deterministic regardless of source file glob ordering.
if (declaredOrder.length > 0 && targets.size > 1) {
const sorted = [...targets.entries()].sort((a, b) => {
const aIdx = declaredOrder.indexOf(a[0]);
const bIdx = declaredOrder.indexOf(b[0]);
return (aIdx === -1 ? declaredOrder.length : aIdx) - (bIdx === -1 ? declaredOrder.length : bIdx);
});
// Move each block after the previous one to enforce order.
// The first block anchors the sequence; subsequent blocks are placed after it.
for (let i = 1; i < sorted.length; i++) {
sorted[i - 1][1].after(sorted[i][1]);
}
}
},
});
postcssMergeLayers.postcss = true;
export default postcssMergeLayers;
|