Skip to content
This repository was archived by the owner on Apr 6, 2021. It is now read-only.

Commit 7c1e53e

Browse files
committed
Switch everything to PostCSS nodes instead of custom format
1 parent d8209e6 commit 7c1e53e

13 files changed

+227
-180
lines changed

TODO.md

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -88,10 +88,14 @@
8888
- [x] Support "unconditional" rules — styles that should be included in the CSS no matter what
8989
- [x] Unify components/utilities into single "rules" concept, remove tons of duplication
9090

91+
### Mar 5
92+
93+
- [x] Make rule tuple format support comments and any other node types (@font-face) properly (considering abandoning this entire data structure in favor of PostCSS)
94+
- [x] Unify base styles into the same "rules" abstraction
95+
9196
#### Next
9297

93-
- [ ] Make rule tuple format support comments and any other node types (@font-face) properly (considering abandoning this entire data structure in favor of PostCSS)
94-
- [ ] Unify base styles into the same "rules" abstraction
98+
- [ ] Support `modifySelectors` in variant API
9599
- [ ] Make prefixes work
96100
- [ ] Make important work
97101
- [ ] Make separator work

src/corePlugins/index.js

Lines changed: 25 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,10 @@
1+
const postcss = require('postcss')
12
const nameClass = require('tailwindcss/lib/util/nameClass').default
23
const buildMediaQuery = require('tailwindcss/lib/util/buildMediaQuery').default
34
const transformThemeValue = require('tailwindcss/lib/util/transformThemeValue').default
45
const {
56
updateLastClasses,
67
updateAllClasses,
7-
transformRule,
88
transformAllSelectors,
99
transformAllClasses,
1010
transformLastClasses,
@@ -60,16 +60,22 @@ module.exports = {
6060

6161
addVariant(
6262
'motion-safe',
63-
transformLastClasses((className) => {
64-
return `motion-safe:${className}`
65-
}, '@media (prefers-reduced-motion: no-preference)')
63+
transformLastClasses(
64+
(className) => {
65+
return `motion-safe:${className}`
66+
},
67+
() => postcss.atRule({ name: 'media', params: '(prefers-reduced-motion: no-preference)' })
68+
)
6669
)
6770

6871
addVariant(
6972
'motion-reduce',
70-
transformLastClasses((className) => {
71-
return `motion-reduce:${className}`
72-
}, '@media (prefers-reduced-motion: reduce)')
73+
transformLastClasses(
74+
(className) => {
75+
return `motion-reduce:${className}`
76+
},
77+
() => postcss.atRule({ name: 'media', params: '(prefers-reduced-motion: reduce)' })
78+
)
7379
)
7480

7581
addVariant(
@@ -104,9 +110,12 @@ module.exports = {
104110
} else if (config.darkMode === 'media') {
105111
addVariant(
106112
'dark',
107-
transformLastClasses((className) => {
108-
return `dark:${className}`
109-
}, '@media (prefers-color-scheme: dark)')
113+
transformLastClasses(
114+
(className) => {
115+
return `dark:${className}`
116+
},
117+
() => postcss.atRule({ name: 'media', params: '(prefers-color-scheme: dark)' })
118+
)
110119
)
111120
}
112121

@@ -116,9 +125,12 @@ module.exports = {
116125

117126
addVariant(
118127
screen,
119-
transformLastClasses((className) => {
120-
return `${screen}:${className}`
121-
}, `@media ${query}`)
128+
transformLastClasses(
129+
(className) => {
130+
return `${screen}:${className}`
131+
},
132+
() => postcss.atRule({ name: 'media', params: query })
133+
)
122134
)
123135
}
124136
},
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
<div
2+
class="opacity-50 custom-util hover:custom-util group-hover:custom-util foo:custom-util foo:hover:custom-util sm:custom-util dark:custom-util motion-safe:custom-util md:dark:motion-safe:foo:active:custom-util"
3+
></div>

src/index.test.css

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,12 @@
99
color: purple;
1010
}
1111
}
12+
.apply-1 {
13+
margin-top: 1.5rem;
14+
}
15+
.apply-2 {
16+
margin-top: 1.5rem;
17+
}
1218
.apply-test {
1319
margin-top: 1.5rem;
1420
--tw-bg-opacity: 1;
@@ -223,6 +229,9 @@ div {
223229
* {
224230
padding: 5px;
225231
}
232+
.mt-6 {
233+
margin-top: 1.5rem;
234+
}
226235
.grid-cols-\[200px\,repeat\(auto-fill\,minmax\(15\%\,100px\)\)\,300px\] {
227236
grid-template-columns: 200px repeat(auto-fill, minmax(15%, 100px)) 300px;
228237
}

src/index.test.html

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
class="container hover:container sm:container md:container text-center sm:text-center md:text-center"
1414
></div>
1515
<div class="grid-cols-[200px,repeat(auto-fill,minmax(15%,100px)),300px]"></div>
16+
<div class="mt-6"></div>
1617
<div class="aspect-w-1 aspect-h-2"></div>
1718
<div class="aspect-w-3 aspect-h-4"></div>
1819
<div class="filter-none filter-grayscale"></div>

src/index.test.js

Lines changed: 83 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -81,6 +81,12 @@ test('it works', () => {
8181
color: purple;
8282
}
8383
}
84+
.apply-1 {
85+
@apply mt-6;
86+
}
87+
.apply-2 {
88+
@apply mt-6;
89+
}
8490
.apply-test {
8591
@apply mt-6 bg-pink-500 hover:font-bold focus:hover:font-bold sm:bg-green-500 sm:focus:even:bg-pink-200;
8692
}
@@ -138,6 +144,83 @@ test('it works', () => {
138144
})
139145
})
140146

