tailwindcss 0.0.0-insiders.df011dc → 0.0.0-insiders.e2d5f21

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 (120) hide show
  1. package/CHANGELOG.md +106 -1
  2. package/README.md +1 -1
  3. package/colors.js +1 -304
  4. package/defaultConfig.js +1 -4
  5. package/defaultTheme.js +1 -4
  6. package/lib/cli-peer-dependencies.js +4 -9
  7. package/lib/cli.js +646 -705
  8. package/lib/constants.js +15 -15
  9. package/lib/corePluginList.js +174 -4
  10. package/lib/corePlugins.js +3637 -2934
  11. package/lib/css/preflight.css +14 -1
  12. package/lib/featureFlags.js +44 -44
  13. package/lib/index.js +25 -24
  14. package/lib/lib/collapseAdjacentRules.js +33 -35
  15. package/lib/lib/collapseDuplicateDeclarations.js +29 -0
  16. package/lib/lib/detectNesting.js +34 -0
  17. package/lib/lib/evaluateTailwindFunctions.js +134 -155
  18. package/lib/lib/expandApplyAtRules.js +183 -200
  19. package/lib/lib/expandTailwindAtRules.js +219 -232
  20. package/lib/lib/generateRules.js +478 -311
  21. package/lib/lib/getModuleDependencies.js +36 -41
  22. package/lib/lib/normalizeTailwindDirectives.js +66 -58
  23. package/lib/lib/resolveDefaultsAtRules.js +110 -90
  24. package/lib/lib/setupContextUtils.js +740 -580
  25. package/lib/lib/setupTrackingContext.js +142 -162
  26. package/lib/lib/setupWatchingContext.js +235 -278
  27. package/lib/lib/sharedState.js +7 -17
  28. package/lib/lib/substituteScreenAtRules.js +22 -26
  29. package/lib/processTailwindFeatures.js +38 -44
  30. package/lib/public/colors.js +329 -0
  31. package/lib/public/create-plugin.js +13 -0
  32. package/lib/public/default-config.js +14 -0
  33. package/lib/public/default-theme.js +14 -0
  34. package/lib/public/resolve-config.js +19 -0
  35. package/lib/util/bigSign.js +3 -5
  36. package/lib/util/buildMediaQuery.js +15 -31
  37. package/lib/util/cloneDeep.js +14 -13
  38. package/lib/util/cloneNodes.js +9 -13
  39. package/lib/util/color.js +69 -59
  40. package/lib/util/configurePlugins.js +13 -14
  41. package/lib/util/createPlugin.js +21 -24
  42. package/lib/util/createUtilityPlugin.js +47 -45
  43. package/lib/util/dataTypes.js +231 -0
  44. package/lib/util/defaults.js +10 -14
  45. package/lib/util/escapeClassName.js +12 -14
  46. package/lib/util/escapeCommas.js +3 -5
  47. package/lib/util/flattenColorPalette.js +14 -10
  48. package/lib/util/formatVariantSelector.js +186 -0
  49. package/lib/util/getAllConfigs.js +23 -16
  50. package/lib/util/hashConfig.js +10 -10
  51. package/lib/util/isKeyframeRule.js +3 -5
  52. package/lib/util/isPlainObject.js +7 -10
  53. package/lib/util/isValidArbitraryValue.js +64 -0
  54. package/lib/util/log.js +39 -30
  55. package/lib/util/nameClass.js +23 -23
  56. package/lib/util/negateValue.js +14 -15
  57. package/lib/util/normalizeConfig.js +208 -0
  58. package/lib/util/normalizeScreens.js +58 -0
  59. package/lib/util/parseAnimationValue.js +82 -53
  60. package/lib/util/parseBoxShadowValue.js +77 -0
  61. package/lib/util/parseDependency.js +49 -60
  62. package/lib/util/parseObjectStyles.js +23 -20
  63. package/lib/util/pluginUtils.js +153 -285
  64. package/lib/util/prefixSelector.js +14 -16
  65. package/lib/util/resolveConfig.js +194 -249
  66. package/lib/util/resolveConfigPath.js +38 -45
  67. package/lib/util/responsive.js +12 -11
  68. package/lib/util/tap.js +4 -6
  69. package/lib/util/toColorValue.js +4 -5
  70. package/lib/util/toPath.js +4 -6
  71. package/lib/util/transformThemeValue.js +57 -26
  72. package/lib/util/withAlphaVariable.js +56 -54
  73. package/package.json +27 -37
  74. package/peers/index.js +73099 -84294
  75. package/plugin.js +1 -3
  76. package/resolveConfig.js +1 -7
  77. package/scripts/create-plugin-list.js +2 -2
  78. package/src/cli.js +27 -18
  79. package/src/corePluginList.js +1 -1
  80. package/src/corePlugins.js +2124 -1968
  81. package/src/css/preflight.css +14 -1
  82. package/src/featureFlags.js +12 -6
  83. package/src/lib/collapseDuplicateDeclarations.js +28 -0
  84. package/src/lib/detectNesting.js +39 -0
  85. package/src/lib/evaluateTailwindFunctions.js +5 -2
  86. package/src/lib/expandTailwindAtRules.js +24 -32
  87. package/src/lib/generateRules.js +230 -20
  88. package/src/lib/normalizeTailwindDirectives.js +3 -2
  89. package/src/lib/resolveDefaultsAtRules.js +46 -30
  90. package/src/lib/setupContextUtils.js +275 -113
  91. package/src/lib/setupTrackingContext.js +9 -8
  92. package/src/lib/setupWatchingContext.js +12 -7
  93. package/src/lib/sharedState.js +1 -4
  94. package/src/lib/substituteScreenAtRules.js +6 -3
  95. package/src/processTailwindFeatures.js +8 -1
  96. package/src/public/colors.js +300 -0
  97. package/src/public/create-plugin.js +2 -0
  98. package/src/public/default-config.js +4 -0
  99. package/src/public/default-theme.js +4 -0
  100. package/src/public/resolve-config.js +7 -0
  101. package/src/util/buildMediaQuery.js +14 -18
  102. package/src/util/createUtilityPlugin.js +2 -11
  103. package/src/util/dataTypes.js +246 -0
  104. package/src/util/formatVariantSelector.js +196 -0
  105. package/src/util/isValidArbitraryValue.js +61 -0
  106. package/src/util/log.js +18 -21
  107. package/src/util/nameClass.js +10 -6
  108. package/src/util/negateValue.js +4 -2
  109. package/src/util/normalizeConfig.js +249 -0
  110. package/src/util/normalizeScreens.js +42 -0
  111. package/src/util/parseAnimationValue.js +7 -1
  112. package/src/util/parseBoxShadowValue.js +71 -0
  113. package/src/util/parseDependency.js +4 -0
  114. package/src/util/pluginUtils.js +101 -204
  115. package/src/util/prefixSelector.js +1 -4
  116. package/src/util/resolveConfig.js +12 -65
  117. package/src/util/transformThemeValue.js +22 -7
  118. package/src/util/withAlphaVariable.js +13 -8
  119. package/stubs/defaultConfig.stub.js +163 -97
  120. package/stubs/simpleConfig.stub.js +0 -1
