tailwindcss 3.0.8 → 3.0.12
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 +53 -1
- package/lib/cli.js +49 -12
- package/lib/corePlugins.js +114 -142
- package/lib/css/preflight.css +1 -1
- package/lib/featureFlags.js +1 -3
- package/lib/index.js +1 -3
- package/lib/lib/collapseDuplicateDeclarations.js +52 -1
- package/lib/lib/defaultExtractor.js +2 -0
- package/lib/lib/expandApplyAtRules.js +0 -40
- package/lib/lib/expandTailwindAtRules.js +12 -25
- package/lib/lib/normalizeTailwindDirectives.js +8 -1
- package/lib/lib/resolveDefaultsAtRules.js +4 -4
- package/lib/lib/setupContextUtils.js +131 -69
- package/lib/lib/setupTrackingContext.js +11 -10
- package/lib/lib/sharedState.js +33 -4
- package/lib/processTailwindFeatures.js +3 -2
- package/lib/util/pluginUtils.js +1 -1
- package/package.json +7 -8
- package/peers/index.js +606 -606
- package/src/cli.js +57 -12
- package/src/corePlugins.js +121 -155
- package/src/css/preflight.css +1 -1
- package/src/featureFlags.js +1 -5
- package/src/index.js +1 -7
- package/src/lib/collapseDuplicateDeclarations.js +66 -1
- package/src/lib/defaultExtractor.js +2 -0
- package/src/lib/expandApplyAtRules.js +0 -42
- package/src/lib/expandTailwindAtRules.js +12 -21
- package/src/lib/normalizeTailwindDirectives.js +6 -1
- package/src/lib/resolveDefaultsAtRules.js +5 -5
- package/src/lib/setupContextUtils.js +108 -19
- package/src/lib/setupTrackingContext.js +11 -10
- package/src/lib/sharedState.js +40 -4
- package/src/processTailwindFeatures.js +4 -2
- package/src/util/pluginUtils.js +1 -1
- package/lib/lib/setupWatchingContext.js +0 -288
- package/src/lib/setupWatchingContext.js +0 -311
package/src/css/preflight.css
CHANGED
package/src/featureFlags.js
CHANGED
|
@@ -2,11 +2,7 @@ import chalk from 'chalk'
|
|
|
2
2
|
import log from './util/log'
|
|
3
3
|
|
|
4
4
|
let defaults = {
|
|
5
|
-
|
|
6
|
-
// the default.
|
|
7
|
-
optimizeUniversalDefaults: process.env.NODE_ENV === 'test' ? true : false,
|
|
8
|
-
|
|
9
|
-
// optimizeUniversalDefaults: true
|
|
5
|
+
optimizeUniversalDefaults: false,
|
|
10
6
|
}
|
|
11
7
|
|
|
12
8
|
let featureFlags = {
|
package/src/index.js
CHANGED
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
import setupTrackingContext from './lib/setupTrackingContext'
|
|
2
|
-
import setupWatchingContext from './lib/setupWatchingContext'
|
|
3
2
|
import processTailwindFeatures from './processTailwindFeatures'
|
|
4
3
|
import { env } from './lib/sharedState'
|
|
5
4
|
|
|
@@ -14,12 +13,7 @@ module.exports = function tailwindcss(configOrPath) {
|
|
|
14
13
|
return root
|
|
15
14
|
},
|
|
16
15
|
function (root, result) {
|
|
17
|
-
|
|
18
|
-
env.TAILWIND_MODE === 'watch'
|
|
19
|
-
? setupWatchingContext(configOrPath)
|
|
20
|
-
: setupTrackingContext(configOrPath)
|
|
21
|
-
|
|
22
|
-
processTailwindFeatures(setupContext)(root, result)
|
|
16
|
+
processTailwindFeatures(setupTrackingContext(configOrPath))(root, result)
|
|
23
17
|
},
|
|
24
18
|
env.DEBUG &&
|
|
25
19
|
function (root) {
|
|
@@ -3,6 +3,7 @@ export default function collapseDuplicateDeclarations() {
|
|
|
3
3
|
root.walkRules((node) => {
|
|
4
4
|
let seen = new Map()
|
|
5
5
|
let droppable = new Set([])
|
|
6
|
+
let byProperty = new Map()
|
|
6
7
|
|
|
7
8
|
node.walkDecls((decl) => {
|
|
8
9
|
// This could happen if we have nested selectors. In that case the
|
|
@@ -14,15 +15,79 @@ export default function collapseDuplicateDeclarations() {
|
|
|
14
15
|
}
|
|
15
16
|
|
|
16
17
|
if (seen.has(decl.prop)) {
|
|
17
|
-
|
|
18
|
+
// Exact same value as what we have seen so far
|
|
19
|
+
if (seen.get(decl.prop).value === decl.value) {
|
|
20
|
+
// Keep the last one, drop the one we've seen so far
|
|
21
|
+
droppable.add(seen.get(decl.prop))
|
|
22
|
+
// Override the existing one with the new value. This is necessary
|
|
23
|
+
// so that if we happen to have more than one declaration with the
|
|
24
|
+
// same value, that we keep removing the previous one. Otherwise we
|
|
25
|
+
// will only remove the *first* one.
|
|
26
|
+
seen.set(decl.prop, decl)
|
|
27
|
+
return
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
// Not the same value, so we need to check if we can merge it so
|
|
31
|
+
// let's collect it first.
|
|
32
|
+
if (!byProperty.has(decl.prop)) {
|
|
33
|
+
byProperty.set(decl.prop, new Set())
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
byProperty.get(decl.prop).add(seen.get(decl.prop))
|
|
37
|
+
byProperty.get(decl.prop).add(decl)
|
|
18
38
|
}
|
|
19
39
|
|
|
20
40
|
seen.set(decl.prop, decl)
|
|
21
41
|
})
|
|
22
42
|
|
|
43
|
+
// Drop all the duplicate declarations with the exact same value we've
|
|
44
|
+
// already seen so far.
|
|
23
45
|
for (let decl of droppable) {
|
|
24
46
|
decl.remove()
|
|
25
47
|
}
|
|
48
|
+
|
|
49
|
+
// Analyze the declarations based on its unit, drop all the declarations
|
|
50
|
+
// with the same unit but the last one in the list.
|
|
51
|
+
for (let declarations of byProperty.values()) {
|
|
52
|
+
let byUnit = new Map()
|
|
53
|
+
|
|
54
|
+
for (let decl of declarations) {
|
|
55
|
+
let unit = resolveUnit(decl.value)
|
|
56
|
+
if (unit === null) {
|
|
57
|
+
// We don't have a unit, so should never try and collapse this
|
|
58
|
+
// value. This is because we can't know how to do it in a correct
|
|
59
|
+
// way (e.g.: overrides for older browsers)
|
|
60
|
+
continue
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
if (!byUnit.has(unit)) {
|
|
64
|
+
byUnit.set(unit, new Set())
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
byUnit.get(unit).add(decl)
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
for (let declarations of byUnit.values()) {
|
|
71
|
+
// Get all but the last one
|
|
72
|
+
let removableDeclarations = Array.from(declarations).slice(0, -1)
|
|
73
|
+
|
|
74
|
+
for (let decl of removableDeclarations) {
|
|
75
|
+
decl.remove()
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
}
|
|
26
79
|
})
|
|
27
80
|
}
|
|
28
81
|
}
|
|
82
|
+
|
|
83
|
+
let UNITLESS_NUMBER = Symbol('unitless-number')
|
|
84
|
+
|
|
85
|
+
function resolveUnit(input) {
|
|
86
|
+
let result = /^-?\d*.?\d+([\w%]+)?$/g.exec(input)
|
|
87
|
+
|
|
88
|
+
if (result) {
|
|
89
|
+
return result[1] ?? UNITLESS_NUMBER
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
return null
|
|
93
|
+
}
|
|
@@ -10,9 +10,11 @@ const PATTERNS = [
|
|
|
10
10
|
/([^<>"'`\s]*\[\w*\("[^'`\s]*"\)\])/.source, // bg-[url("..."),url("...")]
|
|
11
11
|
/([^<>"'`\s]*\['[^"'`\s]*'\])/.source, // `content-['hello']` but not `content-['hello']']`
|
|
12
12
|
/([^<>"'`\s]*\["[^"'`\s]*"\])/.source, // `content-["hello"]` but not `content-["hello"]"]`
|
|
13
|
+
/([^<>"'`\s]*\[[^<>"'`\s]*:[^\]\s]*\])/.source, // `[attr:value]`
|
|
13
14
|
/([^<>"'`\s]*\[[^<>"'`\s]*:'[^"'`\s]*'\])/.source, // `[content:'hello']` but not `[content:"hello"]`
|
|
14
15
|
/([^<>"'`\s]*\[[^<>"'`\s]*:"[^"'`\s]*"\])/.source, // `[content:"hello"]` but not `[content:'hello']`
|
|
15
16
|
/([^<>"'`\s]*\[[^"'`\s]+\][^<>"'`\s]*)/.source, // `fill-[#bada55]`, `fill-[#bada55]/50`
|
|
17
|
+
/([^"'`\s]*[^<>"'`\s:\\])/.source, // `<sm:underline`, `md>:font-bold`
|
|
16
18
|
/([^<>"'`\s]*[^"'`\s:\\])/.source, // `px-1.5`, `uppercase` but not `uppercase:`
|
|
17
19
|
|
|
18
20
|
// Arbitrary properties
|
|
@@ -72,47 +72,6 @@ function extractApplyCandidates(params) {
|
|
|
72
72
|
return [candidates, false]
|
|
73
73
|
}
|
|
74
74
|
|
|
75
|
-
function partitionApplyParents(root) {
|
|
76
|
-
let applyParents = new Set()
|
|
77
|
-
|
|
78
|
-
root.walkAtRules('apply', (rule) => {
|
|
79
|
-
applyParents.add(rule.parent)
|
|
80
|
-
})
|
|
81
|
-
|
|
82
|
-
for (let rule of applyParents) {
|
|
83
|
-
let nodeGroups = []
|
|
84
|
-
let lastGroup = []
|
|
85
|
-
|
|
86
|
-
for (let node of rule.nodes) {
|
|
87
|
-
if (node.type === 'atrule' && node.name === 'apply') {
|
|
88
|
-
if (lastGroup.length > 0) {
|
|
89
|
-
nodeGroups.push(lastGroup)
|
|
90
|
-
lastGroup = []
|
|
91
|
-
}
|
|
92
|
-
nodeGroups.push([node])
|
|
93
|
-
} else {
|
|
94
|
-
lastGroup.push(node)
|
|
95
|
-
}
|
|
96
|
-
}
|
|
97
|
-
|
|
98
|
-
if (lastGroup.length > 0) {
|
|
99
|
-
nodeGroups.push(lastGroup)
|
|
100
|
-
}
|
|
101
|
-
|
|
102
|
-
if (nodeGroups.length === 1) {
|
|
103
|
-
continue
|
|
104
|
-
}
|
|
105
|
-
|
|
106
|
-
for (let group of [...nodeGroups].reverse()) {
|
|
107
|
-
let newParent = rule.clone({ nodes: [] })
|
|
108
|
-
newParent.append(group)
|
|
109
|
-
rule.after(newParent)
|
|
110
|
-
}
|
|
111
|
-
|
|
112
|
-
rule.remove()
|
|
113
|
-
}
|
|
114
|
-
}
|
|
115
|
-
|
|
116
75
|
function processApply(root, context) {
|
|
117
76
|
let applyCandidates = new Set()
|
|
118
77
|
|
|
@@ -343,7 +302,6 @@ function processApply(root, context) {
|
|
|
343
302
|
|
|
344
303
|
export default function expandApplyAtRules(context) {
|
|
345
304
|
return (root) => {
|
|
346
|
-
partitionApplyParents(root)
|
|
347
305
|
processApply(root, context)
|
|
348
306
|
}
|
|
349
307
|
}
|
|
@@ -129,8 +129,6 @@ function buildStylesheet(rules, context) {
|
|
|
129
129
|
return returnValue
|
|
130
130
|
}
|
|
131
131
|
|
|
132
|
-
export const DEFAULTS_LAYER = Symbol('defaults-layer')
|
|
133
|
-
|
|
134
132
|
export default function expandTailwindAtRules(context) {
|
|
135
133
|
return (root) => {
|
|
136
134
|
let layerNodes = {
|
|
@@ -140,13 +138,15 @@ export default function expandTailwindAtRules(context) {
|
|
|
140
138
|
variants: null,
|
|
141
139
|
}
|
|
142
140
|
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
if (
|
|
149
|
-
layerNodes
|
|
141
|
+
root.walkAtRules((rule) => {
|
|
142
|
+
// Make sure this file contains Tailwind directives. If not, we can save
|
|
143
|
+
// a lot of work and bail early. Also we don't have to register our touch
|
|
144
|
+
// file as a dependency since the output of this CSS does not depend on
|
|
145
|
+
// the source of any templates. Think Vue <style> blocks for example.
|
|
146
|
+
if (rule.name === 'tailwind') {
|
|
147
|
+
if (Object.keys(layerNodes).includes(rule.params)) {
|
|
148
|
+
layerNodes[rule.params] = rule
|
|
149
|
+
}
|
|
150
150
|
}
|
|
151
151
|
})
|
|
152
152
|
|
|
@@ -168,6 +168,8 @@ export default function expandTailwindAtRules(context) {
|
|
|
168
168
|
getClassCandidates(transformer(content), extractor, candidates, seen)
|
|
169
169
|
}
|
|
170
170
|
|
|
171
|
+
env.DEBUG && console.timeEnd('Reading changed files')
|
|
172
|
+
|
|
171
173
|
// ---
|
|
172
174
|
|
|
173
175
|
// Generate the actual CSS
|
|
@@ -201,18 +203,7 @@ export default function expandTailwindAtRules(context) {
|
|
|
201
203
|
// Replace any Tailwind directives with generated CSS
|
|
202
204
|
|
|
203
205
|
if (layerNodes.base) {
|
|
204
|
-
layerNodes.base.before(cloneNodes([...baseNodes], layerNodes.base.source))
|
|
205
|
-
}
|
|
206
|
-
|
|
207
|
-
// @defaults rules are unconditionally added first to ensure that
|
|
208
|
-
// using any utility that relies on defaults will work even when
|
|
209
|
-
// compiled in an isolated environment like CSS modules
|
|
210
|
-
if (context.tailwindConfig[DEFAULTS_LAYER] !== false) {
|
|
211
|
-
if (layerNodes.base) {
|
|
212
|
-
layerNodes.base.after(cloneNodes([...defaultNodes], root.source))
|
|
213
|
-
} else {
|
|
214
|
-
root.prepend(cloneNodes([...defaultNodes], root.source))
|
|
215
|
-
}
|
|
206
|
+
layerNodes.base.before(cloneNodes([...baseNodes, ...defaultNodes], layerNodes.base.source))
|
|
216
207
|
}
|
|
217
208
|
|
|
218
209
|
if (layerNodes.base) {
|
|
@@ -3,8 +3,13 @@ import log from '../util/log'
|
|
|
3
3
|
export default function normalizeTailwindDirectives(root) {
|
|
4
4
|
let tailwindDirectives = new Set()
|
|
5
5
|
let layerDirectives = new Set()
|
|
6
|
+
let applyDirectives = new Set()
|
|
6
7
|
|
|
7
8
|
root.walkAtRules((atRule) => {
|
|
9
|
+
if (atRule.name === 'apply') {
|
|
10
|
+
applyDirectives.add(atRule)
|
|
11
|
+
}
|
|
12
|
+
|
|
8
13
|
if (atRule.name === 'import') {
|
|
9
14
|
if (atRule.params === '"tailwindcss/base"' || atRule.params === "'tailwindcss/base'") {
|
|
10
15
|
atRule.name = 'tailwind'
|
|
@@ -74,5 +79,5 @@ export default function normalizeTailwindDirectives(root) {
|
|
|
74
79
|
}
|
|
75
80
|
}
|
|
76
81
|
|
|
77
|
-
return tailwindDirectives
|
|
82
|
+
return { tailwindDirectives, applyDirectives }
|
|
78
83
|
}
|
|
@@ -113,12 +113,12 @@ export default function resolveDefaultsAtRules({ tailwindConfig }) {
|
|
|
113
113
|
}
|
|
114
114
|
}
|
|
115
115
|
|
|
116
|
-
if (selectorGroups.size === 0) {
|
|
117
|
-
universal.remove()
|
|
118
|
-
continue
|
|
119
|
-
}
|
|
120
|
-
|
|
121
116
|
if (flagEnabled(tailwindConfig, 'optimizeUniversalDefaults')) {
|
|
117
|
+
if (selectorGroups.size === 0) {
|
|
118
|
+
universal.remove()
|
|
119
|
+
continue
|
|
120
|
+
}
|
|
121
|
+
|
|
122
122
|
for (let [, selectors] of selectorGroups) {
|
|
123
123
|
let universalRule = postcss.rule()
|
|
124
124
|
|
|
@@ -20,6 +20,58 @@ import log from '../util/log'
|
|
|
20
20
|
import negateValue from '../util/negateValue'
|
|
21
21
|
import isValidArbitraryValue from '../util/isValidArbitraryValue'
|
|
22
22
|
|
|
23
|
+
function partitionRules(root) {
|
|
24
|
+
if (!root.walkAtRules) return [root]
|
|
25
|
+
|
|
26
|
+
let applyParents = new Set()
|
|
27
|
+
let rules = []
|
|
28
|
+
|
|
29
|
+
root.walkAtRules('apply', (rule) => {
|
|
30
|
+
applyParents.add(rule.parent)
|
|
31
|
+
})
|
|
32
|
+
|
|
33
|
+
if (applyParents.size === 0) {
|
|
34
|
+
rules.push(root)
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
for (let rule of applyParents) {
|
|
38
|
+
let nodeGroups = []
|
|
39
|
+
let lastGroup = []
|
|
40
|
+
|
|
41
|
+
for (let node of rule.nodes) {
|
|
42
|
+
if (node.type === 'atrule' && node.name === 'apply') {
|
|
43
|
+
if (lastGroup.length > 0) {
|
|
44
|
+
nodeGroups.push(lastGroup)
|
|
45
|
+
lastGroup = []
|
|
46
|
+
}
|
|
47
|
+
nodeGroups.push([node])
|
|
48
|
+
} else {
|
|
49
|
+
lastGroup.push(node)
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
if (lastGroup.length > 0) {
|
|
54
|
+
nodeGroups.push(lastGroup)
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
if (nodeGroups.length === 1) {
|
|
58
|
+
rules.push(rule)
|
|
59
|
+
continue
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
for (let group of [...nodeGroups].reverse()) {
|
|
63
|
+
let clone = rule.clone({ nodes: [] })
|
|
64
|
+
clone.append(group)
|
|
65
|
+
rules.unshift(clone)
|
|
66
|
+
rule.after(clone)
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
rule.remove()
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
return rules
|
|
73
|
+
}
|
|
74
|
+
|
|
23
75
|
function parseVariantFormatString(input) {
|
|
24
76
|
if (input.includes('{')) {
|
|
25
77
|
if (!isBalanced(input)) throw new Error(`Your { and } are unbalanced.`)
|
|
@@ -89,39 +141,55 @@ function getClasses(selector) {
|
|
|
89
141
|
return parser.transformSync(selector)
|
|
90
142
|
}
|
|
91
143
|
|
|
92
|
-
function extractCandidates(node) {
|
|
144
|
+
function extractCandidates(node, state = { containsNonOnDemandable: false }, depth = 0) {
|
|
93
145
|
let classes = []
|
|
94
146
|
|
|
147
|
+
// Handle normal rules
|
|
95
148
|
if (node.type === 'rule') {
|
|
96
149
|
for (let selector of node.selectors) {
|
|
97
150
|
let classCandidates = getClasses(selector)
|
|
98
151
|
// At least one of the selectors contains non-"on-demandable" candidates.
|
|
99
|
-
if (classCandidates.length === 0)
|
|
152
|
+
if (classCandidates.length === 0) {
|
|
153
|
+
state.containsNonOnDemandable = true
|
|
154
|
+
}
|
|
100
155
|
|
|
101
|
-
|
|
156
|
+
for (let classCandidate of classCandidates) {
|
|
157
|
+
classes.push(classCandidate)
|
|
158
|
+
}
|
|
102
159
|
}
|
|
103
|
-
return classes
|
|
104
160
|
}
|
|
105
161
|
|
|
106
|
-
|
|
162
|
+
// Handle at-rules (which contains nested rules)
|
|
163
|
+
else if (node.type === 'atrule') {
|
|
107
164
|
node.walkRules((rule) => {
|
|
108
|
-
|
|
165
|
+
for (let classCandidate of rule.selectors.flatMap((selector) =>
|
|
166
|
+
getClasses(selector, state, depth + 1)
|
|
167
|
+
)) {
|
|
168
|
+
classes.push(classCandidate)
|
|
169
|
+
}
|
|
109
170
|
})
|
|
110
171
|
}
|
|
111
172
|
|
|
173
|
+
if (depth === 0) {
|
|
174
|
+
return [state.containsNonOnDemandable || classes.length === 0, classes]
|
|
175
|
+
}
|
|
176
|
+
|
|
112
177
|
return classes
|
|
113
178
|
}
|
|
114
179
|
|
|
115
180
|
function withIdentifiers(styles) {
|
|
116
181
|
return parseStyles(styles).flatMap((node) => {
|
|
117
182
|
let nodeMap = new Map()
|
|
118
|
-
let candidates = extractCandidates(node)
|
|
183
|
+
let [containsNonOnDemandableSelectors, candidates] = extractCandidates(node)
|
|
119
184
|
|
|
120
|
-
// If this isn't "on-demandable", assign it a universal candidate.
|
|
121
|
-
if (
|
|
122
|
-
|
|
185
|
+
// If this isn't "on-demandable", assign it a universal candidate to always include it.
|
|
186
|
+
if (containsNonOnDemandableSelectors) {
|
|
187
|
+
candidates.unshift('*')
|
|
123
188
|
}
|
|
124
189
|
|
|
190
|
+
// However, it could be that it also contains "on-demandable" candidates.
|
|
191
|
+
// E.g.: `span, .foo {}`, in that case it should still be possible to use
|
|
192
|
+
// `@apply foo` for example.
|
|
125
193
|
return candidates.map((c) => {
|
|
126
194
|
if (!nodeMap.has(node)) {
|
|
127
195
|
nodeMap.set(node, node)
|
|
@@ -216,7 +284,9 @@ function buildPluginApi(tailwindConfig, context, { variantList, variantMap, offs
|
|
|
216
284
|
context.candidateRuleMap.set(identifier, [])
|
|
217
285
|
}
|
|
218
286
|
|
|
219
|
-
context.candidateRuleMap
|
|
287
|
+
context.candidateRuleMap
|
|
288
|
+
.get(identifier)
|
|
289
|
+
.push(...partitionRules(rule).map((rule) => [{ sort: offset, layer: 'user' }, rule]))
|
|
220
290
|
}
|
|
221
291
|
},
|
|
222
292
|
addBase(base) {
|
|
@@ -230,7 +300,7 @@ function buildPluginApi(tailwindConfig, context, { variantList, variantMap, offs
|
|
|
230
300
|
|
|
231
301
|
context.candidateRuleMap
|
|
232
302
|
.get(prefixedIdentifier)
|
|
233
|
-
.push([{ sort: offset, layer: 'base' }, rule])
|
|
303
|
+
.push(...partitionRules(rule).map((rule) => [{ sort: offset, layer: 'base' }, rule]))
|
|
234
304
|
}
|
|
235
305
|
},
|
|
236
306
|
/**
|
|
@@ -244,7 +314,6 @@ function buildPluginApi(tailwindConfig, context, { variantList, variantMap, offs
|
|
|
244
314
|
|
|
245
315
|
for (let [identifier, rule] of withIdentifiers(groups)) {
|
|
246
316
|
let prefixedIdentifier = prefixIdentifier(identifier, {})
|
|
247
|
-
let offset = offsets.base++
|
|
248
317
|
|
|
249
318
|
if (!context.candidateRuleMap.has(prefixedIdentifier)) {
|
|
250
319
|
context.candidateRuleMap.set(prefixedIdentifier, [])
|
|
@@ -252,7 +321,12 @@ function buildPluginApi(tailwindConfig, context, { variantList, variantMap, offs
|
|
|
252
321
|
|
|
253
322
|
context.candidateRuleMap
|
|
254
323
|
.get(prefixedIdentifier)
|
|
255
|
-
.push(
|
|
324
|
+
.push(
|
|
325
|
+
...partitionRules(rule).map((rule) => [
|
|
326
|
+
{ sort: offsets.base++, layer: 'defaults' },
|
|
327
|
+
rule,
|
|
328
|
+
])
|
|
329
|
+
)
|
|
256
330
|
}
|
|
257
331
|
},
|
|
258
332
|
addComponents(components, options) {
|
|
@@ -265,7 +339,6 @@ function buildPluginApi(tailwindConfig, context, { variantList, variantMap, offs
|
|
|
265
339
|
|
|
266
340
|
for (let [identifier, rule] of withIdentifiers(components)) {
|
|
267
341
|
let prefixedIdentifier = prefixIdentifier(identifier, options)
|
|
268
|
-
let offset = offsets.components++
|
|
269
342
|
|
|
270
343
|
classList.add(prefixedIdentifier)
|
|
271
344
|
|
|
@@ -275,7 +348,12 @@ function buildPluginApi(tailwindConfig, context, { variantList, variantMap, offs
|
|
|
275
348
|
|
|
276
349
|
context.candidateRuleMap
|
|
277
350
|
.get(prefixedIdentifier)
|
|
278
|
-
.push(
|
|
351
|
+
.push(
|
|
352
|
+
...partitionRules(rule).map((rule) => [
|
|
353
|
+
{ sort: offsets.components++, layer: 'components', options },
|
|
354
|
+
rule,
|
|
355
|
+
])
|
|
356
|
+
)
|
|
279
357
|
}
|
|
280
358
|
},
|
|
281
359
|
addUtilities(utilities, options) {
|
|
@@ -288,7 +366,6 @@ function buildPluginApi(tailwindConfig, context, { variantList, variantMap, offs
|
|
|
288
366
|
|
|
289
367
|
for (let [identifier, rule] of withIdentifiers(utilities)) {
|
|
290
368
|
let prefixedIdentifier = prefixIdentifier(identifier, options)
|
|
291
|
-
let offset = offsets.utilities++
|
|
292
369
|
|
|
293
370
|
classList.add(prefixedIdentifier)
|
|
294
371
|
|
|
@@ -298,7 +375,12 @@ function buildPluginApi(tailwindConfig, context, { variantList, variantMap, offs
|
|
|
298
375
|
|
|
299
376
|
context.candidateRuleMap
|
|
300
377
|
.get(prefixedIdentifier)
|
|
301
|
-
.push(
|
|
378
|
+
.push(
|
|
379
|
+
...partitionRules(rule).map((rule) => [
|
|
380
|
+
{ sort: offsets.utilities++, layer: 'utilities', options },
|
|
381
|
+
rule,
|
|
382
|
+
])
|
|
383
|
+
)
|
|
302
384
|
}
|
|
303
385
|
},
|
|
304
386
|
matchUtilities: function (utilities, options) {
|
|
@@ -435,7 +517,14 @@ function trackModified(files, fileModifiedMap) {
|
|
|
435
517
|
let parsed = url.parse(file)
|
|
436
518
|
let pathname = parsed.hash ? parsed.href.replace(parsed.hash, '') : parsed.href
|
|
437
519
|
pathname = parsed.search ? pathname.replace(parsed.search, '') : pathname
|
|
438
|
-
let newModified = fs.statSync(decodeURIComponent(pathname))
|
|
520
|
+
let newModified = fs.statSync(decodeURIComponent(pathname), { throwIfNoEntry: false })?.mtimeMs
|
|
521
|
+
if (!newModified) {
|
|
522
|
+
// It could happen that a file is passed in that doesn't exist. E.g.:
|
|
523
|
+
// postcss-cli will provide you a fake path when reading from stdin. This
|
|
524
|
+
// path then looks like /path-to-your-project/stdin In that case we just
|
|
525
|
+
// want to ignore it and don't track changes at all.
|
|
526
|
+
continue
|
|
527
|
+
}
|
|
439
528
|
|
|
440
529
|
if (!fileModifiedMap.has(file) || newModified > fileModifiedMap.get(file)) {
|
|
441
530
|
changed = true
|
|
@@ -112,19 +112,20 @@ function resolveChangedFiles(candidateFiles, fileModifiedMap) {
|
|
|
112
112
|
// source path), or set up a new one (including setting up watchers and registering
|
|
113
113
|
// plugins) then return it
|
|
114
114
|
export default function setupTrackingContext(configOrPath) {
|
|
115
|
-
return ({ tailwindDirectives, registerDependency }) => {
|
|
115
|
+
return ({ tailwindDirectives, registerDependency, applyDirectives }) => {
|
|
116
116
|
return (root, result) => {
|
|
117
117
|
let [tailwindConfig, userConfigPath, tailwindConfigHash, configDependencies] =
|
|
118
118
|
getTailwindConfig(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
|
|
127
|
-
|
|
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.
|
|
128
|
+
if (tailwindDirectives.size > 0 || applyDirectives.size > 0) {
|
|
128
129
|
// Add current css file as a context dependencies.
|
|
129
130
|
contextDependencies.add(result.opts.from)
|
|
130
131
|
|
|
@@ -147,12 +148,12 @@ 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.
|
|
155
|
-
if (tailwindDirectives.size > 0) {
|
|
156
|
+
if (tailwindDirectives.size > 0 || applyDirectives.size > 0) {
|
|
156
157
|
let fileModifiedMap = getFileModifiedMap(context)
|
|
157
158
|
|
|
158
159
|
// Add template paths as postcss dependencies.
|
package/src/lib/sharedState.js
CHANGED
|
@@ -1,10 +1,46 @@
|
|
|
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
|
+
|
|
9
|
+
export function resolveDebug(debug) {
|
|
10
|
+
if (debug === undefined) {
|
|
11
|
+
return false
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
// Environment variables are strings, so convert to boolean
|
|
15
|
+
if (debug === 'true' || debug === '1') {
|
|
16
|
+
return true
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
if (debug === 'false' || debug === '0') {
|
|
20
|
+
return false
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
// Keep the debug convention into account:
|
|
24
|
+
// DEBUG=* -> This enables all debug modes
|
|
25
|
+
// DEBUG=projectA,projectB,projectC -> This enables debug for projectA, projectB and projectC
|
|
26
|
+
// DEBUG=projectA:* -> This enables all debug modes for projectA (if you have sub-types)
|
|
27
|
+
// DEBUG=projectA,-projectB -> This enables debug for projectA and explicitly disables it for projectB
|
|
28
|
+
|
|
29
|
+
if (debug === '*') {
|
|
30
|
+
return true
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
let debuggers = debug.split(',').map((d) => d.split(':')[0])
|
|
34
|
+
|
|
35
|
+
// Ignoring tailwindcss
|
|
36
|
+
if (debuggers.includes('-tailwindcss')) {
|
|
37
|
+
return false
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
// Including tailwindcss
|
|
41
|
+
if (debuggers.includes('tailwindcss')) {
|
|
42
|
+
return true
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
return false
|
|
46
|
+
}
|
|
@@ -12,10 +12,13 @@ import { issueFlagNotices } from './featureFlags'
|
|
|
12
12
|
|
|
13
13
|
export default function processTailwindFeatures(setupContext) {
|
|
14
14
|
return function (root, result) {
|
|
15
|
-
let tailwindDirectives = normalizeTailwindDirectives(root)
|
|
15
|
+
let { tailwindDirectives, applyDirectives } = normalizeTailwindDirectives(root)
|
|
16
|
+
|
|
17
|
+
detectNesting()(root, result)
|
|
16
18
|
|
|
17
19
|
let context = setupContext({
|
|
18
20
|
tailwindDirectives,
|
|
21
|
+
applyDirectives,
|
|
19
22
|
registerDependency(dependency) {
|
|
20
23
|
result.messages.push({
|
|
21
24
|
plugin: 'tailwindcss',
|
|
@@ -36,7 +39,6 @@ export default function processTailwindFeatures(setupContext) {
|
|
|
36
39
|
|
|
37
40
|
issueFlagNotices(context.tailwindConfig)
|
|
38
41
|
|
|
39
|
-
detectNesting(context)(root, result)
|
|
40
42
|
expandTailwindAtRules(context)(root, result)
|
|
41
43
|
expandApplyAtRules(context)(root, result)
|
|
42
44
|
evaluateTailwindFunctions(context)(root, result)
|
package/src/util/pluginUtils.js
CHANGED
|
@@ -185,7 +185,7 @@ export function coerceValue(types, modifier, options, tailwindConfig) {
|
|
|
185
185
|
// Find first matching type
|
|
186
186
|
for (let type of [].concat(types)) {
|
|
187
187
|
let result = typeMap[type](modifier, options, { tailwindConfig })
|
|
188
|
-
if (result) return [result, type]
|
|
188
|
+
if (result !== undefined) return [result, type]
|
|
189
189
|
}
|
|
190
190
|
|
|
191
191
|
return []
|