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.
- package/CHANGELOG.md +246 -6
- package/README.md +13 -9
- package/defaultTheme.d.ts +2 -1
- package/lib/cli/build/deps.js +54 -0
- package/lib/cli/build/index.js +48 -0
- package/lib/cli/build/plugin.js +362 -0
- package/lib/cli/build/utils.js +78 -0
- package/lib/cli/build/watching.js +178 -0
- package/lib/cli/help/index.js +71 -0
- package/lib/cli/index.js +18 -0
- package/lib/cli/init/index.js +46 -0
- package/lib/cli/shared.js +12 -0
- package/lib/cli-peer-dependencies.js +18 -5
- package/lib/cli.js +62 -592
- package/lib/constants.js +19 -12
- package/lib/corePluginList.js +5 -3
- package/lib/corePlugins.js +619 -347
- package/lib/css/preflight.css +7 -0
- package/lib/featureFlags.js +34 -22
- package/lib/index.js +16 -9
- package/lib/lib/cacheInvalidation.js +42 -21
- package/lib/lib/collapseAdjacentRules.js +16 -14
- package/lib/lib/collapseDuplicateDeclarations.js +13 -10
- package/lib/lib/content.js +177 -0
- package/lib/lib/defaultExtractor.js +230 -33
- package/lib/lib/detectNesting.js +13 -4
- package/lib/lib/evaluateTailwindFunctions.js +105 -42
- package/lib/lib/expandApplyAtRules.js +151 -92
- package/lib/lib/expandTailwindAtRules.js +134 -149
- package/lib/lib/findAtConfigPath.js +44 -0
- package/lib/lib/generateRules.js +404 -185
- package/lib/lib/getModuleDependencies.js +21 -18
- package/lib/lib/normalizeTailwindDirectives.js +11 -8
- package/lib/lib/offsets.js +285 -0
- package/lib/lib/partitionApplyAtRules.js +14 -11
- package/lib/lib/regex.js +60 -0
- package/lib/lib/remap-bitfield.js +87 -0
- package/lib/lib/resolveDefaultsAtRules.js +97 -79
- package/lib/lib/setupContextUtils.js +567 -242
- package/lib/lib/setupTrackingContext.js +77 -106
- package/lib/lib/sharedState.js +18 -10
- package/lib/lib/substituteScreenAtRules.js +9 -7
- package/lib/postcss-plugins/nesting/index.js +7 -5
- package/lib/postcss-plugins/nesting/plugin.js +10 -8
- package/lib/processTailwindFeatures.js +34 -31
- package/lib/public/colors.js +7 -5
- package/lib/public/create-plugin.js +6 -4
- package/lib/public/default-config.js +7 -5
- package/lib/public/default-theme.js +7 -5
- package/lib/public/resolve-config.js +13 -10
- package/lib/util/bigSign.js +4 -1
- package/lib/util/buildMediaQuery.js +10 -5
- package/lib/util/cloneDeep.js +6 -5
- package/lib/util/cloneNodes.js +9 -2
- package/lib/util/color.js +20 -17
- package/lib/util/configurePlugins.js +5 -2
- package/lib/util/createPlugin.js +6 -5
- package/lib/util/createUtilityPlugin.js +15 -13
- package/lib/util/dataTypes.js +75 -51
- package/lib/util/defaults.js +8 -5
- package/lib/util/escapeClassName.js +13 -10
- package/lib/util/escapeCommas.js +4 -1
- package/lib/util/flattenColorPalette.js +7 -8
- package/lib/util/formatVariantSelector.js +270 -103
- package/lib/util/getAllConfigs.js +34 -15
- package/lib/util/hashConfig.js +10 -7
- package/lib/util/isKeyframeRule.js +4 -1
- package/lib/util/isPlainObject.js +4 -1
- package/lib/util/{isValidArbitraryValue.js → isSyntacticallyValidPropertyValue.js} +34 -26
- package/lib/util/log.js +14 -8
- package/lib/util/nameClass.js +19 -8
- package/lib/util/negateValue.js +20 -5
- package/lib/util/normalizeConfig.js +94 -53
- package/lib/util/normalizeScreens.js +115 -4
- package/lib/util/parseAnimationValue.js +43 -41
- package/lib/util/parseBoxShadowValue.js +12 -45
- package/lib/util/parseDependency.js +37 -55
- package/lib/util/parseGlob.js +34 -0
- package/lib/util/parseObjectStyles.js +15 -12
- package/lib/util/pluginUtils.js +146 -42
- package/lib/util/prefixSelector.js +26 -9
- package/lib/util/removeAlphaVariables.js +29 -0
- package/lib/util/resolveConfig.js +104 -105
- package/lib/util/resolveConfigPath.js +20 -17
- package/lib/util/responsive.js +12 -9
- package/lib/util/splitAtTopLevelOnly.js +43 -0
- package/lib/util/tap.js +4 -1
- package/lib/util/toColorValue.js +4 -1
- package/lib/util/toPath.js +18 -2
- package/lib/util/transformThemeValue.js +23 -11
- package/lib/util/validateConfig.js +24 -0
- package/lib/util/validateFormalSyntax.js +24 -0
- package/lib/util/withAlphaVariable.js +32 -24
- package/package.json +32 -29
- package/peers/index.js +6693 -3036
- package/plugin.d.ts +10 -5
- package/resolveConfig.d.ts +12 -0
- package/scripts/generate-types.js +53 -0
- package/scripts/release-channel.js +18 -0
- package/scripts/release-notes.js +21 -0
- package/scripts/type-utils.js +27 -0
- package/src/cli/build/deps.js +56 -0
- package/src/cli/build/index.js +49 -0
- package/src/cli/build/plugin.js +434 -0
- package/src/cli/build/utils.js +76 -0
- package/src/cli/build/watching.js +227 -0
- package/src/cli/help/index.js +70 -0
- package/src/cli/index.js +3 -0
- package/src/cli/init/index.js +50 -0
- package/src/cli/shared.js +5 -0
- package/src/cli-peer-dependencies.js +7 -1
- package/src/cli.js +46 -659
- package/src/corePlugins.js +346 -79
- package/src/css/preflight.css +7 -0
- package/src/featureFlags.js +12 -2
- package/src/index.js +5 -0
- package/src/lib/content.js +213 -0
- package/src/lib/defaultExtractor.js +202 -35
- package/src/lib/detectNesting.js +9 -1
- package/src/lib/evaluateTailwindFunctions.js +78 -7
- package/src/lib/expandApplyAtRules.js +129 -34
- package/src/lib/expandTailwindAtRules.js +12 -50
- package/src/lib/findAtConfigPath.js +48 -0
- package/src/lib/generateRules.js +379 -126
- package/src/lib/offsets.js +351 -0
- package/src/lib/regex.js +74 -0
- package/src/lib/remap-bitfield.js +82 -0
- package/src/lib/resolveDefaultsAtRules.js +53 -36
- package/src/lib/setupContextUtils.js +510 -120
- package/src/lib/setupTrackingContext.js +8 -45
- package/src/lib/sharedState.js +2 -0
- package/src/util/buildMediaQuery.js +5 -3
- package/src/util/cloneNodes.js +5 -1
- package/src/util/dataTypes.js +51 -26
- package/src/util/formatVariantSelector.js +317 -101
- package/src/util/getAllConfigs.js +19 -0
- package/src/util/{isValidArbitraryValue.js → isSyntacticallyValidPropertyValue.js} +1 -1
- package/src/util/log.js +1 -1
- package/src/util/nameClass.js +4 -0
- package/src/util/negateValue.js +10 -2
- package/src/util/normalizeConfig.js +40 -10
- package/src/util/normalizeScreens.js +99 -4
- package/src/util/parseBoxShadowValue.js +3 -50
- package/src/util/parseDependency.js +37 -42
- package/src/util/parseGlob.js +24 -0
- package/src/util/pluginUtils.js +132 -10
- package/src/util/prefixSelector.js +28 -10
- package/src/util/removeAlphaVariables.js +24 -0
- package/src/util/resolveConfig.js +70 -62
- package/src/util/splitAtTopLevelOnly.js +45 -0
- package/src/util/toPath.js +1 -1
- package/src/util/transformThemeValue.js +13 -3
- package/src/util/validateConfig.js +13 -0
- package/src/util/validateFormalSyntax.js +34 -0
- package/src/util/withAlphaVariable.js +1 -1
- package/stubs/defaultConfig.stub.js +165 -164
- package/stubs/simpleConfig.stub.js +1 -0
- package/types/config.d.ts +77 -37
- package/types/generated/default-theme.d.ts +342 -0
- package/types/index.d.ts +7 -1
- package/types.d.ts +0 -1
package/src/lib/generateRules.js
CHANGED
|
@@ -3,19 +3,22 @@ import selectorParser from 'postcss-selector-parser'
|
|
|
3
3
|
import parseObjectStyles from '../util/parseObjectStyles'
|
|
4
4
|
import isPlainObject from '../util/isPlainObject'
|
|
5
5
|
import prefixSelector from '../util/prefixSelector'
|
|
6
|
-
import { updateAllClasses } from '../util/pluginUtils'
|
|
6
|
+
import { updateAllClasses, filterSelectorsForClass, getMatchingTypes } from '../util/pluginUtils'
|
|
7
7
|
import log from '../util/log'
|
|
8
8
|
import * as sharedState from './sharedState'
|
|
9
9
|
import { formatVariantSelector, finalizeSelector } from '../util/formatVariantSelector'
|
|
10
10
|
import { asClass } from '../util/nameClass'
|
|
11
11
|
import { normalize } from '../util/dataTypes'
|
|
12
|
-
import
|
|
12
|
+
import { isValidVariantFormatString, parseVariant } from './setupContextUtils'
|
|
13
|
+
import isValidArbitraryValue from '../util/isSyntacticallyValidPropertyValue'
|
|
14
|
+
import { splitAtTopLevelOnly } from '../util/splitAtTopLevelOnly.js'
|
|
15
|
+
import { flagEnabled } from '../featureFlags'
|
|
13
16
|
|
|
14
17
|
let classNameParser = selectorParser((selectors) => {
|
|
15
18
|
return selectors.first.filter(({ type }) => type === 'class').pop().value
|
|
16
19
|
})
|
|
17
20
|
|
|
18
|
-
function getClassNameFromSelector(selector) {
|
|
21
|
+
export function getClassNameFromSelector(selector) {
|
|
19
22
|
return classNameParser.transformSync(selector)
|
|
20
23
|
}
|
|
21
24
|
|
|
@@ -31,13 +34,24 @@ function* candidatePermutations(candidate) {
|
|
|
31
34
|
|
|
32
35
|
while (lastIndex >= 0) {
|
|
33
36
|
let dashIdx
|
|
37
|
+
let wasSlash = false
|
|
34
38
|
|
|
35
39
|
if (lastIndex === Infinity && candidate.endsWith(']')) {
|
|
36
40
|
let bracketIdx = candidate.indexOf('[')
|
|
37
41
|
|
|
38
42
|
// If character before `[` isn't a dash or a slash, this isn't a dynamic class
|
|
39
43
|
// eg. string[]
|
|
40
|
-
|
|
44
|
+
if (candidate[bracketIdx - 1] === '-') {
|
|
45
|
+
dashIdx = bracketIdx - 1
|
|
46
|
+
} else if (candidate[bracketIdx - 1] === '/') {
|
|
47
|
+
dashIdx = bracketIdx - 1
|
|
48
|
+
wasSlash = true
|
|
49
|
+
} else {
|
|
50
|
+
dashIdx = -1
|
|
51
|
+
}
|
|
52
|
+
} else if (lastIndex === Infinity && candidate.includes('/')) {
|
|
53
|
+
dashIdx = candidate.lastIndexOf('/')
|
|
54
|
+
wasSlash = true
|
|
41
55
|
} else {
|
|
42
56
|
dashIdx = candidate.lastIndexOf('-', lastIndex)
|
|
43
57
|
}
|
|
@@ -47,11 +61,16 @@ function* candidatePermutations(candidate) {
|
|
|
47
61
|
}
|
|
48
62
|
|
|
49
63
|
let prefix = candidate.slice(0, dashIdx)
|
|
50
|
-
let modifier = candidate.slice(dashIdx + 1)
|
|
51
|
-
|
|
52
|
-
yield [prefix, modifier]
|
|
64
|
+
let modifier = candidate.slice(wasSlash ? dashIdx : dashIdx + 1)
|
|
53
65
|
|
|
54
66
|
lastIndex = dashIdx - 1
|
|
67
|
+
|
|
68
|
+
// TODO: This feels a bit hacky
|
|
69
|
+
if (prefix === '' || modifier === '/') {
|
|
70
|
+
continue
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
yield [prefix, modifier]
|
|
55
74
|
}
|
|
56
75
|
}
|
|
57
76
|
|
|
@@ -97,12 +116,15 @@ function applyImportant(matches, classCandidate) {
|
|
|
97
116
|
for (let [meta, rule] of matches) {
|
|
98
117
|
let container = postcss.root({ nodes: [rule.clone()] })
|
|
99
118
|
container.walkRules((r) => {
|
|
100
|
-
r.selector = updateAllClasses(
|
|
101
|
-
|
|
102
|
-
|
|
119
|
+
r.selector = updateAllClasses(
|
|
120
|
+
filterSelectorsForClass(r.selector, classCandidate),
|
|
121
|
+
(className) => {
|
|
122
|
+
if (className === classCandidate) {
|
|
123
|
+
return `!${className}`
|
|
124
|
+
}
|
|
125
|
+
return className
|
|
103
126
|
}
|
|
104
|
-
|
|
105
|
-
})
|
|
127
|
+
)
|
|
106
128
|
r.walkDecls((d) => (d.important = true))
|
|
107
129
|
})
|
|
108
130
|
result.push([{ ...meta, important: true }, container.nodes[0]])
|
|
@@ -125,8 +147,62 @@ function applyVariant(variant, matches, context) {
|
|
|
125
147
|
return matches
|
|
126
148
|
}
|
|
127
149
|
|
|
150
|
+
/** @type {{modifier: string | null, value: string | null}} */
|
|
151
|
+
let args = { modifier: null, value: sharedState.NONE }
|
|
152
|
+
|
|
153
|
+
// Retrieve "modifier"
|
|
154
|
+
{
|
|
155
|
+
let match = /(.*)\/(.*)$/g.exec(variant)
|
|
156
|
+
if (match) {
|
|
157
|
+
variant = match[1]
|
|
158
|
+
args.modifier = match[2]
|
|
159
|
+
|
|
160
|
+
if (!flagEnabled(context.tailwindConfig, 'generalizedModifiers')) {
|
|
161
|
+
return []
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
// Retrieve "arbitrary value"
|
|
167
|
+
if (variant.endsWith(']') && !variant.startsWith('[')) {
|
|
168
|
+
// We either have:
|
|
169
|
+
// @[200px]
|
|
170
|
+
// group-[:hover]
|
|
171
|
+
//
|
|
172
|
+
// But we don't want:
|
|
173
|
+
// @-[200px] (`-` is incorrect)
|
|
174
|
+
// group[:hover] (`-` is missing)
|
|
175
|
+
let match = /(.)(-?)\[(.*)\]/g.exec(variant)
|
|
176
|
+
if (match) {
|
|
177
|
+
let [, char, seperator, value] = match
|
|
178
|
+
// @-[200px] case
|
|
179
|
+
if (char === '@' && seperator === '-') return []
|
|
180
|
+
// group[:hover] case
|
|
181
|
+
if (char !== '@' && seperator === '') return []
|
|
182
|
+
|
|
183
|
+
variant = variant.replace(`${seperator}[${value}]`, '')
|
|
184
|
+
args.value = value
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
// Register arbitrary variants
|
|
189
|
+
if (isArbitraryValue(variant) && !context.variantMap.has(variant)) {
|
|
190
|
+
let selector = normalize(variant.slice(1, -1))
|
|
191
|
+
|
|
192
|
+
if (!isValidVariantFormatString(selector)) {
|
|
193
|
+
return []
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
let fn = parseVariant(selector)
|
|
197
|
+
|
|
198
|
+
let sort = context.offsets.recordVariant(variant)
|
|
199
|
+
|
|
200
|
+
context.variantMap.set(variant, [[sort, fn]])
|
|
201
|
+
}
|
|
202
|
+
|
|
128
203
|
if (context.variantMap.has(variant)) {
|
|
129
|
-
let
|
|
204
|
+
let isArbitraryVariant = isArbitraryValue(variant)
|
|
205
|
+
let variantFunctionTuples = context.variantMap.get(variant).slice()
|
|
130
206
|
let result = []
|
|
131
207
|
|
|
132
208
|
for (let [meta, rule] of matches) {
|
|
@@ -137,15 +213,17 @@ function applyVariant(variant, matches, context) {
|
|
|
137
213
|
|
|
138
214
|
let container = postcss.root({ nodes: [rule.clone()] })
|
|
139
215
|
|
|
140
|
-
for (let [variantSort, variantFunction] of variantFunctionTuples) {
|
|
141
|
-
let clone = container.clone()
|
|
216
|
+
for (let [variantSort, variantFunction, containerFromArray] of variantFunctionTuples) {
|
|
217
|
+
let clone = (containerFromArray ?? container).clone()
|
|
142
218
|
let collectedFormats = []
|
|
143
219
|
|
|
144
|
-
let originals = new Map()
|
|
145
|
-
|
|
146
220
|
function prepareBackup() {
|
|
147
|
-
|
|
148
|
-
clone.
|
|
221
|
+
// Already prepared, chicken out
|
|
222
|
+
if (clone.raws.neededBackup) {
|
|
223
|
+
return
|
|
224
|
+
}
|
|
225
|
+
clone.raws.neededBackup = true
|
|
226
|
+
clone.walkRules((rule) => (rule.raws.originalSelector = rule.selector))
|
|
149
227
|
}
|
|
150
228
|
|
|
151
229
|
function modifySelectors(modifierFunction) {
|
|
@@ -185,25 +263,54 @@ function applyVariant(variant, matches, context) {
|
|
|
185
263
|
clone.append(wrapper)
|
|
186
264
|
},
|
|
187
265
|
format(selectorFormat) {
|
|
188
|
-
collectedFormats.push(
|
|
266
|
+
collectedFormats.push({
|
|
267
|
+
format: selectorFormat,
|
|
268
|
+
isArbitraryVariant,
|
|
269
|
+
})
|
|
189
270
|
},
|
|
271
|
+
args,
|
|
190
272
|
})
|
|
191
273
|
|
|
274
|
+
// It can happen that a list of format strings is returned from within the function. In that
|
|
275
|
+
// case, we have to process them as well. We can use the existing `variantSort`.
|
|
276
|
+
if (Array.isArray(ruleWithVariant)) {
|
|
277
|
+
for (let [idx, variantFunction] of ruleWithVariant.entries()) {
|
|
278
|
+
// This is a little bit scary since we are pushing to an array of items that we are
|
|
279
|
+
// currently looping over. However, you can also think of it like a processing queue
|
|
280
|
+
// where you keep handling jobs until everything is done and each job can queue more
|
|
281
|
+
// jobs if needed.
|
|
282
|
+
variantFunctionTuples.push([
|
|
283
|
+
context.offsets.applyParallelOffset(variantSort, idx),
|
|
284
|
+
variantFunction,
|
|
285
|
+
|
|
286
|
+
// If the clone has been modified we have to pass that back
|
|
287
|
+
// though so each rule can use the modified container
|
|
288
|
+
clone.clone(),
|
|
289
|
+
])
|
|
290
|
+
}
|
|
291
|
+
continue
|
|
292
|
+
}
|
|
293
|
+
|
|
192
294
|
if (typeof ruleWithVariant === 'string') {
|
|
193
|
-
collectedFormats.push(
|
|
295
|
+
collectedFormats.push({
|
|
296
|
+
format: ruleWithVariant,
|
|
297
|
+
isArbitraryVariant,
|
|
298
|
+
})
|
|
194
299
|
}
|
|
195
300
|
|
|
196
301
|
if (ruleWithVariant === null) {
|
|
197
302
|
continue
|
|
198
303
|
}
|
|
199
304
|
|
|
200
|
-
// We
|
|
305
|
+
// We had to backup selectors, therefore we assume that somebody touched
|
|
201
306
|
// `container` or `modifySelectors`. Let's see if they did, so that we
|
|
202
307
|
// can restore the selectors, and collect the format strings.
|
|
203
|
-
if (
|
|
308
|
+
if (clone.raws.neededBackup) {
|
|
309
|
+
delete clone.raws.neededBackup
|
|
204
310
|
clone.walkRules((rule) => {
|
|
205
|
-
|
|
206
|
-
|
|
311
|
+
let before = rule.raws.originalSelector
|
|
312
|
+
if (!before) return
|
|
313
|
+
delete rule.raws.originalSelector
|
|
207
314
|
if (before === rule.selector) return // No mutation happened
|
|
208
315
|
|
|
209
316
|
let modified = rule.selector
|
|
@@ -229,7 +336,10 @@ function applyVariant(variant, matches, context) {
|
|
|
229
336
|
// modified (by plugin): .foo .foo\\:markdown > p
|
|
230
337
|
// rebuiltBase (internal): .foo\\:markdown > p
|
|
231
338
|
// format: .foo &
|
|
232
|
-
collectedFormats.push(
|
|
339
|
+
collectedFormats.push({
|
|
340
|
+
format: modified.replace(rebuiltBase, '&'),
|
|
341
|
+
isArbitraryVariant,
|
|
342
|
+
})
|
|
233
343
|
rule.selector = before
|
|
234
344
|
})
|
|
235
345
|
}
|
|
@@ -243,7 +353,11 @@ function applyVariant(variant, matches, context) {
|
|
|
243
353
|
let withOffset = [
|
|
244
354
|
{
|
|
245
355
|
...meta,
|
|
246
|
-
sort:
|
|
356
|
+
sort: context.offsets.applyVariantOffset(
|
|
357
|
+
meta.sort,
|
|
358
|
+
variantSort,
|
|
359
|
+
Object.assign(args, context.variantOptions.get(variant))
|
|
360
|
+
),
|
|
247
361
|
collectedFormats: (meta.collectedFormats ?? []).concat(collectedFormats),
|
|
248
362
|
},
|
|
249
363
|
clone.nodes[0],
|
|
@@ -307,7 +421,7 @@ function isParsableNode(node) {
|
|
|
307
421
|
let isParsable = true
|
|
308
422
|
|
|
309
423
|
node.walkDecls((decl) => {
|
|
310
|
-
if (!isParsableCssValue(decl.
|
|
424
|
+
if (!isParsableCssValue(decl.prop, decl.value)) {
|
|
311
425
|
isParsable = false
|
|
312
426
|
return false
|
|
313
427
|
}
|
|
@@ -353,9 +467,11 @@ function extractArbitraryProperty(classCandidate, context) {
|
|
|
353
467
|
return null
|
|
354
468
|
}
|
|
355
469
|
|
|
470
|
+
let sort = context.offsets.arbitraryProperty()
|
|
471
|
+
|
|
356
472
|
return [
|
|
357
473
|
[
|
|
358
|
-
{ sort
|
|
474
|
+
{ sort, layer: 'utilities' },
|
|
359
475
|
() => ({
|
|
360
476
|
[asClass(classCandidate)]: {
|
|
361
477
|
[property]: normalized,
|
|
@@ -407,18 +523,22 @@ function splitWithSeparator(input, separator) {
|
|
|
407
523
|
return [sharedState.NOT_ON_DEMAND]
|
|
408
524
|
}
|
|
409
525
|
|
|
410
|
-
return input
|
|
526
|
+
return splitAtTopLevelOnly(input, separator)
|
|
411
527
|
}
|
|
412
528
|
|
|
413
529
|
function* recordCandidates(matches, classCandidate) {
|
|
414
530
|
for (const match of matches) {
|
|
415
|
-
match[1].raws.tailwind = {
|
|
531
|
+
match[1].raws.tailwind = {
|
|
532
|
+
...match[1].raws.tailwind,
|
|
533
|
+
classCandidate,
|
|
534
|
+
preserveSource: match[0].options?.preserveSource ?? false,
|
|
535
|
+
}
|
|
416
536
|
|
|
417
537
|
yield match
|
|
418
538
|
}
|
|
419
539
|
}
|
|
420
540
|
|
|
421
|
-
function* resolveMatches(candidate, context) {
|
|
541
|
+
function* resolveMatches(candidate, context, original = candidate) {
|
|
422
542
|
let separator = context.tailwindConfig.separator
|
|
423
543
|
let [classCandidate, ...variants] = splitWithSeparator(candidate, separator).reverse()
|
|
424
544
|
let important = false
|
|
@@ -428,6 +548,15 @@ function* resolveMatches(candidate, context) {
|
|
|
428
548
|
classCandidate = classCandidate.slice(1)
|
|
429
549
|
}
|
|
430
550
|
|
|
551
|
+
if (flagEnabled(context.tailwindConfig, 'variantGrouping')) {
|
|
552
|
+
if (classCandidate.startsWith('(') && classCandidate.endsWith(')')) {
|
|
553
|
+
let base = variants.slice().reverse().join(separator)
|
|
554
|
+
for (let part of splitAtTopLevelOnly(classCandidate.slice(1, -1), ',')) {
|
|
555
|
+
yield* resolveMatches(base + separator + part, context, original)
|
|
556
|
+
}
|
|
557
|
+
}
|
|
558
|
+
}
|
|
559
|
+
|
|
431
560
|
// TODO: Reintroduce this in ways that doesn't break on false positives
|
|
432
561
|
// function sortAgainst(toSort, against) {
|
|
433
562
|
// return toSort.slice().sort((a, z) => {
|
|
@@ -468,68 +597,133 @@ function* resolveMatches(candidate, context) {
|
|
|
468
597
|
}
|
|
469
598
|
|
|
470
599
|
if (matchesPerPlugin.length > 0) {
|
|
471
|
-
|
|
600
|
+
let matchingTypes = Array.from(
|
|
601
|
+
getMatchingTypes(
|
|
602
|
+
sort.options?.types ?? [],
|
|
603
|
+
modifier,
|
|
604
|
+
sort.options ?? {},
|
|
605
|
+
context.tailwindConfig
|
|
606
|
+
)
|
|
607
|
+
).map(([_, type]) => type)
|
|
608
|
+
|
|
609
|
+
if (matchingTypes.length > 0) {
|
|
610
|
+
typesByMatches.set(matchesPerPlugin, matchingTypes)
|
|
611
|
+
}
|
|
612
|
+
|
|
472
613
|
matches.push(matchesPerPlugin)
|
|
473
614
|
}
|
|
474
615
|
}
|
|
475
616
|
|
|
476
617
|
if (isArbitraryValue(modifier)) {
|
|
477
|
-
// When generated arbitrary values are ambiguous, we can't know
|
|
478
|
-
// which to pick so don't generate any utilities for them
|
|
479
618
|
if (matches.length > 1) {
|
|
480
|
-
|
|
619
|
+
// Partition plugins in 2 categories so that we can start searching in the plugins that
|
|
620
|
+
// don't have `any` as a type first.
|
|
621
|
+
let [withAny, withoutAny] = matches.reduce(
|
|
622
|
+
(group, plugin) => {
|
|
623
|
+
let hasAnyType = plugin.some(([{ options }]) =>
|
|
624
|
+
options.types.some(({ type }) => type === 'any')
|
|
625
|
+
)
|
|
481
626
|
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
627
|
+
if (hasAnyType) {
|
|
628
|
+
group[0].push(plugin)
|
|
629
|
+
} else {
|
|
630
|
+
group[1].push(plugin)
|
|
631
|
+
}
|
|
632
|
+
return group
|
|
633
|
+
},
|
|
634
|
+
[[], []]
|
|
635
|
+
)
|
|
486
636
|
|
|
487
|
-
|
|
488
|
-
|
|
637
|
+
function findFallback(matches) {
|
|
638
|
+
// If only a single plugin matches, let's take that one
|
|
639
|
+
if (matches.length === 1) {
|
|
640
|
+
return matches[0]
|
|
641
|
+
}
|
|
489
642
|
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
643
|
+
// Otherwise, find the plugin that creates a valid rule given the arbitrary value, and
|
|
644
|
+
// also has the correct type which preferOnConflicts the plugin in case of clashes.
|
|
645
|
+
return matches.find((rules) => {
|
|
646
|
+
let matchingTypes = typesByMatches.get(rules)
|
|
647
|
+
return rules.some(([{ options }, rule]) => {
|
|
648
|
+
if (!isParsableNode(rule)) {
|
|
649
|
+
return false
|
|
493
650
|
}
|
|
494
|
-
}
|
|
495
651
|
|
|
496
|
-
|
|
497
|
-
|
|
652
|
+
return options.types.some(
|
|
653
|
+
({ type, preferOnConflict }) => matchingTypes.includes(type) && preferOnConflict
|
|
654
|
+
)
|
|
655
|
+
})
|
|
656
|
+
})
|
|
498
657
|
}
|
|
499
658
|
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
.flat()
|
|
507
|
-
.map((rule) =>
|
|
508
|
-
rule
|
|
509
|
-
.toString()
|
|
510
|
-
.split('\n')
|
|
511
|
-
.slice(1, -1) // Remove selector and closing '}'
|
|
512
|
-
.map((line) => line.trim())
|
|
513
|
-
.map((x) => ` ${x}`) // Re-indent
|
|
514
|
-
.join('\n')
|
|
515
|
-
)
|
|
516
|
-
.join('\n\n')
|
|
659
|
+
// Try to find a fallback plugin, because we already know that multiple plugins matched for
|
|
660
|
+
// the given arbitrary value.
|
|
661
|
+
let fallback = findFallback(withoutAny) ?? findFallback(withAny)
|
|
662
|
+
if (fallback) {
|
|
663
|
+
matches = [fallback]
|
|
664
|
+
}
|
|
517
665
|
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
666
|
+
// We couldn't find a fallback plugin which means that there are now multiple plugins that
|
|
667
|
+
// generated css for the current candidate. This means that the result is ambiguous and this
|
|
668
|
+
// should not happen. We won't generate anything right now, so let's report this to the user
|
|
669
|
+
// by logging some options about what they can do.
|
|
670
|
+
else {
|
|
671
|
+
let typesPerPlugin = matches.map(
|
|
672
|
+
(match) => new Set([...(typesByMatches.get(match) ?? [])])
|
|
673
|
+
)
|
|
674
|
+
|
|
675
|
+
// Remove duplicates, so that we can detect proper unique types for each plugin.
|
|
676
|
+
for (let pluginTypes of typesPerPlugin) {
|
|
677
|
+
for (let type of pluginTypes) {
|
|
678
|
+
let removeFromOwnGroup = false
|
|
679
|
+
|
|
680
|
+
for (let otherGroup of typesPerPlugin) {
|
|
681
|
+
if (pluginTypes === otherGroup) continue
|
|
682
|
+
|
|
683
|
+
if (otherGroup.has(type)) {
|
|
684
|
+
otherGroup.delete(type)
|
|
685
|
+
removeFromOwnGroup = true
|
|
686
|
+
}
|
|
687
|
+
}
|
|
688
|
+
|
|
689
|
+
if (removeFromOwnGroup) pluginTypes.delete(type)
|
|
690
|
+
}
|
|
522
691
|
}
|
|
523
|
-
}
|
|
524
692
|
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
693
|
+
let messages = []
|
|
694
|
+
|
|
695
|
+
for (let [idx, group] of typesPerPlugin.entries()) {
|
|
696
|
+
for (let type of group) {
|
|
697
|
+
let rules = matches[idx]
|
|
698
|
+
.map(([, rule]) => rule)
|
|
699
|
+
.flat()
|
|
700
|
+
.map((rule) =>
|
|
701
|
+
rule
|
|
702
|
+
.toString()
|
|
703
|
+
.split('\n')
|
|
704
|
+
.slice(1, -1) // Remove selector and closing '}'
|
|
705
|
+
.map((line) => line.trim())
|
|
706
|
+
.map((x) => ` ${x}`) // Re-indent
|
|
707
|
+
.join('\n')
|
|
708
|
+
)
|
|
709
|
+
.join('\n\n')
|
|
710
|
+
|
|
711
|
+
messages.push(
|
|
712
|
+
` Use \`${candidate.replace('[', `[${type}:`)}\` for \`${rules.trim()}\``
|
|
713
|
+
)
|
|
714
|
+
break
|
|
715
|
+
}
|
|
716
|
+
}
|
|
717
|
+
|
|
718
|
+
log.warn([
|
|
719
|
+
`The class \`${candidate}\` is ambiguous and matches multiple utilities.`,
|
|
720
|
+
...messages,
|
|
721
|
+
`If this is content and not a class, replace it with \`${candidate
|
|
722
|
+
.replace('[', '[')
|
|
723
|
+
.replace(']', ']')}\` to silence this warning.`,
|
|
724
|
+
])
|
|
725
|
+
continue
|
|
726
|
+
}
|
|
533
727
|
}
|
|
534
728
|
|
|
535
729
|
matches = matches.map((list) => list.filter((match) => isParsableNode(match[1])))
|
|
@@ -551,19 +745,13 @@ function* resolveMatches(candidate, context) {
|
|
|
551
745
|
match[1].raws.tailwind = { ...match[1].raws.tailwind, candidate }
|
|
552
746
|
|
|
553
747
|
// Apply final format selector
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
selector: rule.selector,
|
|
562
|
-
candidate,
|
|
563
|
-
context,
|
|
564
|
-
})
|
|
565
|
-
})
|
|
566
|
-
match[1] = container.nodes[0]
|
|
748
|
+
match = applyFinalFormat(match, { context, candidate, original })
|
|
749
|
+
|
|
750
|
+
// Skip rules with invalid selectors
|
|
751
|
+
// This will cause the candidate to be added to the "not class"
|
|
752
|
+
// cache skipping it entirely for future builds
|
|
753
|
+
if (match === null) {
|
|
754
|
+
continue
|
|
567
755
|
}
|
|
568
756
|
|
|
569
757
|
yield match
|
|
@@ -571,20 +759,105 @@ function* resolveMatches(candidate, context) {
|
|
|
571
759
|
}
|
|
572
760
|
}
|
|
573
761
|
|
|
762
|
+
function applyFinalFormat(match, { context, candidate, original }) {
|
|
763
|
+
if (!match[0].collectedFormats) {
|
|
764
|
+
return match
|
|
765
|
+
}
|
|
766
|
+
|
|
767
|
+
let isValid = true
|
|
768
|
+
let finalFormat
|
|
769
|
+
|
|
770
|
+
try {
|
|
771
|
+
finalFormat = formatVariantSelector(match[0].collectedFormats, {
|
|
772
|
+
context,
|
|
773
|
+
candidate,
|
|
774
|
+
})
|
|
775
|
+
} catch {
|
|
776
|
+
// The format selector we produced is invalid
|
|
777
|
+
// This could be because:
|
|
778
|
+
// - A bug exists
|
|
779
|
+
// - A plugin introduced an invalid variant selector (ex: `addVariant('foo', '&;foo')`)
|
|
780
|
+
// - The user used an invalid arbitrary variant (ex: `[&;foo]:underline`)
|
|
781
|
+
// Either way the build will fail because of this
|
|
782
|
+
// We would rather that the build pass "silently" given that this could
|
|
783
|
+
// happen because of picking up invalid things when scanning content
|
|
784
|
+
// So we'll throw out the candidate instead
|
|
785
|
+
|
|
786
|
+
return null
|
|
787
|
+
}
|
|
788
|
+
|
|
789
|
+
let container = postcss.root({ nodes: [match[1].clone()] })
|
|
790
|
+
|
|
791
|
+
container.walkRules((rule) => {
|
|
792
|
+
if (inKeyframes(rule)) {
|
|
793
|
+
return
|
|
794
|
+
}
|
|
795
|
+
|
|
796
|
+
try {
|
|
797
|
+
rule.selector = finalizeSelector(rule.selector, finalFormat, {
|
|
798
|
+
candidate: original,
|
|
799
|
+
context,
|
|
800
|
+
})
|
|
801
|
+
} catch {
|
|
802
|
+
// If this selector is invalid we also want to skip it
|
|
803
|
+
// But it's likely that being invalid here means there's a bug in a plugin rather than too loosely matching content
|
|
804
|
+
isValid = false
|
|
805
|
+
return false
|
|
806
|
+
}
|
|
807
|
+
})
|
|
808
|
+
|
|
809
|
+
if (!isValid) {
|
|
810
|
+
return null
|
|
811
|
+
}
|
|
812
|
+
|
|
813
|
+
match[1] = container.nodes[0]
|
|
814
|
+
|
|
815
|
+
return match
|
|
816
|
+
}
|
|
817
|
+
|
|
574
818
|
function inKeyframes(rule) {
|
|
575
819
|
return rule.parent && rule.parent.type === 'atrule' && rule.parent.name === 'keyframes'
|
|
576
820
|
}
|
|
577
821
|
|
|
822
|
+
function getImportantStrategy(important) {
|
|
823
|
+
if (important === true) {
|
|
824
|
+
return (rule) => {
|
|
825
|
+
if (inKeyframes(rule)) {
|
|
826
|
+
return
|
|
827
|
+
}
|
|
828
|
+
|
|
829
|
+
rule.walkDecls((d) => {
|
|
830
|
+
if (d.parent.type === 'rule' && !inKeyframes(d.parent)) {
|
|
831
|
+
d.important = true
|
|
832
|
+
}
|
|
833
|
+
})
|
|
834
|
+
}
|
|
835
|
+
}
|
|
836
|
+
|
|
837
|
+
if (typeof important === 'string') {
|
|
838
|
+
return (rule) => {
|
|
839
|
+
if (inKeyframes(rule)) {
|
|
840
|
+
return
|
|
841
|
+
}
|
|
842
|
+
|
|
843
|
+
rule.selectors = rule.selectors.map((selector) => {
|
|
844
|
+
return `${important} ${selector}`
|
|
845
|
+
})
|
|
846
|
+
}
|
|
847
|
+
}
|
|
848
|
+
}
|
|
849
|
+
|
|
578
850
|
function generateRules(candidates, context) {
|
|
579
851
|
let allRules = []
|
|
852
|
+
let strategy = getImportantStrategy(context.tailwindConfig.important)
|
|
580
853
|
|
|
581
854
|
for (let candidate of candidates) {
|
|
582
855
|
if (context.notClassCache.has(candidate)) {
|
|
583
856
|
continue
|
|
584
857
|
}
|
|
585
858
|
|
|
586
|
-
if (context.
|
|
587
|
-
allRules.
|
|
859
|
+
if (context.candidateRuleCache.has(candidate)) {
|
|
860
|
+
allRules = allRules.concat(Array.from(context.candidateRuleCache.get(candidate)))
|
|
588
861
|
continue
|
|
589
862
|
}
|
|
590
863
|
|
|
@@ -596,47 +869,27 @@ function generateRules(candidates, context) {
|
|
|
596
869
|
}
|
|
597
870
|
|
|
598
871
|
context.classCache.set(candidate, matches)
|
|
599
|
-
allRules.push(matches)
|
|
600
|
-
}
|
|
601
872
|
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
if (important === true) {
|
|
605
|
-
return (rule) => {
|
|
606
|
-
rule.walkDecls((d) => {
|
|
607
|
-
if (d.parent.type === 'rule' && !inKeyframes(d.parent)) {
|
|
608
|
-
d.important = true
|
|
609
|
-
}
|
|
610
|
-
})
|
|
611
|
-
}
|
|
612
|
-
}
|
|
873
|
+
let rules = context.candidateRuleCache.get(candidate) ?? new Set()
|
|
874
|
+
context.candidateRuleCache.set(candidate, rules)
|
|
613
875
|
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
rule.selectors = rule.selectors.map((selector) => {
|
|
617
|
-
return `${important} ${selector}`
|
|
618
|
-
})
|
|
619
|
-
}
|
|
620
|
-
}
|
|
621
|
-
})(context.tailwindConfig.important)
|
|
876
|
+
for (const match of matches) {
|
|
877
|
+
let [{ sort, options }, rule] = match
|
|
622
878
|
|
|
623
|
-
|
|
624
|
-
if (options.respectImportant) {
|
|
625
|
-
if (strategy) {
|
|
879
|
+
if (options.respectImportant && strategy) {
|
|
626
880
|
let container = postcss.root({ nodes: [rule.clone()] })
|
|
627
|
-
container.walkRules(
|
|
628
|
-
if (inKeyframes(r)) {
|
|
629
|
-
return
|
|
630
|
-
}
|
|
631
|
-
|
|
632
|
-
strategy(r)
|
|
633
|
-
})
|
|
881
|
+
container.walkRules(strategy)
|
|
634
882
|
rule = container.nodes[0]
|
|
635
883
|
}
|
|
884
|
+
|
|
885
|
+
let newEntry = [sort, rule]
|
|
886
|
+
rules.add(newEntry)
|
|
887
|
+
context.ruleCache.add(newEntry)
|
|
888
|
+
allRules.push(newEntry)
|
|
636
889
|
}
|
|
890
|
+
}
|
|
637
891
|
|
|
638
|
-
|
|
639
|
-
})
|
|
892
|
+
return allRules
|
|
640
893
|
}
|
|
641
894
|
|
|
642
895
|
function isArbitraryValue(input) {
|