tailwindcss 0.0.0-insiders.ca1dfd6 → 0.0.0-insiders.cab1fce
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 +380 -1
- package/LICENSE +1 -2
- package/README.md +8 -4
- 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 +3 -0
- package/defaultTheme.js +2 -1
- package/lib/cli-peer-dependencies.js +10 -5
- package/lib/cli.js +311 -213
- package/lib/constants.js +9 -9
- package/lib/corePluginList.js +11 -1
- package/lib/corePlugins.js +1895 -1773
- package/lib/css/preflight.css +19 -13
- package/lib/featureFlags.js +22 -16
- package/lib/index.js +17 -8
- package/lib/lib/cacheInvalidation.js +69 -0
- package/lib/lib/collapseAdjacentRules.js +30 -14
- package/lib/lib/collapseDuplicateDeclarations.js +80 -0
- package/lib/lib/defaultExtractor.js +187 -0
- package/lib/lib/detectNesting.js +17 -2
- package/lib/lib/evaluateTailwindFunctions.js +40 -24
- package/lib/lib/expandApplyAtRules.js +414 -157
- package/lib/lib/expandTailwindAtRules.js +156 -151
- package/lib/lib/generateRules.js +402 -68
- package/lib/lib/getModuleDependencies.js +14 -14
- package/lib/lib/normalizeTailwindDirectives.js +45 -36
- package/lib/lib/partitionApplyAtRules.js +53 -0
- package/lib/lib/regex.js +53 -0
- package/lib/lib/resolveDefaultsAtRules.js +105 -90
- package/lib/lib/setupContextUtils.js +416 -291
- package/lib/lib/setupTrackingContext.js +60 -56
- package/lib/lib/sharedState.js +38 -15
- package/lib/lib/substituteScreenAtRules.js +9 -6
- package/{nesting → lib/postcss-plugins/nesting}/README.md +2 -2
- package/lib/postcss-plugins/nesting/index.js +17 -0
- package/lib/postcss-plugins/nesting/plugin.js +85 -0
- package/lib/processTailwindFeatures.js +21 -9
- package/lib/public/colors.js +244 -248
- package/lib/public/resolve-config.js +5 -5
- package/lib/util/buildMediaQuery.js +13 -24
- package/lib/util/cloneDeep.js +1 -1
- package/lib/util/cloneNodes.js +12 -1
- package/lib/util/color.js +43 -32
- package/lib/util/createPlugin.js +1 -2
- package/lib/util/createUtilityPlugin.js +13 -17
- package/lib/util/dataTypes.js +114 -74
- package/lib/util/defaults.js +6 -0
- package/lib/util/escapeClassName.js +5 -5
- package/lib/util/escapeCommas.js +1 -1
- package/lib/util/flattenColorPalette.js +2 -4
- package/lib/util/formatVariantSelector.js +194 -0
- package/lib/util/getAllConfigs.js +13 -5
- package/lib/util/hashConfig.js +5 -5
- package/lib/util/isKeyframeRule.js +1 -1
- package/lib/util/isPlainObject.js +1 -1
- package/lib/util/isValidArbitraryValue.js +64 -0
- package/lib/util/log.js +35 -19
- package/lib/util/nameClass.js +7 -6
- package/lib/util/negateValue.js +5 -3
- package/lib/util/normalizeConfig.js +233 -0
- package/lib/util/normalizeScreens.js +59 -0
- package/lib/util/parseAnimationValue.js +56 -56
- package/lib/util/parseBoxShadowValue.js +76 -0
- package/lib/util/parseDependency.js +32 -32
- package/lib/util/parseObjectStyles.js +6 -6
- package/lib/util/pluginUtils.js +67 -170
- package/lib/util/prefixSelector.js +4 -7
- package/lib/util/resolveConfig.js +123 -133
- package/lib/util/resolveConfigPath.js +17 -18
- package/lib/util/responsive.js +6 -6
- package/lib/util/splitAtTopLevelOnly.js +72 -0
- package/lib/util/toColorValue.js +1 -2
- package/lib/util/toPath.js +6 -1
- package/lib/util/transformThemeValue.js +42 -34
- package/lib/util/validateConfig.js +21 -0
- package/lib/util/withAlphaVariable.js +19 -19
- package/nesting/index.js +2 -12
- package/package.json +41 -42
- package/peers/index.js +11539 -10859
- package/plugin.d.ts +11 -0
- package/plugin.js +2 -1
- package/resolveConfig.js +2 -1
- package/scripts/create-plugin-list.js +2 -2
- package/scripts/generate-types.js +52 -0
- package/src/cli-peer-dependencies.js +7 -1
- package/src/cli.js +174 -41
- package/src/corePluginList.js +1 -1
- package/src/corePlugins.js +641 -596
- package/src/css/preflight.css +19 -13
- package/src/featureFlags.js +16 -10
- package/src/index.js +14 -6
- package/src/lib/cacheInvalidation.js +52 -0
- package/src/lib/collapseAdjacentRules.js +21 -2
- package/src/lib/collapseDuplicateDeclarations.js +93 -0
- package/src/lib/defaultExtractor.js +192 -0
- package/src/lib/detectNesting.js +22 -3
- package/src/lib/evaluateTailwindFunctions.js +24 -7
- package/src/lib/expandApplyAtRules.js +442 -154
- package/src/lib/expandTailwindAtRules.js +91 -65
- package/src/lib/generateRules.js +409 -43
- package/src/lib/normalizeTailwindDirectives.js +10 -3
- package/src/lib/partitionApplyAtRules.js +52 -0
- package/src/lib/regex.js +74 -0
- package/src/lib/resolveDefaultsAtRules.js +74 -53
- package/src/lib/setupContextUtils.js +388 -208
- package/src/lib/setupTrackingContext.js +15 -12
- package/src/lib/sharedState.js +42 -7
- package/src/lib/substituteScreenAtRules.js +6 -3
- 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 +17 -2
- package/src/public/colors.js +4 -9
- package/src/util/buildMediaQuery.js +14 -18
- package/src/util/cloneNodes.js +14 -1
- package/src/util/color.js +31 -14
- package/src/util/createUtilityPlugin.js +2 -2
- package/src/util/dataTypes.js +56 -16
- package/src/util/defaults.js +6 -0
- package/src/util/formatVariantSelector.js +213 -0
- package/src/util/getAllConfigs.js +7 -0
- package/src/util/isValidArbitraryValue.js +61 -0
- package/src/util/log.js +23 -22
- package/src/util/nameClass.js +2 -2
- package/src/util/negateValue.js +4 -2
- package/src/util/normalizeConfig.js +262 -0
- package/src/util/normalizeScreens.js +45 -0
- package/src/util/parseBoxShadowValue.js +72 -0
- package/src/util/pluginUtils.js +51 -147
- package/src/util/prefixSelector.js +7 -8
- package/src/util/resolveConfig.js +108 -79
- package/src/util/splitAtTopLevelOnly.js +71 -0
- package/src/util/toPath.js +23 -1
- package/src/util/transformThemeValue.js +24 -7
- package/src/util/validateConfig.js +13 -0
- package/stubs/defaultConfig.stub.js +157 -94
- package/stubs/simpleConfig.stub.js +0 -1
- package/types/config.d.ts +325 -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/index.d.ts +1 -0
- package/types.d.ts +1 -0
- package/lib/lib/setupWatchingContext.js +0 -284
- package/nesting/plugin.js +0 -41
- package/src/lib/setupWatchingContext.js +0 -306
|
@@ -12,11 +12,51 @@ import escapeClassName from '../util/escapeClassName'
|
|
|
12
12
|
import nameClass, { formatClass } from '../util/nameClass'
|
|
13
13
|
import { coerceValue } from '../util/pluginUtils'
|
|
14
14
|
import bigSign from '../util/bigSign'
|
|
15
|
-
import corePlugins from '../corePlugins'
|
|
15
|
+
import { variantPlugins, corePlugins } from '../corePlugins'
|
|
16
16
|
import * as sharedState from './sharedState'
|
|
17
17
|
import { env } from './sharedState'
|
|
18
18
|
import { toPath } from '../util/toPath'
|
|
19
19
|
import log from '../util/log'
|
|
20
|
+
import negateValue from '../util/negateValue'
|
|
21
|
+
import isValidArbitraryValue from '../util/isValidArbitraryValue'
|
|
22
|
+
import { generateRules } from './generateRules'
|
|
23
|
+
import { hasContentChanged } from './cacheInvalidation.js'
|
|
24
|
+
|
|
25
|
+
let MATCH_VARIANT = Symbol()
|
|
26
|
+
|
|
27
|
+
function prefix(context, selector) {
|
|
28
|
+
let prefix = context.tailwindConfig.prefix
|
|
29
|
+
return typeof prefix === 'function' ? prefix(selector) : prefix + selector
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
function parseVariantFormatString(input) {
|
|
33
|
+
if (input.includes('{')) {
|
|
34
|
+
if (!isBalanced(input)) throw new Error(`Your { and } are unbalanced.`)
|
|
35
|
+
|
|
36
|
+
return input
|
|
37
|
+
.split(/{(.*)}/gim)
|
|
38
|
+
.flatMap((line) => parseVariantFormatString(line))
|
|
39
|
+
.filter(Boolean)
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
return [input.trim()]
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
function isBalanced(input) {
|
|
46
|
+
let count = 0
|
|
47
|
+
|
|
48
|
+
for (let char of input) {
|
|
49
|
+
if (char === '{') {
|
|
50
|
+
count++
|
|
51
|
+
} else if (char === '}') {
|
|
52
|
+
if (--count < 0) {
|
|
53
|
+
return false // unbalanced
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
return count === 0
|
|
59
|
+
}
|
|
20
60
|
|
|
21
61
|
function insertInto(list, value, { before = [] } = {}) {
|
|
22
62
|
before = [].concat(before)
|
|
@@ -47,50 +87,82 @@ function parseStyles(styles) {
|
|
|
47
87
|
})
|
|
48
88
|
}
|
|
49
89
|
|
|
50
|
-
function getClasses(selector) {
|
|
90
|
+
function getClasses(selector, mutate) {
|
|
51
91
|
let parser = selectorParser((selectors) => {
|
|
52
92
|
let allClasses = []
|
|
93
|
+
|
|
94
|
+
if (mutate) {
|
|
95
|
+
mutate(selectors)
|
|
96
|
+
}
|
|
97
|
+
|
|
53
98
|
selectors.walkClasses((classNode) => {
|
|
54
99
|
allClasses.push(classNode.value)
|
|
55
100
|
})
|
|
101
|
+
|
|
56
102
|
return allClasses
|
|
57
103
|
})
|
|
58
104
|
return parser.transformSync(selector)
|
|
59
105
|
}
|
|
60
106
|
|
|
61
|
-
function extractCandidates(node) {
|
|
107
|
+
function extractCandidates(node, state = { containsNonOnDemandable: false }, depth = 0) {
|
|
62
108
|
let classes = []
|
|
63
109
|
|
|
110
|
+
// Handle normal rules
|
|
64
111
|
if (node.type === 'rule') {
|
|
112
|
+
// Ignore everything inside a :not(...). This allows you to write code like
|
|
113
|
+
// `div:not(.foo)`. If `.foo` is never found in your code, then we used to
|
|
114
|
+
// not generated it. But now we will ignore everything inside a `:not`, so
|
|
115
|
+
// that it still gets generated.
|
|
116
|
+
function ignoreNot(selectors) {
|
|
117
|
+
selectors.walkPseudos((pseudo) => {
|
|
118
|
+
if (pseudo.value === ':not') {
|
|
119
|
+
pseudo.remove()
|
|
120
|
+
}
|
|
121
|
+
})
|
|
122
|
+
}
|
|
123
|
+
|
|
65
124
|
for (let selector of node.selectors) {
|
|
66
|
-
let classCandidates = getClasses(selector)
|
|
125
|
+
let classCandidates = getClasses(selector, ignoreNot)
|
|
67
126
|
// At least one of the selectors contains non-"on-demandable" candidates.
|
|
68
|
-
if (classCandidates.length === 0)
|
|
127
|
+
if (classCandidates.length === 0) {
|
|
128
|
+
state.containsNonOnDemandable = true
|
|
129
|
+
}
|
|
69
130
|
|
|
70
|
-
|
|
131
|
+
for (let classCandidate of classCandidates) {
|
|
132
|
+
classes.push(classCandidate)
|
|
133
|
+
}
|
|
71
134
|
}
|
|
72
|
-
return classes
|
|
73
135
|
}
|
|
74
136
|
|
|
75
|
-
|
|
137
|
+
// Handle at-rules (which contains nested rules)
|
|
138
|
+
else if (node.type === 'atrule') {
|
|
76
139
|
node.walkRules((rule) => {
|
|
77
|
-
|
|
140
|
+
for (let classCandidate of rule.selectors.flatMap((selector) => getClasses(selector))) {
|
|
141
|
+
classes.push(classCandidate)
|
|
142
|
+
}
|
|
78
143
|
})
|
|
79
144
|
}
|
|
80
145
|
|
|
146
|
+
if (depth === 0) {
|
|
147
|
+
return [state.containsNonOnDemandable || classes.length === 0, classes]
|
|
148
|
+
}
|
|
149
|
+
|
|
81
150
|
return classes
|
|
82
151
|
}
|
|
83
152
|
|
|
84
153
|
function withIdentifiers(styles) {
|
|
85
154
|
return parseStyles(styles).flatMap((node) => {
|
|
86
155
|
let nodeMap = new Map()
|
|
87
|
-
let candidates = extractCandidates(node)
|
|
156
|
+
let [containsNonOnDemandableSelectors, candidates] = extractCandidates(node)
|
|
88
157
|
|
|
89
|
-
// If this isn't "on-demandable", assign it a universal candidate.
|
|
90
|
-
if (
|
|
91
|
-
|
|
158
|
+
// If this isn't "on-demandable", assign it a universal candidate to always include it.
|
|
159
|
+
if (containsNonOnDemandableSelectors) {
|
|
160
|
+
candidates.unshift(sharedState.NOT_ON_DEMAND)
|
|
92
161
|
}
|
|
93
162
|
|
|
163
|
+
// However, it could be that it also contains "on-demandable" candidates.
|
|
164
|
+
// E.g.: `span, .foo {}`, in that case it should still be possible to use
|
|
165
|
+
// `@apply foo` for example.
|
|
94
166
|
return candidates.map((c) => {
|
|
95
167
|
if (!nodeMap.has(node)) {
|
|
96
168
|
nodeMap.set(node, node)
|
|
@@ -100,62 +172,32 @@ function withIdentifiers(styles) {
|
|
|
100
172
|
})
|
|
101
173
|
}
|
|
102
174
|
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
['(', ')'],
|
|
107
|
-
])
|
|
108
|
-
let inverseMatchingBrackets = new Map(
|
|
109
|
-
Array.from(matchingBrackets.entries()).map(([k, v]) => [v, k])
|
|
110
|
-
)
|
|
111
|
-
|
|
112
|
-
let quotes = new Set(['"', "'", '`'])
|
|
113
|
-
|
|
114
|
-
// Arbitrary values must contain balanced brackets (), [] and {}. Escaped
|
|
115
|
-
// values don't count, and brackets inside quotes also don't count.
|
|
116
|
-
//
|
|
117
|
-
// E.g.: w-[this-is]w-[weird-and-invalid]
|
|
118
|
-
// E.g.: w-[this-is\\]w-\\[weird-but-valid]
|
|
119
|
-
// E.g.: content-['this-is-also-valid]-weirdly-enough']
|
|
120
|
-
function isValidArbitraryValue(value) {
|
|
121
|
-
let stack = []
|
|
122
|
-
let inQuotes = false
|
|
123
|
-
|
|
124
|
-
for (let i = 0; i < value.length; i++) {
|
|
125
|
-
let char = value[i]
|
|
126
|
-
|
|
127
|
-
// Non-escaped quotes allow us to "allow" anything in between
|
|
128
|
-
if (quotes.has(char) && value[i - 1] !== '\\') {
|
|
129
|
-
inQuotes = !inQuotes
|
|
130
|
-
}
|
|
131
|
-
|
|
132
|
-
if (inQuotes) continue
|
|
133
|
-
if (value[i - 1] === '\\') continue // Escaped
|
|
175
|
+
export function isValidVariantFormatString(format) {
|
|
176
|
+
return format.startsWith('@') || format.includes('&')
|
|
177
|
+
}
|
|
134
178
|
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
179
|
+
export function parseVariant(variant) {
|
|
180
|
+
variant = variant
|
|
181
|
+
.replace(/\n+/g, '')
|
|
182
|
+
.replace(/\s{1,}/g, ' ')
|
|
183
|
+
.trim()
|
|
139
184
|
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
185
|
+
let fns = parseVariantFormatString(variant)
|
|
186
|
+
.map((str) => {
|
|
187
|
+
if (!str.startsWith('@')) {
|
|
188
|
+
return ({ format }) => format(str)
|
|
143
189
|
}
|
|
144
190
|
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
}
|
|
150
|
-
}
|
|
191
|
+
let [, name, params] = /@(.*?)( .+|[({].*)/g.exec(str)
|
|
192
|
+
return ({ wrap }) => wrap(postcss.atRule({ name, params: params.trim() }))
|
|
193
|
+
})
|
|
194
|
+
.reverse()
|
|
151
195
|
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
196
|
+
return (api) => {
|
|
197
|
+
for (let fn of fns) {
|
|
198
|
+
fn(api)
|
|
199
|
+
}
|
|
155
200
|
}
|
|
156
|
-
|
|
157
|
-
// All good, totally balanced!
|
|
158
|
-
return true
|
|
159
201
|
}
|
|
160
202
|
|
|
161
203
|
function buildPluginApi(tailwindConfig, context, { variantList, variantMap, offsets, classList }) {
|
|
@@ -168,37 +210,36 @@ function buildPluginApi(tailwindConfig, context, { variantList, variantMap, offs
|
|
|
168
210
|
}
|
|
169
211
|
|
|
170
212
|
function prefixIdentifier(identifier, options) {
|
|
171
|
-
if (identifier ===
|
|
172
|
-
return
|
|
213
|
+
if (identifier === sharedState.NOT_ON_DEMAND) {
|
|
214
|
+
return sharedState.NOT_ON_DEMAND
|
|
173
215
|
}
|
|
174
216
|
|
|
175
217
|
if (!options.respectPrefix) {
|
|
176
218
|
return identifier
|
|
177
219
|
}
|
|
178
220
|
|
|
179
|
-
if (typeof context.tailwindConfig.prefix === 'function') {
|
|
180
|
-
return prefixSelector(context.tailwindConfig.prefix, `.${identifier}`).substr(1)
|
|
181
|
-
}
|
|
182
|
-
|
|
183
221
|
return context.tailwindConfig.prefix + identifier
|
|
184
222
|
}
|
|
185
223
|
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
224
|
+
function resolveThemeValue(path, defaultValue, opts = {}) {
|
|
225
|
+
const [pathRoot, ...subPaths] = toPath(path)
|
|
226
|
+
const value = getConfigValue(['theme', pathRoot, ...subPaths], defaultValue)
|
|
227
|
+
return transformThemeValue(pathRoot)(value, opts)
|
|
228
|
+
}
|
|
189
229
|
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
230
|
+
const theme = Object.assign(
|
|
231
|
+
(path, defaultValue = undefined) => resolveThemeValue(path, defaultValue),
|
|
232
|
+
{
|
|
233
|
+
withAlpha: (path, opacityValue) => resolveThemeValue(path, undefined, { opacityValue }),
|
|
234
|
+
}
|
|
235
|
+
)
|
|
236
|
+
|
|
237
|
+
let api = {
|
|
193
238
|
postcss,
|
|
194
239
|
prefix: applyConfiguredPrefix,
|
|
195
240
|
e: escapeClassName,
|
|
196
241
|
config: getConfigValue,
|
|
197
|
-
theme
|
|
198
|
-
const [pathRoot, ...subPaths] = toPath(path)
|
|
199
|
-
const value = getConfigValue(['theme', pathRoot, ...subPaths], defaultValue)
|
|
200
|
-
return transformThemeValue(pathRoot)(value)
|
|
201
|
-
},
|
|
242
|
+
theme,
|
|
202
243
|
corePlugins: (path) => {
|
|
203
244
|
if (Array.isArray(tailwindConfig.corePlugins)) {
|
|
204
245
|
return tailwindConfig.corePlugins.includes(path)
|
|
@@ -210,21 +251,31 @@ function buildPluginApi(tailwindConfig, context, { variantList, variantMap, offs
|
|
|
210
251
|
// Preserved for backwards compatibility but not used in v3.0+
|
|
211
252
|
return []
|
|
212
253
|
},
|
|
213
|
-
|
|
214
|
-
for (let [identifier, rule] of withIdentifiers(
|
|
215
|
-
let
|
|
254
|
+
addBase(base) {
|
|
255
|
+
for (let [identifier, rule] of withIdentifiers(base)) {
|
|
256
|
+
let prefixedIdentifier = prefixIdentifier(identifier, {})
|
|
257
|
+
let offset = offsets.base++
|
|
216
258
|
|
|
217
|
-
if (!context.candidateRuleMap.has(
|
|
218
|
-
context.candidateRuleMap.set(
|
|
259
|
+
if (!context.candidateRuleMap.has(prefixedIdentifier)) {
|
|
260
|
+
context.candidateRuleMap.set(prefixedIdentifier, [])
|
|
219
261
|
}
|
|
220
262
|
|
|
221
|
-
context.candidateRuleMap
|
|
263
|
+
context.candidateRuleMap
|
|
264
|
+
.get(prefixedIdentifier)
|
|
265
|
+
.push([{ sort: offset, layer: 'base' }, rule])
|
|
222
266
|
}
|
|
223
267
|
},
|
|
224
|
-
|
|
225
|
-
|
|
268
|
+
/**
|
|
269
|
+
* @param {string} group
|
|
270
|
+
* @param {Record<string, string | string[]>} declarations
|
|
271
|
+
*/
|
|
272
|
+
addDefaults(group, declarations) {
|
|
273
|
+
const groups = {
|
|
274
|
+
[`@defaults ${group}`]: declarations,
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
for (let [identifier, rule] of withIdentifiers(groups)) {
|
|
226
278
|
let prefixedIdentifier = prefixIdentifier(identifier, {})
|
|
227
|
-
let offset = offsets.base++
|
|
228
279
|
|
|
229
280
|
if (!context.candidateRuleMap.has(prefixedIdentifier)) {
|
|
230
281
|
context.candidateRuleMap.set(prefixedIdentifier, [])
|
|
@@ -232,26 +283,19 @@ function buildPluginApi(tailwindConfig, context, { variantList, variantMap, offs
|
|
|
232
283
|
|
|
233
284
|
context.candidateRuleMap
|
|
234
285
|
.get(prefixedIdentifier)
|
|
235
|
-
.push([{ sort:
|
|
286
|
+
.push([{ sort: offsets.base++, layer: 'defaults' }, rule])
|
|
236
287
|
}
|
|
237
288
|
},
|
|
238
289
|
addComponents(components, options) {
|
|
239
290
|
let defaultOptions = {
|
|
240
|
-
variants: [],
|
|
241
291
|
respectPrefix: true,
|
|
242
292
|
respectImportant: false,
|
|
243
|
-
respectVariants: true,
|
|
244
293
|
}
|
|
245
294
|
|
|
246
|
-
options = Object.assign(
|
|
247
|
-
{},
|
|
248
|
-
defaultOptions,
|
|
249
|
-
Array.isArray(options) ? { variants: options } : options
|
|
250
|
-
)
|
|
295
|
+
options = Object.assign({}, defaultOptions, Array.isArray(options) ? {} : options)
|
|
251
296
|
|
|
252
297
|
for (let [identifier, rule] of withIdentifiers(components)) {
|
|
253
298
|
let prefixedIdentifier = prefixIdentifier(identifier, options)
|
|
254
|
-
let offset = offsets.components++
|
|
255
299
|
|
|
256
300
|
classList.add(prefixedIdentifier)
|
|
257
301
|
|
|
@@ -261,26 +305,19 @@ function buildPluginApi(tailwindConfig, context, { variantList, variantMap, offs
|
|
|
261
305
|
|
|
262
306
|
context.candidateRuleMap
|
|
263
307
|
.get(prefixedIdentifier)
|
|
264
|
-
.push([{ sort:
|
|
308
|
+
.push([{ sort: offsets.components++, layer: 'components', options }, rule])
|
|
265
309
|
}
|
|
266
310
|
},
|
|
267
311
|
addUtilities(utilities, options) {
|
|
268
312
|
let defaultOptions = {
|
|
269
|
-
variants: [],
|
|
270
313
|
respectPrefix: true,
|
|
271
314
|
respectImportant: true,
|
|
272
|
-
respectVariants: true,
|
|
273
315
|
}
|
|
274
316
|
|
|
275
|
-
options = Object.assign(
|
|
276
|
-
{},
|
|
277
|
-
defaultOptions,
|
|
278
|
-
Array.isArray(options) ? { variants: options } : options
|
|
279
|
-
)
|
|
317
|
+
options = Object.assign({}, defaultOptions, Array.isArray(options) ? {} : options)
|
|
280
318
|
|
|
281
319
|
for (let [identifier, rule] of withIdentifiers(utilities)) {
|
|
282
320
|
let prefixedIdentifier = prefixIdentifier(identifier, options)
|
|
283
|
-
let offset = offsets.utilities++
|
|
284
321
|
|
|
285
322
|
classList.add(prefixedIdentifier)
|
|
286
323
|
|
|
@@ -290,15 +327,13 @@ function buildPluginApi(tailwindConfig, context, { variantList, variantMap, offs
|
|
|
290
327
|
|
|
291
328
|
context.candidateRuleMap
|
|
292
329
|
.get(prefixedIdentifier)
|
|
293
|
-
.push([{ sort:
|
|
330
|
+
.push([{ sort: offsets.utilities++, layer: 'utilities', options }, rule])
|
|
294
331
|
}
|
|
295
332
|
},
|
|
296
333
|
matchUtilities: function (utilities, options) {
|
|
297
334
|
let defaultOptions = {
|
|
298
|
-
variants: [],
|
|
299
335
|
respectPrefix: true,
|
|
300
336
|
respectImportant: true,
|
|
301
|
-
respectVariants: true,
|
|
302
337
|
}
|
|
303
338
|
|
|
304
339
|
options = { ...defaultOptions, ...options }
|
|
@@ -314,7 +349,59 @@ function buildPluginApi(tailwindConfig, context, { variantList, variantMap, offs
|
|
|
314
349
|
function wrapped(modifier, { isOnlyPlugin }) {
|
|
315
350
|
let { type = 'any' } = options
|
|
316
351
|
type = [].concat(type)
|
|
317
|
-
let [value, coercedType] = coerceValue(type, modifier, options
|
|
352
|
+
let [value, coercedType] = coerceValue(type, modifier, options, tailwindConfig)
|
|
353
|
+
|
|
354
|
+
if (value === undefined) {
|
|
355
|
+
return []
|
|
356
|
+
}
|
|
357
|
+
|
|
358
|
+
if (!type.includes(coercedType) && !isOnlyPlugin) {
|
|
359
|
+
return []
|
|
360
|
+
}
|
|
361
|
+
|
|
362
|
+
if (!isValidArbitraryValue(value)) {
|
|
363
|
+
return []
|
|
364
|
+
}
|
|
365
|
+
|
|
366
|
+
let ruleSets = []
|
|
367
|
+
.concat(rule(value))
|
|
368
|
+
.filter(Boolean)
|
|
369
|
+
.map((declaration) => ({
|
|
370
|
+
[nameClass(identifier, modifier)]: declaration,
|
|
371
|
+
}))
|
|
372
|
+
|
|
373
|
+
return ruleSets
|
|
374
|
+
}
|
|
375
|
+
|
|
376
|
+
let withOffsets = [{ sort: offset, layer: 'utilities', options }, wrapped]
|
|
377
|
+
|
|
378
|
+
if (!context.candidateRuleMap.has(prefixedIdentifier)) {
|
|
379
|
+
context.candidateRuleMap.set(prefixedIdentifier, [])
|
|
380
|
+
}
|
|
381
|
+
|
|
382
|
+
context.candidateRuleMap.get(prefixedIdentifier).push(withOffsets)
|
|
383
|
+
}
|
|
384
|
+
},
|
|
385
|
+
matchComponents: function (components, options) {
|
|
386
|
+
let defaultOptions = {
|
|
387
|
+
respectPrefix: true,
|
|
388
|
+
respectImportant: false,
|
|
389
|
+
}
|
|
390
|
+
|
|
391
|
+
options = { ...defaultOptions, ...options }
|
|
392
|
+
|
|
393
|
+
let offset = offsets.components++
|
|
394
|
+
|
|
395
|
+
for (let identifier in components) {
|
|
396
|
+
let prefixedIdentifier = prefixIdentifier(identifier, options)
|
|
397
|
+
let rule = components[identifier]
|
|
398
|
+
|
|
399
|
+
classList.add([prefixedIdentifier, options])
|
|
400
|
+
|
|
401
|
+
function wrapped(modifier, { isOnlyPlugin }) {
|
|
402
|
+
let { type = 'any' } = options
|
|
403
|
+
type = [].concat(type)
|
|
404
|
+
let [value, coercedType] = coerceValue(type, modifier, options, tailwindConfig)
|
|
318
405
|
|
|
319
406
|
if (value === undefined) {
|
|
320
407
|
return []
|
|
@@ -338,24 +425,17 @@ function buildPluginApi(tailwindConfig, context, { variantList, variantMap, offs
|
|
|
338
425
|
return []
|
|
339
426
|
}
|
|
340
427
|
|
|
341
|
-
let includedRules = []
|
|
342
428
|
let ruleSets = []
|
|
343
|
-
.concat(
|
|
344
|
-
rule(value, {
|
|
345
|
-
includeRules(rules) {
|
|
346
|
-
includedRules.push(...rules)
|
|
347
|
-
},
|
|
348
|
-
})
|
|
349
|
-
)
|
|
429
|
+
.concat(rule(value))
|
|
350
430
|
.filter(Boolean)
|
|
351
431
|
.map((declaration) => ({
|
|
352
432
|
[nameClass(identifier, modifier)]: declaration,
|
|
353
433
|
}))
|
|
354
434
|
|
|
355
|
-
return
|
|
435
|
+
return ruleSets
|
|
356
436
|
}
|
|
357
437
|
|
|
358
|
-
let withOffsets = [{ sort: offset, layer: '
|
|
438
|
+
let withOffsets = [{ sort: offset, layer: 'components', options }, wrapped]
|
|
359
439
|
|
|
360
440
|
if (!context.candidateRuleMap.has(prefixedIdentifier)) {
|
|
361
441
|
context.candidateRuleMap.set(prefixedIdentifier, [])
|
|
@@ -364,7 +444,58 @@ function buildPluginApi(tailwindConfig, context, { variantList, variantMap, offs
|
|
|
364
444
|
context.candidateRuleMap.get(prefixedIdentifier).push(withOffsets)
|
|
365
445
|
}
|
|
366
446
|
},
|
|
447
|
+
addVariant(variantName, variantFunctions, options = {}) {
|
|
448
|
+
variantFunctions = [].concat(variantFunctions).map((variantFunction) => {
|
|
449
|
+
if (typeof variantFunction !== 'string') {
|
|
450
|
+
// Safelist public API functions
|
|
451
|
+
return (api) => {
|
|
452
|
+
let { args, modifySelectors, container, separator, wrap, format } = api
|
|
453
|
+
let result = variantFunction(
|
|
454
|
+
Object.assign(
|
|
455
|
+
{ modifySelectors, container, separator },
|
|
456
|
+
variantFunction[MATCH_VARIANT] && { args, wrap, format }
|
|
457
|
+
)
|
|
458
|
+
)
|
|
459
|
+
|
|
460
|
+
if (typeof result === 'string' && !isValidVariantFormatString(result)) {
|
|
461
|
+
throw new Error(
|
|
462
|
+
`Your custom variant \`${variantName}\` has an invalid format string. Make sure it's an at-rule or contains a \`&\` placeholder.`
|
|
463
|
+
)
|
|
464
|
+
}
|
|
465
|
+
|
|
466
|
+
// result may be undefined with legacy variants that use APIs like `modifySelectors`
|
|
467
|
+
return result && parseVariant(result)(api)
|
|
468
|
+
}
|
|
469
|
+
}
|
|
470
|
+
|
|
471
|
+
if (!isValidVariantFormatString(variantFunction)) {
|
|
472
|
+
throw new Error(
|
|
473
|
+
`Your custom variant \`${variantName}\` has an invalid format string. Make sure it's an at-rule or contains a \`&\` placeholder.`
|
|
474
|
+
)
|
|
475
|
+
}
|
|
476
|
+
|
|
477
|
+
return parseVariant(variantFunction)
|
|
478
|
+
})
|
|
479
|
+
|
|
480
|
+
insertInto(variantList, variantName, options)
|
|
481
|
+
variantMap.set(variantName, variantFunctions)
|
|
482
|
+
},
|
|
483
|
+
matchVariant: function (variants, options) {
|
|
484
|
+
for (let variant in variants) {
|
|
485
|
+
for (let [k, v] of Object.entries(options?.values ?? {})) {
|
|
486
|
+
api.addVariant(`${variant}-${k}`, variants[variant](v))
|
|
487
|
+
}
|
|
488
|
+
|
|
489
|
+
api.addVariant(
|
|
490
|
+
variant,
|
|
491
|
+
Object.assign(({ args }) => variants[variant](args), { [MATCH_VARIANT]: true }),
|
|
492
|
+
options
|
|
493
|
+
)
|
|
494
|
+
}
|
|
495
|
+
},
|
|
367
496
|
}
|
|
497
|
+
|
|
498
|
+
return api
|
|
368
499
|
}
|
|
369
500
|
|
|
370
501
|
let fileModifiedMapCache = new WeakMap()
|
|
@@ -384,7 +515,14 @@ function trackModified(files, fileModifiedMap) {
|
|
|
384
515
|
let parsed = url.parse(file)
|
|
385
516
|
let pathname = parsed.hash ? parsed.href.replace(parsed.hash, '') : parsed.href
|
|
386
517
|
pathname = parsed.search ? pathname.replace(parsed.search, '') : pathname
|
|
387
|
-
let newModified = fs.statSync(decodeURIComponent(pathname))
|
|
518
|
+
let newModified = fs.statSync(decodeURIComponent(pathname), { throwIfNoEntry: false })?.mtimeMs
|
|
519
|
+
if (!newModified) {
|
|
520
|
+
// It could happen that a file is passed in that doesn't exist. E.g.:
|
|
521
|
+
// postcss-cli will provide you a fake path when reading from stdin. This
|
|
522
|
+
// path then looks like /path-to-your-project/stdin In that case we just
|
|
523
|
+
// want to ignore it and don't track changes at all.
|
|
524
|
+
continue
|
|
525
|
+
}
|
|
388
526
|
|
|
389
527
|
if (!fileModifiedMap.has(file) || newModified > fileModifiedMap.get(file)) {
|
|
390
528
|
changed = true
|
|
@@ -444,20 +582,11 @@ function collectLayerPlugins(root) {
|
|
|
444
582
|
}
|
|
445
583
|
})
|
|
446
584
|
|
|
447
|
-
root.walkRules((rule) => {
|
|
448
|
-
// At this point it is safe to include all the left-over css from the
|
|
449
|
-
// user's css file. This is because the `@tailwind` and `@layer` directives
|
|
450
|
-
// will already be handled and will be removed from the css tree.
|
|
451
|
-
layerPlugins.push(function ({ addUserCss }) {
|
|
452
|
-
addUserCss(rule, { respectPrefix: false })
|
|
453
|
-
})
|
|
454
|
-
})
|
|
455
|
-
|
|
456
585
|
return layerPlugins
|
|
457
586
|
}
|
|
458
587
|
|
|
459
588
|
function resolvePlugins(context, root) {
|
|
460
|
-
let corePluginList = Object.entries(corePlugins)
|
|
589
|
+
let corePluginList = Object.entries({ ...variantPlugins, ...corePlugins })
|
|
461
590
|
.map(([name, plugin]) => {
|
|
462
591
|
if (!context.tailwindConfig.corePlugins.includes(name)) {
|
|
463
592
|
return null
|
|
@@ -479,12 +608,18 @@ function resolvePlugins(context, root) {
|
|
|
479
608
|
|
|
480
609
|
// TODO: This is a workaround for backwards compatibility, since custom variants
|
|
481
610
|
// were historically sorted before screen/stackable variants.
|
|
482
|
-
let beforeVariants = [
|
|
611
|
+
let beforeVariants = [
|
|
612
|
+
variantPlugins['pseudoElementVariants'],
|
|
613
|
+
variantPlugins['pseudoClassVariants'],
|
|
614
|
+
]
|
|
483
615
|
let afterVariants = [
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
616
|
+
variantPlugins['directionVariants'],
|
|
617
|
+
variantPlugins['reducedMotionVariants'],
|
|
618
|
+
variantPlugins['prefersContrastVariants'],
|
|
619
|
+
variantPlugins['darkVariants'],
|
|
620
|
+
variantPlugins['printVariant'],
|
|
621
|
+
variantPlugins['screenVariants'],
|
|
622
|
+
variantPlugins['orientationVariants'],
|
|
488
623
|
]
|
|
489
624
|
|
|
490
625
|
return [...corePluginList, ...beforeVariants, ...userPlugins, ...afterVariants, ...layerPlugins]
|
|
@@ -494,6 +629,7 @@ function registerPlugins(plugins, context) {
|
|
|
494
629
|
let variantList = []
|
|
495
630
|
let variantMap = new Map()
|
|
496
631
|
let offsets = {
|
|
632
|
+
defaults: 0n,
|
|
497
633
|
base: 0n,
|
|
498
634
|
components: 0n,
|
|
499
635
|
utilities: 0n,
|
|
@@ -521,20 +657,26 @@ function registerPlugins(plugins, context) {
|
|
|
521
657
|
|
|
522
658
|
let highestOffset = ((args) => args.reduce((m, e) => (e > m ? e : m)))([
|
|
523
659
|
offsets.base,
|
|
660
|
+
offsets.defaults,
|
|
524
661
|
offsets.components,
|
|
525
662
|
offsets.utilities,
|
|
526
663
|
offsets.user,
|
|
527
664
|
])
|
|
528
665
|
let reservedBits = BigInt(highestOffset.toString(2).length)
|
|
529
666
|
|
|
667
|
+
// A number one less than the top range of the highest offset area
|
|
668
|
+
// so arbitrary properties are always sorted at the end.
|
|
669
|
+
context.arbitraryPropertiesSort = ((1n << reservedBits) << 0n) - 1n
|
|
670
|
+
|
|
530
671
|
context.layerOrder = {
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
672
|
+
defaults: (1n << reservedBits) << 0n,
|
|
673
|
+
base: (1n << reservedBits) << 1n,
|
|
674
|
+
components: (1n << reservedBits) << 2n,
|
|
675
|
+
utilities: (1n << reservedBits) << 3n,
|
|
676
|
+
user: (1n << reservedBits) << 4n,
|
|
535
677
|
}
|
|
536
678
|
|
|
537
|
-
reservedBits +=
|
|
679
|
+
reservedBits += 5n
|
|
538
680
|
|
|
539
681
|
let offset = 0
|
|
540
682
|
context.variantOrder = new Map(
|
|
@@ -559,105 +701,141 @@ function registerPlugins(plugins, context) {
|
|
|
559
701
|
)
|
|
560
702
|
}
|
|
561
703
|
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
context.safelist = function () {
|
|
565
|
-
let safelist = (context.tailwindConfig.safelist ?? []).filter(Boolean)
|
|
566
|
-
if (safelist.length <= 0) return []
|
|
567
|
-
|
|
568
|
-
let output = []
|
|
704
|
+
let safelist = (context.tailwindConfig.safelist ?? []).filter(Boolean)
|
|
705
|
+
if (safelist.length > 0) {
|
|
569
706
|
let checks = []
|
|
570
707
|
|
|
571
708
|
for (let value of safelist) {
|
|
572
709
|
if (typeof value === 'string') {
|
|
573
|
-
|
|
710
|
+
context.changedContent.push({ content: value, extension: 'html' })
|
|
574
711
|
continue
|
|
575
712
|
}
|
|
576
713
|
|
|
577
714
|
if (value instanceof RegExp) {
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
])
|
|
584
|
-
warnedAbout.add('root-regex')
|
|
585
|
-
}
|
|
715
|
+
log.warn('root-regex', [
|
|
716
|
+
'Regular expressions in `safelist` work differently in Tailwind CSS v3.0.',
|
|
717
|
+
'Update your `safelist` configuration to eliminate this warning.',
|
|
718
|
+
'https://tailwindcss.com/docs/content-configuration#safelisting-classes',
|
|
719
|
+
])
|
|
586
720
|
continue
|
|
587
721
|
}
|
|
588
722
|
|
|
589
723
|
checks.push(value)
|
|
590
724
|
}
|
|
591
725
|
|
|
592
|
-
if (checks.length
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
726
|
+
if (checks.length > 0) {
|
|
727
|
+
let patternMatchingCount = new Map()
|
|
728
|
+
let prefixLength = context.tailwindConfig.prefix.length
|
|
729
|
+
|
|
730
|
+
for (let util of classList) {
|
|
731
|
+
let utils = Array.isArray(util)
|
|
732
|
+
? (() => {
|
|
733
|
+
let [utilName, options] = util
|
|
734
|
+
let values = Object.keys(options?.values ?? {})
|
|
735
|
+
let classes = values.map((value) => formatClass(utilName, value))
|
|
736
|
+
|
|
737
|
+
if (options?.supportsNegativeValues) {
|
|
738
|
+
// This is the normal negated version
|
|
739
|
+
// e.g. `-inset-1` or `-tw-inset-1`
|
|
740
|
+
classes = [...classes, ...classes.map((cls) => '-' + cls)]
|
|
741
|
+
|
|
742
|
+
// This is the negated version *after* the prefix
|
|
743
|
+
// e.g. `tw--inset-1`
|
|
744
|
+
// The prefix is already attached to util name
|
|
745
|
+
// So we add the negative after the prefix
|
|
746
|
+
classes = [
|
|
747
|
+
...classes,
|
|
748
|
+
...classes.map(
|
|
749
|
+
(cls) => cls.slice(0, prefixLength) + '-' + cls.slice(prefixLength)
|
|
750
|
+
),
|
|
751
|
+
]
|
|
752
|
+
}
|
|
753
|
+
|
|
754
|
+
return classes
|
|
755
|
+
})()
|
|
756
|
+
: [util]
|
|
757
|
+
|
|
758
|
+
for (let util of utils) {
|
|
759
|
+
for (let { pattern, variants = [] } of checks) {
|
|
760
|
+
// RegExp with the /g flag are stateful, so let's reset the last
|
|
761
|
+
// index pointer to reset the state.
|
|
762
|
+
pattern.lastIndex = 0
|
|
763
|
+
|
|
764
|
+
if (!patternMatchingCount.has(pattern)) {
|
|
765
|
+
patternMatchingCount.set(pattern, 0)
|
|
766
|
+
}
|
|
613
767
|
|
|
614
|
-
|
|
768
|
+
if (!pattern.test(util)) continue
|
|
615
769
|
|
|
616
|
-
|
|
770
|
+
patternMatchingCount.set(pattern, patternMatchingCount.get(pattern) + 1)
|
|
617
771
|
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
772
|
+
context.changedContent.push({ content: util, extension: 'html' })
|
|
773
|
+
for (let variant of variants) {
|
|
774
|
+
context.changedContent.push({
|
|
775
|
+
content: variant + context.tailwindConfig.separator + util,
|
|
776
|
+
extension: 'html',
|
|
777
|
+
})
|
|
778
|
+
}
|
|
621
779
|
}
|
|
622
780
|
}
|
|
623
781
|
}
|
|
624
|
-
}
|
|
625
782
|
|
|
626
|
-
|
|
627
|
-
|
|
783
|
+
for (let [regex, count] of patternMatchingCount.entries()) {
|
|
784
|
+
if (count !== 0) continue
|
|
628
785
|
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
786
|
+
log.warn([
|
|
787
|
+
`The safelist pattern \`${regex}\` doesn't match any Tailwind CSS classes.`,
|
|
788
|
+
'Fix this pattern or remove it from your `safelist` configuration.',
|
|
789
|
+
'https://tailwindcss.com/docs/content-configuration#safelisting-classes',
|
|
790
|
+
])
|
|
791
|
+
}
|
|
634
792
|
}
|
|
793
|
+
}
|
|
635
794
|
|
|
636
|
-
|
|
795
|
+
// A list of utilities that are used by certain Tailwind CSS utilities but
|
|
796
|
+
// that don't exist on their own. This will result in them "not existing" and
|
|
797
|
+
// sorting could be weird since you still require them in order to make the
|
|
798
|
+
// host utitlies work properly. (Thanks Biology)
|
|
799
|
+
let parasiteUtilities = new Set([prefix(context, 'group'), prefix(context, 'peer')])
|
|
800
|
+
context.getClassOrder = function getClassOrder(classes) {
|
|
801
|
+
let sortedClassNames = new Map()
|
|
802
|
+
for (let [sort, rule] of generateRules(new Set(classes), context)) {
|
|
803
|
+
if (sortedClassNames.has(rule.raws.tailwind.candidate)) continue
|
|
804
|
+
sortedClassNames.set(rule.raws.tailwind.candidate, sort)
|
|
805
|
+
}
|
|
806
|
+
|
|
807
|
+
return classes.map((className) => {
|
|
808
|
+
let order = sortedClassNames.get(className) ?? null
|
|
809
|
+
|
|
810
|
+
if (order === null && parasiteUtilities.has(className)) {
|
|
811
|
+
// This will make sure that it is at the very beginning of the
|
|
812
|
+
// `components` layer which technically means 'before any
|
|
813
|
+
// components'.
|
|
814
|
+
order = context.layerOrder.components
|
|
815
|
+
}
|
|
816
|
+
|
|
817
|
+
return [className, order]
|
|
818
|
+
})
|
|
637
819
|
}
|
|
638
820
|
|
|
639
|
-
// Generate a list of strings for autocompletion purposes.
|
|
640
|
-
//
|
|
641
|
-
|
|
642
|
-
context.completions = function () {
|
|
643
|
-
// TODO: Try and detect color from components?
|
|
644
|
-
// TODO: Should we provide a simple "public api" file with functions?
|
|
821
|
+
// Generate a list of strings for autocompletion purposes, e.g.
|
|
822
|
+
// ['uppercase', 'lowercase', ...]
|
|
823
|
+
context.getClassList = function getClassList() {
|
|
645
824
|
let output = []
|
|
646
825
|
|
|
647
826
|
for (let util of classList) {
|
|
648
827
|
if (Array.isArray(util)) {
|
|
649
828
|
let [utilName, options] = util
|
|
650
|
-
let
|
|
829
|
+
let negativeClasses = []
|
|
651
830
|
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
} else {
|
|
657
|
-
for (let value of Object.keys(options?.values ?? {})) {
|
|
658
|
-
output.push(formatClass(utilName, value))
|
|
831
|
+
for (let [key, value] of Object.entries(options?.values ?? {})) {
|
|
832
|
+
output.push(formatClass(utilName, key))
|
|
833
|
+
if (options?.supportsNegativeValues && negateValue(value)) {
|
|
834
|
+
negativeClasses.push(formatClass(utilName, `-${key}`))
|
|
659
835
|
}
|
|
660
836
|
}
|
|
837
|
+
|
|
838
|
+
output.push(...negativeClasses)
|
|
661
839
|
} else {
|
|
662
840
|
output.push(util)
|
|
663
841
|
}
|
|
@@ -717,6 +895,8 @@ export function getContext(
|
|
|
717
895
|
existingContext = context
|
|
718
896
|
}
|
|
719
897
|
|
|
898
|
+
let cssDidChange = hasContentChanged(sourcePath, root)
|
|
899
|
+
|
|
720
900
|
// If there's already a context in the cache and we don't need to
|
|
721
901
|
// reset the context, return the cached context.
|
|
722
902
|
if (existingContext) {
|
|
@@ -724,7 +904,7 @@ export function getContext(
|
|
|
724
904
|
[...contextDependencies],
|
|
725
905
|
getFileModifiedMap(existingContext)
|
|
726
906
|
)
|
|
727
|
-
if (!contextDependenciesChanged) {
|
|
907
|
+
if (!contextDependenciesChanged && !cssDidChange) {
|
|
728
908
|
return [existingContext, false]
|
|
729
909
|
}
|
|
730
910
|
}
|