Skip to content

Commit 4d3a3f8

Browse files
committed
feat: add option to use v3 style selectors
1 parent 6456d21 commit 4d3a3f8

File tree

2 files changed

+41
-14
lines changed

2 files changed

+41
-14
lines changed

index.js

Lines changed: 24 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -36,28 +36,40 @@ function getUpdatedDeclValue(prop, valueSpecifier) {
3636
}
3737
}
3838

39+
const v3CompatibleChildSelector = "* + *"
40+
const v4ChildSelector = ":not(:last-child)"
41+
42+
/**
43+
* @typedef PluginOptions
44+
* @property {boolean} [useV3CompatibleSelector] - Switch to using the "* + *" child selector as the ":not(:last-child)" selector implemented in tailwindcss v4 requires modifying attributes
45+
*/
46+
3947
/**
40-
* @type {import('postcss').PluginCreator}
48+
* @type {import('postcss').PluginCreator<PluginOptions>}
4149
*/
50+
4251
module.exports = (opts = {}) => {
4352
// Work with options here
4453

4554
return {
4655
postcssPlugin: 'postcss-tailwind-space-divide',
4756
Rule(rule) {
4857
rule.selectors = rule.selectors.map(selector => {
49-
return selector.replaceAll(/:not\(\[hidden\]\)\s?~\s?:not\(\[hidden\]\)/g, ':not(:last-child)')
50-
})
51-
rule.walkDecls(/(?:margin-(?:top|bottom|right|left|(?:inline-|block-)(?:end|start))|border-(?:top|bottom|right|left|(?:inline-|block-)(?:end|start))-width)/, decl => {
52-
// Grab the value specifier from the declaration value (eg. '1rem' or possibly any arbitrary value)
53-
const valueSpecifierMatch = decl.value.trim().match(/^calc\((.*) \* (?:calc\(1 - )?var\(--tw/)
54-
if (!valueSpecifierMatch) { return }
55-
const valueSpecifier = valueSpecifierMatch[1]
56-
const optimizedValue = getUpdatedDeclValue(decl.prop, valueSpecifier)
57-
if (optimizedValue) {
58-
decl.value = optimizedValue
59-
}
58+
return selector.replaceAll(/:not\(\[hidden\]\)\s?~\s?:not\(\[hidden\]\)/g, opts.useV3CompatibleSelector ? v3CompatibleChildSelector: v4ChildSelector)
6059
})
60+
if (!opts.useV3CompatibleSelector) {
61+
// If using the v4 child selector, we need to update the declarations for margin and border widths
62+
rule.walkDecls(/(?:margin-(?:top|bottom|right|left|(?:inline-|block-)(?:end|start))|border-(?:top|bottom|right|left|(?:inline-|block-)(?:end|start))-width)/, decl => {
63+
// Grab the value specifier from the declaration value (eg. '1rem' or possibly any arbitrary value)
64+
const valueSpecifierMatch = decl.value.trim().match(/^calc\((.*) \* (?:calc\(1 - )?var\(--tw/)
65+
if (!valueSpecifierMatch) { return }
66+
const valueSpecifier = valueSpecifierMatch[1]
67+
const optimizedValue = getUpdatedDeclValue(decl.prop, valueSpecifier)
68+
if (optimizedValue) {
69+
decl.value = optimizedValue
70+
}
71+
})
72+
}
6173
}
6274
}
6375
}

index.test.js

Lines changed: 17 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
import postcss from 'postcss'
22
import plugin from '.'
33
import { it, expect } from 'vitest'
4-
import { describe } from 'vitest'
54

65
async function run(input, output, opts = {}) {
76
let result = await postcss([plugin(opts)]).process(input, { from: undefined })
@@ -15,7 +14,7 @@ it('does not change unrelated CSS', async () => {
1514

1615
it('handles space-x-* selector', async () => {
1716
const input = `
18-
.space-x-4 > :not(:last-child) {
17+
.space-x-4 > :not([hidden]) ~ :not([hidden]) {
1918
--tw-space-x-reverse: 0;
2019
margin-right: calc(1rem * var(--tw-space-x-reverse));
2120
margin-left: calc(1rem * calc(1 - var(--tw-space-x-reverse)));
@@ -141,6 +140,22 @@ it('handles prefixed space classes', async () => {
141140
await run(input, output, {})
142141
})
143142

143+
it('preserves attributes when using v3 compatible selector', async () => {
144+
const input = `.divide-y-4 > :not([hidden]) ~ :not([hidden]) {
145+
--tw-divide-y-reverse: 0;
146+
border-top-width: calc(4px * calc(1 - var(--tw-divide-y-reverse)));
147+
border-bottom-width: calc(4px * var(--tw-divide-y-reverse));
148+
}
149+
`
150+
const ouput = `.divide-y-4 > * + * {
151+
--tw-divide-y-reverse: 0;
152+
border-top-width: calc(4px * calc(1 - var(--tw-divide-y-reverse)));
153+
border-bottom-width: calc(4px * var(--tw-divide-y-reverse));
154+
}
155+
`
156+
await run(input, ouput, { useV3CompatibleSelector: true })
157+
})
158+
144159

145160
it('ignores non-tailwind selectors', async () => {
146161
const input = `

0 commit comments

Comments
 (0)