tailwindcss 3.3.2 → 3.3.4

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 (65) hide show
  1. package/CHANGELOG.md +50 -1
  2. package/lib/cli/build/plugin.js +2 -2
  3. package/lib/cli/build/watching.js +1 -1
  4. package/lib/corePlugins.js +11 -7
  5. package/lib/css/preflight.css +9 -0
  6. package/lib/lib/defaultExtractor.js +16 -22
  7. package/lib/lib/evaluateTailwindFunctions.js +5 -3
  8. package/lib/lib/expandApplyAtRules.js +6 -0
  9. package/lib/lib/expandTailwindAtRules.js +20 -6
  10. package/lib/lib/generateRules.js +34 -24
  11. package/lib/lib/setupContextUtils.js +53 -33
  12. package/lib/lib/setupTrackingContext.js +2 -1
  13. package/lib/oxide/cli/build/plugin.js +2 -2
  14. package/lib/plugin.js +3 -3
  15. package/lib/processTailwindFeatures.js +2 -2
  16. package/lib/util/color.js +1 -1
  17. package/lib/util/dataTypes.js +100 -16
  18. package/lib/util/formatVariantSelector.js +10 -3
  19. package/lib/util/isPlainObject.js +1 -1
  20. package/lib/util/prefixSelector.js +1 -1
  21. package/lib/util/pseudoElements.js +18 -34
  22. package/lib/value-parser/LICENSE +22 -0
  23. package/lib/value-parser/README.md +3 -0
  24. package/lib/value-parser/index.d.js +2 -0
  25. package/lib/value-parser/index.js +22 -0
  26. package/lib/value-parser/parse.js +259 -0
  27. package/lib/value-parser/stringify.js +38 -0
  28. package/lib/value-parser/unit.js +86 -0
  29. package/lib/value-parser/walk.js +16 -0
  30. package/nesting/index.d.ts +4 -0
  31. package/package.json +5 -6
  32. package/peers/index.js +701 -617
  33. package/src/cli/build/plugin.js +2 -2
  34. package/src/cli/build/watching.js +1 -1
  35. package/src/corePlugins.js +13 -7
  36. package/src/css/preflight.css +9 -0
  37. package/src/featureFlags.js +0 -1
  38. package/src/lib/defaultExtractor.js +12 -13
  39. package/src/lib/evaluateTailwindFunctions.js +4 -1
  40. package/src/lib/expandApplyAtRules.js +7 -0
  41. package/src/lib/expandTailwindAtRules.js +20 -6
  42. package/src/lib/generateRules.js +34 -25
  43. package/src/lib/setupContextUtils.js +50 -35
  44. package/src/lib/setupTrackingContext.js +1 -3
  45. package/src/oxide/cli/build/plugin.ts +2 -2
  46. package/src/plugin.js +3 -3
  47. package/src/processTailwindFeatures.js +3 -2
  48. package/src/util/color.js +1 -1
  49. package/src/util/dataTypes.js +101 -18
  50. package/src/util/formatVariantSelector.js +11 -3
  51. package/src/util/isPlainObject.js +1 -1
  52. package/src/util/prefixSelector.js +1 -0
  53. package/src/util/pseudoElements.js +14 -17
  54. package/src/value-parser/LICENSE +22 -0
  55. package/src/value-parser/README.md +3 -0
  56. package/src/value-parser/index.d.ts +177 -0
  57. package/src/value-parser/index.js +28 -0
  58. package/src/value-parser/parse.js +303 -0
  59. package/src/value-parser/stringify.js +41 -0
  60. package/src/value-parser/unit.js +118 -0
  61. package/src/value-parser/walk.js +18 -0
  62. package/stubs/config.full.js +1 -0
  63. package/types/config.d.ts +6 -6
  64. package/types/generated/default-theme.d.ts +1 -0
  65. package/types/index.d.ts +7 -3
@@ -10,13 +10,34 @@ function isCSSFunction(value) {
10
10
  return cssFunctions.some((fn) => new RegExp(`^${fn}\\(.*\\)`).test(value))
11
11
  }
