tailwindcss 3.1.7 → 3.2.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 (112) hide show
  1. package/README.md +12 -8
  2. package/lib/cli/build/deps.js +54 -0
  3. package/lib/cli/build/index.js +44 -0
  4. package/lib/cli/build/plugin.js +335 -0
  5. package/lib/cli/build/utils.js +78 -0
  6. package/lib/cli/build/watching.js +113 -0
  7. package/lib/cli/help/index.js +71 -0
  8. package/lib/cli/index.js +18 -0
  9. package/lib/cli/init/index.js +46 -0
  10. package/lib/cli/shared.js +12 -0
  11. package/lib/cli.js +11 -590
  12. package/lib/corePlugins.js +332 -108
  13. package/lib/css/preflight.css +5 -0
  14. package/lib/featureFlags.js +7 -4
  15. package/lib/index.js +6 -1
  16. package/lib/lib/content.js +167 -0
  17. package/lib/lib/defaultExtractor.js +15 -10
  18. package/lib/lib/detectNesting.js +2 -2
  19. package/lib/lib/evaluateTailwindFunctions.js +17 -1
  20. package/lib/lib/expandApplyAtRules.js +71 -37
  21. package/lib/lib/expandTailwindAtRules.js +10 -42
  22. package/lib/lib/findAtConfigPath.js +44 -0
  23. package/lib/lib/generateRules.js +181 -96
  24. package/lib/lib/normalizeTailwindDirectives.js +1 -1
  25. package/lib/lib/offsets.js +217 -0
  26. package/lib/lib/regex.js +1 -1
  27. package/lib/lib/setupContextUtils.js +339 -100
  28. package/lib/lib/setupTrackingContext.js +5 -39
  29. package/lib/lib/sharedState.js +2 -0
  30. package/lib/public/colors.js +1 -1
  31. package/lib/util/buildMediaQuery.js +6 -3
  32. package/lib/util/configurePlugins.js +1 -1
  33. package/lib/util/dataTypes.js +15 -19
  34. package/lib/util/formatVariantSelector.js +92 -8
  35. package/lib/util/getAllConfigs.js +14 -3
  36. package/lib/util/isValidArbitraryValue.js +1 -1
  37. package/lib/util/nameClass.js +3 -0
  38. package/lib/util/negateValue.js +15 -2
  39. package/lib/util/normalizeConfig.js +17 -3
  40. package/lib/util/normalizeScreens.js +100 -3
  41. package/lib/util/parseAnimationValue.js +1 -1
  42. package/lib/util/parseBoxShadowValue.js +1 -1
  43. package/lib/util/parseDependency.js +33 -54
  44. package/lib/util/parseGlob.js +34 -0
  45. package/lib/util/parseObjectStyles.js +1 -1
  46. package/lib/util/pluginUtils.js +86 -17
  47. package/lib/util/resolveConfig.js +3 -3
  48. package/lib/util/splitAtTopLevelOnly.js +31 -81
  49. package/lib/util/transformThemeValue.js +9 -2
  50. package/lib/util/validateConfig.js +1 -1
  51. package/lib/util/validateFormalSyntax.js +24 -0
  52. package/package.json +14 -12
  53. package/peers/.DS_Store +0 -0
  54. package/peers/.svgo.yml +75 -0
  55. package/peers/index.js +3690 -2274
  56. package/peers/orders/concentric-css.json +299 -0
  57. package/peers/orders/smacss.json +299 -0
  58. package/peers/orders/source.json +295 -0
  59. package/plugin.d.ts +3 -3
  60. package/scripts/release-channel.js +18 -0
  61. package/scripts/release-notes.js +21 -0
  62. package/src/.DS_Store +0 -0
  63. package/src/cli/build/deps.js +56 -0
  64. package/src/cli/build/index.js +45 -0
  65. package/src/cli/build/plugin.js +397 -0
  66. package/src/cli/build/utils.js +76 -0
  67. package/src/cli/build/watching.js +134 -0
  68. package/src/cli/help/index.js +70 -0
  69. package/src/cli/index.js +3 -0
  70. package/src/cli/init/index.js +50 -0
  71. package/src/cli/shared.js +5 -0
  72. package/src/cli.js +4 -696
  73. package/src/corePlugins.js +262 -39
  74. package/src/css/preflight.css +5 -0
  75. package/src/featureFlags.js +12 -2
  76. package/src/index.js +5 -0
  77. package/src/lib/content.js +205 -0
  78. package/src/lib/defaultExtractor.js +3 -0
  79. package/src/lib/evaluateTailwindFunctions.js +22 -1
  80. package/src/lib/expandApplyAtRules.js +76 -29
  81. package/src/lib/expandTailwindAtRules.js +8 -46
  82. package/src/lib/findAtConfigPath.js +48 -0
  83. package/src/lib/generateRules.js +224 -105
  84. package/src/lib/offsets.js +270 -0
  85. package/src/lib/setupContextUtils.js +376 -89
  86. package/src/lib/setupTrackingContext.js +4 -45
  87. package/src/lib/sharedState.js +2 -0
  88. package/src/util/buildMediaQuery.js +5 -3
  89. package/src/util/dataTypes.js +15 -17
  90. package/src/util/formatVariantSelector.js +113 -9
  91. package/src/util/getAllConfigs.js +14 -2
  92. package/src/util/nameClass.js +4 -0
  93. package/src/util/negateValue.js +10 -2
  94. package/src/util/normalizeConfig.js +22 -2
  95. package/src/util/normalizeScreens.js +99 -4
  96. package/src/util/parseBoxShadowValue.js +1 -1
  97. package/src/util/parseDependency.js +37 -42
  98. package/src/util/parseGlob.js +24 -0
  99. package/src/util/pluginUtils.js +90 -14
  100. package/src/util/resolveConfig.js +2 -2
  101. package/src/util/splitAtTopLevelOnly.js +23 -49
  102. package/src/util/transformThemeValue.js +9 -1
  103. package/src/util/validateFormalSyntax.js +34 -0
  104. package/stubs/defaultConfig.stub.js +19 -3
  105. package/tmp.css +11 -0
  106. package/tmp.dependency-graph.js +2 -0
  107. package/tmp.in.css +3 -0
  108. package/tmp.js +0 -0
  109. package/tmp.out.css +524 -0
  110. package/types/config.d.ts +47 -13
  111. package/types/generated/default-theme.d.ts +11 -0
  112. package/CHANGELOG.md +0 -2222
