mirror of
https://github.com/mermaid-js/mermaid.git
synced 2025-08-16 23:09:28 +02:00
Compare commits
9 Commits
mermaid@11
...
sidv/chevr
Author | SHA1 | Date | |
---|---|---|---|
![]() |
68626df061 | ||
![]() |
64e72f67c9 | ||
![]() |
23f27af610 | ||
![]() |
a2219f7e4b | ||
![]() |
00dc2d73de | ||
![]() |
6138dd8714 | ||
![]() |
f60761609c | ||
![]() |
9164d8b57f | ||
![]() |
16b384f588 |
@@ -91,6 +91,7 @@
|
|||||||
"@typescript-eslint/parser": "^5.36.1",
|
"@typescript-eslint/parser": "^5.36.1",
|
||||||
"babel-jest": "^29.0.2",
|
"babel-jest": "^29.0.2",
|
||||||
"babel-loader": "^8.2.2",
|
"babel-loader": "^8.2.2",
|
||||||
|
"chevrotain": "^10.3.0",
|
||||||
"concurrently": "^7.0.0",
|
"concurrently": "^7.0.0",
|
||||||
"coveralls": "^3.0.2",
|
"coveralls": "^3.0.2",
|
||||||
"css-to-string-loader": "^0.1.3",
|
"css-to-string-loader": "^0.1.3",
|
||||||
|
@@ -28,11 +28,9 @@ import ganttStyles from '../diagrams/gantt/styles';
|
|||||||
|
|
||||||
import infoDb from '../diagrams/info/infoDb';
|
import infoDb from '../diagrams/info/infoDb';
|
||||||
import infoRenderer from '../diagrams/info/infoRenderer';
|
import infoRenderer from '../diagrams/info/infoRenderer';
|
||||||
// @ts-ignore
|
import infoParser from '../diagrams/info/infoParser';
|
||||||
import infoParser from '../diagrams/info/parser/info';
|
|
||||||
import infoStyles from '../diagrams/info/styles';
|
import infoStyles from '../diagrams/info/styles';
|
||||||
// @ts-ignore
|
import pieParser from '../diagrams/pie/pieParser';
|
||||||
import pieParser from '../diagrams/pie/parser/pie';
|
|
||||||
import pieDb from '../diagrams/pie/pieDb';
|
import pieDb from '../diagrams/pie/pieDb';
|
||||||
import pieRenderer from '../diagrams/pie/pieRenderer';
|
import pieRenderer from '../diagrams/pie/pieRenderer';
|
||||||
import pieStyles from '../diagrams/pie/styles';
|
import pieStyles from '../diagrams/pie/styles';
|
||||||
|
@@ -1,14 +0,0 @@
|
|||||||
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);
|
|
||||||
});
|
|
||||||
});
|
|
53
src/diagrams/info/info.spec.ts
Normal file
53
src/diagrams/info/info.spec.ts
Normal file
@@ -0,0 +1,53 @@
|
|||||||
|
import infoDb from './infoDb';
|
||||||
|
import infoParser from './infoParser';
|
||||||
|
|
||||||
|
describe('when parsing an info graph it', function () {
|
||||||
|
beforeEach(() => {
|
||||||
|
infoDb.clear();
|
||||||
|
});
|
||||||
|
|
||||||
|
it.each([
|
||||||
|
// Without newlines
|
||||||
|
`info
|
||||||
|
showInfo`,
|
||||||
|
|
||||||
|
// With newlines at beginning
|
||||||
|
`
|
||||||
|
info
|
||||||
|
showInfo`,
|
||||||
|
// Extra newlines
|
||||||
|
`
|
||||||
|
|
||||||
|
info
|
||||||
|
|
||||||
|
showInfo
|
||||||
|
|
||||||
|
`,
|
||||||
|
])('should handle valid info definitions', function (str: string = '') {
|
||||||
|
expect(infoDb.getInfo()).toEqual(false);
|
||||||
|
infoParser.parse(str);
|
||||||
|
expect(infoDb.getInfo()).toEqual(true);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should throw an error when the info is not defined', function () {
|
||||||
|
expect(() => {
|
||||||
|
infoParser.parse(``);
|
||||||
|
}).toThrow();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should not throw an error when showInfo is not defined', function () {
|
||||||
|
infoParser.parse('info');
|
||||||
|
expect(infoDb.getInfo()).toEqual(true);
|
||||||
|
});
|
||||||
|
|
||||||
|
it.each([
|
||||||
|
`InFo`,
|
||||||
|
`Info
|
||||||
|
showinfo`,
|
||||||
|
`info
|
||||||
|
shOWINFO`,
|
||||||
|
])('should be case insensitive', function (str) {
|
||||||
|
infoParser.parse(str);
|
||||||
|
expect(infoDb.getInfo()).toEqual(true);
|
||||||
|
});
|
||||||
|
});
|
@@ -1,11 +1,11 @@
|
|||||||
/** Created by knut on 15-01-14. */
|
/** Created by knut on 15-01-14. */
|
||||||
import { log } from '../../logger';
|
import { log } from '../../logger';
|
||||||
import { clear } from '../../commonDb';
|
import { clear as commonClear } from '../../commonDb';
|
||||||
|
|
||||||
var message = '';
|
var message = '';
|
||||||
var info = false;
|
var info = false;
|
||||||
|
|
||||||
export const setMessage = (txt) => {
|
export const setMessage = (txt: string) => {
|
||||||
log.debug('Setting message to: ' + txt);
|
log.debug('Setting message to: ' + txt);
|
||||||
message = txt;
|
message = txt;
|
||||||
};
|
};
|
||||||
@@ -14,7 +14,7 @@ export const getMessage = () => {
|
|||||||
return message;
|
return message;
|
||||||
};
|
};
|
||||||
|
|
||||||
export const setInfo = (inf) => {
|
export const setInfo = (inf: boolean) => {
|
||||||
info = inf;
|
info = inf;
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -22,9 +22,11 @@ export const getInfo = () => {
|
|||||||
return info;
|
return info;
|
||||||
};
|
};
|
||||||
|
|
||||||
// export const parseError = (err, hash) => {
|
export const clear = () => {
|
||||||
// global.mermaidAPI.parseError(err, hash)
|
commonClear();
|
||||||
// }
|
message = '';
|
||||||
|
info = false;
|
||||||
|
};
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
setMessage,
|
setMessage,
|
||||||
@@ -32,5 +34,4 @@ export default {
|
|||||||
setInfo,
|
setInfo,
|
||||||
getInfo,
|
getInfo,
|
||||||
clear,
|
clear,
|
||||||
// parseError
|
|
||||||
};
|
};
|
79
src/diagrams/info/infoParser.ts
Normal file
79
src/diagrams/info/infoParser.ts
Normal file
@@ -0,0 +1,79 @@
|
|||||||
|
import { createToken, EmbeddedActionsParser, Lexer } from 'chevrotain';
|
||||||
|
import { log } from '../../logger';
|
||||||
|
import infoDb from './infoDb';
|
||||||
|
|
||||||
|
const Info = createToken({ name: 'Info', pattern: /info/i });
|
||||||
|
const ShowInfo = createToken({ name: 'ShowInfo', pattern: /showInfo/i });
|
||||||
|
const NewLine = createToken({
|
||||||
|
name: 'NewLine',
|
||||||
|
pattern: /\r?\n/,
|
||||||
|
});
|
||||||
|
const WhiteSpace = createToken({
|
||||||
|
name: 'WhiteSpace',
|
||||||
|
pattern: /\s+/,
|
||||||
|
group: Lexer.SKIPPED,
|
||||||
|
});
|
||||||
|
|
||||||
|
const allTokens = [NewLine, WhiteSpace, ShowInfo, Info];
|
||||||
|
const InfoLexer = new Lexer(allTokens);
|
||||||
|
|
||||||
|
class InfoParser extends EmbeddedActionsParser {
|
||||||
|
constructor() {
|
||||||
|
super(allTokens);
|
||||||
|
this.performSelfAnalysis();
|
||||||
|
}
|
||||||
|
|
||||||
|
public reset(): void {
|
||||||
|
super.reset();
|
||||||
|
infoDb.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
public diagram = this.RULE('diagram', () => {
|
||||||
|
this.MANY(() => {
|
||||||
|
this.CONSUME(NewLine);
|
||||||
|
});
|
||||||
|
this.SUBRULE(this.hdr);
|
||||||
|
this.MANY2(() => {
|
||||||
|
this.SUBRULE2(this.row);
|
||||||
|
});
|
||||||
|
this.ACTION(() => infoDb.setInfo(true));
|
||||||
|
this.MANY3(() => {
|
||||||
|
this.CONSUME2(NewLine);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
public hdr = this.RULE('hdr', () => {
|
||||||
|
this.CONSUME(Info);
|
||||||
|
this.OPTION(() => this.CONSUME(NewLine));
|
||||||
|
});
|
||||||
|
|
||||||
|
public row = this.RULE('row', () => {
|
||||||
|
this.SUBRULE(this.field);
|
||||||
|
this.MANY(() => {
|
||||||
|
this.CONSUME(NewLine);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
public field = this.RULE('field', () => {
|
||||||
|
this.CONSUME(ShowInfo);
|
||||||
|
this.ACTION(() => infoDb.setInfo(true));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
const parser = new InfoParser();
|
||||||
|
|
||||||
|
const parse = (text: string): void => {
|
||||||
|
const lexResult = InfoLexer.tokenize(text);
|
||||||
|
parser.input = lexResult.tokens;
|
||||||
|
parser.diagram();
|
||||||
|
|
||||||
|
if (parser.errors.length > 0 || lexResult.errors.length > 0) {
|
||||||
|
log.error(
|
||||||
|
{ parserErrors: parser.errors, lexerErrors: lexResult.errors },
|
||||||
|
'Error parsing info diagram'
|
||||||
|
);
|
||||||
|
throw new Error(`Parser errors: ${parser.errors} Lex errors: ${lexResult.errors}`);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
export default { parser: {}, parse };
|
@@ -1,48 +0,0 @@
|
|||||||
/** 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); }
|
|
||||||
;
|
|
||||||
|
|
||||||
%%
|
|
@@ -1,106 +0,0 @@
|
|||||||
/** mermaid
|
|
||||||
* https://knsv.github.io/mermaid
|
|
||||||
* (c) 2015 Knut Sveidqvist
|
|
||||||
* MIT license.
|
|
||||||
*/
|
|
||||||
%lex
|
|
||||||
%options case-insensitive
|
|
||||||
|
|
||||||
%x string
|
|
||||||
%x title
|
|
||||||
%x open_directive
|
|
||||||
%x type_directive
|
|
||||||
%x arg_directive
|
|
||||||
%x close_directive
|
|
||||||
%x acc_title
|
|
||||||
%x acc_descr
|
|
||||||
%x acc_descr_multiline
|
|
||||||
%%
|
|
||||||
\%\%\{ { this.begin('open_directive'); return 'open_directive'; }
|
|
||||||
<open_directive>((?:(?!\}\%\%)[^:.])*) { this.begin('type_directive'); return 'type_directive'; }
|
|
||||||
<type_directive>":" { this.popState(); this.begin('arg_directive'); return ':'; }
|
|
||||||
<type_directive,arg_directive>\}\%\% { this.popState(); this.popState(); return 'close_directive'; }
|
|
||||||
<arg_directive>((?:(?!\}\%\%).|\n)*) return 'arg_directive';
|
|
||||||
\%\%(?!\{)[^\n]* /* skip comments */
|
|
||||||
[^\}]\%\%[^\n]* /* skip comments */{ /*console.log('');*/ }
|
|
||||||
[\n\r]+ return 'NEWLINE';
|
|
||||||
\%\%[^\n]* /* do nothing */
|
|
||||||
[\s]+ /* ignore */
|
|
||||||
title { this.begin("title");return 'title'; }
|
|
||||||
<title>(?!\n|;|#)*[^\n]* { this.popState(); return "title_value"; }
|
|
||||||
|
|
||||||
accTitle\s*":"\s* { this.begin("acc_title");return 'acc_title'; }
|
|
||||||
<acc_title>(?!\n|;|#)*[^\n]* { this.popState(); return "acc_title_value"; }
|
|
||||||
accDescr\s*":"\s* { this.begin("acc_descr");return 'acc_descr'; }
|
|
||||||
<acc_descr>(?!\n|;|#)*[^\n]* { this.popState(); return "acc_descr_value"; }
|
|
||||||
accDescr\s*"{"\s* { this.begin("acc_descr_multiline");}
|
|
||||||
<acc_descr_multiline>[\}] { this.popState(); }
|
|
||||||
<acc_descr_multiline>[^\}]* return "acc_descr_multiline_value";
|
|
||||||
["] { this.begin("string"); }
|
|
||||||
<string>["] { this.popState(); }
|
|
||||||
<string>[^"]* { return "txt"; }
|
|
||||||
"pie" return 'PIE';
|
|
||||||
"showData" return 'showData';
|
|
||||||
":"[\s]*[\d]+(?:\.[\d]+)? return "value";
|
|
||||||
<<EOF>> return 'EOF';
|
|
||||||
|
|
||||||
/lex
|
|
||||||
|
|
||||||
%start start
|
|
||||||
|
|
||||||
%% /* language grammar */
|
|
||||||
|
|
||||||
start
|
|
||||||
: eol start
|
|
||||||
| directive start
|
|
||||||
| PIE document
|
|
||||||
| PIE showData document {yy.setShowData(true);}
|
|
||||||
;
|
|
||||||
|
|
||||||
document
|
|
||||||
: /* empty */
|
|
||||||
| document line
|
|
||||||
;
|
|
||||||
|
|
||||||
line
|
|
||||||
: statement eol { $$ = $1 }
|
|
||||||
;
|
|
||||||
|
|
||||||
statement
|
|
||||||
:
|
|
||||||
| txt value { yy.addSection($1,yy.cleanupValue($2)); }
|
|
||||||
| title title_value { $$=$2.trim();yy.setDiagramTitle($$); }
|
|
||||||
| acc_title acc_title_value { $$=$2.trim();yy.setAccTitle($$); }
|
|
||||||
| acc_descr acc_descr_value { $$=$2.trim();yy.setAccDescription($$); }
|
|
||||||
| acc_descr_multiline_value { $$=$1.trim();yy.setAccDescription($$); } | section {yy.addSection($1.substr(8));$$=$1.substr(8);}
|
|
||||||
| directive
|
|
||||||
;
|
|
||||||
|
|
||||||
directive
|
|
||||||
: openDirective typeDirective closeDirective
|
|
||||||
| openDirective typeDirective ':' argDirective closeDirective
|
|
||||||
;
|
|
||||||
|
|
||||||
eol
|
|
||||||
: NEWLINE
|
|
||||||
| ';'
|
|
||||||
| EOF
|
|
||||||
;
|
|
||||||
|
|
||||||
openDirective
|
|
||||||
: open_directive { yy.parseDirective('%%{', 'open_directive'); }
|
|
||||||
;
|
|
||||||
|
|
||||||
typeDirective
|
|
||||||
: type_directive { yy.parseDirective($1, 'type_directive'); }
|
|
||||||
;
|
|
||||||
|
|
||||||
argDirective
|
|
||||||
: arg_directive { $1 = $1.trim().replace(/'/g, '"'); yy.parseDirective($1, 'arg_directive'); }
|
|
||||||
;
|
|
||||||
|
|
||||||
closeDirective
|
|
||||||
: close_directive { yy.parseDirective('}%%', 'close_directive', 'pie'); }
|
|
||||||
;
|
|
||||||
|
|
||||||
%%
|
|
@@ -1,132 +0,0 @@
|
|||||||
import pieDb from '../pieDb';
|
|
||||||
import pie from './pie';
|
|
||||||
import { setConfig } from '../../../config';
|
|
||||||
|
|
||||||
setConfig({
|
|
||||||
securityLevel: 'strict',
|
|
||||||
});
|
|
||||||
|
|
||||||
describe('when parsing pie', function () {
|
|
||||||
beforeEach(function () {
|
|
||||||
pie.parser.yy = pieDb;
|
|
||||||
pie.parser.yy.clear();
|
|
||||||
});
|
|
||||||
it('should handle very simple pie', function () {
|
|
||||||
const res = pie.parser.parse(`pie
|
|
||||||
"ash" : 100
|
|
||||||
`);
|
|
||||||
const sections = pieDb.getSections();
|
|
||||||
const section1 = sections['ash'];
|
|
||||||
expect(section1).toBe(100);
|
|
||||||
});
|
|
||||||
it('should handle simple pie', function () {
|
|
||||||
const res = pie.parser.parse(`pie
|
|
||||||
"ash" : 60
|
|
||||||
"bat" : 40
|
|
||||||
`);
|
|
||||||
const sections = pieDb.getSections();
|
|
||||||
const section1 = sections['ash'];
|
|
||||||
expect(section1).toBe(60);
|
|
||||||
});
|
|
||||||
it('should handle simple pie with comments', function () {
|
|
||||||
const res = pie.parser.parse(`pie
|
|
||||||
%% comments
|
|
||||||
"ash" : 60
|
|
||||||
"bat" : 40
|
|
||||||
`);
|
|
||||||
const sections = pieDb.getSections();
|
|
||||||
const section1 = sections['ash'];
|
|
||||||
expect(section1).toBe(60);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should handle simple pie with a directive', function () {
|
|
||||||
const res = pie.parser.parse(`%%{init: {'logLevel':0}}%%
|
|
||||||
pie
|
|
||||||
"ash" : 60
|
|
||||||
"bat" : 40
|
|
||||||
`);
|
|
||||||
const sections = pieDb.getSections();
|
|
||||||
const section1 = sections['ash'];
|
|
||||||
expect(section1).toBe(60);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should handle simple pie with a title', function () {
|
|
||||||
const res = pie.parser.parse(`pie title a 60/40 pie
|
|
||||||
"ash" : 60
|
|
||||||
"bat" : 40
|
|
||||||
`);
|
|
||||||
const sections = pieDb.getSections();
|
|
||||||
const title = pieDb.getDiagramTitle();
|
|
||||||
const section1 = sections['ash'];
|
|
||||||
expect(section1).toBe(60);
|
|
||||||
expect(title).toBe('a 60/40 pie');
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should handle simple pie without an acc description (accDescr)', function () {
|
|
||||||
const res = pie.parser.parse(`pie title a neat chart
|
|
||||||
"ash" : 60
|
|
||||||
"bat" : 40
|
|
||||||
`);
|
|
||||||
|
|
||||||
const sections = pieDb.getSections();
|
|
||||||
const title = pieDb.getDiagramTitle();
|
|
||||||
const description = pieDb.getAccDescription();
|
|
||||||
const section1 = sections['ash'];
|
|
||||||
expect(section1).toBe(60);
|
|
||||||
expect(title).toBe('a neat chart');
|
|
||||||
expect(description).toBe('');
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should handle simple pie with an acc description (accDescr)', function () {
|
|
||||||
const res = pie.parser.parse(`pie title a neat chart
|
|
||||||
accDescr: a neat description
|
|
||||||
"ash" : 60
|
|
||||||
"bat" : 40
|
|
||||||
`);
|
|
||||||
|
|
||||||
const sections = pieDb.getSections();
|
|
||||||
const title = pieDb.getDiagramTitle();
|
|
||||||
const description = pieDb.getAccDescription();
|
|
||||||
const section1 = sections['ash'];
|
|
||||||
expect(section1).toBe(60);
|
|
||||||
expect(title).toBe('a neat chart');
|
|
||||||
expect(description).toBe('a neat description');
|
|
||||||
});
|
|
||||||
it('should handle simple pie with a multiline acc description (accDescr)', function () {
|
|
||||||
const res = pie.parser.parse(`pie title a neat chart
|
|
||||||
accDescr {
|
|
||||||
a neat description
|
|
||||||
on multiple lines
|
|
||||||
}
|
|
||||||
"ash" : 60
|
|
||||||
"bat" : 40
|
|
||||||
`);
|
|
||||||
|
|
||||||
const sections = pieDb.getSections();
|
|
||||||
const title = pieDb.getDiagramTitle();
|
|
||||||
const description = pieDb.getAccDescription();
|
|
||||||
const section1 = sections['ash'];
|
|
||||||
expect(section1).toBe(60);
|
|
||||||
expect(title).toBe('a neat chart');
|
|
||||||
expect(description).toBe('a neat description\non multiple lines');
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should handle simple pie with positive decimal', function () {
|
|
||||||
const res = pie.parser.parse(`pie
|
|
||||||
"ash" : 60.67
|
|
||||||
"bat" : 40
|
|
||||||
`);
|
|
||||||
const sections = pieDb.getSections();
|
|
||||||
const section1 = sections['ash'];
|
|
||||||
expect(section1).toBe(60.67);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should handle simple pie with negative decimal', function () {
|
|
||||||
expect(() => {
|
|
||||||
pie.parser.parse(`pie
|
|
||||||
"ash" : 60.67
|
|
||||||
"bat" : 40..12
|
|
||||||
`);
|
|
||||||
}).toThrowError();
|
|
||||||
});
|
|
||||||
});
|
|
141
src/diagrams/pie/pie.spec.js
Normal file
141
src/diagrams/pie/pie.spec.js
Normal file
@@ -0,0 +1,141 @@
|
|||||||
|
import pieDb from './pieDb';
|
||||||
|
import pie from './pieParser';
|
||||||
|
import { setConfig } from '../../config';
|
||||||
|
|
||||||
|
setConfig({
|
||||||
|
securityLevel: 'strict',
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('when parsing pie', function () {
|
||||||
|
beforeEach(function () {
|
||||||
|
pieDb.clear();
|
||||||
|
});
|
||||||
|
it('should handle simple pie', function () {
|
||||||
|
pie.parser.parse(`pie
|
||||||
|
"ash" : 100
|
||||||
|
`);
|
||||||
|
const sections = pieDb.getSections();
|
||||||
|
const section1 = sections['ash'];
|
||||||
|
expect(section1).toBe(100);
|
||||||
|
});
|
||||||
|
it('should handle pie', function () {
|
||||||
|
pie.parser.parse(`pie
|
||||||
|
"ash" : 60
|
||||||
|
"bat" : 40
|
||||||
|
`);
|
||||||
|
const sections = pieDb.getSections();
|
||||||
|
const section1 = sections['ash'];
|
||||||
|
expect(section1).toBe(60);
|
||||||
|
});
|
||||||
|
it('should handle pie with comments', function () {
|
||||||
|
pie.parser.parse(`pie
|
||||||
|
%% comments
|
||||||
|
"ash" : 60
|
||||||
|
"bat" : 40
|
||||||
|
`);
|
||||||
|
const sections = pieDb.getSections();
|
||||||
|
const section1 = sections['ash'];
|
||||||
|
expect(section1).toBe(60);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should handle pie with a directive', function () {
|
||||||
|
pie.parser.parse(`%%{init: {'logLevel':0}}%%
|
||||||
|
pie
|
||||||
|
"ash" : 60
|
||||||
|
"bat" : 40
|
||||||
|
`);
|
||||||
|
const sections = pieDb.getSections();
|
||||||
|
const section1 = sections['ash'];
|
||||||
|
expect(section1).toBe(60);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should handle pie with a title and emoji', function () {
|
||||||
|
pie.parser.parse(`pie title a 60/40 pie ❤️
|
||||||
|
"ash" : 60
|
||||||
|
"bat" : 40
|
||||||
|
`);
|
||||||
|
const sections = pieDb.getSections();
|
||||||
|
const title = pieDb.getDiagramTitle();
|
||||||
|
const section1 = sections['ash'];
|
||||||
|
expect(section1).toBe(60);
|
||||||
|
expect(title).toBe('a 60/40 pie ❤️');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should handle pie with an acc title (accTitle)', function () {
|
||||||
|
pie.parser.parse(`pie title a neat chart
|
||||||
|
accTitle: Hello World
|
||||||
|
"ash" : 60
|
||||||
|
"bat" : 40
|
||||||
|
`);
|
||||||
|
|
||||||
|
const description = pieDb.getAccDescription();
|
||||||
|
const title = pieDb.getAccTitle();
|
||||||
|
expect(title).toBe('Hello World');
|
||||||
|
expect(description).toBe('');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should handle pie with an acc description (accDescr)', function () {
|
||||||
|
pie.parser.parse(`pie title a neat chart
|
||||||
|
accDescr: a neat description
|
||||||
|
"ash" : 60
|
||||||
|
"bat" : 40
|
||||||
|
`);
|
||||||
|
|
||||||
|
const sections = pieDb.getSections();
|
||||||
|
const title = pieDb.getDiagramTitle();
|
||||||
|
const description = pieDb.getAccDescription();
|
||||||
|
const section1 = sections['ash'];
|
||||||
|
expect(section1).toBe(60);
|
||||||
|
expect(title).toBe('a neat chart');
|
||||||
|
expect(description).toBe('a neat description');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should handle pie with a multiline acc description (accDescr)', function () {
|
||||||
|
pie.parser.parse(`pie title a neat chart
|
||||||
|
accDescr {
|
||||||
|
a neat description
|
||||||
|
on multiple lines
|
||||||
|
}
|
||||||
|
"ash" : 60
|
||||||
|
|
||||||
|
"bat" : 40
|
||||||
|
|
||||||
|
`);
|
||||||
|
|
||||||
|
const sections = pieDb.getSections();
|
||||||
|
const title = pieDb.getDiagramTitle();
|
||||||
|
const description = pieDb.getAccDescription();
|
||||||
|
const section1 = sections['ash'];
|
||||||
|
expect(section1).toBe(60);
|
||||||
|
expect(title).toBe('a neat chart');
|
||||||
|
expect(description).toBe('a neat description\non multiple lines');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should handle pie with positive decimal', function () {
|
||||||
|
pie.parser.parse(`pie
|
||||||
|
"ash" : 60.67
|
||||||
|
"bat" : 40
|
||||||
|
`);
|
||||||
|
const sections = pieDb.getSections();
|
||||||
|
const section1 = sections['ash'];
|
||||||
|
expect(section1).toBe(60.67);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should handle pie with invalid decimal', function () {
|
||||||
|
expect(() => {
|
||||||
|
pie.parser.parse(`pie
|
||||||
|
"ash" : 60.67
|
||||||
|
"bat" : 40..12
|
||||||
|
`);
|
||||||
|
}).toThrowError();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should handle pie with negative decimal', function () {
|
||||||
|
expect(() => {
|
||||||
|
pie.parser.parse(`pie
|
||||||
|
"ash" : 60.67
|
||||||
|
"bat" : -40.12
|
||||||
|
`);
|
||||||
|
}).toThrowError();
|
||||||
|
});
|
||||||
|
});
|
@@ -1,3 +1,4 @@
|
|||||||
|
// @ts-nocheck
|
||||||
import { log } from '../../logger';
|
import { log } from '../../logger';
|
||||||
import mermaidAPI from '../../mermaidAPI';
|
import mermaidAPI from '../../mermaidAPI';
|
||||||
import * as configApi from '../../config';
|
import * as configApi from '../../config';
|
||||||
@@ -13,8 +14,6 @@ import {
|
|||||||
} from '../../commonDb';
|
} from '../../commonDb';
|
||||||
|
|
||||||
let sections = {};
|
let sections = {};
|
||||||
let title = '';
|
|
||||||
let description = '';
|
|
||||||
let showData = false;
|
let showData = false;
|
||||||
|
|
||||||
export const parseDirective = function (statement, context, type) {
|
export const parseDirective = function (statement, context, type) {
|
||||||
@@ -25,7 +24,7 @@ const addSection = function (id, value) {
|
|||||||
id = common.sanitizeText(id, configApi.getConfig());
|
id = common.sanitizeText(id, configApi.getConfig());
|
||||||
if (typeof sections[id] === 'undefined') {
|
if (typeof sections[id] === 'undefined') {
|
||||||
sections[id] = value;
|
sections[id] = value;
|
||||||
log.debug('Added new section :', id);
|
log.debug('Added new section : ', id, ' with value:', value);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
const getSections = () => sections;
|
const getSections = () => sections;
|
||||||
@@ -49,7 +48,6 @@ const cleanupValue = function (value) {
|
|||||||
|
|
||||||
const clear = function () {
|
const clear = function () {
|
||||||
sections = {};
|
sections = {};
|
||||||
title = '';
|
|
||||||
showData = false;
|
showData = false;
|
||||||
commonClear();
|
commonClear();
|
||||||
};
|
};
|
167
src/diagrams/pie/pieParser.ts
Normal file
167
src/diagrams/pie/pieParser.ts
Normal file
@@ -0,0 +1,167 @@
|
|||||||
|
import { createToken, EmbeddedActionsParser, Lexer } from 'chevrotain';
|
||||||
|
import { log } from '../../logger';
|
||||||
|
import pieDb from './pieDb';
|
||||||
|
|
||||||
|
const NewLine = createToken({
|
||||||
|
name: 'NewLine',
|
||||||
|
pattern: /\r?\n/,
|
||||||
|
});
|
||||||
|
const WhiteSpace = createToken({
|
||||||
|
name: 'WhiteSpace',
|
||||||
|
pattern: /\s+/,
|
||||||
|
group: Lexer.SKIPPED,
|
||||||
|
});
|
||||||
|
|
||||||
|
const Colon = createToken({ name: 'Colon', pattern: /:/ });
|
||||||
|
const Text = createToken({ name: 'Text', pattern: /[^\n\r"]+/ });
|
||||||
|
const StringLiteral = createToken({
|
||||||
|
name: 'StringLiteral',
|
||||||
|
pattern: /"(:?[^\\"]|\\(:?[bfnrtv"\\/]|u[0-9a-fA-F]{4}))*"/,
|
||||||
|
});
|
||||||
|
const NumberLiteral = createToken({
|
||||||
|
name: 'NumberLiteral',
|
||||||
|
pattern: /(0|[1-9]\d*)(\.\d+)?([eE][+-]?\d+)?/,
|
||||||
|
});
|
||||||
|
// TODO: Fix
|
||||||
|
const Comment = createToken({
|
||||||
|
name: 'Comment',
|
||||||
|
pattern: /%%.*\n/,
|
||||||
|
group: Lexer.SKIPPED,
|
||||||
|
});
|
||||||
|
|
||||||
|
const Pie = createToken({ name: 'Pie', pattern: /pie/i });
|
||||||
|
const ShowData = createToken({ name: 'ShowData', pattern: /showData/i });
|
||||||
|
const Title = createToken({ name: 'Title', pattern: /title/i });
|
||||||
|
const AccDescription = createToken({ name: 'AccDescription', pattern: /accDescr/i });
|
||||||
|
const AccTitle = createToken({ name: 'AccTitle', pattern: /accTitle/i });
|
||||||
|
const LeftCurly = createToken({ name: 'LeftCurly', pattern: /{/ });
|
||||||
|
const RightCurly = createToken({ name: 'RightCurly', pattern: /}/ });
|
||||||
|
// TODO: Figure out ordering of tokens
|
||||||
|
const allTokens = [
|
||||||
|
NewLine,
|
||||||
|
WhiteSpace,
|
||||||
|
Colon,
|
||||||
|
LeftCurly,
|
||||||
|
RightCurly,
|
||||||
|
Comment,
|
||||||
|
// Keywords
|
||||||
|
Pie,
|
||||||
|
ShowData,
|
||||||
|
Title,
|
||||||
|
AccTitle,
|
||||||
|
AccDescription,
|
||||||
|
// Literals
|
||||||
|
NumberLiteral,
|
||||||
|
StringLiteral,
|
||||||
|
Text,
|
||||||
|
];
|
||||||
|
const PieLexer = new Lexer(allTokens);
|
||||||
|
|
||||||
|
class PieParser extends EmbeddedActionsParser {
|
||||||
|
constructor() {
|
||||||
|
super(allTokens);
|
||||||
|
this.performSelfAnalysis();
|
||||||
|
}
|
||||||
|
|
||||||
|
public reset(): void {
|
||||||
|
super.reset();
|
||||||
|
pieDb.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
public diagram = this.RULE('diagram', () => {
|
||||||
|
this.SUBRULE(this.header);
|
||||||
|
this.OPTION(() => {
|
||||||
|
this.SUBRULE(this.accTitle);
|
||||||
|
this.CONSUME(NewLine);
|
||||||
|
});
|
||||||
|
this.OPTION2(() => {
|
||||||
|
this.CONSUME2(AccDescription);
|
||||||
|
this.OR([
|
||||||
|
{ ALT: () => this.SUBRULE(this.accDescriptionSingleLine) },
|
||||||
|
{ ALT: () => this.SUBRULE(this.accDescriptionMultiLine) },
|
||||||
|
]);
|
||||||
|
this.CONSUME3(NewLine);
|
||||||
|
});
|
||||||
|
this.AT_LEAST_ONE(() => {
|
||||||
|
this.SUBRULE2(this.row);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
public header = this.RULE('header', () => {
|
||||||
|
this.CONSUME(Pie);
|
||||||
|
this.OPTION(() => {
|
||||||
|
this.CONSUME(ShowData);
|
||||||
|
this.ACTION(() => pieDb.setShowData(true));
|
||||||
|
});
|
||||||
|
this.OPTION2(() => {
|
||||||
|
this.SUBRULE(this.title);
|
||||||
|
});
|
||||||
|
this.CONSUME(NewLine);
|
||||||
|
});
|
||||||
|
|
||||||
|
public title = this.RULE('title', () => {
|
||||||
|
this.CONSUME(Title);
|
||||||
|
const titleText = this.CONSUME(Text).image;
|
||||||
|
this.ACTION(() => pieDb.setDiagramTitle(titleText));
|
||||||
|
});
|
||||||
|
|
||||||
|
public accTitle = this.RULE('accTitle', () => {
|
||||||
|
this.CONSUME(AccTitle);
|
||||||
|
this.CONSUME(Colon);
|
||||||
|
const accTitleText = this.CONSUME(Text).image;
|
||||||
|
this.ACTION(() => pieDb.setAccTitle(accTitleText));
|
||||||
|
});
|
||||||
|
|
||||||
|
public accDescriptionSingleLine = this.RULE('accDescriptionSingleLine', () => {
|
||||||
|
this.CONSUME(Colon);
|
||||||
|
const accDescrText = this.CONSUME(Text).image;
|
||||||
|
this.ACTION(() => pieDb.setAccDescription(accDescrText));
|
||||||
|
});
|
||||||
|
|
||||||
|
public accDescriptionMultiLine = this.RULE('accDescriptionMultiLine', () => {
|
||||||
|
this.CONSUME(LeftCurly);
|
||||||
|
this.MANY(() => this.CONSUME(NewLine));
|
||||||
|
|
||||||
|
const text: string[] = [];
|
||||||
|
this.AT_LEAST_ONE(() => {
|
||||||
|
const line = this.CONSUME(Text);
|
||||||
|
text.push(line.image);
|
||||||
|
this.MANY1(() => this.CONSUME2(NewLine));
|
||||||
|
});
|
||||||
|
this.CONSUME(RightCurly);
|
||||||
|
this.ACTION(() => pieDb.setAccDescription(text.join('\n')));
|
||||||
|
});
|
||||||
|
|
||||||
|
public row = this.RULE('row', () => {
|
||||||
|
this.SUBRULE(this.section);
|
||||||
|
this.MANY(() => {
|
||||||
|
this.CONSUME(NewLine);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
public section = this.RULE('section', () => {
|
||||||
|
const quotedKey = this.CONSUME(StringLiteral).image;
|
||||||
|
const key = quotedKey.slice(1, quotedKey.length - 1);
|
||||||
|
this.CONSUME(Colon);
|
||||||
|
const value = parseFloat(this.CONSUME(NumberLiteral).image);
|
||||||
|
this.ACTION(() => pieDb.addSection(key, value));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
const parser = new PieParser();
|
||||||
|
|
||||||
|
const parse = (text: string): void => {
|
||||||
|
const lexResult = PieLexer.tokenize(text);
|
||||||
|
parser.input = lexResult.tokens;
|
||||||
|
parser.diagram();
|
||||||
|
|
||||||
|
if (parser.errors.length > 0 || lexResult.errors.length > 0) {
|
||||||
|
log.error(
|
||||||
|
{ parserErrors: parser.errors, lexerErrors: lexResult.errors },
|
||||||
|
'Error parsing info diagram'
|
||||||
|
);
|
||||||
|
throw new Error(`Parser errors: ${parser.errors} Lex errors: ${lexResult.errors}`);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
export default { parser: { parse }, parse };
|
46
yarn.lock
46
yarn.lock
@@ -1532,6 +1532,33 @@
|
|||||||
resolved "https://registry.yarnpkg.com/@braintree/sanitize-url/-/sanitize-url-6.0.0.tgz#fe364f025ba74f6de6c837a84ef44bdb1d61e68f"
|
resolved "https://registry.yarnpkg.com/@braintree/sanitize-url/-/sanitize-url-6.0.0.tgz#fe364f025ba74f6de6c837a84ef44bdb1d61e68f"
|
||||||
integrity sha512-mgmE7XBYY/21erpzhexk4Cj1cyTQ9LzvnTxtzM17BJ7ERMNE6W72mQRo0I1Ud8eFJ+RVVIcBNhLFZ3GX4XFz5w==
|
integrity sha512-mgmE7XBYY/21erpzhexk4Cj1cyTQ9LzvnTxtzM17BJ7ERMNE6W72mQRo0I1Ud8eFJ+RVVIcBNhLFZ3GX4XFz5w==
|
||||||
|
|
||||||
|
"@chevrotain/cst-dts-gen@10.3.0":
|
||||||
|
version "10.3.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/@chevrotain/cst-dts-gen/-/cst-dts-gen-10.3.0.tgz#434b267f7b38b2a760002a1bad699b4be1bd27a5"
|
||||||
|
integrity sha512-7DJPfCtfK1SU1/F3ZmmVv5IsTTcP/iiKFqU1m0H4VzJNvG3/GNxJFQjy4t/veWTAFSPWSj1WCNyq+sc5XKq9yA==
|
||||||
|
dependencies:
|
||||||
|
"@chevrotain/gast" "10.3.0"
|
||||||
|
"@chevrotain/types" "10.3.0"
|
||||||
|
lodash "4.17.21"
|
||||||
|
|
||||||
|
"@chevrotain/gast@10.3.0":
|
||||||
|
version "10.3.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/@chevrotain/gast/-/gast-10.3.0.tgz#0c242546768183a13509e732ab35cb7402a3d6cd"
|
||||||
|
integrity sha512-kLbGubyLprlyFZ1/c5pkpciCi6WTcRcnKhkfflSdKsZuoy0OmVTEXzrmFCQWzJ+QtmQNtPZTKwIBXJ6Zixp6nA==
|
||||||
|
dependencies:
|
||||||
|
"@chevrotain/types" "10.3.0"
|
||||||
|
lodash "4.17.21"
|
||||||
|
|
||||||
|
"@chevrotain/types@10.3.0":
|
||||||
|
version "10.3.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/@chevrotain/types/-/types-10.3.0.tgz#aa002b643534b5ff1333a46984f63e0ef3898088"
|
||||||
|
integrity sha512-LGesL4c5+FyweDsmFukcxmsowpagj1iC4iqkQSIDG3Y7krV2rIOmCDDq4kZff51Asr6yQHEJsWTyvGVHeWQLkw==
|
||||||
|
|
||||||
|
"@chevrotain/utils@10.3.0":
|
||||||
|
version "10.3.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/@chevrotain/utils/-/utils-10.3.0.tgz#a5c02c388207d032b563bfc3f051fec646192920"
|
||||||
|
integrity sha512-KCpFcX+kNyKlVZQW60ZUGRW5Nsg5u0F2CIgHiDQyg282ouHS9xap2ZEKqhwGE/0nYP46nAPnGPdb/IYh7ZHOsA==
|
||||||
|
|
||||||
"@commitlint/cli@^17.1.2":
|
"@commitlint/cli@^17.1.2":
|
||||||
version "17.1.2"
|
version "17.1.2"
|
||||||
resolved "https://registry.yarnpkg.com/@commitlint/cli/-/cli-17.1.2.tgz#38240f84936df5216f749f06f838dc50cc85a43d"
|
resolved "https://registry.yarnpkg.com/@commitlint/cli/-/cli-17.1.2.tgz#38240f84936df5216f749f06f838dc50cc85a43d"
|
||||||
@@ -4031,6 +4058,18 @@ check-more-types@2.24.0, check-more-types@^2.24.0:
|
|||||||
resolved "https://registry.yarnpkg.com/check-more-types/-/check-more-types-2.24.0.tgz#1420ffb10fd444dcfc79b43891bbfffd32a84600"
|
resolved "https://registry.yarnpkg.com/check-more-types/-/check-more-types-2.24.0.tgz#1420ffb10fd444dcfc79b43891bbfffd32a84600"
|
||||||
integrity sha1-FCD/sQ/URNz8ebQ4kbv//TKoRgA=
|
integrity sha1-FCD/sQ/URNz8ebQ4kbv//TKoRgA=
|
||||||
|
|
||||||
|
chevrotain@^10.3.0:
|
||||||
|
version "10.3.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/chevrotain/-/chevrotain-10.3.0.tgz#16bad78cfffefe58bfe9e4943ba585bad2a2b6a1"
|
||||||
|
integrity sha512-sy3yTBfvNJmxzYOGWaDStZFuA7va5/MXwzBU+XBIol0bR0aYlfGqTjzgedeu32Ki/j7IVyvYAZO2PInBjmmMjg==
|
||||||
|
dependencies:
|
||||||
|
"@chevrotain/cst-dts-gen" "10.3.0"
|
||||||
|
"@chevrotain/gast" "10.3.0"
|
||||||
|
"@chevrotain/types" "10.3.0"
|
||||||
|
"@chevrotain/utils" "10.3.0"
|
||||||
|
lodash "4.17.21"
|
||||||
|
regexp-to-ast "0.5.0"
|
||||||
|
|
||||||
chokidar@^3.4.0, chokidar@^3.5.3:
|
chokidar@^3.4.0, chokidar@^3.5.3:
|
||||||
version "3.5.3"
|
version "3.5.3"
|
||||||
resolved "https://registry.yarnpkg.com/chokidar/-/chokidar-3.5.3.tgz#1cf37c8707b932bd1af1ae22c0432e2acd1903bd"
|
resolved "https://registry.yarnpkg.com/chokidar/-/chokidar-3.5.3.tgz#1cf37c8707b932bd1af1ae22c0432e2acd1903bd"
|
||||||
@@ -8486,7 +8525,7 @@ lodash.once@^4.1.1:
|
|||||||
resolved "https://registry.yarnpkg.com/lodash.once/-/lodash.once-4.1.1.tgz#0dd3971213c7c56df880977d504c88fb471a97ac"
|
resolved "https://registry.yarnpkg.com/lodash.once/-/lodash.once-4.1.1.tgz#0dd3971213c7c56df880977d504c88fb471a97ac"
|
||||||
integrity sha1-DdOXEhPHxW34gJd9UEyI+0cal6w=
|
integrity sha1-DdOXEhPHxW34gJd9UEyI+0cal6w=
|
||||||
|
|
||||||
lodash@^4.17.10, lodash@^4.17.15, lodash@^4.17.19, lodash@^4.17.21, lodash@^4.17.4:
|
lodash@4.17.21, lodash@^4.17.10, lodash@^4.17.15, lodash@^4.17.19, lodash@^4.17.21, lodash@^4.17.4:
|
||||||
version "4.17.21"
|
version "4.17.21"
|
||||||
resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.21.tgz#679591c564c3bffaae8454cf0b3df370c3d6911c"
|
resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.21.tgz#679591c564c3bffaae8454cf0b3df370c3d6911c"
|
||||||
integrity sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==
|
integrity sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==
|
||||||
@@ -10274,6 +10313,11 @@ regex-not@^1.0.0, regex-not@^1.0.2:
|
|||||||
extend-shallow "^3.0.2"
|
extend-shallow "^3.0.2"
|
||||||
safe-regex "^1.1.0"
|
safe-regex "^1.1.0"
|
||||||
|
|
||||||
|
regexp-to-ast@0.5.0:
|
||||||
|
version "0.5.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/regexp-to-ast/-/regexp-to-ast-0.5.0.tgz#56c73856bee5e1fef7f73a00f1473452ab712a24"
|
||||||
|
integrity sha512-tlbJqcMHnPKI9zSrystikWKwHkBqu2a/Sgw01h3zFjvYrMxEDYHzzoMZnUrbIfpTFEsoRnnviOXNCzFiSc54Qw==
|
||||||
|
|
||||||
regexpp@^3.2.0:
|
regexpp@^3.2.0:
|
||||||
version "3.2.0"
|
version "3.2.0"
|
||||||
resolved "https://registry.yarnpkg.com/regexpp/-/regexpp-3.2.0.tgz#0425a2768d8f23bad70ca4b90461fa2f1213e1b2"
|
resolved "https://registry.yarnpkg.com/regexpp/-/regexpp-3.2.0.tgz#0425a2768d8f23bad70ca4b90461fa2f1213e1b2"
|
||||||
|
Reference in New Issue
Block a user