diff --git a/package.json b/package.json index 9017a5936..e1709730e 100644 --- a/package.json +++ b/package.json @@ -73,6 +73,7 @@ "fast-clone": "^1.5.13", "graphlib": "^2.1.8", "khroma": "^2.0.0", + "lodash": "^4.17.21", "moment-mini": "^2.24.0", "non-layered-tidy-tree-layout": "^2.0.2", "stylis": "^4.0.10" @@ -88,6 +89,7 @@ "@types/d3": "^7.4.0", "@types/dompurify": "^2.3.4", "@types/jest": "^28.1.7", + "@types/lodash": "^4.14.184", "@types/stylis": "^4.0.2", "@typescript-eslint/eslint-plugin": "^5.36.1", "@typescript-eslint/parser": "^5.36.1", diff --git a/src/utils.spec.js b/src/utils.spec.js index ebaa1415c..d024da3db 100644 --- a/src/utils.spec.js +++ b/src/utils.spec.js @@ -2,6 +2,7 @@ import utils from './utils'; import assignWithDepth from './assignWithDepth'; import { detectType } from './diagram-api/detectType'; import { addDiagrams } from './diagram-api/diagram-orchestration'; +import memoize from 'lodash/memoize'; addDiagrams(); describe('when assignWithDepth: should merge objects within objects', function () { @@ -121,20 +122,30 @@ describe('when assignWithDepth: should merge objects within objects', function ( }); describe('when memoizing', function () { it('should return the same value', function () { - const fib = utils.memoize(function (n, canary) { - canary.flag = true; - if (n < 2) { - return 1; - } else { - //We'll console.log a loader every time we have to recurse - return fib(n - 2, canary) + fib(n - 1, canary); - } - }); + const fib = memoize( + function (n, x, canary) { + canary.flag = true; + if (n < 2) { + return 1; + } else { + //We'll console.log a loader every time we have to recurse + return fib(n - 2, x, canary) + fib(n - 1, x, canary); + } + }, + (n, x, _) => `${n}${x}` + ); let canary = { flag: false }; - fib(10, canary); + fib(10, 'a', canary); expect(canary.flag).toBe(true); canary = { flag: false }; - fib(10, canary); + fib(10, 'a', canary); + expect(canary.flag).toBe(false); + fib(10, 'b', canary); + fib(10, 'b', canary); + expect(canary.flag).toBe(true); + canary = { flag: false }; + fib(10, 'b', canary); + fib(10, 'a', canary); expect(canary.flag).toBe(false); }); }); diff --git a/src/utils.ts b/src/utils.ts index f88327367..7cd700e85 100644 --- a/src/utils.ts +++ b/src/utils.ts @@ -20,6 +20,7 @@ import { log } from './logger'; import { detectType } from './diagram-api/detectType'; import assignWithDepth from './assignWithDepth'; import { MermaidConfig } from './config.type'; +import memoize from 'lodash/memoize'; // Effectively an enum of the supported curve types, accessible by name const d3CurveTypes = { @@ -43,9 +44,10 @@ const anyComment = /\s*%%.*\n/gm; /** * @function detectInit Detects the init config object from the text - * * @param config + * * ```mermaid + * * %%{init: {"theme": "debug", "logLevel": 1 }}%% * graph LR * a-->b @@ -165,27 +167,6 @@ export const detectDirective = function (text, type = null) { } }; -/** - * Caches results of functions based on input - * - * @param {Function} fn Function to run - * @param {Function} resolver Function that resolves to an ID given arguments the `fn` takes - * @returns {Function} An optimized caching function - */ -const memoize = (fn, resolver) => { - let cache = {}; - return (...args) => { - let n = resolver ? resolver.apply(this, args) : args[0]; - if (n in cache) { - return cache[n]; - } else { - let result = fn(...args); - cache[n] = result; - return result; - } - }; -}; - /** * @function isSubstringInArray Detects whether a substring in present in a given array * @param {string} str The substring to detect @@ -594,7 +575,7 @@ export const wrapLabel = memoize( return completedLines.filter((line) => line !== '').join(config.joinWith); }, (label, maxWidth, config) => - `${label}-${maxWidth}-${config.fontSize}-${config.fontWeight}-${config.fontFamily}-${config.joinWith}` + `${label}${maxWidth}${config.fontSize}${config.fontWeight}${config.fontFamily}${config.joinWith}` ); const breakString = memoize( @@ -622,7 +603,7 @@ const breakString = memoize( return { hyphenatedStrings: lines, remainingWord: currentLine }; }, (word, maxWidth, hyphenCharacter = '-', config) => - `${word}-${maxWidth}-${hyphenCharacter}-${config.fontSize}-${config.fontWeight}-${config.fontFamily}` + `${word}${maxWidth}${hyphenCharacter}${config.fontSize}${config.fontWeight}${config.fontFamily}` ); /** @@ -632,7 +613,8 @@ const breakString = memoize( * If the wrapped text text has greater height, we extend the height, so it's value won't overflow. * * @param {any} text The text to measure - * @param {any} config - The config for fontSize, fontFamily, and fontWeight all impacting the resulting size + * @param {any} config - The config for fontSize, fontFamily, and fontWeight all impacting the + * resulting size * @returns {any} - The height for the given text */ export const calculateTextHeight = function (text, config) { @@ -647,7 +629,8 @@ export const calculateTextHeight = function (text, config) { * This calculates the width of the given text, font size and family. * * @param {any} text - The text to calculate the width of - * @param {any} config - The config for fontSize, fontFamily, and fontWeight all impacting the resulting size + * @param {any} config - The config for fontSize, fontFamily, and fontWeight all impacting the + * resulting size * @returns {any} - The width for the given text */ export const calculateTextWidth = function (text, config) { @@ -656,7 +639,8 @@ export const calculateTextWidth = function (text, config) { }; /** - * This calculates the dimensions of the given text, font size, font family, font weight, and margins. + * This calculates the dimensions of the given text, font size, font family, font weight, and + * margins. * * @param {any} text - The text to calculate the width of * @param {any} config - The config for fontSize, fontFamily, fontWeight, and margin all impacting @@ -720,14 +704,15 @@ export const calculateTextDimensions = memoize( : 1; return dims[index]; }, - (text, config) => `${text}-${config.fontSize}-${config.fontWeight}-${config.fontFamily}` + (text, config) => `${text}${config.fontSize}${config.fontWeight}${config.fontFamily}` ); /** * Applys d3 attributes * * @param {any} d3Elem D3 Element to apply the attributes onto - * @param {[string, string][]} attrs Object.keys equivalent format of key to value mapping of attributes + * @param {[string, string][]} attrs Object.keys equivalent format of key to value mapping of + * attributes */ const d3Attrs = function (d3Elem, attrs) { for (let attr of attrs) { @@ -860,18 +845,12 @@ export interface DetailedError { hash: any; } -/** - * - * @param error - */ +/** @param error */ export function isDetailedError(error: unknown): error is DetailedError { return 'str' in error; } -/** - * - * @param error - */ +/** @param error */ export function getErrorMessage(error: unknown): string { if (error instanceof Error) return error.message; return String(error); @@ -894,7 +873,6 @@ export default { getStylesFromArray, generateId, random, - memoize, runFunc, entityDecode, initIdGenerator: initIdGenerator, diff --git a/yarn.lock b/yarn.lock index 148b07402..1967b28b1 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2643,6 +2643,11 @@ dependencies: "@types/node" "*" +"@types/lodash@^4.14.184": + version "4.14.184" + resolved "https://registry.yarnpkg.com/@types/lodash/-/lodash-4.14.184.tgz#23f96cd2a21a28e106dc24d825d4aa966de7a9fe" + integrity sha512-RoZphVtHbxPZizt4IcILciSWiC6dcn+eZ8oX9IWEYfDMcocdd42f7NPI6fQj+6zI8y4E0L7gu2pcZKLGTRaV9Q== + "@types/mdast@^3.0.0": version "3.0.10" resolved "https://registry.yarnpkg.com/@types/mdast/-/mdast-3.0.10.tgz#4724244a82a4598884cbbe9bcfd73dff927ee8af"