diff --git a/docs/INSTALL.md b/docs/INSTALL.md index 7e73a3a..f223c31 100644 --- a/docs/INSTALL.md +++ b/docs/INSTALL.md @@ -13,7 +13,7 @@ If you do not have a "composer.local.json" file yet, create one and add the foll ``` { "require": { - "mediawiki/mermaid": "~6.0.0" + "mediawiki/mermaid": "~6.0.1" } } ``` @@ -21,7 +21,7 @@ If you do not have a "composer.local.json" file yet, create one and add the foll If you already have a "composer.local.json" file add the following line to the end of the "require" section in your file: - "mediawiki/mermaid": "~6.0.0" + "mediawiki/mermaid": "~6.0.1" Remember to add a comma to the end of the preceding line in this section. diff --git a/docs/RELEASE-NOTES.md b/docs/RELEASE-NOTES.md index ba3752a..dacd83b 100644 --- a/docs/RELEASE-NOTES.md +++ b/docs/RELEASE-NOTES.md @@ -1,6 +1,23 @@ This file contains the *release notes* of the **Mermaid** extension. See also the [readme], the [installation and configuration information] and [usage examples]. +### 6.0.1 + +Released July 9, 2025. + +The `$configMap` is now automatically generated from the `Mermaid.js` source, grouped, and type-aware. + +* Fetches and parses `config.type.ts` from Mermaid.js v10.9.3 +* Recursively extracts all config properties including nested interfaces into a flat dot-notation map +* Detects boolean properties and assigns FILTER_VALIDATE_BOOLEAN for correct PHP typing +* Groups configuration keys by diagram prefix (e.g. flowchart, gantt, sequence) with comment headers in the PHP array +* Automatically updates `src/MermaidConfigExtractor.php` by replacing the `$configMap` block in-place +* Eliminates manual syncing and reduces risk of human error or outdated configs +* Improves clarity and maintainability of the config structure +* Adds more test cases for different types of config keys +* Simplifies future Mermaid.js upgrades — just update the version in `scripts/generateConfigMap.ts` and regenerate +* steps how to update `configMap` added to `docs/UPDATEMERMAID.md` + ### 6.0.0 Released July 7, 2025. diff --git a/docs/UPDATEMERMAID.md b/docs/UPDATEMERMAID.md index 560cc9f..4935b6d 100644 --- a/docs/UPDATEMERMAID.md +++ b/docs/UPDATEMERMAID.md @@ -26,12 +26,15 @@ After running `npm install`, the following occurs: This approach ensures that ResourceLoader can treat the final file as a single script without requiring multiple scripts or worrying about load order. -### To Update `mermaid` +### To Update `mermaid` and `configMap` 1. Open `package.json` and change the version under `"mermaid"` (e.g., `"mermaid": "latest"` or a specific version like `"10.9.3"`). -2. Run: +2. Open `scripts/generateConfigMap.ts` and change the version under `"version"` + (e.g., specific version like `"v10.9.3"` or `"mermaid@11.8.1"`, so it is important to be the same as here https://github.com/mermaid-js/mermaid/releases ). + +3. Run: ```bash npm install diff --git a/extension.json b/extension.json index 3ff5514..6b6c9f3 100644 --- a/extension.json +++ b/extension.json @@ -1,6 +1,6 @@ { "name": "Mermaid", - "version": "6.0.0", + "version": "6.0.1", "author": [ "James Hong Kong", "Tyler Gibson" diff --git a/package.json b/package.json index 684c242..739b605 100644 --- a/package.json +++ b/package.json @@ -15,7 +15,7 @@ ], "scripts": { "build": "rollup -c", - "postinstall": "npm run build && node resources/utility/inject-nomin.cjs" + "postinstall": "npm run build && node resources/utility/inject-nomin.cjs && npx tsx scripts/generateConfigMap.ts" }, "repository": { "type": "git", @@ -29,7 +29,9 @@ ] }, "dependencies": { - "mermaid": "10.9.3" + "mermaid": "10.9.3", + "node-fetch": "^2.7.0", + "ts-morph": "^26.0.0" }, "files": [ "lib", @@ -42,8 +44,11 @@ "@rollup/plugin-commonjs": "^28.0.6", "@rollup/plugin-node-resolve": "^16.0.1", "@rollup/plugin-terser": "^0.4.4", + "@types/node": "^24.0.12", + "@types/node-fetch": "^2.6.12", "copy-files-from-to": "^3.12.1", "insert-line": "^1.1.0", - "rollup": "^4.0.0" + "rollup": "^4.0.0", + "tsx": "^4.20.3" } } diff --git a/scripts/generateConfigMap.ts b/scripts/generateConfigMap.ts new file mode 100644 index 0000000..68c9ee0 --- /dev/null +++ b/scripts/generateConfigMap.ts @@ -0,0 +1,188 @@ +import fetch from 'node-fetch'; +import { Node, SyntaxKind, TypeLiteralNode, InterfaceDeclaration, Project } from "ts-morph"; +import { readFileSync, writeFileSync } from 'fs'; +import path from 'path'; + +async function generateConfigMap() { + const version = 'v10.9.3'; + const url = `https://raw.githubusercontent.com/mermaid-js/mermaid/${version}/packages/mermaid/src/config.type.ts`; + const response = await fetch(url); + const sourceCode = await response.text(); + + // Create a ts-morph project and parse the source code + const project = new Project({ useInMemoryFileSystem: true }); + const sourceFile = project.createSourceFile('config.type.ts', sourceCode); + + // Get the main interface MermaidConfig + const mainIface = sourceFile.getInterfaceOrThrow('MermaidConfig'); + + // Helper function to find InterfaceDeclaration by name + function getInterfaceByName(name: string): InterfaceDeclaration | undefined { + return sourceFile.getInterface(name); + } + + // Function to determine if a property is boolean or boolean union + function isBooleanProperty(prop: import("ts-morph").PropertySignature): boolean { + const type = prop.getType(); + return type.isBoolean() || (type.isUnion() && type.getUnionTypes().some(t => t.isBoolean())); + } + + // Recursive function to extract all properties from an interface, flattening nested properties + // Returns a map of property names (dot notation) to either 'FILTER_VALIDATE_BOOLEAN' or null + function extractProperties( + iface: InterfaceDeclaration, + prefix = '' + ): Record { + const result: Record = {}; + + iface.getProperties().forEach(prop => { + const name = prop.getName(); + const typeNode = prop.getTypeNode(); + + if (!typeNode) { + // No explicit type node, assign null + result[prefix + name] = null; + return; + } + + if (Node.isTypeLiteral(typeNode)) { + // Inline object type (TypeLiteral) - recurse into it + const nestedProps = extractFromTypeLiteral(typeNode as TypeLiteralNode, prefix + name + '.'); + Object.assign(result, nestedProps); + } else if (Node.isTypeReference(typeNode)) { + // Type reference - lookup interface and recurse + const typeName = typeNode.getText(); + const refIface = getInterfaceByName(typeName); + if (refIface) { + const nestedProps = extractProperties(refIface, prefix + name + '.'); + Object.assign(result, nestedProps); + } else { + // Reference not found - just assign null + result[prefix + name] = null; + } + } else { + // Other types - check if boolean to assign filter + if (isBooleanProperty(prop)) { + result[prefix + name] = 'FILTER_VALIDATE_BOOLEAN'; + } else { + result[prefix + name] = null; + } + } + }); + + return result; + } + + // Recursive extraction of properties from TypeLiteralNode, with same filter logic + function extractFromTypeLiteral( + typeLiteralNode: TypeLiteralNode, + prefix = '' + ): Record { + const result: Record = {}; + + typeLiteralNode.getMembers().forEach(member => { + if (member.getKind() === SyntaxKind.PropertySignature) { + const propSig = member.asKindOrThrow(SyntaxKind.PropertySignature); + const name = propSig.getName(); + const typeNode = propSig.getTypeNode(); + + if (!typeNode) { + result[prefix + name] = null; + return; + } + + if (Node.isTypeLiteral(typeNode)) { + const nestedProps = extractFromTypeLiteral(typeNode as TypeLiteralNode, prefix + name + '.'); + Object.assign(result, nestedProps); + } else if (Node.isTypeReference(typeNode)) { + const typeName = typeNode.getText(); + const refIface = getInterfaceByName(typeName); + if (refIface) { + const nestedProps = extractProperties(refIface, prefix + name + '.'); + Object.assign(result, nestedProps); + } else { + result[prefix + name] = null; + } + } else { + if (isBooleanProperty(propSig)) { + result[prefix + name] = 'FILTER_VALIDATE_BOOLEAN'; + } else { + result[prefix + name] = null; + } + } + } + }); + + return result; + } + + // Extract all properties starting from the main interface + const configMap = extractProperties(mainIface); + + // Sort keys for nicer output + const sortedKeys = Object.keys(configMap).sort(); + + // Group keys by their prefix (segment before first dot), global group for keys without dot + const grouped: Record = {}; + for (const key of sortedKeys) { + const group = key.includes('.') ? key.split('.')[0] : 'global'; + if (!grouped[group]) grouped[group] = []; + grouped[group].push(key); + } + + // Indentation string: 2 tabs + const indent = '\t\t'; + + // Generate PHP array lines with comments grouping each diagram type + const phpLines: string[] = []; + phpLines.push('private $configMap = ['); + + // Output global group first without comment + if (grouped['global']) { + phpLines.push(`${indent}// global`); + for (const key of grouped['global']) { + const val = configMap[key]; + phpLines.push(`${indent}'${key}' => ${val ?? 'null'},`); + } + delete grouped['global']; + } + + // Output other groups with comment headers + for (const group of Object.keys(grouped).sort()) { + phpLines.push(`\n${indent}// ${group}`); + for (const key of grouped[group]) { + const val = configMap[key]; + phpLines.push(`${indent}'${key}' => ${val ?? 'null'},`); + } + } + + phpLines.push(`\t];`); + + const newConfigMapBlock = phpLines.join('\n'); + + // --- Automatically update PHP file --- + + const phpFilePath = path.resolve('src', 'MermaidConfigExtractor.php'); + const phpFileContent = readFileSync(phpFilePath, 'utf-8'); + + // Regex to find the existing $configMap block + const configMapRegex = /private \$configMap = \[[\s\S]*?\];/m; + + if (!configMapRegex.test(phpFileContent)) { + console.error('ERROR: Could not find $configMap block in PHP file.'); + process.exit(1); + } + + // Replace the old block with the new generated block + const newPhpFileContent = phpFileContent.replace(configMapRegex, newConfigMapBlock); + + // Write back to the PHP file + writeFileSync(phpFilePath, newPhpFileContent, 'utf-8'); + + console.log(`ConfigMap updated successfully!`); +} + +generateConfigMap().catch(err => { + console.error('Error:', err); + process.exit(1); +}); diff --git a/src/MermaidConfigExtractor.php b/src/MermaidConfigExtractor.php index b72be07..d0e9974 100644 --- a/src/MermaidConfigExtractor.php +++ b/src/MermaidConfigExtractor.php @@ -9,90 +9,431 @@ * @author howlowck */ class MermaidConfigExtractor { - // TODO: This should eventually be autogenerated by programmatically inspecting the config json set by mermaid.js + + /** + * Configuration map for Mermaid settings. + * Automatically generated from mermaid/config.type.ts. + * + * @var array + */ private $configMap = [ // global - 'theme' => null, - 'themeCSS' => null, + 'altFontFamily' => null, + 'arrowMarkerAbsolute' => FILTER_VALIDATE_BOOLEAN, + 'darkMode' => FILTER_VALIDATE_BOOLEAN, + 'deterministicIDSeed' => null, + 'deterministicIds' => FILTER_VALIDATE_BOOLEAN, + 'dompurifyConfig' => null, 'fontFamily' => null, + 'fontSize' => null, + 'htmlLabels' => FILTER_VALIDATE_BOOLEAN, + 'legacyMathML' => FILTER_VALIDATE_BOOLEAN, 'logLevel' => null, + 'maxEdges' => null, + 'maxTextSize' => null, + 'secure' => null, 'securityLevel' => null, 'startOnLoad' => FILTER_VALIDATE_BOOLEAN, - 'arrowMarkerAbsolute' => FILTER_VALIDATE_BOOLEAN, - 'secure' => null, - 'legacyMathML' => FILTER_VALIDATE_BOOLEAN, - 'forceLegacyMathML' => FILTER_VALIDATE_BOOLEAN, - 'deterministicIds' => FILTER_VALIDATE_BOOLEAN, - 'deterministicIDSeed' => null, + 'theme' => null, + 'themeCSS' => null, + 'themeVariables' => null, + 'wrap' => FILTER_VALIDATE_BOOLEAN, + + // block + 'block.padding' => null, + + // c4 + 'c4.boundaryFont' => null, + 'c4.boundaryFontFamily' => null, + 'c4.boundaryFontSize' => null, + 'c4.boundaryFontWeight' => null, + 'c4.boxMargin' => null, + 'c4.c4BoundaryInRow' => null, + 'c4.c4ShapeInRow' => null, + 'c4.c4ShapeMargin' => null, + 'c4.c4ShapePadding' => null, + 'c4.componentFont' => null, + 'c4.componentFontFamily' => null, + 'c4.componentFontSize' => null, + 'c4.componentFontWeight' => null, + 'c4.component_bg_color' => null, + 'c4.component_border_color' => null, + 'c4.component_dbFont' => null, + 'c4.component_dbFontFamily' => null, + 'c4.component_dbFontSize' => null, + 'c4.component_dbFontWeight' => null, + 'c4.component_db_bg_color' => null, + 'c4.component_db_border_color' => null, + 'c4.component_queueFont' => null, + 'c4.component_queueFontFamily' => null, + 'c4.component_queueFontSize' => null, + 'c4.component_queueFontWeight' => null, + 'c4.component_queue_bg_color' => null, + 'c4.component_queue_border_color' => null, + 'c4.containerFont' => null, + 'c4.containerFontFamily' => null, + 'c4.containerFontSize' => null, + 'c4.containerFontWeight' => null, + 'c4.container_bg_color' => null, + 'c4.container_border_color' => null, + 'c4.container_dbFont' => null, + 'c4.container_dbFontFamily' => null, + 'c4.container_dbFontSize' => null, + 'c4.container_dbFontWeight' => null, + 'c4.container_db_bg_color' => null, + 'c4.container_db_border_color' => null, + 'c4.container_queueFont' => null, + 'c4.container_queueFontFamily' => null, + 'c4.container_queueFontSize' => null, + 'c4.container_queueFontWeight' => null, + 'c4.container_queue_bg_color' => null, + 'c4.container_queue_border_color' => null, + 'c4.diagramMarginX' => null, + 'c4.diagramMarginY' => null, + 'c4.external_componentFont' => null, + 'c4.external_componentFontFamily' => null, + 'c4.external_componentFontSize' => null, + 'c4.external_componentFontWeight' => null, + 'c4.external_component_bg_color' => null, + 'c4.external_component_border_color' => null, + 'c4.external_component_dbFont' => null, + 'c4.external_component_dbFontFamily' => null, + 'c4.external_component_dbFontSize' => null, + 'c4.external_component_dbFontWeight' => null, + 'c4.external_component_db_bg_color' => null, + 'c4.external_component_db_border_color' => null, + 'c4.external_component_queueFont' => null, + 'c4.external_component_queueFontFamily' => null, + 'c4.external_component_queueFontSize' => null, + 'c4.external_component_queueFontWeight' => null, + 'c4.external_component_queue_bg_color' => null, + 'c4.external_component_queue_border_color' => null, + 'c4.external_containerFont' => null, + 'c4.external_containerFontFamily' => null, + 'c4.external_containerFontSize' => null, + 'c4.external_containerFontWeight' => null, + 'c4.external_container_bg_color' => null, + 'c4.external_container_border_color' => null, + 'c4.external_container_dbFont' => null, + 'c4.external_container_dbFontFamily' => null, + 'c4.external_container_dbFontSize' => null, + 'c4.external_container_dbFontWeight' => null, + 'c4.external_container_db_bg_color' => null, + 'c4.external_container_db_border_color' => null, + 'c4.external_container_queueFont' => null, + 'c4.external_container_queueFontFamily' => null, + 'c4.external_container_queueFontSize' => null, + 'c4.external_container_queueFontWeight' => null, + 'c4.external_container_queue_bg_color' => null, + 'c4.external_container_queue_border_color' => null, + 'c4.external_personFont' => null, + 'c4.external_personFontFamily' => null, + 'c4.external_personFontSize' => null, + 'c4.external_personFontWeight' => null, + 'c4.external_person_bg_color' => null, + 'c4.external_person_border_color' => null, + 'c4.external_systemFont' => null, + 'c4.external_systemFontFamily' => null, + 'c4.external_systemFontSize' => null, + 'c4.external_systemFontWeight' => null, + 'c4.external_system_bg_color' => null, + 'c4.external_system_border_color' => null, + 'c4.external_system_dbFont' => null, + 'c4.external_system_dbFontFamily' => null, + 'c4.external_system_dbFontSize' => null, + 'c4.external_system_dbFontWeight' => null, + 'c4.external_system_db_bg_color' => null, + 'c4.external_system_db_border_color' => null, + 'c4.external_system_queueFont' => null, + 'c4.external_system_queueFontFamily' => null, + 'c4.external_system_queueFontSize' => null, + 'c4.external_system_queueFontWeight' => null, + 'c4.external_system_queue_bg_color' => null, + 'c4.external_system_queue_border_color' => null, + 'c4.height' => null, + 'c4.messageFont' => null, + 'c4.messageFontFamily' => null, + 'c4.messageFontSize' => null, + 'c4.messageFontWeight' => null, + 'c4.nextLinePaddingX' => null, + 'c4.personFont' => null, + 'c4.personFontFamily' => null, + 'c4.personFontSize' => null, + 'c4.personFontWeight' => null, + 'c4.person_bg_color' => null, + 'c4.person_border_color' => null, + 'c4.systemFont' => null, + 'c4.systemFontFamily' => null, + 'c4.systemFontSize' => null, + 'c4.systemFontWeight' => null, + 'c4.system_bg_color' => null, + 'c4.system_border_color' => null, + 'c4.system_dbFont' => null, + 'c4.system_dbFontFamily' => null, + 'c4.system_dbFontSize' => null, + 'c4.system_dbFontWeight' => null, + 'c4.system_db_bg_color' => null, + 'c4.system_db_border_color' => null, + 'c4.system_queueFont' => null, + 'c4.system_queueFontFamily' => null, + 'c4.system_queueFontSize' => null, + 'c4.system_queueFontWeight' => null, + 'c4.system_queue_bg_color' => null, + 'c4.system_queue_border_color' => null, + 'c4.width' => null, + 'c4.wrap' => FILTER_VALIDATE_BOOLEAN, + 'c4.wrapPadding' => null, + + // class + 'class.arrowMarkerAbsolute' => FILTER_VALIDATE_BOOLEAN, + 'class.defaultRenderer' => null, + 'class.diagramPadding' => null, + 'class.dividerMargin' => null, + 'class.htmlLabels' => FILTER_VALIDATE_BOOLEAN, + 'class.nodeSpacing' => null, + 'class.padding' => null, + 'class.rankSpacing' => null, + 'class.textHeight' => null, + 'class.titleTopMargin' => null, + + // er + 'er.diagramPadding' => null, + 'er.entityPadding' => null, + 'er.fill' => null, + 'er.fontSize' => null, + 'er.layoutDirection' => null, + 'er.minEntityHeight' => null, + 'er.minEntityWidth' => null, + 'er.stroke' => null, + 'er.titleTopMargin' => null, // flowchart + 'flowchart.arrowMarkerAbsolute' => FILTER_VALIDATE_BOOLEAN, 'flowchart.curve' => null, 'flowchart.defaultRenderer' => null, - 'flowchart.useMaxWidth' => FILTER_VALIDATE_BOOLEAN, + 'flowchart.diagramPadding' => null, 'flowchart.htmlLabels' => FILTER_VALIDATE_BOOLEAN, 'flowchart.nodeSpacing' => null, - 'flowchart.rankSpacing' => null, - 'flowchart.minLen' => null, 'flowchart.padding' => null, - 'flowchart.defaultLevelSeparation' => null, - 'flowchart.defaultRankSeparation' => null, - 'flowchart.defaultNodeSeparation' => null, - 'flowchart.wrap' => FILTER_VALIDATE_BOOLEAN, - - // sequence - 'sequence.diagramMarginX' => null, - 'sequence.diagramMarginY' => null, - 'sequence.actorMargin' => null, - 'sequence.width' => null, - 'sequence.height' => null, - 'sequence.boxMargin' => null, - 'sequence.boxTextMargin' => null, - 'sequence.noteMargin' => null, - 'sequence.messageMargin' => null, - 'sequence.messageAlign' => null, - 'sequence.mirrorActors' => FILTER_VALIDATE_BOOLEAN, - 'sequence.bottomMarginAdj' => null, - 'sequence.useMaxWidth' => FILTER_VALIDATE_BOOLEAN, - 'sequence.rightAngles' => FILTER_VALIDATE_BOOLEAN, - 'sequence.showSequenceNumbers' => FILTER_VALIDATE_BOOLEAN, - 'sequence.actorFontSize' => null, - 'sequence.messageFontSize' => null, + 'flowchart.rankSpacing' => null, + 'flowchart.subGraphTitleMargin.bottom' => null, + 'flowchart.subGraphTitleMargin.top' => null, + 'flowchart.titleTopMargin' => null, + 'flowchart.wrappingWidth' => null, // gantt - 'gantt.titleTopMargin' => null, - 'gantt.barHeight' => null, + 'gantt.axisFormat' => null, 'gantt.barGap' => null, - 'gantt.topPadding' => null, - 'gantt.leftPadding' => null, - 'gantt.gridLineStartPadding' => null, + 'gantt.barHeight' => null, + 'gantt.displayMode' => null, 'gantt.fontSize' => null, - 'gantt.fontFamily' => null, + 'gantt.gridLineStartPadding' => null, + 'gantt.leftPadding' => null, 'gantt.numberSectionStyles' => null, - 'gantt.axisFormat' => null, - 'gantt.displayMode' => null, - 'gantt.useMaxWidth' => FILTER_VALIDATE_BOOLEAN, + 'gantt.rightPadding' => null, + 'gantt.sectionFontSize' => null, + 'gantt.tickInterval' => null, + 'gantt.titleTopMargin' => null, + 'gantt.topAxis' => FILTER_VALIDATE_BOOLEAN, + 'gantt.topPadding' => null, + 'gantt.weekday' => null, + + // gitGraph + 'gitGraph.arrowMarkerAbsolute' => FILTER_VALIDATE_BOOLEAN, + 'gitGraph.diagramPadding' => null, + 'gitGraph.mainBranchName' => null, + 'gitGraph.mainBranchOrder' => null, + 'gitGraph.nodeLabel.height' => null, + 'gitGraph.nodeLabel.width' => null, + 'gitGraph.nodeLabel.x' => null, + 'gitGraph.nodeLabel.y' => null, + 'gitGraph.parallelCommits' => FILTER_VALIDATE_BOOLEAN, + 'gitGraph.rotateCommitLabel' => FILTER_VALIDATE_BOOLEAN, + 'gitGraph.showBranches' => FILTER_VALIDATE_BOOLEAN, + 'gitGraph.showCommitLabel' => FILTER_VALIDATE_BOOLEAN, + 'gitGraph.titleTopMargin' => null, // journey - 'journey.useMaxWidth' => FILTER_VALIDATE_BOOLEAN, - 'journey.fontSize' => null, - 'journey.rightMargin' => null, + 'journey.activationWidth' => null, + 'journey.actorColours' => null, + 'journey.bottomMarginAdj' => null, + 'journey.boxMargin' => null, + 'journey.boxTextMargin' => null, + 'journey.diagramMarginX' => null, + 'journey.diagramMarginY' => null, + 'journey.height' => null, + 'journey.leftMargin' => null, + 'journey.messageAlign' => null, + 'journey.messageMargin' => null, + 'journey.noteMargin' => null, + 'journey.rightAngles' => FILTER_VALIDATE_BOOLEAN, + 'journey.sectionColours' => null, + 'journey.sectionFills' => null, + 'journey.taskFontFamily' => null, + 'journey.taskFontSize' => null, + 'journey.taskMargin' => null, + 'journey.textPlacement' => null, + 'journey.width' => null, + + // mindmap + 'mindmap.maxNodeWidth' => null, + 'mindmap.padding' => null, // pie - 'pie.useMaxWidth' => FILTER_VALIDATE_BOOLEAN, - 'pie.titleFontSize' => null, + 'pie.textPosition' => null, + + // quadrantChart + 'quadrantChart.chartHeight' => null, + 'quadrantChart.chartWidth' => null, + 'quadrantChart.pointLabelFontSize' => null, + 'quadrantChart.pointRadius' => null, + 'quadrantChart.pointTextPadding' => null, + 'quadrantChart.quadrantExternalBorderStrokeWidth' => null, + 'quadrantChart.quadrantInternalBorderStrokeWidth' => null, + 'quadrantChart.quadrantLabelFontSize' => null, + 'quadrantChart.quadrantPadding' => null, + 'quadrantChart.quadrantTextTopPadding' => null, + 'quadrantChart.titleFontSize' => null, + 'quadrantChart.titlePadding' => null, + 'quadrantChart.xAxisLabelFontSize' => null, + 'quadrantChart.xAxisLabelPadding' => null, + 'quadrantChart.xAxisPosition' => null, + 'quadrantChart.yAxisLabelFontSize' => null, + 'quadrantChart.yAxisLabelPadding' => null, + 'quadrantChart.yAxisPosition' => null, + + // requirement + 'requirement.fontSize' => null, + 'requirement.line_height' => null, + 'requirement.rect_border_color' => null, + 'requirement.rect_border_size' => null, + 'requirement.rect_fill' => null, + 'requirement.rect_min_height' => null, + 'requirement.rect_min_width' => null, + 'requirement.rect_padding' => null, + 'requirement.text_color' => null, + + // sankey + 'sankey.height' => null, + 'sankey.linkColor' => null, + 'sankey.nodeAlignment' => null, + 'sankey.prefix' => null, + 'sankey.showValues' => FILTER_VALIDATE_BOOLEAN, + 'sankey.suffix' => null, + 'sankey.useMaxWidth' => FILTER_VALIDATE_BOOLEAN, + 'sankey.width' => null, + + // sequence + 'sequence.activationWidth' => null, + 'sequence.actorFont' => null, + 'sequence.actorFontFamily' => null, + 'sequence.actorFontSize' => null, + 'sequence.actorFontWeight' => null, + 'sequence.actorMargin' => null, + 'sequence.arrowMarkerAbsolute' => FILTER_VALIDATE_BOOLEAN, + 'sequence.bottomMarginAdj' => null, + 'sequence.boxMargin' => null, + 'sequence.boxTextMargin' => null, + 'sequence.diagramMarginX' => null, + 'sequence.diagramMarginY' => null, + 'sequence.forceMenus' => FILTER_VALIDATE_BOOLEAN, + 'sequence.height' => null, + 'sequence.hideUnusedParticipants' => FILTER_VALIDATE_BOOLEAN, + 'sequence.labelBoxHeight' => null, + 'sequence.labelBoxWidth' => null, + 'sequence.messageAlign' => null, + 'sequence.messageFont' => null, + 'sequence.messageFontFamily' => null, + 'sequence.messageFontSize' => null, + 'sequence.messageFontWeight' => null, + 'sequence.messageMargin' => null, + 'sequence.mirrorActors' => FILTER_VALIDATE_BOOLEAN, + 'sequence.noteAlign' => null, + 'sequence.noteFont' => null, + 'sequence.noteFontFamily' => null, + 'sequence.noteFontSize' => null, + 'sequence.noteFontWeight' => null, + 'sequence.noteMargin' => null, + 'sequence.rightAngles' => FILTER_VALIDATE_BOOLEAN, + 'sequence.showSequenceNumbers' => FILTER_VALIDATE_BOOLEAN, + 'sequence.width' => null, + 'sequence.wrap' => FILTER_VALIDATE_BOOLEAN, + 'sequence.wrapPadding' => null, // state - 'state.useMaxWidth' => FILTER_VALIDATE_BOOLEAN, + 'state.arrowMarkerAbsolute' => FILTER_VALIDATE_BOOLEAN, + 'state.compositTitleSize' => null, + 'state.defaultRenderer' => null, + 'state.dividerMargin' => null, + 'state.edgeLengthFactor' => null, + 'state.fontSize' => null, + 'state.fontSizeFactor' => null, + 'state.forkHeight' => null, + 'state.forkWidth' => null, + 'state.labelHeight' => null, + 'state.miniPadding' => null, + 'state.noteMargin' => null, + 'state.padding' => null, + 'state.radius' => null, + 'state.sizeUnit' => null, + 'state.textHeight' => null, + 'state.titleShift' => null, + 'state.titleTopMargin' => null, // timeline - 'timeline.useMaxWidth' => FILTER_VALIDATE_BOOLEAN, - 'timeline.axisFormatter' => null, - - // class diagram - 'class.useMaxWidth' => FILTER_VALIDATE_BOOLEAN, + 'timeline.activationWidth' => null, + 'timeline.actorColours' => null, + 'timeline.bottomMarginAdj' => null, + 'timeline.boxMargin' => null, + 'timeline.boxTextMargin' => null, + 'timeline.diagramMarginX' => null, + 'timeline.diagramMarginY' => null, + 'timeline.disableMulticolor' => FILTER_VALIDATE_BOOLEAN, + 'timeline.height' => null, + 'timeline.leftMargin' => null, + 'timeline.messageAlign' => null, + 'timeline.messageMargin' => null, + 'timeline.noteMargin' => null, + 'timeline.padding' => null, + 'timeline.rightAngles' => FILTER_VALIDATE_BOOLEAN, + 'timeline.sectionColours' => null, + 'timeline.sectionFills' => null, + 'timeline.taskFontFamily' => null, + 'timeline.taskFontSize' => null, + 'timeline.taskMargin' => null, + 'timeline.textPlacement' => null, + 'timeline.width' => null, - // er diagram - 'er.useMaxWidth' => FILTER_VALIDATE_BOOLEAN, + // xyChart + 'xyChart.chartOrientation' => null, + 'xyChart.height' => null, + 'xyChart.plotReservedSpacePercent' => null, + 'xyChart.showTitle' => FILTER_VALIDATE_BOOLEAN, + 'xyChart.titleFontSize' => null, + 'xyChart.titlePadding' => null, + 'xyChart.width' => null, + 'xyChart.xAxis.axisLineWidth' => null, + 'xyChart.xAxis.labelFontSize' => null, + 'xyChart.xAxis.labelPadding' => null, + 'xyChart.xAxis.showAxisLine' => FILTER_VALIDATE_BOOLEAN, + 'xyChart.xAxis.showLabel' => FILTER_VALIDATE_BOOLEAN, + 'xyChart.xAxis.showTick' => FILTER_VALIDATE_BOOLEAN, + 'xyChart.xAxis.showTitle' => FILTER_VALIDATE_BOOLEAN, + 'xyChart.xAxis.tickLength' => null, + 'xyChart.xAxis.tickWidth' => null, + 'xyChart.xAxis.titleFontSize' => null, + 'xyChart.xAxis.titlePadding' => null, + 'xyChart.yAxis.axisLineWidth' => null, + 'xyChart.yAxis.labelFontSize' => null, + 'xyChart.yAxis.labelPadding' => null, + 'xyChart.yAxis.showAxisLine' => FILTER_VALIDATE_BOOLEAN, + 'xyChart.yAxis.showLabel' => FILTER_VALIDATE_BOOLEAN, + 'xyChart.yAxis.showTick' => FILTER_VALIDATE_BOOLEAN, + 'xyChart.yAxis.showTitle' => FILTER_VALIDATE_BOOLEAN, + 'xyChart.yAxis.tickLength' => null, + 'xyChart.yAxis.tickWidth' => null, + 'xyChart.yAxis.titleFontSize' => null, + 'xyChart.yAxis.titlePadding' => null, ]; /** diff --git a/tests/phpunit/Unit/MermaidParserFunctionTest.php b/tests/phpunit/Unit/MermaidParserFunctionTest.php index 20376c8..d9454ac 100644 --- a/tests/phpunit/Unit/MermaidParserFunctionTest.php +++ b/tests/phpunit/Unit/MermaidParserFunctionTest.php @@ -91,7 +91,7 @@ public function textProvider() { yield [ [ 'graph LR;', 'config.theme=foo', 'config.flowchart.useMaxWidth=false' ], - '
' + '
' ]; } diff --git a/tests/phpunit/Unit/TestingConsts.php b/tests/phpunit/Unit/TestingConsts.php index 74cd75d..04bea14 100644 --- a/tests/phpunit/Unit/TestingConsts.php +++ b/tests/phpunit/Unit/TestingConsts.php @@ -7,21 +7,202 @@ class TestingConsts { [ [ 'sequenceDiagram...', 'config.theme=foo' ], [ [ 'theme' => 'foo' ], [ 'sequenceDiagram...' ] ], - ], [ + ], + [ [ 'sequenceDiagram id1["This is the (text) in the box"]', 'config.theme=foo' ], [ [ 'theme' => 'foo' ], [ 'sequenceDiagram id1["This is the (text) in the box"]' ] ], - ], [ + ], + [ [ 'A[Hard edge] -->|Link text| B(Round edge)' ], [ [], [ 'A[Hard edge] -->|Link text| B(Round edge)' ] ], - ], [ + ], + [ [ 'graph LR;', 'config.theme=foo', 'config.flowchart.curve=basis' ], [ [ 'theme' => 'foo', 'flowchart' => [ 'curve' => 'basis' ] ], [ 'graph LR;' ] ], - ], [ + ], + [ [ 'graph LR;', 'config.theme=foo', 'config.flowchart.useMaxWidth=false' ], - [ [ 'theme' => 'foo', 'flowchart' => [ 'useMaxWidth' => false ] ], [ 'graph LR;' ] ], - ], [ + [ [ 'theme' => 'foo' ], [ 'graph LR;', 'config.flowchart.useMaxWidth=false' ] ], + ], + [ [ 'config.gantt.titleTopMargin=40', 'gantt title A Gantt Diagram' ], - [ [ 'gantt' => [ 'titleTopMargin' => '40' ] ], [ 'gantt title A Gantt Diagram' ] ] - ] + [ [ 'gantt' => [ 'titleTopMargin' => '40' ] ], [ 'gantt title A Gantt Diagram' ] ], + ], + [ + [ + 'sequenceDiagram;', + 'config.theme=dark', + 'config.flowchart.htmlLabels=true', + 'config.gantt.topAxis=false', + 'config.sequence.actorMargin=25', + 'config.sankey.showValues=true', + ], + [ + [ + 'theme' => 'dark', + 'flowchart' => [ 'htmlLabels' => true ], + 'gantt' => [ 'topAxis' => false ], + 'sequence' => [ 'actorMargin' => '25' ], + 'sankey' => [ 'showValues' => true ], + ], + [ 'sequenceDiagram;' ], + ], + ], + [ + [ + 'graph TD;', + 'config.c4.componentFontSize=14', + 'config.c4.wrap=true', + 'config.c4.component_border_color=#000000', + ], + [ + [ + 'c4' => [ + 'componentFontSize' => '14', + 'wrap' => true, + 'component_border_color' => '#000000', + ], + ], + [ 'graph TD;' ], + ], + ], + [ + [ + 'flowchart;', + 'config.flowchart.arrowMarkerAbsolute=true', + 'config.flowchart.padding=15', + 'config.flowchart.wrappingWidth=300', + ], + [ + [ + 'flowchart' => [ + 'arrowMarkerAbsolute' => true, + 'padding' => '15', + 'wrappingWidth' => '300', + ], + ], + [ 'flowchart;' ], + ], + ], + [ + [ + 'stateDiagram;', + 'config.state.arrowMarkerAbsolute=false', + 'config.state.fontSize=16', + 'config.state.titleTopMargin=30', + ], + [ + [ + 'state' => [ + 'arrowMarkerAbsolute' => false, + 'fontSize' => '16', + 'titleTopMargin' => '30', + ], + ], + [ 'stateDiagram;' ], + ], + ], + [ + [ + 'sequenceDiagram;', + 'config.sequence.mirrorActors=true', + 'config.sequence.forceMenus=false', + 'config.sequence.showSequenceNumbers=true', + ], + [ + [ + 'sequence' => [ + 'mirrorActors' => true, + 'forceMenus' => false, + 'showSequenceNumbers' => true, + ], + ], + [ 'sequenceDiagram;' ], + ], + ], + [ + [ + 'timeline;', + 'config.timeline.activationWidth=120', + 'config.timeline.disableMulticolor=true', + 'config.timeline.rightAngles=false', + 'config.timeline.taskFontSize=14', + 'config.timeline.width=600', + ], + [ + [ + 'timeline' => [ + 'activationWidth' => '120', + 'disableMulticolor' => true, + 'rightAngles' => false, + 'taskFontSize' => '14', + 'width' => '600', + ], + ], + [ 'timeline;' ], + ], + ], + [ + [ + 'xyChart;', + 'config.xyChart.chartOrientation=horizontal', + 'config.xyChart.showTitle=true', + 'config.xyChart.titleFontSize=18', + 'config.xyChart.width=800', + 'config.xyChart.xAxis.showAxisLine=true', + 'config.xyChart.xAxis.showLabel=false', + 'config.xyChart.xAxis.tickLength=10', + 'config.xyChart.yAxis.showTitle=true', + 'config.xyChart.yAxis.tickWidth=2', + ], + [ + [ + 'xyChart' => [ + 'chartOrientation' => 'horizontal', + 'showTitle' => true, + 'titleFontSize' => '18', + 'width' => '800', + 'xAxis' => [ + 'showAxisLine' => true, + 'showLabel' => false, + 'tickLength' => '10', + ], + 'yAxis' => [ + 'showTitle' => true, + 'tickWidth' => '2', + ], + ], + ], + [ 'xyChart;' ], + ], + ], + [ + [ + 'xyChart;', + 'config.xyChart.height=400', + 'config.xyChart.plotReservedSpacePercent=15', + 'config.xyChart.xAxis.labelFontSize=12', + 'config.xyChart.xAxis.showTick=false', + 'config.xyChart.yAxis.labelPadding=8', + 'config.xyChart.yAxis.showAxisLine=false', + ], + [ + [ + 'xyChart' => [ + 'height' => '400', + 'plotReservedSpacePercent' => '15', + 'xAxis' => [ + 'labelFontSize' => '12', + 'showTick' => false, + ], + 'yAxis' => [ + 'labelPadding' => '8', + 'showAxisLine' => false, + ], + ], + ], + [ 'xyChart;' ], + ], + ], ]; } diff --git a/tsconfig.json b/tsconfig.json new file mode 100644 index 0000000..b5bc166 --- /dev/null +++ b/tsconfig.json @@ -0,0 +1,15 @@ +{ + "compilerOptions": { + "target": "ESNext", + "module": "ESNext", + "moduleResolution": "node", + "strict": true, + "esModuleInterop": true, + "skipLibCheck": true, + "forceConsistentCasingInFileNames": true + }, + "include": [ + "scripts/**/*", + "src/**/*" + ] +}