Update: Added folder structure for usecase diagram

on-behalf-of: @Mermaid-Chart <hello@mermaidchart.com>
This commit is contained in:
shubham-mermaid
2025-08-06 14:54:29 +05:30
parent d93d9a521d
commit 5c04a8d09d
18 changed files with 351 additions and 2 deletions

View File

@@ -143,6 +143,7 @@ typeof
typestr
unshift
urlsafe
usecase
verifymethod
VERIFYMTHD
WARN_DOCSDIR_DOESNT_MATCH

View File

@@ -28,6 +28,7 @@ import architecture from '../diagrams/architecture/architectureDetector.js';
import { registerLazyLoadedDiagrams } from './detectType.js';
import { registerDiagram } from './diagramAPI.js';
import { treemap } from '../diagrams/treemap/detector.js';
import { usecase } from '../diagrams/usecase/detector.js';
import '../type.d.ts';
let hasLoadedDiagrams = false;
@@ -101,6 +102,7 @@ export const addDiagrams = () => {
xychart,
block,
radar,
treemap
treemap,
usecase
);
};

View File

@@ -0,0 +1,38 @@
import type { DiagramDB } from '../../diagram-api/types.js';
import type { UsecaseDiagramConfig, UsecaseNode } from './types.js';
import { cleanAndMerge } from '../../utils.js';
export class UsecaseDiagramDB implements DiagramDB {
public getNodes() {
return [];
}
public getConfig() {
return cleanAndMerge({}) as Required<UsecaseDiagramConfig>;
}
public addNode(node: UsecaseNode, level: number) {
if (level === 0) {
// TODO
}
}
public getRoot() {
return { name: '', children: [] };
}
public addClass(_id: string, _style: string) {
// TODO
}
public getClasses() {
// TODO
}
public getStylesForClass(_classSelector: string) {
// TODO
}
public clear() {
// commonClear();
}
}

View File

@@ -0,0 +1,22 @@
import type {
DiagramDetector,
DiagramLoader,
ExternalDiagramDefinition,
} from '../../diagram-api/types.js';
const id = 'usecase';
const detector: DiagramDetector = (txt) => {
return /^\s*usecase/.test(txt);
};
const loader: DiagramLoader = async () => {
const { diagram } = await import('./diagram.js');
return { id, diagram };
};
export const usecase: ExternalDiagramDefinition = {
id,
detector,
loader,
};

View File

@@ -0,0 +1,14 @@
import type { DiagramDefinition } from '../../diagram-api/types.js';
import { UsecaseDiagramDB } from './db.js';
import { parser } from './parser.js';
import { renderer } from './renderer.js';
import styles from './styles.js';
export const diagram: DiagramDefinition = {
parser,
get db() {
return new UsecaseDiagramDB();
},
renderer,
styles,
};

View File

@@ -0,0 +1,40 @@
import { parse } from '@mermaid-js/parser';
import type { ParserDefinition } from '../../diagram-api/types.js';
import { log } from '../../logger.js';
import { populateCommonDb } from '../common/populateCommonDb.js';
import type { UsecaseAst } from './types.js';
import { UsecaseDiagramDB } from './db.js';
/**
* Populates the database with data from the Usecase AST
* @param ast - The Usecase AST
*/
const populate = (ast: UsecaseAst, db: UsecaseDiagramDB) => {
// We need to bypass the type checking for populateCommonDb
// eslint-disable-next-line @typescript-eslint/no-explicit-any
populateCommonDb(ast as any, db);
};
export const parser: ParserDefinition = {
// @ts-expect-error - UsecaseDB is not assignable to DiagramDB
parser: { yy: undefined },
parse: async (text: string): Promise<void> => {
try {
// Use a generic parse that accepts any diagram type
const parseFunc = parse as (diagramType: string, text: string) => Promise<UsecaseAst>;
const ast = await parseFunc('usecase', text);
log.debug('Usecase AST:', ast);
const db = parser.parser?.yy;
if (!(db instanceof UsecaseDiagramDB)) {
throw new Error(
'parser.parser?.yy was not a UsecaseDiagramDB. This is due to a bug within Mermaid, please report this issue at https://github.com/mermaid-js/mermaid/issues.'
);
}
populate(ast, db);
} catch (error) {
log.error('Error parsing usecase:', error);
throw error;
}
},
};

View File

