tailwindcss 0.0.0-insiders.7c337f2 → 0.0.0-insiders.7d8eb21

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 (161) hide show
  1. package/CHANGELOG.md +246 -6
  2. package/README.md +13 -9
  3. package/defaultTheme.d.ts +2 -1
  4. package/lib/cli/build/deps.js +54 -0
  5. package/lib/cli/build/index.js +48 -0
  6. package/lib/cli/build/plugin.js +362 -0
  7. package/lib/cli/build/utils.js +78 -0
  8. package/lib/cli/build/watching.js +178 -0
  9. package/lib/cli/help/index.js +71 -0
  10. package/lib/cli/index.js +18 -0
  11. package/lib/cli/init/index.js +46 -0
  12. package/lib/cli/shared.js +12 -0
  13. package/lib/cli-peer-dependencies.js +18 -5
  14. package/lib/cli.js +62 -592
  15. package/lib/constants.js +19 -12
  16. package/lib/corePluginList.js +5 -3
  17. package/lib/corePlugins.js +619 -347
  18. package/lib/css/preflight.css +7 -0
  19. package/lib/featureFlags.js +34 -22
  20. package/lib/index.js +16 -9
  21. package/lib/lib/cacheInvalidation.js +42 -21
  22. package/lib/lib/collapseAdjacentRules.js +16 -14
  23. package/lib/lib/collapseDuplicateDeclarations.js +13 -10
  24. package/lib/lib/content.js +177 -0
  25. package/lib/lib/defaultExtractor.js +230 -33
  26. package/lib/lib/detectNesting.js +13 -4
  27. package/lib/lib/evaluateTailwindFunctions.js +105 -42
  28. package/lib/lib/expandApplyAtRules.js +151 -92
  29. package/lib/lib/expandTailwindAtRules.js +134 -149
  30. package/lib/lib/findAtConfigPath.js +44 -0
  31. package/lib/lib/generateRules.js +404 -185
  32. package/lib/lib/getModuleDependencies.js +21 -18
  33. package/lib/lib/normalizeTailwindDirectives.js +11 -8
  34. package/lib/lib/offsets.js +285 -0
  35. package/lib/lib/partitionApplyAtRules.js +14 -11
  36. package/lib/lib/regex.js +60 -0
  37. package/lib/lib/remap-bitfield.js +87 -0
  38. package/lib/lib/resolveDefaultsAtRules.js +97 -79
  39. package/lib/lib/setupContextUtils.js +567 -242
  40. package/lib/lib/setupTrackingContext.js +77 -106
  41. package/lib/lib/sharedState.js +18 -10
  42. package/lib/lib/substituteScreenAtRules.js +9 -7
  43. package/lib/postcss-plugins/nesting/index.js +7 -5
  44. package/lib/postcss-plugins/nesting/plugin.js +10 -8
  45. package/lib/processTailwindFeatures.js +34 -31
  46. package/lib/public/colors.js +7 -5
  47. package/lib/public/create-plugin.js +6 -4
  48. package/lib/public/default-config.js +7 -5
  49. package/lib/public/default-theme.js +7 -5
  50. package/lib/public/resolve-config.js +13 -10
  51. package/lib/util/bigSign.js +4 -1
  52. package/lib/util/buildMediaQuery.js +10 -5
  53. package/lib/util/cloneDeep.js +6 -5
  54. package/lib/util/cloneNodes.js +9 -2
  55. package/lib/util/color.js +20 -17
  56. package/lib/util/configurePlugins.js +5 -2
  57. package/lib/util/createPlugin.js +6 -5
  58. package/lib/util/createUtilityPlugin.js +15 -13
  59. package/lib/util/dataTypes.js +75 -51
  60. package/lib/util/defaults.js +8 -5
  61. package/lib/util/escapeClassName.js +13 -10
  62. package/lib/util/escapeCommas.js +4 -1
  63. package/lib/util/flattenColorPalette.js +7 -8
  64. package/lib/util/formatVariantSelector.js +270 -103
  65. package/lib/util/getAllConfigs.js +34 -15
  66. package/lib/util/hashConfig.js +10 -7
  67. package/lib/util/isKeyframeRule.js +4 -1
  68. package/lib/util/isPlainObject.js +4 -1
  69. package/lib/util/{isValidArbitraryValue.js → isSyntacticallyValidPropertyValue.js} +34 -26
  70. package/lib/util/log.js +14 -8
  71. package/lib/util/nameClass.js +19 -8
  72. package/lib/util/negateValue.js +20 -5
  73. package/lib/util/normalizeConfig.js +94 -53
  74. package/lib/util/normalizeScreens.js +115 -4
  75. package/lib/util/parseAnimationValue.js +43 -41
  76. package/lib/util/parseBoxShadowValue.js +12 -45
  77. package/lib/util/parseDependency.js +37 -55
  78. package/lib/util/parseGlob.js +34 -0
  79. package/lib/util/parseObjectStyles.js +15 -12
  80. package/lib/util/pluginUtils.js +146 -42
  81. package/lib/util/prefixSelector.js +26 -9
  82. package/lib/util/removeAlphaVariables.js +29 -0
  83. package/lib/util/resolveConfig.js +104 -105
  84. package/lib/util/resolveConfigPath.js +20 -17
  85. package/lib/util/responsive.js +12 -9
  86. package/lib/util/splitAtTopLevelOnly.js +43 -0
  87. package/lib/util/tap.js +4 -1
  88. package/lib/util/toColorValue.js +4 -1
  89. package/lib/util/toPath.js +18 -2
  90. package/lib/util/transformThemeValue.js +23 -11
  91. package/lib/util/validateConfig.js +24 -0
  92. package/lib/util/validateFormalSyntax.js +24 -0
  93. package/lib/util/withAlphaVariable.js +32 -24
  94. package/package.json +32 -29
  95. package/peers/index.js +6693 -3036
  96. package/plugin.d.ts +10 -5
  97. package/resolveConfig.d.ts +12 -0
  98. package/scripts/generate-types.js +53 -0
  99. package/scripts/release-channel.js +18 -0
  100. package/scripts/release-notes.js +21 -0
  101. package/scripts/type-utils.js +27 -0
  102. package/src/cli/build/deps.js +56 -0
  103. package/src/cli/build/index.js +49 -0
  104. package/src/cli/build/plugin.js +434 -0
  105. package/src/cli/build/utils.js +76 -0
  106. package/src/cli/build/watching.js +227 -0
  107. package/src/cli/help/index.js +70 -0
  108. package/src/cli/index.js +3 -0
  109. package/src/cli/init/index.js +50 -0
  110. package/src/cli/shared.js +5 -0
  111. package/src/cli-peer-dependencies.js +7 -1
  112. package/src/cli.js +46 -659
  113. package/src/corePlugins.js +346 -79
  114. package/src/css/preflight.css +7 -0
  115. package/src/featureFlags.js +12 -2
  116. package/src/index.js +5 -0
  117. package/src/lib/content.js +213 -0
  118. package/src/lib/defaultExtractor.js +202 -35
  119. package/src/lib/detectNesting.js +9 -1
  120. package/src/lib/evaluateTailwindFunctions.js +78 -7
  121. package/src/lib/expandApplyAtRules.js +129 -34
  122. package/src/lib/expandTailwindAtRules.js +12 -50
  123. package/src/lib/findAtConfigPath.js +48 -0
  124. package/src/lib/generateRules.js +379 -126
  125. package/src/lib/offsets.js +351 -0
  126. package/src/lib/regex.js +74 -0
  127. package/src/lib/remap-bitfield.js +82 -0
  128. package/src/lib/resolveDefaultsAtRules.js +53 -36
  129. package/src/lib/setupContextUtils.js +510 -120
  130. package/src/lib/setupTrackingContext.js +8 -45
  131. package/src/lib/sharedState.js +2 -0
  132. package/src/util/buildMediaQuery.js +5 -3
  133. package/src/util/cloneNodes.js +5 -1
  134. package/src/util/dataTypes.js +51 -26
  135. package/src/util/formatVariantSelector.js +317 -101
  136. package/src/util/getAllConfigs.js +19 -0
  137. package/src/util/{isValidArbitraryValue.js → isSyntacticallyValidPropertyValue.js} +1 -1
  138. package/src/util/log.js +1 -1
  139. package/src/util/nameClass.js +4 -0
  140. package/src/util/negateValue.js +10 -2
  141. package/src/util/normalizeConfig.js +40 -10
  142. package/src/util/normalizeScreens.js +99 -4
  143. package/src/util/parseBoxShadowValue.js +3 -50
  144. package/src/util/parseDependency.js +37 -42
  145. package/src/util/parseGlob.js +24 -0
  146. package/src/util/pluginUtils.js +132 -10
  147. package/src/util/prefixSelector.js +28 -10
  148. package/src/util/removeAlphaVariables.js +24 -0
  149. package/src/util/resolveConfig.js +70 -62
  150. package/src/util/splitAtTopLevelOnly.js +45 -0
  151. package/src/util/toPath.js +1 -1
  152. package/src/util/transformThemeValue.js +13 -3
  153. package/src/util/validateConfig.js +13 -0
  154. package/src/util/validateFormalSyntax.js +34 -0
  155. package/src/util/withAlphaVariable.js +1 -1
  156. package/stubs/defaultConfig.stub.js +165 -164
  157. package/stubs/simpleConfig.stub.js +1 -0
  158. package/types/config.d.ts +77 -37
  159. package/types/generated/default-theme.d.ts +342 -0
  160. package/types/index.d.ts +7 -1
  161. package/types.d.ts +0 -1