@@ -4,7 +4,6 @@ import postcss from 'postcss'
4
4
  import dlv from 'dlv'
5
5
  import selectorParser from 'postcss-selector-parser'
6
6
 
7
- import { flagEnabled } from '../featureFlags.js'
8
7
  import transformThemeValue from '../util/transformThemeValue'
9
8
  import parseObjectStyles from '../util/parseObjectStyles'
10
9
  import prefixSelector from '../util/prefixSelector'
@@ -12,7 +11,6 @@ import isPlainObject from '../util/isPlainObject'
12
11
  import escapeClassName from '../util/escapeClassName'
13
12
  import nameClass, { formatClass } from '../util/nameClass'
14
13
  import { coerceValue } from '../util/pluginUtils'
15
- import bigSign from '../util/bigSign'
16
14
  import { variantPlugins, corePlugins } from '../corePlugins'
17
15
  import * as sharedState from './sharedState'
18
16
  import { env } from './sharedState'
@@ -20,16 +18,41 @@ import { toPath } from '../util/toPath'
20
18
  import log from '../util/log'
21
19
  import negateValue from '../util/negateValue'
22
20
  import isValidArbitraryValue from '../util/isValidArbitraryValue'
23
- import { generateRules } from './generateRules'
21
+ import { generateRules, getClassNameFromSelector } from './generateRules'
24
22
  import { hasContentChanged } from './cacheInvalidation.js'
23
+ import { Offsets } from './offsets.js'
24
+ import { flagEnabled } from '../featureFlags.js'
25
+ import { finalizeSelector, formatVariantSelector } from '../util/formatVariantSelector'
25
26
 
26
- let MATCH_VARIANT = Symbol()
27
+ const VARIANT_TYPES = {
28
+ AddVariant: Symbol.for('ADD_VARIANT'),
29
+ MatchVariant: Symbol.for('MATCH_VARIANT'),
30
+ }
31
+
32
+ const VARIANT_INFO = {
33
+ Base: 1 << 0,
34
+ Dynamic: 1 << 1,
35
+ }
27
36
 
28
37
  function prefix(context, selector) {
29
38
  let prefix = context.tailwindConfig.prefix
30
39
  return typeof prefix === 'function' ? prefix(selector) : prefix + selector
31
40
  }
32
41
 
