postcss-merge-rules 8.0.3 → 8.0.4
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/package.json +6 -4
- package/src/data/propertyGroups.json +1782 -0
- package/src/index.js +160 -139
- package/src/lib/propertyRelations.js +171 -0
- package/types/index.d.ts.map +1 -1
- package/types/lib/propertyRelations.d.ts +17 -0
- package/types/lib/propertyRelations.d.ts.map +1 -0
package/src/index.js
CHANGED
|
@@ -10,6 +10,7 @@ const {
|
|
|
10
10
|
sameVendor,
|
|
11
11
|
noVendor,
|
|
12
12
|
} = require('./lib/ensureCompatibility');
|
|
13
|
+
const { isConflictingProp } = require('./lib/propertyRelations.js');
|
|
13
14
|
/** @import {Declaration, Rule} from 'postcss' */
|
|
14
15
|
/**
|
|
15
16
|
* @param {Declaration} a
|
|
@@ -105,10 +106,7 @@ function canMerge(
|
|
|
105
106
|
return false;
|
|
106
107
|
}
|
|
107
108
|
|
|
108
|
-
const parent = sameParent(
|
|
109
|
-
/** @type {any} */ (ruleA),
|
|
110
|
-
/** @type {any} */ (ruleB)
|
|
111
|
-
);
|
|
109
|
+
const parent = sameParent(ruleA, ruleB);
|
|
112
110
|
if (
|
|
113
111
|
parent &&
|
|
114
112
|
ruleA.parent &&
|
|
@@ -200,83 +198,6 @@ function ruleLength(...rules) {
|
|
|
200
198
|
return rules.map((r) => (r.nodes.length ? String(r) : '')).join('').length;
|
|
201
199
|
}
|
|
202
200
|
|
|
203
|
-
/**
|
|
204
|
-
* @param {string} prop
|
|
205
|
-
* @return {{prefix: string?, base:string?, rest:string[]}}
|
|
206
|
-
*/
|
|
207
|
-
function splitProp(prop) {
|
|
208
|
-
// Treat vendor prefixed properties as if they were unprefixed;
|
|
209
|
-
// moving them when combined with non-prefixed properties can
|
|
210
|
-
// cause issues. e.g. moving -webkit-background-clip when there
|
|
211
|
-
// is a background shorthand definition.
|
|
212
|
-
|
|
213
|
-
const parts = prop.split('-');
|
|
214
|
-
if (prop[0] !== '-') {
|
|
215
|
-
return {
|
|
216
|
-
prefix: '',
|
|
217
|
-
base: parts[0],
|
|
218
|
-
rest: parts.slice(1),
|
|
219
|
-
};
|
|
220
|
-
}
|
|
221
|
-
// Don't split css variables
|
|
222
|
-
if (prop[1] === '-') {
|
|
223
|
-
return {
|
|
224
|
-
prefix: null,
|
|
225
|
-
base: null,
|
|
226
|
-
rest: [prop],
|
|
227
|
-
};
|
|
228
|
-
}
|
|
229
|
-
// Found prefix
|
|
230
|
-
return {
|
|
231
|
-
prefix: parts[1],
|
|
232
|
-
base: parts[2],
|
|
233
|
-
rest: parts.slice(3),
|
|
234
|
-
};
|
|
235
|
-
}
|
|
236
|
-
|
|
237
|
-
/**
|
|
238
|
-
* @param {string} propA
|
|
239
|
-
* @param {string} propB
|
|
240
|
-
* @return {boolean}
|
|
241
|
-
*/
|
|
242
|
-
function isConflictingProp(propA, propB) {
|
|
243
|
-
if (propA === propB) {
|
|
244
|
-
// Same specificity
|
|
245
|
-
return true;
|
|
246
|
-
}
|
|
247
|
-
const a = splitProp(propA);
|
|
248
|
-
const b = splitProp(propB);
|
|
249
|
-
// Don't resort css variables
|
|
250
|
-
if (!a.base && !b.base) {
|
|
251
|
-
return true;
|
|
252
|
-
}
|
|
253
|
-
|
|
254
|
-
// Different base and none is `place`;
|
|
255
|
-
if (a.base !== b.base && a.base !== 'place' && b.base !== 'place') {
|
|
256
|
-
return false;
|
|
257
|
-
}
|
|
258
|
-
|
|
259
|
-
// Conflict if rest-count mismatches
|
|
260
|
-
if (a.rest.length !== b.rest.length) {
|
|
261
|
-
return true;
|
|
262
|
-
}
|
|
263
|
-
|
|
264
|
-
/* Do not merge conflicting border properties */
|
|
265
|
-
if (a.base === 'border') {
|
|
266
|
-
const allRestProps = new Set([...a.rest, ...b.rest]);
|
|
267
|
-
if (
|
|
268
|
-
allRestProps.has('image') ||
|
|
269
|
-
allRestProps.has('width') ||
|
|
270
|
-
allRestProps.has('color') ||
|
|
271
|
-
allRestProps.has('style')
|
|
272
|
-
) {
|
|
273
|
-
return true;
|
|
274
|
-
}
|
|
275
|
-
}
|
|
276
|
-
// Conflict if rest parameters are equal (same but unprefixed)
|
|
277
|
-
return a.rest.every((s, index) => b.rest[index] === s);
|
|
278
|
-
}
|
|
279
|
-
|
|
280
201
|
/**
|
|
281
202
|
* @param {Rule} first
|
|
282
203
|
* @param {Rule} second
|
|
@@ -308,11 +229,12 @@ function getNextRule(second) {
|
|
|
308
229
|
let nextRule = second.next();
|
|
309
230
|
if (!nextRule) {
|
|
310
231
|
// Grab next cousin
|
|
311
|
-
/** @type {any} */
|
|
312
232
|
const parentSibling =
|
|
313
|
-
/** @type {import('postcss').Container
|
|
314
|
-
|
|
315
|
-
|
|
233
|
+
/** @type {import('postcss').Container | undefined} */ (
|
|
234
|
+
/** @type {import('postcss').Container<import('postcss').ChildNode>} */ (
|
|
235
|
+
second.parent
|
|
236
|
+
).next()
|
|
237
|
+
);
|
|
316
238
|
nextRule = parentSibling && parentSibling.nodes && parentSibling.nodes[0];
|
|
317
239
|
}
|
|
318
240
|
return nextRule?.type === 'rule' ? nextRule : null;
|
|
@@ -365,66 +287,145 @@ function mergeWithNextRule(
|
|
|
365
287
|
}
|
|
366
288
|
|
|
367
289
|
/**
|
|
368
|
-
*
|
|
369
|
-
*
|
|
370
|
-
*
|
|
371
|
-
*
|
|
290
|
+
* Returns true if hoisting `candidate` cannot reverse the cascade: every
|
|
291
|
+
* declaration that might override it is itself being
|
|
292
|
+
* hoisted, in the same relative order, so declarations move together.
|
|
293
|
+
*
|
|
294
|
+
* @param {Declaration} candidate
|
|
295
|
+
* @param {number} candidateIndex
|
|
296
|
+
* @param {Declaration[]} hoistCandidates
|
|
297
|
+
* @param {Declaration[]} earlierRuleDeclarations
|
|
298
|
+
* @return {boolean}
|
|
372
299
|
*/
|
|
373
|
-
function
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
);
|
|
392
|
-
|
|
300
|
+
function hoistingPreservesOverrideOrder(
|
|
301
|
+
candidate,
|
|
302
|
+
candidateIndex,
|
|
303
|
+
hoistCandidates,
|
|
304
|
+
earlierRuleDeclarations
|
|
305
|
+
) {
|
|
306
|
+
const indexInEarlierRule = indexOfDeclaration(
|
|
307
|
+
earlierRuleDeclarations,
|
|
308
|
+
candidate
|
|
309
|
+
);
|
|
310
|
+
const overridesInEarlierRule = earlierRuleDeclarations
|
|
311
|
+
.slice(indexInEarlierRule + 1)
|
|
312
|
+
.filter((d) => isConflictingProp(d.prop, candidate.prop));
|
|
313
|
+
if (overridesInEarlierRule.length === 0) {
|
|
314
|
+
return true;
|
|
315
|
+
}
|
|
316
|
+
const overridesAmongCandidates = hoistCandidates
|
|
317
|
+
.slice(candidateIndex + 1)
|
|
318
|
+
.filter((d) => isConflictingProp(d.prop, candidate.prop));
|
|
319
|
+
if (overridesInEarlierRule.length !== overridesAmongCandidates.length) {
|
|
320
|
+
return false;
|
|
321
|
+
}
|
|
322
|
+
return overridesInEarlierRule.every((d, index) =>
|
|
323
|
+
declarationIsEqual(d, overridesAmongCandidates[index])
|
|
324
|
+
);
|
|
325
|
+
}
|
|
326
|
+
|
|
327
|
+
/**
|
|
328
|
+
* True if the later rule has an unclaimed declaration equal to `candidate`.
|
|
329
|
+
* Records its index in `claimedIndices`, so no other candidate can match the
|
|
330
|
+
* same declaration. Always false when the later rule contains `all`,
|
|
331
|
+
* since `all` resets everything except `direction`/`unicode-bidi`.
|
|
332
|
+
*
|
|
333
|
+
* @param {Declaration} candidate
|
|
334
|
+
* @param {Declaration[]} laterDeclarations
|
|
335
|
+
* @param {Set<number>} claimedIndices
|
|
336
|
+
* @return {boolean}
|
|
337
|
+
*/
|
|
338
|
+
function claimMatchInLaterRule(candidate, laterDeclarations, claimedIndices) {
|
|
339
|
+
const matchIndex = laterDeclarations.findIndex(
|
|
340
|
+
(d, index) =>
|
|
341
|
+
!claimedIndices.has(index) && isConflictingProp(d.prop, candidate.prop)
|
|
342
|
+
);
|
|
343
|
+
if (matchIndex === -1) {
|
|
344
|
+
return false;
|
|
345
|
+
}
|
|
346
|
+
if (!declarationIsEqual(laterDeclarations[matchIndex], candidate)) {
|
|
347
|
+
return false;
|
|
348
|
+
}
|
|
349
|
+
if (
|
|
350
|
+
candidate.prop.toLowerCase() !== 'direction' &&
|
|
351
|
+
candidate.prop.toLowerCase() !== 'unicode-bidi' &&
|
|
352
|
+
laterDeclarations.some(
|
|
353
|
+
(declaration) => declaration.prop.toLowerCase() === 'all'
|
|
354
|
+
)
|
|
355
|
+
) {
|
|
356
|
+
return false;
|
|
357
|
+
}
|
|
358
|
+
claimedIndices.add(matchIndex);
|
|
359
|
+
return true;
|
|
360
|
+
}
|
|
393
361
|
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
362
|
+
/**
|
|
363
|
+
* Narrows the declarations shared by two adjacent rules down to those that can
|
|
364
|
+
* safely move into a merged rule, along with the indices of the later rule's
|
|
365
|
+
* declarations they claim.
|
|
366
|
+
*
|
|
367
|
+
* The merged rule is emitted after the earlier rule's remaining declarations,
|
|
368
|
+
* so hoisting a declaration that a later declaration in the earlier rule
|
|
369
|
+
* overrides would reverse the cascade and make the previously dead value
|
|
370
|
+
* effective again, unless that overriding declaration is hoisted too, so both
|
|
371
|
+
* move together and keep their relative order.
|
|
372
|
+
*
|
|
373
|
+
* Dropping a candidate can invalidate one that was only admitted because it
|
|
374
|
+
* travelled with it, so the pass repeats until the surviving set stops
|
|
375
|
+
* shrinking.
|
|
376
|
+
*
|
|
377
|
+
* @param {Declaration[]} hoistCandidates
|
|
378
|
+
* @param {Declaration[]} earlierRuleDeclarations
|
|
379
|
+
* @param {Declaration[]} laterRuleDeclarations
|
|
380
|
+
* @return {{intersection: Declaration[], claimedIndices: Set<number>}}
|
|
381
|
+
*/
|
|
382
|
+
function filterRuleIntersections(
|
|
383
|
+
hoistCandidates,
|
|
384
|
+
earlierRuleDeclarations,
|
|
385
|
+
laterRuleDeclarations
|
|
386
|
+
) {
|
|
387
|
+
let remainingCandidates = hoistCandidates;
|
|
388
|
+
for (;;) {
|
|
389
|
+
// A candidate rejected by the override check never claims a match.
|
|
390
|
+
const claimedIndices = new Set();
|
|
391
|
+
const survivors = remainingCandidates.filter(
|
|
392
|
+
(candidate, candidateIndex) =>
|
|
393
|
+
hoistingPreservesOverrideOrder(
|
|
394
|
+
candidate,
|
|
395
|
+
candidateIndex,
|
|
396
|
+
remainingCandidates,
|
|
397
|
+
earlierRuleDeclarations
|
|
398
|
+
) &&
|
|
399
|
+
claimMatchInLaterRule(candidate, laterRuleDeclarations, claimedIndices)
|
|
398
400
|
);
|
|
399
|
-
if (nextConflictIndex === -1) {
|
|
400
|
-
return false;
|
|
401
|
-
}
|
|
402
|
-
if (!declarationIsEqual(secondDecls[nextConflictIndex], decl)) {
|
|
403
|
-
return false;
|
|
404
|
-
}
|
|
405
401
|
if (
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
secondDecls.some(
|
|
409
|
-
(declaration) => declaration.prop.toLowerCase() === 'all'
|
|
410
|
-
)
|
|
402
|
+
survivors.length === remainingCandidates.length ||
|
|
403
|
+
survivors.length === 0
|
|
411
404
|
) {
|
|
412
|
-
return
|
|
405
|
+
return { intersection: survivors, claimedIndices };
|
|
413
406
|
}
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
});
|
|
407
|
+
remainingCandidates = survivors;
|
|
408
|
+
}
|
|
417
409
|
}
|
|
418
410
|
|
|
419
411
|
/**
|
|
420
412
|
* @param {Rule} first
|
|
421
413
|
* @param {Rule} second
|
|
422
414
|
* @param {Declaration[]} intersection
|
|
415
|
+
* @param {Set<number>} claimedIndices Positions of the declarations the
|
|
416
|
+
* intersection claimed in `second`, which the merged rule replaces.
|
|
423
417
|
* @param {WeakSet<Rule>} ruleCache
|
|
424
418
|
* @param {WeakMap<Rule, RuleMeta>} ruleMeta
|
|
425
419
|
* @return {Rule}
|
|
426
420
|
*/
|
|
427
|
-
function buildMergedRule(
|
|
421
|
+
function buildMergedRule(
|
|
422
|
+
first,
|
|
423
|
+
second,
|
|
424
|
+
intersection,
|
|
425
|
+
claimedIndices,
|
|
426
|
+
ruleCache,
|
|
427
|
+
ruleMeta
|
|
428
|
+
) {
|
|
428
429
|
const receivingBlock = second.clone();
|
|
429
430
|
const firstSelectors = getMeta(first, ruleMeta).selectors;
|
|
430
431
|
const secondSelectors = getMeta(second, ruleMeta).selectors;
|
|
@@ -462,7 +463,15 @@ function buildMergedRule(first, second, intersection, ruleCache, ruleMeta) {
|
|
|
462
463
|
}
|
|
463
464
|
)
|
|
464
465
|
);
|
|
465
|
-
|
|
466
|
+
// Remove exactly the declarations that were claimed: matching by value would
|
|
467
|
+
// also delete a duplicate that re-asserts the value later in the rule, after
|
|
468
|
+
// an overriding declaration that stays behind.
|
|
469
|
+
let laterIndex = 0;
|
|
470
|
+
secondClone.walkDecls((decl) => {
|
|
471
|
+
if (claimedIndices.has(laterIndex++)) {
|
|
472
|
+
decl.remove();
|
|
473
|
+
}
|
|
474
|
+
});
|
|
466
475
|
|
|
467
476
|
// Ensure original rules are flushed for accurate length comparison
|
|
468
477
|
if (ruleMeta) {
|
|
@@ -530,23 +539,35 @@ function partialMerge(
|
|
|
530
539
|
ruleCache,
|
|
531
540
|
ruleMeta
|
|
532
541
|
);
|
|
533
|
-
|
|
534
|
-
|
|
542
|
+
const mergedFirst = mergedNext.first;
|
|
543
|
+
const mergedSecond = mergedNext.second;
|
|
535
544
|
intersection = mergedNext.intersection;
|
|
536
545
|
|
|
537
|
-
const metaFirstActual = getMeta(
|
|
538
|
-
const metaSecondActual = getMeta(
|
|
539
|
-
const
|
|
540
|
-
const
|
|
546
|
+
const metaFirstActual = getMeta(mergedFirst, ruleMeta);
|
|
547
|
+
const metaSecondActual = getMeta(mergedSecond, ruleMeta);
|
|
548
|
+
const earlierRuleDeclarations = [...metaFirstActual.declarations];
|
|
549
|
+
const laterRuleDeclarations = [...metaSecondActual.declarations];
|
|
541
550
|
|
|
542
|
-
|
|
551
|
+
const filtered = filterRuleIntersections(
|
|
552
|
+
intersection,
|
|
553
|
+
earlierRuleDeclarations,
|
|
554
|
+
laterRuleDeclarations
|
|
555
|
+
);
|
|
556
|
+
intersection = filtered.intersection;
|
|
543
557
|
|
|
544
558
|
if (intersection.length === 0) {
|
|
545
559
|
// Nothing to merge
|
|
546
|
-
return
|
|
560
|
+
return mergedSecond;
|
|
547
561
|
}
|
|
548
562
|
|
|
549
|
-
return buildMergedRule(
|
|
563
|
+
return buildMergedRule(
|
|
564
|
+
mergedFirst,
|
|
565
|
+
mergedSecond,
|
|
566
|
+
intersection,
|
|
567
|
+
filtered.claimedIndices,
|
|
568
|
+
ruleCache,
|
|
569
|
+
ruleMeta
|
|
570
|
+
);
|
|
550
571
|
}
|
|
551
572
|
|
|
552
573
|
/**
|
|
@@ -0,0 +1,171 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const data = require('../data/propertyGroups.json');
|
|
4
|
+
|
|
5
|
+
const vendorPrefixRegex = /^-\w+-/;
|
|
6
|
+
/**
|
|
7
|
+
* @param {string} prop
|
|
8
|
+
* @return {string}
|
|
9
|
+
*/
|
|
10
|
+
function vendorUnprefixed(prop) {
|
|
11
|
+
return prop.replace(vendorPrefixRegex, '');
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
// The generated file is JSON, so its maps arrive as plain objects. Property
|
|
15
|
+
// names come from the stylesheet, and `constructor` or `toString` are things a
|
|
16
|
+
// declaration can be called, so never index those objects directly.
|
|
17
|
+
const knownProperties = new Set(data.properties);
|
|
18
|
+
const aliases = new Map(Object.entries(data.aliases));
|
|
19
|
+
const shorthands = new Map(Object.entries(data.shorthands));
|
|
20
|
+
const logicalGroups = new Map(Object.entries(data.logicalGroups));
|
|
21
|
+
const flowRelative = new Set(data.flowRelative);
|
|
22
|
+
|
|
23
|
+
const ALL = 'all';
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* An alias target, or the candidate itself if it's a known property outright.
|
|
27
|
+
*
|
|
28
|
+
* @param {string} candidate
|
|
29
|
+
* @return {string|undefined}
|
|
30
|
+
*/
|
|
31
|
+
function lookupProperty(candidate) {
|
|
32
|
+
const alias = aliases.get(candidate);
|
|
33
|
+
if (alias !== undefined) {
|
|
34
|
+
return alias;
|
|
35
|
+
}
|
|
36
|
+
return knownProperties.has(candidate) ? candidate : undefined;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* Resolves a property to the name the generated data knows it by: vendor
|
|
41
|
+
* prefixed spellings collapse onto the property they alias, and a prefix we
|
|
42
|
+
* have no data for is dropped, since moving `-webkit-background-clip` past a
|
|
43
|
+
* `background` shorthand is as unsafe as moving the unprefixed property. Webref
|
|
44
|
+
* lists some prefixed spellings, like `-webkit-user-select`, as properties in
|
|
45
|
+
* their own right with no alias back to the unprefixed one, so the unprefixed
|
|
46
|
+
* spelling is always tried first, not just when the prefixed one is unknown.
|
|
47
|
+
*
|
|
48
|
+
* @param {string} name Lowercased property name.
|
|
49
|
+
* @return {{name: string, known: boolean}}
|
|
50
|
+
*/
|
|
51
|
+
function resolveProperty(name) {
|
|
52
|
+
if (name.startsWith('-')) {
|
|
53
|
+
const resolved = lookupProperty(vendorUnprefixed(name));
|
|
54
|
+
if (resolved !== undefined) {
|
|
55
|
+
return { name: resolved, known: true };
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
const resolved = lookupProperty(name);
|
|
59
|
+
return resolved !== undefined
|
|
60
|
+
? { name: resolved, known: true }
|
|
61
|
+
: { name, known: false };
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
/**
|
|
65
|
+
* The longhands a property sets. A longhand sets only itself.
|
|
66
|
+
*
|
|
67
|
+
* @param {string} name
|
|
68
|
+
* @return {string[]}
|
|
69
|
+
*/
|
|
70
|
+
function longhandsOf(name) {
|
|
71
|
+
return shorthands.get(name) ?? [name];
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
/**
|
|
75
|
+
* True if two longhands can be the same physical property. Beyond being the
|
|
76
|
+
* same property, that happens between the flow-relative and the physical
|
|
77
|
+
* members of a logical property group, since `margin-inline-start` is
|
|
78
|
+
* `margin-top` under a vertical writing mode. Two physical members, or two
|
|
79
|
+
* flow-relative ones, always address different sides.
|
|
80
|
+
*
|
|
81
|
+
* @param {string} a
|
|
82
|
+
* @param {string} b
|
|
83
|
+
* @return {boolean}
|
|
84
|
+
*/
|
|
85
|
+
function isSameLonghand(a, b) {
|
|
86
|
+
if (a === b) {
|
|
87
|
+
return true;
|
|
88
|
+
}
|
|
89
|
+
const group = logicalGroups.get(a);
|
|
90
|
+
return (
|
|
91
|
+
group !== undefined &&
|
|
92
|
+
group === logicalGroups.get(b) &&
|
|
93
|
+
flowRelative.has(a) !== flowRelative.has(b)
|
|
94
|
+
);
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
/**
|
|
98
|
+
* The name-based approximation the plugin relied on before it had property
|
|
99
|
+
* data: two properties interact when they share their leading segment and
|
|
100
|
+
* their remaining segments either match or differ in number. `place` is
|
|
101
|
+
* treated as a wildcard leading segment, since `place-content` expands to
|
|
102
|
+
* `align-content`/`justify-content` and the like. Reached only for vendor
|
|
103
|
+
* extensions no spec describes, such as `-webkit-box-direction`.
|
|
104
|
+
*
|
|
105
|
+
* @param {string} nameA
|
|
106
|
+
* @param {string} nameB
|
|
107
|
+
* @return {boolean}
|
|
108
|
+
*/
|
|
109
|
+
function conflictingSegments(nameA, nameB) {
|
|
110
|
+
const a = vendorUnprefixed(nameA).split('-');
|
|
111
|
+
const b = vendorUnprefixed(nameB).split('-');
|
|
112
|
+
if (a[0] !== b[0] && a[0] !== 'place' && b[0] !== 'place') {
|
|
113
|
+
return false;
|
|
114
|
+
}
|
|
115
|
+
if (a.length !== b.length) {
|
|
116
|
+
return true;
|
|
117
|
+
}
|
|
118
|
+
return a.every((segment, index) => b[index] === segment);
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
/**
|
|
122
|
+
* True if declarations of `propA` and `propB` can set the same underlying
|
|
123
|
+
* property, so that reordering them within a rule can change what the rule
|
|
124
|
+
* computes to. The relation is symmetric: a shorthand setting a longhand and a
|
|
125
|
+
* longhand overriding part of a shorthand are the same conflict seen from
|
|
126
|
+
* either end.
|
|
127
|
+
*
|
|
128
|
+
* @param {string} propA
|
|
129
|
+
* @param {string} propB
|
|
130
|
+
* @return {boolean}
|
|
131
|
+
*/
|
|
132
|
+
function isConflictingProp(propA, propB) {
|
|
133
|
+
if (propA === propB) {
|
|
134
|
+
return true;
|
|
135
|
+
}
|
|
136
|
+
// Nothing sets a custom property except itself, and custom properties are
|
|
137
|
+
// case-sensitive, so this must run before the names are lowercased below.
|
|
138
|
+
if (propA.startsWith('--') || propB.startsWith('--')) {
|
|
139
|
+
return false;
|
|
140
|
+
}
|
|
141
|
+
const nameA = propA.toLowerCase();
|
|
142
|
+
const nameB = propB.toLowerCase();
|
|
143
|
+
if (nameA === nameB) {
|
|
144
|
+
return true;
|
|
145
|
+
}
|
|
146
|
+
if (nameA === ALL || nameB === ALL) {
|
|
147
|
+
const other = nameA === ALL ? nameB : nameA;
|
|
148
|
+
return other !== 'direction' && other !== 'unicode-bidi';
|
|
149
|
+
}
|
|
150
|
+
const a = resolveProperty(nameA);
|
|
151
|
+
const b = resolveProperty(nameB);
|
|
152
|
+
if (a.name === b.name) {
|
|
153
|
+
return true;
|
|
154
|
+
}
|
|
155
|
+
if (!a.known || !b.known) {
|
|
156
|
+
// A vendor extension the data says nothing about. The shorthand relations
|
|
157
|
+
// that would settle it are exactly what is missing, so fall back to
|
|
158
|
+
// comparing the names.
|
|
159
|
+
return conflictingSegments(a.name, b.name);
|
|
160
|
+
}
|
|
161
|
+
for (const longhandA of longhandsOf(a.name)) {
|
|
162
|
+
for (const longhandB of longhandsOf(b.name)) {
|
|
163
|
+
if (isSameLonghand(longhandA, longhandB)) {
|
|
164
|
+
return true;
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
return false;
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
module.exports = { isConflictingProp };
|
package/types/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.js"],"names":[],"mappings":";;AAII,OAAQ,KAAA,YAAY,MAAM,cAAc,CAAA;
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.js"],"names":[],"mappings":";;AAII,OAAQ,KAAA,YAAY,MAAM,cAAc,CAAA;AASxC,OAAQ,KAAA,EAAC,WAAW,EAAO,MAAM,SAAS,CAAA;AAmD3C,YAAkB,QAAQ,GAC1B;;;;IAAqB,SAAS,EAAnB,MAAM,EAAE,CACnB;;;;IAA0B,YAAY,EAA3B,WAAW,EAAE,CACxB;;;;IAAoB,KAAK,EAAd,OAAO,CACpB;CAAA,CAAA;AA8lBE,YAAwD,mBAAmB,GAAjE;IAAE,oBAAoB,CAAC,EAAE,MAAM,GAAG,MAAM,EAAE,CAAA;CAAE,CAAqB;AAC3E,YAAgE,mBAAmB,GAAzE,IAAI,CAAC,YAAY,CAAC,OAAO,EAAE,OAAO,GAAG,MAAM,GAAG,KAAK,CAAC,CAAqB;AACnF,YAAqD,OAAO,GAAlD,mBAAmB,GAAG,mBAAmB,CAAS"}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
declare const _exports: {
|
|
2
|
+
isConflictingProp: typeof isConflictingProp;
|
|
3
|
+
};
|
|
4
|
+
export = _exports;
|
|
5
|
+
/**
|
|
6
|
+
* True if declarations of `propA` and `propB` can set the same underlying
|
|
7
|
+
* property, so that reordering them within a rule can change what the rule
|
|
8
|
+
* computes to. The relation is symmetric: a shorthand setting a longhand and a
|
|
9
|
+
* longhand overriding part of a shorthand are the same conflict seen from
|
|
10
|
+
* either end.
|
|
11
|
+
*
|
|
12
|
+
* @param {string} propA
|
|
13
|
+
* @param {string} propB
|
|
14
|
+
* @return {boolean}
|
|
15
|
+
*/
|
|
16
|
+
declare function isConflictingProp(propA: string, propB: string): boolean;
|
|
17
|
+
//# sourceMappingURL=propertyRelations.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"propertyRelations.d.ts","sourceRoot":"","sources":["../../src/lib/propertyRelations.js"],"names":[],"mappings":";;;;AAwHA;;;;;;;;;;GAUG;AACH,iBAAS,iBAAiB,CAAC,KAAK,EAJrB,MAIqB,EAAE,KAAK,EAH5B,MAG4B,GAF3B,OAAO,CAuClB"}
|