tailwindcss 0.0.0-insiders.ea80db2 → 0.0.0-insiders.eae2b7a

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 (213) hide show
  1. package/README.md +13 -9
  2. package/defaultTheme.d.ts +2 -1
  3. package/index.css +5 -0
  4. package/lib/cli/build/index.js +57 -0
  5. package/lib/cli/build/plugin.js +427 -0
  6. package/lib/cli/build/utils.js +88 -0
  7. package/lib/cli/build/watching.js +182 -0
  8. package/lib/cli/help/index.js +73 -0
  9. package/lib/cli/index.js +231 -0
  10. package/lib/cli/init/index.js +63 -0
  11. package/lib/cli.js +1 -759
  12. package/lib/corePluginList.js +12 -3
  13. package/lib/corePlugins.js +2308 -1739
  14. package/lib/css/preflight.css +13 -5
  15. package/lib/featureFlags.js +50 -25
  16. package/lib/index.js +1 -40
  17. package/lib/lib/cacheInvalidation.js +48 -25
  18. package/lib/lib/collapseAdjacentRules.js +17 -10
  19. package/lib/lib/collapseDuplicateDeclarations.js +13 -8
  20. package/lib/lib/content.js +207 -0
  21. package/lib/lib/defaultExtractor.js +232 -33
  22. package/lib/lib/detectNesting.js +21 -10
  23. package/lib/lib/evaluateTailwindFunctions.js +117 -50
  24. package/lib/lib/expandApplyAtRules.js +316 -215
  25. package/lib/lib/expandTailwindAtRules.js +167 -115
  26. package/lib/lib/findAtConfigPath.js +46 -0
  27. package/lib/lib/generateRules.js +444 -211
  28. package/lib/lib/getModuleDependencies.js +88 -37
  29. package/lib/lib/handleImportAtRules.js +50 -0
  30. package/lib/lib/load-config.js +42 -0
  31. package/lib/lib/normalizeTailwindDirectives.js +29 -39
  32. package/lib/lib/offsets.js +306 -0
  33. package/lib/lib/partitionApplyAtRules.js +13 -8
  34. package/lib/lib/regex.js +74 -0
  35. package/lib/lib/remap-bitfield.js +89 -0
  36. package/lib/lib/resolveDefaultsAtRules.js +96 -74
  37. package/lib/lib/setupContextUtils.js +672 -300
  38. package/lib/lib/setupTrackingContext.js +63 -68
  39. package/lib/lib/sharedState.js +44 -17
  40. package/lib/lib/substituteScreenAtRules.js +14 -10
  41. package/lib/plugin.js +160 -0
  42. package/lib/postcss-plugins/nesting/README.md +2 -2
  43. package/lib/postcss-plugins/nesting/index.js +10 -6
  44. package/lib/postcss-plugins/nesting/plugin.js +24 -20
  45. package/lib/processTailwindFeatures.js +34 -29
  46. package/lib/public/colors.js +272 -246
  47. package/lib/public/create-plugin.js +9 -5
  48. package/lib/public/default-config.js +10 -6
  49. package/lib/public/default-theme.js +10 -6
  50. package/lib/public/load-config.js +12 -0
  51. package/lib/public/resolve-config.js +11 -6
  52. package/lib/util/applyImportantSelector.js +36 -0
  53. package/lib/util/bigSign.js +6 -1
  54. package/lib/util/buildMediaQuery.js +13 -6
  55. package/lib/util/cloneDeep.js +9 -6
  56. package/lib/util/cloneNodes.js +12 -3
  57. package/lib/util/color.js +66 -50
  58. package/lib/util/colorNames.js +752 -0
  59. package/lib/util/configurePlugins.js +7 -2
  60. package/lib/util/createPlugin.js +8 -5
  61. package/lib/util/createUtilityPlugin.js +13 -9
  62. package/lib/util/dataTypes.js +182 -111
  63. package/lib/util/defaults.js +12 -7
  64. package/lib/util/escapeClassName.js +13 -8
  65. package/lib/util/escapeCommas.js +7 -2
  66. package/lib/util/flattenColorPalette.js +11 -10
  67. package/lib/util/formatVariantSelector.js +228 -151
  68. package/lib/util/getAllConfigs.js +33 -12
  69. package/lib/util/hashConfig.js +9 -4
  70. package/lib/util/isKeyframeRule.js +7 -2
  71. package/lib/util/isPlainObject.js +7 -2
  72. package/lib/util/{isValidArbitraryValue.js → isSyntacticallyValidPropertyValue.js} +25 -15
  73. package/lib/util/log.js +40 -13
  74. package/lib/util/nameClass.js +27 -10
  75. package/lib/util/negateValue.js +25 -8
  76. package/lib/util/normalizeConfig.js +155 -87
  77. package/lib/util/normalizeScreens.js +127 -8
  78. package/lib/util/parseAnimationValue.js +44 -40
  79. package/lib/util/parseBoxShadowValue.js +33 -62
  80. package/lib/util/parseDependency.js +39 -55
  81. package/lib/util/parseGlob.js +36 -0
  82. package/lib/util/parseObjectStyles.js +15 -10
  83. package/lib/util/pluginUtils.js +154 -59
  84. package/lib/util/prefixSelector.js +29 -10
  85. package/lib/util/pseudoElements.js +209 -0
  86. package/lib/util/removeAlphaVariables.js +31 -0
  87. package/lib/util/resolveConfig.js +96 -95
  88. package/lib/util/resolveConfigPath.js +29 -10
  89. package/lib/util/responsive.js +11 -6
  90. package/lib/util/splitAtTopLevelOnly.js +51 -0
  91. package/lib/util/tap.js +6 -1
  92. package/lib/util/toColorValue.js +7 -2
  93. package/lib/util/toPath.js +22 -4
  94. package/lib/util/transformThemeValue.js +40 -26
  95. package/lib/util/validateConfig.js +48 -0
  96. package/lib/util/validateFormalSyntax.js +26 -0
  97. package/lib/util/withAlphaVariable.js +27 -15
  98. package/lib/value-parser/LICENSE +22 -0
  99. package/lib/value-parser/README.md +3 -0
  100. package/lib/value-parser/index.d.js +2 -0
  101. package/lib/value-parser/index.js +22 -0
  102. package/lib/value-parser/parse.js +259 -0
  103. package/lib/value-parser/stringify.js +38 -0
  104. package/lib/value-parser/unit.js +86 -0
  105. package/lib/value-parser/walk.js +16 -0
  106. package/loadConfig.d.ts +4 -0
  107. package/loadConfig.js +2 -0
  108. package/package.json +54 -52
  109. package/plugin.d.ts +10 -5
  110. package/resolveConfig.d.ts +12 -0
  111. package/scripts/generate-types.js +53 -0
  112. package/scripts/release-channel.js +18 -0
  113. package/scripts/release-notes.js +21 -0
  114. package/scripts/type-utils.js +27 -0
  115. package/src/cli/build/index.js +53 -0
  116. package/src/cli/build/plugin.js +465 -0
  117. package/src/cli/build/utils.js +76 -0
  118. package/src/cli/build/watching.js +229 -0
  119. package/src/cli/help/index.js +70 -0
  120. package/src/cli/index.js +217 -0
  121. package/src/cli/init/index.js +79 -0
  122. package/src/cli.js +1 -836
  123. package/src/corePluginList.js +1 -1
  124. package/src/corePlugins.js +576 -106
  125. package/src/css/preflight.css +13 -5
  126. package/src/featureFlags.js +17 -3
  127. package/src/index.js +1 -42
  128. package/src/lib/collapseAdjacentRules.js +5 -1
  129. package/src/lib/content.js +240 -0
  130. package/src/lib/defaultExtractor.js +202 -35
  131. package/src/lib/detectNesting.js +9 -1
  132. package/src/lib/evaluateTailwindFunctions.js +82 -8
  133. package/src/lib/expandApplyAtRules.js +322 -189
  134. package/src/lib/expandTailwindAtRules.js +82 -59
  135. package/src/lib/findAtConfigPath.js +48 -0
  136. package/src/lib/generateRules.js +413 -132
  137. package/src/lib/getModuleDependencies.js +70 -30
  138. package/src/lib/handleImportAtRules.js +34 -0
  139. package/src/lib/load-config.ts +31 -0
  140. package/src/lib/normalizeTailwindDirectives.js +0 -27
  141. package/src/lib/offsets.js +373 -0
  142. package/src/lib/regex.js +74 -0
  143. package/src/lib/remap-bitfield.js +82 -0
  144. package/src/lib/resolveDefaultsAtRules.js +53 -36
  145. package/src/lib/setupContextUtils.js +570 -151
  146. package/src/lib/setupTrackingContext.js +44 -58
  147. package/src/lib/sharedState.js +13 -4
  148. package/src/plugin.js +128 -0
  149. package/src/postcss-plugins/nesting/README.md +2 -2
  150. package/src/public/colors.js +22 -0
  151. package/src/public/default-config.js +1 -1
  152. package/src/public/default-theme.js +2 -2
  153. package/src/public/load-config.js +2 -0
  154. package/src/util/applyImportantSelector.js +27 -0
  155. package/src/util/buildMediaQuery.js +5 -3
  156. package/src/util/cloneNodes.js +5 -1
  157. package/src/util/color.js +34 -17
  158. package/src/util/colorNames.js +150 -0
  159. package/src/util/dataTypes.js +67 -23
  160. package/src/util/formatVariantSelector.js +264 -144
  161. package/src/util/getAllConfigs.js +21 -2
  162. package/src/util/{isValidArbitraryValue.js → isSyntacticallyValidPropertyValue.js} +1 -1
  163. package/src/util/log.js +25 -1
  164. package/src/util/nameClass.js +4 -0
  165. package/src/util/negateValue.js +11 -3
  166. package/src/util/normalizeConfig.js +78 -20
  167. package/src/util/normalizeScreens.js +99 -4
  168. package/src/util/parseBoxShadowValue.js +3 -50
  169. package/src/util/parseDependency.js +37 -42
  170. package/src/util/parseGlob.js +24 -0
  171. package/src/util/pluginUtils.js +118 -23
  172. package/src/util/prefixSelector.js +28 -10
  173. package/src/util/pseudoElements.js +167 -0
  174. package/src/util/removeAlphaVariables.js +24 -0
  175. package/src/util/resolveConfig.js +70 -62
  176. package/src/util/resolveConfigPath.js +12 -1
  177. package/src/util/splitAtTopLevelOnly.js +52 -0
  178. package/src/util/toPath.js +1 -1
  179. package/src/util/transformThemeValue.js +13 -3
  180. package/src/util/validateConfig.js +36 -0
  181. package/src/util/validateFormalSyntax.js +34 -0
  182. package/src/util/withAlphaVariable.js +1 -1
  183. package/src/value-parser/LICENSE +22 -0
  184. package/src/value-parser/README.md +3 -0
  185. package/src/value-parser/index.d.ts +177 -0
  186. package/src/value-parser/index.js +28 -0
  187. package/src/value-parser/parse.js +303 -0
  188. package/src/value-parser/stringify.js +41 -0
  189. package/src/value-parser/unit.js +118 -0
  190. package/src/value-parser/walk.js +18 -0
  191. package/stubs/.gitignore +1 -0
  192. package/stubs/.prettierrc.json +6 -0
  193. package/stubs/{defaultConfig.stub.js → config.full.js} +215 -165
  194. package/stubs/{simpleConfig.stub.js → config.simple.js} +1 -1
  195. package/stubs/{defaultPostCssConfig.stub.js → postcss.config.cjs} +0 -1
  196. package/stubs/postcss.config.js +5 -0
  197. package/stubs/tailwind.config.cjs +2 -0
  198. package/stubs/tailwind.config.js +2 -0
  199. package/stubs/tailwind.config.ts +3 -0
  200. package/types/config.d.ts +102 -56
  201. package/types/generated/colors.d.ts +22 -0
  202. package/types/generated/corePluginList.d.ts +1 -1
  203. package/types/generated/default-theme.d.ts +372 -0
  204. package/types/index.d.ts +7 -1
  205. package/CHANGELOG.md +0 -2099
  206. package/lib/cli-peer-dependencies.js +0 -15
  207. package/lib/constants.js +0 -37
  208. package/peers/index.js +0 -75156
  209. package/scripts/install-integrations.js +0 -27
  210. package/scripts/rebuildFixtures.js +0 -68
  211. package/src/cli-peer-dependencies.js +0 -9
  212. package/src/constants.js +0 -17
  213. package/types.d.ts +0 -1
