tailwindcss 0.0.0-insiders.dddaded → 0.0.0-insiders.de00a62

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 (160) hide show
  1. package/lib/cli/build/deps.js +17 -9
  2. package/lib/cli/build/index.js +10 -8
  3. package/lib/cli/build/plugin.js +31 -29
  4. package/lib/cli/build/utils.js +18 -8
  5. package/lib/cli/build/watching.js +13 -11
  6. package/lib/cli/help/index.js +6 -4
  7. package/lib/cli/index.js +4 -4
  8. package/lib/cli/init/index.js +8 -6
  9. package/lib/cli-peer-dependencies.js +12 -4
  10. package/lib/cli.js +1 -1
  11. package/lib/corePluginList.js +8 -2
  12. package/lib/corePlugins.js +211 -48
  13. package/lib/css/preflight.css +24 -8
  14. package/lib/featureFlags.js +14 -9
  15. package/lib/index.js +1 -5
  16. package/lib/lib/cacheInvalidation.js +7 -5
  17. package/lib/lib/collapseAdjacentRules.js +3 -1
  18. package/lib/lib/collapseDuplicateDeclarations.js +10 -8
  19. package/lib/lib/content.js +51 -14
  20. package/lib/lib/defaultExtractor.js +38 -28
  21. package/lib/lib/detectNesting.js +3 -1
  22. package/lib/lib/evaluateTailwindFunctions.js +20 -16
  23. package/lib/lib/expandApplyAtRules.js +41 -33
  24. package/lib/lib/expandTailwindAtRules.js +38 -19
  25. package/lib/lib/findAtConfigPath.js +6 -4
  26. package/lib/lib/generateRules.js +83 -55
  27. package/lib/lib/getModuleDependencies.js +10 -8
  28. package/lib/lib/load-config.js +18 -5
  29. package/lib/lib/normalizeTailwindDirectives.js +5 -3
  30. package/lib/lib/offsets.js +58 -7
  31. package/lib/lib/partitionApplyAtRules.js +3 -1
  32. package/lib/lib/regex.js +21 -7
  33. package/lib/lib/remap-bitfield.js +3 -1
  34. package/lib/lib/resolveDefaultsAtRules.js +27 -21
  35. package/lib/lib/setupContextUtils.js +143 -94
  36. package/lib/lib/setupTrackingContext.js +27 -24
  37. package/lib/lib/sharedState.js +28 -24
  38. package/lib/lib/substituteScreenAtRules.js +5 -3
  39. package/lib/oxide/cli/build/deps.js +16 -8
  40. package/lib/oxide/cli/build/index.js +6 -4
  41. package/lib/oxide/cli/build/plugin.js +25 -23
  42. package/lib/oxide/cli/build/utils.js +18 -8
  43. package/lib/oxide/cli/build/watching.js +10 -8
  44. package/lib/oxide/cli/help/index.js +6 -4
  45. package/lib/oxide/cli/index.js +4 -4
  46. package/lib/oxide/cli/init/index.js +6 -4
  47. package/lib/plugin.js +9 -9
  48. package/lib/postcss-plugins/nesting/index.js +3 -1
  49. package/lib/postcss-plugins/nesting/plugin.js +8 -6
  50. package/lib/processTailwindFeatures.js +16 -14
  51. package/lib/public/colors.js +5 -3
  52. package/lib/public/create-plugin.js +5 -3
  53. package/lib/public/default-config.js +6 -4
  54. package/lib/public/default-theme.js +6 -4
  55. package/lib/public/load-config.js +5 -3
  56. package/lib/public/resolve-config.js +6 -4
  57. package/lib/util/applyImportantSelector.js +7 -5
  58. package/lib/util/bigSign.js +3 -1
  59. package/lib/util/buildMediaQuery.js +3 -1
  60. package/lib/util/cloneDeep.js +3 -1
  61. package/lib/util/cloneNodes.js +36 -14
  62. package/lib/util/color.js +11 -7
  63. package/lib/util/colorNames.js +752 -0
  64. package/lib/util/configurePlugins.js +3 -1
  65. package/lib/util/createPlugin.js +3 -1
  66. package/lib/util/createUtilityPlugin.js +5 -3
  67. package/lib/util/dataTypes.js +180 -31
  68. package/lib/util/defaults.js +6 -4
  69. package/lib/util/escapeClassName.js +7 -5
  70. package/lib/util/escapeCommas.js +3 -1
  71. package/lib/util/flattenColorPalette.js +3 -1
  72. package/lib/util/formatVariantSelector.js +36 -21
  73. package/lib/util/getAllConfigs.js +6 -4
  74. package/lib/util/hashConfig.js +6 -4
  75. package/lib/util/isKeyframeRule.js +3 -1
  76. package/lib/util/isPlainObject.js +4 -2
  77. package/lib/util/isSyntacticallyValidPropertyValue.js +3 -1
  78. package/lib/util/log.js +8 -4
  79. package/lib/util/nameClass.js +12 -6
  80. package/lib/util/negateValue.js +3 -1
  81. package/lib/util/normalizeConfig.js +7 -5
  82. package/lib/util/normalizeScreens.js +12 -4
  83. package/lib/util/parseAnimationValue.js +3 -1
  84. package/lib/util/parseBoxShadowValue.js +6 -2
  85. package/lib/util/parseDependency.js +3 -1
  86. package/lib/util/parseGlob.js +6 -4
  87. package/lib/util/parseObjectStyles.js +9 -7
  88. package/lib/util/pluginUtils.js +43 -18
  89. package/lib/util/prefixSelector.js +6 -4
  90. package/lib/util/pseudoElements.js +24 -35
  91. package/lib/util/removeAlphaVariables.js +3 -1
  92. package/lib/util/resolveConfig.js +10 -8
  93. package/lib/util/resolveConfigPath.js +9 -5
  94. package/lib/util/responsive.js +6 -4
  95. package/lib/util/splitAtTopLevelOnly.js +3 -1
  96. package/lib/util/tap.js +3 -1
  97. package/lib/util/toColorValue.js +3 -1
  98. package/lib/util/toPath.js +3 -1
  99. package/lib/util/transformThemeValue.js +6 -4
  100. package/lib/util/validateConfig.js +5 -3
  101. package/lib/util/validateFormalSyntax.js +3 -1
  102. package/lib/util/withAlphaVariable.js +6 -2
  103. package/lib/value-parser/LICENSE +22 -0
  104. package/lib/value-parser/README.md +3 -0
  105. package/lib/value-parser/index.d.js +2 -0
  106. package/lib/value-parser/index.js +22 -0
  107. package/lib/value-parser/parse.js +259 -0
  108. package/lib/value-parser/stringify.js +38 -0
  109. package/lib/value-parser/unit.js +86 -0
  110. package/lib/value-parser/walk.js +16 -0
  111. package/nesting/index.d.ts +4 -0
  112. package/package.json +31 -36
  113. package/peers/index.js +65266 -48224
  114. package/resolveConfig.d.ts +22 -3
  115. package/scripts/generate-types.js +1 -2
  116. package/src/cli/build/plugin.js +7 -7
  117. package/src/cli/build/watching.js +1 -1
  118. package/src/cli.js +1 -1
  119. package/src/corePluginList.js +1 -1
  120. package/src/corePlugins.js +165 -29
  121. package/src/css/preflight.css +24 -8
  122. package/src/featureFlags.js +3 -8
  123. package/src/index.js +1 -5
  124. package/src/lib/content.js +42 -1
  125. package/src/lib/defaultExtractor.js +30 -17
  126. package/src/lib/evaluateTailwindFunctions.js +4 -1
  127. package/src/lib/expandApplyAtRules.js +7 -0
  128. package/src/lib/expandTailwindAtRules.js +26 -9
  129. package/src/lib/generateRules.js +50 -26
  130. package/src/lib/load-config.ts +8 -0
  131. package/src/lib/offsets.js +61 -2
  132. package/src/lib/resolveDefaultsAtRules.js +5 -1
  133. package/src/lib/setupContextUtils.js +77 -38
  134. package/src/lib/setupTrackingContext.js +2 -4
  135. package/src/lib/sharedState.js +0 -15
  136. package/src/oxide/cli/build/plugin.ts +7 -7
  137. package/src/plugin.js +4 -4
  138. package/src/processTailwindFeatures.js +3 -2
  139. package/src/util/cloneNodes.js +35 -14
  140. package/src/util/color.js +2 -2
  141. package/src/util/colorNames.js +150 -0
  142. package/src/util/dataTypes.js +143 -18
  143. package/src/util/formatVariantSelector.js +11 -3
  144. package/src/util/isPlainObject.js +1 -1
  145. package/src/util/pluginUtils.js +17 -5
  146. package/src/util/prefixSelector.js +1 -0
  147. package/src/util/pseudoElements.js +18 -17
  148. package/src/value-parser/LICENSE +22 -0
  149. package/src/value-parser/README.md +3 -0
  150. package/src/value-parser/index.d.ts +177 -0
  151. package/src/value-parser/index.js +28 -0
  152. package/src/value-parser/parse.js +303 -0
  153. package/src/value-parser/stringify.js +41 -0
  154. package/src/value-parser/unit.js +118 -0
  155. package/src/value-parser/walk.js +18 -0
  156. package/stubs/config.full.js +86 -14
  157. package/types/config.d.ts +17 -9
  158. package/types/generated/corePluginList.d.ts +1 -1
  159. package/types/generated/default-theme.d.ts +35 -9
  160. package/types/index.d.ts +7 -3
