tailwindcss 3.3.0 → 3.3.1
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 +12 -1
- package/lib/lib/expandApplyAtRules.js +10 -0
- package/lib/lib/load-config.js +9 -5
- package/lib/lib/sharedState.js +6 -1
- package/lib/util/applyImportantSelector.js +27 -12
- package/lib/util/formatVariantSelector.js +34 -32
- package/lib/util/normalizeConfig.js +0 -14
- package/lib/util/validateConfig.js +11 -0
- package/package.json +1 -1
- package/src/lib/expandApplyAtRules.js +12 -0
- package/src/lib/load-config.ts +9 -5
- package/src/lib/sharedState.js +15 -6
- package/src/util/applyImportantSelector.js +23 -12
- package/src/util/formatVariantSelector.js +36 -10
- package/src/util/normalizeConfig.js +0 -17
- package/src/util/validateConfig.js +13 -0
- package/types/config.d.ts +1 -6
package/CHANGELOG.md
CHANGED
|
@@ -9,6 +9,16 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
9
9
|
|
|
10
10
|
- Nothing yet!
|
|
11
11
|
|
|
12
|
+
## [3.3.1] - 2023-03-30
|
|
13
|
+
|
|
14
|
+
### Fixed
|
|
15
|
+
|
|
16
|
+
- Try resolving `config.default` before `config` to ensure the config file is resolved correctly ([#10898](https://github.com/tailwindlabs/tailwindcss/pull/10898))
|
|
17
|
+
- Pull pseudo elements outside of `:is` and `:has` when using `@apply` ([#10903](https://github.com/tailwindlabs/tailwindcss/pull/10903))
|
|
18
|
+
- Update the types for the `safelist` config ([#10901](https://github.com/tailwindlabs/tailwindcss/pull/10901))
|
|
19
|
+
- Fix `@tailwindcss/line-clamp` warning ([#10915](https://github.com/tailwindlabs/tailwindcss/pull/10915), [#10919](https://github.com/tailwindlabs/tailwindcss/pull/10919))
|
|
20
|
+
- Fix `process` is not defined error ([#10919](https://github.com/tailwindlabs/tailwindcss/pull/10919))
|
|
21
|
+
|
|
12
22
|
## [3.3.0] - 2023-03-27
|
|
13
23
|
|
|
14
24
|
### Added
|
|
@@ -2211,7 +2221,8 @@ No release notes
|
|
|
2211
2221
|
|
|
2212
2222
|
- Everything!
|
|
2213
2223
|
|
|
2214
|
-
[unreleased]: https://github.com/tailwindlabs/tailwindcss/compare/v3.3.
|
|
2224
|
+
[unreleased]: https://github.com/tailwindlabs/tailwindcss/compare/v3.3.1...HEAD
|
|
2225
|
+
[3.3.1]: https://github.com/tailwindlabs/tailwindcss/compare/v3.3.0...v3.3.1
|
|
2215
2226
|
[3.3.0]: https://github.com/tailwindlabs/tailwindcss/compare/v3.2.7...v3.3.0
|
|
2216
2227
|
[3.2.7]: https://github.com/tailwindlabs/tailwindcss/compare/v3.2.6...v3.2.7
|
|
2217
2228
|
[3.2.6]: https://github.com/tailwindlabs/tailwindcss/compare/v3.2.5...v3.2.6
|
|
@@ -11,6 +11,7 @@ const _postcssSelectorParser = /*#__PURE__*/ _interopRequireDefault(require("pos
|
|
|
11
11
|
const _generateRules = require("./generateRules");
|
|
12
12
|
const _escapeClassName = /*#__PURE__*/ _interopRequireDefault(require("../util/escapeClassName"));
|
|
13
13
|
const _applyImportantSelector = require("../util/applyImportantSelector");
|
|
14
|
+
const _formatVariantSelectorJs = require("../util/formatVariantSelector.js");
|
|
14
15
|
function _interopRequireDefault(obj) {
|
|
15
16
|
return obj && obj.__esModule ? obj : {
|
|
16
17
|
default: obj
|
|
@@ -487,6 +488,15 @@ function processApply(root, context, localCache) {
|
|
|
487
488
|
rule.walkDecls((d)=>{
|
|
488
489
|
d.important = meta.important || important1;
|
|
489
490
|
});
|
|
491
|
+
// Move pseudo elements to the end of the selector (if necessary)
|
|
492
|
+
let selector = (0, _postcssSelectorParser.default)().astSync(rule.selector);
|
|
493
|
+
selector.each((sel)=>{
|
|
494
|
+
let [pseudoElements] = (0, _formatVariantSelectorJs.collectPseudoElements)(sel);
|
|
495
|
+
if (pseudoElements.length > 0) {
|
|
496
|
+
sel.nodes.push(...pseudoElements.sort(_formatVariantSelectorJs.sortSelector));
|
|
497
|
+
}
|
|
498
|
+
});
|
|
499
|
+
rule.selector = selector.toString();
|
|
490
500
|
});
|
|
491
501
|
}
|
|
492
502
|
// It could be that the node we were inserted was removed because the class didn't match
|
package/lib/lib/load-config.js
CHANGED
|
@@ -28,9 +28,13 @@ function lazyJiti() {
|
|
|
28
28
|
});
|
|
29
29
|
}
|
|
30
30
|
function loadConfig(path) {
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
31
|
+
let config = function() {
|
|
32
|
+
try {
|
|
33
|
+
return path ? require(path) : {};
|
|
34
|
+
} catch {
|
|
35
|
+
return lazyJiti()(path);
|
|
36
|
+
}
|
|
37
|
+
}();
|
|
38
|
+
var _config_default;
|
|
39
|
+
return (_config_default = config.default) !== null && _config_default !== void 0 ? _config_default : config;
|
|
36
40
|
}
|
package/lib/lib/sharedState.js
CHANGED
|
@@ -25,11 +25,16 @@ function _interopRequireDefault(obj) {
|
|
|
25
25
|
};
|
|
26
26
|
}
|
|
27
27
|
let OXIDE_DEFAULT_ENABLED = _packageJson.default.tailwindcss.engine === "oxide";
|
|
28
|
-
const env = {
|
|
28
|
+
const env = typeof process !== "undefined" ? {
|
|
29
29
|
NODE_ENV: process.env.NODE_ENV,
|
|
30
30
|
DEBUG: resolveDebug(process.env.DEBUG),
|
|
31
31
|
ENGINE: _packageJson.default.tailwindcss.engine,
|
|
32
32
|
OXIDE: resolveBoolean(process.env.OXIDE, OXIDE_DEFAULT_ENABLED)
|
|
33
|
+
} : {
|
|
34
|
+
NODE_ENV: "production",
|
|
35
|
+
DEBUG: false,
|
|
36
|
+
ENGINE: _packageJson.default.tailwindcss.engine,
|
|
37
|
+
OXIDE: OXIDE_DEFAULT_ENABLED
|
|
33
38
|
};
|
|
34
39
|
const contextMap = new Map();
|
|
35
40
|
const configContextMap = new Map();
|
|
@@ -6,17 +6,32 @@ Object.defineProperty(exports, "applyImportantSelector", {
|
|
|
6
6
|
enumerable: true,
|
|
7
7
|
get: ()=>applyImportantSelector
|
|
8
8
|
});
|
|
9
|
-
const
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
9
|
+
const _postcssSelectorParser = /*#__PURE__*/ _interopRequireDefault(require("postcss-selector-parser"));
|
|
10
|
+
const _formatVariantSelectorJs = require("./formatVariantSelector.js");
|
|
11
|
+
function _interopRequireDefault(obj) {
|
|
12
|
+
return obj && obj.__esModule ? obj : {
|
|
13
|
+
default: obj
|
|
14
|
+
};
|
|
15
15
|
}
|
|
16
|
-
function
|
|
17
|
-
let
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
16
|
+
function applyImportantSelector(selector, important) {
|
|
17
|
+
let sel = (0, _postcssSelectorParser.default)().astSync(selector);
|
|
18
|
+
sel.each((sel)=>{
|
|
19
|
+
// Wrap with :is if it's not already wrapped
|
|
20
|
+
let isWrapped = sel.nodes[0].type === "pseudo" && sel.nodes[0].value === ":is" && sel.nodes.every((node)=>node.type !== "combinator");
|
|
21
|
+
if (!isWrapped) {
|
|
22
|
+
sel.nodes = [
|
|
23
|
+
_postcssSelectorParser.default.pseudo({
|
|
24
|
+
value: ":is",
|
|
25
|
+
nodes: [
|
|
26
|
+
sel.clone()
|
|
27
|
+
]
|
|
28
|
+
})
|
|
29
|
+
];
|
|
30
|
+
}
|
|
31
|
+
let [pseudoElements] = (0, _formatVariantSelectorJs.collectPseudoElements)(sel);
|
|
32
|
+
if (pseudoElements.length > 0) {
|
|
33
|
+
sel.nodes.push(...pseudoElements.sort(_formatVariantSelectorJs.sortSelector));
|
|
34
|
+
}
|
|
35
|
+
});
|
|
36
|
+
return `${important} ${sel.toString()}`;
|
|
22
37
|
}
|
|
@@ -12,7 +12,9 @@ _export(exports, {
|
|
|
12
12
|
formatVariantSelector: ()=>formatVariantSelector,
|
|
13
13
|
eliminateIrrelevantSelectors: ()=>eliminateIrrelevantSelectors,
|
|
14
14
|
finalizeSelector: ()=>finalizeSelector,
|
|
15
|
-
handleMergePseudo: ()=>handleMergePseudo
|
|
15
|
+
handleMergePseudo: ()=>handleMergePseudo,
|
|
16
|
+
collectPseudoElements: ()=>collectPseudoElements,
|
|
17
|
+
sortSelector: ()=>sortSelector
|
|
16
18
|
});
|
|
17
19
|
const _postcssSelectorParser = /*#__PURE__*/ _interopRequireDefault(require("postcss-selector-parser"));
|
|
18
20
|
const _unesc = /*#__PURE__*/ _interopRequireDefault(require("postcss-selector-parser/dist/util/unesc"));
|
|
@@ -202,9 +204,9 @@ function finalizeSelector(current, formats, { context , candidate , base }) {
|
|
|
202
204
|
});
|
|
203
205
|
// Move pseudo elements to the end of the selector (if necessary)
|
|
204
206
|
selector.each((sel)=>{
|
|
205
|
-
let pseudoElements = collectPseudoElements(sel);
|
|
207
|
+
let [pseudoElements] = collectPseudoElements(sel);
|
|
206
208
|
if (pseudoElements.length > 0) {
|
|
207
|
-
sel.nodes.push(pseudoElements.sort(sortSelector));
|
|
209
|
+
sel.nodes.push(...pseudoElements.sort(sortSelector));
|
|
208
210
|
}
|
|
209
211
|
});
|
|
210
212
|
return selector.toString();
|
|
@@ -279,41 +281,38 @@ let pseudoElementExceptions = [
|
|
|
279
281
|
"::-webkit-scrollbar-corner",
|
|
280
282
|
"::-webkit-resizer"
|
|
281
283
|
];
|
|
282
|
-
|
|
283
|
-
* This will make sure to move pseudo's to the correct spot (the end for
|
|
284
|
-
* pseudo elements) because otherwise the selector will never work
|
|
285
|
-
* anyway.
|
|
286
|
-
*
|
|
287
|
-
* E.g.:
|
|
288
|
-
* - `before:hover:text-center` would result in `.before\:hover\:text-center:hover::before`
|
|
289
|
-
* - `hover:before:text-center` would result in `.hover\:before\:text-center:hover::before`
|
|
290
|
-
*
|
|
291
|
-
* `::before:hover` doesn't work, which means that we can make it work for you by flipping the order.
|
|
292
|
-
*
|
|
293
|
-
* @param {Selector} selector
|
|
294
|
-
**/ function collectPseudoElements(selector) {
|
|
284
|
+
function collectPseudoElements(selector, force = false) {
|
|
295
285
|
/** @type {Node[]} */ let nodes = [];
|
|
296
|
-
|
|
297
|
-
|
|
286
|
+
let seenPseudoElement = null;
|
|
287
|
+
for (let node of [
|
|
288
|
+
...selector.nodes
|
|
289
|
+
]){
|
|
290
|
+
if (isPseudoElement(node, force)) {
|
|
298
291
|
nodes.push(node);
|
|
299
292
|
selector.removeChild(node);
|
|
293
|
+
seenPseudoElement = node.value;
|
|
294
|
+
} else if (seenPseudoElement !== null) {
|
|
295
|
+
if (pseudoElementExceptions.includes(seenPseudoElement) && isPseudoClass(node, force)) {
|
|
296
|
+
nodes.push(node);
|
|
297
|
+
selector.removeChild(node);
|
|
298
|
+
} else {
|
|
299
|
+
seenPseudoElement = null;
|
|
300
|
+
}
|
|
300
301
|
}
|
|
301
302
|
if (node === null || node === void 0 ? void 0 : node.nodes) {
|
|
302
|
-
|
|
303
|
+
let hasPseudoElementRestrictions = node.type === "pseudo" && (node.value === ":is" || node.value === ":has");
|
|
304
|
+
let [collected, seenPseudoElementInSelector] = collectPseudoElements(node, force || hasPseudoElementRestrictions);
|
|
305
|
+
if (seenPseudoElementInSelector) {
|
|
306
|
+
seenPseudoElement = seenPseudoElementInSelector;
|
|
307
|
+
}
|
|
308
|
+
nodes.push(...collected);
|
|
303
309
|
}
|
|
304
310
|
}
|
|
305
|
-
return
|
|
311
|
+
return [
|
|
312
|
+
nodes,
|
|
313
|
+
seenPseudoElement
|
|
314
|
+
];
|
|
306
315
|
}
|
|
307
|
-
// This will make sure to move pseudo's to the correct spot (the end for
|
|
308
|
-
// pseudo elements) because otherwise the selector will never work
|
|
309
|
-
// anyway.
|
|
310
|
-
//
|
|
311
|
-
// E.g.:
|
|
312
|
-
// - `before:hover:text-center` would result in `.before\:hover\:text-center:hover::before`
|
|
313
|
-
// - `hover:before:text-center` would result in `.hover\:before\:text-center:hover::before`
|
|
314
|
-
//
|
|
315
|
-
// `::before:hover` doesn't work, which means that we can make it work
|
|
316
|
-
// for you by flipping the order.
|
|
317
316
|
function sortSelector(a, z) {
|
|
318
317
|
// Both nodes are non-pseudo's so we can safely ignore them and keep
|
|
319
318
|
// them in the same order.
|
|
@@ -334,8 +333,11 @@ function sortSelector(a, z) {
|
|
|
334
333
|
// ::file-selector-button) to the right.
|
|
335
334
|
return isPseudoElement(a) - isPseudoElement(z);
|
|
336
335
|
}
|
|
337
|
-
function isPseudoElement(node) {
|
|
336
|
+
function isPseudoElement(node, force = false) {
|
|
338
337
|
if (node.type !== "pseudo") return false;
|
|
339
|
-
if (pseudoElementExceptions.includes(node.value)) return false;
|
|
338
|
+
if (pseudoElementExceptions.includes(node.value) && !force) return false;
|
|
340
339
|
return node.value.startsWith("::") || pseudoElementsBC.includes(node.value);
|
|
341
340
|
}
|
|
341
|
+
function isPseudoClass(node, force) {
|
|
342
|
+
return node.type === "pseudo" && !isPseudoElement(node, force);
|
|
343
|
+
}
|
|
@@ -276,19 +276,5 @@ function normalizeConfig(config) {
|
|
|
276
276
|
break;
|
|
277
277
|
}
|
|
278
278
|
}
|
|
279
|
-
// Warn if the line-clamp plugin is installed
|
|
280
|
-
if (config.plugins.length > 0) {
|
|
281
|
-
let plugin;
|
|
282
|
-
try {
|
|
283
|
-
plugin = require("@tailwindcss/line-clamp");
|
|
284
|
-
} catch {}
|
|
285
|
-
if (plugin && config.plugins.includes(plugin)) {
|
|
286
|
-
_log.default.warn("line-clamp-in-core", [
|
|
287
|
-
"As of Tailwind CSS v3.3, the `@tailwindcss/line-clamp` plugin is now included by default.",
|
|
288
|
-
"Remove it from the `plugins` array in your configuration to eliminate this warning."
|
|
289
|
-
]);
|
|
290
|
-
config.plugins = config.plugins.filter((p)=>p !== plugin);
|
|
291
|
-
}
|
|
292
|
-
}
|
|
293
279
|
return config;
|
|
294
280
|
}
|
|
@@ -20,5 +20,16 @@ function validateConfig(config) {
|
|
|
20
20
|
"https://tailwindcss.com/docs/content-configuration"
|
|
21
21
|
]);
|
|
22
22
|
}
|
|
23
|
+
// Warn if the line-clamp plugin is installed
|
|
24
|
+
try {
|
|
25
|
+
let plugin = require("@tailwindcss/line-clamp");
|
|
26
|
+
if (config.plugins.includes(plugin)) {
|
|
27
|
+
_log.default.warn("line-clamp-in-core", [
|
|
28
|
+
"As of Tailwind CSS v3.3, the `@tailwindcss/line-clamp` plugin is now included by default.",
|
|
29
|
+
"Remove it from the `plugins` array in your configuration to eliminate this warning."
|
|
30
|
+
]);
|
|
31
|
+
config.plugins = config.plugins.filter((p)=>p !== plugin);
|
|
32
|
+
}
|
|
33
|
+
} catch {}
|
|
23
34
|
return config;
|
|
24
35
|
}
|
package/package.json
CHANGED
|
@@ -4,6 +4,7 @@ import parser from 'postcss-selector-parser'
|
|
|
4
4
|
import { resolveMatches } from './generateRules'
|
|
5
5
|
import escapeClassName from '../util/escapeClassName'
|
|
6
6
|
import { applyImportantSelector } from '../util/applyImportantSelector'
|
|
7
|
+
import { collectPseudoElements, sortSelector } from '../util/formatVariantSelector.js'
|
|
7
8
|
|
|
8
9
|
/** @typedef {Map<string, [any, import('postcss').Rule[]]>} ApplyCache */
|
|
9
10
|
|
|
@@ -562,6 +563,17 @@ function processApply(root, context, localCache) {
|
|
|
562
563
|
rule.walkDecls((d) => {
|
|
563
564
|
d.important = meta.important || important
|
|
564
565
|
})
|
|
566
|
+
|
|
567
|
+
// Move pseudo elements to the end of the selector (if necessary)
|
|
568
|
+
let selector = parser().astSync(rule.selector)
|
|
569
|
+
selector.each((sel) => {
|
|
570
|
+
let [pseudoElements] = collectPseudoElements(sel)
|
|
571
|
+
if (pseudoElements.length > 0) {
|
|
572
|
+
sel.nodes.push(...pseudoElements.sort(sortSelector))
|
|
573
|
+
}
|
|
574
|
+
})
|
|
575
|
+
|
|
576
|
+
rule.selector = selector.toString()
|
|
565
577
|
})
|
|
566
578
|
}
|
|
567
579
|
|
package/src/lib/load-config.ts
CHANGED
|
@@ -19,9 +19,13 @@ function lazyJiti() {
|
|
|
19
19
|
}
|
|
20
20
|
|
|
21
21
|
export function loadConfig(path: string): Config {
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
22
|
+
let config = (function () {
|
|
23
|
+
try {
|
|
24
|
+
return path ? require(path) : {}
|
|
25
|
+
} catch {
|
|
26
|
+
return lazyJiti()(path)
|
|
27
|
+
}
|
|
28
|
+
})()
|
|
29
|
+
|
|
30
|
+
return config.default ?? config
|
|
27
31
|
}
|
package/src/lib/sharedState.js
CHANGED
|
@@ -1,12 +1,21 @@
|
|
|
1
1
|
import pkg from '../../package.json'
|
|
2
2
|
let OXIDE_DEFAULT_ENABLED = pkg.tailwindcss.engine === 'oxide'
|
|
3
3
|
|
|
4
|
-
export const env =
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
4
|
+
export const env =
|
|
5
|
+
typeof process !== 'undefined'
|
|
6
|
+
? {
|
|
7
|
+
NODE_ENV: process.env.NODE_ENV,
|
|
8
|
+
DEBUG: resolveDebug(process.env.DEBUG),
|
|
9
|
+
ENGINE: pkg.tailwindcss.engine,
|
|
10
|
+
OXIDE: resolveBoolean(process.env.OXIDE, OXIDE_DEFAULT_ENABLED),
|
|
11
|
+
}
|
|
12
|
+
: {
|
|
13
|
+
NODE_ENV: 'production',
|
|
14
|
+
DEBUG: false,
|
|
15
|
+
ENGINE: pkg.tailwindcss.engine,
|
|
16
|
+
OXIDE: OXIDE_DEFAULT_ENABLED,
|
|
17
|
+
}
|
|
18
|
+
|
|
10
19
|
export const contextMap = new Map()
|
|
11
20
|
export const configContextMap = new Map()
|
|
12
21
|
export const contextSourcesMap = new Map()
|
|
@@ -1,19 +1,30 @@
|
|
|
1
|
-
import
|
|
1
|
+
import parser from 'postcss-selector-parser'
|
|
2
|
+
import { collectPseudoElements, sortSelector } from './formatVariantSelector.js'
|
|
2
3
|
|
|
3
4
|
export function applyImportantSelector(selector, important) {
|
|
4
|
-
let
|
|
5
|
-
if (!matches) return `${important} ${wrapWithIs(selector)}`
|
|
5
|
+
let sel = parser().astSync(selector)
|
|
6
6
|
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
7
|
+
sel.each((sel) => {
|
|
8
|
+
// Wrap with :is if it's not already wrapped
|
|
9
|
+
let isWrapped =
|
|
10
|
+
sel.nodes[0].type === 'pseudo' &&
|
|
11
|
+
sel.nodes[0].value === ':is' &&
|
|
12
|
+
sel.nodes.every((node) => node.type !== 'combinator')
|
|
10
13
|
|
|
11
|
-
|
|
12
|
-
|
|
14
|
+
if (!isWrapped) {
|
|
15
|
+
sel.nodes = [
|
|
16
|
+
parser.pseudo({
|
|
17
|
+
value: ':is',
|
|
18
|
+
nodes: [sel.clone()],
|
|
19
|
+
}),
|
|
20
|
+
]
|
|
21
|
+
}
|
|
13
22
|
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
23
|
+
let [pseudoElements] = collectPseudoElements(sel)
|
|
24
|
+
if (pseudoElements.length > 0) {
|
|
25
|
+
sel.nodes.push(...pseudoElements.sort(sortSelector))
|
|
26
|
+
}
|
|
27
|
+
})
|
|
17
28
|
|
|
18
|
-
return
|
|
29
|
+
return `${important} ${sel.toString()}`
|
|
19
30
|
}
|
|
@@ -246,9 +246,9 @@ export function finalizeSelector(current, formats, { context, candidate, base })
|
|
|
246
246
|
|
|
247
247
|
// Move pseudo elements to the end of the selector (if necessary)
|
|
248
248
|
selector.each((sel) => {
|
|
249
|
-
let pseudoElements = collectPseudoElements(sel)
|
|
249
|
+
let [pseudoElements] = collectPseudoElements(sel)
|
|
250
250
|
if (pseudoElements.length > 0) {
|
|
251
|
-
sel.nodes.push(pseudoElements.sort(sortSelector))
|
|
251
|
+
sel.nodes.push(...pseudoElements.sort(sortSelector))
|
|
252
252
|
}
|
|
253
253
|
})
|
|
254
254
|
|
|
@@ -351,23 +351,45 @@ let pseudoElementExceptions = [
|
|
|
351
351
|
* `::before:hover` doesn't work, which means that we can make it work for you by flipping the order.
|
|
352
352
|
*
|
|
353
353
|
* @param {Selector} selector
|
|
354
|
+
* @param {boolean} force
|
|
354
355
|
**/
|
|
355
|
-
function collectPseudoElements(selector) {
|
|
356
|
+
export function collectPseudoElements(selector, force = false) {
|
|
356
357
|
/** @type {Node[]} */
|
|
357
358
|
let nodes = []
|
|
359
|
+
let seenPseudoElement = null
|
|
358
360
|
|
|
359
|
-
for (let node of selector.nodes) {
|
|
360
|
-
if (isPseudoElement(node)) {
|
|
361
|
+
for (let node of [...selector.nodes]) {
|
|
362
|
+
if (isPseudoElement(node, force)) {
|
|
361
363
|
nodes.push(node)
|
|
362
364
|
selector.removeChild(node)
|
|
365
|
+
seenPseudoElement = node.value
|
|
366
|
+
} else if (seenPseudoElement !== null) {
|
|
367
|
+
if (pseudoElementExceptions.includes(seenPseudoElement) && isPseudoClass(node, force)) {
|
|
368
|
+
nodes.push(node)
|
|
369
|
+
selector.removeChild(node)
|
|
370
|
+
} else {
|
|
371
|
+
seenPseudoElement = null
|
|
372
|
+
}
|
|
363
373
|
}
|
|
364
374
|
|
|
365
375
|
if (node?.nodes) {
|
|
366
|
-
|
|
376
|
+
let hasPseudoElementRestrictions =
|
|
377
|
+
node.type === 'pseudo' && (node.value === ':is' || node.value === ':has')
|
|
378
|
+
|
|
379
|
+
let [collected, seenPseudoElementInSelector] = collectPseudoElements(
|
|
380
|
+
node,
|
|
381
|
+
force || hasPseudoElementRestrictions
|
|
382
|
+
)
|
|
383
|
+
|
|
384
|
+
if (seenPseudoElementInSelector) {
|
|
385
|
+
seenPseudoElement = seenPseudoElementInSelector
|
|
386
|
+
}
|
|
387
|
+
|
|
388
|
+
nodes.push(...collected)
|
|
367
389
|
}
|
|
368
390
|
}
|
|
369
391
|
|
|
370
|
-
return nodes
|
|
392
|
+
return [nodes, seenPseudoElement]
|
|
371
393
|
}
|
|
372
394
|
|
|
373
395
|
// This will make sure to move pseudo's to the correct spot (the end for
|
|
@@ -380,7 +402,7 @@ function collectPseudoElements(selector) {
|
|
|
380
402
|
//
|
|
381
403
|
// `::before:hover` doesn't work, which means that we can make it work
|
|
382
404
|
// for you by flipping the order.
|
|
383
|
-
function sortSelector(a, z) {
|
|
405
|
+
export function sortSelector(a, z) {
|
|
384
406
|
// Both nodes are non-pseudo's so we can safely ignore them and keep
|
|
385
407
|
// them in the same order.
|
|
386
408
|
if (a.type !== 'pseudo' && z.type !== 'pseudo') {
|
|
@@ -404,9 +426,13 @@ function sortSelector(a, z) {
|
|
|
404
426
|
return isPseudoElement(a) - isPseudoElement(z)
|
|
405
427
|
}
|
|
406
428
|
|
|
407
|
-
function isPseudoElement(node) {
|
|
429
|
+
function isPseudoElement(node, force = false) {
|
|
408
430
|
if (node.type !== 'pseudo') return false
|
|
409
|
-
if (pseudoElementExceptions.includes(node.value)) return false
|
|
431
|
+
if (pseudoElementExceptions.includes(node.value) && !force) return false
|
|
410
432
|
|
|
411
433
|
return node.value.startsWith('::') || pseudoElementsBC.includes(node.value)
|
|
412
434
|
}
|
|
435
|
+
|
|
436
|
+
function isPseudoClass(node, force) {
|
|
437
|
+
return node.type === 'pseudo' && !isPseudoElement(node, force)
|
|
438
|
+
}
|
|
@@ -297,22 +297,5 @@ export function normalizeConfig(config) {
|
|
|
297
297
|
}
|
|
298
298
|
}
|
|
299
299
|
|
|
300
|
-
// Warn if the line-clamp plugin is installed
|
|
301
|
-
if (config.plugins.length > 0) {
|
|
302
|
-
let plugin
|
|
303
|
-
try {
|
|
304
|
-
plugin = require('@tailwindcss/line-clamp')
|
|
305
|
-
} catch {}
|
|
306
|
-
|
|
307
|
-
if (plugin && config.plugins.includes(plugin)) {
|
|
308
|
-
log.warn('line-clamp-in-core', [
|
|
309
|
-
'As of Tailwind CSS v3.3, the `@tailwindcss/line-clamp` plugin is now included by default.',
|
|
310
|
-
'Remove it from the `plugins` array in your configuration to eliminate this warning.',
|
|
311
|
-
])
|
|
312
|
-
|
|
313
|
-
config.plugins = config.plugins.filter((p) => p !== plugin)
|
|
314
|
-
}
|
|
315
|
-
}
|
|
316
|
-
|
|
317
300
|
return config
|
|
318
301
|
}
|
|
@@ -9,5 +9,18 @@ export function validateConfig(config) {
|
|
|
9
9
|
])
|
|
10
10
|
}
|
|
11
11
|
|
|
12
|
+
// Warn if the line-clamp plugin is installed
|
|
13
|
+
try {
|
|
14
|
+
let plugin = require('@tailwindcss/line-clamp')
|
|
15
|
+
if (config.plugins.includes(plugin)) {
|
|
16
|
+
log.warn('line-clamp-in-core', [
|
|
17
|
+
'As of Tailwind CSS v3.3, the `@tailwindcss/line-clamp` plugin is now included by default.',
|
|
18
|
+
'Remove it from the `plugins` array in your configuration to eliminate this warning.',
|
|
19
|
+
])
|
|
20
|
+
|
|
21
|
+
config.plugins = config.plugins.filter((p) => p !== plugin)
|
|
22
|
+
}
|
|
23
|
+
} catch {}
|
|
24
|
+
|
|
12
25
|
return config
|
|
13
26
|
}
|
package/types/config.d.ts
CHANGED
|
@@ -46,12 +46,7 @@ type PrefixConfig = string
|
|
|
46
46
|
type SeparatorConfig = string
|
|
47
47
|
|
|
48
48
|
// Safelist related config
|
|
49
|
-
type SafelistConfig =
|
|
50
|
-
| string[]
|
|
51
|
-
| {
|
|
52
|
-
pattern: RegExp
|
|
53
|
-
variants?: string[]
|
|
54
|
-
}[]
|
|
49
|
+
type SafelistConfig = (string | { pattern: RegExp; variants?: string[] })[]
|
|
55
50
|
|
|
56
51
|
// Blocklist related config
|
|
57
52
|
type BlocklistConfig = string[]
|