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.
- package/CHANGELOG.md +35 -1
- package/LICENSE +1 -2
- package/lib/css/preflight.css +1 -1
- package/lib/lib/expandApplyAtRules.js +7 -4
- package/lib/lib/generateRules.js +72 -27
- package/lib/lib/setupContextUtils.js +55 -2
- package/lib/processTailwindFeatures.js +5 -0
- package/lib/util/parseBoxShadowValue.js +1 -1
- package/lib/util/prefixSelector.js +4 -5
- package/package.json +8 -12
- package/peers/index.js +203 -430
- package/src/css/preflight.css +1 -1
- package/src/lib/expandApplyAtRules.js +5 -5
- package/src/lib/generateRules.js +76 -23
- package/src/lib/setupContextUtils.js +57 -4
- package/src/processTailwindFeatures.js +6 -0
- package/src/util/parseBoxShadowValue.js +1 -1
- package/src/util/prefixSelector.js +7 -5
package/src/css/preflight.css
CHANGED
|
@@ -161,12 +161,12 @@ function processApply(root, context) {
|
|
|
161
161
|
}
|
|
162
162
|
|
|
163
163
|
for (let applyCandidate of applyCandidates) {
|
|
164
|
-
if (
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
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
|
)
|
package/src/lib/generateRules.js
CHANGED
|
@@ -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
|
|
29
|
-
|
|
30
|
-
return
|
|
31
|
-
}
|
|
28
|
+
function* candidatePermutations(candidate) {
|
|
29
|
+
let lastIndex = Infinity
|
|
32
30
|
|
|
33
|
-
|
|
31
|
+
while (lastIndex >= 0) {
|
|
32
|
+
let dashIdx
|
|
34
33
|
|
|
35
|
-
|
|
36
|
-
|
|
34
|
+
if (lastIndex === Infinity && candidate.endsWith(']')) {
|
|
35
|
+
let bracketIdx = candidate.indexOf('[')
|
|
37
36
|
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
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
|
-
|
|
46
|
-
|
|
47
|
-
|
|
44
|
+
if (dashIdx < 0) {
|
|
45
|
+
break
|
|
46
|
+
}
|
|
48
47
|
|
|
49
|
-
|
|
50
|
-
|
|
48
|
+
let prefix = candidate.slice(0, dashIdx)
|
|
49
|
+
let modifier = candidate.slice(dashIdx + 1)
|
|
51
50
|
|
|
52
|
-
|
|
51
|
+
yield [prefix, modifier]
|
|
53
52
|
|
|
54
|
-
|
|
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
|
-
|
|
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 =
|
|
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
|
|
675
|
-
|
|
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
|
-
|
|
8
|
-
|
|
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
|
}
|