tailwindcss 3.0.24 → 3.1.2

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 (88) hide show
  1. package/CHANGELOG.md +75 -3
  2. package/colors.d.ts +3 -0
  3. package/defaultConfig.d.ts +3 -0
  4. package/defaultTheme.d.ts +3 -0
  5. package/lib/cli-peer-dependencies.js +8 -3
  6. package/lib/cli.js +125 -83
  7. package/lib/corePluginList.js +1 -0
  8. package/lib/corePlugins.js +146 -117
  9. package/lib/css/preflight.css +1 -8
  10. package/lib/featureFlags.js +8 -6
  11. package/lib/index.js +10 -13
  12. package/lib/lib/cacheInvalidation.js +32 -14
  13. package/lib/lib/collapseAdjacentRules.js +5 -3
  14. package/lib/lib/defaultExtractor.js +191 -32
  15. package/lib/lib/evaluateTailwindFunctions.js +22 -13
  16. package/lib/lib/expandApplyAtRules.js +232 -195
  17. package/lib/lib/expandTailwindAtRules.js +40 -26
  18. package/lib/lib/generateRules.js +106 -42
  19. package/lib/lib/regex.js +52 -0
  20. package/lib/lib/resolveDefaultsAtRules.js +56 -45
  21. package/lib/lib/setupContextUtils.js +131 -79
  22. package/lib/lib/setupTrackingContext.js +7 -9
  23. package/lib/lib/sharedState.js +1 -2
  24. package/lib/lib/substituteScreenAtRules.js +1 -2
  25. package/lib/postcss-plugins/nesting/plugin.js +1 -2
  26. package/lib/util/buildMediaQuery.js +1 -2
  27. package/lib/util/cloneDeep.js +2 -4
  28. package/lib/util/color.js +26 -36
  29. package/lib/util/createPlugin.js +1 -2
  30. package/lib/util/createUtilityPlugin.js +1 -2
  31. package/lib/util/dataTypes.js +14 -12
  32. package/lib/util/flattenColorPalette.js +2 -5
  33. package/lib/util/formatVariantSelector.js +64 -57
  34. package/lib/util/getAllConfigs.js +10 -5
  35. package/lib/util/isValidArbitraryValue.js +1 -2
  36. package/lib/util/log.js +2 -3
  37. package/lib/util/negateValue.js +1 -2
  38. package/lib/util/normalizeConfig.js +33 -23
  39. package/lib/util/normalizeScreens.js +1 -2
  40. package/lib/util/parseAnimationValue.js +1 -2
  41. package/lib/util/parseBoxShadowValue.js +2 -43
  42. package/lib/util/pluginUtils.js +11 -3
  43. package/lib/util/resolveConfig.js +57 -34
  44. package/lib/util/splitAtTopLevelOnly.js +90 -0
  45. package/lib/util/transformThemeValue.js +4 -2
  46. package/lib/util/validateConfig.js +21 -0
  47. package/lib/util/withAlphaVariable.js +5 -5
  48. package/package.json +21 -16
  49. package/peers/index.js +3264 -1330
  50. package/plugin.d.ts +11 -0
  51. package/src/cli-peer-dependencies.js +7 -1
  52. package/src/cli.js +104 -34
  53. package/src/corePluginList.js +1 -1
  54. package/src/corePlugins.js +57 -40
  55. package/src/css/preflight.css +1 -8
  56. package/src/featureFlags.js +2 -2
  57. package/src/index.js +0 -2
  58. package/src/lib/collapseAdjacentRules.js +5 -1
  59. package/src/lib/defaultExtractor.js +177 -35
  60. package/src/lib/evaluateTailwindFunctions.js +20 -4
  61. package/src/lib/expandApplyAtRules.js +247 -188
  62. package/src/lib/expandTailwindAtRules.js +4 -4
  63. package/src/lib/generateRules.js +69 -5
  64. package/src/lib/regex.js +74 -0
  65. package/src/lib/resolveDefaultsAtRules.js +53 -36
  66. package/src/lib/setupContextUtils.js +103 -39
  67. package/src/lib/setupTrackingContext.js +4 -0
  68. package/src/util/color.js +20 -18
  69. package/src/util/dataTypes.js +11 -5
  70. package/src/util/formatVariantSelector.js +79 -62
  71. package/src/util/getAllConfigs.js +7 -0
  72. package/src/util/log.js +1 -1
  73. package/src/util/normalizeConfig.js +0 -8
  74. package/src/util/parseBoxShadowValue.js +3 -50
  75. package/src/util/pluginUtils.js +13 -1
  76. package/src/util/resolveConfig.js +66 -54
  77. package/src/util/splitAtTopLevelOnly.js +71 -0
  78. package/src/util/toPath.js +1 -1
  79. package/src/util/transformThemeValue.js +4 -2
  80. package/src/util/validateConfig.js +13 -0
  81. package/src/util/withAlphaVariable.js +1 -1
  82. package/stubs/defaultConfig.stub.js +2 -3
  83. package/stubs/simpleConfig.stub.js +1 -0
  84. package/types/config.d.ts +325 -0
  85. package/types/generated/.gitkeep +0 -0
  86. package/types/generated/colors.d.ts +276 -0
  87. package/types/generated/corePluginList.d.ts +1 -0
  88. package/types/index.d.ts +7 -0
