tailwindcss 0.0.0-insiders.bd970d0 → 0.0.0-insiders.bda8421
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/README.md +1 -2
- package/lib/cli/build/deps.js +62 -0
- package/lib/cli/build/index.js +12 -15
- package/lib/cli/build/plugin.js +107 -167
- package/lib/cli/build/utils.js +13 -13
- package/lib/cli/build/watching.js +10 -10
- package/lib/cli/help/index.js +11 -11
- package/lib/cli/index.js +60 -61
- package/lib/cli/init/index.js +16 -16
- package/lib/cli-peer-dependencies.js +36 -0
- package/lib/cli.js +1 -1
- package/lib/corePluginList.js +4 -1
- package/lib/corePlugins.js +2018 -1905
- package/lib/css/preflight.css +18 -12
- package/lib/featureFlags.js +17 -23
- package/lib/index.js +1 -1
- package/lib/lib/cacheInvalidation.js +3 -3
- package/lib/lib/collapseAdjacentRules.js +6 -6
- package/lib/lib/collapseDuplicateDeclarations.js +1 -1
- package/lib/lib/content.js +39 -32
- package/lib/lib/defaultExtractor.js +40 -17
- package/lib/lib/evaluateTailwindFunctions.js +23 -23
- package/lib/lib/expandApplyAtRules.js +37 -18
- package/lib/lib/expandTailwindAtRules.js +49 -85
- package/lib/lib/findAtConfigPath.js +5 -5
- package/lib/lib/generateRules.js +82 -55
- package/lib/lib/getModuleDependencies.js +23 -23
- package/lib/lib/load-config.js +21 -6
- package/lib/lib/normalizeTailwindDirectives.js +36 -21
- package/lib/lib/offsets.js +97 -48
- package/lib/lib/partitionApplyAtRules.js +2 -2
- package/lib/lib/regex.js +5 -5
- package/lib/lib/resolveDefaultsAtRules.js +27 -25
- package/lib/lib/setupContextUtils.js +195 -167
- package/lib/lib/setupTrackingContext.js +4 -5
- package/lib/lib/sharedState.js +10 -10
- package/lib/lib/substituteScreenAtRules.js +4 -4
- package/lib/plugin.js +8 -124
- package/lib/postcss-plugins/nesting/index.js +2 -2
- package/lib/postcss-plugins/nesting/plugin.js +11 -11
- package/lib/processTailwindFeatures.js +3 -5
- package/lib/public/colors.js +264 -264
- package/lib/util/applyImportantSelector.js +2 -2
- package/lib/util/buildMediaQuery.js +2 -2
- package/lib/util/cloneDeep.js +1 -1
- package/lib/util/cloneNodes.js +33 -13
- package/lib/util/color.js +19 -19
- package/lib/util/createUtilityPlugin.js +3 -3
- package/lib/util/dataTypes.js +170 -113
- package/lib/util/defaults.js +4 -4
- package/lib/util/escapeClassName.js +2 -2
- package/lib/util/escapeCommas.js +1 -1
- package/lib/util/flattenColorPalette.js +2 -2
- package/lib/util/formatVariantSelector.js +24 -19
- package/lib/util/getAllConfigs.js +4 -5
- package/lib/util/isKeyframeRule.js +1 -1
- package/lib/util/isPlainObject.js +2 -2
- package/lib/util/isSyntacticallyValidPropertyValue.js +10 -10
- package/lib/util/log.js +6 -23
- package/lib/util/nameClass.js +4 -4
- package/lib/util/negateValue.js +8 -8
- package/lib/util/normalizeConfig.js +63 -94
- package/lib/util/normalizeScreens.js +18 -18
- package/lib/util/parseAnimationValue.js +38 -38
- package/lib/util/parseBoxShadowValue.js +18 -18
- package/lib/util/parseDependency.js +4 -4
- package/lib/util/parseGlob.js +4 -4
- package/lib/util/parseObjectStyles.js +1 -1
- package/lib/util/pluginUtils.js +36 -21
- package/lib/util/prefixSelector.js +4 -4
- package/lib/util/pseudoElements.js +72 -69
- package/lib/util/removeAlphaVariables.js +1 -1
- package/lib/util/resolveConfig.js +11 -15
- package/lib/util/resolveConfigPath.js +6 -6
- package/lib/util/responsive.js +1 -1
- package/lib/util/splitAtTopLevelOnly.js +3 -3
- package/lib/util/toColorValue.js +1 -1
- package/lib/util/toPath.js +2 -2
- package/lib/util/transformThemeValue.js +24 -24
- package/lib/util/validateConfig.js +9 -20
- package/lib/util/validateFormalSyntax.js +5 -5
- package/lib/util/withAlphaVariable.js +5 -5
- package/lib/value-parser/index.js +5 -5
- package/lib/value-parser/parse.js +43 -43
- package/lib/value-parser/stringify.js +11 -11
- package/lib/value-parser/unit.js +5 -5
- package/lib/value-parser/walk.js +1 -1
- package/nesting/index.d.ts +4 -0
- package/package.json +41 -36
- package/peers/index.js +96503 -0
- package/resolveConfig.d.ts +22 -3
- package/scripts/generate-types.js +1 -2
- package/scripts/release-notes.js +1 -1
- package/src/cli/build/deps.js +56 -0
- package/src/cli/build/index.js +0 -4
- package/src/cli/build/plugin.js +53 -85
- package/src/cli/build/watching.js +1 -1
- package/src/cli/index.js +0 -1
- package/src/cli-peer-dependencies.js +15 -0
- package/src/corePluginList.js +1 -1
- package/src/corePlugins.js +118 -31
- package/src/css/preflight.css +18 -12
- package/src/featureFlags.js +3 -5
- package/src/lib/content.js +39 -30
- package/src/lib/defaultExtractor.js +29 -6
- package/src/lib/expandApplyAtRules.js +24 -0
- package/src/lib/expandTailwindAtRules.js +19 -56
- package/src/lib/generateRules.js +39 -8
- package/src/lib/load-config.ts +13 -0
- package/src/lib/normalizeTailwindDirectives.js +27 -0
- package/src/lib/offsets.js +62 -3
- package/src/lib/resolveDefaultsAtRules.js +5 -1
- package/src/lib/setupContextUtils.js +79 -39
- package/src/plugin.js +0 -86
- package/src/processTailwindFeatures.js +0 -3
- package/src/util/cloneNodes.js +35 -14
- package/src/util/dataTypes.js +72 -6
- package/src/util/formatVariantSelector.js +7 -0
- package/src/util/isPlainObject.js +1 -1
- package/src/util/log.js +0 -24
- package/src/util/normalizeConfig.js +11 -38
- package/src/util/pluginUtils.js +20 -0
- package/src/util/pseudoElements.js +4 -0
- package/src/util/validateConfig.js +0 -10
- package/stubs/config.full.js +68 -8
- package/stubs/config.simple.js +1 -1
- package/stubs/postcss.config.cjs +1 -0
- package/stubs/postcss.config.js +1 -0
- package/types/config.d.ts +12 -5
- package/types/generated/corePluginList.d.ts +1 -1
- package/types/generated/default-theme.d.ts +28 -9
- package/types/index.d.ts +7 -3
- package/index.css +0 -5
- package/lib/lib/detectNesting.js +0 -45
- package/lib/lib/handleImportAtRules.js +0 -50
- package/src/lib/detectNesting.js +0 -47
- package/src/lib/handleImportAtRules.js +0 -34
|
@@ -1,12 +1,10 @@
|
|
|
1
1
|
import fs from 'fs'
|
|
2
2
|
import LRU from '@alloc/quick-lru'
|
|
3
|
-
import { parseCandidateStrings, IO, Parsing } from '@tailwindcss/oxide'
|
|
4
3
|
import * as sharedState from './sharedState'
|
|
5
4
|
import { generateRules } from './generateRules'
|
|
6
5
|
import log from '../util/log'
|
|
7
6
|
import cloneNodes from '../util/cloneNodes'
|
|
8
7
|
import { defaultExtractor } from './defaultExtractor'
|
|
9
|
-
import { flagEnabled } from '../featureFlags'
|
|
10
8
|
|
|
11
9
|
let env = sharedState.env
|
|
12
10
|
|
|
@@ -26,10 +24,7 @@ function getExtractor(context, fileExtension) {
|
|
|
26
24
|
extractors[fileExtension] ||
|
|
27
25
|
extractors.DEFAULT ||
|
|
28
26
|
builtInExtractors[fileExtension] ||
|
|
29
|
-
|
|
30
|
-
// stable identity. Marking it with `DEFAULT_EXTRACTOR` allows us to check if it is the default
|
|
31
|
-
// extractor without relying on the function identity.
|
|
32
|
-
Object.assign(builtInExtractors.DEFAULT(context), { DEFAULT_EXTRACTOR: true })
|
|
27
|
+
builtInExtractors.DEFAULT(context)
|
|
33
28
|
)
|
|
34
29
|
}
|
|
35
30
|
|
|
@@ -135,43 +130,21 @@ export default function expandTailwindAtRules(context) {
|
|
|
135
130
|
|
|
136
131
|
env.DEBUG && console.time('Reading changed files')
|
|
137
132
|
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
let regexParserContent = []
|
|
133
|
+
/** @type {[item: {file?: string, content?: string}, meta: {transformer: any, extractor: any}][]} */
|
|
134
|
+
let regexParserContent = []
|
|
141
135
|
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
rustParserContent.push(item)
|
|
148
|
-
} else {
|
|
149
|
-
regexParserContent.push([item, { transformer, extractor }])
|
|
150
|
-
}
|
|
151
|
-
}
|
|
136
|
+
for (let item of context.changedContent) {
|
|
137
|
+
let transformer = getTransformer(context.tailwindConfig, item.extension)
|
|
138
|
+
let extractor = getExtractor(context, item.extension)
|
|
139
|
+
regexParserContent.push([item, { transformer, extractor }])
|
|
140
|
+
}
|
|
152
141
|
|
|
153
|
-
|
|
154
|
-
for (let candidate of parseCandidateStrings(
|
|
155
|
-
rustParserContent,
|
|
156
|
-
IO.Parallel | Parsing.Parallel
|
|
157
|
-
)) {
|
|
158
|
-
candidates.add(candidate)
|
|
159
|
-
}
|
|
160
|
-
}
|
|
142
|
+
const BATCH_SIZE = 500
|
|
161
143
|
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
regexParserContent.map(async ([{ file, content }, { transformer, extractor }]) => {
|
|
165
|
-
content = file ? await fs.promises.readFile(file, 'utf8') : content
|
|
166
|
-
getClassCandidates(transformer(content), extractor, candidates, seen)
|
|
167
|
-
})
|
|
168
|
-
)
|
|
169
|
-
}
|
|
170
|
-
} else {
|
|
144
|
+
for (let i = 0; i < regexParserContent.length; i += BATCH_SIZE) {
|
|
145
|
+
let batch = regexParserContent.slice(i, i + BATCH_SIZE)
|
|
171
146
|
await Promise.all(
|
|
172
|
-
|
|
173
|
-
let transformer = getTransformer(context.tailwindConfig, extension)
|
|
174
|
-
let extractor = getExtractor(context, extension)
|
|
147
|
+
batch.map(async ([{ file, content }, { transformer, extractor }]) => {
|
|
175
148
|
content = file ? await fs.promises.readFile(file, 'utf8') : content
|
|
176
149
|
getClassCandidates(transformer(content), extractor, candidates, seen)
|
|
177
150
|
})
|
|
@@ -187,9 +160,6 @@ export default function expandTailwindAtRules(context) {
|
|
|
187
160
|
|
|
188
161
|
env.DEBUG && console.time('Generate rules')
|
|
189
162
|
env.DEBUG && console.time('Sorting candidates')
|
|
190
|
-
// TODO: only sort if we are not using the oxide parser (flagEnabled(context.tailwindConfig,
|
|
191
|
-
// 'oxideParser')) AND if we got all the candidates form the oxideParser alone. This will not
|
|
192
|
-
// be the case currently if you have custom transformers / extractors.
|
|
193
163
|
let sortedCandidates = new Set(
|
|
194
164
|
[...candidates].sort((a, z) => {
|
|
195
165
|
if (a === z) return 0
|
|
@@ -270,23 +240,16 @@ export default function expandTailwindAtRules(context) {
|
|
|
270
240
|
)
|
|
271
241
|
layerNodes.variants.remove()
|
|
272
242
|
} else if (variantNodes.length > 0) {
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
cloned.forEach((node) => {
|
|
278
|
-
let parentLayer = node.raws.tailwind?.parentLayer ?? null
|
|
279
|
-
|
|
280
|
-
node.walk((n) => {
|
|
281
|
-
if (!n.source) {
|
|
282
|
-
n.source = layerNodes[parentLayer].source
|
|
283
|
-
}
|
|
243
|
+
root.append(
|
|
244
|
+
cloneNodes(variantNodes, root.source, {
|
|
245
|
+
layer: 'variants',
|
|
284
246
|
})
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
root.append(cloned)
|
|
247
|
+
)
|
|
288
248
|
}
|
|
289
249
|
|
|
250
|
+
// TODO: Why is the root node having no source location for `end` possible?
|
|
251
|
+
root.source.end = root.source.end ?? root.source.start
|
|
252
|
+
|
|
290
253
|
// If we've got a utility layer and no utilities are generated there's likely something wrong
|
|
291
254
|
const hasUtilityVariants = variantNodes.some(
|
|
292
255
|
(node) => node.raws.tailwind?.parentLayer === 'utilities'
|
package/src/lib/generateRules.js
CHANGED
|
@@ -16,6 +16,7 @@ import { normalize } from '../util/dataTypes'
|
|
|
16
16
|
import { isValidVariantFormatString, parseVariant, INTERNAL_FEATURES } from './setupContextUtils'
|
|
17
17
|
import isValidArbitraryValue from '../util/isSyntacticallyValidPropertyValue'
|
|
18
18
|
import { splitAtTopLevelOnly } from '../util/splitAtTopLevelOnly.js'
|
|
19
|
+
import { flagEnabled } from '../featureFlags'
|
|
19
20
|
import { applyImportantSelector } from '../util/applyImportantSelector'
|
|
20
21
|
|
|
21
22
|
let classNameParser = selectorParser((selectors) => {
|
|
@@ -44,7 +45,7 @@ function* candidatePermutations(candidate) {
|
|
|
44
45
|
let bracketIdx = candidate.indexOf('[')
|
|
45
46
|
|
|
46
47
|
// If character before `[` isn't a dash or a slash, this isn't a dynamic class
|
|
47
|
-
//
|
|
48
|
+
// eg. string[]
|
|
48
49
|
if (candidate[bracketIdx - 1] === '-') {
|
|
49
50
|
dashIdx = bracketIdx - 1
|
|
50
51
|
} else if (candidate[bracketIdx - 1] === '/') {
|
|
@@ -118,10 +119,20 @@ function applyImportant(matches, classCandidate) {
|
|
|
118
119
|
|
|
119
120
|
let result = []
|
|
120
121
|
|
|
122
|
+
function isInKeyframes(rule) {
|
|
123
|
+
return rule.parent && rule.parent.type === 'atrule' && rule.parent.name === 'keyframes'
|
|
124
|
+
}
|
|
125
|
+
|
|
121
126
|
for (let [meta, rule] of matches) {
|
|
122
127
|
let container = postcss.root({ nodes: [rule.clone()] })
|
|
123
128
|
|
|
124
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
|
+
|
|
125
136
|
let ast = selectorParser().astSync(r.selector)
|
|
126
137
|
|
|
127
138
|
// Remove extraneous selectors that do not include the base candidate
|
|
@@ -174,6 +185,10 @@ function applyVariant(variant, matches, context) {
|
|
|
174
185
|
if (modifiers.length && !context.variantMap.has(variant)) {
|
|
175
186
|
variant = baseVariant
|
|
176
187
|
args.modifier = modifiers[0]
|
|
188
|
+
|
|
189
|
+
if (!flagEnabled(context.tailwindConfig, 'generalizedModifiers')) {
|
|
190
|
+
return []
|
|
191
|
+
}
|
|
177
192
|
}
|
|
178
193
|
}
|
|
179
194
|
|
|
@@ -461,9 +476,9 @@ function isParsableNode(node) {
|
|
|
461
476
|
}
|
|
462
477
|
|
|
463
478
|
function isParsableCssValue(property, value) {
|
|
464
|
-
// We don't want to treat [https://example.com] as a custom property
|
|
479
|
+
// We don't want to to treat [https://example.com] as a custom property
|
|
465
480
|
// Even though, according to the CSS grammar, it's a totally valid CSS declaration
|
|
466
|
-
// So we short-circuit here by checking if the custom property looks like a
|
|
481
|
+
// So we short-circuit here by checking if the custom property looks like a url
|
|
467
482
|
if (looksLikeUri(`${property}:${value}`)) {
|
|
468
483
|
return false
|
|
469
484
|
}
|
|
@@ -491,13 +506,13 @@ function extractArbitraryProperty(classCandidate, context) {
|
|
|
491
506
|
return null
|
|
492
507
|
}
|
|
493
508
|
|
|
494
|
-
let normalized = normalize(value)
|
|
509
|
+
let normalized = normalize(value, { property })
|
|
495
510
|
|
|
496
511
|
if (!isParsableCssValue(property, normalized)) {
|
|
497
512
|
return null
|
|
498
513
|
}
|
|
499
514
|
|
|
500
|
-
let sort = context.offsets.arbitraryProperty()
|
|
515
|
+
let sort = context.offsets.arbitraryProperty(classCandidate)
|
|
501
516
|
|
|
502
517
|
return [
|
|
503
518
|
[
|
|
@@ -815,10 +830,19 @@ function applyFinalFormat(match, { context, candidate }) {
|
|
|
815
830
|
}
|
|
816
831
|
|
|
817
832
|
try {
|
|
818
|
-
|
|
833
|
+
let selector = finalizeSelector(rule.selector, finalFormat, {
|
|
819
834
|
candidate,
|
|
820
835
|
context,
|
|
821
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
|
|
822
846
|
} catch {
|
|
823
847
|
// If this selector is invalid we also want to skip it
|
|
824
848
|
// But it's likely that being invalid here means there's a bug in a plugin rather than too loosely matching content
|
|
@@ -831,6 +855,11 @@ function applyFinalFormat(match, { context, candidate }) {
|
|
|
831
855
|
return null
|
|
832
856
|
}
|
|
833
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
|
+
|
|
834
863
|
match[1] = container.nodes[0]
|
|
835
864
|
|
|
836
865
|
return match
|
|
@@ -868,7 +897,7 @@ function getImportantStrategy(important) {
|
|
|
868
897
|
}
|
|
869
898
|
}
|
|
870
899
|
|
|
871
|
-
function generateRules(candidates, context) {
|
|
900
|
+
function generateRules(candidates, context, isSorting = false) {
|
|
872
901
|
let allRules = []
|
|
873
902
|
let strategy = getImportantStrategy(context.tailwindConfig.important)
|
|
874
903
|
|
|
@@ -903,7 +932,9 @@ function generateRules(candidates, context) {
|
|
|
903
932
|
rule = container.nodes[0]
|
|
904
933
|
}
|
|
905
934
|
|
|
906
|
-
|
|
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]
|
|
907
938
|
rules.add(newEntry)
|
|
908
939
|
context.ruleCache.add(newEntry)
|
|
909
940
|
allRules.push(newEntry)
|
package/src/lib/load-config.ts
CHANGED
|
@@ -4,12 +4,25 @@ 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 ??
|
|
10
18
|
(jiti = jitiFactory(__filename, {
|
|
11
19
|
interopDefault: true,
|
|
12
20
|
transform: (opts) => {
|
|
21
|
+
// Sucrase can't transform import.meta so we have to use Babel
|
|
22
|
+
if (opts.source.includes('import.meta')) {
|
|
23
|
+
return require('jiti/dist/babel.js')(opts)
|
|
24
|
+
}
|
|
25
|
+
|
|
13
26
|
return transform(opts.source, {
|
|
14
27
|
transforms: ['typescript', 'imports'],
|
|
15
28
|
})
|
|
@@ -10,6 +10,33 @@ export default function normalizeTailwindDirectives(root) {
|
|
|
10
10
|
applyDirectives.add(atRule)
|
|
11
11
|
}
|
|
12
12
|
|
|
13
|
+
if (atRule.name === 'import') {
|
|
14
|
+
if (atRule.params === '"tailwindcss/base"' || atRule.params === "'tailwindcss/base'") {
|
|
15
|
+
atRule.name = 'tailwind'
|
|
16
|
+
atRule.params = 'base'
|
|
17
|
+
} else if (
|
|
18
|
+
atRule.params === '"tailwindcss/components"' ||
|
|
19
|
+
atRule.params === "'tailwindcss/components'"
|
|
20
|
+
) {
|
|
21
|
+
atRule.name = 'tailwind'
|
|
22
|
+
atRule.params = 'components'
|
|
23
|
+
} else if (
|
|
24
|
+
atRule.params === '"tailwindcss/utilities"' ||
|
|
25
|
+
atRule.params === "'tailwindcss/utilities'"
|
|
26
|
+
) {
|
|
27
|
+
atRule.name = 'tailwind'
|
|
28
|
+
atRule.params = 'utilities'
|
|
29
|
+
} else if (
|
|
30
|
+
atRule.params === '"tailwindcss/screens"' ||
|
|
31
|
+
atRule.params === "'tailwindcss/screens'" ||
|
|
32
|
+
atRule.params === '"tailwindcss/variants"' ||
|
|
33
|
+
atRule.params === "'tailwindcss/variants'"
|
|
34
|
+
) {
|
|
35
|
+
atRule.name = 'tailwind'
|
|
36
|
+
atRule.params = 'variants'
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
|
|
13
40
|
if (atRule.name === 'tailwind') {
|
|
14
41
|
if (atRule.params === 'screens') {
|
|
15
42
|
atRule.params = 'variants'
|
package/src/lib/offsets.js
CHANGED
|
@@ -24,6 +24,8 @@ import { remapBitfield } from './remap-bitfield.js'
|
|
|
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
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
|
}
|
|
@@ -302,7 +313,7 @@ export class Offsets {
|
|
|
302
313
|
let mapping = this.recalculateVariantOffsets()
|
|
303
314
|
|
|
304
315
|
// No arbitrary variants? Nothing to do.
|
|
305
|
-
//
|
|
316
|
+
// Everyhing already in order? Nothing to do.
|
|
306
317
|
if (mapping.length === 0) {
|
|
307
318
|
return list
|
|
308
319
|
}
|
|
@@ -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
|
}
|
|
@@ -354,7 +413,7 @@ function max(nums) {
|
|
|
354
413
|
* Using `.sort()` without a custom compare function is faster
|
|
355
414
|
* But you can only use that if you're sorting an array of
|
|
356
415
|
* only strings. If you're sorting strings inside objects
|
|
357
|
-
* or arrays, you need
|
|
416
|
+
* or arrays, you need must use a custom compare function.
|
|
358
417
|
*
|
|
359
418
|
* @param {string} a
|
|
360
419
|
* @param {string} b
|
|
@@ -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)
|
|
@@ -21,6 +21,7 @@ import isSyntacticallyValidPropertyValue from '../util/isSyntacticallyValidPrope
|
|
|
21
21
|
import { generateRules, getClassNameFromSelector } from './generateRules'
|
|
22
22
|
import { hasContentChanged } from './cacheInvalidation.js'
|
|
23
23
|
import { Offsets } from './offsets.js'
|
|
24
|
+
import { flagEnabled } from '../featureFlags.js'
|
|
24
25
|
import { finalizeSelector, formatVariantSelector } from '../util/formatVariantSelector'
|
|
25
26
|
|
|
26
27
|
export const INTERNAL_FEATURES = Symbol()
|
|
@@ -147,43 +148,45 @@ function getClasses(selector, mutate) {
|
|
|
147
148
|
return parser.transformSync(selector)
|
|
148
149
|
}
|
|
149
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
|
+
|
|
150
167
|
function extractCandidates(node, state = { containsNonOnDemandable: false }, depth = 0) {
|
|
151
168
|
let classes = []
|
|
169
|
+
let selectors = []
|
|
152
170
|
|
|
153
|
-
// Handle normal rules
|
|
154
171
|
if (node.type === 'rule') {
|
|
155
|
-
//
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
//
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
if (pseudo.value === ':not') {
|
|
162
|
-
pseudo.remove()
|
|
163
|
-
}
|
|
164
|
-
})
|
|
165
|
-
}
|
|
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
|
+
}
|
|
166
178
|
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
// At least one of the selectors contains non-"on-demandable" candidates.
|
|
170
|
-
if (classCandidates.length === 0) {
|
|
171
|
-
state.containsNonOnDemandable = true
|
|
172
|
-
}
|
|
179
|
+
for (let selector of selectors) {
|
|
180
|
+
let classCandidates = getClasses(selector, ignoreNot)
|
|
173
181
|
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
182
|
+
// At least one of the selectors contains non-"on-demandable" candidates.
|
|
183
|
+
if (classCandidates.length === 0) {
|
|
184
|
+
state.containsNonOnDemandable = true
|
|
177
185
|
}
|
|
178
|
-
}
|
|
179
186
|
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
for (let classCandidate of rule.selectors.flatMap((selector) => getClasses(selector))) {
|
|
184
|
-
classes.push(classCandidate)
|
|
185
|
-
}
|
|
186
|
-
})
|
|
187
|
+
for (let classCandidate of classCandidates) {
|
|
188
|
+
classes.push(classCandidate)
|
|
189
|
+
}
|
|
187
190
|
}
|
|
188
191
|
|
|
189
192
|
if (depth === 0) {
|
|
@@ -435,8 +438,10 @@ function buildPluginApi(tailwindConfig, context, { variantList, variantMap, offs
|
|
|
435
438
|
},
|
|
436
439
|
}
|
|
437
440
|
|
|
441
|
+
let modifiersEnabled = flagEnabled(tailwindConfig, 'generalizedModifiers')
|
|
442
|
+
|
|
438
443
|
let ruleSets = []
|
|
439
|
-
.concat(rule(value, extras))
|
|
444
|
+
.concat(modifiersEnabled ? rule(value, extras) : rule(value))
|
|
440
445
|
.filter(Boolean)
|
|
441
446
|
.map((declaration) => ({
|
|
442
447
|
[nameClass(identifier, modifier)]: declaration,
|
|
@@ -513,8 +518,10 @@ function buildPluginApi(tailwindConfig, context, { variantList, variantMap, offs
|
|
|
513
518
|
},
|
|
514
519
|
}
|
|
515
520
|
|
|
521
|
+
let modifiersEnabled = flagEnabled(tailwindConfig, 'generalizedModifiers')
|
|
522
|
+
|
|
516
523
|
let ruleSets = []
|
|
517
|
-
.concat(rule(value, extras))
|
|
524
|
+
.concat(modifiersEnabled ? rule(value, extras) : rule(value))
|
|
518
525
|
.filter(Boolean)
|
|
519
526
|
.map((declaration) => ({
|
|
520
527
|
[nameClass(identifier, modifier)]: declaration,
|
|
@@ -582,13 +589,18 @@ function buildPluginApi(tailwindConfig, context, { variantList, variantMap, offs
|
|
|
582
589
|
let id = options?.id ?? ++variantIdentifier
|
|
583
590
|
let isSpecial = variant === '@'
|
|
584
591
|
|
|
592
|
+
let modifiersEnabled = flagEnabled(tailwindConfig, 'generalizedModifiers')
|
|
593
|
+
|
|
585
594
|
for (let [key, value] of Object.entries(options?.values ?? {})) {
|
|
586
595
|
if (key === 'DEFAULT') continue
|
|
587
596
|
|
|
588
597
|
api.addVariant(
|
|
589
598
|
isSpecial ? `${variant}${key}` : `${variant}-${key}`,
|
|
590
599
|
({ args, container }) => {
|
|
591
|
-
return variantFn(
|
|
600
|
+
return variantFn(
|
|
601
|
+
value,
|
|
602
|
+
modifiersEnabled ? { modifier: args?.modifier, container } : { container }
|
|
603
|
+
)
|
|
592
604
|
},
|
|
593
605
|
|
|
594
606
|
{
|
|
@@ -616,7 +628,7 @@ function buildPluginApi(tailwindConfig, context, { variantList, variantMap, offs
|
|
|
616
628
|
: // Falling back to args if it is a string, otherwise '' for older intellisense
|
|
617
629
|
// (JetBrains) plugins.
|
|
618
630
|
args?.value ?? (typeof args === 'string' ? args : ''),
|
|
619
|
-
{ modifier: args?.modifier, container }
|
|
631
|
+
modifiersEnabled ? { modifier: args?.modifier, container } : { container }
|
|
620
632
|
)
|
|
621
633
|
},
|
|
622
634
|
{
|
|
@@ -721,7 +733,7 @@ function collectLayerPlugins(root) {
|
|
|
721
733
|
}
|
|
722
734
|
|
|
723
735
|
function resolvePlugins(context, root) {
|
|
724
|
-
let corePluginList = Object.entries(corePlugins)
|
|
736
|
+
let corePluginList = Object.entries({ ...variantPlugins, ...corePlugins })
|
|
725
737
|
.map(([name, plugin]) => {
|
|
726
738
|
if (!context.tailwindConfig.corePlugins.includes(name)) {
|
|
727
739
|
return null
|
|
@@ -744,6 +756,7 @@ function resolvePlugins(context, root) {
|
|
|
744
756
|
// TODO: This is a workaround for backwards compatibility, since custom variants
|
|
745
757
|
// were historically sorted before screen/stackable variants.
|
|
746
758
|
let beforeVariants = [
|
|
759
|
+
variantPlugins['childVariant'],
|
|
747
760
|
variantPlugins['pseudoElementVariants'],
|
|
748
761
|
variantPlugins['pseudoClassVariants'],
|
|
749
762
|
variantPlugins['hasVariants'],
|
|
@@ -752,15 +765,37 @@ function resolvePlugins(context, root) {
|
|
|
752
765
|
]
|
|
753
766
|
let afterVariants = [
|
|
754
767
|
variantPlugins['supportsVariants'],
|
|
755
|
-
variantPlugins['directionVariants'],
|
|
756
768
|
variantPlugins['reducedMotionVariants'],
|
|
757
769
|
variantPlugins['prefersContrastVariants'],
|
|
758
|
-
variantPlugins['darkVariants'],
|
|
759
|
-
variantPlugins['printVariant'],
|
|
760
770
|
variantPlugins['screenVariants'],
|
|
761
771
|
variantPlugins['orientationVariants'],
|
|
772
|
+
variantPlugins['directionVariants'],
|
|
773
|
+
variantPlugins['darkVariants'],
|
|
774
|
+
variantPlugins['forcedColorsVariants'],
|
|
775
|
+
variantPlugins['printVariant'],
|
|
762
776
|
]
|
|
763
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
|
+
|
|
764
799
|
return [...corePluginList, ...beforeVariants, ...userPlugins, ...afterVariants, ...layerPlugins]
|
|
765
800
|
}
|
|
766
801
|
|
|
@@ -936,7 +971,7 @@ function registerPlugins(plugins, context) {
|
|
|
936
971
|
|
|
937
972
|
// Sort all classes in order
|
|
938
973
|
// Non-tailwind classes won't be generated and will be left as `null`
|
|
939
|
-
let rules = generateRules(new Set(sorted), context)
|
|
974
|
+
let rules = generateRules(new Set(sorted), context, true)
|
|
940
975
|
rules = context.offsets.sort(rules)
|
|
941
976
|
|
|
942
977
|
let idx = BigInt(parasiteUtilities.length)
|
|
@@ -1009,6 +1044,11 @@ function registerPlugins(plugins, context) {
|
|
|
1009
1044
|
|
|
1010
1045
|
// Generate a list of available variants with meta information of the type of variant.
|
|
1011
1046
|
context.getVariants = function getVariants() {
|
|
1047
|
+
// We use a unique, random ID for candidate names to avoid conflicts
|
|
1048
|
+
// We can't use characters like `_`, `:`, `@` or `.` because they might
|
|
1049
|
+
// be used as a separator
|
|
1050
|
+
let id = Math.random().toString(36).substring(7).toUpperCase()
|
|
1051
|
+
|
|
1012
1052
|
let result = []
|
|
1013
1053
|
for (let [name, options] of context.variantOptions.entries()) {
|
|
1014
1054
|
if (options.variantInfo === VARIANT_INFO.Base) continue
|
|
@@ -1019,7 +1059,7 @@ function registerPlugins(plugins, context) {
|
|
|
1019
1059
|
values: Object.keys(options.values ?? {}),
|
|
1020
1060
|
hasDash: name !== '@',
|
|
1021
1061
|
selectors({ modifier, value } = {}) {
|
|
1022
|
-
let candidate =
|
|
1062
|
+
let candidate = `TAILWINDPLACEHOLDER${id}`
|
|
1023
1063
|
|
|
1024
1064
|
let rule = postcss.rule({ selector: `.${candidate}` })
|
|
1025
1065
|
let container = postcss.root({ nodes: [rule.clone()] })
|