tailwindcss 0.0.0-insiders.8259dfd → 0.0.0-insiders.8293c2d

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 (93) hide show
  1. package/CHANGELOG.md +207 -2
  2. package/README.md +8 -4
  3. package/colors.js +2 -1
  4. package/defaultConfig.js +2 -1
  5. package/defaultTheme.js +2 -1
  6. package/lib/cli.js +94 -61
  7. package/lib/constants.js +1 -1
  8. package/lib/corePluginList.js +8 -1
  9. package/lib/corePlugins.js +477 -517
  10. package/lib/css/preflight.css +18 -5
  11. package/lib/featureFlags.js +5 -6
  12. package/lib/index.js +1 -3
  13. package/lib/lib/collapseDuplicateDeclarations.js +80 -0
  14. package/lib/lib/defaultExtractor.js +42 -0
  15. package/lib/lib/detectNesting.js +17 -2
  16. package/lib/lib/evaluateTailwindFunctions.js +10 -6
  17. package/lib/lib/expandApplyAtRules.js +94 -45
  18. package/lib/lib/expandTailwindAtRules.js +32 -33
  19. package/lib/lib/generateRules.js +167 -21
  20. package/lib/lib/normalizeTailwindDirectives.js +8 -1
  21. package/lib/lib/partitionApplyAtRules.js +53 -0
  22. package/lib/lib/resolveDefaultsAtRules.js +28 -14
  23. package/lib/lib/setupContextUtils.js +153 -129
  24. package/lib/lib/setupTrackingContext.js +11 -10
  25. package/lib/lib/sharedState.js +34 -5
  26. package/lib/lib/substituteScreenAtRules.js +7 -4
  27. package/lib/processTailwindFeatures.js +7 -2
  28. package/lib/util/buildMediaQuery.js +13 -24
  29. package/lib/util/color.js +23 -8
  30. package/lib/util/createPlugin.js +1 -2
  31. package/lib/util/createUtilityPlugin.js +6 -10
  32. package/lib/util/dataTypes.js +38 -7
  33. package/lib/util/defaults.js +6 -0
  34. package/lib/util/flattenColorPalette.js +1 -3
  35. package/lib/util/formatVariantSelector.js +186 -0
  36. package/lib/util/isValidArbitraryValue.js +64 -0
  37. package/lib/util/log.js +4 -0
  38. package/lib/util/nameClass.js +1 -0
  39. package/lib/util/normalizeConfig.js +63 -21
  40. package/lib/util/normalizeScreens.js +59 -0
  41. package/lib/util/parseBoxShadowValue.js +77 -0
  42. package/lib/util/pluginUtils.js +27 -158
  43. package/lib/util/prefixSelector.js +1 -3
  44. package/lib/util/resolveConfig.js +30 -31
  45. package/lib/util/resolveConfigPath.js +1 -2
  46. package/lib/util/toColorValue.js +1 -2
  47. package/lib/util/toPath.js +6 -1
  48. package/lib/util/transformThemeValue.js +19 -13
  49. package/nesting/plugin.js +4 -1
  50. package/package.json +19 -21
  51. package/peers/index.js +4621 -5626
  52. package/plugin.js +2 -1
  53. package/resolveConfig.js +2 -1
  54. package/src/cli.js +66 -14
  55. package/src/corePluginList.js +1 -1
  56. package/src/corePlugins.js +406 -518
  57. package/src/css/preflight.css +18 -5
  58. package/src/featureFlags.js +1 -1
  59. package/src/index.js +1 -7
  60. package/src/lib/collapseDuplicateDeclarations.js +93 -0
  61. package/src/lib/defaultExtractor.js +48 -0
  62. package/src/lib/detectNesting.js +22 -3
  63. package/src/lib/evaluateTailwindFunctions.js +6 -3
  64. package/src/lib/expandApplyAtRules.js +101 -45
  65. package/src/lib/expandTailwindAtRules.js +33 -27
  66. package/src/lib/generateRules.js +178 -11
  67. package/src/lib/normalizeTailwindDirectives.js +6 -1
  68. package/src/lib/partitionApplyAtRules.js +52 -0
  69. package/src/lib/resolveDefaultsAtRules.js +31 -10
  70. package/src/lib/setupContextUtils.js +140 -84
  71. package/src/lib/setupTrackingContext.js +11 -10
  72. package/src/lib/sharedState.js +40 -4
  73. package/src/lib/substituteScreenAtRules.js +6 -3
  74. package/src/processTailwindFeatures.js +8 -2
  75. package/src/util/buildMediaQuery.js +14 -18
  76. package/src/util/color.js +20 -7
  77. package/src/util/dataTypes.js +43 -11
  78. package/src/util/defaults.js +6 -0
  79. package/src/util/formatVariantSelector.js +196 -0
  80. package/src/util/isValidArbitraryValue.js +61 -0
  81. package/src/util/log.js +4 -0
  82. package/src/util/nameClass.js +1 -1
  83. package/src/util/normalizeConfig.js +37 -2
  84. package/src/util/normalizeScreens.js +45 -0
  85. package/src/util/parseBoxShadowValue.js +71 -0
  86. package/src/util/pluginUtils.js +15 -138
  87. package/src/util/prefixSelector.js +1 -4
  88. package/src/util/resolveConfig.js +11 -1
  89. package/src/util/toPath.js +23 -1
  90. package/src/util/transformThemeValue.js +22 -7
  91. package/stubs/defaultConfig.stub.js +42 -16
  92. package/lib/lib/setupWatchingContext.js +0 -284
  93. package/src/lib/setupWatchingContext.js +0 -304