@@ -0,0 +1,351 @@
1
+ // @ts-check
2
+
3
+ import bigSign from '../util/bigSign'
4
+ import { remapBitfield } from './remap-bitfield.js'
5
+
6
+ /**
7
+ * @typedef {'base' | 'defaults' | 'components' | 'utilities' | 'variants' | 'user'} Layer
8
+ */
9
+
10
+ /**
11
+ * @typedef {object} VariantOption
12
+ * @property {number} id An unique identifier to identify `matchVariant`
13
+ * @property {function | undefined} sort The sort function
14
+ * @property {string|null} value The value we want to compare
15
+ * @property {string|null} modifier The modifier that was used (if any)
16
+ */
17
+
18
+ /**
19
+ * @typedef {object} RuleOffset
20
+ * @property {Layer} layer The layer that this rule belongs to
21
+ * @property {Layer} parentLayer The layer that this rule originally belonged to. Only different from layer if this is a variant.
22
+ * @property {bigint} arbitrary 0n if false, 1n if true
23
+ * @property {bigint} variants Dynamic size. 1 bit per registered variant. 0n means no variants
24
+ * @property {bigint} parallelIndex Rule index for the parallel variant. 0 if not applicable.
25
+ * @property {bigint} index Index of the rule / utility in it's given *parent* layer. Monotonically increasing.
26
+ * @property {VariantOption[]} options Some information on how we can sort arbitrary variants
27
+ */
28
+
29
+ export class Offsets {
30
+ constructor() {
31
+ /**
32
+ * Offsets for the next rule in a given layer
33
+ *
34
+ * @type {Record<Layer, bigint>}
35
+ */
36
+ this.offsets = {
37
+ defaults: 0n,
38
+ base: 0n,
39
+ components: 0n,
40
+ utilities: 0n,
41
+ variants: 0n,
42
+ user: 0n,
43
+ }
44
+
45
+ /**
46
+ * Positions for a given layer
47
+ *
48
+ * @type {Record<Layer, bigint>}
49
+ */
50
+ this.layerPositions = {
51
+ defaults: 0n,
52
+ base: 1n,
53
+ components: 2n,
54
+ utilities: 3n,
55
+
56
+ // There isn't technically a "user" layer, but we need to give it a position
57
+ // Because it's used for ordering user-css from @apply
58
+ user: 4n,
59
+
60
+ variants: 5n,
61
+ }
62
+
63
+ /**
64
+ * The total number of functions currently registered across all variants (including arbitrary variants)
65
+ *
66
+ * @type {bigint}
67
+ */
68
+ this.reservedVariantBits = 0n
69
+
70
+ /**
71
+ * Positions for a given variant
72
+ *
73
+ * @type {Map<string, bigint>}
74
+ */
75
+ this.variantOffsets = new Map()
76
+ }
77
+
78
+ /**
79
+ * @param {Layer} layer
80
+ * @returns {RuleOffset}
81
+ */
82
+ create(layer) {
83
+ return {
84
+ layer,
85
+ parentLayer: layer,
86
+ arbitrary: 0n,
87
+ variants: 0n,
88
+ parallelIndex: 0n,
89
+ index: this.offsets[layer]++,
90
+ options: [],
91
+ }
92
+ }
93
+
94
+ /**
95
+ * @returns {RuleOffset}
96
+ */
97
+ arbitraryProperty() {
98
+ return {
99
+ ...this.create('utilities'),
100
+ arbitrary: 1n,
101
+ }
102
+ }
103
+
104
+ /**
105
+ * Get the offset for a variant
106
+ *
107
+ * @param {string} variant
108
+ * @param {number} index
109
+ * @returns {RuleOffset}
110
+ */
111
+ forVariant(variant, index = 0) {
112
+ let offset = this.variantOffsets.get(variant)
113
+ if (offset === undefined) {
114
+ throw new Error(`Cannot find offset for unknown variant ${variant}`)
115
+ }
116
+
117
+ return {
118
+ ...this.create('variants'),
119
+ variants: offset << BigInt(index),
120
+ }
121
+ }
122
+
123
+ /**
124
+ * @param {RuleOffset} rule
125
+ * @param {RuleOffset} variant
126
+ * @param {VariantOption} options
127
+ * @returns {RuleOffset}
128
+ */
129
+ applyVariantOffset(rule, variant, options) {
130
+ return {
131
+ ...rule,
132
+ layer: 'variants',
133
+ parentLayer: rule.layer === 'variants' ? rule.parentLayer : rule.layer,
134
+ variants: rule.variants | variant.variants,
135
+ options: options.sort ? [].concat(options, rule.options) : rule.options,
136
+
137
+ // TODO: Technically this is wrong. We should be handling parallel index on a per variant basis.
138
+ // We'll take the max of all the parallel indexes for now.
139
+ // @ts-ignore
140
+ parallelIndex: max([rule.parallelIndex, variant.parallelIndex]),
141
+ }
142
+ }
143
+
144
+ /**
145
+ * @param {RuleOffset} offset
146
+ * @param {number} parallelIndex
147
+ * @returns {RuleOffset}
148
+ */
149
+ applyParallelOffset(offset, parallelIndex) {
150
+ return {
151
+ ...offset,
152
+ parallelIndex: BigInt(parallelIndex),
153
+ }
154
+ }
155
+
156
+ /**
157
+ * Each variant gets 1 bit per function / rule registered.
158
+ * This is because multiple variants can be applied to a single rule and we need to know which ones are present and which ones are not.
159
+ * Additionally, every unique group of variants is grouped together in the stylesheet.
160
+ *
161
+ * This grouping is order-independent. For instance, we do not differentiate between `hover:focus` and `focus:hover`.
162
+ *
163
+ * @param {string[]} variants
164
+ * @param {(name: string) => number} getLength
165
+ */
166
+ recordVariants(variants, getLength) {
167
+ for (let variant of variants) {
168
+ this.recordVariant(variant, getLength(variant))
169
+ }
170
+ }
171
+
172
+ /**
173
+ * The same as `recordVariants` but for a single arbitrary variant at runtime.
174
+ * @param {string} variant
175
+ * @param {number} fnCount
176
+ *
177
+ * @returns {RuleOffset} The highest offset for this variant
178
+ */
179
+ recordVariant(variant, fnCount = 1) {
180
+ this.variantOffsets.set(variant, 1n << this.reservedVariantBits)
181
+
182
+ // Ensure space is reserved for each "function" in the parallel variant
183
+ // by offsetting the next variant by the number of parallel variants
184
+ // in the one we just added.
185
+
186
+ // Single functions that return parallel variants are NOT handled separately here
187
+ // They're offset by 1 (or the number of functions) as usual
188
+ // And each rule returned is tracked separately since the functions are evaluated lazily.
189
+ // @see `RuleOffset.parallelIndex`
190
+ this.reservedVariantBits += BigInt(fnCount)
191
+
192
+ return {
193
+ ...this.create('variants'),
194
+ variants: this.variantOffsets.get(variant),
195
+ }
196
+ }
197
+
198
+ /**
199
+ * @param {RuleOffset} a
200
+ * @param {RuleOffset} b
201
+ * @returns {bigint}
202
+ */
203
+ compare(a, b) {
204
+ // Sort layers together
205
+ if (a.layer !== b.layer) {
206
+ return this.layerPositions[a.layer] - this.layerPositions[b.layer]
207
+ }
208
+
209
+ // Sort based on the sorting function
210
+ for (let aOptions of a.options) {
211
+ for (let bOptions of b.options) {
212
+ if (aOptions.id !== bOptions.id) continue
213
+ if (!aOptions.sort || !bOptions.sort) continue
214
+ let result = aOptions.sort(
215
+ {
216
+ value: aOptions.value,
217
+ modifier: aOptions.modifier,
218
+ },
219
+ {
220
+ value: bOptions.value,
221
+ modifier: bOptions.modifier,
222
+ }
223
+ )
224
+ if (result !== 0) return result
225
+ }
226
+ }
227
+
228
+ // Sort variants in the order they were registered
229
+ if (a.variants !== b.variants) {
230
+ return a.variants - b.variants
231
+ }
232
+
233
+ // Make sure each rule returned by a parallel variant is sorted in ascending order
234
+ if (a.parallelIndex !== b.parallelIndex) {
235
+ return a.parallelIndex - b.parallelIndex
236
+ }
237
+
238
+ // Always sort arbitrary properties after other utilities
239
+ if (a.arbitrary !== b.arbitrary) {
240
+ return a.arbitrary - b.arbitrary
241
+ }
242
+
243
+ // Sort utilities, components, etc… in the order they were registered
244
+ return a.index - b.index
245
+ }
246
+
247
+ /**
248
+ * Arbitrary variants are recorded in the order they're encountered.
249
+ * This means that the order is not stable between environments and sets of content files.
250
+ *
251
+ * In order to make the order stable, we need to remap the arbitrary variant offsets to
252
+ * be in alphabetical order starting from the offset of the first arbitrary variant.
253
+ */
254
+ recalculateVariantOffsets() {
255
+ // Sort the variants by their name
256
+ let variants = Array.from(this.variantOffsets.entries())
257
+ .filter(([v]) => v.startsWith('['))
258
+ .sort(([a], [z]) => fastCompare(a, z))
259
+
260
+ // Sort the list of offsets
261
+ // This is not necessarily a discrete range of numbers which is why
262
+ // we're using sort instead of creating a range from min/max
263
+ let newOffsets = variants.map(([, offset]) => offset).sort((a, z) => bigSign(a - z))
264
+
265
+ // Create a map from the old offsets to the new offsets in the new sort order
266
+ /** @type {[bigint, bigint][]} */
267
+ let mapping = variants.map(([, oldOffset], i) => [oldOffset, newOffsets[i]])
268
+
269
+ // Remove any variants that will not move letting us skip
270
+ // remapping if everything happens to be in order
271
+ return mapping.filter(([a, z]) => a !== z)
272
+ }
273
+
274
+ /**
275
+ * @template T
276
+ * @param {[RuleOffset, T][]} list
277
+ * @returns {[RuleOffset, T][]}
278
+ */
279
+ remapArbitraryVariantOffsets(list) {
280
+ let mapping = this.recalculateVariantOffsets()
281
+
282
+ // No arbitrary variants? Nothing to do.
283
+ // Everyhing already in order? Nothing to do.
284
+ if (mapping.length === 0) {
285
+ return list
286
+ }
287
+
288
+ // Remap every variant offset in the list
289
+ return list.map((item) => {
290
+ let [offset, rule] = item
291
+
292
+ offset = {
293
+ ...offset,
294
+ variants: remapBitfield(offset.variants, mapping),
295
+ }
296
+
297
+ return [offset, rule]
298
+ })
299
+ }
300
+
301
+ /**
302
+ * @template T
303
+ * @param {[RuleOffset, T][]} list
304
+ * @returns {[RuleOffset, T][]}
305
+ */
306
+ sort(list) {
307
+ list = this.remapArbitraryVariantOffsets(list)
308
+
309
+ return list.sort(([a], [b]) => bigSign(this.compare(a, b)))
310
+ }
311
+ }
312
+
313
+ /**
314
+ *
315
+ * @param {bigint[]} nums
316
+ * @returns {bigint|null}
317
+ */
318
+ function max(nums) {
319
+ let max = null
320
+
321
+ for (const num of nums) {
322
+ max = max ?? num
323
+ max = max > num ? max : num
324
+ }
325
+
326
+ return max
327
+ }
328
+
329
+ /**
330
+ * A fast ASCII order string comparison function.
331
+ *
332
+ * Using `.sort()` without a custom compare function is faster
333
+ * But you can only use that if you're sorting an array of
334
+ * only strings. If you're sorting strings inside objects
335
+ * or arrays, you need must use a custom compare function.
336
+ *
337
+ * @param {string} a
338
+ * @param {string} b
339
+ */
340
+ function fastCompare(a, b) {
341
+ let aLen = a.length
342
+ let bLen = b.length
343
+ let minLen = aLen < bLen ? aLen : bLen
344
+
345
+ for (let i = 0; i < minLen; i++) {
346
+ let cmp = a.charCodeAt(i) - b.charCodeAt(i)
347
+ if (cmp !== 0) return cmp
348
+ }
349
+
350
+ return aLen - bLen
351
+ }
@@ -0,0 +1,74 @@
1
+ const REGEX_SPECIAL = /[\\^$.*+?()[\]{}|]/g
2
+ const REGEX_HAS_SPECIAL = RegExp(REGEX_SPECIAL.source)
3
+
4
+ /**
5
+ * @param {string|RegExp|Array<string|RegExp>} source
6
+ */
7
+ function toSource(source) {
8
+ source = Array.isArray(source) ? source : [source]
9
+
10
+ source = source.map((item) => (item instanceof RegExp ? item.source : item))
11
+
12
+ return source.join('')
13
+ }
14
+
15
+ /**
16
+ * @param {string|RegExp|Array<string|RegExp>} source
17
+ */
18
+ export function pattern(source) {
19
+ return new RegExp(toSource(source), 'g')
20
+ }
21
+
22
+ /**
23
+ * @param {string|RegExp|Array<string|RegExp>} source
24
+ */
25
+ export function withoutCapturing(source) {
26
+ return new RegExp(`(?:${toSource(source)})`, 'g')
27
+ }
28
+
29
+ /**
30
+ * @param {Array<string|RegExp>} sources
31
+ */
32
+ export function any(sources) {
33
+ return `(?:${sources.map(toSource).join('|')})`
34
+ }
35
+
36
+ /**
37
+ * @param {string|RegExp} source
38
+ */
39
+ export function optional(source) {
40
+ return `(?:${toSource(source)})?`
41
+ }
42
+
43
+ /**
44
+ * @param {string|RegExp|Array<string|RegExp>} source
45
+ */
46
+ export function zeroOrMore(source) {
47
+ return `(?:${toSource(source)})*`
48
+ }
49
+
50
+ /**
51
+ * Generate a RegExp that matches balanced brackets for a given depth
52
+ * We have to specify a depth because JS doesn't support recursive groups using ?R
53
+ *
54
+ * Based on https://stackoverflow.com/questions/17759004/how-to-match-string-within-parentheses-nested-in-java/17759264#17759264
55
+ *
56
+ * @param {string|RegExp|Array<string|RegExp>} source
57
+ */
58
+ export function nestedBrackets(open, close, depth = 1) {
59
+ return withoutCapturing([
60
+ escape(open),
61
+ /[^\s]*/,
62
+ depth === 1
63
+ ? `[^${escape(open)}${escape(close)}\s]*`
64
+ : any([`[^${escape(open)}${escape(close)}\s]*`, nestedBrackets(open, close, depth - 1)]),
65
+ /[^\s]*/,
66
+ escape(close),
67
+ ])
68
+ }
69
+
70
+ export function escape(string) {
71
+ return string && REGEX_HAS_SPECIAL.test(string)
72
+ ? string.replace(REGEX_SPECIAL, '\\$&')
73
+ : string || ''
74
+ }
@@ -0,0 +1,82 @@
1
+ // @ts-check
2
+
3
+ /**
4
+ * We must remap all the old bits to new bits for each set variant
5
+ * Only arbitrary variants are considered as those are the only
6
+ * ones that need to be re-sorted at this time
7
+ *
8
+ * An iterated process that removes and sets individual bits simultaneously
9
+ * will not work because we may have a new bit that is also a later old bit
10
+ * This means that we would be removing a previously set bit which we don't
11
+ * want to do
12
+ *
13
+ * For example (assume `bN` = `1<<N`)
14
+ * Given the "total" mapping `[[b1, b3], [b2, b4], [b3, b1], [b4, b2]]`
15
+ * The mapping is "total" because:
16
+ * 1. Every input and output is accounted for
17
+ * 2. All combinations are unique
18
+ * 3. No one input maps to multiple outputs and vice versa
19
+ * And, given an offset with all bits set:
20
+ * V = b1 | b2 | b3 | b4
21
+ *
22
+ * Let's explore the issue with removing and setting bits simultaneously:
23
+ * V & ~b1 | b3 = b2 | b3 | b4
24
+ * V & ~b2 | b4 = b3 | b4
25
+ * V & ~b3 | b1 = b1 | b4
26
+ * V & ~b4 | b2 = b1 | b2
27
+ *
28
+ * As you can see, we end up with the wrong result.
29
+ * This is because we're removing a bit that was previously set.
30
+ * And, thus the final result is missing b3 and b4.
31
+ *
32
+ * Now, let's explore the issue with removing the bits first:
33
+ * V & ~b1 = b2 | b3 | b4
34
+ * V & ~b2 = b3 | b4
35
+ * V & ~b3 = b4
36
+ * V & ~b4 = 0
37
+ *
38
+ * And then setting the bits:
39
+ * V | b3 = b3
40
+ * V | b4 = b3 | b4
41
+ * V | b1 = b1 | b3 | b4
42
+ * V | b2 = b1 | b2 | b3 | b4
43
+ *
44
+ * We get the correct result because we're not removing any bits that were
45
+ * previously set thus properly remapping the bits to the new order
46
+ *
47
+ * To collect this into a single operation that can be done simultaneously
48
+ * we must first create a mask for the old bits that are set and a mask for
49
+ * the new bits that are set. Then we can remove the old bits and set the new
50
+ * bits simultaneously in a "single" operation like so:
51
+ * OldMask = b1 | b2 | b3 | b4
52
+ * NewMask = b3 | b4 | b1 | b2
53
+ *
54
+ * So this:
55
+ * V & ~oldMask | newMask
56
+ *
57
+ * Expands to this:
58
+ * V & ~b1 & ~b2 & ~b3 & ~b4 | b3 | b4 | b1 | b2
59
+ *
60
+ * Which becomes this:
61
+ * b1 | b2 | b3 | b4
62
+ *
63
+ * Which is the correct result!
64
+ *
65
+ * @param {bigint} num
66
+ * @param {[bigint, bigint][]} mapping
67
+ */
68
+ export function remapBitfield(num, mapping) {
69
+ // Create masks for the old and new bits that are set
70
+ let oldMask = 0n
71
+ let newMask = 0n
72
+ for (let [oldBit, newBit] of mapping) {
73
+ if (num & oldBit) {
74
+ oldMask = oldMask | oldBit
75
+ newMask = newMask | newBit
76
+ }
77
+ }
78
+
79
+ // Remove all old bits
80
+ // Set all new bits
81
+ return (num & ~oldMask) | newMask
82
+ }
@@ -91,56 +91,73 @@ export default function resolveDefaultsAtRules({ tailwindConfig }) {
91
91
  rule.remove()
92
92
  })
