Skip to content

improvement: merge :root rules, implements #16 #39

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 1 commit into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
34 changes: 30 additions & 4 deletions index.js
Original file line number Diff line number Diff line change
Expand Up @@ -50,12 +50,11 @@ module.exports = (options) => {
target_media_dark: null, // dark media query props
}

const targetSelector = custom_selector || ':root'
const adaptivePropSelector = getAdaptivePropSelector(adaptive_prop_selector)

return {
Once(node, { parse, result, Rule, AtRule }) {
let target_selector = custom_selector || ':root'

if (!files && !Object.keys(props).length) {
return console.warn('postcss-jit-props: Variable source(s) not passed.')
}
Expand Down Expand Up @@ -120,8 +119,8 @@ module.exports = (options) => {
STATE.mapped = new Set()
STATE.mapped_dark = new Set()

STATE.target_rule = new Rule({ selector: target_selector, source: node.first.source })
STATE.target_rule_dark = new Rule({ selector: target_selector, source: node.first.source })
STATE.target_rule = new Rule({ selector: targetSelector, source: node.first.source })
STATE.target_rule_dark = new Rule({ selector: targetSelector, source: node.first.source })
STATE.target_media_dark = new AtRule({ name: 'media', params: '(prefers-color-scheme: dark)', source: node.first.source })

if (layer) {
Expand All @@ -133,6 +132,33 @@ module.exports = (options) => {
STATE.target_ss = node.root()
},

OnceExit(root) {
// .filter creates a shallow copy of nodes, should be safe
const rootRules = root.nodes.filter(
(node) => node.type === 'rule' && node.selector === targetSelector
);

// :root is created by this plugin, skip merging
if (rootRules.length === 1) {
return;
}

// merge all the :root rules into :root generated by this plugin
// as it is prepended into the root (a file generally speaking)
// we just merge all in a first rule
const firstRootRule = rootRules[0];

for (let i = 1; i < rootRules.length; i++) {
const rootRule = rootRules[i];

rootRule.walkDecls((decl) => {
firstRootRule.append(decl.clone());
});

rootRule.remove();
}
},

AtRule: {
media: (atrule, { parse }) => {
// bail early if possible
Expand Down
89 changes: 55 additions & 34 deletions index.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -54,13 +54,13 @@ it('Can jit a single prop', async () => {
await run(
`a {
color: var(--red);
}`,
}`,
`:root {
--red: #f00;
}
a {
color: var(--red);
}`,
}`,
MockProps
)
})
Expand All @@ -84,13 +84,13 @@ it('Can jit a single prop that has fallbacks', async () => {
await run(
`a {
color: var(--red, hotpink);
}`,
}`,
`:root {
--red: #f00;
}
a {
color: var(--red, hotpink);
}`,
}`,
MockProps
)
})
Expand All @@ -114,14 +114,14 @@ it('Can jit a single prop that has fallbacks and nested props', async () => {
await run(
`a {
color: var(--red, var(--pink), hotpink);
}`,
}`,
`:root {
--red: #f00;
--pink: #ffc0cb;
}
a {
color: var(--red, var(--pink), hotpink);
}`,
}`,
MockProps
)
})
Expand All @@ -130,13 +130,13 @@ it('Can jit a single, undefined prop that has fallbacks and nested props', async
await run(
`a {
color: var(--orange, var(--pink), hotpink);
}`,
}`,
`:root {
--pink: #ffc0cb;
}
a {
color: var(--orange, var(--pink), hotpink);
}`,
}`,
MockProps
)
})
Expand Down Expand Up @@ -164,7 +164,7 @@ it('Can jit multiple props', async () => {
color: var(--red);
border-color: var(--pink);
padding-block-start: var( --size-1 );
}`,
}`,
`:root {
--red: #f00;
--pink: #ffc0cb;
Expand All @@ -174,7 +174,7 @@ a {
color: var(--red);
border-color: var(--pink);
padding-block-start: var( --size-1 );
}`,
}`,
MockProps
)
})
Expand All @@ -183,14 +183,14 @@ it('Can jit multiple props from shorthand', async () => {
await run(
`a {
padding-block: var(--size-1) var( --size-2 );
}`,
}`,
`:root {
--size-1: 1rem;
--size-2: 2rem;
}
a {
padding-block: var(--size-1) var( --size-2 );
}`,
}`,
MockProps
)
})
Expand All @@ -199,15 +199,15 @@ it('Can jit props from inside functions', async () => {
await run(
`a {
color: hsl(var(--h) var(--s) var( --l ));
}`,
}`,
`:root {
--h: 200;
--s: 50%;
--l: 50%;
}
a {
color: hsl(var(--h) var(--s) var( --l ));
}`,
}`,
MockProps
)
})
Expand All @@ -217,14 +217,14 @@ it('Only adds a prop one time to :root', async () => {
`a {
color: var(--red);
border-color: var(--red );
}`,
}`,
`:root {
--red: #f00;
}
a {
color: var(--red);
border-color: var(--red );
}`,
}`,
MockProps
)
})
Expand All @@ -233,7 +233,7 @@ it('Can jit props into a layer', async () => {
await run(
`a {
color: hsl(var(--h) var(--s) var( --l ));
}`,
}`,
`@layer test {
:root {
--h: 200;
Expand All @@ -243,7 +243,7 @@ it('Can jit props into a layer', async () => {
}
a {
color: hsl(var(--h) var(--s) var( --l ));
}`,
}`,
{
... MockProps,
layer: 'test',
Expand All @@ -255,12 +255,12 @@ it('Can jit a keyframe animation', async () => {
await run(
`a {
animation: var(--fade-in);
}`,
}`,
`:root {
--fade-in: fade-in .5s ease;
}a {
animation: var(--fade-in);
}@keyframes fade-in {to { opacity: 1 }}`,
}@keyframes fade-in {to { opacity: 1 }}`,
MockProps
)
})
Expand All @@ -269,13 +269,13 @@ it('Can jit an adaptive keyframe animation', async () => {
await run(
`a {
animation: var(--adaptive-fade);
}`,
}`,
`:root {
--adaptive-fade: adaptive-fade .5s ease;
}a {
animation: var(--adaptive-fade);
}@keyframes adaptive-fade {to { background: white }}@media (prefers-color-scheme: dark) {:root {}@keyframes adaptive-fade {to { background: black }}
}`,
}`,
MockProps
)
})
Expand All @@ -286,14 +286,14 @@ it('Can jit @custom-media', async () => {
a {
color: white;
}
}`,
}`,
`@custom-media --dark (prefers-color-scheme: dark);
:root{}
@media (--dark) {
a {
color: white;
}
}`,
}`,
MockProps
)
})
Expand Down Expand Up @@ -342,7 +342,7 @@ it('Can jit props from a CSS file', async () => {
border-color: var( --pink );
animation: var(--fade-in);
}
}`,
}`,
`@custom-media --dark (prefers-color-scheme: dark);
:root{
--red: #f00;
Expand All @@ -356,7 +356,7 @@ it('Can jit props from a CSS file', async () => {
animation: var(--fade-in);
}
}
@keyframes fade-in {to { opacity: 1 }}`,
@keyframes fade-in {to { opacity: 1 }}`,
{ files: ['./props.test.css']}
)
})
Expand All @@ -369,7 +369,7 @@ it('Can jit props from a CSS file via glob', async () => {
border-color: var( --pink );
animation: var(--fade-in);
}
}`,
}`,
`@custom-media --dark (prefers-color-scheme: dark);
:root{
--red: #f00;
Expand All @@ -383,7 +383,7 @@ it('Can jit props from a CSS file via glob', async () => {
animation: var(--fade-in);
}
}
@keyframes fade-in {to { opacity: 1 }}`,
@keyframes fade-in {to { opacity: 1 }}`,
{ files: ['./*.test.css']}
)
})
Expand All @@ -399,13 +399,13 @@ it('Can jit props to a custom selector', async () => {
await run(
`a {
color: var(--red);
}`,
}`,
`:global {
--red: #f00;
}
a {
color: var(--red);
}`,
}`,
{
... MockProps,
custom_selector: ':global',
Expand All @@ -417,10 +417,10 @@ it('Wont create a :root {} context unless props are found', async () => {
await run(
`a {
color: red;
}`,
}`,
`a {
color: red;
}`,
}`,
{
... MockProps
}
Expand All @@ -431,7 +431,7 @@ it('Can jit a light and dark adaptive prop', async () => {
await run(
`p {
color: var(--text);
}`,
}`,
`:root {
--text: white;
}
Expand All @@ -442,7 +442,7 @@ p {
:root {
--text: black;
}
}`,
}`,
MockProps
)
})
Expand Down Expand Up @@ -528,3 +528,24 @@ it('Supports parallel runners when reading from a file', async () => {
expect(resultE.css).toEqual('a { color: green; }')
expect(resultE.warnings()).toHaveLength(0)
})

it('Can merge :root rules', async () => {
await run(
`:root {
--red: red;
}

p {
color: var(--pink);
}`,
`:root {
--pink: #ffc0cb;
--red: red;
}

p {
color: var(--pink);
}`,
MockProps
)
})