tailwindcss 3.0.23 → 3.1.1

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 +1640 -1562
  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 -32
  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 +177 -95
  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 +96 -81
  27. package/lib/lib/setupContextUtils.js +202 -159
  28. package/lib/lib/setupTrackingContext.js +61 -63
  29. package/lib/lib/sharedState.js +10 -9
  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 +7702 -5822
  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 +109 -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 -35
  87. package/src/lib/evaluateTailwindFunctions.js +20 -4
  88. package/src/lib/expandApplyAtRules.js +418 -186
  89. package/src/lib/expandTailwindAtRules.js +29 -9
  90. package/src/lib/generateRules.js +137 -51
  91. package/src/lib/regex.js +74 -0
  92. package/src/lib/resolveDefaultsAtRules.js +53 -32
  93. package/src/lib/setupContextUtils.js +128 -65
  94. package/src/lib/setupTrackingContext.js +7 -3
  95. package/src/lib/sharedState.js +1 -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 +7 -0
@@ -17,14 +17,14 @@ const builtInTransformers = {
17
17
  svelte: (content) => content.replace(/(?:^|\s)class:/g, ' '),
18
18
  }
19
19
 
20
- function getExtractor(tailwindConfig, fileExtension) {
21
- let extractors = tailwindConfig.content.extract
20
+ function getExtractor(context, fileExtension) {
21
+ let extractors = context.tailwindConfig.content.extract
22
22
 
23
23
  return (
24
24
  extractors[fileExtension] ||
25
25
  extractors.DEFAULT ||
26
26
  builtInExtractors[fileExtension] ||
27
- builtInExtractors.DEFAULT
27
+ builtInExtractors.DEFAULT(context)
28
28
  )
29
29
  }
30
30
 
@@ -165,7 +165,7 @@ export default function expandTailwindAtRules(context) {
165
165
 
166
166
  for (let { content, extension } of context.changedContent) {
167
167
  let transformer = getTransformer(context.tailwindConfig, extension)
168
- let extractor = getExtractor(context.tailwindConfig, extension)
168
+ let extractor = getExtractor(context, extension)
169
169
  getClassCandidates(transformer(content), extractor, candidates, seen)
170
170
  }
171
171
 
@@ -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
@@ -9,7 +9,10 @@ import * as sharedState from './sharedState'
9
9
  import { formatVariantSelector, finalizeSelector } from '../util/formatVariantSelector'
10
10
  import { asClass } from '../util/nameClass'
11
11
  import { normalize } from '../util/dataTypes'
12
+ import { isValidVariantFormatString, parseVariant } from './setupContextUtils'
12
13
  import isValidArbitraryValue from '../util/isValidArbitraryValue'
14
+ import { splitAtTopLevelOnly } from '../util/splitAtTopLevelOnly.js'
15
+ import { flagEnabled } from '../featureFlags'
13
16
 
14
17
  let classNameParser = selectorParser((selectors) => {
15
18
  return selectors.first.filter(({ type }) => type === 'class').pop().value
@@ -88,7 +91,7 @@ function applyPrefix(matches, context) {
88
91
  return matches
89
92
  }
90
93
 
91
- function applyImportant(matches) {
94
+ function applyImportant(matches, classCandidate) {
92
95
  if (matches.length === 0) {
93
96
  return matches
94
97
  }
@@ -98,7 +101,10 @@ function applyImportant(matches) {
98
101
  let container = postcss.root({ nodes: [rule.clone()] })
99
102
  container.walkRules((r) => {
100
103
  r.selector = updateAllClasses(r.selector, (className) => {
101
- return `!${className}`
104
+ if (className === classCandidate) {
105
+ return `!${className}`
106
+ }
107
+ return className
102
108
  })
103
109
  r.walkDecls((d) => (d.important = true))
104
110
  })
@@ -122,8 +128,31 @@ function applyVariant(variant, matches, context) {
122
128
  return matches
123
129
  }
124
130
 
131
+ let args
132
+
133
+ // Find partial arbitrary variants
134
+ if (variant.endsWith(']') && !variant.startsWith('[')) {
135
+ args = variant.slice(variant.lastIndexOf('[') + 1, -1)
136
+ variant = variant.slice(0, variant.indexOf(args) - 1 /* - */ - 1 /* [ */)
137
+ }
138
+
139
+ // Register arbitrary variants
140
+ if (isArbitraryValue(variant) && !context.variantMap.has(variant)) {
141
+ let selector = normalize(variant.slice(1, -1))
142
+
143
+ if (!isValidVariantFormatString(selector)) {
144
+ return []
145
+ }
146
+
147
+ let fn = parseVariant(selector)
148
+
149
+ let sort = Array.from(context.variantOrder.values()).pop() << 1n
150
+ context.variantMap.set(variant, [[sort, fn]])
151
+ context.variantOrder.set(variant, sort)
152
+ }
153
+
125
154
  if (context.variantMap.has(variant)) {
126
- let variantFunctionTuples = context.variantMap.get(variant)
155
+ let variantFunctionTuples = context.variantMap.get(variant).slice()
127
156
  let result = []
128
157
 
129
158
  for (let [meta, rule] of matches) {
@@ -184,8 +213,29 @@ function applyVariant(variant, matches, context) {
184
213
  format(selectorFormat) {
185
214
  collectedFormats.push(selectorFormat)
186
215
  },
216
+ args,
187
217
  })
188
218
 
219
+ // It can happen that a list of format strings is returned from within the function. In that
220
+ // case, we have to process them as well. We can use the existing `variantSort`.
221
+ if (Array.isArray(ruleWithVariant)) {
222
+ for (let [idx, variantFunction] of ruleWithVariant.entries()) {
223
+ // This is a little bit scary since we are pushing to an array of items that we are
224
+ // currently looping over. However, you can also think of it like a processing queue
225
+ // where you keep handling jobs until everything is done and each job can queue more
226
+ // jobs if needed.
227
+ variantFunctionTuples.push([
228
+ // TODO: This could have potential bugs if we shift the sort order from variant A far
229
+ // enough into the sort space of variant B. The chances are low, but if this happens
230
+ // then this might be the place too look at. One potential solution to this problem is
231
+ // reserving additional X places for these 'unknown' variants in between.
232
+ variantSort | BigInt(idx << ruleWithVariant.length),
233
+ variantFunction,
234
+ ])
235
+ }
236
+ continue
237
+ }
238
+
189
239
  if (typeof ruleWithVariant === 'string') {
190
240
  collectedFormats.push(ruleWithVariant)
191
241
  }
@@ -300,6 +350,19 @@ function looksLikeUri(declaration) {
300
350
  }
301
351
  }
302
352
 
353
+ function isParsableNode(node) {
354
+ let isParsable = true
355
+
356
+ node.walkDecls((decl) => {
357
+ if (!isParsableCssValue(decl.name, decl.value)) {
358
+ isParsable = false
359
+ return false
360
+ }
361
+ })
362
+
363
+ return isParsable
364
+ }
365
+
303
366
  function isParsableCssValue(property, value) {
304
367
  // We don't want to to treat [https://example.com] as a custom property
305
368
  // Even though, according to the CSS grammar, it's a totally valid CSS declaration
@@ -366,7 +429,11 @@ function* resolveMatchedPlugins(classCandidate, context) {
366
429
  const twConfigPrefix = context.tailwindConfig.prefix
367
430
 
368
431
  const twConfigPrefixLen = twConfigPrefix.length
369
- if (candidatePrefix[twConfigPrefixLen] === '-') {
432
+
433
+ const hasMatchingPrefix =
434
+ candidatePrefix.startsWith(twConfigPrefix) || candidatePrefix.startsWith(`-${twConfigPrefix}`)
435
+
436
+ if (candidatePrefix[twConfigPrefixLen] === '-' && hasMatchingPrefix) {
370
437
  negative = true
371
438
  candidatePrefix = twConfigPrefix + candidatePrefix.slice(twConfigPrefixLen + 1)
372
439
  }
@@ -387,7 +454,7 @@ function splitWithSeparator(input, separator) {
387
454
  return [sharedState.NOT_ON_DEMAND]
388
455
  }
389
456
 
390
- return input.split(new RegExp(`\\${separator}(?![^[]*\\])`, 'g'))
457
+ return Array.from(splitAtTopLevelOnly(input, separator))
391
458
  }
392
459
 
393
460
  function* recordCandidates(matches, classCandidate) {
@@ -398,7 +465,7 @@ function* recordCandidates(matches, classCandidate) {
398
465
  }
399
466
  }
400
467
 
401
- function* resolveMatches(candidate, context) {
468
+ function* resolveMatches(candidate, context, original = candidate) {
402
469
  let separator = context.tailwindConfig.separator
403
470
  let [classCandidate, ...variants] = splitWithSeparator(candidate, separator).reverse()
404
471
  let important = false
@@ -408,6 +475,15 @@ function* resolveMatches(candidate, context) {
408
475
  classCandidate = classCandidate.slice(1)
409
476
  }
410
477
 
478
+ if (flagEnabled(context.tailwindConfig, 'variantGrouping')) {
479
+ if (classCandidate.startsWith('(') && classCandidate.endsWith(')')) {
480
+ let base = variants.slice().reverse().join(separator)
481
+ for (let part of splitAtTopLevelOnly(classCandidate.slice(1, -1), ',')) {
482
+ yield* resolveMatches(base + separator + part, context, original)
483
+ }
484
+ }
485
+ }
486
+
411
487
  // TODO: Reintroduce this in ways that doesn't break on false positives
412
488
  // function sortAgainst(toSort, against) {
413
489
  // return toSort.slice().sort((a, z) => {
@@ -453,60 +529,66 @@ function* resolveMatches(candidate, context) {
453
529
  }
454
530
  }
455
531
 
456
- // Only keep the result of the very first plugin if we are dealing with
457
- // arbitrary values, to protect against ambiguity.
458
- if (isArbitraryValue(modifier) && matches.length > 1) {
459
- let typesPerPlugin = matches.map((match) => new Set([...(typesByMatches.get(match) ?? [])]))
532
+ if (isArbitraryValue(modifier)) {
533
+ // When generated arbitrary values are ambiguous, we can't know
534
+ // which to pick so don't generate any utilities for them
535
+ if (matches.length > 1) {
536
+ let typesPerPlugin = matches.map((match) => new Set([...(typesByMatches.get(match) ?? [])]))
460
537
 
461
- // Remove duplicates, so that we can detect proper unique types for each plugin.
462
- for (let pluginTypes of typesPerPlugin) {
463
- for (let type of pluginTypes) {
464
- let removeFromOwnGroup = false
538
+ // Remove duplicates, so that we can detect proper unique types for each plugin.
539
+ for (let pluginTypes of typesPerPlugin) {
540
+ for (let type of pluginTypes) {
541
+ let removeFromOwnGroup = false
465
542
 
466
- for (let otherGroup of typesPerPlugin) {
467
- if (pluginTypes === otherGroup) continue
543
+ for (let otherGroup of typesPerPlugin) {
544
+ if (pluginTypes === otherGroup) continue
468
545
 
469
- if (otherGroup.has(type)) {
470
- otherGroup.delete(type)
471
- removeFromOwnGroup = true
546
+ if (otherGroup.has(type)) {
547
+ otherGroup.delete(type)
548
+ removeFromOwnGroup = true
549
+ }
472
550
  }
473
- }
474
551
 
475
- if (removeFromOwnGroup) pluginTypes.delete(type)
552
+ if (removeFromOwnGroup) pluginTypes.delete(type)
553
+ }
476
554
  }
477
- }
478
555
 
479
- let messages = []
480
-
481
- for (let [idx, group] of typesPerPlugin.entries()) {
482
- for (let type of group) {
483
- let rules = matches[idx]
484
- .map(([, rule]) => rule)
485
- .flat()
486
- .map((rule) =>
487
- rule
488
- .toString()
489
- .split('\n')
490
- .slice(1, -1) // Remove selector and closing '}'
491
- .map((line) => line.trim())
492
- .map((x) => ` ${x}`) // Re-indent
493
- .join('\n')
556
+ let messages = []
557
+
558
+ for (let [idx, group] of typesPerPlugin.entries()) {
559
+ for (let type of group) {
560
+ let rules = matches[idx]
561
+ .map(([, rule]) => rule)
562
+ .flat()
563
+ .map((rule) =>
564
+ rule
565
+ .toString()
566
+ .split('\n')
567
+ .slice(1, -1) // Remove selector and closing '}'
568
+ .map((line) => line.trim())
569
+ .map((x) => ` ${x}`) // Re-indent
570
+ .join('\n')
571
+ )
572
+ .join('\n\n')
573
+
574
+ messages.push(
575
+ ` Use \`${candidate.replace('[', `[${type}:`)}\` for \`${rules.trim()}\``
494
576
  )
495
- .join('\n\n')
496
-
497
- messages.push(` Use \`${candidate.replace('[', `[${type}:`)}\` for \`${rules.trim()}\``)
498
- break
577
+ break
578
+ }
499
579
  }
580
+
581
+ log.warn([
582
+ `The class \`${candidate}\` is ambiguous and matches multiple utilities.`,
583
+ ...messages,
584
+ `If this is content and not a class, replace it with \`${candidate
585
+ .replace('[', '&lsqb;')
586
+ .replace(']', '&rsqb;')}\` to silence this warning.`,
587
+ ])
588
+ continue
500
589
  }
501
590
 
502
- log.warn([
503
- `The class \`${candidate}\` is ambiguous and matches multiple utilities.`,
504
- ...messages,
505
- `If this is content and not a class, replace it with \`${candidate
506
- .replace('[', '&lsqb;')
507
- .replace(']', '&rsqb;')}\` to silence this warning.`,
508
- ])
509
- continue
591
+ matches = matches.map((list) => list.filter((match) => isParsableNode(match[1])))
510
592
  }
511
593
 
512
594
  matches = matches.flat()
@@ -514,7 +596,7 @@ function* resolveMatches(candidate, context) {
514
596
  matches = applyPrefix(matches, context)
515
597
 
516
598
  if (important) {
517
- matches = applyImportant(matches, context)
599
+ matches = applyImportant(matches, classCandidate)
518
600
  }
519
601
 
520
602
  for (let variant of variants) {
@@ -533,7 +615,11 @@ function* resolveMatches(candidate, context) {
533
615
 
534
616
  rule.selector = finalizeSelector(finalFormat, {
535
617
  selector: rule.selector,
536
- candidate,
618
+ candidate: original,
619
+ base: candidate
620
+ .split(new RegExp(`\\${context?.tailwindConfig?.separator ?? ':'}(?![^[]*\\])`))
621
+ .pop(),
622
+
537
623
  context,
538
624
  })
539
625
  })
@@ -0,0 +1,74 @@
1
+ const REGEX_SPECIAL = /[\\^$.*+?()[\]{}|]/g
2
+ const REGEX_HAS_SPECIAL = RegExp(REGEX_SPECIAL.source)
3
+
4
+ /**
5
+ * @param {string|RegExp|Array<string|RegExp>} source
6
+ */
7
+ function toSource(source) {
8
+ source = Array.isArray(source) ? source : [source]
9
+
10
+ source = source.map((item) => (item instanceof RegExp ? item.source : item))
11
+
12
+ return source.join('')
13
+ }
14
+
15
+ /**
16
+ * @param {string|RegExp|Array<string|RegExp>} source
17
+ */
18
+ export function pattern(source) {
19
+ return new RegExp(toSource(source), 'g')
20
+ }
21
+
22
+ /**
23
+ * @param {string|RegExp|Array<string|RegExp>} source
24
+ */
25
+ export function withoutCapturing(source) {
26
+ return new RegExp(`(?:${toSource(source)})`, 'g')
27
+ }
28
+
29
+ /**
30
+ * @param {Array<string|RegExp>} sources
31
+ */
32
+ export function any(sources) {
33
+ return `(?:${sources.map(toSource).join('|')})`
34
+ }
35
+
36
+ /**
37
+ * @param {string|RegExp} source
38
+ */
39
+ export function optional(source) {
40
+ return `(?:${toSource(source)})?`
41
+ }
42
+
43
+ /**
44
+ * @param {string|RegExp|Array<string|RegExp>} source
45
+ */
46
+ export function zeroOrMore(source) {
47
+ return `(?:${toSource(source)})*`
48
+ }
49
+
50
+ /**
51
+ * Generate a RegExp that matches balanced brackets for a given depth
52
+ * We have to specify a depth because JS doesn't support recursive groups using ?R
53
+ *
54
+ * Based on https://stackoverflow.com/questions/17759004/how-to-match-string-within-parentheses-nested-in-java/17759264#17759264
55
+ *
56
+ * @param {string|RegExp|Array<string|RegExp>} source
57
+ */
58
+ export function nestedBrackets(open, close, depth = 1) {
59
+ return withoutCapturing([
60
+ escape(open),
61
+ /[^\s]*/,
62
+ depth === 1
63
+ ? `[^${escape(open)}${escape(close)}\s]*`
64
+ : any([`[^${escape(open)}${escape(close)}\s]*`, nestedBrackets(open, close, depth - 1)]),
65
+ /[^\s]*/,
66
+ escape(close),
67
+ ])
68
+ }
69
+
70
+ export function escape(string) {
71
+ return string && REGEX_HAS_SPECIAL.test(string)
72
+ ? string.replace(REGEX_SPECIAL, '\\$&')
73
+ : string || ''
74
+ }
@@ -91,52 +91,73 @@ export default function resolveDefaultsAtRules({ tailwindConfig }) {
91
91
  rule.remove()
92
92
  })
93
93
 
94
- for (let universal of universals) {
95
- /** @type {Map<string, Set<string>>} */
96
- let selectorGroups = new Map()
94
+ if (flagEnabled(tailwindConfig, 'optimizeUniversalDefaults')) {
95
+ for (let universal of universals) {
96
+ /** @type {Map<string, Set<string>>} */
97
+ let selectorGroups = new Map()
98
+
99
+ let rules = variableNodeMap.get(universal.params) ?? []
100
+
101
+ for (let rule of rules) {
102
+ for (let selector of extractElementSelector(rule.selector)) {
103
+ // If selector contains a vendor prefix after a pseudo element or class,
104
+ // we consider them separately because merging the declarations into
105
+ // a single rule will cause browsers that do not understand the
106
+ // vendor prefix to throw out the whole rule
107
+ let selectorGroupName =
108
+ selector.includes(':-') || selector.includes('::-') ? selector : '__DEFAULT__'
109
+
110
+ let selectors = selectorGroups.get(selectorGroupName) ?? new Set()
111
+ selectorGroups.set(selectorGroupName, selectors)
112
+
113
+ selectors.add(selector)
114
+ }
115
+ }
97
116
 
98
- let rules = variableNodeMap.get(universal.params) ?? []
117
+ if (flagEnabled(tailwindConfig, 'optimizeUniversalDefaults')) {
118
+ if (selectorGroups.size === 0) {
119
+ universal.remove()
120
+ continue
121
+ }
99
122
 
100
- for (let rule of rules) {
101
- for (let selector of extractElementSelector(rule.selector)) {
102
- // If selector contains a vendor prefix after a pseudo element or class,
103
- // we consider them separately because merging the declarations into
104
- // a single rule will cause browsers that do not understand the
105
- // vendor prefix to throw out the whole rule
106
- let selectorGroupName =
107
- selector.includes(':-') || selector.includes('::-') ? selector : '__DEFAULT__'
123
+ for (let [, selectors] of selectorGroups) {
124
+ let universalRule = postcss.rule({
125
+ source: universal.source,
126
+ })
108
127
 
109
- let selectors = selectorGroups.get(selectorGroupName) ?? new Set()
110
- selectorGroups.set(selectorGroupName, selectors)
128
+ universalRule.selectors = [...selectors]
111
129
 
112
- selectors.add(selector)
130
+ universalRule.append(universal.nodes.map((node) => node.clone()))
131
+ universal.before(universalRule)
132
+ }
113
133
  }
114
- }
115
134
 
116
- if (flagEnabled(tailwindConfig, 'optimizeUniversalDefaults')) {
117
- if (selectorGroups.size === 0) {
118
- universal.remove()
119
- continue
120
- }
121
-
122
- for (let [, selectors] of selectorGroups) {
123
- let universalRule = postcss.rule()
135
+ universal.remove()
136
+ }
137
+ } else if (universals.size) {
138
+ let universalRule = postcss.rule({
139
+ selectors: ['*', '::before', '::after'],
140
+ })
124
141
 
125
- universalRule.selectors = [...selectors]
142
+ for (let universal of universals) {
143
+ universalRule.append(universal.nodes)
126
144
 
127
- universalRule.append(universal.nodes.map((node) => node.clone()))
145
+ if (!universalRule.parent) {
128
146
  universal.before(universalRule)
129
147
  }
130
- } else {
131
- let universalRule = postcss.rule()
132
148
 
133
- universalRule.selectors = ['*', '::before', '::after']
149
+ if (!universalRule.source) {
150
+ universalRule.source = universal.source
151
+ }
134
152
 
135
- universalRule.append(universal.nodes)
136
- universal.before(universalRule)
153
+ universal.remove()
137
154
  }
138
155
 
139
- universal.remove()
156
+ let backdropRule = universalRule.clone({
157
+ selectors: ['::backdrop'],
158
+ })
159
+
160
+ universalRule.after(backdropRule)
140
161
  }
141
162
  }
142
163
  }