tailwindcss 0.0.0-insiders.ee945bb → 0.0.0-insiders.ef2ebb2

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 (215) hide show
  1. package/README.md +13 -9
  2. package/colors.d.ts +3 -0
  3. package/defaultConfig.d.ts +3 -0
  4. package/defaultTheme.d.ts +4 -0
  5. package/index.css +5 -0
  6. package/lib/cli/build/index.js +57 -0
  7. package/lib/cli/build/plugin.js +433 -0
  8. package/lib/cli/build/utils.js +88 -0
  9. package/lib/cli/build/watching.js +182 -0
  10. package/lib/cli/help/index.js +73 -0
  11. package/lib/cli/index.js +231 -0
  12. package/lib/cli/init/index.js +63 -0
  13. package/lib/cli.js +1 -751
  14. package/lib/corePluginList.js +13 -3
  15. package/lib/corePlugins.js +2323 -1720
  16. package/lib/css/preflight.css +13 -5
  17. package/lib/featureFlags.js +50 -25
  18. package/lib/index.js +1 -40
  19. package/lib/lib/cacheInvalidation.js +48 -25
  20. package/lib/lib/collapseAdjacentRules.js +17 -10
  21. package/lib/lib/collapseDuplicateDeclarations.js +13 -8
  22. package/lib/lib/content.js +207 -0
  23. package/lib/lib/defaultExtractor.js +232 -33
  24. package/lib/lib/detectNesting.js +21 -10
  25. package/lib/lib/evaluateTailwindFunctions.js +117 -50
  26. package/lib/lib/expandApplyAtRules.js +316 -215
  27. package/lib/lib/expandTailwindAtRules.js +167 -115
  28. package/lib/lib/findAtConfigPath.js +46 -0
  29. package/lib/lib/generateRules.js +454 -206
  30. package/lib/lib/getModuleDependencies.js +88 -37
  31. package/lib/lib/handleImportAtRules.js +50 -0
  32. package/lib/lib/load-config.js +42 -0
  33. package/lib/lib/normalizeTailwindDirectives.js +29 -39
  34. package/lib/lib/offsets.js +306 -0
  35. package/lib/lib/partitionApplyAtRules.js +13 -8
  36. package/lib/lib/regex.js +74 -0
  37. package/lib/lib/remap-bitfield.js +89 -0
  38. package/lib/lib/resolveDefaultsAtRules.js +96 -74
  39. package/lib/lib/setupContextUtils.js +687 -301
  40. package/lib/lib/setupTrackingContext.js +63 -68
  41. package/lib/lib/sharedState.js +44 -17
  42. package/lib/lib/substituteScreenAtRules.js +14 -10
  43. package/lib/plugin.js +166 -0
  44. package/lib/postcss-plugins/nesting/README.md +2 -2
  45. package/lib/postcss-plugins/nesting/index.js +10 -6
  46. package/lib/postcss-plugins/nesting/plugin.js +24 -20
  47. package/lib/processTailwindFeatures.js +34 -29
  48. package/lib/public/colors.js +272 -246
  49. package/lib/public/create-plugin.js +9 -5
  50. package/lib/public/default-config.js +10 -6
  51. package/lib/public/default-theme.js +10 -6
  52. package/lib/public/load-config.js +12 -0
  53. package/lib/public/resolve-config.js +11 -6
  54. package/lib/util/applyImportantSelector.js +36 -0
  55. package/lib/util/bigSign.js +6 -1
  56. package/lib/util/buildMediaQuery.js +13 -6
  57. package/lib/util/cloneDeep.js +9 -6
  58. package/lib/util/cloneNodes.js +12 -3
  59. package/lib/util/color.js +70 -53
  60. package/lib/util/colorNames.js +752 -0
  61. package/lib/util/configurePlugins.js +7 -2
  62. package/lib/util/createPlugin.js +8 -5
  63. package/lib/util/createUtilityPlugin.js +13 -9
  64. package/lib/util/dataTypes.js +182 -111
  65. package/lib/util/defaults.js +12 -7
  66. package/lib/util/escapeClassName.js +13 -8
  67. package/lib/util/escapeCommas.js +7 -2
  68. package/lib/util/flattenColorPalette.js +11 -10
  69. package/lib/util/formatVariantSelector.js +228 -151
  70. package/lib/util/getAllConfigs.js +33 -12
  71. package/lib/util/hashConfig.js +9 -4
  72. package/lib/util/isKeyframeRule.js +7 -2
  73. package/lib/util/isPlainObject.js +7 -2
  74. package/lib/util/{isValidArbitraryValue.js → isSyntacticallyValidPropertyValue.js} +25 -15
  75. package/lib/util/log.js +40 -13
  76. package/lib/util/nameClass.js +27 -10
  77. package/lib/util/negateValue.js +25 -8
  78. package/lib/util/normalizeConfig.js +155 -87
  79. package/lib/util/normalizeScreens.js +127 -8
  80. package/lib/util/parseAnimationValue.js +44 -40
  81. package/lib/util/parseBoxShadowValue.js +33 -62
  82. package/lib/util/parseDependency.js +39 -55
  83. package/lib/util/parseGlob.js +36 -0
  84. package/lib/util/parseObjectStyles.js +15 -10
  85. package/lib/util/pluginUtils.js +154 -59
  86. package/lib/util/prefixSelector.js +29 -10
  87. package/lib/util/pseudoElements.js +209 -0
  88. package/lib/util/removeAlphaVariables.js +31 -0
  89. package/lib/util/resolveConfig.js +96 -95
  90. package/lib/util/resolveConfigPath.js +29 -10
  91. package/lib/util/responsive.js +11 -6
  92. package/lib/util/splitAtTopLevelOnly.js +51 -0
  93. package/lib/util/tap.js +6 -1
  94. package/lib/util/toColorValue.js +7 -2
  95. package/lib/util/toPath.js +22 -4
  96. package/lib/util/transformThemeValue.js +40 -26
  97. package/lib/util/validateConfig.js +48 -0
  98. package/lib/util/validateFormalSyntax.js +26 -0
  99. package/lib/util/withAlphaVariable.js +27 -15
  100. package/lib/value-parser/LICENSE +22 -0
  101. package/lib/value-parser/README.md +3 -0
  102. package/lib/value-parser/index.d.js +2 -0
  103. package/lib/value-parser/index.js +22 -0
  104. package/lib/value-parser/parse.js +259 -0
  105. package/lib/value-parser/stringify.js +38 -0
  106. package/lib/value-parser/unit.js +86 -0
  107. package/lib/value-parser/walk.js +16 -0
  108. package/loadConfig.d.ts +4 -0
  109. package/loadConfig.js +2 -0
  110. package/package.json +59 -53
  111. package/plugin.d.ts +11 -0
  112. package/resolveConfig.d.ts +12 -0
  113. package/scripts/generate-types.js +105 -0
  114. package/scripts/release-channel.js +18 -0
  115. package/scripts/release-notes.js +21 -0
  116. package/scripts/type-utils.js +27 -0
  117. package/src/cli/build/index.js +53 -0
  118. package/src/cli/build/plugin.js +476 -0
  119. package/src/cli/build/utils.js +76 -0
  120. package/src/cli/build/watching.js +229 -0
  121. package/src/cli/help/index.js +70 -0
  122. package/src/cli/index.js +217 -0
  123. package/src/cli/init/index.js +79 -0
  124. package/src/cli.js +1 -825
  125. package/src/corePluginList.js +1 -1
  126. package/src/corePlugins.js +614 -106
  127. package/src/css/preflight.css +13 -5
  128. package/src/featureFlags.js +17 -3
  129. package/src/index.js +1 -42
  130. package/src/lib/collapseAdjacentRules.js +5 -1
  131. package/src/lib/content.js +240 -0
  132. package/src/lib/defaultExtractor.js +202 -35
  133. package/src/lib/detectNesting.js +9 -1
  134. package/src/lib/evaluateTailwindFunctions.js +82 -8
  135. package/src/lib/expandApplyAtRules.js +322 -189
  136. package/src/lib/expandTailwindAtRules.js +82 -59
  137. package/src/lib/findAtConfigPath.js +48 -0
  138. package/src/lib/generateRules.js +427 -127
  139. package/src/lib/getModuleDependencies.js +70 -30
  140. package/src/lib/handleImportAtRules.js +34 -0
  141. package/src/lib/load-config.ts +31 -0
  142. package/src/lib/normalizeTailwindDirectives.js +0 -27
  143. package/src/lib/offsets.js +373 -0
  144. package/src/lib/regex.js +74 -0
  145. package/src/lib/remap-bitfield.js +82 -0
  146. package/src/lib/resolveDefaultsAtRules.js +53 -36
  147. package/src/lib/setupContextUtils.js +591 -156
  148. package/src/lib/setupTrackingContext.js +44 -58
  149. package/src/lib/sharedState.js +13 -4
  150. package/src/plugin.js +141 -0
  151. package/src/postcss-plugins/nesting/README.md +2 -2
  152. package/src/public/colors.js +22 -0
  153. package/src/public/default-config.js +1 -1
  154. package/src/public/default-theme.js +2 -2
  155. package/src/public/load-config.js +2 -0
  156. package/src/util/applyImportantSelector.js +27 -0
  157. package/src/util/buildMediaQuery.js +5 -3
  158. package/src/util/cloneNodes.js +5 -1
  159. package/src/util/color.js +39 -20
  160. package/src/util/colorNames.js +150 -0
  161. package/src/util/dataTypes.js +67 -23
  162. package/src/util/formatVariantSelector.js +264 -144
  163. package/src/util/getAllConfigs.js +21 -2
  164. package/src/util/{isValidArbitraryValue.js → isSyntacticallyValidPropertyValue.js} +1 -1
  165. package/src/util/log.js +25 -1
  166. package/src/util/nameClass.js +4 -0
  167. package/src/util/negateValue.js +11 -3
  168. package/src/util/normalizeConfig.js +78 -20
  169. package/src/util/normalizeScreens.js +99 -4
  170. package/src/util/parseBoxShadowValue.js +3 -50
  171. package/src/util/parseDependency.js +37 -42
  172. package/src/util/parseGlob.js +24 -0
  173. package/src/util/pluginUtils.js +118 -23
  174. package/src/util/prefixSelector.js +28 -10
  175. package/src/util/pseudoElements.js +167 -0
  176. package/src/util/removeAlphaVariables.js +24 -0
  177. package/src/util/resolveConfig.js +70 -62
  178. package/src/util/resolveConfigPath.js +12 -1
  179. package/src/util/splitAtTopLevelOnly.js +52 -0
  180. package/src/util/toPath.js +1 -1
  181. package/src/util/transformThemeValue.js +13 -3
  182. package/src/util/validateConfig.js +36 -0
  183. package/src/util/validateFormalSyntax.js +34 -0
  184. package/src/util/withAlphaVariable.js +1 -1
  185. package/src/value-parser/LICENSE +22 -0
  186. package/src/value-parser/README.md +3 -0
  187. package/src/value-parser/index.d.ts +177 -0
  188. package/src/value-parser/index.js +28 -0
  189. package/src/value-parser/parse.js +303 -0
  190. package/src/value-parser/stringify.js +41 -0
  191. package/src/value-parser/unit.js +118 -0
  192. package/src/value-parser/walk.js +18 -0
  193. package/stubs/.gitignore +1 -0
  194. package/stubs/.prettierrc.json +6 -0
  195. package/stubs/{defaultConfig.stub.js → config.full.js} +218 -165
  196. package/stubs/{simpleConfig.stub.js → config.simple.js} +1 -1
  197. package/stubs/{defaultPostCssConfig.stub.js → postcss.config.cjs} +0 -1
  198. package/stubs/postcss.config.js +5 -0
  199. package/stubs/tailwind.config.cjs +2 -0
  200. package/stubs/tailwind.config.js +2 -0
  201. package/stubs/tailwind.config.ts +3 -0
  202. package/types/config.d.ts +369 -0
  203. package/types/generated/.gitkeep +0 -0
  204. package/types/generated/colors.d.ts +298 -0
  205. package/types/generated/corePluginList.d.ts +1 -0
  206. package/types/generated/default-theme.d.ts +372 -0
  207. package/types/index.d.ts +7 -0
  208. package/CHANGELOG.md +0 -2090
  209. package/lib/cli-peer-dependencies.js +0 -15
  210. package/lib/constants.js +0 -37
  211. package/peers/index.js +0 -75077
  212. package/scripts/install-integrations.js +0 -27
  213. package/scripts/rebuildFixtures.js +0 -68
  214. package/src/cli-peer-dependencies.js +0 -9
  215. package/src/constants.js +0 -17
