tailwindcss 3.0.18 → 3.0.19

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.
@@ -9,7 +9,7 @@
9
9
  box-sizing: border-box; /* 1 */
10
10
  border-width: 0; /* 2 */
11
11
  border-style: solid; /* 2 */
12
- border-color: theme('borderColor.DEFAULT', 'currentColor'); /* 2 */
12
+ border-color: theme('borderColor.DEFAULT', currentColor); /* 2 */
13
13
  }
14
14
 
15
15
  ::before,
@@ -25,33 +25,33 @@ function getClassNameFromSelector(selector) {
25
25
  // Example with dynamic classes:
26
26
  // ['grid-cols', '[[linename],1fr,auto]']
27
27
  // ['grid', 'cols-[[linename],1fr,auto]']
28
- function* candidatePermutations(candidate, lastIndex = Infinity) {
29
- if (lastIndex < 0) {
30
- return
31
- }
28
+ function* candidatePermutations(candidate) {
29
+ let lastIndex = Infinity
32
30
 
33
- let dashIdx
31
+ while (lastIndex >= 0) {
32
+ let dashIdx
34
33
 
35
- if (lastIndex === Infinity && candidate.endsWith(']')) {
36
- let bracketIdx = candidate.indexOf('[')
34
+ if (lastIndex === Infinity && candidate.endsWith(']')) {
35
+ let bracketIdx = candidate.indexOf('[')
37
36
 
38
- // If character before `[` isn't a dash or a slash, this isn't a dynamic class
39
- // eg. string[]
40
- dashIdx = ['-', '/'].includes(candidate[bracketIdx - 1]) ? bracketIdx - 1 : -1
41
- } else {
42
- dashIdx = candidate.lastIndexOf('-', lastIndex)
43
- }
37
+ // If character before `[` isn't a dash or a slash, this isn't a dynamic class
38
+ // eg. string[]
39
+ dashIdx = ['-', '/'].includes(candidate[bracketIdx - 1]) ? bracketIdx - 1 : -1
40
+ } else {
41
+ dashIdx = candidate.lastIndexOf('-', lastIndex)
42
+ }
44
43
 
45
- if (dashIdx < 0) {
46
- return
47
- }
44
+ if (dashIdx < 0) {
45
+ break
46
+ }
48
47
 
49
- let prefix = candidate.slice(0, dashIdx)
50
- let modifier = candidate.slice(dashIdx + 1)
48
+ let prefix = candidate.slice(0, dashIdx)
49
+ let modifier = candidate.slice(dashIdx + 1)
51
50
 
52
- yield [prefix, modifier]
51
+ yield [prefix, modifier]
53
52
 
54
- yield* candidatePermutations(candidate, dashIdx - 1)
53
+ lastIndex = dashIdx - 1
54
+ }
55
55
  }
56
56
 
57
57
  function applyPrefix(matches, context) {
@@ -63,9 +63,23 @@ function applyPrefix(matches, context) {
63
63
  let [meta] = match
64
64
  if (meta.options.respectPrefix) {
65
65
  let container = postcss.root({ nodes: [match[1].clone()] })
66
+ let classCandidate = match[1].raws.tailwind.classCandidate
67
+
66
68
  container.walkRules((r) => {
67
- r.selector = prefixSelector(context.tailwindConfig.prefix, r.selector)
69
+ // If this is a negative utility with a dash *before* the prefix we
70
+ // have to ensure that the generated selector matches the candidate
71
+
72
+ // Not doing this will cause `-tw-top-1` to generate the class `.tw--top-1`
73
+ // The disconnect between candidate <-> class can cause @apply to hard crash.
74
+ let shouldPrependNegative = classCandidate.startsWith('-')
75
+
76
+ r.selector = prefixSelector(
77
+ context.tailwindConfig.prefix,
78
+ r.selector,
79
+ shouldPrependNegative
80
+ )
68
81
  })
82
+
69
83
  match[1] = container.nodes[0]
70
84
  }
71
85
  }
@@ -262,11 +276,37 @@ function parseRules(rule, cache, options = {}) {
262
276
  const IS_VALID_PROPERTY_NAME = /^[a-z_-]/
263
277
 
264
278
  function isValidPropName(name) {
265
- // TODO: properly fix this!
266
- return IS_VALID_PROPERTY_NAME.test(name) && !name.startsWith('http')
279
+ return IS_VALID_PROPERTY_NAME.test(name)
280
+ }
281
+
282
+ /**
283
+ * @param {string} declaration
284
+ * @returns {boolean}
285
+ */
286
+ function looksLikeUri(declaration) {
287
+ // Quick bailout for obvious non-urls
288
+ // This doesn't support schemes that don't use a leading // but that's unlikely to be a problem
289
+ if (!declaration.includes('://')) {
290
+ return false
291
+ }
292
+
293
+ try {
294
+ const url = new URL(declaration)
295
+ return url.scheme !== '' && url.host !== ''
296
+ } catch (err) {
297
+ // Definitely not a valid url
298
+ return false
299
+ }
267
300
  }
268
301
 
269
302
  function isParsableCssValue(property, value) {
303
+ // We don't want to to treat [https://example.com] as a custom property
304
+ // Even though, according to the CSS grammar, it's a totally valid CSS declaration
305
+ // So we short-circuit here by checking if the custom property looks like a url
306
+ if (looksLikeUri(`${property}:${value}`)) {
307
+ return false
308
+ }
309
+
270
310
  try {
271
311
  postcss.parse(`a{${property}:${value}}`).toResult()
272
312
  return true
@@ -345,6 +385,14 @@ function splitWithSeparator(input, separator) {
345
385
  return input.split(new RegExp(`\\${separator}(?![^[]*\\])`, 'g'))
346
386
  }
347
387
 
388
+ function* recordCandidates(matches, classCandidate) {
389
+ for (const match of matches) {
390
+ match[1].raws.tailwind = { classCandidate }
391
+
392
+ yield match
393
+ }
394
+ }
395
+
348
396
  function* resolveMatches(candidate, context) {
349
397
  let separator = context.tailwindConfig.separator
350
398
  let [classCandidate, ...variants] = splitWithSeparator(candidate, separator).reverse()
@@ -456,7 +504,9 @@ function* resolveMatches(candidate, context) {
456
504
  continue
457
505
  }
458
506
 
459
- matches = applyPrefix(matches.flat(), context)
507
+ matches = matches.flat()
508
+ matches = Array.from(recordCandidates(matches, classCandidate))
509
+ matches = applyPrefix(matches, context)
460
510
 
461
511
  if (important) {
462
512
  matches = applyImportant(matches, context)
@@ -666,17 +666,30 @@ function registerPlugins(plugins, context) {
666
666
 
667
667
  if (checks.length > 0) {
668
668
  let patternMatchingCount = new Map()
669
+ let prefixLength = context.tailwindConfig.prefix.length
669
670
 
670
671
  for (let util of classList) {
671
672
  let utils = Array.isArray(util)
672
673
  ? (() => {
673
674
  let [utilName, options] = util
674
- let classes = Object.keys(options?.values ?? {}).map((value) =>
675
- formatClass(utilName, value)
676
- )
675
+ let values = Object.keys(options?.values ?? {})
676
+ let classes = values.map((value) => formatClass(utilName, value))
677
677
 
678
678
  if (options?.supportsNegativeValues) {
679
+ // This is the normal negated version
680
+ // e.g. `-inset-1` or `-tw-inset-1`
679
681
  classes = [...classes, ...classes.map((cls) => '-' + cls)]
682
+
683
+ // This is the negated version *after* the prefix
684
+ // e.g. `tw--inset-1`
685
+ // The prefix is already attached to util name
686
+ // So we add the negative after the prefix
687
+ classes = [
688
+ ...classes,
689
+ ...classes.map(
690
+ (cls) => cls.slice(0, prefixLength) + '-' + cls.slice(prefixLength)
691
+ ),
692
+ ]
680
693
  }
681
694
 
682
695
  return classes
@@ -1,7 +1,7 @@
1
1
  let KEYWORDS = new Set(['inset', 'inherit', 'initial', 'revert', 'unset'])
2
2
  let COMMA = /\,(?![^(]*\))/g // Comma separator that is not located between brackets. E.g.: `cubiz-bezier(a, b, c)` these don't count.
3
3
  let SPACE = /\ +(?![^(]*\))/g // Similar to the one above, but with spaces instead.
4
- let LENGTH = /^-?(\d+)(.*?)$/g
4
+ let LENGTH = /^-?(\d+|\.\d+)(.*?)$/g
5
5
 
6
6
  export function parseBoxShadowValue(input) {
7
7
  let shadows = input.split(COMMA)
@@ -1,12 +1,14 @@
1
1
  import parser from 'postcss-selector-parser'
2
- import { tap } from './tap'
3
2
 
4
- export default function (prefix, selector) {
3
+ export default function (prefix, selector, prependNegative = false) {
5
4
  return parser((selectors) => {
6
5
  selectors.walkClasses((classSelector) => {
7
- tap(classSelector.value, (baseClass) => {
8
- classSelector.value = `${prefix}${baseClass}`
9
- })
6
+ let baseClass = classSelector.value
7
+ let shouldPlaceNegativeBeforePrefix = prependNegative && baseClass.startsWith('-')
8
+
9
+ classSelector.value = shouldPlaceNegativeBeforePrefix
10
+ ? `-${prefix}${baseClass.slice(1)}`
11
+ : `${prefix}${baseClass}`
10
12
  })
11
13
  }).processSync(selector)
12
14
  }