@@ -2,42 +2,58 @@ import postcss from 'postcss'
2
2
  import selectorParser from 'postcss-selector-parser'
3
3
  import { flagEnabled } from '../featureFlags'
4
4
 
5
+ let getNode = {
6
+ id(node) {
7
+ return selectorParser.attribute({
8
+ attribute: 'id',
9
+ operator: '=',
10
+ value: node.value,
11
+ quoteMark: '"',
12
+ })
13
+ },
14
+ }
15
+
5
16
  function minimumImpactSelector(nodes) {
6
- let pseudos = nodes.filter((n) => n.type === 'pseudo')
7
- let [bestNode] = nodes
8
-
9
- for (let [type, getNode = (n) => n] of [
10
- ['class'],
11
- [
12
- 'id',
13
- (n) =>
14
- selectorParser.attribute({
15
- attribute: 'id',
16
- operator: '=',
17
- value: n.value,
18
- quoteMark: '"',
19
- }),
20
- ],
21
- ['attribute'],
22
- ]) {
23
- let match = nodes.find((n) => n.type === type)
24
-
25
- if (match) {
26
- bestNode = getNode(match)
27
- break
28
- }
17
+ let rest = nodes
18
+ .filter((node) => {
19
+ // Keep non-pseudo nodes
20
+ if (node.type !== 'pseudo') return true
21
+
22
+ // Keep pseudo nodes that have subnodes
23
+ // E.g.: `:not()` contains subnodes inside the parentheses
24
+ if (node.nodes.length > 0) return true
25
+
26
+ // Keep pseudo `elements`
27
+ // This implicitly means that we ignore pseudo `classes`
28
+ return (
29
+ node.value.startsWith('::') ||
30
+ [':before', ':after', ':first-line', ':first-letter'].includes(node.value)
31
+ )
32
+ })
33
+ .reverse()
34
+
35
+ let searchFor = new Set(['tag', 'class', 'id', 'attribute'])
36
+
37
+ let splitPointIdx = rest.findIndex((n) => searchFor.has(n.type))
38
+ if (splitPointIdx === -1) return rest.reverse().join('').trim()
39
+
40
+ let node = rest[splitPointIdx]
41
+ let bestNode = getNode[node.type] ? getNode[node.type](node) : node
42
+
43
+ rest = rest.slice(0, splitPointIdx)
44
+
45
+ let combinatorIdx = rest.findIndex((n) => n.type === 'combinator' && n.value === '>')
46
+ if (combinatorIdx !== -1) {
47
+ rest.splice(0, combinatorIdx)
48
+ rest.unshift(selectorParser.universal())
29
49
  }
30
50
 
31
- return [bestNode, ...pseudos].join('').trim()
51
+ return [bestNode, ...rest.reverse()].join('').trim()
32
52
  }
33
53
 
34
- let elementSelectorParser = selectorParser((selectors) => {
54
+ export let elementSelectorParser = selectorParser((selectors) => {
35
55
  return selectors.map((s) => {
36
- let nodes = s
37
- .split((n) => n.type === 'combinator')
38
- .pop()
39
- .filter((n) => n.type !== 'pseudo' || n.value.startsWith('::'))
40
-
56
+ let nodes = s.split((n) => n.type === 'combinator' && n.value === ' ').pop()
41
57
  return minimumImpactSelector(nodes)
42
58
  })
43
59
  })
@@ -9,13 +9,45 @@ import parseObjectStyles from '../util/parseObjectStyles'
9
9
  import prefixSelector from '../util/prefixSelector'
10
10
  import isPlainObject from '../util/isPlainObject'
11
11
  import escapeClassName from '../util/escapeClassName'
12
- import nameClass from '../util/nameClass'
12
+ import nameClass, { formatClass } from '../util/nameClass'
13
13
  import { coerceValue } from '../util/pluginUtils'
14
14
  import bigSign from '../util/bigSign'
15
- import * as corePlugins from '../corePlugins'
15
+ import { variantPlugins, corePlugins } from '../corePlugins'
16
16
  import * as sharedState from './sharedState'
17
17
  import { env } from './sharedState'
18
18
  import { toPath } from '../util/toPath'
19
+ import log from '../util/log'
20
+ import negateValue from '../util/negateValue'
21
+ import isValidArbitraryValue from '../util/isValidArbitraryValue'
22
+
23
+ function parseVariantFormatString(input) {
24
+ if (input.includes('{')) {
25
+ if (!isBalanced(input)) throw new Error(`Your { and } are unbalanced.`)
26
+
27
+ return input
28
+ .split(/{(.*)}/gim)
29
+ .flatMap((line) => parseVariantFormatString(line))
30
+ .filter(Boolean)
31
+ }
32
+
33
+ return [input.trim()]
34
+ }
35
+
36
+ function isBalanced(input) {
37
+ let count = 0
38
+
39
+ for (let char of input) {
40
+ if (char === '{') {
41
+ count++
42
+ } else if (char === '}') {
43
+ if (--count < 0) {
44
+ return false // unbalanced
45
+ }
46
+ }
47
+ }
48
+
49
+ return count === 0
50
+ }
19
51
 
20
52
  function insertInto(list, value, { before = [] } = {}) {
21
53
  before = [].concat(before)
@@ -58,11 +90,22 @@ function getClasses(selector) {
58
90
  }
59
91
 
60
92
  function extractCandidates(node) {
61
- let classes = node.type === 'rule' ? getClasses(node.selector) : []
93
+ let classes = []
94
+
95
+ if (node.type === 'rule') {
96
+ for (let selector of node.selectors) {
97
+ let classCandidates = getClasses(selector)
98
+ // At least one of the selectors contains non-"on-demandable" candidates.
99
+ if (classCandidates.length === 0) return []
100
+
101
+ classes = [...classes, ...classCandidates]
102
+ }
103
+ return classes
104
+ }
62
105
 
63
106
  if (node.type === 'atrule') {
64
107
  node.walkRules((rule) => {
65
- classes = [...classes, ...getClasses(rule.selector)]
108
+ classes = [...classes, ...rule.selectors.flatMap((selector) => getClasses(selector))]
66
109
  })
67
110
  }
68
111
 
@@ -88,65 +131,7 @@ function withIdentifiers(styles) {
88
131
  })
89
132
  }
90
133
 
91
- let matchingBrackets = new Map([
92
- ['{', '}'],
93
- ['[', ']'],
94
- ['(', ')'],
95
- ])
96
- let inverseMatchingBrackets = new Map(
97
- Array.from(matchingBrackets.entries()).map(([k, v]) => [v, k])
98
- )
99
-
100
- let quotes = new Set(['"', "'", '`'])
101
-
102
- // Arbitrary values must contain balanced brackets (), [] and {}. Escaped
103
- // values don't count, and brackets inside quotes also don't count.
104
- //
105
- // E.g.: w-[this-is]w-[weird-and-invalid]
106
- // E.g.: w-[this-is\\]w-\\[weird-but-valid]
107
- // E.g.: content-['this-is-also-valid]-weirdly-enough']
108
- function isValidArbitraryValue(value) {
109
- let stack = []
110
- let inQuotes = false
111
-
112
- for (let i = 0; i < value.length; i++) {
113
- let char = value[i]
114
-
115
- // Non-escaped quotes allow us to "allow" anything in between
116
- if (quotes.has(char) && value[i - 1] !== '\\') {
117
- inQuotes = !inQuotes
118
- }
119
-
120
- if (inQuotes) continue
121
- if (value[i - 1] === '\\') continue // Escaped
122
-
123
- if (matchingBrackets.has(char)) {
124
- stack.push(char)
125
- } else if (inverseMatchingBrackets.has(char)) {
126
- let inverse = inverseMatchingBrackets.get(char)
127
-
128
- // Nothing to pop from, therefore it is unbalanced
129
- if (stack.length <= 0) {
130
- return false
131
- }
132
-
133
- // Popped value must match the inverse value, otherwise it is unbalanced
134
- if (stack.pop() !== inverse) {
135
- return false
136
- }
137
- }
138
- }
139
-
140
- // If there is still something on the stack, it is also unbalanced
141
- if (stack.length > 0) {
142
- return false
143
- }
144
-
145
- // All good, totally balanced!
146
- return true
147
- }
148
-
149
- function buildPluginApi(tailwindConfig, context, { variantList, variantMap, offsets }) {
134
+ function buildPluginApi(tailwindConfig, context, { variantList, variantMap, offsets, classList }) {
150
135
  function getConfigValue(path, defaultValue) {
151
136
  return path ? dlv(tailwindConfig, path, defaultValue) : tailwindConfig
152
137
  }
@@ -164,16 +149,41 @@ function buildPluginApi(tailwindConfig, context, { variantList, variantMap, offs
164
149
  return identifier
165
150
  }
166
151
 
167
- if (typeof context.tailwindConfig.prefix === 'function') {
168
- return prefixSelector(context.tailwindConfig.prefix, `.${identifier}`).substr(1)
169
- }
170
-
171
152
  return context.tailwindConfig.prefix + identifier
172
153
  }
173
154
 
174
155
  return {
175
156
  addVariant(variantName, variantFunctions, options = {}) {
176
- variantFunctions = [].concat(variantFunctions)
157
+ variantFunctions = [].concat(variantFunctions).map((variantFunction) => {
158
+ if (typeof variantFunction !== 'string') {
159
+ // Safelist public API functions
160
+ return ({ modifySelectors, container, separator }) => {
161
+ return variantFunction({ modifySelectors, container, separator })
162
+ }
163
+ }
164
+
165
+ variantFunction = variantFunction
166
+ .replace(/\n+/g, '')
167
+ .replace(/\s{1,}/g, ' ')
168
+ .trim()
169
+
170
+ let fns = parseVariantFormatString(variantFunction)
171
+ .map((str) => {
172
+ if (!str.startsWith('@')) {
173
+ return ({ format }) => format(str)
174
+ }
175
+
176
+ let [, name, params] = /@(.*?) (.*)/g.exec(str)
177
+ return ({ wrap }) => wrap(postcss.atRule({ name, params }))
178
+ })
179
+ .reverse()
180
+
181
+ return (api) => {
182
+ for (let fn of fns) {
183
+ fn(api)
184
+ }
185
+ }
186
+ })
177
187
 
178
188
  insertInto(variantList, variantName, options)
179
189
  variantMap.set(variantName, variantFunctions)
@@ -225,22 +235,18 @@ function buildPluginApi(tailwindConfig, context, { variantList, variantMap, offs
225
235
  },
226
236
  addComponents(components, options) {
227
237
  let defaultOptions = {
228
- variants: [],
229
238
  respectPrefix: true,
230
239
  respectImportant: false,
231
- respectVariants: true,
232
240
  }
233
241
 
234
- options = Object.assign(
235
- {},
236
- defaultOptions,
237
- Array.isArray(options) ? { variants: options } : options
238
- )
242
+ options = Object.assign({}, defaultOptions, Array.isArray(options) ? {} : options)
239
243
 
240
244
  for (let [identifier, rule] of withIdentifiers(components)) {
241
245
  let prefixedIdentifier = prefixIdentifier(identifier, options)
242
246
  let offset = offsets.components++
243
247
 
248
+ classList.add(prefixedIdentifier)
249
+
244
250
  if (!context.candidateRuleMap.has(prefixedIdentifier)) {
245
251
  context.candidateRuleMap.set(prefixedIdentifier, [])
246
252
  }
@@ -252,22 +258,18 @@ function buildPluginApi(tailwindConfig, context, { variantList, variantMap, offs
252
258
  },
253
259
  addUtilities(utilities, options) {
254
260
  let defaultOptions = {
255
- variants: [],
256
261
  respectPrefix: true,
257
262
  respectImportant: true,
258
- respectVariants: true,
259
263
  }
260
264
 
261
- options = Object.assign(
262
- {},
263
- defaultOptions,
264
- Array.isArray(options) ? { variants: options } : options
265
- )
265
+ options = Object.assign({}, defaultOptions, Array.isArray(options) ? {} : options)
266
266
 
267
267
  for (let [identifier, rule] of withIdentifiers(utilities)) {
268
268
  let prefixedIdentifier = prefixIdentifier(identifier, options)
269
269
  let offset = offsets.utilities++
270
270
 
271
+ classList.add(prefixedIdentifier)
272
+
271
273
  if (!context.candidateRuleMap.has(prefixedIdentifier)) {
272
274
  context.candidateRuleMap.set(prefixedIdentifier, [])
273
275
  }
@@ -279,10 +281,8 @@ function buildPluginApi(tailwindConfig, context, { variantList, variantMap, offs
279
281
  },
280
282
  matchUtilities: function (utilities, options) {
281
283
  let defaultOptions = {
282
- variants: [],
283
284
  respectPrefix: true,
284
285
  respectImportant: true,
285
- respectVariants: true,
286
286
  }
287
287
 
288
288
  options = { ...defaultOptions, ...options }
@@ -293,12 +293,18 @@ function buildPluginApi(tailwindConfig, context, { variantList, variantMap, offs
293
293
  let prefixedIdentifier = prefixIdentifier(identifier, options)
294
294
  let rule = utilities[identifier]
295
295
 
296
- function wrapped(modifier) {
296
+ classList.add([prefixedIdentifier, options])
297
+
298
+ function wrapped(modifier, { isOnlyPlugin }) {
297
299
  let { type = 'any' } = options
298
300
  type = [].concat(type)
299
- let [value, coercedType] = coerceValue(type, modifier, options.values, tailwindConfig)
301
+ let [value, coercedType] = coerceValue(type, modifier, options, tailwindConfig)
302
+
303
+ if (value === undefined) {
304
+ return []
305
+ }
300
306
 
301
- if (!type.includes(coercedType) || value === undefined) {
307
+ if (!type.includes(coercedType) && !isOnlyPlugin) {
302
308
  return []
303
309
  }
304
310
 
@@ -306,21 +312,14 @@ function buildPluginApi(tailwindConfig, context, { variantList, variantMap, offs
306
312
  return []
307
313
  }
308
314
 
309
- let includedRules = []
310
315
  let ruleSets = []
311
- .concat(
312
- rule(value, {
313
- includeRules(rules) {
314
- includedRules.push(...rules)
315
- },
316
- })
317
- )
316
+ .concat(rule(value))
318
317
  .filter(Boolean)
319
318
  .map((declaration) => ({
320
319
  [nameClass(identifier, modifier)]: declaration,
321
320
  }))
322
321
 
323
- return [...includedRules, ...ruleSets]
322
+ return ruleSets
324
323
  }
325
324
 
326
325
  let withOffsets = [{ sort: offset, layer: 'utilities', options }, wrapped]
@@ -329,6 +328,68 @@ function buildPluginApi(tailwindConfig, context, { variantList, variantMap, offs
329
328
  context.candidateRuleMap.set(prefixedIdentifier, [])
330
329
  }
331
330
 
331
+ context.candidateRuleMap.get(prefixedIdentifier).push(withOffsets)
332
+ }
333
+ },
334
+ matchComponents: function (components, options) {
335
+ let defaultOptions = {
336
+ respectPrefix: true,
337
+ respectImportant: false,
338
+ }
339
+
340
+ options = { ...defaultOptions, ...options }
341
+
342
+ let offset = offsets.components++
343
+
344
+ for (let identifier in components) {
345
+ let prefixedIdentifier = prefixIdentifier(identifier, options)
346
+ let rule = components[identifier]
347
+
348
+ classList.add([prefixedIdentifier, options])
349
+
350
+ function wrapped(modifier, { isOnlyPlugin }) {
351
+ let { type = 'any' } = options
352
+ type = [].concat(type)
353
+ let [value, coercedType] = coerceValue(type, modifier, options, tailwindConfig)
354
+
355
+ if (value === undefined) {
356
+ return []
357
+ }
358
+
359
+ if (!type.includes(coercedType)) {
360
+ if (isOnlyPlugin) {
361
+ log.warn([
362
+ `Unnecessary typehint \`${coercedType}\` in \`${identifier}-${modifier}\`.`,
363
+ `You can safely update it to \`${identifier}-${modifier.replace(
364
+ coercedType + ':',
365
+ ''
366
+ )}\`.`,
367
+ ])
368
+ } else {
369
+ return []
370
+ }
371
+ }
372
+
373
+ if (!isValidArbitraryValue(value)) {
374
+ return []
375
+ }
376
+
377
+ let ruleSets = []
378
+ .concat(rule(value))
379
+ .filter(Boolean)
380
+ .map((declaration) => ({
381
+ [nameClass(identifier, modifier)]: declaration,
382
+ }))
383
+
384
+ return ruleSets
385
+ }
386
+
387
+ let withOffsets = [{ sort: offset, layer: 'components', options }, wrapped]
388
+
389
+ if (!context.candidateRuleMap.has(prefixedIdentifier)) {
390
+ context.candidateRuleMap.set(prefixedIdentifier, [])
391
+ }
392
+
332
393
  context.candidateRuleMap.get(prefixedIdentifier).push(withOffsets)
333
394
  }
334
395
  },
@@ -424,8 +485,8 @@ function collectLayerPlugins(root) {
424
485
  return layerPlugins
425
486
  }
426
487
 
427
- function resolvePlugins(context, tailwindDirectives, root) {
428
- let corePluginList = Object.entries(corePlugins)
488
+ function resolvePlugins(context, root) {
489
+ let corePluginList = Object.entries({ ...variantPlugins, ...corePlugins })
429
490
  .map(([name, plugin]) => {
430
491
  if (!context.tailwindConfig.corePlugins.includes(name)) {
431
492
  return null
@@ -443,16 +504,21 @@ function resolvePlugins(context, tailwindDirectives, root) {
443
504
  return typeof plugin === 'function' ? plugin : plugin.handler
444
505
  })
445
506
 
446
- let layerPlugins = collectLayerPlugins(root, tailwindDirectives)
507
+ let layerPlugins = collectLayerPlugins(root)
447
508
 
448
509
  // TODO: This is a workaround for backwards compatibility, since custom variants
449
510
  // were historically sorted before screen/stackable variants.
450
- let beforeVariants = [corePlugins['pseudoElementVariants'], corePlugins['pseudoClassVariants']]
511
+ let beforeVariants = [
512
+ variantPlugins['pseudoElementVariants'],
513
+ variantPlugins['pseudoClassVariants'],
514
+ ]
451
515
  let afterVariants = [
452
- corePlugins['directionVariants'],
453
- corePlugins['reducedMotionVariants'],
454
- corePlugins['darkVariants'],
455
- corePlugins['screenVariants'],
516
+ variantPlugins['directionVariants'],
517
+ variantPlugins['reducedMotionVariants'],
518
+ variantPlugins['darkVariants'],
519
+ variantPlugins['printVariant'],
520
+ variantPlugins['screenVariants'],
521
+ variantPlugins['orientationVariants'],
456
522
  ]
457
523
 
458
524
  return [...corePluginList, ...beforeVariants, ...userPlugins, ...afterVariants, ...layerPlugins]
@@ -468,10 +534,13 @@ function registerPlugins(plugins, context) {
468
534
  user: 0n,
469
535
  }
470
536
 
537
+ let classList = new Set()
538
+
471
539
  let pluginApi = buildPluginApi(context.tailwindConfig, context, {
472
540
  variantList,
473
541
  variantMap,
474
542
  offsets,
543
+ classList,
475
544
  })
476
545
 
477
546
  for (let plugin of plugins) {
@@ -492,6 +561,10 @@ function registerPlugins(plugins, context) {
492
561
  ])
493
562
  let reservedBits = BigInt(highestOffset.toString(2).length)
494
563
 
564
+ // A number one less than the top range of the highest offset area
565
+ // so arbitrary properties are always sorted at the end.
566
+ context.arbitraryPropertiesSort = ((1n << reservedBits) << 0n) - 1n
567
+
495
568
  context.layerOrder = {
496
569
  base: (1n << reservedBits) << 0n,
497
570
  components: (1n << reservedBits) << 1n,
@@ -523,14 +596,104 @@ function registerPlugins(plugins, context) {
523
596
  variantFunctions.map((variantFunction, idx) => [sort << BigInt(idx), variantFunction])
524
597
  )
525
598
  }
599
+
600
+ let safelist = (context.tailwindConfig.safelist ?? []).filter(Boolean)
601
+ if (safelist.length > 0) {
602
+ let checks = []
603
+
604
+ for (let value of safelist) {
605
+ if (typeof value === 'string') {
606
+ context.changedContent.push({ content: value, extension: 'html' })
607
+ continue
608
+ }
609
+
610
+ if (value instanceof RegExp) {
611
+ log.warn('root-regex', [
612
+ 'Regular expressions in `safelist` work differently in Tailwind CSS v3.0.',
613
+ 'Update your `safelist` configuration to eliminate this warning.',
614
+ // TODO: Add https://tw.wtf/regex-safelist
615
+ ])
616
+ continue
617
+ }
618
+
619
+ checks.push(value)
620
+ }
621
+
622
+ if (checks.length > 0) {
623
+ let patternMatchingCount = new Map()
624
+
625
+ for (let util of classList) {
626
+ let utils = Array.isArray(util)
627
+ ? (() => {
628
+ let [utilName, options] = util
629
+ return Object.keys(options?.values ?? {}).map((value) => formatClass(utilName, value))
630
+ })()
631
+ : [util]
632
+
633
+ for (let util of utils) {
634
+ for (let { pattern, variants = [] } of checks) {
635
+ // RegExp with the /g flag are stateful, so let's reset the last
636
+ // index pointer to reset the state.
637
+ pattern.lastIndex = 0
638
+
639
+ if (!patternMatchingCount.has(pattern)) {
640
+ patternMatchingCount.set(pattern, 0)
641
+ }
642
+
643
+ if (!pattern.test(util)) continue
644
+
645
+ patternMatchingCount.set(pattern, patternMatchingCount.get(pattern) + 1)
646
+
647
+ context.changedContent.push({ content: util, extension: 'html' })
648
+ for (let variant of variants) {
649
+ context.changedContent.push({
650
+ content: variant + context.tailwindConfig.separator + util,
651
+ extension: 'html',
652
+ })
653
+ }
654
+ }
655
+ }
656
+ }
657
+
658
+ for (let [regex, count] of patternMatchingCount.entries()) {
659
+ if (count !== 0) continue
660
+
661
+ log.warn([
662
+ `The safelist pattern \`${regex}\` doesn't match any Tailwind CSS classes.`,
663
+ 'Fix this pattern or remove it from your `safelist` configuration.',
664
+ ])
665
+ }
666
+ }
667
+ }
668
+
669
+ // Generate a list of strings for autocompletion purposes, e.g.
670
+ // ['uppercase', 'lowercase', ...]
671
+ context.getClassList = function () {
672
+ let output = []
673
+
674
+ for (let util of classList) {
675
+ if (Array.isArray(util)) {
676
+ let [utilName, options] = util
677
+ let negativeClasses = []
678
+
679
+ for (let [key, value] of Object.entries(options?.values ?? {})) {
680
+ output.push(formatClass(utilName, key))
681
+ if (options?.supportsNegativeValues && negateValue(value)) {
682
+ negativeClasses.push(formatClass(utilName, `-${key}`))
683
+ }
684
+ }
685
+
686
+ output.push(...negativeClasses)
687
+ } else {
688
+ output.push(util)
689
+ }
690
+ }
691
+
692
+ return output
693
+ }
526
694
  }
527
695
 
528
- export function createContext(
529
- tailwindConfig,
530
- changedContent = [],
531
- tailwindDirectives = new Set(),
532
- root = postcss.root()
533
- ) {
696
+ export function createContext(tailwindConfig, changedContent = [], root = postcss.root()) {
534
697
  let context = {
535
698
  disposables: [],
536
699
  ruleCache: new Set(),
@@ -545,7 +708,7 @@ export function createContext(
545
708
  stylesheetCache: null,
546
709
  }
547
710
 
548
- let resolvedPlugins = resolvePlugins(context, tailwindDirectives, root)
711
+ let resolvedPlugins = resolvePlugins(context, root)
549
712
  registerPlugins(resolvedPlugins, context)
550
713
 
551
714
  return context
@@ -556,7 +719,6 @@ let configContextMap = sharedState.configContextMap
556
719
  let contextSourcesMap = sharedState.contextSourcesMap
557
720
 
558
721
  export function getContext(
559
- tailwindDirectives,
560
722
  root,
561
723
  result,
562
724
  tailwindConfig,
@@ -618,7 +780,7 @@ export function getContext(
618
780
 
619
781
  env.DEBUG && console.log('Setting up new context...')
620
782
 
621
- let context = createContext(tailwindConfig, [], tailwindDirectives, root)
783
+ let context = createContext(tailwindConfig, [], root)
622
784
 
623
785
  trackModified([...contextDependencies], getFileModifiedMap(context))
624
786
 
@@ -8,7 +8,7 @@ import normalizePath from 'normalize-path'
8
8
  import hash from '../util/hashConfig'
9
9
  import getModuleDependencies from '../lib/getModuleDependencies'
10
10
 
11
- import resolveConfig from '../../resolveConfig'
11
+ import resolveConfig from '../public/resolve-config'
12
12
 
13
13
  import resolveConfigPath from '../util/resolveConfigPath'
14
14
 
@@ -26,9 +26,9 @@ function getCandidateFiles(context, tailwindConfig) {
26
26
  return candidateFilesCache.get(context)
27
27
  }
28
28
 
29
- let candidateFiles = tailwindConfig.content.content
29
+ let candidateFiles = tailwindConfig.content.files
30
30
  .filter((item) => typeof item === 'string')
31
- .map((contentPath) => normalizePath(path.resolve(contentPath)))
31
+ .map((contentPath) => normalizePath(contentPath))
32
32
 
33
33
  return candidateFilesCache.set(context, candidateFiles).get(context)
34
34
  }
@@ -77,10 +77,9 @@ function getTailwindConfig(configOrPath) {
77
77
  }
78
78
 
79
79
  function resolvedChangedContent(context, candidateFiles, fileModifiedMap) {
80
- let changedContent = context.tailwindConfig.content.content
80
+ let changedContent = context.tailwindConfig.content.files
81
81
  .filter((item) => typeof item.raw === 'string')
82
- .concat(context.tailwindConfig.content.safelist)
83
- .map(({ raw, extension }) => ({ content: raw, extension }))
82
+ .map(({ raw, extension = 'html' }) => ({ content: raw, extension }))
84
83
 
85
84
  for (let changedFile of resolveChangedFiles(candidateFiles, fileModifiedMap)) {
86
85
  let content = fs.readFileSync(changedFile, 'utf8')
@@ -138,7 +137,6 @@ export default function setupTrackingContext(configOrPath) {
138
137
  }
139
138
 
140
139
  let [context] = getContext(
141
- tailwindDirectives,
142
140
  root,
143
141
  result,
144
142
  tailwindConfig,
@@ -159,7 +157,10 @@ export default function setupTrackingContext(configOrPath) {
159
157
 
160
158
  // Add template paths as postcss dependencies.
161
159
  for (let fileOrGlob of candidateFiles) {
162
- registerDependency(parseDependency(fileOrGlob))
160
+ let dependency = parseDependency(fileOrGlob)
161
+ if (dependency) {
162
+ registerDependency(dependency)
163
+ }
163
164
  }
164
165
 
165
166
  for (let changedContent of resolvedChangedContent(