@@ -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 = [] } = {}) {
@@ -85,12 +128,18 @@ function parseStyles(styles) {
85
128
  })
86
129
  }
87
130
 
88
- function getClasses(selector) {
131
+ function getClasses(selector, mutate) {
89
132
  let parser = selectorParser((selectors) => {
90
133
  let allClasses = []
134
+
135
+ if (mutate) {
136
+ mutate(selectors)
137
+ }
138
+
91
139
  selectors.walkClasses((classNode) => {
92
140
  allClasses.push(classNode.value)
93
141
  })
142
+
94
143
  return allClasses
95
144
  })
96
145
  return parser.transformSync(selector)
@@ -101,8 +150,20 @@ function extractCandidates(node, state = { containsNonOnDemandable: false }, dep
101
150
 
102
151
  // Handle normal rules
103
152
  if (node.type === 'rule') {
153
+ // Ignore everything inside a :not(...). This allows you to write code like
154
+ // `div:not(.foo)`. If `.foo` is never found in your code, then we used to
155
+ // not generated it. But now we will ignore everything inside a `:not`, so
156
+ // that it still gets generated.
157
+ function ignoreNot(selectors) {
158
+ selectors.walkPseudos((pseudo) => {
159
+ if (pseudo.value === ':not') {
160
+ pseudo.remove()
161
+ }
162
+ })
163
+ }
164
+
104
165
  for (let selector of node.selectors) {
105
- let classCandidates = getClasses(selector)
166
+ let classCandidates = getClasses(selector, ignoreNot)
106
167
  // At least one of the selectors contains non-"on-demandable" candidates.
107
168
  if (classCandidates.length === 0) {
108
169
  state.containsNonOnDemandable = true
@@ -117,9 +178,7 @@ function extractCandidates(node, state = { containsNonOnDemandable: false }, dep
117
178
  // Handle at-rules (which contains nested rules)
118
179
  else if (node.type === 'atrule') {
119
180
  node.walkRules((rule) => {
120
- for (let classCandidate of rule.selectors.flatMap((selector) =>
121
- getClasses(selector, state, depth + 1)
122
- )) {
181
+ for (let classCandidate of rule.selectors.flatMap((selector) => getClasses(selector))) {
123
182
  classes.push(classCandidate)
124
183
  }
125
184
  })
@@ -154,6 +213,41 @@ function withIdentifiers(styles) {
154
213
  })
155
214
  }
156
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
+ */
157
251
  function buildPluginApi(tailwindConfig, context, { variantList, variantMap, offsets, classList }) {
158
252
  function getConfigValue(path, defaultValue) {
159
253
  return path ? dlv(tailwindConfig, path, defaultValue) : tailwindConfig
@@ -175,51 +269,19 @@ function buildPluginApi(tailwindConfig, context, { variantList, variantMap, offs
175
269
  return context.tailwindConfig.prefix + identifier
176
270
  }
177
271
 
178
- return {
179
- addVariant(variantName, variantFunctions, options = {}) {
180
- variantFunctions = [].concat(variantFunctions).map((variantFunction) => {
181
- if (typeof variantFunction !== 'string') {
182
- // Safelist public API functions
183
- return ({ modifySelectors, container, separator }) => {
184
- return variantFunction({ modifySelectors, container, separator })
185
- }
186
- }
187
-
188
- variantFunction = variantFunction
189
- .replace(/\n+/g, '')
190
- .replace(/\s{1,}/g, ' ')
191
- .trim()
192
-
193
- let fns = parseVariantFormatString(variantFunction)
194
- .map((str) => {
195
- if (!str.startsWith('@')) {
196
- return ({ format }) => format(str)
197
- }
198
-
199
- let [, name, params] = /@(.*?) (.*)/g.exec(str)
200
- return ({ wrap }) => wrap(postcss.atRule({ name, params }))
201
- })
202
- .reverse()
203
-
204
- return (api) => {
205
- for (let fn of fns) {
206
- fn(api)
207
- }
208
- }
209
- })
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
+ }
210
277
 
211
- insertInto(variantList, variantName, options)
212
- variantMap.set(variantName, variantFunctions)
213
- },
278
+ let variantIdentifier = 0
279
+ let api = {
214
280
  postcss,
215
281
  prefix: applyConfiguredPrefix,
216
282
  e: escapeClassName,
217
283
  config: getConfigValue,
218
- theme(path, defaultValue) {
219
- const [pathRoot, ...subPaths] = toPath(path)
220
- const value = getConfigValue(['theme', pathRoot, ...subPaths], defaultValue)
221
- return transformThemeValue(pathRoot)(value)
222
- },
284
+ theme: resolveThemeValue,
223
285
  corePlugins: (path) => {
224
286
  if (Array.isArray(tailwindConfig.corePlugins)) {
225
287
  return tailwindConfig.corePlugins.includes(path)
@@ -234,7 +296,7 @@ function buildPluginApi(tailwindConfig, context, { variantList, variantMap, offs
234
296
  addBase(base) {
235
297
  for (let [identifier, rule] of withIdentifiers(base)) {
236
298
  let prefixedIdentifier = prefixIdentifier(identifier, {})
237
- let offset = offsets.base++
299
+ let offset = offsets.create('base')
238
300
 
239
301
  if (!context.candidateRuleMap.has(prefixedIdentifier)) {
240
302
  context.candidateRuleMap.set(prefixedIdentifier, [])
@@ -263,11 +325,12 @@ function buildPluginApi(tailwindConfig, context, { variantList, variantMap, offs
263
325
 
264
326
  context.candidateRuleMap
265
327
  .get(prefixedIdentifier)
266
- .push([{ sort: offsets.base++, layer: 'defaults' }, rule])
328
+ .push([{ sort: offsets.create('defaults'), layer: 'defaults' }, rule])
267
329
  }
268
330
  },
269
331
  addComponents(components, options) {
270
332
  let defaultOptions = {
333
+ preserveSource: false,
271
334
  respectPrefix: true,
272
335
  respectImportant: false,
273
336
  }
@@ -285,11 +348,12 @@ function buildPluginApi(tailwindConfig, context, { variantList, variantMap, offs
285
348
 
286
349
  context.candidateRuleMap
287
350
  .get(prefixedIdentifier)
288
- .push([{ sort: offsets.components++, layer: 'components', options }, rule])
351
+ .push([{ sort: offsets.create('components'), layer: 'components', options }, rule])
289
352
  }
290
353
  },
291
354
  addUtilities(utilities, options) {
292
355
  let defaultOptions = {
356
+ preserveSource: false,
293
357
  respectPrefix: true,
294
358
  respectImportant: true,
295
359
  }
@@ -307,18 +371,19 @@ function buildPluginApi(tailwindConfig, context, { variantList, variantMap, offs
307
371
 
308
372
  context.candidateRuleMap
309
373
  .get(prefixedIdentifier)
310
- .push([{ sort: offsets.utilities++, layer: 'utilities', options }, rule])
374
+ .push([{ sort: offsets.create('utilities'), layer: 'utilities', options }, rule])
311
375
  }
312
376
  },
313
377
  matchUtilities: function (utilities, options) {
314
378
  let defaultOptions = {
315
379
  respectPrefix: true,
316
380
  respectImportant: true,
381
+ modifiers: false,
317
382
  }
318
383
 
319
- options = { ...defaultOptions, ...options }
384
+ options = normalizeOptionTypes({ ...defaultOptions, ...options })
320
385
 
321
- let offset = offsets.utilities++
386
+ let offset = offsets.create('utilities')
322
387
 
323
388
  for (let identifier in utilities) {
324
389
  let prefixedIdentifier = prefixIdentifier(identifier, options)
@@ -327,24 +392,49 @@ function buildPluginApi(tailwindConfig, context, { variantList, variantMap, offs
327
392
  classList.add([prefixedIdentifier, options])
328
393
 
329
394
  function wrapped(modifier, { isOnlyPlugin }) {
330
- let { type = 'any' } = options
331
- type = [].concat(type)
332
- let [value, coercedType] = coerceValue(type, modifier, options, tailwindConfig)
395
+ let [value, coercedType, utilityModifier] = coerceValue(
396
+ options.types,
397
+ modifier,
398
+ options,
399
+ tailwindConfig
400
+ )
333
401
 
334
402
  if (value === undefined) {
335
403
  return []
336
404
  }
337
405
 
338
- if (!type.includes(coercedType) && !isOnlyPlugin) {
339
- 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
+ }
340
418
  }
341
419
 
342
- if (!isValidArbitraryValue(value)) {
420
+ if (!isSyntacticallyValidPropertyValue(value)) {
343
421
  return []
344
422
  }
345
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
+
346
436
  let ruleSets = []
347
- .concat(rule(value))
437
+ .concat(rule(value, extras))
348
438
  .filter(Boolean)
349
439
  .map((declaration) => ({
350
440
  [nameClass(identifier, modifier)]: declaration,
@@ -366,11 +456,12 @@ function buildPluginApi(tailwindConfig, context, { variantList, variantMap, offs
366
456
  let defaultOptions = {
367
457
  respectPrefix: true,
368
458
  respectImportant: false,
459
+ modifiers: false,
369
460
  }
370
461
 
371
- options = { ...defaultOptions, ...options }
462
+ options = normalizeOptionTypes({ ...defaultOptions, ...options })
372
463
 
373
- let offset = offsets.components++
464
+ let offset = offsets.create('components')
374
465
 
375
466
  for (let identifier in components) {
376
467
  let prefixedIdentifier = prefixIdentifier(identifier, options)
@@ -379,15 +470,18 @@ function buildPluginApi(tailwindConfig, context, { variantList, variantMap, offs
379
470
  classList.add([prefixedIdentifier, options])
380
471
 
381
472
  function wrapped(modifier, { isOnlyPlugin }) {
382
- let { type = 'any' } = options
383
- type = [].concat(type)
384
- let [value, coercedType] = coerceValue(type, modifier, options, tailwindConfig)
473
+ let [value, coercedType, utilityModifier] = coerceValue(
474
+ options.types,
475
+ modifier,
476
+ options,
477
+ tailwindConfig
478
+ )
385
479
 
386
480
  if (value === undefined) {
387
481
  return []
388
482
  }
389
483
 
390
- if (!type.includes(coercedType)) {
484
+ if (!options.types.some(({ type }) => type === coercedType)) {
391
485
  if (isOnlyPlugin) {
392
486
  log.warn([
393
487
  `Unnecessary typehint \`${coercedType}\` in \`${identifier}-${modifier}\`.`,
@@ -401,12 +495,24 @@ function buildPluginApi(tailwindConfig, context, { variantList, variantMap, offs
401
495
  }
402
496
  }
403
497
 
404
- if (!isValidArbitraryValue(value)) {
498
+ if (!isSyntacticallyValidPropertyValue(value)) {
405
499
  return []
406
500
  }
407
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
+
408
514
  let ruleSets = []
409
- .concat(rule(value))
515
+ .concat(rule(value, extras))
410
516
  .filter(Boolean)
411
517
  .map((declaration) => ({
412
518
  [nameClass(identifier, modifier)]: declaration,
@@ -424,7 +530,104 @@ function buildPluginApi(tailwindConfig, context, { variantList, variantMap, offs
424
530
  context.candidateRuleMap.get(prefixedIdentifier).push(withOffsets)
425
531
  }
426
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
+ },
427
628
  }
629
+
630
+ return api
428
631
  }
429
632
 
430
633
  let fileModifiedMapCache = new WeakMap()
@@ -437,6 +640,7 @@ export function getFileModifiedMap(context) {
437
640
 
438
641
  function trackModified(files, fileModifiedMap) {
439
642
  let changed = false
643
+ let mtimesToCommit = new Map()
440
644
 
441
645
  for (let file of files) {
442
646
  if (!file) continue
@@ -457,10 +661,10 @@ function trackModified(files, fileModifiedMap) {
457
661
  changed = true
458
662
  }
459
663
 
460
- fileModifiedMap.set(file, newModified)
664
+ mtimesToCommit.set(file, newModified)
461
665
  }
462
666
 
463
- return changed
667
+ return [changed, mtimesToCommit]
464
668
  }
465
669
 
466
670
  function extractVariantAtRules(node) {
@@ -497,14 +701,14 @@ function collectLayerPlugins(root) {
497
701
  } else if (layerRule.params === 'components') {
498
702
  for (let node of layerRule.nodes) {
499
703
  layerPlugins.push(function ({ addComponents }) {
500
- addComponents(node, { respectPrefix: false })
704
+ addComponents(node, { respectPrefix: false, preserveSource: true })
501
705
  })
502
706
  }
503
707
  layerRule.remove()
504
708
  } else if (layerRule.params === 'utilities') {
505
709
  for (let node of layerRule.nodes) {
506
710
  layerPlugins.push(function ({ addUtilities }) {
507
- addUtilities(node, { respectPrefix: false })
711
+ addUtilities(node, { respectPrefix: false, preserveSource: true })
508
712
  })
509
713
  }
510
714
  layerRule.remove()
@@ -515,7 +719,7 @@ function collectLayerPlugins(root) {
515
719
  }
516
720
 
517
721
  function resolvePlugins(context, root) {
518
- let corePluginList = Object.entries({ ...variantPlugins, ...corePlugins })
722
+ let corePluginList = Object.entries(corePlugins)
519
723
  .map(([name, plugin]) => {
520
724
  if (!context.tailwindConfig.corePlugins.includes(name)) {
521
725
  return null
@@ -540,10 +744,15 @@ function resolvePlugins(context, root) {
540
744
  let beforeVariants = [
541
745
  variantPlugins['pseudoElementVariants'],
542
746
  variantPlugins['pseudoClassVariants'],
747
+ variantPlugins['hasVariants'],
748
+ variantPlugins['ariaVariants'],
749
+ variantPlugins['dataVariants'],
543
750
  ]
544
751
  let afterVariants = [
752
+ variantPlugins['supportsVariants'],
545
753
  variantPlugins['directionVariants'],
546
754
  variantPlugins['reducedMotionVariants'],
755
+ variantPlugins['prefersContrastVariants'],
547
756
  variantPlugins['darkVariants'],
548
757
  variantPlugins['printVariant'],
549
758
  variantPlugins['screenVariants'],
@@ -556,13 +765,10 @@ function resolvePlugins(context, root) {
556
765
  function registerPlugins(plugins, context) {
557
766
  let variantList = []
558
767
  let variantMap = new Map()
559
- let offsets = {
560
- defaults: 0n,
561
- base: 0n,
562
- components: 0n,
563
- utilities: 0n,
564
- user: 0n,
565
- }
768
+ context.variantMap = variantMap
769
+
770
+ let offsets = new Offsets()
771
+ context.offsets = offsets
566
772
 
567
773
  let classList = new Set()
568
774
 
@@ -583,49 +789,17 @@ function registerPlugins(plugins, context) {
583
789
  }
584
790
  }
585
791
 
586
- let highestOffset = ((args) => args.reduce((m, e) => (e > m ? e : m)))([
587
- offsets.base,
588
- offsets.defaults,
589
- offsets.components,
590
- offsets.utilities,
591
- offsets.user,
592
- ])
593
- let reservedBits = BigInt(highestOffset.toString(2).length)
594
-
595
- // A number one less than the top range of the highest offset area
596
- // so arbitrary properties are always sorted at the end.
597
- context.arbitraryPropertiesSort = ((1n << reservedBits) << 0n) - 1n
598
-
599
- context.layerOrder = {
600
- defaults: (1n << reservedBits) << 0n,
601
- base: (1n << reservedBits) << 1n,
602
- components: (1n << reservedBits) << 2n,
603
- utilities: (1n << reservedBits) << 3n,
604
- user: (1n << reservedBits) << 4n,
605
- }
606
-
607
- reservedBits += 5n
608
-
609
- let offset = 0
610
- context.variantOrder = new Map(
611
- variantList
612
- .map((variant, i) => {
613
- let variantFunctions = variantMap.get(variant).length
614
- let bits = (1n << BigInt(i + offset)) << reservedBits
615
- offset += variantFunctions - 1
616
- return [variant, bits]
617
- })
618
- .sort(([, a], [, z]) => bigSign(a - z))
619
- )
620
-
621
- 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)
622
794
 
623
795
  // Build variantMap
624
796
  for (let [variantName, variantFunctions] of variantMap.entries()) {
625
- let sort = context.variantOrder.get(variantName)
626
797
  context.variantMap.set(
627
798
  variantName,
628
- variantFunctions.map((variantFunction, idx) => [sort << BigInt(idx), variantFunction])
799
+ variantFunctions.map((variantFunction, idx) => [
800
+ offsets.forVariant(variantName, idx),
801
+ variantFunction,
802
+ ])
629
803
  )
630
804
  }
631
805
 
@@ -654,6 +828,7 @@ function registerPlugins(plugins, context) {
654
828
  if (checks.length > 0) {
655
829
  let patternMatchingCount = new Map()
656
830
  let prefixLength = context.tailwindConfig.prefix.length
831
+ let checkImportantUtils = checks.some((check) => check.pattern.source.includes('!'))
657
832
 
658
833
  for (let util of classList) {
659
834
  let utils = Array.isArray(util)
@@ -679,6 +854,21 @@ function registerPlugins(plugins, context) {
679
854
  ]
680
855
  }
681
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
+
682
872
  return classes
683
873
  })()
684
874
  : [util]
@@ -720,26 +910,48 @@ function registerPlugins(plugins, context) {
720
910
  }
721
911
  }
722
912
 
913
+ let darkClassName = [].concat(context.tailwindConfig.darkMode ?? 'media')[1] ?? 'dark'
914
+
723
915
  // A list of utilities that are used by certain Tailwind CSS utilities but
724
916
  // that don't exist on their own. This will result in them "not existing" and
725
917
  // sorting could be weird since you still require them in order to make the
726
- // host utitlies work properly. (Thanks Biology)
727
- 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
+ ]
728
924
  context.getClassOrder = function getClassOrder(classes) {
729
- let sortedClassNames = new Map()
730
- for (let [sort, rule] of generateRules(new Set(classes), context)) {
731
- if (sortedClassNames.has(rule.raws.tailwind.candidate)) continue
732
- 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++)
733
944
  }
734
945
 
735
946
  return classes.map((className) => {
736
947
  let order = sortedClassNames.get(className) ?? null
948
+ let parasiteIndex = parasiteUtilities.indexOf(className)
737
949
 
738
- if (order === null && parasiteUtilities.has(className)) {
950
+ if (order === null && parasiteIndex !== -1) {
739
951
  // This will make sure that it is at the very beginning of the
740
952
  // `components` layer which technically means 'before any
741
953
  // components'.
742
- order = context.layerOrder.components
954
+ order = BigInt(parasiteIndex)
743
955
  }
744
956
 
745
957
  return [className, order]
@@ -748,18 +960,35 @@ function registerPlugins(plugins, context) {
748
960
 
749
961
  // Generate a list of strings for autocompletion purposes, e.g.
750
962
  // ['uppercase', 'lowercase', ...]
751
- context.getClassList = function getClassList() {
963
+ context.getClassList = function getClassList(options = {}) {
752
964
  let output = []
753
965
 
754
966
  for (let util of classList) {
755
967
  if (Array.isArray(util)) {
756
- let [utilName, options] = util
968
+ let [utilName, utilOptions] = util
757
969
  let negativeClasses = []
758
970
 
759
- for (let [key, value] of Object.entries(options?.values ?? {})) {
760
- output.push(formatClass(utilName, key))
761
- if (options?.supportsNegativeValues && negateValue(value)) {
762
- 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)
763
992
  }
764
993
  }
765
994
 
@@ -771,21 +1000,223 @@ function registerPlugins(plugins, context) {
771
1000
 
772
1001
  return output
773
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)
774
1199
  }
775
1200
 
776
1201
  export function createContext(tailwindConfig, changedContent = [], root = postcss.root()) {
777
1202
  let context = {
778
1203
  disposables: [],
779
1204
  ruleCache: new Set(),
1205
+ candidateRuleCache: new Map(),
780
1206
  classCache: new Map(),
781
1207
  applyClassCache: new Map(),
782
- notClassCache: new Set(),
1208
+ // Seed the not class cache with the blocklist (which is only strings)
1209
+ notClassCache: new Set(tailwindConfig.blocklist ?? []),
783
1210
  postCssNodeCache: new Map(),
784
1211
  candidateRuleMap: new Map(),
785
1212
  tailwindConfig,
786
1213
  changedContent: changedContent,
787
1214
  variantMap: new Map(),
788
1215
  stylesheetCache: null,
1216
+ variantOptions: new Map(),
1217
+
1218
+ markInvalidUtilityCandidate: (candidate) => markInvalidUtilityCandidate(context, candidate),
1219
+ markInvalidUtilityNode: (node) => markInvalidUtilityNode(context, node),
789
1220
  }
790
1221
 
791
1222
  let resolvedPlugins = resolvePlugins(context, root)
@@ -828,12 +1259,12 @@ export function getContext(
828
1259
  // If there's already a context in the cache and we don't need to
829
1260
  // reset the context, return the cached context.
830
1261
  if (existingContext) {
831
- let contextDependenciesChanged = trackModified(
1262
+ let [contextDependenciesChanged, mtimesToCommit] = trackModified(
832
1263
  [...contextDependencies],
833
1264
  getFileModifiedMap(existingContext)
834
1265
  )
835
1266
  if (!contextDependenciesChanged && !cssDidChange) {
836
- return [existingContext, false]
1267
+ return [existingContext, false, mtimesToCommit]
837
1268
  }
838
1269
  }
839
1270
 
@@ -864,7 +1295,11 @@ export function getContext(
864
1295
 
865
1296
  let context = createContext(tailwindConfig, [], root)
866
1297
 
867
- trackModified([...contextDependencies], getFileModifiedMap(context))
1298
+ Object.assign(context, {
1299
+ userConfigPath,
1300
+ })
1301
+
1302
+ let [, mtimesToCommit] = trackModified([...contextDependencies], getFileModifiedMap(context))
868
1303
 
869
1304
  // ---
870
1305
 
@@ -879,5 +1314,5 @@ export function getContext(
879
1314
 
880
1315
  contextSourcesMap.get(context).add(sourcePath)
881
1316
 
882
- return [context, true]
1317
+ return [context, true, mtimesToCommit]
883
1318
  }