12
12
 
13
- const placeholder = '--tw-placeholder'
14
- const placeholderRe = new RegExp(placeholder, 'g')
13
+ // These properties accept a `<dashed-ident>` as one of the values. This means that you can use them
14
+ // as: `timeline-scope: --tl;`
15
+ //
16
+ // Without the `var(--tl)`, in these cases we don't want to normalize the value, and you should add
17
+ // the `var()` yourself.
18
+ //
19
+ // More info:
20
+ // - https://drafts.csswg.org/scroll-animations/#propdef-timeline-scope
21
+ // - https://developer.mozilla.org/en-US/docs/Web/CSS/timeline-scope#dashed-ident
22
+ //
23
+ const AUTO_VAR_INJECTION_EXCEPTIONS = new Set([
24
+ // Concrete properties
25
+ 'scroll-timeline-name',
26
+ 'timeline-scope',
27
+ 'view-timeline-name',
28
+ 'font-palette',
29
+
30
+ // Shorthand properties
31
+ 'scroll-timeline',
32
+ 'animation-timeline',
33
+ 'view-timeline',
34
+ ])
15
35
 
16
36
  // This is not a data type, but rather a function that can normalize the
17
37
  // correct values.
18
- export function normalize(value, isRoot = true) {
19
- if (value.startsWith('--')) {
38
+ export function normalize(value, context = null, isRoot = true) {
39
+ let isVarException = context && AUTO_VAR_INJECTION_EXCEPTIONS.has(context.property)
40
+ if (value.startsWith('--') && !isVarException) {
20
41
  return `var(${value})`
21
42
  }
22
43
 
@@ -30,7 +51,7 @@ export function normalize(value, isRoot = true) {
30
51
  return part
31
52
  }
32
53
 
33
- return normalize(part, false)
54
+ return normalize(part, context, false)
34
55
  })
35
56
  .join('')
36
57
  }
@@ -49,22 +70,83 @@ export function normalize(value, isRoot = true) {
49
70
  value = value.trim()
50
71
  }
51
72
 
