tailwindcss 3.0.23 → 3.1.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +92 -2
- package/colors.d.ts +3 -0
- package/defaultConfig.d.ts +3 -0
- package/defaultTheme.d.ts +3 -0
- package/lib/cli-peer-dependencies.js +10 -5
- package/lib/cli.js +266 -203
- package/lib/constants.js +8 -8
- package/lib/corePluginList.js +1 -0
- package/lib/corePlugins.js +1640 -1562
- package/lib/css/preflight.css +1 -8
- package/lib/featureFlags.js +14 -12
- package/lib/index.js +16 -6
- package/lib/lib/cacheInvalidation.js +87 -0
- package/lib/lib/collapseAdjacentRules.js +30 -15
- package/lib/lib/collapseDuplicateDeclarations.js +1 -1
- package/lib/lib/defaultExtractor.js +191 -32
- package/lib/lib/detectNesting.js +9 -9
- package/lib/lib/evaluateTailwindFunctions.js +37 -28
- package/lib/lib/expandApplyAtRules.js +379 -189
- package/lib/lib/expandTailwindAtRules.js +168 -144
- package/lib/lib/generateRules.js +177 -95
- package/lib/lib/getModuleDependencies.js +14 -14
- package/lib/lib/normalizeTailwindDirectives.js +35 -35
- package/lib/lib/partitionApplyAtRules.js +7 -7
- package/lib/lib/regex.js +52 -0
- package/lib/lib/resolveDefaultsAtRules.js +96 -81
- package/lib/lib/setupContextUtils.js +202 -159
- package/lib/lib/setupTrackingContext.js +61 -63
- package/lib/lib/sharedState.js +10 -9
- package/lib/lib/substituteScreenAtRules.js +3 -4
- package/lib/postcss-plugins/nesting/README.md +2 -2
- package/lib/postcss-plugins/nesting/index.js +1 -1
- package/lib/postcss-plugins/nesting/plugin.js +40 -9
- package/lib/processTailwindFeatures.js +7 -7
- package/lib/public/colors.js +241 -241
- package/lib/public/resolve-config.js +5 -5
- package/lib/util/buildMediaQuery.js +2 -3
- package/lib/util/cloneDeep.js +3 -5
- package/lib/util/cloneNodes.js +12 -1
- package/lib/util/color.js +42 -51
- package/lib/util/createPlugin.js +1 -2
- package/lib/util/createUtilityPlugin.js +6 -7
- package/lib/util/dataTypes.js +85 -81
- package/lib/util/escapeClassName.js +5 -5
- package/lib/util/escapeCommas.js +1 -1
- package/lib/util/flattenColorPalette.js +4 -7
- package/lib/util/formatVariantSelector.js +82 -75
- package/lib/util/getAllConfigs.js +15 -10
- package/lib/util/hashConfig.js +5 -5
- package/lib/util/isKeyframeRule.js +1 -1
- package/lib/util/isPlainObject.js +1 -1
- package/lib/util/isValidArbitraryValue.js +26 -27
- package/lib/util/log.js +9 -10
- package/lib/util/nameClass.js +7 -7
- package/lib/util/negateValue.js +4 -5
- package/lib/util/normalizeConfig.js +68 -58
- package/lib/util/normalizeScreens.js +5 -6
- package/lib/util/parseAnimationValue.js +56 -57
- package/lib/util/parseBoxShadowValue.js +19 -20
- package/lib/util/parseDependency.js +32 -32
- package/lib/util/parseObjectStyles.js +6 -6
- package/lib/util/pluginUtils.js +20 -12
- package/lib/util/prefixSelector.js +1 -1
- package/lib/util/resolveConfig.js +81 -58
- package/lib/util/resolveConfigPath.js +16 -16
- package/lib/util/responsive.js +6 -6
- package/lib/util/splitAtTopLevelOnly.js +90 -0
- package/lib/util/toColorValue.js +1 -1
- package/lib/util/toPath.js +2 -2
- package/lib/util/transformThemeValue.js +30 -28
- package/lib/util/validateConfig.js +21 -0
- package/lib/util/withAlphaVariable.js +23 -23
- package/package.json +33 -27
- package/peers/index.js +7702 -5822
- package/plugin.d.ts +11 -0
- package/scripts/generate-types.js +52 -0
- package/src/cli-peer-dependencies.js +7 -1
- package/src/cli.js +118 -24
- package/src/corePluginList.js +1 -1
- package/src/corePlugins.js +109 -30
- package/src/css/preflight.css +1 -8
- package/src/featureFlags.js +4 -4
- package/src/index.js +15 -1
- package/src/lib/cacheInvalidation.js +52 -0
- package/src/lib/collapseAdjacentRules.js +21 -2
- package/src/lib/defaultExtractor.js +177 -35
- package/src/lib/evaluateTailwindFunctions.js +20 -4
- package/src/lib/expandApplyAtRules.js +418 -186
- package/src/lib/expandTailwindAtRules.js +29 -9
- package/src/lib/generateRules.js +137 -51
- package/src/lib/regex.js +74 -0
- package/src/lib/resolveDefaultsAtRules.js +53 -32
- package/src/lib/setupContextUtils.js +128 -65
- package/src/lib/setupTrackingContext.js +7 -3
- package/src/lib/sharedState.js +1 -0
- package/src/postcss-plugins/nesting/README.md +2 -2
- package/src/postcss-plugins/nesting/plugin.js +36 -0
- package/src/util/cloneNodes.js +14 -1
- package/src/util/color.js +25 -21
- package/src/util/dataTypes.js +14 -6
- package/src/util/formatVariantSelector.js +79 -62
- package/src/util/getAllConfigs.js +7 -0
- package/src/util/log.js +8 -8
- package/src/util/normalizeConfig.js +0 -8
- package/src/util/parseBoxShadowValue.js +3 -2
- package/src/util/pluginUtils.js +13 -1
- package/src/util/resolveConfig.js +66 -22
- package/src/util/splitAtTopLevelOnly.js +71 -0
- package/src/util/toPath.js +1 -1
- package/src/util/transformThemeValue.js +4 -2
- package/src/util/validateConfig.js +13 -0
- package/src/util/withAlphaVariable.js +1 -1
- package/stubs/defaultConfig.stub.js +5 -1
- package/stubs/simpleConfig.stub.js +1 -0
- package/types/config.d.ts +325 -0
- package/types/generated/.gitkeep +0 -0
- package/types/generated/colors.d.ts +276 -0
- package/types/generated/corePluginList.d.ts +1 -0
- package/types/index.d.ts +7 -0
|
@@ -4,6 +4,7 @@ import postcss from 'postcss'
|
|
|
4
4
|
import dlv from 'dlv'
|
|
5
5
|
import selectorParser from 'postcss-selector-parser'
|
|
6
6
|
|
|
7
|
+
import { flagEnabled } from '../featureFlags.js'
|
|
7
8
|
import transformThemeValue from '../util/transformThemeValue'
|
|
8
9
|
import parseObjectStyles from '../util/parseObjectStyles'
|
|
9
10
|
import prefixSelector from '../util/prefixSelector'
|
|
@@ -20,6 +21,9 @@ import log from '../util/log'
|
|
|
20
21
|
import negateValue from '../util/negateValue'
|
|
21
22
|
import isValidArbitraryValue from '../util/isValidArbitraryValue'
|
|
22
23
|
import { generateRules } from './generateRules'
|
|
24
|
+
import { hasContentChanged } from './cacheInvalidation.js'
|
|
25
|
+
|
|
26
|
+
let MATCH_VARIANT = Symbol()
|
|
23
27
|
|
|
24
28
|
function prefix(context, selector) {
|
|
25
29
|
let prefix = context.tailwindConfig.prefix
|
|
@@ -84,12 +88,18 @@ function parseStyles(styles) {
|
|
|
84
88
|
})
|
|
85
89
|
}
|
|
86
90
|
|
|
87
|
-
function getClasses(selector) {
|
|
91
|
+
function getClasses(selector, mutate) {
|
|
88
92
|
let parser = selectorParser((selectors) => {
|
|
89
93
|
let allClasses = []
|
|
94
|
+
|
|
95
|
+
if (mutate) {
|
|
96
|
+
mutate(selectors)
|
|
97
|
+
}
|
|
98
|
+
|
|
90
99
|
selectors.walkClasses((classNode) => {
|
|
91
100
|
allClasses.push(classNode.value)
|
|
92
101
|
})
|
|
102
|
+
|
|
93
103
|
return allClasses
|
|
94
104
|
})
|
|
95
105
|
return parser.transformSync(selector)
|
|
@@ -100,8 +110,20 @@ function extractCandidates(node, state = { containsNonOnDemandable: false }, dep
|
|
|
100
110
|
|
|
101
111
|
// Handle normal rules
|
|
102
112
|
if (node.type === 'rule') {
|
|
113
|
+
// Ignore everything inside a :not(...). This allows you to write code like
|
|
114
|
+
// `div:not(.foo)`. If `.foo` is never found in your code, then we used to
|
|
115
|
+
// not generated it. But now we will ignore everything inside a `:not`, so
|
|
116
|
+
// that it still gets generated.
|
|
117
|
+
function ignoreNot(selectors) {
|
|
118
|
+
selectors.walkPseudos((pseudo) => {
|
|
119
|
+
if (pseudo.value === ':not') {
|
|
120
|
+
pseudo.remove()
|
|
121
|
+
}
|
|
122
|
+
})
|
|
123
|
+
}
|
|
124
|
+
|
|
103
125
|
for (let selector of node.selectors) {
|
|
104
|
-
let classCandidates = getClasses(selector)
|
|
126
|
+
let classCandidates = getClasses(selector, ignoreNot)
|
|
105
127
|
// At least one of the selectors contains non-"on-demandable" candidates.
|
|
106
128
|
if (classCandidates.length === 0) {
|
|
107
129
|
state.containsNonOnDemandable = true
|
|
@@ -116,9 +138,7 @@ function extractCandidates(node, state = { containsNonOnDemandable: false }, dep
|
|
|
116
138
|
// Handle at-rules (which contains nested rules)
|
|
117
139
|
else if (node.type === 'atrule') {
|
|
118
140
|
node.walkRules((rule) => {
|
|
119
|
-
for (let classCandidate of rule.selectors.flatMap((selector) =>
|
|
120
|
-
getClasses(selector, state, depth + 1)
|
|
121
|
-
)) {
|
|
141
|
+
for (let classCandidate of rule.selectors.flatMap((selector) => getClasses(selector))) {
|
|
122
142
|
classes.push(classCandidate)
|
|
123
143
|
}
|
|
124
144
|
})
|
|
@@ -153,6 +173,34 @@ function withIdentifiers(styles) {
|
|
|
153
173
|
})
|
|
154
174
|
}
|
|
155
175
|
|
|
176
|
+
export function isValidVariantFormatString(format) {
|
|
177
|
+
return format.startsWith('@') || format.includes('&')
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
export function parseVariant(variant) {
|
|
181
|
+
variant = variant
|
|
182
|
+
.replace(/\n+/g, '')
|
|
183
|
+
.replace(/\s{1,}/g, ' ')
|
|
184
|
+
.trim()
|
|
185
|
+
|
|
186
|
+
let fns = parseVariantFormatString(variant)
|
|
187
|
+
.map((str) => {
|
|
188
|
+
if (!str.startsWith('@')) {
|
|
189
|
+
return ({ format }) => format(str)
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
let [, name, params] = /@(.*?)( .+|[({].*)/g.exec(str)
|
|
193
|
+
return ({ wrap }) => wrap(postcss.atRule({ name, params: params.trim() }))
|
|
194
|
+
})
|
|
195
|
+
.reverse()
|
|
196
|
+
|
|
197
|
+
return (api) => {
|
|
198
|
+
for (let fn of fns) {
|
|
199
|
+
fn(api)
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
|
|
156
204
|
function buildPluginApi(tailwindConfig, context, { variantList, variantMap, offsets, classList }) {
|
|
157
205
|
function getConfigValue(path, defaultValue) {
|
|
158
206
|
return path ? dlv(tailwindConfig, path, defaultValue) : tailwindConfig
|
|
@@ -174,51 +222,25 @@ function buildPluginApi(tailwindConfig, context, { variantList, variantMap, offs
|
|
|
174
222
|
return context.tailwindConfig.prefix + identifier
|
|
175
223
|
}
|
|
176
224
|
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
return ({ modifySelectors, container, separator }) => {
|
|
183
|
-
return variantFunction({ modifySelectors, container, separator })
|
|
184
|
-
}
|
|
185
|
-
}
|
|
186
|
-
|
|
187
|
-
variantFunction = variantFunction
|
|
188
|
-
.replace(/\n+/g, '')
|
|
189
|
-
.replace(/\s{1,}/g, ' ')
|
|
190
|
-
.trim()
|
|
191
|
-
|
|
192
|
-
let fns = parseVariantFormatString(variantFunction)
|
|
193
|
-
.map((str) => {
|
|
194
|
-
if (!str.startsWith('@')) {
|
|
195
|
-
return ({ format }) => format(str)
|
|
196
|
-
}
|
|
197
|
-
|
|
198
|
-
let [, name, params] = /@(.*?) (.*)/g.exec(str)
|
|
199
|
-
return ({ wrap }) => wrap(postcss.atRule({ name, params }))
|
|
200
|
-
})
|
|
201
|
-
.reverse()
|
|
225
|
+
function resolveThemeValue(path, defaultValue, opts = {}) {
|
|
226
|
+
const [pathRoot, ...subPaths] = toPath(path)
|
|
227
|
+
const value = getConfigValue(['theme', pathRoot, ...subPaths], defaultValue)
|
|
228
|
+
return transformThemeValue(pathRoot)(value, opts)
|
|
229
|
+
}
|
|
202
230
|
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
231
|
+
const theme = Object.assign(
|
|
232
|
+
(path, defaultValue = undefined) => resolveThemeValue(path, defaultValue),
|
|
233
|
+
{
|
|
234
|
+
withAlpha: (path, opacityValue) => resolveThemeValue(path, undefined, { opacityValue }),
|
|
235
|
+
}
|
|
236
|
+
)
|
|
209
237
|
|
|
210
|
-
|
|
211
|
-
variantMap.set(variantName, variantFunctions)
|
|
212
|
-
},
|
|
238
|
+
let api = {
|
|
213
239
|
postcss,
|
|
214
240
|
prefix: applyConfiguredPrefix,
|
|
215
241
|
e: escapeClassName,
|
|
216
242
|
config: getConfigValue,
|
|
217
|
-
theme
|
|
218
|
-
const [pathRoot, ...subPaths] = toPath(path)
|
|
219
|
-
const value = getConfigValue(['theme', pathRoot, ...subPaths], defaultValue)
|
|
220
|
-
return transformThemeValue(pathRoot)(value)
|
|
221
|
-
},
|
|
243
|
+
theme,
|
|
222
244
|
corePlugins: (path) => {
|
|
223
245
|
if (Array.isArray(tailwindConfig.corePlugins)) {
|
|
224
246
|
return tailwindConfig.corePlugins.includes(path)
|
|
@@ -230,17 +252,6 @@ function buildPluginApi(tailwindConfig, context, { variantList, variantMap, offs
|
|
|
230
252
|
// Preserved for backwards compatibility but not used in v3.0+
|
|
231
253
|
return []
|
|
232
254
|
},
|
|
233
|
-
addUserCss(userCss) {
|
|
234
|
-
for (let [identifier, rule] of withIdentifiers(userCss)) {
|
|
235
|
-
let offset = offsets.user++
|
|
236
|
-
|
|
237
|
-
if (!context.candidateRuleMap.has(identifier)) {
|
|
238
|
-
context.candidateRuleMap.set(identifier, [])
|
|
239
|
-
}
|
|
240
|
-
|
|
241
|
-
context.candidateRuleMap.get(identifier).push([{ sort: offset, layer: 'user' }, rule])
|
|
242
|
-
}
|
|
243
|
-
},
|
|
244
255
|
addBase(base) {
|
|
245
256
|
for (let [identifier, rule] of withIdentifiers(base)) {
|
|
246
257
|
let prefixedIdentifier = prefixIdentifier(identifier, {})
|
|
@@ -434,7 +445,65 @@ function buildPluginApi(tailwindConfig, context, { variantList, variantMap, offs
|
|
|
434
445
|
context.candidateRuleMap.get(prefixedIdentifier).push(withOffsets)
|
|
435
446
|
}
|
|
436
447
|
},
|
|
448
|
+
addVariant(variantName, variantFunctions, options = {}) {
|
|
449
|
+
variantFunctions = [].concat(variantFunctions).map((variantFunction) => {
|
|
450
|
+
if (typeof variantFunction !== 'string') {
|
|
451
|
+
// Safelist public API functions
|
|
452
|
+
return (api) => {
|
|
453
|
+
let { args, modifySelectors, container, separator, wrap, format } = api
|
|
454
|
+
let result = variantFunction(
|
|
455
|
+
Object.assign(
|
|
456
|
+
{ modifySelectors, container, separator },
|
|
457
|
+
variantFunction[MATCH_VARIANT] && { args, wrap, format }
|
|
458
|
+
)
|
|
459
|
+
)
|
|
460
|
+
|
|
461
|
+
if (typeof result === 'string' && !isValidVariantFormatString(result)) {
|
|
462
|
+
throw new Error(
|
|
463
|
+
`Your custom variant \`${variantName}\` has an invalid format string. Make sure it's an at-rule or contains a \`&\` placeholder.`
|
|
464
|
+
)
|
|
465
|
+
}
|
|
466
|
+
|
|
467
|
+
if (Array.isArray(result)) {
|
|
468
|
+
return result.map((variant) => parseVariant(variant))
|
|
469
|
+
}
|
|
470
|
+
|
|
471
|
+
// result may be undefined with legacy variants that use APIs like `modifySelectors`
|
|
472
|
+
return result && parseVariant(result)(api)
|
|
473
|
+
}
|
|
474
|
+
}
|
|
475
|
+
|
|
476
|
+
if (!isValidVariantFormatString(variantFunction)) {
|
|
477
|
+
throw new Error(
|
|
478
|
+
`Your custom variant \`${variantName}\` has an invalid format string. Make sure it's an at-rule or contains a \`&\` placeholder.`
|
|
479
|
+
)
|
|
480
|
+
}
|
|
481
|
+
|
|
482
|
+
return parseVariant(variantFunction)
|
|
483
|
+
})
|
|
484
|
+
|
|
485
|
+
insertInto(variantList, variantName, options)
|
|
486
|
+
variantMap.set(variantName, variantFunctions)
|
|
487
|
+
},
|
|
488
|
+
}
|
|
489
|
+
|
|
490
|
+
if (flagEnabled(tailwindConfig, 'matchVariant')) {
|
|
491
|
+
api.matchVariant = function (variants, options) {
|
|
492
|
+
for (let variant in variants) {
|
|
493
|
+
for (let [k, v] of Object.entries(options?.values ?? {})) {
|
|
494
|
+
api.addVariant(`${variant}-${k}`, variants[variant](v))
|
|
495
|
+
}
|
|
496
|
+
|
|
497
|
+
api.addVariant(
|
|
498
|
+
variant,
|
|
499
|
+
Object.assign(({ args }) => variants[variant](args), { [MATCH_VARIANT]: true }),
|
|
500
|
+
options
|
|
501
|
+
)
|
|
502
|
+
}
|
|
503
|
+
}
|
|
437
504
|
}
|
|
505
|
+
|
|
506
|
+
return api
|
|
438
507
|
}
|
|
439
508
|
|
|
440
509
|
let fileModifiedMapCache = new WeakMap()
|
|
@@ -521,15 +590,6 @@ function collectLayerPlugins(root) {
|
|
|
521
590
|
}
|
|
522
591
|
})
|
|
523
592
|
|
|
524
|
-
root.walkRules((rule) => {
|
|
525
|
-
// At this point it is safe to include all the left-over css from the
|
|
526
|
-
// user's css file. This is because the `@tailwind` and `@layer` directives
|
|
527
|
-
// will already be handled and will be removed from the css tree.
|
|
528
|
-
layerPlugins.push(function ({ addUserCss }) {
|
|
529
|
-
addUserCss(rule, { respectPrefix: false })
|
|
530
|
-
})
|
|
531
|
-
})
|
|
532
|
-
|
|
533
593
|
return layerPlugins
|
|
534
594
|
}
|
|
535
595
|
|
|
@@ -563,6 +623,7 @@ function resolvePlugins(context, root) {
|
|
|
563
623
|
let afterVariants = [
|
|
564
624
|
variantPlugins['directionVariants'],
|
|
565
625
|
variantPlugins['reducedMotionVariants'],
|
|
626
|
+
variantPlugins['prefersContrastVariants'],
|
|
566
627
|
variantPlugins['darkVariants'],
|
|
567
628
|
variantPlugins['printVariant'],
|
|
568
629
|
variantPlugins['screenVariants'],
|
|
@@ -842,6 +903,8 @@ export function getContext(
|
|
|
842
903
|
existingContext = context
|
|
843
904
|
}
|
|
844
905
|
|
|
906
|
+
let cssDidChange = hasContentChanged(sourcePath, root)
|
|
907
|
+
|
|
845
908
|
// If there's already a context in the cache and we don't need to
|
|
846
909
|
// reset the context, return the cached context.
|
|
847
910
|
if (existingContext) {
|
|
@@ -849,7 +912,7 @@ export function getContext(
|
|
|
849
912
|
[...contextDependencies],
|
|
850
913
|
getFileModifiedMap(existingContext)
|
|
851
914
|
)
|
|
852
|
-
if (!contextDependenciesChanged) {
|
|
915
|
+
if (!contextDependenciesChanged && !cssDidChange) {
|
|
853
916
|
return [existingContext, false]
|
|
854
917
|
}
|
|
855
918
|
}
|
|
@@ -16,6 +16,7 @@ import { env } from './sharedState'
|
|
|
16
16
|
|
|
17
17
|
import { getContext, getFileModifiedMap } from './setupContextUtils'
|
|
18
18
|
import parseDependency from '../util/parseDependency'
|
|
19
|
+
import { validateConfig } from '../util/validateConfig.js'
|
|
19
20
|
|
|
20
21
|
let configPathCache = new LRU({ maxSize: 100 })
|
|
21
22
|
|
|
@@ -63,6 +64,7 @@ function getTailwindConfig(configOrPath) {
|
|
|
63
64
|
delete require.cache[file]
|
|
64
65
|
}
|
|
65
66
|
let newConfig = resolveConfig(require(userConfigPath))
|
|
67
|
+
newConfig = validateConfig(newConfig)
|
|
66
68
|
let newHash = hash(newConfig)
|
|
67
69
|
configPathCache.set(userConfigPath, [newConfig, newHash, newDeps, newModified])
|
|
68
70
|
return [newConfig, userConfigPath, newHash, newDeps]
|
|
@@ -73,6 +75,8 @@ function getTailwindConfig(configOrPath) {
|
|
|
73
75
|
configOrPath.config === undefined ? configOrPath : configOrPath.config
|
|
74
76
|
)
|
|
75
77
|
|
|
78
|
+
newConfig = validateConfig(newConfig)
|
|
79
|
+
|
|
76
80
|
return [newConfig, null, hash(newConfig), []]
|
|
77
81
|
}
|
|
78
82
|
|
|
@@ -112,7 +116,7 @@ function resolveChangedFiles(candidateFiles, fileModifiedMap) {
|
|
|
112
116
|
// source path), or set up a new one (including setting up watchers and registering
|
|
113
117
|
// plugins) then return it
|
|
114
118
|
export default function setupTrackingContext(configOrPath) {
|
|
115
|
-
return ({ tailwindDirectives, registerDependency
|
|
119
|
+
return ({ tailwindDirectives, registerDependency }) => {
|
|
116
120
|
return (root, result) => {
|
|
117
121
|
let [tailwindConfig, userConfigPath, tailwindConfigHash, configDependencies] =
|
|
118
122
|
getTailwindConfig(configOrPath)
|
|
@@ -125,7 +129,7 @@ export default function setupTrackingContext(configOrPath) {
|
|
|
125
129
|
// being part of this trigger too, but it's tough because it's impossible
|
|
126
130
|
// for a layer in one file to end up in the actual @tailwind rule in
|
|
127
131
|
// another file since independent sources are effectively isolated.
|
|
128
|
-
if (tailwindDirectives.size > 0
|
|
132
|
+
if (tailwindDirectives.size > 0) {
|
|
129
133
|
// Add current css file as a context dependencies.
|
|
130
134
|
contextDependencies.add(result.opts.from)
|
|
131
135
|
|
|
@@ -153,7 +157,7 @@ export default function setupTrackingContext(configOrPath) {
|
|
|
153
157
|
// We may want to think about `@layer` being part of this trigger too, but it's tough
|
|
154
158
|
// because it's impossible for a layer in one file to end up in the actual @tailwind rule
|
|
155
159
|
// in another file since independent sources are effectively isolated.
|
|
156
|
-
if (tailwindDirectives.size > 0
|
|
160
|
+
if (tailwindDirectives.size > 0) {
|
|
157
161
|
let fileModifiedMap = getFileModifiedMap(context)
|
|
158
162
|
|
|
159
163
|
// Add template paths as postcss dependencies.
|
package/src/lib/sharedState.js
CHANGED
|
@@ -5,6 +5,7 @@ export const env = {
|
|
|
5
5
|
export const contextMap = new Map()
|
|
6
6
|
export const configContextMap = new Map()
|
|
7
7
|
export const contextSourcesMap = new Map()
|
|
8
|
+
export const sourceHashMap = new Map()
|
|
8
9
|
export const NOT_ON_DEMAND = new String('*')
|
|
9
10
|
|
|
10
11
|
export function resolveDebug(debug) {
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# tailwindcss/nesting
|
|
2
2
|
|
|
3
|
-
This is a PostCSS plugin that wraps [postcss-nested](https://github.com/postcss/postcss-nested) or [postcss-nesting](https://github.com/
|
|
3
|
+
This is a PostCSS plugin that wraps [postcss-nested](https://github.com/postcss/postcss-nested) or [postcss-nesting](https://github.com/csstools/postcss-plugins/tree/main/plugins/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
4
|
|
|
5
5
|
Add it to your PostCSS configuration, somewhere before Tailwind itself:
|
|
6
6
|
|
|
@@ -18,7 +18,7 @@ module.exports = {
|
|
|
18
18
|
|
|
19
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
20
|
|
|
21
|
-
If you'd rather use [postcss-nesting](https://github.com/
|
|
21
|
+
If you'd rather use [postcss-nesting](https://github.com/csstools/postcss-plugins/tree/main/plugins/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
22
|
|
|
23
23
|
```shell
|
|
24
24
|
npm install postcss-nesting
|
|
@@ -39,6 +39,42 @@ export function nesting(opts = postcssNested) {
|
|
|
39
39
|
decl.remove()
|
|
40
40
|
})
|
|
41
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
|
+
|
|
42
78
|
return root
|
|
43
79
|
}
|
|
44
80
|
}
|
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
|
@@ -2,17 +2,21 @@ import namedColors from 'color-name'
|
|
|
2
2
|
|
|
3
3
|
let HEX = /^#([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})?$/i
|
|
4
4
|
let SHORT_HEX = /^#([a-f\d])([a-f\d])([a-f\d])([a-f\d])?$/i
|
|
5
|
-
let VALUE =
|
|
6
|
-
let SEP =
|
|
7
|
-
let ALPHA_SEP =
|
|
5
|
+
let VALUE = /(?:\d+|\d*\.\d+)%?/
|
|
6
|
+
let SEP = /(?:\s*,\s*|\s+)/
|
|
7
|
+
let ALPHA_SEP = /\s*[,/]\s*/
|
|
8
|
+
let CUSTOM_PROPERTY = /var\(--(?:[^ )]*?)\)/
|
|
9
|
+
|
|
8
10
|
let RGB = new RegExp(
|
|
9
|
-
`^
|
|
11
|
+
`^(rgb)a?\\(\\s*(${VALUE.source}|${CUSTOM_PROPERTY.source})(?:${SEP.source}(${VALUE.source}|${CUSTOM_PROPERTY.source}))?(?:${SEP.source}(${VALUE.source}|${CUSTOM_PROPERTY.source}))?(?:${ALPHA_SEP.source}(${VALUE.source}|${CUSTOM_PROPERTY.source}))?\\s*\\)$`
|
|
10
12
|
)
|
|
11
13
|
let HSL = new RegExp(
|
|
12
|
-
`^
|
|
14
|
+
`^(hsl)a?\\(\\s*((?:${VALUE.source})(?:deg|rad|grad|turn)?|${CUSTOM_PROPERTY.source})(?:${SEP.source}(${VALUE.source}|${CUSTOM_PROPERTY.source}))?(?:${SEP.source}(${VALUE.source}|${CUSTOM_PROPERTY.source}))?(?:${ALPHA_SEP.source}(${VALUE.source}|${CUSTOM_PROPERTY.source}))?\\s*\\)$`
|
|
13
15
|
)
|
|
14
16
|
|
|
15
|
-
|
|
17
|
+
// In "loose" mode the color may contain fewer than 3 parts, as long as at least
|
|
18
|
+
// one of the parts is variable.
|
|
19
|
+
export function parseColor(value, { loose = false } = {}) {
|
|
16
20
|
if (typeof value !== 'string') {
|
|
17
21
|
return null
|
|
18
22
|
}
|
|
@@ -40,27 +44,27 @@ export function parseColor(value) {
|
|
|
40
44
|
}
|
|
41
45
|
}
|
|
42
46
|
|
|
43
|
-
let
|
|
47
|
+
let match = value.match(RGB) ?? value.match(HSL)
|
|
44
48
|
|
|
45
|
-
if (
|
|
46
|
-
return
|
|
47
|
-
mode: 'rgb',
|
|
48
|
-
color: [rgbMatch[1], rgbMatch[2], rgbMatch[3]].map((v) => v.toString()),
|
|
49
|
-
alpha: rgbMatch[4]?.toString?.(),
|
|
50
|
-
}
|
|
49
|
+
if (match === null) {
|
|
50
|
+
return null
|
|
51
51
|
}
|
|
52
52
|
|
|
53
|
-
let
|
|
53
|
+
let color = [match[2], match[3], match[4]].filter(Boolean).map((v) => v.toString())
|
|
54
54
|
|
|
55
|
-
if (
|
|
56
|
-
return
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
55
|
+
if (!loose && color.length !== 3) {
|
|
56
|
+
return null
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
if (color.length < 3 && !color.some((part) => /^var\(.*?\)$/.test(part))) {
|
|
60
|
+
return null
|
|
61
61
|
}
|
|
62
62
|
|
|
63
|
-
return
|
|
63
|
+
return {
|
|
64
|
+
mode: match[1],
|
|
65
|
+
color,
|
|
66
|
+
alpha: match[5]?.toString?.(),
|
|
67
|
+
}
|
|
64
68
|
}
|
|
65
69
|
|
|
66
70
|
export function formatColor({ mode, color, alpha }) {
|
package/src/util/dataTypes.js
CHANGED
|
@@ -42,10 +42,16 @@ export function normalize(value, isRoot = true) {
|
|
|
42
42
|
|
|
43
43
|
// Add spaces around operators inside calc() that do not follow an operator
|
|
44
44
|
// or '('.
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
45
|
+
value = value.replace(/calc\(.+\)/g, (match) => {
|
|
46
|
+
return match.replace(
|
|
47
|
+
/(-?\d*\.?\d(?!\b-.+[,)](?![^+\-/*])\D)(?:%|[a-z]+)?|\))([+\-/*])/g,
|
|
48
|
+
'$1 $2 '
|
|
49
|
+
)
|
|
50
|
+
})
|
|
51
|
+
|
|
52
|
+
// Add spaces around some operators not inside calc() that do not follow an operator
|
|
53
|
+
// or '('.
|
|
54
|
+
return value.replace(/(-?\d*\.?\d(?!\b-.+[,)](?![^+\-/*])\D)(?:%|[a-z]+)?|\))([\/])/g, '$1 $2 ')
|
|
49
55
|
}
|
|
50
56
|
|
|
51
57
|
export function url(value) {
|
|
@@ -57,7 +63,9 @@ export function number(value) {
|
|
|
57
63
|
}
|
|
58
64
|
|
|
59
65
|
export function percentage(value) {
|
|
60
|
-
return
|
|
66
|
+
return value.split(UNDERSCORE).every((part) => {
|
|
67
|
+
return /%$/g.test(part) || cssFunctions.some((fn) => new RegExp(`^${fn}\\(.+?%`).test(part))
|
|
68
|
+
})
|
|
61
69
|
}
|
|
62
70
|
|
|
63
71
|
let lengthUnits = [
|
|
@@ -113,7 +121,7 @@ export function color(value) {
|
|
|
113
121
|
part = normalize(part)
|
|
114
122
|
|
|
115
123
|
if (part.startsWith('var(')) return true
|
|
116
|
-
if (parseColor(part) !== null) return colors++, true
|
|
124
|
+
if (parseColor(part, { loose: true }) !== null) return colors++, true
|
|
117
125
|
|
|
118
126
|
return false
|
|
119
127
|
})
|