From 19bdee03d7f9ac68471424fd62735094d3c75724 Mon Sep 17 00:00:00 2001 From: Ludovico Fischer Date: Thu, 30 Jan 2025 15:44:32 +0100 Subject: [PATCH] fix: handle calc keywords correctly Fix #210 --- parser.jison | 7 +++++++ src/lib/reducer.js | 3 +++ src/lib/stringifier.js | 8 +++++++- src/parser.d.ts | 7 ++++++- test/index.js | 27 +++++++++++++++++++++++++++ 5 files changed, 50 insertions(+), 2 deletions(-) diff --git a/parser.jison b/parser.jison index b46b718..575aabd 100644 --- a/parser.jison +++ b/parser.jison @@ -73,6 +73,8 @@ (([0-9]+("."[0-9]+)?|"."[0-9]+)(e(\+|-)[0-9]+)?)dppx\b return 'RES'; (([0-9]+("."[0-9]+)?|"."[0-9]+)(e(\+|-)[0-9]+)?)\% return 'PERCENTAGE'; (([0-9]+("."[0-9]+)?|"."[0-9]+)(e(\+|-)[0-9]+)?)\b return 'NUMBER'; +("infinity"|"pi"|"e")\b return 'CALC_KEYWORD'; + (([0-9]+("."[0-9]+)?|"."[0-9]+)(e(\+|-)[0-9]+)?)-?([a-zA-Z_]|[\240-\377]|(\\[0-9a-fA-F]{1,6}(\r\n|[ \t\r\n\f])?|\\[^\r\n\f0-9a-fA-F]))([a-zA-Z0-9_-]|[\240-\377]|(\\[0-9a-fA-F]{1,6}(\r\n|[ \t\r\n\f])?|\\[^\r\n\f0-9a-fA-F]))*\b return 'UNKNOWN_DIMENSION'; @@ -106,6 +108,7 @@ expression | function { $$ = $1; } | dimension { $$ = $1; } | number { $$ = $1; } + | calc_keyword { $$ = $1; } ; function @@ -160,6 +163,10 @@ expression | SUB dimension { var prev = $2; prev.value *= -1; $$ = prev; } ; + calc_keyword + : CALC_KEYWORD { $$ = { type: 'CalcKeyword', value: $1 }; } + ; + number : NUMBER { $$ = { type: 'Number', value: parseFloat($1) }; } | ADD NUMBER { $$ = { type: 'Number', value: parseFloat($2) }; } diff --git a/src/lib/reducer.js b/src/lib/reducer.js index 92cc4a3..7828a2e 100644 --- a/src/lib/reducer.js +++ b/src/lib/reducer.js @@ -361,6 +361,9 @@ function includesNoCssProperties(node) { * @return {import('../parser').CalcNode} */ function reduce(node, precision) { + if (node.type === 'MathExpression' && (node.left.type === 'CalcKeyword' || node.right.type === 'CalcKeyword')) { + return node; + } if (node.type === 'MathExpression') { if (isAddSubOperator(node.operator)) { // reduceAddSubExpression will call reduce recursively diff --git a/src/lib/stringifier.js b/src/lib/stringifier.js index 50195bb..51eea01 100644 --- a/src/lib/stringifier.js +++ b/src/lib/stringifier.js @@ -31,6 +31,8 @@ function stringify(node, prec) { let str = ''; if (left.type === 'MathExpression' && order[op] < order[left.operator]) { str += `(${stringify(left, prec)})`; + } else if (left.type === 'CalcKeyword') { + str += left.value; } else { str += stringify(left, prec); } @@ -42,6 +44,8 @@ function stringify(node, prec) { order[op] < order[right.operator] ) { str += `(${stringify(right, prec)})`; + } else if (right.type === 'CalcKeyword') { + str += right.value; } else { str += stringify(right, prec); } @@ -54,6 +58,8 @@ function stringify(node, prec) { return node.value.toString(); case 'ParenthesizedExpression': return `(${stringify(node.content, prec)})`; + case 'CalcKeyword': + return node.value; default: return round(node.value, prec) + node.unit; } @@ -74,7 +80,7 @@ module.exports = function (calc, node, originalValue, options, result, item) { const shouldPrintCalc = node.type === 'MathExpression' || node.type === 'Function' || - node.type === 'ParenthesizedExpression'; + node.type === 'ParenthesizedExpression' || node.type === 'CalcKeyword'; if (shouldPrintCalc) { // if calc expression couldn't be resolved to a single value, re-wrap it as diff --git a/src/parser.d.ts b/src/parser.d.ts index e3a19fb..1d46d67 100644 --- a/src/parser.d.ts +++ b/src/parser.d.ts @@ -68,9 +68,14 @@ export interface FunctionExpression { value: string; } +export interface CalcKeywordExpression { + type: 'CalcKeyword'; + value: string; +} + export type ValueExpression = DimensionExpression | NumberExpression; -export type CalcNode = MathExpression | ValueExpression | FunctionExpression | ParenthesizedExpression; +export type CalcNode = MathExpression | ValueExpression | FunctionExpression | ParenthesizedExpression | CalcKeywordExpression; export interface Parser { parse: (arg: string) => CalcNode; diff --git a/test/index.js b/test/index.js index a1ebe82..677403a 100644 --- a/test/index.js +++ b/test/index.js @@ -259,6 +259,33 @@ test( ) ); + +test( + 'should ignore multiplication with infinity', +testValue('calc(infinity * 1px)', 'calc(infinity*1px)') +); + +test( + 'should ignore addition with infinity', +testValue('calc(infinity + 1px)', 'calc(infinity + 1px)') +); + +test( + 'should ignore multiplication with pi', +testValue('calc(1px * pi)', 'calc(1px*pi)') +); + +test( + 'should ignore addition with pi', +testValue('calc(43 + pi)', 'calc(43 + pi)') +); + +test( + 'should preserve e', +testValue('calc(e)', 'calc(e)') +); + + test( 'should reduce calc with newline characters', testValue('calc(\n1rem \n* 2 \n* 1.5)', '3rem')