mirror of
https://github.com/mermaid-js/mermaid.git
synced 2025-10-13 19:19:37 +02:00
Merge pull request #3004 from timmaffett/parse_parseError_fixes
fix mermaidAPI.parse() behavior to match documentation, add tests to ensure behavior matches docs
This commit is contained in:
@@ -29,7 +29,7 @@ statement
|
|||||||
In the extract of the grammar above, it is defined that a call to the setTitle method in the data object will be done when parsing and the title keyword is encountered.
|
In the extract of the grammar above, it is defined that a call to the setTitle method in the data object will be done when parsing and the title keyword is encountered.
|
||||||
|
|
||||||
```tip
|
```tip
|
||||||
Make sure that the `parseError` function for the parser is defined and calling `mermaidPAI.parseError`. This way a common way of detecting parse errors is provided for the end-user.
|
Make sure that the `parseError` function for the parser is defined and calling `mermaid.parseError`. This way a common way of detecting parse errors is provided for the end-user.
|
||||||
```
|
```
|
||||||
|
|
||||||
For more info look in the example diagram type:
|
For more info look in the example diagram type:
|
||||||
@@ -38,7 +38,7 @@ The `yy` object has the following function:
|
|||||||
|
|
||||||
```javascript
|
```javascript
|
||||||
exports.parseError = function(err, hash){
|
exports.parseError = function(err, hash){
|
||||||
mermaidAPI.parseError(err, hash)
|
mermaid.parseError(err, hash)
|
||||||
};
|
};
|
||||||
```
|
```
|
||||||
|
|
||||||
|
@@ -188,18 +188,38 @@ if (typeof document !== 'undefined') {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* ## setParseErrorHandler Alternativet to directly setting parseError using:
|
||||||
|
*
|
||||||
|
* ```js
|
||||||
|
* mermaid.parseError = function(err,hash){=
|
||||||
|
* forExampleDisplayErrorInGui(err); // do something with the error
|
||||||
|
* };
|
||||||
|
* ```
|
||||||
|
*
|
||||||
|
* 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.
|
||||||
|
*/
|
||||||
|
const setParseErrorHandler = function (newParseErrorHandler) {
|
||||||
|
mermaid.parseError = newParseErrorHandler;
|
||||||
|
};
|
||||||
|
|
||||||
const mermaid = {
|
const mermaid = {
|
||||||
startOnLoad: true,
|
startOnLoad: true,
|
||||||
htmlLabels: true,
|
htmlLabels: true,
|
||||||
|
|
||||||
mermaidAPI,
|
mermaidAPI,
|
||||||
parse: mermaidAPI.parse,
|
parse: mermaidAPI != undefined ? mermaidAPI.parse : null,
|
||||||
render: mermaidAPI.render,
|
render: mermaidAPI != undefined ? mermaidAPI.render : null,
|
||||||
|
|
||||||
init,
|
init,
|
||||||
initialize,
|
initialize,
|
||||||
|
|
||||||
contentLoaded,
|
contentLoaded,
|
||||||
|
|
||||||
|
setParseErrorHandler,
|
||||||
};
|
};
|
||||||
|
|
||||||
export default mermaid;
|
export default mermaid;
|
||||||
|
@@ -63,99 +63,120 @@ import getStyles from './styles';
|
|||||||
import theme from './themes';
|
import theme from './themes';
|
||||||
import utils, { directiveSanitizer, assignWithDepth, sanitizeCss } from './utils';
|
import utils, { directiveSanitizer, assignWithDepth, sanitizeCss } from './utils';
|
||||||
import DOMPurify from 'dompurify';
|
import DOMPurify from 'dompurify';
|
||||||
|
import mermaid from './mermaid';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param text
|
* @param text
|
||||||
* @returns {any}
|
* @returns {any}
|
||||||
*/
|
*/
|
||||||
function parse(text) {
|
function parse(text) {
|
||||||
text = text + '\n';
|
var parseEncounteredException = false;
|
||||||
const cnf = configApi.getConfig();
|
try {
|
||||||
const graphInit = utils.detectInit(text, cnf);
|
text = text + '\n';
|
||||||
if (graphInit) {
|
const cnf = configApi.getConfig();
|
||||||
reinitialize(graphInit);
|
const graphInit = utils.detectInit(text, cnf);
|
||||||
log.info('reinit ', graphInit);
|
if (graphInit) {
|
||||||
}
|
reinitialize(graphInit);
|
||||||
const graphType = utils.detectType(text, cnf);
|
log.info('reinit ', graphInit);
|
||||||
let parser;
|
}
|
||||||
|
const graphType = utils.detectType(text, cnf);
|
||||||
|
let parser;
|
||||||
|
|
||||||
log.debug('Type ' + graphType);
|
log.debug('Type ' + graphType);
|
||||||
switch (graphType) {
|
switch (graphType) {
|
||||||
case 'gitGraph':
|
case 'gitGraph':
|
||||||
gitGraphAst.clear();
|
gitGraphAst.clear();
|
||||||
parser = gitGraphParser;
|
parser = gitGraphParser;
|
||||||
parser.parser.yy = gitGraphAst;
|
parser.parser.yy = gitGraphAst;
|
||||||
break;
|
break;
|
||||||
case 'flowchart':
|
case 'flowchart':
|
||||||
flowDb.clear();
|
flowDb.clear();
|
||||||
parser = flowParser;
|
parser = flowParser;
|
||||||
parser.parser.yy = flowDb;
|
parser.parser.yy = flowDb;
|
||||||
break;
|
break;
|
||||||
case 'flowchart-v2':
|
case 'flowchart-v2':
|
||||||
flowDb.clear();
|
flowDb.clear();
|
||||||
parser = flowParser;
|
parser = flowParser;
|
||||||
parser.parser.yy = flowDb;
|
parser.parser.yy = flowDb;
|
||||||
break;
|
break;
|
||||||
case 'sequence':
|
case 'sequence':
|
||||||
sequenceDb.clear();
|
sequenceDb.clear();
|
||||||
parser = sequenceParser;
|
parser = sequenceParser;
|
||||||
parser.parser.yy = sequenceDb;
|
parser.parser.yy = sequenceDb;
|
||||||
break;
|
break;
|
||||||
case 'gantt':
|
case 'gantt':
|
||||||
parser = ganttParser;
|
parser = ganttParser;
|
||||||
parser.parser.yy = ganttDb;
|
parser.parser.yy = ganttDb;
|
||||||
break;
|
break;
|
||||||
case 'class':
|
case 'class':
|
||||||
parser = classParser;
|
parser = classParser;
|
||||||
parser.parser.yy = classDb;
|
parser.parser.yy = classDb;
|
||||||
break;
|
break;
|
||||||
case 'classDiagram':
|
case 'classDiagram':
|
||||||
parser = classParser;
|
parser = classParser;
|
||||||
parser.parser.yy = classDb;
|
parser.parser.yy = classDb;
|
||||||
break;
|
break;
|
||||||
case 'state':
|
case 'state':
|
||||||
parser = stateParser;
|
parser = stateParser;
|
||||||
parser.parser.yy = stateDb;
|
parser.parser.yy = stateDb;
|
||||||
break;
|
break;
|
||||||
case 'stateDiagram':
|
case 'stateDiagram':
|
||||||
parser = stateParser;
|
parser = stateParser;
|
||||||
parser.parser.yy = stateDb;
|
parser.parser.yy = stateDb;
|
||||||
break;
|
break;
|
||||||
case 'info':
|
case 'info':
|
||||||
log.debug('info info info');
|
log.debug('info info info');
|
||||||
parser = infoParser;
|
parser = infoParser;
|
||||||
parser.parser.yy = infoDb;
|
parser.parser.yy = infoDb;
|
||||||
break;
|
break;
|
||||||
case 'pie':
|
case 'pie':
|
||||||
log.debug('pie');
|
log.debug('pie');
|
||||||
parser = pieParser;
|
parser = pieParser;
|
||||||
parser.parser.yy = pieDb;
|
parser.parser.yy = pieDb;
|
||||||
break;
|
break;
|
||||||
case 'er':
|
case 'er':
|
||||||
log.debug('er');
|
log.debug('er');
|
||||||
parser = erParser;
|
parser = erParser;
|
||||||
parser.parser.yy = erDb;
|
parser.parser.yy = erDb;
|
||||||
break;
|
break;
|
||||||
case 'journey':
|
case 'journey':
|
||||||
log.debug('Journey');
|
log.debug('Journey');
|
||||||
parser = journeyParser;
|
parser = journeyParser;
|
||||||
parser.parser.yy = journeyDb;
|
parser.parser.yy = journeyDb;
|
||||||
break;
|
break;
|
||||||
case 'requirement':
|
case 'requirement':
|
||||||
case 'requirementDiagram':
|
case 'requirementDiagram':
|
||||||
log.debug('RequirementDiagram');
|
log.debug('RequirementDiagram');
|
||||||
parser = requirementParser;
|
parser = requirementParser;
|
||||||
parser.parser.yy = requirementDb;
|
parser.parser.yy = requirementDb;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
parser.parser.yy.graphType = graphType;
|
parser.parser.yy.graphType = graphType;
|
||||||
parser.parser.yy.parseError = (str, hash) => {
|
parser.parser.yy.parseError = (str, hash) => {
|
||||||
const error = { str, hash };
|
const error = { str, hash };
|
||||||
throw error;
|
throw error;
|
||||||
};
|
};
|
||||||
|
|
||||||
parser.parse(text);
|
parser.parse(text);
|
||||||
return parser;
|
} catch (error) {
|
||||||
|
parseEncounteredException = true;
|
||||||
|
// Is this the correct way to access mermiad's parseError()
|
||||||
|
// method ? (or global.mermaid.parseError()) ?
|
||||||
|
if (mermaid.parseError) {
|
||||||
|
if (error.str != undefined) {
|
||||||
|
// handle case where error string and hash were
|
||||||
|
// wrapped in object like`const error = { str, hash };`
|
||||||
|
mermaid.parseError(error.str, error.hash);
|
||||||
|
} else {
|
||||||
|
// assume it is just error string and pass it on
|
||||||
|
mermaid.parseError(error);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// No mermaid.parseError() handler defined, so re-throw it
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return !parseEncounteredException;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const encodeEntities = function (text) {
|
export const encodeEntities = function (text) {
|
||||||
|
@@ -1,3 +1,4 @@
|
|||||||
|
import mermaid from './mermaid';
|
||||||
import mermaidAPI from './mermaidAPI';
|
import mermaidAPI from './mermaidAPI';
|
||||||
import { assignWithDepth } from './utils';
|
import { assignWithDepth } from './utils';
|
||||||
|
|
||||||
@@ -119,12 +120,28 @@ describe('when using mermaidAPI and ', function () {
|
|||||||
expect(mermaidAPI.getConfig().dompurifyConfig.ADD_ATTR).toEqual(['onclick']);
|
expect(mermaidAPI.getConfig().dompurifyConfig.ADD_ATTR).toEqual(['onclick']);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
describe('checking validity of input ', function () {
|
describe('test mermaidApi.parse() for checking validity of input ', function () {
|
||||||
it('it should throw for an invalid definiton', function () {
|
mermaid.parseError = undefined; // ensure it parseError undefined
|
||||||
|
it('it should throw for an invalid definiton (with no mermaid.parseError() defined)', function () {
|
||||||
|
expect(mermaid.parseError).toEqual(undefined);
|
||||||
expect(() => mermaidAPI.parse('this is not a mermaid diagram definition')).toThrow();
|
expect(() => mermaidAPI.parse('this is not a mermaid diagram definition')).toThrow();
|
||||||
});
|
});
|
||||||
it('it should not throw for a valid definiton', function () {
|
it('it should not throw for a valid definiton', function () {
|
||||||
expect(() => mermaidAPI.parse('graph TD;A--x|text including URL space|B;')).not.toThrow();
|
expect(() => mermaidAPI.parse('graph TD;A--x|text including URL space|B;')).not.toThrow();
|
||||||
});
|
});
|
||||||
|
it('it should return false for invalid definiton WITH a parseError() callback defined', function () {
|
||||||
|
var parseErrorWasCalled = false;
|
||||||
|
// also test setParseErrorHandler() call working to set mermaid.parseError
|
||||||
|
mermaid.setParseErrorHandler(function (error, hash) {
|
||||||
|
// got here.
|
||||||
|
parseErrorWasCalled = true;
|
||||||
|
});
|
||||||
|
expect(mermaid.parseError).not.toEqual(undefined);
|
||||||
|
expect(mermaidAPI.parse('this is not a mermaid diagram definition')).toEqual(false);
|
||||||
|
expect(parseErrorWasCalled).toEqual(true);
|
||||||
|
});
|
||||||
|
it('it should return true for valid definiton', function () {
|
||||||
|
expect(mermaidAPI.parse('graph TD;A--x|text including URL space|B;')).toEqual(true);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
Reference in New Issue
Block a user