tailwindcss 3.0.22 → 3.1.0

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 (119) hide show
  1. package/CHANGELOG.md +92 -2
  2. package/colors.d.ts +3 -0
  3. package/defaultConfig.d.ts +3 -0
  4. package/defaultTheme.d.ts +3 -0
  5. package/lib/cli-peer-dependencies.js +10 -5
  6. package/lib/cli.js +266 -203
  7. package/lib/constants.js +8 -8
  8. package/lib/corePluginList.js +1 -0
  9. package/lib/corePlugins.js +1662 -1554
  10. package/lib/css/preflight.css +1 -8
  11. package/lib/featureFlags.js +14 -12
  12. package/lib/index.js +16 -6
  13. package/lib/lib/cacheInvalidation.js +87 -0
  14. package/lib/lib/collapseAdjacentRules.js +30 -15
  15. package/lib/lib/collapseDuplicateDeclarations.js +1 -1
  16. package/lib/lib/defaultExtractor.js +191 -30
  17. package/lib/lib/detectNesting.js +9 -9
  18. package/lib/lib/evaluateTailwindFunctions.js +37 -28
  19. package/lib/lib/expandApplyAtRules.js +379 -189
  20. package/lib/lib/expandTailwindAtRules.js +168 -144
  21. package/lib/lib/generateRules.js +190 -81
  22. package/lib/lib/getModuleDependencies.js +14 -14
  23. package/lib/lib/normalizeTailwindDirectives.js +35 -35
  24. package/lib/lib/partitionApplyAtRules.js +7 -7
  25. package/lib/lib/regex.js +52 -0
  26. package/lib/lib/resolveDefaultsAtRules.js +80 -79
  27. package/lib/lib/setupContextUtils.js +207 -170
  28. package/lib/lib/setupTrackingContext.js +61 -63
  29. package/lib/lib/sharedState.js +11 -8
  30. package/lib/lib/substituteScreenAtRules.js +3 -4
  31. package/lib/postcss-plugins/nesting/README.md +2 -2
  32. package/lib/postcss-plugins/nesting/index.js +1 -1
  33. package/lib/postcss-plugins/nesting/plugin.js +40 -9
  34. package/lib/processTailwindFeatures.js +7 -7
  35. package/lib/public/colors.js +241 -241
  36. package/lib/public/resolve-config.js +5 -5
  37. package/lib/util/buildMediaQuery.js +2 -3
  38. package/lib/util/cloneDeep.js +3 -5
  39. package/lib/util/cloneNodes.js +12 -1
  40. package/lib/util/color.js +42 -51
  41. package/lib/util/createPlugin.js +1 -2
  42. package/lib/util/createUtilityPlugin.js +6 -7
  43. package/lib/util/dataTypes.js +85 -81
  44. package/lib/util/escapeClassName.js +5 -5
  45. package/lib/util/escapeCommas.js +1 -1
  46. package/lib/util/flattenColorPalette.js +4 -7
  47. package/lib/util/formatVariantSelector.js +82 -75
  48. package/lib/util/getAllConfigs.js +15 -10
  49. package/lib/util/hashConfig.js +5 -5
  50. package/lib/util/isKeyframeRule.js +1 -1
  51. package/lib/util/isPlainObject.js +1 -1
  52. package/lib/util/isValidArbitraryValue.js +26 -27
  53. package/lib/util/log.js +9 -10
  54. package/lib/util/nameClass.js +7 -7
  55. package/lib/util/negateValue.js +4 -5
  56. package/lib/util/normalizeConfig.js +68 -58
  57. package/lib/util/normalizeScreens.js +5 -6
  58. package/lib/util/parseAnimationValue.js +56 -57
  59. package/lib/util/parseBoxShadowValue.js +19 -20
  60. package/lib/util/parseDependency.js +32 -32
  61. package/lib/util/parseObjectStyles.js +6 -6
  62. package/lib/util/pluginUtils.js +20 -12
  63. package/lib/util/prefixSelector.js +1 -1
  64. package/lib/util/resolveConfig.js +81 -58
  65. package/lib/util/resolveConfigPath.js +16 -16
  66. package/lib/util/responsive.js +6 -6
  67. package/lib/util/splitAtTopLevelOnly.js +90 -0
  68. package/lib/util/toColorValue.js +1 -1
  69. package/lib/util/toPath.js +2 -2
  70. package/lib/util/transformThemeValue.js +30 -28
  71. package/lib/util/validateConfig.js +21 -0
  72. package/lib/util/withAlphaVariable.js +23 -23
  73. package/package.json +33 -27
  74. package/peers/index.js +7728 -5848
  75. package/plugin.d.ts +11 -0
  76. package/scripts/generate-types.js +52 -0
  77. package/src/cli-peer-dependencies.js +7 -1
  78. package/src/cli.js +118 -24
  79. package/src/corePluginList.js +1 -1
  80. package/src/corePlugins.js +142 -30
  81. package/src/css/preflight.css +1 -8
  82. package/src/featureFlags.js +4 -4
  83. package/src/index.js +15 -1
  84. package/src/lib/cacheInvalidation.js +52 -0
  85. package/src/lib/collapseAdjacentRules.js +21 -2
  86. package/src/lib/defaultExtractor.js +177 -33
  87. package/src/lib/evaluateTailwindFunctions.js +20 -4
  88. package/src/lib/expandApplyAtRules.js +418 -186
  89. package/src/lib/expandTailwindAtRules.js +30 -10
  90. package/src/lib/generateRules.js +142 -51
  91. package/src/lib/regex.js +74 -0
  92. package/src/lib/resolveDefaultsAtRules.js +7 -3
  93. package/src/lib/setupContextUtils.js +142 -87
  94. package/src/lib/setupTrackingContext.js +7 -3
  95. package/src/lib/sharedState.js +2 -0
  96. package/src/postcss-plugins/nesting/README.md +2 -2
  97. package/src/postcss-plugins/nesting/plugin.js +36 -0
  98. package/src/util/cloneNodes.js +14 -1
  99. package/src/util/color.js +25 -21
  100. package/src/util/dataTypes.js +14 -6
  101. package/src/util/formatVariantSelector.js +79 -62
  102. package/src/util/getAllConfigs.js +7 -0
  103. package/src/util/log.js +8 -8
  104. package/src/util/normalizeConfig.js +0 -8
  105. package/src/util/parseBoxShadowValue.js +3 -2
  106. package/src/util/pluginUtils.js +13 -1
  107. package/src/util/resolveConfig.js +66 -22
  108. package/src/util/splitAtTopLevelOnly.js +71 -0
  109. package/src/util/toPath.js +1 -1
  110. package/src/util/transformThemeValue.js +4 -2
  111. package/src/util/validateConfig.js +13 -0
  112. package/src/util/withAlphaVariable.js +1 -1
  113. package/stubs/defaultConfig.stub.js +5 -1
  114. package/stubs/simpleConfig.stub.js +1 -0
  115. package/types/config.d.ts +325 -0
  116. package/types/generated/.gitkeep +0 -0
  117. package/types/generated/colors.d.ts +276 -0
  118. package/types/generated/corePluginList.d.ts +1 -0
  119. package/types/index.d.ts +1 -0