package/src/util/log.js CHANGED
@@ -3,7 +3,7 @@ import colors from 'picocolors'
3
3
  let alreadyShown = new Set()
4
4
 
5
5
  function log(type, messages, key) {
6
- if (process.env.JEST_WORKER_ID !== undefined) return
6
+ if (typeof process !== 'undefined' && process.env.JEST_WORKER_ID) return
7
7
 
8
8
  if (key && alreadyShown.has(key)) return
9
9
  if (key) alreadyShown.add(key)
@@ -258,13 +258,5 @@ 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` option in your Tailwind CSS configuration is missing or empty.',
264
- 'Configure your content sources or your generated CSS will be missing styles.',
265
- 'https://tailwindcss.com/docs/content-configuration',
266
- ])
267
- }
268
-
269
261
  return config
270
262
  }
@@ -1,58 +1,11 @@
1
+ import { splitAtTopLevelOnly } from './splitAtTopLevelOnly'
2
+
1
3
  let KEYWORDS = new Set(['inset', 'inherit', 'initial', 'revert', 'unset'])
2
4
  let SPACE = /\ +(?![^(]*\))/g // Similar to the one above, but with spaces instead.
3
5
  let LENGTH = /^-?(\d+|\.\d+)(.*?)$/g
4
6
 
5
- let SPECIALS = /[(),]/g
6
-
7
- /**
8
- * This splits a string on top-level commas.
9
- *
10
- * Regex doesn't support recursion (at least not the JS-flavored version).
11
- * So we have to use a tiny state machine to keep track of paren vs comma
12
- * placement. Before we'd only exclude commas from the inner-most nested
13
- * set of parens rather than any commas that were not contained in parens
14
- * at all which is the intended behavior here.
15
- *
16
- * Expected behavior:
17
- * var(--a, 0 0 1px rgb(0, 0, 0)), 0 0 1px rgb(0, 0, 0)
18
- * ─┬─ ┬ ┬ ┬
19
- * x x x ╰──────── Split because top-level
20
- * ╰──────────────┴──┴───────────── Ignored b/c inside >= 1 levels of parens
21
- *
22
- * @param {string} input
23
- */
24
- function* splitByTopLevelCommas(input) {
25
- SPECIALS.lastIndex = -1
26
-
27
- let depth = 0
28
- let lastIndex = 0
29
- let found = false
30
-
31
- // Find all parens & commas
32
- // And only split on commas if they're top-level
33
- for (let match of input.matchAll(SPECIALS)) {
34
- if (match[0] === '(') depth++
35
- if (match[0] === ')') depth--
36
- if (match[0] === ',' && depth === 0) {
37
- found = true
38
-
39
- yield input.substring(lastIndex, match.index)
40
- lastIndex = match.index + match[0].length
41
- }
42
- }
43
-
44
- // Provide the last segment of the string if available
45
- // Otherwise the whole string since no commas were found
46
- // This mirrors the behavior of string.split()
47
- if (found) {
48
- yield input.substring(lastIndex)
49
- } else {
50
- yield input
51
- }
52
- }
53
-
54
7
  export function parseBoxShadowValue(input) {
55
- let shadows = Array.from(splitByTopLevelCommas(input))
8
+ let shadows = Array.from(splitAtTopLevelOnly(input, ','))
56
9
  return shadows.map((shadow) => {
57
10
  let value = shadow.trim()
58
11
  let result = { raw: value }
@@ -95,9 +95,19 @@ function splitAlpha(modifier) {
95
95
  return [modifier.slice(0, slashIdx), modifier.slice(slashIdx + 1)]
96
96
  }
97
97
 
98
+ export function parseColorFormat(value) {
99
+ if (typeof value === 'string' && value.includes('<alpha-value>')) {
100
+ let oldValue = value
101
+
102
+ return ({ opacityValue = 1 }) => oldValue.replace('<alpha-value>', opacityValue)
103
+ }
104
+
105
+ return value
106
+ }
107
+
98
108
  export function asColor(modifier, options = {}, { tailwindConfig = {} } = {}) {
99
109
  if (options.values?.[modifier] !== undefined) {
100
- return options.values?.[modifier]
110
+ return parseColorFormat(options.values?.[modifier])
101
111
  }
102
112
 
103
113
  let [color, alpha] = splitAlpha(modifier)
@@ -110,6 +120,8 @@ export function asColor(modifier, options = {}, { tailwindConfig = {} } = {}) {
110
120
  return undefined
111
121
  }
112
122
 
123
+ normalizedColor = parseColorFormat(normalizedColor)
124
+
113
125
  if (isArbitraryValue(alpha)) {
114
126
  return withAlphaValue(normalizedColor, alpha.slice(1, -1))
115
127
  }
@@ -8,6 +8,9 @@ import { toPath } from './toPath'
8
8
  import { normalizeConfig } from './normalizeConfig'
9
9
  import isPlainObject from './isPlainObject'
10
10
  import { cloneDeep } from './cloneDeep'
11
+ import { parseColorFormat } from './pluginUtils'
12
+ import { withAlphaValue } from './withAlphaVariable'
13
+ import toColorValue from './toColorValue'
11
14
 
12
15
  function isFunction(input) {
13
16
  return typeof input === 'function'
@@ -66,38 +69,6 @@ const configUtils = {
66
69
  {}
67
70
  )
68
71
  },
69
- /*
70
- rgb(property) {
71
- if (!property.startsWith('--')) {
72
- throw new Error(
73
- 'The rgb() helper requires a custom property name to be passed as the first argument.'
74
- )
75
- }
76
-
77
- return ({ opacityValue }) => {
78
- if (opacityValue === undefined || opacityValue === 1) {
79
- return `rgb(var(${property}) / 1.0)`
80
- }
81
-
82
- return `rgb(var(${property}) / ${opacityValue})`
83
- }
84
- },
85
- hsl(property) {
86
- if (!property.startsWith('--')) {
87
- throw new Error(
88
- 'The hsl() helper requires a custom property name to be passed as the first argument.'
89
- )
90
- }
91
-
92
- return ({ opacityValue }) => {
93
- if (opacityValue === undefined || opacityValue === 1) {
94
- return `hsl(var(${property}) / 1)`
95
- }
96
-
97
- return `hsl(var(${property}) / ${opacityValue})`
98
- }
99
- },
100
- */
101
72
  }
102
73
 
103
74
  function value(valueToResolve, ...args) {
@@ -166,40 +137,81 @@ function mergeExtensions({ extend, ...theme }) {
166
137
  })
167
138
  }
168
139
 
140
+ /**
141
+ *
142
+ * @param {string} key
143
+ * @return {Iterable<string[] & {alpha: string | undefined}>}
144
+ */
145
+ function* toPaths(key) {
146
+ let path = toPath(key)
147
+
148
+ if (path.length === 0) {
149
+ return
150
+ }
151
+
152
+ yield path
153
+
154
+ if (Array.isArray(key)) {
155
+ return
156
+ }
157
+
158
+ let pattern = /^(.*?)\s*\/\s*([^/]+)$/
159
+ let matches = key.match(pattern)
160
+
161
+ if (matches !== null) {
162
+ let [, prefix, alpha] = matches
163
+
164
+ let newPath = toPath(prefix)
165
+ newPath.alpha = alpha
166
+
167
+ yield newPath
168
+ }
169
+ }
170
+
169
171
  function resolveFunctionKeys(object) {
172
+ // theme('colors.red.500 / 0.5') -> ['colors', 'red', '500 / 0', '5]
173
+
170
174
  const resolvePath = (key, defaultValue) => {
171
- const path = toPath(key)
175
+ for (const path of toPaths(key)) {
176
+ let index = 0
177
+ let val = object
172
178
 
173
- let index = 0
174
- let val = object
179
+ while (val !== undefined && val !== null && index < path.length) {
180
+ val = val[path[index++]]
175
181
 
176
- while (val !== undefined && val !== null && index < path.length) {
177
- val = val[path[index++]]
178
- val = isFunction(val) ? val(resolvePath, configUtils) : val
179
- }
182
+ let shouldResolveAsFn =
183
+ isFunction(val) && (path.alpha === undefined || index < path.length - 1)
180
184
 
181
- if (val === undefined) {
182
- return defaultValue
183
- }
185
+ val = shouldResolveAsFn ? val(resolvePath, configUtils) : val
186
+ }
184
187
 
185
- if (isPlainObject(val)) {
186
- return cloneDeep(val)
187
- }
188
+ if (val !== undefined) {
189
+ if (path.alpha !== undefined) {
190
+ let normalized = parseColorFormat(val)
188
191
 
189
- return val
190
- }
192
+ return withAlphaValue(normalized, path.alpha, toColorValue(normalized))
193
+ }
191
194
 
192
- resolvePath.theme = resolvePath
195
+ if (isPlainObject(val)) {
196
+ return cloneDeep(val)
197
+ }
198
+
199
+ return val
200
+ }
201
+ }
193
202
 
194
- for (let key in configUtils) {
195
- resolvePath[key] = configUtils[key]
203
+ return defaultValue
196
204
  }
197
205
 
206
+ Object.assign(resolvePath, {
207
+ theme: resolvePath,
208
+ ...configUtils,
209
+ })
210
+
198
211
  return Object.keys(object).reduce((resolved, key) => {
199
- return {
200
- ...resolved,
201
- [key]: isFunction(object[key]) ? object[key](resolvePath, configUtils) : object[key],
202
- }
212
+ resolved[key] = isFunction(object[key]) ? object[key](resolvePath, configUtils) : object[key]
213
+
214
+ return resolved
203
215
  }, {})
204
216
  }
205
217
 
@@ -0,0 +1,71 @@
1
+ import * as regex from '../lib/regex'
2
+
3
+ /**
4
+ * This splits a string on a top-level character.
5
+ *
6
+ * Regex doesn't support recursion (at least not the JS-flavored version).
7
+ * So we have to use a tiny state machine to keep track of paren placement.
8
+ *
9
+ * Expected behavior using commas:
10
+ * var(--a, 0 0 1px rgb(0, 0, 0)), 0 0 1px rgb(0, 0, 0)
11
+ * ─┬─ ┬ ┬ ┬
12
+ * x x x ╰──────── Split because top-level
13
+ * ╰──────────────┴──┴───────────── Ignored b/c inside >= 1 levels of parens
14
+ *
15
+ * @param {string} input
16
+ * @param {string} separator
17
+ */
18
+ export function* splitAtTopLevelOnly(input, separator) {
19
+ let SPECIALS = new RegExp(`[(){}\\[\\]${regex.escape(separator)}]`, 'g')
20
+
21
+ let depth = 0
22
+ let lastIndex = 0
23
+ let found = false
24
+ let separatorIndex = 0
25
+ let separatorStart = 0
26
+ let separatorLength = separator.length
27
+
28
+ // Find all paren-like things & character
29
+ // And only split on commas if they're top-level
30
+ for (let match of input.matchAll(SPECIALS)) {
31
+ let matchesSeparator = match[0] === separator[separatorIndex]
32
+ let atEndOfSeparator = separatorIndex === separatorLength - 1
33
+ let matchesFullSeparator = matchesSeparator && atEndOfSeparator
34
+
35
+ if (match[0] === '(') depth++
36
+ if (match[0] === ')') depth--
37
+ if (match[0] === '[') depth++
38
+ if (match[0] === ']') depth--
39
+ if (match[0] === '{') depth++
40
+ if (match[0] === '}') depth--
41
+
42
+ if (matchesSeparator && depth === 0) {
43
+ if (separatorStart === 0) {
44
+ separatorStart = match.index
45
+ }
46
+
47
+ separatorIndex++
48
+ }
49
+
50
+ if (matchesFullSeparator && depth === 0) {
51
+ found = true
52
+
53
+ yield input.substring(lastIndex, separatorStart)
54
+ lastIndex = separatorStart + separatorLength
55
+ }
56
+
57
+ if (separatorIndex === separatorLength) {
58
+ separatorIndex = 0
59
+ separatorStart = 0
60
+ }
61
+ }
62
+
63
+ // Provide the last segment of the string if available
64
+ // Otherwise the whole string since no `char`s were found
65
+ // This mirrors the behavior of string.split()
66
+ if (found) {
67
+ yield input.substring(lastIndex)
68
+ } else {
69
+ yield input
70
+ }
71
+ }
@@ -4,7 +4,7 @@
4
4
  * Square bracket notation `a[b]` may be used to "escape" dots that would otherwise be interpreted as path separators.
5
5
  *
6
6
  * Example:
7
- * a -> ['a]
7
+ * a -> ['a']
8
8
  * a.b.c -> ['a', 'b', 'c']
9
9
  * a[b].c -> ['a', 'b', 'c']
10
10
  * a[b.c].e.f -> ['a', 'b.c', 'e', 'f']
@@ -44,8 +44,10 @@ export default function transformThemeValue(themeSection) {
44
44
  }
45
45
  }
46
46
 
47
- return (value) => {
48
- if (typeof value === 'function') value = value({})
47
+ return (value, opts = {}) => {
48
+ if (typeof value === 'function') {
49
+ value = value(opts)
50
+ }
49
51
 
50
52
  return value
51
53
  }
@@ -0,0 +1,13 @@
1
+ import log from './log'
2
+
3
+ export function validateConfig(config) {
4
+ if (config.content.files.length === 0) {
5
+ log.warn('content-problems', [
6
+ 'The `content` option in your Tailwind CSS configuration is missing or empty.',
7
+ 'Configure your content sources or your generated CSS will be missing styles.',
8
+ 'https://tailwindcss.com/docs/content-configuration',
9
+ ])
10
+ }
11
+
12
+ return config
13
+ }
@@ -5,7 +5,7 @@ export function withAlphaValue(color, alphaValue, defaultValue) {
5
5
  return color({ opacityValue: alphaValue })
6
6
  }
7
7
 
8
- let parsed = parseColor(color)
8
+ let parsed = parseColor(color, { loose: true })
9
9
 
10
10
  if (parsed === null) {
11
11
  return defaultValue
@@ -1,3 +1,4 @@
1
+ /** @type {import('tailwindcss').Config} */
1
2
  module.exports = {
2
3
  content: [],
3
4
  presets: [],
@@ -194,11 +195,9 @@ module.exports = {
194
195
  '3xl': '1.5rem',
195
196
  full: '9999px',
196
197
  },
197
- /*
198
198
  borderSpacing: ({ theme }) => ({
199
199
  ...theme('spacing'),
200
200
  }),
201
- */
202
201
  borderWidth: {
203
202
  DEFAULT: '1px',
204
203
  0: '0px',
@@ -722,7 +721,7 @@ module.exports = {
722
721
  8: '8px',
723
722
  },
724
723
  ringColor: ({ theme }) => ({
725
- DEFAULT: theme('colors.blue.500', '#3b82f6'),
724
+ DEFAULT: theme(`colors.blue.500`, '#3b82f6'),
726
725
  ...theme('colors'),
727
726
  }),
728
727
  ringOffsetColor: ({ theme }) => theme('colors'),
@@ -1,3 +1,4 @@
1
+ /** @type {import('tailwindcss').Config} */
1
2
  module.exports = {
2
3
  content: [],
3
4
  theme: {