tailwindcss 3.0.10 → 3.0.14
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 +43 -1
- package/lib/cli.js +8 -16
- package/lib/corePlugins.js +183 -280
- package/lib/css/preflight.css +1 -1
- package/lib/featureFlags.js +2 -5
- package/lib/lib/expandApplyAtRules.js +0 -40
- package/lib/lib/expandTailwindAtRules.js +32 -39
- package/lib/lib/generateRules.js +8 -2
- package/lib/lib/partitionApplyAtRules.js +53 -0
- package/lib/lib/resolveDefaultsAtRules.js +4 -4
- package/lib/lib/setupContextUtils.js +48 -52
- package/lib/processTailwindFeatures.js +3 -1
- package/lib/util/createPlugin.js +1 -2
- package/lib/util/createUtilityPlugin.js +4 -8
- package/lib/util/flattenColorPalette.js +1 -3
- package/lib/util/normalizeConfig.js +11 -12
- package/lib/util/normalizeScreens.js +2 -4
- package/lib/util/pluginUtils.js +6 -13
- package/lib/util/resolveConfig.js +9 -18
- package/lib/util/resolveConfigPath.js +1 -2
- package/lib/util/toColorValue.js +1 -2
- package/lib/util/transformThemeValue.js +4 -8
- package/nesting/plugin.js +4 -1
- package/package.json +10 -12
- package/peers/index.js +652 -651
- package/src/corePlugins.js +121 -155
- package/src/css/preflight.css +1 -1
- package/src/featureFlags.js +1 -5
- package/src/lib/expandApplyAtRules.js +0 -42
- package/src/lib/expandTailwindAtRules.js +34 -30
- package/src/lib/generateRules.js +6 -0
- package/src/lib/partitionApplyAtRules.js +52 -0
- package/src/lib/resolveDefaultsAtRules.js +5 -5
- package/src/lib/setupContextUtils.js +37 -17
- package/src/processTailwindFeatures.js +4 -1
- package/src/util/normalizeConfig.js +6 -0
- package/src/util/pluginUtils.js +1 -1
|
@@ -2,6 +2,7 @@ import LRU from 'quick-lru'
|
|
|
2
2
|
import * as sharedState from './sharedState'
|
|
3
3
|
import { generateRules } from './generateRules'
|
|
4
4
|
import bigSign from '../util/bigSign'
|
|
5
|
+
import log from '../util/log'
|
|
5
6
|
import cloneNodes from '../util/cloneNodes'
|
|
6
7
|
import { defaultExtractor } from './defaultExtractor'
|
|
7
8
|
|
|
@@ -129,8 +130,6 @@ function buildStylesheet(rules, context) {
|
|
|
129
130
|
return returnValue
|
|
130
131
|
}
|
|
131
132
|
|
|
132
|
-
export const DEFAULTS_LAYER = Symbol('defaults-layer')
|
|
133
|
-
|
|
134
133
|
export default function expandTailwindAtRules(context) {
|
|
135
134
|
return (root) => {
|
|
136
135
|
let layerNodes = {
|
|
@@ -140,8 +139,6 @@ export default function expandTailwindAtRules(context) {
|
|
|
140
139
|
variants: null,
|
|
141
140
|
}
|
|
142
141
|
|
|
143
|
-
let hasApply = false
|
|
144
|
-
|
|
145
142
|
root.walkAtRules((rule) => {
|
|
146
143
|
// Make sure this file contains Tailwind directives. If not, we can save
|
|
147
144
|
// a lot of work and bail early. Also we don't have to register our touch
|
|
@@ -152,16 +149,9 @@ export default function expandTailwindAtRules(context) {
|
|
|
152
149
|
layerNodes[rule.params] = rule
|
|
153
150
|
}
|
|
154
151
|
}
|
|
155
|
-
|
|
156
|
-
// We also want to check for @apply because the user can
|
|
157
|
-
// apply classes in an isolated environment like CSS
|
|
158
|
-
// modules and we still need to inject defaults
|
|
159
|
-
if (rule.name === 'apply') {
|
|
160
|
-
hasApply = true
|
|
161
|
-
}
|
|
162
152
|
})
|
|
163
153
|
|
|
164
|
-
if (Object.values(layerNodes).every((n) => n === null)
|
|
154
|
+
if (Object.values(layerNodes).every((n) => n === null)) {
|
|
165
155
|
return root
|
|
166
156
|
}
|
|
167
157
|
|
|
@@ -179,6 +169,8 @@ export default function expandTailwindAtRules(context) {
|
|
|
179
169
|
getClassCandidates(transformer(content), extractor, candidates, seen)
|
|
180
170
|
}
|
|
181
171
|
|
|
172
|
+
env.DEBUG && console.timeEnd('Reading changed files')
|
|
173
|
+
|
|
182
174
|
// ---
|
|
183
175
|
|
|
184
176
|
// Generate the actual CSS
|
|
@@ -212,21 +204,7 @@ export default function expandTailwindAtRules(context) {
|
|
|
212
204
|
// Replace any Tailwind directives with generated CSS
|
|
213
205
|
|
|
214
206
|
if (layerNodes.base) {
|
|
215
|
-
layerNodes.base.before(cloneNodes([...baseNodes], layerNodes.base.source))
|
|
216
|
-
}
|
|
217
|
-
|
|
218
|
-
// @defaults rules are unconditionally added first to ensure that
|
|
219
|
-
// using any utility that relies on defaults will work even when
|
|
220
|
-
// compiled in an isolated environment like CSS modules
|
|
221
|
-
if (context.tailwindConfig[DEFAULTS_LAYER] !== false) {
|
|
222
|
-
if (layerNodes.base) {
|
|
223
|
-
layerNodes.base.after(cloneNodes([...defaultNodes], root.source))
|
|
224
|
-
} else {
|
|
225
|
-
root.prepend(cloneNodes([...defaultNodes], root.source))
|
|
226
|
-
}
|
|
227
|
-
}
|
|
228
|
-
|
|
229
|
-
if (layerNodes.base) {
|
|
207
|
+
layerNodes.base.before(cloneNodes([...baseNodes, ...defaultNodes], layerNodes.base.source))
|
|
230
208
|
layerNodes.base.remove()
|
|
231
209
|
}
|
|
232
210
|
|
|
@@ -240,11 +218,37 @@ export default function expandTailwindAtRules(context) {
|
|
|
240
218
|
layerNodes.utilities.remove()
|
|
241
219
|
}
|
|
242
220
|
|
|
221
|
+
// We do post-filtering to not alter the emitted order of the variants
|
|
222
|
+
const variantNodes = Array.from(screenNodes).filter((node) => {
|
|
223
|
+
const parentLayer = node.raws.tailwind?.parentLayer
|
|
224
|
+
|
|
225
|
+
if (parentLayer === 'components') {
|
|
226
|
+
return layerNodes.components !== null
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
if (parentLayer === 'utilities') {
|
|
230
|
+
return layerNodes.utilities !== null
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
return true
|
|
234
|
+
})
|
|
235
|
+
|
|
243
236
|
if (layerNodes.variants) {
|
|
244
|
-
layerNodes.variants.before(cloneNodes(
|
|
237
|
+
layerNodes.variants.before(cloneNodes(variantNodes, layerNodes.variants.source))
|
|
245
238
|
layerNodes.variants.remove()
|
|
246
|
-
} else {
|
|
247
|
-
root.append(cloneNodes(
|
|
239
|
+
} else if (variantNodes.length > 0) {
|
|
240
|
+
root.append(cloneNodes(variantNodes, root.source))
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
// If we've got a utility layer and no utilities are generated there's likely something wrong
|
|
244
|
+
const hasUtilityVariants = variantNodes.some(
|
|
245
|
+
(node) => node.raws.tailwind?.parentLayer === 'utilities'
|
|
246
|
+
)
|
|
247
|
+
|
|
248
|
+
if (layerNodes.utilities && utilityNodes.size === 0 && !hasUtilityVariants) {
|
|
249
|
+
log.warn('content-problems', [
|
|
250
|
+
'No utilities were generated there is likely a problem with the `content` key in the tailwind config. For more information see the documentation: https://tailwindcss.com/docs/content-configuration',
|
|
251
|
+
])
|
|
248
252
|
}
|
|
249
253
|
|
|
250
254
|
// ---
|
package/src/lib/generateRules.js
CHANGED
|
@@ -216,6 +216,12 @@ function applyVariant(variant, matches, context) {
|
|
|
216
216
|
})
|
|
217
217
|
}
|
|
218
218
|
|
|
219
|
+
// This tracks the originating layer for the variant
|
|
220
|
+
// For example:
|
|
221
|
+
// .sm:underline {} is a variant of something in the utilities layer
|
|
222
|
+
// .sm:container {} is a variant of the container component
|
|
223
|
+
clone.nodes[0].raws.tailwind = { parentLayer: meta.layer }
|
|
224
|
+
|
|
219
225
|
let withOffset = [
|
|
220
226
|
{
|
|
221
227
|
...meta,
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
function partitionRules(root) {
|
|
2
|
+
if (!root.walkAtRules) return
|
|
3
|
+
|
|
4
|
+
let applyParents = new Set()
|
|
5
|
+
|
|
6
|
+
root.walkAtRules('apply', (rule) => {
|
|
7
|
+
applyParents.add(rule.parent)
|
|
8
|
+
})
|
|
9
|
+
|
|
10
|
+
if (applyParents.size === 0) {
|
|
11
|
+
return
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
for (let rule of applyParents) {
|
|
15
|
+
let nodeGroups = []
|
|
16
|
+
let lastGroup = []
|
|
17
|
+
|
|
18
|
+
for (let node of rule.nodes) {
|
|
19
|
+
if (node.type === 'atrule' && node.name === 'apply') {
|
|
20
|
+
if (lastGroup.length > 0) {
|
|
21
|
+
nodeGroups.push(lastGroup)
|
|
22
|
+
lastGroup = []
|
|
23
|
+
}
|
|
24
|
+
nodeGroups.push([node])
|
|
25
|
+
} else {
|
|
26
|
+
lastGroup.push(node)
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
if (lastGroup.length > 0) {
|
|
31
|
+
nodeGroups.push(lastGroup)
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
if (nodeGroups.length === 1) {
|
|
35
|
+
continue
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
for (let group of [...nodeGroups].reverse()) {
|
|
39
|
+
let clone = rule.clone({ nodes: [] })
|
|
40
|
+
clone.append(group)
|
|
41
|
+
rule.after(clone)
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
rule.remove()
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
export default function expandApplyAtRules() {
|
|
49
|
+
return (root) => {
|
|
50
|
+
partitionRules(root)
|
|
51
|
+
}
|
|
52
|
+
}
|
|
@@ -113,12 +113,12 @@ export default function resolveDefaultsAtRules({ tailwindConfig }) {
|
|
|
113
113
|
}
|
|
114
114
|
}
|
|
115
115
|
|
|
116
|
-
if (selectorGroups.size === 0) {
|
|
117
|
-
universal.remove()
|
|
118
|
-
continue
|
|
119
|
-
}
|
|
120
|
-
|
|
121
116
|
if (flagEnabled(tailwindConfig, 'optimizeUniversalDefaults')) {
|
|
117
|
+
if (selectorGroups.size === 0) {
|
|
118
|
+
universal.remove()
|
|
119
|
+
continue
|
|
120
|
+
}
|
|
121
|
+
|
|
122
122
|
for (let [, selectors] of selectorGroups) {
|
|
123
123
|
let universalRule = postcss.rule()
|
|
124
124
|
|
|
@@ -89,39 +89,55 @@ function getClasses(selector) {
|
|
|
89
89
|
return parser.transformSync(selector)
|
|
90
90
|
}
|
|
91
91
|
|
|
92
|
-
function extractCandidates(node) {
|
|
92
|
+
function extractCandidates(node, state = { containsNonOnDemandable: false }, depth = 0) {
|
|
93
93
|
let classes = []
|
|
94
94
|
|
|
95
|
+
// Handle normal rules
|
|
95
96
|
if (node.type === 'rule') {
|
|
96
97
|
for (let selector of node.selectors) {
|
|
97
98
|
let classCandidates = getClasses(selector)
|
|
98
99
|
// At least one of the selectors contains non-"on-demandable" candidates.
|
|
99
|
-
if (classCandidates.length === 0)
|
|
100
|
+
if (classCandidates.length === 0) {
|
|
101
|
+
state.containsNonOnDemandable = true
|
|
102
|
+
}
|
|
100
103
|
|
|
101
|
-
|
|
104
|
+
for (let classCandidate of classCandidates) {
|
|
105
|
+
classes.push(classCandidate)
|
|
106
|
+
}
|
|
102
107
|
}
|
|
103
|
-
return classes
|
|
104
108
|
}
|
|
105
109
|
|
|
106
|
-
|
|
110
|
+
// Handle at-rules (which contains nested rules)
|
|
111
|
+
else if (node.type === 'atrule') {
|
|
107
112
|
node.walkRules((rule) => {
|
|
108
|
-
|
|
113
|
+
for (let classCandidate of rule.selectors.flatMap((selector) =>
|
|
114
|
+
getClasses(selector, state, depth + 1)
|
|
115
|
+
)) {
|
|
116
|
+
classes.push(classCandidate)
|
|
117
|
+
}
|
|
109
118
|
})
|
|
110
119
|
}
|
|
111
120
|
|
|
121
|
+
if (depth === 0) {
|
|
122
|
+
return [state.containsNonOnDemandable || classes.length === 0, classes]
|
|
123
|
+
}
|
|
124
|
+
|
|
112
125
|
return classes
|
|
113
126
|
}
|
|
114
127
|
|
|
115
128
|
function withIdentifiers(styles) {
|
|
116
129
|
return parseStyles(styles).flatMap((node) => {
|
|
117
130
|
let nodeMap = new Map()
|
|
118
|
-
let candidates = extractCandidates(node)
|
|
131
|
+
let [containsNonOnDemandableSelectors, candidates] = extractCandidates(node)
|
|
119
132
|
|
|
120
|
-
// If this isn't "on-demandable", assign it a universal candidate.
|
|
121
|
-
if (
|
|
122
|
-
|
|
133
|
+
// If this isn't "on-demandable", assign it a universal candidate to always include it.
|
|
134
|
+
if (containsNonOnDemandableSelectors) {
|
|
135
|
+
candidates.unshift('*')
|
|
123
136
|
}
|
|
124
137
|
|
|
138
|
+
// However, it could be that it also contains "on-demandable" candidates.
|
|
139
|
+
// E.g.: `span, .foo {}`, in that case it should still be possible to use
|
|
140
|
+
// `@apply foo` for example.
|
|
125
141
|
return candidates.map((c) => {
|
|
126
142
|
if (!nodeMap.has(node)) {
|
|
127
143
|
nodeMap.set(node, node)
|
|
@@ -244,7 +260,6 @@ function buildPluginApi(tailwindConfig, context, { variantList, variantMap, offs
|
|
|
244
260
|
|
|
245
261
|
for (let [identifier, rule] of withIdentifiers(groups)) {
|
|
246
262
|
let prefixedIdentifier = prefixIdentifier(identifier, {})
|
|
247
|
-
let offset = offsets.base++
|
|
248
263
|
|
|
249
264
|
if (!context.candidateRuleMap.has(prefixedIdentifier)) {
|
|
250
265
|
context.candidateRuleMap.set(prefixedIdentifier, [])
|
|
@@ -252,7 +267,7 @@ function buildPluginApi(tailwindConfig, context, { variantList, variantMap, offs
|
|
|
252
267
|
|
|
253
268
|
context.candidateRuleMap
|
|
254
269
|
.get(prefixedIdentifier)
|
|
255
|
-
.push([{ sort:
|
|
270
|
+
.push([{ sort: offsets.base++, layer: 'defaults' }, rule])
|
|
256
271
|
}
|
|
257
272
|
},
|
|
258
273
|
addComponents(components, options) {
|
|
@@ -265,7 +280,6 @@ function buildPluginApi(tailwindConfig, context, { variantList, variantMap, offs
|
|
|
265
280
|
|
|
266
281
|
for (let [identifier, rule] of withIdentifiers(components)) {
|
|
267
282
|
let prefixedIdentifier = prefixIdentifier(identifier, options)
|
|
268
|
-
let offset = offsets.components++
|
|
269
283
|
|
|
270
284
|
classList.add(prefixedIdentifier)
|
|
271
285
|
|
|
@@ -275,7 +289,7 @@ function buildPluginApi(tailwindConfig, context, { variantList, variantMap, offs
|
|
|
275
289
|
|
|
276
290
|
context.candidateRuleMap
|
|
277
291
|
.get(prefixedIdentifier)
|
|
278
|
-
.push([{ sort:
|
|
292
|
+
.push([{ sort: offsets.components++, layer: 'components', options }, rule])
|
|
279
293
|
}
|
|
280
294
|
},
|
|
281
295
|
addUtilities(utilities, options) {
|
|
@@ -288,7 +302,6 @@ function buildPluginApi(tailwindConfig, context, { variantList, variantMap, offs
|
|
|
288
302
|
|
|
289
303
|
for (let [identifier, rule] of withIdentifiers(utilities)) {
|
|
290
304
|
let prefixedIdentifier = prefixIdentifier(identifier, options)
|
|
291
|
-
let offset = offsets.utilities++
|
|
292
305
|
|
|
293
306
|
classList.add(prefixedIdentifier)
|
|
294
307
|
|
|
@@ -298,7 +311,7 @@ function buildPluginApi(tailwindConfig, context, { variantList, variantMap, offs
|
|
|
298
311
|
|
|
299
312
|
context.candidateRuleMap
|
|
300
313
|
.get(prefixedIdentifier)
|
|
301
|
-
.push([{ sort:
|
|
314
|
+
.push([{ sort: offsets.utilities++, layer: 'utilities', options }, rule])
|
|
302
315
|
}
|
|
303
316
|
},
|
|
304
317
|
matchUtilities: function (utilities, options) {
|
|
@@ -435,7 +448,14 @@ function trackModified(files, fileModifiedMap) {
|
|
|
435
448
|
let parsed = url.parse(file)
|
|
436
449
|
let pathname = parsed.hash ? parsed.href.replace(parsed.hash, '') : parsed.href
|
|
437
450
|
pathname = parsed.search ? pathname.replace(parsed.search, '') : pathname
|
|
438
|
-
let newModified = fs.statSync(decodeURIComponent(pathname))
|
|
451
|
+
let newModified = fs.statSync(decodeURIComponent(pathname), { throwIfNoEntry: false })?.mtimeMs
|
|
452
|
+
if (!newModified) {
|
|
453
|
+
// It could happen that a file is passed in that doesn't exist. E.g.:
|
|
454
|
+
// postcss-cli will provide you a fake path when reading from stdin. This
|
|
455
|
+
// path then looks like /path-to-your-project/stdin In that case we just
|
|
456
|
+
// want to ignore it and don't track changes at all.
|
|
457
|
+
continue
|
|
458
|
+
}
|
|
439
459
|
|
|
440
460
|
if (!fileModifiedMap.has(file) || newModified > fileModifiedMap.get(file)) {
|
|
441
461
|
changed = true
|
|
@@ -6,6 +6,7 @@ import substituteScreenAtRules from './lib/substituteScreenAtRules'
|
|
|
6
6
|
import resolveDefaultsAtRules from './lib/resolveDefaultsAtRules'
|
|
7
7
|
import collapseAdjacentRules from './lib/collapseAdjacentRules'
|
|
8
8
|
import collapseDuplicateDeclarations from './lib/collapseDuplicateDeclarations'
|
|
9
|
+
import partitionApplyAtRules from './lib/partitionApplyAtRules'
|
|
9
10
|
import detectNesting from './lib/detectNesting'
|
|
10
11
|
import { createContext } from './lib/setupContextUtils'
|
|
11
12
|
import { issueFlagNotices } from './featureFlags'
|
|
@@ -14,6 +15,9 @@ export default function processTailwindFeatures(setupContext) {
|
|
|
14
15
|
return function (root, result) {
|
|
15
16
|
let { tailwindDirectives, applyDirectives } = normalizeTailwindDirectives(root)
|
|
16
17
|
|
|
18
|
+
detectNesting()(root, result)
|
|
19
|
+
partitionApplyAtRules()(root, result)
|
|
20
|
+
|
|
17
21
|
let context = setupContext({
|
|
18
22
|
tailwindDirectives,
|
|
19
23
|
applyDirectives,
|
|
@@ -37,7 +41,6 @@ export default function processTailwindFeatures(setupContext) {
|
|
|
37
41
|
|
|
38
42
|
issueFlagNotices(context.tailwindConfig)
|
|
39
43
|
|
|
40
|
-
detectNesting(context)(root, result)
|
|
41
44
|
expandTailwindAtRules(context)(root, result)
|
|
42
45
|
expandApplyAtRules(context)(root, result)
|
|
43
46
|
evaluateTailwindFunctions(context)(root, result)
|
|
@@ -258,5 +258,11 @@ export function normalizeConfig(config) {
|
|
|
258
258
|
}
|
|
259
259
|
}
|
|
260
260
|
|
|
261
|
+
if (config.content.files.length === 0) {
|
|
262
|
+
log.warn('content-problems', [
|
|
263
|
+
'The `content` key is missing or empty. Please populate the content key as Tailwind generates utilities on-demand based on the files that use them. For more information see the documentation: https://tailwindcss.com/docs/content-configuration',
|
|
264
|
+
])
|
|
265
|
+
}
|
|
266
|
+
|
|
261
267
|
return config
|
|
262
268
|
}
|
package/src/util/pluginUtils.js
CHANGED
|
@@ -185,7 +185,7 @@ export function coerceValue(types, modifier, options, tailwindConfig) {
|
|
|
185
185
|
// Find first matching type
|
|
186
186
|
for (let type of [].concat(types)) {
|
|
187
187
|
let result = typeMap[type](modifier, options, { tailwindConfig })
|
|
188
|
-
if (result) return [result, type]
|
|
188
|
+
if (result !== undefined) return [result, type]
|
|
189
189
|
}
|
|
190
190
|
|
|
191
191
|
return []
|