52
- // Add spaces around operators inside math functions like calc() that do not follow an operator
53
- // or '('.
54
- value = value.replace(/(calc|min|max|clamp)\(.+\)/g, (match) => {
55
- let vars = []
56
- return match
57
- .replace(/var\((--.+?)[,)]/g, (match, g1) => {
58
- vars.push(g1)
59
- return match.replace(g1, placeholder)
60
- })
61
- .replace(/(-?\d*\.?\d(?!\b-\d.+[,)](?![^+\-/*])\D)(?:%|[a-z]+)?|\))([+\-/*])/g, '$1 $2 ')
62
- .replace(placeholderRe, () => vars.shift())
63
- })
73
+ value = normalizeMathOperatorSpacing(value)
64
74
 
65
75
  return value
66
76
  }
67
77
 
78
+ /**
79
+ * Add spaces around operators inside math functions
80
+ * like calc() that do not follow an operator or '('.
81
+ *
82
+ * @param {string} value
83
+ * @returns {string}
84
+ */
85
+ function normalizeMathOperatorSpacing(value) {
86
+ let preventFormattingInFunctions = ['theme']
87
+
88
+ return value.replace(/(calc|min|max|clamp)\(.+\)/g, (match) => {
89
+ let result = ''
90
+
91
+ function lastChar() {
92
+ let char = result.trimEnd()
93
+ return char[char.length - 1]
94
+ }
95
+
96
+ for (let i = 0; i < match.length; i++) {
97
+ function peek(word) {
98
+ return word.split('').every((char, j) => match[i + j] === char)
99
+ }
100
+
101
+ function consumeUntil(chars) {
102
+ let minIndex = Infinity
103
+ for (let char of chars) {
104
+ let index = match.indexOf(char, i)
105
+ if (index !== -1 && index < minIndex) {
106
+ minIndex = index
107
+ }
108
+ }
109
+
110
+ let result = match.slice(i, minIndex)
111
+ i += result.length - 1
112
+ return result
113
+ }
114
+
115
+ let char = match[i]
116
+
117
+ // Handle `var(--variable)`
118
+ if (peek('var')) {
119
+ // When we consume until `)`, then we are dealing with this scenario:
120
+ // `var(--example)`
121
+ //
122
+ // When we consume until `,`, then we are dealing with this scenario:
123
+ // `var(--example, 1rem)`
124
+ //
125
+ // In this case we do want to "format", the default value as well
126
+ result += consumeUntil([')', ','])
127
+ }
128
+
129
+ // Skip formatting inside known functions
130
+ else if (preventFormattingInFunctions.some((fn) => peek(fn))) {
131
+ result += consumeUntil([')'])
132
+ }
133
+
134
+ // Handle operators
135
+ else if (
136
+ ['+', '-', '*', '/'].includes(char) &&
137
+ !['(', '+', '-', '*', '/'].includes(lastChar())
138
+ ) {
139
+ result += ` ${char} `
140
+ } else {
141
+ result += char
142
+ }
143
+ }
144
+
145
+ // Simplify multiple spaces
146
+ return result.replace(/\s+/g, ' ')
147
+ })
148
+ }
149
+
68
150
  export function url(value) {
69
151
  return value.startsWith('url(')
70
152
  }
@@ -178,11 +260,12 @@ export function image(value) {
178
260
  }
179
261
 
180
262
  let gradientTypes = new Set([
263
+ 'conic-gradient',
181
264
  'linear-gradient',
182
265
  'radial-gradient',
266
+ 'repeating-conic-gradient',
183
267
  'repeating-linear-gradient',
184
268
  'repeating-radial-gradient',
185
- 'conic-gradient',
186
269
  ])
187
270
  export function gradient(value) {
188
271
  value = normalize(value)
@@ -3,13 +3,14 @@ import unescape from 'postcss-selector-parser/dist/util/unesc'
3
3
  import escapeClassName from '../util/escapeClassName'
4
4
  import prefixSelector from '../util/prefixSelector'
5
5
  import { movePseudos } from './pseudoElements'
6
+ import { splitAtTopLevelOnly } from './splitAtTopLevelOnly'
6
7
 
7
8
  /** @typedef {import('postcss-selector-parser').Root} Root */
8
9
  /** @typedef {import('postcss-selector-parser').Selector} Selector */
9
10
  /** @typedef {import('postcss-selector-parser').Pseudo} Pseudo */
10
11
  /** @typedef {import('postcss-selector-parser').Node} Node */
11
12
 
12
- /** @typedef {{format: string, isArbitraryVariant: boolean}[]} RawFormats */
13
+ /** @typedef {{format: string, respectPrefix: boolean}[]} RawFormats */
13
14
  /** @typedef {import('postcss-selector-parser').Root} ParsedFormats */
14
15
  /** @typedef {RawFormats | ParsedFormats} AcceptedFormats */
15
16
 
@@ -29,7 +30,7 @@ export function formatVariantSelector(formats, { context, candidate }) {
29
30
 
30
31
  return {
31
32
  ...format,
32
- ast: format.isArbitraryVariant ? ast : prefixSelector(prefix, ast),
33
+ ast: format.respectPrefix ? prefixSelector(prefix, ast) : ast,
33
34
  }
34
35
  })
35
36
 
@@ -160,7 +161,7 @@ export function finalizeSelector(current, formats, { context, candidate, base })
160
161
  // │ │ │ ╰── We will not split here
161
162
  // ╰──┴─────┴─────────────── We will split here
162
163
  //
163
- base = base ?? candidate.split(new RegExp(`\\${separator}(?![^[]*\\])`)).pop()
164
+ base = base ?? splitAtTopLevelOnly(candidate, separator).pop()
164
165
 
165
166
  // Parse the selector into an AST
166
167
  let selector = selectorParser().astSync(current)
@@ -185,6 +186,13 @@ export function finalizeSelector(current, formats, { context, candidate, base })
185
186
  // Remove extraneous selectors that do not include the base candidate
186
187
  selector.each((sel) => eliminateIrrelevantSelectors(sel, base))
187
188
 
189
+ // If ffter eliminating irrelevant selectors, we end up with nothing
190
+ // Then the whole "rule" this is associated with does not need to exist
191
+ // We use `null` as a marker value for that case
192
+ if (selector.length === 0) {
193
+ return null
194
+ }
195
+
188
196
  // If there are no formats that means there were no variants added to the candidate
189
197
  // so we can just return the selector as-is
190
198
  let formatAst = Array.isArray(formats)
@@ -4,5 +4,5 @@ export default function isPlainObject(value) {
4
4
  }
5
5
 
6
6
  const prototype = Object.getPrototypeOf(value)
7
- return prototype === null || prototype === Object.prototype
7
+ return prototype === null || Object.getPrototypeOf(prototype) === null
8
8
  }
@@ -17,6 +17,7 @@ export default function (prefix, selector, prependNegative = false) {
17
17
  return selector
18
18
  }
19
19
 
20
+ /** @type {import('postcss-selector-parser').Root} */
20
21
  let ast = typeof selector === 'string' ? parser().astSync(selector) : selector
21
22
 
22
23
  ast.walkClasses((classSelector) => {
@@ -19,40 +19,37 @@
19
19
  // **Jumpable**
20
20
  // Any terminal element may "jump" over combinators when moving to the end of the selector
21
21
  //
22
- // This is a backwards-compat quirk of :before and :after variants.
22
+ // This is a backwards-compat quirk of pseudo element variants from earlier versions of Tailwind CSS.
23
23
 
24
24
  /** @typedef {'terminal' | 'actionable' | 'jumpable'} PseudoProperty */
25
25
 
26
26
  /** @type {Record<string, PseudoProperty[]>} */
27
27
  let elementProperties = {
28
+ // Pseudo elements from the spec
28
29
  '::after': ['terminal', 'jumpable'],
29
- '::backdrop': ['terminal'],
30
+ '::backdrop': ['terminal', 'jumpable'],
30
31
  '::before': ['terminal', 'jumpable'],
31
32
  '::cue': ['terminal'],
32
33
  '::cue-region': ['terminal'],
33
34
  '::first-letter': ['terminal', 'jumpable'],
34
35
  '::first-line': ['terminal', 'jumpable'],
35
36
  '::grammar-error': ['terminal'],
36
- '::marker': ['terminal'],
37
+ '::marker': ['terminal', 'jumpable'],
37
38
  '::part': ['terminal', 'actionable'],
38
- '::placeholder': ['terminal'],
39
- '::selection': ['terminal'],
39
+ '::placeholder': ['terminal', 'jumpable'],
40
+ '::selection': ['terminal', 'jumpable'],
40
41
  '::slotted': ['terminal'],
41
42
  '::spelling-error': ['terminal'],
42
43
  '::target-text': ['terminal'],
43
44
 
44
- // other
45
+ // Pseudo elements from the spec with special rules
45
46
  '::file-selector-button': ['terminal', 'actionable'],
46
- '::-webkit-progress-bar': ['terminal', 'actionable'],
47
47
 
48
- // Webkit scroll bar pseudo elements can be combined with user-action pseudo classes
49
- '::-webkit-scrollbar': ['terminal', 'actionable'],
50
- '::-webkit-scrollbar-button': ['terminal', 'actionable'],
51
- '::-webkit-scrollbar-thumb': ['terminal', 'actionable'],
52
- '::-webkit-scrollbar-track': ['terminal', 'actionable'],
53
- '::-webkit-scrollbar-track-piece': ['terminal', 'actionable'],
54
- '::-webkit-scrollbar-corner': ['terminal', 'actionable'],
55
- '::-webkit-resizer': ['terminal', 'actionable'],
48
+ // Library-specific pseudo elements used by component libraries
49
+ // These are Shadow DOM-like
50
+ '::deep': ['actionable'],
51
+ '::v-deep': ['actionable'],
52
+ '::ng-deep': ['actionable'],
56
53
 
57
54
  // Note: As a rule, double colons (::) should be used instead of a single colon
58
55
  // (:). This distinguishes pseudo-classes from pseudo-elements. However, since
@@ -65,8 +62,8 @@ let elementProperties = {
65
62
 
66
63
  // The default value is used when the pseudo-element is not recognized
67
64
  // Because it's not recognized, we don't know if it's terminal or not
68
- // So we assume it can't be moved AND can have user-action pseudo classes attached to it
69
- __default__: ['actionable'],
65
+ // So we assume it can be moved AND can have user-action pseudo classes attached to it
66
+ __default__: ['terminal', 'actionable'],
70
67
  }
71
68
 
72
69
  /**
@@ -0,0 +1,22 @@
1
+ Copyright (c) Bogdan Chadkin <trysound@yandex.ru>
2
+
3
+ Permission is hereby granted, free of charge, to any person
4
+ obtaining a copy of this software and associated documentation
5
+ files (the "Software"), to deal in the Software without
6
+ restriction, including without limitation the rights to use,
7
+ copy, modify, merge, publish, distribute, sublicense, and/or sell
8
+ copies of the Software, and to permit persons to whom the
9
+ Software is furnished to do so, subject to the following
10
+ conditions:
11
+
12
+ The above copyright notice and this permission notice shall be
13
+ included in all copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
16
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
17
+ OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
18
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
19
+ HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
20
+ WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
21
+ FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
22
+ OTHER DEALINGS IN THE SOFTWARE.
@@ -0,0 +1,3 @@
1
+ # postcss-value-parser (forked + inlined)
2
+
3
+ This is a customized version of of [PostCSS Value Parser](https://github.com/TrySound/postcss-value-parser) to fix some bugs around parsing CSS functions.
@@ -0,0 +1,177 @@
1
+ declare namespace postcssValueParser {
2
+ interface BaseNode {
3
+ /**
4
+ * The offset, inclusive, inside the CSS value at which the node starts.
5
+ */
6
+ sourceIndex: number
7
+
8
+ /**
9
+ * The offset, exclusive, inside the CSS value at which the node ends.
10
+ */
11
+ sourceEndIndex: number
12
+
13
+ /**
14
+ * The node's characteristic value
15
+ */
16
+ value: string
17
+ }
18
+
19
+ interface ClosableNode {
20
+ /**
21
+ * Whether the parsed CSS value ended before the node was properly closed
22
+ */
23
+ unclosed?: true
24
+ }
25
+
26
+ interface AdjacentAwareNode {
27
+ /**
28
+ * The token at the start of the node
29
+ */
30
+ before: string
31
+
32
+ /**
33
+ * The token at the end of the node
34
+ */
35
+ after: string
36
+ }
37
+
38
+ interface CommentNode extends BaseNode, ClosableNode {
39
+ type: 'comment'
40
+ }
41
+
42
+ interface DivNode extends BaseNode, AdjacentAwareNode {
43
+ type: 'div'
44
+ }
45
+
46
+ interface FunctionNode extends BaseNode, ClosableNode, AdjacentAwareNode {
47
+ type: 'function'
48
+
49
+ /**
50
+ * Nodes inside the function
51
+ */
52
+ nodes: Node[]
53
+ }
54
+
55
+ interface SpaceNode extends BaseNode {
56
+ type: 'space'
57
+ }
58
+
59
+ interface StringNode extends BaseNode, ClosableNode {
60
+ type: 'string'
61
+
62
+ /**
63
+ * The quote type delimiting the string
64
+ */
65
+ quote: '"' | "'"
66
+ }
67
+
68
+ interface UnicodeRangeNode extends BaseNode {
69
+ type: 'unicode-range'
70
+ }
71
+
72
+ interface WordNode extends BaseNode {
73
+ type: 'word'
74
+ }
75
+
76
+ /**
77
+ * Any node parsed from a CSS value
78
+ */
79
+ type Node =
80
+ | CommentNode
81
+ | DivNode
82
+ | FunctionNode
83
+ | SpaceNode
84
+ | StringNode
85
+ | UnicodeRangeNode
86
+ | WordNode
87
+
88
+ interface CustomStringifierCallback {
89
+ /**
90
+ * @param node The node to stringify
91
+ * @returns The serialized CSS representation of the node
92
+ */
93
+ (nodes: Node): string | undefined
94
+ }
95
+
96
+ interface WalkCallback {
97
+ /**
98
+ * @param node The currently visited node
99
+ * @param index The index of the node in the series of parsed nodes
100
+ * @param nodes The series of parsed nodes
101
+ * @returns Returning `false` will prevent traversal of descendant nodes (only applies if `bubble` was set to `true` in the `walk()` call)
102
+ */
103
+ (node: Node, index: number, nodes: Node[]): void | boolean
104
+ }
105
+
106
+ /**
107
+ * A CSS dimension, decomposed into its numeric and unit parts
108
+ */
109
+ interface Dimension {
110
+ number: string
111
+ unit: string
112
+ }
113
+
114
+ /**
115
+ * A wrapper around a parsed CSS value that allows for inspecting and walking nodes
116
+ */
117
+ interface ParsedValue {
118
+ /**
119
+ * The series of parsed nodes
120
+ */
121
+ nodes: Node[]
122
+
123
+ /**
124
+ * Walk all parsed nodes, applying a callback
125
+ *
126
+ * @param callback A visitor callback that will be executed for each node
127
+ * @param bubble When set to `true`, walking will be done inside-out instead of outside-in
128
+ */
129
+ walk(callback: WalkCallback, bubble?: boolean): this
130
+ }
131
+
132
+ interface ValueParser {
133
+ /**
134
+ * Decompose a CSS dimension into its numeric and unit part
135
+ *
136
+ * @param value The dimension to decompose
137
+ * @returns An object representing `number` and `unit` part of the dimension or `false` if the decomposing fails
138
+ */
139
+ unit(value: string): Dimension | false
140
+
141
+ /**
142
+ * Serialize a series of nodes into a CSS value
143
+ *
144
+ * @param nodes The nodes to stringify
145
+ * @param custom A custom stringifier callback
146
+ * @returns The generated CSS value
147
+ */
148
+ stringify(nodes: Node | Node[], custom?: CustomStringifierCallback): string
149
+
150
+ /**
151
+ * Walk a series of nodes, applying a callback
152
+ *
153
+ * @param nodes The nodes to walk
154
+ * @param callback A visitor callback that will be executed for each node
155
+ * @param bubble When set to `true`, walking will be done inside-out instead of outside-in
156
+ */
157
+ walk(nodes: Node[], callback: WalkCallback, bubble?: boolean): void
158
+
159
+ /**
160
+ * Parse a CSS value into a series of nodes to operate on
161
+ *
162
+ * @param value The value to parse
163
+ */
164
+ new (value: string): ParsedValue
165
+
166
+ /**
167
+ * Parse a CSS value into a series of nodes to operate on
168
+ *
169
+ * @param value The value to parse
170
+ */
171
+ (value: string): ParsedValue
172
+ }
173
+ }
174
+
175
+ declare const postcssValueParser: postcssValueParser.ValueParser
176
+
177
+ export = postcssValueParser
@@ -0,0 +1,28 @@
1
+ var parse = require('./parse')
2
+ var walk = require('./walk')
3
+ var stringify = require('./stringify')
4
+
5
+ function ValueParser(value) {
6
+ if (this instanceof ValueParser) {
7
+ this.nodes = parse(value)
8
+ return this
9
+ }
10
+ return new ValueParser(value)
11
+ }
12
+
13
+ ValueParser.prototype.toString = function () {
14
+ return Array.isArray(this.nodes) ? stringify(this.nodes) : ''
15
+ }
16
+
17
+ ValueParser.prototype.walk = function (cb, bubble) {
18
+ walk(this.nodes, cb, bubble)
19
+ return this
20
+ }
21
+
22
+ ValueParser.unit = require('./unit')
23
+
24
+ ValueParser.walk = walk
25
+
26
+ ValueParser.stringify = stringify
27
+
28
+ module.exports = ValueParser