42
+ function normalizeOptionTypes({ type = 'any', ...options }) {
43
+ let types = [].concat(type)
44
+
45
+ return {
46
+ ...options,
47
+ types: types.map((type) => {
48
+ if (Array.isArray(type)) {
49
+ return { type: type[0], ...type[1] }
50
+ }
51
+ return { type, preferOnConflict: false }
52
+ }),
53
+ }
54
+ }
55
+
33
56
  function parseVariantFormatString(input) {
34
57
  if (input.includes('{')) {
35
58
  if (!isBalanced(input)) throw new Error(`Your { and } are unbalanced.`)
@@ -201,6 +224,13 @@ export function parseVariant(variant) {
201
224
  }
202
225
  }
203
226
 
227
+ /**
228
+ *
229
+ * @param {any} tailwindConfig
230
+ * @param {any} context
231
+ * @param {object} param2
232
+ * @param {Offsets} param2.offsets
233
+ */
204
234
  function buildPluginApi(tailwindConfig, context, { variantList, variantMap, offsets, classList }) {
205
235
  function getConfigValue(path, defaultValue) {
206
236
  return path ? dlv(tailwindConfig, path, defaultValue) : tailwindConfig
@@ -235,6 +265,7 @@ function buildPluginApi(tailwindConfig, context, { variantList, variantMap, offs
235
265
  }
236
266
  )
237
267
 
268
+ let variantIdentifier = 0
238
269
  let api = {
239
270
  postcss,
240
271
  prefix: applyConfiguredPrefix,
@@ -255,7 +286,7 @@ function buildPluginApi(tailwindConfig, context, { variantList, variantMap, offs
255
286
  addBase(base) {
256
287
  for (let [identifier, rule] of withIdentifiers(base)) {
257
288
  let prefixedIdentifier = prefixIdentifier(identifier, {})
258
- let offset = offsets.base++
289
+ let offset = offsets.create('base')
259
290
 
260
291
  if (!context.candidateRuleMap.has(prefixedIdentifier)) {
261
292
  context.candidateRuleMap.set(prefixedIdentifier, [])
@@ -284,7 +315,7 @@ function buildPluginApi(tailwindConfig, context, { variantList, variantMap, offs
284
315
 
285
316
  context.candidateRuleMap
286
317
  .get(prefixedIdentifier)
287
- .push([{ sort: offsets.base++, layer: 'defaults' }, rule])
318
+ .push([{ sort: offsets.create('defaults'), layer: 'defaults' }, rule])
288
319
  }
289
320
  },
290
321
  addComponents(components, options) {
@@ -307,7 +338,7 @@ function buildPluginApi(tailwindConfig, context, { variantList, variantMap, offs
307
338
 
308
339
  context.candidateRuleMap
309
340
  .get(prefixedIdentifier)
310
- .push([{ sort: offsets.components++, layer: 'components', options }, rule])
341
+ .push([{ sort: offsets.create('components'), layer: 'components', options }, rule])
311
342
  }
312
343
  },
313
344
  addUtilities(utilities, options) {
@@ -330,18 +361,19 @@ function buildPluginApi(tailwindConfig, context, { variantList, variantMap, offs
330
361
 
331
362
  context.candidateRuleMap
332
363
  .get(prefixedIdentifier)
333
- .push([{ sort: offsets.utilities++, layer: 'utilities', options }, rule])
364
+ .push([{ sort: offsets.create('utilities'), layer: 'utilities', options }, rule])
334
365
  }
335
366
  },
336
367
  matchUtilities: function (utilities, options) {
337
368
  let defaultOptions = {
338
369
  respectPrefix: true,
339
370
  respectImportant: true,
371
+ modifiers: false,
340
372
  }
341
373
 
342
- options = { ...defaultOptions, ...options }
374
+ options = normalizeOptionTypes({ ...defaultOptions, ...options })
343
375
 
344
- let offset = offsets.utilities++
376
+ let offset = offsets.create('utilities')
345
377
 
346
378
  for (let identifier in utilities) {
347
379
  let prefixedIdentifier = prefixIdentifier(identifier, options)
@@ -350,24 +382,51 @@ function buildPluginApi(tailwindConfig, context, { variantList, variantMap, offs
350
382
  classList.add([prefixedIdentifier, options])
351
383
 
352
384
  function wrapped(modifier, { isOnlyPlugin }) {
353
- let { type = 'any' } = options
354
- type = [].concat(type)
355
- let [value, coercedType] = coerceValue(type, modifier, options, tailwindConfig)
385
+ let [value, coercedType, utilityModifier] = coerceValue(
386
+ options.types,
387
+ modifier,
388
+ options,
389
+ tailwindConfig
390
+ )
356
391
 
357
392
  if (value === undefined) {
358
393
  return []
359
394
  }
360
395
 
361
- if (!type.includes(coercedType) && !isOnlyPlugin) {
362
- return []
396
+ if (!options.types.some(({ type }) => type === coercedType)) {
397
+ if (isOnlyPlugin) {
398
+ log.warn([
399
+ `Unnecessary typehint \`${coercedType}\` in \`${identifier}-${modifier}\`.`,
400
+ `You can safely update it to \`${identifier}-${modifier.replace(
401
+ coercedType + ':',
402
+ ''
403
+ )}\`.`,
404
+ ])
405
+ } else {
406
+ return []
407
+ }
363
408
  }
364
409
 
365
410
  if (!isValidArbitraryValue(value)) {
366
411
  return []
367
412
  }
368
413
 
414
+ let extras = {
415
+ get modifier() {
416
+ if (!options.modifiers) {
417
+ log.warn(`modifier-used-without-options-for-${identifier}`, [
418
+ 'Your plugin must set `modifiers: true` in its options to support modifiers.',
419
+ ])
420
+ }
421
+
422
+ return utilityModifier
423
+ },
424
+ }
425
+
426
+ let modifiersEnabled = flagEnabled(tailwindConfig, 'generalizedModifiers')
427
+
369
428
  let ruleSets = []
370
- .concat(rule(value))
429
+ .concat(modifiersEnabled ? rule(value, extras) : rule(value))
371
430
  .filter(Boolean)
372
431
  .map((declaration) => ({
373
432
  [nameClass(identifier, modifier)]: declaration,
@@ -389,11 +448,12 @@ function buildPluginApi(tailwindConfig, context, { variantList, variantMap, offs
389
448
  let defaultOptions = {
390
449
  respectPrefix: true,
391
450
  respectImportant: false,
451
+ modifiers: false,
392
452
  }
393
453
 
394
- options = { ...defaultOptions, ...options }
454
+ options = normalizeOptionTypes({ ...defaultOptions, ...options })
395
455
 
396
- let offset = offsets.components++
456
+ let offset = offsets.create('components')
397
457
 
398
458
  for (let identifier in components) {
399
459
  let prefixedIdentifier = prefixIdentifier(identifier, options)
@@ -402,15 +462,18 @@ function buildPluginApi(tailwindConfig, context, { variantList, variantMap, offs
402
462
  classList.add([prefixedIdentifier, options])
403
463
 
404
464
  function wrapped(modifier, { isOnlyPlugin }) {
405
- let { type = 'any' } = options
406
- type = [].concat(type)
407
- let [value, coercedType] = coerceValue(type, modifier, options, tailwindConfig)
465
+ let [value, coercedType, utilityModifier] = coerceValue(
466
+ options.types,
467
+ modifier,
468
+ options,
469
+ tailwindConfig
470
+ )
408
471
 
409
472
  if (value === undefined) {
410
473
  return []
411
474
  }
412
475
 
413
- if (!type.includes(coercedType)) {
476
+ if (!options.types.some(({ type }) => type === coercedType)) {
414
477
  if (isOnlyPlugin) {
415
478
  log.warn([
416
479
  `Unnecessary typehint \`${coercedType}\` in \`${identifier}-${modifier}\`.`,
@@ -428,8 +491,22 @@ function buildPluginApi(tailwindConfig, context, { variantList, variantMap, offs
428
491
  return []
429
492
  }
430
493
 
494
+ let extras = {
495
+ get modifier() {
496
+ if (!options.modifiers) {
497
+ log.warn(`modifier-used-without-options-for-${identifier}`, [
498
+ 'Your plugin must set `modifiers: true` in its options to support modifiers.',
499
+ ])
500
+ }
501
+
502
+ return utilityModifier
503
+ },
504
+ }
505
+
506
+ let modifiersEnabled = flagEnabled(tailwindConfig, 'generalizedModifiers')
507
+
431
508
  let ruleSets = []
432
- .concat(rule(value))
509
+ .concat(modifiersEnabled ? rule(value, extras) : rule(value))
433
510
  .filter(Boolean)
434
511
  .map((declaration) => ({
435
512
  [nameClass(identifier, modifier)]: declaration,
@@ -456,7 +533,7 @@ function buildPluginApi(tailwindConfig, context, { variantList, variantMap, offs
456
533
  let result = variantFunction(
457
534
  Object.assign(
458
535
  { modifySelectors, container, separator },
459
- variantFunction[MATCH_VARIANT] && { args, wrap, format }
536
+ options.type === VARIANT_TYPES.MatchVariant && { args, wrap, format }
460
537
  )
461
538
  )
462
539
 
@@ -489,23 +566,58 @@ function buildPluginApi(tailwindConfig, context, { variantList, variantMap, offs
489
566
 
490
567
  insertInto(variantList, variantName, options)
491
568
  variantMap.set(variantName, variantFunctions)
569
+ context.variantOptions.set(variantName, options)
492
570
  },
493
- }
571
+ matchVariant(variant, variantFn, options) {
572
+ // A unique identifier that "groups" these variants together.
573
+ // This is for internal use only which is why it is not present in the types
574
+ let id = options?.id ?? ++variantIdentifier
575
+ let isSpecial = variant === '@'
494
576
 
495
- if (flagEnabled(tailwindConfig, 'matchVariant')) {
496
- api.matchVariant = function (variants, options) {
497
- for (let variant in variants) {
498
- for (let [k, v] of Object.entries(options?.values ?? {})) {
499
- api.addVariant(`${variant}-${k}`, variants[variant](v))
500
- }
577
+ let modifiersEnabled = flagEnabled(tailwindConfig, 'generalizedModifiers')
578
+
579
+ for (let [key, value] of Object.entries(options?.values ?? {})) {
580
+ if (key === 'DEFAULT') continue
501
581
 
502
582
  api.addVariant(
503
- variant,
504
- Object.assign(({ args }) => variants[variant](args), { [MATCH_VARIANT]: true }),
505
- options
583
+ isSpecial ? `${variant}${key}` : `${variant}-${key}`,
584
+ ({ args, container }) =>
585
+ variantFn(
586
+ value,
587
+ modifiersEnabled ? { modifier: args.modifier, container } : { container }
588
+ ),
589
+ {
590
+ ...options,
591
+ value,
592
+ id,
593
+ type: VARIANT_TYPES.MatchVariant,
594
+ variantInfo: VARIANT_INFO.Base,
595
+ }
506
596
  )
507
597
  }
508
- }
598
+
599
+ let hasDefault = 'DEFAULT' in (options?.values ?? {})
600
+
601
+ api.addVariant(
602
+ variant,
603
+ ({ args, container }) => {
604
+ if (args.value === sharedState.NONE && !hasDefault) {
605
+ return null
606
+ }
607
+
608
+ return variantFn(
609
+ args.value === sharedState.NONE ? options.values.DEFAULT : args.value,
610
+ modifiersEnabled ? { modifier: args.modifier, container } : { container }
611
+ )
612
+ },
613
+ {
614
+ ...options,
615
+ id,
616
+ type: VARIANT_TYPES.MatchVariant,
617
+ variantInfo: VARIANT_INFO.Dynamic,
618
+ }
619
+ )
620
+ },
509
621
  }
510
622
 
511
623
  return api
@@ -624,8 +736,11 @@ function resolvePlugins(context, root) {
624
736
  let beforeVariants = [
625
737
  variantPlugins['pseudoElementVariants'],
626
738
  variantPlugins['pseudoClassVariants'],
739
+ variantPlugins['ariaVariants'],
740
+ variantPlugins['dataVariants'],
627
741
  ]
628
742
  let afterVariants = [
743
+ variantPlugins['supportsVariants'],
629
744
  variantPlugins['directionVariants'],
630
745
  variantPlugins['reducedMotionVariants'],
631
746
  variantPlugins['prefersContrastVariants'],
@@ -641,13 +756,10 @@ function resolvePlugins(context, root) {
641
756
  function registerPlugins(plugins, context) {
642
757
  let variantList = []
643
758
  let variantMap = new Map()
644
- let offsets = {
645
- defaults: 0n,
646
- base: 0n,
647
- components: 0n,
648
- utilities: 0n,
649
- user: 0n,
650
- }
759
+ context.variantMap = variantMap
760
+
761
+ let offsets = new Offsets()
762
+ context.offsets = offsets
651
763
 
652
764
  let classList = new Set()
653
765
 
@@ -668,49 +780,17 @@ function registerPlugins(plugins, context) {
668
780
  }
669
781
  }
670
782
 
671
- let highestOffset = ((args) => args.reduce((m, e) => (e > m ? e : m)))([
672
- offsets.base,
673
- offsets.defaults,
674
- offsets.components,
675
- offsets.utilities,
676
- offsets.user,
677
- ])
678
- let reservedBits = BigInt(highestOffset.toString(2).length)
679
-
680
- // A number one less than the top range of the highest offset area
681
- // so arbitrary properties are always sorted at the end.
682
- context.arbitraryPropertiesSort = ((1n << reservedBits) << 0n) - 1n
683
-
684
- context.layerOrder = {
685
- defaults: (1n << reservedBits) << 0n,
686
- base: (1n << reservedBits) << 1n,
687
- components: (1n << reservedBits) << 2n,
688
- utilities: (1n << reservedBits) << 3n,
689
- user: (1n << reservedBits) << 4n,
690
- }
691
-
692
- reservedBits += 5n
693
-
694
- let offset = 0
695
- context.variantOrder = new Map(
696
- variantList
697
- .map((variant, i) => {
698
- let variantFunctions = variantMap.get(variant).length
699
- let bits = (1n << BigInt(i + offset)) << reservedBits
700
- offset += variantFunctions - 1
701
- return [variant, bits]
702
- })
703
- .sort(([, a], [, z]) => bigSign(a - z))
704
- )
705
-
706
- context.minimumScreen = [...context.variantOrder.values()].shift()
783
+ // Make sure to record bit masks for every variant
784
+ offsets.recordVariants(variantList, (variant) => variantMap.get(variant).length)
707
785
 
708
786
  // Build variantMap
709
787
  for (let [variantName, variantFunctions] of variantMap.entries()) {
710
- let sort = context.variantOrder.get(variantName)
711
788
  context.variantMap.set(
712
789
  variantName,
713
- variantFunctions.map((variantFunction, idx) => [sort << BigInt(idx), variantFunction])
790
+ variantFunctions.map((variantFunction, idx) => [
791
+ offsets.forVariant(variantName, idx),
792
+ variantFunction,
793
+ ])
714
794
  )
715
795
  }
716
796
 
@@ -764,7 +844,7 @@ function registerPlugins(plugins, context) {
764
844
  ]
765
845
  }
766
846
 
767
- if ([].concat(options?.type).includes('color')) {
847
+ if (options.types.some(({ type }) => type === 'color')) {
768
848
  classes = [
769
849
  ...classes,
770
850
  ...classes.flatMap((cls) =>
@@ -816,26 +896,41 @@ function registerPlugins(plugins, context) {
816
896
  }
817
897
  }
818
898
 
899
+ let darkClassName = [].concat(context.tailwindConfig.darkMode ?? 'media')[1] ?? 'dark'
900
+
819
901
  // A list of utilities that are used by certain Tailwind CSS utilities but
820
902
  // that don't exist on their own. This will result in them "not existing" and
821
903
  // sorting could be weird since you still require them in order to make the
822
- // host utitlies work properly. (Thanks Biology)
823
- let parasiteUtilities = new Set([prefix(context, 'group'), prefix(context, 'peer')])
904
+ // host utilities work properly. (Thanks Biology)
905
+ let parasiteUtilities = [
906
+ prefix(context, darkClassName),
907
+ prefix(context, 'group'),
908
+ prefix(context, 'peer'),
909
+ ]
824
910
  context.getClassOrder = function getClassOrder(classes) {
825
- let sortedClassNames = new Map()
826
- for (let [sort, rule] of generateRules(new Set(classes), context)) {
827
- if (sortedClassNames.has(rule.raws.tailwind.candidate)) continue
828
- sortedClassNames.set(rule.raws.tailwind.candidate, sort)
911
+ // Non-util classes won't be generated, so we default them to null
912
+ let sortedClassNames = new Map(classes.map((className) => [className, null]))
913
+
914
+ // Sort all classes in order
915
+ // Non-tailwind classes won't be generated and will be left as `null`
916
+ let rules = generateRules(new Set(classes), context)
917
+ rules = context.offsets.sort(rules)
918
+
919
+ let idx = BigInt(parasiteUtilities.length)
920
+
921
+ for (const [, rule] of rules) {
922
+ sortedClassNames.set(rule.raws.tailwind.candidate, idx++)
829
923
  }
830
924
 
831
925
  return classes.map((className) => {
832
926
  let order = sortedClassNames.get(className) ?? null
927
+ let parasiteIndex = parasiteUtilities.indexOf(className)
833
928
 
834
- if (order === null && parasiteUtilities.has(className)) {
929
+ if (order === null && parasiteIndex !== -1) {
835
930
  // This will make sure that it is at the very beginning of the
836
931
  // `components` layer which technically means 'before any
837
932
  // components'.
838
- order = context.layerOrder.components
933
+ order = BigInt(parasiteIndex)
839
934
  }
840
935
 
841
936
  return [className, order]
@@ -853,6 +948,11 @@ function registerPlugins(plugins, context) {
853
948
  let negativeClasses = []
854
949
 
855
950
  for (let [key, value] of Object.entries(options?.values ?? {})) {
951
+ // Ignore undefined and null values
952
+ if (value == null) {
953
+ continue
954
+ }
955
+
856
956
  output.push(formatClass(utilName, key))
857
957
  if (options?.supportsNegativeValues && negateValue(value)) {
858
958
  negativeClasses.push(formatClass(utilName, `-${key}`))
@@ -867,12 +967,191 @@ function registerPlugins(plugins, context) {
867
967
 
868
968
  return output
869
969
  }
970
+
971
+ // Generate a list of available variants with meta information of the type of variant.
972
+ context.getVariants = function getVariants() {
973
+ let result = []
974
+ for (let [name, options] of context.variantOptions.entries()) {
975
+ if (options.variantInfo === VARIANT_INFO.Base) continue
976
+
977
+ result.push({
978
+ name,
979
+ isArbitrary: options.type === Symbol.for('MATCH_VARIANT'),
980
+ values: Object.keys(options.values ?? {}),
981
+ hasDash: name !== '@',
982
+ selectors({ modifier, value } = {}) {
983
+ let candidate = '__TAILWIND_PLACEHOLDER__'
984
+
985
+ let rule = postcss.rule({ selector: `.${candidate}` })
986
+ let container = postcss.root({ nodes: [rule.clone()] })
987
+
988
+ let before = container.toString()
989
+
990
+ let fns = (context.variantMap.get(name) ?? []).flatMap(([_, fn]) => fn)
991
+ let formatStrings = []
992
+ for (let fn of fns) {
993
+ let localFormatStrings = []
994
+
995
+ let api = {
996
+ args: { modifier, value: options.values?.[value] ?? value },
997
+ separator: context.tailwindConfig.separator,
998
+ modifySelectors(modifierFunction) {
999
+ // Run the modifierFunction over each rule
1000
+ container.each((rule) => {
1001
+ if (rule.type !== 'rule') {
1002
+ return
1003
+ }
1004
+
1005
+ rule.selectors = rule.selectors.map((selector) => {
1006
+ return modifierFunction({
1007
+ get className() {
1008
+ return getClassNameFromSelector(selector)
1009
+ },
1010
+ selector,
1011
+ })
1012
+ })
1013
+ })
1014
+
1015
+ return container
1016
+ },
1017
+ format(str) {
1018
+ localFormatStrings.push(str)
1019
+ },
1020
+ wrap(wrapper) {
1021
+ localFormatStrings.push(`@${wrapper.name} ${wrapper.params} { & }`)
1022
+ },
1023
+ container,
1024
+ }
1025
+
1026
+ let ruleWithVariant = fn(api)
1027
+ if (localFormatStrings.length > 0) {
1028
+ formatStrings.push(localFormatStrings)
1029
+ }
1030
+
1031
+ if (Array.isArray(ruleWithVariant)) {
1032
+ for (let variantFunction of ruleWithVariant) {
1033
+ localFormatStrings = []
1034
+ variantFunction(api)
1035
+ formatStrings.push(localFormatStrings)
1036
+ }
1037
+ }
1038
+ }
1039
+
1040
+ // Reverse engineer the result of the `container`
1041
+ let manualFormatStrings = []
1042
+ let after = container.toString()
1043
+
1044
+ if (before !== after) {
1045
+ // Figure out all selectors
1046
+ container.walkRules((rule) => {
1047
+ let modified = rule.selector
1048
+
1049
+ // Rebuild the base selector, this is what plugin authors would do
1050
+ // as well. E.g.: `${variant}${separator}${className}`.
1051
+ // However, plugin authors probably also prepend or append certain
1052
+ // classes, pseudos, ids, ...
1053
+ let rebuiltBase = selectorParser((selectors) => {
1054
+ selectors.walkClasses((classNode) => {
1055
+ classNode.value = `${name}${context.tailwindConfig.separator}${classNode.value}`
1056
+ })
1057
+ }).processSync(modified)
1058
+
1059
+ // Now that we know the original selector, the new selector, and
1060
+ // the rebuild part in between, we can replace the part that plugin
1061
+ // authors need to rebuild with `&`, and eventually store it in the
1062
+ // collectedFormats. Similar to what `format('...')` would do.
1063
+ //
1064
+ // E.g.:
1065
+ // variant: foo
1066
+ // selector: .markdown > p
1067
+ // modified (by plugin): .foo .foo\\:markdown > p
1068
+ // rebuiltBase (internal): .foo\\:markdown > p
1069
+ // format: .foo &
1070
+ manualFormatStrings.push(modified.replace(rebuiltBase, '&').replace(candidate, '&'))
1071
+ })
1072
+
1073
+ // Figure out all atrules
1074
+ container.walkAtRules((atrule) => {
1075
+ manualFormatStrings.push(`@${atrule.name} (${atrule.params}) { & }`)
1076
+ })
1077
+ }
1078
+
1079
+ let result = formatStrings.map((formatString) =>
1080
+ finalizeSelector(formatVariantSelector('&', ...formatString), {
1081
+ selector: `.${candidate}`,
1082
+ candidate,
1083
+ context,
1084
+ isArbitraryVariant: !(value in (options.values ?? {})),
1085
+ })
1086
+ .replace(`.${candidate}`, '&')
1087
+ .replace('{ & }', '')
1088
+ .trim()
1089
+ )
1090
+
1091
+ if (manualFormatStrings.length > 0) {
1092
+ result.push(formatVariantSelector('&', ...manualFormatStrings))
1093
+ }
1094
+
1095
+ return result
1096
+ },
1097
+ })
1098
+ }
1099
+
1100
+ return result
1101
+ }
1102
+ }
1103
+
1104
+ /**
1105
+ * Mark as class as retroactively invalid
1106
+ *
1107
+ *
1108
+ * @param {string} candidate
1109
+ */
1110
+ function markInvalidUtilityCandidate(context, candidate) {
1111
+ if (!context.classCache.has(candidate)) {
1112
+ return
1113
+ }
1114
+
1115
+ // Mark this as not being a real utility
1116
+ context.notClassCache.add(candidate)
1117
+
1118
+ // Remove it from any candidate-specific caches
1119
+ context.classCache.delete(candidate)
1120
+ context.applyClassCache.delete(candidate)
1121
+ context.candidateRuleMap.delete(candidate)
1122
+ context.candidateRuleCache.delete(candidate)
1123
+
1124
+ // Ensure the stylesheet gets rebuilt
1125
+ context.stylesheetCache = null
1126
+ }
1127
+
1128
+ /**
1129
+ * Mark as class as retroactively invalid
1130
+ *
1131
+ * @param {import('postcss').Node} node
1132
+ */
1133
+ function markInvalidUtilityNode(context, node) {
1134
+ let candidate = node.raws.tailwind.candidate
1135
+
1136
+ if (!candidate) {
1137
+ return
1138
+ }
1139
+
1140
+ for (const entry of context.ruleCache) {
1141
+ if (entry[1].raws.tailwind.candidate === candidate) {
1142
+ context.ruleCache.delete(entry)
1143
+ // context.postCssNodeCache.delete(node)
1144
+ }
1145
+ }
1146
+
1147
+ markInvalidUtilityCandidate(context, candidate)
870
1148
  }
871
1149
 
872
1150
  export function createContext(tailwindConfig, changedContent = [], root = postcss.root()) {
873
1151
  let context = {
874
1152
  disposables: [],
875
1153
  ruleCache: new Set(),
1154
+ candidateRuleCache: new Map(),
876
1155
  classCache: new Map(),
877
1156
  applyClassCache: new Map(),
878
1157
  notClassCache: new Set(),
@@ -882,6 +1161,10 @@ export function createContext(tailwindConfig, changedContent = [], root = postcs
882
1161
  changedContent: changedContent,
883
1162
  variantMap: new Map(),
884
1163
  stylesheetCache: null,
1164
+ variantOptions: new Map(),
1165
+
1166
+ markInvalidUtilityCandidate: (candidate) => markInvalidUtilityCandidate(context, candidate),
1167
+ markInvalidUtilityNode: (node) => markInvalidUtilityNode(context, node),
885
1168
  }
886
1169
 
887
1170
  let resolvedPlugins = resolvePlugins(context, root)
@@ -960,6 +1243,10 @@ export function getContext(
960
1243
 
961
1244
  let context = createContext(tailwindConfig, [], root)
962
1245
 
1246
+ Object.assign(context, {
1247
+ userConfigPath,
1248
+ })
1249
+
963
1250
  trackModified([...contextDependencies], getFileModifiedMap(context))
964
1251
 
965
1252
  // ---