@@ -5,19 +5,42 @@ 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
- let classes = new Set()
11
+ /** @type {Map<string, Set<string>>} */
12
+ let groups = new Map()
13
+
10
14
  let container = postcss.root({ nodes: [node.clone()] })
11
15
 
12
16
  container.walkRules((rule) => {
13
17
  parser((selectors) => {
14
18
  selectors.walkClasses((classSelector) => {
19
+ let parentSelector = classSelector.parent.toString()
20
+
21
+ let classes = groups.get(parentSelector)
22
+ if (!classes) {
23
+ groups.set(parentSelector, (classes = new Set()))
24
+ }
25
+
15
26
  classes.add(classSelector.value)
16
27
  })
17
28
  }).processSync(rule.selector)
18
29
  })
19
30
 
20
- return Array.from(classes)
31
+ let normalizedGroups = Array.from(groups.values(), (classes) => Array.from(classes))
32
+ let classes = normalizedGroups.flat()
33
+
34
+ return Object.assign(classes, { groups: normalizedGroups })
35
+ }
36
+
37
+ let selectorExtractor = parser((root) => root.nodes.map((node) => node.toString()))
38
+
39
+ /**
40
+ * @param {string} ruleSelectors
41
+ */
42
+ function extractSelectors(ruleSelectors) {
43
+ return selectorExtractor.transformSync(ruleSelectors)
21
44
  }
