From 92098e23eb881ab7ff132616b8cb5a7826820f78 Mon Sep 17 00:00:00 2001 From: Reda Al Sulais Date: Fri, 11 Aug 2023 20:54:39 +0300 Subject: [PATCH 1/5] convert `svgDrawCommon` to TS --- .../common/{common.spec.js => common.spec.ts} | 24 +++++++++---------- .../{svgDrawCommon.js => svgDrawCommon.ts} | 9 +++---- 2 files changed, 17 insertions(+), 16 deletions(-) rename packages/mermaid/src/diagrams/common/{common.spec.js => common.spec.ts} (76%) rename packages/mermaid/src/diagrams/common/{svgDrawCommon.js => svgDrawCommon.ts} (92%) diff --git a/packages/mermaid/src/diagrams/common/common.spec.js b/packages/mermaid/src/diagrams/common/common.spec.ts similarity index 76% rename from packages/mermaid/src/diagrams/common/common.spec.js rename to packages/mermaid/src/diagrams/common/common.spec.ts index d1c68e8926..cfef5d58d3 100644 --- a/packages/mermaid/src/diagrams/common/common.spec.js +++ b/packages/mermaid/src/diagrams/common/common.spec.ts @@ -1,15 +1,15 @@ import { sanitizeText, removeScript, parseGenericTypes } from './common.js'; -describe('when securityLevel is antiscript, all script must be removed', function () { +describe('when securityLevel is antiscript, all script must be removed', () => { /** - * @param {string} original The original text - * @param {string} result The expected sanitized text + * @param original - The original text + * @param result - The expected sanitized text */ - function compareRemoveScript(original, result) { + function compareRemoveScript(original: string, result: string) { expect(removeScript(original).trim()).toEqual(result); } - it('should remove all script block, script inline.', function () { + it('should remove all script block, script inline.', () => { const labelString = `1 Act1: Hello 11 Act2: @@ -25,7 +25,7 @@ describe('when securityLevel is antiscript, all script must be removed', functio compareRemoveScript(labelString, exactlyString); }); - it('should remove all javascript urls', function () { + it('should remove all javascript urls', () => { compareRemoveScript( `This is a clean link + clean link and me too`, @@ -34,11 +34,11 @@ describe('when securityLevel is antiscript, all script must be removed', functio ); }); - it('should detect malicious images', function () { + it('should detect malicious images', () => { compareRemoveScript(``, ``); }); - it('should detect iframes', function () { + it('should detect iframes', () => { compareRemoveScript( ` `, @@ -47,8 +47,8 @@ describe('when securityLevel is antiscript, all script must be removed', functio }); }); -describe('Sanitize text', function () { - it('should remove script tag', function () { +describe('Sanitize text', () => { + it('should remove script tag', () => { const maliciousStr = 'javajavascript:script:alert(1)'; const result = sanitizeText(maliciousStr, { securityLevel: 'strict', @@ -58,8 +58,8 @@ describe('Sanitize text', function () { }); }); -describe('generic parser', function () { - it('should parse generic types', function () { +describe('generic parser', () => { + it('should parse generic types', () => { expect(parseGenericTypes('test~T~')).toEqual('test'); expect(parseGenericTypes('test~Array~Array~string~~~')).toEqual('test>>'); expect(parseGenericTypes('test~Array~Array~string[]~~~')).toEqual( diff --git a/packages/mermaid/src/diagrams/common/svgDrawCommon.js b/packages/mermaid/src/diagrams/common/svgDrawCommon.ts similarity index 92% rename from packages/mermaid/src/diagrams/common/svgDrawCommon.js rename to packages/mermaid/src/diagrams/common/svgDrawCommon.ts index 9a4ce8aa2b..7e89b7e7f7 100644 --- a/packages/mermaid/src/diagrams/common/svgDrawCommon.js +++ b/packages/mermaid/src/diagrams/common/svgDrawCommon.ts @@ -1,3 +1,4 @@ +// @ts-nocheck - ignore to convert to TS import { sanitizeUrl } from '@braintree/sanitize-url'; export const drawRect = function (elem, rectData) { @@ -12,7 +13,7 @@ export const drawRect = function (elem, rectData) { rectElem.attr('ry', rectData.ry); if (rectData.attrs !== 'undefined' && rectData.attrs !== null) { - for (let attrKey in rectData.attrs) { + for (const attrKey in rectData.attrs) { rectElem.attr(attrKey, rectData.attrs[attrKey]); } } @@ -27,8 +28,8 @@ export const drawRect = function (elem, rectData) { /** * Draws a background rectangle * - * @param {any} elem Diagram (reference for bounds) - * @param {any} bounds Shape of the rectangle + * @param elem - Diagram (reference for bounds) + * @param bounds - Shape of the rectangle */ export const drawBackgroundRect = function (elem, bounds) { const rectElem = drawRect(elem, { @@ -69,7 +70,7 @@ export const drawImage = function (elem, x, y, link) { const imageElem = elem.append('image'); imageElem.attr('x', x); imageElem.attr('y', y); - var sanitizedLink = sanitizeUrl(link); + const sanitizedLink = sanitizeUrl(link); imageElem.attr('xlink:href', sanitizedLink); }; From 22b172d873ac54256d409586e1d6ce401a6086ef Mon Sep 17 00:00:00 2001 From: Reda Al Sulais Date: Fri, 11 Aug 2023 20:56:00 +0300 Subject: [PATCH 2/5] add types to `svgDrawCommon.ts` --- .../src/diagrams/common/commonTypes.ts | 58 +++++++++ .../src/diagrams/common/svgDrawCommon.ts | 110 ++++++++++-------- 2 files changed, 121 insertions(+), 47 deletions(-) create mode 100644 packages/mermaid/src/diagrams/common/commonTypes.ts diff --git a/packages/mermaid/src/diagrams/common/commonTypes.ts b/packages/mermaid/src/diagrams/common/commonTypes.ts new file mode 100644 index 0000000000..6a728bd5d8 --- /dev/null +++ b/packages/mermaid/src/diagrams/common/commonTypes.ts @@ -0,0 +1,58 @@ +export interface RectData { + x?: number; + y?: number; + fill?: string; + width?: number; + height?: number; + stroke?: string; + class?: string; + color?: string | number; + rx?: number; + ry?: number; + attrs?: Record; + anchor?: string; +} + +export interface Bound { + startx: number; + stopx: number; + starty: number; + stopy: number; + fill: string; + stroke: string; +} + +export interface TextData { + x: number; + y: number; + anchor: string; + text: string; + textMargin: number; + class?: string; +} + +export interface TextObject { + x: number; + y: number; + width: number; + height: number; + fill?: string; + anchor?: string; + 'text-anchor': string; + style: string; + textMargin: number; + rx: number; + ry: number; + tspan: boolean; + valign: unknown; +} + +export type D3RectElement = d3.Selection; + +export type D3UseElement = d3.Selection; + +export type D3ImageElement = d3.Selection; + +export type D3TextElement = d3.Selection; + +export type D3TSpanElement = d3.Selection; diff --git a/packages/mermaid/src/diagrams/common/svgDrawCommon.ts b/packages/mermaid/src/diagrams/common/svgDrawCommon.ts index 7e89b7e7f7..316a659fc8 100644 --- a/packages/mermaid/src/diagrams/common/svgDrawCommon.ts +++ b/packages/mermaid/src/diagrams/common/svgDrawCommon.ts @@ -1,38 +1,49 @@ -// @ts-nocheck - ignore to convert to TS import { sanitizeUrl } from '@braintree/sanitize-url'; +import type { Group, SVG } from '../../diagram-api/types.js'; +import type { + Bound, + D3ImageElement, + D3RectElement, + D3TSpanElement, + D3TextElement, + D3UseElement, + RectData, + TextData, + TextObject, +} from './commonTypes.js'; -export const drawRect = function (elem, rectData) { - const rectElem = elem.append('rect'); - rectElem.attr('x', rectData.x); - rectElem.attr('y', rectData.y); - rectElem.attr('fill', rectData.fill); - rectElem.attr('stroke', rectData.stroke); - rectElem.attr('width', rectData.width); - rectElem.attr('height', rectData.height); - rectElem.attr('rx', rectData.rx); - rectElem.attr('ry', rectData.ry); +export const drawRect = (element: SVG | Group, rectData: RectData): D3RectElement => { + const rectElement: D3RectElement = element.append('rect'); + rectData.x !== undefined && rectElement.attr('x', rectData.x); + rectData.y !== undefined && rectElement.attr('y', rectData.y); + rectData.fill !== undefined && rectElement.attr('fill', rectData.fill); + rectData.stroke !== undefined && rectElement.attr('stroke', rectData.stroke); + rectData.width !== undefined && rectElement.attr('width', rectData.width); + rectData.height !== undefined && rectElement.attr('height', rectData.height); + rectData.rx !== undefined && rectElement.attr('rx', rectData.rx); + rectData.ry !== undefined && rectElement.attr('ry', rectData.ry); - if (rectData.attrs !== 'undefined' && rectData.attrs !== null) { + if (rectData.attrs !== undefined && rectData.attrs !== null) { for (const attrKey in rectData.attrs) { - rectElem.attr(attrKey, rectData.attrs[attrKey]); + rectElement.attr(attrKey, rectData.attrs[attrKey]); } } - if (rectData.class !== 'undefined') { - rectElem.attr('class', rectData.class); + if (rectData.class !== undefined) { + rectElement.attr('class', rectData.class); } - return rectElem; + return rectElement; }; /** * Draws a background rectangle * - * @param elem - Diagram (reference for bounds) + * @param element - Diagram (reference for bounds) * @param bounds - Shape of the rectangle */ -export const drawBackgroundRect = function (elem, bounds) { - const rectElem = drawRect(elem, { +export const drawBackgroundRect = (element: SVG | Group, bounds: Bound): void => { + const rectData: RectData = { x: bounds.startx, y: bounds.starty, width: bounds.stopx - bounds.startx, @@ -40,50 +51,53 @@ export const drawBackgroundRect = function (elem, bounds) { fill: bounds.fill, stroke: bounds.stroke, class: 'rect', - }); - rectElem.lower(); + }; + const rectElement: D3RectElement = drawRect(element, rectData); + rectElement.lower(); }; -export const drawText = function (elem, textData) { +export const drawText = (element: SVG | Group, textData: TextData): D3TextElement => { // Remove and ignore br:s - const nText = textData.text.replace(//gi, ' '); + const nText: string = textData.text.replace(//gi, ' '); - const textElem = elem.append('text'); + const textElem: D3TextElement = element.append('text'); textElem.attr('x', textData.x); textElem.attr('y', textData.y); textElem.attr('class', 'legend'); textElem.style('text-anchor', textData.anchor); + textData.class !== undefined && textElem.attr('class', textData.class); - if (textData.class !== undefined) { - textElem.attr('class', textData.class); - } - - const span = textElem.append('tspan'); - span.attr('x', textData.x + textData.textMargin * 2); - span.text(nText); + const tspan: D3TSpanElement = textElem.append('tspan'); + tspan.attr('x', textData.x + textData.textMargin * 2); + tspan.text(nText); return textElem; }; -export const drawImage = function (elem, x, y, link) { - const imageElem = elem.append('image'); - imageElem.attr('x', x); - imageElem.attr('y', y); - const sanitizedLink = sanitizeUrl(link); - imageElem.attr('xlink:href', sanitizedLink); +export const drawImage = (elem: SVG | Group, x: number, y: number, link: string): void => { + const imageElement: D3ImageElement = elem.append('image'); + imageElement.attr('x', x); + imageElement.attr('y', y); + const sanitizedLink: string = sanitizeUrl(link); + imageElement.attr('xlink:href', sanitizedLink); }; -export const drawEmbeddedImage = function (elem, x, y, link) { - const imageElem = elem.append('use'); - imageElem.attr('x', x); - imageElem.attr('y', y); - const sanitizedLink = sanitizeUrl(link); - imageElem.attr('xlink:href', '#' + sanitizedLink); +export const drawEmbeddedImage = ( + element: SVG | Group, + x: number, + y: number, + link: string +): void => { + const imageElement: D3UseElement = element.append('use'); + imageElement.attr('x', x); + imageElement.attr('y', y); + const sanitizedLink: string = sanitizeUrl(link); + imageElement.attr('xlink:href', `#${sanitizedLink}`); }; -export const getNoteRect = function () { - return { +export const getNoteRect = (): RectData => { + const noteRectData: RectData = { x: 0, y: 0, width: 100, @@ -94,10 +108,11 @@ export const getNoteRect = function () { rx: 0, ry: 0, }; + return noteRectData; }; -export const getTextObj = function () { - return { +export const getTextObj = (): TextObject => { + const testObject: TextObject = { x: 0, y: 0, width: 100, @@ -112,4 +127,5 @@ export const getTextObj = function () { tspan: true, valign: undefined, }; + return testObject; }; From 5a2ea7c297d7424207aa5e4f4a9b525a74c4a451 Mon Sep 17 00:00:00 2001 From: Reda Al Sulais Date: Fri, 11 Aug 2023 21:09:00 +0300 Subject: [PATCH 3/5] fix svgDrawCommon import by adding `.js` --- packages/mermaid/src/diagrams/c4/svgDraw.js | 2 +- packages/mermaid/src/diagrams/sequence/sequenceRenderer.ts | 2 +- packages/mermaid/src/diagrams/sequence/svgDraw.js | 2 +- packages/mermaid/src/diagrams/user-journey/svgDraw.js | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/packages/mermaid/src/diagrams/c4/svgDraw.js b/packages/mermaid/src/diagrams/c4/svgDraw.js index 5ca2f55f81..9ec7422613 100644 --- a/packages/mermaid/src/diagrams/c4/svgDraw.js +++ b/packages/mermaid/src/diagrams/c4/svgDraw.js @@ -1,5 +1,5 @@ import common from '../common/common.js'; -import * as svgDrawCommon from '../common/svgDrawCommon'; +import * as svgDrawCommon from '../common/svgDrawCommon.js'; import { sanitizeUrl } from '@braintree/sanitize-url'; export const drawRect = function (elem, rectData) { diff --git a/packages/mermaid/src/diagrams/sequence/sequenceRenderer.ts b/packages/mermaid/src/diagrams/sequence/sequenceRenderer.ts index f6fde5001d..feee7157f7 100644 --- a/packages/mermaid/src/diagrams/sequence/sequenceRenderer.ts +++ b/packages/mermaid/src/diagrams/sequence/sequenceRenderer.ts @@ -3,7 +3,7 @@ import { select, selectAll } from 'd3'; import svgDraw, { ACTOR_TYPE_WIDTH, drawText, fixLifeLineHeights } from './svgDraw.js'; import { log } from '../../logger.js'; import common from '../common/common.js'; -import * as svgDrawCommon from '../common/svgDrawCommon'; +import * as svgDrawCommon from '../common/svgDrawCommon.js'; import * as configApi from '../../config.js'; import assignWithDepth from '../../assignWithDepth.js'; import utils from '../../utils.js'; diff --git a/packages/mermaid/src/diagrams/sequence/svgDraw.js b/packages/mermaid/src/diagrams/sequence/svgDraw.js index 0c7bc64213..e0aaa1eb93 100644 --- a/packages/mermaid/src/diagrams/sequence/svgDraw.js +++ b/packages/mermaid/src/diagrams/sequence/svgDraw.js @@ -1,5 +1,5 @@ import common from '../common/common.js'; -import * as svgDrawCommon from '../common/svgDrawCommon'; +import * as svgDrawCommon from '../common/svgDrawCommon.js'; import { addFunction } from '../../interactionDb.js'; import { ZERO_WIDTH_SPACE, parseFontSize } from '../../utils.js'; import { sanitizeUrl } from '@braintree/sanitize-url'; diff --git a/packages/mermaid/src/diagrams/user-journey/svgDraw.js b/packages/mermaid/src/diagrams/user-journey/svgDraw.js index 108f4b2f96..7a8f791fac 100644 --- a/packages/mermaid/src/diagrams/user-journey/svgDraw.js +++ b/packages/mermaid/src/diagrams/user-journey/svgDraw.js @@ -1,5 +1,5 @@ import { arc as d3arc } from 'd3'; -import * as svgDrawCommon from '../common/svgDrawCommon'; +import * as svgDrawCommon from '../common/svgDrawCommon.js'; export const drawRect = function (elem, rectData) { return svgDrawCommon.drawRect(elem, rectData); From 95382335739f2a3954c627f23843b493fe0a2d82 Mon Sep 17 00:00:00 2001 From: Reda Al Sulais Date: Fri, 11 Aug 2023 21:16:53 +0300 Subject: [PATCH 4/5] use lineBreakRegex in `svgDrawCommon` --- packages/mermaid/src/diagrams/common/common.ts | 1 + packages/mermaid/src/diagrams/common/svgDrawCommon.ts | 4 ++-- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/packages/mermaid/src/diagrams/common/common.ts b/packages/mermaid/src/diagrams/common/common.ts index 243c0cbf25..24591642b2 100644 --- a/packages/mermaid/src/diagrams/common/common.ts +++ b/packages/mermaid/src/diagrams/common/common.ts @@ -1,6 +1,7 @@ import DOMPurify from 'dompurify'; import { MermaidConfig } from '../../config.type.js'; +// Remove and ignore br:s export const lineBreakRegex = //gi; /** diff --git a/packages/mermaid/src/diagrams/common/svgDrawCommon.ts b/packages/mermaid/src/diagrams/common/svgDrawCommon.ts index 316a659fc8..c2d9864b1e 100644 --- a/packages/mermaid/src/diagrams/common/svgDrawCommon.ts +++ b/packages/mermaid/src/diagrams/common/svgDrawCommon.ts @@ -11,6 +11,7 @@ import type { TextData, TextObject, } from './commonTypes.js'; +import { lineBreakRegex } from './common.js'; export const drawRect = (element: SVG | Group, rectData: RectData): D3RectElement => { const rectElement: D3RectElement = element.append('rect'); @@ -57,8 +58,7 @@ export const drawBackgroundRect = (element: SVG | Group, bounds: Bound): void => }; export const drawText = (element: SVG | Group, textData: TextData): D3TextElement => { - // Remove and ignore br:s - const nText: string = textData.text.replace(//gi, ' '); + const nText: string = textData.text.replace(lineBreakRegex, ' '); const textElem: D3TextElement = element.append('text'); textElem.attr('x', textData.x); From 99c1758490306cef3b5d038061891c132e029da6 Mon Sep 17 00:00:00 2001 From: Reda Al Sulais Date: Sat, 12 Aug 2023 20:25:10 +0300 Subject: [PATCH 5/5] make more `RectData` required and remove optional assignment --- .../src/diagrams/common/commonTypes.ts | 16 +++++++------- .../src/diagrams/common/svgDrawCommon.ts | 21 +++++++------------ 2 files changed, 16 insertions(+), 21 deletions(-) diff --git a/packages/mermaid/src/diagrams/common/commonTypes.ts b/packages/mermaid/src/diagrams/common/commonTypes.ts index 6a728bd5d8..84c26db6e1 100644 --- a/packages/mermaid/src/diagrams/common/commonTypes.ts +++ b/packages/mermaid/src/diagrams/common/commonTypes.ts @@ -1,12 +1,12 @@ export interface RectData { - x?: number; - y?: number; - fill?: string; - width?: number; - height?: number; - stroke?: string; + x: number; + y: number; + fill: string; + width: number; + height: number; + stroke: string; class?: string; - color?: string | number; + color?: string; rx?: number; ry?: number; attrs?: Record; @@ -44,7 +44,7 @@ export interface TextObject { rx: number; ry: number; tspan: boolean; - valign: unknown; + valign?: string; } export type D3RectElement = d3.Selection; diff --git a/packages/mermaid/src/diagrams/common/svgDrawCommon.ts b/packages/mermaid/src/diagrams/common/svgDrawCommon.ts index c2d9864b1e..706d43ab9d 100644 --- a/packages/mermaid/src/diagrams/common/svgDrawCommon.ts +++ b/packages/mermaid/src/diagrams/common/svgDrawCommon.ts @@ -15,24 +15,22 @@ import { lineBreakRegex } from './common.js'; export const drawRect = (element: SVG | Group, rectData: RectData): D3RectElement => { const rectElement: D3RectElement = element.append('rect'); - rectData.x !== undefined && rectElement.attr('x', rectData.x); - rectData.y !== undefined && rectElement.attr('y', rectData.y); - rectData.fill !== undefined && rectElement.attr('fill', rectData.fill); - rectData.stroke !== undefined && rectElement.attr('stroke', rectData.stroke); - rectData.width !== undefined && rectElement.attr('width', rectData.width); - rectData.height !== undefined && rectElement.attr('height', rectData.height); + rectElement.attr('x', rectData.x); + rectElement.attr('y', rectData.y); + rectElement.attr('fill', rectData.fill); + rectElement.attr('stroke', rectData.stroke); + rectElement.attr('width', rectData.width); + rectElement.attr('height', rectData.height); rectData.rx !== undefined && rectElement.attr('rx', rectData.rx); rectData.ry !== undefined && rectElement.attr('ry', rectData.ry); - if (rectData.attrs !== undefined && rectData.attrs !== null) { + if (rectData.attrs !== undefined) { for (const attrKey in rectData.attrs) { rectElement.attr(attrKey, rectData.attrs[attrKey]); } } - if (rectData.class !== undefined) { - rectElement.attr('class', rectData.class); - } + rectData.class !== undefined && rectElement.attr('class', rectData.class); return rectElement; }; @@ -117,15 +115,12 @@ export const getTextObj = (): TextObject => { y: 0, width: 100, height: 100, - fill: undefined, - anchor: undefined, 'text-anchor': 'start', style: '#666', textMargin: 0, rx: 0, ry: 0, tspan: true, - valign: undefined, }; return testObject; };