tailwindcss 0.0.0-insiders.fe08e91 → 0.0.0-oxide.956419c
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 +384 -3
- package/LICENSE +1 -2
- package/README.md +12 -8
- package/colors.d.ts +3 -0
- package/defaultConfig.d.ts +3 -0
- package/defaultTheme.d.ts +4 -0
- package/lib/cli/build/deps.js +54 -0
- package/lib/cli/build/index.js +48 -0
- package/lib/cli/build/plugin.js +367 -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 +239 -0
- package/lib/cli/init/index.js +46 -0
- package/lib/cli/shared.js +13 -0
- package/lib/cli-peer-dependencies.js +20 -7
- package/lib/cli.js +4 -740
- package/lib/constants.js +27 -20
- package/lib/corePluginList.js +6 -3
- package/lib/corePlugins.js +2064 -1811
- package/lib/css/preflight.css +5 -5
- package/lib/featureFlags.js +31 -22
- package/lib/index.js +4 -28
- package/lib/lib/cacheInvalidation.js +90 -0
- package/lib/lib/collapseAdjacentRules.js +27 -9
- package/lib/lib/collapseDuplicateDeclarations.js +12 -9
- package/lib/lib/content.js +176 -0
- package/lib/lib/defaultExtractor.js +225 -31
- package/lib/lib/detectNesting.js +13 -10
- package/lib/lib/evaluateTailwindFunctions.js +118 -55
- package/lib/lib/expandApplyAtRules.js +439 -190
- package/lib/lib/expandTailwindAtRules.js +151 -134
- package/lib/lib/findAtConfigPath.js +44 -0
- package/lib/lib/generateRules.js +454 -187
- package/lib/lib/getModuleDependencies.js +11 -8
- package/lib/lib/normalizeTailwindDirectives.js +36 -32
- package/lib/lib/offsets.js +217 -0
- package/lib/lib/partitionApplyAtRules.js +56 -0
- package/lib/lib/regex.js +60 -0
- package/lib/lib/resolveDefaultsAtRules.js +89 -67
- package/lib/lib/setupContextUtils.js +667 -376
- package/lib/lib/setupTrackingContext.js +38 -67
- package/lib/lib/sharedState.js +27 -14
- package/lib/lib/substituteScreenAtRules.js +11 -9
- package/lib/oxide/cli.d.js +1 -0
- package/lib/oxide/cli.js +2 -0
- package/lib/oxide/postcss-plugin.d.js +1 -0
- package/lib/oxide/postcss-plugin.js +2 -0
- package/lib/plugin.js +48 -0
- package/{nesting → lib/postcss-plugins/nesting}/README.md +2 -2
- package/lib/postcss-plugins/nesting/index.js +19 -0
- package/lib/postcss-plugins/nesting/plugin.js +87 -0
- package/lib/processTailwindFeatures.js +35 -25
- package/lib/public/colors.js +247 -245
- 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 +8 -5
- package/lib/util/bigSign.js +4 -1
- package/lib/util/buildMediaQuery.js +11 -6
- package/lib/util/cloneDeep.js +7 -6
- package/lib/util/cloneNodes.js +21 -3
- package/lib/util/color.js +53 -54
- package/lib/util/configurePlugins.js +5 -2
- package/lib/util/createPlugin.js +6 -6
- package/lib/util/createUtilityPlugin.js +12 -14
- package/lib/util/dataTypes.js +119 -110
- package/lib/util/defaults.js +4 -1
- package/lib/util/escapeClassName.js +7 -4
- package/lib/util/escapeCommas.js +5 -2
- package/lib/util/flattenColorPalette.js +9 -12
- package/lib/util/formatVariantSelector.js +184 -85
- package/lib/util/getAllConfigs.js +27 -8
- package/lib/util/hashConfig.js +6 -3
- package/lib/util/isKeyframeRule.js +5 -2
- package/lib/util/isPlainObject.js +5 -2
- package/lib/util/{isValidArbitraryValue.js → isSyntacticallyValidPropertyValue.js} +23 -15
- package/lib/util/log.js +20 -14
- package/lib/util/nameClass.js +20 -9
- package/lib/util/negateValue.js +23 -8
- package/lib/util/normalizeConfig.js +116 -72
- package/lib/util/normalizeScreens.js +120 -11
- package/lib/util/parseAnimationValue.js +42 -40
- package/lib/util/parseBoxShadowValue.js +30 -23
- package/lib/util/parseDependency.js +38 -56
- package/lib/util/parseGlob.js +34 -0
- package/lib/util/parseObjectStyles.js +11 -8
- package/lib/util/pluginUtils.js +147 -50
- package/lib/util/prefixSelector.js +10 -8
- package/lib/util/removeAlphaVariables.js +29 -0
- package/lib/util/resolveConfig.js +97 -85
- package/lib/util/resolveConfigPath.js +11 -9
- package/lib/util/responsive.js +8 -5
- package/lib/util/splitAtTopLevelOnly.js +43 -0
- package/lib/util/tap.js +4 -1
- package/lib/util/toColorValue.js +5 -3
- package/lib/util/toPath.js +20 -4
- package/lib/util/transformThemeValue.js +37 -29
- package/lib/util/validateConfig.js +24 -0
- package/lib/util/validateFormalSyntax.js +24 -0
- package/lib/util/withAlphaVariable.js +23 -15
- package/nesting/index.js +2 -12
- package/package.json +50 -45
- package/peers/index.js +11381 -7950
- package/plugin.d.ts +11 -0
- package/resolveConfig.d.ts +12 -0
- package/scripts/generate-types.js +105 -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 +439 -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 +234 -0
- package/src/cli/init/index.js +50 -0
- package/src/cli/shared.js +6 -0
- package/src/cli-peer-dependencies.js +7 -1
- package/src/cli.js +4 -810
- package/src/corePluginList.js +1 -1
- package/src/corePlugins.js +532 -217
- package/src/css/preflight.css +5 -5
- package/src/featureFlags.js +15 -9
- package/src/index.js +4 -27
- package/src/lib/cacheInvalidation.js +52 -0
- package/src/lib/collapseAdjacentRules.js +21 -2
- package/src/lib/content.js +212 -0
- package/src/lib/defaultExtractor.js +196 -33
- package/src/lib/evaluateTailwindFunctions.js +78 -7
- package/src/lib/expandApplyAtRules.js +482 -183
- package/src/lib/expandTailwindAtRules.js +106 -85
- package/src/lib/findAtConfigPath.js +48 -0
- package/src/lib/generateRules.js +418 -129
- package/src/lib/normalizeTailwindDirectives.js +1 -0
- package/src/lib/offsets.js +270 -0
- package/src/lib/partitionApplyAtRules.js +52 -0
- package/src/lib/regex.js +74 -0
- package/src/lib/resolveDefaultsAtRules.js +51 -30
- package/src/lib/setupContextUtils.js +556 -208
- package/src/lib/setupTrackingContext.js +11 -48
- package/src/lib/sharedState.js +5 -0
- package/src/oxide/cli.d.ts +0 -0
- package/src/oxide/cli.ts +1 -0
- package/src/oxide/postcss-plugin.d.ts +0 -0
- package/src/oxide/postcss-plugin.ts +1 -0
- package/src/plugin.js +47 -0
- package/src/postcss-plugins/nesting/README.md +42 -0
- package/src/postcss-plugins/nesting/index.js +13 -0
- package/src/postcss-plugins/nesting/plugin.js +80 -0
- package/src/processTailwindFeatures.js +8 -0
- package/src/util/buildMediaQuery.js +5 -3
- package/src/util/cloneNodes.js +19 -2
- package/src/util/color.js +25 -21
- package/src/util/dataTypes.js +29 -21
- package/src/util/formatVariantSelector.js +184 -61
- package/src/util/getAllConfigs.js +19 -0
- package/src/util/{isValidArbitraryValue.js → isSyntacticallyValidPropertyValue.js} +1 -1
- package/src/util/log.js +8 -8
- package/src/util/nameClass.js +4 -0
- package/src/util/negateValue.js +11 -3
- package/src/util/normalizeConfig.js +44 -6
- package/src/util/normalizeScreens.js +99 -4
- package/src/util/parseBoxShadowValue.js +4 -3
- 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 +7 -5
- package/src/util/removeAlphaVariables.js +24 -0
- package/src/util/resolveConfig.js +70 -32
- 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 +167 -164
- package/stubs/simpleConfig.stub.js +1 -0
- package/types/config.d.ts +362 -0
- package/types/generated/.gitkeep +0 -0
- package/types/generated/colors.d.ts +276 -0
- package/types/generated/corePluginList.d.ts +1 -0
- package/types/generated/default-theme.d.ts +342 -0
- package/types/index.d.ts +7 -0
- package/nesting/plugin.js +0 -41
package/src/lib/generateRules.js
CHANGED
|
@@ -3,18 +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
|
+
import * as sharedState from './sharedState'
|
|
8
9
|
import { formatVariantSelector, finalizeSelector } from '../util/formatVariantSelector'
|
|
9
10
|
import { asClass } from '../util/nameClass'
|
|
10
11
|
import { normalize } from '../util/dataTypes'
|
|
11
|
-
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'
|
|
12
16
|
|
|
13
17
|
let classNameParser = selectorParser((selectors) => {
|
|
14
18
|
return selectors.first.filter(({ type }) => type === 'class').pop().value
|
|
15
19
|
})
|
|
16
20
|
|
|
17
|
-
function getClassNameFromSelector(selector) {
|
|
21
|
+
export function getClassNameFromSelector(selector) {
|
|
18
22
|
return classNameParser.transformSync(selector)
|
|
19
23
|
}
|
|
20
24
|
|
|
@@ -25,33 +29,49 @@ function getClassNameFromSelector(selector) {
|
|
|
25
29
|
// Example with dynamic classes:
|
|
26
30
|
// ['grid-cols', '[[linename],1fr,auto]']
|
|
27
31
|
// ['grid', 'cols-[[linename],1fr,auto]']
|
|
28
|
-
function* candidatePermutations(candidate
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
32
|
+
function* candidatePermutations(candidate) {
|
|
33
|
+
let lastIndex = Infinity
|
|
34
|
+
|
|
35
|
+
while (lastIndex >= 0) {
|
|
36
|
+
let dashIdx
|
|
37
|
+
let wasSlash = false
|
|
38
|
+
|
|
39
|
+
if (lastIndex === Infinity && candidate.endsWith(']')) {
|
|
40
|
+
let bracketIdx = candidate.indexOf('[')
|
|
41
|
+
|
|
42
|
+
// If character before `[` isn't a dash or a slash, this isn't a dynamic class
|
|
43
|
+
// eg. string[]
|
|
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
|
|
55
|
+
} else {
|
|
56
|
+
dashIdx = candidate.lastIndexOf('-', lastIndex)
|
|
57
|
+
}
|
|
37
58
|
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
} else {
|
|
42
|
-
dashIdx = candidate.lastIndexOf('-', lastIndex)
|
|
43
|
-
}
|
|
59
|
+
if (dashIdx < 0) {
|
|
60
|
+
break
|
|
61
|
+
}
|
|
44
62
|
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
}
|
|
63
|
+
let prefix = candidate.slice(0, dashIdx)
|
|
64
|
+
let modifier = candidate.slice(wasSlash ? dashIdx : dashIdx + 1)
|
|
48
65
|
|
|
49
|
-
|
|
50
|
-
let modifier = candidate.slice(dashIdx + 1)
|
|
66
|
+
lastIndex = dashIdx - 1
|
|
51
67
|
|
|
52
|
-
|
|
68
|
+
// TODO: This feels a bit hacky
|
|
69
|
+
if (prefix === '' || modifier === '/') {
|
|
70
|
+
continue
|
|
71
|
+
}
|
|
53
72
|
|
|
54
|
-
|
|
73
|
+
yield [prefix, modifier]
|
|
74
|
+
}
|
|
55
75
|
}
|
|
56
76
|
|
|
57
77
|
function applyPrefix(matches, context) {
|
|
@@ -63,9 +83,23 @@ function applyPrefix(matches, context) {
|
|
|
63
83
|
let [meta] = match
|
|
64
84
|
if (meta.options.respectPrefix) {
|
|
65
85
|
let container = postcss.root({ nodes: [match[1].clone()] })
|
|
86
|
+
let classCandidate = match[1].raws.tailwind.classCandidate
|
|
87
|
+
|
|
66
88
|
container.walkRules((r) => {
|
|
67
|
-
|
|
89
|
+
// If this is a negative utility with a dash *before* the prefix we
|
|
90
|
+
// have to ensure that the generated selector matches the candidate
|
|
91
|
+
|
|
92
|
+
// Not doing this will cause `-tw-top-1` to generate the class `.tw--top-1`
|
|
93
|
+
// The disconnect between candidate <-> class can cause @apply to hard crash.
|
|
94
|
+
let shouldPrependNegative = classCandidate.startsWith('-')
|
|
95
|
+
|
|
96
|
+
r.selector = prefixSelector(
|
|
97
|
+
context.tailwindConfig.prefix,
|
|
98
|
+
r.selector,
|
|
99
|
+
shouldPrependNegative
|
|
100
|
+
)
|
|
68
101
|
})
|
|
102
|
+
|
|
69
103
|
match[1] = container.nodes[0]
|
|
70
104
|
}
|
|
71
105
|
}
|
|
@@ -73,7 +107,7 @@ function applyPrefix(matches, context) {
|
|
|
73
107
|
return matches
|
|
74
108
|
}
|
|
75
109
|
|
|
76
|
-
function applyImportant(matches) {
|
|
110
|
+
function applyImportant(matches, classCandidate) {
|
|
77
111
|
if (matches.length === 0) {
|
|
78
112
|
return matches
|
|
79
113
|
}
|
|
@@ -82,9 +116,15 @@ function applyImportant(matches) {
|
|
|
82
116
|
for (let [meta, rule] of matches) {
|
|
83
117
|
let container = postcss.root({ nodes: [rule.clone()] })
|
|
84
118
|
container.walkRules((r) => {
|
|
85
|
-
r.selector = updateAllClasses(
|
|
86
|
-
|
|
87
|
-
|
|
119
|
+
r.selector = updateAllClasses(
|
|
120
|
+
filterSelectorsForClass(r.selector, classCandidate),
|
|
121
|
+
(className) => {
|
|
122
|
+
if (className === classCandidate) {
|
|
123
|
+
return `!${className}`
|
|
124
|
+
}
|
|
125
|
+
return className
|
|
126
|
+
}
|
|
127
|
+
)
|
|
88
128
|
r.walkDecls((d) => (d.important = true))
|
|
89
129
|
})
|
|
90
130
|
result.push([{ ...meta, important: true }, container.nodes[0]])
|
|
@@ -107,8 +147,61 @@ function applyVariant(variant, matches, context) {
|
|
|
107
147
|
return matches
|
|
108
148
|
}
|
|
109
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
|
+
|
|
110
203
|
if (context.variantMap.has(variant)) {
|
|
111
|
-
let variantFunctionTuples = context.variantMap.get(variant)
|
|
204
|
+
let variantFunctionTuples = context.variantMap.get(variant).slice()
|
|
112
205
|
let result = []
|
|
113
206
|
|
|
114
207
|
for (let [meta, rule] of matches) {
|
|
@@ -119,15 +212,17 @@ function applyVariant(variant, matches, context) {
|
|
|
119
212
|
|
|
120
213
|
let container = postcss.root({ nodes: [rule.clone()] })
|
|
121
214
|
|
|
122
|
-
for (let [variantSort, variantFunction] of variantFunctionTuples) {
|
|
123
|
-
let clone = container.clone()
|
|
215
|
+
for (let [variantSort, variantFunction, containerFromArray] of variantFunctionTuples) {
|
|
216
|
+
let clone = (containerFromArray ?? container).clone()
|
|
124
217
|
let collectedFormats = []
|
|
125
218
|
|
|
126
|
-
let originals = new Map()
|
|
127
|
-
|
|
128
219
|
function prepareBackup() {
|
|
129
|
-
|
|
130
|
-
clone.
|
|
220
|
+
// Already prepared, chicken out
|
|
221
|
+
if (clone.raws.neededBackup) {
|
|
222
|
+
return
|
|
223
|
+
}
|
|
224
|
+
clone.raws.neededBackup = true
|
|
225
|
+
clone.walkRules((rule) => (rule.raws.originalSelector = rule.selector))
|
|
131
226
|
}
|
|
132
227
|
|
|
133
228
|
function modifySelectors(modifierFunction) {
|
|
@@ -169,8 +264,29 @@ function applyVariant(variant, matches, context) {
|
|
|
169
264
|
format(selectorFormat) {
|
|
170
265
|
collectedFormats.push(selectorFormat)
|
|
171
266
|
},
|
|
267
|
+
args,
|
|
172
268
|
})
|
|
173
269
|
|
|
270
|
+
// It can happen that a list of format strings is returned from within the function. In that
|
|
271
|
+
// case, we have to process them as well. We can use the existing `variantSort`.
|
|
272
|
+
if (Array.isArray(ruleWithVariant)) {
|
|
273
|
+
for (let [idx, variantFunction] of ruleWithVariant.entries()) {
|
|
274
|
+
// This is a little bit scary since we are pushing to an array of items that we are
|
|
275
|
+
// currently looping over. However, you can also think of it like a processing queue
|
|
276
|
+
// where you keep handling jobs until everything is done and each job can queue more
|
|
277
|
+
// jobs if needed.
|
|
278
|
+
variantFunctionTuples.push([
|
|
279
|
+
context.offsets.applyParallelOffset(variantSort, idx),
|
|
280
|
+
variantFunction,
|
|
281
|
+
|
|
282
|
+
// If the clone has been modified we have to pass that back
|
|
283
|
+
// though so each rule can use the modified container
|
|
284
|
+
clone.clone(),
|
|
285
|
+
])
|
|
286
|
+
}
|
|
287
|
+
continue
|
|
288
|
+
}
|
|
289
|
+
|
|
174
290
|
if (typeof ruleWithVariant === 'string') {
|
|
175
291
|
collectedFormats.push(ruleWithVariant)
|
|
176
292
|
}
|
|
@@ -179,13 +295,15 @@ function applyVariant(variant, matches, context) {
|
|
|
179
295
|
continue
|
|
180
296
|
}
|
|
181
297
|
|
|
182
|
-
// We
|
|
298
|
+
// We had to backup selectors, therefore we assume that somebody touched
|
|
183
299
|
// `container` or `modifySelectors`. Let's see if they did, so that we
|
|
184
300
|
// can restore the selectors, and collect the format strings.
|
|
185
|
-
if (
|
|
301
|
+
if (clone.raws.neededBackup) {
|
|
302
|
+
delete clone.raws.neededBackup
|
|
186
303
|
clone.walkRules((rule) => {
|
|
187
|
-
|
|
188
|
-
|
|
304
|
+
let before = rule.raws.originalSelector
|
|
305
|
+
if (!before) return
|
|
306
|
+
delete rule.raws.originalSelector
|
|
189
307
|
if (before === rule.selector) return // No mutation happened
|
|
190
308
|
|
|
191
309
|
let modified = rule.selector
|
|
@@ -216,11 +334,22 @@ function applyVariant(variant, matches, context) {
|
|
|
216
334
|
})
|
|
217
335
|
}
|
|
218
336
|
|
|
337
|
+
// This tracks the originating layer for the variant
|
|
338
|
+
// For example:
|
|
339
|
+
// .sm:underline {} is a variant of something in the utilities layer
|
|
340
|
+
// .sm:container {} is a variant of the container component
|
|
341
|
+
clone.nodes[0].raws.tailwind = { ...clone.nodes[0].raws.tailwind, parentLayer: meta.layer }
|
|
342
|
+
|
|
219
343
|
let withOffset = [
|
|
220
344
|
{
|
|
221
345
|
...meta,
|
|
222
|
-
sort:
|
|
346
|
+
sort: context.offsets.applyVariantOffset(
|
|
347
|
+
meta.sort,
|
|
348
|
+
variantSort,
|
|
349
|
+
Object.assign(args, context.variantOptions.get(variant))
|
|
350
|
+
),
|
|
223
351
|
collectedFormats: (meta.collectedFormats ?? []).concat(collectedFormats),
|
|
352
|
+
isArbitraryVariant: isArbitraryValue(variant),
|
|
224
353
|
},
|
|
225
354
|
clone.nodes[0],
|
|
226
355
|
]
|
|
@@ -259,7 +388,47 @@ function isValidPropName(name) {
|
|
|
259
388
|
return IS_VALID_PROPERTY_NAME.test(name)
|
|
260
389
|
}
|
|
261
390
|
|
|
391
|
+
/**
|
|
392
|
+
* @param {string} declaration
|
|
393
|
+
* @returns {boolean}
|
|
394
|
+
*/
|
|
395
|
+
function looksLikeUri(declaration) {
|
|
396
|
+
// Quick bailout for obvious non-urls
|
|
397
|
+
// This doesn't support schemes that don't use a leading // but that's unlikely to be a problem
|
|
398
|
+
if (!declaration.includes('://')) {
|
|
399
|
+
return false
|
|
400
|
+
}
|
|
401
|
+
|
|
402
|
+
try {
|
|
403
|
+
const url = new URL(declaration)
|
|
404
|
+
return url.scheme !== '' && url.host !== ''
|
|
405
|
+
} catch (err) {
|
|
406
|
+
// Definitely not a valid url
|
|
407
|
+
return false
|
|
408
|
+
}
|
|
409
|
+
}
|
|
410
|
+
|
|
411
|
+
function isParsableNode(node) {
|
|
412
|
+
let isParsable = true
|
|
413
|
+
|
|
414
|
+
node.walkDecls((decl) => {
|
|
415
|
+
if (!isParsableCssValue(decl.name, decl.value)) {
|
|
416
|
+
isParsable = false
|
|
417
|
+
return false
|
|
418
|
+
}
|
|
419
|
+
})
|
|
420
|
+
|
|
421
|
+
return isParsable
|
|
422
|
+
}
|
|
423
|
+
|
|
262
424
|
function isParsableCssValue(property, value) {
|
|
425
|
+
// We don't want to to treat [https://example.com] as a custom property
|
|
426
|
+
// Even though, according to the CSS grammar, it's a totally valid CSS declaration
|
|
427
|
+
// So we short-circuit here by checking if the custom property looks like a url
|
|
428
|
+
if (looksLikeUri(`${property}:${value}`)) {
|
|
429
|
+
return false
|
|
430
|
+
}
|
|
431
|
+
|
|
263
432
|
try {
|
|
264
433
|
postcss.parse(`a{${property}:${value}}`).toResult()
|
|
265
434
|
return true
|
|
@@ -289,9 +458,11 @@ function extractArbitraryProperty(classCandidate, context) {
|
|
|
289
458
|
return null
|
|
290
459
|
}
|
|
291
460
|
|
|
461
|
+
let sort = context.offsets.arbitraryProperty()
|
|
462
|
+
|
|
292
463
|
return [
|
|
293
464
|
[
|
|
294
|
-
{ sort
|
|
465
|
+
{ sort, layer: 'utilities' },
|
|
295
466
|
() => ({
|
|
296
467
|
[asClass(classCandidate)]: {
|
|
297
468
|
[property]: normalized,
|
|
@@ -318,7 +489,11 @@ function* resolveMatchedPlugins(classCandidate, context) {
|
|
|
318
489
|
const twConfigPrefix = context.tailwindConfig.prefix
|
|
319
490
|
|
|
320
491
|
const twConfigPrefixLen = twConfigPrefix.length
|
|
321
|
-
|
|
492
|
+
|
|
493
|
+
const hasMatchingPrefix =
|
|
494
|
+
candidatePrefix.startsWith(twConfigPrefix) || candidatePrefix.startsWith(`-${twConfigPrefix}`)
|
|
495
|
+
|
|
496
|
+
if (candidatePrefix[twConfigPrefixLen] === '-' && hasMatchingPrefix) {
|
|
322
497
|
negative = true
|
|
323
498
|
candidatePrefix = twConfigPrefix + candidatePrefix.slice(twConfigPrefixLen + 1)
|
|
324
499
|
}
|
|
@@ -335,10 +510,26 @@ function* resolveMatchedPlugins(classCandidate, context) {
|
|
|
335
510
|
}
|
|
336
511
|
|
|
337
512
|
function splitWithSeparator(input, separator) {
|
|
338
|
-
|
|
513
|
+
if (input === sharedState.NOT_ON_DEMAND) {
|
|
514
|
+
return [sharedState.NOT_ON_DEMAND]
|
|
515
|
+
}
|
|
516
|
+
|
|
517
|
+
return splitAtTopLevelOnly(input, separator)
|
|
518
|
+
}
|
|
519
|
+
|
|
520
|
+
function* recordCandidates(matches, classCandidate) {
|
|
521
|
+
for (const match of matches) {
|
|
522
|
+
match[1].raws.tailwind = {
|
|
523
|
+
...match[1].raws.tailwind,
|
|
524
|
+
classCandidate,
|
|
525
|
+
preserveSource: match[0].options?.preserveSource ?? false,
|
|
526
|
+
}
|
|
527
|
+
|
|
528
|
+
yield match
|
|
529
|
+
}
|
|
339
530
|
}
|
|
340
531
|
|
|
341
|
-
function* resolveMatches(candidate, context) {
|
|
532
|
+
function* resolveMatches(candidate, context, original = candidate) {
|
|
342
533
|
let separator = context.tailwindConfig.separator
|
|
343
534
|
let [classCandidate, ...variants] = splitWithSeparator(candidate, separator).reverse()
|
|
344
535
|
let important = false
|
|
@@ -348,6 +539,15 @@ function* resolveMatches(candidate, context) {
|
|
|
348
539
|
classCandidate = classCandidate.slice(1)
|
|
349
540
|
}
|
|
350
541
|
|
|
542
|
+
if (flagEnabled(context.tailwindConfig, 'variantGrouping')) {
|
|
543
|
+
if (classCandidate.startsWith('(') && classCandidate.endsWith(')')) {
|
|
544
|
+
let base = variants.slice().reverse().join(separator)
|
|
545
|
+
for (let part of splitAtTopLevelOnly(classCandidate.slice(1, -1), ',')) {
|
|
546
|
+
yield* resolveMatches(base + separator + part, context, original)
|
|
547
|
+
}
|
|
548
|
+
}
|
|
549
|
+
}
|
|
550
|
+
|
|
351
551
|
// TODO: Reintroduce this in ways that doesn't break on false positives
|
|
352
552
|
// function sortAgainst(toSort, against) {
|
|
353
553
|
// return toSort.slice().sort((a, z) => {
|
|
@@ -388,71 +588,144 @@ function* resolveMatches(candidate, context) {
|
|
|
388
588
|
}
|
|
389
589
|
|
|
390
590
|
if (matchesPerPlugin.length > 0) {
|
|
391
|
-
|
|
591
|
+
let matchingTypes = Array.from(
|
|
592
|
+
getMatchingTypes(
|
|
593
|
+
sort.options?.types ?? [],
|
|
594
|
+
modifier,
|
|
595
|
+
sort.options ?? {},
|
|
596
|
+
context.tailwindConfig
|
|
597
|
+
)
|
|
598
|
+
).map(([_, type]) => type)
|
|
599
|
+
|
|
600
|
+
if (matchingTypes.length > 0) {
|
|
601
|
+
typesByMatches.set(matchesPerPlugin, matchingTypes)
|
|
602
|
+
}
|
|
603
|
+
|
|
392
604
|
matches.push(matchesPerPlugin)
|
|
393
605
|
}
|
|
394
606
|
}
|
|
395
607
|
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
for (let otherGroup of typesPerPlugin) {
|
|
407
|
-
if (pluginTypes === otherGroup) continue
|
|
608
|
+
if (isArbitraryValue(modifier)) {
|
|
609
|
+
if (matches.length > 1) {
|
|
610
|
+
// Partition plugins in 2 categories so that we can start searching in the plugins that
|
|
611
|
+
// don't have `any` as a type first.
|
|
612
|
+
let [withAny, withoutAny] = matches.reduce(
|
|
613
|
+
(group, plugin) => {
|
|
614
|
+
let hasAnyType = plugin.some(([{ options }]) =>
|
|
615
|
+
options.types.some(({ type }) => type === 'any')
|
|
616
|
+
)
|
|
408
617
|
|
|
409
|
-
if (
|
|
410
|
-
|
|
411
|
-
|
|
618
|
+
if (hasAnyType) {
|
|
619
|
+
group[0].push(plugin)
|
|
620
|
+
} else {
|
|
621
|
+
group[1].push(plugin)
|
|
412
622
|
}
|
|
623
|
+
return group
|
|
624
|
+
},
|
|
625
|
+
[[], []]
|
|
626
|
+
)
|
|
627
|
+
|
|
628
|
+
function findFallback(matches) {
|
|
629
|
+
// If only a single plugin matches, let's take that one
|
|
630
|
+
if (matches.length === 1) {
|
|
631
|
+
return matches[0]
|
|
413
632
|
}
|
|
414
633
|
|
|
415
|
-
|
|
634
|
+
// Otherwise, find the plugin that creates a valid rule given the arbitrary value, and
|
|
635
|
+
// also has the correct type which preferOnConflicts the plugin in case of clashes.
|
|
636
|
+
return matches.find((rules) => {
|
|
637
|
+
let matchingTypes = typesByMatches.get(rules)
|
|
638
|
+
return rules.some(([{ options }, rule]) => {
|
|
639
|
+
if (!isParsableNode(rule)) {
|
|
640
|
+
return false
|
|
641
|
+
}
|
|
642
|
+
|
|
643
|
+
return options.types.some(
|
|
644
|
+
({ type, preferOnConflict }) => matchingTypes.includes(type) && preferOnConflict
|
|
645
|
+
)
|
|
646
|
+
})
|
|
647
|
+
})
|
|
416
648
|
}
|
|
417
|
-
}
|
|
418
649
|
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
.flat()
|
|
426
|
-
.map((rule) =>
|
|
427
|
-
rule
|
|
428
|
-
.toString()
|
|
429
|
-
.split('\n')
|
|
430
|
-
.slice(1, -1) // Remove selector and closing '}'
|
|
431
|
-
.map((line) => line.trim())
|
|
432
|
-
.map((x) => ` ${x}`) // Re-indent
|
|
433
|
-
.join('\n')
|
|
434
|
-
)
|
|
435
|
-
.join('\n\n')
|
|
650
|
+
// Try to find a fallback plugin, because we already know that multiple plugins matched for
|
|
651
|
+
// the given arbitrary value.
|
|
652
|
+
let fallback = findFallback(withoutAny) ?? findFallback(withAny)
|
|
653
|
+
if (fallback) {
|
|
654
|
+
matches = [fallback]
|
|
655
|
+
}
|
|
436
656
|
|
|
437
|
-
|
|
438
|
-
|
|
657
|
+
// We couldn't find a fallback plugin which means that there are now multiple plugins that
|
|
658
|
+
// generated css for the current candidate. This means that the result is ambiguous and this
|
|
659
|
+
// should not happen. We won't generate anything right now, so let's report this to the user
|
|
660
|
+
// by logging some options about what they can do.
|
|
661
|
+
else {
|
|
662
|
+
let typesPerPlugin = matches.map(
|
|
663
|
+
(match) => new Set([...(typesByMatches.get(match) ?? [])])
|
|
664
|
+
)
|
|
665
|
+
|
|
666
|
+
// Remove duplicates, so that we can detect proper unique types for each plugin.
|
|
667
|
+
for (let pluginTypes of typesPerPlugin) {
|
|
668
|
+
for (let type of pluginTypes) {
|
|
669
|
+
let removeFromOwnGroup = false
|
|
670
|
+
|
|
671
|
+
for (let otherGroup of typesPerPlugin) {
|
|
672
|
+
if (pluginTypes === otherGroup) continue
|
|
673
|
+
|
|
674
|
+
if (otherGroup.has(type)) {
|
|
675
|
+
otherGroup.delete(type)
|
|
676
|
+
removeFromOwnGroup = true
|
|
677
|
+
}
|
|
678
|
+
}
|
|
679
|
+
|
|
680
|
+
if (removeFromOwnGroup) pluginTypes.delete(type)
|
|
681
|
+
}
|
|
682
|
+
}
|
|
683
|
+
|
|
684
|
+
let messages = []
|
|
685
|
+
|
|
686
|
+
for (let [idx, group] of typesPerPlugin.entries()) {
|
|
687
|
+
for (let type of group) {
|
|
688
|
+
let rules = matches[idx]
|
|
689
|
+
.map(([, rule]) => rule)
|
|
690
|
+
.flat()
|
|
691
|
+
.map((rule) =>
|
|
692
|
+
rule
|
|
693
|
+
.toString()
|
|
694
|
+
.split('\n')
|
|
695
|
+
.slice(1, -1) // Remove selector and closing '}'
|
|
696
|
+
.map((line) => line.trim())
|
|
697
|
+
.map((x) => ` ${x}`) // Re-indent
|
|
698
|
+
.join('\n')
|
|
699
|
+
)
|
|
700
|
+
.join('\n\n')
|
|
701
|
+
|
|
702
|
+
messages.push(
|
|
703
|
+
` Use \`${candidate.replace('[', `[${type}:`)}\` for \`${rules.trim()}\``
|
|
704
|
+
)
|
|
705
|
+
break
|
|
706
|
+
}
|
|
707
|
+
}
|
|
708
|
+
|
|
709
|
+
log.warn([
|
|
710
|
+
`The class \`${candidate}\` is ambiguous and matches multiple utilities.`,
|
|
711
|
+
...messages,
|
|
712
|
+
`If this is content and not a class, replace it with \`${candidate
|
|
713
|
+
.replace('[', '[')
|
|
714
|
+
.replace(']', ']')}\` to silence this warning.`,
|
|
715
|
+
])
|
|
716
|
+
continue
|
|
439
717
|
}
|
|
440
718
|
}
|
|
441
719
|
|
|
442
|
-
|
|
443
|
-
`The class \`${candidate}\` is ambiguous and matches multiple utilities.`,
|
|
444
|
-
...messages,
|
|
445
|
-
`If this is content and not a class, replace it with \`${candidate
|
|
446
|
-
.replace('[', '[')
|
|
447
|
-
.replace(']', ']')}\` to silence this warning.`,
|
|
448
|
-
])
|
|
449
|
-
continue
|
|
720
|
+
matches = matches.map((list) => list.filter((match) => isParsableNode(match[1])))
|
|
450
721
|
}
|
|
451
722
|
|
|
452
|
-
matches =
|
|
723
|
+
matches = matches.flat()
|
|
724
|
+
matches = Array.from(recordCandidates(matches, classCandidate))
|
|
725
|
+
matches = applyPrefix(matches, context)
|
|
453
726
|
|
|
454
727
|
if (important) {
|
|
455
|
-
matches = applyImportant(matches,
|
|
728
|
+
matches = applyImportant(matches, classCandidate)
|
|
456
729
|
}
|
|
457
730
|
|
|
458
731
|
for (let variant of variants) {
|
|
@@ -460,6 +733,8 @@ function* resolveMatches(candidate, context) {
|
|
|
460
733
|
}
|
|
461
734
|
|
|
462
735
|
for (let match of matches) {
|
|
736
|
+
match[1].raws.tailwind = { ...match[1].raws.tailwind, candidate }
|
|
737
|
+
|
|
463
738
|
// Apply final format selector
|
|
464
739
|
if (match[0].collectedFormats) {
|
|
465
740
|
let finalFormat = formatVariantSelector('&', ...match[0].collectedFormats)
|
|
@@ -469,7 +744,12 @@ function* resolveMatches(candidate, context) {
|
|
|
469
744
|
|
|
470
745
|
rule.selector = finalizeSelector(finalFormat, {
|
|
471
746
|
selector: rule.selector,
|
|
472
|
-
candidate,
|
|
747
|
+
candidate: original,
|
|
748
|
+
base: candidate
|
|
749
|
+
.split(new RegExp(`\\${context?.tailwindConfig?.separator ?? ':'}(?![^[]*\\])`))
|
|
750
|
+
.pop(),
|
|
751
|
+
isArbitraryVariant: match[0].isArbitraryVariant,
|
|
752
|
+
|
|
473
753
|
context,
|
|
474
754
|
})
|
|
475
755
|
})
|
|
@@ -485,16 +765,45 @@ function inKeyframes(rule) {
|
|
|
485
765
|
return rule.parent && rule.parent.type === 'atrule' && rule.parent.name === 'keyframes'
|
|
486
766
|
}
|
|
487
767
|
|
|
768
|
+
function getImportantStrategy(important) {
|
|
769
|
+
if (important === true) {
|
|
770
|
+
return (rule) => {
|
|
771
|
+
if (inKeyframes(rule)) {
|
|
772
|
+
return
|
|
773
|
+
}
|
|
774
|
+
|
|
775
|
+
rule.walkDecls((d) => {
|
|
776
|
+
if (d.parent.type === 'rule' && !inKeyframes(d.parent)) {
|
|
777
|
+
d.important = true
|
|
778
|
+
}
|
|
779
|
+
})
|
|
780
|
+
}
|
|
781
|
+
}
|
|
782
|
+
|
|
783
|
+
if (typeof important === 'string') {
|
|
784
|
+
return (rule) => {
|
|
785
|
+
if (inKeyframes(rule)) {
|
|
786
|
+
return
|
|
787
|
+
}
|
|
788
|
+
|
|
789
|
+
rule.selectors = rule.selectors.map((selector) => {
|
|
790
|
+
return `${important} ${selector}`
|
|
791
|
+
})
|
|
792
|
+
}
|
|
793
|
+
}
|
|
794
|
+
}
|
|
795
|
+
|
|
488
796
|
function generateRules(candidates, context) {
|
|
489
797
|
let allRules = []
|
|
798
|
+
let strategy = getImportantStrategy(context.tailwindConfig.important)
|
|
490
799
|
|
|
491
800
|
for (let candidate of candidates) {
|
|
492
801
|
if (context.notClassCache.has(candidate)) {
|
|
493
802
|
continue
|
|
494
803
|
}
|
|
495
804
|
|
|
496
|
-
if (context.
|
|
497
|
-
allRules.
|
|
805
|
+
if (context.candidateRuleCache.has(candidate)) {
|
|
806
|
+
allRules = allRules.concat(Array.from(context.candidateRuleCache.get(candidate)))
|
|
498
807
|
continue
|
|
499
808
|
}
|
|
500
809
|
|
|
@@ -506,47 +815,27 @@ function generateRules(candidates, context) {
|
|
|
506
815
|
}
|
|
507
816
|
|
|
508
817
|
context.classCache.set(candidate, matches)
|
|
509
|
-
allRules.push(matches)
|
|
510
|
-
}
|
|
511
818
|
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
if (important === true) {
|
|
515
|
-
return (rule) => {
|
|
516
|
-
rule.walkDecls((d) => {
|
|
517
|
-
if (d.parent.type === 'rule' && !inKeyframes(d.parent)) {
|
|
518
|
-
d.important = true
|
|
519
|
-
}
|
|
520
|
-
})
|
|
521
|
-
}
|
|
522
|
-
}
|
|
819
|
+
let rules = context.candidateRuleCache.get(candidate) ?? new Set()
|
|
820
|
+
context.candidateRuleCache.set(candidate, rules)
|
|
523
821
|
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
rule.selectors = rule.selectors.map((selector) => {
|
|
527
|
-
return `${important} ${selector}`
|
|
528
|
-
})
|
|
529
|
-
}
|
|
530
|
-
}
|
|
531
|
-
})(context.tailwindConfig.important)
|
|
822
|
+
for (const match of matches) {
|
|
823
|
+
let [{ sort, options }, rule] = match
|
|
532
824
|
|
|
533
|
-
|
|
534
|
-
if (options.respectImportant) {
|
|
535
|
-
if (strategy) {
|
|
825
|
+
if (options.respectImportant && strategy) {
|
|
536
826
|
let container = postcss.root({ nodes: [rule.clone()] })
|
|
537
|
-
container.walkRules(
|
|
538
|
-
if (inKeyframes(r)) {
|
|
539
|
-
return
|
|
540
|
-
}
|
|
541
|
-
|
|
542
|
-
strategy(r)
|
|
543
|
-
})
|
|
827
|
+
container.walkRules(strategy)
|
|
544
828
|
rule = container.nodes[0]
|
|
545
829
|
}
|
|
830
|
+
|
|
831
|
+
let newEntry = [sort, rule]
|
|
832
|
+
rules.add(newEntry)
|
|
833
|
+
context.ruleCache.add(newEntry)
|
|
834
|
+
allRules.push(newEntry)
|
|
546
835
|
}
|
|
836
|
+
}
|
|
547
837
|
|
|
548
|
-
|
|
549
|
-
})
|
|
838
|
+
return allRules
|
|
550
839
|
}
|
|
551
840
|
|
|
552
841
|
function isArbitraryValue(input) {
|