tailwindcss 3.0.15 → 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.
- package/CHANGELOG.md +38 -2
- package/LICENSE +1 -2
- package/lib/cli.js +9 -3
- package/lib/corePlugins.js +2 -1
- package/lib/css/preflight.css +1 -1
- package/lib/lib/expandTailwindAtRules.js +2 -1
- package/lib/lib/generateRules.js +66 -27
- package/lib/lib/normalizeTailwindDirectives.js +2 -1
- package/lib/lib/setupContextUtils.js +18 -3
- package/{nesting → lib/postcss-plugins/nesting}/README.md +0 -0
- package/lib/postcss-plugins/nesting/index.js +17 -0
- package/lib/postcss-plugins/nesting/plugin.js +53 -0
- package/lib/processTailwindFeatures.js +5 -0
- package/lib/util/normalizeConfig.js +9 -5
- package/lib/util/parseBoxShadowValue.js +1 -1
- package/lib/util/prefixSelector.js +4 -5
- package/nesting/index.js +2 -12
- package/package.json +10 -15
- package/peers/index.js +951 -1143
- package/src/cli.js +5 -3
- package/src/corePlugins.js +1 -0
- package/src/css/preflight.css +1 -1
- package/src/lib/expandTailwindAtRules.js +2 -1
- package/src/lib/generateRules.js +73 -22
- package/src/lib/normalizeTailwindDirectives.js +1 -0
- package/src/lib/setupContextUtils.js +18 -4
- package/src/postcss-plugins/nesting/README.md +42 -0
- package/src/postcss-plugins/nesting/index.js +13 -0
- package/{nesting → src/postcss-plugins/nesting}/plugin.js +4 -4
- package/src/processTailwindFeatures.js +6 -0
- package/src/util/normalizeConfig.js +7 -5
- package/src/util/parseBoxShadowValue.js +1 -1
- package/src/util/prefixSelector.js +7 -5
package/src/cli.js
CHANGED
|
@@ -437,7 +437,6 @@ async function build() {
|
|
|
437
437
|
|
|
438
438
|
function resolveConfig() {
|
|
439
439
|
let config = configPath ? require(configPath) : {}
|
|
440
|
-
let resolvedConfig = resolveConfigInternal(config)
|
|
441
440
|
|
|
442
441
|
if (args['--purge']) {
|
|
443
442
|
log.warn('purge-flag-deprecated', [
|
|
@@ -450,10 +449,13 @@ async function build() {
|
|
|
450
449
|
}
|
|
451
450
|
|
|
452
451
|
if (args['--content']) {
|
|
453
|
-
|
|
452
|
+
let files = args['--content'].split(/(?<!{[^}]+),/)
|
|
453
|
+
let resolvedConfig = resolveConfigInternal(config, { content: { files } })
|
|
454
|
+
resolvedConfig.content.files = files
|
|
455
|
+
return resolvedConfig
|
|
454
456
|
}
|
|
455
457
|
|
|
456
|
-
return
|
|
458
|
+
return resolveConfigInternal(config)
|
|
457
459
|
}
|
|
458
460
|
|
|
459
461
|
function extractFileGlobs(config) {
|
package/src/corePlugins.js
CHANGED
|
@@ -144,6 +144,7 @@ export let variantPlugins = {
|
|
|
144
144
|
log.warn('darkmode-false', [
|
|
145
145
|
'The `darkMode` option in your Tailwind CSS configuration is set to `false`, which now behaves the same as `media`.',
|
|
146
146
|
'Change `darkMode` to `media` or remove it entirely.',
|
|
147
|
+
'https://tailwindcss.com/docs/upgrade-guide#remove-dark-mode-configuration',
|
|
147
148
|
])
|
|
148
149
|
}
|
|
149
150
|
|
package/src/css/preflight.css
CHANGED
|
@@ -247,7 +247,8 @@ export default function expandTailwindAtRules(context) {
|
|
|
247
247
|
|
|
248
248
|
if (layerNodes.utilities && utilityNodes.size === 0 && !hasUtilityVariants) {
|
|
249
249
|
log.warn('content-problems', [
|
|
250
|
-
'No
|
|
250
|
+
'No utility classes were detected in your source files. If this is unexpected, double-check the `content` option in your Tailwind CSS configuration.',
|
|
251
|
+
'https://tailwindcss.com/docs/content-configuration',
|
|
251
252
|
])
|
|
252
253
|
}
|
|
253
254
|
|
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
|
}
|
|
@@ -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 = { 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)
|
|
@@ -49,6 +49,7 @@ export default function normalizeTailwindDirectives(root) {
|
|
|
49
49
|
log.warn(`${atRule.name}-at-rule-deprecated`, [
|
|
50
50
|
`The \`@${atRule.name}\` directive has been deprecated in Tailwind CSS v3.0.`,
|
|
51
51
|
`Use \`@layer utilities\` or \`@layer components\` instead.`,
|
|
52
|
+
'https://tailwindcss.com/docs/upgrade-guide#replace-variants-with-layer',
|
|
52
53
|
])
|
|
53
54
|
}
|
|
54
55
|
layerDirectives.add(atRule)
|
|
@@ -656,7 +656,7 @@ function registerPlugins(plugins, context) {
|
|
|
656
656
|
log.warn('root-regex', [
|
|
657
657
|
'Regular expressions in `safelist` work differently in Tailwind CSS v3.0.',
|
|
658
658
|
'Update your `safelist` configuration to eliminate this warning.',
|
|
659
|
-
|
|
659
|
+
'https://tailwindcss.com/docs/content-configuration#safelisting-classes',
|
|
660
660
|
])
|
|
661
661
|
continue
|
|
662
662
|
}
|
|
@@ -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
|
|
675
|
-
|
|
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
|
|
@@ -714,6 +727,7 @@ function registerPlugins(plugins, context) {
|
|
|
714
727
|
log.warn([
|
|
715
728
|
`The safelist pattern \`${regex}\` doesn't match any Tailwind CSS classes.`,
|
|
716
729
|
'Fix this pattern or remove it from your `safelist` configuration.',
|
|
730
|
+
'https://tailwindcss.com/docs/content-configuration#safelisting-classes',
|
|
717
731
|
])
|
|
718
732
|
}
|
|
719
733
|
}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
# tailwindcss/nesting
|
|
2
|
+
|
|
3
|
+
This is a PostCSS plugin that wraps [postcss-nested](https://github.com/postcss/postcss-nested) or [postcss-nesting](https://github.com/jonathantneal/postcss-nesting) and acts as a compatibility layer to make sure your nesting plugin of choice properly understands Tailwind's custom syntax like `@apply` and `@screen`.
|
|
4
|
+
|
|
5
|
+
Add it to your PostCSS configuration, somewhere before Tailwind itself:
|
|
6
|
+
|
|
7
|
+
```js
|
|
8
|
+
// postcss.config.js
|
|
9
|
+
module.exports = {
|
|
10
|
+
plugins: [
|
|
11
|
+
require('postcss-import'),
|
|
12
|
+
require('tailwindcss/nesting'),
|
|
13
|
+
require('tailwindcss'),
|
|
14
|
+
require('autoprefixer'),
|
|
15
|
+
]
|
|
16
|
+
}
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
By default, it uses the [postcss-nested](https://github.com/postcss/postcss-nested) plugin under the hood, which uses a Sass-like syntax and is the plugin that powers nesting support in the [Tailwind CSS plugin API](https://tailwindcss.com/docs/plugins#css-in-js-syntax).
|
|
20
|
+
|
|
21
|
+
If you'd rather use [postcss-nesting](https://github.com/jonathantneal/postcss-nesting) (which is based on the work-in-progress [CSS Nesting](https://drafts.csswg.org/css-nesting-1/) specification), first install the plugin alongside:
|
|
22
|
+
|
|
23
|
+
```shell
|
|
24
|
+
npm install postcss-nesting
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
Then pass the plugin itself as an argument to `tailwindcss/nesting` in your PostCSS configuration:
|
|
28
|
+
|
|
29
|
+
```js
|
|
30
|
+
// postcss.config.js
|
|
31
|
+
module.exports = {
|
|
32
|
+
plugins: [
|
|
33
|
+
require('postcss-import'),
|
|
34
|
+
require('tailwindcss/nesting')(require('postcss-nesting')),
|
|
35
|
+
require('tailwindcss'),
|
|
36
|
+
require('autoprefixer'),
|
|
37
|
+
]
|
|
38
|
+
}
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
This can also be helpful if for whatever reason you need to use a very specific version of `postcss-nested` and want to override the version we bundle with `tailwindcss/nesting` itself.
|
|
42
|
+
|
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
1
|
+
import postcss from 'postcss'
|
|
2
|
+
import postcssNested from 'postcss-nested'
|
|
3
3
|
|
|
4
|
-
|
|
4
|
+
export function nesting(opts = postcssNested) {
|
|
5
5
|
return (root, result) => {
|
|
6
6
|
root.walkAtRules('screen', (rule) => {
|
|
7
7
|
rule.name = 'media'
|
|
@@ -16,7 +16,7 @@ module.exports = function nesting(opts = postcssNested) {
|
|
|
16
16
|
let plugin = (() => {
|
|
17
17
|
if (
|
|
18
18
|
typeof opts === 'function' ||
|
|
19
|
-
(typeof opts === 'object' && opts
|
|
19
|
+
(typeof opts === 'object' && opts?.hasOwnProperty?.('postcssPlugin'))
|
|
20
20
|
) {
|
|
21
21
|
return opts
|
|
22
22
|
}
|
|
@@ -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)
|
|
@@ -124,7 +124,7 @@ export function normalizeConfig(config) {
|
|
|
124
124
|
log.warn('purge-deprecation', [
|
|
125
125
|
'The `purge`/`content` options have changed in Tailwind CSS v3.0.',
|
|
126
126
|
'Update your configuration file to eliminate this warning.',
|
|
127
|
-
|
|
127
|
+
'https://tailwindcss.com/docs/upgrade-guide#configure-content-sources',
|
|
128
128
|
])
|
|
129
129
|
}
|
|
130
130
|
|
|
@@ -145,7 +145,7 @@ export function normalizeConfig(config) {
|
|
|
145
145
|
log.warn('prefix-function', [
|
|
146
146
|
'As of Tailwind CSS v3.0, `prefix` cannot be a function.',
|
|
147
147
|
'Update `prefix` in your configuration to be a string to eliminate this warning.',
|
|
148
|
-
|
|
148
|
+
'https://tailwindcss.com/docs/upgrade-guide#prefix-cannot-be-a-function',
|
|
149
149
|
])
|
|
150
150
|
config.prefix = ''
|
|
151
151
|
} else {
|
|
@@ -250,8 +250,8 @@ export function normalizeConfig(config) {
|
|
|
250
250
|
for (let file of config.content.files) {
|
|
251
251
|
if (typeof file === 'string' && /{([^,]*?)}/g.test(file)) {
|
|
252
252
|
log.warn('invalid-glob-braces', [
|
|
253
|
-
`The glob pattern ${dim(file)} in your
|
|
254
|
-
`
|
|
253
|
+
`The glob pattern ${dim(file)} in your Tailwind CSS configuration is invalid.`,
|
|
254
|
+
`Update it to ${dim(file.replace(/{([^,]*?)}/g, '$1'))} to silence this warning.`,
|
|
255
255
|
// TODO: Add https://tw.wtf/invalid-glob-braces
|
|
256
256
|
])
|
|
257
257
|
break
|
|
@@ -260,7 +260,9 @@ export function normalizeConfig(config) {
|
|
|
260
260
|
|
|
261
261
|
if (config.content.files.length === 0) {
|
|
262
262
|
log.warn('content-problems', [
|
|
263
|
-
'The `content`
|
|
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',
|
|
264
266
|
])
|
|
265
267
|
}
|
|
266
268
|
|
|
@@ -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
|
}
|