You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
151 lines
3.9 KiB
151 lines
3.9 KiB
'use strict'; |
|
|
|
const { hasOwnProperty } = Object.prototype; |
|
|
|
function isEqualSelectors(a, b) { |
|
let cursor1 = a.head; |
|
let cursor2 = b.head; |
|
|
|
while (cursor1 !== null && cursor2 !== null && cursor1.data.id === cursor2.data.id) { |
|
cursor1 = cursor1.next; |
|
cursor2 = cursor2.next; |
|
} |
|
|
|
return cursor1 === null && cursor2 === null; |
|
} |
|
|
|
function isEqualDeclarations(a, b) { |
|
let cursor1 = a.head; |
|
let cursor2 = b.head; |
|
|
|
while (cursor1 !== null && cursor2 !== null && cursor1.data.id === cursor2.data.id) { |
|
cursor1 = cursor1.next; |
|
cursor2 = cursor2.next; |
|
} |
|
|
|
return cursor1 === null && cursor2 === null; |
|
} |
|
|
|
function compareDeclarations(declarations1, declarations2) { |
|
const result = { |
|
eq: [], |
|
ne1: [], |
|
ne2: [], |
|
ne2overrided: [] |
|
}; |
|
|
|
const fingerprints = Object.create(null); |
|
const declarations2hash = Object.create(null); |
|
|
|
for (let cursor = declarations2.head; cursor; cursor = cursor.next) { |
|
declarations2hash[cursor.data.id] = true; |
|
} |
|
|
|
for (let cursor = declarations1.head; cursor; cursor = cursor.next) { |
|
const data = cursor.data; |
|
|
|
if (data.fingerprint) { |
|
fingerprints[data.fingerprint] = data.important; |
|
} |
|
|
|
if (declarations2hash[data.id]) { |
|
declarations2hash[data.id] = false; |
|
result.eq.push(data); |
|
} else { |
|
result.ne1.push(data); |
|
} |
|
} |
|
|
|
for (let cursor = declarations2.head; cursor; cursor = cursor.next) { |
|
const data = cursor.data; |
|
|
|
if (declarations2hash[data.id]) { |
|
// when declarations1 has an overriding declaration, this is not a difference |
|
// unless no !important is used on prev and !important is used on the following |
|
if (!hasOwnProperty.call(fingerprints, data.fingerprint) || |
|
(!fingerprints[data.fingerprint] && data.important)) { |
|
result.ne2.push(data); |
|
} |
|
|
|
result.ne2overrided.push(data); |
|
} |
|
} |
|
|
|
return result; |
|
} |
|
|
|
function addSelectors(dest, source) { |
|
source.forEach((sourceData) => { |
|
const newStr = sourceData.id; |
|
let cursor = dest.head; |
|
|
|
while (cursor) { |
|
const nextStr = cursor.data.id; |
|
|
|
if (nextStr === newStr) { |
|
return; |
|
} |
|
|
|
if (nextStr > newStr) { |
|
break; |
|
} |
|
|
|
cursor = cursor.next; |
|
} |
|
|
|
dest.insert(dest.createItem(sourceData), cursor); |
|
}); |
|
|
|
return dest; |
|
} |
|
|
|
// check if simpleselectors has no equal specificity and element selector |
|
function hasSimilarSelectors(selectors1, selectors2) { |
|
let cursor1 = selectors1.head; |
|
|
|
while (cursor1 !== null) { |
|
let cursor2 = selectors2.head; |
|
|
|
while (cursor2 !== null) { |
|
if (cursor1.data.compareMarker === cursor2.data.compareMarker) { |
|
return true; |
|
} |
|
|
|
cursor2 = cursor2.next; |
|
} |
|
|
|
cursor1 = cursor1.next; |
|
} |
|
|
|
return false; |
|
} |
|
|
|
// test node can't to be skipped |
|
function unsafeToSkipNode(node) { |
|
switch (node.type) { |
|
case 'Rule': |
|
// unsafe skip ruleset with selector similarities |
|
return hasSimilarSelectors(node.prelude.children, this); |
|
|
|
case 'Atrule': |
|
// can skip at-rules with blocks |
|
if (node.block) { |
|
// unsafe skip at-rule if block contains something unsafe to skip |
|
return node.block.children.some(unsafeToSkipNode, this); |
|
} |
|
break; |
|
|
|
case 'Declaration': |
|
return false; |
|
} |
|
|
|
// unsafe by default |
|
return true; |
|
} |
|
|
|
exports.addSelectors = addSelectors; |
|
exports.compareDeclarations = compareDeclarations; |
|
exports.hasSimilarSelectors = hasSimilarSelectors; |
|
exports.isEqualDeclarations = isEqualDeclarations; |
|
exports.isEqualSelectors = isEqualSelectors; |
|
exports.unsafeToSkipNode = unsafeToSkipNode;
|
|
|