tailwindcss 0.0.0-insiders.ea139f2 → 0.0.0-insiders.ea4e1cd
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/LICENSE +1 -2
- package/README.md +15 -7
- package/colors.d.ts +3 -0
- package/colors.js +2 -1
- package/defaultConfig.d.ts +3 -0
- package/defaultConfig.js +2 -1
- package/defaultTheme.d.ts +4 -0
- package/defaultTheme.js +2 -1
- package/lib/cli/build/deps.js +62 -0
- package/lib/cli/build/index.js +54 -0
- package/lib/cli/build/plugin.js +378 -0
- package/lib/cli/build/utils.js +88 -0
- package/lib/cli/build/watching.js +182 -0
- package/lib/cli/help/index.js +73 -0
- package/lib/cli/index.js +230 -0
- package/lib/cli/init/index.js +63 -0
- package/lib/cli-peer-dependencies.js +28 -7
- package/lib/cli.js +4 -703
- package/lib/corePluginList.js +12 -3
- package/lib/corePlugins.js +2373 -1863
- package/lib/css/preflight.css +10 -8
- package/lib/featureFlags.js +49 -26
- package/lib/index.js +1 -31
- package/lib/lib/cacheInvalidation.js +92 -0
- package/lib/lib/collapseAdjacentRules.js +30 -10
- package/lib/lib/collapseDuplicateDeclarations.js +60 -4
- package/lib/lib/content.js +181 -0
- package/lib/lib/defaultExtractor.js +243 -0
- package/lib/lib/detectNesting.js +21 -10
- package/lib/lib/evaluateTailwindFunctions.js +115 -50
- package/lib/lib/expandApplyAtRules.js +467 -161
- package/lib/lib/expandTailwindAtRules.js +160 -133
- package/lib/lib/findAtConfigPath.js +46 -0
- package/lib/lib/generateRules.js +553 -200
- package/lib/lib/getModuleDependencies.js +88 -37
- package/lib/lib/load-config.js +42 -0
- package/lib/lib/normalizeTailwindDirectives.js +46 -33
- package/lib/lib/offsets.js +306 -0
- package/lib/lib/partitionApplyAtRules.js +58 -0
- package/lib/lib/regex.js +74 -0
- package/lib/lib/remap-bitfield.js +89 -0
- package/lib/lib/resolveDefaultsAtRules.js +98 -58
- package/lib/lib/setupContextUtils.js +773 -321
- package/lib/lib/setupTrackingContext.js +70 -75
- package/lib/lib/sharedState.js +78 -10
- package/lib/lib/substituteScreenAtRules.js +14 -10
- package/lib/oxide/cli/build/deps.js +89 -0
- package/lib/oxide/cli/build/index.js +53 -0
- package/lib/oxide/cli/build/plugin.js +375 -0
- package/lib/oxide/cli/build/utils.js +87 -0
- package/lib/oxide/cli/build/watching.js +179 -0
- package/lib/oxide/cli/help/index.js +72 -0
- package/lib/oxide/cli/index.js +214 -0
- package/lib/oxide/cli/init/index.js +52 -0
- package/lib/oxide/cli.js +5 -0
- package/lib/oxide/postcss-plugin.js +2 -0
- package/lib/plugin.js +98 -0
- package/{nesting → lib/postcss-plugins/nesting}/README.md +2 -2
- package/lib/postcss-plugins/nesting/index.js +21 -0
- package/lib/postcss-plugins/nesting/plugin.js +89 -0
- package/lib/processTailwindFeatures.js +39 -26
- package/lib/public/colors.js +272 -246
- package/lib/public/create-plugin.js +9 -5
- package/lib/public/default-config.js +10 -6
- package/lib/public/default-theme.js +10 -6
- package/lib/public/load-config.js +12 -0
- package/lib/public/resolve-config.js +11 -6
- package/lib/util/applyImportantSelector.js +36 -0
- package/lib/util/bigSign.js +6 -1
- package/lib/util/buildMediaQuery.js +13 -6
- package/lib/util/cloneDeep.js +9 -6
- package/lib/util/cloneNodes.js +23 -3
- package/lib/util/color.js +70 -38
- package/lib/util/colorNames.js +752 -0
- package/lib/util/configurePlugins.js +7 -2
- package/lib/util/createPlugin.js +8 -6
- package/lib/util/createUtilityPlugin.js +16 -16
- package/lib/util/dataTypes.js +173 -108
- package/lib/util/defaults.js +14 -3
- package/lib/util/escapeClassName.js +13 -8
- package/lib/util/escapeCommas.js +7 -2
- package/lib/util/flattenColorPalette.js +11 -12
- package/lib/util/formatVariantSelector.js +228 -151
- package/lib/util/getAllConfigs.js +33 -12
- package/lib/util/hashConfig.js +9 -4
- package/lib/util/isKeyframeRule.js +7 -2
- package/lib/util/isPlainObject.js +7 -2
- package/lib/util/{isValidArbitraryValue.js → isSyntacticallyValidPropertyValue.js} +25 -15
- package/lib/util/log.js +27 -13
- package/lib/util/nameClass.js +27 -10
- package/lib/util/negateValue.js +25 -8
- package/lib/util/normalizeConfig.js +139 -65
- package/lib/util/normalizeScreens.js +131 -11
- package/lib/util/parseAnimationValue.js +44 -40
- package/lib/util/parseBoxShadowValue.js +34 -23
- package/lib/util/parseDependency.js +39 -55
- package/lib/util/parseGlob.js +36 -0
- package/lib/util/parseObjectStyles.js +15 -10
- package/lib/util/pluginUtils.js +159 -69
- package/lib/util/prefixSelector.js +30 -12
- package/lib/util/pseudoElements.js +229 -0
- package/lib/util/removeAlphaVariables.js +31 -0
- package/lib/util/resolveConfig.js +97 -75
- package/lib/util/resolveConfigPath.js +30 -12
- package/lib/util/responsive.js +11 -6
- package/lib/util/splitAtTopLevelOnly.js +51 -0
- package/lib/util/tap.js +6 -1
- package/lib/util/toColorValue.js +7 -3
- package/lib/util/toPath.js +26 -3
- package/lib/util/transformThemeValue.js +40 -30
- package/lib/util/validateConfig.js +37 -0
- package/lib/util/validateFormalSyntax.js +26 -0
- package/lib/util/withAlphaVariable.js +27 -15
- package/loadConfig.d.ts +4 -0
- package/loadConfig.js +2 -0
- package/nesting/index.js +2 -12
- package/package.json +66 -57
- package/peers/index.js +75964 -55560
- package/plugin.d.ts +11 -0
- package/plugin.js +2 -1
- package/resolveConfig.d.ts +12 -0
- package/resolveConfig.js +2 -1
- package/scripts/generate-types.js +105 -0
- package/scripts/release-channel.js +18 -0
- package/scripts/release-notes.js +21 -0
- package/scripts/swap-engines.js +40 -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 +444 -0
- package/src/cli/build/utils.js +76 -0
- package/src/cli/build/watching.js +229 -0
- package/src/cli/help/index.js +70 -0
- package/src/cli/index.js +216 -0
- package/src/cli/init/index.js +79 -0
- package/src/cli-peer-dependencies.js +7 -1
- package/src/cli.js +4 -765
- package/src/corePluginList.js +1 -1
- package/src/corePlugins.js +786 -306
- package/src/css/preflight.css +10 -8
- package/src/featureFlags.js +21 -5
- package/src/index.js +1 -34
- package/src/lib/cacheInvalidation.js +52 -0
- package/src/lib/collapseAdjacentRules.js +21 -2
- package/src/lib/collapseDuplicateDeclarations.js +66 -1
- package/src/lib/content.js +208 -0
- package/src/lib/defaultExtractor.js +217 -0
- package/src/lib/detectNesting.js +9 -1
- package/src/lib/evaluateTailwindFunctions.js +79 -8
- package/src/lib/expandApplyAtRules.js +515 -153
- package/src/lib/expandTailwindAtRules.js +115 -86
- package/src/lib/findAtConfigPath.js +48 -0
- package/src/lib/generateRules.js +545 -147
- package/src/lib/getModuleDependencies.js +70 -30
- package/src/lib/load-config.ts +31 -0
- package/src/lib/normalizeTailwindDirectives.js +7 -1
- package/src/lib/offsets.js +373 -0
- package/src/lib/partitionApplyAtRules.js +52 -0
- package/src/lib/regex.js +74 -0
- package/src/lib/remap-bitfield.js +82 -0
- package/src/lib/resolveDefaultsAtRules.js +59 -17
- package/src/lib/setupContextUtils.js +701 -175
- package/src/lib/setupTrackingContext.js +51 -62
- package/src/lib/sharedState.js +58 -7
- package/src/oxide/cli/build/deps.ts +91 -0
- package/src/oxide/cli/build/index.ts +47 -0
- package/src/oxide/cli/build/plugin.ts +442 -0
- package/src/oxide/cli/build/utils.ts +74 -0
- package/src/oxide/cli/build/watching.ts +225 -0
- package/src/oxide/cli/help/index.ts +69 -0
- package/src/oxide/cli/index.ts +204 -0
- package/src/oxide/cli/init/index.ts +59 -0
- package/src/oxide/cli.ts +1 -0
- package/src/oxide/postcss-plugin.ts +1 -0
- package/src/plugin.js +107 -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 +12 -2
- package/src/public/colors.js +22 -0
- package/src/public/default-config.js +1 -1
- package/src/public/default-theme.js +2 -2
- package/src/public/load-config.js +2 -0
- package/src/util/applyImportantSelector.js +27 -0
- package/src/util/buildMediaQuery.js +5 -3
- package/src/util/cloneNodes.js +19 -2
- package/src/util/color.js +44 -12
- package/src/util/colorNames.js +150 -0
- package/src/util/dataTypes.js +51 -16
- package/src/util/defaults.js +6 -0
- package/src/util/formatVariantSelector.js +264 -144
- package/src/util/getAllConfigs.js +21 -2
- package/src/util/{isValidArbitraryValue.js → isSyntacticallyValidPropertyValue.js} +1 -1
- package/src/util/log.js +11 -7
- package/src/util/nameClass.js +4 -0
- package/src/util/negateValue.js +11 -3
- package/src/util/normalizeConfig.js +57 -5
- package/src/util/normalizeScreens.js +105 -7
- 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 +123 -24
- package/src/util/prefixSelector.js +30 -10
- package/src/util/pseudoElements.js +170 -0
- package/src/util/removeAlphaVariables.js +24 -0
- package/src/util/resolveConfig.js +74 -26
- package/src/util/resolveConfigPath.js +12 -1
- package/src/util/splitAtTopLevelOnly.js +52 -0
- package/src/util/toPath.js +23 -1
- package/src/util/transformThemeValue.js +13 -3
- package/src/util/validateConfig.js +26 -0
- package/src/util/validateFormalSyntax.js +34 -0
- package/src/util/withAlphaVariable.js +1 -1
- package/stubs/.gitignore +1 -0
- package/stubs/.prettierrc.json +6 -0
- package/stubs/{defaultConfig.stub.js → config.full.js} +206 -166
- package/stubs/postcss.config.js +6 -0
- package/stubs/tailwind.config.cjs +2 -0
- package/stubs/tailwind.config.js +2 -0
- package/stubs/tailwind.config.ts +3 -0
- package/types/config.d.ts +368 -0
- package/types/generated/.gitkeep +0 -0
- package/types/generated/colors.d.ts +298 -0
- package/types/generated/corePluginList.d.ts +1 -0
- package/types/generated/default-theme.d.ts +371 -0
- package/types/index.d.ts +7 -0
- package/CHANGELOG.md +0 -1843
- package/lib/constants.js +0 -37
- package/lib/lib/setupWatchingContext.js +0 -288
- package/nesting/plugin.js +0 -41
- package/scripts/install-integrations.js +0 -27
- package/scripts/rebuildFixtures.js +0 -68
- package/src/constants.js +0 -17
- package/src/lib/setupWatchingContext.js +0 -311
- /package/stubs/{simpleConfig.stub.js → config.simple.js} +0 -0
- /package/stubs/{defaultPostCssConfig.stub.js → postcss.config.cjs} +0 -0
package/src/lib/generateRules.js
CHANGED
|
@@ -3,18 +3,27 @@ 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, getMatchingTypes } from '../util/pluginUtils'
|
|
7
7
|
import log from '../util/log'
|
|
8
|
-
import
|
|
8
|
+
import * as sharedState from './sharedState'
|
|
9
|
+
import {
|
|
10
|
+
formatVariantSelector,
|
|
11
|
+
finalizeSelector,
|
|
12
|
+
eliminateIrrelevantSelectors,
|
|
13
|
+
} from '../util/formatVariantSelector'
|
|
9
14
|
import { asClass } from '../util/nameClass'
|
|
10
15
|
import { normalize } from '../util/dataTypes'
|
|
11
|
-
import
|
|
16
|
+
import { isValidVariantFormatString, parseVariant } from './setupContextUtils'
|
|
17
|
+
import isValidArbitraryValue from '../util/isSyntacticallyValidPropertyValue'
|
|
18
|
+
import { splitAtTopLevelOnly } from '../util/splitAtTopLevelOnly.js'
|
|
19
|
+
import { flagEnabled } from '../featureFlags'
|
|
20
|
+
import { applyImportantSelector } from '../util/applyImportantSelector'
|
|
12
21
|
|
|
13
22
|
let classNameParser = selectorParser((selectors) => {
|
|
14
23
|
return selectors.first.filter(({ type }) => type === 'class').pop().value
|
|
15
24
|
})
|
|
16
25
|
|
|
17
|
-
function getClassNameFromSelector(selector) {
|
|
26
|
+
export function getClassNameFromSelector(selector) {
|
|
18
27
|
return classNameParser.transformSync(selector)
|
|
19
28
|
}
|
|
20
29
|
|
|
@@ -25,33 +34,49 @@ function getClassNameFromSelector(selector) {
|
|
|
25
34
|
// Example with dynamic classes:
|
|
26
35
|
// ['grid-cols', '[[linename],1fr,auto]']
|
|
27
36
|
// ['grid', 'cols-[[linename],1fr,auto]']
|
|
28
|
-
function* candidatePermutations(candidate
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
+
function* candidatePermutations(candidate) {
|
|
38
|
+
let lastIndex = Infinity
|
|
39
|
+
|
|
40
|
+
while (lastIndex >= 0) {
|
|
41
|
+
let dashIdx
|
|
42
|
+
let wasSlash = false
|
|
43
|
+
|
|
44
|
+
if (lastIndex === Infinity && candidate.endsWith(']')) {
|
|
45
|
+
let bracketIdx = candidate.indexOf('[')
|
|
46
|
+
|
|
47
|
+
// If character before `[` isn't a dash or a slash, this isn't a dynamic class
|
|
48
|
+
// eg. string[]
|
|
49
|
+
if (candidate[bracketIdx - 1] === '-') {
|
|
50
|
+
dashIdx = bracketIdx - 1
|
|
51
|
+
} else if (candidate[bracketIdx - 1] === '/') {
|
|
52
|
+
dashIdx = bracketIdx - 1
|
|
53
|
+
wasSlash = true
|
|
54
|
+
} else {
|
|
55
|
+
dashIdx = -1
|
|
56
|
+
}
|
|
57
|
+
} else if (lastIndex === Infinity && candidate.includes('/')) {
|
|
58
|
+
dashIdx = candidate.lastIndexOf('/')
|
|
59
|
+
wasSlash = true
|
|
60
|
+
} else {
|
|
61
|
+
dashIdx = candidate.lastIndexOf('-', lastIndex)
|
|
62
|
+
}
|
|
37
63
|
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
} else {
|
|
42
|
-
dashIdx = candidate.lastIndexOf('-', lastIndex)
|
|
43
|
-
}
|
|
64
|
+
if (dashIdx < 0) {
|
|
65
|
+
break
|
|
66
|
+
}
|
|
44
67
|
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
}
|
|
68
|
+
let prefix = candidate.slice(0, dashIdx)
|
|
69
|
+
let modifier = candidate.slice(wasSlash ? dashIdx : dashIdx + 1)
|
|
48
70
|
|
|
49
|
-
|
|
50
|
-
let modifier = candidate.slice(dashIdx + 1)
|
|
71
|
+
lastIndex = dashIdx - 1
|
|
51
72
|
|
|
52
|
-
|
|
73
|
+
// TODO: This feels a bit hacky
|
|
74
|
+
if (prefix === '' || modifier === '/') {
|
|
75
|
+
continue
|
|
76
|
+
}
|
|
53
77
|
|
|
54
|
-
|
|
78
|
+
yield [prefix, modifier]
|
|
79
|
+
}
|
|
55
80
|
}
|
|
56
81
|
|
|
57
82
|
function applyPrefix(matches, context) {
|
|
@@ -63,9 +88,23 @@ function applyPrefix(matches, context) {
|
|
|
63
88
|
let [meta] = match
|
|
64
89
|
if (meta.options.respectPrefix) {
|
|
65
90
|
let container = postcss.root({ nodes: [match[1].clone()] })
|
|
91
|
+
let classCandidate = match[1].raws.tailwind.classCandidate
|
|
92
|
+
|
|
66
93
|
container.walkRules((r) => {
|
|
67
|
-
|
|
94
|
+
// If this is a negative utility with a dash *before* the prefix we
|
|
95
|
+
// have to ensure that the generated selector matches the candidate
|
|
96
|
+
|
|
97
|
+
// Not doing this will cause `-tw-top-1` to generate the class `.tw--top-1`
|
|
98
|
+
// The disconnect between candidate <-> class can cause @apply to hard crash.
|
|
99
|
+
let shouldPrependNegative = classCandidate.startsWith('-')
|
|
100
|
+
|
|
101
|
+
r.selector = prefixSelector(
|
|
102
|
+
context.tailwindConfig.prefix,
|
|
103
|
+
r.selector,
|
|
104
|
+
shouldPrependNegative
|
|
105
|
+
)
|
|
68
106
|
})
|
|
107
|
+
|
|
69
108
|
match[1] = container.nodes[0]
|
|
70
109
|
}
|
|
71
110
|
}
|
|
@@ -73,20 +112,32 @@ function applyPrefix(matches, context) {
|
|
|
73
112
|
return matches
|
|
74
113
|
}
|
|
75
114
|
|
|
76
|
-
function applyImportant(matches) {
|
|
115
|
+
function applyImportant(matches, classCandidate) {
|
|
77
116
|
if (matches.length === 0) {
|
|
78
117
|
return matches
|
|
79
118
|
}
|
|
119
|
+
|
|
80
120
|
let result = []
|
|
81
121
|
|
|
82
122
|
for (let [meta, rule] of matches) {
|
|
83
123
|
let container = postcss.root({ nodes: [rule.clone()] })
|
|
124
|
+
|
|
84
125
|
container.walkRules((r) => {
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
126
|
+
let ast = selectorParser().astSync(r.selector)
|
|
127
|
+
|
|
128
|
+
// Remove extraneous selectors that do not include the base candidate
|
|
129
|
+
ast.each((sel) => eliminateIrrelevantSelectors(sel, classCandidate))
|
|
130
|
+
|
|
131
|
+
// Update all instances of the base candidate to include the important marker
|
|
132
|
+
updateAllClasses(ast, (className) =>
|
|
133
|
+
className === classCandidate ? `!${className}` : className
|
|
134
|
+
)
|
|
135
|
+
|
|
136
|
+
r.selector = ast.toString()
|
|
137
|
+
|
|
88
138
|
r.walkDecls((d) => (d.important = true))
|
|
89
139
|
})
|
|
140
|
+
|
|
90
141
|
result.push([{ ...meta, important: true }, container.nodes[0]])
|
|
91
142
|
}
|
|
92
143
|
|
|
@@ -107,22 +158,100 @@ function applyVariant(variant, matches, context) {
|
|
|
107
158
|
return matches
|
|
108
159
|
}
|
|
109
160
|
|
|
161
|
+
/** @type {{modifier: string | null, value: string | null}} */
|
|
162
|
+
let args = { modifier: null, value: sharedState.NONE }
|
|
163
|
+
|
|
164
|
+
// Retrieve "modifier"
|
|
165
|
+
{
|
|
166
|
+
let [baseVariant, ...modifiers] = splitAtTopLevelOnly(variant, '/')
|
|
167
|
+
|
|
168
|
+
// This is a hack to support variants with `/` in them, like `ar-1/10/20:text-red-500`
|
|
169
|
+
// In this case 1/10 is a value but /20 is a modifier
|
|
170
|
+
if (modifiers.length > 1) {
|
|
171
|
+
baseVariant = baseVariant + '/' + modifiers.slice(0, -1).join('/')
|
|
172
|
+
modifiers = modifiers.slice(-1)
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
if (modifiers.length && !context.variantMap.has(variant)) {
|
|
176
|
+
variant = baseVariant
|
|
177
|
+
args.modifier = modifiers[0]
|
|
178
|
+
|
|
179
|
+
if (!flagEnabled(context.tailwindConfig, 'generalizedModifiers')) {
|
|
180
|
+
return []
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
// Retrieve "arbitrary value"
|
|
186
|
+
if (variant.endsWith(']') && !variant.startsWith('[')) {
|
|
187
|
+
// We either have:
|
|
188
|
+
// @[200px]
|
|
189
|
+
// group-[:hover]
|
|
190
|
+
//
|
|
191
|
+
// But we don't want:
|
|
192
|
+
// @-[200px] (`-` is incorrect)
|
|
193
|
+
// group[:hover] (`-` is missing)
|
|
194
|
+
let match = /(.)(-?)\[(.*)\]/g.exec(variant)
|
|
195
|
+
if (match) {
|
|
196
|
+
let [, char, seperator, value] = match
|
|
197
|
+
// @-[200px] case
|
|
198
|
+
if (char === '@' && seperator === '-') return []
|
|
199
|
+
// group[:hover] case
|
|
200
|
+
if (char !== '@' && seperator === '') return []
|
|
201
|
+
|
|
202
|
+
variant = variant.replace(`${seperator}[${value}]`, '')
|
|
203
|
+
args.value = value
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
// Register arbitrary variants
|
|
208
|
+
if (isArbitraryValue(variant) && !context.variantMap.has(variant)) {
|
|
209
|
+
let sort = context.offsets.recordVariant(variant)
|
|
210
|
+
|
|
211
|
+
let selector = normalize(variant.slice(1, -1))
|
|
212
|
+
let selectors = splitAtTopLevelOnly(selector, ',')
|
|
213
|
+
|
|
214
|
+
// We do not support multiple selectors for arbitrary variants
|
|
215
|
+
if (selectors.length > 1) {
|
|
216
|
+
return []
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
if (!selectors.every(isValidVariantFormatString)) {
|
|
220
|
+
return []
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
let records = selectors.map((sel, idx) => [
|
|
224
|
+
context.offsets.applyParallelOffset(sort, idx),
|
|
225
|
+
parseVariant(sel.trim()),
|
|
226
|
+
])
|
|
227
|
+
|
|
228
|
+
context.variantMap.set(variant, records)
|
|
229
|
+
}
|
|
230
|
+
|
|
110
231
|
if (context.variantMap.has(variant)) {
|
|
111
|
-
let
|
|
232
|
+
let isArbitraryVariant = isArbitraryValue(variant)
|
|
233
|
+
let variantFunctionTuples = context.variantMap.get(variant).slice()
|
|
112
234
|
let result = []
|
|
113
235
|
|
|
114
236
|
for (let [meta, rule] of matches) {
|
|
237
|
+
// Don't generate variants for user css
|
|
238
|
+
if (meta.layer === 'user') {
|
|
239
|
+
continue
|
|
240
|
+
}
|
|
241
|
+
|
|
115
242
|
let container = postcss.root({ nodes: [rule.clone()] })
|
|
116
243
|
|
|
117
|
-
for (let [variantSort, variantFunction] of variantFunctionTuples) {
|
|
118
|
-
let clone = container.clone()
|
|
244
|
+
for (let [variantSort, variantFunction, containerFromArray] of variantFunctionTuples) {
|
|
245
|
+
let clone = (containerFromArray ?? container).clone()
|
|
119
246
|
let collectedFormats = []
|
|
120
247
|
|
|
121
|
-
let originals = new Map()
|
|
122
|
-
|
|
123
248
|
function prepareBackup() {
|
|
124
|
-
|
|
125
|
-
clone.
|
|
249
|
+
// Already prepared, chicken out
|
|
250
|
+
if (clone.raws.neededBackup) {
|
|
251
|
+
return
|
|
252
|
+
}
|
|
253
|
+
clone.raws.neededBackup = true
|
|
254
|
+
clone.walkRules((rule) => (rule.raws.originalSelector = rule.selector))
|
|
126
255
|
}
|
|
127
256
|
|
|
128
257
|
function modifySelectors(modifierFunction) {
|
|
@@ -162,25 +291,54 @@ function applyVariant(variant, matches, context) {
|
|
|
162
291
|
clone.append(wrapper)
|
|
163
292
|
},
|
|
164
293
|
format(selectorFormat) {
|
|
165
|
-
collectedFormats.push(
|
|
294
|
+
collectedFormats.push({
|
|
295
|
+
format: selectorFormat,
|
|
296
|
+
isArbitraryVariant,
|
|
297
|
+
})
|
|
166
298
|
},
|
|
299
|
+
args,
|
|
167
300
|
})
|
|
168
301
|
|
|
302
|
+
// It can happen that a list of format strings is returned from within the function. In that
|
|
303
|
+
// case, we have to process them as well. We can use the existing `variantSort`.
|
|
304
|
+
if (Array.isArray(ruleWithVariant)) {
|
|
305
|
+
for (let [idx, variantFunction] of ruleWithVariant.entries()) {
|
|
306
|
+
// This is a little bit scary since we are pushing to an array of items that we are
|
|
307
|
+
// currently looping over. However, you can also think of it like a processing queue
|
|
308
|
+
// where you keep handling jobs until everything is done and each job can queue more
|
|
309
|
+
// jobs if needed.
|
|
310
|
+
variantFunctionTuples.push([
|
|
311
|
+
context.offsets.applyParallelOffset(variantSort, idx),
|
|
312
|
+
variantFunction,
|
|
313
|
+
|
|
314
|
+
// If the clone has been modified we have to pass that back
|
|
315
|
+
// though so each rule can use the modified container
|
|
316
|
+
clone.clone(),
|
|
317
|
+
])
|
|
318
|
+
}
|
|
319
|
+
continue
|
|
320
|
+
}
|
|
321
|
+
|
|
169
322
|
if (typeof ruleWithVariant === 'string') {
|
|
170
|
-
collectedFormats.push(
|
|
323
|
+
collectedFormats.push({
|
|
324
|
+
format: ruleWithVariant,
|
|
325
|
+
isArbitraryVariant,
|
|
326
|
+
})
|
|
171
327
|
}
|
|
172
328
|
|
|
173
329
|
if (ruleWithVariant === null) {
|
|
174
330
|
continue
|
|
175
331
|
}
|
|
176
332
|
|
|
177
|
-
// We
|
|
333
|
+
// We had to backup selectors, therefore we assume that somebody touched
|
|
178
334
|
// `container` or `modifySelectors`. Let's see if they did, so that we
|
|
179
335
|
// can restore the selectors, and collect the format strings.
|
|
180
|
-
if (
|
|
336
|
+
if (clone.raws.neededBackup) {
|
|
337
|
+
delete clone.raws.neededBackup
|
|
181
338
|
clone.walkRules((rule) => {
|
|
182
|
-
|
|
183
|
-
|
|
339
|
+
let before = rule.raws.originalSelector
|
|
340
|
+
if (!before) return
|
|
341
|
+
delete rule.raws.originalSelector
|
|
184
342
|
if (before === rule.selector) return // No mutation happened
|
|
185
343
|
|
|
186
344
|
let modified = rule.selector
|
|
@@ -206,15 +364,28 @@ function applyVariant(variant, matches, context) {
|
|
|
206
364
|
// modified (by plugin): .foo .foo\\:markdown > p
|
|
207
365
|
// rebuiltBase (internal): .foo\\:markdown > p
|
|
208
366
|
// format: .foo &
|
|
209
|
-
collectedFormats.push(
|
|
367
|
+
collectedFormats.push({
|
|
368
|
+
format: modified.replace(rebuiltBase, '&'),
|
|
369
|
+
isArbitraryVariant,
|
|
370
|
+
})
|
|
210
371
|
rule.selector = before
|
|
211
372
|
})
|
|
212
373
|
}
|
|
213
374
|
|
|
375
|
+
// This tracks the originating layer for the variant
|
|
376
|
+
// For example:
|
|
377
|
+
// .sm:underline {} is a variant of something in the utilities layer
|
|
378
|
+
// .sm:container {} is a variant of the container component
|
|
379
|
+
clone.nodes[0].raws.tailwind = { ...clone.nodes[0].raws.tailwind, parentLayer: meta.layer }
|
|
380
|
+
|
|
214
381
|
let withOffset = [
|
|
215
382
|
{
|
|
216
383
|
...meta,
|
|
217
|
-
sort:
|
|
384
|
+
sort: context.offsets.applyVariantOffset(
|
|
385
|
+
meta.sort,
|
|
386
|
+
variantSort,
|
|
387
|
+
Object.assign(args, context.variantOptions.get(variant))
|
|
388
|
+
),
|
|
218
389
|
collectedFormats: (meta.collectedFormats ?? []).concat(collectedFormats),
|
|
219
390
|
},
|
|
220
391
|
clone.nodes[0],
|
|
@@ -248,6 +419,61 @@ function parseRules(rule, cache, options = {}) {
|
|
|
248
419
|
return [cache.get(rule), options]
|
|
249
420
|
}
|
|
250
421
|
|
|
422
|
+
const IS_VALID_PROPERTY_NAME = /^[a-z_-]/
|
|
423
|
+
|
|
424
|
+
function isValidPropName(name) {
|
|
425
|
+
return IS_VALID_PROPERTY_NAME.test(name)
|
|
426
|
+
}
|
|
427
|
+
|
|
428
|
+
/**
|
|
429
|
+
* @param {string} declaration
|
|
430
|
+
* @returns {boolean}
|
|
431
|
+
*/
|
|
432
|
+
function looksLikeUri(declaration) {
|
|
433
|
+
// Quick bailout for obvious non-urls
|
|
434
|
+
// This doesn't support schemes that don't use a leading // but that's unlikely to be a problem
|
|
435
|
+
if (!declaration.includes('://')) {
|
|
436
|
+
return false
|
|
437
|
+
}
|
|
438
|
+
|
|
439
|
+
try {
|
|
440
|
+
const url = new URL(declaration)
|
|
441
|
+
return url.scheme !== '' && url.host !== ''
|
|
442
|
+
} catch (err) {
|
|
443
|
+
// Definitely not a valid url
|
|
444
|
+
return false
|
|
445
|
+
}
|
|
446
|
+
}
|
|
447
|
+
|
|
448
|
+
function isParsableNode(node) {
|
|
449
|
+
let isParsable = true
|
|
450
|
+
|
|
451
|
+
node.walkDecls((decl) => {
|
|
452
|
+
if (!isParsableCssValue(decl.prop, decl.value)) {
|
|
453
|
+
isParsable = false
|
|
454
|
+
return false
|
|
455
|
+
}
|
|
456
|
+
})
|
|
457
|
+
|
|
458
|
+
return isParsable
|
|
459
|
+
}
|
|
460
|
+
|
|
461
|
+
function isParsableCssValue(property, value) {
|
|
462
|
+
// We don't want to to treat [https://example.com] as a custom property
|
|
463
|
+
// Even though, according to the CSS grammar, it's a totally valid CSS declaration
|
|
464
|
+
// So we short-circuit here by checking if the custom property looks like a url
|
|
465
|
+
if (looksLikeUri(`${property}:${value}`)) {
|
|
466
|
+
return false
|
|
467
|
+
}
|
|
468
|
+
|
|
469
|
+
try {
|
|
470
|
+
postcss.parse(`a{${property}:${value}}`).toResult()
|
|
471
|
+
return true
|
|
472
|
+
} catch (err) {
|
|
473
|
+
return false
|
|
474
|
+
}
|
|
475
|
+
}
|
|
476
|
+
|
|
251
477
|
function extractArbitraryProperty(classCandidate, context) {
|
|
252
478
|
let [, property, value] = classCandidate.match(/^\[([a-zA-Z0-9-_]+):(\S+)\]$/) ?? []
|
|
253
479
|
|
|
@@ -255,15 +481,25 @@ function extractArbitraryProperty(classCandidate, context) {
|
|
|
255
481
|
return null
|
|
256
482
|
}
|
|
257
483
|
|
|
484
|
+
if (!isValidPropName(property)) {
|
|
485
|
+
return null
|
|
486
|
+
}
|
|
487
|
+
|
|
488
|
+
if (!isValidArbitraryValue(value)) {
|
|
489
|
+
return null
|
|
490
|
+
}
|
|
491
|
+
|
|
258
492
|
let normalized = normalize(value)
|
|
259
493
|
|
|
260
|
-
if (!
|
|
494
|
+
if (!isParsableCssValue(property, normalized)) {
|
|
261
495
|
return null
|
|
262
496
|
}
|
|
263
497
|
|
|
498
|
+
let sort = context.offsets.arbitraryProperty()
|
|
499
|
+
|
|
264
500
|
return [
|
|
265
501
|
[
|
|
266
|
-
{ sort
|
|
502
|
+
{ sort, layer: 'utilities' },
|
|
267
503
|
() => ({
|
|
268
504
|
[asClass(classCandidate)]: {
|
|
269
505
|
[property]: normalized,
|
|
@@ -290,7 +526,11 @@ function* resolveMatchedPlugins(classCandidate, context) {
|
|
|
290
526
|
const twConfigPrefix = context.tailwindConfig.prefix
|
|
291
527
|
|
|
292
528
|
const twConfigPrefixLen = twConfigPrefix.length
|
|
293
|
-
|
|
529
|
+
|
|
530
|
+
const hasMatchingPrefix =
|
|
531
|
+
candidatePrefix.startsWith(twConfigPrefix) || candidatePrefix.startsWith(`-${twConfigPrefix}`)
|
|
532
|
+
|
|
533
|
+
if (candidatePrefix[twConfigPrefixLen] === '-' && hasMatchingPrefix) {
|
|
294
534
|
negative = true
|
|
295
535
|
candidatePrefix = twConfigPrefix + candidatePrefix.slice(twConfigPrefixLen + 1)
|
|
296
536
|
}
|
|
@@ -302,16 +542,31 @@ function* resolveMatchedPlugins(classCandidate, context) {
|
|
|
302
542
|
for (let [prefix, modifier] of candidatePermutations(candidatePrefix)) {
|
|
303
543
|
if (context.candidateRuleMap.has(prefix)) {
|
|
304
544
|
yield [context.candidateRuleMap.get(prefix), negative ? `-${modifier}` : modifier]
|
|
305
|
-
return
|
|
306
545
|
}
|
|
307
546
|
}
|
|
308
547
|
}
|
|
309
548
|
|
|
310
549
|
function splitWithSeparator(input, separator) {
|
|
311
|
-
|
|
550
|
+
if (input === sharedState.NOT_ON_DEMAND) {
|
|
551
|
+
return [sharedState.NOT_ON_DEMAND]
|
|
552
|
+
}
|
|
553
|
+
|
|
554
|
+
return splitAtTopLevelOnly(input, separator)
|
|
555
|
+
}
|
|
556
|
+
|
|
557
|
+
function* recordCandidates(matches, classCandidate) {
|
|
558
|
+
for (const match of matches) {
|
|
559
|
+
match[1].raws.tailwind = {
|
|
560
|
+
...match[1].raws.tailwind,
|
|
561
|
+
classCandidate,
|
|
562
|
+
preserveSource: match[0].options?.preserveSource ?? false,
|
|
563
|
+
}
|
|
564
|
+
|
|
565
|
+
yield match
|
|
566
|
+
}
|
|
312
567
|
}
|
|
313
568
|
|
|
314
|
-
function* resolveMatches(candidate, context) {
|
|
569
|
+
function* resolveMatches(candidate, context, original = candidate) {
|
|
315
570
|
let separator = context.tailwindConfig.separator
|
|
316
571
|
let [classCandidate, ...variants] = splitWithSeparator(candidate, separator).reverse()
|
|
317
572
|
let important = false
|
|
@@ -321,6 +576,15 @@ function* resolveMatches(candidate, context) {
|
|
|
321
576
|
classCandidate = classCandidate.slice(1)
|
|
322
577
|
}
|
|
323
578
|
|
|
579
|
+
if (flagEnabled(context.tailwindConfig, 'variantGrouping')) {
|
|
580
|
+
if (classCandidate.startsWith('(') && classCandidate.endsWith(')')) {
|
|
581
|
+
let base = variants.slice().reverse().join(separator)
|
|
582
|
+
for (let part of splitAtTopLevelOnly(classCandidate.slice(1, -1), ',')) {
|
|
583
|
+
yield* resolveMatches(base + separator + part, context, original)
|
|
584
|
+
}
|
|
585
|
+
}
|
|
586
|
+
}
|
|
587
|
+
|
|
324
588
|
// TODO: Reintroduce this in ways that doesn't break on false positives
|
|
325
589
|
// function sortAgainst(toSort, against) {
|
|
326
590
|
// return toSort.slice().sort((a, z) => {
|
|
@@ -361,71 +625,144 @@ function* resolveMatches(candidate, context) {
|
|
|
361
625
|
}
|
|
362
626
|
|
|
363
627
|
if (matchesPerPlugin.length > 0) {
|
|
364
|
-
|
|
628
|
+
let matchingTypes = Array.from(
|
|
629
|
+
getMatchingTypes(
|
|
630
|
+
sort.options?.types ?? [],
|
|
631
|
+
modifier,
|
|
632
|
+
sort.options ?? {},
|
|
633
|
+
context.tailwindConfig
|
|
634
|
+
)
|
|
635
|
+
).map(([_, type]) => type)
|
|
636
|
+
|
|
637
|
+
if (matchingTypes.length > 0) {
|
|
638
|
+
typesByMatches.set(matchesPerPlugin, matchingTypes)
|
|
639
|
+
}
|
|
640
|
+
|
|
365
641
|
matches.push(matchesPerPlugin)
|
|
366
642
|
}
|
|
367
643
|
}
|
|
368
644
|
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
for (let otherGroup of typesPerPlugin) {
|
|
380
|
-
if (pluginTypes === otherGroup) continue
|
|
645
|
+
if (isArbitraryValue(modifier)) {
|
|
646
|
+
if (matches.length > 1) {
|
|
647
|
+
// Partition plugins in 2 categories so that we can start searching in the plugins that
|
|
648
|
+
// don't have `any` as a type first.
|
|
649
|
+
let [withAny, withoutAny] = matches.reduce(
|
|
650
|
+
(group, plugin) => {
|
|
651
|
+
let hasAnyType = plugin.some(([{ options }]) =>
|
|
652
|
+
options.types.some(({ type }) => type === 'any')
|
|
653
|
+
)
|
|
381
654
|
|
|
382
|
-
if (
|
|
383
|
-
|
|
384
|
-
|
|
655
|
+
if (hasAnyType) {
|
|
656
|
+
group[0].push(plugin)
|
|
657
|
+
} else {
|
|
658
|
+
group[1].push(plugin)
|
|
385
659
|
}
|
|
660
|
+
return group
|
|
661
|
+
},
|
|
662
|
+
[[], []]
|
|
663
|
+
)
|
|
664
|
+
|
|
665
|
+
function findFallback(matches) {
|
|
666
|
+
// If only a single plugin matches, let's take that one
|
|
667
|
+
if (matches.length === 1) {
|
|
668
|
+
return matches[0]
|
|
386
669
|
}
|
|
387
670
|
|
|
388
|
-
|
|
671
|
+
// Otherwise, find the plugin that creates a valid rule given the arbitrary value, and
|
|
672
|
+
// also has the correct type which preferOnConflicts the plugin in case of clashes.
|
|
673
|
+
return matches.find((rules) => {
|
|
674
|
+
let matchingTypes = typesByMatches.get(rules)
|
|
675
|
+
return rules.some(([{ options }, rule]) => {
|
|
676
|
+
if (!isParsableNode(rule)) {
|
|
677
|
+
return false
|
|
678
|
+
}
|
|
679
|
+
|
|
680
|
+
return options.types.some(
|
|
681
|
+
({ type, preferOnConflict }) => matchingTypes.includes(type) && preferOnConflict
|
|
682
|
+
)
|
|
683
|
+
})
|
|
684
|
+
})
|
|
389
685
|
}
|
|
390
|
-
}
|
|
391
686
|
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
.flat()
|
|
399
|
-
.map((rule) =>
|
|
400
|
-
rule
|
|
401
|
-
.toString()
|
|
402
|
-
.split('\n')
|
|
403
|
-
.slice(1, -1) // Remove selector and closing '}'
|
|
404
|
-
.map((line) => line.trim())
|
|
405
|
-
.map((x) => ` ${x}`) // Re-indent
|
|
406
|
-
.join('\n')
|
|
407
|
-
)
|
|
408
|
-
.join('\n\n')
|
|
687
|
+
// Try to find a fallback plugin, because we already know that multiple plugins matched for
|
|
688
|
+
// the given arbitrary value.
|
|
689
|
+
let fallback = findFallback(withoutAny) ?? findFallback(withAny)
|
|
690
|
+
if (fallback) {
|
|
691
|
+
matches = [fallback]
|
|
692
|
+
}
|
|
409
693
|
|
|
410
|
-
|
|
411
|
-
|
|
694
|
+
// We couldn't find a fallback plugin which means that there are now multiple plugins that
|
|
695
|
+
// generated css for the current candidate. This means that the result is ambiguous and this
|
|
696
|
+
// should not happen. We won't generate anything right now, so let's report this to the user
|
|
697
|
+
// by logging some options about what they can do.
|
|
698
|
+
else {
|
|
699
|
+
let typesPerPlugin = matches.map(
|
|
700
|
+
(match) => new Set([...(typesByMatches.get(match) ?? [])])
|
|
701
|
+
)
|
|
702
|
+
|
|
703
|
+
// Remove duplicates, so that we can detect proper unique types for each plugin.
|
|
704
|
+
for (let pluginTypes of typesPerPlugin) {
|
|
705
|
+
for (let type of pluginTypes) {
|
|
706
|
+
let removeFromOwnGroup = false
|
|
707
|
+
|
|
708
|
+
for (let otherGroup of typesPerPlugin) {
|
|
709
|
+
if (pluginTypes === otherGroup) continue
|
|
710
|
+
|
|
711
|
+
if (otherGroup.has(type)) {
|
|
712
|
+
otherGroup.delete(type)
|
|
713
|
+
removeFromOwnGroup = true
|
|
714
|
+
}
|
|
715
|
+
}
|
|
716
|
+
|
|
717
|
+
if (removeFromOwnGroup) pluginTypes.delete(type)
|
|
718
|
+
}
|
|
719
|
+
}
|
|
720
|
+
|
|
721
|
+
let messages = []
|
|
722
|
+
|
|
723
|
+
for (let [idx, group] of typesPerPlugin.entries()) {
|
|
724
|
+
for (let type of group) {
|
|
725
|
+
let rules = matches[idx]
|
|
726
|
+
.map(([, rule]) => rule)
|
|
727
|
+
.flat()
|
|
728
|
+
.map((rule) =>
|
|
729
|
+
rule
|
|
730
|
+
.toString()
|
|
731
|
+
.split('\n')
|
|
732
|
+
.slice(1, -1) // Remove selector and closing '}'
|
|
733
|
+
.map((line) => line.trim())
|
|
734
|
+
.map((x) => ` ${x}`) // Re-indent
|
|
735
|
+
.join('\n')
|
|
736
|
+
)
|
|
737
|
+
.join('\n\n')
|
|
738
|
+
|
|
739
|
+
messages.push(
|
|
740
|
+
` Use \`${candidate.replace('[', `[${type}:`)}\` for \`${rules.trim()}\``
|
|
741
|
+
)
|
|
742
|
+
break
|
|
743
|
+
}
|
|
744
|
+
}
|
|
745
|
+
|
|
746
|
+
log.warn([
|
|
747
|
+
`The class \`${candidate}\` is ambiguous and matches multiple utilities.`,
|
|
748
|
+
...messages,
|
|
749
|
+
`If this is content and not a class, replace it with \`${candidate
|
|
750
|
+
.replace('[', '[')
|
|
751
|
+
.replace(']', ']')}\` to silence this warning.`,
|
|
752
|
+
])
|
|
753
|
+
continue
|
|
412
754
|
}
|
|
413
755
|
}
|
|
414
756
|
|
|
415
|
-
|
|
416
|
-
`The class \`${candidate}\` is ambiguous and matches multiple utilities.`,
|
|
417
|
-
...messages,
|
|
418
|
-
`If this is content and not a class, replace it with \`${candidate
|
|
419
|
-
.replace('[', '[')
|
|
420
|
-
.replace(']', ']')}\` to silence this warning.`,
|
|
421
|
-
])
|
|
422
|
-
continue
|
|
757
|
+
matches = matches.map((list) => list.filter((match) => isParsableNode(match[1])))
|
|
423
758
|
}
|
|
424
759
|
|
|
425
|
-
matches =
|
|
760
|
+
matches = matches.flat()
|
|
761
|
+
matches = Array.from(recordCandidates(matches, classCandidate))
|
|
762
|
+
matches = applyPrefix(matches, context)
|
|
426
763
|
|
|
427
764
|
if (important) {
|
|
428
|
-
matches = applyImportant(matches,
|
|
765
|
+
matches = applyImportant(matches, classCandidate)
|
|
429
766
|
}
|
|
430
767
|
|
|
431
768
|
for (let variant of variants) {
|
|
@@ -433,20 +770,16 @@ function* resolveMatches(candidate, context) {
|
|
|
433
770
|
}
|
|
434
771
|
|
|
435
772
|
for (let match of matches) {
|
|
773
|
+
match[1].raws.tailwind = { ...match[1].raws.tailwind, candidate }
|
|
774
|
+
|
|
436
775
|
// Apply final format selector
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
selector: rule.selector,
|
|
445
|
-
candidate,
|
|
446
|
-
context,
|
|
447
|
-
})
|
|
448
|
-
})
|
|
449
|
-
match[1] = container.nodes[0]
|
|
776
|
+
match = applyFinalFormat(match, { context, candidate, original })
|
|
777
|
+
|
|
778
|
+
// Skip rules with invalid selectors
|
|
779
|
+
// This will cause the candidate to be added to the "not class"
|
|
780
|
+
// cache skipping it entirely for future builds
|
|
781
|
+
if (match === null) {
|
|
782
|
+
continue
|
|
450
783
|
}
|
|
451
784
|
|
|
452
785
|
yield match
|
|
@@ -454,20 +787,105 @@ function* resolveMatches(candidate, context) {
|
|
|
454
787
|
}
|
|
455
788
|
}
|
|
456
789
|
|
|
790
|
+
function applyFinalFormat(match, { context, candidate, original }) {
|
|
791
|
+
if (!match[0].collectedFormats) {
|
|
792
|
+
return match
|
|
793
|
+
}
|
|
794
|
+
|
|
795
|
+
let isValid = true
|
|
796
|
+
let finalFormat
|
|
797
|
+
|
|
798
|
+
try {
|
|
799
|
+
finalFormat = formatVariantSelector(match[0].collectedFormats, {
|
|
800
|
+
context,
|
|
801
|
+
candidate,
|
|
802
|
+
})
|
|
803
|
+
} catch {
|
|
804
|
+
// The format selector we produced is invalid
|
|
805
|
+
// This could be because:
|
|
806
|
+
// - A bug exists
|
|
807
|
+
// - A plugin introduced an invalid variant selector (ex: `addVariant('foo', '&;foo')`)
|
|
808
|
+
// - The user used an invalid arbitrary variant (ex: `[&;foo]:underline`)
|
|
809
|
+
// Either way the build will fail because of this
|
|
810
|
+
// We would rather that the build pass "silently" given that this could
|
|
811
|
+
// happen because of picking up invalid things when scanning content
|
|
812
|
+
// So we'll throw out the candidate instead
|
|
813
|
+
|
|
814
|
+
return null
|
|
815
|
+
}
|
|
816
|
+
|
|
817
|
+
let container = postcss.root({ nodes: [match[1].clone()] })
|
|
818
|
+
|
|
819
|
+
container.walkRules((rule) => {
|
|
820
|
+
if (inKeyframes(rule)) {
|
|
821
|
+
return
|
|
822
|
+
}
|
|
823
|
+
|
|
824
|
+
try {
|
|
825
|
+
rule.selector = finalizeSelector(rule.selector, finalFormat, {
|
|
826
|
+
candidate: original,
|
|
827
|
+
context,
|
|
828
|
+
})
|
|
829
|
+
} catch {
|
|
830
|
+
// If this selector is invalid we also want to skip it
|
|
831
|
+
// But it's likely that being invalid here means there's a bug in a plugin rather than too loosely matching content
|
|
832
|
+
isValid = false
|
|
833
|
+
return false
|
|
834
|
+
}
|
|
835
|
+
})
|
|
836
|
+
|
|
837
|
+
if (!isValid) {
|
|
838
|
+
return null
|
|
839
|
+
}
|
|
840
|
+
|
|
841
|
+
match[1] = container.nodes[0]
|
|
842
|
+
|
|
843
|
+
return match
|
|
844
|
+
}
|
|
845
|
+
|
|
457
846
|
function inKeyframes(rule) {
|
|
458
847
|
return rule.parent && rule.parent.type === 'atrule' && rule.parent.name === 'keyframes'
|
|
459
848
|
}
|
|
460
849
|
|
|
850
|
+
function getImportantStrategy(important) {
|
|
851
|
+
if (important === true) {
|
|
852
|
+
return (rule) => {
|
|
853
|
+
if (inKeyframes(rule)) {
|
|
854
|
+
return
|
|
855
|
+
}
|
|
856
|
+
|
|
857
|
+
rule.walkDecls((d) => {
|
|
858
|
+
if (d.parent.type === 'rule' && !inKeyframes(d.parent)) {
|
|
859
|
+
d.important = true
|
|
860
|
+
}
|
|
861
|
+
})
|
|
862
|
+
}
|
|
863
|
+
}
|
|
864
|
+
|
|
865
|
+
if (typeof important === 'string') {
|
|
866
|
+
return (rule) => {
|
|
867
|
+
if (inKeyframes(rule)) {
|
|
868
|
+
return
|
|
869
|
+
}
|
|
870
|
+
|
|
871
|
+
rule.selectors = rule.selectors.map((selector) => {
|
|
872
|
+
return applyImportantSelector(selector, important)
|
|
873
|
+
})
|
|
874
|
+
}
|
|
875
|
+
}
|
|
876
|
+
}
|
|
877
|
+
|
|
461
878
|
function generateRules(candidates, context) {
|
|
462
879
|
let allRules = []
|
|
880
|
+
let strategy = getImportantStrategy(context.tailwindConfig.important)
|
|
463
881
|
|
|
464
882
|
for (let candidate of candidates) {
|
|
465
883
|
if (context.notClassCache.has(candidate)) {
|
|
466
884
|
continue
|
|
467
885
|
}
|
|
468
886
|
|
|
469
|
-
if (context.
|
|
470
|
-
allRules.
|
|
887
|
+
if (context.candidateRuleCache.has(candidate)) {
|
|
888
|
+
allRules = allRules.concat(Array.from(context.candidateRuleCache.get(candidate)))
|
|
471
889
|
continue
|
|
472
890
|
}
|
|
473
891
|
|
|
@@ -479,47 +897,27 @@ function generateRules(candidates, context) {
|
|
|
479
897
|
}
|
|
480
898
|
|
|
481
899
|
context.classCache.set(candidate, matches)
|
|
482
|
-
allRules.push(matches)
|
|
483
|
-
}
|
|
484
900
|
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
if (important === true) {
|
|
488
|
-
return (rule) => {
|
|
489
|
-
rule.walkDecls((d) => {
|
|
490
|
-
if (d.parent.type === 'rule' && !inKeyframes(d.parent)) {
|
|
491
|
-
d.important = true
|
|
492
|
-
}
|
|
493
|
-
})
|
|
494
|
-
}
|
|
495
|
-
}
|
|
901
|
+
let rules = context.candidateRuleCache.get(candidate) ?? new Set()
|
|
902
|
+
context.candidateRuleCache.set(candidate, rules)
|
|
496
903
|
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
rule.selectors = rule.selectors.map((selector) => {
|
|
500
|
-
return `${important} ${selector}`
|
|
501
|
-
})
|
|
502
|
-
}
|
|
503
|
-
}
|
|
504
|
-
})(context.tailwindConfig.important)
|
|
904
|
+
for (const match of matches) {
|
|
905
|
+
let [{ sort, options }, rule] = match
|
|
505
906
|
|
|
506
|
-
|
|
507
|
-
if (options.respectImportant) {
|
|
508
|
-
if (strategy) {
|
|
907
|
+
if (options.respectImportant && strategy) {
|
|
509
908
|
let container = postcss.root({ nodes: [rule.clone()] })
|
|
510
|
-
container.walkRules(
|
|
511
|
-
if (inKeyframes(r)) {
|
|
512
|
-
return
|
|
513
|
-
}
|
|
514
|
-
|
|
515
|
-
strategy(r)
|
|
516
|
-
})
|
|
909
|
+
container.walkRules(strategy)
|
|
517
910
|
rule = container.nodes[0]
|
|
518
911
|
}
|
|
912
|
+
|
|
913
|
+
let newEntry = [sort, rule]
|
|
914
|
+
rules.add(newEntry)
|
|
915
|
+
context.ruleCache.add(newEntry)
|
|
916
|
+
allRules.push(newEntry)
|
|
519
917
|
}
|
|
918
|
+
}
|
|
520
919
|
|
|
521
|
-
|
|
522
|
-
})
|
|
920
|
+
return allRules
|
|
523
921
|
}
|
|
524
922
|
|
|
525
923
|
function isArbitraryValue(input) {
|