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.
Files changed (89) hide show
  1. package/CHANGELOG.md +45 -2
  2. package/lib/cli-peer-dependencies.js +3 -3
  3. package/lib/cli.js +183 -161
  4. package/lib/constants.js +8 -8
  5. package/lib/corePlugins.js +1597 -1518
  6. package/lib/featureFlags.js +9 -9
  7. package/lib/index.js +19 -6
  8. package/lib/lib/cacheInvalidation.js +69 -0
  9. package/lib/lib/collapseAdjacentRules.js +26 -13
  10. package/lib/lib/collapseDuplicateDeclarations.js +1 -1
  11. package/lib/lib/defaultExtractor.js +8 -6
  12. package/lib/lib/detectNesting.js +9 -9
  13. package/lib/lib/evaluateTailwindFunctions.js +16 -16
  14. package/lib/lib/expandApplyAtRules.js +180 -27
  15. package/lib/lib/expandTailwindAtRules.js +132 -122
  16. package/lib/lib/generateRules.js +117 -72
  17. package/lib/lib/getModuleDependencies.js +14 -14
  18. package/lib/lib/normalizeTailwindDirectives.js +35 -35
  19. package/lib/lib/partitionApplyAtRules.js +7 -7
  20. package/lib/lib/resolveDefaultsAtRules.js +81 -77
  21. package/lib/lib/setupContextUtils.js +83 -98
  22. package/lib/lib/setupTrackingContext.js +57 -57
  23. package/lib/lib/sharedState.js +11 -7
  24. package/lib/lib/substituteScreenAtRules.js +2 -2
  25. package/lib/postcss-plugins/nesting/README.md +2 -2
  26. package/lib/postcss-plugins/nesting/index.js +1 -1
  27. package/lib/postcss-plugins/nesting/plugin.js +41 -9
  28. package/lib/processTailwindFeatures.js +7 -7
  29. package/lib/public/colors.js +241 -241
  30. package/lib/public/resolve-config.js +5 -5
  31. package/lib/util/buildMediaQuery.js +2 -2
  32. package/lib/util/cloneDeep.js +1 -1
  33. package/lib/util/cloneNodes.js +12 -1
  34. package/lib/util/color.js +21 -20
  35. package/lib/util/createUtilityPlugin.js +6 -6
  36. package/lib/util/dataTypes.js +77 -75
  37. package/lib/util/escapeClassName.js +5 -5
  38. package/lib/util/escapeCommas.js +1 -1
  39. package/lib/util/flattenColorPalette.js +2 -2
  40. package/lib/util/formatVariantSelector.js +19 -19
  41. package/lib/util/getAllConfigs.js +5 -5
  42. package/lib/util/hashConfig.js +5 -5
  43. package/lib/util/isKeyframeRule.js +1 -1
  44. package/lib/util/isPlainObject.js +1 -1
  45. package/lib/util/isValidArbitraryValue.js +27 -27
  46. package/lib/util/log.js +8 -8
  47. package/lib/util/nameClass.js +7 -7
  48. package/lib/util/negateValue.js +4 -4
  49. package/lib/util/normalizeConfig.js +39 -39
  50. package/lib/util/normalizeScreens.js +4 -4
  51. package/lib/util/parseAnimationValue.js +56 -56
  52. package/lib/util/parseBoxShadowValue.js +60 -20
  53. package/lib/util/parseDependency.js +32 -32
  54. package/lib/util/parseObjectStyles.js +6 -6
  55. package/lib/util/pluginUtils.js +9 -9
  56. package/lib/util/prefixSelector.js +1 -1
  57. package/lib/util/resolveConfig.js +28 -28
  58. package/lib/util/resolveConfigPath.js +16 -16
  59. package/lib/util/responsive.js +6 -6
  60. package/lib/util/toColorValue.js +1 -1
  61. package/lib/util/toPath.js +2 -2
  62. package/lib/util/transformThemeValue.js +27 -27
  63. package/lib/util/withAlphaVariable.js +19 -19
  64. package/package.json +26 -25
  65. package/peers/index.js +4803 -4857
  66. package/scripts/generate-types.js +52 -0
  67. package/src/cli.js +41 -11
  68. package/src/corePlugins.js +104 -9
  69. package/src/featureFlags.js +2 -2
  70. package/src/index.js +17 -1
  71. package/src/lib/cacheInvalidation.js +52 -0
  72. package/src/lib/collapseAdjacentRules.js +16 -1
  73. package/src/lib/defaultExtractor.js +6 -4
  74. package/src/lib/expandApplyAtRules.js +179 -6
  75. package/src/lib/expandTailwindAtRules.js +26 -6
  76. package/src/lib/generateRules.js +73 -46
  77. package/src/lib/resolveDefaultsAtRules.js +6 -2
  78. package/src/lib/setupContextUtils.js +39 -48
  79. package/src/lib/setupTrackingContext.js +3 -3
  80. package/src/lib/sharedState.js +2 -0
  81. package/src/postcss-plugins/nesting/README.md +2 -2
  82. package/src/postcss-plugins/nesting/plugin.js +36 -0
  83. package/src/util/cloneNodes.js +14 -1
  84. package/src/util/color.js +7 -5
  85. package/src/util/dataTypes.js +3 -1
  86. package/src/util/log.js +7 -7
  87. package/src/util/parseBoxShadowValue.js +50 -2
  88. package/src/util/resolveConfig.js +32 -0
  89. 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
