tailwindcss 3.4.0 → 3.4.2
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 +40 -1
- package/README.md +2 -3
- package/lib/cli/build/plugin.js +4 -11
- package/lib/cli.js +1 -5
- package/lib/corePluginList.js +1 -0
- package/lib/corePlugins.js +92 -25
- package/lib/css/preflight.css +4 -3
- package/lib/featureFlags.js +2 -6
- package/lib/lib/content.js +36 -3
- package/lib/lib/defaultExtractor.js +2 -2
- package/lib/lib/expandApplyAtRules.js +13 -0
- package/lib/lib/expandTailwindAtRules.js +20 -32
- package/lib/lib/generateRules.js +13 -2
- package/lib/lib/load-config.js +4 -0
- package/lib/lib/offsets.js +51 -2
- package/lib/lib/resolveDefaultsAtRules.js +3 -1
- package/lib/lib/setupContextUtils.js +23 -3
- package/lib/lib/sharedState.js +2 -10
- package/lib/plugin.js +0 -50
- package/lib/processTailwindFeatures.js +0 -2
- package/lib/util/dataTypes.js +12 -2
- package/lib/util/pseudoElements.js +3 -0
- package/package.json +5 -8
- package/peers/index.js +61 -61
- package/src/cli/build/plugin.js +4 -11
- package/src/cli.js +1 -5
- package/src/corePluginList.js +1 -1
- package/src/corePlugins.js +88 -27
- package/src/css/preflight.css +4 -3
- package/src/featureFlags.js +2 -6
- package/src/lib/content.js +42 -1
- package/src/lib/defaultExtractor.js +2 -2
- package/src/lib/expandApplyAtRules.js +17 -0
- package/src/lib/expandTailwindAtRules.js +23 -41
- package/src/lib/generateRules.js +12 -2
- package/src/lib/load-config.ts +5 -0
- package/src/lib/offsets.js +61 -2
- package/src/lib/resolveDefaultsAtRules.js +5 -1
- package/src/lib/setupContextUtils.js +28 -2
- package/src/lib/sharedState.js +0 -4
- package/src/plugin.js +0 -60
- package/src/processTailwindFeatures.js +0 -3
- package/src/util/dataTypes.js +13 -1
- package/src/util/pseudoElements.js +4 -0
- package/types/config.d.ts +7 -0
- package/types/generated/corePluginList.d.ts +1 -1
- package/lib/lib/detectNesting.js +0 -45
- package/lib/oxide/cli/build/deps.js +0 -89
- package/lib/oxide/cli/build/index.js +0 -53
- package/lib/oxide/cli/build/plugin.js +0 -375
- package/lib/oxide/cli/build/utils.js +0 -87
- package/lib/oxide/cli/build/watching.js +0 -179
- package/lib/oxide/cli/help/index.js +0 -72
- package/lib/oxide/cli/index.js +0 -214
- package/lib/oxide/cli/init/index.js +0 -52
- package/lib/oxide/cli.js +0 -5
- package/lib/oxide/postcss-plugin.js +0 -2
- package/scripts/swap-engines.js +0 -40
- package/src/lib/detectNesting.js +0 -47
- package/src/oxide/cli/build/deps.ts +0 -91
- package/src/oxide/cli/build/index.ts +0 -47
- package/src/oxide/cli/build/plugin.ts +0 -442
- package/src/oxide/cli/build/utils.ts +0 -74
- package/src/oxide/cli/build/watching.ts +0 -225
- package/src/oxide/cli/help/index.ts +0 -69
- package/src/oxide/cli/index.ts +0 -204
- package/src/oxide/cli/init/index.ts +0 -59
- package/src/oxide/cli.ts +0 -1
- package/src/oxide/postcss-plugin.ts +0 -1
package/src/corePlugins.js
CHANGED
|
@@ -207,8 +207,8 @@ export let variantPlugins = {
|
|
|
207
207
|
},
|
|
208
208
|
|
|
209
209
|
directionVariants: ({ addVariant }) => {
|
|
210
|
-
addVariant('ltr', '
|
|
211
|
-
addVariant('rtl', '
|
|
210
|
+
addVariant('ltr', '&:where([dir="ltr"], [dir="ltr"] *)')
|
|
211
|
+
addVariant('rtl', '&:where([dir="rtl"], [dir="rtl"] *)')
|
|
212
212
|
},
|
|
213
213
|
|
|
214
214
|
reducedMotionVariants: ({ addVariant }) => {
|
|
@@ -217,7 +217,7 @@ export let variantPlugins = {
|
|
|
217
217
|
},
|
|
218
218
|
|
|
219
219
|
darkVariants: ({ config, addVariant }) => {
|
|
220
|
-
let [mode,
|
|
220
|
+
let [mode, selector = '.dark'] = [].concat(config('darkMode', 'media'))
|
|
221
221
|
|
|
222
222
|
if (mode === false) {
|
|
223
223
|
mode = 'media'
|
|
@@ -228,10 +228,49 @@ export let variantPlugins = {
|
|
|
228
228
|
])
|
|
229
229
|
}
|
|
230
230
|
|
|
231
|
-
if (mode === '
|
|
232
|
-
|
|
231
|
+
if (mode === 'variant') {
|
|
232
|
+
let formats
|
|
233
|
+
if (Array.isArray(selector)) {
|
|
234
|
+
formats = selector
|
|
235
|
+
} else if (typeof selector === 'function') {
|
|
236
|
+
formats = selector
|
|
237
|
+
} else if (typeof selector === 'string') {
|
|
238
|
+
formats = [selector]
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
// TODO: We could also add these warnings if the user passes a function that returns string | string[]
|
|
242
|
+
// But this is an advanced enough use case that it's probably not necessary
|
|
243
|
+
if (Array.isArray(formats)) {
|
|
244
|
+
for (let format of formats) {
|
|
245
|
+
if (format === '.dark') {
|
|
246
|
+
mode = false
|
|
247
|
+
log.warn('darkmode-variant-without-selector', [
|
|
248
|
+
'When using `variant` for `darkMode`, you must provide a selector.',
|
|
249
|
+
'Example: `darkMode: ["variant", ".your-selector &"]`',
|
|
250
|
+
])
|
|
251
|
+
} else if (!format.includes('&')) {
|
|
252
|
+
mode = false
|
|
253
|
+
log.warn('darkmode-variant-without-ampersand', [
|
|
254
|
+
'When using `variant` for `darkMode`, your selector must contain `&`.',
|
|
255
|
+
'Example `darkMode: ["variant", ".your-selector &"]`',
|
|
256
|
+
])
|
|
257
|
+
}
|
|
258
|
+
}
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
selector = formats
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
if (mode === 'selector') {
|
|
265
|
+
// New preferred behavior
|
|
266
|
+
addVariant('dark', `&:where(${selector}, ${selector} *)`)
|
|
233
267
|
} else if (mode === 'media') {
|
|
234
268
|
addVariant('dark', '@media (prefers-color-scheme: dark)')
|
|
269
|
+
} else if (mode === 'variant') {
|
|
270
|
+
addVariant('dark', selector)
|
|
271
|
+
} else if (mode === 'class') {
|
|
272
|
+
// Old behavior
|
|
273
|
+
addVariant('dark', `&:is(${selector} *)`)
|
|
235
274
|
}
|
|
236
275
|
},
|
|
237
276
|
|
|
@@ -1301,16 +1340,6 @@ export let corePlugins = {
|
|
|
1301
1340
|
'space-x': (value) => {
|
|
1302
1341
|
value = value === '0' ? '0px' : value
|
|
1303
1342
|
|
|
1304
|
-
if (__OXIDE__) {
|
|
1305
|
-
return {
|
|
1306
|
-
'& > :not([hidden]) ~ :not([hidden])': {
|
|
1307
|
-
'--tw-space-x-reverse': '0',
|
|
1308
|
-
'margin-inline-end': `calc(${value} * var(--tw-space-x-reverse))`,
|
|
1309
|
-
'margin-inline-start': `calc(${value} * calc(1 - var(--tw-space-x-reverse)))`,
|
|
1310
|
-
},
|
|
1311
|
-
}
|
|
1312
|
-
}
|
|
1313
|
-
|
|
1314
1343
|
return {
|
|
1315
1344
|
'& > :not([hidden]) ~ :not([hidden])': {
|
|
1316
1345
|
'--tw-space-x-reverse': '0',
|
|
@@ -1346,17 +1375,6 @@ export let corePlugins = {
|
|
|
1346
1375
|
'divide-x': (value) => {
|
|
1347
1376
|
value = value === '0' ? '0px' : value
|
|
1348
1377
|
|
|
1349
|
-
if (__OXIDE__) {
|
|
1350
|
-
return {
|
|
1351
|
-
'& > :not([hidden]) ~ :not([hidden])': {
|
|
1352
|
-
'@defaults border-width': {},
|
|
1353
|
-
'--tw-divide-x-reverse': '0',
|
|
1354
|
-
'border-inline-end-width': `calc(${value} * var(--tw-divide-x-reverse))`,
|
|
1355
|
-
'border-inline-start-width': `calc(${value} * calc(1 - var(--tw-divide-x-reverse)))`,
|
|
1356
|
-
},
|
|
1357
|
-
}
|
|
1358
|
-
}
|
|
1359
|
-
|
|
1360
1378
|
return {
|
|
1361
1379
|
'& > :not([hidden]) ~ :not([hidden])': {
|
|
1362
1380
|
'@defaults border-width': {},
|
|
@@ -2345,6 +2363,7 @@ export let corePlugins = {
|
|
|
2345
2363
|
'.mix-blend-saturation': { 'mix-blend-mode': 'saturation' },
|
|
2346
2364
|
'.mix-blend-color': { 'mix-blend-mode': 'color' },
|
|
2347
2365
|
'.mix-blend-luminosity': { 'mix-blend-mode': 'luminosity' },
|
|
2366
|
+
'.mix-blend-plus-darker': { 'mix-blend-mode': 'plus-darker' },
|
|
2348
2367
|
'.mix-blend-plus-lighter': { 'mix-blend-mode': 'plus-lighter' },
|
|
2349
2368
|
})
|
|
2350
2369
|
},
|
|
@@ -2358,7 +2377,7 @@ export let corePlugins = {
|
|
|
2358
2377
|
].join(', ')
|
|
2359
2378
|
|
|
2360
2379
|
return function ({ matchUtilities, addDefaults, theme }) {
|
|
2361
|
-
addDefaults('
|
|
2380
|
+
addDefaults('box-shadow', {
|
|
2362
2381
|
'--tw-ring-offset-shadow': '0 0 #0000',
|
|
2363
2382
|
'--tw-ring-shadow': '0 0 #0000',
|
|
2364
2383
|
'--tw-shadow': '0 0 #0000',
|
|
@@ -2892,6 +2911,48 @@ export let corePlugins = {
|
|
|
2892
2911
|
{ filterDefault: true }
|
|
2893
2912
|
),
|
|
2894
2913
|
willChange: createUtilityPlugin('willChange', [['will-change', ['will-change']]]),
|
|
2914
|
+
contain: ({ addDefaults, addUtilities }) => {
|
|
2915
|
+
let cssContainValue =
|
|
2916
|
+
'var(--tw-contain-size) var(--tw-contain-layout) var(--tw-contain-paint) var(--tw-contain-style)'
|
|
2917
|
+
|
|
2918
|
+
addDefaults('contain', {
|
|
2919
|
+
'--tw-contain-size': ' ',
|
|
2920
|
+
'--tw-contain-layout': ' ',
|
|
2921
|
+
'--tw-contain-paint': ' ',
|
|
2922
|
+
'--tw-contain-style': ' ',
|
|
2923
|
+
})
|
|
2924
|
+
|
|
2925
|
+
addUtilities({
|
|
2926
|
+
'.contain-none': { contain: 'none' },
|
|
2927
|
+
'.contain-content': { contain: 'content' },
|
|
2928
|
+
'.contain-strict': { contain: 'strict' },
|
|
2929
|
+
'.contain-size': {
|
|
2930
|
+
'@defaults contain': {},
|
|
2931
|
+
'--tw-contain-size': 'size',
|
|
2932
|
+
contain: cssContainValue,
|
|
2933
|
+
},
|
|
2934
|
+
'.contain-inline-size': {
|
|
2935
|
+
'@defaults contain': {},
|
|
2936
|
+
'--tw-contain-size': 'inline-size',
|
|
2937
|
+
contain: cssContainValue,
|
|
2938
|
+
},
|
|
2939
|
+
'.contain-layout': {
|
|
2940
|
+
'@defaults contain': {},
|
|
2941
|
+
'--tw-contain-layout': 'layout',
|
|
2942
|
+
contain: cssContainValue,
|
|
2943
|
+
},
|
|
2944
|
+
'.contain-paint': {
|
|
2945
|
+
'@defaults contain': {},
|
|
2946
|
+
'--tw-contain-paint': 'paint',
|
|
2947
|
+
contain: cssContainValue,
|
|
2948
|
+
},
|
|
2949
|
+
'.contain-style': {
|
|
2950
|
+
'@defaults contain': {},
|
|
2951
|
+
'--tw-contain-style': 'style',
|
|
2952
|
+
contain: cssContainValue,
|
|
2953
|
+
},
|
|
2954
|
+
})
|
|
2955
|
+
},
|
|
2895
2956
|
content: createUtilityPlugin('content', [
|
|
2896
2957
|
['content', ['--tw-content', ['content', 'var(--tw-content)']]],
|
|
2897
2958
|
]),
|
package/src/css/preflight.css
CHANGED
|
@@ -175,6 +175,7 @@ textarea {
|
|
|
175
175
|
font-size: 100%; /* 1 */
|
|
176
176
|
font-weight: inherit; /* 1 */
|
|
177
177
|
line-height: inherit; /* 1 */
|
|
178
|
+
letter-spacing: inherit; /* 1 */
|
|
178
179
|
color: inherit; /* 1 */
|
|
179
180
|
margin: 0; /* 2 */
|
|
180
181
|
padding: 0; /* 3 */
|
|
@@ -195,9 +196,9 @@ select {
|
|
|
195
196
|
*/
|
|
196
197
|
|
|
197
198
|
button,
|
|
198
|
-
[type='button'],
|
|
199
|
-
[type='reset'],
|
|
200
|
-
[type='submit'] {
|
|
199
|
+
input:where([type='button']),
|
|
200
|
+
input:where([type='reset']),
|
|
201
|
+
input:where([type='submit']) {
|
|
201
202
|
-webkit-appearance: button; /* 1 */
|
|
202
203
|
background-color: transparent; /* 2 */
|
|
203
204
|
background-image: none; /* 2 */
|
package/src/featureFlags.js
CHANGED
|
@@ -4,12 +4,8 @@ import log from './util/log'
|
|
|
4
4
|
let defaults = {
|
|
5
5
|
optimizeUniversalDefaults: false,
|
|
6
6
|
generalizedModifiers: true,
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
},
|
|
10
|
-
get relativeContentPathsByDefault() {
|
|
11
|
-
return __OXIDE__
|
|
12
|
-
},
|
|
7
|
+
disableColorOpacityUtilitiesByDefault: false,
|
|
8
|
+
relativeContentPathsByDefault: false,
|
|
13
9
|
}
|
|
14
10
|
|
|
15
11
|
let featureFlags = {
|
package/src/lib/content.js
CHANGED
|
@@ -4,10 +4,47 @@ 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
|
|
|
10
|
+
/*!
|
|
11
|
+
* Modified version of normalize-path, original license below
|
|
12
|
+
*
|
|
13
|
+
* normalize-path <https://github.com/jonschlinkert/normalize-path>
|
|
14
|
+
*
|
|
15
|
+
* Copyright (c) 2014-2018, Jon Schlinkert.
|
|
16
|
+
* Released under the MIT License.
|
|
17
|
+
*/
|
|
18
|
+
|
|
19
|
+
function normalizePath(path) {
|
|
20
|
+
if (typeof path !== 'string') {
|
|
21
|
+
throw new TypeError('expected path to be a string')
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
if (path === '\\' || path === '/') return '/'
|
|
25
|
+
|
|
26
|
+
var len = path.length
|
|
27
|
+
if (len <= 1) return path
|
|
28
|
+
|
|
29
|
+
// ensure that win32 namespaces has two leading slashes, so that the path is
|
|
30
|
+
// handled properly by the win32 version of path.parse() after being normalized
|
|
31
|
+
// https://msdn.microsoft.com/library/windows/desktop/aa365247(v=vs.85).aspx#namespaces
|
|
32
|
+
var prefix = ''
|
|
33
|
+
if (len > 4 && path[3] === '\\') {
|
|
34
|
+
var ch = path[2]
|
|
35
|
+
if ((ch === '?' || ch === '.') && path.slice(0, 2) === '\\\\') {
|
|
36
|
+
path = path.slice(2)
|
|
37
|
+
prefix = '//'
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
// Modified part: instead of purely splitting on `\\` and `/`, we split on
|
|
42
|
+
// `/` and `\\` that is _not_ followed by any of the following characters: ()[]
|
|
43
|
+
// This is to ensure that we keep the escaping of brackets and parentheses
|
|
44
|
+
let segs = path.split(/[/\\]+(?![\(\)\[\]])/)
|
|
45
|
+
return prefix + segs.join('/')
|
|
46
|
+
}
|
|
47
|
+
|
|
11
48
|
/** @typedef {import('../../types/config.js').RawFile} RawFile */
|
|
12
49
|
/** @typedef {import('../../types/config.js').FilePath} FilePath */
|
|
13
50
|
|
|
@@ -73,6 +110,10 @@ export function parseCandidateFiles(context, tailwindConfig) {
|
|
|
73
110
|
* @returns {ContentPath}
|
|
74
111
|
*/
|
|
75
112
|
function parseFilePath(filePath, ignore) {
|
|
113
|
+
// Escape special characters in the file path such as: ()[]
|
|
114
|
+
// But only if the special character isn't already escaped
|
|
115
|
+
filePath = filePath.replace(/(?<!\\)([\[\]\(\)])/g, '\\$1')
|
|
116
|
+
|
|
76
117
|
let contentPath = {
|
|
77
118
|
original: filePath,
|
|
78
119
|
base: filePath,
|
|
@@ -96,7 +96,7 @@ function* buildRegExps(context) {
|
|
|
96
96
|
regex.pattern([/@\[[^\s"'`]+\](\/[^\s"'`]+)?/, separator]),
|
|
97
97
|
|
|
98
98
|
// With variant modifier (e.g.: group-[..]/modifier)
|
|
99
|
-
regex.pattern([/([^\s"'`\[\\]+-)?\[[^\s"'`]+\]
|
|
99
|
+
regex.pattern([/([^\s"'`\[\\]+-)?\[[^\s"'`]+\]\/[\w_-]+/, separator]),
|
|
100
100
|
|
|
101
101
|
regex.pattern([/([^\s"'`\[\\]+-)?\[[^\s"'`]+\]/, separator]),
|
|
102
102
|
regex.pattern([/[^\s"'`\[\\]+/, separator]),
|
|
@@ -105,7 +105,7 @@ function* buildRegExps(context) {
|
|
|
105
105
|
// With quotes allowed
|
|
106
106
|
regex.any([
|
|
107
107
|
// With variant modifier (e.g.: group-[..]/modifier)
|
|
108
|
-
regex.pattern([/([^\s"'`\[\\]+-)?\[[^\s`]+\]
|
|
108
|
+
regex.pattern([/([^\s"'`\[\\]+-)?\[[^\s`]+\]\/[\w_-]+/, separator]),
|
|
109
109
|
|
|
110
110
|
regex.pattern([/([^\s"'`\[\\]+-)?\[[^\s`]+\]/, separator]),
|
|
111
111
|
regex.pattern([/[^\s`\[\\]+/, separator]),
|
|
@@ -432,6 +432,23 @@ function processApply(root, context, localCache) {
|
|
|
432
432
|
|
|
433
433
|
let rules = applyClassCache.get(applyCandidate)
|
|
434
434
|
|
|
435
|
+
// Verify that we can apply the class
|
|
436
|
+
for (let [, rule] of rules) {
|
|
437
|
+
if (rule.type === 'atrule') {
|
|
438
|
+
continue
|
|
439
|
+
}
|
|
440
|
+
|
|
441
|
+
rule.walkRules(() => {
|
|
442
|
+
throw apply.error(
|
|
443
|
+
[
|
|
444
|
+
`The \`${applyCandidate}\` class cannot be used with \`@apply\` because \`@apply\` does not currently support nested CSS.`,
|
|
445
|
+
'Rewrite the selector without nesting or configure the `tailwindcss/nesting` plugin:',
|
|
446
|
+
'https://tailwindcss.com/docs/using-with-preprocessors#nesting',
|
|
447
|
+
].join('\n')
|
|
448
|
+
)
|
|
449
|
+
})
|
|
450
|
+
}
|
|
451
|
+
|
|
435
452
|
candidates.push([applyCandidate, important, rules])
|
|
436
453
|
}
|
|
437
454
|
}
|
|
@@ -130,41 +130,25 @@ export default function expandTailwindAtRules(context) {
|
|
|
130
130
|
|
|
131
131
|
env.DEBUG && console.time('Reading changed files')
|
|
132
132
|
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
for (let candidate of require('@tailwindcss/oxide').parseCandidateStringsFromFiles(
|
|
136
|
-
context.changedContent
|
|
137
|
-
// Object.assign({}, builtInTransformers, context.tailwindConfig.content.transform)
|
|
138
|
-
)) {
|
|
139
|
-
candidates.add(candidate)
|
|
140
|
-
}
|
|
141
|
-
|
|
142
|
-
// for (let { file, content, extension } of context.changedContent) {
|
|
143
|
-
// let transformer = getTransformer(context.tailwindConfig, extension)
|
|
144
|
-
// let extractor = getExtractor(context, extension)
|
|
145
|
-
// getClassCandidatesOxide(file, transformer(content), extractor, candidates, seen)
|
|
146
|
-
// }
|
|
147
|
-
} else {
|
|
148
|
-
/** @type {[item: {file?: string, content?: string}, meta: {transformer: any, extractor: any}][]} */
|
|
149
|
-
let regexParserContent = []
|
|
133
|
+
/** @type {[item: {file?: string, content?: string}, meta: {transformer: any, extractor: any}][]} */
|
|
134
|
+
let regexParserContent = []
|
|
150
135
|
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
136
|
+
for (let item of context.changedContent) {
|
|
137
|
+
let transformer = getTransformer(context.tailwindConfig, item.extension)
|
|
138
|
+
let extractor = getExtractor(context, item.extension)
|
|
139
|
+
regexParserContent.push([item, { transformer, extractor }])
|
|
140
|
+
}
|
|
156
141
|
|
|
157
|
-
|
|
142
|
+
const BATCH_SIZE = 500
|
|
158
143
|
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
}
|
|
144
|
+
for (let i = 0; i < regexParserContent.length; i += BATCH_SIZE) {
|
|
145
|
+
let batch = regexParserContent.slice(i, i + BATCH_SIZE)
|
|
146
|
+
await Promise.all(
|
|
147
|
+
batch.map(async ([{ file, content }, { transformer, extractor }]) => {
|
|
148
|
+
content = file ? await fs.promises.readFile(file, 'utf8') : content
|
|
149
|
+
getClassCandidates(transformer(content), extractor, candidates, seen)
|
|
150
|
+
})
|
|
151
|
+
)
|
|
168
152
|
}
|
|
169
153
|
|
|
170
154
|
env.DEBUG && console.timeEnd('Reading changed files')
|
|
@@ -176,15 +160,13 @@ export default function expandTailwindAtRules(context) {
|
|
|
176
160
|
|
|
177
161
|
env.DEBUG && console.time('Generate rules')
|
|
178
162
|
env.DEBUG && console.time('Sorting candidates')
|
|
179
|
-
let sortedCandidates =
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
})
|
|
187
|
-
)
|
|
163
|
+
let sortedCandidates = new Set(
|
|
164
|
+
[...candidates].sort((a, z) => {
|
|
165
|
+
if (a === z) return 0
|
|
166
|
+
if (a < z) return -1
|
|
167
|
+
return 1
|
|
168
|
+
})
|
|
169
|
+
)
|
|
188
170
|
env.DEBUG && console.timeEnd('Sorting candidates')
|
|
189
171
|
generateRules(sortedCandidates, context)
|
|
190
172
|
env.DEBUG && console.timeEnd('Generate rules')
|
package/src/lib/generateRules.js
CHANGED
|
@@ -119,10 +119,20 @@ function applyImportant(matches, classCandidate) {
|
|
|
119
119
|
|
|
120
120
|
let result = []
|
|
121
121
|
|
|
122
|
+
function isInKeyframes(rule) {
|
|
123
|
+
return rule.parent && rule.parent.type === 'atrule' && rule.parent.name === 'keyframes'
|
|
124
|
+
}
|
|
125
|
+
|
|
122
126
|
for (let [meta, rule] of matches) {
|
|
123
127
|
let container = postcss.root({ nodes: [rule.clone()] })
|
|
124
128
|
|
|
125
129
|
container.walkRules((r) => {
|
|
130
|
+
// Declarations inside keyframes cannot be marked as important
|
|
131
|
+
// They will be ignored by the browser
|
|
132
|
+
if (isInKeyframes(r)) {
|
|
133
|
+
return
|
|
134
|
+
}
|
|
135
|
+
|
|
126
136
|
let ast = selectorParser().astSync(r.selector)
|
|
127
137
|
|
|
128
138
|
// Remove extraneous selectors that do not include the base candidate
|
|
@@ -502,11 +512,11 @@ function extractArbitraryProperty(classCandidate, context) {
|
|
|
502
512
|
return null
|
|
503
513
|
}
|
|
504
514
|
|
|
505
|
-
let sort = context.offsets.arbitraryProperty()
|
|
515
|
+
let sort = context.offsets.arbitraryProperty(classCandidate)
|
|
506
516
|
|
|
507
517
|
return [
|
|
508
518
|
[
|
|
509
|
-
{ sort, layer: 'utilities' },
|
|
519
|
+
{ sort, layer: 'utilities', options: { respectImportant: true } },
|
|
510
520
|
() => ({
|
|
511
521
|
[asClass(classCandidate)]: {
|
|
512
522
|
[property]: normalized,
|
package/src/lib/load-config.ts
CHANGED
|
@@ -18,6 +18,11 @@ function lazyJiti() {
|
|
|
18
18
|
(jiti = jitiFactory(__filename, {
|
|
19
19
|
interopDefault: true,
|
|
20
20
|
transform: (opts) => {
|
|
21
|
+
// Sucrase can't transform import.meta so we have to use Babel
|
|
22
|
+
if (opts.source.includes('import.meta')) {
|
|
23
|
+
return require('jiti/dist/babel.js')(opts)
|
|
24
|
+
}
|
|
25
|
+
|
|
21
26
|
return transform(opts.source, {
|
|
22
27
|
transforms: ['typescript', 'imports'],
|
|
23
28
|
})
|
package/src/lib/offsets.js
CHANGED
|
@@ -23,7 +23,9 @@ import { remapBitfield } from './remap-bitfield.js'
|
|
|
23
23
|
* @property {bigint} arbitrary 0n if false, 1n if true
|
|
24
24
|
* @property {bigint} variants Dynamic size. 1 bit per registered variant. 0n means no variants
|
|
25
25
|
* @property {bigint} parallelIndex Rule index for the parallel variant. 0 if not applicable.
|
|
26
|
-
* @property {bigint} index Index of the rule / utility in
|
|
26
|
+
* @property {bigint} index Index of the rule / utility in its given *parent* layer. Monotonically increasing.
|
|
27
|
+
* @property {bigint} propertyOffset Offset for the arbitrary property. Only valid after sorting.
|
|
28
|
+
* @property {string} property Name/Value of the arbitrary property.
|
|
27
29
|
* @property {VariantOption[]} options Some information on how we can sort arbitrary variants
|
|
28
30
|
*/
|
|
29
31
|
|
|
@@ -88,17 +90,21 @@ export class Offsets {
|
|
|
88
90
|
variants: 0n,
|
|
89
91
|
parallelIndex: 0n,
|
|
90
92
|
index: this.offsets[layer]++,
|
|
93
|
+
propertyOffset: 0n,
|
|
94
|
+
property: '',
|
|
91
95
|
options: [],
|
|
92
96
|
}
|
|
93
97
|
}
|
|
94
98
|
|
|
95
99
|
/**
|
|
100
|
+
* @param {string} name
|
|
96
101
|
* @returns {RuleOffset}
|
|
97
102
|
*/
|
|
98
|
-
arbitraryProperty() {
|
|
103
|
+
arbitraryProperty(name) {
|
|
99
104
|
return {
|
|
100
105
|
...this.create('utilities'),
|
|
101
106
|
arbitrary: 1n,
|
|
107
|
+
property: name,
|
|
102
108
|
}
|
|
103
109
|
}
|
|
104
110
|
|
|
@@ -262,6 +268,11 @@ export class Offsets {
|
|
|
262
268
|
return a.arbitrary - b.arbitrary
|
|
263
269
|
}
|
|
264
270
|
|
|
271
|
+
// Always sort arbitrary properties alphabetically
|
|
272
|
+
if (a.propertyOffset !== b.propertyOffset) {
|
|
273
|
+
return a.propertyOffset - b.propertyOffset
|
|
274
|
+
}
|
|
275
|
+
|
|
265
276
|
// Sort utilities, components, etc… in the order they were registered
|
|
266
277
|
return a.index - b.index
|
|
267
278
|
}
|
|
@@ -320,14 +331,62 @@ export class Offsets {
|
|
|
320
331
|
})
|
|
321
332
|
}
|
|
322
333
|
|
|
334
|
+
/**
|
|
335
|
+
* @template T
|
|
336
|
+
* @param {[RuleOffset, T][]} list
|
|
337
|
+
* @returns {[RuleOffset, T][]}
|
|
338
|
+
*/
|
|
339
|
+
sortArbitraryProperties(list) {
|
|
340
|
+
// Collect all known arbitrary properties
|
|
341
|
+
let known = new Set()
|
|
342
|
+
|
|
343
|
+
for (let [offset] of list) {
|
|
344
|
+
if (offset.arbitrary === 1n) {
|
|
345
|
+
known.add(offset.property)
|
|
346
|
+
}
|
|
347
|
+
}
|
|
348
|
+
|
|
349
|
+
// No arbitrary properties? Nothing to do.
|
|
350
|
+
if (known.size === 0) {
|
|
351
|
+
return list
|
|
352
|
+
}
|
|
353
|
+
|
|
354
|
+
// Sort the properties alphabetically
|
|
355
|
+
let properties = Array.from(known).sort()
|
|
356
|
+
|
|
357
|
+
// Create a map from the property name to its offset
|
|
358
|
+
let offsets = new Map()
|
|
359
|
+
|
|
360
|
+
let offset = 1n
|
|
361
|
+
for (let property of properties) {
|
|
362
|
+
offsets.set(property, offset++)
|
|
363
|
+
}
|
|
364
|
+
|
|
365
|
+
// Apply the sorted offsets to the list
|
|
366
|
+
return list.map((item) => {
|
|
367
|
+
let [offset, rule] = item
|
|
368
|
+
|
|
369
|
+
offset = {
|
|
370
|
+
...offset,
|
|
371
|
+
propertyOffset: offsets.get(offset.property) ?? 0n,
|
|
372
|
+
}
|
|
373
|
+
|
|
374
|
+
return [offset, rule]
|
|
375
|
+
})
|
|
376
|
+
}
|
|
377
|
+
|
|
323
378
|
/**
|
|
324
379
|
* @template T
|
|
325
380
|
* @param {[RuleOffset, T][]} list
|
|
326
381
|
* @returns {[RuleOffset, T][]}
|
|
327
382
|
*/
|
|
328
383
|
sort(list) {
|
|
384
|
+
// Sort arbitrary variants so they're in alphabetical order
|
|
329
385
|
list = this.remapArbitraryVariantOffsets(list)
|
|
330
386
|
|
|
387
|
+
// Sort arbitrary properties so they're in alphabetical order
|
|
388
|
+
list = this.sortArbitraryProperties(list)
|
|
389
|
+
|
|
331
390
|
return list.sort(([a], [b]) => bigSign(this.compare(a, b)))
|
|
332
391
|
}
|
|
333
392
|
}
|
|
@@ -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)
|
|
@@ -767,14 +767,35 @@ function resolvePlugins(context, root) {
|
|
|
767
767
|
variantPlugins['supportsVariants'],
|
|
768
768
|
variantPlugins['reducedMotionVariants'],
|
|
769
769
|
variantPlugins['prefersContrastVariants'],
|
|
770
|
-
variantPlugins['printVariant'],
|
|
771
770
|
variantPlugins['screenVariants'],
|
|
772
771
|
variantPlugins['orientationVariants'],
|
|
773
772
|
variantPlugins['directionVariants'],
|
|
774
773
|
variantPlugins['darkVariants'],
|
|
775
774
|
variantPlugins['forcedColorsVariants'],
|
|
775
|
+
variantPlugins['printVariant'],
|
|
776
776
|
]
|
|
777
777
|
|
|
778
|
+
// This is a compatibility fix for the pre 3.4 dark mode behavior
|
|
779
|
+
// `class` retains the old behavior, but `selector` keeps the new behavior
|
|
780
|
+
let isLegacyDarkMode =
|
|
781
|
+
context.tailwindConfig.darkMode === 'class' ||
|
|
782
|
+
(Array.isArray(context.tailwindConfig.darkMode) &&
|
|
783
|
+
context.tailwindConfig.darkMode[0] === 'class')
|
|
784
|
+
|
|
785
|
+
if (isLegacyDarkMode) {
|
|
786
|
+
afterVariants = [
|
|
787
|
+
variantPlugins['supportsVariants'],
|
|
788
|
+
variantPlugins['reducedMotionVariants'],
|
|
789
|
+
variantPlugins['prefersContrastVariants'],
|
|
790
|
+
variantPlugins['darkVariants'],
|
|
791
|
+
variantPlugins['screenVariants'],
|
|
792
|
+
variantPlugins['orientationVariants'],
|
|
793
|
+
variantPlugins['directionVariants'],
|
|
794
|
+
variantPlugins['forcedColorsVariants'],
|
|
795
|
+
variantPlugins['printVariant'],
|
|
796
|
+
]
|
|
797
|
+
}
|
|
798
|
+
|
|
778
799
|
return [...corePluginList, ...beforeVariants, ...userPlugins, ...afterVariants, ...layerPlugins]
|
|
779
800
|
}
|
|
780
801
|
|
|
@@ -1023,6 +1044,11 @@ function registerPlugins(plugins, context) {
|
|
|
1023
1044
|
|
|
1024
1045
|
// Generate a list of available variants with meta information of the type of variant.
|
|
1025
1046
|
context.getVariants = function getVariants() {
|
|
1047
|
+
// We use a unique, random ID for candidate names to avoid conflicts
|
|
1048
|
+
// We can't use characters like `_`, `:`, `@` or `.` because they might
|
|
1049
|
+
// be used as a separator
|
|
1050
|
+
let id = Math.random().toString(36).substring(7).toUpperCase()
|
|
1051
|
+
|
|
1026
1052
|
let result = []
|
|
1027
1053
|
for (let [name, options] of context.variantOptions.entries()) {
|
|
1028
1054
|
if (options.variantInfo === VARIANT_INFO.Base) continue
|
|
@@ -1033,7 +1059,7 @@ function registerPlugins(plugins, context) {
|
|
|
1033
1059
|
values: Object.keys(options.values ?? {}),
|
|
1034
1060
|
hasDash: name !== '@',
|
|
1035
1061
|
selectors({ modifier, value } = {}) {
|
|
1036
|
-
let candidate =
|
|
1062
|
+
let candidate = `TAILWINDPLACEHOLDER${id}`
|
|
1037
1063
|
|
|
1038
1064
|
let rule = postcss.rule({ selector: `.${candidate}` })
|
|
1039
1065
|
let container = postcss.root({ nodes: [rule.clone()] })
|
package/src/lib/sharedState.js
CHANGED
|
@@ -1,16 +1,12 @@
|
|
|
1
|
-
import pkg from '../../package.json'
|
|
2
|
-
|
|
3
1
|
export const env =
|
|
4
2
|
typeof process !== 'undefined'
|
|
5
3
|
? {
|
|
6
4
|
NODE_ENV: process.env.NODE_ENV,
|
|
7
5
|
DEBUG: resolveDebug(process.env.DEBUG),
|
|
8
|
-
ENGINE: pkg.tailwindcss.engine,
|
|
9
6
|
}
|
|
10
7
|
: {
|
|
11
8
|
NODE_ENV: 'production',
|
|
12
9
|
DEBUG: false,
|
|
13
|
-
ENGINE: pkg.tailwindcss.engine,
|
|
14
10
|
}
|
|
15
11
|
|
|
16
12
|
export const contextMap = new Map()
|