tailwindcss 0.0.0-insiders.eb2fe94 → 0.0.0-insiders.eb40518
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/lib/corePlugins.js +58 -5
- package/lib/css/preflight.css +3 -3
- package/lib/lib/content.js +36 -3
- package/lib/lib/defaultExtractor.js +17 -3
- package/lib/lib/expandTailwindAtRules.js +3 -0
- package/lib/lib/generateRules.js +12 -0
- package/lib/lib/load-config.js +14 -3
- package/lib/lib/resolveDefaultsAtRules.js +3 -1
- package/lib/lib/setupContextUtils.js +22 -5
- package/lib/util/cloneNodes.js +33 -13
- package/lib/util/dataTypes.js +11 -1
- package/lib/util/pluginUtils.js +13 -0
- package/lib/util/pseudoElements.js +3 -0
- package/package.json +12 -12
- package/src/corePlugins.js +51 -5
- package/src/css/preflight.css +3 -3
- package/src/lib/content.js +42 -1
- package/src/lib/defaultExtractor.js +18 -3
- package/src/lib/expandTailwindAtRules.js +3 -0
- package/src/lib/generateRules.js +15 -0
- package/src/lib/load-config.ts +8 -0
- package/src/lib/resolveDefaultsAtRules.js +5 -1
- package/src/lib/setupContextUtils.js +26 -4
- package/src/util/cloneNodes.js +35 -14
- package/src/util/dataTypes.js +12 -0
- package/src/util/pluginUtils.js +16 -0
- package/src/util/pseudoElements.js +4 -0
- package/types/config.d.ts +7 -0
package/lib/corePlugins.js
CHANGED
|
@@ -82,6 +82,9 @@ function _interop_require_wildcard(obj, nodeInterop) {
|
|
|
82
82
|
return newObj;
|
|
83
83
|
}
|
|
84
84
|
let variantPlugins = {
|
|
85
|
+
childVariant: ({ addVariant })=>{
|
|
86
|
+
addVariant('*', '& > *');
|
|
87
|
+
},
|
|
85
88
|
pseudoElementVariants: ({ addVariant })=>{
|
|
86
89
|
addVariant('first-letter', '&::first-letter');
|
|
87
90
|
addVariant('first-line', '&::first-line');
|
|
@@ -265,15 +268,15 @@ let variantPlugins = {
|
|
|
265
268
|
}
|
|
266
269
|
},
|
|
267
270
|
directionVariants: ({ addVariant })=>{
|
|
268
|
-
addVariant('ltr', '
|
|
269
|
-
addVariant('rtl', '
|
|
271
|
+
addVariant('ltr', '&:where([dir="ltr"], [dir="ltr"] *)');
|
|
272
|
+
addVariant('rtl', '&:where([dir="rtl"], [dir="rtl"] *)');
|
|
270
273
|
},
|
|
271
274
|
reducedMotionVariants: ({ addVariant })=>{
|
|
272
275
|
addVariant('motion-safe', '@media (prefers-reduced-motion: no-preference)');
|
|
273
276
|
addVariant('motion-reduce', '@media (prefers-reduced-motion: reduce)');
|
|
274
277
|
},
|
|
275
278
|
darkVariants: ({ config, addVariant })=>{
|
|
276
|
-
let [mode,
|
|
279
|
+
let [mode, selector = '.dark'] = [].concat(config('darkMode', 'media'));
|
|
277
280
|
if (mode === false) {
|
|
278
281
|
mode = 'media';
|
|
279
282
|
_log.default.warn('darkmode-false', [
|
|
@@ -282,10 +285,48 @@ let variantPlugins = {
|
|
|
282
285
|
'https://tailwindcss.com/docs/upgrade-guide#remove-dark-mode-configuration'
|
|
283
286
|
]);
|
|
284
287
|
}
|
|
285
|
-
if (mode === '
|
|
286
|
-
|
|
288
|
+
if (mode === 'variant') {
|
|
289
|
+
let formats;
|
|
290
|
+
if (Array.isArray(selector)) {
|
|
291
|
+
formats = selector;
|
|
292
|
+
} else if (typeof selector === 'function') {
|
|
293
|
+
formats = selector;
|
|
294
|
+
} else if (typeof selector === 'string') {
|
|
295
|
+
formats = [
|
|
296
|
+
selector
|
|
297
|
+
];
|
|
298
|
+
}
|
|
299
|
+
// TODO: We could also add these warnings if the user passes a function that returns string | string[]
|
|
300
|
+
// But this is an advanced enough use case that it's probably not necessary
|
|
301
|
+
if (Array.isArray(formats)) {
|
|
302
|
+
for (let format of formats){
|
|
303
|
+
if (format === '.dark') {
|
|
304
|
+
mode = false;
|
|
305
|
+
_log.default.warn('darkmode-variant-without-selector', [
|
|
306
|
+
'When using `variant` for `darkMode`, you must provide a selector.',
|
|
307
|
+
'Example: `darkMode: ["variant", ".your-selector &"]`'
|
|
308
|
+
]);
|
|
309
|
+
} else if (!format.includes('&')) {
|
|
310
|
+
mode = false;
|
|
311
|
+
_log.default.warn('darkmode-variant-without-ampersand', [
|
|
312
|
+
'When using `variant` for `darkMode`, your selector must contain `&`.',
|
|
313
|
+
'Example `darkMode: ["variant", ".your-selector &"]`'
|
|
314
|
+
]);
|
|
315
|
+
}
|
|
316
|
+
}
|
|
317
|
+
}
|
|
318
|
+
selector = formats;
|
|
319
|
+
}
|
|
320
|
+
if (mode === 'selector') {
|
|
321
|
+
// New preferred behavior
|
|
322
|
+
addVariant('dark', `&:where(${selector}, ${selector} *)`);
|
|
287
323
|
} else if (mode === 'media') {
|
|
288
324
|
addVariant('dark', '@media (prefers-color-scheme: dark)');
|
|
325
|
+
} else if (mode === 'variant') {
|
|
326
|
+
addVariant('dark', selector);
|
|
327
|
+
} else if (mode === 'class') {
|
|
328
|
+
// Old behavior
|
|
329
|
+
addVariant('dark', `:is(${selector} &)`);
|
|
289
330
|
}
|
|
290
331
|
},
|
|
291
332
|
printVariant: ({ addVariant })=>{
|
|
@@ -784,6 +825,12 @@ let corePlugins = {
|
|
|
784
825
|
]),
|
|
785
826
|
float: ({ addUtilities })=>{
|
|
786
827
|
addUtilities({
|
|
828
|
+
'.float-start': {
|
|
829
|
+
float: 'inline-start'
|
|
830
|
+
},
|
|
831
|
+
'.float-end': {
|
|
832
|
+
float: 'inline-end'
|
|
833
|
+
},
|
|
787
834
|
'.float-right': {
|
|
788
835
|
float: 'right'
|
|
789
836
|
},
|
|
@@ -797,6 +844,12 @@ let corePlugins = {
|
|
|
797
844
|
},
|
|
798
845
|
clear: ({ addUtilities })=>{
|
|
799
846
|
addUtilities({
|
|
847
|
+
'.clear-start': {
|
|
848
|
+
clear: 'inline-start'
|
|
849
|
+
},
|
|
850
|
+
'.clear-end': {
|
|
851
|
+
clear: 'inline-end'
|
|
852
|
+
},
|
|
800
853
|
'.clear-left': {
|
|
801
854
|
clear: 'left'
|
|
802
855
|
},
|
package/lib/css/preflight.css
CHANGED
|
@@ -195,9 +195,9 @@ select {
|
|
|
195
195
|
*/
|
|
196
196
|
|
|
197
197
|
button,
|
|
198
|
-
[type='button'],
|
|
199
|
-
[type='reset'],
|
|
200
|
-
[type='submit'] {
|
|
198
|
+
input:where([type='button']),
|
|
199
|
+
input:where([type='reset']),
|
|
200
|
+
input:where([type='submit']) {
|
|
201
201
|
-webkit-appearance: button; /* 1 */
|
|
202
202
|
background-color: transparent; /* 2 */
|
|
203
203
|
background-image: none; /* 2 */
|
package/lib/lib/content.js
CHANGED
|
@@ -21,7 +21,6 @@ const _fs = /*#__PURE__*/ _interop_require_default(require("fs"));
|
|
|
21
21
|
const _path = /*#__PURE__*/ _interop_require_default(require("path"));
|
|
22
22
|
const _isglob = /*#__PURE__*/ _interop_require_default(require("is-glob"));
|
|
23
23
|
const _fastglob = /*#__PURE__*/ _interop_require_default(require("fast-glob"));
|
|
24
|
-
const _normalizepath = /*#__PURE__*/ _interop_require_default(require("normalize-path"));
|
|
25
24
|
const _parseGlob = require("../util/parseGlob");
|
|
26
25
|
const _sharedState = require("./sharedState");
|
|
27
26
|
const _oxide = require("@tailwindcss/oxide");
|
|
@@ -30,6 +29,37 @@ function _interop_require_default(obj) {
|
|
|
30
29
|
default: obj
|
|
31
30
|
};
|
|
32
31
|
}
|
|
32
|
+
/*!
|
|
33
|
+
* Modified version of normalize-path, original license below
|
|
34
|
+
*
|
|
35
|
+
* normalize-path <https://github.com/jonschlinkert/normalize-path>
|
|
36
|
+
*
|
|
37
|
+
* Copyright (c) 2014-2018, Jon Schlinkert.
|
|
38
|
+
* Released under the MIT License.
|
|
39
|
+
*/ function normalizePath(path) {
|
|
40
|
+
if (typeof path !== 'string') {
|
|
41
|
+
throw new TypeError('expected path to be a string');
|
|
42
|
+
}
|
|
43
|
+
if (path === '\\' || path === '/') return '/';
|
|
44
|
+
var len = path.length;
|
|
45
|
+
if (len <= 1) return path;
|
|
46
|
+
// ensure that win32 namespaces has two leading slashes, so that the path is
|
|
47
|
+
// handled properly by the win32 version of path.parse() after being normalized
|
|
48
|
+
// https://msdn.microsoft.com/library/windows/desktop/aa365247(v=vs.85).aspx#namespaces
|
|
49
|
+
var prefix = '';
|
|
50
|
+
if (len > 4 && path[3] === '\\') {
|
|
51
|
+
var ch = path[2];
|
|
52
|
+
if ((ch === '?' || ch === '.') && path.slice(0, 2) === '\\\\') {
|
|
53
|
+
path = path.slice(2);
|
|
54
|
+
prefix = '//';
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
// Modified part: instead of purely splitting on `\\` and `/`, we split on
|
|
58
|
+
// `/` and `\\` that is _not_ followed by any of the following characters: ()[]
|
|
59
|
+
// This is to ensure that we keep the escaping of brackets and parentheses
|
|
60
|
+
let segs = path.split(/[/\\]+(?![\(\)\[\]])/);
|
|
61
|
+
return prefix + segs.join('/');
|
|
62
|
+
}
|
|
33
63
|
/** @typedef {import('../../types/config.js').RawFile} RawFile */ /** @typedef {import('../../types/config.js').FilePath} FilePath */ /*
|
|
34
64
|
* @param {import('tailwindcss').Config} tailwindConfig
|
|
35
65
|
* @param {{skip:string[]}} options
|
|
@@ -59,7 +89,7 @@ function parseCandidateFiles(context, tailwindConfig) {
|
|
|
59
89
|
});
|
|
60
90
|
// Normalize the file globs
|
|
61
91
|
files = files.filter((filePath)=>typeof filePath === 'string');
|
|
62
|
-
files = files.map(
|
|
92
|
+
files = files.map(normalizePath);
|
|
63
93
|
// Split into included and excluded globs
|
|
64
94
|
let tasks = _fastglob.default.generateTasks(files);
|
|
65
95
|
/** @type {ContentPath[]} */ let included = [];
|
|
@@ -86,6 +116,9 @@ function parseCandidateFiles(context, tailwindConfig) {
|
|
|
86
116
|
* @param {boolean} ignore
|
|
87
117
|
* @returns {ContentPath}
|
|
88
118
|
*/ function parseFilePath(filePath, ignore) {
|
|
119
|
+
// Escape special characters in the file path such as: ()[]
|
|
120
|
+
// But only if the special character isn't already escaped
|
|
121
|
+
filePath = filePath.replace(RegExp("(?<!\\\\)([\\[\\]\\(\\)])", "g"), '\\$1');
|
|
89
122
|
let contentPath = {
|
|
90
123
|
original: filePath,
|
|
91
124
|
base: filePath,
|
|
@@ -107,7 +140,7 @@ function parseCandidateFiles(context, tailwindConfig) {
|
|
|
107
140
|
// Afaik, this technically shouldn't be needed but there's probably
|
|
108
141
|
// some internal, direct path matching with a normalized path in
|
|
109
142
|
// a package which can't handle mixed directory separators
|
|
110
|
-
let base = (
|
|
143
|
+
let base = normalizePath(contentPath.base);
|
|
111
144
|
// If the user's file path contains any special characters (like parens) for instance fast-glob
|
|
112
145
|
// is like "OOOH SHINY" and treats them as such. So we have to escape the base path to fix this
|
|
113
146
|
base = _fastglob.default.escapePath(base);
|
|
@@ -82,12 +82,21 @@ function* buildRegExps(context) {
|
|
|
82
82
|
// Utilities
|
|
83
83
|
_regex.pattern([
|
|
84
84
|
// Utility Name / Group Name
|
|
85
|
-
|
|
85
|
+
_regex.any([
|
|
86
|
+
/-?(?:\w+)/,
|
|
87
|
+
// This is here to make sure @container supports everything that other utilities do
|
|
88
|
+
/@(?:\w+)/
|
|
89
|
+
]),
|
|
86
90
|
// Normal/Arbitrary values
|
|
87
91
|
_regex.optional(_regex.any([
|
|
88
92
|
_regex.pattern([
|
|
89
93
|
// Arbitrary values
|
|
90
|
-
|
|
94
|
+
_regex.any([
|
|
95
|
+
/-(?:\w+-)*\['[^\s]+'\]/,
|
|
96
|
+
/-(?:\w+-)*\["[^\s]+"\]/,
|
|
97
|
+
/-(?:\w+-)*\[`[^\s]+`\]/,
|
|
98
|
+
/-(?:\w+-)*\[(?:[^\s\[\]]+\[[^\s\[\]]+\])*[^\s:\[\]]+\]/
|
|
99
|
+
]),
|
|
91
100
|
// Not immediately followed by an `{[(`
|
|
92
101
|
/(?![{([]])/,
|
|
93
102
|
// optionally followed by an opacity modifier
|
|
@@ -95,7 +104,12 @@ function* buildRegExps(context) {
|
|
|
95
104
|
]),
|
|
96
105
|
_regex.pattern([
|
|
97
106
|
// Arbitrary values
|
|
98
|
-
|
|
107
|
+
_regex.any([
|
|
108
|
+
/-(?:\w+-)*\['[^\s]+'\]/,
|
|
109
|
+
/-(?:\w+-)*\["[^\s]+"\]/,
|
|
110
|
+
/-(?:\w+-)*\[`[^\s]+`\]/,
|
|
111
|
+
/-(?:\w+-)*\[(?:[^\s\[\]]+\[[^\s\[\]]+\])*[^\s\[\]]+\]/
|
|
112
|
+
]),
|
|
99
113
|
// Not immediately followed by an `{[(`
|
|
100
114
|
/(?![{([]])/,
|
|
101
115
|
// optionally followed by an opacity modifier
|
|
@@ -285,6 +285,9 @@ function expandTailwindAtRules(context) {
|
|
|
285
285
|
});
|
|
286
286
|
root.append(cloned);
|
|
287
287
|
}
|
|
288
|
+
var _root_source_end;
|
|
289
|
+
// TODO: Why is the root node having no source location for `end` possible?
|
|
290
|
+
root.source.end = (_root_source_end = root.source.end) !== null && _root_source_end !== void 0 ? _root_source_end : root.source.start;
|
|
288
291
|
// If we've got a utility layer and no utilities are generated there's likely something wrong
|
|
289
292
|
const hasUtilityVariants = variantNodes.some((node)=>{
|
|
290
293
|
var _node_raws_tailwind;
|
package/lib/lib/generateRules.js
CHANGED
|
@@ -163,6 +163,9 @@ function applyImportant(matches, classCandidate) {
|
|
|
163
163
|
return matches;
|
|
164
164
|
}
|
|
165
165
|
let result = [];
|
|
166
|
+
function isInKeyframes(rule) {
|
|
167
|
+
return rule.parent && rule.parent.type === 'atrule' && rule.parent.name === 'keyframes';
|
|
168
|
+
}
|
|
166
169
|
for (let [meta, rule] of matches){
|
|
167
170
|
let container = _postcss.default.root({
|
|
168
171
|
nodes: [
|
|
@@ -170,6 +173,11 @@ function applyImportant(matches, classCandidate) {
|
|
|
170
173
|
]
|
|
171
174
|
});
|
|
172
175
|
container.walkRules((r)=>{
|
|
176
|
+
// Declarations inside keyframes cannot be marked as important
|
|
177
|
+
// They will be ignored by the browser
|
|
178
|
+
if (isInKeyframes(r)) {
|
|
179
|
+
return;
|
|
180
|
+
}
|
|
173
181
|
let ast = (0, _postcssselectorparser.default)().astSync(r.selector);
|
|
174
182
|
// Remove extraneous selectors that do not include the base candidate
|
|
175
183
|
ast.each((sel)=>(0, _formatVariantSelector.eliminateIrrelevantSelectors)(sel, classCandidate));
|
|
@@ -813,6 +821,10 @@ function applyFinalFormat(match, { context, candidate }) {
|
|
|
813
821
|
if (!isValid) {
|
|
814
822
|
return null;
|
|
815
823
|
}
|
|
824
|
+
// If all rules have been eliminated we can skip this candidate entirely
|
|
825
|
+
if (container.nodes.length === 0) {
|
|
826
|
+
return null;
|
|
827
|
+
}
|
|
816
828
|
match[1] = container.nodes[0];
|
|
817
829
|
return match;
|
|
818
830
|
}
|
package/lib/lib/load-config.js
CHANGED
|
@@ -2,10 +2,18 @@
|
|
|
2
2
|
Object.defineProperty(exports, "__esModule", {
|
|
3
3
|
value: true
|
|
4
4
|
});
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
5
|
+
function _export(target, all) {
|
|
6
|
+
for(var name in all)Object.defineProperty(target, name, {
|
|
7
|
+
enumerable: true,
|
|
8
|
+
get: all[name]
|
|
9
|
+
});
|
|
10
|
+
}
|
|
11
|
+
_export(exports, {
|
|
12
|
+
loadConfig: function() {
|
|
8
13
|
return loadConfig;
|
|
14
|
+
},
|
|
15
|
+
useCustomJiti: function() {
|
|
16
|
+
return useCustomJiti;
|
|
9
17
|
}
|
|
10
18
|
});
|
|
11
19
|
const _jiti = /*#__PURE__*/ _interop_require_default(require("jiti"));
|
|
@@ -16,6 +24,9 @@ function _interop_require_default(obj) {
|
|
|
16
24
|
};
|
|
17
25
|
}
|
|
18
26
|
let jiti = null;
|
|
27
|
+
function useCustomJiti(_jiti) {
|
|
28
|
+
jiti = _jiti();
|
|
29
|
+
}
|
|
19
30
|
function lazyJiti() {
|
|
20
31
|
return jiti !== null && jiti !== void 0 ? jiti : jiti = (0, _jiti.default)(__filename, {
|
|
21
32
|
interopDefault: true,
|
|
@@ -111,7 +111,9 @@ function resolveDefaultsAtRules({ tailwindConfig }) {
|
|
|
111
111
|
// we consider them separately because merging the declarations into
|
|
112
112
|
// a single rule will cause browsers that do not understand the
|
|
113
113
|
// vendor prefix to throw out the whole rule
|
|
114
|
-
|
|
114
|
+
// Additionally if a selector contains `:has` we also consider
|
|
115
|
+
// it separately because FF only recently gained support for it
|
|
116
|
+
let selectorGroupName = selector.includes(':-') || selector.includes('::-') || selector.includes(':has') ? selector : '__DEFAULT__';
|
|
115
117
|
var _selectorGroups_get;
|
|
116
118
|
let selectors = (_selectorGroups_get = selectorGroups.get(selectorGroupName)) !== null && _selectorGroups_get !== void 0 ? _selectorGroups_get : new Set();
|
|
117
119
|
selectorGroups.set(selectorGroupName, selectors);
|
|
@@ -742,6 +742,7 @@ function resolvePlugins(context, root) {
|
|
|
742
742
|
// TODO: This is a workaround for backwards compatibility, since custom variants
|
|
743
743
|
// were historically sorted before screen/stackable variants.
|
|
744
744
|
let beforeVariants = [
|
|
745
|
+
_corePlugins.variantPlugins['childVariant'],
|
|
745
746
|
_corePlugins.variantPlugins['pseudoElementVariants'],
|
|
746
747
|
_corePlugins.variantPlugins['pseudoClassVariants'],
|
|
747
748
|
_corePlugins.variantPlugins['hasVariants'],
|
|
@@ -750,15 +751,31 @@ function resolvePlugins(context, root) {
|
|
|
750
751
|
];
|
|
751
752
|
let afterVariants = [
|
|
752
753
|
_corePlugins.variantPlugins['supportsVariants'],
|
|
753
|
-
_corePlugins.variantPlugins['directionVariants'],
|
|
754
754
|
_corePlugins.variantPlugins['reducedMotionVariants'],
|
|
755
755
|
_corePlugins.variantPlugins['prefersContrastVariants'],
|
|
756
|
-
_corePlugins.variantPlugins['forcedColorsVariants'],
|
|
757
|
-
_corePlugins.variantPlugins['darkVariants'],
|
|
758
|
-
_corePlugins.variantPlugins['printVariant'],
|
|
759
756
|
_corePlugins.variantPlugins['screenVariants'],
|
|
760
|
-
_corePlugins.variantPlugins['orientationVariants']
|
|
757
|
+
_corePlugins.variantPlugins['orientationVariants'],
|
|
758
|
+
_corePlugins.variantPlugins['directionVariants'],
|
|
759
|
+
_corePlugins.variantPlugins['darkVariants'],
|
|
760
|
+
_corePlugins.variantPlugins['forcedColorsVariants'],
|
|
761
|
+
_corePlugins.variantPlugins['printVariant']
|
|
761
762
|
];
|
|
763
|
+
// This is a compatibility fix for the pre 3.4 dark mode behavior
|
|
764
|
+
// `class` retains the old behavior, but `selector` keeps the new behavior
|
|
765
|
+
let isLegacyDarkMode = context.tailwindConfig.darkMode === 'class' || Array.isArray(context.tailwindConfig.darkMode) && context.tailwindConfig.darkMode[0] === 'class';
|
|
766
|
+
if (isLegacyDarkMode) {
|
|
767
|
+
afterVariants = [
|
|
768
|
+
_corePlugins.variantPlugins['supportsVariants'],
|
|
769
|
+
_corePlugins.variantPlugins['reducedMotionVariants'],
|
|
770
|
+
_corePlugins.variantPlugins['prefersContrastVariants'],
|
|
771
|
+
_corePlugins.variantPlugins['darkVariants'],
|
|
772
|
+
_corePlugins.variantPlugins['screenVariants'],
|
|
773
|
+
_corePlugins.variantPlugins['orientationVariants'],
|
|
774
|
+
_corePlugins.variantPlugins['directionVariants'],
|
|
775
|
+
_corePlugins.variantPlugins['forcedColorsVariants'],
|
|
776
|
+
_corePlugins.variantPlugins['printVariant']
|
|
777
|
+
];
|
|
778
|
+
}
|
|
762
779
|
return [
|
|
763
780
|
...corePluginList,
|
|
764
781
|
...beforeVariants,
|
package/lib/util/cloneNodes.js
CHANGED
|
@@ -1,4 +1,9 @@
|
|
|
1
|
-
|
|
1
|
+
/**
|
|
2
|
+
* @param {import('postcss').Container[]} nodes
|
|
3
|
+
* @param {any} source
|
|
4
|
+
* @param {any} raws
|
|
5
|
+
* @returns {import('postcss').Container[]}
|
|
6
|
+
*/ "use strict";
|
|
2
7
|
Object.defineProperty(exports, "__esModule", {
|
|
3
8
|
value: true
|
|
4
9
|
});
|
|
@@ -10,25 +15,40 @@ Object.defineProperty(exports, "default", {
|
|
|
10
15
|
});
|
|
11
16
|
function cloneNodes(nodes, source = undefined, raws = undefined) {
|
|
12
17
|
return nodes.map((node)=>{
|
|
13
|
-
var _node_raws_tailwind;
|
|
14
18
|
let cloned = node.clone();
|
|
15
|
-
// We always want override the source map
|
|
16
|
-
// except when explicitly told not to
|
|
17
|
-
let shouldOverwriteSource = ((_node_raws_tailwind = node.raws.tailwind) === null || _node_raws_tailwind === void 0 ? void 0 : _node_raws_tailwind.preserveSource) !== true || !cloned.source;
|
|
18
|
-
if (source !== undefined && shouldOverwriteSource) {
|
|
19
|
-
cloned.source = source;
|
|
20
|
-
if ('walk' in cloned) {
|
|
21
|
-
cloned.walk((child)=>{
|
|
22
|
-
child.source = source;
|
|
23
|
-
});
|
|
24
|
-
}
|
|
25
|
-
}
|
|
26
19
|
if (raws !== undefined) {
|
|
27
20
|
cloned.raws.tailwind = {
|
|
28
21
|
...cloned.raws.tailwind,
|
|
29
22
|
...raws
|
|
30
23
|
};
|
|
31
24
|
}
|
|
25
|
+
if (source !== undefined) {
|
|
26
|
+
traverse(cloned, (node)=>{
|
|
27
|
+
var _node_raws_tailwind;
|
|
28
|
+
// Do not traverse nodes that have opted
|
|
29
|
+
// to preserve their original source
|
|
30
|
+
let shouldPreserveSource = ((_node_raws_tailwind = node.raws.tailwind) === null || _node_raws_tailwind === void 0 ? void 0 : _node_raws_tailwind.preserveSource) === true && node.source;
|
|
31
|
+
if (shouldPreserveSource) {
|
|
32
|
+
return false;
|
|
33
|
+
}
|
|
34
|
+
// Otherwise we can safely replace the source
|
|
35
|
+
// And continue traversing
|
|
36
|
+
node.source = source;
|
|
37
|
+
});
|
|
38
|
+
}
|
|
32
39
|
return cloned;
|
|
33
40
|
});
|
|
34
41
|
}
|
|
42
|
+
/**
|
|
43
|
+
* Traverse a tree of nodes and don't traverse children if the callback
|
|
44
|
+
* returns false. Ideally we'd use Container#walk instead of this
|
|
45
|
+
* function but it stops traversing siblings too.
|
|
46
|
+
*
|
|
47
|
+
* @param {import('postcss').Container} node
|
|
48
|
+
* @param {(node: import('postcss').Container) => boolean} onNode
|
|
49
|
+
*/ function traverse(node, onNode) {
|
|
50
|
+
if (onNode(node) !== false) {
|
|
51
|
+
var _node_each;
|
|
52
|
+
(_node_each = node.each) === null || _node_each === void 0 ? void 0 : _node_each.call(node, (child)=>traverse(child, onNode));
|
|
53
|
+
}
|
|
54
|
+
}
|
package/lib/util/dataTypes.js
CHANGED
|
@@ -141,7 +141,13 @@ function normalize(value, context = null, isRoot = true) {
|
|
|
141
141
|
'keyboard-inset-bottom',
|
|
142
142
|
'keyboard-inset-left',
|
|
143
143
|
'keyboard-inset-width',
|
|
144
|
-
'keyboard-inset-height'
|
|
144
|
+
'keyboard-inset-height',
|
|
145
|
+
'radial-gradient',
|
|
146
|
+
'linear-gradient',
|
|
147
|
+
'conic-gradient',
|
|
148
|
+
'repeating-radial-gradient',
|
|
149
|
+
'repeating-linear-gradient',
|
|
150
|
+
'repeating-conic-gradient'
|
|
145
151
|
];
|
|
146
152
|
return value.replace(/(calc|min|max|clamp)\(.+\)/g, (match)=>{
|
|
147
153
|
let result = '';
|
|
@@ -187,6 +193,10 @@ function normalize(value, context = null, isRoot = true) {
|
|
|
187
193
|
result += consumeUntil([
|
|
188
194
|
')'
|
|
189
195
|
]);
|
|
196
|
+
} else if (peek('[')) {
|
|
197
|
+
result += consumeUntil([
|
|
198
|
+
']'
|
|
199
|
+
]);
|
|
190
200
|
} else if ([
|
|
191
201
|
'+',
|
|
192
202
|
'-',
|
package/lib/util/pluginUtils.js
CHANGED
|
@@ -91,6 +91,19 @@ function isArbitraryValue(input) {
|
|
|
91
91
|
}
|
|
92
92
|
function splitUtilityModifier(modifier) {
|
|
93
93
|
let slashIdx = modifier.lastIndexOf('/');
|
|
94
|
+
// If the `/` is inside an arbitrary, we want to find the previous one if any
|
|
95
|
+
// This logic probably isn't perfect but it should work for most cases
|
|
96
|
+
let arbitraryStartIdx = modifier.lastIndexOf('[', slashIdx);
|
|
97
|
+
let arbitraryEndIdx = modifier.indexOf(']', slashIdx);
|
|
98
|
+
let isNextToArbitrary = modifier[slashIdx - 1] === ']' || modifier[slashIdx + 1] === '[';
|
|
99
|
+
// Backtrack to the previous `/` if the one we found was inside an arbitrary
|
|
100
|
+
if (!isNextToArbitrary) {
|
|
101
|
+
if (arbitraryStartIdx !== -1 && arbitraryEndIdx !== -1) {
|
|
102
|
+
if (arbitraryStartIdx < slashIdx && slashIdx < arbitraryEndIdx) {
|
|
103
|
+
slashIdx = modifier.lastIndexOf('/', arbitraryStartIdx);
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
}
|
|
94
107
|
if (slashIdx === -1 || slashIdx === modifier.length - 1) {
|
|
95
108
|
return [
|
|
96
109
|
modifier,
|
|
@@ -114,6 +114,9 @@ let elementProperties = {
|
|
|
114
114
|
'terminal',
|
|
115
115
|
'jumpable'
|
|
116
116
|
],
|
|
117
|
+
':where': [],
|
|
118
|
+
':is': [],
|
|
119
|
+
':has': [],
|
|
117
120
|
// The default value is used when the pseudo-element is not recognized
|
|
118
121
|
// Because it's not recognized, we don't know if it's terminal or not
|
|
119
122
|
// So we assume it can be moved AND can have user-action pseudo classes attached to it
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "tailwindcss",
|
|
3
|
-
"version": "0.0.0-insiders.
|
|
3
|
+
"version": "0.0.0-insiders.eb40518",
|
|
4
4
|
"description": "A utility-first CSS framework for rapidly building custom user interfaces.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"main": "lib/index.js",
|
|
@@ -43,26 +43,26 @@
|
|
|
43
43
|
"*.js"
|
|
44
44
|
],
|
|
45
45
|
"devDependencies": {
|
|
46
|
-
"@swc/cli": "^0.1.
|
|
47
|
-
"@swc/core": "^1.3.
|
|
46
|
+
"@swc/cli": "^0.1.63",
|
|
47
|
+
"@swc/core": "^1.3.101",
|
|
48
48
|
"@swc/jest": "^0.2.29",
|
|
49
49
|
"@swc/register": "^0.1.10",
|
|
50
50
|
"concurrently": "^8.0.1",
|
|
51
|
-
"eslint": "^8.
|
|
52
|
-
"eslint-config-prettier": "^9.
|
|
51
|
+
"eslint": "^8.56.0",
|
|
52
|
+
"eslint-config-prettier": "^9.1.0",
|
|
53
53
|
"eslint-plugin-prettier": "^4.2.1",
|
|
54
54
|
"jest": "^29.7.0",
|
|
55
55
|
"jest-diff": "^29.7.0",
|
|
56
56
|
"prettier": "^2.8.8",
|
|
57
57
|
"rimraf": "^5.0.0",
|
|
58
58
|
"source-map-js": "^1.0.2",
|
|
59
|
-
"turbo": "^1.
|
|
59
|
+
"turbo": "^1.11.2"
|
|
60
60
|
},
|
|
61
61
|
"dependencies": {
|
|
62
62
|
"@alloc/quick-lru": "^5.2.0",
|
|
63
|
-
"@tailwindcss/oxide": "0.0.0-insiders.
|
|
63
|
+
"@tailwindcss/oxide": "0.0.0-insiders.eb40518",
|
|
64
64
|
"arg": "^5.0.2",
|
|
65
|
-
"browserslist": "^4.22.
|
|
65
|
+
"browserslist": "^4.22.2",
|
|
66
66
|
"chokidar": "^3.5.3",
|
|
67
67
|
"didyoumean": "^1.2.2",
|
|
68
68
|
"dlv": "^1.1.3",
|
|
@@ -71,7 +71,7 @@
|
|
|
71
71
|
"is-glob": "^4.0.3",
|
|
72
72
|
"jiti": "^1.21.0",
|
|
73
73
|
"lightningcss": "^1.22.1",
|
|
74
|
-
"lilconfig": "^
|
|
74
|
+
"lilconfig": "^3.0.0",
|
|
75
75
|
"micromatch": "^4.0.5",
|
|
76
76
|
"normalize-path": "^3.0.0",
|
|
77
77
|
"object-hash": "^3.0.0",
|
|
@@ -79,12 +79,12 @@
|
|
|
79
79
|
"postcss": "^8.4.31",
|
|
80
80
|
"postcss-import": "^15.1.0",
|
|
81
81
|
"postcss-js": "^4.0.1",
|
|
82
|
-
"postcss-load-config": "^4.0.
|
|
82
|
+
"postcss-load-config": "^4.0.2",
|
|
83
83
|
"postcss-nested": "^6.0.1",
|
|
84
|
-
"postcss-selector-parser": "^6.0.
|
|
84
|
+
"postcss-selector-parser": "^6.0.15",
|
|
85
85
|
"postcss-value-parser": "^4.2.0",
|
|
86
86
|
"resolve": "^1.22.8",
|
|
87
|
-
"sucrase": "^3.
|
|
87
|
+
"sucrase": "^3.35.0"
|
|
88
88
|
},
|
|
89
89
|
"browserslist": [
|
|
90
90
|
"defaults and supports css-variables and supports css-matches-pseudo",
|
package/src/corePlugins.js
CHANGED
|
@@ -24,6 +24,9 @@ import { normalize } from './util/dataTypes'
|
|
|
24
24
|
import { INTERNAL_FEATURES } from './lib/setupContextUtils'
|
|
25
25
|
|
|
26
26
|
export let variantPlugins = {
|
|
27
|
+
childVariant: ({ addVariant }) => {
|
|
28
|
+
addVariant('*', '& > *')
|
|
29
|
+
},
|
|
27
30
|
pseudoElementVariants: ({ addVariant }) => {
|
|
28
31
|
addVariant('first-letter', '&::first-letter')
|
|
29
32
|
addVariant('first-line', '&::first-line')
|
|
@@ -203,8 +206,8 @@ export let variantPlugins = {
|
|
|
203
206
|
},
|
|
204
207
|
|
|
205
208
|
directionVariants: ({ addVariant }) => {
|
|
206
|
-
addVariant('ltr', '
|
|
207
|
-
addVariant('rtl', '
|
|
209
|
+
addVariant('ltr', '&:where([dir="ltr"], [dir="ltr"] *)')
|
|
210
|
+
addVariant('rtl', '&:where([dir="rtl"], [dir="rtl"] *)')
|
|
208
211
|
},
|
|
209
212
|
|
|
210
213
|
reducedMotionVariants: ({ addVariant }) => {
|
|
@@ -213,7 +216,7 @@ export let variantPlugins = {
|
|
|
213
216
|
},
|
|
214
217
|
|
|
215
218
|
darkVariants: ({ config, addVariant }) => {
|
|
216
|
-
let [mode,
|
|
219
|
+
let [mode, selector = '.dark'] = [].concat(config('darkMode', 'media'))
|
|
217
220
|
|
|
218
221
|
if (mode === false) {
|
|
219
222
|
mode = 'media'
|
|
@@ -224,10 +227,49 @@ export let variantPlugins = {
|
|
|
224
227
|
])
|
|
225
228
|
}
|
|
226
229
|
|
|
227
|
-
if (mode === '
|
|
228
|
-
|
|
230
|
+
if (mode === 'variant') {
|
|
231
|
+
let formats
|
|
232
|
+
if (Array.isArray(selector)) {
|
|
233
|
+
formats = selector
|
|
234
|
+
} else if (typeof selector === 'function') {
|
|
235
|
+
formats = selector
|
|
236
|
+
} else if (typeof selector === 'string') {
|
|
237
|
+
formats = [selector]
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
// TODO: We could also add these warnings if the user passes a function that returns string | string[]
|
|
241
|
+
// But this is an advanced enough use case that it's probably not necessary
|
|
242
|
+
if (Array.isArray(formats)) {
|
|
243
|
+
for (let format of formats) {
|
|
244
|
+
if (format === '.dark') {
|
|
245
|
+
mode = false
|
|
246
|
+
log.warn('darkmode-variant-without-selector', [
|
|
247
|
+
'When using `variant` for `darkMode`, you must provide a selector.',
|
|
248
|
+
'Example: `darkMode: ["variant", ".your-selector &"]`',
|
|
249
|
+
])
|
|
250
|
+
} else if (!format.includes('&')) {
|
|
251
|
+
mode = false
|
|
252
|
+
log.warn('darkmode-variant-without-ampersand', [
|
|
253
|
+
'When using `variant` for `darkMode`, your selector must contain `&`.',
|
|
254
|
+
'Example `darkMode: ["variant", ".your-selector &"]`',
|
|
255
|
+
])
|
|
256
|
+
}
|
|
257
|
+
}
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
selector = formats
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
if (mode === 'selector') {
|
|
264
|
+
// New preferred behavior
|
|
265
|
+
addVariant('dark', `&:where(${selector}, ${selector} *)`)
|
|
229
266
|
} else if (mode === 'media') {
|
|
230
267
|
addVariant('dark', '@media (prefers-color-scheme: dark)')
|
|
268
|
+
} else if (mode === 'variant') {
|
|
269
|
+
addVariant('dark', selector)
|
|
270
|
+
} else if (mode === 'class') {
|
|
271
|
+
// Old behavior
|
|
272
|
+
addVariant('dark', `:is(${selector} &)`)
|
|
231
273
|
}
|
|
232
274
|
},
|
|
233
275
|
|
|
@@ -684,6 +726,8 @@ export let corePlugins = {
|
|
|
684
726
|
|
|
685
727
|
float: ({ addUtilities }) => {
|
|
686
728
|
addUtilities({
|
|
729
|
+
'.float-start': { float: 'inline-start' },
|
|
730
|
+
'.float-end': { float: 'inline-end' },
|
|
687
731
|
'.float-right': { float: 'right' },
|
|
688
732
|
'.float-left': { float: 'left' },
|
|
689
733
|
'.float-none': { float: 'none' },
|
|
@@ -692,6 +736,8 @@ export let corePlugins = {
|
|
|
692
736
|
|
|
693
737
|
clear: ({ addUtilities }) => {
|
|
694
738
|
addUtilities({
|
|
739
|
+
'.clear-start': { clear: 'inline-start' },
|
|
740
|
+
'.clear-end': { clear: 'inline-end' },
|
|
695
741
|
'.clear-left': { clear: 'left' },
|
|
696
742
|
'.clear-right': { clear: 'right' },
|
|
697
743
|
'.clear-both': { clear: 'both' },
|
package/src/css/preflight.css
CHANGED
|
@@ -195,9 +195,9 @@ select {
|
|
|
195
195
|
*/
|
|
196
196
|
|
|
197
197
|
button,
|
|
198
|
-
[type='button'],
|
|
199
|
-
[type='reset'],
|
|
200
|
-
[type='submit'] {
|
|
198
|
+
input:where([type='button']),
|
|
199
|
+
input:where([type='reset']),
|
|
200
|
+
input:where([type='submit']) {
|
|
201
201
|
-webkit-appearance: button; /* 1 */
|
|
202
202
|
background-color: transparent; /* 2 */
|
|
203
203
|
background-image: none; /* 2 */
|
package/src/lib/content.js
CHANGED
|
@@ -4,11 +4,48 @@ import fs from 'fs'
|
|
|
4
4
|
import path from 'path'
|
|
5
5
|
import isGlob from 'is-glob'
|
|
6
6
|
import fastGlob from 'fast-glob'
|
|
7
|
-
import normalizePath from 'normalize-path'
|
|
8
7
|
import { parseGlob } from '../util/parseGlob'
|
|
9
8
|
import { env } from './sharedState'
|
|
10
9
|
import { resolveContentPaths } from '@tailwindcss/oxide'
|
|
11
10
|
|
|
11
|
+
/*!
|
|
12
|
+
* Modified version of normalize-path, original license below
|
|
13
|
+
*
|
|
14
|
+
* normalize-path <https://github.com/jonschlinkert/normalize-path>
|
|
15
|
+
*
|
|
16
|
+
* Copyright (c) 2014-2018, Jon Schlinkert.
|
|
17
|
+
* Released under the MIT License.
|
|
18
|
+
*/
|
|
19
|
+
|
|
20
|
+
function normalizePath(path) {
|
|
21
|
+
if (typeof path !== 'string') {
|
|
22
|
+
throw new TypeError('expected path to be a string')
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
if (path === '\\' || path === '/') return '/'
|
|
26
|
+
|
|
27
|
+
var len = path.length
|
|
28
|
+
if (len <= 1) return path
|
|
29
|
+
|
|
30
|
+
// ensure that win32 namespaces has two leading slashes, so that the path is
|
|
31
|
+
// handled properly by the win32 version of path.parse() after being normalized
|
|
32
|
+
// https://msdn.microsoft.com/library/windows/desktop/aa365247(v=vs.85).aspx#namespaces
|
|
33
|
+
var prefix = ''
|
|
34
|
+
if (len > 4 && path[3] === '\\') {
|
|
35
|
+
var ch = path[2]
|
|
36
|
+
if ((ch === '?' || ch === '.') && path.slice(0, 2) === '\\\\') {
|
|
37
|
+
path = path.slice(2)
|
|
38
|
+
prefix = '//'
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
// Modified part: instead of purely splitting on `\\` and `/`, we split on
|
|
43
|
+
// `/` and `\\` that is _not_ followed by any of the following characters: ()[]
|
|
44
|
+
// This is to ensure that we keep the escaping of brackets and parentheses
|
|
45
|
+
let segs = path.split(/[/\\]+(?![\(\)\[\]])/)
|
|
46
|
+
return prefix + segs.join('/')
|
|
47
|
+
}
|
|
48
|
+
|
|
12
49
|
/** @typedef {import('../../types/config.js').RawFile} RawFile */
|
|
13
50
|
/** @typedef {import('../../types/config.js').FilePath} FilePath */
|
|
14
51
|
|
|
@@ -105,6 +142,10 @@ export function parseCandidateFiles(context, tailwindConfig) {
|
|
|
105
142
|
* @returns {ContentPath}
|
|
106
143
|
*/
|
|
107
144
|
function parseFilePath(filePath, ignore) {
|
|
145
|
+
// Escape special characters in the file path such as: ()[]
|
|
146
|
+
// But only if the special character isn't already escaped
|
|
147
|
+
filePath = filePath.replace(/(?<!\\)([\[\]\(\)])/g, '\\$1')
|
|
148
|
+
|
|
108
149
|
let contentPath = {
|
|
109
150
|
original: filePath,
|
|
110
151
|
base: filePath,
|
|
@@ -40,14 +40,24 @@ function* buildRegExps(context) {
|
|
|
40
40
|
// Utilities
|
|
41
41
|
regex.pattern([
|
|
42
42
|
// Utility Name / Group Name
|
|
43
|
-
|
|
43
|
+
regex.any([
|
|
44
|
+
/-?(?:\w+)/,
|
|
45
|
+
|
|
46
|
+
// This is here to make sure @container supports everything that other utilities do
|
|
47
|
+
/@(?:\w+)/,
|
|
48
|
+
]),
|
|
44
49
|
|
|
45
50
|
// Normal/Arbitrary values
|
|
46
51
|
regex.optional(
|
|
47
52
|
regex.any([
|
|
48
53
|
regex.pattern([
|
|
49
54
|
// Arbitrary values
|
|
50
|
-
|
|
55
|
+
regex.any([
|
|
56
|
+
/-(?:\w+-)*\['[^\s]+'\]/,
|
|
57
|
+
/-(?:\w+-)*\["[^\s]+"\]/,
|
|
58
|
+
/-(?:\w+-)*\[`[^\s]+`\]/,
|
|
59
|
+
/-(?:\w+-)*\[(?:[^\s\[\]]+\[[^\s\[\]]+\])*[^\s:\[\]]+\]/,
|
|
60
|
+
]),
|
|
51
61
|
|
|
52
62
|
// Not immediately followed by an `{[(`
|
|
53
63
|
/(?![{([]])/,
|
|
@@ -58,7 +68,12 @@ function* buildRegExps(context) {
|
|
|
58
68
|
|
|
59
69
|
regex.pattern([
|
|
60
70
|
// Arbitrary values
|
|
61
|
-
|
|
71
|
+
regex.any([
|
|
72
|
+
/-(?:\w+-)*\['[^\s]+'\]/,
|
|
73
|
+
/-(?:\w+-)*\["[^\s]+"\]/,
|
|
74
|
+
/-(?:\w+-)*\[`[^\s]+`\]/,
|
|
75
|
+
/-(?:\w+-)*\[(?:[^\s\[\]]+\[[^\s\[\]]+\])*[^\s\[\]]+\]/,
|
|
76
|
+
]),
|
|
62
77
|
|
|
63
78
|
// Not immediately followed by an `{[(`
|
|
64
79
|
/(?![{([]])/,
|
|
@@ -291,6 +291,9 @@ export default function expandTailwindAtRules(context) {
|
|
|
291
291
|
root.append(cloned)
|
|
292
292
|
}
|
|
293
293
|
|
|
294
|
+
// TODO: Why is the root node having no source location for `end` possible?
|
|
295
|
+
root.source.end = root.source.end ?? root.source.start
|
|
296
|
+
|
|
294
297
|
// If we've got a utility layer and no utilities are generated there's likely something wrong
|
|
295
298
|
const hasUtilityVariants = variantNodes.some(
|
|
296
299
|
(node) => node.raws.tailwind?.parentLayer === 'utilities'
|
package/src/lib/generateRules.js
CHANGED
|
@@ -118,10 +118,20 @@ function applyImportant(matches, classCandidate) {
|
|
|
118
118
|
|
|
119
119
|
let result = []
|
|
120
120
|
|
|
121
|
+
function isInKeyframes(rule) {
|
|
122
|
+
return rule.parent && rule.parent.type === 'atrule' && rule.parent.name === 'keyframes'
|
|
123
|
+
}
|
|
124
|
+
|
|
121
125
|
for (let [meta, rule] of matches) {
|
|
122
126
|
let container = postcss.root({ nodes: [rule.clone()] })
|
|
123
127
|
|
|
124
128
|
container.walkRules((r) => {
|
|
129
|
+
// Declarations inside keyframes cannot be marked as important
|
|
130
|
+
// They will be ignored by the browser
|
|
131
|
+
if (isInKeyframes(r)) {
|
|
132
|
+
return
|
|
133
|
+
}
|
|
134
|
+
|
|
125
135
|
let ast = selectorParser().astSync(r.selector)
|
|
126
136
|
|
|
127
137
|
// Remove extraneous selectors that do not include the base candidate
|
|
@@ -840,6 +850,11 @@ function applyFinalFormat(match, { context, candidate }) {
|
|
|
840
850
|
return null
|
|
841
851
|
}
|
|
842
852
|
|
|
853
|
+
// If all rules have been eliminated we can skip this candidate entirely
|
|
854
|
+
if (container.nodes.length === 0) {
|
|
855
|
+
return null
|
|
856
|
+
}
|
|
857
|
+
|
|
843
858
|
match[1] = container.nodes[0]
|
|
844
859
|
|
|
845
860
|
return match
|
package/src/lib/load-config.ts
CHANGED
|
@@ -4,6 +4,14 @@ import { transform } from 'sucrase'
|
|
|
4
4
|
import { Config } from '../../types/config'
|
|
5
5
|
|
|
6
6
|
let jiti: ReturnType<typeof jitiFactory> | null = null
|
|
7
|
+
|
|
8
|
+
// @internal
|
|
9
|
+
// This WILL be removed in some future release
|
|
10
|
+
// If you rely on this your stuff WILL break
|
|
11
|
+
export function useCustomJiti(_jiti: () => ReturnType<typeof jitiFactory>) {
|
|
12
|
+
jiti = _jiti()
|
|
13
|
+
}
|
|
14
|
+
|
|
7
15
|
function lazyJiti() {
|
|
8
16
|
return (
|
|
9
17
|
jiti ??
|
|
@@ -104,8 +104,12 @@ export default function resolveDefaultsAtRules({ tailwindConfig }) {
|
|
|
104
104
|
// we consider them separately because merging the declarations into
|
|
105
105
|
// a single rule will cause browsers that do not understand the
|
|
106
106
|
// vendor prefix to throw out the whole rule
|
|
107
|
+
// Additionally if a selector contains `:has` we also consider
|
|
108
|
+
// it separately because FF only recently gained support for it
|
|
107
109
|
let selectorGroupName =
|
|
108
|
-
selector.includes(':-') || selector.includes('::-')
|
|
110
|
+
selector.includes(':-') || selector.includes('::-') || selector.includes(':has')
|
|
111
|
+
? selector
|
|
112
|
+
: '__DEFAULT__'
|
|
109
113
|
|
|
110
114
|
let selectors = selectorGroups.get(selectorGroupName) ?? new Set()
|
|
111
115
|
selectorGroups.set(selectorGroupName, selectors)
|
|
@@ -746,6 +746,7 @@ function resolvePlugins(context, root) {
|
|
|
746
746
|
// TODO: This is a workaround for backwards compatibility, since custom variants
|
|
747
747
|
// were historically sorted before screen/stackable variants.
|
|
748
748
|
let beforeVariants = [
|
|
749
|
+
variantPlugins['childVariant'],
|
|
749
750
|
variantPlugins['pseudoElementVariants'],
|
|
750
751
|
variantPlugins['pseudoClassVariants'],
|
|
751
752
|
variantPlugins['hasVariants'],
|
|
@@ -754,16 +755,37 @@ function resolvePlugins(context, root) {
|
|
|
754
755
|
]
|
|
755
756
|
let afterVariants = [
|
|
756
757
|
variantPlugins['supportsVariants'],
|
|
757
|
-
variantPlugins['directionVariants'],
|
|
758
758
|
variantPlugins['reducedMotionVariants'],
|
|
759
759
|
variantPlugins['prefersContrastVariants'],
|
|
760
|
-
variantPlugins['forcedColorsVariants'],
|
|
761
|
-
variantPlugins['darkVariants'],
|
|
762
|
-
variantPlugins['printVariant'],
|
|
763
760
|
variantPlugins['screenVariants'],
|
|
764
761
|
variantPlugins['orientationVariants'],
|
|
762
|
+
variantPlugins['directionVariants'],
|
|
763
|
+
variantPlugins['darkVariants'],
|
|
764
|
+
variantPlugins['forcedColorsVariants'],
|
|
765
|
+
variantPlugins['printVariant'],
|
|
765
766
|
]
|
|
766
767
|
|
|
768
|
+
// This is a compatibility fix for the pre 3.4 dark mode behavior
|
|
769
|
+
// `class` retains the old behavior, but `selector` keeps the new behavior
|
|
770
|
+
let isLegacyDarkMode =
|
|
771
|
+
context.tailwindConfig.darkMode === 'class' ||
|
|
772
|
+
(Array.isArray(context.tailwindConfig.darkMode) &&
|
|
773
|
+
context.tailwindConfig.darkMode[0] === 'class')
|
|
774
|
+
|
|
775
|
+
if (isLegacyDarkMode) {
|
|
776
|
+
afterVariants = [
|
|
777
|
+
variantPlugins['supportsVariants'],
|
|
778
|
+
variantPlugins['reducedMotionVariants'],
|
|
779
|
+
variantPlugins['prefersContrastVariants'],
|
|
780
|
+
variantPlugins['darkVariants'],
|
|
781
|
+
variantPlugins['screenVariants'],
|
|
782
|
+
variantPlugins['orientationVariants'],
|
|
783
|
+
variantPlugins['directionVariants'],
|
|
784
|
+
variantPlugins['forcedColorsVariants'],
|
|
785
|
+
variantPlugins['printVariant'],
|
|
786
|
+
]
|
|
787
|
+
}
|
|
788
|
+
|
|
767
789
|
return [...corePluginList, ...beforeVariants, ...userPlugins, ...afterVariants, ...layerPlugins]
|
|
768
790
|
}
|
|
769
791
|
|
package/src/util/cloneNodes.js
CHANGED
|
@@ -1,21 +1,13 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @param {import('postcss').Container[]} nodes
|
|
3
|
+
* @param {any} source
|
|
4
|
+
* @param {any} raws
|
|
5
|
+
* @returns {import('postcss').Container[]}
|
|
6
|
+
*/
|
|
1
7
|
export default function cloneNodes(nodes, source = undefined, raws = undefined) {
|
|
2
8
|
return nodes.map((node) => {
|
|
3
9
|
let cloned = node.clone()
|
|
4
10
|
|
|
5
|
-
// We always want override the source map
|
|
6
|
-
// except when explicitly told not to
|
|
7
|
-
let shouldOverwriteSource = node.raws.tailwind?.preserveSource !== true || !cloned.source
|
|
8
|
-
|
|
9
|
-
if (source !== undefined && shouldOverwriteSource) {
|
|
10
|
-
cloned.source = source
|
|
11
|
-
|
|
12
|
-
if ('walk' in cloned) {
|
|
13
|
-
cloned.walk((child) => {
|
|
14
|
-
child.source = source
|
|
15
|
-
})
|
|
16
|
-
}
|
|
17
|
-
}
|
|
18
|
-
|
|
19
11
|
if (raws !== undefined) {
|
|
20
12
|
cloned.raws.tailwind = {
|
|
21
13
|
...cloned.raws.tailwind,
|
|
@@ -23,6 +15,35 @@ export default function cloneNodes(nodes, source = undefined, raws = undefined)
|
|
|
23
15
|
}
|
|
24
16
|
}
|
|
25
17
|
|
|
18
|
+
if (source !== undefined) {
|
|
19
|
+
traverse(cloned, (node) => {
|
|
20
|
+
// Do not traverse nodes that have opted
|
|
21
|
+
// to preserve their original source
|
|
22
|
+
let shouldPreserveSource = node.raws.tailwind?.preserveSource === true && node.source
|
|
23
|
+
if (shouldPreserveSource) {
|
|
24
|
+
return false
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
// Otherwise we can safely replace the source
|
|
28
|
+
// And continue traversing
|
|
29
|
+
node.source = source
|
|
30
|
+
})
|
|
31
|
+
}
|
|
32
|
+
|
|
26
33
|
return cloned
|
|
27
34
|
})
|
|
28
35
|
}
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* Traverse a tree of nodes and don't traverse children if the callback
|
|
39
|
+
* returns false. Ideally we'd use Container#walk instead of this
|
|
40
|
+
* function but it stops traversing siblings too.
|
|
41
|
+
*
|
|
42
|
+
* @param {import('postcss').Container} node
|
|
43
|
+
* @param {(node: import('postcss').Container) => boolean} onNode
|
|
44
|
+
*/
|
|
45
|
+
function traverse(node, onNode) {
|
|
46
|
+
if (onNode(node) !== false) {
|
|
47
|
+
node.each?.((child) => traverse(child, onNode))
|
|
48
|
+
}
|
|
49
|
+
}
|
package/src/util/dataTypes.js
CHANGED
|
@@ -107,6 +107,13 @@ function normalizeMathOperatorSpacing(value) {
|
|
|
107
107
|
'keyboard-inset-left',
|
|
108
108
|
'keyboard-inset-width',
|
|
109
109
|
'keyboard-inset-height',
|
|
110
|
+
|
|
111
|
+
'radial-gradient',
|
|
112
|
+
'linear-gradient',
|
|
113
|
+
'conic-gradient',
|
|
114
|
+
'repeating-radial-gradient',
|
|
115
|
+
'repeating-linear-gradient',
|
|
116
|
+
'repeating-conic-gradient',
|
|
110
117
|
]
|
|
111
118
|
|
|
112
119
|
return value.replace(/(calc|min|max|clamp)\(.+\)/g, (match) => {
|
|
@@ -162,6 +169,11 @@ function normalizeMathOperatorSpacing(value) {
|
|
|
162
169
|
result += consumeUntil([')'])
|
|
163
170
|
}
|
|
164
171
|
|
|
172
|
+
// Don't break CSS grid track names
|
|
173
|
+
else if (peek('[')) {
|
|
174
|
+
result += consumeUntil([']'])
|
|
175
|
+
}
|
|
176
|
+
|
|
165
177
|
// Handle operators
|
|
166
178
|
else if (
|
|
167
179
|
['+', '-', '*', '/'].includes(char) &&
|
package/src/util/pluginUtils.js
CHANGED
|
@@ -87,6 +87,22 @@ function isArbitraryValue(input) {
|
|
|
87
87
|
function splitUtilityModifier(modifier) {
|
|
88
88
|
let slashIdx = modifier.lastIndexOf('/')
|
|
89
89
|
|
|
90
|
+
// If the `/` is inside an arbitrary, we want to find the previous one if any
|
|
91
|
+
// This logic probably isn't perfect but it should work for most cases
|
|
92
|
+
let arbitraryStartIdx = modifier.lastIndexOf('[', slashIdx)
|
|
93
|
+
let arbitraryEndIdx = modifier.indexOf(']', slashIdx)
|
|
94
|
+
|
|
95
|
+
let isNextToArbitrary = modifier[slashIdx - 1] === ']' || modifier[slashIdx + 1] === '['
|
|
96
|
+
|
|
97
|
+
// Backtrack to the previous `/` if the one we found was inside an arbitrary
|
|
98
|
+
if (!isNextToArbitrary) {
|
|
99
|
+
if (arbitraryStartIdx !== -1 && arbitraryEndIdx !== -1) {
|
|
100
|
+
if (arbitraryStartIdx < slashIdx && slashIdx < arbitraryEndIdx) {
|
|
101
|
+
slashIdx = modifier.lastIndexOf('/', arbitraryStartIdx)
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
|
|
90
106
|
if (slashIdx === -1 || slashIdx === modifier.length - 1) {
|
|
91
107
|
return [modifier, undefined]
|
|
92
108
|
}
|
|
@@ -60,6 +60,10 @@ let elementProperties = {
|
|
|
60
60
|
':first-letter': ['terminal', 'jumpable'],
|
|
61
61
|
':first-line': ['terminal', 'jumpable'],
|
|
62
62
|
|
|
63
|
+
':where': [],
|
|
64
|
+
':is': [],
|
|
65
|
+
':has': [],
|
|
66
|
+
|
|
63
67
|
// The default value is used when the pseudo-element is not recognized
|
|
64
68
|
// Because it's not recognized, we don't know if it's terminal or not
|
|
65
69
|
// So we assume it can be moved AND can have user-action pseudo classes attached to it
|
package/types/config.d.ts
CHANGED
|
@@ -75,6 +75,13 @@ type DarkModeConfig =
|
|
|
75
75
|
| 'class'
|
|
76
76
|
// Use the `class` strategy with a custom class instead of `.dark`.
|
|
77
77
|
| ['class', string]
|
|
78
|
+
// Use the `selector` strategy — same as `class` but uses `:where()` for more predicable behavior
|
|
79
|
+
| 'selector'
|
|
80
|
+
// Use the `selector` strategy with a custom selector instead of `.dark`.
|
|
81
|
+
| ['selector', string]
|
|
82
|
+
// Use the `variant` strategy, which allows you to completely customize the selector
|
|
83
|
+
// It takes a string or an array of strings, which are passed directly to `addVariant()`
|
|
84
|
+
| ['variant', string | string[]]
|
|
78
85
|
|
|
79
86
|
type Screen = { raw: string } | { min: string } | { max: string } | { min: string; max: string }
|
|
80
87
|
type ScreensConfig = string[] | KeyValuePair<string, string | Screen | Screen[]>
|