tailwindcss 3.3.2 → 3.3.4

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 (65) hide show
  1. package/CHANGELOG.md +50 -1
  2. package/lib/cli/build/plugin.js +2 -2
  3. package/lib/cli/build/watching.js +1 -1
  4. package/lib/corePlugins.js +11 -7
  5. package/lib/css/preflight.css +9 -0
  6. package/lib/lib/defaultExtractor.js +16 -22
  7. package/lib/lib/evaluateTailwindFunctions.js +5 -3
  8. package/lib/lib/expandApplyAtRules.js +6 -0
  9. package/lib/lib/expandTailwindAtRules.js +20 -6
  10. package/lib/lib/generateRules.js +34 -24
  11. package/lib/lib/setupContextUtils.js +53 -33
  12. package/lib/lib/setupTrackingContext.js +2 -1
  13. package/lib/oxide/cli/build/plugin.js +2 -2
  14. package/lib/plugin.js +3 -3
  15. package/lib/processTailwindFeatures.js +2 -2
  16. package/lib/util/color.js +1 -1
  17. package/lib/util/dataTypes.js +100 -16
  18. package/lib/util/formatVariantSelector.js +10 -3
  19. package/lib/util/isPlainObject.js +1 -1
  20. package/lib/util/prefixSelector.js +1 -1
  21. package/lib/util/pseudoElements.js +18 -34
  22. package/lib/value-parser/LICENSE +22 -0
  23. package/lib/value-parser/README.md +3 -0
  24. package/lib/value-parser/index.d.js +2 -0
  25. package/lib/value-parser/index.js +22 -0
  26. package/lib/value-parser/parse.js +259 -0
  27. package/lib/value-parser/stringify.js +38 -0
  28. package/lib/value-parser/unit.js +86 -0
  29. package/lib/value-parser/walk.js +16 -0
  30. package/nesting/index.d.ts +4 -0
  31. package/package.json +5 -6
  32. package/peers/index.js +701 -617
  33. package/src/cli/build/plugin.js +2 -2
  34. package/src/cli/build/watching.js +1 -1
  35. package/src/corePlugins.js +13 -7
  36. package/src/css/preflight.css +9 -0
  37. package/src/featureFlags.js +0 -1
  38. package/src/lib/defaultExtractor.js +12 -13
  39. package/src/lib/evaluateTailwindFunctions.js +4 -1
  40. package/src/lib/expandApplyAtRules.js +7 -0
  41. package/src/lib/expandTailwindAtRules.js +20 -6
  42. package/src/lib/generateRules.js +34 -25
  43. package/src/lib/setupContextUtils.js +50 -35
  44. package/src/lib/setupTrackingContext.js +1 -3
  45. package/src/oxide/cli/build/plugin.ts +2 -2
  46. package/src/plugin.js +3 -3
  47. package/src/processTailwindFeatures.js +3 -2
  48. package/src/util/color.js +1 -1
  49. package/src/util/dataTypes.js +101 -18
  50. package/src/util/formatVariantSelector.js +11 -3
  51. package/src/util/isPlainObject.js +1 -1
  52. package/src/util/prefixSelector.js +1 -0
  53. package/src/util/pseudoElements.js +14 -17
  54. package/src/value-parser/LICENSE +22 -0
  55. package/src/value-parser/README.md +3 -0
  56. package/src/value-parser/index.d.ts +177 -0
  57. package/src/value-parser/index.js +28 -0
  58. package/src/value-parser/parse.js +303 -0
  59. package/src/value-parser/stringify.js +41 -0
  60. package/src/value-parser/unit.js +118 -0
  61. package/src/value-parser/walk.js +18 -0
  62. package/stubs/config.full.js +1 -0
  63. package/types/config.d.ts +6 -6
  64. package/types/generated/default-theme.d.ts +1 -0
  65. package/types/index.d.ts +7 -3
