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
@@ -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
 
@@ -158,14 +158,14 @@ 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')
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
@@ -5,10 +5,14 @@ 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'
12
+ import { isValidVariantFormatString, parseVariant } from './setupContextUtils'
11
13
  import isValidArbitraryValue from '../util/isValidArbitraryValue'
14
+ import { splitAtTopLevelOnly } from '../util/splitAtTopLevelOnly.js'
15
+ import { flagEnabled } from '../featureFlags'
12
16
 
13
17
  let classNameParser = selectorParser((selectors) => {
14
18
  return selectors.first.filter(({ type }) => type === 'class').pop().value
@@ -87,7 +91,7 @@ function applyPrefix(matches, context) {
87
91
  return matches
88
92
  }
89
93
 
90
- function applyImportant(matches) {
94
+ function applyImportant(matches, classCandidate) {
91
95
  if (matches.length === 0) {
92
96
  return matches
93
97
  }
@@ -97,7 +101,10 @@ function applyImportant(matches) {
97
101
  let container = postcss.root({ nodes: [rule.clone()] })
98
102
  container.walkRules((r) => {
99
103
  r.selector = updateAllClasses(r.selector, (className) => {
100
- return `!${className}`
104
+ if (className === classCandidate) {
105
+ return `!${className}`
106
+ }
107
+ return className
101
108
  })
102
109
  r.walkDecls((d) => (d.important = true))
103
110
  })
@@ -121,8 +128,31 @@ function applyVariant(variant, matches, context) {
121
128
  return matches
122
129
  }
123
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
+
124
154
  if (context.variantMap.has(variant)) {
125
- let variantFunctionTuples = context.variantMap.get(variant)
155
+ let variantFunctionTuples = context.variantMap.get(variant).slice()
126
156
  let result = []
127
157
 
128
158
  for (let [meta, rule] of matches) {
@@ -183,8 +213,29 @@ function applyVariant(variant, matches, context) {
183
213
  format(selectorFormat) {
184
214
  collectedFormats.push(selectorFormat)
185
215
  },
216
+ args,
186
217
  })
187
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
+
188
239
  if (typeof ruleWithVariant === 'string') {
189
240
  collectedFormats.push(ruleWithVariant)
190
241
  }
@@ -299,6 +350,19 @@ function looksLikeUri(declaration) {
299
350
  }
300
351
  }
301
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
+
302
366
  function isParsableCssValue(property, value) {
303
367
  // We don't want to to treat [https://example.com] as a custom property
304
368
  // Even though, according to the CSS grammar, it's a totally valid CSS declaration
@@ -365,7 +429,11 @@ function* resolveMatchedPlugins(classCandidate, context) {
365
429
  const twConfigPrefix = context.tailwindConfig.prefix
366
430
 
367
431
  const twConfigPrefixLen = twConfigPrefix.length
368
- if (candidatePrefix[twConfigPrefixLen] === '-') {
432
+
433
+ const hasMatchingPrefix =
434
+ candidatePrefix.startsWith(twConfigPrefix) || candidatePrefix.startsWith(`-${twConfigPrefix}`)
435
+
436
+ if (candidatePrefix[twConfigPrefixLen] === '-' && hasMatchingPrefix) {
369
437
  negative = true
370
438
  candidatePrefix = twConfigPrefix + candidatePrefix.slice(twConfigPrefixLen + 1)
371
439
  }
@@ -382,7 +450,11 @@ function* resolveMatchedPlugins(classCandidate, context) {
382
450
  }
383
451
 
384
452
  function splitWithSeparator(input, separator) {
385
- return input.split(new RegExp(`\\${separator}(?![^[]*\\])`, 'g'))
453
+ if (input === sharedState.NOT_ON_DEMAND) {
454
+ return [sharedState.NOT_ON_DEMAND]
455
+ }
456
+
457
+ return Array.from(splitAtTopLevelOnly(input, separator))
386
458
  }
387
459
 
388
460
  function* recordCandidates(matches, classCandidate) {
@@ -393,7 +465,7 @@ function* recordCandidates(matches, classCandidate) {
393
465
  }
394
466
  }
395
467
 
396
- function* resolveMatches(candidate, context) {
468
+ function* resolveMatches(candidate, context, original = candidate) {
397
469
  let separator = context.tailwindConfig.separator
398
470
  let [classCandidate, ...variants] = splitWithSeparator(candidate, separator).reverse()
399
471
  let important = false
@@ -403,6 +475,15 @@ function* resolveMatches(candidate, context) {
403
475
  classCandidate = classCandidate.slice(1)
404
476
  }
405
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
+
406
487
  // TODO: Reintroduce this in ways that doesn't break on false positives
407
488
  // function sortAgainst(toSort, against) {
408
489
  // return toSort.slice().sort((a, z) => {
@@ -448,60 +529,66 @@ function* resolveMatches(candidate, context) {
448
529
  }
449
530
  }
450
531
 
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) ?? [])]))
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) ?? [])]))
455
537
 
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
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
460
542
 
461
- for (let otherGroup of typesPerPlugin) {
462
- if (pluginTypes === otherGroup) continue
543
+ for (let otherGroup of typesPerPlugin) {
544
+ if (pluginTypes === otherGroup) continue
463
545
 
464
- if (otherGroup.has(type)) {
465
- otherGroup.delete(type)
466
- removeFromOwnGroup = true
546
+ if (otherGroup.has(type)) {
547
+ otherGroup.delete(type)
548
+ removeFromOwnGroup = true
549
+ }
467
550
  }
468
- }
469
551
 
470
- if (removeFromOwnGroup) pluginTypes.delete(type)
552
+ if (removeFromOwnGroup) pluginTypes.delete(type)
553
+ }
471
554
  }
472
- }
473
555
 
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')
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()}\``
489
576
  )
490
- .join('\n\n')
491
-
492
- messages.push(` Use \`${candidate.replace('[', `[${type}:`)}\` for \`${rules.trim()}\``)
493
- break
577
+ break
578
+ }
494
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
495
589
  }
496
590
 
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
591
+ matches = matches.map((list) => list.filter((match) => isParsableNode(match[1])))
505
592
  }
506
593
 
507
594
  matches = matches.flat()
@@ -509,7 +596,7 @@ function* resolveMatches(candidate, context) {
509
596
  matches = applyPrefix(matches, context)
510
597
 
511
598
  if (important) {
512
- matches = applyImportant(matches, context)
599
+ matches = applyImportant(matches, classCandidate)
513
600
  }
514
601
 
515
602
  for (let variant of variants) {
@@ -528,7 +615,11 @@ function* resolveMatches(candidate, context) {
528
615
 
529
616
  rule.selector = finalizeSelector(finalFormat, {
530
617
  selector: rule.selector,
531
- candidate,
618
+ candidate: original,
619
+ base: candidate
620
+ .split(new RegExp(`\\${context?.tailwindConfig?.separator ?? ':'}(?![^[]*\\])`))
621
+ .pop(),
622
+
532
623
  context,
533
624
  })
534
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
+ }
@@ -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,9 +130,11 @@ 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
- universalRule.selectors = ['*', '::before', '::after']
137
+ universalRule.selectors = ['*', '::before', '::after', '::backdrop']
134
138
 
135
139
  universalRule.append(universal.nodes)
136
140
  universal.before(universalRule)