tailwindcss 0.0.0-insiders.e2d5f21 → 0.0.0-insiders.e302ef1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +244 -3
- package/LICENSE +1 -2
- package/README.md +8 -4
- package/colors.js +2 -1
- package/defaultConfig.js +2 -1
- package/defaultTheme.js +2 -1
- package/lib/cli.js +123 -88
- package/lib/corePlugins.js +288 -360
- package/lib/css/preflight.css +4 -4
- package/lib/featureFlags.js +4 -5
- package/lib/index.js +12 -3
- package/lib/lib/cacheInvalidation.js +69 -0
- package/lib/lib/collapseAdjacentRules.js +14 -1
- package/lib/lib/collapseDuplicateDeclarations.js +52 -1
- package/lib/lib/defaultExtractor.js +44 -0
- package/lib/lib/evaluateTailwindFunctions.js +1 -1
- package/lib/lib/expandApplyAtRules.js +265 -60
- package/lib/lib/expandTailwindAtRules.js +63 -48
- package/lib/lib/generateRules.js +136 -34
- package/lib/lib/normalizeTailwindDirectives.js +10 -2
- package/lib/lib/partitionApplyAtRules.js +53 -0
- package/lib/lib/resolveDefaultsAtRules.js +30 -12
- package/lib/lib/setupContextUtils.js +137 -81
- package/lib/lib/setupTrackingContext.js +8 -7
- package/lib/lib/sharedState.js +38 -5
- package/{nesting → lib/postcss-plugins/nesting}/README.md +0 -0
- package/lib/postcss-plugins/nesting/index.js +17 -0
- package/lib/postcss-plugins/nesting/plugin.js +85 -0
- package/lib/processTailwindFeatures.js +10 -2
- package/lib/util/cloneNodes.js +12 -1
- package/lib/util/color.js +23 -8
- package/lib/util/createPlugin.js +1 -2
- package/lib/util/createUtilityPlugin.js +4 -8
- package/lib/util/dataTypes.js +4 -2
- package/lib/util/defaults.js +6 -0
- package/lib/util/flattenColorPalette.js +1 -3
- package/lib/util/log.js +10 -6
- package/lib/util/normalizeConfig.js +47 -15
- package/lib/util/normalizeScreens.js +6 -5
- package/lib/util/parseBoxShadowValue.js +44 -4
- package/lib/util/pluginUtils.js +6 -13
- package/lib/util/prefixSelector.js +4 -5
- package/lib/util/resolveConfig.js +48 -27
- package/lib/util/resolveConfigPath.js +1 -2
- package/lib/util/toColorValue.js +1 -2
- package/lib/util/toPath.js +6 -1
- package/lib/util/transformThemeValue.js +4 -8
- package/nesting/index.js +2 -12
- package/package.json +28 -33
- package/peers/index.js +3209 -4239
- package/plugin.js +2 -1
- package/resolveConfig.js +2 -1
- package/src/cli.js +62 -15
- package/src/corePlugins.js +212 -217
- package/src/css/preflight.css +4 -4
- package/src/featureFlags.js +3 -3
- package/src/index.js +14 -6
- package/src/lib/cacheInvalidation.js +52 -0
- package/src/lib/collapseAdjacentRules.js +16 -1
- package/src/lib/collapseDuplicateDeclarations.js +66 -1
- package/src/lib/defaultExtractor.js +50 -0
- package/src/lib/evaluateTailwindFunctions.js +1 -1
- package/src/lib/expandApplyAtRules.js +285 -56
- package/src/lib/expandTailwindAtRules.js +75 -37
- package/src/lib/generateRules.js +121 -27
- package/src/lib/normalizeTailwindDirectives.js +7 -1
- package/src/lib/partitionApplyAtRules.js +52 -0
- package/src/lib/resolveDefaultsAtRules.js +35 -10
- package/src/lib/setupContextUtils.js +127 -45
- package/src/lib/setupTrackingContext.js +8 -7
- package/src/lib/sharedState.js +42 -4
- package/src/postcss-plugins/nesting/README.md +42 -0
- package/src/postcss-plugins/nesting/index.js +13 -0
- package/src/postcss-plugins/nesting/plugin.js +80 -0
- package/src/processTailwindFeatures.js +12 -2
- package/src/util/cloneNodes.js +14 -1
- package/src/util/color.js +20 -7
- package/src/util/dataTypes.js +7 -5
- package/src/util/defaults.js +6 -0
- package/src/util/log.js +10 -6
- package/src/util/normalizeConfig.js +24 -3
- package/src/util/normalizeScreens.js +6 -3
- package/src/util/parseBoxShadowValue.js +51 -3
- package/src/util/pluginUtils.js +1 -1
- package/src/util/prefixSelector.js +7 -5
- package/src/util/resolveConfig.js +41 -1
- package/src/util/toPath.js +23 -1
- package/stubs/defaultConfig.stub.js +2 -2
- package/lib/lib/setupWatchingContext.js +0 -288
- package/nesting/plugin.js +0 -41
- package/src/lib/setupWatchingContext.js +0 -311
|
@@ -1,13 +1,167 @@
|
|
|
1
1
|
import postcss from 'postcss'
|
|
2
|
+
import parser from 'postcss-selector-parser'
|
|
3
|
+
|
|
2
4
|
import { resolveMatches } from './generateRules'
|
|
3
5
|
import bigSign from '../util/bigSign'
|
|
4
6
|
import escapeClassName from '../util/escapeClassName'
|
|
5
7
|
|
|
8
|
+
/** @typedef {Map<string, [any, import('postcss').Rule[]]>} ApplyCache */
|
|
9
|
+
|
|
10
|
+
function extractClasses(node) {
|
|
11
|
+
let classes = new Set()
|
|
12
|
+
let container = postcss.root({ nodes: [node.clone()] })
|
|
13
|
+
|
|
14
|
+
container.walkRules((rule) => {
|
|
15
|
+
parser((selectors) => {
|
|
16
|
+
selectors.walkClasses((classSelector) => {
|
|
17
|
+
classes.add(classSelector.value)
|
|
18
|
+
})
|
|
19
|
+
}).processSync(rule.selector)
|
|
20
|
+
})
|
|
21
|
+
|
|
22
|
+
return Array.from(classes)
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
function extractBaseCandidates(candidates, separator) {
|
|
26
|
+
let baseClasses = new Set()
|
|
27
|
+
|
|
28
|
+
for (let candidate of candidates) {
|
|
29
|
+
baseClasses.add(candidate.split(separator).pop())
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
return Array.from(baseClasses)
|
|
33
|
+
}
|
|
34
|
+
|
|
6
35
|
function prefix(context, selector) {
|
|
7
36
|
let prefix = context.tailwindConfig.prefix
|
|
8
37
|
return typeof prefix === 'function' ? prefix(selector) : prefix + selector
|
|
9
38
|
}
|
|
10
39
|
|
|
40
|
+
function* pathToRoot(node) {
|
|
41
|
+
yield node
|
|
42
|
+
while (node.parent) {
|
|
43
|
+
yield node.parent
|
|
44
|
+
node = node.parent
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* Only clone the node itself and not its children
|
|
50
|
+
*
|
|
51
|
+
* @param {*} node
|
|
52
|
+
* @param {*} overrides
|
|
53
|
+
* @returns
|
|
54
|
+
*/
|
|
55
|
+
function shallowClone(node, overrides = {}) {
|
|
56
|
+
let children = node.nodes
|
|
57
|
+
node.nodes = []
|
|
58
|
+
|
|
59
|
+
let tmp = node.clone(overrides)
|
|
60
|
+
|
|
61
|
+
node.nodes = children
|
|
62
|
+
|
|
63
|
+
return tmp
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
/**
|
|
67
|
+
* Clone just the nodes all the way to the top that are required to represent
|
|
68
|
+
* this singular rule in the tree.
|
|
69
|
+
*
|
|
70
|
+
* For example, if we have CSS like this:
|
|
71
|
+
* ```css
|
|
72
|
+
* @media (min-width: 768px) {
|
|
73
|
+
* @supports (display: grid) {
|
|
74
|
+
* .foo {
|
|
75
|
+
* display: grid;
|
|
76
|
+
* grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
|
|
77
|
+
* }
|
|
78
|
+
* }
|
|
79
|
+
*
|
|
80
|
+
* @supports (backdrop-filter: blur(1px)) {
|
|
81
|
+
* .bar {
|
|
82
|
+
* backdrop-filter: blur(1px);
|
|
83
|
+
* }
|
|
84
|
+
* }
|
|
85
|
+
*
|
|
86
|
+
* .baz {
|
|
87
|
+
* color: orange;
|
|
88
|
+
* }
|
|
89
|
+
* }
|
|
90
|
+
* ```
|
|
91
|
+
*
|
|
92
|
+
* And we're cloning `.bar` it'll return a cloned version of what's required for just that single node:
|
|
93
|
+
*
|
|
94
|
+
* ```css
|
|
95
|
+
* @media (min-width: 768px) {
|
|
96
|
+
* @supports (backdrop-filter: blur(1px)) {
|
|
97
|
+
* .bar {
|
|
98
|
+
* backdrop-filter: blur(1px);
|
|
99
|
+
* }
|
|
100
|
+
* }
|
|
101
|
+
* }
|
|
102
|
+
* ```
|
|
103
|
+
*
|
|
104
|
+
* @param {import('postcss').Node} node
|
|
105
|
+
*/
|
|
106
|
+
function nestedClone(node) {
|
|
107
|
+
for (let parent of pathToRoot(node)) {
|
|
108
|
+
if (node === parent) {
|
|
109
|
+
continue
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
if (parent.type === 'root') {
|
|
113
|
+
break
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
node = shallowClone(parent, {
|
|
117
|
+
nodes: [node],
|
|
118
|
+
})
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
return node
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
/**
|
|
125
|
+
* @param {import('postcss').Root} root
|
|
126
|
+
*/
|
|
127
|
+
function buildLocalApplyCache(root, context) {
|
|
128
|
+
/** @type {ApplyCache} */
|
|
129
|
+
let cache = new Map()
|
|
130
|
+
|
|
131
|
+
let highestOffset = context.layerOrder.user >> 4n
|
|
132
|
+
|
|
133
|
+
root.walkRules((rule, idx) => {
|
|
134
|
+
// Ignore rules generated by Tailwind
|
|
135
|
+
for (let node of pathToRoot(rule)) {
|
|
136
|
+
if (node.raws.tailwind?.layer !== undefined) {
|
|
137
|
+
return
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
// Clone what's required to represent this singular rule in the tree
|
|
142
|
+
let container = nestedClone(rule)
|
|
143
|
+
|
|
144
|
+
for (let className of extractClasses(rule)) {
|
|
145
|
+
let list = cache.get(className) || []
|
|
146
|
+
cache.set(className, list)
|
|
147
|
+
|
|
148
|
+
list.push([
|
|
149
|
+
{
|
|
150
|
+
layer: 'user',
|
|
151
|
+
sort: BigInt(idx) + highestOffset,
|
|
152
|
+
important: false,
|
|
153
|
+
},
|
|
154
|
+
container,
|
|
155
|
+
])
|
|
156
|
+
}
|
|
157
|
+
})
|
|
158
|
+
|
|
159
|
+
return cache
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
/**
|
|
163
|
+
* @returns {ApplyCache}
|
|
164
|
+
*/
|
|
11
165
|
function buildApplyCache(applyCandidates, context) {
|
|
12
166
|
for (let candidate of applyCandidates) {
|
|
13
167
|
if (context.notClassCache.has(candidate) || context.applyClassCache.has(candidate)) {
|
|
@@ -35,6 +189,43 @@ function buildApplyCache(applyCandidates, context) {
|
|
|
35
189
|
return context.applyClassCache
|
|
36
190
|
}
|
|
37
191
|
|
|
192
|
+
/**
|
|
193
|
+
* Build a cache only when it's first used
|
|
194
|
+
*
|
|
195
|
+
* @param {() => ApplyCache} buildCacheFn
|
|
196
|
+
* @returns {ApplyCache}
|
|
197
|
+
*/
|
|
198
|
+
function lazyCache(buildCacheFn) {
|
|
199
|
+
let cache = null
|
|
200
|
+
|
|
201
|
+
return {
|
|
202
|
+
get: (name) => {
|
|
203
|
+
cache = cache || buildCacheFn()
|
|
204
|
+
|
|
205
|
+
return cache.get(name)
|
|
206
|
+
},
|
|
207
|
+
has: (name) => {
|
|
208
|
+
cache = cache || buildCacheFn()
|
|
209
|
+
|
|
210
|
+
return cache.has(name)
|
|
211
|
+
},
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
/**
|
|
216
|
+
* Take a series of multiple caches and merge
|
|
217
|
+
* them so they act like one large cache
|
|
218
|
+
*
|
|
219
|
+
* @param {ApplyCache[]} caches
|
|
220
|
+
* @returns {ApplyCache}
|
|
221
|
+
*/
|
|
222
|
+
function combineCaches(caches) {
|
|
223
|
+
return {
|
|
224
|
+
get: (name) => caches.flatMap((cache) => cache.get(name) || []),
|
|
225
|
+
has: (name) => caches.some((cache) => cache.has(name)),
|
|
226
|
+
}
|
|
227
|
+
}
|
|
228
|
+
|
|
38
229
|
function extractApplyCandidates(params) {
|
|
39
230
|
let candidates = params.split(/[\s\t\n]+/g)
|
|
40
231
|
|
|
@@ -45,48 +236,7 @@ function extractApplyCandidates(params) {
|
|
|
45
236
|
return [candidates, false]
|
|
46
237
|
}
|
|
47
238
|
|
|
48
|
-
function
|
|
49
|
-
let applyParents = new Set()
|
|
50
|
-
|
|
51
|
-
root.walkAtRules('apply', (rule) => {
|
|
52
|
-
applyParents.add(rule.parent)
|
|
53
|
-
})
|
|
54
|
-
|
|
55
|
-
for (let rule of applyParents) {
|
|
56
|
-
let nodeGroups = []
|
|
57
|
-
let lastGroup = []
|
|
58
|
-
|
|
59
|
-
for (let node of rule.nodes) {
|
|
60
|
-
if (node.type === 'atrule' && node.name === 'apply') {
|
|
61
|
-
if (lastGroup.length > 0) {
|
|
62
|
-
nodeGroups.push(lastGroup)
|
|
63
|
-
lastGroup = []
|
|
64
|
-
}
|
|
65
|
-
nodeGroups.push([node])
|
|
66
|
-
} else {
|
|
67
|
-
lastGroup.push(node)
|
|
68
|
-
}
|
|
69
|
-
}
|
|
70
|
-
|
|
71
|
-
if (lastGroup.length > 0) {
|
|
72
|
-
nodeGroups.push(lastGroup)
|
|
73
|
-
}
|
|
74
|
-
|
|
75
|
-
if (nodeGroups.length === 1) {
|
|
76
|
-
continue
|
|
77
|
-
}
|
|
78
|
-
|
|
79
|
-
for (let group of [...nodeGroups].reverse()) {
|
|
80
|
-
let newParent = rule.clone({ nodes: [] })
|
|
81
|
-
newParent.append(group)
|
|
82
|
-
rule.after(newParent)
|
|
83
|
-
}
|
|
84
|
-
|
|
85
|
-
rule.remove()
|
|
86
|
-
}
|
|
87
|
-
}
|
|
88
|
-
|
|
89
|
-
function processApply(root, context) {
|
|
239
|
+
function processApply(root, context, localCache) {
|
|
90
240
|
let applyCandidates = new Set()
|
|
91
241
|
|
|
92
242
|
// Collect all @apply rules and candidates
|
|
@@ -104,7 +254,7 @@ function processApply(root, context) {
|
|
|
104
254
|
// Start the @apply process if we have rules with @apply in them
|
|
105
255
|
if (applies.length > 0) {
|
|
106
256
|
// Fill up some caches!
|
|
107
|
-
let applyClassCache = buildApplyCache(applyCandidates, context)
|
|
257
|
+
let applyClassCache = combineCaches([localCache, buildApplyCache(applyCandidates, context)])
|
|
108
258
|
|
|
109
259
|
/**
|
|
110
260
|
* When we have an apply like this:
|
|
@@ -129,10 +279,10 @@ function processApply(root, context) {
|
|
|
129
279
|
// TODO: Should we use postcss-selector-parser for this instead?
|
|
130
280
|
function replaceSelector(selector, utilitySelectors, candidate) {
|
|
131
281
|
let needle = `.${escapeClassName(candidate)}`
|
|
132
|
-
let utilitySelectorsList = utilitySelectors.split(/\s
|
|
282
|
+
let utilitySelectorsList = utilitySelectors.split(/\s*\,(?![^(]*\))\s*/g)
|
|
133
283
|
|
|
134
284
|
return selector
|
|
135
|
-
.split(/\s
|
|
285
|
+
.split(/\s*\,(?![^(]*\))\s*/g)
|
|
136
286
|
.map((s) => {
|
|
137
287
|
let replaced = []
|
|
138
288
|
|
|
@@ -154,7 +304,7 @@ function processApply(root, context) {
|
|
|
154
304
|
for (let apply of applies) {
|
|
155
305
|
let candidates = perParentApplies.get(apply.parent) || []
|
|
156
306
|
|
|
157
|
-
perParentApplies.set(apply.parent, candidates)
|
|
307
|
+
perParentApplies.set(apply.parent, [candidates, apply.source])
|
|
158
308
|
|
|
159
309
|
let [applyCandidates, important] = extractApplyCandidates(apply.params)
|
|
160
310
|
|
|
@@ -175,12 +325,12 @@ function processApply(root, context) {
|
|
|
175
325
|
}
|
|
176
326
|
|
|
177
327
|
for (let applyCandidate of applyCandidates) {
|
|
178
|
-
if (
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
}
|
|
328
|
+
if ([prefix(context, 'group'), prefix(context, 'peer')].includes(applyCandidate)) {
|
|
329
|
+
// TODO: Link to specific documentation page with error code.
|
|
330
|
+
throw apply.error(`@apply should not be used with the '${applyCandidate}' utility`)
|
|
331
|
+
}
|
|
183
332
|
|
|
333
|
+
if (!applyClassCache.has(applyCandidate)) {
|
|
184
334
|
throw apply.error(
|
|
185
335
|
`The \`${applyCandidate}\` class does not exist. If \`${applyCandidate}\` is a custom class, make sure it is defined within a \`@layer\` directive.`
|
|
186
336
|
)
|
|
@@ -192,17 +342,95 @@ function processApply(root, context) {
|
|
|
192
342
|
}
|
|
193
343
|
}
|
|
194
344
|
|
|
195
|
-
for (const [parent, candidates] of perParentApplies) {
|
|
345
|
+
for (const [parent, [candidates, atApplySource]] of perParentApplies) {
|
|
196
346
|
let siblings = []
|
|
197
347
|
|
|
198
348
|
for (let [applyCandidate, important, rules] of candidates) {
|
|
199
349
|
for (let [meta, node] of rules) {
|
|
350
|
+
let parentClasses = extractClasses(parent)
|
|
351
|
+
let nodeClasses = extractClasses(node)
|
|
352
|
+
|
|
353
|
+
// Add base utility classes from the @apply node to the list of
|
|
354
|
+
// classes to check whether it intersects and therefore results in a
|
|
355
|
+
// circular dependency or not.
|
|
356
|
+
//
|
|
357
|
+
// E.g.:
|
|
358
|
+
// .foo {
|
|
359
|
+
// @apply hover:a; // This applies "a" but with a modifier
|
|
360
|
+
// }
|
|
361
|
+
//
|
|
362
|
+
// We only have to do that with base classes of the `node`, not of the `parent`
|
|
363
|
+
// E.g.:
|
|
364
|
+
// .hover\:foo {
|
|
365
|
+
// @apply bar;
|
|
366
|
+
// }
|
|
367
|
+
// .bar {
|
|
368
|
+
// @apply foo;
|
|
369
|
+
// }
|
|
370
|
+
//
|
|
371
|
+
// This should not result in a circular dependency because we are
|
|
372
|
+
// just applying `.foo` and the rule above is `.hover\:foo` which is
|
|
373
|
+
// unrelated. However, if we were to apply `hover:foo` then we _did_
|
|
374
|
+
// have to include this one.
|
|
375
|
+
nodeClasses = nodeClasses.concat(
|
|
376
|
+
extractBaseCandidates(nodeClasses, context.tailwindConfig.separator)
|
|
377
|
+
)
|
|
378
|
+
|
|
379
|
+
let intersects = parentClasses.some((selector) => nodeClasses.includes(selector))
|
|
380
|
+
if (intersects) {
|
|
381
|
+
throw node.error(
|
|
382
|
+
`You cannot \`@apply\` the \`${applyCandidate}\` utility here because it creates a circular dependency.`
|
|
383
|
+
)
|
|
384
|
+
}
|
|
385
|
+
|
|
200
386
|
let root = postcss.root({ nodes: [node.clone()] })
|
|
387
|
+
|
|
388
|
+
// Make sure every node in the entire tree points back at the @apply rule that generated it
|
|
389
|
+
root.walk((node) => {
|
|
390
|
+
node.source = atApplySource
|
|
391
|
+
})
|
|
392
|
+
|
|
201
393
|
let canRewriteSelector =
|
|
202
394
|
node.type !== 'atrule' || (node.type === 'atrule' && node.name !== 'keyframes')
|
|
203
395
|
|
|
204
396
|
if (canRewriteSelector) {
|
|
205
397
|
root.walkRules((rule) => {
|
|
398
|
+
// Let's imagine you have the following structure:
|
|
399
|
+
//
|
|
400
|
+
// .foo {
|
|
401
|
+
// @apply bar;
|
|
402
|
+
// }
|
|
403
|
+
//
|
|
404
|
+
// @supports (a: b) {
|
|
405
|
+
// .bar {
|
|
406
|
+
// color: blue
|
|
407
|
+
// }
|
|
408
|
+
//
|
|
409
|
+
// .something-unrelated {}
|
|
410
|
+
// }
|
|
411
|
+
//
|
|
412
|
+
// In this case we want to apply `.bar` but it happens to be in
|
|
413
|
+
// an atrule node. We clone that node instead of the nested one
|
|
414
|
+
// because we still want that @supports rule to be there once we
|
|
415
|
+
// applied everything.
|
|
416
|
+
//
|
|
417
|
+
// However it happens to be that the `.something-unrelated` is
|
|
418
|
+
// also in that same shared @supports atrule. This is not good,
|
|
419
|
+
// and this should not be there. The good part is that this is
|
|
420
|
+
// a clone already and it can be safely removed. The question is
|
|
421
|
+
// how do we know we can remove it. Basically what we can do is
|
|
422
|
+
// match it against the applyCandidate that you want to apply. If
|
|
423
|
+
// it doesn't match the we can safely delete it.
|
|
424
|
+
//
|
|
425
|
+
// If we didn't do this, then the `replaceSelector` function
|
|
426
|
+
// would have replaced this with something that didn't exist and
|
|
427
|
+
// therefore it removed the selector altogether. In this specific
|
|
428
|
+
// case it would result in `{}` instead of `.something-unrelated {}`
|
|
429
|
+
if (!extractClasses(rule).some((candidate) => candidate === applyCandidate)) {
|
|
430
|
+
rule.remove()
|
|
431
|
+
return
|
|
432
|
+
}
|
|
433
|
+
|
|
206
434
|
rule.selector = replaceSelector(parent.selector, rule.selector, applyCandidate)
|
|
207
435
|
|
|
208
436
|
rule.walkDecls((d) => {
|
|
@@ -223,7 +451,6 @@ function processApply(root, context) {
|
|
|
223
451
|
// Inject the rules, sorted, correctly
|
|
224
452
|
let nodes = siblings.sort(([a], [z]) => bigSign(a.sort - z.sort)).map((s) => s[1])
|
|
225
453
|
|
|
226
|
-
// console.log(parent)
|
|
227
454
|
// `parent` refers to the node at `.abc` in: .abc { @apply mt-2 }
|
|
228
455
|
parent.after(nodes)
|
|
229
456
|
}
|
|
@@ -239,13 +466,15 @@ function processApply(root, context) {
|
|
|
239
466
|
}
|
|
240
467
|
|
|
241
468
|
// Do it again, in case we have other `@apply` rules
|
|
242
|
-
processApply(root, context)
|
|
469
|
+
processApply(root, context, localCache)
|
|
243
470
|
}
|
|
244
471
|
}
|
|
245
472
|
|
|
246
473
|
export default function expandApplyAtRules(context) {
|
|
247
474
|
return (root) => {
|
|
248
|
-
|
|
249
|
-
|
|
475
|
+
// Build a cache of the user's CSS so we can use it to resolve classes used by @apply
|
|
476
|
+
let localCache = lazyCache(() => buildLocalApplyCache(root, context))
|
|
477
|
+
|
|
478
|
+
processApply(root, context, localCache)
|
|
250
479
|
}
|
|
251
480
|
}
|
|
@@ -2,34 +2,14 @@ import LRU from 'quick-lru'
|
|
|
2
2
|
import * as sharedState from './sharedState'
|
|
3
3
|
import { generateRules } from './generateRules'
|
|
4
4
|
import bigSign from '../util/bigSign'
|
|
5
|
+
import log from '../util/log'
|
|
5
6
|
import cloneNodes from '../util/cloneNodes'
|
|
7
|
+
import { defaultExtractor } from './defaultExtractor'
|
|
6
8
|
|
|
7
9
|
let env = sharedState.env
|
|
8
10
|
|
|
9
|
-
const PATTERNS = [
|
|
10
|
-
/([^<>"'`\s]*\[\w*'[^"`\s]*'?\])/.source, // font-['some_font',sans-serif]
|
|
11
|
-
/([^<>"'`\s]*\[\w*"[^"`\s]*"?\])/.source, // font-["some_font",sans-serif]
|
|
12
|
-
/([^<>"'`\s]*\[\w*\('[^"'`\s]*'\)\])/.source, // bg-[url('...')]
|
|
13
|
-
/([^<>"'`\s]*\[\w*\("[^"'`\s]*"\)\])/.source, // bg-[url("...")]
|
|
14
|
-
/([^<>"'`\s]*\[\w*\('[^"`\s]*'\)\])/.source, // bg-[url('...'),url('...')]
|
|
15
|
-
/([^<>"'`\s]*\[\w*\("[^'`\s]*"\)\])/.source, // bg-[url("..."),url("...")]
|
|
16
|
-
/([^<>"'`\s]*\['[^"'`\s]*'\])/.source, // `content-['hello']` but not `content-['hello']']`
|
|
17
|
-
/([^<>"'`\s]*\["[^"'`\s]*"\])/.source, // `content-["hello"]` but not `content-["hello"]"]`
|
|
18
|
-
/([^<>"'`\s]*\[[^<>"'`\s]*:'[^"'`\s]*'\])/.source, // `[content:'hello']` but not `[content:"hello"]`
|
|
19
|
-
/([^<>"'`\s]*\[[^<>"'`\s]*:"[^"'`\s]*"\])/.source, // `[content:"hello"]` but not `[content:'hello']`
|
|
20
|
-
/([^<>"'`\s]*\[[^"'`\s]+\][^<>"'`\s]*)/.source, // `fill-[#bada55]`, `fill-[#bada55]/50`
|
|
21
|
-
/([^<>"'`\s]*[^"'`\s:])/.source, // `px-1.5`, `uppercase` but not `uppercase:`
|
|
22
|
-
].join('|')
|
|
23
|
-
const BROAD_MATCH_GLOBAL_REGEXP = new RegExp(PATTERNS, 'g')
|
|
24
|
-
const INNER_MATCH_GLOBAL_REGEXP = /[^<>"'`\s.(){}[\]#=%]*[^<>"'`\s.(){}[\]#=%:]/g
|
|
25
|
-
|
|
26
11
|
const builtInExtractors = {
|
|
27
|
-
DEFAULT:
|
|
28
|
-
let broadMatches = content.match(BROAD_MATCH_GLOBAL_REGEXP) || []
|
|
29
|
-
let innerMatches = content.match(INNER_MATCH_GLOBAL_REGEXP) || []
|
|
30
|
-
|
|
31
|
-
return [...broadMatches, ...innerMatches]
|
|
32
|
-
},
|
|
12
|
+
DEFAULT: defaultExtractor,
|
|
33
13
|
}
|
|
34
14
|
|
|
35
15
|
const builtInTransformers = {
|
|
@@ -99,6 +79,7 @@ function buildStylesheet(rules, context) {
|
|
|
99
79
|
|
|
100
80
|
let returnValue = {
|
|
101
81
|
base: new Set(),
|
|
82
|
+
defaults: new Set(),
|
|
102
83
|
components: new Set(),
|
|
103
84
|
utilities: new Set(),
|
|
104
85
|
variants: new Set(),
|
|
@@ -125,6 +106,11 @@ function buildStylesheet(rules, context) {
|
|
|
125
106
|
continue
|
|
126
107
|
}
|
|
127
108
|
|
|
109
|
+
if (sort & context.layerOrder.defaults) {
|
|
110
|
+
returnValue.defaults.add(rule)
|
|
111
|
+
continue
|
|
112
|
+
}
|
|
113
|
+
|
|
128
114
|
if (sort & context.layerOrder.components) {
|
|
129
115
|
returnValue.components.add(rule)
|
|
130
116
|
continue
|
|
@@ -153,13 +139,15 @@ export default function expandTailwindAtRules(context) {
|
|
|
153
139
|
variants: null,
|
|
154
140
|
}
|
|
155
141
|
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
if (
|
|
162
|
-
layerNodes
|
|
142
|
+
root.walkAtRules((rule) => {
|
|
143
|
+
// Make sure this file contains Tailwind directives. If not, we can save
|
|
144
|
+
// a lot of work and bail early. Also we don't have to register our touch
|
|
145
|
+
// file as a dependency since the output of this CSS does not depend on
|
|
146
|
+
// the source of any templates. Think Vue <style> blocks for example.
|
|
147
|
+
if (rule.name === 'tailwind') {
|
|
148
|
+
if (Object.keys(layerNodes).includes(rule.params)) {
|
|
149
|
+
layerNodes[rule.params] = rule
|
|
150
|
+
}
|
|
163
151
|
}
|
|
164
152
|
})
|
|
165
153
|
|
|
@@ -170,7 +158,7 @@ export default function expandTailwindAtRules(context) {
|
|
|
170
158
|
// ---
|
|
171
159
|
|
|
172
160
|
// Find potential rules in changed files
|
|
173
|
-
let candidates = new Set([
|
|
161
|
+
let candidates = new Set([sharedState.NOT_ON_DEMAND])
|
|
174
162
|
let seen = new Set()
|
|
175
163
|
|
|
176
164
|
env.DEBUG && console.time('Reading changed files')
|
|
@@ -181,6 +169,8 @@ export default function expandTailwindAtRules(context) {
|
|
|
181
169
|
getClassCandidates(transformer(content), extractor, candidates, seen)
|
|
182
170
|
}
|
|
183
171
|
|
|
172
|
+
env.DEBUG && console.timeEnd('Reading changed files')
|
|
173
|
+
|
|
184
174
|
// ---
|
|
185
175
|
|
|
186
176
|
// Generate the actual CSS
|
|
@@ -202,6 +192,7 @@ export default function expandTailwindAtRules(context) {
|
|
|
202
192
|
env.DEBUG && console.timeEnd('Build stylesheet')
|
|
203
193
|
|
|
204
194
|
let {
|
|
195
|
+
defaults: defaultNodes,
|
|
205
196
|
base: baseNodes,
|
|
206
197
|
components: componentNodes,
|
|
207
198
|
utilities: utilityNodes,
|
|
@@ -213,25 +204,72 @@ export default function expandTailwindAtRules(context) {
|
|
|
213
204
|
// Replace any Tailwind directives with generated CSS
|
|
214
205
|
|
|
215
206
|
if (layerNodes.base) {
|
|
216
|
-
layerNodes.base.before(
|
|
207
|
+
layerNodes.base.before(
|
|
208
|
+
cloneNodes([...baseNodes, ...defaultNodes], layerNodes.base.source, {
|
|
209
|
+
layer: 'base',
|
|
210
|
+
})
|
|
211
|
+
)
|
|
217
212
|
layerNodes.base.remove()
|
|
218
213
|
}
|
|
219
214
|
|
|
220
215
|
if (layerNodes.components) {
|
|
221
|
-
layerNodes.components.before(
|
|
216
|
+
layerNodes.components.before(
|
|
217
|
+
cloneNodes([...componentNodes], layerNodes.components.source, {
|
|
218
|
+
layer: 'components',
|
|
219
|
+
})
|
|
220
|
+
)
|
|
222
221
|
layerNodes.components.remove()
|
|
223
222
|
}
|
|
224
223
|
|
|
225
224
|
if (layerNodes.utilities) {
|
|
226
|
-
layerNodes.utilities.before(
|
|
225
|
+
layerNodes.utilities.before(
|
|
226
|
+
cloneNodes([...utilityNodes], layerNodes.utilities.source, {
|
|
227
|
+
layer: 'utilities',
|
|
228
|
+
})
|
|
229
|
+
)
|
|
227
230
|
layerNodes.utilities.remove()
|
|
228
231
|
}
|
|
229
232
|
|
|
233
|
+
// We do post-filtering to not alter the emitted order of the variants
|
|
234
|
+
const variantNodes = Array.from(screenNodes).filter((node) => {
|
|
235
|
+
const parentLayer = node.raws.tailwind?.parentLayer
|
|
236
|
+
|
|
237
|
+
if (parentLayer === 'components') {
|
|
238
|
+
return layerNodes.components !== null
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
if (parentLayer === 'utilities') {
|
|
242
|
+
return layerNodes.utilities !== null
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
return true
|
|
246
|
+
})
|
|
247
|
+
|
|
230
248
|
if (layerNodes.variants) {
|
|
231
|
-
layerNodes.variants.before(
|
|
249
|
+
layerNodes.variants.before(
|
|
250
|
+
cloneNodes(variantNodes, layerNodes.variants.source, {
|
|
251
|
+
layer: 'variants',
|
|
252
|
+
})
|
|
253
|
+
)
|
|
232
254
|
layerNodes.variants.remove()
|
|
233
|
-
} else {
|
|
234
|
-
root.append(
|
|
255
|
+
} else if (variantNodes.length > 0) {
|
|
256
|
+
root.append(
|
|
257
|
+
cloneNodes(variantNodes, root.source, {
|
|
258
|
+
layer: 'variants',
|
|
259
|
+
})
|
|
260
|
+
)
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
// If we've got a utility layer and no utilities are generated there's likely something wrong
|
|
264
|
+
const hasUtilityVariants = variantNodes.some(
|
|
265
|
+
(node) => node.raws.tailwind?.parentLayer === 'utilities'
|
|
266
|
+
)
|
|
267
|
+
|
|
268
|
+
if (layerNodes.utilities && utilityNodes.size === 0 && !hasUtilityVariants) {
|
|
269
|
+
log.warn('content-problems', [
|
|
270
|
+
'No utility classes were detected in your source files. If this is unexpected, double-check the `content` option in your Tailwind CSS configuration.',
|
|
271
|
+
'https://tailwindcss.com/docs/content-configuration',
|
|
272
|
+
])
|
|
235
273
|
}
|
|
236
274
|
|
|
237
275
|
// ---
|