@@ -278,9 +278,9 @@ export async function createProcessor(args, cliConfigPath) {
278
278
  let tailwindPlugin = () => {
279
279
  return {
280
280
  postcssPlugin: 'tailwindcss',
281
- Once(root, { result }) {
281
+ async Once(root, { result }) {
282
282
  env.DEBUG && console.time('Compiling CSS')
283
- tailwind(({ createContext }) => {
283
+ await tailwind(({ createContext }) => {
284
284
  console.error()
285
285
  console.error('Rebuilding...')
286
286
 
@@ -164,7 +164,7 @@ export function createWatcher(args, { state, rebuild }) {
164
164
  // This is very likely a chokidar bug but it's one we need to work around
165
165
  // We treat this as a change event and rebuild the CSS
166
166
  watcher.on('raw', (evt, filePath, meta) => {
167
- if (evt !== 'rename') {
167
+ if (evt !== 'rename' || filePath === null) {
168
168
  return
169
169
  }
170
170
 
@@ -22,6 +22,7 @@ import { formatBoxShadowValue, parseBoxShadowValue } from './util/parseBoxShadow
22
22
  import { removeAlphaVariables } from './util/removeAlphaVariables'
23
23
  import { flagEnabled } from './featureFlags'
24
24
  import { normalize } from './util/dataTypes'
25
+ import { INTERNAL_FEATURES } from './lib/setupContextUtils'
25
26
 
26
27
  export let variantPlugins = {
27
28
  pseudoElementVariants: ({ addVariant }) => {
@@ -80,7 +81,7 @@ export let variantPlugins = {
80
81
  })
81
82
  },
82
83
 
83
- pseudoClassVariants: ({ addVariant, matchVariant, config }) => {
84
+ pseudoClassVariants: ({ addVariant, matchVariant, config, prefix }) => {
84
85
  let pseudoVariants = [
85
86
  // Positional
86
87
  ['first', '&:first-child'],
@@ -151,12 +152,12 @@ export let variantPlugins = {
151
152
  let variants = {
152
153
  group: (_, { modifier }) =>
153
154
  modifier
154
- ? [`:merge(.group\\/${escapeClassName(modifier)})`, ' &']
155
- : [`:merge(.group)`, ' &'],
155
+ ? [`:merge(${prefix('.group')}\\/${escapeClassName(modifier)})`, ' &']
156
+ : [`:merge(${prefix('.group')})`, ' &'],
156
157
  peer: (_, { modifier }) =>
157
158
  modifier
158
- ? [`:merge(.peer\\/${escapeClassName(modifier)})`, ' ~ &']
159
- : [`:merge(.peer)`, ' ~ &'],
159
+ ? [`:merge(${prefix('.peer')}\\/${escapeClassName(modifier)})`, ' ~ &']
160
+ : [`:merge(${prefix('.peer')})`, ' ~ &'],
160
161
  }
161
162
 
162
163
  for (let [name, fn] of Object.entries(variants)) {
@@ -192,7 +193,12 @@ export let variantPlugins = {
192
193
 
193
194
  return result.slice(0, start) + a + result.slice(start + 1, end) + b + result.slice(end)
194
195
  },
195
- { values: Object.fromEntries(pseudoVariants) }
196
+ {
197
+ values: Object.fromEntries(pseudoVariants),
198
+ [INTERNAL_FEATURES]: {
199
+ respectPrefix: false,
200
+ },
201
+ }
196
202
  )
197
203
  }
198
204
  },
@@ -913,7 +919,7 @@ export let corePlugins = {
913
919
  },
914
920
 
915
921
  animation: ({ matchUtilities, theme, config }) => {
916
- let prefixName = (name) => `${config('prefix')}${escapeClassName(name)}`
922
+ let prefixName = (name) => escapeClassName(config('prefix') + name)
917
923
  let keyframes = Object.fromEntries(
918
924
  Object.entries(theme('keyframes') ?? {}).map(([key, value]) => {
919
925
  return [key, { [`@keyframes ${prefixName(key)}`]: value }]
@@ -163,6 +163,8 @@ optgroup,
163
163
  select,
164
164
  textarea {
165
165
  font-family: inherit; /* 1 */
166
+ font-feature-settings: inherit; /* 1 */
167
+ font-variation-settings: inherit; /* 1 */
166
168
  font-size: 100%; /* 1 */
167
169
  font-weight: inherit; /* 1 */
168
170
  line-height: inherit; /* 1 */
@@ -300,6 +302,13 @@ menu {
300
302
  padding: 0;
301
303
  }
302
304
 
305
+ /*
306
+ Reset default styling for dialogs.
307
+ */
308
+ dialog {
309
+ padding: 0;
310
+ }
311
+
303
312
  /*
304
313
  Prevent resizing textareas horizontally by default.
305
314
  */
@@ -22,7 +22,6 @@ let featureFlags = {
22
22
  experimental: [
23
23
  'optimizeUniversalDefaults',
24
24
  'generalizedModifiers',
25
- // 'variantGrouping',
26
25
  ],
27
26
  }
28
27
 
@@ -12,16 +12,17 @@ export function defaultExtractor(context) {
12
12
  let results = []
13
13
 
14
14
  for (let pattern of patterns) {
15
- results = [...results, ...(content.match(pattern) ?? [])]
15
+ for (let result of content.match(pattern) ?? []) {
16
+ results.push(clipAtBalancedParens(result))
17
+ }
16
18
  }
17
19
 
18
- return results.filter((v) => v !== undefined).map(clipAtBalancedParens)
20
+ return results
19
21
  }
20
22
  }
21
23
 
22
24
  function* buildRegExps(context) {
23
25
  let separator = context.tailwindConfig.separator
24
- let variantGroupingEnabled = flagEnabled(context.tailwindConfig, 'variantGrouping')
25
26
  let prefix =
26
27
  context.tailwindConfig.prefix !== ''
27
28
  ? regex.optional(regex.pattern([/-?/, regex.escape(context.tailwindConfig.prefix)]))
@@ -35,7 +36,7 @@ function* buildRegExps(context) {
35
36
  // This is a targeted fix to continue to allow theme()
36
37
  // with square brackets to work in arbitrary properties
37
38
  // while fixing a problem with the regex matching too much
38
- /\[[^\s:'"`]+:[^\s]+?\[[^\s]+\][^\s]+?\]/,
39
+ /\[[^\s:'"`\]]+:[^\s]+?\[[^\s]+\][^\s]+?\]/,
39
40
 
40
41
  // Utilities
41
42
  regex.pattern([
@@ -80,12 +81,18 @@ function* buildRegExps(context) {
80
81
  // This is here to provide special support for the `@` variant
81
82
  regex.pattern([/@\[[^\s"'`]+\](\/[^\s"'`]+)?/, separator]),
82
83
 
84
+ // With variant modifier (e.g.: group-[..]/modifier)
85
+ regex.pattern([/([^\s"'`\[\\]+-)?\[[^\s"'`]+\]\/\w+/, separator]),
86
+
83
87
  regex.pattern([/([^\s"'`\[\\]+-)?\[[^\s"'`]+\]/, separator]),
84
88
  regex.pattern([/[^\s"'`\[\\]+/, separator]),
85
89
  ]),
86
90
 
87
91
  // With quotes allowed
88
92
  regex.any([
93
+ // With variant modifier (e.g.: group-[..]/modifier)
94
+ regex.pattern([/([^\s"'`\[\\]+-)?\[[^\s`]+\]\/\w+/, separator]),
95
+
89
96
  regex.pattern([/([^\s"'`\[\\]+-)?\[[^\s`]+\]/, separator]),
90
97
  regex.pattern([/[^\s`\[\\]+/, separator]),
91
98
  ]),
@@ -103,15 +110,7 @@ function* buildRegExps(context) {
103
110
 
104
111
  prefix,
105
112
 
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,
113
+ utility,
115
114
  ])
116
115
  }
117
116
 
@@ -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
@@ -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,
@@ -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
 
@@ -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'
@@ -193,13 +193,13 @@ function applyVariant(variant, matches, context) {
193
193
  // group[:hover] (`-` is missing)
194
194
  let match = /(.)(-?)\[(.*)\]/g.exec(variant)
195
195
  if (match) {
196
- let [, char, seperator, value] = match
196
+ let [, char, separator, value] = match
197
197
  // @-[200px] case
198
- if (char === '@' && seperator === '-') return []
198
+ if (char === '@' && separator === '-') return []
199
199
  // group[:hover] case
200
- if (char !== '@' && seperator === '') return []
200
+ if (char !== '@' && separator === '') return []
201
201
 
202
- variant = variant.replace(`${seperator}[${value}]`, '')
202
+ variant = variant.replace(`${separator}[${value}]`, '')
203
203
  args.value = value
204
204
  }
205
205
  }
@@ -230,9 +230,16 @@ function applyVariant(variant, matches, context) {
230
230
 
231
231
  if (context.variantMap.has(variant)) {
232
232
  let isArbitraryVariant = isArbitraryValue(variant)
233
+ let internalFeatures = context.variantOptions.get(variant)?.[INTERNAL_FEATURES] ?? {}
233
234
  let variantFunctionTuples = context.variantMap.get(variant).slice()
234
235
  let result = []
235
236
 
237
+ let respectPrefix = (() => {
238
+ if (isArbitraryVariant) return false
239
+ if (internalFeatures.respectPrefix === false) return false
240
+ return true
241
+ })()
242
+
236
243
  for (let [meta, rule] of matches) {
237
244
  // Don't generate variants for user css
238
245
  if (meta.layer === 'user') {
@@ -293,7 +300,7 @@ function applyVariant(variant, matches, context) {
293
300
  format(selectorFormat) {
294
301
  collectedFormats.push({
295
302
  format: selectorFormat,
296
- isArbitraryVariant,
303
+ respectPrefix,
297
304
  })
298
305
  },
299
306
  args,
@@ -322,7 +329,7 @@ function applyVariant(variant, matches, context) {
322
329
  if (typeof ruleWithVariant === 'string') {
323
330
  collectedFormats.push({
324
331
  format: ruleWithVariant,
325
- isArbitraryVariant,
332
+ respectPrefix,
326
333
  })
327
334
  }
328
335
 
@@ -366,7 +373,7 @@ function applyVariant(variant, matches, context) {
366
373
  // format: .foo &
367
374
  collectedFormats.push({
368
375
  format: modified.replace(rebuiltBase, '&'),
369
- isArbitraryVariant,
376
+ respectPrefix,
370
377
  })
371
378
  rule.selector = before
372
379
  })
@@ -489,7 +496,7 @@ function extractArbitraryProperty(classCandidate, context) {
489
496
  return null
490
497
  }
491
498
 
492
- let normalized = normalize(value)
499
+ let normalized = normalize(value, { property })
493
500
 
494
501
  if (!isParsableCssValue(property, normalized)) {
495
502
  return null
@@ -566,7 +573,7 @@ function* recordCandidates(matches, classCandidate) {
566
573
  }
567
574
  }
568
575
 
569
- function* resolveMatches(candidate, context, original = candidate) {
576
+ function* resolveMatches(candidate, context) {
570
577
  let separator = context.tailwindConfig.separator
571
578
  let [classCandidate, ...variants] = splitWithSeparator(candidate, separator).reverse()
572
579
  let important = false
@@ -576,15 +583,6 @@ function* resolveMatches(candidate, context, original = candidate) {
576
583
  classCandidate = classCandidate.slice(1)
577
584
  }
578
585
 
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
586
  // TODO: Reintroduce this in ways that doesn't break on false positives
589
587
  // function sortAgainst(toSort, against) {
590
588
  // return toSort.slice().sort((a, z) => {
@@ -773,7 +771,7 @@ function* resolveMatches(candidate, context, original = candidate) {
773
771
  match[1].raws.tailwind = { ...match[1].raws.tailwind, candidate }
774
772
 
775
773
  // Apply final format selector
776
- match = applyFinalFormat(match, { context, candidate, original })
774
+ match = applyFinalFormat(match, { context, candidate })
777
775
 
778
776
  // Skip rules with invalid selectors
779
777
  // This will cause the candidate to be added to the "not class"
@@ -787,7 +785,7 @@ function* resolveMatches(candidate, context, original = candidate) {
787
785
  }
788
786
  }
789
787
 
790
- function applyFinalFormat(match, { context, candidate, original }) {
788
+ function applyFinalFormat(match, { context, candidate }) {
791
789
  if (!match[0].collectedFormats) {
792
790
  return match
793
791
  }
@@ -822,10 +820,19 @@ function applyFinalFormat(match, { context, candidate, original }) {
822
820
  }
823
821
 
824
822
  try {
825
- rule.selector = finalizeSelector(rule.selector, finalFormat, {
826
- candidate: original,
823
+ let selector = finalizeSelector(rule.selector, finalFormat, {
824
+ candidate,
827
825
  context,
828
826
  })
827
+
828
+ // Finalize Selector determined that this candidate is irrelevant
829
+ // TODO: This elimination should happen earlier so this never happens
830
+ if (selector === null) {
831
+ rule.remove()
832
+ return
833
+ }
834
+
835
+ rule.selector = selector
829
836
  } catch {
830
837
  // If this selector is invalid we also want to skip it
831
838
  // But it's likely that being invalid here means there's a bug in a plugin rather than too loosely matching content
@@ -875,7 +882,7 @@ function getImportantStrategy(important) {
875
882
  }
876
883
  }
877
884
 
878
- function generateRules(candidates, context) {
885
+ function generateRules(candidates, context, isSorting = false) {
879
886
  let allRules = []
880
887
  let strategy = getImportantStrategy(context.tailwindConfig.important)
881
888
 
@@ -910,7 +917,9 @@ function generateRules(candidates, context) {
910
917
  rule = container.nodes[0]
911
918
  }
912
919
 
913
- let newEntry = [sort, rule]
920
+ // Note: We have to clone rules during sorting
921
+ // so we eliminate some shared mutable state
922
+ let newEntry = [sort, isSorting ? rule.clone() : rule]
914
923
  rules.add(newEntry)
915
924
  context.ruleCache.add(newEntry)
916
925
  allRules.push(newEntry)
@@ -24,6 +24,8 @@ import { Offsets } from './offsets.js'
24
24
  import { flagEnabled } from '../featureFlags.js'
25
25
  import { finalizeSelector, formatVariantSelector } from '../util/formatVariantSelector'
26
26
 
27
+ export const INTERNAL_FEATURES = Symbol()
28
+
27
29
  const VARIANT_TYPES = {
28
30
  AddVariant: Symbol.for('ADD_VARIANT'),
29
31
  MatchVariant: Symbol.for('MATCH_VARIANT'),
@@ -146,43 +148,45 @@ function getClasses(selector, mutate) {
146
148
  return parser.transformSync(selector)
147
149
  }
148
150
 
151
+ /**
152
+ * Ignore everything inside a :not(...). This allows you to write code like
153
+ * `div:not(.foo)`. If `.foo` is never found in your code, then we used to
154
+ * not generated it. But now we will ignore everything inside a `:not`, so
155
+ * that it still gets generated.
156
+ *
157
+ * @param {selectorParser.Root} selectors
158
+ */
159
+ function ignoreNot(selectors) {
160
+ selectors.walkPseudos((pseudo) => {
161
+ if (pseudo.value === ':not') {
162
+ pseudo.remove()
163
+ }
164
+ })
165
+ }
166
+
149
167
  function extractCandidates(node, state = { containsNonOnDemandable: false }, depth = 0) {
150
168
  let classes = []
169
+ let selectors = []
151
170
 
152
- // Handle normal rules
153
171
  if (node.type === 'rule') {
154
- // Ignore everything inside a :not(...). This allows you to write code like
155
- // `div:not(.foo)`. If `.foo` is never found in your code, then we used to
156
- // not generated it. But now we will ignore everything inside a `:not`, so
157
- // that it still gets generated.
158
- function ignoreNot(selectors) {
159
- selectors.walkPseudos((pseudo) => {
160
- if (pseudo.value === ':not') {
161
- pseudo.remove()
162
- }
163
- })
164
- }
172
+ // Handle normal rules
173
+ selectors.push(...node.selectors)
174
+ } else if (node.type === 'atrule') {
175
+ // Handle at-rules (which contains nested rules)
176
+ node.walkRules((rule) => selectors.push(...rule.selectors))
177
+ }
165
178
 
166
- for (let selector of node.selectors) {
167
- let classCandidates = getClasses(selector, ignoreNot)
168
- // At least one of the selectors contains non-"on-demandable" candidates.
169
- if (classCandidates.length === 0) {
170
- state.containsNonOnDemandable = true
171
- }
179
+ for (let selector of selectors) {
180
+ let classCandidates = getClasses(selector, ignoreNot)
172
181
 
173
- for (let classCandidate of classCandidates) {
174
- classes.push(classCandidate)
175
- }
182
+ // At least one of the selectors contains non-"on-demandable" candidates.
183
+ if (classCandidates.length === 0) {
184
+ state.containsNonOnDemandable = true
176
185
  }
177
- }
178
186
 
179
- // Handle at-rules (which contains nested rules)
180
- else if (node.type === 'atrule') {
181
- node.walkRules((rule) => {
182
- for (let classCandidate of rule.selectors.flatMap((selector) => getClasses(selector))) {
183
- classes.push(classCandidate)
184
- }
185
- })
187
+ for (let classCandidate of classCandidates) {
188
+ classes.push(classCandidate)
189
+ }
186
190
  }
187
191
 
188
192
  if (depth === 0) {
@@ -230,8 +234,8 @@ export function parseVariant(variant) {
230
234
  return ({ format }) => format(str)
231
235
  }
232
236
 
233
- let [, name, params] = /@(.*?)( .+|[({].*)/g.exec(str)
234
- return ({ wrap }) => wrap(postcss.atRule({ name, params: params.trim() }))
237
+ let [, name, params] = /@(\S*)( .+|[({].*)?/g.exec(str)
238
+ return ({ wrap }) => wrap(postcss.atRule({ name, params: params?.trim() ?? '' }))
235
239
  })
236
240
  .reverse()
237
241
 
@@ -943,13 +947,17 @@ function registerPlugins(plugins, context) {
943
947
 
944
948
  // Sort all classes in order
945
949
  // Non-tailwind classes won't be generated and will be left as `null`
946
- let rules = generateRules(new Set(sorted), context)
950
+ let rules = generateRules(new Set(sorted), context, true)
947
951
  rules = context.offsets.sort(rules)
948
952
 
949
953
  let idx = BigInt(parasiteUtilities.length)
950
954
 
951
955
  for (const [, rule] of rules) {
952
- sortedClassNames.set(rule.raws.tailwind.candidate, idx++)
956
+ let candidate = rule.raws.tailwind.candidate
957
+
958
+ // When multiple rules match a candidate
959
+ // always take the position of the first one
960
+ sortedClassNames.set(candidate, sortedClassNames.get(candidate) ?? idx++)
953
961
  }
954
962
 
955
963
  return classes.map((className) => {
@@ -1119,17 +1127,24 @@ function registerPlugins(plugins, context) {
1119
1127
  }
1120
1128
 
1121
1129
  let isArbitraryVariant = !(value in (options.values ?? {}))
1130
+ let internalFeatures = options[INTERNAL_FEATURES] ?? {}
1131
+
1132
+ let respectPrefix = (() => {
1133
+ if (isArbitraryVariant) return false
1134
+ if (internalFeatures.respectPrefix === false) return false
1135
+ return true
1136
+ })()
1122
1137
 
1123
1138
  formatStrings = formatStrings.map((format) =>
1124
1139
  format.map((str) => ({
1125
1140
  format: str,
1126
- isArbitraryVariant,
1141
+ respectPrefix,
1127
1142
  }))
1128
1143
  )
1129
1144
 
1130
1145
  manualFormatStrings = manualFormatStrings.map((format) => ({
1131
1146
  format,
1132
- isArbitraryVariant,
1147
+ respectPrefix,
1133
1148
  }))
1134
1149
 
1135
1150
  let opts = {
@@ -63,9 +63,7 @@ function getTailwindConfig(configOrPath) {
63
63
  }
64
64
 
65
65
  // It's a plain object, not a path
66
- let newConfig = resolveConfig(
67
- configOrPath.config === undefined ? configOrPath : configOrPath.config
68
- )
66
+ let newConfig = resolveConfig(configOrPath?.config ?? configOrPath ?? {})
69
67
 
70
68
  newConfig = validateConfig(newConfig)
71
69
 
@@ -277,9 +277,9 @@ export async function createProcessor(args, cliConfigPath) {
277
277
  let tailwindPlugin = () => {
278
278
  return {
279
279
  postcssPlugin: 'tailwindcss',
280
- Once(root, { result }) {
280
+ async Once(root, { result }) {
281
281
  env.DEBUG && console.time('Compiling CSS')
282
- tailwind(({ createContext }) => {
282
+ await tailwind(({ createContext }) => {
283
283
  console.error()
284
284
  console.error('Rebuilding...')
285
285
 
package/src/plugin.js CHANGED
@@ -13,7 +13,7 @@ module.exports = function tailwindcss(configOrPath) {
13
13
  console.time('JIT TOTAL')
14
14
  return root
15
15
  },
16
- function (root, result) {
16
+ async function (root, result) {
17
17
  // Use the path for the `@config` directive if it exists, otherwise use the
18
18
  // path for the file being processed
19
19
  configOrPath = findAtConfigPath(root, result) ?? configOrPath
@@ -25,14 +25,14 @@ module.exports = function tailwindcss(configOrPath) {
25
25
 
26
26
  for (const root of roots) {
27
27
  if (root.type === 'root') {
28
- processTailwindFeatures(context)(root, result)
28
+ await processTailwindFeatures(context)(root, result)
29
29
  }
30
30
  }
31
31
 
32
32
  return
33
33
  }
34
34
 
35
- processTailwindFeatures(context)(root, result)
35
+ await processTailwindFeatures(context)(root, result)
36
36
  },
37
37
  __OXIDE__ &&
38
38
  function lightningCssPlugin(_root, result) {
@@ -12,7 +12,7 @@ import { createContext } from './lib/setupContextUtils'
12
12
  import { issueFlagNotices } from './featureFlags'
13
13
 
14
14
  export default function processTailwindFeatures(setupContext) {
15
- return function (root, result) {
15
+ return async function (root, result) {
16
16
  let { tailwindDirectives, applyDirectives } = normalizeTailwindDirectives(root)
17
17
 
18
18
  detectNesting()(root, result)
@@ -44,7 +44,8 @@ export default function processTailwindFeatures(setupContext) {
44
44
 
45
45
  issueFlagNotices(context.tailwindConfig)
46
46
 
47
- expandTailwindAtRules(context)(root, result)
47
+ await expandTailwindAtRules(context)(root, result)
48
+
48
49
  // Partition apply rules that are generated by
49
50
  // addComponents, addUtilities and so on.
50
51
  partitionApplyAtRules()(root, result)
package/src/util/color.js CHANGED
@@ -5,7 +5,7 @@ let SHORT_HEX = /^#([a-f\d])([a-f\d])([a-f\d])([a-f\d])?$/i
5
5
  let VALUE = /(?:\d+|\d*\.\d+)%?/
6
6
  let SEP = /(?:\s*,\s*|\s+)/
7
7
  let ALPHA_SEP = /\s*[,/]\s*/
8
- let CUSTOM_PROPERTY = /var\(--(?:[^ )]*?)\)/
8
+ let CUSTOM_PROPERTY = /var\(--(?:[^ )]*?)(?:,(?:[^ )]*?|var\(--[^ )]*?\)))?\)/
9
9
 
10
10
  let RGB = new RegExp(
11
11
  `^(rgba?)\\(\\s*(${VALUE.source}|${CUSTOM_PROPERTY.source})(?:${SEP.source}(${VALUE.source}|${CUSTOM_PROPERTY.source}))?(?:${SEP.source}(${VALUE.source}|${CUSTOM_PROPERTY.source}))?(?:${ALPHA_SEP.source}(${VALUE.source}|${CUSTOM_PROPERTY.source}))?\\s*\\)$`