93
93
 
94
- for (let universal of universals) {
95
- /** @type {Map<string, Set<string>>} */
96
- let selectorGroups = new Map()
94
+ if (flagEnabled(tailwindConfig, 'optimizeUniversalDefaults')) {
95
+ for (let universal of universals) {
96
+ /** @type {Map<string, Set<string>>} */
97
+ let selectorGroups = new Map()
98
+
99
+ let rules = variableNodeMap.get(universal.params) ?? []
100
+
101
+ for (let rule of rules) {
102
+ for (let selector of extractElementSelector(rule.selector)) {
103
+ // If selector contains a vendor prefix after a pseudo element or class,
104
+ // we consider them separately because merging the declarations into
105
+ // a single rule will cause browsers that do not understand the
106
+ // vendor prefix to throw out the whole rule
107
+ let selectorGroupName =
108
+ selector.includes(':-') || selector.includes('::-') ? selector : '__DEFAULT__'
109
+
110
+ let selectors = selectorGroups.get(selectorGroupName) ?? new Set()
111
+ selectorGroups.set(selectorGroupName, selectors)
112
+
113
+ selectors.add(selector)
114
+ }
115
+ }
97
116
 
98
- let rules = variableNodeMap.get(universal.params) ?? []
117
+ if (flagEnabled(tailwindConfig, 'optimizeUniversalDefaults')) {
118
+ if (selectorGroups.size === 0) {
119
+ universal.remove()
120
+ continue
121
+ }
99
122
 
100
- for (let rule of rules) {
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__'
123
+ for (let [, selectors] of selectorGroups) {
124
+ let universalRule = postcss.rule({
125
+ source: universal.source,
126
+ })
108
127
 
109
- let selectors = selectorGroups.get(selectorGroupName) ?? new Set()
110
- selectorGroups.set(selectorGroupName, selectors)
128
+ universalRule.selectors = [...selectors]
111
129
 
112
- selectors.add(selector)
130
+ universalRule.append(universal.nodes.map((node) => node.clone()))
131
+ universal.before(universalRule)
132
+ }
113
133
  }
114
- }
115
134
 
116
- if (flagEnabled(tailwindConfig, 'optimizeUniversalDefaults')) {
117
- if (selectorGroups.size === 0) {
118
- universal.remove()
119
- continue
120
- }
121
-
122
- for (let [, selectors] of selectorGroups) {
123
- let universalRule = postcss.rule({
124
- source: universal.source,
125
- })
135
+ universal.remove()
136
+ }
137
+ } else if (universals.size) {
138
+ let universalRule = postcss.rule({
139
+ selectors: ['*', '::before', '::after'],
140
+ })
126
141
 
127
- universalRule.selectors = [...selectors]
142
+ for (let universal of universals) {
143
+ universalRule.append(universal.nodes)
128
144
 
129
- universalRule.append(universal.nodes.map((node) => node.clone()))
145
+ if (!universalRule.parent) {
130
146
  universal.before(universalRule)
131
147
  }
132
- } else {
133
- let universalRule = postcss.rule({
134
- source: universal.source,
135
- })
136
148
 
137
- universalRule.selectors = ['*', '::before', '::after']
149
+ if (!universalRule.source) {
150
+ universalRule.source = universal.source
151
+ }
138
152
 
139
- universalRule.append(universal.nodes)
140
- universal.before(universalRule)
153
+ universal.remove()
141
154
  }
142
155
 
143
- universal.remove()
156
+ let backdropRule = universalRule.clone({
157
+ selectors: ['::backdrop'],
158
+ })
159
+
160
+ universalRule.after(backdropRule)
144
161
  }
145
162
  }
146
163
  }