@@ -1,4 +1,3 @@
1
- import { flagEnabled } from '../featureFlags'
2
1
  import * as regex from './regex'
3
2
 
4
3
  export function defaultExtractor(context) {
@@ -12,16 +11,17 @@ export function defaultExtractor(context) {
12
11
  let results = []
13
12
 
14
13
  for (let pattern of patterns) {
15
- results = [...results, ...(content.match(pattern) ?? [])]
14
+ for (let result of content.match(pattern) ?? []) {
15
+ results.push(clipAtBalancedParens(result))
16
+ }
16
17
  }
17
18
 
18
- return results.filter((v) => v !== undefined).map(clipAtBalancedParens)
19
+ return results
19
20
  }
20
21
  }
21
22
 
22
23
  function* buildRegExps(context) {
23
24
  let separator = context.tailwindConfig.separator
24
- let variantGroupingEnabled = flagEnabled(context.tailwindConfig, 'variantGrouping')
25
25
  let prefix =
26
26
  context.tailwindConfig.prefix !== ''
27
27
  ? regex.optional(regex.pattern([/-?/, regex.escape(context.tailwindConfig.prefix)]))
@@ -35,19 +35,29 @@ function* buildRegExps(context) {
35
35
  // This is a targeted fix to continue to allow theme()
36
36
  // with square brackets to work in arbitrary properties
37
37
  // while fixing a problem with the regex matching too much
38
- /\[[^\s:'"`]+:[^\s]+?\[[^\s]+?\][^\s]+?\]/,
38
+ /\[[^\s:'"`\]]+:[^\s]+?\[[^\s]+\][^\s]+?\]/,
39
39
 
40
40
  // Utilities
41
41
  regex.pattern([
42
42
  // Utility Name / Group Name
43
- /-?(?:\w+)/,
43
+ regex.any([
44
+ /-?(?:\w+)/,
45
+
46
+ // This is here to make sure @container supports everything that other utilities do
47
+ /@(?:\w+)/,
48
+ ]),
44
49
 
45
50
  // Normal/Arbitrary values
46
51
  regex.optional(
47
52
  regex.any([
48
53
  regex.pattern([
49
54
  // Arbitrary values
50
- /-(?:\w+-)*\[[^\s:]+\]/,
55
+ regex.any([
56
+ /-(?:\w+-)*\['[^\s]+'\]/,
57
+ /-(?:\w+-)*\["[^\s]+"\]/,
58
+ /-(?:\w+-)*\[`[^\s]+`\]/,
59
+ /-(?:\w+-)*\[(?:[^\s\[\]]+\[[^\s\[\]]+\])*[^\s:\[\]]+\]/,
60
+ ]),
51
61
 
52
62
  // Not immediately followed by an `{[(`
53
63
  /(?![{([]])/,
@@ -58,7 +68,12 @@ function* buildRegExps(context) {
58
68
 
59
69
  regex.pattern([
60
70
  // Arbitrary values
61
- /-(?:\w+-)*\[[^\s]+\]/,
71
+ regex.any([
72
+ /-(?:\w+-)*\['[^\s]+'\]/,
73
+ /-(?:\w+-)*\["[^\s]+"\]/,
74
+ /-(?:\w+-)*\[`[^\s]+`\]/,
75
+ /-(?:\w+-)*\[(?:[^\s\[\]]+\[[^\s\[\]]+\])*[^\s\[\]]+\]/,
76
+ ]),
62
77
 
63
78
  // Not immediately followed by an `{[(`
64
79
  /(?![{([]])/,
@@ -80,12 +95,18 @@ function* buildRegExps(context) {
80
95
  // This is here to provide special support for the `@` variant
81
96
  regex.pattern([/@\[[^\s"'`]+\](\/[^\s"'`]+)?/, separator]),
82
97
 
98
+ // With variant modifier (e.g.: group-[..]/modifier)
99
+ regex.pattern([/([^\s"'`\[\\]+-)?\[[^\s"'`]+\]\/\w+/, separator]),
100
+
83
101
  regex.pattern([/([^\s"'`\[\\]+-)?\[[^\s"'`]+\]/, separator]),
84
102
  regex.pattern([/[^\s"'`\[\\]+/, separator]),
85
103
  ]),
86
104
 
87
105
  // With quotes allowed
88
106
  regex.any([
107
+ // With variant modifier (e.g.: group-[..]/modifier)
108
+ regex.pattern([/([^\s"'`\[\\]+-)?\[[^\s`]+\]\/\w+/, separator]),
109
+
89
110
  regex.pattern([/([^\s"'`\[\\]+-)?\[[^\s`]+\]/, separator]),
90
111
  regex.pattern([/[^\s`\[\\]+/, separator]),
91
112
  ]),
@@ -103,15 +124,7 @@ function* buildRegExps(context) {
103
124
 
104
125
  prefix,
105
126
 
106
- variantGroupingEnabled
107
- ? regex.any([
108
- // Or any of those things but grouped separated by commas
109
- regex.pattern([/\(/, utility, regex.zeroOrMore([/,/, utility]), /\)/]),
110
-
111
- // Arbitrary properties, constrained utilities, arbitrary values, etc…
112
- utility,
113
- ])
114
- : utility,
127
+ utility,
115
128
  ])
116
129
  }
117
130
 
@@ -1,7 +1,7 @@
1
1
  import dlv from 'dlv'
2
2
  import didYouMean from 'didyoumean'
3
3
  import transformThemeValue from '../util/transformThemeValue'
4
- import parseValue from 'postcss-value-parser'
4
+ import parseValue from '../value-parser/index'
5
5
  import { normalizeScreens } from '../util/normalizeScreens'
6
6
  import buildMediaQuery from '../util/buildMediaQuery'
7
7
  import { toPath } from '../util/toPath'
@@ -146,6 +146,9 @@ function resolveVNode(node, vNode, functions) {
146
146
  }
147
147
 
148
148
  function resolveFunctions(node, input, functions) {
149
+ let hasAnyFn = Object.keys(functions).some((fn) => input.includes(`${fn}(`))
150
+ if (!hasAnyFn) return input
151
+
149
152
  return parseValue(input)
150
153
  .walk((vNode) => {
151
154
  resolveVNode(node, vNode, functions)
@@ -553,6 +553,13 @@ function processApply(root, context, localCache) {
553
553
  ? parent.selector.slice(importantSelector.length)
554
554
  : parent.selector
555
555
 
556
+ // If the selector becomes empty after replacing the important selector
557
+ // This means that it's the same as the parent selector and we don't want to replace it
558
+ // Otherwise we'll crash
559
+ if (parentSelector === '') {
560
+ parentSelector = parent.selector
561
+ }
562
+
556
563
  rule.selector = replaceSelector(parentSelector, rule.selector, applyCandidate)
557
564
 
558
565
  // And then re-add it if it was removed
@@ -1,5 +1,5 @@
1
1
  import fs from 'fs'
2
- import LRU from 'quick-lru'
2
+ import LRU from '@alloc/quick-lru'
3
3
  import * as sharedState from './sharedState'
4
4
  import { generateRules } from './generateRules'
5
5
  import log from '../util/log'
@@ -98,7 +98,7 @@ function buildStylesheet(rules, context) {
98
98
  }
99
99
 
100
100
  export default function expandTailwindAtRules(context) {
101
- return (root) => {
101
+ return async (root) => {
102
102
  let layerNodes = {
103
103
  base: null,
104
104
  components: null,
@@ -130,7 +130,7 @@ export default function expandTailwindAtRules(context) {
130
130
 
131
131
  env.DEBUG && console.time('Reading changed files')
132
132
 
133
- if (env.OXIDE) {
133
+ if (__OXIDE__) {
134
134
  // TODO: Pass through or implement `extractor`
135
135
  for (let candidate of require('@tailwindcss/oxide').parseCandidateStringsFromFiles(
136
136
  context.changedContent
@@ -145,11 +145,25 @@ export default function expandTailwindAtRules(context) {
145
145
  // getClassCandidatesOxide(file, transformer(content), extractor, candidates, seen)
146
146
  // }
147
147
  } else {
148
- for (let { file, content, extension } of context.changedContent) {
149
- let transformer = getTransformer(context.tailwindConfig, extension)
150
- let extractor = getExtractor(context, extension)
151
- content = file ? fs.readFileSync(file, 'utf8') : content
152
- getClassCandidates(transformer(content), extractor, candidates, seen)
148
+ /** @type {[item: {file?: string, content?: string}, meta: {transformer: any, extractor: any}][]} */
149
+ let regexParserContent = []
150
+
151
+ for (let item of context.changedContent) {
152
+ let transformer = getTransformer(context.tailwindConfig, item.extension)
153
+ let extractor = getExtractor(context, item.extension)
154
+ regexParserContent.push([item, { transformer, extractor }])
155
+ }
156
+
157
+ const BATCH_SIZE = 500
158
+
159
+ for (let i = 0; i < regexParserContent.length; i += BATCH_SIZE) {
160
+ let batch = regexParserContent.slice(i, i + BATCH_SIZE)
161
+ await Promise.all(
162
+ batch.map(async ([{ file, content }, { transformer, extractor }]) => {
163
+ content = file ? await fs.promises.readFile(file, 'utf8') : content
164
+ getClassCandidates(transformer(content), extractor, candidates, seen)
165
+ })
166
+ )
153
167
  }
154
168
  }
155
169
 
@@ -162,7 +176,7 @@ export default function expandTailwindAtRules(context) {
162
176
 
163
177
  env.DEBUG && console.time('Generate rules')
164
178
  env.DEBUG && console.time('Sorting candidates')
165
- let sortedCandidates = env.OXIDE
179
+ let sortedCandidates = __OXIDE__
166
180
  ? candidates
167
181
  : new Set(
168
182
  [...candidates].sort((a, z) => {
@@ -251,6 +265,9 @@ export default function expandTailwindAtRules(context) {
251
265
  )
252
266
  }
253
267
 
268
+ // TODO: Why is the root node having no source location for `end` possible?
269
+ root.source.end = root.source.end ?? root.source.start
270
+
254
271
  // If we've got a utility layer and no utilities are generated there's likely something wrong
255
272
  const hasUtilityVariants = variantNodes.some(
256
273
  (node) => node.raws.tailwind?.parentLayer === 'utilities'
@@ -13,7 +13,7 @@ import {
13
13
  } from '../util/formatVariantSelector'
14
14
  import { asClass } from '../util/nameClass'
15
15
  import { normalize } from '../util/dataTypes'
16
- import { isValidVariantFormatString, parseVariant } from './setupContextUtils'
16
+ import { isValidVariantFormatString, parseVariant, INTERNAL_FEATURES } from './setupContextUtils'
17
17
  import isValidArbitraryValue from '../util/isSyntacticallyValidPropertyValue'
18
18
  import { splitAtTopLevelOnly } from '../util/splitAtTopLevelOnly.js'
19
19
  import { flagEnabled } from '../featureFlags'
@@ -119,10 +119,20 @@ function applyImportant(matches, classCandidate) {
119
119
 
120
120
  let result = []
121
121
 
122
+ function isInKeyframes(rule) {
123
+ return rule.parent && rule.parent.type === 'atrule' && rule.parent.name === 'keyframes'
124
+ }
125
+
122
126
  for (let [meta, rule] of matches) {
123
127
  let container = postcss.root({ nodes: [rule.clone()] })
124
128
 
125
129
  container.walkRules((r) => {
130
+ // Declarations inside keyframes cannot be marked as important
131
+ // They will be ignored by the browser
132
+ if (isInKeyframes(r)) {
133
+ return
134
+ }
135
+
126
136
  let ast = selectorParser().astSync(r.selector)
127
137
 
128
138
  // Remove extraneous selectors that do not include the base candidate
@@ -193,13 +203,13 @@ function applyVariant(variant, matches, context) {
193
203
  // group[:hover] (`-` is missing)
194
204
  let match = /(.)(-?)\[(.*)\]/g.exec(variant)
195
205
  if (match) {
196
- let [, char, seperator, value] = match
206
+ let [, char, separator, value] = match
197
207
  // @-[200px] case
198
- if (char === '@' && seperator === '-') return []
208
+ if (char === '@' && separator === '-') return []
199
209
  // group[:hover] case
200
- if (char !== '@' && seperator === '') return []
210
+ if (char !== '@' && separator === '') return []
201
211
 
202
- variant = variant.replace(`${seperator}[${value}]`, '')
212
+ variant = variant.replace(`${separator}[${value}]`, '')
203
213
  args.value = value
204
214
  }
205
215
  }
@@ -230,9 +240,16 @@ function applyVariant(variant, matches, context) {
230
240
 
231
241
  if (context.variantMap.has(variant)) {
232
242
  let isArbitraryVariant = isArbitraryValue(variant)
243
+ let internalFeatures = context.variantOptions.get(variant)?.[INTERNAL_FEATURES] ?? {}
233
244
  let variantFunctionTuples = context.variantMap.get(variant).slice()
234
245
  let result = []
235
246
 
247
+ let respectPrefix = (() => {
248
+ if (isArbitraryVariant) return false
249
+ if (internalFeatures.respectPrefix === false) return false
250
+ return true
251
+ })()
252
+
236
253
  for (let [meta, rule] of matches) {
237
254
  // Don't generate variants for user css
238
255
  if (meta.layer === 'user') {
@@ -293,7 +310,7 @@ function applyVariant(variant, matches, context) {
293
310
  format(selectorFormat) {
294
311
  collectedFormats.push({
295
312
  format: selectorFormat,
296
- isArbitraryVariant,
313
+ respectPrefix,
297
314
  })
298
315
  },
299
316
  args,
@@ -322,7 +339,7 @@ function applyVariant(variant, matches, context) {
322
339
  if (typeof ruleWithVariant === 'string') {
323
340
  collectedFormats.push({
324
341
  format: ruleWithVariant,
325
- isArbitraryVariant,
342
+ respectPrefix,
326
343
  })
327
344
  }
328
345
 
@@ -366,7 +383,7 @@ function applyVariant(variant, matches, context) {
366
383
  // format: .foo &
367
384
  collectedFormats.push({
368
385
  format: modified.replace(rebuiltBase, '&'),
369
- isArbitraryVariant,
386
+ respectPrefix,
370
387
  })
371
388
  rule.selector = before
372
389
  })
@@ -489,13 +506,13 @@ function extractArbitraryProperty(classCandidate, context) {
489
506
  return null
490
507
  }
491
508
 
492
- let normalized = normalize(value)
509
+ let normalized = normalize(value, { property })
493
510
 
494
511
  if (!isParsableCssValue(property, normalized)) {
495
512
  return null
496
513
  }
497
514
 
498
- let sort = context.offsets.arbitraryProperty()
515
+ let sort = context.offsets.arbitraryProperty(classCandidate)
499
516
 
500
517
  return [
501
518
  [
@@ -566,7 +583,7 @@ function* recordCandidates(matches, classCandidate) {
566
583
  }
567
584
  }
568
585
 
569
- function* resolveMatches(candidate, context, original = candidate) {
586
+ function* resolveMatches(candidate, context) {
570
587
  let separator = context.tailwindConfig.separator
571
588
  let [classCandidate, ...variants] = splitWithSeparator(candidate, separator).reverse()
572
589
  let important = false
@@ -576,15 +593,6 @@ function* resolveMatches(candidate, context, original = candidate) {
576
593
  classCandidate = classCandidate.slice(1)
577
594
  }
578
595
 
579
- if (flagEnabled(context.tailwindConfig, 'variantGrouping')) {
580
- if (classCandidate.startsWith('(') && classCandidate.endsWith(')')) {
581
- let base = variants.slice().reverse().join(separator)
582
- for (let part of splitAtTopLevelOnly(classCandidate.slice(1, -1), ',')) {
583
- yield* resolveMatches(base + separator + part, context, original)
584
- }
585
- }
586
- }
587
-
588
596
  // TODO: Reintroduce this in ways that doesn't break on false positives
589
597
  // function sortAgainst(toSort, against) {
590
598
  // return toSort.slice().sort((a, z) => {
@@ -773,7 +781,7 @@ function* resolveMatches(candidate, context, original = candidate) {
773
781
  match[1].raws.tailwind = { ...match[1].raws.tailwind, candidate }
774
782
 
775
783
  // Apply final format selector
776
- match = applyFinalFormat(match, { context, candidate, original })
784
+ match = applyFinalFormat(match, { context, candidate })
777
785
 
778
786
  // Skip rules with invalid selectors
779
787
  // This will cause the candidate to be added to the "not class"
@@ -787,7 +795,7 @@ function* resolveMatches(candidate, context, original = candidate) {
787
795
  }
788
796
  }
789
797
 
790
- function applyFinalFormat(match, { context, candidate, original }) {
798
+ function applyFinalFormat(match, { context, candidate }) {
791
799
  if (!match[0].collectedFormats) {
792
800
  return match
793
801
  }
@@ -822,10 +830,19 @@ function applyFinalFormat(match, { context, candidate, original }) {
822
830
  }
823
831
 
824
832
  try {
825
- rule.selector = finalizeSelector(rule.selector, finalFormat, {
826
- candidate: original,
833
+ let selector = finalizeSelector(rule.selector, finalFormat, {
834
+ candidate,
827
835
  context,
828
836
  })
837
+
838
+ // Finalize Selector determined that this candidate is irrelevant
839
+ // TODO: This elimination should happen earlier so this never happens
840
+ if (selector === null) {
841
+ rule.remove()
842
+ return
843
+ }
844
+
845
+ rule.selector = selector
829
846
  } catch {
830
847
  // If this selector is invalid we also want to skip it
831
848
  // But it's likely that being invalid here means there's a bug in a plugin rather than too loosely matching content
@@ -838,6 +855,11 @@ function applyFinalFormat(match, { context, candidate, original }) {
838
855
  return null
839
856
  }
840
857
 
858
+ // If all rules have been eliminated we can skip this candidate entirely
859
+ if (container.nodes.length === 0) {
860
+ return null
861
+ }
862
+
841
863
  match[1] = container.nodes[0]
842
864
 
843
865
  return match
@@ -875,7 +897,7 @@ function getImportantStrategy(important) {
875
897
  }
876
898
  }
877
899
 
878
- function generateRules(candidates, context) {
900
+ function generateRules(candidates, context, isSorting = false) {
879
901
  let allRules = []
880
902
  let strategy = getImportantStrategy(context.tailwindConfig.important)
881
903
 
@@ -910,7 +932,9 @@ function generateRules(candidates, context) {
910
932
  rule = container.nodes[0]
911
933
  }
912
934
 
913
- let newEntry = [sort, rule]
935
+ // Note: We have to clone rules during sorting
936
+ // so we eliminate some shared mutable state
937
+ let newEntry = [sort, isSorting ? rule.clone() : rule]
914
938
  rules.add(newEntry)
915
939
  context.ruleCache.add(newEntry)
916
940
  allRules.push(newEntry)
@@ -4,6 +4,14 @@ import { transform } from 'sucrase'
4
4
  import { Config } from '../../types/config'
5
5
 
6
6
  let jiti: ReturnType<typeof jitiFactory> | null = null
7
+
8
+ // @internal
9
+ // This WILL be removed in some future release
10
+ // If you rely on this your stuff WILL break
11
+ export function useCustomJiti(_jiti: () => ReturnType<typeof jitiFactory>) {
12
+ jiti = _jiti()
13
+ }
14
+
7
15
  function lazyJiti() {
8
16
  return (
9
17
  jiti ??
@@ -23,7 +23,9 @@ import { remapBitfield } from './remap-bitfield.js'
23
23
  * @property {bigint} arbitrary 0n if false, 1n if true
24
24
  * @property {bigint} variants Dynamic size. 1 bit per registered variant. 0n means no variants
25
25
  * @property {bigint} parallelIndex Rule index for the parallel variant. 0 if not applicable.
26
- * @property {bigint} index Index of the rule / utility in it's given *parent* layer. Monotonically increasing.
26
+ * @property {bigint} index Index of the rule / utility in its given *parent* layer. Monotonically increasing.
27
+ * @property {bigint} propertyOffset Offset for the arbitrary property. Only valid after sorting.
28
+ * @property {string} property Name/Value of the arbitrary property.
27
29
  * @property {VariantOption[]} options Some information on how we can sort arbitrary variants
28
30
  */
29
31
 
@@ -88,17 +90,21 @@ export class Offsets {
88
90
  variants: 0n,
89
91
  parallelIndex: 0n,
90
92
  index: this.offsets[layer]++,
93
+ propertyOffset: 0n,
94
+ property: '',
91
95
  options: [],
92
96
  }
93
97
  }
94
98
 
95
99
  /**
100
+ * @param {string} name
96
101
  * @returns {RuleOffset}
97
102
  */
98
- arbitraryProperty() {
103
+ arbitraryProperty(name) {
99
104
  return {
100
105
  ...this.create('utilities'),
101
106
  arbitrary: 1n,
107
+ property: name,
102
108
  }
103
109
  }
104
110
 
@@ -262,6 +268,11 @@ export class Offsets {
262
268
  return a.arbitrary - b.arbitrary
263
269
  }
264
270
 
271
+ // Always sort arbitrary properties alphabetically
272
+ if (a.propertyOffset !== b.propertyOffset) {
273
+ return a.propertyOffset - b.propertyOffset
274
+ }
275
+
265
276
  // Sort utilities, components, etc… in the order they were registered
266
277
  return a.index - b.index
267
278
  }
@@ -320,14 +331,62 @@ export class Offsets {
320
331
  })
321
332
  }
322
333
 
334
+ /**
335
+ * @template T
336
+ * @param {[RuleOffset, T][]} list
337
+ * @returns {[RuleOffset, T][]}
338
+ */
339
+ sortArbitraryProperties(list) {
340
+ // Collect all known arbitrary properties
341
+ let known = new Set()
342
+
343
+ for (let [offset] of list) {
344
+ if (offset.arbitrary === 1n) {
345
+ known.add(offset.property)
346
+ }
347
+ }
348
+
349
+ // No arbitrary properties? Nothing to do.
350
+ if (known.size === 0) {
351
+ return list
352
+ }
353
+
354
+ // Sort the properties alphabetically
355
+ let properties = Array.from(known).sort()
356
+
357
+ // Create a map from the property name to its offset
358
+ let offsets = new Map()
359
+
360
+ let offset = 1n
361
+ for (let property of properties) {
362
+ offsets.set(property, offset++)
363
+ }
364
+
365
+ // Apply the sorted offsets to the list
366
+ return list.map((item) => {
367
+ let [offset, rule] = item
368
+
369
+ offset = {
370
+ ...offset,
371
+ propertyOffset: offsets.get(offset.property) ?? 0n,
372
+ }
373
+
374
+ return [offset, rule]
375
+ })
376
+ }
377
+
323
378
  /**
324
379
  * @template T
325
380
  * @param {[RuleOffset, T][]} list
326
381
  * @returns {[RuleOffset, T][]}
327
382
  */
328
383
  sort(list) {
384
+ // Sort arbitrary variants so they're in alphabetical order
329
385
  list = this.remapArbitraryVariantOffsets(list)
330
386
 
387
+ // Sort arbitrary properties so they're in alphabetical order
388
+ list = this.sortArbitraryProperties(list)
389
+
331
390
  return list.sort(([a], [b]) => bigSign(this.compare(a, b)))
332
391
  }
333
392
  }
@@ -104,8 +104,12 @@ export default function resolveDefaultsAtRules({ tailwindConfig }) {
104
104
  // we consider them separately because merging the declarations into
105
105
  // a single rule will cause browsers that do not understand the
106
106
  // vendor prefix to throw out the whole rule
107
+ // Additionally if a selector contains `:has` we also consider
108
+ // it separately because FF only recently gained support for it
107
109
  let selectorGroupName =
108
- selector.includes(':-') || selector.includes('::-') ? selector : '__DEFAULT__'
110
+ selector.includes(':-') || selector.includes('::-') || selector.includes(':has')
111
+ ? selector
112
+ : '__DEFAULT__'
109
113
 
110
114
  let selectors = selectorGroups.get(selectorGroupName) ?? new Set()
111
115
  selectorGroups.set(selectorGroupName, selectors)