tailwindcss 3.0.17 → 3.0.21

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,
@@ -161,12 +161,12 @@ function processApply(root, context) {
161
161
  }
162
162
 
163
163
  for (let applyCandidate of applyCandidates) {
164
- if (!applyClassCache.has(applyCandidate)) {
165
- if (applyCandidate === prefix(context, 'group')) {
166
- // TODO: Link to specific documentation page with error code.
167
- throw apply.error(`@apply should not be used with the '${applyCandidate}' utility`)
168
- }
164
+ if ([prefix(context, 'group'), prefix(context, 'peer')].includes(applyCandidate)) {
165
+ // TODO: Link to specific documentation page with error code.
166
+ throw apply.error(`@apply should not be used with the '${applyCandidate}' utility`)
167
+ }
169
168
 
169
+ if (!applyClassCache.has(applyCandidate)) {
170
170
  throw apply.error(
171
171
  `The \`${applyCandidate}\` class does not exist. If \`${applyCandidate}\` is a custom class, make sure it is defined within a \`@layer\` directive.`
172
172
  )
@@ -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
  }
@@ -220,7 +234,7 @@ function applyVariant(variant, matches, context) {
220
234
  // For example:
221
235
  // .sm:underline {} is a variant of something in the utilities layer
222
236
  // .sm:container {} is a variant of the container component
223
- clone.nodes[0].raws.tailwind = { parentLayer: meta.layer }
237
+ clone.nodes[0].raws.tailwind = { ...clone.nodes[0].raws.tailwind, parentLayer: meta.layer }
224
238
 
225
239
  let withOffset = [
226
240
  {
@@ -265,7 +279,34 @@ function isValidPropName(name) {
265
279
  return IS_VALID_PROPERTY_NAME.test(name)
266
280
  }
267
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
+ }
300
+ }
301
+
268
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
+
269
310
  try {
270
311
  postcss.parse(`a{${property}:${value}}`).toResult()
271
312
  return true
@@ -344,6 +385,14 @@ function splitWithSeparator(input, separator) {
344
385
  return input.split(new RegExp(`\\${separator}(?![^[]*\\])`, 'g'))
345
386
  }
346
387
 
388
+ function* recordCandidates(matches, classCandidate) {
389
+ for (const match of matches) {
390
+ match[1].raws.tailwind = { ...match[1].raws.tailwind, classCandidate }
391
+
392
+ yield match
393
+ }
394
+ }
395
+
347
396
  function* resolveMatches(candidate, context) {
348
397
  let separator = context.tailwindConfig.separator
349
398
  let [classCandidate, ...variants] = splitWithSeparator(candidate, separator).reverse()
@@ -455,7 +504,9 @@ function* resolveMatches(candidate, context) {
455
504
  continue
456
505
  }
457
506
 
458
- matches = applyPrefix(matches.flat(), context)
507
+ matches = matches.flat()
508
+ matches = Array.from(recordCandidates(matches, classCandidate))
509
+ matches = applyPrefix(matches, context)
459
510
 
460
511
  if (important) {
461
512
  matches = applyImportant(matches, context)
@@ -466,6 +517,8 @@ function* resolveMatches(candidate, context) {
466
517
  }
467
518
 
468
519
  for (let match of matches) {
520
+ match[1].raws.tailwind = { ...match[1].raws.tailwind, candidate }
521
+
469
522
  // Apply final format selector
470
523
  if (match[0].collectedFormats) {
471
524
  let finalFormat = formatVariantSelector('&', ...match[0].collectedFormats)
@@ -19,6 +19,12 @@ import { toPath } from '../util/toPath'
19
19
  import log from '../util/log'
20
20
  import negateValue from '../util/negateValue'
21
21
  import isValidArbitraryValue from '../util/isValidArbitraryValue'
22
+ import { generateRules } from './generateRules'
23
+
24
+ function prefix(context, selector) {
25
+ let prefix = context.tailwindConfig.prefix
26
+ return typeof prefix === 'function' ? prefix(selector) : prefix + selector
27
+ }
22
28
 
23
29
  function parseVariantFormatString(input) {
24
30
  if (input.includes('{')) {
@@ -666,17 +672,30 @@ function registerPlugins(plugins, context) {
666
672
 
667
673
  if (checks.length > 0) {
668
674
  let patternMatchingCount = new Map()
675
+ let prefixLength = context.tailwindConfig.prefix.length
669
676
 
670
677
  for (let util of classList) {
671
678
  let utils = Array.isArray(util)
672
679
  ? (() => {
673
680
  let [utilName, options] = util
674
- let classes = Object.keys(options?.values ?? {}).map((value) =>
675
- formatClass(utilName, value)
676
- )
681
+ let values = Object.keys(options?.values ?? {})
682
+ let classes = values.map((value) => formatClass(utilName, value))
677
683
 
678
684
  if (options?.supportsNegativeValues) {
685
+ // This is the normal negated version
686
+ // e.g. `-inset-1` or `-tw-inset-1`
679
687
  classes = [...classes, ...classes.map((cls) => '-' + cls)]
688
+
689
+ // This is the negated version *after* the prefix
690
+ // e.g. `tw--inset-1`
691
+ // The prefix is already attached to util name
692
+ // So we add the negative after the prefix
693
+ classes = [
694
+ ...classes,
695
+ ...classes.map(
696
+ (cls) => cls.slice(0, prefixLength) + '-' + cls.slice(prefixLength)
697
+ ),
698
+ ]
680
699
  }
681
700
 
682
701
  return classes
@@ -720,9 +739,43 @@ function registerPlugins(plugins, context) {
720
739
  }
721
740
  }
722
741
 
742
+ // A list of utilities that are used by certain Tailwind CSS utilities but
743
+ // that don't exist on their own. This will result in them "not existing" and
744
+ // sorting could be weird since you still require them in order to make the
745
+ // host utitlies work properly. (Thanks Biology)
746
+ let parasiteUtilities = new Set([prefix(context, 'group'), prefix(context, 'peer')])
747
+ context.sortClassList = function sortClassList(classes) {
748
+ let sortedClassNames = new Map()
749
+ for (let [sort, rule] of generateRules(new Set(classes), context)) {
750
+ if (sortedClassNames.has(rule.raws.tailwind.candidate)) continue
751
+ sortedClassNames.set(rule.raws.tailwind.candidate, sort)
752
+ }
753
+
754
+ return classes
755
+ .map((className) => {
756
+ let order = sortedClassNames.get(className) ?? null
757
+
758
+ if (order === null && parasiteUtilities.has(className)) {
759
+ // This will make sure that it is at the very beginning of the
760
+ // `components` layer which technically means 'before any
761
+ // components'.
762
+ order = context.layerOrder.components
763
+ }
764
+
765
+ return [className, order]
766
+ })
767
+ .sort(([, a], [, z]) => {
768
+ if (a === z) return 0
769
+ if (a === null) return -1
770
+ if (z === null) return 1
771
+ return bigSign(a - z)
772
+ })
773
+ .map(([className]) => className)
774
+ }
775
+
723
776
  // Generate a list of strings for autocompletion purposes, e.g.
724
777
  // ['uppercase', 'lowercase', ...]
725
- context.getClassList = function () {
778
+ context.getClassList = function getClassList() {
726
779
  let output = []
727
780
 
728
781
  for (let util of classList) {
@@ -16,6 +16,9 @@ export default function processTailwindFeatures(setupContext) {
16
16
  let { tailwindDirectives, applyDirectives } = normalizeTailwindDirectives(root)
17
17
 
18
18
  detectNesting()(root, result)
19
+
20
+ // Partition apply rules that are found in the css
21
+ // itself.
19
22
  partitionApplyAtRules()(root, result)
20
23
 
21
24
  let context = setupContext({
@@ -42,6 +45,9 @@ export default function processTailwindFeatures(setupContext) {
42
45
  issueFlagNotices(context.tailwindConfig)
43
46
 
44
47
  expandTailwindAtRules(context)(root, result)
48
+ // Partition apply rules that are generated by
49
+ // addComponents, addUtilities and so on.
50
+ partitionApplyAtRules()(root, result)
45
51
  expandApplyAtRules(context)(root, result)
46
52
  evaluateTailwindFunctions(context)(root, result)
47
53
  substituteScreenAtRules(context)(root, result)
@@ -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
  }