Skip to content

Commit

Permalink
feat: avoid fatal error (#137)
Browse files Browse the repository at this point in the history
  • Loading branch information
alexander-akait authored Dec 29, 2021
1 parent cefd2c3 commit 125be6a
Show file tree
Hide file tree
Showing 3 changed files with 4,375 additions and 4,902 deletions.
29 changes: 20 additions & 9 deletions src/__tests__/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,34 +5,36 @@ import reduceCalc from '../../dist';

const postcssOpts = { from: undefined };

function testValue(t, fixture, expected = null, opts = {}) {
function testValue(t, fixture, expected = null, opts = {}, plan = 1) {
if (expected === null) {
expected = fixture;
}

fixture = `foo{bar:${fixture}}`;
expected = `foo{bar:${expected}}`;

return testCss(t, fixture, expected, opts);
return testCss(t, fixture, expected, opts, plan);
}

function testCss(t, fixture, expected = null, opts = {}) {
function testCss(t, fixture, expected = null, opts = {}, plan = 1) {
if (expected === null) {
expected = fixture;
}

t.plan(1);
t.plan(plan);

return postcss(reduceCalc(opts)).process(fixture, postcssOpts).then(result => {
t.deepEqual(result.css, expected);

return result;
});
}

async function testThrows(t, fixture, expected, opts) {
fixture = `foo{bar:${fixture}}`;
async function testThrows(t, fixture, expected, warning, opts) {
const result = await testValue(t, fixture, expected, opts, 2);
const warnings = result.warnings();

const error = await t.throwsAsync(() => postcss(reduceCalc(opts)).process(fixture, postcssOpts))
t.is(error.message, expected);
t.is(warnings[0].text, warning);
}

test(
Expand Down Expand Up @@ -523,7 +525,6 @@ test(
'calc(100% + 1px)',
'calc(100% + 1px)',
{ warnWhenCannotResolve: true },
[ /^Could not reduce expression:/ ]
);

test(
Expand Down Expand Up @@ -678,13 +679,15 @@ test(
'should throw an exception when attempting to divide by zero',
testThrows,
'calc(500px/0)',
'calc(500px/0)',
'Cannot divide by zero'
);

test(
'should throw an exception when attempting to divide by unit (#1)',
testThrows,
'calc(500px/2px)',
'calc(500px/2px)',
'Cannot divide by "px", number expected',
);

Expand Down Expand Up @@ -1170,3 +1173,11 @@ test(
'calc(1px + 2unknown)',
'calc(1px + 2unknown)'
);

test(
'error with parsing',
testThrows,
'calc(10pc + unknown)',
'calc(10pc + unknown)',
'Lexical error on line 1: Unrecognized text.\n\n Erroneous area:\n1: 10pc + unknown\n^.........^'
);
76 changes: 44 additions & 32 deletions src/lib/transform.js
Original file line number Diff line number Diff line change
@@ -1,55 +1,58 @@
import selectorParser from 'postcss-selector-parser';
import valueParser from 'postcss-value-parser';
import selectorParser from "postcss-selector-parser";
import valueParser from "postcss-value-parser";

// eslint-disable-next-line import/no-unresolved
import { parser } from '../parser';
import { parser } from "../parser";

import reducer from './reducer';
import stringifier from './stringifier';
import reducer from "./reducer";
import stringifier from "./stringifier";

const MATCH_CALC = /((?:-(moz|webkit)-)?calc)/i;

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 node;
}
return valueParser(value)
.walk(node => {
// skip anything which isn't a calc() function
if (node.type !== "function" || !MATCH_CALC.test(node.value)) {
return node;
}

// stringify calc expression and produce an AST
const contents = valueParser.stringify(node.nodes);
const ast = parser.parse(contents);
// 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);
// 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
node.type = 'word';
node.value = stringifier(
node.value,
reducedAst,
value,
options,
result,
item);
// stringify AST and write it back
node.type = "word";
node.value = stringifier(
node.value,
reducedAst,
value,
options,
result,
item
);

return false;
}).toString();
return false;
})
.toString();
}

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) {
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') {
if (node.type === "tag") {
node.value = transformValue(node.value, options, result, item);
}

Expand All @@ -59,9 +62,18 @@ function transformSelector(value, options, result, item) {
}

export default (node, property, options, result) => {
const value = property === "selector"
? transformSelector(node[property], options, result, node)
: transformValue(node[property], options, result, node);
let value = node[property];

try {
value =
property === "selector"
? transformSelector(node[property], options, result, node)
: transformValue(node[property], options, result, node);
} catch (error) {
result.warn(error.message, { 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
Expand Down
Loading

0 comments on commit 125be6a

Please sign in to comment.