tailwindcss 0.0.0-insiders.bd4dddd → 0.0.0-insiders.bda8421
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +1 -2
- package/README.md +13 -10
- package/colors.d.ts +3 -0
- package/defaultConfig.d.ts +3 -0
- package/defaultTheme.d.ts +4 -0
- package/lib/cli/build/deps.js +62 -0
- package/lib/cli/build/index.js +54 -0
- package/lib/cli/build/plugin.js +371 -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 +1 -741
- package/lib/corePluginList.js +17 -4
- package/lib/corePlugins.js +2503 -1839
- package/lib/css/preflight.css +32 -13
- package/lib/featureFlags.js +45 -28
- package/lib/index.js +1 -29
- package/lib/lib/cacheInvalidation.js +92 -0
- package/lib/lib/collapseAdjacentRules.js +30 -10
- package/lib/lib/collapseDuplicateDeclarations.js +13 -8
- package/lib/lib/content.js +214 -0
- package/lib/lib/defaultExtractor.js +240 -31
- package/lib/lib/evaluateTailwindFunctions.js +117 -50
- package/lib/lib/expandApplyAtRules.js +466 -230
- package/lib/lib/expandTailwindAtRules.js +156 -135
- package/lib/lib/findAtConfigPath.js +46 -0
- package/lib/lib/generateRules.js +554 -200
- package/lib/lib/getModuleDependencies.js +88 -37
- package/lib/lib/load-config.js +57 -0
- package/lib/lib/normalizeTailwindDirectives.js +39 -33
- package/lib/lib/offsets.js +355 -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 +97 -69
- package/lib/lib/setupContextUtils.js +796 -341
- package/lib/lib/setupTrackingContext.js +66 -71
- package/lib/lib/sharedState.js +45 -14
- package/lib/lib/substituteScreenAtRules.js +14 -10
- package/lib/plugin.js +48 -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 +37 -27
- 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 +44 -4
- package/lib/util/color.js +70 -53
- 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 +295 -113
- package/lib/util/defaults.js +12 -7
- 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 +235 -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 +8 -3
- package/lib/util/{isValidArbitraryValue.js → isSyntacticallyValidPropertyValue.js} +25 -15
- package/lib/util/log.js +25 -15
- package/lib/util/nameClass.js +27 -10
- package/lib/util/negateValue.js +25 -8
- package/lib/util/normalizeConfig.js +127 -82
- package/lib/util/normalizeScreens.js +128 -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 +172 -69
- package/lib/util/prefixSelector.js +30 -12
- package/lib/util/pseudoElements.js +212 -0
- package/lib/util/removeAlphaVariables.js +31 -0
- package/lib/util/resolveConfig.js +101 -87
- 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 +22 -4
- 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/lib/value-parser/LICENSE +22 -0
- package/lib/value-parser/README.md +3 -0
- package/lib/value-parser/index.d.js +2 -0
- package/lib/value-parser/index.js +22 -0
- package/lib/value-parser/parse.js +259 -0
- package/lib/value-parser/stringify.js +38 -0
- package/lib/value-parser/unit.js +86 -0
- package/lib/value-parser/walk.js +16 -0
- package/loadConfig.d.ts +4 -0
- package/loadConfig.js +2 -0
- package/nesting/index.d.ts +4 -0
- package/nesting/index.js +2 -12
- package/package.json +60 -55
- package/peers/index.js +75503 -55095
- package/plugin.d.ts +11 -0
- package/resolveConfig.d.ts +31 -0
- package/scripts/generate-types.js +104 -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 +437 -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 +1 -811
- package/src/corePluginList.js +1 -1
- package/src/corePlugins.js +849 -243
- package/src/css/preflight.css +32 -13
- package/src/featureFlags.js +13 -9
- package/src/index.js +1 -28
- package/src/lib/cacheInvalidation.js +52 -0
- package/src/lib/collapseAdjacentRules.js +21 -2
- package/src/lib/content.js +249 -0
- package/src/lib/defaultExtractor.js +215 -33
- package/src/lib/evaluateTailwindFunctions.js +82 -8
- package/src/lib/expandApplyAtRules.js +512 -224
- package/src/lib/expandTailwindAtRules.js +102 -87
- package/src/lib/findAtConfigPath.js +48 -0
- package/src/lib/generateRules.js +541 -146
- package/src/lib/getModuleDependencies.js +70 -30
- package/src/lib/load-config.ts +44 -0
- package/src/lib/normalizeTailwindDirectives.js +1 -0
- package/src/lib/offsets.js +432 -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 +55 -30
- package/src/lib/setupContextUtils.js +720 -190
- package/src/lib/setupTrackingContext.js +47 -61
- package/src/lib/sharedState.js +15 -4
- 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 +11 -4
- 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 +40 -2
- package/src/util/color.js +39 -20
- package/src/util/colorNames.js +150 -0
- package/src/util/dataTypes.js +184 -26
- package/src/util/formatVariantSelector.js +272 -144
- package/src/util/getAllConfigs.js +21 -2
- package/src/util/isPlainObject.js +1 -1
- 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 +45 -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 +139 -24
- package/src/util/prefixSelector.js +31 -10
- package/src/util/pseudoElements.js +171 -0
- package/src/util/removeAlphaVariables.js +24 -0
- package/src/util/resolveConfig.js +70 -32
- package/src/util/resolveConfigPath.js +12 -1
- package/src/util/splitAtTopLevelOnly.js +52 -0
- package/src/util/toPath.js +1 -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/src/value-parser/LICENSE +22 -0
- package/src/value-parser/README.md +3 -0
- package/src/value-parser/index.d.ts +177 -0
- package/src/value-parser/index.js +28 -0
- package/src/value-parser/parse.js +303 -0
- package/src/value-parser/stringify.js +41 -0
- package/src/value-parser/unit.js +118 -0
- package/src/value-parser/walk.js +18 -0
- package/stubs/.gitignore +1 -0
- package/stubs/.prettierrc.json +6 -0
- package/stubs/{defaultConfig.stub.js → config.full.js} +290 -178
- 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 +376 -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 +397 -0
- package/types/index.d.ts +11 -0
- package/CHANGELOG.md +0 -1959
- package/lib/constants.js +0 -37
- package/lib/lib/detectNesting.js +0 -34
- 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/detectNesting.js +0 -39
- /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, INTERNAL_FEATURES } 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,42 @@ 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
|
|
|
122
|
+
function isInKeyframes(rule) {
|
|
123
|
+
return rule.parent && rule.parent.type === 'atrule' && rule.parent.name === 'keyframes'
|
|
124
|
+
}
|
|
125
|
+
|
|
82
126
|
for (let [meta, rule] of matches) {
|
|
83
127
|
let container = postcss.root({ nodes: [rule.clone()] })
|
|
128
|
+
|
|
84
129
|
container.walkRules((r) => {
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
130
|
+
// Declarations inside keyframes cannot be marked as important
|
|
131
|
+
// They will be ignored by the browser
|
|
132
|
+
if (isInKeyframes(r)) {
|
|
133
|
+
return
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
let ast = selectorParser().astSync(r.selector)
|
|
137
|
+
|
|
138
|
+
// Remove extraneous selectors that do not include the base candidate
|
|
139
|
+
ast.each((sel) => eliminateIrrelevantSelectors(sel, classCandidate))
|
|
140
|
+
|
|
141
|
+
// Update all instances of the base candidate to include the important marker
|
|
142
|
+
updateAllClasses(ast, (className) =>
|
|
143
|
+
className === classCandidate ? `!${className}` : className
|
|
144
|
+
)
|
|
145
|
+
|
|
146
|
+
r.selector = ast.toString()
|
|
147
|
+
|
|
88
148
|
r.walkDecls((d) => (d.important = true))
|
|
89
149
|
})
|
|
150
|
+
|
|
90
151
|
result.push([{ ...meta, important: true }, container.nodes[0]])
|
|
91
152
|
}
|
|
92
153
|
|
|
@@ -107,10 +168,88 @@ function applyVariant(variant, matches, context) {
|
|
|
107
168
|
return matches
|
|
108
169
|
}
|
|
109
170
|
|
|
171
|
+
/** @type {{modifier: string | null, value: string | null}} */
|
|
172
|
+
let args = { modifier: null, value: sharedState.NONE }
|
|
173
|
+
|
|
174
|
+
// Retrieve "modifier"
|
|
175
|
+
{
|
|
176
|
+
let [baseVariant, ...modifiers] = splitAtTopLevelOnly(variant, '/')
|
|
177
|
+
|
|
178
|
+
// This is a hack to support variants with `/` in them, like `ar-1/10/20:text-red-500`
|
|
179
|
+
// In this case 1/10 is a value but /20 is a modifier
|
|
180
|
+
if (modifiers.length > 1) {
|
|
181
|
+
baseVariant = baseVariant + '/' + modifiers.slice(0, -1).join('/')
|
|
182
|
+
modifiers = modifiers.slice(-1)
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
if (modifiers.length && !context.variantMap.has(variant)) {
|
|
186
|
+
variant = baseVariant
|
|
187
|
+
args.modifier = modifiers[0]
|
|
188
|
+
|
|
189
|
+
if (!flagEnabled(context.tailwindConfig, 'generalizedModifiers')) {
|
|
190
|
+
return []
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
// Retrieve "arbitrary value"
|
|
196
|
+
if (variant.endsWith(']') && !variant.startsWith('[')) {
|
|
197
|
+
// We either have:
|
|
198
|
+
// @[200px]
|
|
199
|
+
// group-[:hover]
|
|
200
|
+
//
|
|
201
|
+
// But we don't want:
|
|
202
|
+
// @-[200px] (`-` is incorrect)
|
|
203
|
+
// group[:hover] (`-` is missing)
|
|
204
|
+
let match = /(.)(-?)\[(.*)\]/g.exec(variant)
|
|
205
|
+
if (match) {
|
|
206
|
+
let [, char, separator, value] = match
|
|
207
|
+
// @-[200px] case
|
|
208
|
+
if (char === '@' && separator === '-') return []
|
|
209
|
+
// group[:hover] case
|
|
210
|
+
if (char !== '@' && separator === '') return []
|
|
211
|
+
|
|
212
|
+
variant = variant.replace(`${separator}[${value}]`, '')
|
|
213
|
+
args.value = value
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
// Register arbitrary variants
|
|
218
|
+
if (isArbitraryValue(variant) && !context.variantMap.has(variant)) {
|
|
219
|
+
let sort = context.offsets.recordVariant(variant)
|
|
220
|
+
|
|
221
|
+
let selector = normalize(variant.slice(1, -1))
|
|
222
|
+
let selectors = splitAtTopLevelOnly(selector, ',')
|
|
223
|
+
|
|
224
|
+
// We do not support multiple selectors for arbitrary variants
|
|
225
|
+
if (selectors.length > 1) {
|
|
226
|
+
return []
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
if (!selectors.every(isValidVariantFormatString)) {
|
|
230
|
+
return []
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
let records = selectors.map((sel, idx) => [
|
|
234
|
+
context.offsets.applyParallelOffset(sort, idx),
|
|
235
|
+
parseVariant(sel.trim()),
|
|
236
|
+
])
|
|
237
|
+
|
|
238
|
+
context.variantMap.set(variant, records)
|
|
239
|
+
}
|
|
240
|
+
|
|
110
241
|
if (context.variantMap.has(variant)) {
|
|
111
|
-
let
|
|
242
|
+
let isArbitraryVariant = isArbitraryValue(variant)
|
|
243
|
+
let internalFeatures = context.variantOptions.get(variant)?.[INTERNAL_FEATURES] ?? {}
|
|
244
|
+
let variantFunctionTuples = context.variantMap.get(variant).slice()
|
|
112
245
|
let result = []
|
|
113
246
|
|
|
247
|
+
let respectPrefix = (() => {
|
|
248
|
+
if (isArbitraryVariant) return false
|
|
249
|
+
if (internalFeatures.respectPrefix === false) return false
|
|
250
|
+
return true
|
|
251
|
+
})()
|
|
252
|
+
|
|
114
253
|
for (let [meta, rule] of matches) {
|
|
115
254
|
// Don't generate variants for user css
|
|
116
255
|
if (meta.layer === 'user') {
|
|
@@ -119,15 +258,17 @@ function applyVariant(variant, matches, context) {
|
|
|
119
258
|
|
|
120
259
|
let container = postcss.root({ nodes: [rule.clone()] })
|
|
121
260
|
|
|
122
|
-
for (let [variantSort, variantFunction] of variantFunctionTuples) {
|
|
123
|
-
let clone = container.clone()
|
|
261
|
+
for (let [variantSort, variantFunction, containerFromArray] of variantFunctionTuples) {
|
|
262
|
+
let clone = (containerFromArray ?? container).clone()
|
|
124
263
|
let collectedFormats = []
|
|
125
264
|
|
|
126
|
-
let originals = new Map()
|
|
127
|
-
|
|
128
265
|
function prepareBackup() {
|
|
129
|
-
|
|
130
|
-
clone.
|
|
266
|
+
// Already prepared, chicken out
|
|
267
|
+
if (clone.raws.neededBackup) {
|
|
268
|
+
return
|
|
269
|
+
}
|
|
270
|
+
clone.raws.neededBackup = true
|
|
271
|
+
clone.walkRules((rule) => (rule.raws.originalSelector = rule.selector))
|
|
131
272
|
}
|
|
132
273
|
|
|
133
274
|
function modifySelectors(modifierFunction) {
|
|
@@ -167,25 +308,54 @@ function applyVariant(variant, matches, context) {
|
|
|
167
308
|
clone.append(wrapper)
|
|
168
309
|
},
|
|
169
310
|
format(selectorFormat) {
|
|
170
|
-
collectedFormats.push(
|
|
311
|
+
collectedFormats.push({
|
|
312
|
+
format: selectorFormat,
|
|
313
|
+
respectPrefix,
|
|
314
|
+
})
|
|
171
315
|
},
|
|
316
|
+
args,
|
|
172
317
|
})
|
|
173
318
|
|
|
319
|
+
// It can happen that a list of format strings is returned from within the function. In that
|
|
320
|
+
// case, we have to process them as well. We can use the existing `variantSort`.
|
|
321
|
+
if (Array.isArray(ruleWithVariant)) {
|
|
322
|
+
for (let [idx, variantFunction] of ruleWithVariant.entries()) {
|
|
323
|
+
// This is a little bit scary since we are pushing to an array of items that we are
|
|
324
|
+
// currently looping over. However, you can also think of it like a processing queue
|
|
325
|
+
// where you keep handling jobs until everything is done and each job can queue more
|
|
326
|
+
// jobs if needed.
|
|
327
|
+
variantFunctionTuples.push([
|
|
328
|
+
context.offsets.applyParallelOffset(variantSort, idx),
|
|
329
|
+
variantFunction,
|
|
330
|
+
|
|
331
|
+
// If the clone has been modified we have to pass that back
|
|
332
|
+
// though so each rule can use the modified container
|
|
333
|
+
clone.clone(),
|
|
334
|
+
])
|
|
335
|
+
}
|
|
336
|
+
continue
|
|
337
|
+
}
|
|
338
|
+
|
|
174
339
|
if (typeof ruleWithVariant === 'string') {
|
|
175
|
-
collectedFormats.push(
|
|
340
|
+
collectedFormats.push({
|
|
341
|
+
format: ruleWithVariant,
|
|
342
|
+
respectPrefix,
|
|
343
|
+
})
|
|
176
344
|
}
|
|
177
345
|
|
|
178
346
|
if (ruleWithVariant === null) {
|
|
179
347
|
continue
|
|
180
348
|
}
|
|
181
349
|
|
|
182
|
-
// We
|
|
350
|
+
// We had to backup selectors, therefore we assume that somebody touched
|
|
183
351
|
// `container` or `modifySelectors`. Let's see if they did, so that we
|
|
184
352
|
// can restore the selectors, and collect the format strings.
|
|
185
|
-
if (
|
|
353
|
+
if (clone.raws.neededBackup) {
|
|
354
|
+
delete clone.raws.neededBackup
|
|
186
355
|
clone.walkRules((rule) => {
|
|
187
|
-
|
|
188
|
-
|
|
356
|
+
let before = rule.raws.originalSelector
|
|
357
|
+
if (!before) return
|
|
358
|
+
delete rule.raws.originalSelector
|
|
189
359
|
if (before === rule.selector) return // No mutation happened
|
|
190
360
|
|
|
191
361
|
let modified = rule.selector
|
|
@@ -211,15 +381,28 @@ function applyVariant(variant, matches, context) {
|
|
|
211
381
|
// modified (by plugin): .foo .foo\\:markdown > p
|
|
212
382
|
// rebuiltBase (internal): .foo\\:markdown > p
|
|
213
383
|
// format: .foo &
|
|
214
|
-
collectedFormats.push(
|
|
384
|
+
collectedFormats.push({
|
|
385
|
+
format: modified.replace(rebuiltBase, '&'),
|
|
386
|
+
respectPrefix,
|
|
387
|
+
})
|
|
215
388
|
rule.selector = before
|
|
216
389
|
})
|
|
217
390
|
}
|
|
218
391
|
|
|
392
|
+
// This tracks the originating layer for the variant
|
|
393
|
+
// For example:
|
|
394
|
+
// .sm:underline {} is a variant of something in the utilities layer
|
|
395
|
+
// .sm:container {} is a variant of the container component
|
|
396
|
+
clone.nodes[0].raws.tailwind = { ...clone.nodes[0].raws.tailwind, parentLayer: meta.layer }
|
|
397
|
+
|
|
219
398
|
let withOffset = [
|
|
220
399
|
{
|
|
221
400
|
...meta,
|
|
222
|
-
sort:
|
|
401
|
+
sort: context.offsets.applyVariantOffset(
|
|
402
|
+
meta.sort,
|
|
403
|
+
variantSort,
|
|
404
|
+
Object.assign(args, context.variantOptions.get(variant))
|
|
405
|
+
),
|
|
223
406
|
collectedFormats: (meta.collectedFormats ?? []).concat(collectedFormats),
|
|
224
407
|
},
|
|
225
408
|
clone.nodes[0],
|
|
@@ -259,7 +442,47 @@ function isValidPropName(name) {
|
|
|
259
442
|
return IS_VALID_PROPERTY_NAME.test(name)
|
|
260
443
|
}
|
|
261
444
|
|
|
445
|
+
/**
|
|
446
|
+
* @param {string} declaration
|
|
447
|
+
* @returns {boolean}
|
|
448
|
+
*/
|
|
449
|
+
function looksLikeUri(declaration) {
|
|
450
|
+
// Quick bailout for obvious non-urls
|
|
451
|
+
// This doesn't support schemes that don't use a leading // but that's unlikely to be a problem
|
|
452
|
+
if (!declaration.includes('://')) {
|
|
453
|
+
return false
|
|
454
|
+
}
|
|
455
|
+
|
|
456
|
+
try {
|
|
457
|
+
const url = new URL(declaration)
|
|
458
|
+
return url.scheme !== '' && url.host !== ''
|
|
459
|
+
} catch (err) {
|
|
460
|
+
// Definitely not a valid url
|
|
461
|
+
return false
|
|
462
|
+
}
|
|
463
|
+
}
|
|
464
|
+
|
|
465
|
+
function isParsableNode(node) {
|
|
466
|
+
let isParsable = true
|
|
467
|
+
|
|
468
|
+
node.walkDecls((decl) => {
|
|
469
|
+
if (!isParsableCssValue(decl.prop, decl.value)) {
|
|
470
|
+
isParsable = false
|
|
471
|
+
return false
|
|
472
|
+
}
|
|
473
|
+
})
|
|
474
|
+
|
|
475
|
+
return isParsable
|
|
476
|
+
}
|
|
477
|
+
|
|
262
478
|
function isParsableCssValue(property, value) {
|
|
479
|
+
// We don't want to to treat [https://example.com] as a custom property
|
|
480
|
+
// Even though, according to the CSS grammar, it's a totally valid CSS declaration
|
|
481
|
+
// So we short-circuit here by checking if the custom property looks like a url
|
|
482
|
+
if (looksLikeUri(`${property}:${value}`)) {
|
|
483
|
+
return false
|
|
484
|
+
}
|
|
485
|
+
|
|
263
486
|
try {
|
|
264
487
|
postcss.parse(`a{${property}:${value}}`).toResult()
|
|
265
488
|
return true
|
|
@@ -283,15 +506,17 @@ function extractArbitraryProperty(classCandidate, context) {
|
|
|
283
506
|
return null
|
|
284
507
|
}
|
|
285
508
|
|
|
286
|
-
let normalized = normalize(value)
|
|
509
|
+
let normalized = normalize(value, { property })
|
|
287
510
|
|
|
288
511
|
if (!isParsableCssValue(property, normalized)) {
|
|
289
512
|
return null
|
|
290
513
|
}
|
|
291
514
|
|
|
515
|
+
let sort = context.offsets.arbitraryProperty(classCandidate)
|
|
516
|
+
|
|
292
517
|
return [
|
|
293
518
|
[
|
|
294
|
-
{ sort
|
|
519
|
+
{ sort, layer: 'utilities' },
|
|
295
520
|
() => ({
|
|
296
521
|
[asClass(classCandidate)]: {
|
|
297
522
|
[property]: normalized,
|
|
@@ -318,7 +543,11 @@ function* resolveMatchedPlugins(classCandidate, context) {
|
|
|
318
543
|
const twConfigPrefix = context.tailwindConfig.prefix
|
|
319
544
|
|
|
320
545
|
const twConfigPrefixLen = twConfigPrefix.length
|
|
321
|
-
|
|
546
|
+
|
|
547
|
+
const hasMatchingPrefix =
|
|
548
|
+
candidatePrefix.startsWith(twConfigPrefix) || candidatePrefix.startsWith(`-${twConfigPrefix}`)
|
|
549
|
+
|
|
550
|
+
if (candidatePrefix[twConfigPrefixLen] === '-' && hasMatchingPrefix) {
|
|
322
551
|
negative = true
|
|
323
552
|
candidatePrefix = twConfigPrefix + candidatePrefix.slice(twConfigPrefixLen + 1)
|
|
324
553
|
}
|
|
@@ -335,7 +564,23 @@ function* resolveMatchedPlugins(classCandidate, context) {
|
|
|
335
564
|
}
|
|
336
565
|
|
|
337
566
|
function splitWithSeparator(input, separator) {
|
|
338
|
-
|
|
567
|
+
if (input === sharedState.NOT_ON_DEMAND) {
|
|
568
|
+
return [sharedState.NOT_ON_DEMAND]
|
|
569
|
+
}
|
|
570
|
+
|
|
571
|
+
return splitAtTopLevelOnly(input, separator)
|
|
572
|
+
}
|
|
573
|
+
|
|
574
|
+
function* recordCandidates(matches, classCandidate) {
|
|
575
|
+
for (const match of matches) {
|
|
576
|
+
match[1].raws.tailwind = {
|
|
577
|
+
...match[1].raws.tailwind,
|
|
578
|
+
classCandidate,
|
|
579
|
+
preserveSource: match[0].options?.preserveSource ?? false,
|
|
580
|
+
}
|
|
581
|
+
|
|
582
|
+
yield match
|
|
583
|
+
}
|
|
339
584
|
}
|
|
340
585
|
|
|
341
586
|
function* resolveMatches(candidate, context) {
|
|
@@ -388,71 +633,144 @@ function* resolveMatches(candidate, context) {
|
|
|
388
633
|
}
|
|
389
634
|
|
|
390
635
|
if (matchesPerPlugin.length > 0) {
|
|
391
|
-
|
|
636
|
+
let matchingTypes = Array.from(
|
|
637
|
+
getMatchingTypes(
|
|
638
|
+
sort.options?.types ?? [],
|
|
639
|
+
modifier,
|
|
640
|
+
sort.options ?? {},
|
|
641
|
+
context.tailwindConfig
|
|
642
|
+
)
|
|
643
|
+
).map(([_, type]) => type)
|
|
644
|
+
|
|
645
|
+
if (matchingTypes.length > 0) {
|
|
646
|
+
typesByMatches.set(matchesPerPlugin, matchingTypes)
|
|
647
|
+
}
|
|
648
|
+
|
|
392
649
|
matches.push(matchesPerPlugin)
|
|
393
650
|
}
|
|
394
651
|
}
|
|
395
652
|
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
for (let otherGroup of typesPerPlugin) {
|
|
407
|
-
if (pluginTypes === otherGroup) continue
|
|
653
|
+
if (isArbitraryValue(modifier)) {
|
|
654
|
+
if (matches.length > 1) {
|
|
655
|
+
// Partition plugins in 2 categories so that we can start searching in the plugins that
|
|
656
|
+
// don't have `any` as a type first.
|
|
657
|
+
let [withAny, withoutAny] = matches.reduce(
|
|
658
|
+
(group, plugin) => {
|
|
659
|
+
let hasAnyType = plugin.some(([{ options }]) =>
|
|
660
|
+
options.types.some(({ type }) => type === 'any')
|
|
661
|
+
)
|
|
408
662
|
|
|
409
|
-
if (
|
|
410
|
-
|
|
411
|
-
|
|
663
|
+
if (hasAnyType) {
|
|
664
|
+
group[0].push(plugin)
|
|
665
|
+
} else {
|
|
666
|
+
group[1].push(plugin)
|
|
412
667
|
}
|
|
668
|
+
return group
|
|
669
|
+
},
|
|
670
|
+
[[], []]
|
|
671
|
+
)
|
|
672
|
+
|
|
673
|
+
function findFallback(matches) {
|
|
674
|
+
// If only a single plugin matches, let's take that one
|
|
675
|
+
if (matches.length === 1) {
|
|
676
|
+
return matches[0]
|
|
413
677
|
}
|
|
414
678
|
|
|
415
|
-
|
|
679
|
+
// Otherwise, find the plugin that creates a valid rule given the arbitrary value, and
|
|
680
|
+
// also has the correct type which preferOnConflicts the plugin in case of clashes.
|
|
681
|
+
return matches.find((rules) => {
|
|
682
|
+
let matchingTypes = typesByMatches.get(rules)
|
|
683
|
+
return rules.some(([{ options }, rule]) => {
|
|
684
|
+
if (!isParsableNode(rule)) {
|
|
685
|
+
return false
|
|
686
|
+
}
|
|
687
|
+
|
|
688
|
+
return options.types.some(
|
|
689
|
+
({ type, preferOnConflict }) => matchingTypes.includes(type) && preferOnConflict
|
|
690
|
+
)
|
|
691
|
+
})
|
|
692
|
+
})
|
|
416
693
|
}
|
|
417
|
-
}
|
|
418
694
|
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
695
|
+
// Try to find a fallback plugin, because we already know that multiple plugins matched for
|
|
696
|
+
// the given arbitrary value.
|
|
697
|
+
let fallback = findFallback(withoutAny) ?? findFallback(withAny)
|
|
698
|
+
if (fallback) {
|
|
699
|
+
matches = [fallback]
|
|
700
|
+
}
|
|
701
|
+
|
|
702
|
+
// We couldn't find a fallback plugin which means that there are now multiple plugins that
|
|
703
|
+
// generated css for the current candidate. This means that the result is ambiguous and this
|
|
704
|
+
// should not happen. We won't generate anything right now, so let's report this to the user
|
|
705
|
+
// by logging some options about what they can do.
|
|
706
|
+
else {
|
|
707
|
+
let typesPerPlugin = matches.map(
|
|
708
|
+
(match) => new Set([...(typesByMatches.get(match) ?? [])])
|
|
709
|
+
)
|
|
710
|
+
|
|
711
|
+
// Remove duplicates, so that we can detect proper unique types for each plugin.
|
|
712
|
+
for (let pluginTypes of typesPerPlugin) {
|
|
713
|
+
for (let type of pluginTypes) {
|
|
714
|
+
let removeFromOwnGroup = false
|
|
715
|
+
|
|
716
|
+
for (let otherGroup of typesPerPlugin) {
|
|
717
|
+
if (pluginTypes === otherGroup) continue
|
|
718
|
+
|
|
719
|
+
if (otherGroup.has(type)) {
|
|
720
|
+
otherGroup.delete(type)
|
|
721
|
+
removeFromOwnGroup = true
|
|
722
|
+
}
|
|
723
|
+
}
|
|
724
|
+
|
|
725
|
+
if (removeFromOwnGroup) pluginTypes.delete(type)
|
|
726
|
+
}
|
|
727
|
+
}
|
|
436
728
|
|
|
437
|
-
messages
|
|
438
|
-
|
|
729
|
+
let messages = []
|
|
730
|
+
|
|
731
|
+
for (let [idx, group] of typesPerPlugin.entries()) {
|
|
732
|
+
for (let type of group) {
|
|
733
|
+
let rules = matches[idx]
|
|
734
|
+
.map(([, rule]) => rule)
|
|
735
|
+
.flat()
|
|
736
|
+
.map((rule) =>
|
|
737
|
+
rule
|
|
738
|
+
.toString()
|
|
739
|
+
.split('\n')
|
|
740
|
+
.slice(1, -1) // Remove selector and closing '}'
|
|
741
|
+
.map((line) => line.trim())
|
|
742
|
+
.map((x) => ` ${x}`) // Re-indent
|
|
743
|
+
.join('\n')
|
|
744
|
+
)
|
|
745
|
+
.join('\n\n')
|
|
746
|
+
|
|
747
|
+
messages.push(
|
|
748
|
+
` Use \`${candidate.replace('[', `[${type}:`)}\` for \`${rules.trim()}\``
|
|
749
|
+
)
|
|
750
|
+
break
|
|
751
|
+
}
|
|
752
|
+
}
|
|
753
|
+
|
|
754
|
+
log.warn([
|
|
755
|
+
`The class \`${candidate}\` is ambiguous and matches multiple utilities.`,
|
|
756
|
+
...messages,
|
|
757
|
+
`If this is content and not a class, replace it with \`${candidate
|
|
758
|
+
.replace('[', '[')
|
|
759
|
+
.replace(']', ']')}\` to silence this warning.`,
|
|
760
|
+
])
|
|
761
|
+
continue
|
|
439
762
|
}
|
|
440
763
|
}
|
|
441
764
|
|
|
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
|
|
765
|
+
matches = matches.map((list) => list.filter((match) => isParsableNode(match[1])))
|
|
450
766
|
}
|
|
451
767
|
|
|
452
|
-
matches =
|
|
768
|
+
matches = matches.flat()
|
|
769
|
+
matches = Array.from(recordCandidates(matches, classCandidate))
|
|
770
|
+
matches = applyPrefix(matches, context)
|
|
453
771
|
|
|
454
772
|
if (important) {
|
|
455
|
-
matches = applyImportant(matches,
|
|
773
|
+
matches = applyImportant(matches, classCandidate)
|
|
456
774
|
}
|
|
457
775
|
|
|
458
776
|
for (let variant of variants) {
|
|
@@ -460,20 +778,16 @@ function* resolveMatches(candidate, context) {
|
|
|
460
778
|
}
|
|
461
779
|
|
|
462
780
|
for (let match of matches) {
|
|
781
|
+
match[1].raws.tailwind = { ...match[1].raws.tailwind, candidate }
|
|
782
|
+
|
|
463
783
|
// Apply final format selector
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
selector: rule.selector,
|
|
472
|
-
candidate,
|
|
473
|
-
context,
|
|
474
|
-
})
|
|
475
|
-
})
|
|
476
|
-
match[1] = container.nodes[0]
|
|
784
|
+
match = applyFinalFormat(match, { context, candidate })
|
|
785
|
+
|
|
786
|
+
// Skip rules with invalid selectors
|
|
787
|
+
// This will cause the candidate to be added to the "not class"
|
|
788
|
+
// cache skipping it entirely for future builds
|
|
789
|
+
if (match === null) {
|
|
790
|
+
continue
|
|
477
791
|
}
|
|
478
792
|
|
|
479
793
|
yield match
|
|
@@ -481,20 +795,119 @@ function* resolveMatches(candidate, context) {
|
|
|
481
795
|
}
|
|
482
796
|
}
|
|
483
797
|
|
|
798
|
+
function applyFinalFormat(match, { context, candidate }) {
|
|
799
|
+
if (!match[0].collectedFormats) {
|
|
800
|
+
return match
|
|
801
|
+
}
|
|
802
|
+
|
|
803
|
+
let isValid = true
|
|
804
|
+
let finalFormat
|
|
805
|
+
|
|
806
|
+
try {
|
|
807
|
+
finalFormat = formatVariantSelector(match[0].collectedFormats, {
|
|
808
|
+
context,
|
|
809
|
+
candidate,
|
|
810
|
+
})
|
|
811
|
+
} catch {
|
|
812
|
+
// The format selector we produced is invalid
|
|
813
|
+
// This could be because:
|
|
814
|
+
// - A bug exists
|
|
815
|
+
// - A plugin introduced an invalid variant selector (ex: `addVariant('foo', '&;foo')`)
|
|
816
|
+
// - The user used an invalid arbitrary variant (ex: `[&;foo]:underline`)
|
|
817
|
+
// Either way the build will fail because of this
|
|
818
|
+
// We would rather that the build pass "silently" given that this could
|
|
819
|
+
// happen because of picking up invalid things when scanning content
|
|
820
|
+
// So we'll throw out the candidate instead
|
|
821
|
+
|
|
822
|
+
return null
|
|
823
|
+
}
|
|
824
|
+
|
|
825
|
+
let container = postcss.root({ nodes: [match[1].clone()] })
|
|
826
|
+
|
|
827
|
+
container.walkRules((rule) => {
|
|
828
|
+
if (inKeyframes(rule)) {
|
|
829
|
+
return
|
|
830
|
+
}
|
|
831
|
+
|
|
832
|
+
try {
|
|
833
|
+
let selector = finalizeSelector(rule.selector, finalFormat, {
|
|
834
|
+
candidate,
|
|
835
|
+
context,
|
|
836
|
+
})
|
|
837
|
+
|
|
838
|
+
// Finalize Selector determined that this candidate is irrelevant
|
|
839
|
+
// TODO: This elimination should happen earlier so this never happens
|
|
840
|
+
if (selector === null) {
|
|
841
|
+
rule.remove()
|
|
842
|
+
return
|
|
843
|
+
}
|
|
844
|
+
|
|
845
|
+
rule.selector = selector
|
|
846
|
+
} catch {
|
|
847
|
+
// If this selector is invalid we also want to skip it
|
|
848
|
+
// But it's likely that being invalid here means there's a bug in a plugin rather than too loosely matching content
|
|
849
|
+
isValid = false
|
|
850
|
+
return false
|
|
851
|
+
}
|
|
852
|
+
})
|
|
853
|
+
|
|
854
|
+
if (!isValid) {
|
|
855
|
+
return null
|
|
856
|
+
}
|
|
857
|
+
|
|
858
|
+
// If all rules have been eliminated we can skip this candidate entirely
|
|
859
|
+
if (container.nodes.length === 0) {
|
|
860
|
+
return null
|
|
861
|
+
}
|
|
862
|
+
|
|
863
|
+
match[1] = container.nodes[0]
|
|
864
|
+
|
|
865
|
+
return match
|
|
866
|
+
}
|
|
867
|
+
|
|
484
868
|
function inKeyframes(rule) {
|
|
485
869
|
return rule.parent && rule.parent.type === 'atrule' && rule.parent.name === 'keyframes'
|
|
486
870
|
}
|
|
487
871
|
|
|
488
|
-
function
|
|
872
|
+
function getImportantStrategy(important) {
|
|
873
|
+
if (important === true) {
|
|
874
|
+
return (rule) => {
|
|
875
|
+
if (inKeyframes(rule)) {
|
|
876
|
+
return
|
|
877
|
+
}
|
|
878
|
+
|
|
879
|
+
rule.walkDecls((d) => {
|
|
880
|
+
if (d.parent.type === 'rule' && !inKeyframes(d.parent)) {
|
|
881
|
+
d.important = true
|
|
882
|
+
}
|
|
883
|
+
})
|
|
884
|
+
}
|
|
885
|
+
}
|
|
886
|
+
|
|
887
|
+
if (typeof important === 'string') {
|
|
888
|
+
return (rule) => {
|
|
889
|
+
if (inKeyframes(rule)) {
|
|
890
|
+
return
|
|
891
|
+
}
|
|
892
|
+
|
|
893
|
+
rule.selectors = rule.selectors.map((selector) => {
|
|
894
|
+
return applyImportantSelector(selector, important)
|
|
895
|
+
})
|
|
896
|
+
}
|
|
897
|
+
}
|
|
898
|
+
}
|
|
899
|
+
|
|
900
|
+
function generateRules(candidates, context, isSorting = false) {
|
|
489
901
|
let allRules = []
|
|
902
|
+
let strategy = getImportantStrategy(context.tailwindConfig.important)
|
|
490
903
|
|
|
491
904
|
for (let candidate of candidates) {
|
|
492
905
|
if (context.notClassCache.has(candidate)) {
|
|
493
906
|
continue
|
|
494
907
|
}
|
|
495
908
|
|
|
496
|
-
if (context.
|
|
497
|
-
allRules.
|
|
909
|
+
if (context.candidateRuleCache.has(candidate)) {
|
|
910
|
+
allRules = allRules.concat(Array.from(context.candidateRuleCache.get(candidate)))
|
|
498
911
|
continue
|
|
499
912
|
}
|
|
500
913
|
|
|
@@ -506,47 +919,29 @@ function generateRules(candidates, context) {
|
|
|
506
919
|
}
|
|
507
920
|
|
|
508
921
|
context.classCache.set(candidate, matches)
|
|
509
|
-
allRules.push(matches)
|
|
510
|
-
}
|
|
511
922
|
|
|
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
|
-
}
|
|
923
|
+
let rules = context.candidateRuleCache.get(candidate) ?? new Set()
|
|
924
|
+
context.candidateRuleCache.set(candidate, rules)
|
|
523
925
|
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
rule.selectors = rule.selectors.map((selector) => {
|
|
527
|
-
return `${important} ${selector}`
|
|
528
|
-
})
|
|
529
|
-
}
|
|
530
|
-
}
|
|
531
|
-
})(context.tailwindConfig.important)
|
|
926
|
+
for (const match of matches) {
|
|
927
|
+
let [{ sort, options }, rule] = match
|
|
532
928
|
|
|
533
|
-
|
|
534
|
-
if (options.respectImportant) {
|
|
535
|
-
if (strategy) {
|
|
929
|
+
if (options.respectImportant && strategy) {
|
|
536
930
|
let container = postcss.root({ nodes: [rule.clone()] })
|
|
537
|
-
container.walkRules(
|
|
538
|
-
if (inKeyframes(r)) {
|
|
539
|
-
return
|
|
540
|
-
}
|
|
541
|
-
|
|
542
|
-
strategy(r)
|
|
543
|
-
})
|
|
931
|
+
container.walkRules(strategy)
|
|
544
932
|
rule = container.nodes[0]
|
|
545
933
|
}
|
|
934
|
+
|
|
935
|
+
// Note: We have to clone rules during sorting
|
|
936
|
+
// so we eliminate some shared mutable state
|
|
937
|
+
let newEntry = [sort, isSorting ? rule.clone() : rule]
|
|
938
|
+
rules.add(newEntry)
|
|
939
|
+
context.ruleCache.add(newEntry)
|
|
940
|
+
allRules.push(newEntry)
|
|
546
941
|
}
|
|
942
|
+
}
|
|
547
943
|
|
|
548
|
-
|
|
549
|
-
})
|
|
944
|
+
return allRules
|
|
550
945
|
}
|
|
551
946
|
|
|
552
947
|
function isArbitraryValue(input) {
|