@@ -0,0 +1,21 @@
import type { Diagram } from '../../Diagram.js';
import type {
DiagramRenderer,
DiagramStyleClassDef,
DrawDefinition,
} from '../../diagram-api/types.js';
/**
* Draws the Usecase diagram
*/
const draw: DrawDefinition = (_text, _id, _version, _diagram: Diagram) => {
// TODO: Implement the draw function for the usecase diagram
};
const getClasses = function (
_text: string,
_diagramObj: Pick<Diagram, 'db'>
): Map<string, DiagramStyleClassDef> {
return new Map<string, DiagramStyleClassDef>();
};
export const renderer: DiagramRenderer = { draw, getClasses };

View File

@@ -0,0 +1,51 @@
import type { DiagramStylesProvider } from '../../diagram-api/types.js';
import { cleanAndMerge } from '../../utils.js';
import type { UsecaseStyleOptions } from './types.js';
const defaultUsecaseStyleOptions: UsecaseStyleOptions = {
sectionStrokeColor: 'black',
sectionStrokeWidth: '1',
sectionFillColor: '#efefef',
leafStrokeColor: 'black',
leafStrokeWidth: '1',
leafFillColor: '#efefef',
labelColor: 'black',
labelFontSize: '12px',
valueFontSize: '10px',
valueColor: 'black',
titleColor: 'black',
titleFontSize: '14px',
};
export const getStyles: DiagramStylesProvider = ({
usecase,
}: { usecase?: UsecaseStyleOptions } = {}) => {
const options = cleanAndMerge(defaultUsecaseStyleOptions, usecase);
return `
.usecaseNode.section {
stroke: ${options.sectionStrokeColor};
stroke-width: ${options.sectionStrokeWidth};
fill: ${options.sectionFillColor};
}
.usecaseNode.leaf {
stroke: ${options.leafStrokeColor};
stroke-width: ${options.leafStrokeWidth};
fill: ${options.leafFillColor};
}
.usecaseLabel {
fill: ${options.labelColor};
font-size: ${options.labelFontSize};
}
.usecaseValue {
fill: ${options.valueColor};
font-size: ${options.valueFontSize};
}
.usecaseTitle {
fill: ${options.titleColor};
font-size: ${options.titleFontSize};
}
`;
};
export default getStyles;

View File

@@ -0,0 +1,68 @@
import type { DiagramDBBase, DiagramStyleClassDef } from '../../diagram-api/types.js';
import type { BaseDiagramConfig } from '../../config.type.js';
export interface UsecaseNode {
name: string;
children?: UsecaseNode[];
value?: number;
parent?: UsecaseNode;
classSelector?: string;
cssCompiledStyles?: string[];
}
export interface UsecaseDiagramDB extends DiagramDBBase<UsecaseDiagramConfig> {
getNodes: () => UsecaseNode[];
addNode: (node: UsecaseNode, level: number) => void;
getRoot: () => UsecaseNode | undefined;
getClasses: () => Map<string, DiagramStyleClassDef>;
addClass: (className: string, style: string) => void;
getStylesForClass: (classSelector: string) => string[];
// Update
}
export interface UsecaseStyleOptions {
sectionStrokeColor?: string;
sectionStrokeWidth?: string;
sectionFillColor?: string;
leafStrokeColor?: string;
leafStrokeWidth?: string;
leafFillColor?: string;
labelColor?: string;
labelFontSize?: string;
valueFontSize?: string;
valueColor?: string;
titleColor?: string;
titleFontSize?: string;
}
export interface UsecaseData {
nodes: UsecaseNode[];
levels: Map<UsecaseNode, number>;
root?: UsecaseNode;
outerNodes: UsecaseNode[];
}
export interface UsecaseItem {
$type: string;
name: string;
value?: number;
classSelector?: string;
}
export interface UsecaseAst {
title?: string;
description?: string;
}
// Define the UsecaseDiagramConfig interface
export interface UsecaseDiagramConfig extends BaseDiagramConfig {
padding?: number;
diagramPadding?: number;
showValues?: boolean;
nodeWidth?: number;
nodeHeight?: number;
borderWidth?: number;
valueFontSize?: number;
labelFontSize?: number;
valueFormat?: string;
}

View File

@@ -35,6 +35,11 @@
"id": "treemap",
"grammar": "src/language/treemap/treemap.langium",
"fileExtensions": [".mmd", ".mermaid"]
},
{
"id": "usecase",
"grammar": "src/language/usecase/usecase.langium",
"fileExtensions": [".mmd", ".mermaid"]
}
],
"mode": "production",

View File