- processApply(root, context)
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(cloneNodes([...baseNodes, ...defaultNodes], layerNodes.base.source))
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(cloneNodes([...componentNodes], layerNodes.components.source))
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(cloneNodes([...utilityNodes], layerNodes.utilities.source))
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(cloneNodes(variantNodes, layerNodes.variants.source))
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(cloneNodes(variantNodes, root.source))
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
@@ -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
- return `!${className}`
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
- // Only keep the result of the very first plugin if we are dealing with
452
- // arbitrary values, to protect against ambiguity.
453
- if (isArbitraryValue(modifier) && matches.length > 1) {
454
- let typesPerPlugin = matches.map((match) => new Set([...(typesByMatches.get(match) ?? [])]))
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
- // Remove duplicates, so that we can detect proper unique types for each plugin.
457
- for (let pluginTypes of typesPerPlugin) {
458
- for (let type of pluginTypes) {
459
- let removeFromOwnGroup = false
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
- for (let otherGroup of typesPerPlugin) {
462
- if (pluginTypes === otherGroup) continue
483
+ for (let otherGroup of typesPerPlugin) {
484
+ if (pluginTypes === otherGroup) continue
463
485
 
464
- if (otherGroup.has(type)) {
465
- otherGroup.delete(type)
466
- removeFromOwnGroup = true
486
+ if (otherGroup.has(type)) {
487
+ otherGroup.delete(type)
488
+ removeFromOwnGroup = true
489
+ }
467
490
  }
468
- }
469
491
 
470
- if (removeFromOwnGroup) pluginTypes.delete(type)
492
+ if (removeFromOwnGroup) pluginTypes.delete(type)
493
+ }
471
494
  }
472
- }
473
495
 
474
- let messages = []
475
-
476
- for (let [idx, group] of typesPerPlugin.entries()) {
477
- for (let type of group) {
478
- let rules = matches[idx]
479
- .map(([, rule]) => rule)
480
- .flat()
481
- .map((rule) =>
482
- rule
483
- .toString()
484
- .split('\n')
485
- .slice(1, -1) // Remove selector and closing '}'
486
- .map((line) => line.trim())
487
- .map((x) => ` ${x}`) // Re-indent
488
- .join('\n')
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
- .join('\n\n')
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('[', '&lsqb;')
526
+ .replace(']', '&rsqb;')}\` to silence this warning.`,
527
+ ])
528
+ continue
495
529
  }
496
530
 
497
- log.warn([
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('[', '&lsqb;')
502
- .replace(']', '&rsqb;')}\` 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, context)
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.sortClassList = function sortClassList(classes) {
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
- .map((className) => {
756
- let order = sortedClassNames.get(className) ?? null
751
+ return classes.map((className) => {
752
+ let order = sortedClassNames.get(className) ?? null
757
753
 
758
- if (order === null && parasiteUtilities.has(className)) {
759
- // This will make sure that it is at the very beginning of the
760
- // `components` layer which technically means 'before any
761
- // components'.
762
- order = context.layerOrder.components
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
- return [className, order]
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
  }