mirror of
				https://github.com/mermaid-js/mermaid.git
				synced 2025-11-04 12:54:08 +01:00 
			
		
		
		
	Compare commits
	
		
			2 Commits
		
	
	
		
			14a4ab81c9
			...
			sidv/remov
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
| 
						 | 
					927217d77c | ||
| 
						 | 
					b5f3cdc0b0 | 
							
								
								
									
										12
									
								
								packages/mermaid-flowchart-elk/package.json
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										12
									
								
								packages/mermaid-flowchart-elk/package.json
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,12 @@
 | 
			
		||||
{
 | 
			
		||||
  "name": "@mermaid-js/flowchart-elk",
 | 
			
		||||
  "version": "1.0.0",
 | 
			
		||||
  "description": "",
 | 
			
		||||
  "main": "index.js",
 | 
			
		||||
  "scripts": {
 | 
			
		||||
    "test": "echo \"Error: no test specified\" && exit 1"
 | 
			
		||||
  },
 | 
			
		||||
  "keywords": [],
 | 
			
		||||
  "author": "",
 | 
			
		||||
  "license": "MIT"
 | 
			
		||||
}
 | 
			
		||||
@@ -1,7 +1,7 @@
 | 
			
		||||
import * as configApi from './config.js';
 | 
			
		||||
import { log } from './logger.js';
 | 
			
		||||
import { getDiagram, registerDiagram } from './diagram-api/diagramAPI.js';
 | 
			
		||||
import { detectType, getDiagramLoader } from './diagram-api/detectType.js';
 | 
			
		||||
import { detectType, getDiagramLoaderAndPriority } from './diagram-api/detectType.js';
 | 
			
		||||
import { UnknownDiagramError } from './errors.js';
 | 
			
		||||
import { encodeEntities } from './utils.js';
 | 
			
		||||
 | 
			
		||||
@@ -92,14 +92,14 @@ export const getDiagramFromText = async (
 | 
			
		||||
    // Trying to find the diagram
 | 
			
		||||
    getDiagram(type);
 | 
			
		||||
  } catch (error) {
 | 
			
		||||
    const loader = getDiagramLoader(type);
 | 
			
		||||
    const { loader, priority } = getDiagramLoaderAndPriority(type);
 | 
			
		||||
    if (!loader) {
 | 
			
		||||
      throw new UnknownDiagramError(`Diagram ${type} not found.`);
 | 
			
		||||
    }
 | 
			
		||||
    // Diagram not available, loading it.
 | 
			
		||||
    // new diagram will try getDiagram again and if fails then it is a valid throw
 | 
			
		||||
    const { id, diagram } = await loader();
 | 
			
		||||
    registerDiagram(id, diagram);
 | 
			
		||||
    registerDiagram(id, diagram, priority);
 | 
			
		||||
  }
 | 
			
		||||
  return new Diagram(text, metadata);
 | 
			
		||||
};
 | 
			
		||||
 
 | 
			
		||||