@@ -9,6 +9,7 @@ export {
GitGraph,
Radar,
Treemap,
Usecase,
Branch,
Commit,
Merge,
@@ -24,6 +25,7 @@ export {
isBranch,
isCommit,
isMerge,
isUsecase,
} from './generated/ast.js';
export {
@@ -35,6 +37,7 @@ export {
GitGraphGeneratedModule,
RadarGeneratedModule,
TreemapGeneratedModule,
UsecaseGeneratedModule,
} from './generated/module.js';
export * from './gitGraph/index.js';
@@ -45,3 +48,4 @@ export * from './pie/index.js';
export * from './architecture/index.js';
export * from './radar/index.js';
export * from './treemap/index.js';
export * from './usecase/index.js';

View File

@@ -0,0 +1 @@
export * from './module.js';

View File

@@ -0,0 +1,35 @@
import { type DefaultSharedCoreModuleContext, type LangiumCoreServices } from 'langium';
import type { Module, PartialLangiumCoreServices } from 'langium';
import { EmptyFileSystem } from 'langium';
import { UsecaseTokenBuilder } from './tokenBuilder.js';
import { UsecaseValueConverter } from './valueConverter.js';
import { UsecaseValidator } from './usecase-validator.js';
interface UsecaseAddedServices {
parser: {
TokenBuilder: UsecaseTokenBuilder;
ValueConverter: UsecaseValueConverter;
};
validation: {
UsecaseValidator: UsecaseValidator;
};
}
export type UsecaseServices = LangiumCoreServices & UsecaseAddedServices;
export const UsecaseModule: Module<
UsecaseServices,
PartialLangiumCoreServices & UsecaseAddedServices
> = {
parser: {
TokenBuilder: () => new UsecaseTokenBuilder(),
ValueConverter: () => new UsecaseValueConverter(),
},
validation: {
UsecaseValidator: () => new UsecaseValidator(),
},
};
export function createUsecaseServices(_context: DefaultSharedCoreModuleContext = EmptyFileSystem) {
// TODO
}

View File

@@ -0,0 +1,7 @@
import { AbstractMermaidTokenBuilder } from '../common/index.js';
export class UsecaseTokenBuilder extends AbstractMermaidTokenBuilder {
public constructor() {
super(['usecase']);
}
}

View File

@@ -0,0 +1,12 @@
import type { UsecaseServices } from './module.js';
/**
* Register custom validation checks.
*/
export function registerValidationChecks(_services: UsecaseServices) {
// TODO
}
/**
* Implementation of custom validations.
*/
export class UsecaseValidator {}

View File

@@ -0,0 +1 @@
// TODO

View File

@@ -0,0 +1,12 @@
import type { CstNode, GrammarAST, ValueType } from 'langium';
import { AbstractMermaidValueConverter } from '../common/index.js';
export class UsecaseValueConverter extends AbstractMermaidValueConverter {
protected runCustomConverter(
_rule: GrammarAST.AbstractRule,
_input: string,
_cstNode: CstNode
): ValueType | undefined {
return undefined;
}
}

View File

@@ -1,6 +1,15 @@
import type { LangiumParser, ParseResult } from 'langium';
import type { Info, Packet, Pie, Architecture, GitGraph, Radar, Treemap } from './index.js';
import type {
Info,
Packet,
Pie,
Architecture,
GitGraph,
Radar,
Treemap,
// Usecase,
} from './index.js';
export type DiagramAST = Info | Packet | Pie | Architecture | GitGraph | Radar;
@@ -41,6 +50,11 @@ const initializers = {
const parser = createTreemapServices().Treemap.parser.LangiumParser;
parsers.treemap = parser;
},
// usecase: async () => {
// const { createUsecaseServices } = await import('./language/usecase/index.js');
// const parser = createUsecaseServices().Usecase.parser.LangiumParser;
// parsers.usecase = parser;
// },
} as const;
export async function parse(diagramType: 'info', text: string): Promise<Info>;
@@ -50,6 +64,7 @@ export async function parse(diagramType: 'architecture', text: string): Promise<
export async function parse(diagramType: 'gitGraph', text: string): Promise<GitGraph>;
export async function parse(diagramType: 'radar', text: string): Promise<Radar>;
export async function parse(diagramType: 'treemap', text: string): Promise<Treemap>;
// export async function parse(diagramType: 'usecase', text: string): Promise<Usecase>;
export async function parse<T extends DiagramAST>(
diagramType: keyof typeof initializers,