Using diagram api to add gitGraph

This commit is contained in:
Knut Sveidqvist 2022-07-18 16:00:03 +02:00
parent 0d4b09a0a0
commit 4c30e03f1e
27 changed files with 946 additions and 706 deletions

View File

@ -64,7 +64,7 @@ journey
commit
commit
</div>
<div class="mermaid2" style="width: 50%;">
<div class="mermaid" style="width: 50%;">
sequenceDiagram
title: My Sequence Diagram Title
accTitle: My Acc Sequence Diagram
@ -283,6 +283,9 @@ class Class10 {
class: {
// defaultRenderer: 'dagre-d3',
htmlLabels: true,
},
sequence: {
mirrorActors: false,
},
// gantt: { axisFormat: '%m/%d/%Y' },
// sequence: {

View File

@ -1401,6 +1401,15 @@ This sets the auto-wrap padding for the diagram (sides only)
**Notes:** Default value: 0.
## parse
### Parameters
- `text`
- `dia`
Returns **any**
## setSiteConfig
## setSiteConfig
@ -1420,14 +1429,6 @@ function _Default value: At default, will mirror Global Config_
Returns **[object][5]** The siteConfig
## parse
### Parameters
- `text`
Returns **any**
## getSiteConfig
## getSiteConfig
@ -1485,30 +1486,6 @@ options in-place
- `options` **any** The potential setConfig parameter
## addDirective
Pushes in a directive to the configuration
### Parameters
- `directive` **[object][5]** The directive to push in
## reset
## reset
| Function | Description | Type | Required | Values |
| -------- | ---------------------------- | ----------- | -------- | ------ |
| reset | Resets currentConfig to conf | Put Request | Required | None |
## conf
| Parameter | Description | Type | Required | Values |
| --------- | -------------------------------------------------------------- | ---------- | -------- | -------------------------------------------- |
| conf | base set of values, which currentConfig could be **reset** to. | Dictionary | Required | Any Values, with respect to the secure Array |
**Notes**: (default: current siteConfig ) (optional, default `getSiteConfig()`)
## render
Function that renders an svg with a graph from a chart definition. Usage example below.
@ -1537,16 +1514,36 @@ $(function () {
Returns **any**
## addDirective
Pushes in a directive to the configuration
### Parameters
- `directive` **[object][5]** The directive to push in
## reset
## reset
| Function | Description | Type | Required | Values |
| -------- | ---------------------------- | ----------- | -------- | ------ |
| reset | Resets currentConfig to conf | Put Request | Required | None |
## conf
| Parameter | Description | Type | Required | Values |
| --------- | -------------------------------------------------------------- | ---------- | -------- | -------------------------------------------- |
| conf | base set of values, which currentConfig could be **reset** to. | Dictionary | Required | Any Values, with respect to the secure Array |
**Notes**: (default: current siteConfig ) (optional, default `getSiteConfig()`)
## updateRendererConfigs
### Parameters
- `conf` **any**
## reinitialize
To be removed
## initialize
### Parameters

View File

@ -1,191 +1,32 @@
import c4Db from './diagrams/c4/c4Db';
import c4Renderer from './diagrams/c4/c4Renderer';
import c4Parser from './diagrams/c4/parser/c4Diagram';
import classDb from './diagrams/class/classDb';
import classRenderer from './diagrams/class/classRenderer';
import classRendererV2 from './diagrams/class/classRenderer-v2';
import classParser from './diagrams/class/parser/classDiagram';
import erDb from './diagrams/er/erDb';
import erRenderer from './diagrams/er/erRenderer';
import erParser from './diagrams/er/parser/erDiagram';
import flowDb from './diagrams/flowchart/flowDb';
import flowRenderer from './diagrams/flowchart/flowRenderer';
import flowRendererV2 from './diagrams/flowchart/flowRenderer-v2';
import flowParser from './diagrams/flowchart/parser/flow';
import ganttDb from './diagrams/gantt/ganttDb';
import ganttRenderer from './diagrams/gantt/ganttRenderer';
import ganttParser from './diagrams/gantt/parser/gantt';
import gitGraphAst from './diagrams/git/gitGraphAst';
import gitGraphRenderer from './diagrams/git/gitGraphRenderer';
import gitGraphParser from './diagrams/git/parser/gitGraph';
import infoDb from './diagrams/info/infoDb';
import infoRenderer from './diagrams/info/infoRenderer';
import infoParser from './diagrams/info/parser/info';
import pieParser from './diagrams/pie/parser/pie';
import pieDb from './diagrams/pie/pieDb';
import pieRenderer from './diagrams/pie/pieRenderer';
import requirementParser from './diagrams/requirement/parser/requirementDiagram';
import requirementDb from './diagrams/requirement/requirementDb';
import requirementRenderer from './diagrams/requirement/requirementRenderer';
import sequenceParser from './diagrams/sequence/parser/sequenceDiagram';
import sequenceDb from './diagrams/sequence/sequenceDb';
import sequenceRenderer from './diagrams/sequence/sequenceRenderer';
import stateParser from './diagrams/state/parser/stateDiagram';
import stateDb from './diagrams/state/stateDb';
import stateRenderer from './diagrams/state/stateRenderer';
import stateRendererV2 from './diagrams/state/stateRenderer-v2';
import journeyDb from './diagrams/user-journey/journeyDb';
import journeyRenderer from './diagrams/user-journey/journeyRenderer';
import journeyParser from './diagrams/user-journey/parser/journey';
import utils from './utils';
import * as configApi from './config';
import { log } from './logger';
import { getDiagrams } from './diagram-api/diagramAPI';
import detectType from './diagram-api/detectType';
class Diagram {
type = 'graph';
parser;
renderer;
db;
constructor(txt) {
const diagrams = getDiagrams();
const cnf = configApi.getConfig();
this.txt = txt;
this.type = utils.detectType(txt, cnf);
this.type = detectType(txt, cnf);
log.debug('Type ' + this.type);
switch (this.type) {
case 'c4':
this.parser = c4Parser;
this.parser.parser.yy = c4Db;
this.db = c4Db;
this.renderer = c4Renderer;
this.renderer.setConf(cnf.c4);
break;
case 'gitGraph':
this.parser = gitGraphParser;
this.parser.parser.yy = gitGraphAst;
this.db = gitGraphAst;
this.renderer = gitGraphRenderer;
this.txt = this.txt + '\n';
break;
case 'flowchart':
flowRenderer.setConf(cnf.flowchart);
cnf.flowchart.arrowMarkerAbsolute = cnf.arrowMarkerAbsolute;
flowDb.clear();
flowDb.setGen('gen-1');
this.parser = flowParser;
this.parser.parser.yy = flowDb;
this.db = flowDb;
this.renderer = flowRenderer;
break;
case 'flowchart-v2':
flowRendererV2.setConf(cnf.flowchart);
cnf.flowchart.arrowMarkerAbsolute = cnf.arrowMarkerAbsolute;
flowDb.clear();
flowDb.setGen('gen-2');
this.parser = flowParser;
this.parser.parser.yy = flowDb;
this.db = flowDb;
this.renderer = flowRendererV2;
break;
case 'sequenceDiagram':
case 'sequence':
cnf.sequence.arrowMarkerAbsolute = cnf.arrowMarkerAbsolute;
if (cnf.sequenceDiagram) {
// backwards compatibility
sequenceRenderer.setConf(Object.assign(cnf.sequence, cnf.sequenceDiagram));
console.error(
'`mermaid config.sequenceDiagram` has been renamed to `config.sequence`. Please update your mermaid config.'
);
}
this.parser = sequenceParser;
this.parser.parser.yy = sequenceDb;
this.db = sequenceDb;
this.db.setWrap(cnf.wrap);
this.renderer = sequenceRenderer;
this.renderer.setConf(cnf.sequence);
this.txt = this.txt + '\n';
break;
case 'gantt':
cnf.gantt.arrowMarkerAbsolute = cnf.arrowMarkerAbsolute;
this.parser = ganttParser;
this.parser.parser.yy = ganttDb;
this.db = ganttDb;
this.renderer = ganttRenderer;
ganttRenderer.setConf(cnf.gantt);
break;
case 'class':
cnf.class.arrowMarkerAbsolute = cnf.arrowMarkerAbsolute;
this.parser = classParser;
this.parser.parser.yy = classDb;
this.db = classDb;
this.db.clear();
this.renderer = classRenderer;
break;
case 'classDiagram':
cnf.class.arrowMarkerAbsolute = cnf.arrowMarkerAbsolute;
this.parser = classParser;
this.parser.parser.yy = classDb;
this.db = classDb;
this.db.clear();
this.renderer = classRendererV2;
break;
case 'state':
cnf.class.arrowMarkerAbsolute = cnf.arrowMarkerAbsolute;
this.parser = stateParser;
this.parser.parser.yy = stateDb;
this.db = stateDb;
this.db.clear();
this.renderer = stateRenderer;
break;
case 'stateDiagram':
cnf.class.arrowMarkerAbsolute = cnf.arrowMarkerAbsolute;
this.parser = stateParser;
this.parser.parser.yy = stateDb;
this.db = stateDb;
this.db.clear();
this.renderer = stateRendererV2;
break;
case 'info':
log.debug('info info info');
this.parser = infoParser;
this.parser.parser.yy = infoDb;
this.db = infoDb;
this.renderer = infoRenderer;
break;
case 'pie':
log.debug('pie');
this.parser = pieParser;
this.parser.parser.yy = pieDb;
this.db = pieDb;
this.renderer = pieRenderer;
break;
case 'er':
log.debug('er');
this.parser = erParser;
this.parser.parser.yy = erDb;
this.db = erDb;
this.renderer = erRenderer;
break;
case 'journey':
log.debug('Journey');
journeyRenderer.setConf(cnf.journey);
this.parser = journeyParser;
this.parser.parser.yy = journeyDb;
this.db = journeyDb;
this.db.clear();
this.renderer = journeyRenderer;
break;
case 'requirement':
case 'requirementDiagram':
log.debug('RequirementDiagram');
this.parser = requirementParser;
this.parser.parser.yy = requirementDb;
this.db = requirementDb;
this.renderer = requirementRenderer;
break;
default:
log.error('Unkown graphtype');
throw new Error('Unkown graphtype');
// console.log('this.type', this.type, diagrams[this.type]);
// Setup diagram
this.db = diagrams[this.type].db;
this.renderer = diagrams[this.type].renderer;
this.parser = diagrams[this.type].parser;
this.parser.parser.yy = this.db;
if (typeof diagrams[this.type].init === 'function') {
diagrams[this.type].init(cnf);
log.debug('Initialized diagram ' + this.type, cnf);
}
this.txt = this.txt + '\n';
this.parser.parser.yy.graphType = this.type;
this.parser.parser.yy.parseError = (str, hash) => {
const error = { str, hash };
@ -193,6 +34,33 @@ class Diagram {
};
this.parser.parse(this.txt);
}
parse(text) {
var parseEncounteredException = false;
try {
text = text + '\n';
this.db.clear();
this.parser.parse(text);
} catch (error) {
parseEncounteredException = true;
// Is this the correct way to access mermiad's parseError()
// method ? (or global.mermaid.parseError()) ?
if (global.mermaid.parseError) {
if (error.str != undefined) {
// handle case where error string and hash were
// wrapped in object like`const error = { str, hash };`
global.mermaid.parseError(error.str, error.hash);
} else {
// assume it is just error string and pass it on
global.mermaid.parseError(error);
}
} else {
// No mermaid.parseError() handler defined, so re-throw it
throw error;
}
}
return !parseEncounteredException;
}
getParser() {
return this.parser;
}

74
src/assignWithDepth.js Normal file
View File

@ -0,0 +1,74 @@
/**
* @function assignWithDepth Extends the functionality of {@link ObjectConstructor.assign} with the
* ability to merge arbitrary-depth objects For each key in src with path `k` (recursively)
* performs an Object.assign(dst[`k`], src[`k`]) with a slight change from the typical handling of
* undefined for dst[`k`]: instead of raising an error, dst[`k`] is auto-initialized to {} and
* effectively merged with src[`k`]<p> Additionally, dissimilar types will not clobber unless the
* config.clobber parameter === true. Example:
*
* ```js
* let config_0 = { foo: { bar: 'bar' }, bar: 'foo' };
* let config_1 = { foo: 'foo', bar: 'bar' };
* let result = assignWithDepth(config_0, config_1);
* console.log(result);
* //-> result: { foo: { bar: 'bar' }, bar: 'bar' }
* ```
*
* Traditional Object.assign would have clobbered foo in config_0 with foo in config_1. If src is a
* destructured array of objects and dst is not an array, assignWithDepth will apply each element
* of src to dst in order.
* @param dst
* @param src
* @param config
* @param dst
* @param src
* @param config
* @param dst
* @param src
* @param config
* @param {any} dst - The destination of the merge
* @param {any} src - The source object(s) to merge into destination
* @param {{ depth: number; clobber: boolean }} [config={ depth: 2, clobber: false }] - Depth: depth
* to traverse within src and dst for merging - clobber: should dissimilar types clobber (default:
* { depth: 2, clobber: false }). Default is `{ depth: 2, clobber: false }`
* @returns {any}
*/
const assignWithDepth = function (dst, src, config) {
const { depth, clobber } = Object.assign({ depth: 2, clobber: false }, config);
if (Array.isArray(src) && !Array.isArray(dst)) {
src.forEach((s) => assignWithDepth(dst, s, config));
return dst;
} else if (Array.isArray(src) && Array.isArray(dst)) {
src.forEach((s) => {
if (dst.indexOf(s) === -1) {
dst.push(s);
}
});
return dst;
}
if (typeof dst === 'undefined' || depth <= 0) {
if (dst !== undefined && dst !== null && typeof dst === 'object' && typeof src === 'object') {
return Object.assign(dst, src);
} else {
return src;
}
}
if (typeof src !== 'undefined' && typeof dst === 'object' && typeof src === 'object') {
Object.keys(src).forEach((key) => {
if (
typeof src[key] === 'object' &&
(dst[key] === undefined || typeof dst[key] === 'object')
) {
if (dst[key] === undefined) {
dst[key] = Array.isArray(src[key]) ? [] : {};
}
dst[key] = assignWithDepth(dst[key], src[key], { depth: depth - 1, clobber });
} else if (clobber || (typeof dst[key] !== 'object' && typeof src[key] !== 'object')) {
dst[key] = src[key];
}
});
}
return dst;
};
export default assignWithDepth;

View File

@ -1,4 +1,4 @@
import { assignWithDepth } from './utils';
import assignWithDepth from './assignWithDepth';
import { log } from './logger';
import theme from './themes';
import config from './defaultConfig';

View File

@ -0,0 +1,100 @@
const directive =
/[%]{2}[{]\s*(?:(?:(\w+)\s*:|(\w+))\s*(?:(?:(\w+))|((?:(?![}][%]{2}).|\r?\n)*))?\s*)(?:[}][%]{2})?/gi;
const anyComment = /\s*%%.*\n/gm;
const detectors = {};
/**
* @function detectType Detects the type of the graph text. Takes into consideration the possible
* existence of an %%init directive
*
* ```mermaid
* %%{initialize: {"startOnLoad": true, logLevel: "fatal" }}%%
* graph LR
* a-->b
* b-->c
* c-->d
* d-->e
* e-->f
* 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;
* }} [cnf]
* @returns {string} A graph definition key
*/
const detectType = function (text, cnf) {
text = text.replace(directive, '').replace(anyComment, '\n');
if (text.match(/^\s*C4Context|C4Container|C4Component|C4Dynamic|C4Deployment/)) {
return 'c4';
}
if (text.match(/^\s*sequenceDiagram/)) {
return 'sequence';
}
if (text.match(/^\s*gantt/)) {
return 'gantt';
}
if (text.match(/^\s*classDiagram-v2/)) {
return 'classDiagram';
}
if (text.match(/^\s*classDiagram/)) {
if (cnf && cnf.class && cnf.class.defaultRenderer === 'dagre-wrapper') return 'classDiagram';
return 'class';
}
if (text.match(/^\s*stateDiagram-v2/)) {
return 'stateDiagram';
}
if (text.match(/^\s*stateDiagram/)) {
if (cnf && cnf.class && cnf.state.defaultRenderer === 'dagre-wrapper') return 'stateDiagram';
return 'state';
}
// if (text.match(/^\s*gitGraph/)) {
// return 'gitGraph';
// }
if (text.match(/^\s*flowchart/)) {
return 'flowchart-v2';
}
if (text.match(/^\s*info/)) {
return 'info';
}
if (text.match(/^\s*pie/)) {
return 'pie';
}
if (text.match(/^\s*erDiagram/)) {
return 'er';
}
if (text.match(/^\s*journey/)) {
return 'journey';
}
if (text.match(/^\s*requirement/) || text.match(/^\s*requirementDiagram/)) {
return 'requirement';
}
if (cnf && cnf.flowchart && cnf.flowchart.defaultRenderer === 'dagre-wrapper')
return 'flowchart-v2';
const k = Object.keys(detectors);
for (let i = 0; i < k.length; i++) {
const key = k[i];
const dia = detectors[key];
if (dia && dia.detector(text)) {
return key;
}
}
return 'flowchart';
};
export const addDetector = (key, detector) => {
detectors[key] = {
detector,
};
};
export default detectType;

View File

@ -0,0 +1,31 @@
import { registerDiagram } from './diagramAPI.js';
// import mindmapDb from '../diagrams/mindmap/mindmapDb';
// import mindmapRenderer from '../diagrams/mindmap/mindmapRenderer';
// import mindmapParser from '../diagrams/mindmap/parser/mindmapDiagram';
// import mindmapDetector from '../diagrams/mindmap/mindmapDetector';
import gitGraphDb from '../diagrams/git/gitGraphAst';
import gitGraphRenderer from '../diagrams/git/gitGraphRenderer';
import gitGraphParser from '../diagrams/git/parser/gitGraph';
import gitGraphDetector from '../diagrams/git/gitGraphDetector';
// Register mindmap and other built-in diagrams
// registerDiagram(
// 'mindmap',
// mindmapParser,
// mindmapDb,
// mindmapRenderer,
// undefined,
// mindmapRenderer,
// mindmapDetector
// );
registerDiagram(
'gitGraph',
gitGraphParser,
gitGraphDb,
gitGraphRenderer,
undefined,
gitGraphDetector
);
const apa = { apa: 1 };
export default apa;

View File

@ -1,9 +1,175 @@
const diagrams = {};
import c4Db from '../diagrams/c4/c4Db';
import c4Renderer from '../diagrams/c4/c4Renderer';
import c4Parser from '../diagrams/c4/parser/c4Diagram';
import classDb from '../diagrams/class/classDb';
import classRenderer from '../diagrams/class/classRenderer';
import classRendererV2 from '../diagrams/class/classRenderer-v2';
import classParser from '../diagrams/class/parser/classDiagram';
import erDb from '../diagrams/er/erDb';
import erRenderer from '../diagrams/er/erRenderer';
import erParser from '../diagrams/er/parser/erDiagram';
import flowDb from '../diagrams/flowchart/flowDb';
import flowRenderer from '../diagrams/flowchart/flowRenderer';
import flowRendererV2 from '../diagrams/flowchart/flowRenderer-v2';
import flowParser from '../diagrams/flowchart/parser/flow';
import ganttDb from '../diagrams/gantt/ganttDb';
import ganttRenderer from '../diagrams/gantt/ganttRenderer';
import ganttParser from '../diagrams/gantt/parser/gantt';
import infoDb from '../diagrams/info/infoDb';
import infoRenderer from '../diagrams/info/infoRenderer';
import infoParser from '../diagrams/info/parser/info';
import pieParser from '../diagrams/pie/parser/pie';
import pieDb from '../diagrams/pie/pieDb';
import pieRenderer from '../diagrams/pie/pieRenderer';
import requirementParser from '../diagrams/requirement/parser/requirementDiagram';
import requirementDb from '../diagrams/requirement/requirementDb';
import requirementRenderer from '../diagrams/requirement/requirementRenderer';
import sequenceParser from '../diagrams/sequence/parser/sequenceDiagram';
import sequenceDb from '../diagrams/sequence/sequenceDb';
import sequenceRenderer from '../diagrams/sequence/sequenceRenderer';
import stateParser from '../diagrams/state/parser/stateDiagram';
import stateDb from '../diagrams/state/stateDb';
import stateRenderer from '../diagrams/state/stateRenderer';
import stateRendererV2 from '../diagrams/state/stateRenderer-v2';
import journeyDb from '../diagrams/user-journey/journeyDb';
import journeyRenderer from '../diagrams/user-journey/journeyRenderer';
import journeyParser from '../diagrams/user-journey/parser/journey';
import { addDetector } from './detectType';
export const registerDiagram = (id, parser, identifier, renderer) => {
diagrams[id] = { parser, identifier, renderer };
const diagrams = {
c4: {
db: c4Db,
renderer: c4Renderer,
parser: c4Parser,
init: (cnf) => {
c4Renderer.setConf(cnf.c4);
},
},
class: {
db: classDb,
renderer: classRenderer,
parser: classParser,
init: (cnf) => {
cnf.class.arrowMarkerAbsolute = cnf.arrowMarkerAbsolute;
classDb.clear();
},
},
classDiagram: {
db: classDb,
renderer: classRendererV2,
parser: classParser,
init: (cnf) => {
cnf.class.arrowMarkerAbsolute = cnf.arrowMarkerAbsolute;
classDb.clear();
},
},
er: {
db: erDb,
renderer: erRenderer,
parser: erParser,
},
flowchart: {
db: flowDb,
renderer: flowRenderer,
parser: flowParser,
init: (cnf) => {
flowRenderer.setConf(cnf.flowchart);
cnf.flowchart.arrowMarkerAbsolute = cnf.arrowMarkerAbsolute;
flowDb.clear();
flowDb.setGen('gen-1');
},
},
flowchartV2: {
db: flowDb,
renderer: flowRendererV2,
parser: flowParser,
init: (cnf) => {
flowRendererV2.setConf(cnf.flowchart);
cnf.flowchart.arrowMarkerAbsolute = cnf.arrowMarkerAbsolute;
flowDb.clear();
flowDb.setGen('gen-2');
},
},
gantt: {
db: ganttDb,
renderer: ganttRenderer,
parser: ganttParser,
init: (cnf) => {
ganttRenderer.setConf(cnf.gantt);
},
},
// git: {
// db: gitGraphAst,
// renderer: gitGraphRenderer,
// parser: gitGraphParser,
// },
info: {
db: infoDb,
renderer: infoRenderer,
parser: infoParser,
},
pie: {
db: pieDb,
renderer: pieRenderer,
parser: pieParser,
},
requirement: {
db: requirementDb,
renderer: requirementRenderer,
parser: requirementParser,
},
sequence: {
db: sequenceDb,
renderer: sequenceRenderer,
parser: sequenceParser,
init: (cnf) => {
cnf.sequence.arrowMarkerAbsolute = cnf.arrowMarkerAbsolute;
if (cnf.sequenceDiagram) {
// backwards compatibility
sequenceRenderer.setConf(Object.assign(cnf.sequence, cnf.sequenceDiagram));
console.error(
'`mermaid config.sequenceDiagram` has been renamed to `config.sequence`. Please update your mermaid config.'
);
}
sequenceDb.setWrap(cnf.wrap);
sequenceRenderer.setConf(cnf.sequence);
},
},
state: {
db: stateDb,
renderer: stateRenderer,
parser: stateParser,
init: (cnf) => {
cnf.class.arrowMarkerAbsolute = cnf.arrowMarkerAbsolute;
stateDb.clear();
},
},
stateDiagram: {
db: stateDb,
renderer: stateRendererV2,
parser: stateParser,
init: (cnf) => {
cnf.class.arrowMarkerAbsolute = cnf.arrowMarkerAbsolute;
stateDb.clear();
},
},
journey: {
db: journeyDb,
renderer: journeyRenderer,
parser: journeyParser,
init: (cnf) => {
journeyRenderer.setConf(cnf.journey);
journeyDb.clear();
},
},
};
// console.log(sequenceDb);
export const registerDiagram = (id, parser, db, renderer, init, detector) => {
diagrams[id] = { parser, db, renderer, init };
addDetector(id, detector);
};
export const getDiagrams = () => {
// console.log('diagrams', diagrams);
return diagrams;
};

View File

@ -5,13 +5,8 @@ import { parser } from './parser/c4Diagram';
import common from '../common/common';
import c4Db from './c4Db';
import * as configApi from '../../config';
import utils, {
wrapLabel,
calculateTextWidth,
calculateTextHeight,
assignWithDepth,
configureSvgSize,
} from '../../utils';
import assignWithDepth from '../../assignWithDepth';
import { wrapLabel, calculateTextWidth, calculateTextHeight, configureSvgSize } from '../../utils';
import addSVGAccessibilityFields from '../../accessibility';
let globalBoundaryMaxX = 0,

View File

@ -1,6 +1,5 @@
import { addVertices, addEdges } from './flowRenderer';
import { setConfig } from '../../config';
import Diagram from '../../Diagram';
setConfig({
flowchart: {

View File

@ -0,0 +1,8 @@
const detector = (txt) => {
if (txt.match(/^\s*gitGraph/)) {
return 'gitGraph';
}
return null;
};
export default detector;

View File

@ -0,0 +1,14 @@
describe('when parsing an info graph it', function () {
var ex;
beforeEach(function () {
ex = require('./parser/info').parser;
ex.yy = require('./infoDb');
});
it('should handle an info definition', function () {
var str = `info
showInfo`;
ex.parse(str);
});
});

View File

@ -0,0 +1,34 @@
/** Created by knut on 15-01-14. */
import { log } from '../../logger';
var message = '';
var info = false;
export const setMessage = (txt) => {
log.debug('Setting message to: ' + txt);
message = txt;
};
export const getMessage = () => {
return message;
};
export const setInfo = (inf) => {
info = inf;
};
export const getInfo = () => {
return info;
};
// export const parseError = (err, hash) => {
// global.mermaidAPI.parseError(err, hash)
// }
export default {
setMessage,
getMessage,
setInfo,
getInfo,
// parseError
};

View File

@ -0,0 +1,59 @@
/** Created by knut on 14-12-11. */
import { select } from 'd3';
import { log } from '../../logger';
import { getConfig } from '../../config';
/**
* Draws a an info picture in the tag with id: id based on the graph definition in text.
*
* @param {any} text
* @param {any} id
* @param {any} version
* @param diagObj
*/
export const draw = (text, id, version, diagObj) => {
try {
// const parser = infoParser.parser;
// parser.yy = db;
log.debug('Renering info diagram\n' + text);
const securityLevel = getConfig().securityLevel;
// Handle root and Document for when rendering in sanbox mode
let sandboxElement;
if (securityLevel === 'sandbox') {
sandboxElement = select('#i' + id);
}
const root =
securityLevel === 'sandbox'
? select(sandboxElement.nodes()[0].contentDocument.body)
: select('body');
const doc = securityLevel === 'sandbox' ? sandboxElement.nodes()[0].contentDocument : document;
// Parse the graph definition
// parser.parse(text);
// log.debug('Parsed info diagram');
// Fetch the default direction, use TD if none was found
const svg = root.select('#' + id);
const g = svg.append('g');
g.append('text') // text label for the x axis
.attr('x', 100)
.attr('y', 40)
.attr('class', 'version')
.attr('font-size', '32px')
.style('text-anchor', 'middle')
.text('v ' + version);
svg.attr('height', 100);
svg.attr('width', 400);
// svg.attr('viewBox', '0 0 300 150');
} catch (e) {
log.error('Error while rendering info diagram');
log.error(e.message);
}
};
export default {
draw,
};

View File

@ -0,0 +1,8 @@
const detector = (txt) => {
if (txt.match(/^\s*mindmap/)) {
return 'mindmap';
}
return null;
};
export default detector;

View File

@ -0,0 +1,48 @@
/** mermaid
* https://knsv.github.io/mermaid
* (c) 2015 Knut Sveidqvist
* MIT license.
*/
%lex
%options case-insensitive
%{
// Pre-lexer code can go here
%}
%%
"info" return 'info' ;
[\s\n\r]+ return 'NL' ;
[\s]+ return 'space';
"showInfo" return 'showInfo';
<<EOF>> return 'EOF' ;
. return 'TXT' ;
/lex
%start start
%% /* language grammar */
start
// %{ : info document 'EOF' { return yy; } }
: info document 'EOF' { return yy; }
;
document
: /* empty */
| document line
;
line
: statement { }
| 'NL'
;
statement
: showInfo { yy.setInfo(true); }
;
%%

View File

@ -0,0 +1,3 @@
const getStyles = () => ``;
export default getStyles;

View File

@ -140,7 +140,14 @@ export const setWrap = function (wrapSetting) {
wrapEnabled = wrapSetting;
};
export const autoWrap = () => wrapEnabled;
export const autoWrap = () => {
// if setWrap has been called, use that value, otherwise use the value from the config
// TODO: refactor, always use the config value let setWrap update the config value
if (typeof wrapEnabled !== 'undefined') {
return wrapEnabled;
}
return configApi.getConfig().sequence.wrap;
};
export const clear = function () {
actors = {};

File diff suppressed because it is too large Load Diff

View File

@ -5,7 +5,8 @@ import { log } from '../../logger';
import common from '../common/common';
// import sequenceDb from './sequenceDb';
import * as configApi from '../../config';
import utils, { assignWithDepth, configureSvgSize } from '../../utils';
import assignWithDepth from '../../assignWithDepth';
import utils, { configureSvgSize } from '../../utils';
import addSVGAccessibilityFields from '../../accessibility';
let conf = {};

View File

@ -69,7 +69,6 @@ export const draw = function (text, id, version, diagObj) {
svgDraw.initGraphics(diagram);
const tasks = diagObj.db.getTasks();
console.log('text and tasks', text, tasks);
const title = diagObj.db.getDiagramTitle();
const actorNames = diagObj.db.getActors();

View File

@ -209,7 +209,7 @@ const setParseErrorHandler = function (newParseErrorHandler) {
const mermaid = {
startOnLoad: true,
htmlLabels: true,
diagrams: {},
mermaidAPI,
parse: mermaidAPI != undefined ? mermaidAPI.parse : null,
render: mermaidAPI != undefined ? mermaidAPI.render : null,

View File

@ -19,56 +19,27 @@ import { select } from 'd3';
import { compile, serialize, stringify } from 'stylis';
import pkg from '../package.json';
import * as configApi from './config';
import c4Db from './diagrams/c4/c4Db';
import c4Renderer from './diagrams/c4/c4Renderer';
import c4Parser from './diagrams/c4/parser/c4Diagram';
import apa from './diagram-api/diagram-orchestration';
import classDb from './diagrams/class/classDb';
import classRenderer from './diagrams/class/classRenderer';
import classRendererV2 from './diagrams/class/classRenderer-v2';
import classParser from './diagrams/class/parser/classDiagram';
import erDb from './diagrams/er/erDb';
import erRenderer from './diagrams/er/erRenderer';
import erParser from './diagrams/er/parser/erDiagram';
import flowDb from './diagrams/flowchart/flowDb';
import flowRenderer from './diagrams/flowchart/flowRenderer';
import flowRendererV2 from './diagrams/flowchart/flowRenderer-v2';
import flowParser from './diagrams/flowchart/parser/flow';
import ganttDb from './diagrams/gantt/ganttDb';
import ganttRenderer from './diagrams/gantt/ganttRenderer';
import ganttParser from './diagrams/gantt/parser/gantt';
import gitGraphAst from './diagrams/git/gitGraphAst';
import gitGraphRenderer from './diagrams/git/gitGraphRenderer';
import gitGraphParser from './diagrams/git/parser/gitGraph';
import infoDb from './diagrams/info/infoDb';
import infoRenderer from './diagrams/info/infoRenderer';
import infoParser from './diagrams/info/parser/info';
import pieParser from './diagrams/pie/parser/pie';
import pieDb from './diagrams/pie/pieDb';
import pieRenderer from './diagrams/pie/pieRenderer';
import addSVGAccessibilityFields from './diagrams/pie/pieRenderer';
import requirementParser from './diagrams/requirement/parser/requirementDiagram';
import requirementDb from './diagrams/requirement/requirementDb';
import requirementRenderer from './diagrams/requirement/requirementRenderer';
import sequenceParser from './diagrams/sequence/parser/sequenceDiagram';
import sequenceDb from './diagrams/sequence/sequenceDb';
import sequenceRenderer from './diagrams/sequence/sequenceRenderer';
import stateParser from './diagrams/state/parser/stateDiagram';
import stateDb from './diagrams/state/stateDb';
import stateRenderer from './diagrams/state/stateRenderer';
import stateRendererV2 from './diagrams/state/stateRenderer-v2';
import journeyDb from './diagrams/user-journey/journeyDb';
import journeyRenderer from './diagrams/user-journey/journeyRenderer';
import journeyParser from './diagrams/user-journey/parser/journey';
import Diagram from './Diagram';
import errorRenderer from './errorRenderer';
import { attachFunctions } from './interactionDb';
import { log, setLogLevel } from './logger';
import getStyles from './styles';
import theme from './themes';
import utils, { directiveSanitizer, assignWithDepth, sanitizeCss } from './utils';
import utils, { directiveSanitizer } from './utils';
import assignWithDepth from './assignWithDepth';
import DOMPurify from 'dompurify';
import mermaid from './mermaid';
/**
* @param text
* @param dia
@ -77,20 +48,9 @@ import mermaid from './mermaid';
function parse(text, dia) {
var parseEncounteredException = false;
try {
text = text + '\n';
const diag = dia ? dia : new Diagram(text);
diag.db.clear();
const cnf = configApi.getConfig();
let parser = diag.parser;
log.debug('Type ' + diag.type);
parser.parser.yy.graphType = diag.type;
parser.parser.yy.parseError = (str, hash) => {
const error = { str, hash };
throw error;
};
parser.parse(text);
return diag.parse(text);
} catch (error) {
parseEncounteredException = true;
// Is this the correct way to access mermiad's parseError()
@ -290,6 +250,7 @@ const render = function (id, _txt, cb, container) {
txt = encodeEntities(txt);
// Imortant that we do not create the diagram until after the directives have been included
const diag = new Diagram(txt);
// Get the tmp element containing the the svg
const element = root.select('#d' + id).node();

View File

@ -1,6 +1,6 @@
import mermaid from './mermaid';
import mermaidAPI from './mermaidAPI';
import { assignWithDepth } from './utils';
import assignWithDepth from './assignWithDepth';
describe('when using mermaidAPI and ', function () {
describe('doing initialize ', function () {

View File

@ -16,6 +16,8 @@ import {
import common from './diagrams/common/common';
import { configKeys } from './defaultConfig';
import { log } from './logger';
import detectType from './diagram-api/detectType';
import assignWithDepth from './assignWithDepth';
// Effectively an enum of the supported curve types, accessible by name
const d3CurveTypes = {
@ -160,90 +162,6 @@ export const detectDirective = function (text, type = null) {
}
};
/**
* @function detectType Detects the type of the graph text. Takes into consideration the possible
* existence of an %%init directive
*
* ```mermaid
* %%{initialize: {"startOnLoad": true, logLevel: "fatal" }}%%
* graph LR
* a-->b
* b-->c
* c-->d
* d-->e
* e-->f
* 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;
* }} [cnf]
* @returns {string} A graph definition key
*/
export const detectType = function (text, cnf) {
text = text.replace(directive, '').replace(anyComment, '\n');
if (text.match(/^\s*C4Context|C4Container|C4Component|C4Dynamic|C4Deployment/)) {
return 'c4';
}
if (text.match(/^\s*sequenceDiagram/)) {
return 'sequence';
}
if (text.match(/^\s*gantt/)) {
return 'gantt';
}
if (text.match(/^\s*classDiagram-v2/)) {
return 'classDiagram';
}
if (text.match(/^\s*classDiagram/)) {
if (cnf && cnf.class && cnf.class.defaultRenderer === 'dagre-wrapper') return 'classDiagram';
return 'class';
}
if (text.match(/^\s*stateDiagram-v2/)) {
return 'stateDiagram';
}
if (text.match(/^\s*stateDiagram/)) {
if (cnf && cnf.class && cnf.state.defaultRenderer === 'dagre-wrapper') return 'stateDiagram';
return 'state';
}
if (text.match(/^\s*gitGraph/)) {
return 'gitGraph';
}
if (text.match(/^\s*flowchart/)) {
return 'flowchart-v2';
}
if (text.match(/^\s*info/)) {
return 'info';
}
if (text.match(/^\s*pie/)) {
return 'pie';
}
if (text.match(/^\s*erDiagram/)) {
return 'er';
}
if (text.match(/^\s*journey/)) {
return 'journey';
}
if (text.match(/^\s*requirement/) || text.match(/^\s*requirementDiagram/)) {
return 'requirement';
}
if (cnf && cnf.flowchart && cnf.flowchart.defaultRenderer === 'dagre-wrapper')
return 'flowchart-v2';
return 'flowchart';
};
/**
* Caches results of functions based on input
*
@ -578,79 +496,6 @@ export const random = (options) => {
return makeid(options.length);
};
/**
* @function assignWithDepth Extends the functionality of {@link ObjectConstructor.assign} with the
* ability to merge arbitrary-depth objects For each key in src with path `k` (recursively)
* performs an Object.assign(dst[`k`], src[`k`]) with a slight change from the typical handling of
* undefined for dst[`k`]: instead of raising an error, dst[`k`] is auto-initialized to {} and
* effectively merged with src[`k`]<p> Additionally, dissimilar types will not clobber unless the
* config.clobber parameter === true. Example:
*
* ```js
* let config_0 = { foo: { bar: 'bar' }, bar: 'foo' };
* let config_1 = { foo: 'foo', bar: 'bar' };
* let result = assignWithDepth(config_0, config_1);
* console.log(result);
* //-> result: { foo: { bar: 'bar' }, bar: 'bar' }
* ```
*
* Traditional Object.assign would have clobbered foo in config_0 with foo in config_1. If src is a
* destructured array of objects and dst is not an array, assignWithDepth will apply each element
* of src to dst in order.
* @param dst
* @param src
* @param config
* @param dst
* @param src
* @param config
* @param dst
* @param src
* @param config
* @param {any} dst - The destination of the merge
* @param {any} src - The source object(s) to merge into destination
* @param {{ depth: number; clobber: boolean }} [config={ depth: 2, clobber: false }] - Depth: depth
* to traverse within src and dst for merging - clobber: should dissimilar types clobber (default:
* { depth: 2, clobber: false }). Default is `{ depth: 2, clobber: false }`
* @returns {any}
*/
export const assignWithDepth = function (dst, src, config) {
const { depth, clobber } = Object.assign({ depth: 2, clobber: false }, config);
if (Array.isArray(src) && !Array.isArray(dst)) {
src.forEach((s) => assignWithDepth(dst, s, config));
return dst;
} else if (Array.isArray(src) && Array.isArray(dst)) {
src.forEach((s) => {
if (dst.indexOf(s) === -1) {
dst.push(s);
}
});
return dst;
}
if (typeof dst === 'undefined' || depth <= 0) {
if (dst !== undefined && dst !== null && typeof dst === 'object' && typeof src === 'object') {
return Object.assign(dst, src);
} else {
return src;
}
}
if (typeof src !== 'undefined' && typeof dst === 'object' && typeof src === 'object') {
Object.keys(src).forEach((key) => {
if (
typeof src[key] === 'object' &&
(dst[key] === undefined || typeof dst[key] === 'object')
) {
if (dst[key] === undefined) {
dst[key] = Array.isArray(src[key]) ? [] : {};
}
dst[key] = assignWithDepth(dst[key], src[key], { depth: depth - 1, clobber });
} else if (clobber || (typeof dst[key] !== 'object' && typeof src[key] !== 'object')) {
dst[key] = src[key];
}
});
}
return dst;
};
export const getTextObj = function () {
return {
x: 0,
@ -1096,7 +941,6 @@ export default {
setupGraphViewbox,
detectInit,
detectDirective,
detectType,
isSubstringInArray,
interpolateToCurve,
calcLabelPosition,

View File

@ -1,52 +1,55 @@
import utils from './utils';
import assignWithDepth from './assignWithDepth';
import detectType from './diagram-api/detectType';
import './diagram-api/diagram-orchestration';
describe('when assignWithDepth: should merge objects within objects', function () {
it('should handle simple, depth:1 types (identity)', function () {
let config_0 = { foo: 'bar', bar: 0 };
let config_1 = { foo: 'bar', bar: 0 };
let result = utils.assignWithDepth(config_0, config_1);
let result = assignWithDepth(config_0, config_1);
expect(result).toEqual(config_1);
});
it('should handle simple, depth:1 types (dst: undefined)', function () {
let config_0 = undefined;
let config_1 = { foo: 'bar', bar: 0 };
let result = utils.assignWithDepth(config_0, config_1);
let result = assignWithDepth(config_0, config_1);
expect(result).toEqual(config_1);
});
it('should handle simple, depth:1 types (src: undefined)', function () {
let config_0 = { foo: 'bar', bar: 0 };
let config_1 = undefined;
let result = utils.assignWithDepth(config_0, config_1);
let result = assignWithDepth(config_0, config_1);
expect(result).toEqual(config_0);
});
it('should handle simple, depth:1 types (merge)', function () {
let config_0 = { foo: 'bar', bar: 0 };
let config_1 = { foo: 'foo' };
let result = utils.assignWithDepth(config_0, config_1);
let result = assignWithDepth(config_0, config_1);
expect(result).toEqual({ foo: 'foo', bar: 0 });
});
it('should handle depth:2 types (dst: orphan)', function () {
let config_0 = { foo: 'bar', bar: { foo: 'bar' } };
let config_1 = { foo: 'bar' };
let result = utils.assignWithDepth(config_0, config_1);
let result = assignWithDepth(config_0, config_1);
expect(result).toEqual(config_0);
});
it('should handle depth:2 types (dst: object, src: simple type)', function () {
let config_0 = { foo: 'bar', bar: { foo: 'bar' } };
let config_1 = { foo: 'foo', bar: 'should NOT clobber' };
let result = utils.assignWithDepth(config_0, config_1);
let result = assignWithDepth(config_0, config_1);
expect(result).toEqual({ foo: 'foo', bar: { foo: 'bar' } });
});
it('should handle depth:2 types (src: orphan)', function () {
let config_0 = { foo: 'bar' };
let config_1 = { foo: 'bar', bar: { foo: 'bar' } };
let result = utils.assignWithDepth(config_0, config_1);
let result = assignWithDepth(config_0, config_1);
expect(result).toEqual(config_1);
});
it('should handle depth:2 types (merge)', function () {
let config_0 = { foo: 'bar', bar: { foo: 'bar' }, boofar: 1 };
let config_1 = { foo: 'foo', bar: { bar: 0 }, foobar: 'foobar' };
let result = utils.assignWithDepth(config_0, config_1);
let result = assignWithDepth(config_0, config_1);
expect(result).toEqual({
foo: 'foo',
bar: { foo: 'bar', bar: 0 },
@ -65,7 +68,7 @@ describe('when assignWithDepth: should merge objects within objects', function (
bar: { foo: 'foo', bar: { foo: { message: 'clobbered other foo' } } },
foobar: 'foobar',
};
let result = utils.assignWithDepth(config_0, config_1);
let result = assignWithDepth(config_0, config_1);
expect(result).toEqual({
foo: 'foo',
bar: { foo: 'foo', bar: { foo: { message: 'clobbered other foo' } } },
@ -87,7 +90,7 @@ describe('when assignWithDepth: should merge objects within objects', function (
bar: { foo: 'foo', bar: { foo: { message: 'this' } } },
foobar: 'foobar',
};
let result = utils.assignWithDepth(config_0, config_1, { depth: 1 });
let result = assignWithDepth(config_0, config_1, { depth: 1 });
expect(result).toEqual({
foo: 'foo',
bar: { foo: 'foo', bar: { foo: { message: 'this' } } },
@ -106,7 +109,7 @@ describe('when assignWithDepth: should merge objects within objects', function (
bar: { foo: 'foo', bar: { foo: { message: 'this' } } },
foobar: 'foobar',
};
let result = utils.assignWithDepth(config_0, config_1, { depth: 3 });
let result = assignWithDepth(config_0, config_1, { depth: 3 });
expect(result).toEqual({
foo: 'foo',
bar: { foo: 'foo', bar: { foo: { message: 'this', willbe: 'present' } } },
@ -137,7 +140,7 @@ describe('when memoizing', function () {
describe('when detecting chart type ', function () {
it('should handle a graph definition', function () {
const str = 'graph TB\nbfs1:queue';
const type = utils.detectType(str);
const type = detectType(str);
expect(type).toBe('flowchart');
});
it('should handle an initialize definition', function () {
@ -145,7 +148,7 @@ describe('when detecting chart type ', function () {
%%{initialize: { 'logLevel': 0, 'theme': 'dark' }}%%
sequenceDiagram
Alice->Bob: hi`;
const type = utils.detectType(str);
const type = detectType(str);
const init = utils.detectInit(str);
expect(type).toBe('sequence');
expect(init).toEqual({ logLevel: 0, theme: 'dark' });
@ -155,7 +158,7 @@ Alice->Bob: hi`;
%%{init: { 'logLevel': 0, 'theme': 'dark' }}%%
sequenceDiagram
Alice->Bob: hi`;
const type = utils.detectType(str);
const type = detectType(str);
const init = utils.detectInit(str);
expect(type).toBe('sequence');
expect(init).toEqual({ logLevel: 0, theme: 'dark' });
@ -165,7 +168,7 @@ Alice->Bob: hi`;
%%{init: { 'logLevel': 0, 'theme': 'dark', 'config': {'wrap': true} } }%%
sequenceDiagram
Alice->Bob: hi`;
const type = utils.detectType(str);
const type = detectType(str);
const init = utils.detectInit(str);
expect(type).toBe('sequence');
expect(init).toEqual({ logLevel: 0, theme: 'dark', sequence: { wrap: true } });
@ -180,7 +183,7 @@ Alice->Bob: hi`;
}%%
sequenceDiagram
Alice->Bob: hi`;
const type = utils.detectType(str);
const type = detectType(str);
const init = utils.detectInit(str);
expect(type).toBe('sequence');
expect(init).toEqual({ logLevel: 0, theme: 'dark' });
@ -195,25 +198,25 @@ Alice->Bob: hi`;
}%%
sequenceDiagram
Alice->Bob: hi`;
const type = utils.detectType(str);
const type = detectType(str);
const init = utils.detectInit(str);
expect(type).toBe('sequence');
expect(init).toEqual({ logLevel: 0, theme: 'dark' });
});
it('should handle a graph definition with leading spaces', function () {
const str = ' graph TB\nbfs1:queue';
const type = utils.detectType(str);
const type = detectType(str);
expect(type).toBe('flowchart');
});
it('should handle a graph definition with leading spaces and newline', function () {
const str = ' \n graph TB\nbfs1:queue';
const type = utils.detectType(str);
const type = detectType(str);
expect(type).toBe('flowchart');
});
it('should handle a graph definition for gitGraph', function () {
const str = ' \n gitGraph TB:\nbfs1:queue';
const type = utils.detectType(str);
const type = detectType(str);
expect(type).toBe('gitGraph');
});
});

5
test.js Normal file
View File

@ -0,0 +1,5 @@
function apa() {
// comment's
const a = 1;
return 'apa' + a;
}