summaryrefslogtreecommitdiff
path: root/node_modules/csso/cjs/restructure/prepare
diff options
context:
space:
mode:
authorlittleblack111 <littleblack11111@gmail.com>2023-10-14 15:05:55 +0800
committerlittleblack111 <littleblack11111@gmail.com>2023-10-14 15:05:55 +0800
commitc06f7ee0a40255214c477e13d9b956a7e07c2900 (patch)
treef5982a568ef8fab865d0142e71c7494fea3e4c1b /node_modules/csso/cjs/restructure/prepare
parent3fd9400d4dccc25aa29b5702216732e0b7542073 (diff)
postcss,(plugins): nanocss, postcss-preset-env
Diffstat (limited to 'node_modules/csso/cjs/restructure/prepare')
-rw-r--r--node_modules/csso/cjs/restructure/prepare/createDeclarationIndexer.cjs34
-rw-r--r--node_modules/csso/cjs/restructure/prepare/index.cjs45
-rw-r--r--node_modules/csso/cjs/restructure/prepare/processSelector.cjs101
-rw-r--r--node_modules/csso/cjs/restructure/prepare/specificity.cjs133
4 files changed, 313 insertions, 0 deletions
diff --git a/node_modules/csso/cjs/restructure/prepare/createDeclarationIndexer.cjs b/node_modules/csso/cjs/restructure/prepare/createDeclarationIndexer.cjs
new file mode 100644
index 00000000..94489728
--- /dev/null
+++ b/node_modules/csso/cjs/restructure/prepare/createDeclarationIndexer.cjs
@@ -0,0 +1,34 @@
+'use strict';
+
+const cssTree = require('css-tree');
+
+class Index {
+ constructor() {
+ this.map = new Map();
+ }
+ resolve(str) {
+ let index = this.map.get(str);
+
+ if (index === undefined) {
+ index = this.map.size + 1;
+ this.map.set(str, index);
+ }
+
+ return index;
+ }
+}
+function createDeclarationIndexer() {
+ const ids = new Index();
+
+ return function markDeclaration(node) {
+ const id = cssTree.generate(node);
+
+ node.id = ids.resolve(id);
+ node.length = id.length;
+ node.fingerprint = null;
+
+ return node;
+ };
+}
+
+module.exports = createDeclarationIndexer;
diff --git a/node_modules/csso/cjs/restructure/prepare/index.cjs b/node_modules/csso/cjs/restructure/prepare/index.cjs
new file mode 100644
index 00000000..42b49127
--- /dev/null
+++ b/node_modules/csso/cjs/restructure/prepare/index.cjs
@@ -0,0 +1,45 @@
+'use strict';
+
+const cssTree = require('css-tree');
+const createDeclarationIndexer = require('./createDeclarationIndexer.cjs');
+const processSelector = require('./processSelector.cjs');
+
+function prepare(ast, options) {
+ const markDeclaration = createDeclarationIndexer();
+
+ cssTree.walk(ast, {
+ visit: 'Rule',
+ enter(node) {
+ node.block.children.forEach(markDeclaration);
+ processSelector(node, options.usage);
+ }
+ });
+
+ cssTree.walk(ast, {
+ visit: 'Atrule',
+ enter(node) {
+ if (node.prelude) {
+ node.prelude.id = null; // pre-init property to avoid multiple hidden class for generate
+ node.prelude.id = cssTree.generate(node.prelude);
+ }
+
+ // compare keyframe selectors by its values
+ // NOTE: still no clarification about problems with keyframes selector grouping (issue #197)
+ if (cssTree.keyword(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.forEach(function(rule) {
+ rule.prelude.children.forEach(function(simpleselector) {
+ simpleselector.compareMarker = simpleselector.id;
+ });
+ });
+ }
+ }
+ });
+
+ return {
+ declaration: markDeclaration
+ };
+}
+
+module.exports = prepare;
diff --git a/node_modules/csso/cjs/restructure/prepare/processSelector.cjs b/node_modules/csso/cjs/restructure/prepare/processSelector.cjs
new file mode 100644
index 00000000..5c9e1d21
--- /dev/null
+++ b/node_modules/csso/cjs/restructure/prepare/processSelector.cjs
@@ -0,0 +1,101 @@
+'use strict';
+
+const cssTree = require('css-tree');
+const specificity = require('./specificity.cjs');
+
+const nonFreezePseudoElements = new Set([
+ 'first-letter',
+ 'first-line',
+ 'after',
+ 'before'
+]);
+const nonFreezePseudoClasses = new Set([
+ 'link',
+ 'visited',
+ 'hover',
+ 'active',
+ 'first-letter',
+ 'first-line',
+ 'after',
+ 'before'
+]);
+
+function processSelector(node, usageData) {
+ const pseudos = new Set();
+
+ node.prelude.children.forEach(function(simpleSelector) {
+ let tagName = '*';
+ let scope = 0;
+
+ simpleSelector.children.forEach(function(node) {
+ switch (node.type) {
+ case 'ClassSelector':
+ if (usageData && usageData.scopes) {
+ const classScope = usageData.scopes[node.name] || 0;
+
+ if (scope !== 0 && classScope !== scope) {
+ throw new Error('Selector can\'t has classes from different scopes: ' + cssTree.generate(simpleSelector));
+ }
+
+ scope = classScope;
+ }
+
+ break;
+
+ case 'PseudoClassSelector': {
+ const name = node.name.toLowerCase();
+
+ if (!nonFreezePseudoClasses.has(name)) {
+ pseudos.add(`:${name}`);
+ }
+
+ break;
+ }
+
+ case 'PseudoElementSelector': {
+ const name = node.name.toLowerCase();
+
+ if (!nonFreezePseudoElements.has(name)) {
+ pseudos.add(`::${name}`);
+ }
+
+ break;
+ }
+
+ case 'TypeSelector':
+ tagName = node.name.toLowerCase();
+ break;
+
+ case 'AttributeSelector':
+ if (node.flags) {
+ pseudos.add(`[${node.flags.toLowerCase()}]`);
+ }
+
+ break;
+
+ case 'Combinator':
+ tagName = '*';
+ break;
+ }
+ });
+
+ simpleSelector.compareMarker = specificity(simpleSelector).toString();
+ simpleSelector.id = null; // pre-init property to avoid multiple hidden class
+ simpleSelector.id = cssTree.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 = pseudos.size > 0
+ ? [...pseudos].sort().join(',')
+ : false;
+}
+
+module.exports = processSelector;
diff --git a/node_modules/csso/cjs/restructure/prepare/specificity.cjs b/node_modules/csso/cjs/restructure/prepare/specificity.cjs
new file mode 100644
index 00000000..b62c3291
--- /dev/null
+++ b/node_modules/csso/cjs/restructure/prepare/specificity.cjs
@@ -0,0 +1,133 @@
+'use strict';
+
+const cssTree = require('css-tree');
+
+function ensureSelectorList(node) {
+ if (node.type === 'Raw') {
+ return cssTree.parse(node.value, { context: 'selectorList' });
+ }
+
+ return node;
+}
+
+function maxSpecificity(a, b) {
+ for (let i = 0; i < 3; i++) {
+ if (a[i] !== b[i]) {
+ return a[i] > b[i] ? a : b;
+ }
+ }
+
+ return a;
+}
+
+function maxSelectorListSpecificity(selectorList) {
+ return ensureSelectorList(selectorList).children.reduce(
+ (result, node) => maxSpecificity(specificity(node), result),
+ [0, 0, 0]
+ );
+}
+
+// §16. Calculating a selector’s specificity
+// https://www.w3.org/TR/selectors-4/#specificity-rules
+function specificity(simpleSelector) {
+ let A = 0;
+ let B = 0;
+ let C = 0;
+
+ // A selector’s specificity is calculated for a given element as follows:
+ simpleSelector.children.forEach((node) => {
+ switch (node.type) {
+ // count the number of ID selectors in the selector (= A)
+ case 'IdSelector':
+ A++;
+ break;
+
+ // count the number of class selectors, attributes selectors, ...
+ case 'ClassSelector':
+ case 'AttributeSelector':
+ B++;
+ break;
+
+ // ... and pseudo-classes in the selector (= B)
+ case 'PseudoClassSelector':
+ switch (node.name.toLowerCase()) {
+ // The specificity of an :is(), :not(), or :has() pseudo-class is replaced
+ // by the specificity of the most specific complex selector in its selector list argument.
+ case 'not':
+ case 'has':
+ case 'is':
+ // :matches() is used before it was renamed to :is()
+ // https://github.com/w3c/csswg-drafts/issues/3258
+ case 'matches':
+ // Older browsers support :is() functionality as prefixed pseudo-class :any()
+ // https://developer.mozilla.org/en-US/docs/Web/CSS/:is
+ case '-webkit-any':
+ case '-moz-any': {
+ const [a, b, c] = maxSelectorListSpecificity(node.children.first);
+
+ A += a;
+ B += b;
+ C += c;
+
+ break;
+ }
+
+ // Analogously, the specificity of an :nth-child() or :nth-last-child() selector
+ // is the specificity of the pseudo class itself (counting as one pseudo-class selector)
+ // plus the specificity of the most specific complex selector in its selector list argument (if any).
+ case 'nth-child':
+ case 'nth-last-child': {
+ const arg = node.children.first;
+
+ if (arg.type === 'Nth' && arg.selector) {
+ const [a, b, c] = maxSelectorListSpecificity(arg.selector);
+
+ A += a;
+ B += b + 1;
+ C += c;
+ } else {
+ B++;
+ }
+
+ break;
+ }
+
+ // The specificity of a :where() pseudo-class is replaced by zero.
+ case 'where':
+ break;
+
+ // The four Level 2 pseudo-elements (::before, ::after, ::first-line, and ::first-letter) may,
+ // for legacy reasons, be represented using the <pseudo-class-selector> grammar,
+ // with only a single ":" character at their start.
+ // https://www.w3.org/TR/selectors-4/#single-colon-pseudos
+ case 'before':
+ case 'after':
+ case 'first-line':
+ case 'first-letter':
+ C++;
+ break;
+
+ default:
+ B++;
+ }
+ break;
+
+ // count the number of type selectors ...
+ case 'TypeSelector':
+ // ignore the universal selector
+ if (!node.name.endsWith('*')) {
+ C++;
+ }
+ break;
+
+ // ... and pseudo-elements in the selector (= C)
+ case 'PseudoElementSelector':
+ C++;
+ break;
+ }
+ });
+
+ return [A, B, C];
+}
+
+module.exports = specificity;