UNPKG

@parsify/math

Version:

Parsify plugin for mathematical expressions & unit conversions

58 lines (57 loc) 2.71 kB
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, };