147+
test('using postcss for everything wip', () => {
148+
let config = {
149+
darkMode: 'class',
150+
purge: [path.resolve(__dirname, './index.test.adding-a-custom-variant.html')],
151+
corePlugins: ['opacity'],
152+
theme: {},
153+
plugins: [
154+
function ({ addVariant }) {
155+
addVariant(
156+
'foo',
157+
({ container }) => {
158+
container.walkRules((rule) => {
159+
rule.selector = `.foo\\:${rule.selector.slice(1)}`
160+
rule.walkDecls((decl) => {
161+
decl.important = true
162+
})
163+
})
164+
},
165+
{ before: 'sm' }
166+
)
167+
},
168+
],
169+
}
170+
171+
let css = `
172+
@tailwind utilities;
173+
@layer utilities {
174+
.custom-util {
175+
background: #abcdef;
176+
}
177+
}
178+
`
179+
180+
return run(css, config).then((result) => {
181+
expect(result.css).toMatchCss(`
182+
.opacity-50 {
183+
opacity: 0.5;
184+
}
185+
.custom-util {
186+
background: #abcdef;
187+
}
188+
.hover\\:custom-util:hover {
189+
background: #abcdef;
190+
}
191+
.group:hover .group-hover\\:custom-util {
192+
background: #abcdef;
193+
}
194+
@media (prefers-reduced-motion: no-preference) {
195+
.motion-safe\\:custom-util {
196+
background: #abcdef;
197+
}
198+
}
199+
.dark .dark\\:custom-util {
200+
background: #abcdef;
201+
}
202+
.foo\\:custom-util {
203+
background: #abcdef !important;
204+
}
205+
.foo\\:hover\\:custom-util:hover {
206+
background: #abcdef !important;
207+
}
208+
@media (min-width: 640px) {
209+
.sm\\:custom-util {
210+
background: #abcdef;
211+
}
212+
}
213+
@media (min-width: 768px) {
214+
@media (prefers-reduced-motion: no-preference) {
215+
.dark .md\\:dark\\:motion-safe\\:foo\\:active\\:custom-util:active {
216+
background: #abcdef !important;
217+
}
218+
}
219+
}
220+
`)
221+
})
222+
})
223+
141224
function format(input) {
142225
return prettier.format(input, {
143226
parser: 'css',

src/lib/expandApplyAtRules.js

Lines changed: 11 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
1+
const postcss = require('postcss')
12
const generateRules = require('./generateRules')
2-
const { bigSign, toPostCssNode, isPlainObject } = require('./utils')
3+
const { bigSign } = require('./utils')
34
const escape = require('tailwindcss/lib/util/escapeClassName').default
45

56
function expandApplyAtRules(context) {
@@ -48,15 +49,6 @@ function expandApplyAtRules(context) {
4849
.join(', ')
4950
}
5051

51-
function updateSelectors(rule, apply, candidate) {
52-
return rule.map(([selector, rule]) => {
53-
if (!isPlainObject(rule)) {
54-
return [selector, updateSelectors(rule, apply, candidate)]
55-
}
56-
return [replaceSelector(apply.parent.selector, selector, candidate), rule]
57-
})
58-
}
59-
6052
for (let apply of applies) {
6153
let siblings = []
6254
let applyCandidates = apply.params.split(/[\s\t\n]+/g)
@@ -67,16 +59,15 @@ function expandApplyAtRules(context) {
6759
}
6860

6961
let rules = context.classCache.get(applyCandidate)
70-
for (let [meta, [selector, rule]] of rules) {
71-
siblings.push([
72-
meta,
73-
toPostCssNode(
74-
!isPlainObject(rule)
75-
? [selector, updateSelectors(rule, apply, applyCandidate)]
76-
: [replaceSelector(apply.parent.selector, selector, applyCandidate), rule],
77-
context.postCssNodeCache
78-
),
79-
])
62+
63+
for (let [meta, node] of rules) {
64+
let root = postcss.root({ nodes: [node] })
65+
66+
root.walkRules((rule) => {
67+
rule.selector = replaceSelector(apply.parent.selector, rule.selector, applyCandidate)
68+
})
69+
70+
siblings.push([meta, root.nodes[0]])
8071
}
8172
}
8273

src/lib/expandTailwindAtRules.js

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ const fs = require('fs')
22
const fastGlob = require('fast-glob')
33
const sharedState = require('./sharedState')
44
const generateRules = require('./generateRules')
5-
const { bigSign, toPostCssNode } = require('./utils')
5+
const { bigSign } = require('./utils')
66

77
let env = sharedState.env
88
let contentMatchCache = sharedState.contentMatchCache
@@ -46,6 +46,7 @@ function buildStylesheet(rules, context) {
4646
let sortedRules = rules.sort(([a], [z]) => bigSign(a - z))
4747

4848
let returnValue = {
49+
base: new Set(),
4950
components: new Set(),
5051
utilities: new Set(),
5152
screens: new Set(),
@@ -57,6 +58,11 @@ function buildStylesheet(rules, context) {
5758
continue
5859
}
5960

61+
if (sort & context.layerOrder.base) {
62+
returnValue.base.add(rule)
63+
continue
64+
}
65+
6066
if (sort & context.layerOrder.components) {
6167
returnValue.components.add(rule)
6268
continue
@@ -152,17 +158,18 @@ function expandTailwindAtRules(context, registerDependency) {
152158
env.DEBUG && console.timeEnd('Generate rules')
153159

154160
// We only ever add to the classCache, so if it didn't grow, there is nothing new.
161+
env.DEBUG && console.time('Build stylesheet')
155162
if (context.stylesheetCache === null || context.classCache.size !== classCacheCount) {
156-
env.DEBUG && console.time('Build stylesheet')
157163
for (let rule of rules) {
158164
context.ruleCache.add(rule)
159165
}
160166

161167
context.stylesheetCache = buildStylesheet([...context.ruleCache], context)
162-
env.DEBUG && console.timeEnd('Build stylesheet')
163168
}
169+
env.DEBUG && console.timeEnd('Build stylesheet')
164170

165171
let {
172+
base: baseNodes,
166173
components: componentNodes,
167174
utilities: utilityNodes,
168175
screens: screenNodes,
@@ -173,7 +180,7 @@ function expandTailwindAtRules(context, registerDependency) {
173180
// Replace any Tailwind directives with generated CSS
174181

175182
if (layerNodes.base) {
176-
layerNodes.base.before([...context.baseRules])
183+
layerNodes.base.before([...baseNodes])
177184
layerNodes.base.remove()
178185
}
179186

src/lib/generateRules.js

Lines changed: 16 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
const postcss = require('postcss')
12
const { toPostCssNode } = require('./utils')
23

34
// Generate match permutations for a class candidate, like:
@@ -37,21 +38,22 @@ function applyVariant(variant, matches, { variantMap }) {
3738
let result = []
3839

3940
for (let [{ sort, layer }, rule] of matches) {
40-
// TODO: Support these options again
41-
// let [, , options = {}] = rule
41+
let options = rule.__tailwind ?? {}
4242

43-
// if (options.respectVariants === false) {
44-
// result.push([{ sort, layer }, rule])
45-
// continue
46-
// }
43+
if (options.respectVariants === false) {
44+
result.push([{ sort, layer }, rule])
45+
continue
46+
}
47+
48+
let container = postcss.root({ nodes: [rule] })
4749

48-
let ruleWithVariant = applyThisVariant(rule)
50+
let ruleWithVariant = applyThisVariant({ container })
4951

5052
if (ruleWithVariant === null) {
5153
continue
5254
}
5355

54-
let withOffset = [{ sort: variantSort | sort, layer }, ruleWithVariant]
56+
let withOffset = [{ sort: variantSort | sort, layer }, container.nodes[0]]
5557
result.push(withOffset)
5658
}
5759

@@ -110,15 +112,18 @@ function generateRules(tailwindConfig, candidates, context) {
110112
let matches = []
111113
let [plugins, modifier] = matchedPlugins
112114

113-
// console.log(plugins, modifier)
114-
115115
for (let [sort, plugin] of plugins) {
116-
console.log(plugin)
117116
if (typeof plugin === 'function') {
118117
for (let result of plugin(modifier, pluginHelpers)) {
118+
if (Array.isArray(result)) {
119+
result = toPostCssNode(result, context.postCssNodeCache)
120+
}
119121
matches.push([sort, result])
120122
}
121123
} else {
124+
if (Array.isArray(plugin)) {
125+
plugin = toPostCssNode(plugin, context.postCssNodeCache)
126+
}
122127
matches.push([sort, plugin])
123128
}
124129
}

0 commit comments

Comments
 (0)