tailwindcss 0.0.0-insiders.d72b277 → 0.0.0-insiders.d9bc25d
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 +8 -1
- package/lib/featureFlags.js +2 -2
- package/lib/lib/expandApplyAtRules.js +166 -13
- package/lib/lib/expandTailwindAtRules.js +15 -5
- package/lib/lib/resolveDefaultsAtRules.js +6 -2
- package/lib/lib/setupContextUtils.js +0 -25
- package/lib/lib/setupTrackingContext.js +3 -3
- package/lib/util/cloneNodes.js +12 -1
- package/lib/util/log.js +7 -7
- package/lib/util/parseBoxShadowValue.js +43 -3
- package/package.json +5 -4
- package/src/featureFlags.js +2 -2
- package/src/lib/expandApplyAtRules.js +179 -6
- package/src/lib/expandTailwindAtRules.js +25 -5
- package/src/lib/resolveDefaultsAtRules.js +6 -2
- package/src/lib/setupContextUtils.js +0 -20
- package/src/lib/setupTrackingContext.js +3 -3
- package/src/util/cloneNodes.js +14 -1
- package/src/util/log.js +7 -7
- package/src/util/parseBoxShadowValue.js +50 -2
package/CHANGELOG.md
CHANGED
|
@@ -10,6 +10,13 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
10
10
|
- Prevent nesting plugin from breaking other plugins ([#7563](https://github.com/tailwindlabs/tailwindcss/pull/7563))
|
|
11
11
|
- Recursively collapse adjacent rules ([#7565](https://github.com/tailwindlabs/tailwindcss/pull/7565))
|
|
12
12
|
- Allow default ring color to be a function ([#7587](https://github.com/tailwindlabs/tailwindcss/pull/7587))
|
|
13
|
+
- Preserve source maps for generated CSS ([#7588](https://github.com/tailwindlabs/tailwindcss/pull/7588))
|
|
14
|
+
- Split box shadows on top-level commas only ([#7479](https://github.com/tailwindlabs/tailwindcss/pull/7479))
|
|
15
|
+
- Use local user CSS cache for `@apply` ([#7524](https://github.com/tailwindlabs/tailwindcss/pull/7524))
|
|
16
|
+
|
|
17
|
+
### Changed
|
|
18
|
+
|
|
19
|
+
- Replace `chalk` with `picocolors` ([#6039](https://github.com/tailwindlabs/tailwindcss/pull/6039))
|
|
13
20
|
|
|
14
21
|
## [3.0.23] - 2022-02-16
|
|
15
22
|
|
|
@@ -24,7 +31,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
24
31
|
|
|
25
32
|
### Fixed
|
|
26
33
|
|
|
27
|
-
- Temporarily move postcss to dependencies ([#7424](https://github.com/tailwindlabs/tailwindcss/pull/7424))
|
|
34
|
+
- Temporarily move `postcss` to dependencies ([#7424](https://github.com/tailwindlabs/tailwindcss/pull/7424))
|
|
28
35
|
|
|
29
36
|
## [3.0.21] - 2022-02-10
|
|
30
37
|
|
package/lib/featureFlags.js
CHANGED
|
@@ -5,7 +5,7 @@ Object.defineProperty(exports, "__esModule", {
|
|
|
5
5
|
exports.flagEnabled = flagEnabled;
|
|
6
6
|
exports.issueFlagNotices = issueFlagNotices;
|
|
7
7
|
exports.default = void 0;
|
|
8
|
-
var
|
|
8
|
+
var _picocolors = _interopRequireDefault(require("picocolors"));
|
|
9
9
|
var _log = _interopRequireDefault(require("./util/log"));
|
|
10
10
|
function _interopRequireDefault(obj) {
|
|
11
11
|
return obj && obj.__esModule ? obj : {
|
|
@@ -47,7 +47,7 @@ function issueFlagNotices(config) {
|
|
|
47
47
|
return;
|
|
48
48
|
}
|
|
49
49
|
if (experimentalFlagsEnabled(config).length > 0) {
|
|
50
|
-
let changes = experimentalFlagsEnabled(config).map((s)=>
|
|
50
|
+
let changes = experimentalFlagsEnabled(config).map((s)=>_picocolors.default.yellow(s)
|
|
51
51
|
).join(', ');
|
|
52
52
|
_log.default.warn('experimental-flags-enabled', [
|
|
53
53
|
`You have enabled experimental features: ${changes}`,
|
|
@@ -13,7 +13,7 @@ function _interopRequireDefault(obj) {
|
|
|
13
13
|
default: obj
|
|
14
14
|
};
|
|
15
15
|
}
|
|
16
|
-
function extractClasses(node) {
|
|
16
|
+
/** @typedef {Map<string, [any, import('postcss').Rule[]]>} ApplyCache */ function extractClasses(node) {
|
|
17
17
|
let classes = new Set();
|
|
18
18
|
let container = _postcss.default.root({
|
|
19
19
|
nodes: [
|
|
@@ -40,7 +40,114 @@ function prefix(context, selector) {
|
|
|
40
40
|
let prefix1 = context.tailwindConfig.prefix;
|
|
41
41
|
return typeof prefix1 === 'function' ? prefix1(selector) : prefix1 + selector;
|
|
42
42
|
}
|
|
43
|
-
function
|
|
43
|
+
function* pathToRoot(node) {
|
|
44
|
+
yield node;
|
|
45
|
+
while(node.parent){
|
|
46
|
+
yield node.parent;
|
|
47
|
+
node = node.parent;
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
/**
|
|
51
|
+
* Only clone the node itself and not its children
|
|
52
|
+
*
|
|
53
|
+
* @param {*} node
|
|
54
|
+
* @param {*} overrides
|
|
55
|
+
* @returns
|
|
56
|
+
*/ function shallowClone(node, overrides = {}) {
|
|
57
|
+
let children = node.nodes;
|
|
58
|
+
node.nodes = [];
|
|
59
|
+
let tmp = node.clone(overrides);
|
|
60
|
+
node.nodes = children;
|
|
61
|
+
return tmp;
|
|
62
|
+
}
|
|
63
|
+
/**
|
|
64
|
+
* Clone just the nodes all the way to the top that are required to represent
|
|
65
|
+
* this singular rule in the tree.
|
|
66
|
+
*
|
|
67
|
+
* For example, if we have CSS like this:
|
|
68
|
+
* ```css
|
|
69
|
+
* @media (min-width: 768px) {
|
|
70
|
+
* @supports (display: grid) {
|
|
71
|
+
* .foo {
|
|
72
|
+
* display: grid;
|
|
73
|
+
* grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
|
|
74
|
+
* }
|
|
75
|
+
* }
|
|
76
|
+
*
|
|
77
|
+
* @supports (backdrop-filter: blur(1px)) {
|
|
78
|
+
* .bar {
|
|
79
|
+
* backdrop-filter: blur(1px);
|
|
80
|
+
* }
|
|
81
|
+
* }
|
|
82
|
+
*
|
|
83
|
+
* .baz {
|
|
84
|
+
* color: orange;
|
|
85
|
+
* }
|
|
86
|
+
* }
|
|
87
|
+
* ```
|
|
88
|
+
*
|
|
89
|
+
* And we're cloning `.bar` it'll return a cloned version of what's required for just that single node:
|
|
90
|
+
*
|
|
91
|
+
* ```css
|
|
92
|
+
* @media (min-width: 768px) {
|
|
93
|
+
* @supports (backdrop-filter: blur(1px)) {
|
|
94
|
+
* .bar {
|
|
95
|
+
* backdrop-filter: blur(1px);
|
|
96
|
+
* }
|
|
97
|
+
* }
|
|
98
|
+
* }
|
|
99
|
+
* ```
|
|
100
|
+
*
|
|
101
|
+
* @param {import('postcss').Node} node
|
|
102
|
+
*/ function nestedClone(node) {
|
|
103
|
+
for (let parent of pathToRoot(node)){
|
|
104
|
+
if (node === parent) {
|
|
105
|
+
continue;
|
|
106
|
+
}
|
|
107
|
+
if (parent.type === 'root') {
|
|
108
|
+
break;
|
|
109
|
+
}
|
|
110
|
+
node = shallowClone(parent, {
|
|
111
|
+
nodes: [
|
|
112
|
+
node
|
|
113
|
+
]
|
|
114
|
+
});
|
|
115
|
+
}
|
|
116
|
+
return node;
|
|
117
|
+
}
|
|
118
|
+
/**
|
|
119
|
+
* @param {import('postcss').Root} root
|
|
120
|
+
*/ function buildLocalApplyCache(root, context) {
|
|
121
|
+
/** @type {ApplyCache} */ let cache = new Map();
|
|
122
|
+
let highestOffset = context.layerOrder.user >> 4n;
|
|
123
|
+
root.walkRules((rule, idx)=>{
|
|
124
|
+
// Ignore rules generated by Tailwind
|
|
125
|
+
for (let node of pathToRoot(rule)){
|
|
126
|
+
var ref;
|
|
127
|
+
if (((ref = node.raws.tailwind) === null || ref === void 0 ? void 0 : ref.layer) !== undefined) {
|
|
128
|
+
return;
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
// Clone what's required to represent this singular rule in the tree
|
|
132
|
+
let container = nestedClone(rule);
|
|
133
|
+
for (let className of extractClasses(rule)){
|
|
134
|
+
let list = cache.get(className) || [];
|
|
135
|
+
cache.set(className, list);
|
|
136
|
+
list.push([
|
|
137
|
+
{
|
|
138
|
+
layer: 'user',
|
|
139
|
+
sort: BigInt(idx) + highestOffset,
|
|
140
|
+
important: false
|
|
141
|
+
},
|
|
142
|
+
container,
|
|
143
|
+
]);
|
|
144
|
+
}
|
|
145
|
+
});
|
|
146
|
+
return cache;
|
|
147
|
+
}
|
|
148
|
+
/**
|
|
149
|
+
* @returns {ApplyCache}
|
|
150
|
+
*/ function buildApplyCache(applyCandidates, context) {
|
|
44
151
|
for (let candidate of applyCandidates){
|
|
45
152
|
if (context.notClassCache.has(candidate) || context.applyClassCache.has(candidate)) {
|
|
46
153
|
continue;
|
|
@@ -62,6 +169,39 @@ function buildApplyCache(applyCandidates, context) {
|
|
|
62
169
|
}
|
|
63
170
|
return context.applyClassCache;
|
|
64
171
|
}
|
|
172
|
+
/**
|
|
173
|
+
* Build a cache only when it's first used
|
|
174
|
+
*
|
|
175
|
+
* @param {() => ApplyCache} buildCacheFn
|
|
176
|
+
* @returns {ApplyCache}
|
|
177
|
+
*/ function lazyCache(buildCacheFn) {
|
|
178
|
+
let cache = null;
|
|
179
|
+
return {
|
|
180
|
+
get: (name)=>{
|
|
181
|
+
cache = cache || buildCacheFn();
|
|
182
|
+
return cache.get(name);
|
|
183
|
+
},
|
|
184
|
+
has: (name)=>{
|
|
185
|
+
cache = cache || buildCacheFn();
|
|
186
|
+
return cache.has(name);
|
|
187
|
+
}
|
|
188
|
+
};
|
|
189
|
+
}
|
|
190
|
+
/**
|
|
191
|
+
* Take a series of multiple caches and merge
|
|
192
|
+
* them so they act like one large cache
|
|
193
|
+
*
|
|
194
|
+
* @param {ApplyCache[]} caches
|
|
195
|
+
* @returns {ApplyCache}
|
|
196
|
+
*/ function combineCaches(caches) {
|
|
197
|
+
return {
|
|
198
|
+
get: (name)=>caches.flatMap((cache)=>cache.get(name) || []
|
|
199
|
+
)
|
|
200
|
+
,
|
|
201
|
+
has: (name)=>caches.some((cache)=>cache.has(name)
|
|
202
|
+
)
|
|
203
|
+
};
|
|
204
|
+
}
|
|
65
205
|
function extractApplyCandidates(params) {
|
|
66
206
|
let candidates = params.split(/[\s\t\n]+/g);
|
|
67
207
|
if (candidates[candidates.length - 1] === '!important') {
|
|
@@ -75,7 +215,7 @@ function extractApplyCandidates(params) {
|
|
|
75
215
|
false
|
|
76
216
|
];
|
|
77
217
|
}
|
|
78
|
-
function processApply(root, context) {
|
|
218
|
+
function processApply(root, context, localCache) {
|
|
79
219
|
let applyCandidates = new Set();
|
|
80
220
|
// Collect all @apply rules and candidates
|
|
81
221
|
let applies = [];
|
|
@@ -89,7 +229,10 @@ function processApply(root, context) {
|
|
|
89
229
|
// Start the @apply process if we have rules with @apply in them
|
|
90
230
|
if (applies.length > 0) {
|
|
91
231
|
// Fill up some caches!
|
|
92
|
-
let applyClassCache =
|
|
232
|
+
let applyClassCache = combineCaches([
|
|
233
|
+
localCache,
|
|
234
|
+
buildApplyCache(applyCandidates, context)
|
|
235
|
+
]);
|
|
93
236
|
/**
|
|
94
237
|
* When we have an apply like this:
|
|
95
238
|
*
|
|
@@ -129,7 +272,10 @@ function processApply(root, context) {
|
|
|
129
272
|
// Collect all apply candidates and their rules
|
|
130
273
|
for (let apply of applies){
|
|
131
274
|
let candidates = perParentApplies.get(apply.parent) || [];
|
|
132
|
-
perParentApplies.set(apply.parent,
|
|
275
|
+
perParentApplies.set(apply.parent, [
|
|
276
|
+
candidates,
|
|
277
|
+
apply.source
|
|
278
|
+
]);
|
|
133
279
|
let [applyCandidates, important] = extractApplyCandidates(apply.params);
|
|
134
280
|
if (apply.parent.type === 'atrule') {
|
|
135
281
|
if (apply.parent.name === 'screen') {
|
|
@@ -158,12 +304,12 @@ function processApply(root, context) {
|
|
|
158
304
|
]);
|
|
159
305
|
}
|
|
160
306
|
}
|
|
161
|
-
for (const [parent, candidates] of perParentApplies){
|
|
307
|
+
for (const [parent, [candidates, atApplySource]] of perParentApplies){
|
|
162
308
|
let siblings = [];
|
|
163
309
|
for (let [applyCandidate, important, rules] of candidates){
|
|
164
|
-
for (let [meta,
|
|
310
|
+
for (let [meta, node1] of rules){
|
|
165
311
|
let parentClasses = extractClasses(parent);
|
|
166
|
-
let nodeClasses = extractClasses(
|
|
312
|
+
let nodeClasses = extractClasses(node1);
|
|
167
313
|
// Add base utility classes from the @apply node to the list of
|
|
168
314
|
// classes to check whether it intersects and therefore results in a
|
|
169
315
|
// circular dependency or not.
|
|
@@ -190,14 +336,18 @@ function processApply(root, context) {
|
|
|
190
336
|
let intersects = parentClasses.some((selector)=>nodeClasses.includes(selector)
|
|
191
337
|
);
|
|
192
338
|
if (intersects) {
|
|
193
|
-
throw
|
|
339
|
+
throw node1.error(`You cannot \`@apply\` the \`${applyCandidate}\` utility here because it creates a circular dependency.`);
|
|
194
340
|
}
|
|
195
341
|
let root = _postcss.default.root({
|
|
196
342
|
nodes: [
|
|
197
|
-
|
|
343
|
+
node1.clone()
|
|
198
344
|
]
|
|
199
345
|
});
|
|
200
|
-
|
|
346
|
+
// Make sure every node in the entire tree points back at the @apply rule that generated it
|
|
347
|
+
root.walk((node)=>{
|
|
348
|
+
node.source = atApplySource;
|
|
349
|
+
});
|
|
350
|
+
let canRewriteSelector = node1.type !== 'atrule' || node1.type === 'atrule' && node1.name !== 'keyframes';
|
|
201
351
|
if (canRewriteSelector) {
|
|
202
352
|
root.walkRules((rule)=>{
|
|
203
353
|
// Let's imagine you have the following structure:
|
|
@@ -270,11 +420,14 @@ function processApply(root, context) {
|
|
|
270
420
|
}
|
|
271
421
|
}
|
|
272
422
|
// Do it again, in case we have other `@apply` rules
|
|
273
|
-
processApply(root, context);
|
|
423
|
+
processApply(root, context, localCache);
|
|
274
424
|
}
|
|
275
425
|
}
|
|
276
426
|
function expandApplyAtRules(context) {
|
|
277
427
|
return (root)=>{
|
|
278
|
-
|
|
428
|
+
// Build a cache of the user's CSS so we can use it to resolve classes used by @apply
|
|
429
|
+
let localCache = lazyCache(()=>buildLocalApplyCache(root, context)
|
|
430
|
+
);
|
|
431
|
+
processApply(root, context, localCache);
|
|
279
432
|
};
|
|
280
433
|
}
|
|
@@ -191,19 +191,25 @@ function expandTailwindAtRules(context) {
|
|
|
191
191
|
layerNodes.base.before((0, _cloneNodes).default([
|
|
192
192
|
...baseNodes,
|
|
193
193
|
...defaultNodes
|
|
194
|
-
], layerNodes.base.source
|
|
194
|
+
], layerNodes.base.source, {
|
|
195
|
+
layer: 'base'
|
|
196
|
+
}));
|
|
195
197
|
layerNodes.base.remove();
|
|
196
198
|
}
|
|
197
199
|
if (layerNodes.components) {
|
|
198
200
|
layerNodes.components.before((0, _cloneNodes).default([
|
|
199
201
|
...componentNodes
|
|
200
|
-
], layerNodes.components.source
|
|
202
|
+
], layerNodes.components.source, {
|
|
203
|
+
layer: 'components'
|
|
204
|
+
}));
|
|
201
205
|
layerNodes.components.remove();
|
|
202
206
|
}
|
|
203
207
|
if (layerNodes.utilities) {
|
|
204
208
|
layerNodes.utilities.before((0, _cloneNodes).default([
|
|
205
209
|
...utilityNodes
|
|
206
|
-
], layerNodes.utilities.source
|
|
210
|
+
], layerNodes.utilities.source, {
|
|
211
|
+
layer: 'utilities'
|
|
212
|
+
}));
|
|
207
213
|
layerNodes.utilities.remove();
|
|
208
214
|
}
|
|
209
215
|
// We do post-filtering to not alter the emitted order of the variants
|
|
@@ -219,10 +225,14 @@ function expandTailwindAtRules(context) {
|
|
|
219
225
|
return true;
|
|
220
226
|
});
|
|
221
227
|
if (layerNodes.variants) {
|
|
222
|
-
layerNodes.variants.before((0, _cloneNodes).default(variantNodes, layerNodes.variants.source
|
|
228
|
+
layerNodes.variants.before((0, _cloneNodes).default(variantNodes, layerNodes.variants.source, {
|
|
229
|
+
layer: 'variants'
|
|
230
|
+
}));
|
|
223
231
|
layerNodes.variants.remove();
|
|
224
232
|
} else if (variantNodes.length > 0) {
|
|
225
|
-
root.append((0, _cloneNodes).default(variantNodes, root.source
|
|
233
|
+
root.append((0, _cloneNodes).default(variantNodes, root.source, {
|
|
234
|
+
layer: 'variants'
|
|
235
|
+
}));
|
|
226
236
|
}
|
|
227
237
|
// If we've got a utility layer and no utilities are generated there's likely something wrong
|
|
228
238
|
const hasUtilityVariants = variantNodes.some((node)=>{
|
|
@@ -115,7 +115,9 @@ function resolveDefaultsAtRules({ tailwindConfig }) {
|
|
|
115
115
|
continue;
|
|
116
116
|
}
|
|
117
117
|
for (let [, selectors] of selectorGroups){
|
|
118
|
-
let universalRule = _postcss.default.rule(
|
|
118
|
+
let universalRule = _postcss.default.rule({
|
|
119
|
+
source: universal.source
|
|
120
|
+
});
|
|
119
121
|
universalRule.selectors = [
|
|
120
122
|
...selectors
|
|
121
123
|
];
|
|
@@ -124,7 +126,9 @@ function resolveDefaultsAtRules({ tailwindConfig }) {
|
|
|
124
126
|
universal.before(universalRule);
|
|
125
127
|
}
|
|
126
128
|
} else {
|
|
127
|
-
let universalRule = _postcss.default.rule(
|
|
129
|
+
let universalRule = _postcss.default.rule({
|
|
130
|
+
source: universal.source
|
|
131
|
+
});
|
|
128
132
|
universalRule.selectors = [
|
|
129
133
|
'*',
|
|
130
134
|
'::before',
|
|
@@ -245,21 +245,6 @@ function buildPluginApi(tailwindConfig, context, { variantList , variantMap , of
|
|
|
245
245
|
// Preserved for backwards compatibility but not used in v3.0+
|
|
246
246
|
return [];
|
|
247
247
|
},
|
|
248
|
-
addUserCss (userCss) {
|
|
249
|
-
for (let [identifier, rule] of withIdentifiers(userCss)){
|
|
250
|
-
let offset = offsets.user++;
|
|
251
|
-
if (!context.candidateRuleMap.has(identifier)) {
|
|
252
|
-
context.candidateRuleMap.set(identifier, []);
|
|
253
|
-
}
|
|
254
|
-
context.candidateRuleMap.get(identifier).push([
|
|
255
|
-
{
|
|
256
|
-
sort: offset,
|
|
257
|
-
layer: 'user'
|
|
258
|
-
},
|
|
259
|
-
rule
|
|
260
|
-
]);
|
|
261
|
-
}
|
|
262
|
-
},
|
|
263
248
|
addBase (base) {
|
|
264
249
|
for (let [identifier, rule] of withIdentifiers(base)){
|
|
265
250
|
let prefixedIdentifier = prefixIdentifier(identifier, {});
|
|
@@ -533,16 +518,6 @@ function collectLayerPlugins(root) {
|
|
|
533
518
|
layerRule.remove();
|
|
534
519
|
}
|
|
535
520
|
});
|
|
536
|
-
root.walkRules((rule)=>{
|
|
537
|
-
// At this point it is safe to include all the left-over css from the
|
|
538
|
-
// user's css file. This is because the `@tailwind` and `@layer` directives
|
|
539
|
-
// will already be handled and will be removed from the css tree.
|
|
540
|
-
layerPlugins.push(function({ addUserCss }) {
|
|
541
|
-
addUserCss(rule, {
|
|
542
|
-
respectPrefix: false
|
|
543
|
-
});
|
|
544
|
-
});
|
|
545
|
-
});
|
|
546
521
|
return layerPlugins;
|
|
547
522
|
}
|
|
548
523
|
function resolvePlugins(context, root) {
|
|
@@ -119,7 +119,7 @@ function resolveChangedFiles(candidateFiles, fileModifiedMap) {
|
|
|
119
119
|
return changedFiles;
|
|
120
120
|
}
|
|
121
121
|
function setupTrackingContext(configOrPath) {
|
|
122
|
-
return ({ tailwindDirectives , registerDependency
|
|
122
|
+
return ({ tailwindDirectives , registerDependency })=>{
|
|
123
123
|
return (root, result)=>{
|
|
124
124
|
let [tailwindConfig, userConfigPath, tailwindConfigHash, configDependencies] = getTailwindConfig(configOrPath);
|
|
125
125
|
let contextDependencies = new Set(configDependencies);
|
|
@@ -129,7 +129,7 @@ function setupTrackingContext(configOrPath) {
|
|
|
129
129
|
// being part of this trigger too, but it's tough because it's impossible
|
|
130
130
|
// for a layer in one file to end up in the actual @tailwind rule in
|
|
131
131
|
// another file since independent sources are effectively isolated.
|
|
132
|
-
if (tailwindDirectives.size > 0
|
|
132
|
+
if (tailwindDirectives.size > 0) {
|
|
133
133
|
// Add current css file as a context dependencies.
|
|
134
134
|
contextDependencies.add(result.opts.from);
|
|
135
135
|
// Add all css @import dependencies as context dependencies.
|
|
@@ -146,7 +146,7 @@ function setupTrackingContext(configOrPath) {
|
|
|
146
146
|
// We may want to think about `@layer` being part of this trigger too, but it's tough
|
|
147
147
|
// because it's impossible for a layer in one file to end up in the actual @tailwind rule
|
|
148
148
|
// in another file since independent sources are effectively isolated.
|
|
149
|
-
if (tailwindDirectives.size > 0
|
|
149
|
+
if (tailwindDirectives.size > 0) {
|
|
150
150
|
let fileModifiedMap = (0, _setupContextUtils).getFileModifiedMap(context);
|
|
151
151
|
// Add template paths as postcss dependencies.
|
|
152
152
|
for (let fileOrGlob of candidateFiles){
|
package/lib/util/cloneNodes.js
CHANGED
|
@@ -3,11 +3,22 @@ Object.defineProperty(exports, "__esModule", {
|
|
|
3
3
|
value: true
|
|
4
4
|
});
|
|
5
5
|
exports.default = cloneNodes;
|
|
6
|
-
function cloneNodes(nodes, source) {
|
|
6
|
+
function cloneNodes(nodes, source = undefined, raws = undefined) {
|
|
7
7
|
return nodes.map((node)=>{
|
|
8
8
|
let cloned = node.clone();
|
|
9
9
|
if (source !== undefined) {
|
|
10
10
|
cloned.source = source;
|
|
11
|
+
if ('walk' in cloned) {
|
|
12
|
+
cloned.walk((child)=>{
|
|
13
|
+
child.source = source;
|
|
14
|
+
});
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
if (raws !== undefined) {
|
|
18
|
+
cloned.raws.tailwind = {
|
|
19
|
+
...cloned.raws.tailwind,
|
|
20
|
+
...raws
|
|
21
|
+
};
|
|
11
22
|
}
|
|
12
23
|
return cloned;
|
|
13
24
|
});
|
package/lib/util/log.js
CHANGED
|
@@ -4,27 +4,27 @@ Object.defineProperty(exports, "__esModule", {
|
|
|
4
4
|
});
|
|
5
5
|
exports.dim = dim;
|
|
6
6
|
exports.default = void 0;
|
|
7
|
-
var
|
|
7
|
+
var _picocolors = _interopRequireDefault(require("picocolors"));
|
|
8
8
|
function _interopRequireDefault(obj) {
|
|
9
9
|
return obj && obj.__esModule ? obj : {
|
|
10
10
|
default: obj
|
|
11
11
|
};
|
|
12
12
|
}
|
|
13
13
|
let alreadyShown = new Set();
|
|
14
|
-
function log(
|
|
14
|
+
function log(type, messages, key) {
|
|
15
15
|
if (process.env.JEST_WORKER_ID !== undefined) return;
|
|
16
16
|
if (key && alreadyShown.has(key)) return;
|
|
17
17
|
if (key) alreadyShown.add(key);
|
|
18
18
|
console.warn('');
|
|
19
|
-
messages.forEach((message)=>console.warn(
|
|
19
|
+
messages.forEach((message)=>console.warn(type, '-', message)
|
|
20
20
|
);
|
|
21
21
|
}
|
|
22
22
|
function dim(input) {
|
|
23
|
-
return
|
|
23
|
+
return _picocolors.default.dim(input);
|
|
24
24
|
}
|
|
25
25
|
var _default = {
|
|
26
26
|
info (key, messages) {
|
|
27
|
-
log(
|
|
27
|
+
log(_picocolors.default.bold(_picocolors.default.cyan('info')), ...Array.isArray(key) ? [
|
|
28
28
|
key
|
|
29
29
|
] : [
|
|
30
30
|
messages,
|
|
@@ -32,7 +32,7 @@ var _default = {
|
|
|
32
32
|
]);
|
|
33
33
|
},
|
|
34
34
|
warn (key, messages) {
|
|
35
|
-
log(
|
|
35
|
+
log(_picocolors.default.bold(_picocolors.default.yellow('warn')), ...Array.isArray(key) ? [
|
|
36
36
|
key
|
|
37
37
|
] : [
|
|
38
38
|
messages,
|
|
@@ -40,7 +40,7 @@ var _default = {
|
|
|
40
40
|
]);
|
|
41
41
|
},
|
|
42
42
|
risk (key, messages) {
|
|
43
|
-
log(
|
|
43
|
+
log(_picocolors.default.bold(_picocolors.default.magenta('risk')), ...Array.isArray(key) ? [
|
|
44
44
|
key
|
|
45
45
|
] : [
|
|
46
46
|
messages,
|
|
@@ -11,13 +11,53 @@ let KEYWORDS = new Set([
|
|
|
11
11
|
'revert',
|
|
12
12
|
'unset'
|
|
13
13
|
]);
|
|
14
|
-
let COMMA = /\,(?![^(]*\))/g // Comma separator that is not located between brackets. E.g.: `cubiz-bezier(a, b, c)` these don't count.
|
|
15
|
-
;
|
|
16
14
|
let SPACE = /\ +(?![^(]*\))/g // Similar to the one above, but with spaces instead.
|
|
17
15
|
;
|
|
18
16
|
let LENGTH = /^-?(\d+|\.\d+)(.*?)$/g;
|
|
17
|
+
let SPECIALS = /[(),]/g;
|
|
18
|
+
/**
|
|
19
|
+
* This splits a string on top-level commas.
|
|
20
|
+
*
|
|
21
|
+
* Regex doesn't support recursion (at least not the JS-flavored version).
|
|
22
|
+
* So we have to use a tiny state machine to keep track of paren vs comma
|
|
23
|
+
* placement. Before we'd only exclude commas from the inner-most nested
|
|
24
|
+
* set of parens rather than any commas that were not contained in parens
|
|
25
|
+
* at all which is the intended behavior here.
|
|
26
|
+
*
|
|
27
|
+
* Expected behavior:
|
|
28
|
+
* var(--a, 0 0 1px rgb(0, 0, 0)), 0 0 1px rgb(0, 0, 0)
|
|
29
|
+
* ─┬─ ┬ ┬ ┬
|
|
30
|
+
* x x x ╰──────── Split because top-level
|
|
31
|
+
* ╰──────────────┴──┴───────────── Ignored b/c inside >= 1 levels of parens
|
|
32
|
+
*
|
|
33
|
+
* @param {string} input
|
|
34
|
+
*/ function* splitByTopLevelCommas(input) {
|
|
35
|
+
SPECIALS.lastIndex = -1;
|
|
36
|
+
let depth = 0;
|
|
37
|
+
let lastIndex = 0;
|
|
38
|
+
let found = false;
|
|
39
|
+
// Find all parens & commas
|
|
40
|
+
// And only split on commas if they're top-level
|
|
41
|
+
for (let match of input.matchAll(SPECIALS)){
|
|
42
|
+
if (match[0] === '(') depth++;
|
|
43
|
+
if (match[0] === ')') depth--;
|
|
44
|
+
if (match[0] === ',' && depth === 0) {
|
|
45
|
+
found = true;
|
|
46
|
+
yield input.substring(lastIndex, match.index);
|
|
47
|
+
lastIndex = match.index + match[0].length;
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
// Provide the last segment of the string if available
|
|
51
|
+
// Otherwise the whole string since no commas were found
|
|
52
|
+
// This mirrors the behavior of string.split()
|
|
53
|
+
if (found) {
|
|
54
|
+
yield input.substring(lastIndex);
|
|
55
|
+
} else {
|
|
56
|
+
yield input;
|
|
57
|
+
}
|
|
58
|
+
}
|
|
19
59
|
function parseBoxShadowValue(input) {
|
|
20
|
-
let shadows =
|
|
60
|
+
let shadows = Array.from(splitByTopLevelCommas(input));
|
|
21
61
|
return shadows.map((shadow)=>{
|
|
22
62
|
let value = shadow.trim();
|
|
23
63
|
let result = {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "tailwindcss",
|
|
3
|
-
"version": "0.0.0-insiders.
|
|
3
|
+
"version": "0.0.0-insiders.d9bc25d",
|
|
4
4
|
"description": "A utility-first CSS framework for rapidly building custom user interfaces.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"main": "lib/index.js",
|
|
@@ -52,7 +52,8 @@
|
|
|
52
52
|
"jest-diff": "^27.5.1",
|
|
53
53
|
"prettier": "^2.5.1",
|
|
54
54
|
"prettier-plugin-tailwindcss": "^0.1.7",
|
|
55
|
-
"rimraf": "^3.0.0"
|
|
55
|
+
"rimraf": "^3.0.0",
|
|
56
|
+
"source-map-js": "^1.0.2"
|
|
56
57
|
},
|
|
57
58
|
"peerDependencies": {
|
|
58
59
|
"autoprefixer": "^10.0.2",
|
|
@@ -60,7 +61,6 @@
|
|
|
60
61
|
},
|
|
61
62
|
"dependencies": {
|
|
62
63
|
"arg": "^5.0.1",
|
|
63
|
-
"chalk": "^4.1.2",
|
|
64
64
|
"chokidar": "^3.5.3",
|
|
65
65
|
"color-name": "^1.1.4",
|
|
66
66
|
"cosmiconfig": "^7.0.1",
|
|
@@ -71,7 +71,8 @@
|
|
|
71
71
|
"glob-parent": "^6.0.2",
|
|
72
72
|
"is-glob": "^4.0.3",
|
|
73
73
|
"normalize-path": "^3.0.0",
|
|
74
|
-
"object-hash": "^
|
|
74
|
+
"object-hash": "^3.0.0",
|
|
75
|
+
"picocolors": "^1.0.0",
|
|
75
76
|
"postcss": "^8.4.6",
|
|
76
77
|
"postcss-js": "^4.0.0",
|
|
77
78
|
"postcss-load-config": "^3.1.3",
|
package/src/featureFlags.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import
|
|
1
|
+
import colors from 'picocolors'
|
|
2
2
|
import log from './util/log'
|
|
3
3
|
|
|
4
4
|
let defaults = {
|
|
@@ -41,7 +41,7 @@ export function issueFlagNotices(config) {
|
|
|
41
41
|
|
|
42
42
|
if (experimentalFlagsEnabled(config).length > 0) {
|
|
43
43
|
let changes = experimentalFlagsEnabled(config)
|
|
44
|
-
.map((s) =>
|
|
44
|
+
.map((s) => colors.yellow(s))
|
|
45
45
|
.join(', ')
|
|
46
46
|
|
|
47
47
|
log.warn('experimental-flags-enabled', [
|
|
@@ -5,6 +5,8 @@ import { resolveMatches } from './generateRules'
|
|
|
5
5
|
import bigSign from '../util/bigSign'
|
|
6
6
|
import escapeClassName from '../util/escapeClassName'
|
|
7
7
|
|
|
8
|
+
/** @typedef {Map<string, [any, import('postcss').Rule[]]>} ApplyCache */
|
|
9
|
+
|
|
8
10
|
function extractClasses(node) {
|
|
9
11
|
let classes = new Set()
|
|
10
12
|
let container = postcss.root({ nodes: [node.clone()] })
|
|
@@ -35,6 +37,131 @@ function prefix(context, selector) {
|
|
|
35
37
|
return typeof prefix === 'function' ? prefix(selector) : prefix + selector
|
|
36
38
|
}
|
|
37
39
|
|
|
40
|
+
function* pathToRoot(node) {
|
|
41
|
+
yield node
|
|
42
|
+
while (node.parent) {
|
|
43
|
+
yield node.parent
|
|
44
|
+
node = node.parent
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* Only clone the node itself and not its children
|
|
50
|
+
*
|
|
51
|
+
* @param {*} node
|
|
52
|
+
* @param {*} overrides
|
|
53
|
+
* @returns
|
|
54
|
+
*/
|
|
55
|
+
function shallowClone(node, overrides = {}) {
|
|
56
|
+
let children = node.nodes
|
|
57
|
+
node.nodes = []
|
|
58
|
+
|
|
59
|
+
let tmp = node.clone(overrides)
|
|
60
|
+
|
|
61
|
+
node.nodes = children
|
|
62
|
+
|
|
63
|
+
return tmp
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
/**
|
|
67
|
+
* Clone just the nodes all the way to the top that are required to represent
|
|
68
|
+
* this singular rule in the tree.
|
|
69
|
+
*
|
|
70
|
+
* For example, if we have CSS like this:
|
|
71
|
+
* ```css
|
|
72
|
+
* @media (min-width: 768px) {
|
|
73
|
+
* @supports (display: grid) {
|
|
74
|
+
* .foo {
|
|
75
|
+
* display: grid;
|
|
76
|
+
* grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
|
|
77
|
+
* }
|
|
78
|
+
* }
|
|
79
|
+
*
|
|
80
|
+
* @supports (backdrop-filter: blur(1px)) {
|
|
81
|
+
* .bar {
|
|
82
|
+
* backdrop-filter: blur(1px);
|
|
83
|
+
* }
|
|
84
|
+
* }
|
|
85
|
+
*
|
|
86
|
+
* .baz {
|
|
87
|
+
* color: orange;
|
|
88
|
+
* }
|
|
89
|
+
* }
|
|
90
|
+
* ```
|
|
91
|
+
*
|
|
92
|
+
* And we're cloning `.bar` it'll return a cloned version of what's required for just that single node:
|
|
93
|
+
*
|
|
94
|
+
* ```css
|
|
95
|
+
* @media (min-width: 768px) {
|
|
96
|
+
* @supports (backdrop-filter: blur(1px)) {
|
|
97
|
+
* .bar {
|
|
98
|
+
* backdrop-filter: blur(1px);
|
|
99
|
+
* }
|
|
100
|
+
* }
|
|
101
|
+
* }
|
|
102
|
+
* ```
|
|
103
|
+
*
|
|
104
|
+
* @param {import('postcss').Node} node
|
|
105
|
+
*/
|
|
106
|
+
function nestedClone(node) {
|
|
107
|
+
for (let parent of pathToRoot(node)) {
|
|
108
|
+
if (node === parent) {
|
|
109
|
+
continue
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
if (parent.type === 'root') {
|
|
113
|
+
break
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
node = shallowClone(parent, {
|
|
117
|
+
nodes: [node],
|
|
118
|
+
})
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
return node
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
/**
|
|
125
|
+
* @param {import('postcss').Root} root
|
|
126
|
+
*/
|
|
127
|
+
function buildLocalApplyCache(root, context) {
|
|
128
|
+
/** @type {ApplyCache} */
|
|
129
|
+
let cache = new Map()
|
|
130
|
+
|
|
131
|
+
let highestOffset = context.layerOrder.user >> 4n
|
|
132
|
+
|
|
133
|
+
root.walkRules((rule, idx) => {
|
|
134
|
+
// Ignore rules generated by Tailwind
|
|
135
|
+
for (let node of pathToRoot(rule)) {
|
|
136
|
+
if (node.raws.tailwind?.layer !== undefined) {
|
|
137
|
+
return
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
// Clone what's required to represent this singular rule in the tree
|
|
142
|
+
let container = nestedClone(rule)
|
|
143
|
+
|
|
144
|
+
for (let className of extractClasses(rule)) {
|
|
145
|
+
let list = cache.get(className) || []
|
|
146
|
+
cache.set(className, list)
|
|
147
|
+
|
|
148
|
+
list.push([
|
|
149
|
+
{
|
|
150
|
+
layer: 'user',
|
|
151
|
+
sort: BigInt(idx) + highestOffset,
|
|
152
|
+
important: false,
|
|
153
|
+
},
|
|
154
|
+
container,
|
|
155
|
+
])
|
|
156
|
+
}
|
|
157
|
+
})
|
|
158
|
+
|
|
159
|
+
return cache
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
/**
|
|
163
|
+
* @returns {ApplyCache}
|
|
164
|
+
*/
|
|
38
165
|
function buildApplyCache(applyCandidates, context) {
|
|
39
166
|
for (let candidate of applyCandidates) {
|
|
40
167
|
if (context.notClassCache.has(candidate) || context.applyClassCache.has(candidate)) {
|
|
@@ -62,6 +189,43 @@ function buildApplyCache(applyCandidates, context) {
|
|
|
62
189
|
return context.applyClassCache
|
|
63
190
|
}
|
|
64
191
|
|
|
192
|
+
/**
|
|
193
|
+
* Build a cache only when it's first used
|
|
194
|
+
*
|
|
195
|
+
* @param {() => ApplyCache} buildCacheFn
|
|
196
|
+
* @returns {ApplyCache}
|
|
197
|
+
*/
|
|
198
|
+
function lazyCache(buildCacheFn) {
|
|
199
|
+
let cache = null
|
|
200
|
+
|
|
201
|
+
return {
|
|
202
|
+
get: (name) => {
|
|
203
|
+
cache = cache || buildCacheFn()
|
|
204
|
+
|
|
205
|
+
return cache.get(name)
|
|
206
|
+
},
|
|
207
|
+
has: (name) => {
|
|
208
|
+
cache = cache || buildCacheFn()
|
|
209
|
+
|
|
210
|
+
return cache.has(name)
|
|
211
|
+
},
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
/**
|
|
216
|
+
* Take a series of multiple caches and merge
|
|
217
|
+
* them so they act like one large cache
|
|
218
|
+
*
|
|
219
|
+
* @param {ApplyCache[]} caches
|
|
220
|
+
* @returns {ApplyCache}
|
|
221
|
+
*/
|
|
222
|
+
function combineCaches(caches) {
|
|
223
|
+
return {
|
|
224
|
+
get: (name) => caches.flatMap((cache) => cache.get(name) || []),
|
|
225
|
+
has: (name) => caches.some((cache) => cache.has(name)),
|
|
226
|
+
}
|
|
227
|
+
}
|
|
228
|
+
|
|
65
229
|
function extractApplyCandidates(params) {
|
|
66
230
|
let candidates = params.split(/[\s\t\n]+/g)
|
|
67
231
|
|
|
@@ -72,7 +236,7 @@ function extractApplyCandidates(params) {
|
|
|
72
236
|
return [candidates, false]
|
|
73
237
|
}
|
|
74
238
|
|
|
75
|
-
function processApply(root, context) {
|
|
239
|
+
function processApply(root, context, localCache) {
|
|
76
240
|
let applyCandidates = new Set()
|
|
77
241
|
|
|
78
242
|
// Collect all @apply rules and candidates
|
|
@@ -90,7 +254,7 @@ function processApply(root, context) {
|
|
|
90
254
|
// Start the @apply process if we have rules with @apply in them
|
|
91
255
|
if (applies.length > 0) {
|
|
92
256
|
// Fill up some caches!
|
|
93
|
-
let applyClassCache = buildApplyCache(applyCandidates, context)
|
|
257
|
+
let applyClassCache = combineCaches([localCache, buildApplyCache(applyCandidates, context)])
|
|
94
258
|
|
|
95
259
|
/**
|
|
96
260
|
* When we have an apply like this:
|
|
@@ -140,7 +304,7 @@ function processApply(root, context) {
|
|
|
140
304
|
for (let apply of applies) {
|
|
141
305
|
let candidates = perParentApplies.get(apply.parent) || []
|
|
142
306
|
|
|
143
|
-
perParentApplies.set(apply.parent, candidates)
|
|
307
|
+
perParentApplies.set(apply.parent, [candidates, apply.source])
|
|
144
308
|
|
|
145
309
|
let [applyCandidates, important] = extractApplyCandidates(apply.params)
|
|
146
310
|
|
|
@@ -178,7 +342,7 @@ function processApply(root, context) {
|
|
|
178
342
|
}
|
|
179
343
|
}
|
|
180
344
|
|
|
181
|
-
for (const [parent, candidates] of perParentApplies) {
|
|
345
|
+
for (const [parent, [candidates, atApplySource]] of perParentApplies) {
|
|
182
346
|
let siblings = []
|
|
183
347
|
|
|
184
348
|
for (let [applyCandidate, important, rules] of candidates) {
|
|
@@ -220,6 +384,12 @@ function processApply(root, context) {
|
|
|
220
384
|
}
|
|
221
385
|
|
|
222
386
|
let root = postcss.root({ nodes: [node.clone()] })
|
|
387
|
+
|
|
388
|
+
// Make sure every node in the entire tree points back at the @apply rule that generated it
|
|
389
|
+
root.walk((node) => {
|
|
390
|
+
node.source = atApplySource
|
|
391
|
+
})
|
|
392
|
+
|
|
223
393
|
let canRewriteSelector =
|
|
224
394
|
node.type !== 'atrule' || (node.type === 'atrule' && node.name !== 'keyframes')
|
|
225
395
|
|
|
@@ -296,12 +466,15 @@ function processApply(root, context) {
|
|
|
296
466
|
}
|
|
297
467
|
|
|
298
468
|
// Do it again, in case we have other `@apply` rules
|
|
299
|
-
processApply(root, context)
|
|
469
|
+
processApply(root, context, localCache)
|
|
300
470
|
}
|
|
301
471
|
}
|
|
302
472
|
|
|
303
473
|
export default function expandApplyAtRules(context) {
|
|
304
474
|
return (root) => {
|
|
305
|
-
|
|
475
|
+
// Build a cache of the user's CSS so we can use it to resolve classes used by @apply
|
|
476
|
+
let localCache = lazyCache(() => buildLocalApplyCache(root, context))
|
|
477
|
+
|
|
478
|
+
processApply(root, context, localCache)
|
|
306
479
|
}
|
|
307
480
|
}
|
|
@@ -204,17 +204,29 @@ export default function expandTailwindAtRules(context) {
|
|
|
204
204
|
// Replace any Tailwind directives with generated CSS
|
|
205
205
|
|
|
206
206
|
if (layerNodes.base) {
|
|
207
|
-
layerNodes.base.before(
|
|
207
|
+
layerNodes.base.before(
|
|
208
|
+
cloneNodes([...baseNodes, ...defaultNodes], layerNodes.base.source, {
|
|
209
|
+
layer: 'base',
|
|
210
|
+
})
|
|
211
|
+
)
|
|
208
212
|
layerNodes.base.remove()
|
|
209
213
|
}
|
|
210
214
|
|
|
211
215
|
if (layerNodes.components) {
|
|
212
|
-
layerNodes.components.before(
|
|
216
|
+
layerNodes.components.before(
|
|
217
|
+
cloneNodes([...componentNodes], layerNodes.components.source, {
|
|
218
|
+
layer: 'components',
|
|
219
|
+
})
|
|
220
|
+
)
|
|
213
221
|
layerNodes.components.remove()
|
|
214
222
|
}
|
|
215
223
|
|
|
216
224
|
if (layerNodes.utilities) {
|
|
217
|
-
layerNodes.utilities.before(
|
|
225
|
+
layerNodes.utilities.before(
|
|
226
|
+
cloneNodes([...utilityNodes], layerNodes.utilities.source, {
|
|
227
|
+
layer: 'utilities',
|
|
228
|
+
})
|
|
229
|
+
)
|
|
218
230
|
layerNodes.utilities.remove()
|
|
219
231
|
}
|
|
220
232
|
|
|
@@ -234,10 +246,18 @@ export default function expandTailwindAtRules(context) {
|
|
|
234
246
|
})
|
|
235
247
|
|
|
236
248
|
if (layerNodes.variants) {
|
|
237
|
-
layerNodes.variants.before(
|
|
249
|
+
layerNodes.variants.before(
|
|
250
|
+
cloneNodes(variantNodes, layerNodes.variants.source, {
|
|
251
|
+
layer: 'variants',
|
|
252
|
+
})
|
|
253
|
+
)
|
|
238
254
|
layerNodes.variants.remove()
|
|
239
255
|
} else if (variantNodes.length > 0) {
|
|
240
|
-
root.append(
|
|
256
|
+
root.append(
|
|
257
|
+
cloneNodes(variantNodes, root.source, {
|
|
258
|
+
layer: 'variants',
|
|
259
|
+
})
|
|
260
|
+
)
|
|
241
261
|
}
|
|
242
262
|
|
|
243
263
|
// If we've got a utility layer and no utilities are generated there's likely something wrong
|
|
@@ -120,7 +120,9 @@ export default function resolveDefaultsAtRules({ tailwindConfig }) {
|
|
|
120
120
|
}
|
|
121
121
|
|
|
122
122
|
for (let [, selectors] of selectorGroups) {
|
|
123
|
-
let universalRule = postcss.rule(
|
|
123
|
+
let universalRule = postcss.rule({
|
|
124
|
+
source: universal.source,
|
|
125
|
+
})
|
|
124
126
|
|
|
125
127
|
universalRule.selectors = [...selectors]
|
|
126
128
|
|
|
@@ -128,7 +130,9 @@ export default function resolveDefaultsAtRules({ tailwindConfig }) {
|
|
|
128
130
|
universal.before(universalRule)
|
|
129
131
|
}
|
|
130
132
|
} else {
|
|
131
|
-
let universalRule = postcss.rule(
|
|
133
|
+
let universalRule = postcss.rule({
|
|
134
|
+
source: universal.source,
|
|
135
|
+
})
|
|
132
136
|
|
|
133
137
|
universalRule.selectors = ['*', '::before', '::after']
|
|
134
138
|
|
|
@@ -230,17 +230,6 @@ function buildPluginApi(tailwindConfig, context, { variantList, variantMap, offs
|
|
|
230
230
|
// Preserved for backwards compatibility but not used in v3.0+
|
|
231
231
|
return []
|
|
232
232
|
},
|
|
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
233
|
addBase(base) {
|
|
245
234
|
for (let [identifier, rule] of withIdentifiers(base)) {
|
|
246
235
|
let prefixedIdentifier = prefixIdentifier(identifier, {})
|
|
@@ -521,15 +510,6 @@ function collectLayerPlugins(root) {
|
|
|
521
510
|
}
|
|
522
511
|
})
|
|
523
512
|
|
|
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
513
|
return layerPlugins
|
|
534
514
|
}
|
|
535
515
|
|
|
@@ -112,7 +112,7 @@ 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 }) => {
|
|
116
116
|
return (root, result) => {
|
|
117
117
|
let [tailwindConfig, userConfigPath, tailwindConfigHash, configDependencies] =
|
|
118
118
|
getTailwindConfig(configOrPath)
|
|
@@ -125,7 +125,7 @@ export default function setupTrackingContext(configOrPath) {
|
|
|
125
125
|
// being part of this trigger too, but it's tough because it's impossible
|
|
126
126
|
// for a layer in one file to end up in the actual @tailwind rule in
|
|
127
127
|
// another file since independent sources are effectively isolated.
|
|
128
|
-
if (tailwindDirectives.size > 0
|
|
128
|
+
if (tailwindDirectives.size > 0) {
|
|
129
129
|
// Add current css file as a context dependencies.
|
|
130
130
|
contextDependencies.add(result.opts.from)
|
|
131
131
|
|
|
@@ -153,7 +153,7 @@ export default function setupTrackingContext(configOrPath) {
|
|
|
153
153
|
// We may want to think about `@layer` being part of this trigger too, but it's tough
|
|
154
154
|
// because it's impossible for a layer in one file to end up in the actual @tailwind rule
|
|
155
155
|
// in another file since independent sources are effectively isolated.
|
|
156
|
-
if (tailwindDirectives.size > 0
|
|
156
|
+
if (tailwindDirectives.size > 0) {
|
|
157
157
|
let fileModifiedMap = getFileModifiedMap(context)
|
|
158
158
|
|
|
159
159
|
// Add template paths as postcss dependencies.
|
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/log.js
CHANGED
|
@@ -1,29 +1,29 @@
|
|
|
1
|
-
import
|
|
1
|
+
import colors from 'picocolors'
|
|
2
2
|
|
|
3
3
|
let alreadyShown = new Set()
|
|
4
4
|
|
|
5
|
-
function log(
|
|
5
|
+
function log(type, messages, key) {
|
|
6
6
|
if (process.env.JEST_WORKER_ID !== undefined) return
|
|
7
7
|
|
|
8
8
|
if (key && alreadyShown.has(key)) return
|
|
9
9
|
if (key) alreadyShown.add(key)
|
|
10
10
|
|
|
11
11
|
console.warn('')
|
|
12
|
-
messages.forEach((message) => console.warn(
|
|
12
|
+
messages.forEach((message) => console.warn(type, '-', message))
|
|
13
13
|
}
|
|
14
14
|
|
|
15
15
|
export function dim(input) {
|
|
16
|
-
return
|
|
16
|
+
return colors.dim(input)
|
|
17
17
|
}
|
|
18
18
|
|
|
19
19
|
export default {
|
|
20
20
|
info(key, messages) {
|
|
21
|
-
log(
|
|
21
|
+
log(colors.bold(colors.cyan('info')), ...(Array.isArray(key) ? [key] : [messages, key]))
|
|
22
22
|
},
|
|
23
23
|
warn(key, messages) {
|
|
24
|
-
log(
|
|
24
|
+
log(colors.bold(colors.yellow('warn')), ...(Array.isArray(key) ? [key] : [messages, key]))
|
|
25
25
|
},
|
|
26
26
|
risk(key, messages) {
|
|
27
|
-
log(
|
|
27
|
+
log(colors.bold(colors.magenta('risk')), ...(Array.isArray(key) ? [key] : [messages, key]))
|
|
28
28
|
},
|
|
29
29
|
}
|
|
@@ -1,10 +1,58 @@
|
|
|
1
1
|
let KEYWORDS = new Set(['inset', 'inherit', 'initial', 'revert', 'unset'])
|
|
2
|
-
let COMMA = /\,(?![^(]*\))/g // Comma separator that is not located between brackets. E.g.: `cubiz-bezier(a, b, c)` these don't count.
|
|
3
2
|
let SPACE = /\ +(?![^(]*\))/g // Similar to the one above, but with spaces instead.
|
|
4
3
|
let LENGTH = /^-?(\d+|\.\d+)(.*?)$/g
|
|
5
4
|
|
|
5
|
+
let SPECIALS = /[(),]/g
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* This splits a string on top-level commas.
|
|
9
|
+
*
|
|
10
|
+
* Regex doesn't support recursion (at least not the JS-flavored version).
|
|
11
|
+
* So we have to use a tiny state machine to keep track of paren vs comma
|
|
12
|
+
* placement. Before we'd only exclude commas from the inner-most nested
|
|
13
|
+
* set of parens rather than any commas that were not contained in parens
|
|
14
|
+
* at all which is the intended behavior here.
|
|
15
|
+
*
|
|
16
|
+
* Expected behavior:
|
|
17
|
+
* var(--a, 0 0 1px rgb(0, 0, 0)), 0 0 1px rgb(0, 0, 0)
|
|
18
|
+
* ─┬─ ┬ ┬ ┬
|
|
19
|
+
* x x x ╰──────── Split because top-level
|
|
20
|
+
* ╰──────────────┴──┴───────────── Ignored b/c inside >= 1 levels of parens
|
|
21
|
+
*
|
|
22
|
+
* @param {string} input
|
|
23
|
+
*/
|
|
24
|
+
function* splitByTopLevelCommas(input) {
|
|
25
|
+
SPECIALS.lastIndex = -1
|
|
26
|
+
|
|
27
|
+
let depth = 0
|
|
28
|
+
let lastIndex = 0
|
|
29
|
+
let found = false
|
|
30
|
+
|
|
31
|
+
// Find all parens & commas
|
|
32
|
+
// And only split on commas if they're top-level
|
|
33
|
+
for (let match of input.matchAll(SPECIALS)) {
|
|
34
|
+
if (match[0] === '(') depth++
|
|
35
|
+
if (match[0] === ')') depth--
|
|
36
|
+
if (match[0] === ',' && depth === 0) {
|
|
37
|
+
found = true
|
|
38
|
+
|
|
39
|
+
yield input.substring(lastIndex, match.index)
|
|
40
|
+
lastIndex = match.index + match[0].length
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
// Provide the last segment of the string if available
|
|
45
|
+
// Otherwise the whole string since no commas were found
|
|
46
|
+
// This mirrors the behavior of string.split()
|
|
47
|
+
if (found) {
|
|
48
|
+
yield input.substring(lastIndex)
|
|
49
|
+
} else {
|
|
50
|
+
yield input
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
|
|
6
54
|
export function parseBoxShadowValue(input) {
|
|
7
|
-
let shadows =
|
|
55
|
+
let shadows = Array.from(splitByTopLevelCommas(input))
|
|
8
56
|
return shadows.map((shadow) => {
|
|
9
57
|
let value = shadow.trim()
|
|
10
58
|
let result = { raw: value }
|