22
45
 
23
46
  function extractBaseCandidates(candidates, separator) {
@@ -35,6 +58,131 @@ function prefix(context, selector) {
35
58
  return typeof prefix === 'function' ? prefix(selector) : prefix + selector
36
59
  }
37
60
 
61
+ function* pathToRoot(node) {
62
+ yield node
63
+ while (node.parent) {
64
+ yield node.parent
65
+ node = node.parent
66
+ }
67
+ }
68
+
69
+ /**
70
+ * Only clone the node itself and not its children
71
+ *
72
+ * @param {*} node
73
+ * @param {*} overrides
74
+ * @returns
75
+ */
76
+ function shallowClone(node, overrides = {}) {
77
+ let children = node.nodes
78
+ node.nodes = []
79
+
80
+ let tmp = node.clone(overrides)
81
+
82
+ node.nodes = children
83
+
84
+ return tmp
85
+ }
86
+
87
+ /**
88
+ * Clone just the nodes all the way to the top that are required to represent
89
+ * this singular rule in the tree.
90
+ *
91
+ * For example, if we have CSS like this:
92
+ * ```css
93
+ * @media (min-width: 768px) {
94
+ * @supports (display: grid) {
95
+ * .foo {
96
+ * display: grid;
97
+ * grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
98
+ * }
99
+ * }
100
+ *
101
+ * @supports (backdrop-filter: blur(1px)) {
102
+ * .bar {
103
+ * backdrop-filter: blur(1px);
104
+ * }
105
+ * }
106
+ *
107
+ * .baz {
108
+ * color: orange;
109
+ * }
110
+ * }
111
+ * ```
112
+ *
113
+ * And we're cloning `.bar` it'll return a cloned version of what's required for just that single node:
114
+ *
115
+ * ```css
116
+ * @media (min-width: 768px) {
117
+ * @supports (backdrop-filter: blur(1px)) {
118
+ * .bar {
119
+ * backdrop-filter: blur(1px);
120
+ * }
121
+ * }
122
+ * }
123
+ * ```
124
+ *
125
+ * @param {import('postcss').Node} node
126
+ */
127
+ function nestedClone(node) {
128
+ for (let parent of pathToRoot(node)) {
129
+ if (node === parent) {
130
+ continue
131
+ }
132
+
133
+ if (parent.type === 'root') {
134
+ break
135
+ }
136
+
137
+ node = shallowClone(parent, {
138
+ nodes: [node],
139
+ })
140
+ }
141
+
142
+ return node
143
+ }
144
+
145
+ /**
146
+ * @param {import('postcss').Root} root
147
+ */
148
+ function buildLocalApplyCache(root, context) {
149
+ /** @type {ApplyCache} */
150
+ let cache = new Map()
151
+
152
+ let highestOffset = context.layerOrder.user >> 4n
153
+
154
+ root.walkRules((rule, idx) => {
155
+ // Ignore rules generated by Tailwind
156
+ for (let node of pathToRoot(rule)) {
157
+ if (node.raws.tailwind?.layer !== undefined) {
158
+ return
159
+ }
160
+ }
161
+
162
+ // Clone what's required to represent this singular rule in the tree
163
+ let container = nestedClone(rule)
164
+
165
+ for (let className of extractClasses(rule)) {
166
+ let list = cache.get(className) || []
167
+ cache.set(className, list)
168
+
169
+ list.push([
170
+ {
171
+ layer: 'user',
172
+ sort: BigInt(idx) + highestOffset,
173
+ important: false,
174
+ },
175
+ container,
176
+ ])
177
+ }
178
+ })
179
+
180
+ return cache
181
+ }
182
+
183
+ /**
184
+ * @returns {ApplyCache}
185
+ */
38
186
  function buildApplyCache(applyCandidates, context) {
39
187
  for (let candidate of applyCandidates) {
40
188
  if (context.notClassCache.has(candidate) || context.applyClassCache.has(candidate)) {
@@ -62,6 +210,43 @@ function buildApplyCache(applyCandidates, context) {
62
210
  return context.applyClassCache
63
211
  }
64
212
 
213
+ /**
214
+ * Build a cache only when it's first used
215
+ *
216
+ * @param {() => ApplyCache} buildCacheFn
217
+ * @returns {ApplyCache}
218
+ */
219
+ function lazyCache(buildCacheFn) {
220
+ let cache = null
221
+
222
+ return {
223
+ get: (name) => {
224
+ cache = cache || buildCacheFn()
225
+
226
+ return cache.get(name)
227
+ },
228
+ has: (name) => {
229
+ cache = cache || buildCacheFn()
230
+
231
+ return cache.has(name)
232
+ },
233
+ }
234
+ }
235
+
236
+ /**
237
+ * Take a series of multiple caches and merge
238
+ * them so they act like one large cache
239
+ *
240
+ * @param {ApplyCache[]} caches
241
+ * @returns {ApplyCache}
242
+ */
243
+ function combineCaches(caches) {
244
+ return {
245
+ get: (name) => caches.flatMap((cache) => cache.get(name) || []),
246
+ has: (name) => caches.some((cache) => cache.has(name)),
247
+ }
248
+ }
249
+
65
250
  function extractApplyCandidates(params) {
66
251
  let candidates = params.split(/[\s\t\n]+/g)
67
252
 
@@ -72,7 +257,7 @@ function extractApplyCandidates(params) {
72
257
  return [candidates, false]
73
258
  }
74
259
 
75
- function processApply(root, context) {
260
+ function processApply(root, context, localCache) {
76
261
  let applyCandidates = new Set()
77
262
 
78
263
  // Collect all @apply rules and candidates
@@ -88,220 +273,267 @@ function processApply(root, context) {
88
273
  })
89
274
 
90
275
  // Start the @apply process if we have rules with @apply in them
91
- if (applies.length > 0) {
92
- // Fill up some caches!
93
- let applyClassCache = buildApplyCache(applyCandidates, context)
94
-
95
- /**
96
- * When we have an apply like this:
97
- *
98
- * .abc {
99
- * @apply hover:font-bold;
100
- * }
101
- *
102
- * What we essentially will do is resolve to this:
103
- *
104
- * .abc {
105
- * @apply .hover\:font-bold:hover {
106
- * font-weight: 500;
107
- * }
108
- * }
109
- *
110
- * Notice that the to-be-applied class is `.hover\:font-bold:hover` and that the utility candidate was `hover:font-bold`.
111
- * What happens in this function is that we prepend a `.` and escape the candidate.
112
- * This will result in `.hover\:font-bold`
113
- * Which means that we can replace `.hover\:font-bold` with `.abc` in `.hover\:font-bold:hover` resulting in `.abc:hover`
114
- */
115
- // TODO: Should we use postcss-selector-parser for this instead?
116
- function replaceSelector(selector, utilitySelectors, candidate) {
117
- let needle = `.${escapeClassName(candidate)}`
118
- let utilitySelectorsList = utilitySelectors.split(/\s*\,(?![^(]*\))\s*/g)
119
-
120
- return selector
121
- .split(/\s*\,(?![^(]*\))\s*/g)
122
- .map((s) => {
123
- let replaced = []
124
-
125
- for (let utilitySelector of utilitySelectorsList) {
126
- let replacedSelector = utilitySelector.replace(needle, s)
127
- if (replacedSelector === utilitySelector) {
128
- continue
129
- }
130
- replaced.push(replacedSelector)
276
+ if (applies.length === 0) {
277
+ return
278
+ }
279
+
280
+ // Fill up some caches!
281
+ let applyClassCache = combineCaches([localCache, buildApplyCache(applyCandidates, context)])
282
+
283
+ /**
284
+ * When we have an apply like this:
285
+ *
286
+ * .abc {
287
+ * @apply hover:font-bold;
288
+ * }
289
+ *
290
+ * What we essentially will do is resolve to this:
291
+ *
292
+ * .abc {
293
+ * @apply .hover\:font-bold:hover {
294
+ * font-weight: 500;
295
+ * }
296
+ * }
297
+ *
298
+ * Notice that the to-be-applied class is `.hover\:font-bold:hover` and that the utility candidate was `hover:font-bold`.
299
+ * What happens in this function is that we prepend a `.` and escape the candidate.
300
+ * This will result in `.hover\:font-bold`
301
+ * Which means that we can replace `.hover\:font-bold` with `.abc` in `.hover\:font-bold:hover` resulting in `.abc:hover`
302
+ */
303
+ // TODO: Should we use postcss-selector-parser for this instead?
304
+ function replaceSelector(selector, utilitySelectors, candidate) {
305
+ let needle = `.${escapeClassName(candidate)}`
306
+ let needles = [...new Set([needle, needle.replace(/\\2c /g, '\\,')])]
307
+ let utilitySelectorsList = extractSelectors(utilitySelectors)
308
+
309
+ return extractSelectors(selector)
310
+ .map((s) => {
311
+ let replaced = []
312
+
313
+ for (let utilitySelector of utilitySelectorsList) {
314
+ let replacedSelector = utilitySelector
315
+ for (const needle of needles) {
316
+ replacedSelector = replacedSelector.replace(needle, s)
131
317
  }
132
- return replaced.join(', ')
133
- })
134
- .join(', ')
135
- }
318
+ if (replacedSelector === utilitySelector) {
319
+ continue
320
+ }
321
+ replaced.push(replacedSelector)
322
+ }
323
+ return replaced.join(', ')
324
+ })
325
+ .join(', ')
326
+ }
136
327
 
137
- let perParentApplies = new Map()
328
+ let perParentApplies = new Map()
138
329
 
139
- // Collect all apply candidates and their rules
140
- for (let apply of applies) {
141
- let candidates = perParentApplies.get(apply.parent) || []
330
+ // Collect all apply candidates and their rules
331
+ for (let apply of applies) {
332
+ let [candidates] = perParentApplies.get(apply.parent) || [[], apply.source]
142
333
 
143
- perParentApplies.set(apply.parent, candidates)
334
+ perParentApplies.set(apply.parent, [candidates, apply.source])
144
335
 
145
- let [applyCandidates, important] = extractApplyCandidates(apply.params)
336
+ let [applyCandidates, important] = extractApplyCandidates(apply.params)
146
337
 
147
- if (apply.parent.type === 'atrule') {
148
- if (apply.parent.name === 'screen') {
149
- const screenType = apply.parent.params
338
+ if (apply.parent.type === 'atrule') {
339
+ if (apply.parent.name === 'screen') {
340
+ const screenType = apply.parent.params
150
341
 
151
- throw apply.error(
152
- `@apply is not supported within nested at-rules like @screen. We suggest you write this as @apply ${applyCandidates
153
- .map((c) => `${screenType}:${c}`)
154
- .join(' ')} instead.`
155
- )
156
- }
342
+ throw apply.error(
343
+ `@apply is not supported within nested at-rules like @screen. We suggest you write this as @apply ${applyCandidates
344
+ .map((c) => `${screenType}:${c}`)
345
+ .join(' ')} instead.`
346
+ )
347
+ }
157
348
 
349
+ throw apply.error(
350
+ `@apply is not supported within nested at-rules like @${apply.parent.name}. You can fix this by un-nesting @${apply.parent.name}.`
351
+ )
352
+ }
353
+
354
+ for (let applyCandidate of applyCandidates) {
355
+ if ([prefix(context, 'group'), prefix(context, 'peer')].includes(applyCandidate)) {
356
+ // TODO: Link to specific documentation page with error code.
357
+ throw apply.error(`@apply should not be used with the '${applyCandidate}' utility`)
358
+ }
359
+
360
+ if (!applyClassCache.has(applyCandidate)) {
158
361
  throw apply.error(
159
- `@apply is not supported within nested at-rules like @${apply.parent.name}. You can fix this by un-nesting @${apply.parent.name}.`
362
+ `The \`${applyCandidate}\` class does not exist. If \`${applyCandidate}\` is a custom class, make sure it is defined within a \`@layer\` directive.`
160
363
  )
161
364
  }
162
365
 
163
- for (let applyCandidate of applyCandidates) {
164
- if ([prefix(context, 'group'), prefix(context, 'peer')].includes(applyCandidate)) {
165
- // TODO: Link to specific documentation page with error code.
166
- throw apply.error(`@apply should not be used with the '${applyCandidate}' utility`)
167
- }
366
+ let rules = applyClassCache.get(applyCandidate)
367
+
368
+ candidates.push([applyCandidate, important, rules])
369
+ }
370
+ }
371
+
372
+ for (const [parent, [candidates, atApplySource]] of perParentApplies) {
373
+ let siblings = []
374
+
375
+ for (let [applyCandidate, important, rules] of candidates) {
376
+ let potentialApplyCandidates = [
377
+ applyCandidate,
378
+ ...extractBaseCandidates([applyCandidate], context.tailwindConfig.separator),
379
+ ]
380
+
381
+ for (let [meta, node] of rules) {
382
+ let parentClasses = extractClasses(parent)
383
+ let nodeClasses = extractClasses(node)
384
+
385
+ // When we encounter a rule like `.dark .a, .b { … }` we only want to be left with `[.dark, .a]` if the base applyCandidate is `.a` or with `[.b]` if the base applyCandidate is `.b`
386
+ // So we've split them into groups
387
+ nodeClasses = nodeClasses.groups
388
+ .filter((classList) =>
389
+ classList.some((className) => potentialApplyCandidates.includes(className))
390
+ )
391
+ .flat()
392
+
393
+ // Add base utility classes from the @apply node to the list of
394
+ // classes to check whether it intersects and therefore results in a
395
+ // circular dependency or not.
396
+ //
397
+ // E.g.:
398
+ // .foo {
399
+ // @apply hover:a; // This applies "a" but with a modifier
400
+ // }
401
+ //
402
+ // We only have to do that with base classes of the `node`, not of the `parent`
403
+ // E.g.:
404
+ // .hover\:foo {
405
+ // @apply bar;
406
+ // }
407
+ // .bar {
408
+ // @apply foo;
409
+ // }
410
+ //
411
+ // This should not result in a circular dependency because we are
412
+ // just applying `.foo` and the rule above is `.hover\:foo` which is
413
+ // unrelated. However, if we were to apply `hover:foo` then we _did_
414
+ // have to include this one.
415
+ nodeClasses = nodeClasses.concat(
416
+ extractBaseCandidates(nodeClasses, context.tailwindConfig.separator)
417
+ )
168
418
 
169
- if (!applyClassCache.has(applyCandidate)) {
170
- throw apply.error(
171
- `The \`${applyCandidate}\` class does not exist. If \`${applyCandidate}\` is a custom class, make sure it is defined within a \`@layer\` directive.`
419
+ let intersects = parentClasses.some((selector) => nodeClasses.includes(selector))
420
+ if (intersects) {
421
+ throw node.error(
422
+ `You cannot \`@apply\` the \`${applyCandidate}\` utility here because it creates a circular dependency.`
172
423
  )
173
424
  }
174
425
 
175
- let rules = applyClassCache.get(applyCandidate)
426
+ let root = postcss.root({ nodes: [node.clone()] })
176
427
 
177
- candidates.push([applyCandidate, important, rules])
178
- }
179
- }
428
+ // Make sure every node in the entire tree points back at the @apply rule that generated it
429
+ root.walk((node) => {
430
+ node.source = atApplySource
431
+ })
180
432
 
181
- for (const [parent, candidates] of perParentApplies) {
182
- let siblings = []
183
-
184
- for (let [applyCandidate, important, rules] of candidates) {
185
- for (let [meta, node] of rules) {
186
- let parentClasses = extractClasses(parent)
187
- let nodeClasses = extractClasses(node)
188
-
189
- // Add base utility classes from the @apply node to the list of
190
- // classes to check whether it intersects and therefore results in a
191
- // circular dependency or not.
192
- //
193
- // E.g.:
194
- // .foo {
195
- // @apply hover:a; // This applies "a" but with a modifier
196
- // }
197
- //
198
- // We only have to do that with base classes of the `node`, not of the `parent`
199
- // E.g.:
200
- // .hover\:foo {
201
- // @apply bar;
202
- // }
203
- // .bar {
204
- // @apply foo;
205
- // }
206
- //
207
- // This should not result in a circular dependency because we are
208
- // just applying `.foo` and the rule above is `.hover\:foo` which is
209
- // unrelated. However, if we were to apply `hover:foo` then we _did_
210
- // have to include this one.
211
- nodeClasses = nodeClasses.concat(
212
- extractBaseCandidates(nodeClasses, context.tailwindConfig.separator)
213
- )
433
+ let canRewriteSelector =
434
+ node.type !== 'atrule' || (node.type === 'atrule' && node.name !== 'keyframes')
435
+
436
+ if (canRewriteSelector) {
437
+ root.walkRules((rule) => {
438
+ // Let's imagine you have the following structure:
439
+ //
440
+ // .foo {
441
+ // @apply bar;
442
+ // }
443
+ //
444
+ // @supports (a: b) {
445
+ // .bar {
446
+ // color: blue
447
+ // }
448
+ //
449
+ // .something-unrelated {}
450
+ // }
451
+ //
452
+ // In this case we want to apply `.bar` but it happens to be in
453
+ // an atrule node. We clone that node instead of the nested one
454
+ // because we still want that @supports rule to be there once we
455
+ // applied everything.
456
+ //
457
+ // However it happens to be that the `.something-unrelated` is
458
+ // also in that same shared @supports atrule. This is not good,
459
+ // and this should not be there. The good part is that this is
460
+ // a clone already and it can be safely removed. The question is
461
+ // how do we know we can remove it. Basically what we can do is
462
+ // match it against the applyCandidate that you want to apply. If
463
+ // it doesn't match the we can safely delete it.
464
+ //
465
+ // If we didn't do this, then the `replaceSelector` function
466
+ // would have replaced this with something that didn't exist and
467
+ // therefore it removed the selector altogether. In this specific
468
+ // case it would result in `{}` instead of `.something-unrelated {}`
469
+ if (!extractClasses(rule).some((candidate) => candidate === applyCandidate)) {
470
+ rule.remove()
471
+ return
472
+ }
214
473
 
215
- let intersects = parentClasses.some((selector) => nodeClasses.includes(selector))
216
- if (intersects) {
217
- throw node.error(
218
- `You cannot \`@apply\` the \`${applyCandidate}\` utility here because it creates a circular dependency.`
219
- )
220
- }
474
+ // Strip the important selector from the parent selector if at the beginning
475
+ let importantSelector =
476
+ typeof context.tailwindConfig.important === 'string'
477
+ ? context.tailwindConfig.important
478
+ : null
221
479
 
222
- let root = postcss.root({ nodes: [node.clone()] })
223
- let canRewriteSelector =
224
- node.type !== 'atrule' || (node.type === 'atrule' && node.name !== 'keyframes')
225
-
226
- if (canRewriteSelector) {
227
- root.walkRules((rule) => {
228
- // Let's imagine you have the following structure:
229
- //
230
- // .foo {
231
- // @apply bar;
232
- // }
233
- //
234
- // @supports (a: b) {
235
- // .bar {
236
- // color: blue
237
- // }
238
- //
239
- // .something-unrelated {}
240
- // }
241
- //
242
- // In this case we want to apply `.bar` but it happens to be in
243
- // an atrule node. We clone that node instead of the nested one
244
- // because we still want that @supports rule to be there once we
245
- // applied everything.
246
- //
247
- // However it happens to be that the `.something-unrelated` is
248
- // also in that same shared @supports atrule. This is not good,
249
- // and this should not be there. The good part is that this is
250
- // a clone already and it can be safely removed. The question is
251
- // how do we know we can remove it. Basically what we can do is
252
- // match it against the applyCandidate that you want to apply. If
253
- // it doesn't match the we can safely delete it.
254
- //
255
- // If we didn't do this, then the `replaceSelector` function
256
- // would have replaced this with something that didn't exist and
257
- // therefore it removed the selector altogether. In this specific
258
- // case it would result in `{}` instead of `.something-unrelated {}`
259
- if (!extractClasses(rule).some((candidate) => candidate === applyCandidate)) {
260
- rule.remove()
261
- return
262
- }
263
-
264
- rule.selector = replaceSelector(parent.selector, rule.selector, applyCandidate)
265
-
266
- rule.walkDecls((d) => {
267
- d.important = meta.important || important
268
- })
269
- })
270
- }
480
+ // We only want to move the "important" selector if this is a Tailwind-generated utility
481
+ // We do *not* want to do this for user CSS that happens to be structured the same
482
+ let isGenerated = parent.raws.tailwind !== undefined
271
483
 
272
- // Insert it
273
- siblings.push([
274
- // Ensure that when we are sorting, that we take the layer order into account
275
- { ...meta, sort: meta.sort | context.layerOrder[meta.layer] },
276
- root.nodes[0],
277
- ])
278
- }
279
- }
484
+ let parentSelector =
485
+ isGenerated && importantSelector && parent.selector.indexOf(importantSelector) === 0
486
+ ? parent.selector.slice(importantSelector.length)
487
+ : parent.selector
280
488
 
281
- // Inject the rules, sorted, correctly
282
- let nodes = siblings.sort(([a], [z]) => bigSign(a.sort - z.sort)).map((s) => s[1])
489
+ rule.selector = replaceSelector(parentSelector, rule.selector, applyCandidate)
283
490
 
284
- // `parent` refers to the node at `.abc` in: .abc { @apply mt-2 }
285
- parent.after(nodes)
286
- }
491
+ // And then re-add it if it was removed
492
+ if (importantSelector && parentSelector !== parent.selector) {
493
+ rule.selector = `${importantSelector} ${rule.selector}`
494
+ }
495
+
496
+ rule.walkDecls((d) => {
497
+ d.important = meta.important || important
498
+ })
499
+ })
500
+ }
287
501
 
288
- for (let apply of applies) {
289
- // If there are left-over declarations, just remove the @apply
290
- if (apply.parent.nodes.length > 1) {
291
- apply.remove()
292
- } else {
293
- // The node is empty, drop the full node
294
- apply.parent.remove()
502
+ // Insert it
503
+ siblings.push([
504
+ // Ensure that when we are sorting, that we take the layer order into account
505
+ { ...meta, sort: meta.sort | context.layerOrder[meta.layer] },
506
+ root.nodes[0],
507
+ ])
295
508
  }
296
509
  }
297
510
 
298
- // Do it again, in case we have other `@apply` rules
299
- processApply(root, context)
511
+ // Inject the rules, sorted, correctly
512
+ let nodes = siblings.sort(([a], [z]) => bigSign(a.sort - z.sort)).map((s) => s[1])
513
+
514
+ // `parent` refers to the node at `.abc` in: .abc { @apply mt-2 }
515
+ parent.after(nodes)
300
516
  }
517
+
518
+ for (let apply of applies) {
519
+ // If there are left-over declarations, just remove the @apply
520
+ if (apply.parent.nodes.length > 1) {
521
+ apply.remove()
522
+ } else {
523
+ // The node is empty, drop the full node
524
+ apply.parent.remove()
525
+ }
526
+ }
527
+
528
+ // Do it again, in case we have other `@apply` rules
529
+ processApply(root, context, localCache)
301
530
  }
302
531
 
303
532
  export default function expandApplyAtRules(context) {
304
533
  return (root) => {
305
- processApply(root, context)
534
+ // Build a cache of the user's CSS so we can use it to resolve classes used by @apply
535
+ let localCache = lazyCache(() => buildLocalApplyCache(root, context))
536
+
537
+ processApply(root, context, localCache)
306
538
  }
307
539
  }