@parsify/math
Version:
Parsify plugin for mathematical expressions & unit conversions
58 lines (57 loc) • 2.71 kB
JavaScript
import process from 'node:process';
import { create, all } from 'mathjs';
import { getElementIndex } from './utils/get-element-index.js';
import { WHITELISTED_SYMBOLS } from './utils/whitelist.js';
const scope = new Map();
const math = create(all, {});
// @ts-expect-error Undocumented feature
math.Unit.setUnitSystem('si');
const isAlphaOriginal = math.parse.isAlpha;
math.parse.isAlpha = (current, previous, next) => isAlphaOriginal(current, previous, next)
|| WHITELISTED_SYMBOLS.includes(current);
// @ts-expect-error Undocumented feature
math.Unit.isValidAlpha = math.parse.isAlpha;
export default ({ precision = 4, customUnits, locale = 'en-US', } = {}) => async (expression) => {
try {
// Replace word operators with sign ones
expression = expression.replace(/plus|and|with /, '+');
expression = expression.replace(/minus|subtract|without/, '-');
expression = expression.replace(/times|multiplied by/, '*');
expression = expression.replace('divided by', '/');
expression = expression.replace('mod', '%');
// Percentage operations with of, off, or on
if (/ off? | on /.test(expression)) {
let updatedExpression = '';
const expressionArray = expression.split(' ');
const number = (name) => expressionArray.slice(0, getElementIndex(expressionArray, name)).join(' ').replace('%', '');
const total = (name) => expressionArray.slice(getElementIndex(expressionArray, name) + 1).join(' ');
if (expression.includes(' of ')) {
updatedExpression = `(${number('of')} / 100) * (${total('of')})`;
}
else if (/ on /i.test(expression)) {
updatedExpression = `(${total('on')}) + ((${number('on')}) / 100) * (${total('on')})`;
}
else if (/ off /i.test(expression)) {
updatedExpression = `(${total('off')}) - ((${number('off')}) / 100) * (${total('off')})`;
}
// Validate percentage operations
if (await math.evaluate(updatedExpression, scope).toString().includes('*') === false) {
expression = updatedExpression;
}
}
if (customUnits) {
math.createUnit(customUnits, { override: true });
}
return math.format(await math.evaluate(expression, scope), number => number.toLocaleString(locale, {
maximumFractionDigits: precision,
}));
// eslint-disable-next-line @typescript-eslint/no-implicit-any-catch
}
catch (error) {
if (process.env['DEBUG']) {
return error.message;
}
return expression;
}
};
export { scope, };