From dbc3b4200d74e6c863c7806d21b45b363ae6b7da Mon Sep 17 00:00:00 2001 From: ed eustace Date: Thu, 9 Sep 2021 12:07:15 +0100 Subject: [PATCH] fix: Remove circular dependency When bundling mathjax - you are warned that there is a circular dependency: ```shell code: CIRCULAR_DEPENDENCY, message: Circular dependency: node_modules/mathjax-full/js/input/tex/TexParser.js -> node_modules/mathjax-full/js/input/tex/ParseUtil.js -> node_modules/mathjax-full/js/input/tex/TexParser.js ``` This can lead to runtime errors as the bundler has to guess which module to define first. This commit removes this circular dependency. > Note: there are still other circular dependencies in place that should also be fixed: ```shell 1) components/loader.js > components/package.js 2) input/tex/mathtools/MathtoolsConfiguration.js > input/tex/mathtools/MathtoolsMappings.js > input/tex/mathtools/MathtoolsMethods.js > input/tex/mathtools/MathtoolsUtil.js 3) input/tex/mathtools/MathtoolsMethods.js > input/tex/mathtools/MathtoolsUtil.js 4) output/svg.js > output/svg/WrapperFactory.js > output/svg/Wrappers.js > output/svg/Wrapper.js ``` --- ts/input/tex/ParseUtil.ts | 80 +--------------------- ts/input/tex/TexParser.ts | 9 +-- ts/input/tex/ams/AmsMethods.ts | 3 +- ts/input/tex/base/BaseItems.ts | 9 +-- ts/input/tex/base/BaseMethods.ts | 4 +- ts/input/tex/bussproofs/BussproofsUtil.ts | 8 +-- ts/input/tex/mathtools/MathtoolsMethods.ts | 4 +- ts/input/tex/mathtools/MathtoolsUtil.ts | 5 +- ts/util/lengths.ts | 75 ++++++++++++++++++++ ts/util/string.ts | 16 +++++ 10 files changed, 115 insertions(+), 98 deletions(-) diff --git a/ts/input/tex/ParseUtil.ts b/ts/input/tex/ParseUtil.ts index 9bd3427c0..98d419e76 100644 --- a/ts/input/tex/ParseUtil.ts +++ b/ts/input/tex/ParseUtil.ts @@ -31,87 +31,9 @@ import TexParser from './TexParser.js'; import TexError from './TexError.js'; import {entities} from '../../util/Entities.js'; import {MmlMunderover} from '../../core/MmlTree/MmlNodes/munderover.js'; - - +import {Em} from '../../util/lengths.js'; namespace ParseUtil { - // TODO (VS): Combine some of this with lengths in util. - const emPerInch = 7.2; - const pxPerInch = 72; - // Note, the following are TeX CM font values. - const UNIT_CASES: {[key: string]: ((m: number) => number)} = { - 'em': m => m, - 'ex': m => m * .43, - 'pt': m => m / 10, // 10 pt to an em - 'pc': m => m * 1.2, // 12 pt to a pc - 'px': m => m * emPerInch / pxPerInch, - 'in': m => m * emPerInch, - 'cm': m => m * emPerInch / 2.54, // 2.54 cm to an inch - 'mm': m => m * emPerInch / 25.4, // 10 mm to a cm - 'mu': m => m / 18, - }; - const num = '([-+]?([.,]\\d+|\\d+([.,]\\d*)?))'; - const unit = '(pt|em|ex|mu|px|mm|cm|in|pc)'; - const dimenEnd = RegExp('^\\s*' + num + '\\s*' + unit + '\\s*$'); - const dimenRest = RegExp('^\\s*' + num + '\\s*' + unit + ' ?'); - - - /** - * Matches for a dimension argument. - * @param {string} dim The argument. - * @param {boolean} rest Allow for trailing garbage in the dimension string. - * @return {[string, string, number]} The match result as (Anglosaxon) value, - * unit name, length of matched string. The latter is interesting in the - * case of trailing garbage. - */ - export function matchDimen( - dim: string, rest: boolean = false): [string, string, number] { - let match = dim.match(rest ? dimenRest : dimenEnd); - return match ? - muReplace([match[1].replace(/,/, '.'), match[4], match[0].length]) : - [null, null, 0]; - } - - - /** - * Transforms mu dimension to em if necessary. - * @param {[string, string, number]} [value, unit, length] The dimension triple. - * @return {[string, string, number]} [value, unit, length] The transformed triple. - */ - function muReplace([value, unit, length]: [string, string, number]): [string, string, number] { - if (unit !== 'mu') { - return [value, unit, length]; - } - let em = Em(UNIT_CASES[unit](parseFloat(value || '1'))); - return [em.slice(0, -2), 'em', length]; - } - - - /** - * Convert a dimension string into standard em dimension. - * @param {string} dim The attribute string. - * @return {number} The numerical value. - */ - export function dimen2em(dim: string): number { - let [value, unit] = matchDimen(dim); - let m = parseFloat(value || '1'); - let func = UNIT_CASES[unit]; - return func ? func(m) : 0; - } - - - /** - * Turns a number into an em value. - * @param {number} m The number. - * @return {string} The em dimension string. - */ - export function Em(m: number): string { - if (Math.abs(m) < .0006) { - return '0em'; - } - return m.toFixed(3).replace(/\.?0+$/, '') + 'em'; - } - /** * Takes an array of numbers and returns a space-separated string of em values. diff --git a/ts/input/tex/TexParser.ts b/ts/input/tex/TexParser.ts index f8706c57f..18d952a96 100644 --- a/ts/input/tex/TexParser.ts +++ b/ts/input/tex/TexParser.ts @@ -23,7 +23,6 @@ * @author v.sorge@mathjax.org (Volker Sorge) */ -import ParseUtil from './ParseUtil.js'; import {HandlerType} from './MapHandler.js'; import Stack from './Stack.js'; import StackItemFactory from './StackItemFactory.js'; @@ -35,6 +34,8 @@ import ParseOptions from './ParseOptions.js'; import {StackItem, EnvList} from './StackItem.js'; import {Symbol} from './Symbol.js'; import {OptionList} from '../../util/Options.js'; +import {matchDimen} from '../../util/lengths.js'; +import {trimSpaces} from '../../util/string.js'; /** @@ -392,7 +393,7 @@ export default class TexParser { public GetDimen(name: string): string { if (this.GetNext() === '{') { let dimen = this.GetArgument(name); - let [value, unit] = ParseUtil.matchDimen(dimen); + let [value, unit] = matchDimen(dimen); if (value) { // @test Raise In Line, Lower 2, (Raise|Lower) Negative return value + unit; @@ -400,7 +401,7 @@ export default class TexParser { } else { // @test Above, Raise, Lower, Modulo, Above With Delims let dimen = this.string.slice(this.i); - let [value, unit, length] = ParseUtil.matchDimen(dimen, true); + let [value, unit, length] = matchDimen(dimen, true); if (value) { this.i += length; return value + unit; @@ -475,7 +476,7 @@ export default class TexParser { * @return {string} The delimiter. */ public GetDelimiterArg(name: string): string { - let c = ParseUtil.trimSpaces(this.GetArgument(name)); + let c = trimSpaces(this.GetArgument(name)); if (c === '') { return null; } diff --git a/ts/input/tex/ams/AmsMethods.ts b/ts/input/tex/ams/AmsMethods.ts index dfaa98e79..e040a91d3 100644 --- a/ts/input/tex/ams/AmsMethods.ts +++ b/ts/input/tex/ams/AmsMethods.ts @@ -37,6 +37,7 @@ import {FlalignItem} from './AmsItems.js'; import BaseMethods from '../base/BaseMethods.js'; import {TEXCLASS} from '../../../core/MmlTree/MmlNode.js'; import {MmlMunderover} from '../../../core/MmlTree/MmlNodes/munderover.js'; +import {Em} from '../../../util/lengths.js'; // Namespace @@ -289,7 +290,7 @@ AmsMethods.MultiIntegral = function(parser: TexParser, name: string, */ AmsMethods.xArrow = function(parser: TexParser, name: string, chr: number, l: number, r: number) { - let def = {width: '+' + ParseUtil.Em((l + r) / 18), lspace: ParseUtil.Em(l / 18)}; + let def = {width: '+' + Em((l + r) / 18), lspace: Em(l / 18)}; let bot = parser.GetBrackets(name); let first = parser.ParseArg(name); let dstrut = parser.create('node', 'mspace', [], {depth: '.25em'}); diff --git a/ts/input/tex/base/BaseItems.ts b/ts/input/tex/base/BaseItems.ts index 04a751396..c0641a300 100644 --- a/ts/input/tex/base/BaseItems.ts +++ b/ts/input/tex/base/BaseItems.ts @@ -34,6 +34,7 @@ import NodeUtil from '../NodeUtil.js'; import {Property} from '../../../core/Tree/Node.js'; import StackItemFactory from '../StackItemFactory.js'; import {CheckType, BaseItem, StackItem, EnvList} from '../StackItem.js'; +import { dimen2em, Em } from '../../../util/lengths.js'; /** @@ -1082,15 +1083,15 @@ export class ArrayItem extends BaseItem { const rows = (this.arraydef['rowspacing'] as string).split(/ /); if (!this.getProperty('rowspacing')) { // @test Array Custom Linebreak - let dimem = ParseUtil.dimen2em(rows[0]); + let dimem = dimen2em(rows[0]); this.setProperty('rowspacing', dimem); } const rowspacing = this.getProperty('rowspacing') as number; while (rows.length < this.table.length) { - rows.push(ParseUtil.Em(rowspacing)); + rows.push(Em(rowspacing)); } - rows[this.table.length - 1] = ParseUtil.Em( - Math.max(0, rowspacing + ParseUtil.dimen2em(spacing))); + rows[this.table.length - 1] = Em( + Math.max(0, rowspacing + dimen2em(spacing))); this.arraydef['rowspacing'] = rows.join(' '); } } diff --git a/ts/input/tex/base/BaseMethods.ts b/ts/input/tex/base/BaseMethods.ts index 9cb0651a0..a80f484a0 100644 --- a/ts/input/tex/base/BaseMethods.ts +++ b/ts/input/tex/base/BaseMethods.ts @@ -35,7 +35,7 @@ import {MmlNode, TEXCLASS} from '../../../core/MmlTree/MmlNode.js'; import {MmlMsubsup} from '../../../core/MmlTree/MmlNodes/msubsup.js'; import {MmlMunderover} from '../../../core/MmlTree/MmlNodes/munderover.js'; import {Label} from '../Tags.js'; -import {em} from '../../../util/lengths.js'; +import {em, matchDimen} from '../../../util/lengths.js'; import {entities} from '../../../util/Entities.js'; import {lookup} from '../../../util/Options.js'; @@ -1239,7 +1239,7 @@ BaseMethods.CrLaTeX = function(parser: TexParser, name: string, nobrackets: bool } if (parser.string.charAt(parser.i) === '[') { let dim = parser.GetBrackets(name, ''); - let [value, unit, ] = ParseUtil.matchDimen(dim); + let [value, unit, ] = matchDimen(dim); // @test Custom Linebreak if (dim && !value) { // @test Dimension Error diff --git a/ts/input/tex/bussproofs/BussproofsUtil.ts b/ts/input/tex/bussproofs/BussproofsUtil.ts index e572bc4b5..ca7e15954 100644 --- a/ts/input/tex/bussproofs/BussproofsUtil.ts +++ b/ts/input/tex/bussproofs/BussproofsUtil.ts @@ -25,12 +25,12 @@ import ParseOptions from '../ParseOptions.js'; import NodeUtil from '../NodeUtil.js'; -import ParseUtil from '../ParseUtil.js'; import {MmlNode} from '../../../core/MmlTree/MmlNode.js'; import {Property} from '../../../core/Tree/Node.js'; import {MathItem} from '../../../core/MathItem.js'; import {MathDocument} from '../../../core/MathDocument.js'; +import {dimen2em, Em} from '../../../util/lengths.js'; type MATHITEM = MathItem; @@ -270,12 +270,12 @@ let addSpace = function(config: ParseOptions, inf: MmlNode, if (NodeUtil.isType(mspace, 'mspace')) { NodeUtil.setAttribute( mspace, 'width', - ParseUtil.Em(ParseUtil.dimen2em( + Em(dimen2em( NodeUtil.getAttribute(mspace, 'width') as string) + space)); return; } mspace = config.nodeFactory.create('node', 'mspace', [], - {width: ParseUtil.Em(space)}); + {width: Em(space)}); if (right) { inf.appendChild(mspace); return; @@ -362,7 +362,7 @@ let adjustSequents = function(config: ParseOptions) { const addSequentSpace = function(config: ParseOptions, sequent: MmlNode, position: number, direction: string, width: number) { let mspace = config.nodeFactory.create('node', 'mspace', [], - {width: ParseUtil.Em(width)}); + {width: Em(width)}); if (direction === 'left') { let row = sequent.childNodes[position].childNodes[0] as MmlNode; mspace.parent = row; diff --git a/ts/input/tex/mathtools/MathtoolsMethods.ts b/ts/input/tex/mathtools/MathtoolsMethods.ts index 91f8ee980..c8b003f3c 100644 --- a/ts/input/tex/mathtools/MathtoolsMethods.ts +++ b/ts/input/tex/mathtools/MathtoolsMethods.ts @@ -32,7 +32,7 @@ import TexParser from '../TexParser.js'; import TexError from '../TexError.js'; import NodeUtil from '../NodeUtil.js'; import {TEXCLASS} from '../../../core/MmlTree/MmlNode.js'; -import {length2em, em} from '../../../util/lengths.js'; +import {length2em, em, Em} from '../../../util/lengths.js'; import {lookup} from '../../../util/Options.js'; import NewcommandUtil from '../newcommand/NewcommandUtil.js'; import NewcommandMethods from '../newcommand/NewcommandMethods.js'; @@ -74,7 +74,7 @@ export const MathtoolsMethods: Record = { align = parser.GetBrackets(`\\begin{${begin.getName()}}`, parser.options.mathtools['smallmatrix-align']); } return MathtoolsMethods.Array( - parser, begin, open, close, align, ParseUtil.Em(1 / 3), '.2em', 'S', 1 + parser, begin, open, close, align, Em(1 / 3), '.2em', 'S', 1 ); }, diff --git a/ts/input/tex/mathtools/MathtoolsUtil.ts b/ts/input/tex/mathtools/MathtoolsUtil.ts index 653009b02..fda06bff2 100644 --- a/ts/input/tex/mathtools/MathtoolsUtil.ts +++ b/ts/input/tex/mathtools/MathtoolsUtil.ts @@ -32,6 +32,7 @@ import {MmlNode} from '../../../core/MmlTree/MmlNode.js'; import {MathtoolsMethods} from './MathtoolsMethods.js'; import {PAIREDDELIMS} from './MathtoolsConfiguration.js'; +import { dimen2em, Em } from '../../../util/lengths.js'; /** * Utility functions for the Mathtools package. @@ -98,10 +99,10 @@ export const MathtoolsUtil = { if (!mtable.isKind('mtable')) return; let rowspacing = mtable.attributes.get('rowspacing') as string; if (rowspacing) { - const add = ParseUtil.dimen2em(spread); + const add = dimen2em(spread); rowspacing = rowspacing .split(/ /) - .map(s => ParseUtil.Em(Math.max(0, ParseUtil.dimen2em(s) + add))) + .map(s => Em(Math.max(0, dimen2em(s) + add))) .join(' '); } else { rowspacing = spread; diff --git a/ts/util/lengths.ts b/ts/util/lengths.ts index dcccb1961..8a2a1715d 100644 --- a/ts/util/lengths.ts +++ b/ts/util/lengths.ts @@ -155,3 +155,78 @@ export function px(m: number, M: number = -BIGDIMEN, em: number = 16): string { if (Math.abs(m) < .1) return '0'; return m.toFixed(1).replace(/\.0$/, '') + 'px'; } + +// TODO (VS): Combine some of this with lengths in util. +const emPerInch = 7.2; +const pxPerInch = 72; +// Note, the following are TeX CM font values. +const UNIT_CASES: { [key: string]: (m: number) => number } = { + em: (m) => m, + ex: (m) => m * 0.43, + pt: (m) => m / 10, // 10 pt to an em + pc: (m) => m * 1.2, // 12 pt to a pc + px: (m) => (m * emPerInch) / pxPerInch, + in: (m) => m * emPerInch, + cm: (m) => (m * emPerInch) / 2.54, // 2.54 cm to an inch + mm: (m) => (m * emPerInch) / 25.4, // 10 mm to a cm + mu: (m) => m / 18, +}; +const num = '([-+]?([.,]\\d+|\\d+([.,]\\d*)?))'; +const unit = '(pt|em|ex|mu|px|mm|cm|in|pc)'; +const dimenEnd = RegExp('^\\s*' + num + '\\s*' + unit + '\\s*$'); +const dimenRest = RegExp('^\\s*' + num + '\\s*' + unit + ' ?'); +/** + * Matches for a dimension argument. + * @param {string} dim The argument. + * @param {boolean} rest Allow for trailing garbage in the dimension string. + * @return {[string, string, number]} The match result as (Anglosaxon) value, + * unit name, length of matched string. The latter is interesting in the + * case of trailing garbage. + */ +export function matchDimen( + dim: string, + rest: boolean = false +): [string, string, number] { + let match = dim.match(rest ? dimenRest : dimenEnd); + return match + ? muReplace([match[1].replace(/,/, '.'), match[4], match[0].length]) + : [null, null, 0]; +} +/** + * Convert a dimension string into standard em dimension. + * @param {string} dim The attribute string. + * @return {number} The numerical value. + */ +export function dimen2em(dim: string): number { + let [value, unit] = matchDimen(dim); + let m = parseFloat(value || '1'); + let func = UNIT_CASES[unit]; + return func ? func(m) : 0; +} +/** + * Transforms mu dimension to em if necessary. + * @param {[string, string, number]} [value, unit, length] The dimension triple. + * @return {[string, string, number]} [value, unit, length] The transformed triple. + */ +function muReplace([value, unit, length]: [string, string, number]): [ + string, + string, + number +] { + if (unit !== 'mu') { + return [value, unit, length]; + } + let em = Em(UNIT_CASES[unit](parseFloat(value || '1'))); + return [em.slice(0, -2), 'em', length]; +} +/** + * Turns a number into an em value. + * @param {number} m The number. + * @return {string} The em dimension string. + */ +export function Em(m: number): string { + if (Math.abs(m) < 0.0006) { + return '0em'; + } + return m.toFixed(3).replace(/\.?0+$/, '') + 'em'; +} \ No newline at end of file diff --git a/ts/util/string.ts b/ts/util/string.ts index 7a24b1a14..921bc0a99 100644 --- a/ts/util/string.ts +++ b/ts/util/string.ts @@ -82,3 +82,19 @@ export function isPercent(x: string): boolean { export function split(x: string): string[] { return x.trim().split(/\s+/); } + +/** + * Trim spaces from a string. + * @param {string} text The string to clean. + * @return {string} The string with leading and trailing whitespace removed. + */ +export function trimSpaces(text: string): string { + if (typeof text !== 'string') { + return text; + } + let TEXT = text.trim(); + if (TEXT.match(/\\$/) && text.match(/ $/)) { + TEXT += ' '; + } + return TEXT; +} \ No newline at end of file