Merge branch 'develop' into feature/AddBoxForSeqDiagrams

This commit is contained in:
Olivier Leveau
2023-01-26 09:14:18 +01:00
30 changed files with 1007 additions and 540 deletions

View File

@@ -4,3 +4,4 @@ docs/Setup.md
cypress.config.js
cypress/plugins/index.js
coverage
*.json

150
.eslintrc.cjs Normal file
View File

@@ -0,0 +1,150 @@
module.exports = {
env: {
browser: true,
es6: true,
'jest/globals': true,
node: true,
},
root: true,
parser: '@typescript-eslint/parser',
parserOptions: {
ecmaFeatures: {
experimentalObjectRestSpread: true,
jsx: true,
},
tsconfigRootDir: __dirname,
sourceType: 'module',
ecmaVersion: 2020,
allowAutomaticSingleRunInference: true,
project: ['./tsconfig.eslint.json', './packages/*/tsconfig.json'],
parser: '@typescript-eslint/parser',
},
extends: [
'eslint:recommended',
'plugin:@typescript-eslint/recommended',
'plugin:json/recommended',
'plugin:markdown/recommended',
'plugin:@cspell/recommended',
'prettier',
],
plugins: [
'@typescript-eslint',
'no-only-tests',
'html',
'jest',
'jsdoc',
'json',
'@cspell',
'lodash',
'unicorn',
],
rules: {
curly: 'error',
'no-console': 'error',
'no-prototype-builtins': 'off',
'no-unused-vars': 'off',
'cypress/no-async-tests': 'off',
'@typescript-eslint/no-floating-promises': 'error',
'@typescript-eslint/no-misused-promises': 'error',
'@typescript-eslint/ban-ts-comment': [
'error',
{
'ts-expect-error': 'allow-with-description',
'ts-ignore': 'allow-with-description',
'ts-nocheck': 'allow-with-description',
'ts-check': 'allow-with-description',
minimumDescriptionLength: 10,
},
],
'json/*': ['error', 'allowComments'],
'@cspell/spellchecker': [
'error',
{
checkIdentifiers: false,
checkStrings: false,
checkStringTemplates: false,
},
],
'no-empty': [
'error',
{
allowEmptyCatch: true,
},
],
'no-only-tests/no-only-tests': 'error',
'lodash/import-scope': ['error', 'method'],
'unicorn/better-regex': 'error',
'unicorn/no-abusive-eslint-disable': 'error',
'unicorn/no-array-push-push': 'error',
'unicorn/no-for-loop': 'error',
'unicorn/no-instanceof-array': 'error',
'unicorn/no-typeof-undefined': 'error',
'unicorn/no-unnecessary-await': 'error',
'unicorn/no-unsafe-regex': 'warn',
'unicorn/no-useless-promise-resolve-reject': 'error',
'unicorn/prefer-array-find': 'error',
'unicorn/prefer-array-flat-map': 'error',
'unicorn/prefer-array-index-of': 'error',
'unicorn/prefer-array-some': 'error',
'unicorn/prefer-default-parameters': 'error',
'unicorn/prefer-includes': 'error',
'unicorn/prefer-negative-index': 'error',
'unicorn/prefer-object-from-entries': 'error',
'unicorn/prefer-string-starts-ends-with': 'error',
'unicorn/prefer-string-trim-start-end': 'error',
'unicorn/string-content': 'error',
'unicorn/prefer-spread': 'error',
'unicorn/no-lonely-if': 'error',
},
overrides: [
{
files: ['cypress/**', 'demos/**'],
rules: {
'no-console': 'off',
},
},
{
files: ['*.{js,jsx,mjs,cjs}'],
extends: ['plugin:jsdoc/recommended'],
rules: {
'jsdoc/check-indentation': 'off',
'jsdoc/check-alignment': 'off',
'jsdoc/check-line-alignment': 'off',
'jsdoc/multiline-blocks': 'off',
'jsdoc/newline-after-description': 'off',
'jsdoc/tag-lines': 'off',
'jsdoc/require-param-description': 'off',
'jsdoc/require-param-type': 'off',
'jsdoc/require-returns': 'off',
'jsdoc/require-returns-description': 'off',
},
},
{
files: ['*.{ts,tsx}'],
plugins: ['tsdoc'],
rules: {
'tsdoc/syntax': 'error',
},
},
{
files: ['*.spec.{ts,js}', 'cypress/**', 'demos/**', '**/docs/**'],
rules: {
'jsdoc/require-jsdoc': 'off',
'@typescript-eslint/no-unused-vars': 'off',
},
},
{
files: ['*.html', '*.md', '**/*.md/*'],
rules: {
'no-var': 'error',
'no-undef': 'off',
'@typescript-eslint/no-unused-vars': 'off',
'@typescript-eslint/no-floating-promises': 'off',
'@typescript-eslint/no-misused-promises': 'off',
},
parserOptions: {
project: null,
},
},
],
};

View File

@@ -1,137 +0,0 @@
{
"env": {
"browser": true,
"es6": true,
"jest/globals": true,
"node": true
},
"parser": "@typescript-eslint/parser",
"parserOptions": {
"ecmaFeatures": {
"experimentalObjectRestSpread": true,
"jsx": true
},
"sourceType": "module"
},
"extends": [
"eslint:recommended",
"plugin:@typescript-eslint/recommended",
"plugin:json/recommended",
"plugin:markdown/recommended",
"plugin:@cspell/recommended",
"prettier"
],
"plugins": [
"@typescript-eslint",
"no-only-tests",
"html",
"jest",
"jsdoc",
"json",
"@cspell",
"lodash",
"unicorn"
],
"rules": {
"curly": "error",
"no-console": "error",
"no-prototype-builtins": "off",
"no-unused-vars": "off",
"cypress/no-async-tests": "off",
"@typescript-eslint/ban-ts-comment": [
"error",
{
"ts-expect-error": "allow-with-description",
"ts-ignore": "allow-with-description",
"ts-nocheck": "allow-with-description",
"ts-check": "allow-with-description",
"minimumDescriptionLength": 10
}
],
"json/*": ["error", "allowComments"],
"@cspell/spellchecker": [
"error",
{
"checkIdentifiers": false,
"checkStrings": false,
"checkStringTemplates": false
}
],
"no-empty": [
"error",
{
"allowEmptyCatch": true
}
],
"no-only-tests/no-only-tests": "error",
"lodash/import-scope": ["error", "method"],
"unicorn/better-regex": "error",
"unicorn/no-abusive-eslint-disable": "error",
"unicorn/no-array-push-push": "error",
"unicorn/no-for-loop": "error",
"unicorn/no-instanceof-array": "error",
"unicorn/no-typeof-undefined": "error",
"unicorn/no-unnecessary-await": "error",
"unicorn/no-unsafe-regex": "warn",
"unicorn/no-useless-promise-resolve-reject": "error",
"unicorn/prefer-array-find": "error",
"unicorn/prefer-array-flat-map": "error",
"unicorn/prefer-array-index-of": "error",
"unicorn/prefer-array-some": "error",
"unicorn/prefer-default-parameters": "error",
"unicorn/prefer-includes": "error",
"unicorn/prefer-negative-index": "error",
"unicorn/prefer-object-from-entries": "error",
"unicorn/prefer-string-starts-ends-with": "error",
"unicorn/prefer-string-trim-start-end": "error",
"unicorn/string-content": "error",
"unicorn/prefer-spread": "error",
"unicorn/no-lonely-if": "error"
},
"overrides": [
{
"files": ["cypress/**", "demos/**"],
"rules": {
"no-console": "off"
}
},
{
"files": ["*.{js,jsx,mjs,cjs}"],
"extends": ["plugin:jsdoc/recommended"],
"rules": {
"jsdoc/check-indentation": "off",
"jsdoc/check-alignment": "off",
"jsdoc/check-line-alignment": "off",
"jsdoc/multiline-blocks": "off",
"jsdoc/newline-after-description": "off",
"jsdoc/tag-lines": "off",
"jsdoc/require-param-description": "off",
"jsdoc/require-param-type": "off",
"jsdoc/require-returns": "off",
"jsdoc/require-returns-description": "off"
}
},
{
"files": ["*.{ts,tsx}"],
"plugins": ["tsdoc"],
"rules": {
"tsdoc/syntax": "error"
}
},
{
"files": ["*.spec.{ts,js}", "cypress/**", "demos/**", "**/docs/**"],
"rules": {
"jsdoc/require-jsdoc": "off",
"@typescript-eslint/no-unused-vars": "off"
}
},
{
"files": ["*.html", "*.md", "**/*.md/*"],
"rules": {
"no-var": "error",
"no-undef": "off",
"@typescript-eslint/no-unused-vars": "off"
}
}
]
}

View File

@@ -151,7 +151,7 @@ if (typeof document !== 'undefined') {
contentLoadedApi();
} else {
this.console.log('Not using api');
contentLoaded();
void contentLoaded();
}
},
false

View File

