summaryrefslogtreecommitdiff
path: root/node_modules/postcss-calc/src/lib
diff options
context:
space:
mode:
authorlittleblack111 <littleblack11111@gmail.com>2023-11-05 17:16:09 +0800
committerlittleblack111 <littleblack11111@gmail.com>2023-11-05 17:16:09 +0800
commitd1d9f00b1bd660842addf5e6b1d1b64c852a9eea (patch)
tree60fb37ae73a1109d95301de5ec9b61b5726a4b61 /node_modules/postcss-calc/src/lib
parent7c85166c4cbdd862fc4252007d2df5befcbc2a58 (diff)
added hover prefixer
Diffstat (limited to 'node_modules/postcss-calc/src/lib')
-rw-r--r--node_modules/postcss-calc/src/lib/convertUnit.js160
-rw-r--r--node_modules/postcss-calc/src/lib/reducer.js362
-rw-r--r--node_modules/postcss-calc/src/lib/stringifier.js98
-rw-r--r--node_modules/postcss-calc/src/lib/transform.js109
4 files changed, 729 insertions, 0 deletions
diff --git a/node_modules/postcss-calc/src/lib/convertUnit.js b/node_modules/postcss-calc/src/lib/convertUnit.js
new file mode 100644
index 00000000..5f7bc268
--- /dev/null
+++ b/node_modules/postcss-calc/src/lib/convertUnit.js
@@ -0,0 +1,160 @@
+'use strict';
+/**
+ * @type {{[key:string]: {[key:string]: number}}}
+ */
+const conversions = {
+ // Absolute length units
+ px: {
+ px: 1,
+ cm: 96 / 2.54,
+ mm: 96 / 25.4,
+ q: 96 / 101.6,
+ in: 96,
+ pt: 96 / 72,
+ pc: 16,
+ },
+ cm: {
+ px: 2.54 / 96,
+ cm: 1,
+ mm: 0.1,
+ q: 0.025,
+ in: 2.54,
+ pt: 2.54 / 72,
+ pc: 2.54 / 6,
+ },
+ mm: {
+ px: 25.4 / 96,
+ cm: 10,
+ mm: 1,
+ q: 0.25,
+ in: 25.4,
+ pt: 25.4 / 72,
+ pc: 25.4 / 6,
+ },
+ q: {
+ px: 101.6 / 96,
+ cm: 40,
+ mm: 4,
+ q: 1,
+ in: 101.6,
+ pt: 101.6 / 72,
+ pc: 101.6 / 6,
+ },
+ in: {
+ px: 1 / 96,
+ cm: 1 / 2.54,
+ mm: 1 / 25.4,
+ q: 1 / 101.6,
+ in: 1,
+ pt: 1 / 72,
+ pc: 1 / 6,
+ },
+ pt: {
+ px: 0.75,
+ cm: 72 / 2.54,
+ mm: 72 / 25.4,
+ q: 72 / 101.6,
+ in: 72,
+ pt: 1,
+ pc: 12,
+ },
+ pc: {
+ px: 0.0625,
+ cm: 6 / 2.54,
+ mm: 6 / 25.4,
+ q: 6 / 101.6,
+ in: 6,
+ pt: 6 / 72,
+ pc: 1,
+ },
+ // Angle units
+ deg: {
+ deg: 1,
+ grad: 0.9,
+ rad: 180 / Math.PI,
+ turn: 360,
+ },
+ grad: {
+ deg: 400 / 360,
+ grad: 1,
+ rad: 200 / Math.PI,
+ turn: 400,
+ },
+ rad: {
+ deg: Math.PI / 180,
+ grad: Math.PI / 200,
+ rad: 1,
+ turn: Math.PI * 2,
+ },
+ turn: {
+ deg: 1 / 360,
+ grad: 0.0025,
+ rad: 0.5 / Math.PI,
+ turn: 1,
+ },
+ // Duration units
+ s: {
+ s: 1,
+ ms: 0.001,
+ },
+ ms: {
+ s: 1000,
+ ms: 1,
+ },
+ // Frequency units
+ hz: {
+ hz: 1,
+ khz: 1000,
+ },
+ khz: {
+ hz: 0.001,
+ khz: 1,
+ },
+ // Resolution units
+ dpi: {
+ dpi: 1,
+ dpcm: 1 / 2.54,
+ dppx: 1 / 96,
+ },
+ dpcm: {
+ dpi: 2.54,
+ dpcm: 1,
+ dppx: 2.54 / 96,
+ },
+ dppx: {
+ dpi: 96,
+ dpcm: 96 / 2.54,
+ dppx: 1,
+ },
+};
+/**
+ * @param {number} value
+ * @param {string} sourceUnit
+ * @param {string} targetUnit
+ * @param {number|false} precision
+ */
+function convertUnit(value, sourceUnit, targetUnit, precision) {
+ const sourceUnitNormalized = sourceUnit.toLowerCase();
+ const targetUnitNormalized = targetUnit.toLowerCase();
+
+ if (!conversions[targetUnitNormalized]) {
+ throw new Error('Cannot convert to ' + targetUnit);
+ }
+
+ if (!conversions[targetUnitNormalized][sourceUnitNormalized]) {
+ throw new Error('Cannot convert from ' + sourceUnit + ' to ' + targetUnit);
+ }
+
+ const converted =
+ conversions[targetUnitNormalized][sourceUnitNormalized] * value;
+
+ if (precision !== false) {
+ precision = Math.pow(10, Math.ceil(precision) || 5);
+
+ return Math.round(converted * precision) / precision;
+ }
+
+ return converted;
+}
+
+module.exports = convertUnit;
diff --git a/node_modules/postcss-calc/src/lib/reducer.js b/node_modules/postcss-calc/src/lib/reducer.js
new file mode 100644
index 00000000..8a36bf13
--- /dev/null
+++ b/node_modules/postcss-calc/src/lib/reducer.js
@@ -0,0 +1,362 @@
+'use strict';
+const convertUnit = require('./convertUnit.js');
+
+/**
+ * @param {import('../parser').CalcNode} node
+ * @return {node is import('../parser').ValueExpression}
+ */
+function isValueType(node) {
+ switch (node.type) {
+ case 'LengthValue':
+ case 'AngleValue':
+ case 'TimeValue':
+ case 'FrequencyValue':
+ case 'ResolutionValue':
+ case 'EmValue':
+ case 'ExValue':
+ case 'ChValue':
+ case 'RemValue':
+ case 'VhValue':
+ case 'VwValue':
+ case 'VminValue':
+ case 'VmaxValue':
+ case 'PercentageValue':
+ case 'Number':
+ return true;
+ }
+ return false;
+}
+
+/** @param {'-'|'+'} operator */
+function flip(operator) {
+ return operator === '+' ? '-' : '+';
+}
+
+/**
+ * @param {string} operator
+ * @returns {operator is '+'|'-'}
+ */
+function isAddSubOperator(operator) {
+ return operator === '+' || operator === '-';
+}
+
+/**
+ * @typedef {{preOperator: '+'|'-', node: import('../parser').CalcNode}} Collectible
+ */
+
+/**
+ * @param {'+'|'-'} preOperator
+ * @param {import('../parser').CalcNode} node
+ * @param {Collectible[]} collected
+ * @param {number} precision
+ */
+function collectAddSubItems(preOperator, node, collected, precision) {
+ if (!isAddSubOperator(preOperator)) {
+ throw new Error(`invalid operator ${preOperator}`);
+ }
+ if (isValueType(node)) {
+ const itemIndex = collected.findIndex((x) => x.node.type === node.type);
+ if (itemIndex >= 0) {
+ if (node.value === 0) {
+ return;
+ }
+ // can cast because of the criterion used to find itemIndex
+ const otherValueNode = /** @type import('../parser').ValueExpression*/ (
+ collected[itemIndex].node
+ );
+ const { left: reducedNode, right: current } = convertNodesUnits(
+ otherValueNode,
+ node,
+ precision
+ );
+
+ if (collected[itemIndex].preOperator === '-') {
+ collected[itemIndex].preOperator = '+';
+ reducedNode.value *= -1;
+ }
+ if (preOperator === '+') {
+ reducedNode.value += current.value;
+ } else {
+ reducedNode.value -= current.value;
+ }
+ // make sure reducedNode.value >= 0
+ if (reducedNode.value >= 0) {
+ collected[itemIndex] = { node: reducedNode, preOperator: '+' };
+ } else {
+ reducedNode.value *= -1;
+ collected[itemIndex] = { node: reducedNode, preOperator: '-' };
+ }
+ } else {
+ // make sure node.value >= 0
+ if (node.value >= 0) {
+ collected.push({ node, preOperator });
+ } else {
+ node.value *= -1;
+ collected.push({ node, preOperator: flip(preOperator) });
+ }
+ }
+ } else if (node.type === 'MathExpression') {
+ if (isAddSubOperator(node.operator)) {
+ collectAddSubItems(preOperator, node.left, collected, precision);
+ const collectRightOperator =
+ preOperator === '-' ? flip(node.operator) : node.operator;
+ collectAddSubItems(
+ collectRightOperator,
+ node.right,
+ collected,
+ precision
+ );
+ } else {
+ // * or /
+ const reducedNode = reduce(node, precision);
+ // prevent infinite recursive call
+ if (
+ reducedNode.type !== 'MathExpression' ||
+ isAddSubOperator(reducedNode.operator)
+ ) {
+ collectAddSubItems(preOperator, reducedNode, collected, precision);
+ } else {
+ collected.push({ node: reducedNode, preOperator });
+ }
+ }
+ } else if (node.type === 'ParenthesizedExpression') {
+ collectAddSubItems(preOperator, node.content, collected, precision);
+ } else {
+ collected.push({ node, preOperator });
+ }
+}
+
+/**
+ * @param {import('../parser').CalcNode} node
+ * @param {number} precision
+ */
+function reduceAddSubExpression(node, precision) {
+ /** @type Collectible[] */
+ const collected = [];
+ collectAddSubItems('+', node, collected, precision);
+
+ const withoutZeroItem = collected.filter(
+ (item) => !(isValueType(item.node) && item.node.value === 0)
+ );
+ const firstNonZeroItem = withoutZeroItem[0]; // could be undefined
+
+ // prevent producing "calc(-var(--a))" or "calc()"
+ // which is invalid css
+ if (
+ !firstNonZeroItem ||
+ (firstNonZeroItem.preOperator === '-' &&
+ !isValueType(firstNonZeroItem.node))
+ ) {
+ const firstZeroItem = collected.find(
+ (item) => isValueType(item.node) && item.node.value === 0
+ );
+ if (firstZeroItem) {
+ withoutZeroItem.unshift(firstZeroItem);
+ }
+ }
+
+ // make sure the preOperator of the first item is +
+ if (
+ withoutZeroItem[0].preOperator === '-' &&
+ isValueType(withoutZeroItem[0].node)
+ ) {
+ withoutZeroItem[0].node.value *= -1;
+ withoutZeroItem[0].preOperator = '+';
+ }
+
+ let root = withoutZeroItem[0].node;
+ for (let i = 1; i < withoutZeroItem.length; i++) {
+ root = {
+ type: 'MathExpression',
+ operator: withoutZeroItem[i].preOperator,
+ left: root,
+ right: withoutZeroItem[i].node,
+ };
+ }
+
+ return root;
+}
+/**
+ * @param {import('../parser').MathExpression} node
+ */
+function reduceDivisionExpression(node) {
+ if (!isValueType(node.right)) {
+ return node;
+ }
+
+ if (node.right.type !== 'Number') {
+ throw new Error(`Cannot divide by "${node.right.unit}", number expected`);
+ }
+
+ return applyNumberDivision(node.left, node.right.value);
+}
+
+/**
+ * apply (expr) / number
+ *
+ * @param {import('../parser').CalcNode} node
+ * @param {number} divisor
+ * @return {import('../parser').CalcNode}
+ */
+function applyNumberDivision(node, divisor) {
+ if (divisor === 0) {
+ throw new Error('Cannot divide by zero');
+ }
+ if (isValueType(node)) {
+ node.value /= divisor;
+ return node;
+ }
+ if (node.type === 'MathExpression' && isAddSubOperator(node.operator)) {
+ // turn (a + b) / num into a/num + b/num
+ // is good for further reduction
+ // checkout the test case
+ // "should reduce division before reducing additions"
+ return {
+ type: 'MathExpression',
+ operator: node.operator,
+ left: applyNumberDivision(node.left, divisor),
+ right: applyNumberDivision(node.right, divisor),
+ };
+ }
+ // it is impossible to reduce it into a single value
+ // .e.g the node contains css variable
+ // so we just preserve the division and let browser do it
+ return {
+ type: 'MathExpression',
+ operator: '/',
+ left: node,
+ right: {
+ type: 'Number',
+ value: divisor,
+ },
+ };
+}
+/**
+ * @param {import('../parser').MathExpression} node
+ */
+function reduceMultiplicationExpression(node) {
+ // (expr) * number
+ if (node.right.type === 'Number') {
+ return applyNumberMultiplication(node.left, node.right.value);
+ }
+ // number * (expr)
+ if (node.left.type === 'Number') {
+ return applyNumberMultiplication(node.right, node.left.value);
+ }
+ return node;
+}
+
+/**
+ * apply (expr) * number
+ * @param {number} multiplier
+ * @param {import('../parser').CalcNode} node
+ * @return {import('../parser').CalcNode}
+ */
+function applyNumberMultiplication(node, multiplier) {
+ if (isValueType(node)) {
+ node.value *= multiplier;
+ return node;
+ }
+ if (node.type === 'MathExpression' && isAddSubOperator(node.operator)) {
+ // turn (a + b) * num into a*num + b*num
+ // is good for further reduction
+ // checkout the test case
+ // "should reduce multiplication before reducing additions"
+ return {
+ type: 'MathExpression',
+ operator: node.operator,
+ left: applyNumberMultiplication(node.left, multiplier),
+ right: applyNumberMultiplication(node.right, multiplier),
+ };
+ }
+ // it is impossible to reduce it into a single value
+ // .e.g the node contains css variable
+ // so we just preserve the division and let browser do it
+ return {
+ type: 'MathExpression',
+ operator: '*',
+ left: node,
+ right: {
+ type: 'Number',
+ value: multiplier,
+ },
+ };
+}
+
+/**
+ * @param {import('../parser').ValueExpression} left
+ * @param {import('../parser').ValueExpression} right
+ * @param {number} precision
+ */
+function convertNodesUnits(left, right, precision) {
+ switch (left.type) {
+ case 'LengthValue':
+ case 'AngleValue':
+ case 'TimeValue':
+ case 'FrequencyValue':
+ case 'ResolutionValue':
+ if (right.type === left.type && right.unit && left.unit) {
+ const converted = convertUnit(
+ right.value,
+ right.unit,
+ left.unit,
+ precision
+ );
+
+ right = {
+ type: left.type,
+ value: converted,
+ unit: left.unit,
+ };
+ }
+
+ return { left, right };
+ default:
+ return { left, right };
+ }
+}
+
+/**
+ * @param {import('../parser').ParenthesizedExpression} node
+ */
+function includesNoCssProperties(node) {
+ return (
+ node.content.type !== 'Function' &&
+ (node.content.type !== 'MathExpression' ||
+ (node.content.right.type !== 'Function' &&
+ node.content.left.type !== 'Function'))
+ );
+}
+/**
+ * @param {import('../parser').CalcNode} node
+ * @param {number} precision
+ * @return {import('../parser').CalcNode}
+ */
+function reduce(node, precision) {
+ if (node.type === 'MathExpression') {
+ if (isAddSubOperator(node.operator)) {
+ // reduceAddSubExpression will call reduce recursively
+ return reduceAddSubExpression(node, precision);
+ }
+ node.left = reduce(node.left, precision);
+ node.right = reduce(node.right, precision);
+ switch (node.operator) {
+ case '/':
+ return reduceDivisionExpression(node);
+ case '*':
+ return reduceMultiplicationExpression(node);
+ }
+
+ return node;
+ }
+
+ if (node.type === 'ParenthesizedExpression') {
+ if (includesNoCssProperties(node)) {
+ return reduce(node.content, precision);
+ }
+ }
+
+ return node;
+}
+
+module.exports = reduce;
diff --git a/node_modules/postcss-calc/src/lib/stringifier.js b/node_modules/postcss-calc/src/lib/stringifier.js
new file mode 100644
index 00000000..50195bb5
--- /dev/null
+++ b/node_modules/postcss-calc/src/lib/stringifier.js
@@ -0,0 +1,98 @@
+'use strict';
+const order = {
+ '*': 0,
+ '/': 0,
+ '+': 1,
+ '-': 1,
+};
+
+/**
+ * @param {number} value
+ * @param {number | false} prec
+ */
+function round(value, prec) {
+ if (prec !== false) {
+ const precision = Math.pow(10, prec);
+ return Math.round(value * precision) / precision;
+ }
+ return value;
+}
+
+/**
+ * @param {number | false} prec
+ * @param {import('../parser').CalcNode} node
+ *
+ * @return {string}
+ */
+function stringify(node, prec) {
+ switch (node.type) {
+ case 'MathExpression': {
+ const { left, right, operator: op } = node;
+ let str = '';
+ if (left.type === 'MathExpression' && order[op] < order[left.operator]) {
+ str += `(${stringify(left, prec)})`;
+ } else {
+ str += stringify(left, prec);
+ }
+
+ str += order[op] ? ` ${node.operator} ` : node.operator;
+
+ if (
+ right.type === 'MathExpression' &&
+ order[op] < order[right.operator]
+ ) {
+ str += `(${stringify(right, prec)})`;
+ } else {
+ str += stringify(right, prec);
+ }
+
+ return str;
+ }
+ case 'Number':
+ return round(node.value, prec).toString();
+ case 'Function':
+ return node.value.toString();
+ case 'ParenthesizedExpression':
+ return `(${stringify(node.content, prec)})`;
+ default:
+ return round(node.value, prec) + node.unit;
+ }
+}
+
+/**
+ * @param {string} calc
+ * @param {import('../parser').CalcNode} node
+ * @param {string} originalValue
+ * @param {{precision: number | false, warnWhenCannotResolve: boolean}} options
+ * @param {import("postcss").Result} result
+ * @param {import("postcss").ChildNode} item
+ *
+ * @returns {string}
+ */
+module.exports = function (calc, node, originalValue, options, result, item) {
+ let str = stringify(node, options.precision);
+
+ const shouldPrintCalc =
+ node.type === 'MathExpression' || node.type === 'Function' ||
+ node.type === 'ParenthesizedExpression';
+
+ if (shouldPrintCalc) {
+ // if calc expression couldn't be resolved to a single value, re-wrap it as
+ // a calc()
+ if (node.type === 'ParenthesizedExpression') {
+ str = `${calc}${str}`;
+ } else {
+ str = `${calc}(${str})`;
+ }
+
+ // if the warnWhenCannotResolve option is on, inform the user that the calc
+ // expression could not be resolved to a single value
+ if (options.warnWhenCannotResolve) {
+ result.warn('Could not reduce expression: ' + originalValue, {
+ plugin: 'postcss-calc',
+ node: item,
+ });
+ }
+ }
+ return str;
+};
diff --git a/node_modules/postcss-calc/src/lib/transform.js b/node_modules/postcss-calc/src/lib/transform.js
new file mode 100644
index 00000000..78bff2ef
--- /dev/null
+++ b/node_modules/postcss-calc/src/lib/transform.js
@@ -0,0 +1,109 @@
+'use strict';
+const selectorParser = require('postcss-selector-parser');
+const valueParser = require('postcss-value-parser');
+
+const { parser } = require('../parser.js');
+
+const reducer = require('./reducer.js');
+const stringifier = require('./stringifier.js');
+
+const MATCH_CALC = /((?:-(moz|webkit)-)?calc)/i;
+
+/**
+ * @param {string} value
+ * @param {{precision: number, warnWhenCannotResolve: boolean}} options
+ * @param {import("postcss").Result} result
+ * @param {import("postcss").ChildNode} item
+ */
+function transformValue(value, options, result, item) {
+ return valueParser(value)
+ .walk((node) => {
+ // skip anything which isn't a calc() function
+ if (node.type !== 'function' || !MATCH_CALC.test(node.value)) {
+ return;
+ }
+
+ // stringify calc expression and produce an AST
+ const contents = valueParser.stringify(node.nodes);
+ const ast = parser.parse(contents);
+
+ // reduce AST to its simplest form, that is, either to a single value
+ // or a simplified calc expression
+ const reducedAst = reducer(ast, options.precision);
+
+ // stringify AST and write it back
+ /** @type {valueParser.Node} */ (node).type = 'word';
+ node.value = stringifier(
+ node.value,
+ reducedAst,
+ value,
+ options,
+ result,
+ item
+ );
+
+ return false;
+ })
+ .toString();
+}
+/**
+ * @param {import("postcss-selector-parser").Selectors} value
+ * @param {{precision: number, warnWhenCannotResolve: boolean}} options
+ * @param {import("postcss").Result} result
+ * @param {import("postcss").ChildNode} item
+ */
+function transformSelector(value, options, result, item) {
+ return selectorParser((selectors) => {
+ selectors.walk((node) => {
+ // attribute value
+ // e.g. the "calc(3*3)" part of "div[data-size="calc(3*3)"]"
+ if (node.type === 'attribute' && node.value) {
+ node.setValue(transformValue(node.value, options, result, item));
+ }
+
+ // tag value
+ // e.g. the "calc(3*3)" part of "div:nth-child(2n + calc(3*3))"
+ if (node.type === 'tag') {
+ node.value = transformValue(node.value, options, result, item);
+ }
+
+ return;
+ });
+ }).processSync(value);
+}
+
+/**
+ * @param {any} node
+ * @param {{precision: number, preserve: boolean, warnWhenCannotResolve: boolean}} options
+ * @param {'value'|'params'|'selector'} property
+ * @param {import("postcss").Result} result
+ */
+module.exports = (node, property, options, result) => {
+ let value = node[property];
+
+ try {
+ value =
+ property === 'selector'
+ ? transformSelector(node[property], options, result, node)
+ : transformValue(node[property], options, result, node);
+ } catch (error) {
+ if (error instanceof Error) {
+ result.warn(error.message, { node });
+ } else {
+ result.warn('Error', { node });
+ }
+ return;
+ }
+
+ // if the preserve option is enabled and the value has changed, write the
+ // transformed value into a cloned node which is inserted before the current
+ // node, preserving the original value. Otherwise, overwrite the original
+ // value.
+ if (options.preserve && node[property] !== value) {
+ const clone = node.clone();
+ clone[property] = value;
+ node.parent.insertBefore(node, clone);
+ } else {
+ node[property] = value;
+ }
+};