tailwindcss 0.0.0-insiders.e2d5f21 → 0.0.0-insiders.e302ef1
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 +244 -3
- package/LICENSE +1 -2
- package/README.md +8 -4
- package/colors.js +2 -1
- package/defaultConfig.js +2 -1
- package/defaultTheme.js +2 -1
- package/lib/cli.js +123 -88
- package/lib/corePlugins.js +288 -360
- package/lib/css/preflight.css +4 -4
- package/lib/featureFlags.js +4 -5
- package/lib/index.js +12 -3
- package/lib/lib/cacheInvalidation.js +69 -0
- package/lib/lib/collapseAdjacentRules.js +14 -1
- package/lib/lib/collapseDuplicateDeclarations.js +52 -1
- package/lib/lib/defaultExtractor.js +44 -0
- package/lib/lib/evaluateTailwindFunctions.js +1 -1
- package/lib/lib/expandApplyAtRules.js +265 -60
- package/lib/lib/expandTailwindAtRules.js +63 -48
- package/lib/lib/generateRules.js +136 -34
- package/lib/lib/normalizeTailwindDirectives.js +10 -2
- package/lib/lib/partitionApplyAtRules.js +53 -0
- package/lib/lib/resolveDefaultsAtRules.js +30 -12
- package/lib/lib/setupContextUtils.js +137 -81
- package/lib/lib/setupTrackingContext.js +8 -7
- package/lib/lib/sharedState.js +38 -5
- package/{nesting → lib/postcss-plugins/nesting}/README.md +0 -0
- package/lib/postcss-plugins/nesting/index.js +17 -0
- package/lib/postcss-plugins/nesting/plugin.js +85 -0
- package/lib/processTailwindFeatures.js +10 -2
- package/lib/util/cloneNodes.js +12 -1
- package/lib/util/color.js +23 -8
- package/lib/util/createPlugin.js +1 -2
- package/lib/util/createUtilityPlugin.js +4 -8
- package/lib/util/dataTypes.js +4 -2
- package/lib/util/defaults.js +6 -0
- package/lib/util/flattenColorPalette.js +1 -3
- package/lib/util/log.js +10 -6
- package/lib/util/normalizeConfig.js +47 -15
- package/lib/util/normalizeScreens.js +6 -5
- package/lib/util/parseBoxShadowValue.js +44 -4
- package/lib/util/pluginUtils.js +6 -13
- package/lib/util/prefixSelector.js +4 -5
- package/lib/util/resolveConfig.js +48 -27
- package/lib/util/resolveConfigPath.js +1 -2
- package/lib/util/toColorValue.js +1 -2
- package/lib/util/toPath.js +6 -1
- package/lib/util/transformThemeValue.js +4 -8
- package/nesting/index.js +2 -12
- package/package.json +28 -33
- package/peers/index.js +3209 -4239
- package/plugin.js +2 -1
- package/resolveConfig.js +2 -1
- package/src/cli.js +62 -15
- package/src/corePlugins.js +212 -217
- package/src/css/preflight.css +4 -4
- package/src/featureFlags.js +3 -3
- package/src/index.js +14 -6
- package/src/lib/cacheInvalidation.js +52 -0
- package/src/lib/collapseAdjacentRules.js +16 -1
- package/src/lib/collapseDuplicateDeclarations.js +66 -1
- package/src/lib/defaultExtractor.js +50 -0
- package/src/lib/evaluateTailwindFunctions.js +1 -1
- package/src/lib/expandApplyAtRules.js +285 -56
- package/src/lib/expandTailwindAtRules.js +75 -37
- package/src/lib/generateRules.js +121 -27
- package/src/lib/normalizeTailwindDirectives.js +7 -1
- package/src/lib/partitionApplyAtRules.js +52 -0
- package/src/lib/resolveDefaultsAtRules.js +35 -10
- package/src/lib/setupContextUtils.js +127 -45
- package/src/lib/setupTrackingContext.js +8 -7
- package/src/lib/sharedState.js +42 -4
- package/src/postcss-plugins/nesting/README.md +42 -0
- package/src/postcss-plugins/nesting/index.js +13 -0
- package/src/postcss-plugins/nesting/plugin.js +80 -0
- package/src/processTailwindFeatures.js +12 -2
- package/src/util/cloneNodes.js +14 -1
- package/src/util/color.js +20 -7
- package/src/util/dataTypes.js +7 -5
- package/src/util/defaults.js +6 -0
- package/src/util/log.js +10 -6
- package/src/util/normalizeConfig.js +24 -3
- package/src/util/normalizeScreens.js +6 -3
- package/src/util/parseBoxShadowValue.js +51 -3
- package/src/util/pluginUtils.js +1 -1
- package/src/util/prefixSelector.js +7 -5
- package/src/util/resolveConfig.js +41 -1
- package/src/util/toPath.js +23 -1
- package/stubs/defaultConfig.stub.js +2 -2
- package/lib/lib/setupWatchingContext.js +0 -288
- package/nesting/plugin.js +0 -41
- package/src/lib/setupWatchingContext.js +0 -311
|
@@ -19,6 +19,13 @@ import { toPath } from '../util/toPath'
|
|
|
19
19
|
import log from '../util/log'
|
|
20
20
|
import negateValue from '../util/negateValue'
|
|
21
21
|
import isValidArbitraryValue from '../util/isValidArbitraryValue'
|
|
22
|
+
import { generateRules } from './generateRules'
|
|
23
|
+
import { hasContentChanged } from './cacheInvalidation.js'
|
|
24
|
+
|
|
25
|
+
function prefix(context, selector) {
|
|
26
|
+
let prefix = context.tailwindConfig.prefix
|
|
27
|
+
return typeof prefix === 'function' ? prefix(selector) : prefix + selector
|
|
28
|
+
}
|
|
22
29
|
|
|
23
30
|
function parseVariantFormatString(input) {
|
|
24
31
|
if (input.includes('{')) {
|
|
@@ -89,39 +96,55 @@ function getClasses(selector) {
|
|
|
89
96
|
return parser.transformSync(selector)
|
|
90
97
|
}
|
|
91
98
|
|
|
92
|
-
function extractCandidates(node) {
|
|
99
|
+
function extractCandidates(node, state = { containsNonOnDemandable: false }, depth = 0) {
|
|
93
100
|
let classes = []
|
|
94
101
|
|
|
102
|
+
// Handle normal rules
|
|
95
103
|
if (node.type === 'rule') {
|
|
96
104
|
for (let selector of node.selectors) {
|
|
97
105
|
let classCandidates = getClasses(selector)
|
|
98
106
|
// At least one of the selectors contains non-"on-demandable" candidates.
|
|
99
|
-
if (classCandidates.length === 0)
|
|
107
|
+
if (classCandidates.length === 0) {
|
|
108
|
+
state.containsNonOnDemandable = true
|
|
109
|
+
}
|
|
100
110
|
|
|
101
|
-
|
|
111
|
+
for (let classCandidate of classCandidates) {
|
|
112
|
+
classes.push(classCandidate)
|
|
113
|
+
}
|
|
102
114
|
}
|
|
103
|
-
return classes
|
|
104
115
|
}
|
|
105
116
|
|
|
106
|
-
|
|
117
|
+
// Handle at-rules (which contains nested rules)
|
|
118
|
+
else if (node.type === 'atrule') {
|
|
107
119
|
node.walkRules((rule) => {
|
|
108
|
-
|
|
120
|
+
for (let classCandidate of rule.selectors.flatMap((selector) =>
|
|
121
|
+
getClasses(selector, state, depth + 1)
|
|
122
|
+
)) {
|
|
123
|
+
classes.push(classCandidate)
|
|
124
|
+
}
|
|
109
125
|
})
|
|
110
126
|
}
|
|
111
127
|
|
|
128
|
+
if (depth === 0) {
|
|
129
|
+
return [state.containsNonOnDemandable || classes.length === 0, classes]
|
|
130
|
+
}
|
|
131
|
+
|
|
112
132
|
return classes
|
|
113
133
|
}
|
|
114
134
|
|
|
115
135
|
function withIdentifiers(styles) {
|
|
116
136
|
return parseStyles(styles).flatMap((node) => {
|
|
117
137
|
let nodeMap = new Map()
|
|
118
|
-
let candidates = extractCandidates(node)
|
|
138
|
+
let [containsNonOnDemandableSelectors, candidates] = extractCandidates(node)
|
|
119
139
|
|
|
120
|
-
// If this isn't "on-demandable", assign it a universal candidate.
|
|
121
|
-
if (
|
|
122
|
-
|
|
140
|
+
// If this isn't "on-demandable", assign it a universal candidate to always include it.
|
|
141
|
+
if (containsNonOnDemandableSelectors) {
|
|
142
|
+
candidates.unshift(sharedState.NOT_ON_DEMAND)
|
|
123
143
|
}
|
|
124
144
|
|
|
145
|
+
// However, it could be that it also contains "on-demandable" candidates.
|
|
146
|
+
// E.g.: `span, .foo {}`, in that case it should still be possible to use
|
|
147
|
+
// `@apply foo` for example.
|
|
125
148
|
return candidates.map((c) => {
|
|
126
149
|
if (!nodeMap.has(node)) {
|
|
127
150
|
nodeMap.set(node, node)
|
|
@@ -141,8 +164,8 @@ function buildPluginApi(tailwindConfig, context, { variantList, variantMap, offs
|
|
|
141
164
|
}
|
|
142
165
|
|
|
143
166
|
function prefixIdentifier(identifier, options) {
|
|
144
|
-
if (identifier ===
|
|
145
|
-
return
|
|
167
|
+
if (identifier === sharedState.NOT_ON_DEMAND) {
|
|
168
|
+
return sharedState.NOT_ON_DEMAND
|
|
146
169
|
}
|
|
147
170
|
|
|
148
171
|
if (!options.respectPrefix) {
|
|
@@ -208,21 +231,31 @@ function buildPluginApi(tailwindConfig, context, { variantList, variantMap, offs
|
|
|
208
231
|
// Preserved for backwards compatibility but not used in v3.0+
|
|
209
232
|
return []
|
|
210
233
|
},
|
|
211
|
-
|
|
212
|
-
for (let [identifier, rule] of withIdentifiers(
|
|
213
|
-
let
|
|
234
|
+
addBase(base) {
|
|
235
|
+
for (let [identifier, rule] of withIdentifiers(base)) {
|
|
236
|
+
let prefixedIdentifier = prefixIdentifier(identifier, {})
|
|
237
|
+
let offset = offsets.base++
|
|
214
238
|
|
|
215
|
-
if (!context.candidateRuleMap.has(
|
|
216
|
-
context.candidateRuleMap.set(
|
|
239
|
+
if (!context.candidateRuleMap.has(prefixedIdentifier)) {
|
|
240
|
+
context.candidateRuleMap.set(prefixedIdentifier, [])
|
|
217
241
|
}
|
|
218
242
|
|
|
219
|
-
context.candidateRuleMap
|
|
243
|
+
context.candidateRuleMap
|
|
244
|
+
.get(prefixedIdentifier)
|
|
245
|
+
.push([{ sort: offset, layer: 'base' }, rule])
|
|
220
246
|
}
|
|
221
247
|
},
|
|
222
|
-
|
|
223
|
-
|
|
248
|
+
/**
|
|
249
|
+
* @param {string} group
|
|
250
|
+
* @param {Record<string, string | string[]>} declarations
|
|
251
|
+
*/
|
|
252
|
+
addDefaults(group, declarations) {
|
|
253
|
+
const groups = {
|
|
254
|
+
[`@defaults ${group}`]: declarations,
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
for (let [identifier, rule] of withIdentifiers(groups)) {
|
|
224
258
|
let prefixedIdentifier = prefixIdentifier(identifier, {})
|
|
225
|
-
let offset = offsets.base++
|
|
226
259
|
|
|
227
260
|
if (!context.candidateRuleMap.has(prefixedIdentifier)) {
|
|
228
261
|
context.candidateRuleMap.set(prefixedIdentifier, [])
|
|
@@ -230,7 +263,7 @@ function buildPluginApi(tailwindConfig, context, { variantList, variantMap, offs
|
|
|
230
263
|
|
|
231
264
|
context.candidateRuleMap
|
|
232
265
|
.get(prefixedIdentifier)
|
|
233
|
-
.push([{ sort:
|
|
266
|
+
.push([{ sort: offsets.base++, layer: 'defaults' }, rule])
|
|
234
267
|
}
|
|
235
268
|
},
|
|
236
269
|
addComponents(components, options) {
|
|
@@ -243,7 +276,6 @@ function buildPluginApi(tailwindConfig, context, { variantList, variantMap, offs
|
|
|
243
276
|
|
|
244
277
|
for (let [identifier, rule] of withIdentifiers(components)) {
|
|
245
278
|
let prefixedIdentifier = prefixIdentifier(identifier, options)
|
|
246
|
-
let offset = offsets.components++
|
|
247
279
|
|
|
248
280
|
classList.add(prefixedIdentifier)
|
|
249
281
|
|
|
@@ -253,7 +285,7 @@ function buildPluginApi(tailwindConfig, context, { variantList, variantMap, offs
|
|
|
253
285
|
|
|
254
286
|
context.candidateRuleMap
|
|
255
287
|
.get(prefixedIdentifier)
|
|
256
|
-
.push([{ sort:
|
|
288
|
+
.push([{ sort: offsets.components++, layer: 'components', options }, rule])
|
|
257
289
|
}
|
|
258
290
|
},
|
|
259
291
|
addUtilities(utilities, options) {
|
|
@@ -266,7 +298,6 @@ function buildPluginApi(tailwindConfig, context, { variantList, variantMap, offs
|
|
|
266
298
|
|
|
267
299
|
for (let [identifier, rule] of withIdentifiers(utilities)) {
|
|
268
300
|
let prefixedIdentifier = prefixIdentifier(identifier, options)
|
|
269
|
-
let offset = offsets.utilities++
|
|
270
301
|
|
|
271
302
|
classList.add(prefixedIdentifier)
|
|
272
303
|
|
|
@@ -276,7 +307,7 @@ function buildPluginApi(tailwindConfig, context, { variantList, variantMap, offs
|
|
|
276
307
|
|
|
277
308
|
context.candidateRuleMap
|
|
278
309
|
.get(prefixedIdentifier)
|
|
279
|
-
.push([{ sort:
|
|
310
|
+
.push([{ sort: offsets.utilities++, layer: 'utilities', options }, rule])
|
|
280
311
|
}
|
|
281
312
|
},
|
|
282
313
|
matchUtilities: function (utilities, options) {
|
|
@@ -413,7 +444,14 @@ function trackModified(files, fileModifiedMap) {
|
|
|
413
444
|
let parsed = url.parse(file)
|
|
414
445
|
let pathname = parsed.hash ? parsed.href.replace(parsed.hash, '') : parsed.href
|
|
415
446
|
pathname = parsed.search ? pathname.replace(parsed.search, '') : pathname
|
|
416
|
-
let newModified = fs.statSync(decodeURIComponent(pathname))
|
|
447
|
+
let newModified = fs.statSync(decodeURIComponent(pathname), { throwIfNoEntry: false })?.mtimeMs
|
|
448
|
+
if (!newModified) {
|
|
449
|
+
// It could happen that a file is passed in that doesn't exist. E.g.:
|
|
450
|
+
// postcss-cli will provide you a fake path when reading from stdin. This
|
|
451
|
+
// path then looks like /path-to-your-project/stdin In that case we just
|
|
452
|
+
// want to ignore it and don't track changes at all.
|
|
453
|
+
continue
|
|
454
|
+
}
|
|
417
455
|
|
|
418
456
|
if (!fileModifiedMap.has(file) || newModified > fileModifiedMap.get(file)) {
|
|
419
457
|
changed = true
|
|
@@ -473,15 +511,6 @@ function collectLayerPlugins(root) {
|
|
|
473
511
|
}
|
|
474
512
|
})
|
|
475
513
|
|
|
476
|
-
root.walkRules((rule) => {
|
|
477
|
-
// At this point it is safe to include all the left-over css from the
|
|
478
|
-
// user's css file. This is because the `@tailwind` and `@layer` directives
|
|
479
|
-
// will already be handled and will be removed from the css tree.
|
|
480
|
-
layerPlugins.push(function ({ addUserCss }) {
|
|
481
|
-
addUserCss(rule, { respectPrefix: false })
|
|
482
|
-
})
|
|
483
|
-
})
|
|
484
|
-
|
|
485
514
|
return layerPlugins
|
|
486
515
|
}
|
|
487
516
|
|
|
@@ -528,6 +557,7 @@ function registerPlugins(plugins, context) {
|
|
|
528
557
|
let variantList = []
|
|
529
558
|
let variantMap = new Map()
|
|
530
559
|
let offsets = {
|
|
560
|
+
defaults: 0n,
|
|
531
561
|
base: 0n,
|
|
532
562
|
components: 0n,
|
|
533
563
|
utilities: 0n,
|
|
@@ -555,6 +585,7 @@ function registerPlugins(plugins, context) {
|
|
|
555
585
|
|
|
556
586
|
let highestOffset = ((args) => args.reduce((m, e) => (e > m ? e : m)))([
|
|
557
587
|
offsets.base,
|
|
588
|
+
offsets.defaults,
|
|
558
589
|
offsets.components,
|
|
559
590
|
offsets.utilities,
|
|
560
591
|
offsets.user,
|
|
@@ -566,13 +597,14 @@ function registerPlugins(plugins, context) {
|
|
|
566
597
|
context.arbitraryPropertiesSort = ((1n << reservedBits) << 0n) - 1n
|
|
567
598
|
|
|
568
599
|
context.layerOrder = {
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
600
|
+
defaults: (1n << reservedBits) << 0n,
|
|
601
|
+
base: (1n << reservedBits) << 1n,
|
|
602
|
+
components: (1n << reservedBits) << 2n,
|
|
603
|
+
utilities: (1n << reservedBits) << 3n,
|
|
604
|
+
user: (1n << reservedBits) << 4n,
|
|
573
605
|
}
|
|
574
606
|
|
|
575
|
-
reservedBits +=
|
|
607
|
+
reservedBits += 5n
|
|
576
608
|
|
|
577
609
|
let offset = 0
|
|
578
610
|
context.variantOrder = new Map(
|
|
@@ -611,7 +643,7 @@ function registerPlugins(plugins, context) {
|
|
|
611
643
|
log.warn('root-regex', [
|
|
612
644
|
'Regular expressions in `safelist` work differently in Tailwind CSS v3.0.',
|
|
613
645
|
'Update your `safelist` configuration to eliminate this warning.',
|
|
614
|
-
|
|
646
|
+
'https://tailwindcss.com/docs/content-configuration#safelisting-classes',
|
|
615
647
|
])
|
|
616
648
|
continue
|
|
617
649
|
}
|
|
@@ -621,12 +653,33 @@ function registerPlugins(plugins, context) {
|
|
|
621
653
|
|
|
622
654
|
if (checks.length > 0) {
|
|
623
655
|
let patternMatchingCount = new Map()
|
|
656
|
+
let prefixLength = context.tailwindConfig.prefix.length
|
|
624
657
|
|
|
625
658
|
for (let util of classList) {
|
|
626
659
|
let utils = Array.isArray(util)
|
|
627
660
|
? (() => {
|
|
628
661
|
let [utilName, options] = util
|
|
629
|
-
|
|
662
|
+
let values = Object.keys(options?.values ?? {})
|
|
663
|
+
let classes = values.map((value) => formatClass(utilName, value))
|
|
664
|
+
|
|
665
|
+
if (options?.supportsNegativeValues) {
|
|
666
|
+
// This is the normal negated version
|
|
667
|
+
// e.g. `-inset-1` or `-tw-inset-1`
|
|
668
|
+
classes = [...classes, ...classes.map((cls) => '-' + cls)]
|
|
669
|
+
|
|
670
|
+
// This is the negated version *after* the prefix
|
|
671
|
+
// e.g. `tw--inset-1`
|
|
672
|
+
// The prefix is already attached to util name
|
|
673
|
+
// So we add the negative after the prefix
|
|
674
|
+
classes = [
|
|
675
|
+
...classes,
|
|
676
|
+
...classes.map(
|
|
677
|
+
(cls) => cls.slice(0, prefixLength) + '-' + cls.slice(prefixLength)
|
|
678
|
+
),
|
|
679
|
+
]
|
|
680
|
+
}
|
|
681
|
+
|
|
682
|
+
return classes
|
|
630
683
|
})()
|
|
631
684
|
: [util]
|
|
632
685
|
|
|
@@ -661,14 +714,41 @@ function registerPlugins(plugins, context) {
|
|
|
661
714
|
log.warn([
|
|
662
715
|
`The safelist pattern \`${regex}\` doesn't match any Tailwind CSS classes.`,
|
|
663
716
|
'Fix this pattern or remove it from your `safelist` configuration.',
|
|
717
|
+
'https://tailwindcss.com/docs/content-configuration#safelisting-classes',
|
|
664
718
|
])
|
|
665
719
|
}
|
|
666
720
|
}
|
|
667
721
|
}
|
|
668
722
|
|
|
723
|
+
// A list of utilities that are used by certain Tailwind CSS utilities but
|
|
724
|
+
// that don't exist on their own. This will result in them "not existing" and
|
|
725
|
+
// sorting could be weird since you still require them in order to make the
|
|
726
|
+
// host utitlies work properly. (Thanks Biology)
|
|
727
|
+
let parasiteUtilities = new Set([prefix(context, 'group'), prefix(context, 'peer')])
|
|
728
|
+
context.getClassOrder = function getClassOrder(classes) {
|
|
729
|
+
let sortedClassNames = new Map()
|
|
730
|
+
for (let [sort, rule] of generateRules(new Set(classes), context)) {
|
|
731
|
+
if (sortedClassNames.has(rule.raws.tailwind.candidate)) continue
|
|
732
|
+
sortedClassNames.set(rule.raws.tailwind.candidate, sort)
|
|
733
|
+
}
|
|
734
|
+
|
|
735
|
+
return classes.map((className) => {
|
|
736
|
+
let order = sortedClassNames.get(className) ?? null
|
|
737
|
+
|
|
738
|
+
if (order === null && parasiteUtilities.has(className)) {
|
|
739
|
+
// This will make sure that it is at the very beginning of the
|
|
740
|
+
// `components` layer which technically means 'before any
|
|
741
|
+
// components'.
|
|
742
|
+
order = context.layerOrder.components
|
|
743
|
+
}
|
|
744
|
+
|
|
745
|
+
return [className, order]
|
|
746
|
+
})
|
|
747
|
+
}
|
|
748
|
+
|
|
669
749
|
// Generate a list of strings for autocompletion purposes, e.g.
|
|
670
750
|
// ['uppercase', 'lowercase', ...]
|
|
671
|
-
context.getClassList = function () {
|
|
751
|
+
context.getClassList = function getClassList() {
|
|
672
752
|
let output = []
|
|
673
753
|
|
|
674
754
|
for (let util of classList) {
|
|
@@ -743,6 +823,8 @@ export function getContext(
|
|
|
743
823
|
existingContext = context
|
|
744
824
|
}
|
|
745
825
|
|
|
826
|
+
let cssDidChange = hasContentChanged(sourcePath, root)
|
|
827
|
+
|
|
746
828
|
// If there's already a context in the cache and we don't need to
|
|
747
829
|
// reset the context, return the cached context.
|
|
748
830
|
if (existingContext) {
|
|
@@ -750,7 +832,7 @@ export function getContext(
|
|
|
750
832
|
[...contextDependencies],
|
|
751
833
|
getFileModifiedMap(existingContext)
|
|
752
834
|
)
|
|
753
|
-
if (!contextDependenciesChanged) {
|
|
835
|
+
if (!contextDependenciesChanged && !cssDidChange) {
|
|
754
836
|
return [existingContext, false]
|
|
755
837
|
}
|
|
756
838
|
}
|
|
@@ -119,11 +119,12 @@ export default function setupTrackingContext(configOrPath) {
|
|
|
119
119
|
|
|
120
120
|
let contextDependencies = new Set(configDependencies)
|
|
121
121
|
|
|
122
|
-
// If there are no @tailwind rules, we don't consider this CSS
|
|
123
|
-
// to be dependencies of the context. Can reuse
|
|
124
|
-
// We may want to think about `@layer`
|
|
125
|
-
//
|
|
126
|
-
// in
|
|
122
|
+
// If there are no @tailwind or @apply rules, we don't consider this CSS
|
|
123
|
+
// file or its dependencies to be dependencies of the context. Can reuse
|
|
124
|
+
// the context even if they change. We may want to think about `@layer`
|
|
125
|
+
// being part of this trigger too, but it's tough because it's impossible
|
|
126
|
+
// for a layer in one file to end up in the actual @tailwind rule in
|
|
127
|
+
// another file since independent sources are effectively isolated.
|
|
127
128
|
if (tailwindDirectives.size > 0) {
|
|
128
129
|
// Add current css file as a context dependencies.
|
|
129
130
|
contextDependencies.add(result.opts.from)
|
|
@@ -147,8 +148,8 @@ export default function setupTrackingContext(configOrPath) {
|
|
|
147
148
|
|
|
148
149
|
let candidateFiles = getCandidateFiles(context, tailwindConfig)
|
|
149
150
|
|
|
150
|
-
// If there are no @tailwind rules, we don't consider this CSS file or it's
|
|
151
|
-
// to be dependencies of the context. Can reuse the context even if they change.
|
|
151
|
+
// If there are no @tailwind or @apply rules, we don't consider this CSS file or it's
|
|
152
|
+
// dependencies to be dependencies of the context. Can reuse the context even if they change.
|
|
152
153
|
// We may want to think about `@layer` being part of this trigger too, but it's tough
|
|
153
154
|
// because it's impossible for a layer in one file to end up in the actual @tailwind rule
|
|
154
155
|
// in another file since independent sources are effectively isolated.
|
package/src/lib/sharedState.js
CHANGED
|
@@ -1,10 +1,48 @@
|
|
|
1
1
|
export const env = {
|
|
2
|
-
TAILWIND_MODE: process.env.TAILWIND_MODE,
|
|
3
2
|
NODE_ENV: process.env.NODE_ENV,
|
|
4
|
-
DEBUG: process.env.DEBUG
|
|
5
|
-
TAILWIND_DISABLE_TOUCH: process.env.TAILWIND_DISABLE_TOUCH !== undefined,
|
|
6
|
-
TAILWIND_TOUCH_DIR: process.env.TAILWIND_TOUCH_DIR,
|
|
3
|
+
DEBUG: resolveDebug(process.env.DEBUG),
|
|
7
4
|
}
|
|
8
5
|
export const contextMap = new Map()
|
|
9
6
|
export const configContextMap = new Map()
|
|
10
7
|
export const contextSourcesMap = new Map()
|
|
8
|
+
export const sourceHashMap = new Map()
|
|
9
|
+
export const NOT_ON_DEMAND = new String('*')
|
|
10
|
+
|
|
11
|
+
export function resolveDebug(debug) {
|
|
12
|
+
if (debug === undefined) {
|
|
13
|
+
return false
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
// Environment variables are strings, so convert to boolean
|
|
17
|
+
if (debug === 'true' || debug === '1') {
|
|
18
|
+
return true
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
if (debug === 'false' || debug === '0') {
|
|
22
|
+
return false
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
// Keep the debug convention into account:
|
|
26
|
+
// DEBUG=* -> This enables all debug modes
|
|
27
|
+
// DEBUG=projectA,projectB,projectC -> This enables debug for projectA, projectB and projectC
|
|
28
|
+
// DEBUG=projectA:* -> This enables all debug modes for projectA (if you have sub-types)
|
|
29
|
+
// DEBUG=projectA,-projectB -> This enables debug for projectA and explicitly disables it for projectB
|
|
30
|
+
|
|
31
|
+
if (debug === '*') {
|
|
32
|
+
return true
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
let debuggers = debug.split(',').map((d) => d.split(':')[0])
|
|
36
|
+
|
|
37
|
+
// Ignoring tailwindcss
|
|
38
|
+
if (debuggers.includes('-tailwindcss')) {
|
|
39
|
+
return false
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
// Including tailwindcss
|
|
43
|
+
if (debuggers.includes('tailwindcss')) {
|
|
44
|
+
return true
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
return false
|
|
48
|
+
}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
# tailwindcss/nesting
|
|
2
|
+
|
|
3
|
+
This is a PostCSS plugin that wraps [postcss-nested](https://github.com/postcss/postcss-nested) or [postcss-nesting](https://github.com/jonathantneal/postcss-nesting) and acts as a compatibility layer to make sure your nesting plugin of choice properly understands Tailwind's custom syntax like `@apply` and `@screen`.
|
|
4
|
+
|
|
5
|
+
Add it to your PostCSS configuration, somewhere before Tailwind itself:
|
|
6
|
+
|
|
7
|
+
```js
|
|
8
|
+
// postcss.config.js
|
|
9
|
+
module.exports = {
|
|
10
|
+
plugins: [
|
|
11
|
+
require('postcss-import'),
|
|
12
|
+
require('tailwindcss/nesting'),
|
|
13
|
+
require('tailwindcss'),
|
|
14
|
+
require('autoprefixer'),
|
|
15
|
+
]
|
|
16
|
+
}
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
By default, it uses the [postcss-nested](https://github.com/postcss/postcss-nested) plugin under the hood, which uses a Sass-like syntax and is the plugin that powers nesting support in the [Tailwind CSS plugin API](https://tailwindcss.com/docs/plugins#css-in-js-syntax).
|
|
20
|
+
|
|
21
|
+
If you'd rather use [postcss-nesting](https://github.com/jonathantneal/postcss-nesting) (which is based on the work-in-progress [CSS Nesting](https://drafts.csswg.org/css-nesting-1/) specification), first install the plugin alongside:
|
|
22
|
+
|
|
23
|
+
```shell
|
|
24
|
+
npm install postcss-nesting
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
Then pass the plugin itself as an argument to `tailwindcss/nesting` in your PostCSS configuration:
|
|
28
|
+
|
|
29
|
+
```js
|
|
30
|
+
// postcss.config.js
|
|
31
|
+
module.exports = {
|
|
32
|
+
plugins: [
|
|
33
|
+
require('postcss-import'),
|
|
34
|
+
require('tailwindcss/nesting')(require('postcss-nesting')),
|
|
35
|
+
require('tailwindcss'),
|
|
36
|
+
require('autoprefixer'),
|
|
37
|
+
]
|
|
38
|
+
}
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
This can also be helpful if for whatever reason you need to use a very specific version of `postcss-nested` and want to override the version we bundle with `tailwindcss/nesting` itself.
|
|
42
|
+
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
import postcss from 'postcss'
|
|
2
|
+
import postcssNested from 'postcss-nested'
|
|
3
|
+
|
|
4
|
+
export function nesting(opts = postcssNested) {
|
|
5
|
+
return (root, result) => {
|
|
6
|
+
root.walkAtRules('screen', (rule) => {
|
|
7
|
+
rule.name = 'media'
|
|
8
|
+
rule.params = `screen(${rule.params})`
|
|
9
|
+
})
|
|
10
|
+
|
|
11
|
+
root.walkAtRules('apply', (rule) => {
|
|
12
|
+
rule.before(postcss.decl({ prop: '__apply', value: rule.params, source: rule.source }))
|
|
13
|
+
rule.remove()
|
|
14
|
+
})
|
|
15
|
+
|
|
16
|
+
let plugin = (() => {
|
|
17
|
+
if (
|
|
18
|
+
typeof opts === 'function' ||
|
|
19
|
+
(typeof opts === 'object' && opts?.hasOwnProperty?.('postcssPlugin'))
|
|
20
|
+
) {
|
|
21
|
+
return opts
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
if (typeof opts === 'string') {
|
|
25
|
+
return require(opts)
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
if (Object.keys(opts).length <= 0) {
|
|
29
|
+
return postcssNested
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
throw new Error('tailwindcss/nesting should be loaded with a nesting plugin.')
|
|
33
|
+
})()
|
|
34
|
+
|
|
35
|
+
postcss([plugin]).process(root, result.opts).sync()
|
|
36
|
+
|
|
37
|
+
root.walkDecls('__apply', (decl) => {
|
|
38
|
+
decl.before(postcss.atRule({ name: 'apply', params: decl.value, source: decl.source }))
|
|
39
|
+
decl.remove()
|
|
40
|
+
})
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
* Use a private PostCSS API to remove the "clean" flag from the entire AST.
|
|
44
|
+
* This is done because running process() on the AST will set the "clean"
|
|
45
|
+
* flag on all nodes, which we don't want.
|
|
46
|
+
*
|
|
47
|
+
* This causes downstream plugins using the visitor API to be skipped.
|
|
48
|
+
*
|
|
49
|
+
* This is guarded because the PostCSS API is not public
|
|
50
|
+
* and may change in future versions of PostCSS.
|
|
51
|
+
*
|
|
52
|
+
* See https://github.com/postcss/postcss/issues/1712 for more details
|
|
53
|
+
*
|
|
54
|
+
* @param {import('postcss').Node} node
|
|
55
|
+
*/
|
|
56
|
+
function markDirty(node) {
|
|
57
|
+
if (!('markDirty' in node)) {
|
|
58
|
+
return
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
// Traverse the tree down to the leaf nodes
|
|
62
|
+
if (node.nodes) {
|
|
63
|
+
node.nodes.forEach((n) => markDirty(n))
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
// If it's a leaf node mark it as dirty
|
|
67
|
+
// We do this here because marking a node as dirty
|
|
68
|
+
// will walk up the tree and mark all parents as dirty
|
|
69
|
+
// resulting in a lot of unnecessary work if we did this
|
|
70
|
+
// for every single node
|
|
71
|
+
if (!node.nodes) {
|
|
72
|
+
node.markDirty()
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
markDirty(root)
|
|
77
|
+
|
|
78
|
+
return root
|
|
79
|
+
}
|
|
80
|
+
}
|
|
@@ -6,16 +6,24 @@ import substituteScreenAtRules from './lib/substituteScreenAtRules'
|
|
|
6
6
|
import resolveDefaultsAtRules from './lib/resolveDefaultsAtRules'
|
|
7
7
|
import collapseAdjacentRules from './lib/collapseAdjacentRules'
|
|
8
8
|
import collapseDuplicateDeclarations from './lib/collapseDuplicateDeclarations'
|
|
9
|
+
import partitionApplyAtRules from './lib/partitionApplyAtRules'
|
|
9
10
|
import detectNesting from './lib/detectNesting'
|
|
10
11
|
import { createContext } from './lib/setupContextUtils'
|
|
11
12
|
import { issueFlagNotices } from './featureFlags'
|
|
12
13
|
|
|
13
14
|
export default function processTailwindFeatures(setupContext) {
|
|
14
15
|
return function (root, result) {
|
|
15
|
-
let tailwindDirectives = normalizeTailwindDirectives(root)
|
|
16
|
+
let { tailwindDirectives, applyDirectives } = normalizeTailwindDirectives(root)
|
|
17
|
+
|
|
18
|
+
detectNesting()(root, result)
|
|
19
|
+
|
|
20
|
+
// Partition apply rules that are found in the css
|
|
21
|
+
// itself.
|
|
22
|
+
partitionApplyAtRules()(root, result)
|
|
16
23
|
|
|
17
24
|
let context = setupContext({
|
|
18
25
|
tailwindDirectives,
|
|
26
|
+
applyDirectives,
|
|
19
27
|
registerDependency(dependency) {
|
|
20
28
|
result.messages.push({
|
|
21
29
|
plugin: 'tailwindcss',
|
|
@@ -36,8 +44,10 @@ export default function processTailwindFeatures(setupContext) {
|
|
|
36
44
|
|
|
37
45
|
issueFlagNotices(context.tailwindConfig)
|
|
38
46
|
|
|
39
|
-
detectNesting(context)(root, result)
|
|
40
47
|
expandTailwindAtRules(context)(root, result)
|
|
48
|
+
// Partition apply rules that are generated by
|
|
49
|
+
// addComponents, addUtilities and so on.
|
|
50
|
+
partitionApplyAtRules()(root, result)
|
|
41
51
|
expandApplyAtRules(context)(root, result)
|
|
42
52
|
evaluateTailwindFunctions(context)(root, result)
|
|
43
53
|
substituteScreenAtRules(context)(root, result)
|
package/src/util/cloneNodes.js
CHANGED
|
@@ -1,9 +1,22 @@
|
|
|
1
|
-
export default function cloneNodes(nodes, source) {
|
|
1
|
+
export default function cloneNodes(nodes, source = undefined, raws = undefined) {
|
|
2
2
|
return nodes.map((node) => {
|
|
3
3
|
let cloned = node.clone()
|
|
4
4
|
|
|
5
5
|
if (source !== undefined) {
|
|
6
6
|
cloned.source = source
|
|
7
|
+
|
|
8
|
+
if ('walk' in cloned) {
|
|
9
|
+
cloned.walk((child) => {
|
|
10
|
+
child.source = source
|
|
11
|
+
})
|
|
12
|
+
}
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
if (raws !== undefined) {
|
|
16
|
+
cloned.raws.tailwind = {
|
|
17
|
+
...cloned.raws.tailwind,
|
|
18
|
+
...raws,
|
|
19
|
+
}
|
|
7
20
|
}
|
|
8
21
|
|
|
9
22
|
return cloned
|
package/src/util/color.js
CHANGED
|
@@ -5,8 +5,11 @@ let SHORT_HEX = /^#([a-f\d])([a-f\d])([a-f\d])([a-f\d])?$/i
|
|
|
5
5
|
let VALUE = `(?:\\d+|\\d*\\.\\d+)%?`
|
|
6
6
|
let SEP = `(?:\\s*,\\s*|\\s+)`
|
|
7
7
|
let ALPHA_SEP = `\\s*[,/]\\s*`
|
|
8
|
-
let
|
|
9
|
-
`^
|
|
8
|
+
let RGB = new RegExp(
|
|
9
|
+
`^rgba?\\(\\s*(${VALUE})${SEP}(${VALUE})${SEP}(${VALUE})(?:${ALPHA_SEP}(${VALUE}))?\\s*\\)$`
|
|
10
|
+
)
|
|
11
|
+
let HSL = new RegExp(
|
|
12
|
+
`^hsla?\\(\\s*((?:${VALUE})(?:deg|rad|grad|turn)?)${SEP}(${VALUE})${SEP}(${VALUE})(?:${ALPHA_SEP}(${VALUE}))?\\s*\\)$`
|
|
10
13
|
)
|
|
11
14
|
|
|
12
15
|
export function parseColor(value) {
|
|
@@ -37,13 +40,23 @@ export function parseColor(value) {
|
|
|
37
40
|
}
|
|
38
41
|
}
|
|
39
42
|
|
|
40
|
-
let
|
|
43
|
+
let rgbMatch = value.match(RGB)
|
|
44
|
+
|
|
45
|
+
if (rgbMatch !== null) {
|
|
46
|
+
return {
|
|
47
|
+
mode: 'rgb',
|
|
48
|
+
color: [rgbMatch[1], rgbMatch[2], rgbMatch[3]].map((v) => v.toString()),
|
|
49
|
+
alpha: rgbMatch[4]?.toString?.(),
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
let hslMatch = value.match(HSL)
|
|
41
54
|
|
|
42
|
-
if (
|
|
55
|
+
if (hslMatch !== null) {
|
|
43
56
|
return {
|
|
44
|
-
mode:
|
|
45
|
-
color: [
|
|
46
|
-
alpha:
|
|
57
|
+
mode: 'hsl',
|
|
58
|
+
color: [hslMatch[1], hslMatch[2], hslMatch[3]].map((v) => v.toString()),
|
|
59
|
+
alpha: hslMatch[4]?.toString?.(),
|
|
47
60
|
}
|
|
48
61
|
}
|
|
49
62
|
|