mirror of
https://github.com/mermaid-js/mermaid.git
synced 2025-01-28 07:03:17 +08:00
Merge pull request #4761 from mermaid-js/sidv/RemoveCircularDeps
Remove Circular Dependencies
This commit is contained in:
commit
6e0f41180f
18
.github/workflows/lint.yml
vendored
18
.github/workflows/lint.yml
vendored
@ -62,8 +62,22 @@ jobs:
|
||||
ERROR_MESSAGE+=' `pnpm run --filter mermaid types:build-config`'
|
||||
ERROR_MESSAGE+=' on your local machine.'
|
||||
echo "::error title=Lint failure::${ERROR_MESSAGE}"
|
||||
# make sure to return an error exitcode so that GitHub actions shows a red-cross
|
||||
exit 1
|
||||
# make sure to return an error exitcode so that GitHub actions shows a red-cross
|
||||
exit 1
|
||||
fi
|
||||
|
||||
- name: Verify no circular dependencies
|
||||
working-directory: ./packages/mermaid
|
||||
shell: bash
|
||||
run: |
|
||||
if ! pnpm run --filter mermaid checkCircle; then
|
||||
ERROR_MESSAGE='Circular dependency detected.'
|
||||
ERROR_MESSAGE+=' This should be fixed by removing the circular dependency.'
|
||||
ERROR_MESSAGE+=' Run `pnpm run --filter mermaid checkCircle` on your local machine'
|
||||
ERROR_MESSAGE+=' to see the circular dependency.'
|
||||
echo "::error title=Lint failure::${ERROR_MESSAGE}"
|
||||
# make sure to return an error exitcode so that GitHub actions shows a red-cross
|
||||
exit 1
|
||||
fi
|
||||
|
||||
- name: Verify Docs
|
||||
|
@ -106,6 +106,7 @@
|
||||
"rects",
|
||||
"reda",
|
||||
"redmine",
|
||||
"regexes",
|
||||
"rehype",
|
||||
"roledescription",
|
||||
"rozhkov",
|
||||
|
22
packages/mermaid/.madgerc
Normal file
22
packages/mermaid/.madgerc
Normal file
@ -0,0 +1,22 @@
|
||||
{
|
||||
"detectiveOptions": {
|
||||
"ts": {
|
||||
"skipTypeImports": true
|
||||
},
|
||||
"es6": {
|
||||
"skipTypeImports": true
|
||||
}
|
||||
},
|
||||
"fileExtensions": [
|
||||
"js",
|
||||
"ts"
|
||||
],
|
||||
"excludeRegExp": [
|
||||
"node_modules",
|
||||
"docs",
|
||||
"vitepress",
|
||||
"detector",
|
||||
"Detector"
|
||||
],
|
||||
"tsConfig": "./tsconfig.json"
|
||||
}
|
@ -38,6 +38,7 @@
|
||||
"docs:verify-version": "ts-node-esm scripts/update-release-version.mts --verify",
|
||||
"types:build-config": "ts-node-esm --transpileOnly scripts/create-types-from-json-schema.mts",
|
||||
"types:verify-config": "ts-node-esm scripts/create-types-from-json-schema.mts --verify",
|
||||
"checkCircle": "npx madge --circular ./src",
|
||||
"release": "pnpm build",
|
||||
"prepublishOnly": "cpy '../../README.*' ./ --cwd=. && pnpm -w run build"
|
||||
},
|
||||
|
@ -6,14 +6,10 @@ import type {
|
||||
DiagramLoader,
|
||||
ExternalDiagramDefinition,
|
||||
} from './types.js';
|
||||
import { frontMatterRegex } from './frontmatter.js';
|
||||
import { getDiagram, registerDiagram } from './diagramAPI.js';
|
||||
import { anyCommentRegex, directiveRegex, frontMatterRegex } from './regexes.js';
|
||||
import { UnknownDiagramError } from '../errors.js';
|
||||
|
||||
const directive = /%{2}{\s*(?:(\w+)\s*:|(\w+))\s*(?:(\w+)|((?:(?!}%{2}).|\r?\n)*))?\s*(?:}%{2})?/gi;
|
||||
const anyComment = /\s*%%.*\n/gm;
|
||||
|
||||
const detectors: Record<string, DetectorRecord> = {};
|
||||
export const detectors: Record<string, DetectorRecord> = {};
|
||||
|
||||
/**
|
||||
* Detects the type of the graph text.
|
||||
@ -38,7 +34,10 @@ const detectors: Record<string, DetectorRecord> = {};
|
||||
* @returns A graph definition key
|
||||
*/
|
||||
export const detectType = function (text: string, config?: MermaidConfig): string {
|
||||
text = text.replace(frontMatterRegex, '').replace(directive, '').replace(anyComment, '\n');
|
||||
text = text
|
||||
.replace(frontMatterRegex, '')
|
||||
.replace(directiveRegex, '')
|
||||
.replace(anyCommentRegex, '\n');
|
||||
for (const [key, { detector }] of Object.entries(detectors)) {
|
||||
const diagram = detector(text, config);
|
||||
if (diagram) {
|
||||
@ -70,39 +69,6 @@ export const registerLazyLoadedDiagrams = (...diagrams: ExternalDiagramDefinitio
|
||||
}
|
||||
};
|
||||
|
||||
export const loadRegisteredDiagrams = async () => {
|
||||
log.debug(`Loading registered diagrams`);
|
||||
// Load all lazy loaded diagrams in parallel
|
||||
const results = await Promise.allSettled(
|
||||
Object.entries(detectors).map(async ([key, { detector, loader }]) => {
|
||||
if (loader) {
|
||||
try {
|
||||
getDiagram(key);
|
||||
} catch (error) {
|
||||
try {
|
||||
// Register diagram if it is not already registered
|
||||
const { diagram, id } = await loader();
|
||||
registerDiagram(id, diagram, detector);
|
||||
} catch (err) {
|
||||
// Remove failed diagram from detectors
|
||||
log.error(`Failed to load external diagram with key ${key}. Removing from detectors.`);
|
||||
delete detectors[key];
|
||||
throw err;
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
);
|
||||
const failed = results.filter((result) => result.status === 'rejected');
|
||||
if (failed.length > 0) {
|
||||
log.error(`Failed to load ${failed.length} external diagrams`);
|
||||
for (const res of failed) {
|
||||
log.error(res);
|
||||
}
|
||||
throw new Error(`Failed to load ${failed.length} external diagrams`);
|
||||
}
|
||||
};
|
||||
|
||||
export const addDetector = (key: string, detector: DiagramDetector, loader?: DiagramLoader) => {
|
||||
if (detectors[key]) {
|
||||
log.error(`Detector with key ${key} already exists`);
|
||||
|
@ -4,7 +4,7 @@ import { getConfig as _getConfig } from '../config.js';
|
||||
import { sanitizeText as _sanitizeText } from '../diagrams/common/common.js';
|
||||
import { setupGraphViewbox as _setupGraphViewbox } from '../setupGraphViewbox.js';
|
||||
import { addStylesForDiagram } from '../styles.js';
|
||||
import { DiagramDefinition, DiagramDetector } from './types.js';
|
||||
import type { DiagramDefinition, DiagramDetector } from './types.js';
|
||||
import * as _commonDb from '../commonDb.js';
|
||||
import { parseDirective as _parseDirective } from '../directiveUtils.js';
|
||||
|
||||
|
@ -1,14 +1,8 @@
|
||||
import { DiagramDB } from './types.js';
|
||||
import type { DiagramDB } from './types.js';
|
||||
import { frontMatterRegex } from './regexes.js';
|
||||
// The "* as yaml" part is necessary for tree-shaking
|
||||
import * as yaml from 'js-yaml';
|
||||
|
||||
// Match Jekyll-style front matter blocks (https://jekyllrb.com/docs/front-matter/).
|
||||
// Based on regex used by Jekyll: https://github.com/jekyll/jekyll/blob/6dd3cc21c40b98054851846425af06c64f9fb466/lib/jekyll/document.rb#L10
|
||||
// Note that JS doesn't support the "\A" anchor, which means we can't use
|
||||
// multiline mode.
|
||||
// Relevant YAML spec: https://yaml.org/spec/1.2.2/#914-explicit-documents
|
||||
export const frontMatterRegex = /^-{3}\s*[\n\r](.*?)[\n\r]-{3}\s*[\n\r]+/s;
|
||||
|
||||
type FrontMatterMetadata = {
|
||||
title?: string;
|
||||
// Allows custom display modes. Currently used for compact mode in gantt charts.
|
||||
|
36
packages/mermaid/src/diagram-api/loadDiagram.ts
Normal file
36
packages/mermaid/src/diagram-api/loadDiagram.ts
Normal file
@ -0,0 +1,36 @@
|
||||
import { log } from '../logger.js';
|
||||
import { detectors } from './detectType.js';
|
||||
import { getDiagram, registerDiagram } from './diagramAPI.js';
|
||||
|
||||
export const loadRegisteredDiagrams = async () => {
|
||||
log.debug(`Loading registered diagrams`);
|
||||
// Load all lazy loaded diagrams in parallel
|
||||
const results = await Promise.allSettled(
|
||||
Object.entries(detectors).map(async ([key, { detector, loader }]) => {
|
||||
if (loader) {
|
||||
try {
|
||||
getDiagram(key);
|
||||
} catch (error) {
|
||||
try {
|
||||
// Register diagram if it is not already registered
|
||||
const { diagram, id } = await loader();
|
||||
registerDiagram(id, diagram, detector);
|
||||
} catch (err) {
|
||||
// Remove failed diagram from detectors
|
||||
log.error(`Failed to load external diagram with key ${key}. Removing from detectors.`);
|
||||
delete detectors[key];
|
||||
throw err;
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
);
|
||||
const failed = results.filter((result) => result.status === 'rejected');
|
||||
if (failed.length > 0) {
|
||||
log.error(`Failed to load ${failed.length} external diagrams`);
|
||||
for (const res of failed) {
|
||||
log.error(res);
|
||||
}
|
||||
throw new Error(`Failed to load ${failed.length} external diagrams`);
|
||||
}
|
||||
};
|
11
packages/mermaid/src/diagram-api/regexes.ts
Normal file
11
packages/mermaid/src/diagram-api/regexes.ts
Normal file
@ -0,0 +1,11 @@
|
||||
// Match Jekyll-style front matter blocks (https://jekyllrb.com/docs/front-matter/).
|
||||
// Based on regex used by Jekyll: https://github.com/jekyll/jekyll/blob/6dd3cc21c40b98054851846425af06c64f9fb466/lib/jekyll/document.rb#L10
|
||||
// Note that JS doesn't support the "\A" anchor, which means we can't use
|
||||
// multiline mode.
|
||||
// Relevant YAML spec: https://yaml.org/spec/1.2.2/#914-explicit-documents
|
||||
export const frontMatterRegex = /^-{3}\s*[\n\r](.*?)[\n\r]-{3}\s*[\n\r]+/s;
|
||||
|
||||
export const directiveRegex =
|
||||
/%{2}{\s*(?:(\w+)\s*:|(\w+))\s*(?:(\w+)|((?:(?!}%{2}).|\r?\n)*))?\s*(?:}%{2})?/gi;
|
||||
|
||||
export const anyCommentRegex = /\s*%%.*\n/gm;
|
@ -17,7 +17,8 @@
|
||||
"dependencies": {
|
||||
"@vueuse/core": "^10.1.0",
|
||||
"jiti": "^1.18.2",
|
||||
"vue": "^3.3"
|
||||
"vue": "^3.3",
|
||||
"mermaid": "workspace:^"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@iconify-json/carbon": "^1.1.16",
|
||||
|
@ -7,11 +7,8 @@ import { MermaidConfig } from './config.type.js';
|
||||
import { log } from './logger.js';
|
||||
import utils from './utils.js';
|
||||
import { mermaidAPI, ParseOptions, RenderResult } from './mermaidAPI.js';
|
||||
import {
|
||||
registerLazyLoadedDiagrams,
|
||||
loadRegisteredDiagrams,
|
||||
detectType,
|
||||
} from './diagram-api/detectType.js';
|
||||
import { registerLazyLoadedDiagrams, detectType } from './diagram-api/detectType.js';
|
||||
import { loadRegisteredDiagrams } from './diagram-api/loadDiagram.js';
|
||||
import type { ParseErrorFunction } from './Diagram.js';
|
||||
import { isDetailedError } from './utils.js';
|
||||
import type { DetailedError } from './utils.js';
|
||||
|
@ -32,6 +32,7 @@ import assignWithDepth from './assignWithDepth.js';
|
||||
import { MermaidConfig } from './config.type.js';
|
||||
import memoize from 'lodash-es/memoize.js';
|
||||
import merge from 'lodash-es/merge.js';
|
||||
import { directiveRegex } from './diagram-api/regexes.js';
|
||||
|
||||
export const ZERO_WIDTH_SPACE = '\u200b';
|
||||
|
||||
@ -58,7 +59,7 @@ const d3CurveTypes = {
|
||||
curveStepAfter: curveStepAfter,
|
||||
curveStepBefore: curveStepBefore,
|
||||
};
|
||||
const directive = /%{2}{\s*(?:(\w+)\s*:|(\w+))\s*(?:(\w+)|((?:(?!}%{2}).|\r?\n)*))?\s*(?:}%{2})?/gi;
|
||||
|
||||
const directiveWithoutOpen =
|
||||
/\s*(?:(\w+)(?=:):|(\w+))\s*(?:(\w+)|((?:(?!}%{2}).|\r?\n)*))?\s*(?:}%{2})?/gi;
|
||||
|
||||
@ -163,10 +164,10 @@ export const detectDirective = function (
|
||||
);
|
||||
let match;
|
||||
const result = [];
|
||||
while ((match = directive.exec(text)) !== null) {
|
||||
while ((match = directiveRegex.exec(text)) !== null) {
|
||||
// This is necessary to avoid infinite loops with zero-width matches
|
||||
if (match.index === directive.lastIndex) {
|
||||
directive.lastIndex++;
|
||||
if (match.index === directiveRegex.lastIndex) {
|
||||
directiveRegex.lastIndex++;
|
||||
}
|
||||
if (
|
||||
(match && !type) ||
|
||||
|
6
pnpm-lock.yaml
generated
6
pnpm-lock.yaml
generated
@ -434,6 +434,9 @@ importers:
|
||||
jiti:
|
||||
specifier: ^1.18.2
|
||||
version: 1.18.2
|
||||
mermaid:
|
||||
specifier: workspace:^
|
||||
version: link:../..
|
||||
vue:
|
||||
specifier: ^3.3
|
||||
version: 3.3.4
|
||||
@ -486,6 +489,9 @@ importers:
|
||||
jiti:
|
||||
specifier: ^1.18.2
|
||||
version: 1.18.2
|
||||
mermaid:
|
||||
specifier: workspace:^
|
||||
version: link:../..
|
||||
vue:
|
||||
specifier: ^3.3
|
||||
version: 3.3.4
|
||||
|
Loading…
x
Reference in New Issue
Block a user