mirror of
				https://github.com/mermaid-js/mermaid.git
				synced 2025-11-04 04:44:08 +01:00 
			
		
		
		
	Compare commits
	
		
			2 Commits
		
	
	
		
			mermaid@11
			...
			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 * as configApi from './config.js';
 | 
				
			||||||
import { log } from './logger.js';
 | 
					import { log } from './logger.js';
 | 
				
			||||||
import { getDiagram, registerDiagram } from './diagram-api/diagramAPI.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 { UnknownDiagramError } from './errors.js';
 | 
				
			||||||
import { encodeEntities } from './utils.js';
 | 
					import { encodeEntities } from './utils.js';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -92,14 +92,14 @@ export const getDiagramFromText = async (
 | 
				
			|||||||
    // Trying to find the diagram
 | 
					    // Trying to find the diagram
 | 
				
			||||||
    getDiagram(type);
 | 
					    getDiagram(type);
 | 
				
			||||||
  } catch (error) {
 | 
					  } catch (error) {
 | 
				
			||||||
    const loader = getDiagramLoader(type);
 | 
					    const { loader, priority } = getDiagramLoaderAndPriority(type);
 | 
				
			||||||
    if (!loader) {
 | 
					    if (!loader) {
 | 
				
			||||||
      throw new UnknownDiagramError(`Diagram ${type} not found.`);
 | 
					      throw new UnknownDiagramError(`Diagram ${type} not found.`);
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
    // Diagram not available, loading it.
 | 
					    // Diagram not available, loading it.
 | 
				
			||||||
    // new diagram will try getDiagram again and if fails then it is a valid throw
 | 
					    // new diagram will try getDiagram again and if fails then it is a valid throw
 | 
				
			||||||
    const { id, diagram } = await loader();
 | 
					    const { id, diagram } = await loader();
 | 
				
			||||||
    registerDiagram(id, diagram);
 | 
					    registerDiagram(id, diagram, priority);
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
  return new Diagram(text, metadata);
 | 
					  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
 | 
					 * The first detector to return `true` is the diagram that will be loaded
 | 
				
			||||||
 * and used, so put more specific detectors at the beginning!
 | 
					 * 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.
 | 
					 * @param diagrams - Diagrams to lazy load, and their detectors, in order of importance.
 | 
				
			||||||
 */
 | 
					 */
 | 
				
			||||||
export const registerLazyLoadedDiagrams = (...diagrams: ExternalDiagramDefinition[]) => {
 | 
					export const registerLazyLoadedDiagrams = (...diagrams: ExternalDiagramDefinition[]) => {
 | 
				
			||||||
  for (const { id, detector, loader } of diagrams) {
 | 
					  for (const { id, detector, priority, loader } of diagrams) {
 | 
				
			||||||
    addDetector(id, detector, loader);
 | 
					    addDetector(id, detector, priority ?? 0, loader);
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export const addDetector = (key: string, detector: DiagramDetector, loader?: DiagramLoader) => {
 | 
					export const addDetector = (
 | 
				
			||||||
  if (detectors[key]) {
 | 
					  key: string,
 | 
				
			||||||
    log.error(`Detector with key ${key} already exists`);
 | 
					  detector: DiagramDetector,
 | 
				
			||||||
  } else {
 | 
					  priority: number,
 | 
				
			||||||
    detectors[key] = { detector, loader };
 | 
					  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) => {
 | 
					export const getDiagramLoaderAndPriority = (key: string) => {
 | 
				
			||||||
  return detectors[key].loader;
 | 
					  const { loader, priority } = detectors[key];
 | 
				
			||||||
 | 
					  return { loader, priority };
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -31,7 +31,7 @@ export const addDiagrams = () => {
 | 
				
			|||||||
  // This is added here to avoid race-conditions.
 | 
					  // This is added here to avoid race-conditions.
 | 
				
			||||||
  // We could optimize the loading logic somehow.
 | 
					  // We could optimize the loading logic somehow.
 | 
				
			||||||
  hasLoadedDiagrams = true;
 | 
					  hasLoadedDiagrams = true;
 | 
				
			||||||
  registerDiagram('error', errorDiagram, (text) => {
 | 
					  registerDiagram('error', errorDiagram, 0, (text) => {
 | 
				
			||||||
    return text.toLowerCase().trim() === 'error';
 | 
					    return text.toLowerCase().trim() === 'error';
 | 
				
			||||||
  });
 | 
					  });
 | 
				
			||||||
  registerDiagram(
 | 
					  registerDiagram(
 | 
				
			||||||
@@ -60,6 +60,7 @@ export const addDiagrams = () => {
 | 
				
			|||||||
      },
 | 
					      },
 | 
				
			||||||
      init: () => null, // no op
 | 
					      init: () => null, // no op
 | 
				
			||||||
    },
 | 
					    },
 | 
				
			||||||
 | 
					    0,
 | 
				
			||||||
    (text) => {
 | 
					    (text) => {
 | 
				
			||||||
      return text.toLowerCase().trimStart().startsWith('---');
 | 
					      return text.toLowerCase().trimStart().startsWith('---');
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -47,6 +47,7 @@ describe('DiagramAPI', () => {
 | 
				
			|||||||
        },
 | 
					        },
 | 
				
			||||||
        styles: {},
 | 
					        styles: {},
 | 
				
			||||||
      },
 | 
					      },
 | 
				
			||||||
 | 
					      0,
 | 
				
			||||||
      detector
 | 
					      detector
 | 
				
			||||||
    );
 | 
					    );
 | 
				
			||||||
    expect(getDiagram('loki')).not.toBeNull();
 | 
					    expect(getDiagram('loki')).not.toBeNull();
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -29,7 +29,7 @@ export const getCommonDb = () => {
 | 
				
			|||||||
  return _commonDb;
 | 
					  return _commonDb;
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const diagrams: Record<string, DiagramDefinition> = {};
 | 
					const diagrams: Record<string, DiagramDefinition & { priority: number }> = {};
 | 
				
			||||||
export interface Detectors {
 | 
					export interface Detectors {
 | 
				
			||||||
  [key: string]: DiagramDetector;
 | 
					  [key: string]: DiagramDetector;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
@@ -37,7 +37,8 @@ export interface Detectors {
 | 
				
			|||||||
/**
 | 
					/**
 | 
				
			||||||
 * Registers the given diagram with Mermaid.
 | 
					 * 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 id - A unique ID for the given diagram.
 | 
				
			||||||
 * @param diagram - The diagram definition.
 | 
					 * @param diagram - The diagram definition.
 | 
				
			||||||
@@ -46,14 +47,17 @@ export interface Detectors {
 | 
				
			|||||||
export const registerDiagram = (
 | 
					export const registerDiagram = (
 | 
				
			||||||
  id: string,
 | 
					  id: string,
 | 
				
			||||||
  diagram: DiagramDefinition,
 | 
					  diagram: DiagramDefinition,
 | 
				
			||||||
 | 
					  priority: number,
 | 
				
			||||||
  detector?: DiagramDetector
 | 
					  detector?: DiagramDetector
 | 
				
			||||||
) => {
 | 
					) => {
 | 
				
			||||||
  if (diagrams[id]) {
 | 
					  if (diagrams[id] && priority <= diagrams[id].priority) {
 | 
				
			||||||
    throw new Error(`Diagram ${id} already registered.`);
 | 
					    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) {
 | 
					  if (detector) {
 | 
				
			||||||
    addDetector(id, detector);
 | 
					    addDetector(id, detector, priority);
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
  addStylesForDiagram(id, diagram.styles);
 | 
					  addStylesForDiagram(id, diagram.styles);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -6,7 +6,7 @@ export const loadRegisteredDiagrams = async () => {
 | 
				
			|||||||
  log.debug(`Loading registered diagrams`);
 | 
					  log.debug(`Loading registered diagrams`);
 | 
				
			||||||
  // Load all lazy loaded diagrams in parallel
 | 
					  // Load all lazy loaded diagrams in parallel
 | 
				
			||||||
  const results = await Promise.allSettled(
 | 
					  const results = await Promise.allSettled(
 | 
				
			||||||
    Object.entries(detectors).map(async ([key, { detector, loader }]) => {
 | 
					    Object.entries(detectors).map(async ([key, { detector, loader, priority }]) => {
 | 
				
			||||||
      if (loader) {
 | 
					      if (loader) {
 | 
				
			||||||
        try {
 | 
					        try {
 | 
				
			||||||
          getDiagram(key);
 | 
					          getDiagram(key);
 | 
				
			||||||
@@ -14,7 +14,7 @@ export const loadRegisteredDiagrams = async () => {
 | 
				
			|||||||
          try {
 | 
					          try {
 | 
				
			||||||
            // Register diagram if it is not already registered
 | 
					            // Register diagram if it is not already registered
 | 
				
			||||||
            const { diagram, id } = await loader();
 | 
					            const { diagram, id } = await loader();
 | 
				
			||||||
            registerDiagram(id, diagram, detector);
 | 
					            registerDiagram(id, diagram, priority, detector);
 | 
				
			||||||
          } catch (err) {
 | 
					          } catch (err) {
 | 
				
			||||||
            // Remove failed diagram from detectors
 | 
					            // Remove failed diagram from detectors
 | 
				
			||||||
            log.error(`Failed to load external diagram with key ${key}. Removing 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 {
 | 
					export interface DetectorRecord {
 | 
				
			||||||
  detector: DiagramDetector;
 | 
					  detector: DiagramDetector;
 | 
				
			||||||
 | 
					  priority: number;
 | 
				
			||||||
  loader?: DiagramLoader;
 | 
					  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 {
 | 
					export interface ExternalDiagramDefinition {
 | 
				
			||||||
  id: string;
 | 
					  id: string;
 | 
				
			||||||
  detector: DiagramDetector;
 | 
					  detector: DiagramDetector;
 | 
				
			||||||
  loader: DiagramLoader;
 | 
					  loader: DiagramLoader;
 | 
				
			||||||
 | 
					  priority?: number;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export type DiagramDetector = (text: string, config?: MermaidConfig) => boolean;
 | 
					export type DiagramDetector = (text: string, config?: MermaidConfig) => boolean;
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -21,6 +21,7 @@ describe('diagram detection', () => {
 | 
				
			|||||||
    addDetector(
 | 
					    addDetector(
 | 
				
			||||||
      'loki',
 | 
					      'loki',
 | 
				
			||||||
      (str) => str.startsWith('loki'),
 | 
					      (str) => str.startsWith('loki'),
 | 
				
			||||||
 | 
					      0,
 | 
				
			||||||
      () =>
 | 
					      () =>
 | 
				
			||||||
        Promise.resolve({
 | 
					        Promise.resolve({
 | 
				
			||||||
          id: 'loki',
 | 
					          id: 'loki',
 | 
				
			||||||
@@ -45,6 +46,37 @@ describe('diagram detection', () => {
 | 
				
			|||||||
    expect(diagram.type).toBe('loki');
 | 
					    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 () => {
 | 
					  test('should throw the right error for incorrect diagram', async () => {
 | 
				
			||||||
    await expect(getDiagramFromText('graph TD; A-->')).rejects.toThrowErrorMatchingInlineSnapshot(`
 | 
					    await expect(getDiagramFromText('graph TD; A-->')).rejects.toThrowErrorMatchingInlineSnapshot(`
 | 
				
			||||||
      "Parse error on line 2:
 | 
					      "Parse error on line 2:
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -15,6 +15,7 @@ import { isDetailedError } from './utils.js';
 | 
				
			|||||||
import type { DetailedError } from './utils.js';
 | 
					import type { DetailedError } from './utils.js';
 | 
				
			||||||
import type { ExternalDiagramDefinition } from './diagram-api/types.js';
 | 
					import type { ExternalDiagramDefinition } from './diagram-api/types.js';
 | 
				
			||||||
import type { UnknownDiagramError } from './errors.js';
 | 
					import type { UnknownDiagramError } from './errors.js';
 | 
				
			||||||
 | 
					import { addDiagrams } from './diagram-api/diagram-orchestration.js';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export type {
 | 
					export type {
 | 
				
			||||||
  MermaidConfig,
 | 
					  MermaidConfig,
 | 
				
			||||||
@@ -243,6 +244,7 @@ const registerExternalDiagrams = async (
 | 
				
			|||||||
    lazyLoad?: boolean;
 | 
					    lazyLoad?: boolean;
 | 
				
			||||||
  } = {}
 | 
					  } = {}
 | 
				
			||||||
) => {
 | 
					) => {
 | 
				
			||||||
 | 
					  addDiagrams();
 | 
				
			||||||
  registerLazyLoadedDiagrams(...diagrams);
 | 
					  registerLazyLoadedDiagrams(...diagrams);
 | 
				
			||||||
  if (lazyLoad === false) {
 | 
					  if (lazyLoad === false) {
 | 
				
			||||||
    await loadRegisteredDiagrams();
 | 
					    await loadRegisteredDiagrams();
 | 
				
			||||||
 
 | 
				
			|||||||
		Reference in New Issue
	
	Block a user