tailwindcss 0.0.0-insiders.ddec022 → 0.0.0-insiders.de00a62
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.
- package/lib/cli/build/plugin.js +6 -6
- package/lib/cli/build/watching.js +1 -1
- package/lib/corePluginList.js +5 -1
- package/lib/corePlugins.js +170 -13
- package/lib/css/preflight.css +24 -8
- package/lib/lib/content.js +36 -3
- package/lib/lib/defaultExtractor.js +33 -25
- package/lib/lib/evaluateTailwindFunctions.js +5 -3
- package/lib/lib/expandApplyAtRules.js +6 -0
- package/lib/lib/expandTailwindAtRules.js +23 -6
- package/lib/lib/generateRules.js +47 -25
- package/lib/lib/load-config.js +14 -3
- package/lib/lib/offsets.js +51 -2
- package/lib/lib/resolveDefaultsAtRules.js +3 -1
- package/lib/lib/setupContextUtils.js +76 -37
- package/lib/lib/setupTrackingContext.js +2 -1
- package/lib/oxide/cli/build/plugin.js +6 -6
- package/lib/plugin.js +3 -3
- package/lib/processTailwindFeatures.js +2 -2
- package/lib/util/cloneNodes.js +33 -13
- package/lib/util/color.js +1 -1
- package/lib/util/dataTypes.js +135 -16
- package/lib/util/formatVariantSelector.js +10 -3
- package/lib/util/isPlainObject.js +1 -1
- package/lib/util/pluginUtils.js +13 -0
- package/lib/util/prefixSelector.js +1 -1
- package/lib/util/pseudoElements.js +21 -34
- package/lib/value-parser/LICENSE +22 -0
- package/lib/value-parser/README.md +3 -0
- package/lib/value-parser/index.d.js +2 -0
- package/lib/value-parser/index.js +22 -0
- package/lib/value-parser/parse.js +259 -0
- package/lib/value-parser/stringify.js +38 -0
- package/lib/value-parser/unit.js +86 -0
- package/lib/value-parser/walk.js +16 -0
- package/nesting/index.d.ts +4 -0
- package/package.json +5 -6
- package/peers/index.js +701 -617
- package/resolveConfig.d.ts +22 -3
- package/scripts/generate-types.js +1 -2
- package/src/cli/build/plugin.js +6 -6
- package/src/cli/build/watching.js +1 -1
- package/src/corePluginList.js +1 -1
- package/src/corePlugins.js +149 -12
- package/src/css/preflight.css +24 -8
- package/src/featureFlags.js +1 -5
- package/src/lib/content.js +42 -1
- package/src/lib/defaultExtractor.js +30 -17
- package/src/lib/evaluateTailwindFunctions.js +4 -1
- package/src/lib/expandApplyAtRules.js +7 -0
- package/src/lib/expandTailwindAtRules.js +23 -6
- package/src/lib/generateRules.js +50 -26
- package/src/lib/load-config.ts +8 -0
- package/src/lib/offsets.js +61 -2
- package/src/lib/resolveDefaultsAtRules.js +5 -1
- package/src/lib/setupContextUtils.js +77 -38
- package/src/lib/setupTrackingContext.js +1 -3
- package/src/oxide/cli/build/plugin.ts +6 -6
- package/src/plugin.js +3 -3
- package/src/processTailwindFeatures.js +3 -2
- package/src/util/cloneNodes.js +35 -14
- package/src/util/color.js +1 -1
- package/src/util/dataTypes.js +143 -18
- package/src/util/formatVariantSelector.js +11 -3
- package/src/util/isPlainObject.js +1 -1
- package/src/util/pluginUtils.js +16 -0
- package/src/util/prefixSelector.js +1 -0
- package/src/util/pseudoElements.js +18 -17
- package/src/value-parser/LICENSE +22 -0
- package/src/value-parser/README.md +3 -0
- package/src/value-parser/index.d.ts +177 -0
- package/src/value-parser/index.js +28 -0
- package/src/value-parser/parse.js +303 -0
- package/src/value-parser/stringify.js +41 -0
- package/src/value-parser/unit.js +118 -0
- package/src/value-parser/walk.js +18 -0
- package/stubs/config.full.js +86 -14
- package/types/config.d.ts +17 -9
- package/types/generated/corePluginList.d.ts +1 -1
- package/types/generated/default-theme.d.ts +35 -9
- package/types/index.d.ts +7 -3
|
@@ -553,6 +553,13 @@ function processApply(root, context, localCache) {
|
|
|
553
553
|
? parent.selector.slice(importantSelector.length)
|
|
554
554
|
: parent.selector
|
|
555
555
|
|
|
556
|
+
// If the selector becomes empty after replacing the important selector
|
|
557
|
+
// This means that it's the same as the parent selector and we don't want to replace it
|
|
558
|
+
// Otherwise we'll crash
|
|
559
|
+
if (parentSelector === '') {
|
|
560
|
+
parentSelector = parent.selector
|
|
561
|
+
}
|
|
562
|
+
|
|
556
563
|
rule.selector = replaceSelector(parentSelector, rule.selector, applyCandidate)
|
|
557
564
|
|
|
558
565
|
// And then re-add it if it was removed
|
|
@@ -98,7 +98,7 @@ function buildStylesheet(rules, context) {
|
|
|
98
98
|
}
|
|
99
99
|
|
|
100
100
|
export default function expandTailwindAtRules(context) {
|
|
101
|
-
return (root) => {
|
|
101
|
+
return async (root) => {
|
|
102
102
|
let layerNodes = {
|
|
103
103
|
base: null,
|
|
104
104
|
components: null,
|
|
@@ -145,11 +145,25 @@ export default function expandTailwindAtRules(context) {
|
|
|
145
145
|
// getClassCandidatesOxide(file, transformer(content), extractor, candidates, seen)
|
|
146
146
|
// }
|
|
147
147
|
} else {
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
148
|
+
/** @type {[item: {file?: string, content?: string}, meta: {transformer: any, extractor: any}][]} */
|
|
149
|
+
let regexParserContent = []
|
|
150
|
+
|
|
151
|
+
for (let item of context.changedContent) {
|
|
152
|
+
let transformer = getTransformer(context.tailwindConfig, item.extension)
|
|
153
|
+
let extractor = getExtractor(context, item.extension)
|
|
154
|
+
regexParserContent.push([item, { transformer, extractor }])
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
const BATCH_SIZE = 500
|
|
158
|
+
|
|
159
|
+
for (let i = 0; i < regexParserContent.length; i += BATCH_SIZE) {
|
|
160
|
+
let batch = regexParserContent.slice(i, i + BATCH_SIZE)
|
|
161
|
+
await Promise.all(
|
|
162
|
+
batch.map(async ([{ file, content }, { transformer, extractor }]) => {
|
|
163
|
+
content = file ? await fs.promises.readFile(file, 'utf8') : content
|
|
164
|
+
getClassCandidates(transformer(content), extractor, candidates, seen)
|
|
165
|
+
})
|
|
166
|
+
)
|
|
153
167
|
}
|
|
154
168
|
}
|
|
155
169
|
|
|
@@ -251,6 +265,9 @@ export default function expandTailwindAtRules(context) {
|
|
|
251
265
|
)
|
|
252
266
|
}
|
|
253
267
|
|
|
268
|
+
// TODO: Why is the root node having no source location for `end` possible?
|
|
269
|
+
root.source.end = root.source.end ?? root.source.start
|
|
270
|
+
|
|
254
271
|
// If we've got a utility layer and no utilities are generated there's likely something wrong
|
|
255
272
|
const hasUtilityVariants = variantNodes.some(
|
|
256
273
|
(node) => node.raws.tailwind?.parentLayer === 'utilities'
|
package/src/lib/generateRules.js
CHANGED
|
@@ -13,7 +13,7 @@ import {
|
|
|
13
13
|
} from '../util/formatVariantSelector'
|
|
14
14
|
import { asClass } from '../util/nameClass'
|
|
15
15
|
import { normalize } from '../util/dataTypes'
|
|
16
|
-
import { isValidVariantFormatString, parseVariant } from './setupContextUtils'
|
|
16
|
+
import { isValidVariantFormatString, parseVariant, INTERNAL_FEATURES } from './setupContextUtils'
|
|
17
17
|
import isValidArbitraryValue from '../util/isSyntacticallyValidPropertyValue'
|
|
18
18
|
import { splitAtTopLevelOnly } from '../util/splitAtTopLevelOnly.js'
|
|
19
19
|
import { flagEnabled } from '../featureFlags'
|
|
@@ -119,10 +119,20 @@ function applyImportant(matches, classCandidate) {
|
|
|
119
119
|
|
|
120
120
|
let result = []
|
|
121
121
|
|
|
122
|
+
function isInKeyframes(rule) {
|
|
123
|
+
return rule.parent && rule.parent.type === 'atrule' && rule.parent.name === 'keyframes'
|
|
124
|
+
}
|
|
125
|
+
|
|
122
126
|
for (let [meta, rule] of matches) {
|
|
123
127
|
let container = postcss.root({ nodes: [rule.clone()] })
|
|
124
128
|
|
|
125
129
|
container.walkRules((r) => {
|
|
130
|
+
// Declarations inside keyframes cannot be marked as important
|
|
131
|
+
// They will be ignored by the browser
|
|
132
|
+
if (isInKeyframes(r)) {
|
|
133
|
+
return
|
|
134
|
+
}
|
|
135
|
+
|
|
126
136
|
let ast = selectorParser().astSync(r.selector)
|
|
127
137
|
|
|
128
138
|
// Remove extraneous selectors that do not include the base candidate
|
|
@@ -193,13 +203,13 @@ function applyVariant(variant, matches, context) {
|
|
|
193
203
|
// group[:hover] (`-` is missing)
|
|
194
204
|
let match = /(.)(-?)\[(.*)\]/g.exec(variant)
|
|
195
205
|
if (match) {
|
|
196
|
-
let [, char,
|
|
206
|
+
let [, char, separator, value] = match
|
|
197
207
|
// @-[200px] case
|
|
198
|
-
if (char === '@' &&
|
|
208
|
+
if (char === '@' && separator === '-') return []
|
|
199
209
|
// group[:hover] case
|
|
200
|
-
if (char !== '@' &&
|
|
210
|
+
if (char !== '@' && separator === '') return []
|
|
201
211
|
|
|
202
|
-
variant = variant.replace(`${
|
|
212
|
+
variant = variant.replace(`${separator}[${value}]`, '')
|
|
203
213
|
args.value = value
|
|
204
214
|
}
|
|
205
215
|
}
|
|
@@ -230,9 +240,16 @@ function applyVariant(variant, matches, context) {
|
|
|
230
240
|
|
|
231
241
|
if (context.variantMap.has(variant)) {
|
|
232
242
|
let isArbitraryVariant = isArbitraryValue(variant)
|
|
243
|
+
let internalFeatures = context.variantOptions.get(variant)?.[INTERNAL_FEATURES] ?? {}
|
|
233
244
|
let variantFunctionTuples = context.variantMap.get(variant).slice()
|
|
234
245
|
let result = []
|
|
235
246
|
|
|
247
|
+
let respectPrefix = (() => {
|
|
248
|
+
if (isArbitraryVariant) return false
|
|
249
|
+
if (internalFeatures.respectPrefix === false) return false
|
|
250
|
+
return true
|
|
251
|
+
})()
|
|
252
|
+
|
|
236
253
|
for (let [meta, rule] of matches) {
|
|
237
254
|
// Don't generate variants for user css
|
|
238
255
|
if (meta.layer === 'user') {
|
|
@@ -293,7 +310,7 @@ function applyVariant(variant, matches, context) {
|
|
|
293
310
|
format(selectorFormat) {
|
|
294
311
|
collectedFormats.push({
|
|
295
312
|
format: selectorFormat,
|
|
296
|
-
|
|
313
|
+
respectPrefix,
|
|
297
314
|
})
|
|
298
315
|
},
|
|
299
316
|
args,
|
|
@@ -322,7 +339,7 @@ function applyVariant(variant, matches, context) {
|
|
|
322
339
|
if (typeof ruleWithVariant === 'string') {
|
|
323
340
|
collectedFormats.push({
|
|
324
341
|
format: ruleWithVariant,
|
|
325
|
-
|
|
342
|
+
respectPrefix,
|
|
326
343
|
})
|
|
327
344
|
}
|
|
328
345
|
|
|
@@ -366,7 +383,7 @@ function applyVariant(variant, matches, context) {
|
|
|
366
383
|
// format: .foo &
|
|
367
384
|
collectedFormats.push({
|
|
368
385
|
format: modified.replace(rebuiltBase, '&'),
|
|
369
|
-
|
|
386
|
+
respectPrefix,
|
|
370
387
|
})
|
|
371
388
|
rule.selector = before
|
|
372
389
|
})
|
|
@@ -489,13 +506,13 @@ function extractArbitraryProperty(classCandidate, context) {
|
|
|
489
506
|
return null
|
|
490
507
|
}
|
|
491
508
|
|
|
492
|
-
let normalized = normalize(value)
|
|
509
|
+
let normalized = normalize(value, { property })
|
|
493
510
|
|
|
494
511
|
if (!isParsableCssValue(property, normalized)) {
|
|
495
512
|
return null
|
|
496
513
|
}
|
|
497
514
|
|
|
498
|
-
let sort = context.offsets.arbitraryProperty()
|
|
515
|
+
let sort = context.offsets.arbitraryProperty(classCandidate)
|
|
499
516
|
|
|
500
517
|
return [
|
|
501
518
|
[
|
|
@@ -566,7 +583,7 @@ function* recordCandidates(matches, classCandidate) {
|
|
|
566
583
|
}
|
|
567
584
|
}
|
|
568
585
|
|
|
569
|
-
function* resolveMatches(candidate, context
|
|
586
|
+
function* resolveMatches(candidate, context) {
|
|
570
587
|
let separator = context.tailwindConfig.separator
|
|
571
588
|
let [classCandidate, ...variants] = splitWithSeparator(candidate, separator).reverse()
|
|
572
589
|
let important = false
|
|
@@ -576,15 +593,6 @@ function* resolveMatches(candidate, context, original = candidate) {
|
|
|
576
593
|
classCandidate = classCandidate.slice(1)
|
|
577
594
|
}
|
|
578
595
|
|
|
579
|
-
if (flagEnabled(context.tailwindConfig, 'variantGrouping')) {
|
|
580
|
-
if (classCandidate.startsWith('(') && classCandidate.endsWith(')')) {
|
|
581
|
-
let base = variants.slice().reverse().join(separator)
|
|
582
|
-
for (let part of splitAtTopLevelOnly(classCandidate.slice(1, -1), ',')) {
|
|
583
|
-
yield* resolveMatches(base + separator + part, context, original)
|
|
584
|
-
}
|
|
585
|
-
}
|
|
586
|
-
}
|
|
587
|
-
|
|
588
596
|
// TODO: Reintroduce this in ways that doesn't break on false positives
|
|
589
597
|
// function sortAgainst(toSort, against) {
|
|
590
598
|
// return toSort.slice().sort((a, z) => {
|
|
@@ -773,7 +781,7 @@ function* resolveMatches(candidate, context, original = candidate) {
|
|
|
773
781
|
match[1].raws.tailwind = { ...match[1].raws.tailwind, candidate }
|
|
774
782
|
|
|
775
783
|
// Apply final format selector
|
|
776
|
-
match = applyFinalFormat(match, { context, candidate
|
|
784
|
+
match = applyFinalFormat(match, { context, candidate })
|
|
777
785
|
|
|
778
786
|
// Skip rules with invalid selectors
|
|
779
787
|
// This will cause the candidate to be added to the "not class"
|
|
@@ -787,7 +795,7 @@ function* resolveMatches(candidate, context, original = candidate) {
|
|
|
787
795
|
}
|
|
788
796
|
}
|
|
789
797
|
|
|
790
|
-
function applyFinalFormat(match, { context, candidate
|
|
798
|
+
function applyFinalFormat(match, { context, candidate }) {
|
|
791
799
|
if (!match[0].collectedFormats) {
|
|
792
800
|
return match
|
|
793
801
|
}
|
|
@@ -822,10 +830,19 @@ function applyFinalFormat(match, { context, candidate, original }) {
|
|
|
822
830
|
}
|
|
823
831
|
|
|
824
832
|
try {
|
|
825
|
-
|
|
826
|
-
candidate
|
|
833
|
+
let selector = finalizeSelector(rule.selector, finalFormat, {
|
|
834
|
+
candidate,
|
|
827
835
|
context,
|
|
828
836
|
})
|
|
837
|
+
|
|
838
|
+
// Finalize Selector determined that this candidate is irrelevant
|
|
839
|
+
// TODO: This elimination should happen earlier so this never happens
|
|
840
|
+
if (selector === null) {
|
|
841
|
+
rule.remove()
|
|
842
|
+
return
|
|
843
|
+
}
|
|
844
|
+
|
|
845
|
+
rule.selector = selector
|
|
829
846
|
} catch {
|
|
830
847
|
// If this selector is invalid we also want to skip it
|
|
831
848
|
// But it's likely that being invalid here means there's a bug in a plugin rather than too loosely matching content
|
|
@@ -838,6 +855,11 @@ function applyFinalFormat(match, { context, candidate, original }) {
|
|
|
838
855
|
return null
|
|
839
856
|
}
|
|
840
857
|
|
|
858
|
+
// If all rules have been eliminated we can skip this candidate entirely
|
|
859
|
+
if (container.nodes.length === 0) {
|
|
860
|
+
return null
|
|
861
|
+
}
|
|
862
|
+
|
|
841
863
|
match[1] = container.nodes[0]
|
|
842
864
|
|
|
843
865
|
return match
|
|
@@ -875,7 +897,7 @@ function getImportantStrategy(important) {
|
|
|
875
897
|
}
|
|
876
898
|
}
|
|
877
899
|
|
|
878
|
-
function generateRules(candidates, context) {
|
|
900
|
+
function generateRules(candidates, context, isSorting = false) {
|
|
879
901
|
let allRules = []
|
|
880
902
|
let strategy = getImportantStrategy(context.tailwindConfig.important)
|
|
881
903
|
|
|
@@ -910,7 +932,9 @@ function generateRules(candidates, context) {
|
|
|
910
932
|
rule = container.nodes[0]
|
|
911
933
|
}
|
|
912
934
|
|
|
913
|
-
|
|
935
|
+
// Note: We have to clone rules during sorting
|
|
936
|
+
// so we eliminate some shared mutable state
|
|
937
|
+
let newEntry = [sort, isSorting ? rule.clone() : rule]
|
|
914
938
|
rules.add(newEntry)
|
|
915
939
|
context.ruleCache.add(newEntry)
|
|
916
940
|
allRules.push(newEntry)
|
package/src/lib/load-config.ts
CHANGED
|
@@ -4,6 +4,14 @@ import { transform } from 'sucrase'
|
|
|
4
4
|
import { Config } from '../../types/config'
|
|
5
5
|
|
|
6
6
|
let jiti: ReturnType<typeof jitiFactory> | null = null
|
|
7
|
+
|
|
8
|
+
// @internal
|
|
9
|
+
// This WILL be removed in some future release
|
|
10
|
+
// If you rely on this your stuff WILL break
|
|
11
|
+
export function useCustomJiti(_jiti: () => ReturnType<typeof jitiFactory>) {
|
|
12
|
+
jiti = _jiti()
|
|
13
|
+
}
|
|
14
|
+
|
|
7
15
|
function lazyJiti() {
|
|
8
16
|
return (
|
|
9
17
|
jiti ??
|
package/src/lib/offsets.js
CHANGED
|
@@ -23,7 +23,9 @@ import { remapBitfield } from './remap-bitfield.js'
|
|
|
23
23
|
* @property {bigint} arbitrary 0n if false, 1n if true
|
|
24
24
|
* @property {bigint} variants Dynamic size. 1 bit per registered variant. 0n means no variants
|
|
25
25
|
* @property {bigint} parallelIndex Rule index for the parallel variant. 0 if not applicable.
|
|
26
|
-
* @property {bigint} index Index of the rule / utility in
|
|
26
|
+
* @property {bigint} index Index of the rule / utility in its given *parent* layer. Monotonically increasing.
|
|
27
|
+
* @property {bigint} propertyOffset Offset for the arbitrary property. Only valid after sorting.
|
|
28
|
+
* @property {string} property Name/Value of the arbitrary property.
|
|
27
29
|
* @property {VariantOption[]} options Some information on how we can sort arbitrary variants
|
|
28
30
|
*/
|
|
29
31
|
|
|
@@ -88,17 +90,21 @@ export class Offsets {
|
|
|
88
90
|
variants: 0n,
|
|
89
91
|
parallelIndex: 0n,
|
|
90
92
|
index: this.offsets[layer]++,
|
|
93
|
+
propertyOffset: 0n,
|
|
94
|
+
property: '',
|
|
91
95
|
options: [],
|
|
92
96
|
}
|
|
93
97
|
}
|
|
94
98
|
|
|
95
99
|
/**
|
|
100
|
+
* @param {string} name
|
|
96
101
|
* @returns {RuleOffset}
|
|
97
102
|
*/
|
|
98
|
-
arbitraryProperty() {
|
|
103
|
+
arbitraryProperty(name) {
|
|
99
104
|
return {
|
|
100
105
|
...this.create('utilities'),
|
|
101
106
|
arbitrary: 1n,
|
|
107
|
+
property: name,
|
|
102
108
|
}
|
|
103
109
|
}
|
|
104
110
|
|
|
@@ -262,6 +268,11 @@ export class Offsets {
|
|
|
262
268
|
return a.arbitrary - b.arbitrary
|
|
263
269
|
}
|
|
264
270
|
|
|
271
|
+
// Always sort arbitrary properties alphabetically
|
|
272
|
+
if (a.propertyOffset !== b.propertyOffset) {
|
|
273
|
+
return a.propertyOffset - b.propertyOffset
|
|
274
|
+
}
|
|
275
|
+
|
|
265
276
|
// Sort utilities, components, etc… in the order they were registered
|
|
266
277
|
return a.index - b.index
|
|
267
278
|
}
|
|
@@ -320,14 +331,62 @@ export class Offsets {
|
|
|
320
331
|
})
|
|
321
332
|
}
|
|
322
333
|
|
|
334
|
+
/**
|
|
335
|
+
* @template T
|
|
336
|
+
* @param {[RuleOffset, T][]} list
|
|
337
|
+
* @returns {[RuleOffset, T][]}
|
|
338
|
+
*/
|
|
339
|
+
sortArbitraryProperties(list) {
|
|
340
|
+
// Collect all known arbitrary properties
|
|
341
|
+
let known = new Set()
|
|
342
|
+
|
|
343
|
+
for (let [offset] of list) {
|
|
344
|
+
if (offset.arbitrary === 1n) {
|
|
345
|
+
known.add(offset.property)
|
|
346
|
+
}
|
|
347
|
+
}
|
|
348
|
+
|
|
349
|
+
// No arbitrary properties? Nothing to do.
|
|
350
|
+
if (known.size === 0) {
|
|
351
|
+
return list
|
|
352
|
+
}
|
|
353
|
+
|
|
354
|
+
// Sort the properties alphabetically
|
|
355
|
+
let properties = Array.from(known).sort()
|
|
356
|
+
|
|
357
|
+
// Create a map from the property name to its offset
|
|
358
|
+
let offsets = new Map()
|
|
359
|
+
|
|
360
|
+
let offset = 1n
|
|
361
|
+
for (let property of properties) {
|
|
362
|
+
offsets.set(property, offset++)
|
|
363
|
+
}
|
|
364
|
+
|
|
365
|
+
// Apply the sorted offsets to the list
|
|
366
|
+
return list.map((item) => {
|
|
367
|
+
let [offset, rule] = item
|
|
368
|
+
|
|
369
|
+
offset = {
|
|
370
|
+
...offset,
|
|
371
|
+
propertyOffset: offsets.get(offset.property) ?? 0n,
|
|
372
|
+
}
|
|
373
|
+
|
|
374
|
+
return [offset, rule]
|
|
375
|
+
})
|
|
376
|
+
}
|
|
377
|
+
|
|
323
378
|
/**
|
|
324
379
|
* @template T
|
|
325
380
|
* @param {[RuleOffset, T][]} list
|
|
326
381
|
* @returns {[RuleOffset, T][]}
|
|
327
382
|
*/
|
|
328
383
|
sort(list) {
|
|
384
|
+
// Sort arbitrary variants so they're in alphabetical order
|
|
329
385
|
list = this.remapArbitraryVariantOffsets(list)
|
|
330
386
|
|
|
387
|
+
// Sort arbitrary properties so they're in alphabetical order
|
|
388
|
+
list = this.sortArbitraryProperties(list)
|
|
389
|
+
|
|
331
390
|
return list.sort(([a], [b]) => bigSign(this.compare(a, b)))
|
|
332
391
|
}
|
|
333
392
|
}
|
|
@@ -104,8 +104,12 @@ export default function resolveDefaultsAtRules({ tailwindConfig }) {
|
|
|
104
104
|
// we consider them separately because merging the declarations into
|
|
105
105
|
// a single rule will cause browsers that do not understand the
|
|
106
106
|
// vendor prefix to throw out the whole rule
|
|
107
|
+
// Additionally if a selector contains `:has` we also consider
|
|
108
|
+
// it separately because FF only recently gained support for it
|
|
107
109
|
let selectorGroupName =
|
|
108
|
-
selector.includes(':-') || selector.includes('::-')
|
|
110
|
+
selector.includes(':-') || selector.includes('::-') || selector.includes(':has')
|
|
111
|
+
? selector
|
|
112
|
+
: '__DEFAULT__'
|
|
109
113
|
|
|
110
114
|
let selectors = selectorGroups.get(selectorGroupName) ?? new Set()
|
|
111
115
|
selectorGroups.set(selectorGroupName, selectors)
|
|
@@ -24,6 +24,8 @@ import { Offsets } from './offsets.js'
|
|
|
24
24
|
import { flagEnabled } from '../featureFlags.js'
|
|
25
25
|
import { finalizeSelector, formatVariantSelector } from '../util/formatVariantSelector'
|
|
26
26
|
|
|
27
|
+
export const INTERNAL_FEATURES = Symbol()
|
|
28
|
+
|
|
27
29
|
const VARIANT_TYPES = {
|
|
28
30
|
AddVariant: Symbol.for('ADD_VARIANT'),
|
|
29
31
|
MatchVariant: Symbol.for('MATCH_VARIANT'),
|
|
@@ -146,43 +148,45 @@ function getClasses(selector, mutate) {
|
|
|
146
148
|
return parser.transformSync(selector)
|
|
147
149
|
}
|
|
148
150
|
|
|
151
|
+
/**
|
|
152
|
+
* Ignore everything inside a :not(...). This allows you to write code like
|
|
153
|
+
* `div:not(.foo)`. If `.foo` is never found in your code, then we used to
|
|
154
|
+
* not generated it. But now we will ignore everything inside a `:not`, so
|
|
155
|
+
* that it still gets generated.
|
|
156
|
+
*
|
|
157
|
+
* @param {selectorParser.Root} selectors
|
|
158
|
+
*/
|
|
159
|
+
function ignoreNot(selectors) {
|
|
160
|
+
selectors.walkPseudos((pseudo) => {
|
|
161
|
+
if (pseudo.value === ':not') {
|
|
162
|
+
pseudo.remove()
|
|
163
|
+
}
|
|
164
|
+
})
|
|
165
|
+
}
|
|
166
|
+
|
|
149
167
|
function extractCandidates(node, state = { containsNonOnDemandable: false }, depth = 0) {
|
|
150
168
|
let classes = []
|
|
169
|
+
let selectors = []
|
|
151
170
|
|
|
152
|
-
// Handle normal rules
|
|
153
171
|
if (node.type === 'rule') {
|
|
154
|
-
//
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
//
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
if (pseudo.value === ':not') {
|
|
161
|
-
pseudo.remove()
|
|
162
|
-
}
|
|
163
|
-
})
|
|
164
|
-
}
|
|
172
|
+
// Handle normal rules
|
|
173
|
+
selectors.push(...node.selectors)
|
|
174
|
+
} else if (node.type === 'atrule') {
|
|
175
|
+
// Handle at-rules (which contains nested rules)
|
|
176
|
+
node.walkRules((rule) => selectors.push(...rule.selectors))
|
|
177
|
+
}
|
|
165
178
|
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
// At least one of the selectors contains non-"on-demandable" candidates.
|
|
169
|
-
if (classCandidates.length === 0) {
|
|
170
|
-
state.containsNonOnDemandable = true
|
|
171
|
-
}
|
|
179
|
+
for (let selector of selectors) {
|
|
180
|
+
let classCandidates = getClasses(selector, ignoreNot)
|
|
172
181
|
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
182
|
+
// At least one of the selectors contains non-"on-demandable" candidates.
|
|
183
|
+
if (classCandidates.length === 0) {
|
|
184
|
+
state.containsNonOnDemandable = true
|
|
176
185
|
}
|
|
177
|
-
}
|
|
178
186
|
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
for (let classCandidate of rule.selectors.flatMap((selector) => getClasses(selector))) {
|
|
183
|
-
classes.push(classCandidate)
|
|
184
|
-
}
|
|
185
|
-
})
|
|
187
|
+
for (let classCandidate of classCandidates) {
|
|
188
|
+
classes.push(classCandidate)
|
|
189
|
+
}
|
|
186
190
|
}
|
|
187
191
|
|
|
188
192
|
if (depth === 0) {
|
|
@@ -230,8 +234,8 @@ export function parseVariant(variant) {
|
|
|
230
234
|
return ({ format }) => format(str)
|
|
231
235
|
}
|
|
232
236
|
|
|
233
|
-
let [, name, params] = /@(
|
|
234
|
-
return ({ wrap }) => wrap(postcss.atRule({ name, params: params
|
|
237
|
+
let [, name, params] = /@(\S*)( .+|[({].*)?/g.exec(str)
|
|
238
|
+
return ({ wrap }) => wrap(postcss.atRule({ name, params: params?.trim() ?? '' }))
|
|
235
239
|
})
|
|
236
240
|
.reverse()
|
|
237
241
|
|
|
@@ -752,22 +756,46 @@ function resolvePlugins(context, root) {
|
|
|
752
756
|
// TODO: This is a workaround for backwards compatibility, since custom variants
|
|
753
757
|
// were historically sorted before screen/stackable variants.
|
|
754
758
|
let beforeVariants = [
|
|
759
|
+
variantPlugins['childVariant'],
|
|
755
760
|
variantPlugins['pseudoElementVariants'],
|
|
756
761
|
variantPlugins['pseudoClassVariants'],
|
|
762
|
+
variantPlugins['hasVariants'],
|
|
757
763
|
variantPlugins['ariaVariants'],
|
|
758
764
|
variantPlugins['dataVariants'],
|
|
759
765
|
]
|
|
760
766
|
let afterVariants = [
|
|
761
767
|
variantPlugins['supportsVariants'],
|
|
762
|
-
variantPlugins['directionVariants'],
|
|
763
768
|
variantPlugins['reducedMotionVariants'],
|
|
764
769
|
variantPlugins['prefersContrastVariants'],
|
|
765
|
-
variantPlugins['darkVariants'],
|
|
766
|
-
variantPlugins['printVariant'],
|
|
767
770
|
variantPlugins['screenVariants'],
|
|
768
771
|
variantPlugins['orientationVariants'],
|
|
772
|
+
variantPlugins['directionVariants'],
|
|
773
|
+
variantPlugins['darkVariants'],
|
|
774
|
+
variantPlugins['forcedColorsVariants'],
|
|
775
|
+
variantPlugins['printVariant'],
|
|
769
776
|
]
|
|
770
777
|
|
|
778
|
+
// This is a compatibility fix for the pre 3.4 dark mode behavior
|
|
779
|
+
// `class` retains the old behavior, but `selector` keeps the new behavior
|
|
780
|
+
let isLegacyDarkMode =
|
|
781
|
+
context.tailwindConfig.darkMode === 'class' ||
|
|
782
|
+
(Array.isArray(context.tailwindConfig.darkMode) &&
|
|
783
|
+
context.tailwindConfig.darkMode[0] === 'class')
|
|
784
|
+
|
|
785
|
+
if (isLegacyDarkMode) {
|
|
786
|
+
afterVariants = [
|
|
787
|
+
variantPlugins['supportsVariants'],
|
|
788
|
+
variantPlugins['reducedMotionVariants'],
|
|
789
|
+
variantPlugins['prefersContrastVariants'],
|
|
790
|
+
variantPlugins['darkVariants'],
|
|
791
|
+
variantPlugins['screenVariants'],
|
|
792
|
+
variantPlugins['orientationVariants'],
|
|
793
|
+
variantPlugins['directionVariants'],
|
|
794
|
+
variantPlugins['forcedColorsVariants'],
|
|
795
|
+
variantPlugins['printVariant'],
|
|
796
|
+
]
|
|
797
|
+
}
|
|
798
|
+
|
|
771
799
|
return [...corePluginList, ...beforeVariants, ...userPlugins, ...afterVariants, ...layerPlugins]
|
|
772
800
|
}
|
|
773
801
|
|
|
@@ -943,13 +971,17 @@ function registerPlugins(plugins, context) {
|
|
|
943
971
|
|
|
944
972
|
// Sort all classes in order
|
|
945
973
|
// Non-tailwind classes won't be generated and will be left as `null`
|
|
946
|
-
let rules = generateRules(new Set(sorted), context)
|
|
974
|
+
let rules = generateRules(new Set(sorted), context, true)
|
|
947
975
|
rules = context.offsets.sort(rules)
|
|
948
976
|
|
|
949
977
|
let idx = BigInt(parasiteUtilities.length)
|
|
950
978
|
|
|
951
979
|
for (const [, rule] of rules) {
|
|
952
|
-
|
|
980
|
+
let candidate = rule.raws.tailwind.candidate
|
|
981
|
+
|
|
982
|
+
// When multiple rules match a candidate
|
|
983
|
+
// always take the position of the first one
|
|
984
|
+
sortedClassNames.set(candidate, sortedClassNames.get(candidate) ?? idx++)
|
|
953
985
|
}
|
|
954
986
|
|
|
955
987
|
return classes.map((className) => {
|
|
@@ -1119,17 +1151,24 @@ function registerPlugins(plugins, context) {
|
|
|
1119
1151
|
}
|
|
1120
1152
|
|
|
1121
1153
|
let isArbitraryVariant = !(value in (options.values ?? {}))
|
|
1154
|
+
let internalFeatures = options[INTERNAL_FEATURES] ?? {}
|
|
1155
|
+
|
|
1156
|
+
let respectPrefix = (() => {
|
|
1157
|
+
if (isArbitraryVariant) return false
|
|
1158
|
+
if (internalFeatures.respectPrefix === false) return false
|
|
1159
|
+
return true
|
|
1160
|
+
})()
|
|
1122
1161
|
|
|
1123
1162
|
formatStrings = formatStrings.map((format) =>
|
|
1124
1163
|
format.map((str) => ({
|
|
1125
1164
|
format: str,
|
|
1126
|
-
|
|
1165
|
+
respectPrefix,
|
|
1127
1166
|
}))
|
|
1128
1167
|
)
|
|
1129
1168
|
|
|
1130
1169
|
manualFormatStrings = manualFormatStrings.map((format) => ({
|
|
1131
1170
|
format,
|
|
1132
|
-
|
|
1171
|
+
respectPrefix,
|
|
1133
1172
|
}))
|
|
1134
1173
|
|
|
1135
1174
|
let opts = {
|
|
@@ -63,9 +63,7 @@ function getTailwindConfig(configOrPath) {
|
|
|
63
63
|
}
|
|
64
64
|
|
|
65
65
|
// It's a plain object, not a path
|
|
66
|
-
let newConfig = resolveConfig(
|
|
67
|
-
configOrPath.config === undefined ? configOrPath : configOrPath.config
|
|
68
|
-
)
|
|
66
|
+
let newConfig = resolveConfig(configOrPath?.config ?? configOrPath ?? {})
|
|
69
67
|
|
|
70
68
|
newConfig = validateConfig(newConfig)
|
|
71
69
|
|
|
@@ -210,16 +210,16 @@ let state = {
|
|
|
210
210
|
},
|
|
211
211
|
|
|
212
212
|
getContext({ createContext, cliConfigPath, root, result, content }) {
|
|
213
|
+
env.DEBUG && console.time('Searching for config')
|
|
214
|
+
let configPath = findAtConfigPath(root, result) ?? cliConfigPath
|
|
215
|
+
env.DEBUG && console.timeEnd('Searching for config')
|
|
216
|
+
|
|
213
217
|
if (this.context) {
|
|
214
218
|
this.context.changedContent = this.changedContent.splice(0)
|
|
215
219
|
|
|
216
220
|
return this.context
|
|
217
221
|
}
|
|
218
222
|
|
|
219
|
-
env.DEBUG && console.time('Searching for config')
|
|
220
|
-
let configPath = findAtConfigPath(root, result) ?? cliConfigPath
|
|
221
|
-
env.DEBUG && console.timeEnd('Searching for config')
|
|
222
|
-
|
|
223
223
|
env.DEBUG && console.time('Loading config')
|
|
224
224
|
let config = this.loadConfig(configPath, content)
|
|
225
225
|
env.DEBUG && console.timeEnd('Loading config')
|
|
@@ -277,9 +277,9 @@ export async function createProcessor(args, cliConfigPath) {
|
|
|
277
277
|
let tailwindPlugin = () => {
|
|
278
278
|
return {
|
|
279
279
|
postcssPlugin: 'tailwindcss',
|
|
280
|
-
Once(root, { result }) {
|
|
280
|
+
async Once(root, { result }) {
|
|
281
281
|
env.DEBUG && console.time('Compiling CSS')
|
|
282
|
-
tailwind(({ createContext }) => {
|
|
282
|
+
await tailwind(({ createContext }) => {
|
|
283
283
|
console.error()
|
|
284
284
|
console.error('Rebuilding...')
|
|
285
285
|
|
package/src/plugin.js
CHANGED
|
@@ -13,7 +13,7 @@ module.exports = function tailwindcss(configOrPath) {
|
|
|
13
13
|
console.time('JIT TOTAL')
|
|
14
14
|
return root
|
|
15
15
|
},
|
|
16
|
-
function (root, result) {
|
|
16
|
+
async function (root, result) {
|
|
17
17
|
// Use the path for the `@config` directive if it exists, otherwise use the
|
|
18
18
|
// path for the file being processed
|
|
19
19
|
configOrPath = findAtConfigPath(root, result) ?? configOrPath
|
|
@@ -25,14 +25,14 @@ module.exports = function tailwindcss(configOrPath) {
|
|
|
25
25
|
|
|
26
26
|
for (const root of roots) {
|
|
27
27
|
if (root.type === 'root') {
|
|
28
|
-
processTailwindFeatures(context)(root, result)
|
|
28
|
+
await processTailwindFeatures(context)(root, result)
|
|
29
29
|
}
|
|
30
30
|
}
|
|
31
31
|
|
|
32
32
|
return
|
|
33
33
|
}
|
|
34
34
|
|
|
35
|
-
processTailwindFeatures(context)(root, result)
|
|
35
|
+
await processTailwindFeatures(context)(root, result)
|
|
36
36
|
},
|
|
37
37
|
__OXIDE__ &&
|
|
38
38
|
function lightningCssPlugin(_root, result) {
|