tailwindcss 3.0.11 → 3.0.15

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.
Files changed (37) hide show
  1. package/CHANGELOG.md +44 -4
  2. package/lib/cli.js +8 -16
  3. package/lib/corePlugins.js +183 -280
  4. package/lib/css/preflight.css +1 -1
  5. package/lib/featureFlags.js +2 -5
  6. package/lib/lib/expandApplyAtRules.js +0 -40
  7. package/lib/lib/expandTailwindAtRules.js +31 -38
  8. package/lib/lib/generateRules.js +8 -2
  9. package/lib/lib/partitionApplyAtRules.js +53 -0
  10. package/lib/lib/resolveDefaultsAtRules.js +4 -4
  11. package/lib/lib/setupContextUtils.js +41 -51
  12. package/lib/processTailwindFeatures.js +3 -1
  13. package/lib/util/createPlugin.js +1 -2
  14. package/lib/util/createUtilityPlugin.js +4 -8
  15. package/lib/util/flattenColorPalette.js +1 -3
  16. package/lib/util/normalizeConfig.js +11 -12
  17. package/lib/util/normalizeScreens.js +2 -4
  18. package/lib/util/pluginUtils.js +6 -13
  19. package/lib/util/resolveConfig.js +9 -18
  20. package/lib/util/resolveConfigPath.js +1 -2
  21. package/lib/util/toColorValue.js +1 -2
  22. package/lib/util/transformThemeValue.js +4 -8
  23. package/nesting/plugin.js +4 -1
  24. package/package.json +9 -11
  25. package/peers/index.js +652 -651
  26. package/src/corePlugins.js +121 -155
  27. package/src/css/preflight.css +1 -1
  28. package/src/featureFlags.js +1 -5
  29. package/src/lib/expandApplyAtRules.js +0 -42
  30. package/src/lib/expandTailwindAtRules.js +33 -29
  31. package/src/lib/generateRules.js +6 -0
  32. package/src/lib/partitionApplyAtRules.js +52 -0
  33. package/src/lib/resolveDefaultsAtRules.js +5 -5
  34. package/src/lib/setupContextUtils.js +29 -16
  35. package/src/processTailwindFeatures.js +4 -1
  36. package/src/util/normalizeConfig.js +6 -0
  37. 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,13 +149,6 @@ 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
154
  if (Object.values(layerNodes).every((n) => n === null)) {
@@ -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([...screenNodes], layerNodes.variants.source))
237
+ layerNodes.variants.before(cloneNodes(variantNodes, layerNodes.variants.source))
245
238
  layerNodes.variants.remove()
246
- } else {
247
- root.append(cloneNodes([...screenNodes], root.source))
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
  // ---
@@ -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) return []
100
+ if (classCandidates.length === 0) {
101
+ state.containsNonOnDemandable = true
102
+ }
100
103
 
101
- classes = [...classes, ...classCandidates]
104
+ for (let classCandidate of classCandidates) {
105
+ classes.push(classCandidate)
106
+ }
102
107
  }
103
- return classes
104
108
  }
105
109
 
106
- if (node.type === 'atrule') {
110
+ // Handle at-rules (which contains nested rules)
111
+ else if (node.type === 'atrule') {
107
112
  node.walkRules((rule) => {
108
- classes = [...classes, ...rule.selectors.flatMap((selector) => getClasses(selector))]
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 (candidates.length === 0) {
122
- return [['*', node]]
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: offset, layer: 'defaults' }, rule])
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: offset, layer: 'components', options }, rule])
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: offset, layer: 'utilities', options }, rule])
314
+ .push([{ sort: offsets.utilities++, layer: 'utilities', options }, rule])
302
315
  }
303
316
  },
304
317
  matchUtilities: function (utilities, options) {
@@ -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
  }
@@ -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 []