-
Notifications
You must be signed in to change notification settings - Fork 39
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge :root rules into the single css-rule (#112)
* Merge :root rules into the single css-rule * Check if we have no error if there's no unused rules * Move file purpose description to the beginning of file * Use (not so) new js syntax to improve readability * Use walkDecls while traversing through the rules in plugin * Use second parameter of OnceExit to get `Rule`
- Loading branch information
Showing
6 changed files
with
208 additions
and
13 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,45 @@ | ||
// PostCSS plugin for ../script/clean-css.ts to merge all rules | ||
// with :root selector into the single rule in the beginning of the file | ||
|
||
import type { ChildNode, Plugin, Rule } from 'postcss' | ||
|
||
export const rootsMerger: Plugin = { | ||
postcssPlugin: 'roots-merger', | ||
prepare() { | ||
let rootNodes = new Map<string, ChildNode>() | ||
let rulesToRemove: Rule[] = [] | ||
|
||
return { | ||
OnceExit(root, { Rule }) { | ||
rulesToRemove.forEach(rule => rule.remove()) | ||
|
||
let rootRule = new Rule({ selector: ':root' }) | ||
|
||
rootRule.append(...rootNodes.values()) | ||
if (rootRule.nodes.length > 0) { | ||
root.prepend(rootRule) | ||
} | ||
}, | ||
Rule(rule) { | ||
if (rule.selector !== ':root') { | ||
return | ||
} | ||
|
||
if (rule.parent?.type === 'atrule') { | ||
return | ||
} | ||
|
||
rule.walkDecls(decl => { | ||
// remove rule from the map to preserve the rules order | ||
if (rootNodes.has(decl.prop)) { | ||
rootNodes.delete(decl.prop) | ||
} | ||
|
||
rootNodes.set(decl.prop, decl) | ||
}) | ||
|
||
rulesToRemove.push(rule) | ||
} | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,71 @@ | ||
import { equal } from 'node:assert' | ||
import { test } from 'node:test' | ||
import postcss from 'postcss' | ||
|
||
import { rootsMerger } from '../postcss/roots-merger.js' | ||
|
||
function run(input: string, output: string): void { | ||
let result = postcss([rootsMerger]).process(input, { from: undefined }) | ||
equal(result.css, output) | ||
} | ||
|
||
test('merges all :roots into one', () => { | ||
run( | ||
':root { --color-red: #f00; }' + | ||
':root { --color-blue: #00f; }' + | ||
'.some-class { color: tomato }' + | ||
':root { --color-green: #0f0; }', | ||
':root {' + | ||
' --color-red: #f00;' + | ||
' --color-blue: #00f;' + | ||
' --color-green: #0f0 ' + | ||
'}\n' + | ||
'.some-class { color: tomato }' | ||
) | ||
}) | ||
|
||
test('merges all :roots into one and respects last defined variable value', () => { | ||
run( | ||
':root { --color-red: #f00; }' + | ||
':root { --color-blue: #00f; }' + | ||
':root { --color-blue: blue; }' + | ||
'.some-class { color: tomato }' + | ||
':root { --color-green: #0f0; }', | ||
':root {' + | ||
' --color-red: #f00;' + | ||
' --color-blue: blue;' + | ||
' --color-green: #0f0 ' + | ||
'}\n' + | ||
'.some-class { color: tomato }' | ||
) | ||
}) | ||
|
||
test('merges all :roots into one except :roots with classes', () => { | ||
run( | ||
':root { --color-red: #f00; }' + | ||
':root.blue { --color-blue: #00f; }' + | ||
'.some-class { color: tomato }' + | ||
':root { --color-green: #0f0; }', | ||
':root {' + | ||
' --color-red: #f00;' + | ||
' --color-green: #0f0; ' + | ||
'}' + | ||
':root.blue { --color-blue: #00f; }' + | ||
'.some-class { color: tomato }' | ||
) | ||
}) | ||
|
||
test('merges all :roots into one except :roots under at-rules', () => { | ||
run( | ||
':root { --color-red: #f00; }' + | ||
'@media screen { :root { --color-blue: #00f; } }' + | ||
'.some-class { color: tomato }' + | ||
':root { --color-green: #0f0; }', | ||
':root {' + | ||
' --color-red: #f00;' + | ||
' --color-green: #0f0; ' + | ||
'}' + | ||
'@media screen { :root { --color-blue: #00f; } }' + | ||
'.some-class { color: tomato }' | ||
) | ||
}) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,62 @@ | ||
import { equal } from 'node:assert' | ||
import { beforeEach, test } from 'node:test' | ||
import postcss from 'postcss' | ||
|
||
import { | ||
getVarsCleanerError, | ||
resetCleanerGlobals, | ||
varsCleaner | ||
} from '../postcss/vars-cleaner.js' | ||
|
||
function run(input: string, output: string): string | undefined { | ||
let result = postcss([varsCleaner]).process(input, { from: undefined }) | ||
equal(result.css, output) | ||
|
||
return getVarsCleanerError() | ||
} | ||
|
||
beforeEach(() => { | ||
resetCleanerGlobals() | ||
}) | ||
|
||
test('clean unused palette colors', () => { | ||
let error = run( | ||
':root {' + | ||
'--red-100: #f00;' + | ||
'--green-200: #0f0;' + | ||
'--blue-300: #00f;' + | ||
'}' + | ||
'.selector {' + | ||
' color: var(--red-100)' + | ||
'}', | ||
':root {' + | ||
'--red-100:#f00;' + | ||
'}' + | ||
'.selector {' + | ||
' color: var(--red-100)' + | ||
'}' | ||
) | ||
|
||
equal(error, undefined) | ||
}) | ||
|
||
test('return error if unused css variables found', () => { | ||
let error = run( | ||
':root {' + | ||
'--used-variable: #00f;' + | ||
'--unused-variable: #00f;' + | ||
'}' + | ||
'.selector {' + | ||
' color: var(--used-variable)' + | ||
'}', | ||
':root {' + | ||
'--used-variable:#00f;' + | ||
'--unused-variable:#00f;' + | ||
'}' + | ||
'.selector {' + | ||
' color: var(--used-variable)' + | ||
'}' | ||
) | ||
|
||
equal(error, 'Unused CSS variables: --unused-variable') | ||
}) |