first commit

This commit is contained in:
monjack
2025-06-20 18:01:48 +08:00
commit 6daa6d65c1
24611 changed files with 2512443 additions and 0 deletions

View File

@ -0,0 +1,31 @@
var generate = require('css-tree').generate;
function Index() {
this.seed = 0;
this.map = Object.create(null);
}
Index.prototype.resolve = function(str) {
var index = this.map[str];
if (!index) {
index = ++this.seed;
this.map[str] = index;
}
return index;
};
module.exports = function createDeclarationIndexer() {
var ids = new Index();
return function markDeclaration(node) {
var id = generate(node);
node.id = ids.resolve(id);
node.length = id.length;
node.fingerprint = null;
return node;
};
};

View File

@ -0,0 +1,43 @@
var resolveKeyword = require('css-tree').keyword;
var walk = require('css-tree').walk;
var generate = require('css-tree').generate;
var createDeclarationIndexer = require('./createDeclarationIndexer');
var processSelector = require('./processSelector');
module.exports = function prepare(ast, options) {
var markDeclaration = createDeclarationIndexer();
walk(ast, {
visit: 'Rule',
enter: function processRule(node) {
node.block.children.each(markDeclaration);
processSelector(node, options.usage);
}
});
walk(ast, {
visit: 'Atrule',
enter: function(node) {
if (node.prelude) {
node.prelude.id = null; // pre-init property to avoid multiple hidden class for generate
node.prelude.id = generate(node.prelude);
}
// compare keyframe selectors by its values
// NOTE: still no clarification about problems with keyframes selector grouping (issue #197)
if (resolveKeyword(node.name).basename === 'keyframes') {
node.block.avoidRulesMerge = true; /* probably we don't need to prevent those merges for @keyframes
TODO: need to be checked */
node.block.children.each(function(rule) {
rule.prelude.children.each(function(simpleselector) {
simpleselector.compareMarker = simpleselector.id;
});
});
}
}
});
return {
declaration: markDeclaration
};
};

View File

@ -0,0 +1,94 @@
var generate = require('css-tree').generate;
var specificity = require('./specificity');
var nonFreezePseudoElements = {
'first-letter': true,
'first-line': true,
'after': true,
'before': true
};
var nonFreezePseudoClasses = {
'link': true,
'visited': true,
'hover': true,
'active': true,
'first-letter': true,
'first-line': true,
'after': true,
'before': true
};
module.exports = function freeze(node, usageData) {
var pseudos = Object.create(null);
var hasPseudo = false;
node.prelude.children.each(function(simpleSelector) {
var tagName = '*';
var scope = 0;
simpleSelector.children.each(function(node) {
switch (node.type) {
case 'ClassSelector':
if (usageData && usageData.scopes) {
var classScope = usageData.scopes[node.name] || 0;
if (scope !== 0 && classScope !== scope) {
throw new Error('Selector can\'t has classes from different scopes: ' + generate(simpleSelector));
}
scope = classScope;
}
break;
case 'PseudoClassSelector':
var name = node.name.toLowerCase();
if (!nonFreezePseudoClasses.hasOwnProperty(name)) {
pseudos[':' + name] = true;
hasPseudo = true;
}
break;
case 'PseudoElementSelector':
var name = node.name.toLowerCase();
if (!nonFreezePseudoElements.hasOwnProperty(name)) {
pseudos['::' + name] = true;
hasPseudo = true;
}
break;
case 'TypeSelector':
tagName = node.name.toLowerCase();
break;
case 'AttributeSelector':
if (node.flags) {
pseudos['[' + node.flags.toLowerCase() + ']'] = true;
hasPseudo = true;
}
break;
case 'WhiteSpace':
case 'Combinator':
tagName = '*';
break;
}
});
simpleSelector.compareMarker = specificity(simpleSelector).toString();
simpleSelector.id = null; // pre-init property to avoid multiple hidden class
simpleSelector.id = generate(simpleSelector);
if (scope) {
simpleSelector.compareMarker += ':' + scope;
}
if (tagName !== '*') {
simpleSelector.compareMarker += ',' + tagName;
}
});
// add property to all rule nodes to avoid multiple hidden class
node.pseudoSignature = hasPseudo && Object.keys(pseudos).sort().join(',');
};

View File

@ -0,0 +1,56 @@
module.exports = function specificity(simpleSelector) {
var A = 0;
var B = 0;
var C = 0;
simpleSelector.children.each(function walk(node) {
switch (node.type) {
case 'SelectorList':
case 'Selector':
node.children.each(walk);
break;
case 'IdSelector':
A++;
break;
case 'ClassSelector':
case 'AttributeSelector':
B++;
break;
case 'PseudoClassSelector':
switch (node.name.toLowerCase()) {
case 'not':
node.children.each(walk);
break;
case 'before':
case 'after':
case 'first-line':
case 'first-letter':
C++;
break;
// TODO: support for :nth-*(.. of <SelectorList>), :matches(), :has()
default:
B++;
}
break;
case 'PseudoElementSelector':
C++;
break;
case 'TypeSelector':
// ignore universal selector
if (node.name.charAt(node.name.length - 1) !== '*') {
C++;
}
break;
}
});
return [A, B, C];
};