@@ -61,23 +61,37 @@ export const detectType = function (text: string, config?: MermaidConfig): strin
 | 
			
		||||
 * The first detector to return `true` is the diagram that will be loaded
 | 
			
		||||
 * and used, so put more specific detectors at the beginning!
 | 
			
		||||
 *
 | 
			
		||||
 * If two diagrams are registered with the same id,
 | 
			
		||||
 * the one with higher `priority` property will be used.
 | 
			
		||||
 *
 | 
			
		||||
 * @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);
 | 
			
		||||
  for (const { id, detector, priority, loader } of diagrams) {
 | 
			
		||||
    addDetector(id, detector, priority ?? 0, loader);
 | 
			
		||||
  }
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
export const addDetector = (key: string, detector: DiagramDetector, loader?: DiagramLoader) => {
 | 
			
		||||
  if (detectors[key]) {
 | 
			
		||||
    log.error(`Detector with key ${key} already exists`);
 | 
			
		||||
  } else {
 | 
			
		||||
    detectors[key] = { detector, loader };
 | 
			
		||||
export const addDetector = (
 | 
			
		||||
  key: string,
 | 
			
		||||
  detector: DiagramDetector,
 | 
			
		||||
  priority: number,
 | 
			
		||||
  loader?: DiagramLoader
 | 
			
		||||
) => {
 | 
			
		||||
  if (detectors[key] && priority <= detectors[key].priority) {
 | 
			
		||||
    log.error(
 | 
			
		||||
      `Detector with key ${key} already exists with priority ${detectors[key].priority}. Cannot add new detector with priority ${priority}`
 | 
			
		||||
    );
 | 
			
		||||
    return;
 | 
			
		||||
  }
 | 
			
		||||
  log.debug(`Detector with key ${key} added${loader ? ' with loader' : ''}`);
 | 
			
		||||
 | 
			
		||||
  detectors[key] = { detector, loader, priority };
 | 
			
		||||
  log.debug(
 | 
			
		||||
    `Detector with key ${key} added with priority ${priority} ${loader ? 'and loader' : ''}`
 | 
			
		||||
  );
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
export const getDiagramLoader = (key: string) => {
 | 
			
		||||
  return detectors[key].loader;
 | 
			
		||||
export const getDiagramLoaderAndPriority = (key: string) => {
 | 
			
		||||
  const { loader, priority } = detectors[key];
 | 
			
		||||
  return { loader, priority };
 | 
			
		||||
};
 | 
			
		||||
 
 | 
			
		||||
@@ -31,7 +31,7 @@ export const addDiagrams = () => {
 | 
			
		||||
  // This is added here to avoid race-conditions.
 | 
			
		||||
  // We could optimize the loading logic somehow.
 | 
			
		||||
  hasLoadedDiagrams = true;
 | 
			
		||||
  registerDiagram('error', errorDiagram, (text) => {
 | 
			
		||||
  registerDiagram('error', errorDiagram, 0, (text) => {
 | 
			
		||||
    return text.toLowerCase().trim() === 'error';
 | 
			
		||||
  });
 | 
			
		||||
  registerDiagram(
 | 
			
		||||
@@ -60,6 +60,7 @@ export const addDiagrams = () => {
 | 
			
		||||
      },
 | 
			
		||||
      init: () => null, // no op
 | 
			
		||||
    },
 | 
			
		||||
    0,
 | 
			
		||||
    (text) => {
 | 
			
		||||
      return text.toLowerCase().trimStart().startsWith('---');
 | 
			
		||||
    }
 | 
			
		||||
 
 | 
			
		||||
@@ -47,6 +47,7 @@ describe('DiagramAPI', () => {
 | 
			
		||||
        },
 | 
			
		||||
        styles: {},
 | 
			
		||||
      },
 | 
			
		||||
      0,
 | 
			
		||||
      detector
 | 
			
		||||
    );
 | 
			
		||||
    expect(getDiagram('loki')).not.toBeNull();
 | 
			
		||||
 
 | 
			
		||||
@@ -29,7 +29,7 @@ export const getCommonDb = () => {
 | 
			
		||||
  return _commonDb;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
const diagrams: Record<string, DiagramDefinition> = {};
 | 
			
		||||
const diagrams: Record<string, DiagramDefinition & { priority: number }> = {};
 | 
			
		||||
export interface Detectors {
 | 
			
		||||
  [key: string]: DiagramDetector;
 | 
			
		||||
}
 | 
			
		||||
@@ -37,7 +37,8 @@ export interface Detectors {
 | 
			
		||||
/**
 | 
			
		||||
 * Registers the given diagram with Mermaid.
 | 
			
		||||
 *
 | 
			
		||||
 * Can be used for third-party custom diagrams.
 | 
			
		||||
 * To be used internally by Mermaid.
 | 
			
		||||
 * Use `mermaid.registerExternalDiagrams` to register external diagrams.
 | 
			
		||||
 *
 | 
			
		||||
 * @param id - A unique ID for the given diagram.
 | 
			
		||||
 * @param diagram - The diagram definition.
 | 
			
		||||
@@ -46,14 +47,17 @@ export interface Detectors {
 | 
			
		||||
export const registerDiagram = (
 | 
			
		||||
  id: string,
 | 
			
		||||
  diagram: DiagramDefinition,
 | 
			
		||||
  priority: number,
 | 
			
		||||
  detector?: DiagramDetector
 | 
			
		||||
) => {
 | 
			
		||||
  if (diagrams[id]) {
 | 
			
		||||
    throw new Error(`Diagram ${id} already registered.`);
 | 
			
		||||
  if (diagrams[id] && priority <= diagrams[id].priority) {
 | 
			
		||||
    throw new Error(
 | 
			
		||||
      `Diagram ${id} already registered with priority ${diagrams[id].priority}. Cannot add new diagram with priority ${priority}`
 | 
			
		||||
    );
 | 
			
		||||
  }
 | 
			
		||||
  diagrams[id] = diagram;
 | 
			
		||||
  diagrams[id] = { ...diagram, priority };
 | 
			
		||||
  if (detector) {
 | 
			
		||||
    addDetector(id, detector);
 | 
			
		||||
    addDetector(id, detector, priority);
 | 
			
		||||
  }
 | 
			
		||||
  addStylesForDiagram(id, diagram.styles);
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -6,7 +6,7 @@ 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 }]) => {
 | 
			
		||||
    Object.entries(detectors).map(async ([key, { detector, loader, priority }]) => {
 | 
			
		||||
      if (loader) {
 | 
			
		||||
        try {
 | 
			
		||||
          getDiagram(key);
 | 
			
		||||
@@ -14,7 +14,7 @@ export const loadRegisteredDiagrams = async () => {
 | 
			
		||||
          try {
 | 
			
		||||
            // Register diagram if it is not already registered
 | 
			
		||||
            const { diagram, id } = await loader();
 | 
			
		||||
            registerDiagram(id, diagram, detector);
 | 
			
		||||
            registerDiagram(id, diagram, priority, detector);
 | 
			
		||||
          } catch (err) {
 | 
			
		||||
            // Remove failed diagram from detectors
 | 
			
		||||
            log.error(`Failed to load external diagram with key ${key}. Removing from detectors.`);
 | 
			
		||||
 
 | 
			
		||||
@@ -76,13 +76,23 @@ export interface DiagramDefinition {
 | 
			
		||||
 | 
			
		||||
export interface DetectorRecord {
 | 
			
		||||
  detector: DiagramDetector;
 | 
			
		||||
  priority: number;
 | 
			
		||||
  loader?: DiagramLoader;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * External diagrams, which are not bundled with mermaid should expose the following to be registered using the `mermaid.registerExternalDiagrams` function.
 | 
			
		||||
 *
 | 
			
		||||
 * @param id - An ID for the given diagram. If two diagrams are registered with the same ID, the one with the higher priority will be used.
 | 
			
		||||
 * @param detector - Function that returns `true` if a given mermaid text satisfies with this diagram definition.
 | 
			
		||||
 * @param loader - Function that returns a promise of the diagram definition.
 | 
			
		||||
 * @param priority - The priority of the diagram. Optional, defaults to 0.
 | 
			
		||||
 */
 | 
			
		||||
export interface ExternalDiagramDefinition {
 | 
			
		||||
  id: string;
 | 
			
		||||
  detector: DiagramDetector;
 | 
			
		||||
  loader: DiagramLoader;
 | 
			
		||||
  priority?: number;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export type DiagramDetector = (text: string, config?: MermaidConfig) => boolean;
 | 
			
		||||
 
 | 
			
		||||
@@ -21,6 +21,7 @@ describe('diagram detection', () => {
 | 
			
		||||
    addDetector(
 | 
			
		||||
      'loki',
 | 
			
		||||
      (str) => str.startsWith('loki'),
 | 
			
		||||
      0,
 | 
			
		||||
      () =>
 | 
			
		||||
        Promise.resolve({
 | 
			
		||||
          id: 'loki',
 | 
			
		||||
@@ -45,6 +46,37 @@ describe('diagram detection', () => {
 | 
			
		||||
    expect(diagram.type).toBe('loki');
 | 
			
		||||
  });
 | 
			
		||||
 | 
			
		||||
  test('should allow external diagrams to override internal ones with same ID', async () => {
 | 
			
		||||
    addDetector(
 | 
			
		||||
      'flowchart-elk',
 | 
			
		||||
      (str) => str.startsWith('flowchart-elk'),
 | 
			
		||||
      1,
 | 
			
		||||
      () =>
 | 
			
		||||
        Promise.resolve({
 | 
			
		||||
          id: 'flowchart-elk',
 | 
			
		||||
          diagram: {
 | 
			
		||||
            db: {
 | 
			
		||||
              getDiagramTitle: () => 'overridden',
 | 
			
		||||
            },
 | 
			
		||||
            parser: {
 | 
			
		||||
              parse: () => {
 | 
			
		||||
                // no-op
 | 
			
		||||
              },
 | 
			
		||||
            },
 | 
			
		||||
            renderer: {
 | 
			
		||||
              draw: () => {
 | 
			
		||||
                // no-op
 | 
			
		||||
              },
 | 
			
		||||
            },
 | 
			
		||||
            styles: {},
 | 
			
		||||
          },
 | 
			
		||||
        })
 | 
			
		||||
    );
 | 
			
		||||
    const diagram = (await getDiagramFromText('flowchart-elk TD; A-->B')) as Diagram;
 | 
			
		||||
    expect(diagram).toBeInstanceOf(Diagram);
 | 
			
		||||
    expect(diagram.db.getDiagramTitle?.()).toBe('overridden');
 | 
			
		||||
  });
 | 
			
		||||
 | 
			
		||||
  test('should throw the right error for incorrect diagram', async () => {
 | 
			
		||||
    await expect(getDiagramFromText('graph TD; A-->')).rejects.toThrowErrorMatchingInlineSnapshot(`
 | 
			
		||||
      "Parse error on line 2:
 | 
			
		||||
 
 | 
			
		||||
@@ -15,6 +15,7 @@ import { isDetailedError } from './utils.js';
 | 
			
		||||
import type { DetailedError } from './utils.js';
 | 
			
		||||
import type { ExternalDiagramDefinition } from './diagram-api/types.js';
 | 
			
		||||
import type { UnknownDiagramError } from './errors.js';
 | 
			
		||||
import { addDiagrams } from './diagram-api/diagram-orchestration.js';
 | 
			
		||||
 | 
			
		||||
export type {
 | 
			
		||||
  MermaidConfig,
 | 
			
		||||
@@ -243,6 +244,7 @@ const registerExternalDiagrams = async (
 | 
			
		||||
    lazyLoad?: boolean;
 | 
			
		||||
  } = {}
 | 
			
		||||
) => {
 | 
			
		||||
  addDiagrams();
 | 
			
		||||
  registerLazyLoadedDiagrams(...diagrams);
 | 
			
		||||
  if (lazyLoad === false) {
 | 
			
		||||
    await loadRegisteredDiagrams();
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user