Compare commits

..

1 Commits

Author SHA1 Message Date
Sidharth Vinod
9491e7035e docs: Release process 2023-02-24 16:46:58 +05:30
49 changed files with 1326 additions and 1513 deletions

View File

@@ -1,18 +1,18 @@
## :bookmark_tabs: Summary
Brief description about the content of your PR.
Resolves #<your issue id here>
## :straight_ruler: Design Decisions
Describe the way your implementation works or what design decisions you made if applicable.
### :clipboard: Tasks
Make sure you
- [ ] :book: have read the [contribution guidelines](https://github.com/mermaid-js/mermaid/blob/develop/CONTRIBUTING.md)
- [ ] :computer: have added unit/e2e tests (if appropriate)
- [ ] :notebook: have added documentation (if appropriate)
- [ ] :bookmark: targeted `develop` branch
## :bookmark_tabs: Summary
Brief description about the content of your PR.
Resolves #<your issue id here>
## :straight_ruler: Design Decisions
Describe the way your implementation works or what design decisions you made if applicable.
### :clipboard: Tasks
Make sure you
- [ ] :book: have read the [contribution guidelines](https://github.com/mermaid-js/mermaid/blob/develop/CONTRIBUTING.md)
- [ ] :computer: have added unit/e2e tests (if appropriate)
- [ ] :notebook: have added documentation (if appropriate)
- [ ] :bookmark: targeted `develop` branch

View File

@@ -0,0 +1,19 @@
# Release vX.X.X
## Release checklist
- [ ] Forked from `develop` branch
- [ ] Draft PR created, targeting `master` branch
- [ ] Ready for review
- [ ] Ready for testing
- [ ] Tested by -
- [ ] Ready for merge
## Release process
- The PR is marked as `Ready for review` by the author
- The PR is reviewed by at least one other person
- The PR is marked as `Ready for testing` by the reviewer once the review and necessary changes are complete
- The PR is tested by the someone other than the author
- The PR is marked as `Tested by - @testerName` by the tester once the testing is complete
- The PR is marked as `Ready for merge` by the author once the testing is complete and CI is green.

View File

@@ -33,14 +33,6 @@ jobs:
run: |
pnpm run ci --coverage
- name: Run ganttDb tests using California timezone
env:
# Makes sure that gantt db works even in a timezone that has daylight savings
# since some days have 25 hours instead of 24.
TZ: America/Los_Angeles
run: |
pnpm exec vitest run ./packages/mermaid/src/diagrams/gantt/ganttDb.spec.ts
- name: Upload Coverage to Coveralls
# it feels a bit weird to use @master, but that's what the docs use
# (coveralls also doesn't publish a @v1 we can use)

View File

@@ -9,11 +9,8 @@ https://mkdocs.org/
https://osawards.com/javascript/#nominees
https://osawards.com/javascript/2019
# Timeout error, maybe Twitter has anti-bot defenses against GitHub's CI servers?
# Timeout error, maybe Twitter has anti-bot defences against GitHub's CI servers?
https://twitter.com/mermaidjs_
# Don't check files that are generated during the build via `pnpm docs:code`
packages/mermaid/src/docs/config/setup/*
# Network error: 502, since few days
https://bundlephobia.com/

View File

@@ -19,7 +19,6 @@
"brkt",
"brolin",
"brotli",
"città",
"classdef",
"codedoc",
"colour",

View File

@@ -22,7 +22,7 @@ export const mermaidUrl = (graphStr, options, api) => {
return url;
};
export const imgSnapshotTest = (graphStr, _options = {}, api = false, validation = undefined) => {
export const imgSnapshotTest = (graphStr, _options, api = false, validation) => {
cy.log(_options);
const options = Object.assign(_options);
if (!options.fontFamily) {

View File

@@ -13,6 +13,7 @@ describe('Class diagram V2', () => {
`,
{ logLevel: 1, flowchart: { htmlLabels: false } }
);
cy.get('svg');
});
it('1: should render a simple class diagram', () => {
@@ -46,6 +47,7 @@ describe('Class diagram V2', () => {
`,
{ logLevel: 1, flowchart: { htmlLabels: false } }
);
cy.get('svg');
});
it('2: should render a simple class diagrams with cardinality', () => {
@@ -74,6 +76,7 @@ describe('Class diagram V2', () => {
`,
{ logLevel: 1, flowchart: { htmlLabels: false } }
);
cy.get('svg');
});
it('should render a simple class diagram with different visibilities', () => {
@@ -91,6 +94,7 @@ describe('Class diagram V2', () => {
`,
{ logLevel: 1, flowchart: { htmlLabels: false } }
);
cy.get('svg');
});
it('should render multiple class diagrams', () => {
@@ -143,6 +147,7 @@ describe('Class diagram V2', () => {
],
{ logLevel: 1, flowchart: { htmlLabels: false } }
);
cy.get('svg');
});
it('4: should render a simple class diagram with comments', () => {
@@ -172,6 +177,7 @@ describe('Class diagram V2', () => {
`,
{ logLevel: 1, flowchart: { htmlLabels: false } }
);
cy.get('svg');
});
it('5: should render a simple class diagram with abstract method', () => {
@@ -183,6 +189,7 @@ describe('Class diagram V2', () => {
`,
{ logLevel: 1, flowchart: { htmlLabels: false } }
);
cy.get('svg');
});
it('6: should render a simple class diagram with static method', () => {
@@ -194,6 +201,7 @@ describe('Class diagram V2', () => {
`,
{ logLevel: 1, flowchart: { htmlLabels: false } }
);
cy.get('svg');
});
it('7: should render a simple class diagram with Generic class', () => {
@@ -213,6 +221,7 @@ describe('Class diagram V2', () => {
`,
{ logLevel: 1, flowchart: { htmlLabels: false } }
);
cy.get('svg');
});
it('8: should render a simple class diagram with Generic class and relations', () => {
@@ -233,6 +242,7 @@ describe('Class diagram V2', () => {
`,
{ logLevel: 1, flowchart: { htmlLabels: false } }
);
cy.get('svg');
});
it('9: should render a simple class diagram with clickable link', () => {
@@ -254,6 +264,7 @@ describe('Class diagram V2', () => {
`,
{ logLevel: 1, flowchart: { htmlLabels: false } }
);
cy.get('svg');
});
it('10: should render a simple class diagram with clickable callback', () => {
@@ -275,6 +286,7 @@ describe('Class diagram V2', () => {
`,
{ logLevel: 1, flowchart: { htmlLabels: false } }
);
cy.get('svg');
});
it('11: should render a simple class diagram with return type on method', () => {
@@ -289,6 +301,7 @@ describe('Class diagram V2', () => {
`,
{ logLevel: 1, flowchart: { htmlLabels: false } }
);
cy.get('svg');
});
it('12: should render a simple class diagram with generic types', () => {
@@ -304,6 +317,7 @@ describe('Class diagram V2', () => {
`,
{ logLevel: 1, flowchart: { htmlLabels: false } }
);
cy.get('svg');
});
it('13: should render a simple class diagram with css classes applied', () => {
@@ -321,6 +335,7 @@ describe('Class diagram V2', () => {
`,
{ logLevel: 1, flowchart: { htmlLabels: false } }
);
cy.get('svg');
});
it('14: should render a simple class diagram with css classes applied directly', () => {
@@ -336,6 +351,7 @@ describe('Class diagram V2', () => {
`,
{ logLevel: 1, flowchart: { htmlLabels: false } }
);
cy.get('svg');
});
it('15: should render a simple class diagram with css classes applied two multiple classes', () => {
@@ -349,6 +365,7 @@ describe('Class diagram V2', () => {
`,
{ logLevel: 1, flowchart: { htmlLabels: false } }
);
cy.get('svg');
});
it('16a: should render a simple class diagram with static field', () => {
@@ -361,6 +378,7 @@ describe('Class diagram V2', () => {
`,
{ logLevel: 1, flowchart: { htmlLabels: false } }
);
cy.get('svg');
});
it('16b: should handle the direction statement with TB', () => {
@@ -385,6 +403,7 @@ describe('Class diagram V2', () => {
`,
{ logLevel: 1, flowchart: { htmlLabels: false } }
);
cy.get('svg');
});
it('18: should handle the direction statement with LR', () => {
@@ -409,6 +428,7 @@ describe('Class diagram V2', () => {
`,
{ logLevel: 1, flowchart: { htmlLabels: false } }
);
cy.get('svg');
});
it('17a: should handle the direction statement with BT', () => {
imgSnapshotTest(
@@ -432,6 +452,7 @@ describe('Class diagram V2', () => {
`,
{ logLevel: 1, flowchart: { htmlLabels: false } }
);
cy.get('svg');
});
it('17b: should handle the direction statement with RL', () => {
imgSnapshotTest(
@@ -455,6 +476,7 @@ describe('Class diagram V2', () => {
`,
{ logLevel: 1, flowchart: { htmlLabels: false } }
);
cy.get('svg');
});
it('18: should render a simple class diagram with notes', () => {
@@ -471,6 +493,7 @@ describe('Class diagram V2', () => {
`,
{ logLevel: 1, flowchart: { htmlLabels: false } }
);
cy.get('svg');
});
it('1433: should render a simple class with a title', () => {
@@ -480,72 +503,8 @@ title: simple class diagram
---
classDiagram-v2
class Class10
`
);
});
it('should render a class with text label', () => {
imgSnapshotTest(
`classDiagram
class C1["Class 1 with text label"]
C1 --> C2`
);
});
it('should render two classes with text labels', () => {
imgSnapshotTest(
`classDiagram
class C1["Class 1 with text label"]
class C2["Class 2 with chars @?"]
C1 --> C2`
);
});
it('should render a class with a text label, members and annotation', () => {
imgSnapshotTest(
`classDiagram
class C1["Class 1 with text label"] {
&lt;&lt;interface&gt;&gt;
+member1
}
C1 --> C2`
);
});
it('should render multiple classes with same text labels', () => {
imgSnapshotTest(
`classDiagram
class C1["Class with text label"]
class C2["Class with text label"]
class C3["Class with text label"]
C1 --> C2
C3 ..> C2
`
);
});
it('should render classes with different text labels', () => {
imgSnapshotTest(
`classDiagram
class C1["OneWord"]
class C2["With, Comma"]
class C3["With (Brackets)"]
class C4["With [Brackets]"]
class C5["With {Brackets}"]
class C7["With 1 number"]
class C8["With . period..."]
class C9["With - dash"]
class C10["With _ underscore"]
class C11["With ' single quote"]
class C12["With ~!@#$%^&*()_+=-/?"]
class C13["With Città foreign language"]
`
);
});
it('should render classLabel if class has already been defined earlier', () => {
imgSnapshotTest(
`classDiagram
Animal <|-- Duck
class Duck["Duck with text label"]
`
`,
{}
);
});
});

View File

@@ -12,6 +12,7 @@
<style>
body {
background: rgb(221, 208, 208);
/*background:#333;*/
font-family: 'Arial';
}
h1 {
@@ -119,9 +120,17 @@ classE o-- classF : aggregation
};
mermaid.initialize({
theme: 'default',
// arrowMarkerAbsolute: true,
// themeCSS: '.edgePath .path {stroke: red;} .arrowheadPath {fill: red;}',
logLevel: 0,
flowchart: { curve: 'linear', htmlLabels: true },
// gantt: { axisFormat: '%m/%d/%Y' },
sequence: { actorMargin: 50, showSequenceNumbers: true },
// sequenceDiagram: { actorMargin: 300 } // deprecated
// fontFamily: '"arial", sans-serif',
// themeVariables: {
// fontFamily: '"arial", sans-serif',
// },
curve: 'linear',
securityLevel: 'loose',
});

View File

@@ -1,17 +1,246 @@
<html>
<head>
<link href="https://fonts.googleapis.com/css?family=Montserrat&display=swap" rel="stylesheet" />
<link href="https://unpkg.com/tailwindcss@^1.0/dist/tailwind.min.css" rel="stylesheet" />
<link
rel="stylesheet"
href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/4.7.0/css/font-awesome.min.css"
/>
<link
href="https://cdn.jsdelivr.net/npm/@mdi/font@6.9.96/css/materialdesignicons.min.css"
rel="stylesheet"
/>
<link
href="https://fonts.googleapis.com/css?family=Noto+Sans+SC&display=swap"
rel="stylesheet"
/>
<style>
body {
/* background: rgb(221, 208, 208); */
/* background:#333; */
font-family: 'Arial';
/* font-size: 18px !important; */
}
h1 {
color: grey;
}
.mermaid2 {
display: none;
}
.mermaid svg {
/* font-size: 18px !important; */
background-color: #eee;
background-image: radial-gradient(#fff 1%, transparent 11%),
radial-gradient(#fff 1%, transparent 11%);
background-size: 20px 20px;
background-position: 0 0, 10px 10px;
background-repeat: repeat;
}
.malware {
position: fixed;
bottom: 0;
left: 0;
right: 0;
height: 150px;
background: red;
color: black;
display: flex;
display: flex;
justify-content: center;
align-items: center;
font-family: monospace;
font-size: 72px;
}
</style>
</head>
<body>
<pre id="diagram" class="mermaid">
%%{init: {"flowchart": {"defaultRenderer": "elk"}} }%%
graph TB
a --> b
a --> c
b --> d
c --> d
</pre>
<pre id="diagram" class="mermaid">
flowchart-elk LR
subgraph A
a --> b
end
subgraph B
b
end
</pre>
<pre id="diagram" class="mermaid">
%%{init: {"flowchart": {"defaultRenderer": "elk"}} }%%
flowchart TB
%% I could not figure out how to use double quotes in labels in Mermaid
subgraph ibm[IBM Espresso CPU]
core0[IBM PowerPC Broadway Core 0]
core1[IBM PowerPC Broadway Core 1]
core2[IBM PowerPC Broadway Core 2]
<div id="d2"></div>
rom[16 KB ROM]
core0 --- core2
rom --> core2
end
subgraph amd[AMD Latte GPU]
mem[Memory & I/O Bridge]
dram[DRAM Controller]
edram[32 MB EDRAM MEM1]
rom[512 B SEEPROM]
sata[SATA IF]
exi[EXI]
subgraph gx[GX]
sram[3 MB 1T-SRAM]
end
radeon[AMD Radeon R7xx GX2]
mem --- gx
mem --- radeon
rom --- mem
mem --- sata
mem --- exi
dram --- sata
dram --- exi
end
ddr3[2 GB DDR3 RAM MEM2]
mem --- ddr3
dram --- ddr3
edram --- ddr3
core1 --- mem
exi --- rtc
rtc{{rtc}}
</pre
>
<br />
<pre id="diagram" class="mermaid">
flowchart TB
%% I could not figure out how to use double quotes in labels in Mermaid
subgraph ibm[IBM Espresso CPU]
core0[IBM PowerPC Broadway Core 0]
core1[IBM PowerPC Broadway Core 1]
core2[IBM PowerPC Broadway Core 2]
rom[16 KB ROM]
core0 --- core2
rom --> core2
end
subgraph amd[AMD Latte GPU]
mem[Memory & I/O Bridge]
dram[DRAM Controller]
edram[32 MB EDRAM MEM1]
rom[512 B SEEPROM]
sata[SATA IF]
exi[EXI]
subgraph gx[GX]
sram[3 MB 1T-SRAM]
end
radeon[AMD Radeon R7xx GX2]
mem --- gx
mem --- radeon
rom --- mem
mem --- sata
mem --- exi
dram --- sata
dram --- exi
end
ddr3[2 GB DDR3 RAM MEM2]
mem --- ddr3
dram --- ddr3
edram --- ddr3
core1 --- mem
exi --- rtc
rtc{{rtc}}
</pre
>
<br />
&nbsp;
<pre id="diagram" class="mermaid2">
flowchart LR
B1 --be be--x B2
B1 --bo bo--o B3
subgraph Ugge
B2
B3
subgraph inner
B4
B5
end
subgraph inner2
subgraph deeper
C4
C5
end
C6
end
B4 --> C4
B3 -- X --> B4
B2 --> inner
C4 --> C5
end
subgraph outer
B6
end
B6 --> B5
</pre
>
<pre id="diagram" class="mermaid2">
sequenceDiagram
Customer->>+Stripe: Makes a payment request
Stripe->>+Bank: Forwards the payment request to the bank
Bank->>+Customer: Asks for authorization
Customer->>+Bank: Provides authorization
Bank->>+Stripe: Sends a response with payment details
Stripe->>+Merchant: Sends a notification of payment receipt
Merchant->>+Stripe: Confirms the payment
Stripe->>+Customer: Sends a confirmation of payment
Customer->>+Merchant: Receives goods or services
</pre
>
<pre id="diagram" class="mermaid2">
gantt
title Style today marker (vertical line should be 5px wide and half-transparent blue)
dateFormat YYYY-MM-DD
axisFormat %d
todayMarker stroke-width:5px,stroke:#00f,opacity:0.5
section Section1
Today: 1, -1h
</pre>
<script type="module">
import mermaid from '/mermaid.esm.mjs';
import mermaid from '../../packages/mermaid/src/mermaid';
mermaid.parseError = function (err, hash) {
// console.error('Mermaid error: ', err);
};
@@ -36,11 +265,6 @@ graph TB
console.error('In parse error:');
console.error(err);
};
const value = `graph TD\nHello --> World`;
const el = document.getElementById('d2');
const { svg } = await mermaid.render('d22', value);
console.log(svg);
el.innerHTML = svg;
// mermaid.test1('first_slow', 1200).then((r) => console.info(r));
// mermaid.test1('second_fast', 200).then((r) => console.info(r));
// mermaid.test1('third_fast', 200).then((r) => console.info(r));

View File

@@ -37,7 +37,7 @@
</pre>
<script type="module">
import mermaid from '../packages/mermaid/src/mermaid';
import mermaid from '../packages/mermaid';
mermaid.initialize({
theme: 'forest',
// themeCSS: '.node rect { fill: red; }',

View File

@@ -95,7 +95,7 @@ mermaid.initialize(config);
#### Defined in
[mermaidAPI.ts:662](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/mermaidAPI.ts#L662)
[mermaidAPI.ts:680](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/mermaidAPI.ts#L680)
## Functions

View File

@@ -130,40 +130,6 @@ classDiagram
Naming convention: a class name should be composed only of alphanumeric characters (including unicode), and underscores.
### Class labels
In case you need to provide a label for a class, you can use the following syntax:
```mermaid-example
classDiagram
class Animal["Animal with a label"]
class Car["Car with *! symbols"]
Animal --> Car
```
```mermaid
classDiagram
class Animal["Animal with a label"]
class Car["Car with *! symbols"]
Animal --> Car
```
You can also use backticks to escape special characters in the label:
```mermaid-example
classDiagram
class `Animal Class!`
class `Car Class`
`Animal Class!` --> `Car Class`
```
```mermaid
classDiagram
class `Animal Class!`
class `Car Class`
`Animal Class!` --> `Car Class`
```
## Defining Members of a class
UML provides mechanisms to represent class members such as attributes and methods, as well as additional information about them.
@@ -726,11 +692,11 @@ Beginner's tip—a full example using interactive links in an HTML page:
### Styling a node
It is possible to apply specific styles such as a thicker border or a different background color to individual nodes. This is done by predefining classes in css styles that can be applied from the graph definition using the `cssClass` statement or the `:::` short hand.
It is possible to apply specific styles such as a thicker border or a different background color to individual nodes. This is done by predefining classes in css styles that can be applied from the graph definition:
```html
<style>
.styleClass > rect {
.cssClass > rect {
fill: #ff0000;
stroke: #ffff00;
stroke-width: 4px;
@@ -740,29 +706,29 @@ It is possible to apply specific styles such as a thicker border or a different
Then attaching that class to a specific node:
cssClass "nodeId1" styleClass;
cssClass "nodeId1" cssClass;
It is also possible to attach a class to a list of nodes in one statement:
cssClass "nodeId1,nodeId2" styleClass;
cssClass "nodeId1,nodeId2" cssClass;
A shorter form of adding a class is to attach the classname to the node using the `:::` operator:
```mermaid-example
classDiagram
class Animal:::styleClass
class Animal:::cssClass
```
```mermaid
classDiagram
class Animal:::styleClass
class Animal:::cssClass
```
Or:
```mermaid-example
classDiagram
class Animal:::styleClass {
class Animal:::cssClass {
-int sizeInFeet
-canEat()
}
@@ -770,7 +736,7 @@ classDiagram
```mermaid
classDiagram
class Animal:::styleClass {
class Animal:::cssClass {
-int sizeInFeet
-canEat()
}

View File

@@ -184,7 +184,7 @@ The following formatting options are supported:
| `YY` | 14 | 2 digit year |
| `Q` | 1..4 | Quarter of year. Sets month to first month in quarter. |
| `M MM` | 1..12 | Month number |
| `MMM MMMM` | January..Dec | Month name in locale set by `dayjs.locale()` |
| `MMM MMMM` | January..Dec | Month name in locale set by `moment.locale()` |
| `D DD` | 1..31 | Day of month |
| `Do` | 1st..31st | Day of month with ordinal |
| `DDD DDDD` | 1..365 | Day of year |
@@ -200,7 +200,7 @@ The following formatting options are supported:
| `SSS` | 0..999 | Thousandths of a second |
| `Z ZZ` | +12:00 | Offset from UTC as +-HH:mm, +-HHmm, or Z |
More info in: <https://day.js.org/docs/en/parse/string-format/>
More info in: <https://momentjs.com/docs/#/parsing/string-format/>
### Output date format on the axis

View File

@@ -1,7 +1,7 @@
{
"name": "mermaid-monorepo",
"private": true,
"version": "10.0.2",
"version": "9.4.0",
"description": "Markdownish syntax for generating flowcharts, sequence diagrams, class diagrams, gantt charts and git graphs.",
"type": "module",
"packageManager": "pnpm@7.27.0",

View File

@@ -1,6 +1,6 @@
{
"name": "mermaid",
"version": "10.0.2",
"version": "10.0.0",
"description": "Markdown-ish syntax for generating flowcharts, sequence diagrams, class diagrams, gantt charts and git graphs.",
"type": "module",
"module": "./dist/mermaid.core.mjs",
@@ -56,12 +56,12 @@
"cytoscape-cose-bilkent": "^4.1.0",
"cytoscape-fcose": "^2.1.0",
"d3": "^7.4.0",
"dagre-d3-es": "7.0.9",
"dayjs": "^1.11.7",
"dagre-d3-es": "7.0.8",
"dompurify": "2.4.3",
"elkjs": "^0.8.2",
"khroma": "^2.0.0",
"lodash-es": "^4.17.21",
"moment-mini": "^2.29.4",
"non-layered-tidy-tree-layout": "^2.0.2",
"stylis": "^4.1.2",
"ts-dedent": "^2.2.0",

View File

@@ -264,10 +264,6 @@ export interface ClassDiagramConfig extends BaseDiagramConfig {
padding?: number;
textHeight?: number;
defaultRenderer?: string;
nodeSpacing?: number;
rankSpacing?: number;
diagramPadding?: number;
htmlLabels?: boolean;
}
export interface JourneyDiagramConfig extends BaseDiagramConfig {
@@ -299,7 +295,6 @@ export interface TimelineDiagramConfig extends BaseDiagramConfig {
leftMargin?: number;
width?: number;
height?: number;
padding?: number;
boxMargin?: number;
boxTextMargin?: number;
noteMargin?: number;
@@ -316,7 +311,6 @@ export interface TimelineDiagramConfig extends BaseDiagramConfig {
sectionFills?: string[];
sectionColours?: string[];
disableMulticolor?: boolean;
useMaxWidth?: boolean;
}
export interface GanttDiagramConfig extends BaseDiagramConfig {

View File

@@ -772,7 +772,7 @@ const class_box = (parent, node) => {
maxWidth += interfaceBBox.width;
}
let classTitleString = node.classData.label;
let classTitleString = node.classData.id;
if (node.classData.type !== undefined && node.classData.type !== '') {
if (getConfig().flowchart.htmlLabels) {
@@ -927,6 +927,61 @@ const class_box = (parent, node) => {
);
verticalPos += classTitleBBox.height + rowPadding;
});
//
// let bbox;
// if (evaluate(getConfig().flowchart.htmlLabels)) {
// const div = interfaceLabel.children[0];
// const dv = select(interfaceLabel);
// bbox = div.getBoundingClientRect();
// dv.attr('width', bbox.width);
// dv.attr('height', bbox.height);
// }
// bbox = labelContainer.getBBox();
// log.info('Text 2', text2);
// const textRows = text2.slice(1, text2.length);
// let titleBox = text.getBBox();
// const descr = label
// .node()
// .appendChild(createLabel(textRows.join('<br/>'), node.labelStyle, true, true));
// if (evaluate(getConfig().flowchart.htmlLabels)) {
// const div = descr.children[0];
// const dv = select(descr);
// bbox = div.getBoundingClientRect();
// dv.attr('width', bbox.width);
// dv.attr('height', bbox.height);
// }
// // bbox = label.getBBox();
// // log.info(descr);
// select(descr).attr(
// 'transform',
// 'translate( ' +
// // (titleBox.width - bbox.width) / 2 +
// (bbox.width > titleBox.width ? 0 : (titleBox.width - bbox.width) / 2) +
// ', ' +
// (titleBox.height + halfPadding + 5) +
// ')'
// );
// select(text).attr(
// 'transform',
// 'translate( ' +
// // (titleBox.width - bbox.width) / 2 +
// (bbox.width < titleBox.width ? 0 : -(titleBox.width - bbox.width) / 2) +
// ', ' +
// 0 +
// ')'
// );
// // Get the size of the label
// // Bounding box for title and text
// bbox = label.node().getBBox();
// // Center the label
// label.attr(
// 'transform',
// 'translate(' + -bbox.width / 2 + ', ' + (-bbox.height / 2 - halfPadding + 3) + ')'
// );
rect
.attr('class', 'outer title-state')
@@ -935,6 +990,13 @@ const class_box = (parent, node) => {
.attr('width', maxWidth + node.padding)
.attr('height', maxHeight + node.padding);
// innerLine
// .attr('class', 'divider')
// .attr('x1', -bbox.width / 2 - halfPadding)
// .attr('x2', bbox.width / 2 + halfPadding)
// .attr('y1', -bbox.height / 2 - halfPadding + titleBox.height + halfPadding)
// .attr('y2', -bbox.height / 2 - halfPadding + titleBox.height + halfPadding);
updateNodeBounds(node, rect);
node.intersect = function (point) {

View File

@@ -46,24 +46,9 @@ export const detectType = function (text: string, config?: MermaidConfig): strin
}
}
throw new UnknownDiagramError(
`No diagram type detected matching given configuration for text: ${text}`
);
throw new UnknownDiagramError(`No diagram type detected for text: ${text}`);
};
/**
* Registers lazy-loaded diagrams to Mermaid.
*
* The diagram function is loaded asynchronously, so that diagrams are only loaded
* if the diagram is detected.
*
* @remarks
* Please note that the order of diagram detectors is important.
* The first detector to return `true` is the diagram that will be loaded
* and used, so put more specific detectors at the beginning!
*
* @param diagrams - Diagrams to lazy load, and their detectors, in order of importance.
*/
export const registerLazyLoadedDiagrams = (...diagrams: ExternalDiagramDefinition[]) => {
for (const { id, detector, loader } of diagrams) {
addDetector(id, detector, loader);
@@ -112,6 +97,4 @@ export const addDetector = (key: string, detector: DiagramDetector, loader?: Dia
log.debug(`Detector with key ${key} added${loader ? ' with loader' : ''}`);
};
export const getDiagramLoader = (key: string) => {
return detectors[key].loader;
};
export const getDiagramLoader = (key: string) => detectors[key].loader;

View File

@@ -1,82 +0,0 @@
import { it, describe, expect } from 'vitest';
import { detectType } from './detectType';
import { addDiagrams } from './diagram-orchestration';
describe('diagram-orchestration', () => {
it('should register diagrams', () => {
expect(() => detectType('graph TD; A-->B')).toThrow();
addDiagrams();
expect(detectType('graph TD; A-->B')).toBe('flowchart');
});
describe('proper diagram types should be detetced', () => {
beforeAll(() => {
addDiagrams();
});
it.each([
{ text: 'graph TD;', expected: 'flowchart' },
{ text: 'flowchart TD;', expected: 'flowchart-v2' },
{ text: 'flowchart-v2 TD;', expected: 'flowchart-v2' },
{ text: 'flowchart-elk TD;', expected: 'flowchart-elk' },
{ text: 'error', expected: 'error' },
{ text: 'C4Context;', expected: 'c4' },
{ text: 'classDiagram', expected: 'class' },
{ text: 'classDiagram-v2', expected: 'classDiagram' },
{ text: 'erDiagram', expected: 'er' },
{ text: 'journey', expected: 'journey' },
{ text: 'gantt', expected: 'gantt' },
{ text: 'pie', expected: 'pie' },
{ text: 'requirementDiagram', expected: 'requirement' },
{ text: 'info', expected: 'info' },
{ text: 'sequenceDiagram', expected: 'sequence' },
{ text: 'mindmap', expected: 'mindmap' },
{ text: 'timeline', expected: 'timeline' },
{ text: 'gitGraph', expected: 'gitGraph' },
{ text: 'stateDiagram', expected: 'state' },
{ text: 'stateDiagram-v2', expected: 'stateDiagram' },
])(
'should $text be detected as $expected',
({ text, expected }: { text: string; expected: string }) => {
expect(detectType(text)).toBe(expected);
}
);
it('should detect proper flowchart type based on config', () => {
// graph & dagre-d3 => flowchart
expect(detectType('graph TD; A-->B')).toBe('flowchart');
// graph & dagre-d3 => flowchart
expect(detectType('graph TD; A-->B', { flowchart: { defaultRenderer: 'dagre-d3' } })).toBe(
'flowchart'
);
// flowchart & dagre-d3 => error
expect(() =>
detectType('flowchart TD; A-->B', { flowchart: { defaultRenderer: 'dagre-d3' } })
).toThrowErrorMatchingInlineSnapshot(
'"No diagram type detected matching given configuration for text: flowchart TD; A-->B"'
);
// graph & dagre-wrapper => flowchart-v2
expect(
detectType('graph TD; A-->B', { flowchart: { defaultRenderer: 'dagre-wrapper' } })
).toBe('flowchart-v2');
// flowchart ==> flowchart-v2
expect(detectType('flowchart TD; A-->B')).toBe('flowchart-v2');
// flowchart && dagre-wrapper ==> flowchart-v2
expect(
detectType('flowchart TD; A-->B', { flowchart: { defaultRenderer: 'dagre-wrapper' } })
).toBe('flowchart-v2');
// flowchart && elk ==> flowchart-elk
expect(detectType('flowchart TD; A-->B', { flowchart: { defaultRenderer: 'elk' } })).toBe(
'flowchart-elk'
);
});
it('should not detect flowchart if pie contains flowchart', () => {
expect(
detectType(`pie title: "flowchart"
flowchart: 1 "pie" pie: 2 "pie"`)
).toBe('pie');
});
});
});

View File

@@ -45,7 +45,7 @@ export const addDiagrams = () => {
throw new Error(
'Diagrams beginning with --- are not valid. ' +
'If you were trying to use a YAML front-matter, please ensure that ' +
"you've correctly opened and closed the YAML front-matter with un-indented `---` blocks"
"you've correctly opened and closed the YAML front-matter with unindented `---` blocks"
);
},
},
@@ -55,26 +55,25 @@ export const addDiagrams = () => {
return text.toLowerCase().trimStart().startsWith('---');
}
);
// Ordering of detectors is important. The first one to return true will be used.
registerLazyLoadedDiagrams(
error,
c4,
classDiagramV2,
classDiagram,
classDiagramV2,
er,
gantt,
info,
pie,
requirement,
sequence,
flowchartElk,
flowchartV2,
flowchart,
flowchartV2,
flowchartElk,
mindmap,
timeline,
git,
stateV2,
state,
stateV2,
journey
);
};

View File

@@ -3,7 +3,6 @@ import { getDiagram, registerDiagram } from './diagramAPI';
import { addDiagrams } from './diagram-orchestration';
import { DiagramDetector } from './types';
import { getDiagramFromText } from '../Diagram';
import { it, describe, expect, beforeAll } from 'vitest';
addDiagrams();
beforeAll(async () => {
@@ -16,17 +15,13 @@ describe('DiagramAPI', () => {
});
it('should throw error if diagram is not defined', () => {
expect(() => getDiagram('loki')).toThrowErrorMatchingInlineSnapshot(
'"Diagram loki not found."'
);
expect(() => getDiagram('loki')).toThrow();
});
it('should handle diagram registrations', () => {
expect(() => getDiagram('loki')).toThrowErrorMatchingInlineSnapshot(
'"Diagram loki not found."'
);
expect(() => detectType('loki diagram')).toThrowErrorMatchingInlineSnapshot(
'"No diagram type detected matching given configuration for text: loki diagram"'
expect(() => getDiagram('loki')).toThrow();
expect(() => detectType('loki diagram')).toThrow(
'No diagram type detected for text: loki diagram'
);
const detector: DiagramDetector = (str: string) => {
return str.match('loki') !== null;

View File

@@ -61,8 +61,8 @@ Expecting 'TXT', got 'NEWLINE'"
});
test('should throw the right error for unregistered diagrams', async () => {
await expect(getDiagramFromText('thor TD; A-->B')).rejects.toThrowErrorMatchingInlineSnapshot(
'"No diagram type detected matching given configuration for text: thor TD; A-->B"'
await expect(getDiagramFromText('thor TD; A-->B')).rejects.toThrowError(
'No diagram type detected for text: thor TD; A-->B'
);
});
});

View File

@@ -1,5 +1,4 @@
// @ts-expect-error - d3 types issue
import { select, Selection } from 'd3';
import { select } from 'd3';
import { log } from '../../logger';
import * as configApi from '../../config';
import common from '../common/common';
@@ -14,54 +13,44 @@ import {
setDiagramTitle,
getDiagramTitle,
} from '../../commonDb';
import { ClassRelation, ClassNode, ClassNote, ClassMap } from './classTypes';
const MERMAID_DOM_ID_PREFIX = 'classId-';
const MERMAID_DOM_ID_PREFIX = 'classid-';
let relations: ClassRelation[] = [];
let classes: ClassMap = {};
let notes: ClassNote[] = [];
let relations = [];
let classes = {};
let notes = [];
let classCounter = 0;
let functions: any[] = [];
let funs = [];
const sanitizeText = (txt: string) => common.sanitizeText(txt, configApi.getConfig());
const sanitizeText = (txt) => common.sanitizeText(txt, configApi.getConfig());
export const parseDirective = function (statement: string, context: string, type: string) {
// @ts-ignore Don't wanna mess it up
export const parseDirective = function (statement, context, type) {
mermaidAPI.parseDirective(this, statement, context, type);
};
const splitClassNameAndType = function (id: string) {
const splitClassNameAndType = function (id) {
let genericType = '';
let className = id;
if (id.indexOf('~') > 0) {
const split = id.split('~');
className = sanitizeText(split[0]);
genericType = sanitizeText(split[1]);
let split = id.split('~');
className = split[0];
genericType = common.sanitizeText(split[1], configApi.getConfig());
}
return { className: className, type: genericType };
};
export const setClassLabel = function (id: string, label: string) {
if (label) {
label = sanitizeText(label);
}
const { className } = splitClassNameAndType(id);
classes[className].label = label;
};
/**
* Function called by parser when a node definition has been found.
*
* @param id - Id of the class to add
* @param id
* @public
*/
export const addClass = function (id: string) {
const classId = splitClassNameAndType(id);
export const addClass = function (id) {
let classId = splitClassNameAndType(id);
// Only add class if not exists
if (classes[classId.className] !== undefined) {
return;
@@ -70,13 +59,12 @@ export const addClass = function (id: string) {
classes[classId.className] = {
id: classId.className,
type: classId.type,
label: classId.className,
cssClasses: [],
methods: [],
members: [],
annotations: [],
domId: MERMAID_DOM_ID_PREFIX + classId.className + '-' + classCounter,
} as ClassNode;
};
classCounter++;
};
@@ -84,33 +72,35 @@ export const addClass = function (id: string) {
/**
* Function to lookup domId from id in the graph definition.
*
* @param id - class ID to lookup
* @param id
* @public
*/
export const lookUpDomId = function (id: string): string {
if (id in classes) {
return classes[id].domId;
export const lookUpDomId = function (id) {
const classKeys = Object.keys(classes);
for (const classKey of classKeys) {
if (classes[classKey].id === id) {
return classes[classKey].domId;
}
}
throw new Error('Class not found: ' + id);
};
export const clear = function () {
relations = [];
classes = {};
notes = [];
functions = [];
functions.push(setupToolTips);
funs = [];
funs.push(setupToolTips);
commonClear();
};
export const getClass = function (id: string) {
export const getClass = function (id) {
return classes[id];
};
export const getClasses = function () {
return classes;
};
export const getRelations = function (): ClassRelation[] {
export const getRelations = function () {
return relations;
};
@@ -118,7 +108,7 @@ export const getNotes = function () {
return notes;
};
export const addRelation = function (relation: ClassRelation) {
export const addRelation = function (relation) {
log.debug('Adding relation: ' + JSON.stringify(relation));
addClass(relation.id1);
addClass(relation.id2);
@@ -143,11 +133,11 @@ export const addRelation = function (relation: ClassRelation) {
* Adds an annotation to the specified class Annotations mark special properties of the given type
* (like 'interface' or 'service')
*
* @param className - The class name
* @param annotation - The name of the annotation without any brackets
* @param className The class name
* @param annotation The name of the annotation without any brackets
* @public
*/
export const addAnnotation = function (className: string, annotation: string) {
export const addAnnotation = function (className, annotation) {
const validatedClassName = splitClassNameAndType(className).className;
classes[validatedClassName].annotations.push(annotation);
};
@@ -155,13 +145,13 @@ export const addAnnotation = function (className: string, annotation: string) {
/**
* Adds a member to the specified class
*
* @param className - The class name
* @param member - The full name of the member. If the member is enclosed in `<<brackets>>` it is
* @param className The class name
* @param member The full name of the member. If the member is enclosed in <<brackets>> it is
* treated as an annotation If the member is ending with a closing bracket ) it is treated as a
* method Otherwise the member will be treated as a normal property
* @public
*/
export const addMember = function (className: string, member: string) {
export const addMember = function (className, member) {
const validatedClassName = splitClassNameAndType(className).className;
const theClass = classes[validatedClassName];
@@ -171,6 +161,7 @@ export const addMember = function (className: string, member: string) {
if (memberString.startsWith('<<') && memberString.endsWith('>>')) {
// Remove leading and trailing brackets
// theClass.annotations.push(memberString.substring(2, memberString.length - 2));
theClass.annotations.push(sanitizeText(memberString.substring(2, memberString.length - 2)));
} else if (memberString.indexOf(')') > 0) {
theClass.methods.push(sanitizeText(memberString));
@@ -180,14 +171,14 @@ export const addMember = function (className: string, member: string) {
}
};
export const addMembers = function (className: string, members: string[]) {
export const addMembers = function (className, members) {
if (Array.isArray(members)) {
members.reverse();
members.forEach((member) => addMember(className, member));
}
};
export const addNote = function (text: string, className: string) {
export const addNote = function (text, className) {
const note = {
id: `note${notes.length}`,
class: className,
@@ -196,20 +187,21 @@ export const addNote = function (text: string, className: string) {
notes.push(note);
};
export const cleanupLabel = function (label: string) {
if (label.startsWith(':')) {
label = label.substring(1);
export const cleanupLabel = function (label) {
if (label.substring(0, 1) === ':') {
return common.sanitizeText(label.substr(1).trim(), configApi.getConfig());
} else {
return sanitizeText(label.trim());
}
return sanitizeText(label.trim());
};
/**
* Called by parser when a special node is found, e.g. a clickable element.
*
* @param ids - Comma separated list of ids
* @param className - Class to add
* @param ids Comma separated list of ids
* @param className Class to add
*/
export const setCssClass = function (ids: string, className: string) {
export const setCssClass = function (ids, className) {
ids.split(',').forEach(function (_id) {
let id = _id;
if (_id[0].match(/\d/)) {
@@ -224,27 +216,28 @@ export const setCssClass = function (ids: string, className: string) {
/**
* Called by parser when a tooltip is found, e.g. a clickable element.
*
* @param ids - Comma separated list of ids
* @param tooltip - Tooltip to add
* @param ids Comma separated list of ids
* @param tooltip Tooltip to add
*/
const setTooltip = function (ids: string, tooltip?: string) {
const setTooltip = function (ids, tooltip) {
const config = configApi.getConfig();
ids.split(',').forEach(function (id) {
if (tooltip !== undefined) {
classes[id].tooltip = sanitizeText(tooltip);
classes[id].tooltip = common.sanitizeText(tooltip, config);
}
});
};
export const getTooltip = function (id: string) {
export const getTooltip = function (id) {
return classes[id].tooltip;
};
/**
* Called by parser when a link is found. Adds the URL to the vertex data.
*
* @param ids - Comma separated list of ids
* @param linkStr - URL to create a link for
* @param target - Target of the link, _blank by default as originally defined in the svgDraw.js file
* @param ids Comma separated list of ids
* @param linkStr URL to create a link for
* @param target Target of the link, _blank by default as originally defined in the svgDraw.js file
*/
export const setLink = function (ids: string, linkStr: string, target: string) {
export const setLink = function (ids, linkStr, target) {
const config = configApi.getConfig();
ids.split(',').forEach(function (_id) {
let id = _id;
@@ -268,11 +261,11 @@ export const setLink = function (ids: string, linkStr: string, target: string) {
/**
* Called by parser when a click definition is found. Registers an event handler.
*
* @param ids - Comma separated list of ids
* @param functionName - Function to be called on click
* @param functionArgs - Function args the function should be called with
* @param ids Comma separated list of ids
* @param functionName Function to be called on click
* @param functionArgs Function args the function should be called with
*/
export const setClickEvent = function (ids: string, functionName: string, functionArgs: string) {
export const setClickEvent = function (ids, functionName, functionArgs) {
ids.split(',').forEach(function (id) {
setClickFunc(id, functionName, functionArgs);
classes[id].haveCallback = true;
@@ -280,19 +273,19 @@ export const setClickEvent = function (ids: string, functionName: string, functi
setCssClass(ids, 'clickable');
};
const setClickFunc = function (domId: string, functionName: string, functionArgs: string) {
const setClickFunc = function (domId, functionName, functionArgs) {
const config = configApi.getConfig();
let id = domId;
let elemId = lookUpDomId(id);
if (config.securityLevel !== 'loose') {
return;
}
if (functionName === undefined) {
return;
}
const id = domId;
if (classes[id] !== undefined) {
const elemId = lookUpDomId(id);
let argList: string[] = [];
let argList = [];
if (typeof functionArgs === 'string') {
/* Splits functionArgs by ',', ignoring all ',' in double quoted strings */
argList = functionArgs.split(/,(?=(?:(?:[^"]*"){2})*[^"]*$)/);
@@ -312,7 +305,7 @@ const setClickFunc = function (domId: string, functionName: string, functionArgs
argList.push(elemId);
}
functions.push(function () {
funs.push(function () {
const elem = document.querySelector(`[id="${elemId}"]`);
if (elem !== null) {
elem.addEventListener(
@@ -327,8 +320,8 @@ const setClickFunc = function (domId: string, functionName: string, functionArgs
}
};
export const bindFunctions = function (element: Element) {
functions.forEach(function (fun) {
export const bindFunctions = function (element) {
funs.forEach(function (fun) {
fun(element);
});
};
@@ -346,10 +339,8 @@ export const relationType = {
LOLLIPOP: 4,
};
const setupToolTips = function (element: Element) {
let tooltipElem: Selection<HTMLDivElement, unknown, HTMLElement, unknown> =
select('.mermaidTooltip');
// @ts-ignore - _groups is a dynamic property
const setupToolTips = function (element) {
let tooltipElem = select('.mermaidTooltip');
if ((tooltipElem._groups || tooltipElem)[0][0] === null) {
tooltipElem = select('body').append('div').attr('class', 'mermaidTooltip').style('opacity', 0);
}
@@ -359,14 +350,12 @@ const setupToolTips = function (element: Element) {
const nodes = svg.selectAll('g.node');
nodes
.on('mouseover', function () {
// @ts-expect-error - select is not part of the d3 type definition
const el = select(this);
const title = el.attr('title');
// Don't try to draw a tooltip if no data is provided
// Dont try to draw a tooltip if no data is provided
if (title === null) {
return;
}
// @ts-ignore - getBoundingClientRect is not part of the d3 type definition
const rect = this.getBoundingClientRect();
tooltipElem.transition().duration(200).style('opacity', '.9');
@@ -379,16 +368,15 @@ const setupToolTips = function (element: Element) {
})
.on('mouseout', function () {
tooltipElem.transition().duration(500).style('opacity', 0);
// @ts-expect-error - select is not part of the d3 type definition
const el = select(this);
el.classed('hover', false);
});
};
functions.push(setupToolTips);
funs.push(setupToolTips);
let direction = 'TB';
const getDirection = () => direction;
const setDirection = (dir: string) => {
const setDirection = (dir) => {
direction = dir;
};
@@ -424,5 +412,4 @@ export default {
lookUpDomId,
setDiagramTitle,
getDiagramTitle,
setClassLabel,
};

View File

@@ -1,11 +1,10 @@
// @ts-expect-error Jison doesn't export types
import { parser } from './parser/classDiagram';
import classDb from './classDb';
import { vi, describe, it, expect } from 'vitest';
import { vi } from 'vitest';
const spyOn = vi.spyOn;
describe('class diagram, ', function () {
describe('when parsing a class diagram', function () {
describe('when parsing an info graph it', function () {
beforeEach(function () {
parser.yy = classDb;
});
@@ -190,37 +189,6 @@ describe('class diagram, ', function () {
parser.parse(str);
});
it('should handle cssClass shorthand with members', () => {
parser.parse(`classDiagram-v2
class Class10:::exClass2 {
int[] id
List~int~ ids
test(List~int~ ids) List~bool~
testArray() bool[]
}`);
expect(classDb.getClass('Class10')).toMatchInlineSnapshot(`
{
"annotations": [],
"cssClasses": [
"exClass2",
],
"domId": "classId-Class10-27",
"id": "Class10",
"label": "Class10",
"members": [
"int[] id",
"List~int~ ids",
],
"methods": [
"test(List~int~ ids) List~bool~",
"testArray() bool[]",
],
"type": "",
}
`);
});
it('should handle method statements', function () {
const str =
'classDiagram\n' +
@@ -573,7 +541,7 @@ foo()
});
});
describe('when fetching data from a classDiagram it', function () {
describe('when fetching data from a classDiagram graph it', function () {
beforeEach(function () {
parser.yy = classDb;
parser.yy.clear();
@@ -978,191 +946,4 @@ foo()
expect(classDb.setTooltip).toHaveBeenCalledWith('Class1', 'A tooltip');
});
});
describe('when parsing classDiagram with text labels', () => {
beforeEach(function () {
parser.yy = classDb;
parser.yy.clear();
});
it('should parse a class with a text label', () => {
parser.parse(`classDiagram
class C1["Class 1 with text label"]
C1 --> C2
`);
const c1 = classDb.getClass('C1');
expect(c1.label).toBe('Class 1 with text label');
const c2 = classDb.getClass('C2');
expect(c2.label).toBe('C2');
});
it('should parse two classes with text labels', () => {
parser.parse(`classDiagram
class C1["Class 1 with text label"]
class C2["Class 2 with chars @?"]
C1 --> C2
`);
const c1 = classDb.getClass('C1');
expect(c1.label).toBe('Class 1 with text label');
const c2 = classDb.getClass('C2');
expect(c2.label).toBe('Class 2 with chars @?');
});
it('should parse a class with a text label and members', () => {
parser.parse(`classDiagram
class C1["Class 1 with text label"] {
+member1
}
C1 --> C2
`);
const c1 = classDb.getClass('C1');
expect(c1.label).toBe('Class 1 with text label');
expect(c1.members.length).toBe(1);
expect(c1.members[0]).toBe('+member1');
const c2 = classDb.getClass('C2');
expect(c2.label).toBe('C2');
});
it('should parse a class with a text label, members and annotation', () => {
parser.parse(`classDiagram
class C1["Class 1 with text label"] {
<<interface>>
+member1
}
C1 --> C2
`);
const c1 = classDb.getClass('C1');
expect(c1.label).toBe('Class 1 with text label');
expect(c1.members.length).toBe(1);
expect(c1.members[0]).toBe('+member1');
expect(c1.annotations.length).toBe(1);
expect(c1.annotations[0]).toBe('interface');
const c2 = classDb.getClass('C2');
expect(c2.label).toBe('C2');
});
it('should parse a class with text label and css class shorthand', () => {
parser.parse(`classDiagram
class C1["Class 1 with text label"]:::styleClass {
+member1
}
C1 --> C2
`);
const c1 = classDb.getClass('C1');
expect(c1.label).toBe('Class 1 with text label');
expect(c1.cssClasses.length).toBe(1);
expect(c1.members[0]).toBe('+member1');
expect(c1.cssClasses[0]).toBe('styleClass');
});
it('should parse a class with text label and css class', () => {
parser.parse(`classDiagram
class C1["Class 1 with text label"] {
+member1
}
C1 --> C2
cssClass "C1" styleClass
`);
const c1 = classDb.getClass('C1');
expect(c1.label).toBe('Class 1 with text label');
expect(c1.cssClasses.length).toBe(1);
expect(c1.members[0]).toBe('+member1');
expect(c1.cssClasses[0]).toBe('styleClass');
});
it('should parse two classes with text labels and css classes', () => {
parser.parse(`classDiagram
class C1["Class 1 with text label"] {
+member1
}
class C2["Long long long long long long long long long long label"]
C1 --> C2
cssClass "C1,C2" styleClass
`);
const c1 = classDb.getClass('C1');
expect(c1.label).toBe('Class 1 with text label');
expect(c1.cssClasses.length).toBe(1);
expect(c1.cssClasses[0]).toBe('styleClass');
const c2 = classDb.getClass('C2');
expect(c2.label).toBe('Long long long long long long long long long long label');
expect(c2.cssClasses.length).toBe(1);
expect(c2.cssClasses[0]).toBe('styleClass');
});
it('should parse two classes with text labels and css class shorthands', () => {
parser.parse(`classDiagram
class C1["Class 1 with text label"]:::styleClass1 {
+member1
}
class C2["Class 2 !@#$%^&*() label"]:::styleClass2
C1 --> C2
`);
const c1 = classDb.getClass('C1');
expect(c1.label).toBe('Class 1 with text label');
expect(c1.cssClasses.length).toBe(1);
expect(c1.cssClasses[0]).toBe('styleClass1');
const c2 = classDb.getClass('C2');
expect(c2.label).toBe('Class 2 !@#$%^&*() label');
expect(c2.cssClasses.length).toBe(1);
expect(c2.cssClasses[0]).toBe('styleClass2');
});
it('should parse multiple classes with same text labels', () => {
parser.parse(`classDiagram
class C1["Class with text label"]
class C2["Class with text label"]
class C3["Class with text label"]
C1 --> C2
C3 ..> C2
`);
const c1 = classDb.getClass('C1');
expect(c1.label).toBe('Class with text label');
const c2 = classDb.getClass('C2');
expect(c2.label).toBe('Class with text label');
const c3 = classDb.getClass('C3');
expect(c3.label).toBe('Class with text label');
});
it('should parse classes with different text labels', () => {
parser.parse(`classDiagram
class C1["OneWord"]
class C2["With, Comma"]
class C3["With (Brackets)"]
class C4["With [Brackets]"]
class C5["With {Brackets}"]
class C6[" "]
class C7["With 1 number"]
class C8["With . period..."]
class C9["With - dash"]
class C10["With _ underscore"]
class C11["With ' single quote"]
class C12["With ~!@#$%^&*()_+=-/?"]
class C13["With Città foreign language"]
`);
expect(classDb.getClass('C1').label).toBe('OneWord');
expect(classDb.getClass('C2').label).toBe('With, Comma');
expect(classDb.getClass('C3').label).toBe('With (Brackets)');
expect(classDb.getClass('C4').label).toBe('With [Brackets]');
expect(classDb.getClass('C5').label).toBe('With {Brackets}');
expect(classDb.getClass('C6').label).toBe(' ');
expect(classDb.getClass('C7').label).toBe('With 1 number');
expect(classDb.getClass('C8').label).toBe('With . period...');
expect(classDb.getClass('C9').label).toBe('With - dash');
expect(classDb.getClass('C10').label).toBe('With _ underscore');
expect(classDb.getClass('C11').label).toBe("With ' single quote");
expect(classDb.getClass('C12').label).toBe('With ~!@#$%^&*()_+=-/?');
expect(classDb.getClass('C13').label).toBe('With Città foreign language');
});
});
});

View File

@@ -0,0 +1,499 @@
import { select } from 'd3';
import * as graphlib from 'dagre-d3-es/src/graphlib/index.js';
import { log } from '../../logger';
import { getConfig } from '../../config';
import { render } from '../../dagre-wrapper/index.js';
import utils from '../../utils';
import { curveLinear } from 'd3';
import { interpolateToCurve, getStylesFromArray } from '../../utils';
import { setupGraphViewbox } from '../../setupGraphViewbox';
import common from '../common/common';
const sanitizeText = (txt) => common.sanitizeText(txt, getConfig());
let conf = {
dividerMargin: 10,
padding: 5,
textHeight: 10,
};
/**
* Function that adds the vertices found during parsing to the graph to be rendered.
*
* @param {Object<
* string,
* { cssClasses: string[]; text: string; id: string; type: string; domId: string }
* >} classes
* Object containing the vertices.
* @param {SVGGElement} g The graph that is to be drawn.
* @param _id
* @param diagObj
*/
export const addClasses = function (classes, g, _id, diagObj) {
// const svg = select(`[id="${svgId}"]`);
const keys = Object.keys(classes);
log.info('keys:', keys);
log.info(classes);
// Iterate through each item in the vertex object (containing all the vertices found) in the graph definition
keys.forEach(function (id) {
const vertex = classes[id];
/**
* Variable for storing the classes for the vertex
*
* @type {string}
*/
let cssClassStr = '';
if (vertex.cssClasses.length > 0) {
cssClassStr = cssClassStr + ' ' + vertex.cssClasses.join(' ');
}
// if (vertex.classes.length > 0) {
// classStr = vertex.classes.join(' ');
// }
const styles = { labelStyle: '' }; //getStylesFromArray(vertex.styles);
// Use vertex id as text in the box if no text is provided by the graph definition
let vertexText = vertex.text !== undefined ? vertex.text : vertex.id;
// We create a SVG label, either by delegating to addHtmlLabel or manually
// let vertexNode;
// if (evaluate(getConfig().flowchart.htmlLabels)) {
// const node = {
// label: vertexText.replace(
// eslint-disable-next-line @cspell/spellchecker
// /fa[lrsb]?:fa-[\w-]+/g,
// s => `<i class='${s.replace(':', ' ')}'></i>`
// )
// };
// vertexNode = addHtmlLabel(svg, node).node();
// vertexNode.parentNode.removeChild(vertexNode);
// } else {
// const svgLabel = document.createElementNS('http://www.w3.org/2000/svg', 'text');
// svgLabel.setAttribute('style', styles.labelStyle.replace('color:', 'fill:'));
// const rows = vertexText.split(common.lineBreakRegex);
// for (let j = 0; j < rows.length; j++) {
// const tspan = document.createElementNS('http://www.w3.org/2000/svg', 'tspan');
// tspan.setAttributeNS('http://www.w3.org/XML/1998/namespace', 'xml:space', 'preserve');
// tspan.setAttribute('dy', '1em');
// tspan.setAttribute('x', '1');
// tspan.textContent = rows[j];
// svgLabel.appendChild(tspan);
// }
// vertexNode = svgLabel;
// }
let radious = 0;
let _shape = '';
// Set the shape based parameters
switch (vertex.type) {
case 'class':
_shape = 'class_box';
break;
default:
_shape = 'class_box';
}
// Add the node
g.setNode(vertex.id, {
labelStyle: styles.labelStyle,
shape: _shape,
labelText: sanitizeText(vertexText),
classData: vertex,
rx: radious,
ry: radious,
class: cssClassStr,
style: styles.style,
id: vertex.id,
domId: vertex.domId,
tooltip: diagObj.db.getTooltip(vertex.id) || '',
haveCallback: vertex.haveCallback,
link: vertex.link,
width: vertex.type === 'group' ? 500 : undefined,
type: vertex.type,
padding: getConfig().flowchart.padding,
});
log.info('setNode', {
labelStyle: styles.labelStyle,
shape: _shape,
labelText: vertexText,
rx: radious,
ry: radious,
class: cssClassStr,
style: styles.style,
id: vertex.id,
width: vertex.type === 'group' ? 500 : undefined,
type: vertex.type,
padding: getConfig().flowchart.padding,
});
});
};
/**
* Function that adds the additional vertices (notes) found during parsing to the graph to be rendered.
*
* @param {{text: string; class: string; placement: number}[]} notes
* Object containing the additional vertices (notes).
* @param {SVGGElement} g The graph that is to be drawn.
* @param {number} startEdgeId starting index for note edge
* @param classes
*/
export const addNotes = function (notes, g, startEdgeId, classes) {
log.info(notes);
// Iterate through each item in the vertex object (containing all the vertices found) in the graph definition
notes.forEach(function (note, i) {
const vertex = note;
/**
* Variable for storing the classes for the vertex
*
* @type {string}
*/
let cssNoteStr = '';
const styles = { labelStyle: '', style: '' };
// Use vertex id as text in the box if no text is provided by the graph definition
let vertexText = vertex.text;
let radious = 0;
let _shape = 'note';
// Add the node
g.setNode(vertex.id, {
labelStyle: styles.labelStyle,
shape: _shape,
labelText: sanitizeText(vertexText),
noteData: vertex,
rx: radious,
ry: radious,
class: cssNoteStr,
style: styles.style,
id: vertex.id,
domId: vertex.id,
tooltip: '',
type: 'note',
padding: getConfig().flowchart.padding,
});
log.info('setNode', {
labelStyle: styles.labelStyle,
shape: _shape,
labelText: vertexText,
rx: radious,
ry: radious,
style: styles.style,
id: vertex.id,
type: 'note',
padding: getConfig().flowchart.padding,
});
if (!vertex.class || !(vertex.class in classes)) {
return;
}
const edgeId = startEdgeId + i;
const edgeData = {};
//Set relationship style and line type
edgeData.classes = 'relation';
edgeData.pattern = 'dotted';
edgeData.id = `edgeNote${edgeId}`;
// Set link type for rendering
edgeData.arrowhead = 'none';
log.info(`Note edge: ${JSON.stringify(edgeData)}, ${JSON.stringify(vertex)}`);
//Set edge extra labels
edgeData.startLabelRight = '';
edgeData.endLabelLeft = '';
//Set relation arrow types
edgeData.arrowTypeStart = 'none';
edgeData.arrowTypeEnd = 'none';
let style = 'fill:none';
let labelStyle = '';
edgeData.style = style;
edgeData.labelStyle = labelStyle;
edgeData.curve = interpolateToCurve(conf.curve, curveLinear);
// Add the edge to the graph
g.setEdge(vertex.id, vertex.class, edgeData, edgeId);
});
};
/**
* Add edges to graph based on parsed graph definition
*
* @param relations
* @param {object} g The graph object
*/
export const addRelations = function (relations, g) {
const conf = getConfig().flowchart;
let cnt = 0;
let defaultStyle;
let defaultLabelStyle;
// if (typeof relations.defaultStyle !== 'undefined') {
// const defaultStyles = getStylesFromArray(relations.defaultStyle);
// defaultStyle = defaultStyles.style;
// defaultLabelStyle = defaultStyles.labelStyle;
// }
relations.forEach(function (edge) {
cnt++;
const edgeData = {};
//Set relationship style and line type
edgeData.classes = 'relation';
edgeData.pattern = edge.relation.lineType == 1 ? 'dashed' : 'solid';
edgeData.id = 'id' + cnt;
// Set link type for rendering
if (edge.type === 'arrow_open') {
edgeData.arrowhead = 'none';
} else {
edgeData.arrowhead = 'normal';
}
log.info(edgeData, edge);
//Set edge extra labels
//edgeData.startLabelLeft = edge.relationTitle1;
edgeData.startLabelRight = edge.relationTitle1 === 'none' ? '' : edge.relationTitle1;
edgeData.endLabelLeft = edge.relationTitle2 === 'none' ? '' : edge.relationTitle2;
//edgeData.endLabelRight = edge.relationTitle2;
//Set relation arrow types
edgeData.arrowTypeStart = getArrowMarker(edge.relation.type1);
edgeData.arrowTypeEnd = getArrowMarker(edge.relation.type2);
let style = '';
let labelStyle = '';
if (edge.style !== undefined) {
const styles = getStylesFromArray(edge.style);
style = styles.style;
labelStyle = styles.labelStyle;
} else {
style = 'fill:none';
if (defaultStyle !== undefined) {
style = defaultStyle;
}
if (defaultLabelStyle !== undefined) {
labelStyle = defaultLabelStyle;
}
}
edgeData.style = style;
edgeData.labelStyle = labelStyle;
if (edge.interpolate !== undefined) {
edgeData.curve = interpolateToCurve(edge.interpolate, curveLinear);
} else if (relations.defaultInterpolate !== undefined) {
edgeData.curve = interpolateToCurve(relations.defaultInterpolate, curveLinear);
} else {
edgeData.curve = interpolateToCurve(conf.curve, curveLinear);
}
edge.text = edge.title;
if (edge.text === undefined) {
if (edge.style !== undefined) {
edgeData.arrowheadStyle = 'fill: #333';
}
} else {
edgeData.arrowheadStyle = 'fill: #333';
edgeData.labelpos = 'c';
if (getConfig().flowchart.htmlLabels) {
edgeData.labelType = 'html';
edgeData.label = '<span class="edgeLabel">' + edge.text + '</span>';
} else {
edgeData.labelType = 'text';
edgeData.label = edge.text.replace(common.lineBreakRegex, '\n');
if (edge.style === undefined) {
edgeData.style = edgeData.style || 'stroke: #333; stroke-width: 1.5px;fill:none';
}
edgeData.labelStyle = edgeData.labelStyle.replace('color:', 'fill:');
}
}
// Add the edge to the graph
g.setEdge(edge.id1, edge.id2, edgeData, cnt);
});
};
/**
* Merges the value of `conf` with the passed `cnf`
*
* @param {object} cnf Config to merge
*/
export const setConf = function (cnf) {
const keys = Object.keys(cnf);
keys.forEach(function (key) {
conf[key] = cnf[key];
});
};
/**
* Draws a flowchart in the tag with id: id based on the graph definition in text.
*
* @param {string} text
* @param {string} id
* @param _version
* @param diagObj
*/
export const draw = function (text, id, _version, diagObj) {
log.info('Drawing class - ', id);
const conf = getConfig().flowchart;
const securityLevel = getConfig().securityLevel;
log.info('config:', conf);
const nodeSpacing = conf.nodeSpacing || 50;
const rankSpacing = conf.rankSpacing || 50;
// Create the input mermaid.graph
const g = new graphlib.Graph({
multigraph: true,
compound: true,
})
.setGraph({
rankdir: diagObj.db.getDirection(),
nodesep: nodeSpacing,
ranksep: rankSpacing,
marginx: 8,
marginy: 8,
})
.setDefaultEdgeLabel(function () {
return {};
});
// Fetch the vertices/nodes and edges/links from the parsed graph definition
const classes = diagObj.db.getClasses();
const relations = diagObj.db.getRelations();
const notes = diagObj.db.getNotes();
log.info(relations);
addClasses(classes, g, id, diagObj);
addRelations(relations, g);
addNotes(notes, g, relations.length + 1, classes);
// Add custom shapes
// flowChartShapes.addToRenderV2(addShape);
// Set up an SVG group so that we can translate the final graph.
let sandboxElement;
if (securityLevel === 'sandbox') {
sandboxElement = select('#i' + id);
}
const root =
securityLevel === 'sandbox'
? select(sandboxElement.nodes()[0].contentDocument.body)
: select('body');
const svg = root.select(`[id="${id}"]`);
// Run the renderer. This is what draws the final graph.
const element = root.select('#' + id + ' g');
render(
element,
g,
['aggregation', 'extension', 'composition', 'dependency', 'lollipop'],
'classDiagram',
id
);
utils.insertTitle(svg, 'classTitleText', conf.titleTopMargin, diagObj.db.getDiagramTitle());
setupGraphViewbox(g, svg, conf.diagramPadding, conf.useMaxWidth);
// Add label rects for non html labels
if (!conf.htmlLabels) {
const doc = securityLevel === 'sandbox' ? sandboxElement.nodes()[0].contentDocument : document;
const labels = doc.querySelectorAll('[id="' + id + '"] .edgeLabel .label');
for (const label of labels) {
// Get dimensions of label
const dim = label.getBBox();
const rect = doc.createElementNS('http://www.w3.org/2000/svg', 'rect');
rect.setAttribute('rx', 0);
rect.setAttribute('ry', 0);
rect.setAttribute('width', dim.width);
rect.setAttribute('height', dim.height);
// rect.setAttribute('style', 'fill:#e8e8e8;');
label.insertBefore(rect, label.firstChild);
}
}
// If node has a link, wrap it in an anchor SVG object.
// const keys = Object.keys(classes);
// keys.forEach(function(key) {
// const vertex = classes[key];
// if (vertex.link) {
// const node = select('#' + id + ' [id="' + key + '"]');
// if (node) {
// const link = document.createElementNS('http://www.w3.org/2000/svg', 'a');
// link.setAttributeNS('http://www.w3.org/2000/svg', 'class', vertex.classes.join(' '));
// link.setAttributeNS('http://www.w3.org/2000/svg', 'href', vertex.link);
// link.setAttributeNS('http://www.w3.org/2000/svg', 'rel', 'noopener');
// const linkNode = node.insert(function() {
// return link;
// }, ':first-child');
// const shape = node.select('.label-container');
// if (shape) {
// linkNode.append(function() {
// return shape.node();
// });
// }
// const label = node.select('.label');
// if (label) {
// linkNode.append(function() {
// return label.node();
// });
// }
// }
// }
// });
};
/**
* Gets the arrow marker for a type index
*
* @param {number} type The type to look for
* @returns {'aggregation' | 'extension' | 'composition' | 'dependency'} The arrow marker
*/
function getArrowMarker(type) {
let marker;
switch (type) {
case 0:
marker = 'aggregation';
break;
case 1:
marker = 'extension';
break;
case 2:
marker = 'composition';
break;
case 3:
marker = 'dependency';
break;
case 4:
marker = 'lollipop';
break;
default:
marker = 'none';
}
return marker;
}
export default {
setConf,
draw,
};

View File

@@ -1,368 +0,0 @@
// @ts-ignore d3 types are not available
import { select, curveLinear } from 'd3';
import * as graphlib from 'dagre-d3-es/src/graphlib/index.js';
import { log } from '../../logger';
import { getConfig } from '../../config';
import { render } from '../../dagre-wrapper/index.js';
import utils from '../../utils';
import { interpolateToCurve, getStylesFromArray } from '../../utils';
import { setupGraphViewbox } from '../../setupGraphViewbox';
import common from '../common/common';
import { ClassRelation, ClassNote, ClassMap, EdgeData } from './classTypes';
const sanitizeText = (txt: string) => common.sanitizeText(txt, getConfig());
let conf = {
dividerMargin: 10,
padding: 5,
textHeight: 10,
curve: undefined,
};
/**
* Function that adds the vertices found during parsing to the graph to be rendered.
*
* @param classes - Object containing the vertices.
* @param g - The graph that is to be drawn.
* @param _id - id of the graph
* @param diagObj - The diagram object
*/
export const addClasses = function (
classes: ClassMap,
g: graphlib.Graph,
_id: string,
diagObj: any
) {
const keys = Object.keys(classes);
log.info('keys:', keys);
log.info(classes);
// Iterate through each item in the vertex object (containing all the vertices found) in the graph definition
keys.forEach(function (id) {
const vertex = classes[id];
/**
* Variable for storing the classes for the vertex
*/
let cssClassStr = '';
if (vertex.cssClasses.length > 0) {
cssClassStr = cssClassStr + ' ' + vertex.cssClasses.join(' ');
}
const styles = { labelStyle: '', style: '' }; //getStylesFromArray(vertex.styles);
// Use vertex id as text in the box if no text is provided by the graph definition
const vertexText = vertex.label ?? vertex.id;
const radius = 0;
const shape = 'class_box';
// Add the node
const node = {
labelStyle: styles.labelStyle,
shape: shape,
labelText: sanitizeText(vertexText),
classData: vertex,
rx: radius,
ry: radius,
class: cssClassStr,
style: styles.style,
id: vertex.id,
domId: vertex.domId,
tooltip: diagObj.db.getTooltip(vertex.id) || '',
haveCallback: vertex.haveCallback,
link: vertex.link,
width: vertex.type === 'group' ? 500 : undefined,
type: vertex.type,
// TODO V10: Flowchart ? Keeping flowchart for backwards compatibility. Remove in next major release
padding: getConfig().flowchart?.padding ?? getConfig().class?.padding,
};
g.setNode(vertex.id, node);
log.info('setNode', node);
});
};
/**
* Function that adds the additional vertices (notes) found during parsing to the graph to be rendered.
*
* @param notes - Object containing the additional vertices (notes).
* @param g - The graph that is to be drawn.
* @param startEdgeId - starting index for note edge
* @param classes - Classes
*/
export const addNotes = function (
notes: ClassNote[],
g: graphlib.Graph,
startEdgeId: number,
classes: ClassMap
) {
log.info(notes);
// Iterate through each item in the vertex object (containing all the vertices found) in the graph definition
notes.forEach(function (note, i) {
const vertex = note;
/**
* Variable for storing the classes for the vertex
*
*/
const cssNoteStr = '';
const styles = { labelStyle: '', style: '' };
// Use vertex id as text in the box if no text is provided by the graph definition
const vertexText = vertex.text;
const radius = 0;
const shape = 'note';
// Add the node
const node = {
labelStyle: styles.labelStyle,
shape: shape,
labelText: sanitizeText(vertexText),
noteData: vertex,
rx: radius,
ry: radius,
class: cssNoteStr,
style: styles.style,
id: vertex.id,
domId: vertex.id,
tooltip: '',
type: 'note',
// TODO V10: Flowchart ? Keeping flowchart for backwards compatibility. Remove in next major release
padding: getConfig().flowchart?.padding ?? getConfig().class?.padding,
};
g.setNode(vertex.id, node);
log.info('setNode', node);
if (!vertex.class || !(vertex.class in classes)) {
return;
}
const edgeId = startEdgeId + i;
const edgeData: EdgeData = {
id: `edgeNote${edgeId}`,
//Set relationship style and line type
classes: 'relation',
pattern: 'dotted',
// Set link type for rendering
arrowhead: 'none',
//Set edge extra labels
startLabelRight: '',
endLabelLeft: '',
//Set relation arrow types
arrowTypeStart: 'none',
arrowTypeEnd: 'none',
style: 'fill:none',
labelStyle: '',
curve: interpolateToCurve(conf.curve, curveLinear),
};
// Add the edge to the graph
g.setEdge(vertex.id, vertex.class, edgeData, edgeId);
});
};
/**
* Add edges to graph based on parsed graph definition
*
* @param relations -
* @param g - The graph object
*/
export const addRelations = function (relations: ClassRelation[], g: graphlib.Graph) {
const conf = getConfig().flowchart;
let cnt = 0;
relations.forEach(function (edge) {
cnt++;
const edgeData: EdgeData = {
//Set relationship style and line type
classes: 'relation',
pattern: edge.relation.lineType == 1 ? 'dashed' : 'solid',
id: 'id' + cnt,
// Set link type for rendering
arrowhead: edge.type === 'arrow_open' ? 'none' : 'normal',
//Set edge extra labels
startLabelRight: edge.relationTitle1 === 'none' ? '' : edge.relationTitle1,
endLabelLeft: edge.relationTitle2 === 'none' ? '' : edge.relationTitle2,
//Set relation arrow types
arrowTypeStart: getArrowMarker(edge.relation.type1),
arrowTypeEnd: getArrowMarker(edge.relation.type2),
style: 'fill:none',
labelStyle: '',
curve: interpolateToCurve(conf?.curve, curveLinear),
};
log.info(edgeData, edge);
if (edge.style !== undefined) {
const styles = getStylesFromArray(edge.style);
edgeData.style = styles.style;
edgeData.labelStyle = styles.labelStyle;
}
edge.text = edge.title;
if (edge.text === undefined) {
if (edge.style !== undefined) {
edgeData.arrowheadStyle = 'fill: #333';
}
} else {
edgeData.arrowheadStyle = 'fill: #333';
edgeData.labelpos = 'c';
// TODO V10: Flowchart ? Keeping flowchart for backwards compatibility. Remove in next major release
if (getConfig().flowchart?.htmlLabels ?? getConfig().htmlLabels) {
edgeData.labelType = 'html';
edgeData.label = '<span class="edgeLabel">' + edge.text + '</span>';
} else {
edgeData.labelType = 'text';
edgeData.label = edge.text.replace(common.lineBreakRegex, '\n');
if (edge.style === undefined) {
edgeData.style = edgeData.style || 'stroke: #333; stroke-width: 1.5px;fill:none';
}
edgeData.labelStyle = edgeData.labelStyle.replace('color:', 'fill:');
}
}
// Add the edge to the graph
g.setEdge(edge.id1, edge.id2, edgeData, cnt);
});
};
/**
* Merges the value of `conf` with the passed `cnf`
*
* @param cnf - Config to merge
*/
export const setConf = function (cnf: any) {
conf = {
...conf,
...cnf,
};
};
/**
* Draws a flowchart in the tag with id: id based on the graph definition in text.
*
* @param text -
* @param id -
* @param _version -
* @param diagObj -
*/
export const draw = function (text: string, id: string, _version: string, diagObj: any) {
log.info('Drawing class - ', id);
// TODO V10: Why flowchart? Might be a mistake when copying.
const conf = getConfig().flowchart ?? getConfig().class;
const securityLevel = getConfig().securityLevel;
log.info('config:', conf);
const nodeSpacing = conf?.nodeSpacing ?? 50;
const rankSpacing = conf?.rankSpacing ?? 50;
// Create the input mermaid.graph
const g: graphlib.Graph = new graphlib.Graph({
multigraph: true,
compound: true,
})
.setGraph({
rankdir: diagObj.db.getDirection(),
nodesep: nodeSpacing,
ranksep: rankSpacing,
marginx: 8,
marginy: 8,
})
.setDefaultEdgeLabel(function () {
return {};
});
// Fetch the vertices/nodes and edges/links from the parsed graph definition
const classes: ClassMap = diagObj.db.getClasses();
const relations: ClassRelation[] = diagObj.db.getRelations();
const notes: ClassNote[] = diagObj.db.getNotes();
log.info(relations);
addClasses(classes, g, id, diagObj);
addRelations(relations, g);
addNotes(notes, g, relations.length + 1, classes);
// Set up an SVG group so that we can translate the final graph.
let sandboxElement;
if (securityLevel === 'sandbox') {
sandboxElement = select('#i' + id);
}
const root =
securityLevel === 'sandbox'
? // @ts-ignore Ignore type error for now
select(sandboxElement.nodes()[0].contentDocument.body)
: select('body');
// @ts-ignore Ignore type error for now
const svg = root.select(`[id="${id}"]`);
// Run the renderer. This is what draws the final graph.
// @ts-ignore Ignore type error for now
const element = root.select('#' + id + ' g');
render(
element,
g,
['aggregation', 'extension', 'composition', 'dependency', 'lollipop'],
'classDiagram',
id
);
utils.insertTitle(svg, 'classTitleText', conf?.titleTopMargin ?? 5, diagObj.db.getDiagramTitle());
setupGraphViewbox(g, svg, conf?.diagramPadding, conf?.useMaxWidth);
// Add label rects for non html labels
if (!conf?.htmlLabels) {
// @ts-ignore Ignore type error for now
const doc = securityLevel === 'sandbox' ? sandboxElement.nodes()[0].contentDocument : document;
const labels = doc.querySelectorAll('[id="' + id + '"] .edgeLabel .label');
for (const label of labels) {
// Get dimensions of label
const dim = label.getBBox();
const rect = doc.createElementNS('http://www.w3.org/2000/svg', 'rect');
rect.setAttribute('rx', 0);
rect.setAttribute('ry', 0);
rect.setAttribute('width', dim.width);
rect.setAttribute('height', dim.height);
label.insertBefore(rect, label.firstChild);
}
}
};
/**
* Gets the arrow marker for a type index
*
* @param type - The type to look for
* @returns The arrow marker
*/
function getArrowMarker(type: number) {
let marker;
switch (type) {
case 0:
marker = 'aggregation';
break;
case 1:
marker = 'extension';
break;
case 2:
marker = 'composition';
break;
case 3:
marker = 'dependency';
break;
case 4:
marker = 'lollipop';
break;
default:
marker = 'none';
}
return marker;
}
export default {
setConf,
draw,
};

View File

@@ -1,55 +0,0 @@
export interface ClassNode {
id: string;
type: string;
label: string;
cssClasses: string[];
methods: string[];
members: string[];
annotations: string[];
domId: string;
link?: string;
linkTarget?: string;
haveCallback?: boolean;
tooltip?: string;
}
export interface ClassNote {
id: string;
class: string;
text: string;
}
export interface EdgeData {
arrowheadStyle?: string;
labelpos?: string;
labelType?: string;
label?: string;
classes: string;
pattern: string;
id: string;
arrowhead: string;
startLabelRight: string;
endLabelLeft: string;
arrowTypeStart: string;
arrowTypeEnd: string;
style: string;
labelStyle: string;
curve: any;
}
export type ClassRelation = {
id1: string;
id2: string;
relationTitle1: string;
relationTitle2: string;
type: string;
title: string;
text: string;
style: string[];
relation: {
type1: number;
type2: number;
lineType: number;
};
};
export type ClassMap = Record<string, ClassNode>;

View File

@@ -119,8 +119,6 @@ Function arguments are optional: 'call <callback_name>()' simply executes 'callb
"=" return 'EQUALS';
\= return 'EQUALS';
\w+ return 'ALPHA';
"[" return 'SQS';
"]" return 'SQE';
[!"#$%&'*+,-.`?\\/] return 'PUNCTUATION';
[0-9]+ return 'NUM';
[\u00AA\u00B5\u00BA\u00C0-\u00D6\u00D8-\u00F6]|
@@ -251,10 +249,6 @@ statements
| statement NEWLINE statements
;
classLabel
: SQS STR SQE { $$=$2; }
;
className
: alphaNumToken { $$=$1; }
| classLiteralName { $$=$1; }
@@ -280,15 +274,10 @@ statement
;
classStatement
: classIdentifier
| classIdentifier STYLE_SEPARATOR alphaNumToken {yy.setCssClass($1, $3);}
| classIdentifier STRUCT_START members STRUCT_STOP {yy.addMembers($1,$3);}
| classIdentifier STYLE_SEPARATOR alphaNumToken STRUCT_START members STRUCT_STOP {yy.setCssClass($1, $3);yy.addMembers($1,$5);}
;
classIdentifier
: CLASS className {$$=$2; yy.addClass($2);}
| CLASS className classLabel {$$=$2; yy.addClass($2);yy.setClassLabel($2, $3);}
: CLASS className {yy.addClass($2);}
| CLASS className STYLE_SEPARATOR alphaNumToken {yy.addClass($2);yy.setCssClass($2, $4);}
| CLASS className STRUCT_START members STRUCT_STOP {/*console.log($2,JSON.stringify($4));*/yy.addClass($2);yy.addMembers($2,$4);}
| CLASS className STYLE_SEPARATOR alphaNumToken STRUCT_START members STRUCT_STOP {yy.addClass($2);yy.setCssClass($2, $4);yy.addMembers($2,$6);}
;
annotationStatement

View File

@@ -35,8 +35,6 @@ accDescr\s*"{"\s* { this.begin("acc_descr_multili
<block>[A-Za-z_][A-Za-z0-9\-_\[\]\(\)]* return 'ATTRIBUTE_WORD'
<block>\"[^"]*\" return 'COMMENT';
<block>[\n]+ /* nothing */
<block>\%%(?!\{)[^\n]* /* skip comments in attribute block */
<block>[^\}]\%\%[^\n]* /* skip comments in attribute block */
<block>"}" { this.popState(); return 'BLOCK_STOP'; }
<block>. return yytext[0];

View File

@@ -9,8 +9,8 @@ import { log } from '../../../logger';
import { setupGraphViewbox } from '../../../setupGraphViewbox';
import common, { evaluate } from '../../common/common';
import { interpolateToCurve, getStylesFromArray } from '../../../utils';
import ELK from 'elkjs/lib/elk.bundled.js';
const elk = new ELK();
let elk;
const portPos = {};
@@ -371,10 +371,6 @@ const getEdgeStartEndPoint = (edge, dir) => {
let source = edge.start;
let target = edge.end;
// Save the original source and target
const sourceId = source;
const targetId = target;
const startNode = nodeDb[source];
const endNode = nodeDb[target];
@@ -391,7 +387,7 @@ const getEdgeStartEndPoint = (edge, dir) => {
}
// Add the edge to the graph
return { source, target, sourceId, targetId };
return { source, target };
};
/**
@@ -534,17 +530,14 @@ export const addEdges = function (edges, diagObj, graph, svg) {
const labelEl = insertEdgeLabel(labelsEl, edgeData);
// calculate start and end points of the edge, note that the source and target
// can be modified for shapes that have ports
const { source, target, sourceId, targetId } = getEdgeStartEndPoint(edge, dir);
// calculate start and end points of the edge
const { source, target } = getEdgeStartEndPoint(edge, dir);
log.debug('abc78 source and target', source, target);
// Add the edge to the graph
graph.edges.push({
id: 'e' + edge.start + edge.end,
sources: [source],
targets: [target],
sourceId,
targetId,
labelEl: labelEl,
labels: [
{
@@ -705,7 +698,7 @@ const calcOffset = function (src, dest, parentLookupDb) {
};
const insertEdge = function (edgesEl, edge, edgeData, diagObj, parentLookupDb) {
const offset = calcOffset(edge.sourceId, edge.targetId, parentLookupDb);
const offset = calcOffset(edge.sources[0], edge.targets[0], parentLookupDb);
const src = edge.sections[0].startPoint;
const dest = edge.sections[0].endPoint;
@@ -772,6 +765,10 @@ const insertChildren = (nodeArray, parentLookupDb) => {
*/
export const draw = async function (text, id, _version, diagObj) {
if (!elk) {
const ELK = (await import('elkjs/lib/elk.bundled.js')).default;
elk = new ELK();
}
// Add temporary render element
diagObj.db.clear();
nodeDb = {};

View File

@@ -4,15 +4,15 @@ import type { ExternalDiagramDefinition } from '../../diagram-api/types';
const id = 'flowchart-v2';
const detector: DiagramDetector = (txt, config) => {
if (
config?.flowchart?.defaultRenderer === 'dagre-d3' ||
config?.flowchart?.defaultRenderer === 'elk'
) {
if (config?.flowchart?.defaultRenderer === 'dagre-d3') {
return false;
}
if (config?.flowchart?.defaultRenderer === 'elk') {
return false;
}
// If we have configured to use dagre-wrapper then we should return true in this function for graph code thus making it use the new flowchart diagram
if (txt.match(/^\s*graph/) !== null && config?.flowchart?.defaultRenderer === 'dagre-wrapper') {
if (txt.match(/^\s*graph/) !== null) {
return true;
}
return txt.match(/^\s*flowchart/) !== null;

View File

@@ -5,10 +5,10 @@ const id = 'flowchart';
const detector: DiagramDetector = (txt, config) => {
// If we have conferred to only use new flow charts this function should always return false
// as in not signalling true for a legacy flowchart
if (
config?.flowchart?.defaultRenderer === 'dagre-wrapper' ||
config?.flowchart?.defaultRenderer === 'elk'
) {
if (config?.flowchart?.defaultRenderer === 'dagre-wrapper') {
return false;
}
if (config?.flowchart?.defaultRenderer === 'elk') {
return false;
}
return txt.match(/^\s*graph/) !== null;

View File

@@ -1,8 +1,5 @@
import moment from 'moment-mini';
import { sanitizeUrl } from '@braintree/sanitize-url';
import dayjs from 'dayjs';
import dayjsIsoWeek from 'dayjs/plugin/isoWeek.js';
import dayjsCustomParseFormat from 'dayjs/plugin/customParseFormat.js';
import dayjsAdvancedFormat from 'dayjs/plugin/advancedFormat.js';
import { log } from '../../logger';
import * as configApi from '../../config';
import utils from '../../utils';
@@ -18,10 +15,6 @@ import {
getDiagramTitle,
} from '../../commonDb';
dayjs.extend(dayjsIsoWeek);
dayjs.extend(dayjsCustomParseFormat);
dayjs.extend(dayjsAdvancedFormat);
let dateFormat = '';
let axisFormat = '';
let tickInterval = undefined;
@@ -169,58 +162,18 @@ export const isInvalidDate = function (date, dateFormat, excludes, includes) {
return excludes.includes(date.format(dateFormat.trim()));
};
/**
* TODO: fully document what this function does and what types it accepts
*
* @param {object} task - The task to check.
* @param {string | Date} task.startTime - Might be a `Date` or a `string`.
* TODO: is this always a Date?
* @param {string | Date} task.endTime - Might be a `Date` or a `string`.
* TODO: is this always a Date?
* @param {string} dateFormat - Dayjs date format string.
* @param {*} excludes
* @param {*} includes
*/
const checkTaskDates = function (task, dateFormat, excludes, includes) {
if (!excludes.length || task.manualEndTime) {
return;
}
let startTime;
if (task.startTime instanceof Date) {
startTime = dayjs(task.startTime);
} else {
startTime = dayjs(task.startTime, dateFormat, true);
}
startTime = startTime.add(1, 'd');
let originalEndTime;
if (task.endTime instanceof Date) {
originalEndTime = dayjs(task.endTime);
} else {
originalEndTime = dayjs(task.endTime, dateFormat, true);
}
const [fixedEndTime, renderEndTime] = fixTaskDates(
startTime,
originalEndTime,
dateFormat,
excludes,
includes
);
task.endTime = fixedEndTime.toDate();
let startTime = moment(task.startTime, dateFormat, true);
startTime.add(1, 'd');
let endTime = moment(task.endTime, dateFormat, true);
let renderEndTime = fixTaskDates(startTime, endTime, dateFormat, excludes, includes);
task.endTime = endTime.toDate();
task.renderEndTime = renderEndTime;
};
/**
* TODO: what does this function do?
*
* @param {dayjs.Dayjs} startTime - The start time.
* @param {dayjs.Dayjs} endTime - The original end time (will return a different end time if it's invalid).
* @param {string} dateFormat - Dayjs date format string.
* @param {*} excludes
* @param {*} includes
* @returns {[endTime: dayjs.Dayjs, renderEndTime: Date | null]} The new `endTime`, and the end time to render.
* `renderEndTime` may be `null` if `startTime` is newer than `endTime`.
*/
const fixTaskDates = function (startTime, endTime, dateFormat, excludes, includes) {
let invalid = false;
let renderEndTime = null;
@@ -230,11 +183,11 @@ const fixTaskDates = function (startTime, endTime, dateFormat, excludes, include
}
invalid = isInvalidDate(startTime, dateFormat, excludes, includes);
if (invalid) {
endTime = endTime.add(1, 'd');
endTime.add(1, 'd');
}
startTime = startTime.add(1, 'd');
startTime.add(1, 'd');
}
return [endTime, renderEndTime];
return renderEndTime;
};
const getStartDate = function (prevTime, dateFormat, str) {
@@ -270,7 +223,7 @@ const getStartDate = function (prevTime, dateFormat, str) {
}
// Check for actual date set
let mDate = dayjs(str, dateFormat.trim(), true);
let mDate = moment(str, dateFormat.trim(), true);
if (mDate.isValid()) {
return mDate.toDate();
} else {
@@ -285,14 +238,11 @@ const getStartDate = function (prevTime, dateFormat, str) {
};
/**
* Parse a string into the args for `dayjs.add()`.
* Parse a string as a moment duration.
*
* The string have to be compound by a value and a shorthand duration unit. For example `5d`
* represents 5 days.
*
* Please be aware that 1 day may be 23 or 25 hours, if the user lives in an area
* that has daylight savings time (or even 23.5/24.5 hours in Lord Howe Island!)
*
* Shorthand unit supported are:
*
* - `y` for years
@@ -304,36 +254,33 @@ const getStartDate = function (prevTime, dateFormat, str) {
* - `ms` for milliseconds
*
* @param {string} str - A string representing the duration.
* @returns {[value: number, unit: dayjs.ManipulateType]} Arguments to pass to `dayjs.add()`
* @returns {moment.Duration} A moment duration, including an invalid moment for invalid input
* string.
*/
const parseDuration = function (str) {
const statement = /^(\d+(?:\.\d+)?)([Mdhmswy]|ms)$/.exec(str.trim());
if (statement !== null) {
return [Number.parseFloat(statement[1]), statement[2]];
return moment.duration(Number.parseFloat(statement[1]), statement[2]);
}
// NaN means an invalid duration
return [NaN, 'ms'];
return moment.duration.invalid();
};
const getEndDate = function (prevTime, dateFormat, str, inclusive = false) {
str = str.trim();
// Check for actual date
let mDate = dayjs(str, dateFormat.trim(), true);
let mDate = moment(str, dateFormat.trim(), true);
if (mDate.isValid()) {
if (inclusive) {
mDate = mDate.add(1, 'd');
mDate.add(1, 'd');
}
return mDate.toDate();
}
let endTime = dayjs(prevTime);
const [durationValue, durationUnit] = parseDuration(str);
if (!Number.isNaN(durationValue)) {
const newEndTime = endTime.add(durationValue, durationUnit);
if (newEndTime.isValid()) {
endTime = newEndTime;
}
const endTime = moment(prevTime);
const duration = parseDuration(str);
if (duration.isValid()) {
endTime.add(duration);
}
return endTime.toDate();
};
@@ -399,7 +346,7 @@ const compileData = function (prevTask, dataStr) {
if (endTimeData) {
task.endTime = getEndDate(task.startTime, dateFormat, endTimeData, inclusiveEndDates);
task.manualEndTime = dayjs(endTimeData, 'YYYY-MM-DD', true).isValid();
task.manualEndTime = moment(endTimeData, 'YYYY-MM-DD', true).isValid();
checkTaskDates(task, dateFormat, excludes, includes);
}
@@ -549,7 +496,7 @@ const compileTasks = function () {
);
if (rawTasks[pos].endTime) {
rawTasks[pos].processed = true;
rawTasks[pos].manualEndTime = dayjs(
rawTasks[pos].manualEndTime = moment(
rawTasks[pos].raw.endTime.data,
'YYYY-MM-DD',
true

View File

@@ -1,5 +1,5 @@
// @ts-nocheck TODO: Fix TS
import dayjs from 'dayjs';
import moment from 'moment-mini';
import ganttDb from './ganttDb';
import { convert } from '../../tests/util';
@@ -9,7 +9,7 @@ describe('when using the ganttDb', function () {
});
describe('when using duration', function () {
it.each([{ str: '1d', expected: [1, 'd'] }])(
it.each([{ str: '1d', expected: moment.duration(1, 'd') }])(
'should %s resulting in $o duration',
({ str, expected }) => {
expect(ganttDb.parseDuration(str)).toEqual(expected);
@@ -19,11 +19,11 @@ describe('when using the ganttDb', function () {
it.each(
convert`
str | expected
${'1d'} | ${[1, 'd']}
${'2w'} | ${[2, 'w']}
${'1ms'} | ${[1, 'ms']}
${'0.1s'} | ${[0.1, 's']}
${'1f'} | ${[NaN, 'ms']}
${'1d'} | ${moment.duration(1, 'd')}
${'2w'} | ${moment.duration(2, 'w')}
${'1ms'} | ${moment.duration(1, 'ms')}
${'0.1s'} | ${moment.duration(100, 'ms')}
${'1f'} | ${moment.duration.invalid()}
`
)('should $str resulting in $expected duration', ({ str, expected }) => {
expect(ganttDb.parseDuration(str)).toEqual(expected);
@@ -171,44 +171,44 @@ describe('when using the ganttDb', function () {
const tasks = ganttDb.getTasks();
expect(tasks[0].startTime).toEqual(dayjs('2019-02-01', 'YYYY-MM-DD').toDate());
expect(tasks[0].endTime).toEqual(dayjs('2019-02-04', 'YYYY-MM-DD').toDate());
expect(tasks[0].renderEndTime).toEqual(dayjs('2019-02-02', 'YYYY-MM-DD').toDate());
expect(tasks[0].startTime).toEqual(moment('2019-02-01', 'YYYY-MM-DD').toDate());
expect(tasks[0].endTime).toEqual(moment('2019-02-04', 'YYYY-MM-DD').toDate());
expect(tasks[0].renderEndTime).toEqual(moment('2019-02-02', 'YYYY-MM-DD').toDate());
expect(tasks[0].id).toEqual('id1');
expect(tasks[0].task).toEqual('test1');
expect(tasks[1].startTime).toEqual(dayjs('2019-02-04', 'YYYY-MM-DD').toDate());
expect(tasks[1].endTime).toEqual(dayjs('2019-02-07', 'YYYY-MM-DD').toDate());
expect(tasks[1].renderEndTime).toEqual(dayjs('2019-02-06', 'YYYY-MM-DD').toDate());
expect(tasks[1].startTime).toEqual(moment('2019-02-04', 'YYYY-MM-DD').toDate());
expect(tasks[1].endTime).toEqual(moment('2019-02-07', 'YYYY-MM-DD').toDate());
expect(tasks[1].renderEndTime).toEqual(moment('2019-02-06', 'YYYY-MM-DD').toDate());
expect(tasks[1].id).toEqual('id2');
expect(tasks[1].task).toEqual('test2');
expect(tasks[2].startTime).toEqual(dayjs('2019-02-07', 'YYYY-MM-DD').toDate());
expect(tasks[2].endTime).toEqual(dayjs('2019-02-20', 'YYYY-MM-DD').toDate());
expect(tasks[2].renderEndTime).toEqual(dayjs('2019-02-20', 'YYYY-MM-DD').toDate());
expect(tasks[2].startTime).toEqual(moment('2019-02-07', 'YYYY-MM-DD').toDate());
expect(tasks[2].endTime).toEqual(moment('2019-02-20', 'YYYY-MM-DD').toDate());
expect(tasks[2].renderEndTime).toEqual(moment('2019-02-20', 'YYYY-MM-DD').toDate());
expect(tasks[2].id).toEqual('id3');
expect(tasks[2].task).toEqual('test3');
expect(tasks[3].startTime).toEqual(dayjs('2019-02-01', 'YYYY-MM-DD').toDate());
expect(tasks[3].endTime).toEqual(dayjs('2019-02-20', 'YYYY-MM-DD').toDate());
expect(tasks[3].startTime).toEqual(moment('2019-02-01', 'YYYY-MM-DD').toDate());
expect(tasks[3].endTime).toEqual(moment('2019-02-20', 'YYYY-MM-DD').toDate());
expect(tasks[3].renderEndTime).toBeNull(); // Fixed end
expect(tasks[3].id).toEqual('id4');
expect(tasks[3].task).toEqual('test4');
expect(tasks[4].startTime).toEqual(dayjs('2019-02-20', 'YYYY-MM-DD').toDate());
expect(tasks[4].endTime).toEqual(dayjs('2019-02-21', 'YYYY-MM-DD').toDate());
expect(tasks[4].renderEndTime).toEqual(dayjs('2019-02-21', 'YYYY-MM-DD').toDate());
expect(tasks[4].startTime).toEqual(moment('2019-02-20', 'YYYY-MM-DD').toDate());
expect(tasks[4].endTime).toEqual(moment('2019-02-21', 'YYYY-MM-DD').toDate());
expect(tasks[4].renderEndTime).toEqual(moment('2019-02-21', 'YYYY-MM-DD').toDate());
expect(tasks[4].id).toEqual('id5');
expect(tasks[4].task).toEqual('test5');
expect(tasks[5].startTime).toEqual(dayjs('2019-02-13', 'YYYY-MM-DD').toDate());
expect(tasks[5].endTime).toEqual(dayjs('2019-02-18', 'YYYY-MM-DD').toDate());
expect(tasks[5].renderEndTime).toEqual(dayjs('2019-02-15', 'YYYY-MM-DD').toDate());
expect(tasks[5].startTime).toEqual(moment('2019-02-13', 'YYYY-MM-DD').toDate());
expect(tasks[5].endTime).toEqual(moment('2019-02-18', 'YYYY-MM-DD').toDate());
expect(tasks[5].renderEndTime).toEqual(moment('2019-02-15', 'YYYY-MM-DD').toDate());
expect(tasks[5].id).toEqual('id6');
expect(tasks[5].task).toEqual('test6');
expect(tasks[6].startTime).toEqual(dayjs('2019-02-18', 'YYYY-MM-DD').toDate());
expect(tasks[6].endTime).toEqual(dayjs('2019-02-19', 'YYYY-MM-DD').toDate());
expect(tasks[6].startTime).toEqual(moment('2019-02-18', 'YYYY-MM-DD').toDate());
expect(tasks[6].endTime).toEqual(moment('2019-02-19', 'YYYY-MM-DD').toDate());
expect(tasks[6].id).toEqual('id7');
expect(tasks[6].task).toEqual('test7');
});
@@ -243,103 +243,109 @@ describe('when using the ganttDb', function () {
const tasks = ganttDb.getTasks();
// Section - A section
expect(tasks[0].startTime).toEqual(dayjs('2014-01-06', 'YYYY-MM-DD').toDate());
expect(tasks[0].endTime).toEqual(dayjs('2014-01-08', 'YYYY-MM-DD').toDate());
expect(tasks[0].startTime).toEqual(moment('2014-01-06', 'YYYY-MM-DD').toDate());
expect(tasks[0].endTime).toEqual(moment('2014-01-08', 'YYYY-MM-DD').toDate());
expect(tasks[0].order).toEqual(0);
expect(tasks[0].id).toEqual('des1');
expect(tasks[0].task).toEqual('Completed task');
expect(tasks[1].startTime).toEqual(dayjs('2014-01-09', 'YYYY-MM-DD').toDate());
expect(tasks[1].endTime).toEqual(dayjs('2014-01-12', 'YYYY-MM-DD').toDate());
expect(tasks[1].startTime).toEqual(moment('2014-01-09', 'YYYY-MM-DD').toDate());
expect(tasks[1].endTime).toEqual(moment('2014-01-12', 'YYYY-MM-DD').toDate());
expect(tasks[1].order).toEqual(1);
expect(tasks[1].id).toEqual('des2');
expect(tasks[1].task).toEqual('Active task');
expect(tasks[2].startTime).toEqual(dayjs('2014-01-12', 'YYYY-MM-DD').toDate());
expect(tasks[2].endTime).toEqual(dayjs('2014-01-17', 'YYYY-MM-DD').toDate());
expect(tasks[2].startTime).toEqual(moment('2014-01-12', 'YYYY-MM-DD').toDate());
expect(tasks[2].endTime).toEqual(moment('2014-01-17', 'YYYY-MM-DD').toDate());
expect(tasks[2].order).toEqual(2);
expect(tasks[2].id).toEqual('des3');
expect(tasks[2].task).toEqual('Future task');
expect(tasks[3].startTime).toEqual(dayjs('2014-01-17', 'YYYY-MM-DD').toDate());
expect(tasks[3].endTime).toEqual(dayjs('2014-01-22', 'YYYY-MM-DD').toDate());
expect(tasks[3].startTime).toEqual(moment('2014-01-17', 'YYYY-MM-DD').toDate());
expect(tasks[3].endTime).toEqual(moment('2014-01-22', 'YYYY-MM-DD').toDate());
expect(tasks[3].order).toEqual(3);
expect(tasks[3].id).toEqual('des4');
expect(tasks[3].task).toEqual('Future task2');
// Section - Critical tasks
expect(tasks[4].startTime).toEqual(dayjs('2014-01-06', 'YYYY-MM-DD').toDate());
expect(tasks[4].endTime).toEqual(dayjs('2014-01-07', 'YYYY-MM-DD').toDate());
expect(tasks[4].startTime).toEqual(moment('2014-01-06', 'YYYY-MM-DD').toDate());
expect(tasks[4].endTime).toEqual(moment('2014-01-07', 'YYYY-MM-DD').toDate());
expect(tasks[4].order).toEqual(4);
expect(tasks[4].id).toEqual('task1');
expect(tasks[4].task).toEqual('Completed task in the critical line');
expect(tasks[5].startTime).toEqual(dayjs('2014-01-08', 'YYYY-MM-DD').toDate());
expect(tasks[5].endTime).toEqual(dayjs('2014-01-10', 'YYYY-MM-DD').toDate());
expect(tasks[5].startTime).toEqual(moment('2014-01-08', 'YYYY-MM-DD').toDate());
expect(tasks[5].endTime).toEqual(moment('2014-01-10', 'YYYY-MM-DD').toDate());
expect(tasks[5].order).toEqual(5);
expect(tasks[5].id).toEqual('task2');
expect(tasks[5].task).toEqual('Implement parser and jison');
expect(tasks[6].startTime).toEqual(dayjs('2014-01-10', 'YYYY-MM-DD').toDate());
expect(tasks[6].endTime).toEqual(dayjs('2014-01-13', 'YYYY-MM-DD').toDate());
expect(tasks[6].startTime).toEqual(moment('2014-01-10', 'YYYY-MM-DD').toDate());
expect(tasks[6].endTime).toEqual(moment('2014-01-13', 'YYYY-MM-DD').toDate());
expect(tasks[6].order).toEqual(6);
expect(tasks[6].id).toEqual('task3');
expect(tasks[6].task).toEqual('Create tests for parser');
expect(tasks[7].startTime).toEqual(dayjs('2014-01-13', 'YYYY-MM-DD').toDate());
expect(tasks[7].endTime).toEqual(dayjs('2014-01-18', 'YYYY-MM-DD').toDate());
expect(tasks[7].startTime).toEqual(moment('2014-01-13', 'YYYY-MM-DD').toDate());
expect(tasks[7].endTime).toEqual(moment('2014-01-18', 'YYYY-MM-DD').toDate());
expect(tasks[7].order).toEqual(7);
expect(tasks[7].id).toEqual('task4');
expect(tasks[7].task).toEqual('Future task in critical line');
expect(tasks[8].startTime).toEqual(dayjs('2014-01-18', 'YYYY-MM-DD').toDate());
expect(tasks[8].endTime).toEqual(dayjs('2014-01-20', 'YYYY-MM-DD').toDate());
expect(tasks[8].startTime).toEqual(moment('2014-01-18', 'YYYY-MM-DD').toDate());
expect(tasks[8].endTime).toEqual(moment('2014-01-20', 'YYYY-MM-DD').toDate());
expect(tasks[8].order).toEqual(8);
expect(tasks[8].id).toEqual('task5');
expect(tasks[8].task).toEqual('Create tests for renderer');
expect(tasks[9].startTime).toEqual(dayjs('2014-01-20', 'YYYY-MM-DD').toDate());
expect(tasks[9].endTime).toEqual(dayjs('2014-01-21', 'YYYY-MM-DD').toDate());
expect(tasks[9].startTime).toEqual(moment('2014-01-20', 'YYYY-MM-DD').toDate());
expect(tasks[9].endTime).toEqual(moment('2014-01-21', 'YYYY-MM-DD').toDate());
expect(tasks[9].order).toEqual(9);
expect(tasks[9].id).toEqual('task6');
expect(tasks[9].task).toEqual('Add to mermaid');
// Section - Documentation
expect(tasks[10].startTime).toEqual(dayjs('2014-01-08', 'YYYY-MM-DD').toDate());
expect(tasks[10].endTime).toEqual(dayjs('2014-01-11', 'YYYY-MM-DD').toDate());
expect(tasks[10].startTime).toEqual(moment('2014-01-08', 'YYYY-MM-DD').toDate());
expect(tasks[10].endTime).toEqual(moment('2014-01-11', 'YYYY-MM-DD').toDate());
expect(tasks[10].order).toEqual(10);
expect(tasks[10].id).toEqual('a1');
expect(tasks[10].task).toEqual('Describe gantt syntax');
expect(tasks[11].startTime).toEqual(dayjs('2014-01-11', 'YYYY-MM-DD').toDate());
expect(tasks[11].endTime).toEqual(dayjs('2014-01-11 20:00:00', 'YYYY-MM-DD HH:mm:ss').toDate());
expect(tasks[11].startTime).toEqual(moment('2014-01-11', 'YYYY-MM-DD').toDate());
expect(tasks[11].endTime).toEqual(
moment('2014-01-11 20:00:00', 'YYYY-MM-DD HH:mm:ss').toDate()
);
expect(tasks[11].order).toEqual(11);
expect(tasks[11].id).toEqual('task7');
expect(tasks[11].task).toEqual('Add gantt diagram to demo page');
expect(tasks[12].startTime).toEqual(dayjs('2014-01-11', 'YYYY-MM-DD').toDate());
expect(tasks[12].endTime).toEqual(dayjs('2014-01-13', 'YYYY-MM-DD').toDate());
expect(tasks[12].startTime).toEqual(moment('2014-01-11', 'YYYY-MM-DD').toDate());
expect(tasks[12].endTime).toEqual(moment('2014-01-13', 'YYYY-MM-DD').toDate());
expect(tasks[12].order).toEqual(12);
expect(tasks[12].id).toEqual('doc1');
expect(tasks[12].task).toEqual('Add another diagram to demo page');
// Section - Last section
expect(tasks[13].startTime).toEqual(dayjs('2014-01-13', 'YYYY-MM-DD').toDate());
expect(tasks[13].endTime).toEqual(dayjs('2014-01-16', 'YYYY-MM-DD').toDate());
expect(tasks[13].startTime).toEqual(moment('2014-01-13', 'YYYY-MM-DD').toDate());
expect(tasks[13].endTime).toEqual(moment('2014-01-16', 'YYYY-MM-DD').toDate());
expect(tasks[13].order).toEqual(13);
expect(tasks[13].id).toEqual('task8');
expect(tasks[13].task).toEqual('Describe gantt syntax');
expect(tasks[14].startTime).toEqual(dayjs('2014-01-16', 'YYYY-MM-DD').toDate());
expect(tasks[14].endTime).toEqual(dayjs('2014-01-16 20:00:00', 'YYYY-MM-DD HH:mm:ss').toDate());
expect(tasks[14].startTime).toEqual(moment('2014-01-16', 'YYYY-MM-DD').toDate());
expect(tasks[14].endTime).toEqual(
moment('2014-01-16 20:00:00', 'YYYY-MM-DD HH:mm:ss').toDate()
);
expect(tasks[14].order).toEqual(14);
expect(tasks[14].id).toEqual('task9');
expect(tasks[14].task).toEqual('Add gantt diagram to demo page');
expect(tasks[15].startTime).toEqual(
dayjs('2014-01-16 20:00:00', 'YYYY-MM-DD HH:mm:ss').toDate()
moment('2014-01-16 20:00:00', 'YYYY-MM-DD HH:mm:ss').toDate()
);
expect(tasks[15].endTime).toEqual(
moment('2014-01-18 20:00:00', 'YYYY-MM-DD HH:mm:ss').toDate()
);
expect(tasks[15].endTime).toEqual(dayjs('2014-01-18 20:00:00', 'YYYY-MM-DD HH:mm:ss').toDate());
expect(tasks[15].order).toEqual(15);
expect(tasks[15].id).toEqual('task10');
expect(tasks[15].task).toEqual('Add another diagram to demo page');
@@ -352,53 +358,19 @@ describe('when using the ganttDb', function () {
ganttDb.addTask('test2', 'id2,after id1,20d');
const tasks = ganttDb.getTasks();
expect(tasks[0].startTime).toEqual(dayjs('2019-09-30', 'YYYY-MM-DD').toDate());
expect(tasks[0].endTime).toEqual(dayjs('2019-10-11', 'YYYY-MM-DD').toDate());
expect(tasks[0].startTime).toEqual(moment('2019-09-30', 'YYYY-MM-DD').toDate());
expect(tasks[0].endTime).toEqual(moment('2019-10-11', 'YYYY-MM-DD').toDate());
expect(tasks[1].renderEndTime).toBeNull(); // Fixed end
expect(tasks[0].id).toEqual('id1');
expect(tasks[0].task).toEqual('test1');
expect(tasks[1].startTime).toEqual(dayjs('2019-10-11', 'YYYY-MM-DD').toDate());
expect(tasks[1].endTime).toEqual(dayjs('2019-10-31', 'YYYY-MM-DD').toDate());
expect(tasks[1].startTime).toEqual(moment('2019-10-11', 'YYYY-MM-DD').toDate());
expect(tasks[1].endTime).toEqual(moment('2019-10-31', 'YYYY-MM-DD').toDate());
expect(tasks[1].renderEndTime).toBeNull(); // Fixed end
expect(tasks[1].id).toEqual('id2');
expect(tasks[1].task).toEqual('test2');
});
/**
* Unfortunately, Vitest has no way of modifying the timezone at runtime, so
* in order to test this, please run this test with
*
* ```bash
* TZ='America/Los_Angeles' pnpm exec vitest run ganttDb
* ```
*/
/* c8 ignore start */ // tell code-coverage to ignore this block of code
describe.skipIf(process.env.TZ != 'America/Los_Angeles')(
'when using a timezone with daylight savings (only run if TZ="America/Los_Angeles")',
() => {
it('should add 1 day even on days with 25 hours', function () {
const startTime = new Date(2020, 10, 1);
expect(startTime.toISOString()).toBe('2020-11-01T07:00:00.000Z');
const endTime = new Date(2020, 10, 2);
expect(endTime.toISOString()).toBe('2020-11-02T08:00:00.000Z');
ganttDb.setDateFormat('YYYY-MM-DD');
ganttDb.addSection('Task handles 25 hour day');
ganttDb.addTask('daylight savings day', 'id1,2020-11-01,1d');
const tasks = ganttDb.getTasks();
expect(tasks[0].startTime).toEqual(startTime);
expect(tasks[0].endTime).toEqual(endTime);
// In USA states that use daylight savings, 2020-11-01 had 25 hours
const millisecondsIn25Hours = 25 * 60 * 60 * 1000;
expect(endTime - startTime).toEqual(millisecondsIn25Hours);
});
}
);
/* c8 ignore stop */
describe('when setting inclusive end dates', function () {
beforeEach(function () {
ganttDb.setDateFormat('YYYY-MM-DD');
@@ -408,13 +380,13 @@ describe('when using the ganttDb', function () {
});
it('should automatically add one day to all end dates', function () {
const tasks = ganttDb.getTasks();
expect(tasks[0].startTime).toEqual(dayjs('2019-02-01', 'YYYY-MM-DD').toDate());
expect(tasks[0].endTime).toEqual(dayjs('2019-02-02', 'YYYY-MM-DD').toDate());
expect(tasks[0].startTime).toEqual(moment('2019-02-01', 'YYYY-MM-DD').toDate());
expect(tasks[0].endTime).toEqual(moment('2019-02-02', 'YYYY-MM-DD').toDate());
expect(tasks[0].id).toEqual('id1');
expect(tasks[0].task).toEqual('test1');
expect(tasks[1].startTime).toEqual(dayjs('2019-02-01', 'YYYY-MM-DD').toDate());
expect(tasks[1].endTime).toEqual(dayjs('2019-02-04', 'YYYY-MM-DD').toDate());
expect(tasks[1].startTime).toEqual(moment('2019-02-01', 'YYYY-MM-DD').toDate());
expect(tasks[1].endTime).toEqual(moment('2019-02-04', 'YYYY-MM-DD').toDate());
expect(tasks[1].renderEndTime).toBeNull(); // Fixed end
expect(tasks[1].manualEndTime).toBeTruthy();
expect(tasks[1].id).toEqual('id2');

View File

@@ -1,4 +1,4 @@
import dayjs from 'dayjs';
import moment from 'moment-mini';
import { log } from '../../logger';
import {
select,
@@ -435,16 +435,16 @@ export const draw = function (text, id, version, diagObj) {
const excludeRanges = [];
let range = null;
let d = dayjs(minTime);
let d = moment(minTime);
while (d.valueOf() <= maxTime) {
if (diagObj.db.isInvalidDate(d, dateFormat, excludes, includes)) {
if (!range) {
range = {
start: d,
end: d,
start: d.clone(),
end: d.clone(),
};
} else {
range.end = d;
range.end = d.clone();
}
} else {
if (range) {
@@ -452,7 +452,7 @@ export const draw = function (text, id, version, diagObj) {
range = null;
}
}
d = d.add(1, 'd');
d.add(1, 'd');
}
const rectangles = svg.append('g').selectAll('rect').data(excludeRanges).enter();
@@ -467,7 +467,7 @@ export const draw = function (text, id, version, diagObj) {
})
.attr('y', conf.gridLineStartPadding)
.attr('width', function (d) {
const renderEnd = d.end.add(1, 'day');
const renderEnd = d.end.clone().add(1, 'day');
return timeScale(renderEnd) - timeScale(d.start);
})
.attr('height', h - theTopPad - conf.gridLineStartPadding)

View File

@@ -4,13 +4,9 @@ import { log } from '../../logger';
import { getConfig } from '../../config';
import { setupGraphViewbox } from '../../setupGraphViewbox';
import svgDraw from './svgDraw';
import cytoscape from 'cytoscape/dist/cytoscape.umd.js';
import coseBilkent from 'cytoscape-cose-bilkent';
import * as db from './mindmapDb';
// Inject the layout algorithm into cytoscape
cytoscape.use(coseBilkent);
let cytoscape;
/**
* @param {any} svg The svg element to draw the diagram onto
* @param {object} mindmap The mindmap data and hierarchy
@@ -93,7 +89,14 @@ function addNodes(mindmap, cy, conf, level) {
* @param conf
* @param cy
*/
function layoutMindmap(node, conf) {
async function layoutMindmap(node, conf) {
if (!cytoscape) {
cytoscape = (await import('cytoscape')).default;
const coseBilkent = (await import('cytoscape-cose-bilkent')).default;
// Inject the layout algorithm into cytoscape
cytoscape.use(coseBilkent);
}
return new Promise((resolve) => {
// Add temporary render element
const renderEl = select('body').append('div').attr('id', 'cy').attr('style', 'display:none');

View File

@@ -1,37 +1,25 @@
// @ts-ignore - db not typed yet
import { select, Selection } from 'd3';
// @ts-nocheck TODO: fix file
import { select } from 'd3';
import svgDraw from './svgDraw';
import { log } from '../../logger';
import { getConfig } from '../../config';
import { setupGraphViewbox } from '../../setupGraphViewbox';
import { Diagram } from '../../Diagram';
import { MermaidConfig } from '../../config.type';
interface Block<TDesc, TSection> {
number: number;
descr: TDesc;
section: TSection;
width: number;
padding: number;
maxHeight: number;
}
export const setConf = function (cnf) {
const keys = Object.keys(cnf);
interface TimelineTask {
id: number;
section: string;
type: string;
task: string;
score: number;
events: string[];
}
export const draw = function (text: string, id: string, version: string, diagObj: Diagram) {
keys.forEach(function (key) {
conf[key] = cnf[key];
});
};
export const draw = function (text, id, version, diagObj) {
//1. Fetch the configuration
const conf = getConfig();
// @ts-expect-error - wrong config?
const LEFT_MARGIN = conf.leftMargin ?? 50;
const LEFT_MARGIN = conf.leftMargin ? conf.leftMargin : 50;
//2. Clear the diagram db before parsing
diagObj.db.clear?.();
diagObj.db.clear();
//3. Parse the diagram text
diagObj.parser.parse(text + '\n');
@@ -46,19 +34,15 @@ export const draw = function (text: string, id: string, version: string, diagObj
}
const root =
securityLevel === 'sandbox'
? // @ts-ignore d3 types are wrong
select(sandboxElement.nodes()[0].contentDocument.body)
? select(sandboxElement.nodes()[0].contentDocument.body)
: select('body');
// @ts-ignore d3 types are wrong
const svg = root.select('#' + id);
svg.append('g');
//4. Fetch the diagram data
// @ts-expect-error - db not typed yet
const tasks: TimelineTask[] = diagObj.db.getTasks();
// @ts-expect-error - db not typed yet
const tasks = diagObj.db.getTasks();
const title = diagObj.db.getCommonDb().getDiagramTitle();
log.debug('task', tasks);
@@ -66,8 +50,7 @@ export const draw = function (text: string, id: string, version: string, diagObj
svgDraw.initGraphics(svg);
// fetch Sections
// @ts-expect-error - db not typed yet
const sections: string[] = diagObj.db.getSections();
const sections = diagObj.db.getSections();
log.debug('sections', sections);
let maxSectionHeight = 0;
@@ -84,8 +67,8 @@ export const draw = function (text: string, id: string, version: string, diagObj
let hasSections = true;
//Calculate the max height of the sections
sections.forEach(function (section: string) {
const sectionNode: Block<string, number> = {
sections.forEach(function (section) {
const sectionNode = {
number: sectionNumber,
descr: section,
section: sectionNumber,
@@ -104,9 +87,8 @@ export const draw = function (text: string, id: string, version: string, diagObj
log.debug('tasks.length', tasks.length);
//calculate max task height
// for loop till tasks.length
for (const [i, task] of tasks.entries()) {
const taskNode: Block<TimelineTask, string> = {
const taskNode = {
number: i,
descr: task,
section: task.section,
@@ -142,14 +124,11 @@ export const draw = function (text: string, id: string, version: string, diagObj
if (sections && sections.length > 0) {
sections.forEach((section) => {
//filter task where tasks.section == section
const tasksForSection = tasks.filter((task) => task.section === section);
const sectionNode: Block<string, number> = {
const sectionNode = {
number: sectionNumber,
descr: section,
section: sectionNumber,
width: 200 * Math.max(tasksForSection.length, 1) - 50,
width: 150,
padding: 20,
maxHeight: maxSectionHeight,
};
@@ -163,6 +142,8 @@ export const draw = function (text: string, id: string, version: string, diagObj
masterY += maxSectionHeight + 50;
//draw tasks for this section
//filter task where tasks.section == section
const tasksForSection = tasks.filter((task) => task.section === section);
if (tasksForSection.length > 0) {
drawTasks(
svg,
@@ -234,25 +215,25 @@ export const draw = function (text: string, id: string, version: string, diagObj
setupGraphViewbox(
undefined,
svg,
conf.timeline?.padding ?? 50,
conf.timeline?.useMaxWidth ?? false
conf.timeline.padding ? conf.timeline.padding : 50,
conf.timeline.useMaxWidth ? conf.timeline.useMaxWidth : false
);
// addSVGAccessibilityFields(diagObj.db, diagram, id);
};
export const drawTasks = function (
diagram: Selection<SVGElement, unknown, null, undefined>,
tasks: TimelineTask[],
sectionColor: number,
masterX: number,
masterY: number,
maxTaskHeight: number,
conf: MermaidConfig,
maxEventCount: number,
maxEventLineLength: number,
maxSectionHeight: number,
isWithoutSections: boolean
diagram,
tasks,
sectionColor,
masterX,
masterY,
maxTaskHeight,
conf,
maxEventCount,
maxEventLineLength,
maxSectionHeight,
isWithoutSections
) {
// Draw the tasks
for (const task of tasks) {
@@ -268,7 +249,6 @@ export const drawTasks = function (
log.debug('taskNode', taskNode);
// create task wrapper
const taskWrapper = diagram.append('g').attr('class', 'taskWrapper');
const node = svgDraw.drawNode(taskWrapper, taskNode, sectionColor, conf);
const taskHeight = node.height;
@@ -283,11 +263,11 @@ export const drawTasks = function (
if (task.events) {
// draw a line between the task and the events
const lineWrapper = diagram.append('g').attr('class', 'lineWrapper');
let lineLength = maxTaskHeight;
let linelength = maxTaskHeight;
//add margin to task
masterY += 100;
lineLength =
lineLength + drawEvents(diagram, task.events, sectionColor, masterX, masterY, conf);
linelength =
linelength + drawEvents(diagram, task.events, sectionColor, masterX, masterY, conf);
masterY -= 100;
lineWrapper
@@ -310,7 +290,7 @@ export const drawTasks = function (
}
masterX = masterX + 200;
if (isWithoutSections && !conf.timeline?.disableMulticolor) {
if (isWithoutSections && !getConfig().timeline.disableMulticolor) {
sectionColor++;
}
}
@@ -319,21 +299,14 @@ export const drawTasks = function (
masterY = masterY - 10;
};
export const drawEvents = function (
diagram: Selection<SVGElement, unknown, null, undefined>,
events: string[],
sectionColor: number,
masterX: number,
masterY: number,
conf: MermaidConfig
) {
export const drawEvents = function (diagram, events, sectionColor, masterX, masterY, conf) {
let maxEventHeight = 0;
const eventBeginY = masterY;
masterY = masterY + 100;
// Draw the events
for (const event of events) {
// create node from event
const eventNode: Block<string, number> = {
const eventNode = {
descr: event,
section: sectionColor,
number: sectionColor,
@@ -358,8 +331,6 @@ export const drawEvents = function (
};
export default {
setConf: () => {
// no-op
},
setConf,
draw,
};

View File

@@ -224,17 +224,6 @@ export const drawTasks = function (diagram, tasks, verticalPos) {
num = sectionNumber % fills.length;
colour = textColours[sectionNumber % textColours.length];
// count how many consecutive tasks have the same section
let taskInSectionCount = 0;
const currentSection = task.section;
for (let taskIndex = i; taskIndex < tasks.length; taskIndex++) {
if (tasks[taskIndex].section == currentSection) {
taskInSectionCount = taskInSectionCount + 1;
} else {
break;
}
}
const section = {
x: i * conf.taskMargin + i * conf.width + LEFT_MARGIN,
y: 50,
@@ -242,7 +231,6 @@ export const drawTasks = function (diagram, tasks, verticalPos) {
fill,
num,
colour,
taskCount: taskInSectionCount,
};
svgDraw.drawSection(diagram, section, conf);

View File

@@ -196,10 +196,7 @@ export const drawSection = function (elem, section, conf) {
rect.x = section.x;
rect.y = section.y;
rect.fill = section.fill;
// section width covers all nested tasks
rect.width =
conf.width * section.taskCount + // width of the tasks
conf.diagramMarginX * (section.taskCount - 1); // width of space between tasks
rect.width = conf.width;
rect.height = conf.height;
rect.class = 'journey-section section-type-' + section.num;
rect.rx = 3;

View File

@@ -76,26 +76,6 @@ classDiagram
Naming convention: a class name should be composed only of alphanumeric characters (including unicode), and underscores.
### Class labels
In case you need to provide a label for a class, you can use the following syntax:
```mermaid-example
classDiagram
class Animal["Animal with a label"]
class Car["Car with *! symbols"]
Animal --> Car
```
You can also use backticks to escape special characters in the label:
```mermaid-example
classDiagram
class `Animal Class!`
class `Car Class`
`Animal Class!` --> `Car Class`
```
## Defining Members of a class
UML provides mechanisms to represent class members such as attributes and methods, as well as additional information about them.
@@ -497,11 +477,11 @@ Beginner's tip—a full example using interactive links in an HTML page:
### Styling a node
It is possible to apply specific styles such as a thicker border or a different background color to individual nodes. This is done by predefining classes in css styles that can be applied from the graph definition using the `cssClass` statement or the `:::` short hand.
It is possible to apply specific styles such as a thicker border or a different background color to individual nodes. This is done by predefining classes in css styles that can be applied from the graph definition:
```html
<style>
.styleClass > rect {
.cssClass > rect {
fill: #ff0000;
stroke: #ffff00;
stroke-width: 4px;
@@ -512,27 +492,27 @@ It is possible to apply specific styles such as a thicker border or a different
Then attaching that class to a specific node:
```
cssClass "nodeId1" styleClass;
cssClass "nodeId1" cssClass;
```
It is also possible to attach a class to a list of nodes in one statement:
```
cssClass "nodeId1,nodeId2" styleClass;
cssClass "nodeId1,nodeId2" cssClass;
```
A shorter form of adding a class is to attach the classname to the node using the `:::` operator:
```mermaid-example
classDiagram
class Animal:::styleClass
class Animal:::cssClass
```
Or:
```mermaid-example
classDiagram
class Animal:::styleClass {
class Animal:::cssClass {
-int sizeInFeet
-canEat()
}

View File

@@ -116,7 +116,7 @@ The following formatting options are supported:
| `YY` | 14 | 2 digit year |
| `Q` | 1..4 | Quarter of year. Sets month to first month in quarter. |
| `M MM` | 1..12 | Month number |
| `MMM MMMM` | January..Dec | Month name in locale set by `dayjs.locale()` |
| `MMM MMMM` | January..Dec | Month name in locale set by `moment.locale()` |
| `D DD` | 1..31 | Day of month |
| `Do` | 1st..31st | Day of month with ordinal |
| `DDD DDDD` | 1..365 | Day of year |
@@ -132,7 +132,7 @@ The following formatting options are supported:
| `SSS` | 0..999 | Thousandths of a second |
| `Z ZZ` | +12:00 | Offset from UTC as +-HH:mm, +-HHmm, or Z |
More info in: https://day.js.org/docs/en/parse/string-format/
More info in: https://momentjs.com/docs/#/parsing/string-format/
### Output date format on the axis

View File

@@ -2,7 +2,7 @@
/* eslint-disable @typescript-eslint/no-unused-vars */
/* eslint-disable @typescript-eslint/no-empty-function */
/* eslint-disable no-console */
import dayjs from 'dayjs';
import moment from 'moment-mini';
export type LogLevel = 'trace' | 'debug' | 'info' | 'warn' | 'error' | 'fatal';
@@ -85,6 +85,6 @@ export const setLogLevel = function (level: keyof typeof LEVELS | number | strin
* @returns The format with the timestamp and log level
*/
const format = (level: Uppercase<LogLevel>): string => {
const time = dayjs().format('ss.SSS');
const time = moment().format('ss.SSS');
return `%c${time} : ${level} : `;
};

View File

@@ -2,7 +2,6 @@ import mermaid from './mermaid';
import { mermaidAPI } from './mermaidAPI';
import './diagram-api/diagram-orchestration';
import { addDiagrams } from './diagram-api/diagram-orchestration';
import { beforeAll, describe, it, expect, vi } from 'vitest';
beforeAll(async () => {
addDiagrams();
@@ -152,11 +151,7 @@ describe('when using mermaid and ', () => {
describe('checking validity of input ', () => {
it('should throw for an invalid definition', async () => {
await expect(
mermaid.parse('this is not a mermaid diagram definition')
).rejects.toThrowErrorMatchingInlineSnapshot(
'"No diagram type detected matching given configuration for text: this is not a mermaid diagram definition"'
);
await expect(mermaid.parse('this is not a mermaid diagram definition')).rejects.toThrow();
});
it('should not throw for a valid flow definition', async () => {
@@ -165,12 +160,7 @@ describe('when using mermaid and ', () => {
).resolves.not.toThrow();
});
it('should throw for an invalid flow definition', async () => {
await expect(mermaid.parse('graph TQ;A--x|text including URL space|B;')).rejects
.toThrowErrorMatchingInlineSnapshot(`
"Lexical error on line 1. Unrecognized text.
graph TQ;A--x|text includ
-----^"
`);
await expect(mermaid.parse('graph TQ;A--x|text including URL space|B;')).rejects.toThrow();
});
it('should not throw for a valid sequenceDiagram definition (mmds1)', async () => {
@@ -198,12 +188,7 @@ describe('when using mermaid and ', () => {
'else isSick\n' +
'Bob-->Alice: Feel sick...\n' +
'end';
await expect(mermaid.parse(text)).rejects.toThrowErrorMatchingInlineSnapshot(`
"Parse error on line 2:
...equenceDiagramAlice:->Bob: Hello Bob, h...
----------------------^
Expecting 'SOLID_OPEN_ARROW', 'DOTTED_OPEN_ARROW', 'SOLID_ARROW', 'DOTTED_ARROW', 'SOLID_CROSS', 'DOTTED_CROSS', 'SOLID_POINT', 'DOTTED_POINT', got 'TXT'"
`);
await expect(mermaid.parse(text)).rejects.toThrow();
});
it('should return false for invalid definition WITH a parseError() callback defined', async () => {
@@ -211,11 +196,7 @@ describe('when using mermaid and ', () => {
mermaid.setParseErrorHandler(() => {
parseErrorWasCalled = true;
});
await expect(
mermaid.parse('this is not a mermaid diagram definition')
).rejects.toThrowErrorMatchingInlineSnapshot(
'"No diagram type detected matching given configuration for text: this is not a mermaid diagram definition"'
);
await expect(mermaid.parse('this is not a mermaid diagram definition')).rejects.toThrow();
expect(parseErrorWasCalled).toEqual(true);
});
});

View File

@@ -12,7 +12,6 @@ import type { ParseErrorFunction } from './Diagram';
import { isDetailedError } from './utils';
import type { DetailedError } from './utils';
import { ExternalDiagramDefinition } from './diagram-api/types';
import { UnknownDiagramError } from './errors';
export type {
MermaidConfig,
@@ -21,7 +20,6 @@ export type {
ParseErrorFunction,
RenderResult,
ParseOptions,
UnknownDiagramError,
};
export interface RunOptions {
@@ -164,7 +162,7 @@ const runThrowsErrors = async function (
log.debug('Detected early reinit: ', init);
}
try {
const { svg, bindFunctions } = await render(id, txt, element);
const { svg, bindFunctions } = await mermaidAPI.render(id, txt, element);
element.innerHTML = svg;
if (postRenderCallback) {
await postRenderCallback(id);
@@ -315,7 +313,7 @@ const executeQueue = async () => {
*/
const parse = async (text: string, parseOptions?: ParseOptions): Promise<boolean | void> => {
return new Promise((resolve, reject) => {
// This promise will resolve when the render call is done.
// This promise will resolve when the mermaidAPI.render call is done.
// It will be queued first and will be executed when it is first in line
const performCall = () =>
new Promise((res, rej) => {
@@ -339,29 +337,6 @@ const parse = async (text: string, parseOptions?: ParseOptions): Promise<boolean
});
};
/**
* Function that renders an svg with a graph from a chart definition. Usage example below.
*
* ```javascript
* element = document.querySelector('#graphDiv');
* const graphDefinition = 'graph TB\na-->b';
* const { svg, bindFunctions } = await mermaid.render('graphDiv', graphDefinition);
* element.innerHTML = svg;
* bindFunctions?.(element);
* ```
*
* @remarks
* Multiple calls to this function will be enqueued to run serially.
*
* @param id - The id for the SVG element (the element to be rendered)
* @param text - The text for the graph definition
* @param container - HTML element where the svg will be inserted. (Is usually element with the .mermaid class)
* If no svgContainingElement is provided then the SVG element will be appended to the body.
* Selector to element in which a div with the graph temporarily will be
* inserted. If one is provided a hidden div will be inserted in the body of the page instead. The
* element will be removed when rendering is completed.
* @returns Returns the SVG Definition and BindFunctions.
*/
const render = (id: string, text: string, container?: Element): Promise<RenderResult> => {
return new Promise((resolve, reject) => {
// This promise will resolve when the mermaidAPI.render call is done.

View File

@@ -666,7 +666,7 @@ describe('mermaidAPI', () => {
).rejects.toThrow(
'Diagrams beginning with --- are not valid. ' +
'If you were trying to use a YAML front-matter, please ensure that ' +
"you've correctly opened and closed the YAML front-matter with un-indented `---` blocks"
"you've correctly opened and closed the YAML front-matter with unindented `---` blocks"
);
});
it('does not throw for a valid definition', async () => {
@@ -678,7 +678,7 @@ describe('mermaidAPI', () => {
await expect(
mermaidAPI.parse('this is not a mermaid diagram definition')
).rejects.toThrowErrorMatchingInlineSnapshot(
'"No diagram type detected matching given configuration for text: this is not a mermaid diagram definition"'
'"No diagram type detected for text: this is not a mermaid diagram definition"'
);
});
it('returns false for invalid definition with silent option', async () => {

View File

@@ -370,9 +370,30 @@ export const removeExistingElements = (
};
/**
* @deprecated - use the `mermaid.render` function instead of `mermaid.mermaidAPI.render`
* Function that renders an svg with a graph from a chart definition. Usage example below.
*
* Deprecated for external use.
* ```javascript
* mermaidAPI.initialize({
* startOnLoad: true,
* });
* $(function () {
* const graphDefinition = 'graph TB\na-->b';
* const cb = function (svgGraph) {
* console.log(svgGraph);
* };
* mermaidAPI.render('id1', graphDefinition, cb);
* });
* ```
*
* @param id - The id for the SVG element (the element to be rendered)
* @param text - The text for the graph definition
* @param cb - Callback which is called after rendering is finished with the svg code as in param.
* @param svgContainingElement - HTML element where the svg will be inserted. (Is usually element with the .mermaid class)
* If no svgContainingElement is provided then the SVG element will be appended to the body.
* Selector to element in which a div with the graph temporarily will be
* inserted. If one is provided a hidden div will be inserted in the body of the page instead. The
* element will be removed when rendering is completed.
* @returns Returns the rendered element as a string containing the SVG definition.
*/
const render = async function (
@@ -558,10 +579,7 @@ const render = async function (
function initialize(options: MermaidConfig = {}) {
// Handle legacy location of font-family configuration
if (options?.fontFamily && !options.themeVariables?.fontFamily) {
if (!options.themeVariables) {
options.themeVariables = {};
}
options.themeVariables.fontFamily = options.fontFamily;
options.themeVariables = { fontFamily: options.fontFamily };
}
// Set default options

View File

@@ -7,8 +7,8 @@ Jest code
```ts
it.each`
str | expected
${'1d'} | ${dayjs.duration(1, 'd')}
${'2w'} | ${dayjs.duration(2, 'w')}
${'1d'} | ${moment.duration(1, 'd')}
${'2w'} | ${moment.duration(2, 'w')}
`('should parse $str to $expected duration', ({ str, expected }) => {
expect(yourFunction(str)).toEqual(expected);
});
@@ -18,8 +18,8 @@ Vitest code
```ts
it.each(convert`
str | expected
${'1d'} | ${dayjs.duration(1, 'd')}
${'2w'} | ${dayjs.duration(2, 'w')}
${'1d'} | ${moment.duration(1, 'd')}
${'2w'} | ${moment.duration(2, 'w')}
`)('should parse $str to $expected duration', ({ str, expected }) => {
expect(yourFunction(str)).toEqual(expected);
});

View File

@@ -230,7 +230,7 @@ export function interpolateToCurve(
* @param config - Configuration passed to MermaidJS
* @returns The formatted URL or `undefined`.
*/
export function formatUrl(linkStr: string, config: MermaidConfig): string | undefined {
export function formatUrl(linkStr: string, config: { securityLevel: string }): string | undefined {
const url = linkStr.trim();
if (url) {

225
pnpm-lock.yaml generated
View File

@@ -48,10 +48,10 @@ importers:
version: 4.2.1
'@typescript-eslint/eslint-plugin':
specifier: ^5.48.2
version: 5.48.2_iljmjqxcygjq3saipl7gerxpvi
version: 5.48.2_azmbqzqvrlvblbdtiwxwvyvjjy
'@typescript-eslint/parser':
specifier: ^5.48.2
version: 5.48.2_yygwinqv3a2io74xmwofqb7uka
version: 5.48.2_et5x32uxl7z5ldub3ye5rhlyqm
'@vitest/coverage-c8':
specifier: ^0.28.4
version: 0.28.4_vun5xzxu3tkrssf3erdbijyyki
@@ -93,7 +93,7 @@ importers:
version: 7.1.0
eslint-plugin-jest:
specifier: ^27.1.5
version: 27.1.5_5rcd23qw3h5vuffwo2owxb3hw4
version: 27.1.5_i5clxtuiaceouxhg5syqkw5wwi
eslint-plugin-jsdoc:
specifier: ^39.6.2
version: 39.6.2_eslint@8.32.0
@@ -162,10 +162,10 @@ importers:
version: 1.15.4
ts-node:
specifier: ^10.9.1
version: 10.9.1_cbe7ovvae6zqfnmtgctpgpys54
version: 10.9.1_w6ufic3jqylcjznzspnj4wjqfe
typescript:
specifier: ^4.8.4
version: 4.8.4
version: 4.9.5
vite:
specifier: ^4.1.1
version: 4.1.1_@types+node@18.11.9
@@ -191,11 +191,8 @@ importers:
specifier: ^7.4.0
version: 7.8.2
dagre-d3-es:
specifier: 7.0.9
version: 7.0.9
dayjs:
specifier: ^1.11.7
version: 1.11.7
specifier: 7.0.8
version: 7.0.8
dompurify:
specifier: 2.4.3
version: 2.4.3
@@ -208,6 +205,9 @@ importers:
lodash-es:
specifier: ^4.17.21
version: 4.17.21
moment-mini:
specifier: ^2.29.4
version: 2.29.4
non-layered-tidy-tree-layout:
specifier: ^2.0.2
version: 2.0.2
@@ -1172,7 +1172,6 @@ packages:
/@braintree/sanitize-url/6.0.0:
resolution: {integrity: sha512-mgmE7XBYY/21erpzhexk4Cj1cyTQ9LzvnTxtzM17BJ7ERMNE6W72mQRo0I1Ud8eFJ+RVVIcBNhLFZ3GX4XFz5w==}
dev: false
/@colors/colors/1.5.0:
resolution: {integrity: sha512-ooWCrlZP11i8GImSjTHYHLkvFDP48nS4+204nGb1RiX/WXYHmJA2III9/e2DWVabCESdW7hBAEzHRqUn9OUVvQ==}
@@ -1266,11 +1265,11 @@ packages:
'@types/node': 14.18.29
chalk: 4.1.2
cosmiconfig: 7.0.1
cosmiconfig-typescript-loader: 4.1.0_nxlrwu45zhpwmwjzs33dzt3ak4
cosmiconfig-typescript-loader: 4.1.0_2uclxasecupgvdn72amnhmyg7y
lodash: 4.17.21
resolve-from: 5.0.0
ts-node: 10.9.1_sqjhzn5m3vxyw66a2xhtc43hby
typescript: 4.8.4
ts-node: 10.9.1_yxpazyh7n5pql7jdaglasgwqki
typescript: 4.9.5
transitivePeerDependencies:
- '@swc/core'
- '@swc/wasm'
@@ -2989,6 +2988,10 @@ packages:
resolution: {integrity: sha512-zmEmF5OIM3rb7SbLCFYoQhO4dGt2FRM9AMkxvA3LaADOF1n8in/zGJlWji9fmafLoNyz+FoL6FE0SLtGIArD7w==}
dev: true
/@types/lodash/4.14.189:
resolution: {integrity: sha512-kb9/98N6X8gyME9Cf7YaqIMvYGnBSWqEci6tiettE6iJWH1XdJz/PO8LB0GtLCG7x8dU3KWhZT+lA1a35127tA==}
dev: true
/@types/markdown-it/12.2.3:
resolution: {integrity: sha512-GKMHFfv3458yYy+v/N8gjufHO6MSZKCOXpZc5GXIWWy8uldwfmPn98vp81gZ5f9SVw8YYBctgfJ22a2d7AOMeQ==}
dependencies:
@@ -3087,7 +3090,7 @@ packages:
/@types/serve-index/1.9.1:
resolution: {integrity: sha512-d/Hs3nWDxNL2xAczmOVZNj92YZCS6RGxfBPjKzuu/XirCgXdpKEb88dYNbrYGint6IVWLNP+yonwVAuRC0T2Dg==}
dependencies:
'@types/express': 4.17.17
'@types/express': 4.17.14
dev: true
/@types/serve-static/1.15.0:
@@ -3190,7 +3193,7 @@ packages:
- supports-color
dev: true
/@typescript-eslint/eslint-plugin/5.48.2_iljmjqxcygjq3saipl7gerxpvi:
/@typescript-eslint/eslint-plugin/5.48.2_azmbqzqvrlvblbdtiwxwvyvjjy:
resolution: {integrity: sha512-sR0Gja9Ky1teIq4qJOl0nC+Tk64/uYdX+mi+5iB//MH8gwyx8e3SOyhEzeLZEFEEfCaLf8KJq+Bd/6je1t+CAg==}
engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0}
peerDependencies:
@@ -3201,18 +3204,18 @@ packages:
typescript:
optional: true
dependencies:
'@typescript-eslint/parser': 5.48.2_yygwinqv3a2io74xmwofqb7uka
'@typescript-eslint/parser': 5.48.2_et5x32uxl7z5ldub3ye5rhlyqm
'@typescript-eslint/scope-manager': 5.48.2
'@typescript-eslint/type-utils': 5.48.2_yygwinqv3a2io74xmwofqb7uka
'@typescript-eslint/utils': 5.48.2_yygwinqv3a2io74xmwofqb7uka
'@typescript-eslint/type-utils': 5.48.2_et5x32uxl7z5ldub3ye5rhlyqm
'@typescript-eslint/utils': 5.48.2_et5x32uxl7z5ldub3ye5rhlyqm
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
tsutils: 3.21.0_typescript@4.9.5
typescript: 4.9.5
transitivePeerDependencies:
- supports-color
dev: true
@@ -3237,7 +3240,7 @@ packages:
- supports-color
dev: true
/@typescript-eslint/parser/5.48.2_yygwinqv3a2io74xmwofqb7uka:
/@typescript-eslint/parser/5.48.2_et5x32uxl7z5ldub3ye5rhlyqm:
resolution: {integrity: sha512-38zMsKsG2sIuM5Oi/olurGwYJXzmtdsHhn5mI/pQogP+BjYVkK5iRazCQ8RGS0V+YLk282uWElN70zAAUmaYHw==}
engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0}
peerDependencies:
@@ -3249,10 +3252,10 @@ packages:
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
'@typescript-eslint/typescript-estree': 5.48.2_typescript@4.9.5
debug: 4.3.4
eslint: 8.32.0
typescript: 4.8.4
typescript: 4.9.5
transitivePeerDependencies:
- supports-color
dev: true
@@ -3293,7 +3296,7 @@ packages:
- supports-color
dev: true
/@typescript-eslint/type-utils/5.48.2_yygwinqv3a2io74xmwofqb7uka:
/@typescript-eslint/type-utils/5.48.2_et5x32uxl7z5ldub3ye5rhlyqm:
resolution: {integrity: sha512-QVWx7J5sPMRiOMJp5dYshPxABRoZV1xbRirqSk8yuIIsu0nvMTZesKErEA3Oix1k+uvsk8Cs8TGJ6kQ0ndAcew==}
engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0}
peerDependencies:
@@ -3303,12 +3306,12 @@ packages:
typescript:
optional: true
dependencies:
'@typescript-eslint/typescript-estree': 5.48.2_typescript@4.8.4
'@typescript-eslint/utils': 5.48.2_yygwinqv3a2io74xmwofqb7uka
'@typescript-eslint/typescript-estree': 5.48.2_typescript@4.9.5
'@typescript-eslint/utils': 5.48.2_et5x32uxl7z5ldub3ye5rhlyqm
debug: 4.3.4
eslint: 8.32.0
tsutils: 3.21.0_typescript@4.8.4
typescript: 4.8.4
tsutils: 3.21.0_typescript@4.9.5
typescript: 4.9.5
transitivePeerDependencies:
- supports-color
dev: true
@@ -3344,7 +3347,28 @@ packages:
- supports-color
dev: true
/@typescript-eslint/typescript-estree/5.48.2_typescript@4.8.4:
/@typescript-eslint/typescript-estree/5.42.1_typescript@4.9.5:
resolution: {integrity: sha512-qElc0bDOuO0B8wDhhW4mYVgi/LZL+igPwXtV87n69/kYC/7NG3MES0jHxJNCr4EP7kY1XVsRy8C/u3DYeTKQmw==}
engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0}
peerDependencies:
typescript: '*'
peerDependenciesMeta:
typescript:
optional: true
dependencies:
'@typescript-eslint/types': 5.42.1
'@typescript-eslint/visitor-keys': 5.42.1
debug: 4.3.4
globby: 11.1.0
is-glob: 4.0.3
semver: 7.3.8
tsutils: 3.21.0_typescript@4.9.5
typescript: 4.9.5
transitivePeerDependencies:
- supports-color
dev: true
/@typescript-eslint/typescript-estree/5.48.2_typescript@4.9.5:
resolution: {integrity: sha512-bibvD3z6ilnoVxUBFEgkO0k0aFvUc4Cttt0dAreEr+nrAHhWzkO83PEVVuieK3DqcgL6VAK5dkzK8XUVja5Zcg==}
engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0}
peerDependencies:
@@ -3359,12 +3383,32 @@ packages:
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
tsutils: 3.21.0_typescript@4.9.5
typescript: 4.9.5
transitivePeerDependencies:
- supports-color
dev: true
/@typescript-eslint/utils/5.42.1_et5x32uxl7z5ldub3ye5rhlyqm:
resolution: {integrity: sha512-Gxvf12xSp3iYZd/fLqiQRD4uKZjDNR01bQ+j8zvhPjpsZ4HmvEFL/tC4amGNyxN9Rq+iqvpHLhlqx6KTxz9ZyQ==}
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.42.1
'@typescript-eslint/types': 5.42.1
'@typescript-eslint/typescript-estree': 5.42.1_typescript@4.9.5
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
- typescript
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}
@@ -3385,7 +3429,7 @@ packages:
- typescript
dev: true
/@typescript-eslint/utils/5.48.2_yygwinqv3a2io74xmwofqb7uka:
/@typescript-eslint/utils/5.48.2_et5x32uxl7z5ldub3ye5rhlyqm:
resolution: {integrity: sha512-2h18c0d7jgkw6tdKTlNaM7wyopbLRBiit8oAxoP89YnuBOzCZ8g8aBCaCqq7h208qUTroL7Whgzam7UY3HVLow==}
engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0}
peerDependencies:
@@ -3395,7 +3439,7 @@ packages:
'@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
'@typescript-eslint/typescript-estree': 5.48.2_typescript@4.9.5
eslint: 8.32.0
eslint-scope: 5.1.1
eslint-utils: 3.0.0_eslint@8.32.0
@@ -4202,7 +4246,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.4
follow-redirects: 1.15.2_debug@4.3.2
transitivePeerDependencies:
- debug
dev: true
@@ -4973,7 +5017,7 @@ packages:
layout-base: 2.0.1
dev: false
/cosmiconfig-typescript-loader/4.1.0_nxlrwu45zhpwmwjzs33dzt3ak4:
/cosmiconfig-typescript-loader/4.1.0_2uclxasecupgvdn72amnhmyg7y:
resolution: {integrity: sha512-HbWIuR5O+XO5Oj9SZ5bzgrD4nN+rfhrm2PMb0FVx+t+XIvC45n8F0oTNnztXtspWGw0i2IzHaUWFD5LzV1JB4A==}
engines: {node: '>=12', npm: '>=6'}
peerDependencies:
@@ -4984,8 +5028,8 @@ packages:
dependencies:
'@types/node': 14.18.29
cosmiconfig: 7.0.1
ts-node: 10.9.1_sqjhzn5m3vxyw66a2xhtc43hby
typescript: 4.8.4
ts-node: 10.9.1_yxpazyh7n5pql7jdaglasgwqki
typescript: 4.9.5
dev: true
/cosmiconfig/7.0.1:
@@ -5337,7 +5381,7 @@ packages:
cli-table3: 0.6.3
commander: 5.1.0
common-tags: 1.8.2
dayjs: 1.11.7
dayjs: 1.11.5
debug: 4.3.4_supports-color@8.1.1
enquirer: 2.3.6
eventemitter2: 6.4.7
@@ -5396,12 +5440,10 @@ packages:
engines: {node: '>=12'}
dependencies:
internmap: 2.0.3
dev: false
/d3-axis/3.0.0:
resolution: {integrity: sha512-IH5tgjV4jE/GhHkRV0HiVYPDtvfjHQlQfJHs0usq7M30XcSBvOotpmH1IgkcXsO/5gEQZD43B//fc7SRT5S+xw==}
engines: {node: '>=12'}
dev: false
/d3-brush/3.0.0:
resolution: {integrity: sha512-ALnjWlVYkXsVIGlOsuWH1+3udkYFI48Ljihfnh8FZPF2QS9o+PzGLBslO0PjzVoHLZ2KCVgAM8NVkXPJB2aNnQ==}
@@ -5412,38 +5454,32 @@ packages:
d3-interpolate: 3.0.1
d3-selection: 3.0.0
d3-transition: 3.0.1_d3-selection@3.0.0
dev: false
/d3-chord/3.0.1:
resolution: {integrity: sha512-VE5S6TNa+j8msksl7HwjxMHDM2yNK3XCkusIlpX5kwauBfXuyLAtNg9jCp/iHH61tgI4sb6R/EIMWCqEIdjT/g==}
engines: {node: '>=12'}
dependencies:
d3-path: 3.0.1
dev: false
/d3-color/3.1.0:
resolution: {integrity: sha512-zg/chbXyeBtMQ1LbD/WSoW2DpC3I0mpmPdW+ynRTj/x2DAWYrIY7qeZIHidozwV24m4iavr15lNwIwLxRmOxhA==}
engines: {node: '>=12'}
dev: false
/d3-contour/4.0.0:
resolution: {integrity: sha512-7aQo0QHUTu/Ko3cP9YK9yUTxtoDEiDGwnBHyLxG5M4vqlBkO/uixMRele3nfsfj6UXOcuReVpVXzAboGraYIJw==}
engines: {node: '>=12'}
dependencies:
d3-array: 3.2.0
dev: false
/d3-delaunay/6.0.2:
resolution: {integrity: sha512-IMLNldruDQScrcfT+MWnazhHbDJhcRJyOEBAJfwQnHle1RPh6WDuLvxNArUju2VSMSUuKlY5BGHRJ2cYyoFLQQ==}
engines: {node: '>=12'}
dependencies:
delaunator: 5.0.0
dev: false
/d3-dispatch/3.0.1:
resolution: {integrity: sha512-rzUyPU/S7rwUflMyLc1ETDeBj0NRuHKKAcvukozwhshr6g6c5d8zh4c2gQjY2bZ0dXeGLWc1PF174P2tVvKhfg==}
engines: {node: '>=12'}
dev: false
/d3-drag/3.0.0:
resolution: {integrity: sha512-pWbUJLdETVA8lQNJecMxoXfH6x+mO2UQo8rSmZ+QqxcbyA3hfeprFgIT//HW2nlHChWeIIMwS2Fq+gEARkhTkg==}
@@ -5451,7 +5487,6 @@ packages:
dependencies:
d3-dispatch: 3.0.1
d3-selection: 3.0.0
dev: false
/d3-dsv/3.0.1:
resolution: {integrity: sha512-UG6OvdI5afDIFP9w4G0mNq50dSOsXHJaRE8arAS5o9ApWnIElp8GZw1Dun8vP8OyHOZ/QJUKUJwxiiCCnUwm+Q==}
@@ -5461,19 +5496,16 @@ packages:
commander: 7.2.0
iconv-lite: 0.6.3
rw: 1.3.3
dev: false
/d3-ease/3.0.1:
resolution: {integrity: sha512-wR/XK3D3XcLIZwpbvQwQ5fK+8Ykds1ip7A2Txe0yxncXSdq1L9skcG7blcedkOX+ZcgxGAmLX1FrRGbADwzi0w==}
engines: {node: '>=12'}
dev: false
/d3-fetch/3.0.1:
resolution: {integrity: sha512-kpkQIM20n3oLVBKGg6oHrUchHM3xODkTzjMoj7aWQFq5QEM+R6E4WkzT5+tojDY7yjez8KgCBRoj4aEr99Fdqw==}
engines: {node: '>=12'}
dependencies:
d3-dsv: 3.0.1
dev: false
/d3-force/3.0.0:
resolution: {integrity: sha512-zxV/SsA+U4yte8051P4ECydjD/S+qeYtnaIyAs9tgHCqfguma/aAQDjo85A9Z6EKhBirHRJHXIgJUlffT4wdLg==}
@@ -5482,51 +5514,42 @@ packages:
d3-dispatch: 3.0.1
d3-quadtree: 3.0.1
d3-timer: 3.0.1
dev: false
/d3-format/3.1.0:
resolution: {integrity: sha512-YyUI6AEuY/Wpt8KWLgZHsIU86atmikuoOmCfommt0LYHiQSPjvX2AcFc38PX0CBpr2RCyZhjex+NS/LPOv6YqA==}
engines: {node: '>=12'}
dev: false
/d3-geo/3.0.1:
resolution: {integrity: sha512-Wt23xBych5tSy9IYAM1FR2rWIBFWa52B/oF/GYe5zbdHrg08FU8+BuI6X4PvTwPDdqdAdq04fuWJpELtsaEjeA==}
engines: {node: '>=12'}
dependencies:
d3-array: 3.2.0
dev: false
/d3-hierarchy/3.1.2:
resolution: {integrity: sha512-FX/9frcub54beBdugHjDCdikxThEqjnR93Qt7PvQTOHxyiNCAlvMrHhclk3cD5VeAaq9fxmfRp+CnWw9rEMBuA==}
engines: {node: '>=12'}
dev: false
/d3-interpolate/3.0.1:
resolution: {integrity: sha512-3bYs1rOD33uo8aqJfKP3JWPAibgw8Zm2+L9vBKEHJ2Rg+viTR7o5Mmv5mZcieN+FRYaAOWX5SJATX6k1PWz72g==}
engines: {node: '>=12'}
dependencies:
d3-color: 3.1.0
dev: false
/d3-path/3.0.1:
resolution: {integrity: sha512-gq6gZom9AFZby0YLduxT1qmrp4xpBA1YZr19OI717WIdKE2OM5ETq5qrHLb301IgxhLwcuxvGZVLeeWc/k1I6w==}
engines: {node: '>=12'}
dev: false
/d3-polygon/3.0.1:
resolution: {integrity: sha512-3vbA7vXYwfe1SYhED++fPUQlWSYTTGmFmQiany/gdbiWgU/iEyQzyymwL9SkJjFFuCS4902BSzewVGsHHmHtXg==}
engines: {node: '>=12'}
dev: false
/d3-quadtree/3.0.1:
resolution: {integrity: sha512-04xDrxQTDTCFwP5H6hRhsRcb9xxv2RzkcsygFzmkSIOJy3PeRJP7sNk3VRIbKXcog561P9oU0/rVH6vDROAgUw==}
engines: {node: '>=12'}
dev: false
/d3-random/3.0.1:
resolution: {integrity: sha512-FXMe9GfxTxqd5D6jFsQ+DJ8BJS4E/fT5mqqdjovykEB2oFbTMDVdg1MGFxfQW+FBOGoB++k8swBrgwSHT1cUXQ==}
engines: {node: '>=12'}
dev: false
/d3-scale-chromatic/3.0.0:
resolution: {integrity: sha512-Lx9thtxAKrO2Pq6OO2Ua474opeziKr279P/TKZsMAhYyNDD3EnCffdbgeSYN5O7m2ByQsxtuP2CSDczNUIZ22g==}
@@ -5534,7 +5557,6 @@ packages:
dependencies:
d3-color: 3.1.0
d3-interpolate: 3.0.1
dev: false
/d3-scale/4.0.2:
resolution: {integrity: sha512-GZW464g1SH7ag3Y7hXjf8RoUuAFIqklOAq3MRl4OaWabTFJY9PN/E1YklhXLh+OQ3fM9yS2nOkCoS+WLZ6kvxQ==}
@@ -5545,38 +5567,32 @@ packages:
d3-interpolate: 3.0.1
d3-time: 3.0.0
d3-time-format: 4.1.0
dev: false
/d3-selection/3.0.0:
resolution: {integrity: sha512-fmTRWbNMmsmWq6xJV8D19U/gw/bwrHfNXxrIN+HfZgnzqTHp9jOmKMhsTUjXOJnZOdZY9Q28y4yebKzqDKlxlQ==}
engines: {node: '>=12'}
dev: false
/d3-shape/3.1.0:
resolution: {integrity: sha512-tGDh1Muf8kWjEDT/LswZJ8WF85yDZLvVJpYU9Nq+8+yW1Z5enxrmXOhTArlkaElU+CTn0OTVNli+/i+HP45QEQ==}
engines: {node: '>=12'}
dependencies:
d3-path: 3.0.1
dev: false
/d3-time-format/4.1.0:
resolution: {integrity: sha512-dJxPBlzC7NugB2PDLwo9Q8JiTR3M3e4/XANkreKSUxF8vvXKqm1Yfq4Q5dl8budlunRVlUUaDUgFt7eA8D6NLg==}
engines: {node: '>=12'}
dependencies:
d3-time: 3.0.0
dev: false
/d3-time/3.0.0:
resolution: {integrity: sha512-zmV3lRnlaLI08y9IMRXSDshQb5Nj77smnfpnd2LrBa/2K281Jijactokeak14QacHs/kKq0AQ121nidNYlarbQ==}
engines: {node: '>=12'}
dependencies:
d3-array: 3.2.0
dev: false
/d3-timer/3.0.1:
resolution: {integrity: sha512-ndfJ/JxxMd3nw31uyKoY2naivF+r29V+Lc0svZxe1JvvIRmi8hUsrMvdOwgS1o6uBHmiz91geQ0ylPP0aj1VUA==}
engines: {node: '>=12'}
dev: false
/d3-transition/3.0.1_d3-selection@3.0.0:
resolution: {integrity: sha512-ApKvfjsSR6tg06xrL434C0WydLr7JewBB3V+/39RMHsaXTOG0zmt/OAXeng5M5LBm0ojmxJrpomQVZ1aPvBL4w==}
@@ -5590,7 +5606,6 @@ packages:
d3-interpolate: 3.0.1
d3-selection: 3.0.0
d3-timer: 3.0.1
dev: false
/d3-zoom/3.0.0:
resolution: {integrity: sha512-b8AmV3kfQaqWAuacbPuNbL6vahnOJflOhexLzMMNLga62+/nh0JzvJ0aO/5a5MVgUFGS7Hu1P9P03o3fJkDCyw==}
@@ -5601,7 +5616,6 @@ packages:
d3-interpolate: 3.0.1
d3-selection: 3.0.0
d3-transition: 3.0.1_d3-selection@3.0.0
dev: false
/d3/7.8.2:
resolution: {integrity: sha512-WXty7qOGSHb7HR7CfOzwN1Gw04MUOzN8qh9ZUsvwycIMb4DYMpY9xczZ6jUorGtO6bR9BPMPaueIKwiDxu9uiQ==}
@@ -5637,10 +5651,9 @@ packages:
d3-timer: 3.0.1
d3-transition: 3.0.1_d3-selection@3.0.0
d3-zoom: 3.0.0
dev: false
/dagre-d3-es/7.0.9:
resolution: {integrity: sha512-rYR4QfVmy+sR44IBDvVtcAmOReGBvRCWDpO2QjYwqgh9yijw6eSHBqaPG/LIOEy7aBsniLvtMW6pg19qJhq60w==}
/dagre-d3-es/7.0.8:
resolution: {integrity: sha512-eykdoYQ4FwCJinEYS0gPL2f2w+BPbSLvnQSJ3Ye1vAoPjdkq6xIMKBv+UkICd3qZE26wBKIn3p+6n0QC7R1LyA==}
dependencies:
d3: 7.8.2
lodash-es: 4.17.21
@@ -5677,8 +5690,9 @@ packages:
engines: {node: '>=0.11'}
dev: true
/dayjs/1.11.7:
resolution: {integrity: sha512-+Yw9U6YO5TQohxLcIkrXBeY73WP3ejHWVvx8XCk3gxvQDCTEmS48ZrSZCKciI7Bhl/uCMyxYtE9UqRILmFphkQ==}
/dayjs/1.11.5:
resolution: {integrity: sha512-CAdX5Q3YW3Gclyo5Vpqkgpj8fSdLQcRuzfX6mC6Phy0nfJ0eGYOeS7m4mt2plDWLAtA4TqTakvbboHvUxfe4iA==}
dev: true
/debug/2.6.9:
resolution: {integrity: sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==}
@@ -5830,7 +5844,6 @@ packages:
resolution: {integrity: sha512-AyLvtyJdbv/U1GkiS6gUUzclRoAY4Gs75qkMygJJhU75LW4DNuSF2RMzpxs9jw9Oz1BobHjTdkG3zdP55VxAqw==}
dependencies:
robust-predicates: 3.0.1
dev: false
/delayed-stream/1.0.0:
resolution: {integrity: sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==}
@@ -6198,7 +6211,7 @@ packages:
htmlparser2: 8.0.1
dev: true
/eslint-plugin-jest/27.1.5_5rcd23qw3h5vuffwo2owxb3hw4:
/eslint-plugin-jest/27.1.5_i5clxtuiaceouxhg5syqkw5wwi:
resolution: {integrity: sha512-CK2dekZ5VBdzsOSOH5Fc1rwC+cWXjkcyrmf1RV714nDUDKu+o73TTJiDxpbILG8PtPPpAAl3ywzh5QA7Ft0mjA==}
engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0}
peerDependencies:
@@ -6211,8 +6224,8 @@ packages:
jest:
optional: true
dependencies:
'@typescript-eslint/eslint-plugin': 5.48.2_iljmjqxcygjq3saipl7gerxpvi
'@typescript-eslint/utils': 5.42.1_yygwinqv3a2io74xmwofqb7uka
'@typescript-eslint/eslint-plugin': 5.48.2_azmbqzqvrlvblbdtiwxwvyvjjy
'@typescript-eslint/utils': 5.42.1_et5x32uxl7z5ldub3ye5rhlyqm
eslint: 8.32.0
jest: 29.3.1_odkjkoia5xunhxkdrka32ib6vi
transitivePeerDependencies:
@@ -6774,6 +6787,18 @@ packages:
resolution: {integrity: sha512-XGozTsMPYkm+6b5QL3Z9wQcJjNYxp0CYn3U1gO7dwD6PAqU1SVWZxI9CCg3z+ml3YfqdPnrBehaBrnH2AGKbNA==}
dev: true
/follow-redirects/1.15.2_debug@4.3.2:
resolution: {integrity: sha512-VQLG33o04KaQ8uYi2tVNbdrWp1QWxNNea+nmIB4EVM28v0hmP17z7aG1+wAkNzVq4KeXTq3221ye5qTJP91JwA==}
engines: {node: '>=4.0'}
peerDependencies:
debug: '*'
peerDependenciesMeta:
debug:
optional: true
dependencies:
debug: 4.3.2
dev: true
/follow-redirects/1.15.2_debug@4.3.4:
resolution: {integrity: sha512-VQLG33o04KaQ8uYi2tVNbdrWp1QWxNNea+nmIB4EVM28v0hmP17z7aG1+wAkNzVq4KeXTq3221ye5qTJP91JwA==}
engines: {node: '>=4.0'}
@@ -7323,7 +7348,7 @@ packages:
engines: {node: '>=8.0.0'}
dependencies:
eventemitter3: 4.0.7
follow-redirects: 1.15.2_debug@4.3.4
follow-redirects: 1.15.2_debug@4.3.2
requires-port: 1.0.0
transitivePeerDependencies:
- debug
@@ -7467,7 +7492,6 @@ packages:
/internmap/2.0.3:
resolution: {integrity: sha512-5Hh7Y1wQbvY5ooGgPbDaL5iYLAPzMTUrjMulskHLH6wnv/A+1q5rgEaiuqEjB+oxGXIVZs1FF+R/KPN3ZSQYYg==}
engines: {node: '>=12'}
dev: false
/interpret/2.2.0:
resolution: {integrity: sha512-Ju0Bz/cEia55xDwUWEa8+olFpCiQoypjnQySseKtmjNrnps3P+xfpUmGr90T7yjlVJmOtybRvPXhKMbHr+fWnw==}
@@ -7840,7 +7864,7 @@ packages:
pretty-format: 29.3.1
slash: 3.0.0
strip-json-comments: 3.1.1
ts-node: 10.9.1_cbe7ovvae6zqfnmtgctpgpys54
ts-node: 10.9.1_w6ufic3jqylcjznzspnj4wjqfe
transitivePeerDependencies:
- supports-color
dev: true
@@ -8217,8 +8241,8 @@ packages:
'@sideway/pinpoint': 2.0.0
dev: true
/joi/17.8.3:
resolution: {integrity: sha512-q5Fn6Tj/jR8PfrLrx4fpGH4v9qM6o+vDUfD4/3vxxyg34OmKcNqYZ1qn2mpLza96S8tL0p0rIw2gOZX+/cTg9w==}
/joi/17.7.1:
resolution: {integrity: sha512-teoLhIvWE298R6AeJywcjR4sX2hHjB3/xJX4qPjg+gTg+c0mzUDsziYlqPmLomq9gVsfaMcgPaGc7VxtD/9StA==}
dependencies:
'@hapi/hoek': 9.3.0
'@hapi/topo': 5.1.0
@@ -8424,7 +8448,6 @@ packages:
/khroma/2.0.0:
resolution: {integrity: sha512-2J8rDNlQWbtiNYThZRvmMv5yt44ZakX+Tz5ZIp/mN1pt4snn+m030Va5Z4v8xA0cQFDXBwO/8i42xL4QPsVk3g==}
dev: false
/kind-of/6.0.3:
resolution: {integrity: sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==}
@@ -9296,6 +9319,9 @@ packages:
ufo: 1.0.1
dev: true
/moment-mini/2.29.4:
resolution: {integrity: sha512-uhXpYwHFeiTbY9KSgPPRoo1nt8OxNVdMVoTBYHfSEKeRkIkwGpO+gERmhuhBtzfaeOyTkykSrm2+noJBgqt3Hg==}
/mri/1.2.0:
resolution: {integrity: sha512-tzzskb3bG8LvYGFF/mDTpq3jpI6Q9wc3LEmBaghu+DdCssd1FakN7Bc0hVNmEyGq1bq3RgfkCb3cmQLpNPOroA==}
engines: {node: '>=4'}
@@ -9397,7 +9423,6 @@ packages:
/non-layered-tidy-tree-layout/2.0.2:
resolution: {integrity: sha512-gkXMxRzUH+PB0ax9dUN0yYF0S25BqeAYqhgMaLUFmpXLEk7Fcu8f4emJuOAY0V8kjDICxROIKsTAKsV/v355xw==}
dev: false
/normalize-package-data/2.5.0:
resolution: {integrity: sha512-/5CMN3T0R4XTj4DcGaexo+roZSdSFW/0AOOTROrjxzCG1wrWXEsGbRKevjlIL+ZDE4sZlJr5ED4YW0yqmkK+eA==}
@@ -10406,7 +10431,6 @@ packages:
/robust-predicates/3.0.1:
resolution: {integrity: sha512-ndEIpszUHiG4HtDsQLeIuMvRsDnn8c8rYStabochtUeCvfuvNptb5TUbVD68LRAILPX7p9nqQGh4xJgn3EHS/g==}
dev: false
/rollup-plugin-visualizer/5.8.3:
resolution: {integrity: sha512-QGJk4Bqe4AOat5AjipOh8esZH1nck5X2KFpf4VytUdSUuuuSwvIQZjMGgjcxe/zXexltqaXp5Vx1V3LmnQH15Q==}
@@ -10447,7 +10471,6 @@ packages:
/rw/1.3.3:
resolution: {integrity: sha512-PdhdWy89SiZogBLaw42zdeqtRJ//zFd2PgQavcICDUgJT5oW10QCRKbJ6bg4r0/UY2M6BWd5tkxuGFRvCkgfHQ==}
dev: false
/rxjs/7.5.6:
resolution: {integrity: sha512-dnyv2/YsXhnm461G+R/Pe5bWP41Nm6LBXEYWI6eiFP4fiwx6WRI/CD0zbdVAudd9xwLEF2IDcKXLHit0FYjUzw==}
@@ -11081,7 +11104,6 @@ packages:
/stylis/4.1.2:
resolution: {integrity: sha512-Nn2CCrG2ZaFziDxaZPN43CXqn+j7tcdjPFCkRBkFue8QYXC2HdEwnw5TCBo4yQZ2WxKYeSi0fdoOrtEqgDrXbA==}
dev: false
/supports-color/2.0.0:
resolution: {integrity: sha512-KKNVtd6pCYgPIKU4cp2733HWYCpplQhddZLBUryaAHou723x+FRzQ5Df824Fj+IyyuiQTRoub4SnIFfIcrp70g==}
@@ -11306,7 +11328,7 @@ packages:
engines: {node: '>=6.10'}
dev: false
/ts-node/10.9.1_cbe7ovvae6zqfnmtgctpgpys54:
/ts-node/10.9.1_w6ufic3jqylcjznzspnj4wjqfe:
resolution: {integrity: sha512-NtVysVPkxxrwFGUUxGYhfux8k78pQB3JqYBXlLRZgdGUqTO5wU/UyHop5p70iEbGhB7q5KmiZiU0Y3KlJrScEw==}
hasBin: true
peerDependencies:
@@ -11332,12 +11354,12 @@ packages:
create-require: 1.1.1
diff: 4.0.2
make-error: 1.3.6
typescript: 4.8.4
typescript: 4.9.5
v8-compile-cache-lib: 3.0.1
yn: 3.1.1
dev: true
/ts-node/10.9.1_sqjhzn5m3vxyw66a2xhtc43hby:
/ts-node/10.9.1_yxpazyh7n5pql7jdaglasgwqki:
resolution: {integrity: sha512-NtVysVPkxxrwFGUUxGYhfux8k78pQB3JqYBXlLRZgdGUqTO5wU/UyHop5p70iEbGhB7q5KmiZiU0Y3KlJrScEw==}
hasBin: true
peerDependencies:
@@ -11363,7 +11385,7 @@ packages:
create-require: 1.1.1
diff: 4.0.2
make-error: 1.3.6
typescript: 4.8.4
typescript: 4.9.5
v8-compile-cache-lib: 3.0.1
yn: 3.1.1
dev: true
@@ -11386,6 +11408,16 @@ packages:
typescript: 4.8.4
dev: true
/tsutils/3.21.0_typescript@4.9.5:
resolution: {integrity: sha512-mHKK3iUXL+3UF6xL5k0PEhKRUBKPBCv/+RkEOpjRWxxx27KKRBmmA60A9pgOUvMi8GKhRMPEmjBRPzs2W7O1OA==}
engines: {node: '>= 6'}
peerDependencies:
typescript: '>=2.8.0 || >= 3.2.0-dev || >= 3.3.0-dev || >= 3.4.0-dev || >= 3.5.0-dev || >= 3.6.0-dev || >= 3.6.0-beta || >= 3.7.0-dev || >= 3.7.0-beta'
dependencies:
tslib: 1.14.1
typescript: 4.9.5
dev: true
/tunnel-agent/0.6.0:
resolution: {integrity: sha512-McnNiV1l8RYeY8tBgEpuodCC1mLUdbSN+CYBL7kJsJNInOP8UjDDEwdk6Mw60vdLLrr5NHKZhMAOSrR2NZuQ+w==}
dependencies:
@@ -11488,6 +11520,12 @@ packages:
hasBin: true
dev: true
/typescript/4.9.5:
resolution: {integrity: sha512-1FXk9E2Hm+QzZQ7z+McJiHL4NW1F2EzMu9Nq9i3zAaGqibafqYwCVU6WyWAuyQRRzOlxou8xZSyXLEN8oKj24g==}
engines: {node: '>=4.2.0'}
hasBin: true
dev: true
/uc.micro/1.0.6:
resolution: {integrity: sha512-8Y75pvTYkLJW2hWQHXxoqRgV7qb9B+9vFEtidML+7koHUFapnVJAZ6cKs+Qjz5Aw3aZWHMC6u0wJE3At+nSGwA==}
dev: true
@@ -11634,7 +11672,6 @@ packages:
/uuid/9.0.0:
resolution: {integrity: sha512-MXcSTerfPa4uqyzStbRoTgt5XIe3x5+42+q1sDuy3R5MDk66URdLMOZe5aPX/SQd+kuYAh0FdP/pO28IkQyTeg==}
hasBin: true
dev: false
/uvu/0.5.6:
resolution: {integrity: sha512-+g8ENReyr8YsOc6fv/NVJs2vFdHBnBNdfE49rshrTzDWOlUx4Gq7KOS2GD8eqhy2j+Ejq29+SbKH8yjkAqXqoA==}
@@ -12084,7 +12121,7 @@ packages:
hasBin: true
dependencies:
axios: 0.27.2_debug@4.3.4
joi: 17.8.3
joi: 17.7.1
lodash: 4.17.21
minimist: 1.2.8
rxjs: 7.8.0