@@ -5,6 +5,10 @@ import isPlainObject from '../util/isPlainObject'
5
5
  import prefixSelector from '../util/prefixSelector'
6
6
  import { updateAllClasses } from '../util/pluginUtils'
7
7
  import log from '../util/log'
8
+ import { formatVariantSelector, finalizeSelector } from '../util/formatVariantSelector'
9
+ import { asClass } from '../util/nameClass'
10
+ import { normalize } from '../util/dataTypes'
11
+ import isValidArbitraryValue from '../util/isValidArbitraryValue'
8
12
 
9
13
  let classNameParser = selectorParser((selectors) => {
10
14
  return selectors.first.filter(({ type }) => type === 'class').pop().value
@@ -108,11 +112,26 @@ function applyVariant(variant, matches, context) {
108
112
  let result = []
109
113
 
110
114
  for (let [meta, rule] of matches) {
115
+ // Don't generate variants for user css
116
+ if (meta.layer === 'user') {
117
+ continue
118
+ }
119
+
111
120
  let container = postcss.root({ nodes: [rule.clone()] })
112
121
 
113
122
  for (let [variantSort, variantFunction] of variantFunctionTuples) {
114
123
  let clone = container.clone()
124
+ let collectedFormats = []
125
+
126
+ let originals = new Map()
127
+
128
+ function prepareBackup() {
129
+ if (originals.size > 0) return // Already prepared, chicken out
130
+ clone.walkRules((rule) => originals.set(rule, rule.selector))
131
+ }
132
+
115
133
  function modifySelectors(modifierFunction) {
134
+ prepareBackup()
116
135
  clone.each((rule) => {
117
136
  if (rule.type !== 'rule') {
118
137
  return
@@ -127,20 +146,84 @@ function applyVariant(variant, matches, context) {
127
146
  })
128
147
  })
129
148
  })
149
+
130
150
  return clone
131
151
  }
132
152
 
133
153
  let ruleWithVariant = variantFunction({
134
- container: clone,
154
+ // Public API
155
+ get container() {
156
+ prepareBackup()
157
+ return clone
158
+ },
135
159
  separator: context.tailwindConfig.separator,
136
160
  modifySelectors,
161
+
162
+ // Private API for now
163
+ wrap(wrapper) {
164
+ let nodes = clone.nodes
165
+ clone.removeAll()
166
+ wrapper.append(nodes)
167
+ clone.append(wrapper)
168
+ },
169
+ format(selectorFormat) {
170
+ collectedFormats.push(selectorFormat)
171
+ },
137
172
  })
138
173
 
174
+ if (typeof ruleWithVariant === 'string') {
175
+ collectedFormats.push(ruleWithVariant)
176
+ }
177
+
139
178
  if (ruleWithVariant === null) {
140
179
  continue
141
180
  }
142
181
 
143
- let withOffset = [{ ...meta, sort: variantSort | meta.sort }, clone.nodes[0]]
182
+ // We filled the `originals`, therefore we assume that somebody touched
183
+ // `container` or `modifySelectors`. Let's see if they did, so that we
184
+ // can restore the selectors, and collect the format strings.
185
+ if (originals.size > 0) {
186
+ clone.walkRules((rule) => {
187
+ if (!originals.has(rule)) return
188
+ let before = originals.get(rule)
189
+ if (before === rule.selector) return // No mutation happened
190
+
191
+ let modified = rule.selector
192
+
193
+ // Rebuild the base selector, this is what plugin authors would do
194
+ // as well. E.g.: `${variant}${separator}${className}`.
195
+ // However, plugin authors probably also prepend or append certain
196
+ // classes, pseudos, ids, ...
197
+ let rebuiltBase = selectorParser((selectors) => {
198
+ selectors.walkClasses((classNode) => {
199
+ classNode.value = `${variant}${context.tailwindConfig.separator}${classNode.value}`
200
+ })
201
+ }).processSync(before)
202
+
203
+ // Now that we know the original selector, the new selector, and
204
+ // the rebuild part in between, we can replace the part that plugin
205
+ // authors need to rebuild with `&`, and eventually store it in the
206
+ // collectedFormats. Similar to what `format('...')` would do.
207
+ //
208
+ // E.g.:
209
+ // variant: foo
210
+ // selector: .markdown > p
211
+ // modified (by plugin): .foo .foo\\:markdown > p
212
+ // rebuiltBase (internal): .foo\\:markdown > p
213
+ // format: .foo &
214
+ collectedFormats.push(modified.replace(rebuiltBase, '&'))
215
+ rule.selector = before
216
+ })
217
+ }
218
+
219
+ let withOffset = [
220
+ {
221
+ ...meta,
222
+ sort: variantSort | meta.sort,
223
+ collectedFormats: (meta.collectedFormats ?? []).concat(collectedFormats),
224
+ },
225
+ clone.nodes[0],
226
+ ]
144
227
  result.push(withOffset)
145
228
  }
146
229
  }
@@ -170,15 +253,70 @@ function parseRules(rule, cache, options = {}) {
170
253
  return [cache.get(rule), options]
171
254
  }
172
255
 
256
+ const IS_VALID_PROPERTY_NAME = /^[a-z_-]/
257
+
258
+ function isValidPropName(name) {
259
+ return IS_VALID_PROPERTY_NAME.test(name)
260
+ }
261
+
262
+ function isParsableCssValue(property, value) {
263
+ try {
264
+ postcss.parse(`a{${property}:${value}}`).toResult()
265
+ return true
266
+ } catch (err) {
267
+ return false
268
+ }
269
+ }
270
+
271
+ function extractArbitraryProperty(classCandidate, context) {
272
+ let [, property, value] = classCandidate.match(/^\[([a-zA-Z0-9-_]+):(\S+)\]$/) ?? []
273
+
274
+ if (value === undefined) {
275
+ return null
276
+ }
277
+
278
+ if (!isValidPropName(property)) {
279
+ return null
280
+ }
281
+
282
+ if (!isValidArbitraryValue(value)) {
283
+ return null
284
+ }
285
+
286
+ let normalized = normalize(value)
287
+
288
+ if (!isParsableCssValue(property, normalized)) {
289
+ return null
290
+ }
291
+
292
+ return [
293
+ [
294
+ { sort: context.arbitraryPropertiesSort, layer: 'utilities' },
295
+ () => ({
296
+ [asClass(classCandidate)]: {
297
+ [property]: normalized,
298
+ },
299
+ }),
300
+ ],
301
+ ]
302
+ }
303
+
173
304
  function* resolveMatchedPlugins(classCandidate, context) {
174
305
  if (context.candidateRuleMap.has(classCandidate)) {
175
306
  yield [context.candidateRuleMap.get(classCandidate), 'DEFAULT']
176
307
  }
177
308
 
309
+ yield* (function* (arbitraryPropertyRule) {
310
+ if (arbitraryPropertyRule !== null) {
311
+ yield [arbitraryPropertyRule, 'DEFAULT']
312
+ }
313
+ })(extractArbitraryProperty(classCandidate, context))
314
+
178
315
  let candidatePrefix = classCandidate
179
316
  let negative = false
180
317
 
181
- const twConfigPrefix = context.tailwindConfig.prefix || ''
318
+ const twConfigPrefix = context.tailwindConfig.prefix
319
+
182
320
  const twConfigPrefixLen = twConfigPrefix.length
183
321
  if (candidatePrefix[twConfigPrefixLen] === '-') {
184
322
  negative = true
@@ -192,7 +330,6 @@ function* resolveMatchedPlugins(classCandidate, context) {
192
330
  for (let [prefix, modifier] of candidatePermutations(candidatePrefix)) {
193
331
  if (context.candidateRuleMap.has(prefix)) {
194
332
  yield [context.candidateRuleMap.get(prefix), negative ? `-${modifier}` : modifier]
195
- return
196
333
  }
197
334
  }
198
335
  }
@@ -323,6 +460,22 @@ function* resolveMatches(candidate, context) {
323
460
  }
324
461
 
325
462
  for (let match of matches) {
463
+ // Apply final format selector
464
+ if (match[0].collectedFormats) {
465
+ let finalFormat = formatVariantSelector('&', ...match[0].collectedFormats)
466
+ let container = postcss.root({ nodes: [match[1].clone()] })
467
+ container.walkRules((rule) => {
468
+ if (inKeyframes(rule)) return
469
+
470
+ rule.selector = finalizeSelector(finalFormat, {
471
+ selector: rule.selector,
472
+ candidate,
473
+ context,
474
+ })
475
+ })
476
+ match[1] = container.nodes[0]
477
+ }
478
+
326
479
  yield match
327
480
  }
328
481
  }
@@ -356,28 +509,42 @@ function generateRules(candidates, context) {
356
509
  allRules.push(matches)
357
510
  }
358
511
 
359
- return allRules.flat(1).map(([{ sort, layer, options }, rule]) => {
360
- if (options.respectImportant) {
361
- if (context.tailwindConfig.important === true) {
512
+ // Strategy based on `tailwindConfig.important`
513
+ let strategy = ((important) => {
514
+ if (important === true) {
515
+ return (rule) => {
362
516
  rule.walkDecls((d) => {
363
517
  if (d.parent.type === 'rule' && !inKeyframes(d.parent)) {
364
518
  d.important = true
365
519
  }
366
520
  })
367
- } else if (typeof context.tailwindConfig.important === 'string') {
521
+ }
522
+ }
523
+
524
+ if (typeof important === 'string') {
525
+ return (rule) => {
526
+ rule.selectors = rule.selectors.map((selector) => {
527
+ return `${important} ${selector}`
528
+ })
529
+ }
530
+ }
531
+ })(context.tailwindConfig.important)
532
+
533
+ return allRules.flat(1).map(([{ sort, layer, options }, rule]) => {
534
+ if (options.respectImportant) {
535
+ if (strategy) {
368
536
  let container = postcss.root({ nodes: [rule.clone()] })
369
537
  container.walkRules((r) => {
370
538
  if (inKeyframes(r)) {
371
539
  return
372
540
  }
373
541
 
374
- r.selectors = r.selectors.map((selector) => {
375
- return `${context.tailwindConfig.important} ${selector}`
376
- })
542
+ strategy(r)
377
543
  })
378
544
  rule = container.nodes[0]
379
545
  }
380
546
  }
547
+
381
548
  return [sort | context.layerOrder[layer], rule]
382
549
  })
383
550
  }
@@ -3,8 +3,13 @@ import log from '../util/log'
3
3
  export default function normalizeTailwindDirectives(root) {
4
4
  let tailwindDirectives = new Set()
5
5
  let layerDirectives = new Set()
6
+ let applyDirectives = new Set()
6
7
 
7
8
  root.walkAtRules((atRule) => {
9
+ if (atRule.name === 'apply') {
10
+ applyDirectives.add(atRule)
11
+ }
12
+
8
13
  if (atRule.name === 'import') {
9
14
  if (atRule.params === '"tailwindcss/base"' || atRule.params === "'tailwindcss/base'") {
10
15
  atRule.name = 'tailwind'
@@ -74,5 +79,5 @@ export default function normalizeTailwindDirectives(root) {
74
79
  }
75
80
  }
76
81
 
77
- return tailwindDirectives
82
+ return { tailwindDirectives, applyDirectives }
78
83
  }
@@ -0,0 +1,52 @@
1
+ function partitionRules(root) {
2
+ if (!root.walkAtRules) return
3
+
4
+ let applyParents = new Set()
5
+
6
+ root.walkAtRules('apply', (rule) => {
7
+ applyParents.add(rule.parent)
8
+ })
9
+
10
+ if (applyParents.size === 0) {
11
+ return
12
+ }
13
+
14
+ for (let rule of applyParents) {
15
+ let nodeGroups = []
16
+ let lastGroup = []
17
+
18
+ for (let node of rule.nodes) {
19
+ if (node.type === 'atrule' && node.name === 'apply') {
20
+ if (lastGroup.length > 0) {
21
+ nodeGroups.push(lastGroup)
22
+ lastGroup = []
23
+ }
24
+ nodeGroups.push([node])
25
+ } else {
26
+ lastGroup.push(node)
27
+ }
28
+ }
29
+
30
+ if (lastGroup.length > 0) {
31
+ nodeGroups.push(lastGroup)
32
+ }
33
+
34
+ if (nodeGroups.length === 1) {
35
+ continue
36
+ }
37
+
38
+ for (let group of [...nodeGroups].reverse()) {
39
+ let clone = rule.clone({ nodes: [] })
40
+ clone.append(group)
41
+ rule.after(clone)
42
+ }
43
+
44
+ rule.remove()
45
+ }
46
+ }
47
+
48
+ export default function expandApplyAtRules() {
49
+ return (root) => {
50
+ partitionRules(root)
51
+ }
52
+ }
@@ -71,6 +71,8 @@ function extractElementSelector(selector) {
71
71
  export default function resolveDefaultsAtRules({ tailwindConfig }) {
72
72
  return (root) => {
73
73
  let variableNodeMap = new Map()
74
+
75
+ /** @type {Set<import('postcss').AtRule>} */
74
76
  let universals = new Set()
75
77
 
76
78
  root.walkAtRules('defaults', (rule) => {
@@ -90,31 +92,50 @@ export default function resolveDefaultsAtRules({ tailwindConfig }) {
90
92
  })
91
93
 
92
94
  for (let universal of universals) {
93
- let selectors = new Set()
95
+ /** @type {Map<string, Set<string>>} */
96
+ let selectorGroups = new Map()
94
97
 
95
98
  let rules = variableNodeMap.get(universal.params) ?? []
96
99
 
97
100
  for (let rule of rules) {
98
101
  for (let selector of extractElementSelector(rule.selector)) {
102
+ // If selector contains a vendor prefix after a pseudo element or class,
103
+ // we consider them separately because merging the declarations into
104
+ // a single rule will cause browsers that do not understand the
105
+ // vendor prefix to throw out the whole rule
106
+ let selectorGroupName =
107
+ selector.includes(':-') || selector.includes('::-') ? selector : '__DEFAULT__'
108
+
109
+ let selectors = selectorGroups.get(selectorGroupName) ?? new Set()
110
+ selectorGroups.set(selectorGroupName, selectors)
111
+
99
112
  selectors.add(selector)
100
113
  }
101
114
  }
102
115
 
103
- if (selectors.size === 0) {
104
- universal.remove()
105
- continue
106
- }
116
+ if (flagEnabled(tailwindConfig, 'optimizeUniversalDefaults')) {
117
+ if (selectorGroups.size === 0) {
118
+ universal.remove()
119
+ continue
120
+ }
107
121
 
108
- let universalRule = postcss.rule()
122
+ for (let [, selectors] of selectorGroups) {
123
+ let universalRule = postcss.rule()
109
124
 
110
- if (flagEnabled(tailwindConfig, 'optimizeUniversalDefaults')) {
111
- universalRule.selectors = [...selectors]
125
+ universalRule.selectors = [...selectors]
126
+
127
+ universalRule.append(universal.nodes.map((node) => node.clone()))
128
+ universal.before(universalRule)
129
+ }
112
130
  } else {
131
+ let universalRule = postcss.rule()
132
+
113
133
  universalRule.selectors = ['*', '::before', '::after']
134
+
135
+ universalRule.append(universal.nodes)
136
+ universal.before(universalRule)
114
137
  }
115
138
 
116
- universalRule.append(universal.nodes)
117
- universal.before(universalRule)
118
139
  universal.remove()
119
140
  }
120
141
  }