tailwindcss 3.0.21 → 3.0.24
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 +45 -2
- package/lib/cli-peer-dependencies.js +3 -3
- package/lib/cli.js +183 -161
- package/lib/constants.js +8 -8
- package/lib/corePlugins.js +1597 -1518
- package/lib/featureFlags.js +9 -9
- package/lib/index.js +19 -6
- package/lib/lib/cacheInvalidation.js +69 -0
- package/lib/lib/collapseAdjacentRules.js +26 -13
- package/lib/lib/collapseDuplicateDeclarations.js +1 -1
- package/lib/lib/defaultExtractor.js +8 -6
- package/lib/lib/detectNesting.js +9 -9
- package/lib/lib/evaluateTailwindFunctions.js +16 -16
- package/lib/lib/expandApplyAtRules.js +180 -27
- package/lib/lib/expandTailwindAtRules.js +132 -122
- package/lib/lib/generateRules.js +117 -72
- package/lib/lib/getModuleDependencies.js +14 -14
- package/lib/lib/normalizeTailwindDirectives.js +35 -35
- package/lib/lib/partitionApplyAtRules.js +7 -7
- package/lib/lib/resolveDefaultsAtRules.js +81 -77
- package/lib/lib/setupContextUtils.js +83 -98
- package/lib/lib/setupTrackingContext.js +57 -57
- package/lib/lib/sharedState.js +11 -7
- package/lib/lib/substituteScreenAtRules.js +2 -2
- package/lib/postcss-plugins/nesting/README.md +2 -2
- package/lib/postcss-plugins/nesting/index.js +1 -1
- package/lib/postcss-plugins/nesting/plugin.js +41 -9
- package/lib/processTailwindFeatures.js +7 -7
- package/lib/public/colors.js +241 -241
- package/lib/public/resolve-config.js +5 -5
- package/lib/util/buildMediaQuery.js +2 -2
- package/lib/util/cloneDeep.js +1 -1
- package/lib/util/cloneNodes.js +12 -1
- package/lib/util/color.js +21 -20
- package/lib/util/createUtilityPlugin.js +6 -6
- package/lib/util/dataTypes.js +77 -75
- package/lib/util/escapeClassName.js +5 -5
- package/lib/util/escapeCommas.js +1 -1
- package/lib/util/flattenColorPalette.js +2 -2
- package/lib/util/formatVariantSelector.js +19 -19
- package/lib/util/getAllConfigs.js +5 -5
- package/lib/util/hashConfig.js +5 -5
- package/lib/util/isKeyframeRule.js +1 -1
- package/lib/util/isPlainObject.js +1 -1
- package/lib/util/isValidArbitraryValue.js +27 -27
- package/lib/util/log.js +8 -8
- package/lib/util/nameClass.js +7 -7
- package/lib/util/negateValue.js +4 -4
- package/lib/util/normalizeConfig.js +39 -39
- package/lib/util/normalizeScreens.js +4 -4
- package/lib/util/parseAnimationValue.js +56 -56
- package/lib/util/parseBoxShadowValue.js +60 -20
- package/lib/util/parseDependency.js +32 -32
- package/lib/util/parseObjectStyles.js +6 -6
- package/lib/util/pluginUtils.js +9 -9
- package/lib/util/prefixSelector.js +1 -1
- package/lib/util/resolveConfig.js +28 -28
- package/lib/util/resolveConfigPath.js +16 -16
- package/lib/util/responsive.js +6 -6
- package/lib/util/toColorValue.js +1 -1
- package/lib/util/toPath.js +2 -2
- package/lib/util/transformThemeValue.js +27 -27
- package/lib/util/withAlphaVariable.js +19 -19
- package/package.json +26 -25
- package/peers/index.js +4803 -4857
- package/scripts/generate-types.js +52 -0
- package/src/cli.js +41 -11
- package/src/corePlugins.js +104 -9
- package/src/featureFlags.js +2 -2
- package/src/index.js +17 -1
- package/src/lib/cacheInvalidation.js +52 -0
- package/src/lib/collapseAdjacentRules.js +16 -1
- package/src/lib/defaultExtractor.js +6 -4
- package/src/lib/expandApplyAtRules.js +179 -6
- package/src/lib/expandTailwindAtRules.js +26 -6
- package/src/lib/generateRules.js +73 -46
- package/src/lib/resolveDefaultsAtRules.js +6 -2
- package/src/lib/setupContextUtils.js +39 -48
- package/src/lib/setupTrackingContext.js +3 -3
- package/src/lib/sharedState.js +2 -0
- package/src/postcss-plugins/nesting/README.md +2 -2
- package/src/postcss-plugins/nesting/plugin.js +36 -0
- package/src/util/cloneNodes.js +14 -1
- package/src/util/color.js +7 -5
- package/src/util/dataTypes.js +3 -1
- package/src/util/log.js +7 -7
- package/src/util/parseBoxShadowValue.js +50 -2
- package/src/util/resolveConfig.js +32 -0
- package/stubs/defaultConfig.stub.js +5 -0
|
@@ -5,6 +5,8 @@ import { resolveMatches } from './generateRules'
|
|
|
5
5
|
import bigSign from '../util/bigSign'
|
|
6
6
|
import escapeClassName from '../util/escapeClassName'
|
|
7
7
|
|
|
8
|
+
/** @typedef {Map<string, [any, import('postcss').Rule[]]>} ApplyCache */
|
|
9
|
+
|
|
8
10
|
function extractClasses(node) {
|
|
9
11
|
let classes = new Set()
|
|
10
12
|
let container = postcss.root({ nodes: [node.clone()] })
|
|
@@ -35,6 +37,131 @@ function prefix(context, selector) {
|
|
|
35
37
|
return typeof prefix === 'function' ? prefix(selector) : prefix + selector
|
|
36
38
|
}
|
|
37
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
|
+
*/
|
|
38
165
|
function buildApplyCache(applyCandidates, context) {
|
|
39
166
|
for (let candidate of applyCandidates) {
|
|
40
167
|
if (context.notClassCache.has(candidate) || context.applyClassCache.has(candidate)) {
|
|
@@ -62,6 +189,43 @@ function buildApplyCache(applyCandidates, context) {
|
|
|
62
189
|
return context.applyClassCache
|
|
63
190
|
}
|
|
64
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
|
+
|
|
65
229
|
function extractApplyCandidates(params) {
|
|
66
230
|
let candidates = params.split(/[\s\t\n]+/g)
|
|
67
231
|
|
|
@@ -72,7 +236,7 @@ function extractApplyCandidates(params) {
|
|
|
72
236
|
return [candidates, false]
|
|
73
237
|
}
|
|
74
238
|
|
|
75
|
-
function processApply(root, context) {
|
|
239
|
+
function processApply(root, context, localCache) {
|
|
76
240
|
let applyCandidates = new Set()
|
|
77
241
|
|
|
78
242
|
// Collect all @apply rules and candidates
|
|
@@ -90,7 +254,7 @@ function processApply(root, context) {
|
|
|
90
254
|
// Start the @apply process if we have rules with @apply in them
|
|
91
255
|
if (applies.length > 0) {
|
|
92
256
|
// Fill up some caches!
|
|
93
|
-
let applyClassCache = buildApplyCache(applyCandidates, context)
|
|
257
|
+
let applyClassCache = combineCaches([localCache, buildApplyCache(applyCandidates, context)])
|
|
94
258
|
|
|
95
259
|
/**
|
|
96
260
|
* When we have an apply like this:
|
|
@@ -140,7 +304,7 @@ function processApply(root, context) {
|
|
|
140
304
|
for (let apply of applies) {
|
|
141
305
|
let candidates = perParentApplies.get(apply.parent) || []
|
|
142
306
|
|
|
143
|
-
perParentApplies.set(apply.parent, candidates)
|
|
307
|
+
perParentApplies.set(apply.parent, [candidates, apply.source])
|
|
144
308
|
|
|
145
309
|
let [applyCandidates, important] = extractApplyCandidates(apply.params)
|
|
146
310
|
|
|
@@ -178,7 +342,7 @@ function processApply(root, context) {
|
|
|
178
342
|
}
|
|
179
343
|
}
|
|
180
344
|
|
|
181
|
-
for (const [parent, candidates] of perParentApplies) {
|
|
345
|
+
for (const [parent, [candidates, atApplySource]] of perParentApplies) {
|
|
182
346
|
let siblings = []
|
|
183
347
|
|
|
184
348
|
for (let [applyCandidate, important, rules] of candidates) {
|
|
@@ -220,6 +384,12 @@ function processApply(root, context) {
|
|
|
220
384
|
}
|
|
221
385
|
|
|
222
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
|
+
|
|
223
393
|
let canRewriteSelector =
|
|
224
394
|
node.type !== 'atrule' || (node.type === 'atrule' && node.name !== 'keyframes')
|
|
225
395
|
|
|
@@ -296,12 +466,15 @@ function processApply(root, context) {
|
|
|
296
466
|
}
|
|
297
467
|
|
|
298
468
|
// Do it again, in case we have other `@apply` rules
|
|
299
|
-
processApply(root, context)
|
|
469
|
+
processApply(root, context, localCache)
|
|
300
470
|
}
|
|
301
471
|
}
|
|
302
472
|
|
|
303
473
|
export default function expandApplyAtRules(context) {
|
|
304
474
|
return (root) => {
|
|
305
|
-
|
|
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)
|
|
306
479
|
}
|
|
307
480
|
}
|
|
@@ -158,7 +158,7 @@ export default function expandTailwindAtRules(context) {
|
|
|
158
158
|
// ---
|
|
159
159
|
|
|
160
160
|
// Find potential rules in changed files
|
|
161
|
-
let candidates = new Set([
|
|
161
|
+
let candidates = new Set([sharedState.NOT_ON_DEMAND])
|
|
162
162
|
let seen = new Set()
|
|
163
163
|
|
|
164
164
|
env.DEBUG && console.time('Reading changed files')
|
|
@@ -204,17 +204,29 @@ export default function expandTailwindAtRules(context) {
|
|
|
204
204
|
// Replace any Tailwind directives with generated CSS
|
|
205
205
|
|
|
206
206
|
if (layerNodes.base) {
|
|
207
|
-
layerNodes.base.before(
|
|
207
|
+
layerNodes.base.before(
|
|
208
|
+
cloneNodes([...baseNodes, ...defaultNodes], layerNodes.base.source, {
|
|
209
|
+
layer: 'base',
|
|
210
|
+
})
|
|
211
|
+
)
|
|
208
212
|
layerNodes.base.remove()
|
|
209
213
|
}
|
|
210
214
|
|
|
211
215
|
if (layerNodes.components) {
|
|
212
|
-
layerNodes.components.before(
|
|
216
|
+
layerNodes.components.before(
|
|
217
|
+
cloneNodes([...componentNodes], layerNodes.components.source, {
|
|
218
|
+
layer: 'components',
|
|
219
|
+
})
|
|
220
|
+
)
|
|
213
221
|
layerNodes.components.remove()
|
|
214
222
|
}
|
|
215
223
|
|
|
216
224
|
if (layerNodes.utilities) {
|
|
217
|
-
layerNodes.utilities.before(
|
|
225
|
+
layerNodes.utilities.before(
|
|
226
|
+
cloneNodes([...utilityNodes], layerNodes.utilities.source, {
|
|
227
|
+
layer: 'utilities',
|
|
228
|
+
})
|
|
229
|
+
)
|
|
218
230
|
layerNodes.utilities.remove()
|
|
219
231
|
}
|
|
220
232
|
|
|
@@ -234,10 +246,18 @@ export default function expandTailwindAtRules(context) {
|
|
|
234
246
|
})
|
|
235
247
|
|
|
236
248
|
if (layerNodes.variants) {
|
|
237
|
-
layerNodes.variants.before(
|
|
249
|
+
layerNodes.variants.before(
|
|
250
|
+
cloneNodes(variantNodes, layerNodes.variants.source, {
|
|
251
|
+
layer: 'variants',
|
|
252
|
+
})
|
|
253
|
+
)
|
|
238
254
|
layerNodes.variants.remove()
|
|
239
255
|
} else if (variantNodes.length > 0) {
|
|
240
|
-
root.append(
|
|
256
|
+
root.append(
|
|
257
|
+
cloneNodes(variantNodes, root.source, {
|
|
258
|
+
layer: 'variants',
|
|
259
|
+
})
|
|
260
|
+
)
|
|
241
261
|
}
|
|
242
262
|
|
|
243
263
|
// If we've got a utility layer and no utilities are generated there's likely something wrong
|
package/src/lib/generateRules.js
CHANGED
|
@@ -5,6 +5,7 @@ import isPlainObject from '../util/isPlainObject'
|
|
|
5
5
|
import prefixSelector from '../util/prefixSelector'
|
|
6
6
|
import { updateAllClasses } from '../util/pluginUtils'
|
|
7
7
|
import log from '../util/log'
|
|
8
|
+
import * as sharedState from './sharedState'
|
|
8
9
|
import { formatVariantSelector, finalizeSelector } from '../util/formatVariantSelector'
|
|
9
10
|
import { asClass } from '../util/nameClass'
|
|
10
11
|
import { normalize } from '../util/dataTypes'
|
|
@@ -87,7 +88,7 @@ function applyPrefix(matches, context) {
|
|
|
87
88
|
return matches
|
|
88
89
|
}
|
|
89
90
|
|
|
90
|
-
function applyImportant(matches) {
|
|
91
|
+
function applyImportant(matches, classCandidate) {
|
|
91
92
|
if (matches.length === 0) {
|
|
92
93
|
return matches
|
|
93
94
|
}
|
|
@@ -97,7 +98,10 @@ function applyImportant(matches) {
|
|
|
97
98
|
let container = postcss.root({ nodes: [rule.clone()] })
|
|
98
99
|
container.walkRules((r) => {
|
|
99
100
|
r.selector = updateAllClasses(r.selector, (className) => {
|
|
100
|
-
|
|
101
|
+
if (className === classCandidate) {
|
|
102
|
+
return `!${className}`
|
|
103
|
+
}
|
|
104
|
+
return className
|
|
101
105
|
})
|
|
102
106
|
r.walkDecls((d) => (d.important = true))
|
|
103
107
|
})
|
|
@@ -299,6 +303,19 @@ function looksLikeUri(declaration) {
|
|
|
299
303
|
}
|
|
300
304
|
}
|
|
301
305
|
|
|
306
|
+
function isParsableNode(node) {
|
|
307
|
+
let isParsable = true
|
|
308
|
+
|
|
309
|
+
node.walkDecls((decl) => {
|
|
310
|
+
if (!isParsableCssValue(decl.name, decl.value)) {
|
|
311
|
+
isParsable = false
|
|
312
|
+
return false
|
|
313
|
+
}
|
|
314
|
+
})
|
|
315
|
+
|
|
316
|
+
return isParsable
|
|
317
|
+
}
|
|
318
|
+
|
|
302
319
|
function isParsableCssValue(property, value) {
|
|
303
320
|
// We don't want to to treat [https://example.com] as a custom property
|
|
304
321
|
// Even though, according to the CSS grammar, it's a totally valid CSS declaration
|
|
@@ -382,6 +399,10 @@ function* resolveMatchedPlugins(classCandidate, context) {
|
|
|
382
399
|
}
|
|
383
400
|
|
|
384
401
|
function splitWithSeparator(input, separator) {
|
|
402
|
+
if (input === sharedState.NOT_ON_DEMAND) {
|
|
403
|
+
return [sharedState.NOT_ON_DEMAND]
|
|
404
|
+
}
|
|
405
|
+
|
|
385
406
|
return input.split(new RegExp(`\\${separator}(?![^[]*\\])`, 'g'))
|
|
386
407
|
}
|
|
387
408
|
|
|
@@ -448,60 +469,66 @@ function* resolveMatches(candidate, context) {
|
|
|
448
469
|
}
|
|
449
470
|
}
|
|
450
471
|
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
472
|
+
if (isArbitraryValue(modifier)) {
|
|
473
|
+
// When generated arbitrary values are ambiguous, we can't know
|
|
474
|
+
// which to pick so don't generate any utilities for them
|
|
475
|
+
if (matches.length > 1) {
|
|
476
|
+
let typesPerPlugin = matches.map((match) => new Set([...(typesByMatches.get(match) ?? [])]))
|
|
455
477
|
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
478
|
+
// Remove duplicates, so that we can detect proper unique types for each plugin.
|
|
479
|
+
for (let pluginTypes of typesPerPlugin) {
|
|
480
|
+
for (let type of pluginTypes) {
|
|
481
|
+
let removeFromOwnGroup = false
|
|
460
482
|
|
|
461
|
-
|
|
462
|
-
|
|
483
|
+
for (let otherGroup of typesPerPlugin) {
|
|
484
|
+
if (pluginTypes === otherGroup) continue
|
|
463
485
|
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
486
|
+
if (otherGroup.has(type)) {
|
|
487
|
+
otherGroup.delete(type)
|
|
488
|
+
removeFromOwnGroup = true
|
|
489
|
+
}
|
|
467
490
|
}
|
|
468
|
-
}
|
|
469
491
|
|
|
470
|
-
|
|
492
|
+
if (removeFromOwnGroup) pluginTypes.delete(type)
|
|
493
|
+
}
|
|
471
494
|
}
|
|
472
|
-
}
|
|
473
495
|
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
496
|
+
let messages = []
|
|
497
|
+
|
|
498
|
+
for (let [idx, group] of typesPerPlugin.entries()) {
|
|
499
|
+
for (let type of group) {
|
|
500
|
+
let rules = matches[idx]
|
|
501
|
+
.map(([, rule]) => rule)
|
|
502
|
+
.flat()
|
|
503
|
+
.map((rule) =>
|
|
504
|
+
rule
|
|
505
|
+
.toString()
|
|
506
|
+
.split('\n')
|
|
507
|
+
.slice(1, -1) // Remove selector and closing '}'
|
|
508
|
+
.map((line) => line.trim())
|
|
509
|
+
.map((x) => ` ${x}`) // Re-indent
|
|
510
|
+
.join('\n')
|
|
511
|
+
)
|
|
512
|
+
.join('\n\n')
|
|
513
|
+
|
|
514
|
+
messages.push(
|
|
515
|
+
` Use \`${candidate.replace('[', `[${type}:`)}\` for \`${rules.trim()}\``
|
|
489
516
|
)
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
messages.push(` Use \`${candidate.replace('[', `[${type}:`)}\` for \`${rules.trim()}\``)
|
|
493
|
-
break
|
|
517
|
+
break
|
|
518
|
+
}
|
|
494
519
|
}
|
|
520
|
+
|
|
521
|
+
log.warn([
|
|
522
|
+
`The class \`${candidate}\` is ambiguous and matches multiple utilities.`,
|
|
523
|
+
...messages,
|
|
524
|
+
`If this is content and not a class, replace it with \`${candidate
|
|
525
|
+
.replace('[', '[')
|
|
526
|
+
.replace(']', ']')}\` to silence this warning.`,
|
|
527
|
+
])
|
|
528
|
+
continue
|
|
495
529
|
}
|
|
496
530
|
|
|
497
|
-
|
|
498
|
-
`The class \`${candidate}\` is ambiguous and matches multiple utilities.`,
|
|
499
|
-
...messages,
|
|
500
|
-
`If this is content and not a class, replace it with \`${candidate
|
|
501
|
-
.replace('[', '[')
|
|
502
|
-
.replace(']', ']')}\` to silence this warning.`,
|
|
503
|
-
])
|
|
504
|
-
continue
|
|
531
|
+
matches = matches.map((list) => list.filter((match) => isParsableNode(match[1])))
|
|
505
532
|
}
|
|
506
533
|
|
|
507
534
|
matches = matches.flat()
|
|
@@ -509,7 +536,7 @@ function* resolveMatches(candidate, context) {
|
|
|
509
536
|
matches = applyPrefix(matches, context)
|
|
510
537
|
|
|
511
538
|
if (important) {
|
|
512
|
-
matches = applyImportant(matches,
|
|
539
|
+
matches = applyImportant(matches, classCandidate)
|
|
513
540
|
}
|
|
514
541
|
|
|
515
542
|
for (let variant of variants) {
|
|
@@ -120,7 +120,9 @@ export default function resolveDefaultsAtRules({ tailwindConfig }) {
|
|
|
120
120
|
}
|
|
121
121
|
|
|
122
122
|
for (let [, selectors] of selectorGroups) {
|
|
123
|
-
let universalRule = postcss.rule(
|
|
123
|
+
let universalRule = postcss.rule({
|
|
124
|
+
source: universal.source,
|
|
125
|
+
})
|
|
124
126
|
|
|
125
127
|
universalRule.selectors = [...selectors]
|
|
126
128
|
|
|
@@ -128,7 +130,9 @@ export default function resolveDefaultsAtRules({ tailwindConfig }) {
|
|
|
128
130
|
universal.before(universalRule)
|
|
129
131
|
}
|
|
130
132
|
} else {
|
|
131
|
-
let universalRule = postcss.rule(
|
|
133
|
+
let universalRule = postcss.rule({
|
|
134
|
+
source: universal.source,
|
|
135
|
+
})
|
|
132
136
|
|
|
133
137
|
universalRule.selectors = ['*', '::before', '::after']
|
|
134
138
|
|
|
@@ -20,6 +20,7 @@ import log from '../util/log'
|
|
|
20
20
|
import negateValue from '../util/negateValue'
|
|
21
21
|
import isValidArbitraryValue from '../util/isValidArbitraryValue'
|
|
22
22
|
import { generateRules } from './generateRules'
|
|
23
|
+
import { hasContentChanged } from './cacheInvalidation.js'
|
|
23
24
|
|
|
24
25
|
function prefix(context, selector) {
|
|
25
26
|
let prefix = context.tailwindConfig.prefix
|
|
@@ -84,12 +85,18 @@ function parseStyles(styles) {
|
|
|
84
85
|
})
|
|
85
86
|
}
|
|
86
87
|
|
|
87
|
-
function getClasses(selector) {
|
|
88
|
+
function getClasses(selector, mutate) {
|
|
88
89
|
let parser = selectorParser((selectors) => {
|
|
89
90
|
let allClasses = []
|
|
91
|
+
|
|
92
|
+
if (mutate) {
|
|
93
|
+
mutate(selectors)
|
|
94
|
+
}
|
|
95
|
+
|
|
90
96
|
selectors.walkClasses((classNode) => {
|
|
91
97
|
allClasses.push(classNode.value)
|
|
92
98
|
})
|
|
99
|
+
|
|
93
100
|
return allClasses
|
|
94
101
|
})
|
|
95
102
|
return parser.transformSync(selector)
|
|
@@ -100,8 +107,20 @@ function extractCandidates(node, state = { containsNonOnDemandable: false }, dep
|
|
|
100
107
|
|
|
101
108
|
// Handle normal rules
|
|
102
109
|
if (node.type === 'rule') {
|
|
110
|
+
// Ignore everything inside a :not(...). This allows you to write code like
|
|
111
|
+
// `div:not(.foo)`. If `.foo` is never found in your code, then we used to
|
|
112
|
+
// not generated it. But now we will ignore everything inside a `:not`, so
|
|
113
|
+
// that it still gets generated.
|
|
114
|
+
function ignoreNot(selectors) {
|
|
115
|
+
selectors.walkPseudos((pseudo) => {
|
|
116
|
+
if (pseudo.value === ':not') {
|
|
117
|
+
pseudo.remove()
|
|
118
|
+
}
|
|
119
|
+
})
|
|
120
|
+
}
|
|
121
|
+
|
|
103
122
|
for (let selector of node.selectors) {
|
|
104
|
-
let classCandidates = getClasses(selector)
|
|
123
|
+
let classCandidates = getClasses(selector, ignoreNot)
|
|
105
124
|
// At least one of the selectors contains non-"on-demandable" candidates.
|
|
106
125
|
if (classCandidates.length === 0) {
|
|
107
126
|
state.containsNonOnDemandable = true
|
|
@@ -116,9 +135,7 @@ function extractCandidates(node, state = { containsNonOnDemandable: false }, dep
|
|
|
116
135
|
// Handle at-rules (which contains nested rules)
|
|
117
136
|
else if (node.type === 'atrule') {
|
|
118
137
|
node.walkRules((rule) => {
|
|
119
|
-
for (let classCandidate of rule.selectors.flatMap((selector) =>
|
|
120
|
-
getClasses(selector, state, depth + 1)
|
|
121
|
-
)) {
|
|
138
|
+
for (let classCandidate of rule.selectors.flatMap((selector) => getClasses(selector))) {
|
|
122
139
|
classes.push(classCandidate)
|
|
123
140
|
}
|
|
124
141
|
})
|
|
@@ -138,7 +155,7 @@ function withIdentifiers(styles) {
|
|
|
138
155
|
|
|
139
156
|
// If this isn't "on-demandable", assign it a universal candidate to always include it.
|
|
140
157
|
if (containsNonOnDemandableSelectors) {
|
|
141
|
-
candidates.unshift(
|
|
158
|
+
candidates.unshift(sharedState.NOT_ON_DEMAND)
|
|
142
159
|
}
|
|
143
160
|
|
|
144
161
|
// However, it could be that it also contains "on-demandable" candidates.
|
|
@@ -163,8 +180,8 @@ function buildPluginApi(tailwindConfig, context, { variantList, variantMap, offs
|
|
|
163
180
|
}
|
|
164
181
|
|
|
165
182
|
function prefixIdentifier(identifier, options) {
|
|
166
|
-
if (identifier ===
|
|
167
|
-
return
|
|
183
|
+
if (identifier === sharedState.NOT_ON_DEMAND) {
|
|
184
|
+
return sharedState.NOT_ON_DEMAND
|
|
168
185
|
}
|
|
169
186
|
|
|
170
187
|
if (!options.respectPrefix) {
|
|
@@ -230,17 +247,6 @@ function buildPluginApi(tailwindConfig, context, { variantList, variantMap, offs
|
|
|
230
247
|
// Preserved for backwards compatibility but not used in v3.0+
|
|
231
248
|
return []
|
|
232
249
|
},
|
|
233
|
-
addUserCss(userCss) {
|
|
234
|
-
for (let [identifier, rule] of withIdentifiers(userCss)) {
|
|
235
|
-
let offset = offsets.user++
|
|
236
|
-
|
|
237
|
-
if (!context.candidateRuleMap.has(identifier)) {
|
|
238
|
-
context.candidateRuleMap.set(identifier, [])
|
|
239
|
-
}
|
|
240
|
-
|
|
241
|
-
context.candidateRuleMap.get(identifier).push([{ sort: offset, layer: 'user' }, rule])
|
|
242
|
-
}
|
|
243
|
-
},
|
|
244
250
|
addBase(base) {
|
|
245
251
|
for (let [identifier, rule] of withIdentifiers(base)) {
|
|
246
252
|
let prefixedIdentifier = prefixIdentifier(identifier, {})
|
|
@@ -521,15 +527,6 @@ function collectLayerPlugins(root) {
|
|
|
521
527
|
}
|
|
522
528
|
})
|
|
523
529
|
|
|
524
|
-
root.walkRules((rule) => {
|
|
525
|
-
// At this point it is safe to include all the left-over css from the
|
|
526
|
-
// user's css file. This is because the `@tailwind` and `@layer` directives
|
|
527
|
-
// will already be handled and will be removed from the css tree.
|
|
528
|
-
layerPlugins.push(function ({ addUserCss }) {
|
|
529
|
-
addUserCss(rule, { respectPrefix: false })
|
|
530
|
-
})
|
|
531
|
-
})
|
|
532
|
-
|
|
533
530
|
return layerPlugins
|
|
534
531
|
}
|
|
535
532
|
|
|
@@ -744,33 +741,25 @@ function registerPlugins(plugins, context) {
|
|
|
744
741
|
// sorting could be weird since you still require them in order to make the
|
|
745
742
|
// host utitlies work properly. (Thanks Biology)
|
|
746
743
|
let parasiteUtilities = new Set([prefix(context, 'group'), prefix(context, 'peer')])
|
|
747
|
-
context.
|
|
744
|
+
context.getClassOrder = function getClassOrder(classes) {
|
|
748
745
|
let sortedClassNames = new Map()
|
|
749
746
|
for (let [sort, rule] of generateRules(new Set(classes), context)) {
|
|
750
747
|
if (sortedClassNames.has(rule.raws.tailwind.candidate)) continue
|
|
751
748
|
sortedClassNames.set(rule.raws.tailwind.candidate, sort)
|
|
752
749
|
}
|
|
753
750
|
|
|
754
|
-
return classes
|
|
755
|
-
.
|
|
756
|
-
let order = sortedClassNames.get(className) ?? null
|
|
751
|
+
return classes.map((className) => {
|
|
752
|
+
let order = sortedClassNames.get(className) ?? null
|
|
757
753
|
|
|
758
|
-
|
|
759
|
-
|
|
760
|
-
|
|
761
|
-
|
|
762
|
-
|
|
763
|
-
|
|
754
|
+
if (order === null && parasiteUtilities.has(className)) {
|
|
755
|
+
// This will make sure that it is at the very beginning of the
|
|
756
|
+
// `components` layer which technically means 'before any
|
|
757
|
+
// components'.
|
|
758
|
+
order = context.layerOrder.components
|
|
759
|
+
}
|
|
764
760
|
|
|
765
|
-
|
|
766
|
-
|
|
767
|
-
.sort(([, a], [, z]) => {
|
|
768
|
-
if (a === z) return 0
|
|
769
|
-
if (a === null) return -1
|
|
770
|
-
if (z === null) return 1
|
|
771
|
-
return bigSign(a - z)
|
|
772
|
-
})
|
|
773
|
-
.map(([className]) => className)
|
|
761
|
+
return [className, order]
|
|
762
|
+
})
|
|
774
763
|
}
|
|
775
764
|
|
|
776
765
|
// Generate a list of strings for autocompletion purposes, e.g.
|
|
@@ -850,6 +839,8 @@ export function getContext(
|
|
|
850
839
|
existingContext = context
|
|
851
840
|
}
|
|
852
841
|
|
|
842
|
+
let cssDidChange = hasContentChanged(sourcePath, root)
|
|
843
|
+
|
|
853
844
|
// If there's already a context in the cache and we don't need to
|
|
854
845
|
// reset the context, return the cached context.
|
|
855
846
|
if (existingContext) {
|
|
@@ -857,7 +848,7 @@ export function getContext(
|
|
|
857
848
|
[...contextDependencies],
|
|
858
849
|
getFileModifiedMap(existingContext)
|
|
859
850
|
)
|
|
860
|
-
if (!contextDependenciesChanged) {
|
|
851
|
+
if (!contextDependenciesChanged && !cssDidChange) {
|
|
861
852
|
return [existingContext, false]
|
|
862
853
|
}
|
|
863
854
|
}
|