@@ -11,49 +11,92 @@ import isPlainObject from '../util/isPlainObject'
11
11
  import escapeClassName from '../util/escapeClassName'
12
12
  import nameClass, { formatClass } from '../util/nameClass'
13
13
  import { coerceValue } from '../util/pluginUtils'
14
- import bigSign from '../util/bigSign'
15
14
  import { variantPlugins, corePlugins } from '../corePlugins'
16
15
  import * as sharedState from './sharedState'
17
16
  import { env } from './sharedState'
18
17
  import { toPath } from '../util/toPath'
19
18
  import log from '../util/log'
20
19
  import negateValue from '../util/negateValue'
21
- import isValidArbitraryValue from '../util/isValidArbitraryValue'
22
- import { generateRules } from './generateRules'
20
+ import isSyntacticallyValidPropertyValue from '../util/isSyntacticallyValidPropertyValue'
21
+ import { generateRules, getClassNameFromSelector } from './generateRules'
23
22
  import { hasContentChanged } from './cacheInvalidation.js'
23
+ import { Offsets } from './offsets.js'
24
+ import { finalizeSelector, formatVariantSelector } from '../util/formatVariantSelector'
25
+
26
+ const VARIANT_TYPES = {
27
+ AddVariant: Symbol.for('ADD_VARIANT'),
28
+ MatchVariant: Symbol.for('MATCH_VARIANT'),
29
+ }
30
+
31
+ const VARIANT_INFO = {
32
+ Base: 1 << 0,
33
+ Dynamic: 1 << 1,
34
+ }
24
35
 
