diff --git a/.eslintrc.json b/.eslintrc.json index 133ab42cd..d83222f3a 100644 --- a/.eslintrc.json +++ b/.eslintrc.json @@ -16,7 +16,6 @@ "extends": [ "eslint:recommended", "plugin:@typescript-eslint/recommended", - "plugin:jsdoc/recommended", "plugin:json/recommended", "plugin:markdown/recommended", "plugin:@cspell/recommended", @@ -28,16 +27,6 @@ "no-console": "error", "no-prototype-builtins": "off", "no-unused-vars": "off", - "jsdoc/check-indentation": "off", - "jsdoc/check-alignment": "off", - "jsdoc/check-line-alignment": "off", - "jsdoc/multiline-blocks": "off", - "jsdoc/newline-after-description": "off", - "jsdoc/tag-lines": "off", - "jsdoc/require-param-description": "off", - "jsdoc/require-param-type": "off", - "jsdoc/require-returns": "off", - "jsdoc/require-returns-description": "off", "cypress/no-async-tests": "off", "@typescript-eslint/ban-ts-comment": [ "error", @@ -73,6 +62,29 @@ "no-console": "off" } }, + { + "files": ["*.{js,jsx,mjs,cjs}"], + "extends": ["plugin:jsdoc/recommended"], + "rules": { + "jsdoc/check-indentation": "off", + "jsdoc/check-alignment": "off", + "jsdoc/check-line-alignment": "off", + "jsdoc/multiline-blocks": "off", + "jsdoc/newline-after-description": "off", + "jsdoc/tag-lines": "off", + "jsdoc/require-param-description": "off", + "jsdoc/require-param-type": "off", + "jsdoc/require-returns": "off", + "jsdoc/require-returns-description": "off" + } + }, + { + "files": ["*.{ts,tsx}"], + "plugins": ["tsdoc"], + "rules": { + "tsdoc/syntax": "error" + } + }, { "files": ["*.spec.{ts,js}", "cypress/**", "demos/**", "**/docs/**"], "rules": { diff --git a/package.json b/package.json index 609799855..ad80859b1 100644 --- a/package.json +++ b/package.json @@ -120,6 +120,7 @@ "eslint-plugin-json": "3.1.0", "eslint-plugin-markdown": "3.0.0", "eslint-plugin-no-only-tests": "^3.0.0", + "eslint-plugin-tsdoc": "^0.2.17", "express": "4.18.2", "globby": "13.1.2", "husky": "8.0.1", diff --git a/packages/mermaid-example-diagram/src/detector.ts b/packages/mermaid-example-diagram/src/detector.ts index 29f6d2a6d..d30b99fba 100644 --- a/packages/mermaid-example-diagram/src/detector.ts +++ b/packages/mermaid-example-diagram/src/detector.ts @@ -4,7 +4,7 @@ export const id = 'example-diagram'; /** * Detector function that will be called by mermaid to determine if the diagram is this type of diagram. * - * @param txt The diagram text will be passed to the detector + * @param txt - The diagram text will be passed to the detector * @returns True if the diagram text matches a diagram of this type */ diff --git a/packages/mermaid-example-diagram/src/mermaidUtils.ts b/packages/mermaid-example-diagram/src/mermaidUtils.ts index 03de59ef9..8894abdff 100644 --- a/packages/mermaid-example-diagram/src/mermaidUtils.ts +++ b/packages/mermaid-example-diagram/src/mermaidUtils.ts @@ -35,18 +35,19 @@ export let setupGraphViewbox: ( /** * Function called by mermaid that injects utility functions that help the diagram to be a good citizen. - * @param _log - * @param _setLogLevel - * @param _getConfig - * @param _sanitizeText - * @param _setupGraphViewbox + * + * @param _log - log from mermaid/src/diagramAPI.ts + * @param _setLogLevel - setLogLevel from mermaid/src/diagramAPI.ts + * @param _getConfig - getConfig from mermaid/src/diagramAPI.ts + * @param _sanitizeText - sanitizeText from mermaid/src/diagramAPI.ts + * @param _setupGraphViewbox - setupGraphViewbox from mermaid/src/diagramAPI.ts */ export const injectUtils = ( _log: Record, - _setLogLevel: any, - _getConfig: any, - _sanitizeText: any, - _setupGraphViewbox: any + _setLogLevel: typeof setLogLevel, + _getConfig: typeof getConfig, + _sanitizeText: typeof sanitizeText, + _setupGraphViewbox: typeof setupGraphViewbox ) => { _log.debug('Mermaid utils injected into example-diagram'); log.trace = _log.trace; diff --git a/packages/mermaid/src/__mocks__/mermaidAPI.ts b/packages/mermaid/src/__mocks__/mermaidAPI.ts index 3102095b9..50018bcad 100644 --- a/packages/mermaid/src/__mocks__/mermaidAPI.ts +++ b/packages/mermaid/src/__mocks__/mermaidAPI.ts @@ -11,10 +11,7 @@ import Diagram, { type ParseErrorFunction } from '../Diagram'; // Normally, we could just do the following to get the original `parse()` // implementation, however, requireActual returns a promise and it's not documented how to use withing mock file. -/** - * @param text - * @param parseError - */ +/** {@inheritDoc mermaidAPI.parse} */ function parse(text: string, parseError?: ParseErrorFunction): boolean { addDiagrams(); const diagram = new Diagram(text, parseError); diff --git a/packages/mermaid/src/config.ts b/packages/mermaid/src/config.ts index 8dfeeafca..884a9931b 100644 --- a/packages/mermaid/src/config.ts +++ b/packages/mermaid/src/config.ts @@ -56,7 +56,7 @@ export const updateCurrentConfig = (siteCfg: MermaidConfig, _directives: any[]) * function _Default value: At default, will mirror Global Config_ * * @param conf - The base currentConfig to use as siteConfig - * @returns {object} - The siteConfig + * @returns The new siteConfig */ export const setSiteConfig = (conf: MermaidConfig): MermaidConfig => { siteConfig = assignWithDepth({}, defaultConfig); @@ -91,7 +91,7 @@ export const updateSiteConfig = (conf: MermaidConfig): MermaidConfig => { * * **Notes**: Returns **any** values in siteConfig. * - * @returns {object} - The siteConfig + * @returns The siteConfig */ export const getSiteConfig = (): MermaidConfig => { return assignWithDepth({}, siteConfig); @@ -107,8 +107,8 @@ export const getSiteConfig = (): MermaidConfig => { * keys. Any values found in conf with key found in siteConfig.secure will be replaced with the * corresponding siteConfig value. * - * @param {any} conf - The potential currentConfig - * @returns {any} - The currentConfig merged with the sanitized conf + * @param conf - The potential currentConfig + * @returns The currentConfig merged with the sanitized conf */ export const setConfig = (conf: MermaidConfig): MermaidConfig => { // sanitize(conf); @@ -131,7 +131,7 @@ export const setConfig = (conf: MermaidConfig): MermaidConfig => { * * **Notes**: Returns **any** the currentConfig * - * @returns {any} - The currentConfig + * @returns The currentConfig */ export const getConfig = (): MermaidConfig => { return assignWithDepth({}, currentConfig); @@ -146,7 +146,7 @@ export const getConfig = (): MermaidConfig => { * Ensures options parameter does not attempt to override siteConfig secure keys **Notes**: modifies * options in-place * - * @param {any} options - The potential setConfig parameter + * @param options - The potential setConfig parameter */ export const sanitize = (options: any) => { // Checking that options are not in the list of excluded options @@ -186,7 +186,7 @@ export const sanitize = (options: any) => { /** * Pushes in a directive to the configuration * - * @param {object} directive The directive to push in + * @param directive - The directive to push in */ export const addDirective = (directive: any) => { if (directive.fontFamily) { @@ -217,7 +217,8 @@ export const addDirective = (directive: any) => { * * **Notes**: (default: current siteConfig ) (optional, default `getSiteConfig()`) * - * @param config + * @param config - base set of values, which currentConfig could be **reset** to. + * Defaults to the current siteConfig (e.g returned by {@link getSiteConfig}). */ export const reset = (config = siteConfig): void => { // Replace current config with siteConfig diff --git a/packages/mermaid/src/defaultConfig.ts b/packages/mermaid/src/defaultConfig.ts index 3a9bd1841..570dc2da3 100644 --- a/packages/mermaid/src/defaultConfig.ts +++ b/packages/mermaid/src/defaultConfig.ts @@ -8,19 +8,27 @@ import { MermaidConfig } from './config.type'; * * These are the default options which can be overridden with the initialization call like so: * - * **Example 1:**
 mermaid.initialize({ flowchart:{ htmlLabels: false } }); 
+ * **Example 1:** * - * **Example 2:**
  
+ * ```html + * + * ``` * * A summary of all options and their defaults is found [here](#mermaidapi-configuration-defaults). * A description of each option follows below. - * - * @name Configuration */ const config: Partial = { /** @@ -30,8 +38,16 @@ const config: Partial = { * | --------- | --------------- | ------ | -------- | ---------------------------------------------- | * | theme | Built in Themes | string | Optional | 'default', 'forest', 'dark', 'neutral', 'null' | * - * **Notes:** To disable any pre-defined mermaid theme, use "null".
 "theme": "forest",
-   * "themeCSS": ".node rect { fill: red; }" 
+ * **Notes:** To disable any pre-defined mermaid theme, use "null". + * + * @example + * + * ```js + * { + * "theme": "forest", + * "themeCSS": ".node rect { fill: red; }" + * } + * ``` */ theme: 'default', themeVariables: theme['default'].getThemeVariables(), diff --git a/packages/mermaid/src/diagram-api/detectType.ts b/packages/mermaid/src/diagram-api/detectType.ts index 9536fded2..1c1abc51c 100644 --- a/packages/mermaid/src/diagram-api/detectType.ts +++ b/packages/mermaid/src/diagram-api/detectType.ts @@ -9,10 +9,13 @@ const anyComment = /\s*%%.*\n/gm; const detectors: Record = {}; /** - * @function detectType Detects the type of the graph text. Takes into consideration the possible - * existence of an %%init directive + * Detects the type of the graph text. * - * ```mermaid + * Takes into consideration the possible existence of an `%%init` directive + * + * @param text - The text defining the graph. For example: + * + * ```mermaid * %%{initialize: {"startOnLoad": true, logLevel: "fatal" }}%% * graph LR * a-->b @@ -23,13 +26,9 @@ const detectors: Record = {}; * f-->g * g-->h * ``` - * @param {string} text The text defining the graph - * @param {{ - * class: { defaultRenderer: string } | undefined; - * state: { defaultRenderer: string } | undefined; - * flowchart: { defaultRenderer: string } | undefined; - * }} [config] - * @returns {string} A graph definition key + * + * @param config - The mermaid config. + * @returns A graph definition key */ export const detectType = function (text: string, config?: MermaidConfig): string { text = text.replace(directive, '').replace(anyComment, '\n'); diff --git a/packages/mermaid/src/diagrams/common/common.ts b/packages/mermaid/src/diagrams/common/common.ts index e7fb395de..782915cc1 100644 --- a/packages/mermaid/src/diagrams/common/common.ts +++ b/packages/mermaid/src/diagrams/common/common.ts @@ -4,8 +4,8 @@ import { MermaidConfig } from '../../config.type'; /** * Gets the rows of lines in a string * - * @param {string | undefined} s The string to check the lines for - * @returns {string[]} The rows in that string + * @param s - The string to check the lines for + * @returns The rows in that string */ export const getRows = (s?: string): string[] => { if (!s) { @@ -18,8 +18,8 @@ export const getRows = (s?: string): string[] => { /** * Removes script tags from a text * - * @param {string} txt The text to sanitize - * @returns {string} The safer text + * @param txt - The text to sanitize + * @returns The safer text */ export const removeScript = (txt: string): string => { return DOMPurify.sanitize(txt); @@ -68,8 +68,8 @@ export const lineBreakRegex = //gi; /** * Whether or not a text has any line breaks * - * @param {string} text The text to test - * @returns {boolean} Whether or not the text has breaks + * @param text - The text to test + * @returns Whether or not the text has breaks */ export const hasBreaks = (text: string): boolean => { return lineBreakRegex.test(text); @@ -78,8 +78,8 @@ export const hasBreaks = (text: string): boolean => { /** * Splits on
tags * - * @param {string} text Text to split - * @returns {string[]} List of lines as strings + * @param text - Text to split + * @returns List of lines as strings */ export const splitBreaks = (text: string): string[] => { return text.split(lineBreakRegex); @@ -88,8 +88,8 @@ export const splitBreaks = (text: string): string[] => { /** * Converts placeholders to line breaks in HTML * - * @param {string} s HTML with placeholders - * @returns {string} HTML with breaks instead of placeholders + * @param s - HTML with placeholders + * @returns HTML with breaks instead of placeholders */ const placeholderToBreak = (s: string): string => { return s.replace(/#br#/g, '
'); @@ -98,8 +98,8 @@ const placeholderToBreak = (s: string): string => { /** * Opposite of `placeholderToBreak`, converts breaks to placeholders * - * @param {string} s HTML string - * @returns {string} String with placeholders + * @param s - HTML string + * @returns String with placeholders */ const breakToPlaceholder = (s: string): string => { return s.replace(lineBreakRegex, '#br#'); @@ -108,8 +108,8 @@ const breakToPlaceholder = (s: string): string => { /** * Gets the current URL * - * @param {boolean} useAbsolute Whether to return the absolute URL or not - * @returns {string} The current URL + * @param useAbsolute - Whether to return the absolute URL or not + * @returns The current URL */ const getUrl = (useAbsolute: boolean): string => { let url = ''; @@ -130,8 +130,8 @@ const getUrl = (useAbsolute: boolean): string => { /** * Converts a string/boolean into a boolean * - * @param {string | boolean} val String or boolean to convert - * @returns {boolean} The result from the input + * @param val - String or boolean to convert + * @returns The result from the input */ export const evaluate = (val?: string | boolean): boolean => val === false || ['false', 'null', '0'].includes(String(val).trim().toLowerCase()) ? false : true; @@ -139,12 +139,15 @@ export const evaluate = (val?: string | boolean): boolean => /** * Makes generics in typescript syntax * - * @example Array of array of strings in typescript syntax - * // returns "Array>" - * parseGenericTypes('Array~Array~string~~'); + * @example + * Array of array of strings in typescript syntax * - * @param {string} text The text to convert - * @returns {string} The converted string + * ```js + * // returns "Array>" + * parseGenericTypes('Array~Array~string~~'); + * ``` + * @param text - The text to convert + * @returns The converted string */ export const parseGenericTypes = function (text: string): string { let cleanedText = text; diff --git a/packages/mermaid/src/diagrams/error/errorRenderer.ts b/packages/mermaid/src/diagrams/error/errorRenderer.ts index df9ce2c6e..b4e267684 100644 --- a/packages/mermaid/src/diagrams/error/errorRenderer.ts +++ b/packages/mermaid/src/diagrams/error/errorRenderer.ts @@ -8,7 +8,7 @@ let conf = {}; /** * Merges the value of `conf` with the passed `cnf` * - * @param {object} cnf Config to merge + * @param cnf - Config to merge */ export const setConf = function (cnf: any) { conf = { ...conf, ...cnf }; @@ -17,11 +17,11 @@ export const setConf = function (cnf: any) { /** * Draws a an info picture in the tag with id: id based on the graph definition in text. * - * @param text - * @param {string} id The text for the error - * @param {string} mermaidVersion The version + * @param _text - Mermaid graph definition. + * @param id - The text for the error + * @param mermaidVersion - The version */ -export const draw = (text: string, id: string, mermaidVersion: string) => { +export const draw = (_text: string, id: string, mermaidVersion: string) => { try { log.debug('Renering svg for syntax error\n'); diff --git a/packages/mermaid/src/diagrams/sequence/sequenceRenderer.ts b/packages/mermaid/src/diagrams/sequence/sequenceRenderer.ts index 22fa5da8c..fa943d658 100644 --- a/packages/mermaid/src/diagrams/sequence/sequenceRenderer.ts +++ b/packages/mermaid/src/diagrams/sequence/sequenceRenderer.ts @@ -10,6 +10,7 @@ import assignWithDepth from '../../assignWithDepth'; import utils from '../../utils'; import { configureSvgSize } from '../../setupGraphViewbox'; import addSVGAccessibilityFields from '../../accessibility'; +import Diagram from '../../Diagram'; let conf = {}; @@ -100,8 +101,8 @@ export const bounds = { // eslint-disable-next-line @typescript-eslint/no-this-alias const _self = this; let cnt = 0; - /** @param {any} type */ - function updateFn(type) { + /** @param type - Either `activation` or `undefined` */ + function updateFn(type?: 'activation') { return function updateItemBounds(item) { cnt++; // The loop sequenceItems is a stack so the biggest margins in the beginning of the sequenceItems @@ -200,15 +201,25 @@ export const bounds = { }, }; +/** Options for drawing a note in {@link drawNote} */ +interface NoteModel { + /** x axis start position */ + startx: number; + /** y axis position */ + starty: number; + /** the message to be shown */ + message: string; + /** Set this with a custom width to override the default configured width. */ + width: number; +} + /** * Draws an note in the diagram with the attached line * - * @param {any} elem - The diagram to draw to. - * @param {{ x: number; y: number; message: string; width: number }} noteModel - startX: x axis - * start position, verticalPos: y axis position, message: the message to be shown, width: Set - * this with a custom width to override the default configured width. + * @param elem - The diagram to draw to. + * @param noteModel - Note model options. */ -const drawNote = function (elem, noteModel) { +const drawNote = function (elem: any, noteModel: NoteModel) { bounds.bumpVerticalPos(conf.boxMargin); noteModel.height = conf.boxMargin; noteModel.starty = bounds.getVerticalPos(); @@ -278,11 +289,11 @@ const actorFont = (cnf) => { * message so it can be drawn later. We do not draw the message at this point so the arrowhead can * be on top of the activation box. * - * @param {any} diagram - The parent of the message element - * @param {any} msgModel - The model containing fields describing a message - * @returns {number} lineStartY - The Y coordinate at which the message line starts + * @param _diagram - The parent of the message element. + * @param msgModel - The model containing fields describing a message + * @returns `lineStartY` - The Y coordinate at which the message line starts */ -const boundMessage = function (diagram, msgModel) { +function boundMessage(_diagram, msgModel): number { bounds.bumpVerticalPos(10); const { startx, stopx, message } = msgModel; const lines = common.splitBreaks(message).length; @@ -321,17 +332,17 @@ const boundMessage = function (diagram, msgModel) { bounds.insert(msgModel.fromBounds, msgModel.starty, msgModel.toBounds, msgModel.stopy); return lineStartY; -}; +} /** * Draws a message. Note that the bounds have previously been updated by boundMessage. * - * @param {any} diagram - The parent of the message element - * @param {any} msgModel - The model containing fields describing a message - * @param {number} lineStartY - The Y coordinate at which the message line starts - * @param diagObj + * @param diagram - The parent of the message element + * @param msgModel - The model containing fields describing a message + * @param lineStartY - The Y coordinate at which the message line starts + * @param diagObj - The diagram object. */ -const drawMessage = function (diagram, msgModel, lineStartY, diagObj) { +const drawMessage = function (diagram, msgModel, lineStartY: number, diagObj: Diagram) { const { startx, stopx, starty, message, type, sequenceIndex, sequenceVisible } = msgModel; const textDims = utils.calculateTextDimensions(message, messageFont(conf)); const textObj = svgDraw.getTextObj(); @@ -554,13 +565,6 @@ const activationBounds = function (actor, actors) { return [left, right]; }; -/** - * @param {any} loopWidths - * @param {any} msg - * @param {any} preMargin - * @param {any} postMargin - * @param {any} addLoopFn - */ function adjustLoopHeightForWrap(loopWidths, msg, preMargin, postMargin, addLoopFn) { bounds.bumpVerticalPos(preMargin); let heightAdjust = postMargin; @@ -584,12 +588,12 @@ function adjustLoopHeightForWrap(loopWidths, msg, preMargin, postMargin, addLoop /** * Draws a sequenceDiagram in the tag with id: id based on the graph definition in text. * - * @param {any} _text The text of the diagram - * @param {any} id The id of the diagram which will be used as a DOM element id¨ - * @param {any} _version Mermaid version from package.json - * @param {any} diagObj A standard diagram containing the db and the text and type etc of the diagram + * @param _text - The text of the diagram + * @param id - The id of the diagram which will be used as a DOM element id¨ + * @param _version - Mermaid version from package.json + * @param diagObj - A standard diagram containing the db and the text and type etc of the diagram */ -export const draw = function (_text, id, _version, diagObj) { +export const draw = function (_text: string, id: string, _version: string, diagObj: Diagram) { const { securityLevel, sequence } = configApi.getConfig(); conf = sequence; // Handle root and Document for when rendering in sandbox mode @@ -632,10 +636,10 @@ export const draw = function (_text, id, _version, diagObj) { svgDraw.insertSequenceNumber(diagram); /** - * @param {any} msg - * @param {any} verticalPos + * @param msg - The message to draw. + * @param verticalPos - The vertical position of the message. */ - function activeEnd(msg, verticalPos) { + function activeEnd(msg: any, verticalPos: number) { const activationData = bounds.endActivation(msg); if (activationData.starty + 18 > verticalPos) { activationData.starty = verticalPos - 6; @@ -910,12 +914,16 @@ export const draw = function (_text, id, _version, diagObj) { * It will enumerate each given message, and will determine its text width, in relation to the actor * it originates from, and destined to. * - * @param {any} actors - The actors map - * @param {Array} messages - A list of message objects to iterate - * @param diagObj - * @returns {any} + * @param actors - The actors map + * @param messages - A list of message objects to iterate + * @param diagObj - The diagram object. + * @returns The max message width of each actor. */ -const getMaxMessageWidthPerActor = function (actors, messages, diagObj) { +function getMaxMessageWidthPerActor( + actors: { [id: string]: any }, + messages: any[], + diagObj: Diagram +): { [id: string]: number } { const maxMessageWidthPerActor = {}; messages.forEach(function (msg) { @@ -1008,7 +1016,7 @@ const getMaxMessageWidthPerActor = function (actors, messages, diagObj) { log.debug('maxMessageWidthPerActor:', maxMessageWidthPerActor); return maxMessageWidthPerActor; -}; +} const getRequiredPopupWidth = function (actor) { let requiredPopupWidth = 0; @@ -1025,15 +1033,19 @@ const getRequiredPopupWidth = function (actor) { }; /** - * This will calculate the optimal margin for each given actor, for a given actor->messageWidth map. + * This will calculate the optimal margin for each given actor, + * for a given actor → messageWidth map. * * An actor's margin is determined by the width of the actor, the width of the largest message that * originates from it, and the configured conf.actorMargin. * - * @param {any} actors - The actors map to calculate margins for - * @param {any} actorToMessageWidth - A map of actor key -> max message width it holds + * @param actors - The actors map to calculate margins for + * @param actorToMessageWidth - A map of actor key → max message width it holds */ -const calculateActorMargins = function (actors, actorToMessageWidth) { +function calculateActorMargins( + actors: { [id: string]: any }, + actorToMessageWidth: ReturnType +) { let maxHeight = 0; Object.keys(actors).forEach((prop) => { const actor = actors[prop]; @@ -1074,7 +1086,7 @@ const calculateActorMargins = function (actors, actorToMessageWidth) { } return Math.max(maxHeight, conf.height); -}; +} const buildNoteModel = function (msg, actors, diagObj) { const startx = actors[msg.from].x; diff --git a/packages/mermaid/src/diagrams/user-journey/journeyRenderer.ts b/packages/mermaid/src/diagrams/user-journey/journeyRenderer.ts index 208391ab3..3880a243a 100644 --- a/packages/mermaid/src/diagrams/user-journey/journeyRenderer.ts +++ b/packages/mermaid/src/diagrams/user-journey/journeyRenderer.ts @@ -15,7 +15,7 @@ export const setConf = function (cnf) { const actors = {}; -/** @param {any} diagram */ +/** @param diagram - The diagram to draw to. */ function drawActorLegend(diagram) { const conf = getConfig().journey; // Draw the actors @@ -157,8 +157,8 @@ export const bounds = { // eslint-disable-next-line @typescript-eslint/no-this-alias const _self = this; let cnt = 0; - /** @param {any} type */ - function updateFn(type) { + /** @param type - Set to `activation` if activation */ + function updateFn(type?: 'activation') { return function updateItemBounds(item) { cnt++; // The loop sequenceItems is a stack so the biggest margins in the beginning of the sequenceItems diff --git a/packages/mermaid/src/logger.ts b/packages/mermaid/src/logger.ts index b01934e88..e38bf93fe 100644 --- a/packages/mermaid/src/logger.ts +++ b/packages/mermaid/src/logger.ts @@ -27,7 +27,7 @@ export const log: Record = { /** * Sets a log level * - * @param {LogLevel} [level="fatal"] The level to set the logging to. Default is `"fatal"` + * @param level - The level to set the logging to. Default is `"fatal"` */ export const setLogLevel = function (level: keyof typeof LEVELS | number | string = 'fatal') { let numericLevel: number = LEVELS.fatal; @@ -80,10 +80,10 @@ export const setLogLevel = function (level: keyof typeof LEVELS | number | strin /** * Returns a format with the timestamp and the log level * - * @param {LogLevel} level The level for the log format - * @returns {string} The format with the timestamp and log level + * @param level - The level for the log format + * @returns The format with the timestamp and log level */ -const format = (level: string): string => { +const format = (level: Uppercase): string => { const time = moment().format('ss.SSS'); return `%c${time} : ${level} : `; }; diff --git a/packages/mermaid/src/mermaid.ts b/packages/mermaid/src/mermaid.ts index 399691083..c8edb3a06 100644 --- a/packages/mermaid/src/mermaid.ts +++ b/packages/mermaid/src/mermaid.ts @@ -19,12 +19,6 @@ import type { ParseErrorFunction } from './Diagram'; * elements with the attribute already set. This way the init function can be triggered several * times. * - * Optionally, `init` can accept in the second argument one of the following: - * - * - A DOM Node - * - An array of DOM nodes (as would come from a jQuery selector) - * - A W3C selector, a la `.mermaid` - * * ```mermaid * graph LR; * a(Find elements)-->b{Processed} @@ -34,9 +28,12 @@ import type { ParseErrorFunction } from './Diagram'; * * Renders the mermaid diagrams * - * @param config - * @param nodes - * @param callback + * @param config - **Deprecated**, please set configuration in {@link initialize}. + * @param nodes - **Default**: `.mermaid`. One of the following: + * - A DOM Node + * - An array of DOM nodes (as would come from a jQuery selector) + * - A W3C selector, a la `.mermaid` + * @param callback - Called once for each rendered diagram's id. */ const init = async function ( config?: MermaidConfig, @@ -202,7 +199,7 @@ if (typeof document !== 'undefined') { * This is provided for environments where the mermaid object can't directly have a new member added * to it (eg. dart interop wrapper). (Initially there is no parseError member of mermaid). * - * @param {function (err, hash)} newParseErrorHandler New parseError() callback. + * @param newParseErrorHandler - New parseError() callback. */ const setParseErrorHandler = function (newParseErrorHandler: (err: any, hash: any) => void) { mermaid.parseError = newParseErrorHandler; diff --git a/packages/mermaid/src/mermaidAPI.ts b/packages/mermaid/src/mermaidAPI.ts index d2c48361a..3a53bd584 100644 --- a/packages/mermaid/src/mermaidAPI.ts +++ b/packages/mermaid/src/mermaidAPI.ts @@ -9,8 +9,6 @@ * page or do something completely different. * * In addition to the render function, a number of behavioral configuration options are available. - * - * @name mermaidAPI */ import { select } from 'd3'; import { compile, serialize, stringify } from 'stylis'; @@ -34,8 +32,8 @@ import { MermaidConfig } from './config.type'; import { evaluate } from './diagrams/common/common'; /** - * @param text - * @param parseError + * @param text - The mermaid diagram definition. + * @param parseError - If set, handles errors. */ function parse(text: string, parseError?: ParseErrorFunction): boolean { addDiagrams(); @@ -100,14 +98,13 @@ export const decodeEntities = function (text: string): string { * }); * ``` * - * @param {string} id The id of the element to be rendered - * @param {string} text The graph definition - * @param {(svgCode: string, bindFunctions?: (element: Element) => void) => void} cb Callback which - * is called after rendering is finished with the svg code as param. - * @param {Element} container Selector to element in which a div with the graph temporarily will be + * @param id - The id of the element to be rendered + * @param text - The graph definition + * @param cb - Callback which is called after rendering is finished with the svg code as param. + * @param container - Selector to element in which a div with the graph temporarily will be * inserted. If one is provided a hidden div will be inserted in the body of the page instead. The * element will be removed when rendering is completed. - * @returns {void} + * @returns - Resolves when finished rendering. */ const render = async function ( id: string, @@ -455,7 +452,7 @@ const handleDirective = function (p: any, directive: any, type: string): void { } }; -/** @param {MermaidConfig} options */ +/** @param options - Initial Mermaid options */ async function initialize(options: MermaidConfig) { // Handle legacy location of font-family configuration if (options?.fontFamily) { diff --git a/packages/mermaid/src/themes/erDiagram-oldHardcodedValues.ts b/packages/mermaid/src/themes/erDiagram-oldHardcodedValues.ts index 8f88a70cd..95ce40e79 100644 --- a/packages/mermaid/src/themes/erDiagram-oldHardcodedValues.ts +++ b/packages/mermaid/src/themes/erDiagram-oldHardcodedValues.ts @@ -1,5 +1,5 @@ /** - * @file Values that have been hardcoded in src/diagrams/er/styles.js. These can be used by + * Values that have been hardcoded in src/diagrams/er/styles.js. These can be used by * theme-_._ files to maintain display styles until themes, styles, renderers are revised. -- * 2022-09-22 */ diff --git a/packages/mermaid/src/utils.ts b/packages/mermaid/src/utils.ts index ba46011dd..a38044bd6 100644 --- a/packages/mermaid/src/utils.ts +++ b/packages/mermaid/src/utils.ts @@ -4,6 +4,7 @@ import { curveBasis, curveBasisClosed, curveBasisOpen, + CurveFactory, curveLinear, curveLinearClosed, curveMonotoneX, @@ -42,13 +43,13 @@ const directiveWithoutOpen = /\s*(?:(?:(\w+)(?=:):|(\w+))\s*(?:(?:(\w+))|((?:(?![}][%]{2}).|\r?\n)*))?\s*)(?:[}][%]{2})?/gi; /** - * @function detectInit Detects the init config object from the text - * @param config + * Detects the init config object from the text * - * ```mermaid + * @param text - The text defining the graph. For example: * - * %%{init: {"theme": "debug", "logLevel": 1 }}%% - * graph LR + * ```mermaid + * %%{init: {"theme": "debug", "logLevel": 1 }}%% + * graph LR * a-->b * b-->c * c-->d @@ -58,11 +59,11 @@ const directiveWithoutOpen = * g-->h * ``` * - * Or + * Or * - * ```mermaid - * %%{initialize: {"theme": "dark", logLevel: "debug" }}%% - * graph LR + * ```mermaid + * %%{initialize: {"theme": "dark", logLevel: "debug" }}%% + * graph LR * a-->b * b-->c * c-->d @@ -71,8 +72,9 @@ const directiveWithoutOpen = * f-->g * g-->h * ``` - * @param {string} text The text defining the graph - * @returns {object} The json object representing the init passed to mermaid.initialize() + * + * @param config - Optional mermaid configuration object. + * @returns The json object representing the init passed to mermaid.initialize() */ export const detectInit = function (text: string, config?: MermaidConfig): MermaidConfig { const inits = detectDirective(text, /(?:init\b)|(?:initialize\b)/); @@ -104,12 +106,14 @@ export const detectInit = function (text: string, config?: MermaidConfig): Merma }; /** - * @function detectDirective Detects the directive from the text. Text can be single line or - * multiline. If type is null or omitted the first directive encountered in text will be returned + * Detects the directive from the text. * - * ```mermaid - * graph LR - * %%{someDirective}%% + * Text can be single line or multiline. If type is null or omitted, + * the first directive encountered in text will be returned + * + * ```mermaid + * graph LR + * %%{someDirective}%% * a-->b * b-->c * c-->d @@ -118,13 +122,16 @@ export const detectInit = function (text: string, config?: MermaidConfig): Merma * f-->g * g-->h * ``` - * @param {string} text The text defining the graph - * @param {string | RegExp} type The directive to return (default: null) - * @returns {object | Array} An object or Array representing the directive(s): { type: string, args: - * object|null } matched by the input type if a single directive was found, that directive object - * will be returned. + * + * @param text - The text defining the graph + * @param type - The directive to return (default: `null`) + * @returns An object or Array representing the directive(s) matched by the input type. + * If a single directive was found, that directive object will be returned. */ -export const detectDirective = function (text, type = null) { +export const detectDirective = function ( + text: string, + type: string | RegExp = null +): { type?: string; args?: any } | { type?: string; args?: any }[] { try { const commentWithoutDirectives = new RegExp( `[%]{2}(?![{]${directiveWithoutOpen.source})(?=[}][%]{2}).*\n`, @@ -166,12 +173,13 @@ export const detectDirective = function (text, type = null) { }; /** - * @function isSubstringInArray Detects whether a substring in present in a given array - * @param {string} str The substring to detect - * @param {Array} arr The array to search - * @returns {number} The array index containing the substring or -1 if not present + * Detects whether a substring in present in a given array + * + * @param str - The substring to detect + * @param arr - The array to search + * @returns The array index containing the substring or -1 if not present */ -export const isSubstringInArray = function (str, arr) { +export const isSubstringInArray = function (str: string, arr: string[]): number { for (let i = 0; i < arr.length; i++) { if (arr[i].match(str)) { return i; @@ -183,26 +191,26 @@ export const isSubstringInArray = function (str, arr) { /** * Returns a d3 curve given a curve name * - * @param {string | undefined} interpolate The interpolation name - * @param {any} defaultCurve The default curve to return - * @returns {import('d3-shape').CurveFactory} The curve factory to use + * @param interpolate - The interpolation name + * @param defaultCurve - The default curve to return + * @returns The curve factory to use */ -export const interpolateToCurve = (interpolate, defaultCurve) => { +export function interpolateToCurve(interpolate?: string, defaultCurve: CurveFactory): CurveFactory { if (!interpolate) { return defaultCurve; } const curveName = `curve${interpolate.charAt(0).toUpperCase() + interpolate.slice(1)}`; return d3CurveTypes[curveName] || defaultCurve; -}; +} /** * Formats a URL string * - * @param {string} linkStr String of the URL - * @param {{ securityLevel: string }} config Configuration passed to MermaidJS - * @returns {string | undefined} The formatted URL + * @param linkStr - String of the URL + * @param config - Configuration passed to MermaidJS + * @returns The formatted URL or `undefined`. */ -export const formatUrl = (linkStr, config) => { +export function formatUrl(linkStr: string, config: { securityLevel: string }): string | undefined { const url = linkStr.trim(); if (url) { @@ -212,15 +220,15 @@ export const formatUrl = (linkStr, config) => { return url; } -}; +} /** * Runs a function * - * @param {string} functionName A dot separated path to the function relative to the `window` - * @param {...any} params Parameters to pass to the function + * @param functionName - A dot separated path to the function relative to the `window` + * @param params - Parameters to pass to the function */ -export const runFunc = (functionName, ...params) => { +export const runFunc = (functionName: string, ...params) => { const arrPaths = functionName.split('.'); const len = arrPaths.length - 1; @@ -237,28 +245,31 @@ export const runFunc = (functionName, ...params) => { obj[fnName](...params); }; -/** - * @typedef {object} Point A (x, y) point - * @property {number} x The x value - * @property {number} y The y value - */ +/** A (x, y) point */ +interface Point { + /** The x value */ + x: number; + /** The y value */ + y: number; +} /** * Finds the distance between two points using the Distance Formula * - * @param {Point} p1 The first point - * @param {Point} p2 The second point - * @returns {number} The distance + * @param p1 - The first point + * @param p2 - The second point + * @returns The distance between the two points. */ -const distance = (p1, p2) => - p1 && p2 ? Math.sqrt(Math.pow(p2.x - p1.x, 2) + Math.pow(p2.y - p1.y, 2)) : 0; +function distance(p1: Point, p2: Point): number { + return p1 && p2 ? Math.sqrt(Math.pow(p2.x - p1.x, 2) + Math.pow(p2.y - p1.y, 2)) : 0; +} /** - * @param {Point[]} points List of points - * @returns {Point} - * @todo Give this a description + * TODO: Give this a description + * + * @param points - List of points */ -const traverseEdge = (points) => { +function traverseEdge(points: Point[]): Point { let prevPoint; let totalDistance = 0; @@ -297,20 +308,17 @@ const traverseEdge = (points) => { prevPoint = point; }); return center; -}; +} /** - * Alias for `traverseEdge` - * - * @param {Point[]} points List of points - * @returns {Point} Return result of `transverseEdge` + * {@inheritdoc traverseEdge} */ -const calcLabelPosition = (points) => { +function calcLabelPosition(points: Point[]): Point { if (points.length === 1) { return points[0]; } return traverseEdge(points); -}; +} const calcCardinalityPosition = (isRelationTypePresent, points, initialPosition) => { let prevPoint; @@ -366,14 +374,18 @@ const calcCardinalityPosition = (isRelationTypePresent, points, initialPosition) }; /** - * Position ['start_left', 'start_right', 'end_left', 'end_right'] + * Calculates the terminal label position. * - * @param {any} terminalMarkerSize - * @param {any} position - * @param {any} _points - * @returns {any} + * @param terminalMarkerSize - Terminal marker size. + * @param position - Position of label relative to points. + * @param _points - Array of points. + * @returns - The `cardinalityPosition`. */ -const calcTerminalLabelPosition = (terminalMarkerSize, position, _points) => { +function calcTerminalLabelPosition( + terminalMarkerSize: number, + position: 'start_left' | 'start_right' | 'end_left' | 'end_right', + _points: Point[] +): Point { // Todo looking to faster cloning method let points = JSON.parse(JSON.stringify(_points)); let prevPoint; @@ -441,15 +453,15 @@ const calcTerminalLabelPosition = (terminalMarkerSize, position, _points) => { cardinalityPosition.y = -Math.cos(angle) * d + (points[0].y + center.y) / 2 - 5; } return cardinalityPosition; -}; +} /** * Gets styles from an array of declarations * - * @param {string[]} arr Declarations - * @returns {{ style: string; labelStyle: string }} The styles grouped as strings + * @param arr - Declarations + * @returns The styles grouped as strings */ -export const getStylesFromArray = (arr) => { +export function getStylesFromArray(arr: string[]): { style: string; labelStyle: string } { let style = ''; let labelStyle = ''; @@ -465,7 +477,7 @@ export const getStylesFromArray = (arr) => { } return { style: style, labelStyle: labelStyle }; -}; +} let cnt = 0; export const generateId = () => { @@ -474,10 +486,12 @@ export const generateId = () => { }; /** - * @param {any} length - * @returns {any} + * Generates a random hexadecimal id of the given length. + * + * @param length - Length of ID. + * @returns The generated ID. */ -function makeid(length) { +function makeid(length: number): string { let result = ''; const characters = '0123456789abcdef'; const charactersLength = characters.length; @@ -510,22 +524,25 @@ export const getTextObj = function () { /** * Adds text to an element * - * @param {SVGElement} elem Element to add text to - * @param {{ - * text: string; - * x: number; - * y: number; - * anchor: 'start' | 'middle' | 'end'; - * fontFamily: string; - * fontSize: string | number; - * fontWeight: string | number; - * fill: string; - * class: string | undefined; - * textMargin: number; - * }} textData - * @returns {SVGTextElement} Text element with given styling and content + * @param elem - SVG Element to add text to + * @param textData - Text options. + * @returns Text element with given styling and content */ -export const drawSimpleText = function (elem, textData) { +export const drawSimpleText = function ( + elem: SVGElement, + textData: { + text: string; + x: number; + y: number; + anchor: 'start' | 'middle' | 'end'; + fontFamily: string; + fontSize: string | number; + fontWeight: string | number; + fill: string; + class: string | undefined; + textMargin: number; + } +): SVGTextElement { // Remove and ignore br:s const nText = textData.text.replace(common.lineBreakRegex, ' '); @@ -623,43 +640,56 @@ 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 + * @param text - The text to measure + * @param config - The config for fontSize, fontFamily, and fontWeight all impacting the * resulting size - * @returns {any} - The height for the given text + * @returns The height for the given text */ -export const calculateTextHeight = function (text, config) { +export function calculateTextHeight( + text: Parameters[0], + config: Parameters[1] +): ReturnType['height'] { config = Object.assign( { fontSize: 12, fontWeight: 400, fontFamily: 'Arial', margin: 15 }, config ); return calculateTextDimensions(text, config).height; -}; +} /** * 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 + * @param text - The text to calculate the width of + * @param config - The config for fontSize, fontFamily, and fontWeight all impacting the * resulting size - * @returns {any} - The width for the given text + * @returns The width for the given text */ -export const calculateTextWidth = function (text, config) { +export function calculateTextWidth( + text: Parameters[0], + config: Parameters[1] +): ReturnType['width'] { config = Object.assign({ fontSize: 12, fontWeight: 400, fontFamily: 'Arial' }, config); return calculateTextDimensions(text, config).width; -}; +} /** * 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 + * @param text - The text to calculate the width of + * @param config - The config for fontSize, fontFamily, fontWeight, and margin all impacting * the resulting size - * @returns - The width for the given text + * @returns The dimensions for the given text */ export const calculateTextDimensions = memoize( - function (text, config) { + function ( + text: string, + config: { + fontSize?: number; + fontWeight?: number; + fontFamily?: string; + } + ) { config = Object.assign({ fontSize: 12, fontWeight: 400, fontFamily: 'Arial' }, config); const { fontSize, fontFamily, fontWeight } = config; if (!text) { @@ -741,10 +771,10 @@ let decoder; /** * Decodes HTML, source: {@link https://github.com/shrpne/entity-decode/blob/v2.0.1/browser.js} * - * @param {string} html HTML as a string - * @returns {string} Unescaped HTML + * @param html - HTML as a string + * @returns Unescaped HTML */ -export const entityDecode = function (html) { +export const entityDecode = function (html: string): string { decoder = decoder || document.createElement('div'); // Escape HTML before decoding for HTML Entities html = escape(html).replace(/%26/g, '&').replace(/%23/g, '#').replace(/%3B/g, ';'); @@ -756,9 +786,9 @@ export const entityDecode = function (html) { /** * Sanitizes directive objects * - * @param {object} args Directive's JSON + * @param args - Directive's JSON */ -export const directiveSanitizer = (args) => { +export const directiveSanitizer = (args: any) => { log.debug('directiveSanitizer called with', args); if (typeof args === 'object') { // check for array @@ -845,12 +875,12 @@ export interface DetailedError { hash: any; } -/** @param error */ +/** @param error - The error to check */ export function isDetailedError(error: unknown): error is DetailedError { return 'str' in error; } -/** @param error */ +/** @param error - The error to convert to an error message */ export function getErrorMessage(error: unknown): string { if (error instanceof Error) { return error.message; diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index ed0da3b69..db4590ed3 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -46,6 +46,7 @@ importers: eslint-plugin-json: 3.1.0 eslint-plugin-markdown: 3.0.0 eslint-plugin-no-only-tests: ^3.0.0 + eslint-plugin-tsdoc: ^0.2.17 express: 4.18.2 fast-clone: 1.5.13 globby: 13.1.2 @@ -129,6 +130,7 @@ importers: eslint-plugin-json: 3.1.0 eslint-plugin-markdown: 3.0.0_eslint@8.25.0 eslint-plugin-no-only-tests: 3.0.0 + eslint-plugin-tsdoc: 0.2.17 express: 4.18.2 globby: 13.1.2 husky: 8.0.1 @@ -2261,6 +2263,19 @@ packages: '@jridgewell/sourcemap-codec': 1.4.14 dev: true + /@microsoft/tsdoc-config/0.16.2: + resolution: {integrity: sha512-OGiIzzoBLgWWR0UdRJX98oYO+XKGf7tiK4Zk6tQ/E4IJqGCe7dvkTvgDZV5cFJUzLGDOjeAXrnZoA6QkVySuxw==} + dependencies: + '@microsoft/tsdoc': 0.14.2 + ajv: 6.12.6 + jju: 1.4.0 + resolve: 1.19.0 + dev: true + + /@microsoft/tsdoc/0.14.2: + resolution: {integrity: sha512-9b8mPpKrfeGRuhFH5iO1iwCLeIIsV6+H1sRfxbkoGXIyQE2BTsPd9zqSqQJ+pv5sJ/hT5M1zvOFL02MnEezFug==} + dev: true + /@nodelib/fs.scandir/2.1.5: resolution: {integrity: sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==} engines: {node: '>= 8'} @@ -6080,6 +6095,13 @@ packages: engines: {node: '>=5.0.0'} dev: true + /eslint-plugin-tsdoc/0.2.17: + resolution: {integrity: sha512-xRmVi7Zx44lOBuYqG8vzTXuL6IdGOeF9nHX17bjJ8+VE6fsxpdGem0/SBTmAwgYMKYB1WBkqRJVQ+n8GK041pA==} + dependencies: + '@microsoft/tsdoc': 0.14.2 + '@microsoft/tsdoc-config': 0.16.2 + dev: true + /eslint-scope/5.1.1: resolution: {integrity: sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw==} engines: {node: '>=8.0.0'} @@ -8807,6 +8829,10 @@ packages: nomnom: 1.5.2 dev: true + /jju/1.4.0: + resolution: {integrity: sha512-8wb9Yw966OSxApiCt0K3yNJL8pnNeIv+OEq2YMidz4FKP6nonSRoOXc80iXY4JaN2FC11B9qsNmDsm+ZOfMROA==} + dev: true + /joi/17.6.0: resolution: {integrity: sha512-OX5dG6DTbcr/kbMFj0KGYxuew69HPcAE3K/sZpEV2nP6e/j/C0HV+HNiBPCASxdx5T7DMoa0s8UeHWMnb6n2zw==} dependencies: @@ -11321,6 +11347,13 @@ packages: resolution: {integrity: sha512-9znBF0vBcaSN3W2j7wKvdERPwqTxSpCq+if5C0WoTCyV9n24rua28jeuQ2pL/HOf+yUe/Mef+H/5p60K0Id3bg==} dev: true + /resolve/1.19.0: + resolution: {integrity: sha512-rArEXAgsBG4UgRGcynxWIWKFvh/XZCcS8UJdHhwy91zwAvCZIbcs+vAbflgBnNjYMs/i/i+/Ux6IZhML1yPvxg==} + dependencies: + is-core-module: 2.10.0 + path-parse: 1.0.7 + dev: true + /resolve/1.22.1: resolution: {integrity: sha512-nBpuuYuY5jFsli/JIs1oldw6fOQCBioohqWZg/2hiaOybXOft4lonv85uDOKXdf8rhyK159cxU5cDcK/NKk8zw==} hasBin: true