@@ -71,6 +71,44 @@
</pre>
<hr />
<pre class="mermaid">
erDiagram
"HOSPITAL" {
int id PK
int doctor_id PK, FK
string address UK
string name
string phone_number
string fax_number
}
</pre
>
<hr />
<pre class="mermaid">
erDiagram
CAR ||--o{ NAMED-DRIVER : allows
CAR {
string registrationNumber PK
string make
string model
string[] parts
}
PERSON ||--o{ NAMED-DRIVER : is
PERSON {
string driversLicense PK "The license #"
string(99) firstName "Only 99 characters are allowed"
string lastName
string phone UK
int age
}
NAMED-DRIVER {
string carRegistrationNumber PK, FK
string driverLicence PK,FK
}
MANUFACTURER only one to zero or more CAR : makes
</pre>
<script src="./mermaid.js"></script>
<script type="module">
mermaid.initialize({

View File

@@ -161,12 +161,19 @@
First --> Second
First --> Third
state First {
state "the first composite" as First {
[*] --> 1st
1st --> [*]
state innerFirst {
state "1 in innerFirst" as 1st1st
1st2nd: 2 in innerFirst
[*] --> 1st1st
1st1st --> 1st2nd
%% 1st2nd --> 1st
}
1st --> innerFirst
innerFirst --> 2nd
}
state Second {
[*] --> 2nd
2nd --> [*]
}
state Third {

View File

@@ -95,9 +95,10 @@ They also serve as proof of concept, for the variety of things that can be built
## Editor Plugins
- [Vs Code](https://code.visualstudio.com/)
- [VS Code](https://code.visualstudio.com/)
- [Markdown Preview Mermaid Support](https://marketplace.visualstudio.com/items?itemName=bierner.markdown-mermaid)
- [Mermaid Preview](https://marketplace.visualstudio.com/items?itemName=vstirbu.vscode-mermaid-preview)
- [Markdown Preview Enhanced](https://marketplace.visualstudio.com/items?itemName=shd101wyy.markdown-preview-enhanced)
- [Mermaid Markdown Syntax Highlighting](https://marketplace.visualstudio.com/items?itemName=bpruitt-goddard.mermaid-markdown-syntax-highlighting)
- [Mermaid Editor](https://marketplace.visualstudio.com/items?itemName=tomoyukim.vscode-mermaid-editor)
- [Mermaid Export](https://marketplace.visualstudio.com/items?itemName=Gruntfuggly.mermaid-export)

View File

@@ -200,14 +200,13 @@ The `type` and `name` values must begin with an alphabetic character and may con
#### Attribute Keys and Comments
Attributes may also have a `key` or comment defined. Keys can be "PK", "FK" or "UK", for Primary Key, Foreign Key or Unique Key. And a `comment` is defined by double quotes at the end of an attribute. Comments themselves cannot have double-quote characters in them.
Attributes may also have a `key` or comment defined. Keys can be `PK`, `FK` or `UK`, for Primary Key, Foreign Key or Unique Key. To specify multiple key constraints on a single attribute, separate them with a comma (e.g., `PK, FK`).. A `comment` is defined by double quotes at the end of an attribute. Comments themselves cannot have double-quote characters in them.
```mermaid-example
erDiagram
CAR ||--o{ NAMED-DRIVER : allows
CAR {
string allowedDriver FK "The license of the allowed driver"
string registrationNumber UK
string registrationNumber PK
string make
string model
string[] parts
@@ -217,17 +216,21 @@ erDiagram
string driversLicense PK "The license #"
string(99) firstName "Only 99 characters are allowed"
string lastName
string phone UK
int age
}
MANUFACTURER only one to zero or more CAR
NAMED-DRIVER {
string carRegistrationNumber PK, FK
string driverLicence PK, FK
}
MANUFACTURER only one to zero or more CAR : makes
```
```mermaid
erDiagram
CAR ||--o{ NAMED-DRIVER : allows
CAR {
string allowedDriver FK "The license of the allowed driver"
string registrationNumber UK
string registrationNumber PK
string make
string model
string[] parts
@@ -237,9 +240,14 @@ erDiagram
string driversLicense PK "The license #"
string(99) firstName "Only 99 characters are allowed"
string lastName
string phone UK
int age
}
MANUFACTURER only one to zero or more CAR
NAMED-DRIVER {
string carRegistrationNumber PK, FK
string driverLicence PK, FK
}
MANUFACTURER only one to zero or more CAR : makes
```
### Other Things

View File

@@ -4,7 +4,7 @@
"version": "9.3.0-rc1",
"description": "Markdownish syntax for generating flowcharts, sequence diagrams, class diagrams, gantt charts and git graphs.",
"type": "module",
"packageManager": "pnpm@7.25.0",
"packageManager": "pnpm@7.25.1",
"keywords": [
"diagram",
"markdown",
@@ -67,8 +67,8 @@
"@types/node": "^18.11.9",
"@types/prettier": "^2.7.1",
"@types/rollup-plugin-visualizer": "^4.2.1",
"@typescript-eslint/eslint-plugin": "^5.42.1",
"@typescript-eslint/parser": "^5.42.1",
"@typescript-eslint/eslint-plugin": "^5.48.2",
"@typescript-eslint/parser": "^5.48.2",
"@vitest/coverage-c8": "^0.27.0",
"@vitest/ui": "^0.27.0",
"concurrently": "^7.5.0",
@@ -76,8 +76,8 @@
"cypress": "^10.11.0",
"cypress-image-snapshot": "^4.0.1",
"esbuild": "^0.17.0",
"eslint": "^8.27.0",
"eslint-config-prettier": "^8.5.0",
"eslint": "^8.32.0",
"eslint-config-prettier": "^8.6.0",
"eslint-plugin-cypress": "^2.12.1",
"eslint-plugin-html": "^7.1.0",
"eslint-plugin-jest": "^27.1.5",

View File

@@ -47,6 +47,7 @@
"non-layered-tidy-tree-layout": "^2.0.2"
},
"devDependencies": {
"@types/cytoscape": "^3.19.9",
"concurrently": "^7.5.0",
"mermaid": "workspace:*",
"rimraf": "^3.0.2"

View File

@@ -90,6 +90,7 @@
"path-browserify": "^1.0.1",
"prettier": "^2.7.1",
"remark": "^14.0.2",
"remark-frontmatter": "^4.0.1",
"remark-gfm": "^3.0.1",
"rimraf": "^3.0.2",
"start-server-and-test": "^1.14.0",

View File

@@ -6,7 +6,7 @@ import { extractFrontMatter } from './diagram-api/frontmatter';
import { isDetailedError } from './utils';
import type { DetailedError } from './utils';
export type ParseErrorFunction = (err: string | DetailedError, hash?: any) => void;
export type ParseErrorFunction = (err: string | DetailedError | unknown, hash?: any) => void;
export class Diagram {
type = 'graph';

View File

@@ -59,7 +59,7 @@ const drawAttributes = (groupNode, entityTextNode, attributes) => {
// Check to see if any of the attributes has a key or a comment
attributes.forEach((item) => {
if (item.attributeKeyType !== undefined) {
if (item.attributeKeyTypeList !== undefined && item.attributeKeyTypeList.length > 0) {
hasKeyType = true;
}
@@ -112,6 +112,9 @@ const drawAttributes = (groupNode, entityTextNode, attributes) => {
nodeHeight = Math.max(typeBBox.height, nameBBox.height);
if (hasKeyType) {
const keyTypeNodeText =
item.attributeKeyTypeList !== undefined ? item.attributeKeyTypeList.join(',') : '';
const keyTypeNode = groupNode
.append('text')
.classed('er entityLabel', true)
@@ -122,7 +125,7 @@ const drawAttributes = (groupNode, entityTextNode, attributes) => {
.style('text-anchor', 'left')
.style('font-family', getConfig().fontFamily)
.style('font-size', attrFontSize + 'px')
.text(item.attributeKeyType || '');
.text(keyTypeNodeText);
attributeNode.kn = keyTypeNode;
const keyTypeBBox = keyTypeNode.node().getBBox();

View File

@@ -28,6 +28,7 @@ accDescr\s*"{"\s* { this.begin("acc_descr_multili
\"[^"]*\" return 'WORD';
"erDiagram" return 'ER_DIAGRAM';
"{" { this.begin("block"); return 'BLOCK_START'; }
<block>"," return 'COMMA';
<block>\s+ /* skip whitespace in block */
<block>\b((?:PK)|(?:FK)|(?:UK))\b return 'ATTRIBUTE_KEY'
<block>(.*?)[~](.*?)*[~] return 'ATTRIBUTE_WORD';
@@ -131,11 +132,12 @@ attributes
attribute
: attributeType attributeName { $$ = { attributeType: $1, attributeName: $2 }; }
| attributeType attributeName attributeKeyType { $$ = { attributeType: $1, attributeName: $2, attributeKeyType: $3 }; }
| attributeType attributeName attributeKeyTypeList { $$ = { attributeType: $1, attributeName: $2, attributeKeyTypeList: $3 }; }
| attributeType attributeName attributeComment { $$ = { attributeType: $1, attributeName: $2, attributeComment: $3 }; }
| attributeType attributeName attributeKeyType attributeComment { $$ = { attributeType: $1, attributeName: $2, attributeKeyType: $3, attributeComment: $4 }; }
| attributeType attributeName attributeKeyTypeList attributeComment { $$ = { attributeType: $1, attributeName: $2, attributeKeyTypeList: $3, attributeComment: $4 }; }
;
attributeType
: ATTRIBUTE_WORD { $$=$1; }
;
@@ -144,6 +146,11 @@ attributeName
: ATTRIBUTE_WORD { $$=$1; }
;
attributeKeyTypeList
: attributeKeyType { $$ = [$1]; }
| attributeKeyTypeList COMMA attributeKeyType { $1.push($3); $$ = $1; }
;
attributeKeyType
: ATTRIBUTE_KEY { $$=$1; }
;

View File

@@ -190,6 +190,28 @@ describe('when parsing ER diagram it...', function () {
expect(entities[entity].attributes.length).toBe(4);
});
it('should allow an entity with attributes that have many constraints and comments', function () {
const entity = 'CUSTOMER';
const attribute1 = 'int customer_number PK, FK "comment1"';
const attribute2 = 'datetime customer_status_start_datetime PK,UK, FK';
const attribute3 = 'datetime customer_status_end_datetime PK , UK "comment3"';
const attribute4 = 'string customer_firstname';
const attribute5 = 'string customer_lastname "comment5"';
erDiagram.parser.parse(
`erDiagram\n${entity} {\n${attribute1}\n${attribute2}\n${attribute3}\n${attribute4}\n${attribute5}\n}`
);
const entities = erDb.getEntities();
expect(entities[entity].attributes[0].attributeKeyTypeList).toEqual(['PK', 'FK']);
expect(entities[entity].attributes[0].attributeComment).toBe('comment1');
expect(entities[entity].attributes[1].attributeKeyTypeList).toEqual(['PK', 'UK', 'FK']);
expect(entities[entity].attributes[2].attributeKeyTypeList).toEqual(['PK', 'UK']);
expect(entities[entity].attributes[2].attributeComment).toBe('comment3');
expect(entities[entity].attributes[3].attributeKeyTypeList).toBeUndefined();
expect(entities[entity].attributes[4].attributeKeyTypeList).toBeUndefined();
expect(entities[entity].attributes[4].attributeComment).toBe('comment5');
});
it('should allow an entity with attribute that has a generic type', function () {
const entity = 'BOOK';
const attribute1 = 'type~T~ type';

View File

@@ -609,7 +609,7 @@ const insertChildren = (nodeArray, parentLookupDb) => {
* @param id
*/
export const draw = function (text, id, _version, diagObj) {
export const draw = async function (text, id, _version, diagObj) {
// Add temporary render element
diagObj.db.clear();
nodeDb = {};
@@ -617,29 +617,14 @@ export const draw = function (text, id, _version, diagObj) {
// Parse the graph definition
diagObj.parser.parse(text);
return new Promise(function (resolve, reject) {
const renderEl = select('body').append('div').attr('style', 'height:400px').attr('id', 'cy');
// .attr('style', 'display:none')
let graph = {
id: 'root',
layoutOptions: {
'elk.hierarchyHandling': 'INCLUDE_CHILDREN',
// 'elk.hierarchyHandling': 'SEPARATE_CHILDREN',
'org.eclipse.elk.padding': '[top=100, left=100, bottom=110, right=110]',
// 'org.eclipse.elk.layered.spacing.nodeNodeBetweenLayers': 120,
// 'elk.layered.spacing.nodeNodeBetweenLayers': '140',
'elk.layered.spacing.edgeNodeBetweenLayers': '30',
// 'elk.algorithm': 'layered',
'elk.direction': 'DOWN',
// 'elk.port.side': 'SOUTH',
// 'nodePlacement.strategy': 'SIMPLE',
// 'org.eclipse.elk.spacing.labelLabel': 120,
// 'org.eclipse.elk.graphviz.concentrate': true,
// 'org.eclipse.elk.spacing.nodeNode': 120,
// 'org.eclipse.elk.spacing.edgeEdge': 120,
// 'org.eclipse.elk.spacing.edgeNode': 120,
// 'org.eclipse.elk.spacing.nodeEdge': 120,
// 'org.eclipse.elk.spacing.componentComponent': 120,
},
children: [],
edges: [],
@@ -727,8 +712,6 @@ export const draw = function (text, id, _version, diagObj) {
if (!node.parent) {
graph.children.push(node);
}
// node.nodePadding = [120, 50, 50, 50];
// node['org.eclipse.elk.spacing.nodeNode'] = 120;
// Subgraph
if (parentLookupDb.childrenById[nodeId] !== undefined) {
node.labels = [
@@ -748,18 +731,14 @@ export const draw = function (text, id, _version, diagObj) {
}
});
insertChildren(graph.children, parentLookupDb);
elk.layout(graph).then(function (g) {
const g = await elk.layout(graph);
drawNodes(0, 0, g.children, svg, subGraphsEl, diagObj, 0);
g.edges.map((edge, id) => {
g.edges?.map((edge) => {
insertEdge(edgesEl, edge, edge.edgeData, diagObj, parentLookupDb);
});
setupGraphViewbox({}, svg, conf.diagramPadding, conf.useMaxWidth);
resolve();
});
// Remove element after layout
renderEl.remove();
});
};
const drawNodes = (relX, relY, nodeArray, svg, subgraphsEl, diagObj, depth) => {

View File

@@ -0,0 +1,133 @@
import stateDb from '../stateDb';
import stateDiagram from './stateDiagram';
import { setConfig } from '../../../config';
setConfig({
securityLevel: 'strict',
});
describe('state parser can parse...', () => {
beforeEach(function () {
stateDiagram.parser.yy = stateDb;
stateDiagram.parser.yy.clear();
});
describe('states with id displayed as a (name)', () => {
describe('syntax 1: stateID as "name in quotes"', () => {
it('stateID as "some name"', () => {
const diagramText = `stateDiagram-v2
state "Small State 1" as namedState1`;
stateDiagram.parser.parse(diagramText);
stateDiagram.parser.yy.extract(stateDiagram.parser.yy.getRootDocV2());
const states = stateDiagram.parser.yy.getStates();
expect(states['namedState1']).not.toBeUndefined();
expect(states['namedState1'].descriptions.join(' ')).toEqual('Small State 1');
});
});
describe('syntax 2: stateID: "name in quotes" [colon after the id]', () => {
it('space before and after the colon', () => {
const diagramText = `stateDiagram-v2
namedState1 : Small State 1`;
stateDiagram.parser.parse(diagramText);
stateDiagram.parser.yy.extract(stateDiagram.parser.yy.getRootDocV2());
const states = stateDiagram.parser.yy.getStates();
expect(states['namedState1']).not.toBeUndefined();
expect(states['namedState1'].descriptions.join(' ')).toEqual('Small State 1');
});
it('no spaces before and after the colon', () => {
const diagramText = `stateDiagram-v2
namedState1:Small State 1`;
stateDiagram.parser.parse(diagramText);
stateDiagram.parser.yy.extract(stateDiagram.parser.yy.getRootDocV2());
const states = stateDiagram.parser.yy.getStates();
expect(states['namedState1']).not.toBeUndefined();
expect(states['namedState1'].descriptions.join(' ')).toEqual('Small State 1');
});
});
});
describe('can handle "as" in a state name', () => {
it('assemble, assemblies, state assemble, state assemblies', function () {
const diagramText = `stateDiagram-v2
assemble
assemblies
state assemble
state assemblies
`;
stateDiagram.parser.parse(diagramText);
stateDiagram.parser.yy.extract(stateDiagram.parser.yy.getRootDocV2());
const states = stateDiagram.parser.yy.getStates();
expect(states['assemble']).not.toBeUndefined();
expect(states['assemblies']).not.toBeUndefined();
});
it('state "as" as as', function () {
const diagramText = `stateDiagram-v2
state "as" as as
`;
stateDiagram.parser.parse(diagramText);
stateDiagram.parser.yy.extract(stateDiagram.parser.yy.getRootDocV2());
const states = stateDiagram.parser.yy.getStates();
expect(states['as']).not.toBeUndefined();
expect(states['as'].descriptions.join(' ')).toEqual('as');
});
});
describe('groups (clusters/containers)', () => {
it('state "Group Name" as stateIdentifier', () => {
const diagramText = `stateDiagram-v2
state "Small State 1" as namedState1
%% Notice that this is named "Big State 1" with an "as"
state "Big State 1" as bigState1 {
bigState1InternalState
}
namedState1 --> bigState1: should point to \\nBig State 1 container
state "Small State 2" as namedState2
%% Notice that bigState2 does not have a name; no "as"
state bigState2 {
bigState2InternalState
}
namedState2 --> bigState2: should point to \\nbigState2 container`;
stateDiagram.parser.parse(diagramText);
stateDiagram.parser.yy.extract(stateDiagram.parser.yy.getRootDocV2());
const states = stateDiagram.parser.yy.getStates();
expect(states['namedState1']).not.toBeUndefined();
expect(states['bigState1']).not.toBeUndefined();
expect(states['bigState1'].doc[0].id).toEqual('bigState1InternalState');
expect(states['namedState2']).not.toBeUndefined();
expect(states['bigState2']).not.toBeUndefined();
expect(states['bigState2'].doc[0].id).toEqual('bigState2InternalState');
const relationships = stateDiagram.parser.yy.getRelations();
expect(relationships[0].id1).toEqual('namedState1');
expect(relationships[0].id2).toEqual('bigState1');
expect(relationships[1].id1).toEqual('namedState2');
expect(relationships[1].id2).toEqual('bigState2');
});
it('group has a state with stateID AS "state name" and state2ID: "another state name"', () => {
const diagramText = `stateDiagram-v2
state "Big State 1" as bigState1 {
state "inner state 1" as inner1
inner2: inner state 2
inner1 --> inner2
}`;
stateDiagram.parser.parse(diagramText);
stateDiagram.parser.yy.extract(stateDiagram.parser.yy.getRootDocV2());
const states = stateDiagram.parser.yy.getStates();
expect(states['bigState1']).not.toBeUndefined();
expect(states['bigState1'].doc[0].id).toEqual('inner1');
expect(states['bigState1'].doc[0].description).toEqual('inner state 1');
expect(states['bigState1'].doc[1].id).toEqual('inner2');
expect(states['bigState1'].doc[1].description).toEqual('inner state 2');
});
});
});

View File

@@ -65,13 +65,13 @@
\%%[^\n]* /* skip comments */
"scale"\s+ { this.pushState('SCALE'); /* console.log('Got scale', yytext);*/ return 'scale'; }
<SCALE>\d+ return 'WIDTH';
<SCALE>\s+"width" {this.popState();}
<SCALE>\s+"width" { this.popState(); }
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");}
accDescr\s*"{"\s* { this.begin("acc_descr_multiline"); }
<acc_descr_multiline>[\}] { this.popState(); }
<acc_descr_multiline>[^\}]* return "acc_descr_multiline_value";
@@ -88,50 +88,53 @@ accDescr\s*"{"\s* { this.begin("acc_descr_multili
<SCALE>\d+ return 'WIDTH';
<SCALE>\s+"width" {this.popState();}
<INITIAL,struct>"state"\s+ { /* console.log('Starting STATE '); */ this.pushState('STATE'); }
<INITIAL,struct>"state"\s+ { /*console.log('Starting STATE zxzx'+yy.getDirection());*/this.pushState('STATE'); }
<STATE>.*"<<fork>>" {this.popState();yytext=yytext.slice(0,-8).trim(); /*console.warn('Fork Fork: ',yytext);*/return 'FORK';}
<STATE>.*"<<join>>" {this.popState();yytext=yytext.slice(0,-8).trim();/*console.warn('Fork Join: ',yytext);*/return 'JOIN';}
<STATE>.*"<<choice>>" {this.popState();yytext=yytext.slice(0,-10).trim();/*console.warn('Fork Join: ',yytext);*/return 'CHOICE';}
<STATE>.*"[[fork]]" {this.popState();yytext=yytext.slice(0,-8).trim();/*console.warn('Fork Fork: ',yytext);*/return 'FORK';}
<STATE>.*"[[join]]" {this.popState();yytext=yytext.slice(0,-8).trim();/*console.warn('Fork Join: ',yytext);*/return 'JOIN';}
<STATE>.*"[[choice]]" {this.popState();yytext=yytext.slice(0,-10).trim();/*console.warn('Fork Join: ',yytext);*/return 'CHOICE';}
<struct>.*direction\s+TB[^\n]* { return 'direction_tb';}
<struct>.*direction\s+BT[^\n]* { return 'direction_bt';}
<struct>.*direction\s+RL[^\n]* { return 'direction_rl';}
<struct>.*direction\s+LR[^\n]* { return 'direction_lr';}
<STATE>["] { /*console.log('Starting STATE_STRING zxzx');*/this.begin("STATE_STRING");}
<STATE>\s*"as"\s+ {this.popState();this.pushState('STATE_ID');return "AS";}
<STATE_ID>[^\n\{]* {this.popState();/* console.log('STATE_ID', yytext);*/return "ID";}
<STATE_STRING>["] this.popState();
<STATE_STRING>[^"]* { /*console.log('Long description:', yytext);*/return "STATE_DESCR";}
<STATE>[^\n\s\{]+ {/*console.log('COMPOSIT_STATE', yytext);*/return 'COMPOSIT_STATE';}
<STATE>\n {this.popState();}
<INITIAL,STATE>\{ {this.popState();this.pushState('struct'); /*console.log('begin struct', yytext);*/return 'STRUCT_START';}
<STATE>["] { /* console.log('Starting STATE_STRING'); */ this.pushState("STATE_STRING"); }
<STATE>\s*"as"\s+ { this.pushState('STATE_ID'); /* console.log('pushState(STATE_ID)'); */ return "AS"; }
<STATE_ID>[^\n\{]* { this.popState(); /* console.log('STATE_ID', yytext); */ return "ID"; }
<STATE_STRING>["] { this.popState(); }
<STATE_STRING>[^"]* { /* console.log('Long description:', yytext); */ return "STATE_DESCR"; }
<STATE>[^\n\s\{]+ { /* console.log('COMPOSIT_STATE', yytext); */ return 'COMPOSIT_STATE'; }
<STATE>\n { this.popState(); }
<INITIAL,STATE>\{ { this.popState(); this.pushState('struct'); /* console.log('begin struct', yytext); */ return 'STRUCT_START'; }
<struct>\%\%(?!\{)[^\n]* /* skip comments inside state*/
<struct>\} { /*console.log('Ending struct');*/ this.popState(); return 'STRUCT_STOP';}}
<struct>\} { /*console.log('Ending struct');*/ this.popState(); return 'STRUCT_STOP';} }
<struct>[\n] /* nothing */
<INITIAL,struct>"note"\s+ { this.begin('NOTE'); return 'note'; }
<NOTE>"left of" { this.popState();this.pushState('NOTE_ID');return 'left_of';}
<NOTE>"right of" { this.popState();this.pushState('NOTE_ID');return 'right_of';}
<NOTE>\" { this.popState();this.pushState('FLOATING_NOTE');}
<FLOATING_NOTE>\s*"as"\s* {this.popState();this.pushState('FLOATING_NOTE_ID');return "AS";}
<NOTE>"left of" { this.popState(); this.pushState('NOTE_ID'); return 'left_of'; }
<NOTE>"right of" { this.popState(); this.pushState('NOTE_ID'); return 'right_of'; }
<NOTE>\" { this.popState(); this.pushState('FLOATING_NOTE'); }
<FLOATING_NOTE>\s*"as"\s* { this.popState(); this.pushState('FLOATING_NOTE_ID'); return "AS"; }
<FLOATING_NOTE>["] /**/
<FLOATING_NOTE>[^"]* { /*console.log('Floating note text: ', yytext);*/return "NOTE_TEXT";}
<FLOATING_NOTE_ID>[^\n]* {this.popState();/*console.log('Floating note ID', yytext);*/return "ID";}
<NOTE_ID>\s*[^:\n\s\-]+ { this.popState();this.pushState('NOTE_TEXT');/*console.log('Got ID for note', yytext);*/return 'ID';}
<NOTE_TEXT>\s*":"[^:\n;]+ { this.popState();/*console.log('Got NOTE_TEXT for note',yytext);*/yytext = yytext.substr(2).trim();return 'NOTE_TEXT';}
<NOTE_TEXT>[\s\S]*?"end note" { this.popState();/*console.log('Got NOTE_TEXT for note',yytext);*/yytext = yytext.slice(0,-8).trim();return 'NOTE_TEXT';}
<FLOATING_NOTE>[^"]* { /* console.log('Floating note text: ', yytext); */ return "NOTE_TEXT"; }
<FLOATING_NOTE_ID>[^\n]* { this.popState(); /* console.log('Floating note ID', yytext);*/ return "ID"; }
<NOTE_ID>\s*[^:\n\s\-]+ { this.popState(); this.pushState('NOTE_TEXT'); /*console.log('Got ID for note', yytext);*/ return 'ID'; }
<NOTE_TEXT>\s*":"[^:\n;]+ { this.popState(); /* console.log('Got NOTE_TEXT for note',yytext);*/yytext = yytext.substr(2).trim(); return 'NOTE_TEXT'; }
<NOTE_TEXT>[\s\S]*?"end note" { this.popState(); /* console.log('Got NOTE_TEXT for note',yytext);*/yytext = yytext.slice(0,-8).trim(); return 'NOTE_TEXT'; }
"stateDiagram"\s+ { /*console.log('Got state diagram', yytext,'#');*/return 'SD'; }
"stateDiagram-v2"\s+ { /*console.log('Got state diagram', yytext,'#');*/return 'SD'; }
"hide empty description" { /*console.log('HIDE_EMPTY', yytext,'#');*/return 'HIDE_EMPTY'; }
<INITIAL,struct>"[*]" { /*console.log('EDGE_STATE=',yytext);*/ return 'EDGE_STATE';}
<INITIAL,struct>[^:\n\s\-\{]+ { /*console.log('=>ID=',yytext);*/ return 'ID';}
// <INITIAL,struct>\s*":"[^\+\->:\n;]+ { yytext = yytext.trim(); /*console.log('Descr = ', yytext);*/ return 'DESCR'; }
<INITIAL,struct>\s*":"[^:\n;]+ { yytext = yytext.trim(); /*console.log('Descr = ', yytext);*/ return 'DESCR'; }
"stateDiagram"\s+ { /* console.log('Got state diagram', yytext,'#'); */ return 'SD'; }
"stateDiagram-v2"\s+ { /* console.log('Got state diagram', yytext,'#'); */ return 'SD'; }
"hide empty description" { /* console.log('HIDE_EMPTY', yytext,'#'); */ return 'HIDE_EMPTY'; }
<INITIAL,struct>"[*]" { /* console.log('EDGE_STATE=',yytext); */ return 'EDGE_STATE'; }
<INITIAL,struct>[^:\n\s\-\{]+ { /* console.log('=>ID=',yytext); */ return 'ID'; }
// <INITIAL,struct>\s*":"[^\+\->:\n;]+ { yytext = yytext.trim(); /* console.log('Descr = ', yytext); */ return 'DESCR'; }
<INITIAL,struct>\s*":"[^:\n;]+ { yytext = yytext.trim(); /* console.log('Descr = ', yytext); */ return 'DESCR'; }
<INITIAL,struct>"-->" return '-->';
<struct>"--" return 'CONCURRENT';
@@ -201,7 +204,7 @@ statement
| COMPOSIT_STATE
| COMPOSIT_STATE STRUCT_START document STRUCT_STOP
{
/* console.log('Adding document for state without id ', $1); */
// console.log('Adding document for state without id ', $1);
$$={ stmt: 'state', id: $1, type: 'default', description: '', doc: $3 }
}
| STATE_DESCR AS ID {
@@ -217,7 +220,7 @@ statement
}
| STATE_DESCR AS ID STRUCT_START document STRUCT_STOP
{
/* console.log('Adding document for state with id zxzx', $3, $4, yy.getDirection()); yy.addDocument($3);*/
// console.log('state with id ', $3,' document = ', $5, );
$$={ stmt: 'state', id: $3, type: 'default', description: $1, doc: $5 }
}
| FORK {

View File

@@ -94,9 +94,14 @@ const docTranslator = (parent, node, first) => {
docTranslator(parent, node.state1, true);
docTranslator(parent, node.state2, false);
} else {
if (node.stmt === STMT_STATE && node.id === '[*]') {
if (node.stmt === STMT_STATE) {
if (node.id === '[*]') {
node.id = first ? parent.id + '_start' : parent.id + '_end';
node.start = first;
} else {
// This is just a plain state, not a start or end
node.id = node.id.trim();
}
}
if (node.doc) {
@@ -170,7 +175,7 @@ const extract = (_doc) => {
switch (item.stmt) {
case STMT_STATE:
addState(
item.id,
item.id.trim(),
item.type,
item.doc,
item.description,
@@ -184,10 +189,10 @@ const extract = (_doc) => {
addRelation(item.state1, item.state2, item.description);
break;
case STMT_CLASSDEF:
addStyleClass(item.id, item.classes);
addStyleClass(item.id.trim(), item.classes);
break;
case STMT_APPLYCLASS:
setCssClass(item.id, item.styleClass);
setCssClass(item.id.trim(), item.styleClass);
break;
}
});
@@ -215,11 +220,12 @@ export const addState = function (
styles = null,
textStyles = null
) {
const trimmedId = id?.trim();
// add the state if needed
if (currentDocument.states[id] === undefined) {
log.info('Adding state ', id, descr);
currentDocument.states[id] = {
id: id,
if (currentDocument.states[trimmedId] === undefined) {
log.info('Adding state ', trimmedId, descr);
currentDocument.states[trimmedId] = {
id: trimmedId,
descriptions: [],
type,
doc,
@@ -229,49 +235,49 @@ export const addState = function (
textStyles: [],
};
} else {
if (!currentDocument.states[id].doc) {
currentDocument.states[id].doc = doc;
if (!currentDocument.states[trimmedId].doc) {
currentDocument.states[trimmedId].doc = doc;
}
if (!currentDocument.states[id].type) {
currentDocument.states[id].type = type;
if (!currentDocument.states[trimmedId].type) {
currentDocument.states[trimmedId].type = type;
}
}
if (descr) {
log.info('Setting state description', id, descr);
log.info('Setting state description', trimmedId, descr);
if (typeof descr === 'string') {
addDescription(id, descr.trim());
addDescription(trimmedId, descr.trim());
}
if (typeof descr === 'object') {
descr.forEach((des) => addDescription(id, des.trim()));
descr.forEach((des) => addDescription(trimmedId, des.trim()));
}
}
if (note) {
currentDocument.states[id].note = note;
currentDocument.states[id].note.text = common.sanitizeText(
currentDocument.states[id].note.text,
currentDocument.states[trimmedId].note = note;
currentDocument.states[trimmedId].note.text = common.sanitizeText(
currentDocument.states[trimmedId].note.text,
configApi.getConfig()
);
}
if (classes) {
log.info('Setting state classes', id, classes);
log.info('Setting state classes', trimmedId, classes);
const classesList = typeof classes === 'string' ? [classes] : classes;
classesList.forEach((klass) => setCssClass(id, klass.trim()));
classesList.forEach((klass) => setCssClass(trimmedId, klass.trim()));
}
if (styles) {
log.info('Setting state styles', id, styles);
log.info('Setting state styles', trimmedId, styles);
const stylesList = typeof styles === 'string' ? [styles] : styles;
stylesList.forEach((style) => setStyle(id, style.trim()));
stylesList.forEach((style) => setStyle(trimmedId, style.trim()));
}
if (textStyles) {
log.info('Setting state styles', id, styles);
log.info('Setting state styles', trimmedId, styles);
const textStylesList = typeof textStyles === 'string' ? [textStyles] : textStyles;
textStylesList.forEach((textStyle) => setTextStyle(id, textStyle.trim()));
textStylesList.forEach((textStyle) => setTextStyle(trimmedId, textStyle.trim()));
}
};
@@ -368,10 +374,10 @@ function endTypeIfNeeded(id = '', type = DEFAULT_STATE_TYPE) {
* @param relationTitle
*/
export function addRelationObjs(item1, item2, relationTitle) {
let id1 = startIdIfNeeded(item1.id);
let type1 = startTypeIfNeeded(item1.id, item1.type);
let id2 = startIdIfNeeded(item2.id);
let type2 = startTypeIfNeeded(item2.id, item2.type);
let id1 = startIdIfNeeded(item1.id.trim());
let type1 = startTypeIfNeeded(item1.id.trim(), item1.type);
let id2 = startIdIfNeeded(item2.id.trim());
let type2 = startTypeIfNeeded(item2.id.trim(), item2.type);
addState(
id1,
@@ -412,9 +418,9 @@ export const addRelation = function (item1, item2, title) {
if (typeof item1 === 'object') {
addRelationObjs(item1, item2, title);
} else {
const id1 = startIdIfNeeded(item1);
const id1 = startIdIfNeeded(item1.trim());
const type1 = startTypeIfNeeded(item1);
const id2 = endIdIfNeeded(item2);
const id2 = endIdIfNeeded(item2.trim());
const type2 = endTypeIfNeeded(item2);
addState(id1, type1);

View File

@@ -307,8 +307,8 @@ const setupNode = (g, parent, parsedItem, diagramStates, diagramDb, altFlag) =>
*
* @param g
* @param parentParsedItem - parsed Item that is the parent of this document (doc)
* @param doc - the document to set up
* @param {object} diagramStates - the list of all known states for the diagram
* @param doc - the document to set up; it is a list of parsed statements
* @param {object[]} diagramStates - the list of all known states for the diagram
* @param diagramDb
* @param {boolean} altFlag
* @todo This duplicates some of what is done in stateDb.js extract method

View File

@@ -37,16 +37,14 @@ import { JSDOM } from 'jsdom';
import type { Code, Root } from 'mdast';
import { posix, dirname, relative, join } from 'path';
import prettier from 'prettier';
import { remark as remarkBuilder } from 'remark';
import { remark } from 'remark';
import remarkFrontmatter from 'remark-frontmatter';
import remarkGfm from 'remark-gfm';
import chokidar from 'chokidar';
import mm from 'micromatch';
// @ts-ignore No typescript declaration file
import flatmap from 'unist-util-flatmap';
// support tables and other GitHub Flavored Markdown syntax in markdown
const remark = remarkBuilder().use(remarkGfm);
const MERMAID_MAJOR_VERSION = (
JSON.parse(readFileSync('../mermaid/package.json', 'utf8')).version as string
).split('.')[0];
@@ -201,22 +199,40 @@ const transformIncludeStatements = (file: string, text: string): string => {
});
};
/** Options for {@link transformMarkdownAst} */
interface TransformMarkdownAstOptions {
/**
* Used to indicate the original/source file.
*/
originalFilename: string;
/** If `true`, add a warning that the file is autogenerated */
addAutogeneratedWarning?: boolean;
/**
* If `true`, remove the YAML metadata from the Markdown input.
* Generally, YAML metadata is only used for Vitepress.
*/
removeYAML?: boolean;
}
/**
* Transform code blocks in a Markdown file.
* Use remark.parse() to turn the given content (a String) into an AST.
* Remark plugin that transforms mermaid repo markdown to Vitepress/GFM markdown.
*
* For any AST node that is a code block: transform it as needed:
* - blocks marked as MERMAID_DIAGRAM_ONLY will be set to a 'mermaid' code block so it will be rendered as (only) a diagram
* - blocks marked as MERMAID_EXAMPLE_KEYWORDS will be copied and the original node will be a code only block and the copy with be rendered as the diagram
* - blocks marked as BLOCK_QUOTE_KEYWORDS will be transformed into block quotes
*
* Convert the AST back to a string and return it.
* If `addAutogeneratedWarning` is `true`, generates a header stating that this file is autogenerated.
*
* @param content - the contents of a Markdown file
* @returns the contents with transformed code blocks
* @returns plugin function for Remark
*/
export const transformBlocks = (content: string): string => {
const ast: Root = remark.parse(content);
const astWithTransformedBlocks = flatmap(ast, (node: Code) => {
export function transformMarkdownAst({
originalFilename,
addAutogeneratedWarning,
removeYAML,
}: TransformMarkdownAstOptions) {
return (tree: Root, _file?: any): Root => {
const astWithTransformedBlocks = flatmap(tree, (node: Code) => {
if (node.type !== 'code' || !node.lang) {
return [node]; // no transformation if this is not a code block
}
@@ -239,10 +255,30 @@ export const transformBlocks = (content: string): string => {
}
return [node]; // default is to do nothing to the node
});
}) as Root;
return remark.stringify(astWithTransformedBlocks);
};
if (addAutogeneratedWarning) {
// Add the header to the start of the file
const headerNode = remark.parse(generateHeader(originalFilename)).children[0];
if (astWithTransformedBlocks.children[0].type === 'yaml') {
// insert header after the YAML frontmatter if it exists
astWithTransformedBlocks.children.splice(1, 0, headerNode);
} else {
astWithTransformedBlocks.children.unshift(headerNode);
}
}
if (removeYAML) {
const firstNode = astWithTransformedBlocks.children[0];
if (firstNode.type == 'yaml') {
// YAML is currently only used for Vitepress metadata, so we should remove it for GFM output
astWithTransformedBlocks.children.shift();
}
}
return astWithTransformedBlocks;
};
}
/**
* Transform a markdown file and write the transformed file to the directory for published
@@ -260,11 +296,18 @@ export const transformBlocks = (content: string): string => {
*/
const transformMarkdown = (file: string) => {
const doc = injectPlaceholders(transformIncludeStatements(file, readSyncedUTF8file(file)));
let transformed = transformBlocks(doc);
if (!noHeader) {
// Add the header to the start of the file
transformed = `${generateHeader(file)}\n${transformed}`;
}
let transformed = remark()
.use(remarkGfm)
.use(remarkFrontmatter, ['yaml']) // support YAML front-matter in Markdown
.use(transformMarkdownAst, {
// mermaid project specific plugin
originalFilename: file,
addAutogeneratedWarning: !noHeader,
removeYAML: !noHeader,
})
.processSync(doc)
.toString();
if (vitepress && file === 'src/docs/index.md') {
// Skip transforming index if vitepress is enabled
@@ -331,7 +374,7 @@ const getFilesFromGlobs = async (globs: string[]): Promise<string[]> => {
};
/** Main method (entry point) */
(async () => {
const main = async () => {
if (verifyOnly) {
console.log('Verifying that all files are in sync with the source files');
}
@@ -400,4 +443,6 @@ const getFilesFromGlobs = async (globs: string[]): Promise<string[]> => {
}
});
}
})();
};
void main();

View File

@@ -1,40 +1,24 @@
import { transformBlocks, transformToBlockQuote } from './docs.mjs';
import { remark as remarkBuilder } from 'remark'; // import it this way so we can mock it
import { transformMarkdownAst, transformToBlockQuote } from './docs.mjs';
import { remark } from 'remark'; // import it this way so we can mock it
import remarkFrontmatter from 'remark-frontmatter';
import { vi, afterEach, describe, it, expect } from 'vitest';
const remark = remarkBuilder();
vi.mock('remark', async (importOriginal) => {
const { remark: originalRemarkBuilder } = (await importOriginal()) as {
remark: typeof remarkBuilder;
};
// make sure that both `docs.mts` and this test file are using the same remark
// object so that we can mock it
const sharedRemark = originalRemarkBuilder();
return {
remark: () => sharedRemark,
};
});
afterEach(() => {
vi.restoreAllMocks();
});
const originalFilename = 'example-input-filename.md';
const remarkBuilder = remark().use(remarkFrontmatter, ['yaml']); // support YAML front-matter in Markdown
describe('docs.mts', () => {
describe('transformBlocks', () => {
it('uses remark.parse to create the AST for the file ', () => {
const remarkParseSpy = vi
.spyOn(remark, 'parse')
.mockReturnValue({ type: 'root', children: [] });
const contents = 'Markdown file contents';
transformBlocks(contents);
expect(remarkParseSpy).toHaveBeenCalledWith(contents);
});
describe('transformMarkdownAst', () => {
describe('checks each AST node', () => {
it('does no transformation if there are no code blocks', async () => {
const contents = 'Markdown file contents\n';
const result = transformBlocks(contents);
const result = (
await remarkBuilder().use(transformMarkdownAst, { originalFilename }).process(contents)
).toString();
expect(result).toEqual(contents);
});
@@ -46,8 +30,12 @@ describe('docs.mts', () => {
const lang_keyword = 'mermaid-nocode';
const contents = beforeCodeLine + '```' + lang_keyword + '\n' + diagram_text + '\n```\n';
it('changes the language to "mermaid"', () => {
const result = transformBlocks(contents);
it('changes the language to "mermaid"', async () => {
const result = (
await remarkBuilder()
.use(transformMarkdownAst, { originalFilename })
.process(contents)
).toString();
expect(result).toEqual(
beforeCodeLine + '\n' + '```' + 'mermaid' + '\n' + diagram_text + '\n```\n'
);
@@ -61,8 +49,12 @@ describe('docs.mts', () => {
const contents =
beforeCodeLine + '```' + lang_keyword + '\n' + diagram_text + '\n```\n';
it('changes the language to "mermaid-example" and adds a copy of the code block with language = "mermaid"', () => {
const result = transformBlocks(contents);
it('changes the language to "mermaid-example" and adds a copy of the code block with language = "mermaid"', async () => {
const result = (
await remarkBuilder()
.use(transformMarkdownAst, { originalFilename })
.process(contents)
).toString();
expect(result).toEqual(
beforeCodeLine +
'\n' +
@@ -77,16 +69,40 @@ describe('docs.mts', () => {
});
});
it('calls transformToBlockQuote with the node information', () => {
it('calls transformToBlockQuote with the node information', async () => {
const lang_keyword = 'note';
const contents =
beforeCodeLine + '```' + lang_keyword + '\n' + 'This is the text\n' + '```\n';
const result = transformBlocks(contents);
const result = (
await remarkBuilder().use(transformMarkdownAst, { originalFilename }).process(contents)
).toString();
expect(result).toEqual(beforeCodeLine + '\n> **Note**\n' + '> This is the text\n');
});
});
});
it('should remove YAML if `removeYAML` is true', async () => {
const contents = `---
title: Flowcharts Syntax
---
This Markdown should be kept.
`;
const withYaml = (
await remarkBuilder().use(transformMarkdownAst, { originalFilename }).process(contents)
).toString();
// no change
expect(withYaml).toEqual(contents);
const withoutYaml = (
await remarkBuilder()
.use(transformMarkdownAst, { originalFilename, removeYAML: true })
.process(contents)
).toString();
// no change
expect(withoutYaml).toEqual('This Markdown should be kept.\n');
});
});
describe('transformToBlockQuote', () => {

View File

@@ -89,9 +89,10 @@ They also serve as proof of concept, for the variety of things that can be built
## Editor Plugins
- [Vs Code](https://code.visualstudio.com/)
- [VS Code](https://code.visualstudio.com/)
- [Markdown Preview Mermaid Support](https://marketplace.visualstudio.com/items?itemName=bierner.markdown-mermaid)
- [Mermaid Preview](https://marketplace.visualstudio.com/items?itemName=vstirbu.vscode-mermaid-preview)
- [Markdown Preview Enhanced](https://marketplace.visualstudio.com/items?itemName=shd101wyy.markdown-preview-enhanced)
- [Mermaid Markdown Syntax Highlighting](https://marketplace.visualstudio.com/items?itemName=bpruitt-goddard.mermaid-markdown-syntax-highlighting)
- [Mermaid Editor](https://marketplace.visualstudio.com/items?itemName=tomoyukim.vscode-mermaid-editor)
- [Mermaid Export](https://marketplace.visualstudio.com/items?itemName=Gruntfuggly.mermaid-export)

View File

@@ -146,14 +146,13 @@ The `type` and `name` values must begin with an alphabetic character and may con
#### Attribute Keys and Comments
Attributes may also have a `key` or comment defined. Keys can be "PK", "FK" or "UK", for Primary Key, Foreign Key or Unique Key. And a `comment` is defined by double quotes at the end of an attribute. Comments themselves cannot have double-quote characters in them.
Attributes may also have a `key` or comment defined. Keys can be `PK`, `FK` or `UK`, for Primary Key, Foreign Key or Unique Key. To specify multiple key constraints on a single attribute, separate them with a comma (e.g., `PK, FK`).. A `comment` is defined by double quotes at the end of an attribute. Comments themselves cannot have double-quote characters in them.
```mermaid-example
erDiagram
CAR ||--o{ NAMED-DRIVER : allows
CAR {
string allowedDriver FK "The license of the allowed driver"
string registrationNumber UK
string registrationNumber PK
string make
string model
string[] parts
@@ -163,9 +162,14 @@ erDiagram
string driversLicense PK "The license #"
string(99) firstName "Only 99 characters are allowed"
string lastName
string phone UK
int age
}
MANUFACTURER only one to zero or more CAR
NAMED-DRIVER {
string carRegistrationNumber PK, FK
string driverLicence PK, FK
}
MANUFACTURER only one to zero or more CAR : makes
```
### Other Things

View File

@@ -1,3 +1,8 @@
---
title: Flowcharts Syntax
outline: 'deep' # shows all h3 headings in outline in Vitepress
---
# Flowcharts - Basic Syntax
All Flowcharts are composed of **nodes**, the geometric shapes and **edges**, the arrows or lines. The mermaid code defines the way that these **nodes** and **edges** are made and interact.

View File

@@ -17,7 +17,6 @@ import { ExternalDiagramDefinition } from './diagram-api/types';
export type { MermaidConfig, DetailedError, ExternalDiagramDefinition, ParseErrorFunction };
let externalDiagramsRegistered = false;
/**
* ## init
*
@@ -51,12 +50,7 @@ const init = async function (
callback?: Function
) {
try {
// Not really sure if we need to check this, or simply call initThrowsErrorsAsync directly.
if (externalDiagramsRegistered) {
await initThrowsErrorsAsync(config, nodes, callback);
} else {
initThrowsErrors(config, nodes, callback);
}
} catch (e) {
log.warn('Syntax Error rendering');
if (isDetailedError(e)) {
@@ -68,8 +62,7 @@ const init = async function (
}
};
// eslint-disable-next-line @typescript-eslint/ban-types
const handleError = (error: unknown, errors: DetailedError[], parseError?: Function) => {
const handleError = (error: unknown, errors: DetailedError[], parseError?: ParseErrorFunction) => {
log.warn(error);
if (isDetailedError(error)) {
// handle case where error string and hash were
@@ -225,7 +218,6 @@ const loadExternalDiagrams = async (...diagrams: ExternalDiagramDefinition[]) =>
*/
const initThrowsErrorsAsync = async function (
config?: MermaidConfig,
// eslint-disable-next-line no-undef
nodes?: string | HTMLElement | NodeListOf<HTMLElement>,
// eslint-disable-next-line @typescript-eslint/ban-types
callback?: Function
@@ -336,7 +328,6 @@ const registerExternalDiagrams = async (
} else {
await loadExternalDiagrams(...diagrams);
}
externalDiagramsRegistered = true;
};
/**
@@ -348,7 +339,7 @@ const contentLoaded = function () {
if (mermaid.startOnLoad) {
const { startOnLoad } = mermaidAPI.getConfig();
if (startOnLoad) {
mermaid.init();
mermaid.init().catch((err) => log.error('Mermaid failed to initialize', err));
}
}
};
@@ -427,7 +418,7 @@ const parseAsync = (txt: string): Promise<boolean> => {
);
});
executionQueue.push(performCall);
executeQueue();
executeQueue().catch(reject);
});
};
@@ -460,7 +451,7 @@ const renderAsync = (
);
});
executionQueue.push(performCall);
executeQueue();
executeQueue().catch(reject);
});
};

313
pnpm-lock.yaml generated
View File

@@ -44,11 +44,11 @@ importers:
specifier: ^4.2.1
version: 4.2.1
'@typescript-eslint/eslint-plugin':
specifier: ^5.42.1
version: 5.42.1_2udltptbznfmezdozpdoa2aemq
specifier: ^5.48.2
version: 5.48.2_iljmjqxcygjq3saipl7gerxpvi
'@typescript-eslint/parser':
specifier: ^5.42.1
version: 5.42.1_rmayb2veg2btbq6mbmnyivgasy
specifier: ^5.48.2
version: 5.48.2_yygwinqv3a2io74xmwofqb7uka
'@vitest/coverage-c8':
specifier: ^0.27.0
version: 0.27.1_6vhkb7zox2ro6wmx3rlvm5i5ce
@@ -71,32 +71,32 @@ importers:
specifier: ^0.17.0
version: 0.17.0
eslint:
specifier: ^8.27.0
version: 8.27.0
specifier: ^8.32.0
version: 8.32.0
eslint-config-prettier:
specifier: ^8.5.0
version: 8.5.0_eslint@8.27.0
specifier: ^8.6.0
version: 8.6.0_eslint@8.32.0
eslint-plugin-cypress:
specifier: ^2.12.1
version: 2.12.1_eslint@8.27.0
version: 2.12.1_eslint@8.32.0
eslint-plugin-html:
specifier: ^7.1.0
version: 7.1.0
eslint-plugin-jest:
specifier: ^27.1.5
version: 27.1.5_kdswgjmqcx7mthqz7ow2zlfevy
version: 27.1.5_5rcd23qw3h5vuffwo2owxb3hw4
eslint-plugin-jsdoc:
specifier: ^39.6.2
version: 39.6.2_eslint@8.27.0
version: 39.6.2_eslint@8.32.0
eslint-plugin-json:
specifier: ^3.1.0
version: 3.1.0
eslint-plugin-lodash:
specifier: ^7.4.0
version: 7.4.0_eslint@8.27.0
version: 7.4.0_eslint@8.32.0
eslint-plugin-markdown:
specifier: ^3.0.0
version: 3.0.0_eslint@8.27.0
version: 3.0.0_eslint@8.32.0
eslint-plugin-no-only-tests:
specifier: ^3.1.0
version: 3.1.0
@@ -105,7 +105,7 @@ importers:
version: 0.2.17
eslint-plugin-unicorn:
specifier: ^45.0.0
version: 45.0.0_eslint@8.27.0
version: 45.0.0_eslint@8.32.0
express:
specifier: ^4.18.2
version: 4.18.2
@@ -229,10 +229,10 @@ importers:
version: 8.3.4
'@typescript-eslint/eslint-plugin':
specifier: ^5.42.1
version: 5.42.1_2udltptbznfmezdozpdoa2aemq
version: 5.42.1_qxgr6oy2qtsmmpo3f6iejuryuq
'@typescript-eslint/parser':
specifier: ^5.42.1
version: 5.42.1_rmayb2veg2btbq6mbmnyivgasy
version: 5.42.1_yygwinqv3a2io74xmwofqb7uka
chokidar:
specifier: ^3.5.3
version: 3.5.3
@@ -275,6 +275,9 @@ importers:
remark:
specifier: ^14.0.2
version: 14.0.2
remark-frontmatter:
specifier: ^4.0.1
version: 4.0.1
remark-gfm:
specifier: ^3.0.1
version: 3.0.1
@@ -336,6 +339,9 @@ importers:
specifier: ^2.0.2
version: 2.0.2
devDependencies:
'@types/cytoscape':
specifier: ^3.19.9
version: 3.19.9
concurrently:
specifier: ^7.5.0
version: 7.5.0
@@ -2144,14 +2150,14 @@ packages:
dev: true
optional: true
/@eslint/eslintrc/1.3.3:
resolution: {integrity: sha512-uj3pT6Mg+3t39fvLrj8iuCIJ38zKO9FpGtJ4BBJebJhEwjoT+KLVNCcHT5QC9NGRIEi7fZ0ZR8YRb884auB4Lg==}
/@eslint/eslintrc/1.4.1:
resolution: {integrity: sha512-XXrH9Uarn0stsyldqDYq8r++mROmWRI1xKMXa640Bb//SY1+ECYX6VzT6Lcx5frD0V30XieqJ0oX9I2Xj5aoMA==}
engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0}
dependencies:
ajv: 6.12.6
debug: 4.3.4
espree: 9.4.0
globals: 13.17.0
globals: 13.19.0
ignore: 5.2.0
import-fresh: 3.3.0
js-yaml: 4.1.0
@@ -2171,8 +2177,8 @@ packages:
'@hapi/hoek': 9.3.0
dev: true
/@humanwhocodes/config-array/0.11.7:
resolution: {integrity: sha512-kBbPWzN8oVMLb0hOUYXhmxggL/1cJE6ydvjDIGi9EnAGUyA7cLVKQg+d/Dsm+KZwx2czGHrCmMVLiyg8s5JPKw==}
/@humanwhocodes/config-array/0.11.8:
resolution: {integrity: sha512-UybHIJzJnR5Qc/MsD9Kr+RpO2h+/P1GhOwdiLPXK5TWk5sgTdu88bTD9UP+CKbPPh5Rni1u0GjAdYQLemG8g+g==}
engines: {node: '>=10.10.0'}
dependencies:
'@humanwhocodes/object-schema': 1.2.1
@@ -2656,6 +2662,10 @@ packages:
'@types/node': 18.11.9
dev: true
/@types/cytoscape/3.19.9:
resolution: {integrity: sha512-oqCx0ZGiBO0UESbjgq052vjDAy2X53lZpMrWqiweMpvVwKw/2IiYDdzPFK6+f4tMfdv9YKEM9raO5bAZc3UYBg==}
dev: true
/@types/d3-array/3.0.3:
resolution: {integrity: sha512-Reoy+pKnvsksN0lQUlcH6dOGjRZ/3WRwXR//m+/8lt1BXeI4xyaUZoqULNjyXXRuh0Mj4LNpkCvhUpQlY3X5xQ==}
dev: true
@@ -3136,7 +3146,7 @@ packages:
dev: true
optional: true
/@typescript-eslint/eslint-plugin/5.42.1_2udltptbznfmezdozpdoa2aemq:
/@typescript-eslint/eslint-plugin/5.42.1_qxgr6oy2qtsmmpo3f6iejuryuq:
resolution: {integrity: sha512-LyR6x784JCiJ1j6sH5Y0K6cdExqCCm8DJUTcwG5ThNXJj/G8o5E56u5EdG4SLy+bZAwZBswC+GYn3eGdttBVCg==}
engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0}
peerDependencies:
@@ -3147,12 +3157,12 @@ packages:
typescript:
optional: true
dependencies:
'@typescript-eslint/parser': 5.42.1_rmayb2veg2btbq6mbmnyivgasy
'@typescript-eslint/parser': 5.42.1_yygwinqv3a2io74xmwofqb7uka
'@typescript-eslint/scope-manager': 5.42.1
'@typescript-eslint/type-utils': 5.42.1_rmayb2veg2btbq6mbmnyivgasy
'@typescript-eslint/utils': 5.42.1_rmayb2veg2btbq6mbmnyivgasy
'@typescript-eslint/type-utils': 5.42.1_yygwinqv3a2io74xmwofqb7uka
'@typescript-eslint/utils': 5.42.1_yygwinqv3a2io74xmwofqb7uka
debug: 4.3.4
eslint: 8.27.0
eslint: 8.32.0
ignore: 5.2.0
natural-compare-lite: 1.4.0
regexpp: 3.2.0
@@ -3163,7 +3173,34 @@ packages:
- supports-color
dev: true
/@typescript-eslint/parser/5.42.1_rmayb2veg2btbq6mbmnyivgasy:
/@typescript-eslint/eslint-plugin/5.48.2_iljmjqxcygjq3saipl7gerxpvi:
resolution: {integrity: sha512-sR0Gja9Ky1teIq4qJOl0nC+Tk64/uYdX+mi+5iB//MH8gwyx8e3SOyhEzeLZEFEEfCaLf8KJq+Bd/6je1t+CAg==}
engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0}
peerDependencies:
'@typescript-eslint/parser': ^5.0.0
eslint: ^6.0.0 || ^7.0.0 || ^8.0.0
typescript: '*'
peerDependenciesMeta:
typescript:
optional: true
dependencies:
'@typescript-eslint/parser': 5.48.2_yygwinqv3a2io74xmwofqb7uka
'@typescript-eslint/scope-manager': 5.48.2
'@typescript-eslint/type-utils': 5.48.2_yygwinqv3a2io74xmwofqb7uka
'@typescript-eslint/utils': 5.48.2_yygwinqv3a2io74xmwofqb7uka
debug: 4.3.4
eslint: 8.32.0
ignore: 5.2.0
natural-compare-lite: 1.4.0
regexpp: 3.2.0
semver: 7.3.8
tsutils: 3.21.0_typescript@4.8.4
typescript: 4.8.4
transitivePeerDependencies:
- supports-color
dev: true
/@typescript-eslint/parser/5.42.1_yygwinqv3a2io74xmwofqb7uka:
resolution: {integrity: sha512-kAV+NiNBWVQDY9gDJDToTE/NO8BHi4f6b7zTsVAJoTkmB/zlfOpiEVBzHOKtlgTndCKe8vj9F/PuolemZSh50Q==}
engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0}
peerDependencies:
@@ -3177,7 +3214,27 @@ packages:
'@typescript-eslint/types': 5.42.1
'@typescript-eslint/typescript-estree': 5.42.1_typescript@4.8.4
debug: 4.3.4
eslint: 8.27.0
eslint: 8.32.0
typescript: 4.8.4
transitivePeerDependencies:
- supports-color
dev: true
/@typescript-eslint/parser/5.48.2_yygwinqv3a2io74xmwofqb7uka:
resolution: {integrity: sha512-38zMsKsG2sIuM5Oi/olurGwYJXzmtdsHhn5mI/pQogP+BjYVkK5iRazCQ8RGS0V+YLk282uWElN70zAAUmaYHw==}
engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0}
peerDependencies:
eslint: ^6.0.0 || ^7.0.0 || ^8.0.0
typescript: '*'
peerDependenciesMeta:
typescript:
optional: true
dependencies:
'@typescript-eslint/scope-manager': 5.48.2
'@typescript-eslint/types': 5.48.2
'@typescript-eslint/typescript-estree': 5.48.2_typescript@4.8.4
debug: 4.3.4
eslint: 8.32.0
typescript: 4.8.4
transitivePeerDependencies:
- supports-color
@@ -3191,7 +3248,15 @@ packages:
'@typescript-eslint/visitor-keys': 5.42.1
dev: true
/@typescript-eslint/type-utils/5.42.1_rmayb2veg2btbq6mbmnyivgasy:
/@typescript-eslint/scope-manager/5.48.2:
resolution: {integrity: sha512-zEUFfonQid5KRDKoI3O+uP1GnrFd4tIHlvs+sTJXiWuypUWMuDaottkJuR612wQfOkjYbsaskSIURV9xo4f+Fw==}
engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0}
dependencies:
'@typescript-eslint/types': 5.48.2
'@typescript-eslint/visitor-keys': 5.48.2
dev: true
/@typescript-eslint/type-utils/5.42.1_yygwinqv3a2io74xmwofqb7uka:
resolution: {integrity: sha512-WWiMChneex5w4xPIX56SSnQQo0tEOy5ZV2dqmj8Z371LJ0E+aymWD25JQ/l4FOuuX+Q49A7pzh/CGIQflxMVXg==}
engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0}
peerDependencies:
@@ -3202,9 +3267,29 @@ packages:
optional: true
dependencies:
'@typescript-eslint/typescript-estree': 5.42.1_typescript@4.8.4
'@typescript-eslint/utils': 5.42.1_rmayb2veg2btbq6mbmnyivgasy
'@typescript-eslint/utils': 5.42.1_yygwinqv3a2io74xmwofqb7uka
debug: 4.3.4
eslint: 8.27.0
eslint: 8.32.0
tsutils: 3.21.0_typescript@4.8.4
typescript: 4.8.4
transitivePeerDependencies:
- supports-color
dev: true
/@typescript-eslint/type-utils/5.48.2_yygwinqv3a2io74xmwofqb7uka:
resolution: {integrity: sha512-QVWx7J5sPMRiOMJp5dYshPxABRoZV1xbRirqSk8yuIIsu0nvMTZesKErEA3Oix1k+uvsk8Cs8TGJ6kQ0ndAcew==}
engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0}
peerDependencies:
eslint: '*'
typescript: '*'
peerDependenciesMeta:
typescript:
optional: true
dependencies:
'@typescript-eslint/typescript-estree': 5.48.2_typescript@4.8.4
'@typescript-eslint/utils': 5.48.2_yygwinqv3a2io74xmwofqb7uka
debug: 4.3.4
eslint: 8.32.0
tsutils: 3.21.0_typescript@4.8.4
typescript: 4.8.4
transitivePeerDependencies:
@@ -3216,6 +3301,11 @@ packages:
engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0}
dev: true
/@typescript-eslint/types/5.48.2:
resolution: {integrity: sha512-hE7dA77xxu7ByBc6KCzikgfRyBCTst6dZQpwaTy25iMYOnbNljDT4hjhrGEJJ0QoMjrfqrx+j1l1B9/LtKeuqA==}
engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0}
dev: true
/@typescript-eslint/typescript-estree/5.42.1_typescript@4.8.4:
resolution: {integrity: sha512-qElc0bDOuO0B8wDhhW4mYVgi/LZL+igPwXtV87n69/kYC/7NG3MES0jHxJNCr4EP7kY1XVsRy8C/u3DYeTKQmw==}
engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0}
@@ -3237,7 +3327,28 @@ packages:
- supports-color
dev: true
/@typescript-eslint/utils/5.42.1_rmayb2veg2btbq6mbmnyivgasy:
/@typescript-eslint/typescript-estree/5.48.2_typescript@4.8.4:
resolution: {integrity: sha512-bibvD3z6ilnoVxUBFEgkO0k0aFvUc4Cttt0dAreEr+nrAHhWzkO83PEVVuieK3DqcgL6VAK5dkzK8XUVja5Zcg==}
engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0}
peerDependencies:
typescript: '*'
peerDependenciesMeta:
typescript:
optional: true
dependencies:
'@typescript-eslint/types': 5.48.2
'@typescript-eslint/visitor-keys': 5.48.2
debug: 4.3.4
globby: 11.1.0
is-glob: 4.0.3
semver: 7.3.8
tsutils: 3.21.0_typescript@4.8.4
typescript: 4.8.4
transitivePeerDependencies:
- supports-color
dev: true
/@typescript-eslint/utils/5.42.1_yygwinqv3a2io74xmwofqb7uka:
resolution: {integrity: sha512-Gxvf12xSp3iYZd/fLqiQRD4uKZjDNR01bQ+j8zvhPjpsZ4HmvEFL/tC4amGNyxN9Rq+iqvpHLhlqx6KTxz9ZyQ==}
engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0}
peerDependencies:
@@ -3248,9 +3359,29 @@ packages:
'@typescript-eslint/scope-manager': 5.42.1
'@typescript-eslint/types': 5.42.1
'@typescript-eslint/typescript-estree': 5.42.1_typescript@4.8.4
eslint: 8.27.0
eslint: 8.32.0
eslint-scope: 5.1.1
eslint-utils: 3.0.0_eslint@8.27.0
eslint-utils: 3.0.0_eslint@8.32.0
semver: 7.3.8
transitivePeerDependencies:
- supports-color
- typescript
dev: true
/@typescript-eslint/utils/5.48.2_yygwinqv3a2io74xmwofqb7uka:
resolution: {integrity: sha512-2h18c0d7jgkw6tdKTlNaM7wyopbLRBiit8oAxoP89YnuBOzCZ8g8aBCaCqq7h208qUTroL7Whgzam7UY3HVLow==}
engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0}
peerDependencies:
eslint: ^6.0.0 || ^7.0.0 || ^8.0.0
dependencies:
'@types/json-schema': 7.0.11
'@types/semver': 7.3.12
'@typescript-eslint/scope-manager': 5.48.2
'@typescript-eslint/types': 5.48.2
'@typescript-eslint/typescript-estree': 5.48.2_typescript@4.8.4
eslint: 8.32.0
eslint-scope: 5.1.1
eslint-utils: 3.0.0_eslint@8.32.0
semver: 7.3.8
transitivePeerDependencies:
- supports-color
@@ -3265,6 +3396,14 @@ packages:
eslint-visitor-keys: 3.3.0
dev: true
/@typescript-eslint/visitor-keys/5.48.2:
resolution: {integrity: sha512-z9njZLSkwmjFWUelGEwEbdf4NwKvfHxvGC0OcGN1Hp/XNDIcJ7D5DpPNPv6x6/mFvc1tQHsaWmpD/a4gOvvCJQ==}
engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0}
dependencies:
'@typescript-eslint/types': 5.48.2
eslint-visitor-keys: 3.3.0
dev: true
/@vitejs/plugin-vue/4.0.0_vite@4.0.1+vue@3.2.45:
resolution: {integrity: sha512-e0X4jErIxAB5oLtDqbHvHpJe/uWNkdpYV83AOG2xo2tEVSzCzewgJMtREZM30wXnM5ls90hxiOtAuVU6H5JgbA==}
engines: {node: ^14.18.0 || >=16.0.0}
@@ -3661,12 +3800,12 @@ packages:
acorn: 8.8.0
dev: true
/acorn-jsx/5.3.2_acorn@8.8.0:
/acorn-jsx/5.3.2_acorn@8.8.1:
resolution: {integrity: sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==}
peerDependencies:
acorn: ^6.0.0 || ^7.0.0 || ^8.0.0
dependencies:
acorn: 8.8.0
acorn: 8.8.1
dev: true
/acorn-walk/7.2.0:
@@ -3970,7 +4109,7 @@ packages:
/axios/0.21.4_debug@4.3.2:
resolution: {integrity: sha512-ut5vewkiu8jjGBdqpM44XxjuCjq9LAKeHVmoVfHVzy8eHgxxq8SbAVQNovDA8mVi05kP0Ea/n/UzcSHcTJQfNg==}
dependencies:
follow-redirects: 1.15.2_debug@4.3.2
follow-redirects: 1.15.2
transitivePeerDependencies:
- debug
dev: true
@@ -6168,21 +6307,21 @@ packages:
source-map: 0.6.1
dev: true
/eslint-config-prettier/8.5.0_eslint@8.27.0:
resolution: {integrity: sha512-obmWKLUNCnhtQRKc+tmnYuQl0pFU1ibYJQ5BGhTVB08bHe9wC8qUeG7c08dj9XX+AuPj1YSGSQIHl1pnDHZR0Q==}
/eslint-config-prettier/8.6.0_eslint@8.32.0:
resolution: {integrity: sha512-bAF0eLpLVqP5oEVUFKpMA+NnRFICwn9X8B5jrR9FcqnYBuPbqWEjTEspPWMj5ye6czoSLDweCzSo3Ko7gGrZaA==}
hasBin: true
peerDependencies:
eslint: '>=7.0.0'
dependencies:
eslint: 8.27.0
eslint: 8.32.0
dev: true
/eslint-plugin-cypress/2.12.1_eslint@8.27.0:
/eslint-plugin-cypress/2.12.1_eslint@8.32.0:
resolution: {integrity: sha512-c2W/uPADl5kospNDihgiLc7n87t5XhUbFDoTl6CfVkmG+kDAb5Ux10V9PoLPu9N+r7znpc+iQlcmAqT1A/89HA==}
peerDependencies:
eslint: '>= 3.2.1'
dependencies:
eslint: 8.27.0
eslint: 8.32.0
globals: 11.12.0
dev: true
@@ -6192,7 +6331,7 @@ packages:
htmlparser2: 8.0.1
dev: true
/eslint-plugin-jest/27.1.5_kdswgjmqcx7mthqz7ow2zlfevy:
/eslint-plugin-jest/27.1.5_5rcd23qw3h5vuffwo2owxb3hw4:
resolution: {integrity: sha512-CK2dekZ5VBdzsOSOH5Fc1rwC+cWXjkcyrmf1RV714nDUDKu+o73TTJiDxpbILG8PtPPpAAl3ywzh5QA7Ft0mjA==}
engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0}
peerDependencies:
@@ -6205,16 +6344,16 @@ packages:
jest:
optional: true
dependencies:
'@typescript-eslint/eslint-plugin': 5.42.1_2udltptbznfmezdozpdoa2aemq
'@typescript-eslint/utils': 5.42.1_rmayb2veg2btbq6mbmnyivgasy
eslint: 8.27.0
'@typescript-eslint/eslint-plugin': 5.48.2_iljmjqxcygjq3saipl7gerxpvi
'@typescript-eslint/utils': 5.42.1_yygwinqv3a2io74xmwofqb7uka
eslint: 8.32.0
jest: 29.3.1_odkjkoia5xunhxkdrka32ib6vi
transitivePeerDependencies:
- supports-color
- typescript
dev: true
/eslint-plugin-jsdoc/39.6.2_eslint@8.27.0:
/eslint-plugin-jsdoc/39.6.2_eslint@8.32.0:
resolution: {integrity: sha512-dvgY/W7eUFoAIIiaWHERIMI61ZWqcz9YFjEeyTzdPlrZc3TY/3aZm5aB91NUoTLWYZmO/vFlYSuQi15tF7uE5A==}
engines: {node: ^14 || ^16 || ^17 || ^18 || ^19}
peerDependencies:
@@ -6224,7 +6363,7 @@ packages:
comment-parser: 1.3.1
debug: 4.3.4
escape-string-regexp: 4.0.0
eslint: 8.27.0
eslint: 8.32.0
esquery: 1.4.0
semver: 7.3.8
spdx-expression-parse: 3.0.1
@@ -6240,23 +6379,23 @@ packages:
vscode-json-languageservice: 4.2.1
dev: true
/eslint-plugin-lodash/7.4.0_eslint@8.27.0:
/eslint-plugin-lodash/7.4.0_eslint@8.32.0:
resolution: {integrity: sha512-Tl83UwVXqe1OVeBRKUeWcfg6/pCW1GTRObbdnbEJgYwjxp5Q92MEWQaH9+dmzbRt6kvYU1Mp893E79nJiCSM8A==}
engines: {node: '>=10'}
peerDependencies:
eslint: '>=2'
dependencies:
eslint: 8.27.0
eslint: 8.32.0
lodash: 4.17.21
dev: true
/eslint-plugin-markdown/3.0.0_eslint@8.27.0:
/eslint-plugin-markdown/3.0.0_eslint@8.32.0:
resolution: {integrity: sha512-hRs5RUJGbeHDLfS7ELanT0e29Ocyssf/7kBM+p7KluY5AwngGkDf8Oyu4658/NZSGTTq05FZeWbkxXtbVyHPwg==}
engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0}
peerDependencies:
eslint: ^6.0.0 || ^7.0.0 || ^8.0.0
dependencies:
eslint: 8.27.0
eslint: 8.32.0
mdast-util-from-markdown: 0.8.5
transitivePeerDependencies:
- supports-color
@@ -6274,7 +6413,7 @@ packages:
'@microsoft/tsdoc-config': 0.16.2
dev: true
/eslint-plugin-unicorn/45.0.0_eslint@8.27.0:
/eslint-plugin-unicorn/45.0.0_eslint@8.32.0:
resolution: {integrity: sha512-iP8cMRxXKHonKioOhnCoCcqVhoqhAp6rB+nsoLjXFDxTHz3btWMAp8xwzjHA0B1K6YV/U/Yvqn1bUXZt8sJPuQ==}
engines: {node: '>=14.18'}
peerDependencies:
@@ -6283,8 +6422,8 @@ packages:
'@babel/helper-validator-identifier': 7.19.1
ci-info: 3.6.2
clean-regexp: 1.0.0
eslint: 8.27.0
eslint-utils: 3.0.0_eslint@8.27.0
eslint: 8.32.0
eslint-utils: 3.0.0_eslint@8.32.0
esquery: 1.4.0
indent-string: 4.0.0
is-builtin-module: 3.2.0
@@ -6315,13 +6454,13 @@ packages:
estraverse: 5.3.0
dev: true
/eslint-utils/3.0.0_eslint@8.27.0:
/eslint-utils/3.0.0_eslint@8.32.0:
resolution: {integrity: sha512-uuQC43IGctw68pJA1RgbQS8/NP7rch6Cwd4j3ZBtgo4/8Flj4eGE7ZYSZRN3iq5pVUv6GPdW5Z1RFleo84uLDA==}
engines: {node: ^10.0.0 || ^12.0.0 || >= 14.0.0}
peerDependencies:
eslint: '>=5'
dependencies:
eslint: 8.27.0
eslint: 8.32.0
eslint-visitor-keys: 2.1.0
dev: true
@@ -6335,13 +6474,13 @@ packages:
engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0}
dev: true
/eslint/8.27.0:
resolution: {integrity: sha512-0y1bfG2ho7mty+SiILVf9PfuRA49ek4Nc60Wmmu62QlobNR+CeXa4xXIJgcuwSQgZiWaPH+5BDsctpIW0PR/wQ==}
/eslint/8.32.0:
resolution: {integrity: sha512-nETVXpnthqKPFyuY2FNjz/bEd6nbosRgKbkgS/y1C7LJop96gYHWpiguLecMHQ2XCPxn77DS0P+68WzG6vkZSQ==}
engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0}
hasBin: true
dependencies:
'@eslint/eslintrc': 1.3.3
'@humanwhocodes/config-array': 0.11.7
'@eslint/eslintrc': 1.4.1
'@humanwhocodes/config-array': 0.11.8
'@humanwhocodes/module-importer': 1.0.1
'@nodelib/fs.walk': 1.2.8
ajv: 6.12.6
@@ -6351,7 +6490,7 @@ packages:
doctrine: 3.0.0
escape-string-regexp: 4.0.0
eslint-scope: 7.1.1
eslint-utils: 3.0.0_eslint@8.27.0
eslint-utils: 3.0.0_eslint@8.32.0
eslint-visitor-keys: 3.3.0
espree: 9.4.0
esquery: 1.4.0
@@ -6360,7 +6499,7 @@ packages:
file-entry-cache: 6.0.1
find-up: 5.0.0
glob-parent: 6.0.2
globals: 13.17.0
globals: 13.19.0
grapheme-splitter: 1.0.4
ignore: 5.2.0
import-fresh: 3.3.0
@@ -6387,8 +6526,8 @@ packages:
resolution: {integrity: sha512-DQmnRpLj7f6TgN/NYb0MTzJXL+vJF9h3pHy4JhCIs3zwcgez8xmGg3sXHcEO97BrmO2OSvCwMdfdlyl+E9KjOw==}
engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0}
dependencies:
acorn: 8.8.0
acorn-jsx: 5.3.2_acorn@8.8.0
acorn: 8.8.1
acorn-jsx: 5.3.2_acorn@8.8.1
eslint-visitor-keys: 3.3.0
dev: true
@@ -6663,6 +6802,12 @@ packages:
reusify: 1.0.4
dev: true
/fault/2.0.1:
resolution: {integrity: sha512-WtySTkS4OKev5JtpHXnib4Gxiurzh5NCGvWrFaZ34m6JehfTUhKZvn9njTfw48t6JumVQOmrKqpmGcdwxnhqBQ==}
dependencies:
format: 0.2.2
dev: true
/faye-websocket/0.11.4:
resolution: {integrity: sha512-CzbClwlXAuiRQAlUyfqPgvPoNKTckTPGfwZV4ZdAhVcP2lh9KUxJg2b5GkE7XbjKQ3YJnQ9z6D9ntLAlB+tP8g==}
engines: {node: '>=0.8.0'}
@@ -6762,7 +6907,7 @@ packages:
resolution: {integrity: sha512-XGozTsMPYkm+6b5QL3Z9wQcJjNYxp0CYn3U1gO7dwD6PAqU1SVWZxI9CCg3z+ml3YfqdPnrBehaBrnH2AGKbNA==}
dev: true
/follow-redirects/1.15.2_debug@4.3.2:
/follow-redirects/1.15.2:
resolution: {integrity: sha512-VQLG33o04KaQ8uYi2tVNbdrWp1QWxNNea+nmIB4EVM28v0hmP17z7aG1+wAkNzVq4KeXTq3221ye5qTJP91JwA==}
engines: {node: '>=4.0'}
peerDependencies:
@@ -6770,8 +6915,6 @@ packages:
peerDependenciesMeta:
debug:
optional: true
dependencies:
debug: 4.3.2
dev: true
/foreground-child/2.0.0:
@@ -6813,6 +6956,11 @@ packages:
mime-types: 2.1.35
dev: true
/format/0.2.2:
resolution: {integrity: sha512-wzsgA6WOq+09wrU1tsJ09udeR/YZRaeArL9e1wPbFg3GG2yDnC2ldKpxs4xunpFF9DgqCqOIra3bc1HWrJ37Ww==}
engines: {node: '>=0.4.x'}
dev: true
/forwarded/0.2.0:
resolution: {integrity: sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==}
engines: {node: '>= 0.6'}
@@ -7050,8 +7198,8 @@ packages:
engines: {node: '>=4'}
dev: true
/globals/13.17.0:
resolution: {integrity: sha512-1C+6nQRb1GwGMKm2dH/E7enFAMxGTmGI7/dEdhy/DNelv85w9B72t3uc5frtMNXIbzrarJJ/lTCjcaZwbLJmyw==}
/globals/13.19.0:
resolution: {integrity: sha512-dkQ957uSRWHw7CFXLUtUHQI3g3aWApYhfNR2O6jn/907riyTYKVBmxYVROkBcY614FSSeSJh7Xm7SrUWCxvJMQ==}
engines: {node: '>=8'}
dependencies:
type-fest: 0.20.2
@@ -7306,7 +7454,7 @@ packages:
engines: {node: '>=8.0.0'}
dependencies:
eventemitter3: 4.0.7
follow-redirects: 1.15.2_debug@4.3.2
follow-redirects: 1.15.2
requires-port: 1.0.0
transitivePeerDependencies:
- debug
@@ -8747,6 +8895,12 @@ packages:
- supports-color
dev: true
/mdast-util-frontmatter/1.0.0:
resolution: {integrity: sha512-7itKvp0arEVNpCktOET/eLFAYaZ+0cNjVtFtIPxgQ5tV+3i+D4SDDTjTzPWl44LT59PC+xdx+glNTawBdF98Mw==}
dependencies:
micromark-extension-frontmatter: 1.0.0
dev: true
/mdast-util-gfm-autolink-literal/1.0.2:
resolution: {integrity: sha512-FzopkOd4xTTBeGXhXSBU0OCDDh5lUj2rd+HQqG92Ld+jL4lpUfgX2AT2OHAVP9aEeDKp7G92fuooSZcYJA3cRg==}
dependencies:
@@ -8917,6 +9071,14 @@ packages:
uvu: 0.5.6
dev: true
/micromark-extension-frontmatter/1.0.0:
resolution: {integrity: sha512-EXjmRnupoX6yYuUJSQhrQ9ggK0iQtQlpi6xeJzVD5xscyAI+giqco5fdymayZhJMbIFecjnE2yz85S9NzIgQpg==}
dependencies:
fault: 2.0.1
micromark-util-character: 1.1.0
micromark-util-symbol: 1.0.1
dev: true
/micromark-extension-gfm-autolink-literal/1.0.3:
resolution: {integrity: sha512-i3dmvU0htawfWED8aHMMAzAVp/F0Z+0bPh3YrbTPPL1v4YAlCZpy5rBO5p0LPYiZo0zFVkoYh7vDU7yQSiCMjg==}
dependencies:
@@ -10148,6 +10310,15 @@ packages:
jsesc: 0.5.0
dev: true
/remark-frontmatter/4.0.1:
resolution: {integrity: sha512-38fJrB0KnmD3E33a5jZC/5+gGAC2WKNiPw1/fdXJvijBlhA7RCsvJklrYJakS0HedninvaCYW8lQGf9C918GfA==}
dependencies:
'@types/mdast': 3.0.10
mdast-util-frontmatter: 1.0.0
micromark-extension-frontmatter: 1.0.0
unified: 10.1.2
dev: true
/remark-gfm/3.0.1:
resolution: {integrity: sha512-lEFDoi2PICJyNrACFOfDD3JlLkuSbOa5Wd8EPt06HUdptv8Gn0bxYTdbU/XXQ3swAPkEaGxxPN9cbnMHvVu1Ig==}
dependencies:

View File

@@ -23,7 +23,7 @@ const lint = async (file: string): Promise<boolean> => {
return result.errorCount === 0;
};
(async () => {
const main = async () => {
const jisonFiles = await globby(['./packages/**/*.jison', '!./**/node_modules/**'], {
dot: true,
});
@@ -31,4 +31,6 @@ const lint = async (file: string): Promise<boolean> => {
if (lintResults.includes(false)) {
process.exit(1);
}
})();
};
void main();

View File

@@ -13,8 +13,8 @@ const load = async () => {
await mermaid.registerExternalDiagrams([mindmap]);
await render('info');
setTimeout(async () => {
await render(`mindmap
setTimeout(() => {
void render(`mindmap
root((mindmap))
Origins
Long history
@@ -35,4 +35,4 @@ const load = async () => {
}, 2500);
};
window.addEventListener('load', load, false);
window.addEventListener('load', () => void load(), false);

9
tsconfig.eslint.json Normal file
View File

@@ -0,0 +1,9 @@
{
// extend your base config to share compilerOptions, etc
"extends": "./tsconfig.json",
"compilerOptions": {
// ensure that nobody can accidentally use this config for a build
"noEmit": true
},
"include": ["packages", "tests", "scripts", "cypress", "__mocks__", "./.eslintrc.cjs", "./*"]
}