25
36
  function prefix(context, selector) {
26
37
  let prefix = context.tailwindConfig.prefix
27
38
  return typeof prefix === 'function' ? prefix(selector) : prefix + selector
28
39
  }
29
40
 
30
- function parseVariantFormatString(input) {
31
- if (input.includes('{')) {
32
- if (!isBalanced(input)) throw new Error(`Your { and } are unbalanced.`)
41
+ function normalizeOptionTypes({ type = 'any', ...options }) {
42
+ let types = [].concat(type)
33
43
 
34
- return input
35
- .split(/{(.*)}/gim)
36
- .flatMap((line) => parseVariantFormatString(line))
37
- .filter(Boolean)
44
+ return {
45
+ ...options,
46
+ types: types.map((type) => {
47
+ if (Array.isArray(type)) {
48
+ return { type: type[0], ...type[1] }
49
+ }
50
+ return { type, preferOnConflict: false }
51
+ }),
38
52
  }
39
-
40
- return [input.trim()]
41
53
  }
42
54
 
43
- function isBalanced(input) {
44
- let count = 0
45
-
46
- for (let char of input) {
47
- if (char === '{') {
48
- count++
55
+ function parseVariantFormatString(input) {
56
+ /** @type {string[]} */
57
+ let parts = []
58
+
59
+ // When parsing whitespace around special characters are insignificant
60
+ // However, _inside_ of a variant they could be
61
+ // Because the selector could look like this
62
+ // @media { &[data-name="foo bar"] }
63
+ // This is why we do not skip whitespace
64
+
65
+ let current = ''
66
+ let depth = 0
67
+
68
+ for (let idx = 0; idx < input.length; idx++) {
69
+ let char = input[idx]
70
+
71
+ if (char === '\\') {
72
+ // Escaped characters are not special
73
+ current += '\\' + input[++idx]
74
+ } else if (char === '{') {
75
+ // Nested rule: start
76
+ ++depth
77
+ parts.push(current.trim())
78
+ current = ''
49
79
  } else if (char === '}') {
50
- if (--count < 0) {
51
- return false // unbalanced
80
+ // Nested rule: end
81
+ if (--depth < 0) {
82
+ throw new Error(`Your { and } are unbalanced.`)
52
83
  }
84
+
85
+ parts.push(current.trim())
86
+ current = ''
87
+ } else {
88
+ // Normal character
89
+ current += char
53
90
  }
54
91
  }
55
92
 
56
- return count === 0
93
+ if (current.length > 0) {
94
+ parts.push(current.trim())
95
+ }
96
+
97
+ parts = parts.filter((part) => part !== '')
98
+
99
+ return parts
57
100
  }
58
101
 
59
102
  function insertInto(list, value, { before = [] } = {}) {
@@ -170,6 +213,41 @@ function withIdentifiers(styles) {
170
213
  })
171
214
  }
172
215
 
216
+ export function isValidVariantFormatString(format) {
217
+ return format.startsWith('@') || format.includes('&')
218
+ }
219
+
220
+ export function parseVariant(variant) {
221
+ variant = variant
222
+ .replace(/\n+/g, '')
223
+ .replace(/\s{1,}/g, ' ')
224
+ .trim()
225
+
226
+ let fns = parseVariantFormatString(variant)
227
+ .map((str) => {
228
+ if (!str.startsWith('@')) {
229
+ return ({ format }) => format(str)
230
+ }
231
+
232
+ let [, name, params] = /@(.*?)( .+|[({].*)/g.exec(str)
233
+ return ({ wrap }) => wrap(postcss.atRule({ name, params: params.trim() }))
234
+ })
235
+ .reverse()
236
+
237
+ return (api) => {
238
+ for (let fn of fns) {
239
+ fn(api)
240
+ }
241
+ }
242
+ }
243
+
244
+ /**
245
+ *
246
+ * @param {any} tailwindConfig
247
+ * @param {any} context
248
+ * @param {object} param2
249
+ * @param {Offsets} param2.offsets
250
+ */
173
251
  function buildPluginApi(tailwindConfig, context, { variantList, variantMap, offsets, classList }) {
174
252
  function getConfigValue(path, defaultValue) {
175
253
  return path ? dlv(tailwindConfig, path, defaultValue) : tailwindConfig
@@ -191,51 +269,19 @@ function buildPluginApi(tailwindConfig, context, { variantList, variantMap, offs
191
269
  return context.tailwindConfig.prefix + identifier
192
270
  }
193
271
 
194
- return {
195
- addVariant(variantName, variantFunctions, options = {}) {
196
- variantFunctions = [].concat(variantFunctions).map((variantFunction) => {
197
- if (typeof variantFunction !== 'string') {
198
- // Safelist public API functions
199
- return ({ modifySelectors, container, separator }) => {
200
- return variantFunction({ modifySelectors, container, separator })
201
- }
202
- }
203
-
204
- variantFunction = variantFunction
205
- .replace(/\n+/g, '')
206
- .replace(/\s{1,}/g, ' ')
207
- .trim()
208
-
209
- let fns = parseVariantFormatString(variantFunction)
210
- .map((str) => {
211
- if (!str.startsWith('@')) {
212
- return ({ format }) => format(str)
213
- }
214
-
215
- let [, name, params] = /@(.*?) (.*)/g.exec(str)
216
- return ({ wrap }) => wrap(postcss.atRule({ name, params }))
217
- })
218
- .reverse()
219
-
220
- return (api) => {
221
- for (let fn of fns) {
222
- fn(api)
223
- }
224
- }
225
- })
272
+ function resolveThemeValue(path, defaultValue, opts = {}) {
273
+ let parts = toPath(path)
274
+ let value = getConfigValue(['theme', ...parts], defaultValue)
275
+ return transformThemeValue(parts[0])(value, opts)
276
+ }
226
277
 
227
- insertInto(variantList, variantName, options)
228
- variantMap.set(variantName, variantFunctions)
229
- },
278
+ let variantIdentifier = 0
279
+ let api = {
230
280
  postcss,
231
281
  prefix: applyConfiguredPrefix,
232
282
  e: escapeClassName,
233
283
  config: getConfigValue,
234
- theme(path, defaultValue) {
235
- const [pathRoot, ...subPaths] = toPath(path)
236
- const value = getConfigValue(['theme', pathRoot, ...subPaths], defaultValue)
237
- return transformThemeValue(pathRoot)(value)
238
- },
284
+ theme: resolveThemeValue,
239
285
  corePlugins: (path) => {
240
286
  if (Array.isArray(tailwindConfig.corePlugins)) {
241
287
  return tailwindConfig.corePlugins.includes(path)
@@ -250,7 +296,7 @@ function buildPluginApi(tailwindConfig, context, { variantList, variantMap, offs
250
296
  addBase(base) {
251
297
  for (let [identifier, rule] of withIdentifiers(base)) {
252
298
  let prefixedIdentifier = prefixIdentifier(identifier, {})
253
- let offset = offsets.base++
299
+ let offset = offsets.create('base')
254
300
 
255
301
  if (!context.candidateRuleMap.has(prefixedIdentifier)) {
256
302
  context.candidateRuleMap.set(prefixedIdentifier, [])
@@ -279,11 +325,12 @@ function buildPluginApi(tailwindConfig, context, { variantList, variantMap, offs
279
325
 
280
326
  context.candidateRuleMap
281
327
  .get(prefixedIdentifier)
282
- .push([{ sort: offsets.base++, layer: 'defaults' }, rule])
328
+ .push([{ sort: offsets.create('defaults'), layer: 'defaults' }, rule])
283
329
  }
284
330
  },
285
331
  addComponents(components, options) {
286
332
  let defaultOptions = {
333
+ preserveSource: false,
287
334
  respectPrefix: true,
288
335
  respectImportant: false,
289
336
  }
@@ -301,11 +348,12 @@ function buildPluginApi(tailwindConfig, context, { variantList, variantMap, offs
301
348
 
302
349
  context.candidateRuleMap
303
350
  .get(prefixedIdentifier)
304
- .push([{ sort: offsets.components++, layer: 'components', options }, rule])
351
+ .push([{ sort: offsets.create('components'), layer: 'components', options }, rule])
305
352
  }
306
353
  },
307
354
  addUtilities(utilities, options) {
308
355
  let defaultOptions = {
356
+ preserveSource: false,
309
357
  respectPrefix: true,
310
358
  respectImportant: true,
311
359
  }
@@ -323,18 +371,19 @@ function buildPluginApi(tailwindConfig, context, { variantList, variantMap, offs
323
371
 
324
372
  context.candidateRuleMap
325
373
  .get(prefixedIdentifier)
326
- .push([{ sort: offsets.utilities++, layer: 'utilities', options }, rule])
374
+ .push([{ sort: offsets.create('utilities'), layer: 'utilities', options }, rule])
327
375
  }
328
376
  },
329
377
  matchUtilities: function (utilities, options) {
330
378
  let defaultOptions = {
331
379
  respectPrefix: true,
332
380
  respectImportant: true,
381
+ modifiers: false,
333
382
  }
334
383
 
335
- options = { ...defaultOptions, ...options }
384
+ options = normalizeOptionTypes({ ...defaultOptions, ...options })
336
385
 
337
- let offset = offsets.utilities++
386
+ let offset = offsets.create('utilities')
338
387
 
339
388
  for (let identifier in utilities) {
340
389
  let prefixedIdentifier = prefixIdentifier(identifier, options)
@@ -343,24 +392,49 @@ function buildPluginApi(tailwindConfig, context, { variantList, variantMap, offs
343
392
  classList.add([prefixedIdentifier, options])
344
393
 
345
394
  function wrapped(modifier, { isOnlyPlugin }) {
346
- let { type = 'any' } = options
347
- type = [].concat(type)
348
- let [value, coercedType] = coerceValue(type, modifier, options, tailwindConfig)
395
+ let [value, coercedType, utilityModifier] = coerceValue(
396
+ options.types,
397
+ modifier,
398
+ options,
399
+ tailwindConfig
400
+ )
349
401
 
350
402
  if (value === undefined) {
351
403
  return []
352
404
  }
353
405
 
354
- if (!type.includes(coercedType) && !isOnlyPlugin) {
355
- return []
406
+ if (!options.types.some(({ type }) => type === coercedType)) {
407
+ if (isOnlyPlugin) {
408
+ log.warn([
409
+ `Unnecessary typehint \`${coercedType}\` in \`${identifier}-${modifier}\`.`,
410
+ `You can safely update it to \`${identifier}-${modifier.replace(
411
+ coercedType + ':',
412
+ ''
413
+ )}\`.`,
414
+ ])
415
+ } else {
416
+ return []
417
+ }
356
418
  }
357
419
 
358
- if (!isValidArbitraryValue(value)) {
420
+ if (!isSyntacticallyValidPropertyValue(value)) {
359
421
  return []
360
422
  }
361
423
 
424
+ let extras = {
425
+ get modifier() {
426
+ if (!options.modifiers) {
427
+ log.warn(`modifier-used-without-options-for-${identifier}`, [
428
+ 'Your plugin must set `modifiers: true` in its options to support modifiers.',
429
+ ])
430
+ }
431
+
432
+ return utilityModifier
433
+ },
434
+ }
435
+
362
436
  let ruleSets = []
363
- .concat(rule(value))
437
+ .concat(rule(value, extras))
364
438
  .filter(Boolean)
365
439
  .map((declaration) => ({
366
440
  [nameClass(identifier, modifier)]: declaration,
@@ -382,11 +456,12 @@ function buildPluginApi(tailwindConfig, context, { variantList, variantMap, offs
382
456
  let defaultOptions = {
383
457
  respectPrefix: true,
384
458
  respectImportant: false,
459
+ modifiers: false,
385
460
  }
386
461
 
387
- options = { ...defaultOptions, ...options }
462
+ options = normalizeOptionTypes({ ...defaultOptions, ...options })
388
463
 
389
- let offset = offsets.components++
464
+ let offset = offsets.create('components')
390
465
 
391
466
  for (let identifier in components) {
392
467
  let prefixedIdentifier = prefixIdentifier(identifier, options)
@@ -395,15 +470,18 @@ function buildPluginApi(tailwindConfig, context, { variantList, variantMap, offs
395
470
  classList.add([prefixedIdentifier, options])
396
471
 
397
472
  function wrapped(modifier, { isOnlyPlugin }) {
398
- let { type = 'any' } = options
399
- type = [].concat(type)
400
- let [value, coercedType] = coerceValue(type, modifier, options, tailwindConfig)
473
+ let [value, coercedType, utilityModifier] = coerceValue(
474
+ options.types,
475
+ modifier,
476
+ options,
477
+ tailwindConfig
478
+ )
401
479
 
402
480
  if (value === undefined) {
403
481
  return []
404
482
  }
405
483
 
406
- if (!type.includes(coercedType)) {
484
+ if (!options.types.some(({ type }) => type === coercedType)) {
407
485
  if (isOnlyPlugin) {
408
486
  log.warn([
409
487
  `Unnecessary typehint \`${coercedType}\` in \`${identifier}-${modifier}\`.`,
@@ -417,12 +495,24 @@ function buildPluginApi(tailwindConfig, context, { variantList, variantMap, offs
417
495
  }
418
496
  }
419
497
 
420
- if (!isValidArbitraryValue(value)) {
498
+ if (!isSyntacticallyValidPropertyValue(value)) {
421
499
  return []
422
500
  }
423
501
 
502
+ let extras = {
503
+ get modifier() {
504
+ if (!options.modifiers) {
505
+ log.warn(`modifier-used-without-options-for-${identifier}`, [
506
+ 'Your plugin must set `modifiers: true` in its options to support modifiers.',
507
+ ])
508
+ }
509
+
510
+ return utilityModifier
511
+ },
512
+ }
513
+
424
514
  let ruleSets = []
425
- .concat(rule(value))
515
+ .concat(rule(value, extras))
426
516
  .filter(Boolean)
427
517
  .map((declaration) => ({
428
518
  [nameClass(identifier, modifier)]: declaration,
@@ -440,7 +530,104 @@ function buildPluginApi(tailwindConfig, context, { variantList, variantMap, offs
440
530
  context.candidateRuleMap.get(prefixedIdentifier).push(withOffsets)
441
531
  }
442
532
  },
533
+ addVariant(variantName, variantFunctions, options = {}) {
534
+ variantFunctions = [].concat(variantFunctions).map((variantFunction) => {
535
+ if (typeof variantFunction !== 'string') {
536
+ // Safelist public API functions
537
+ return (api = {}) => {
538
+ let { args, modifySelectors, container, separator, wrap, format } = api
539
+ let result = variantFunction(
540
+ Object.assign(
541
+ { modifySelectors, container, separator },
542
+ options.type === VARIANT_TYPES.MatchVariant && { args, wrap, format }
543
+ )
544
+ )
545
+
546
+ if (typeof result === 'string' && !isValidVariantFormatString(result)) {
547
+ throw new Error(
548
+ `Your custom variant \`${variantName}\` has an invalid format string. Make sure it's an at-rule or contains a \`&\` placeholder.`
549
+ )
550
+ }
551
+
552
+ if (Array.isArray(result)) {
553
+ return result
554
+ .filter((variant) => typeof variant === 'string')
555
+ .map((variant) => parseVariant(variant))
556
+ }
557
+
558
+ // result may be undefined with legacy variants that use APIs like `modifySelectors`
559
+ // result may also be a postcss node if someone was returning the result from `modifySelectors`
560
+ return result && typeof result === 'string' && parseVariant(result)(api)
561
+ }
562
+ }
563
+
564
+ if (!isValidVariantFormatString(variantFunction)) {
565
+ throw new Error(
566
+ `Your custom variant \`${variantName}\` has an invalid format string. Make sure it's an at-rule or contains a \`&\` placeholder.`
567
+ )
568
+ }
569
+
570
+ return parseVariant(variantFunction)
571
+ })
572
+
573
+ insertInto(variantList, variantName, options)
574
+ variantMap.set(variantName, variantFunctions)
575
+ context.variantOptions.set(variantName, options)
576
+ },
577
+ matchVariant(variant, variantFn, options) {
578
+ // A unique identifier that "groups" these variants together.
579
+ // This is for internal use only which is why it is not present in the types
580
+ let id = options?.id ?? ++variantIdentifier
581
+ let isSpecial = variant === '@'
582
+
583
+ for (let [key, value] of Object.entries(options?.values ?? {})) {
584
+ if (key === 'DEFAULT') continue
585
+
586
+ api.addVariant(
587
+ isSpecial ? `${variant}${key}` : `${variant}-${key}`,
588
+ ({ args, container }) => {
589
+ return variantFn(value, { modifier: args?.modifier, container })
590
+ },
591
+
592
+ {
593
+ ...options,
594
+ value,
595
+ id,
596
+ type: VARIANT_TYPES.MatchVariant,
597
+ variantInfo: VARIANT_INFO.Base,
598
+ }
599
+ )
600
+ }
601
+
602
+ let hasDefault = 'DEFAULT' in (options?.values ?? {})
603
+
604
+ api.addVariant(
605
+ variant,
606
+ ({ args, container }) => {
607
+ if (args?.value === sharedState.NONE && !hasDefault) {
608
+ return null
609
+ }
610
+
611
+ return variantFn(
612
+ args?.value === sharedState.NONE
613
+ ? options.values.DEFAULT
614
+ : // Falling back to args if it is a string, otherwise '' for older intellisense
615
+ // (JetBrains) plugins.
616
+ args?.value ?? (typeof args === 'string' ? args : ''),
617
+ { modifier: args?.modifier, container }
618
+ )
619
+ },
620
+ {
621
+ ...options,
622
+ id,
623
+ type: VARIANT_TYPES.MatchVariant,
624
+ variantInfo: VARIANT_INFO.Dynamic,
625
+ }
626
+ )
627
+ },
443
628
  }
629
+
630
+ return api
444
631
  }
445
632
 
446
633
  let fileModifiedMapCache = new WeakMap()
@@ -453,6 +640,7 @@ export function getFileModifiedMap(context) {
453
640
 
454
641
  function trackModified(files, fileModifiedMap) {
455
642
  let changed = false
643
+ let mtimesToCommit = new Map()
456
644
 
457
645
  for (let file of files) {
458
646
  if (!file) continue
@@ -473,10 +661,10 @@ function trackModified(files, fileModifiedMap) {
473
661
  changed = true
474
662
  }
475
663
 
476
- fileModifiedMap.set(file, newModified)
664
+ mtimesToCommit.set(file, newModified)
477
665
  }
478
666
 
479
- return changed
667
+ return [changed, mtimesToCommit]
480
668
  }
481
669
 
482
670
  function extractVariantAtRules(node) {
@@ -513,14 +701,14 @@ function collectLayerPlugins(root) {
513
701
  } else if (layerRule.params === 'components') {
514
702
  for (let node of layerRule.nodes) {
515
703
  layerPlugins.push(function ({ addComponents }) {
516
- addComponents(node, { respectPrefix: false })
704
+ addComponents(node, { respectPrefix: false, preserveSource: true })
517
705
  })
518
706
  }
519
707
  layerRule.remove()
520
708
  } else if (layerRule.params === 'utilities') {
521
709
  for (let node of layerRule.nodes) {
522
710
  layerPlugins.push(function ({ addUtilities }) {
523
- addUtilities(node, { respectPrefix: false })
711
+ addUtilities(node, { respectPrefix: false, preserveSource: true })
524
712
  })
525
713
  }
526
714
  layerRule.remove()
@@ -531,7 +719,7 @@ function collectLayerPlugins(root) {
531
719
  }
532
720
 
533
721
  function resolvePlugins(context, root) {
534
- let corePluginList = Object.entries({ ...variantPlugins, ...corePlugins })
722
+ let corePluginList = Object.entries(corePlugins)
535
723
  .map(([name, plugin]) => {
536
724
  if (!context.tailwindConfig.corePlugins.includes(name)) {
537
725
  return null
@@ -556,10 +744,15 @@ function resolvePlugins(context, root) {
556
744
  let beforeVariants = [
557
745
  variantPlugins['pseudoElementVariants'],
558
746
  variantPlugins['pseudoClassVariants'],
747
+ variantPlugins['hasVariants'],
748
+ variantPlugins['ariaVariants'],
749
+ variantPlugins['dataVariants'],
559
750
  ]
560
751
  let afterVariants = [
752
+ variantPlugins['supportsVariants'],
561
753
  variantPlugins['directionVariants'],
562
754
  variantPlugins['reducedMotionVariants'],
755
+ variantPlugins['prefersContrastVariants'],
563
756
  variantPlugins['darkVariants'],
564
757
  variantPlugins['printVariant'],
565
758
  variantPlugins['screenVariants'],
@@ -572,13 +765,10 @@ function resolvePlugins(context, root) {
572
765
  function registerPlugins(plugins, context) {
573
766
  let variantList = []
574
767
  let variantMap = new Map()
575
- let offsets = {
576
- defaults: 0n,
577
- base: 0n,
578
- components: 0n,
579
- utilities: 0n,
580
- user: 0n,
581
- }
768
+ context.variantMap = variantMap
769
+
770
+ let offsets = new Offsets()
771
+ context.offsets = offsets
582
772
 
583
773
  let classList = new Set()
584
774
 
@@ -599,49 +789,17 @@ function registerPlugins(plugins, context) {
599
789
  }
600
790
  }
601
791
 
602
- let highestOffset = ((args) => args.reduce((m, e) => (e > m ? e : m)))([
603
- offsets.base,
604
- offsets.defaults,
605
- offsets.components,
606
- offsets.utilities,
607
- offsets.user,
608
- ])
609
- let reservedBits = BigInt(highestOffset.toString(2).length)
610
-
611
- // A number one less than the top range of the highest offset area
612
- // so arbitrary properties are always sorted at the end.
613
- context.arbitraryPropertiesSort = ((1n << reservedBits) << 0n) - 1n
614
-
615
- context.layerOrder = {
616
- defaults: (1n << reservedBits) << 0n,
617
- base: (1n << reservedBits) << 1n,
618
- components: (1n << reservedBits) << 2n,
619
- utilities: (1n << reservedBits) << 3n,
620
- user: (1n << reservedBits) << 4n,
621
- }
622
-
623
- reservedBits += 5n
624
-
625
- let offset = 0
626
- context.variantOrder = new Map(
627
- variantList
628
- .map((variant, i) => {
629
- let variantFunctions = variantMap.get(variant).length
630
- let bits = (1n << BigInt(i + offset)) << reservedBits
631
- offset += variantFunctions - 1
632
- return [variant, bits]
633
- })
634
- .sort(([, a], [, z]) => bigSign(a - z))
635
- )
636
-
637
- context.minimumScreen = [...context.variantOrder.values()].shift()
792
+ // Make sure to record bit masks for every variant
793
+ offsets.recordVariants(variantList, (variant) => variantMap.get(variant).length)
638
794
 
639
795
  // Build variantMap
640
796
  for (let [variantName, variantFunctions] of variantMap.entries()) {
641
- let sort = context.variantOrder.get(variantName)
642
797
  context.variantMap.set(
643
798
  variantName,
644
- variantFunctions.map((variantFunction, idx) => [sort << BigInt(idx), variantFunction])
799
+ variantFunctions.map((variantFunction, idx) => [
800
+ offsets.forVariant(variantName, idx),
801
+ variantFunction,
802
+ ])
645
803
  )
646
804
  }
647
805
 
@@ -670,6 +828,7 @@ function registerPlugins(plugins, context) {
670
828
  if (checks.length > 0) {
671
829
  let patternMatchingCount = new Map()
672
830
  let prefixLength = context.tailwindConfig.prefix.length
831
+ let checkImportantUtils = checks.some((check) => check.pattern.source.includes('!'))
673
832
 
674
833
  for (let util of classList) {
675
834
  let utils = Array.isArray(util)
@@ -695,6 +854,21 @@ function registerPlugins(plugins, context) {
695
854
  ]
696
855
  }
697
856
 
857
+ if (options.types.some(({ type }) => type === 'color')) {
858
+ classes = [
859
+ ...classes,
860
+ ...classes.flatMap((cls) =>
861
+ Object.keys(context.tailwindConfig.theme.opacity).map(
862
+ (opacity) => `${cls}/${opacity}`
863
+ )
864
+ ),
865
+ ]
866
+ }
867
+
868
+ if (checkImportantUtils && options?.respectImportant) {
869
+ classes = [...classes, ...classes.map((cls) => '!' + cls)]
870
+ }
871
+
698
872
  return classes
699
873
  })()
700
874
  : [util]
@@ -736,26 +910,48 @@ function registerPlugins(plugins, context) {
736
910
  }
737
911
  }
738
912
 
913
+ let darkClassName = [].concat(context.tailwindConfig.darkMode ?? 'media')[1] ?? 'dark'
914
+
739
915
  // A list of utilities that are used by certain Tailwind CSS utilities but
740
916
  // that don't exist on their own. This will result in them "not existing" and
741
917
  // sorting could be weird since you still require them in order to make the
742
- // host utitlies work properly. (Thanks Biology)
743
- let parasiteUtilities = new Set([prefix(context, 'group'), prefix(context, 'peer')])
918
+ // host utilities work properly. (Thanks Biology)
919
+ let parasiteUtilities = [
920
+ prefix(context, darkClassName),
921
+ prefix(context, 'group'),
922
+ prefix(context, 'peer'),
923
+ ]
744
924
  context.getClassOrder = function getClassOrder(classes) {
745
- let sortedClassNames = new Map()
746
- for (let [sort, rule] of generateRules(new Set(classes), context)) {
747
- if (sortedClassNames.has(rule.raws.tailwind.candidate)) continue
748
- sortedClassNames.set(rule.raws.tailwind.candidate, sort)
925
+ // Sort classes so they're ordered in a deterministic manner
926
+ let sorted = [...classes].sort((a, z) => {
927
+ if (a === z) return 0
928
+ if (a < z) return -1
929
+ return 1
930
+ })
931
+
932
+ // Non-util classes won't be generated, so we default them to null
933
+ let sortedClassNames = new Map(sorted.map((className) => [className, null]))
934
+
935
+ // Sort all classes in order
936
+ // Non-tailwind classes won't be generated and will be left as `null`
937
+ let rules = generateRules(new Set(sorted), context)
938
+ rules = context.offsets.sort(rules)
939
+
940
+ let idx = BigInt(parasiteUtilities.length)
941
+
942
+ for (const [, rule] of rules) {
943
+ sortedClassNames.set(rule.raws.tailwind.candidate, idx++)
749
944
  }
750
945
 
751
946
  return classes.map((className) => {
752
947
  let order = sortedClassNames.get(className) ?? null
948
+ let parasiteIndex = parasiteUtilities.indexOf(className)
753
949
 
754
- if (order === null && parasiteUtilities.has(className)) {
950
+ if (order === null && parasiteIndex !== -1) {
755
951
  // This will make sure that it is at the very beginning of the
756
952
  // `components` layer which technically means 'before any
757
953
  // components'.
758
- order = context.layerOrder.components
954
+ order = BigInt(parasiteIndex)
759
955
  }
760
956
 
761
957
  return [className, order]
@@ -764,18 +960,35 @@ function registerPlugins(plugins, context) {
764
960
 
765
961
  // Generate a list of strings for autocompletion purposes, e.g.
766
962
  // ['uppercase', 'lowercase', ...]
767
- context.getClassList = function getClassList() {
963
+ context.getClassList = function getClassList(options = {}) {
768
964
  let output = []
769
965
 
770
966
  for (let util of classList) {
771
967
  if (Array.isArray(util)) {
772
- let [utilName, options] = util
968
+ let [utilName, utilOptions] = util
773
969
  let negativeClasses = []
774
970
 
775
- for (let [key, value] of Object.entries(options?.values ?? {})) {
776
- output.push(formatClass(utilName, key))
777
- if (options?.supportsNegativeValues && negateValue(value)) {
778
- negativeClasses.push(formatClass(utilName, `-${key}`))
971
+ let modifiers = Object.keys(utilOptions?.modifiers ?? {})
972
+
973
+ if (utilOptions?.types?.some(({ type }) => type === 'color')) {
974
+ modifiers.push(...Object.keys(context.tailwindConfig.theme.opacity ?? {}))
975
+ }
976
+
977
+ let metadata = { modifiers }
978
+ let includeMetadata = options.includeMetadata && modifiers.length > 0
979
+
980
+ for (let [key, value] of Object.entries(utilOptions?.values ?? {})) {
981
+ // Ignore undefined and null values
982
+ if (value == null) {
983
+ continue
984
+ }
985
+
986
+ let cls = formatClass(utilName, key)
987
+ output.push(includeMetadata ? [cls, metadata] : cls)
988
+
989
+ if (utilOptions?.supportsNegativeValues && negateValue(value)) {
990
+ let cls = formatClass(utilName, `-${key}`)
991
+ negativeClasses.push(includeMetadata ? [cls, metadata] : cls)
779
992
  }
780
993
  }
781
994
 
@@ -787,21 +1000,223 @@ function registerPlugins(plugins, context) {
787
1000
 
788
1001
  return output
789
1002
  }
1003
+
1004
+ // Generate a list of available variants with meta information of the type of variant.
1005
+ context.getVariants = function getVariants() {
1006
+ let result = []
1007
+ for (let [name, options] of context.variantOptions.entries()) {
1008
+ if (options.variantInfo === VARIANT_INFO.Base) continue
1009
+
1010
+ result.push({
1011
+ name,
1012
+ isArbitrary: options.type === Symbol.for('MATCH_VARIANT'),
1013
+ values: Object.keys(options.values ?? {}),
1014
+ hasDash: name !== '@',
1015
+ selectors({ modifier, value } = {}) {
1016
+ let candidate = '__TAILWIND_PLACEHOLDER__'
1017
+
1018
+ let rule = postcss.rule({ selector: `.${candidate}` })
1019
+ let container = postcss.root({ nodes: [rule.clone()] })
1020
+
1021
+ let before = container.toString()
1022
+
1023
+ let fns = (context.variantMap.get(name) ?? []).flatMap(([_, fn]) => fn)
1024
+ let formatStrings = []
1025
+ for (let fn of fns) {
1026
+ let localFormatStrings = []
1027
+
1028
+ let api = {
1029
+ args: { modifier, value: options.values?.[value] ?? value },
1030
+ separator: context.tailwindConfig.separator,
1031
+ modifySelectors(modifierFunction) {
1032
+ // Run the modifierFunction over each rule
1033
+ container.each((rule) => {
1034
+ if (rule.type !== 'rule') {
1035
+ return
1036
+ }
1037
+
1038
+ rule.selectors = rule.selectors.map((selector) => {
1039
+ return modifierFunction({
1040
+ get className() {
1041
+ return getClassNameFromSelector(selector)
1042
+ },
1043
+ selector,
1044
+ })
1045
+ })
1046
+ })
1047
+
1048
+ return container
1049
+ },
1050
+ format(str) {
1051
+ localFormatStrings.push(str)
1052
+ },
1053
+ wrap(wrapper) {
1054
+ localFormatStrings.push(`@${wrapper.name} ${wrapper.params} { & }`)
1055
+ },
1056
+ container,
1057
+ }
1058
+
1059
+ let ruleWithVariant = fn(api)
1060
+ if (localFormatStrings.length > 0) {
1061
+ formatStrings.push(localFormatStrings)
1062
+ }
1063
+
1064
+ if (Array.isArray(ruleWithVariant)) {
1065
+ for (let variantFunction of ruleWithVariant) {
1066
+ localFormatStrings = []
1067
+ variantFunction(api)
1068
+ formatStrings.push(localFormatStrings)
1069
+ }
1070
+ }
1071
+ }
1072
+
1073
+ // Reverse engineer the result of the `container`
1074
+ let manualFormatStrings = []
1075
+ let after = container.toString()
1076
+
1077
+ if (before !== after) {
1078
+ // Figure out all selectors
1079
+ container.walkRules((rule) => {
1080
+ let modified = rule.selector
1081
+
1082
+ // Rebuild the base selector, this is what plugin authors would do
1083
+ // as well. E.g.: `${variant}${separator}${className}`.
1084
+ // However, plugin authors probably also prepend or append certain
1085
+ // classes, pseudos, ids, ...
1086
+ let rebuiltBase = selectorParser((selectors) => {
1087
+ selectors.walkClasses((classNode) => {
1088
+ classNode.value = `${name}${context.tailwindConfig.separator}${classNode.value}`
1089
+ })
1090
+ }).processSync(modified)
1091
+
1092
+ // Now that we know the original selector, the new selector, and
1093
+ // the rebuild part in between, we can replace the part that plugin
1094
+ // authors need to rebuild with `&`, and eventually store it in the
1095
+ // collectedFormats. Similar to what `format('...')` would do.
1096
+ //
1097
+ // E.g.:
1098
+ // variant: foo
1099
+ // selector: .markdown > p
1100
+ // modified (by plugin): .foo .foo\\:markdown > p
1101
+ // rebuiltBase (internal): .foo\\:markdown > p
1102
+ // format: .foo &
1103
+ manualFormatStrings.push(modified.replace(rebuiltBase, '&').replace(candidate, '&'))
1104
+ })
1105
+
1106
+ // Figure out all atrules
1107
+ container.walkAtRules((atrule) => {
1108
+ manualFormatStrings.push(`@${atrule.name} (${atrule.params}) { & }`)
1109
+ })
1110
+ }
1111
+
1112
+ let isArbitraryVariant = !(value in (options.values ?? {}))
1113
+
1114
+ formatStrings = formatStrings.map((format) =>
1115
+ format.map((str) => ({
1116
+ format: str,
1117
+ isArbitraryVariant,
1118
+ }))
1119
+ )
1120
+
1121
+ manualFormatStrings = manualFormatStrings.map((format) => ({
1122
+ format,
1123
+ isArbitraryVariant,
1124
+ }))
1125
+
1126
+ let opts = {
1127
+ candidate,
1128
+ context,
1129
+ }
1130
+
1131
+ let result = formatStrings.map((formats) =>
1132
+ finalizeSelector(`.${candidate}`, formatVariantSelector(formats, opts), opts)
1133
+ .replace(`.${candidate}`, '&')
1134
+ .replace('{ & }', '')
1135
+ .trim()
1136
+ )
1137
+
1138
+ if (manualFormatStrings.length > 0) {
1139
+ result.push(
1140
+ formatVariantSelector(manualFormatStrings, opts)
1141
+ .toString()
1142
+ .replace(`.${candidate}`, '&')
1143
+ )
1144
+ }
1145
+
1146
+ return result
1147
+ },
1148
+ })
1149
+ }
1150
+
1151
+ return result
1152
+ }
1153
+ }
1154
+
1155
+ /**
1156
+ * Mark as class as retroactively invalid
1157
+ *
1158
+ *
1159
+ * @param {string} candidate
1160
+ */
1161
+ function markInvalidUtilityCandidate(context, candidate) {
1162
+ if (!context.classCache.has(candidate)) {
1163
+ return
1164
+ }
1165
+
1166
+ // Mark this as not being a real utility
1167
+ context.notClassCache.add(candidate)
1168
+
1169
+ // Remove it from any candidate-specific caches
1170
+ context.classCache.delete(candidate)
1171
+ context.applyClassCache.delete(candidate)
1172
+ context.candidateRuleMap.delete(candidate)
1173
+ context.candidateRuleCache.delete(candidate)
1174
+
1175
+ // Ensure the stylesheet gets rebuilt
1176
+ context.stylesheetCache = null
1177
+ }
1178
+
1179
+ /**
1180
+ * Mark as class as retroactively invalid
1181
+ *
1182
+ * @param {import('postcss').Node} node
1183
+ */
1184
+ function markInvalidUtilityNode(context, node) {
1185
+ let candidate = node.raws.tailwind.candidate
1186
+
1187
+ if (!candidate) {
1188
+ return
1189
+ }
1190
+
1191
+ for (const entry of context.ruleCache) {
1192
+ if (entry[1].raws.tailwind.candidate === candidate) {
1193
+ context.ruleCache.delete(entry)
1194
+ // context.postCssNodeCache.delete(node)
1195
+ }
1196
+ }
1197
+
1198
+ markInvalidUtilityCandidate(context, candidate)
790
1199
  }
791
1200
 
792
1201
  export function createContext(tailwindConfig, changedContent = [], root = postcss.root()) {
793
1202
  let context = {
794
1203
  disposables: [],
795
1204
  ruleCache: new Set(),
1205
+ candidateRuleCache: new Map(),
796
1206
  classCache: new Map(),
797
1207
  applyClassCache: new Map(),
798
- notClassCache: new Set(),
1208
+ // Seed the not class cache with the blocklist (which is only strings)
1209
+ notClassCache: new Set(tailwindConfig.blocklist ?? []),
799
1210
  postCssNodeCache: new Map(),
800
1211
  candidateRuleMap: new Map(),
801
1212
  tailwindConfig,
802
1213
  changedContent: changedContent,
803
1214
  variantMap: new Map(),
804
1215
  stylesheetCache: null,
1216
+ variantOptions: new Map(),
1217
+
1218
+ markInvalidUtilityCandidate: (candidate) => markInvalidUtilityCandidate(context, candidate),
1219
+ markInvalidUtilityNode: (node) => markInvalidUtilityNode(context, node),
805
1220
  }
806
1221
 
807
1222
  let resolvedPlugins = resolvePlugins(context, root)
@@ -844,12 +1259,12 @@ export function getContext(
844
1259
  // If there's already a context in the cache and we don't need to
845
1260
  // reset the context, return the cached context.
846
1261
  if (existingContext) {
847
- let contextDependenciesChanged = trackModified(
1262
+ let [contextDependenciesChanged, mtimesToCommit] = trackModified(
848
1263
  [...contextDependencies],
849
1264
  getFileModifiedMap(existingContext)
850
1265
  )
851
1266
  if (!contextDependenciesChanged && !cssDidChange) {
852
- return [existingContext, false]
1267
+ return [existingContext, false, mtimesToCommit]
853
1268
  }
854
1269
  }
855
1270
 
@@ -880,7 +1295,11 @@ export function getContext(
880
1295
 
881
1296
  let context = createContext(tailwindConfig, [], root)
882
1297
 
883
- trackModified([...contextDependencies], getFileModifiedMap(context))
1298
+ Object.assign(context, {
1299
+ userConfigPath,
1300
+ })
1301
+
1302
+ let [, mtimesToCommit] = trackModified([...contextDependencies], getFileModifiedMap(context))
884
1303
 
885
1304
  // ---
886
1305
 
@@ -895,5 +1314,5 @@ export function getContext(
895
1314
 
896
1315
  contextSourcesMap.get(context).add(sourcePath)
897
1316
 
898
- return [context, true]
1317
+ return [context, true, mtimesToCommit]
899
1318
  }