mirror of
https://github.com/mermaid-js/mermaid.git
synced 2025-01-28 07:03:17 +08:00
Merge pull request #6002 from aloisklink/fix/make-parse-error-on-invalid-shape
fix: make `mermaid.parse` throw an error on invalid shapes
This commit is contained in:
commit
8d0902de4c
5
.changeset/thick-elephants-search.md
Normal file
5
.changeset/thick-elephants-search.md
Normal file
@ -0,0 +1,5 @@
|
||||
---
|
||||
'mermaid': patch
|
||||
---
|
||||
|
||||
fix: error `mermaid.parse` on an invalid shape, so that it matches the errors thrown by `mermaid.render`
|
@ -2,6 +2,7 @@ import { select } from 'd3';
|
||||
import utils, { getEdgeId } from '../../utils.js';
|
||||
import { getConfig, defaultConfig } from '../../diagram-api/diagramAPI.js';
|
||||
import common from '../common/common.js';
|
||||
import { isValidShape, type ShapeID } from '../../rendering-util/rendering-elements/shapes.js';
|
||||
import type { Node, Edge } from '../../rendering-util/types.js';
|
||||
import { log } from '../../logger.js';
|
||||
import * as yaml from 'js-yaml';
|
||||
@ -14,7 +15,15 @@ import {
|
||||
setDiagramTitle,
|
||||
getDiagramTitle,
|
||||
} from '../common/commonDb.js';
|
||||
import type { FlowVertex, FlowClass, FlowSubGraph, FlowText, FlowEdge, FlowLink } from './types.js';
|
||||
import type {
|
||||
FlowVertex,
|
||||
FlowClass,
|
||||
FlowSubGraph,
|
||||
FlowText,
|
||||
FlowEdge,
|
||||
FlowLink,
|
||||
FlowVertexTypeParam,
|
||||
} from './types.js';
|
||||
import type { NodeMetaData } from '../../types.js';
|
||||
|
||||
const MERMAID_DOM_ID_PREFIX = 'flowchart-';
|
||||
@ -53,12 +62,11 @@ export const lookUpDomId = function (id: string) {
|
||||
|
||||
/**
|
||||
* Function called by parser when a node definition has been found
|
||||
*
|
||||
*/
|
||||
export const addVertex = function (
|
||||
id: string,
|
||||
textObj: FlowText,
|
||||
type: 'group',
|
||||
type: FlowVertexTypeParam,
|
||||
style: string[],
|
||||
classes: string[],
|
||||
dir: string,
|
||||
@ -133,14 +141,15 @@ export const addVertex = function (
|
||||
}
|
||||
// console.log('yamlData', yamlData);
|
||||
const doc = yaml.load(yamlData, { schema: yaml.JSON_SCHEMA }) as NodeMetaData;
|
||||
if (doc.shape && (doc.shape !== doc.shape.toLowerCase() || doc.shape.includes('_'))) {
|
||||
throw new Error(`No such shape: ${doc.shape}. Shape names should be lowercase.`);
|
||||
}
|
||||
|
||||
// console.log('yamlData doc', doc);
|
||||
if (doc?.shape) {
|
||||
if (doc.shape) {
|
||||
if (doc.shape !== doc.shape.toLowerCase() || doc.shape.includes('_')) {
|
||||
throw new Error(`No such shape: ${doc.shape}. Shape names should be lowercase.`);
|
||||
} else if (!isValidShape(doc.shape)) {
|
||||
throw new Error(`No such shape: ${doc.shape}.`);
|
||||
}
|
||||
vertex.type = doc?.shape;
|
||||
}
|
||||
|
||||
if (doc?.label) {
|
||||
vertex.text = doc?.label;
|
||||
}
|
||||
@ -816,7 +825,7 @@ export const lex = {
|
||||
firstGraph,
|
||||
};
|
||||
|
||||
const getTypeFromVertex = (vertex: FlowVertex) => {
|
||||
const getTypeFromVertex = (vertex: FlowVertex): ShapeID => {
|
||||
if (vertex.img) {
|
||||
return 'imageSquare';
|
||||
}
|
||||
@ -832,14 +841,18 @@ const getTypeFromVertex = (vertex: FlowVertex) => {
|
||||
}
|
||||
return 'icon';
|
||||
}
|
||||
if (vertex.type === 'square') {
|
||||
return 'squareRect';
|
||||
switch (vertex.type) {
|
||||
case 'square':
|
||||
case undefined:
|
||||
return 'squareRect';
|
||||
case 'round':
|
||||
return 'roundedRect';
|
||||
case 'ellipse':
|
||||
// @ts-expect-error -- Ellipses are broken, see https://github.com/mermaid-js/mermaid/issues/5976
|
||||
return 'ellipse';
|
||||
default:
|
||||
return vertex.type;
|
||||
}
|
||||
if (vertex.type === 'round') {
|
||||
return 'roundedRect';
|
||||
}
|
||||
|
||||
return vertex.type ?? 'squareRect';
|
||||
};
|
||||
|
||||
const findNode = (nodes: Node[], id: string) => nodes.find((node) => node.id === id);
|
||||
|
@ -197,6 +197,21 @@ describe('when parsing directions', function () {
|
||||
expect(data4Layout.nodes[0].shape).toEqual('squareRect');
|
||||
expect(data4Layout.nodes[0].label).toEqual('This is }');
|
||||
});
|
||||
it('should error on non-existent shape', function () {
|
||||
expect(() => {
|
||||
flow.parser.parse(`flowchart TB
|
||||
A@{ shape: this-shape-does-not-exist }
|
||||
`);
|
||||
}).toThrow('No such shape: this-shape-does-not-exist.');
|
||||
});
|
||||
it('should error on internal-only shape', function () {
|
||||
expect(() => {
|
||||
// this shape does exist, but it's only supposed to be for internal/backwards compatibility use
|
||||
flow.parser.parse(`flowchart TB
|
||||
A@{ shape: rect_left_inv_arrow }
|
||||
`);
|
||||
}).toThrow('No such shape: rect_left_inv_arrow. Shape names should be lowercase.');
|
||||
});
|
||||
it('Diamond shapes should work as usual', function () {
|
||||
const res = flow.parser.parse(`flowchart TB
|
||||
A{This is a label}
|
||||
|
@ -1,3 +1,28 @@
|
||||
import type { ShapeID } from '../../rendering-util/rendering-elements/shapes.js';
|
||||
|
||||
/**
|
||||
* Valid `type` args to `yy.addVertex` taken from
|
||||
* `packages/mermaid/src/diagrams/flowchart/parser/flow.jison`
|
||||
*/
|
||||
export type FlowVertexTypeParam =
|
||||
| undefined
|
||||
| 'square'
|
||||
| 'doublecircle'
|
||||
| 'circle'
|
||||
| 'ellipse'
|
||||
| 'stadium'
|
||||
| 'subroutine'
|
||||
| 'rect'
|
||||
| 'cylinder'
|
||||
| 'round'
|
||||
| 'diamond'
|
||||
| 'hexagon'
|
||||
| 'odd'
|
||||
| 'trapezoid'
|
||||
| 'inv_trapezoid'
|
||||
| 'lean_right'
|
||||
| 'lean_left';
|
||||
|
||||
export interface FlowVertex {
|
||||
classes: string[];
|
||||
dir?: string;
|
||||
@ -10,7 +35,7 @@ export interface FlowVertex {
|
||||
props?: any;
|
||||
styles: string[];
|
||||
text?: string;
|
||||
type?: string;
|
||||
type?: ShapeID | FlowVertexTypeParam;
|
||||
icon?: string;
|
||||
form?: string;
|
||||
pos?: 't' | 'b';
|
||||
|
@ -23,7 +23,7 @@ export async function insertNode(elem: SVGGroup, node: Node, renderOptions: Shap
|
||||
}
|
||||
}
|
||||
|
||||
const shapeHandler = shapes[(node.shape ?? 'undefined') as keyof typeof shapes];
|
||||
const shapeHandler = node.shape ? shapes[node.shape] : undefined;
|
||||
|
||||
if (!shapeHandler) {
|
||||
throw new Error(`No such shape: ${node.shape}. Please check your syntax.`);
|
||||
|
@ -500,4 +500,8 @@ const generateShapeMap = () => {
|
||||
|
||||
export const shapes = generateShapeMap();
|
||||
|
||||
export function isValidShape(shape: string): shape is ShapeID {
|
||||
return shape in shapes;
|
||||
}
|
||||
|
||||
export type ShapeID = keyof typeof shapes;
|
||||
|
@ -1,5 +1,6 @@
|
||||
export type MarkdownWordType = 'normal' | 'strong' | 'em';
|
||||
import type { MermaidConfig } from '../config.type.js';
|
||||
import type { ShapeID } from './rendering-elements/shapes.js';
|
||||
export interface MarkdownWord {
|
||||
content: string;
|
||||
type: MarkdownWordType;
|
||||
@ -37,7 +38,7 @@ export interface Node {
|
||||
linkTarget?: string;
|
||||
tooltip?: string;
|
||||
padding?: number; //REMOVE?, use from LayoutData.config - Keep, this could be shape specific
|
||||
shape?: string;
|
||||
shape?: ShapeID;
|
||||
isGroup: boolean;
|
||||
width?: number;
|
||||
height?: number;
|
||||
|
Loading…
x
Reference in New Issue
Block a user