3.7 KB128 lines
Blame
1import { load, JSON_SCHEMA } from 'js-yaml';
2import assert from 'node:assert';
3import Ajv2019, { type JSONSchemaType } from 'ajv/dist/2019.js';
4import type { MermaidConfig, BaseDiagramConfig } from '../packages/mermaid/src/config.type.js';
5
6/**
7 * All of the keys in the mermaid config that have a mermaid diagram config.
8 */
9const MERMAID_CONFIG_DIAGRAM_KEYS = [
10 'flowchart',
11 'sequence',
12 'gantt',
13 'journey',
14 'class',
15 'state',
16 'er',
17 'pie',
18 'quadrantChart',
19 'xyChart',
20 'requirement',
21 'mindmap',
22 'kanban',
23 'timeline',
24 'gitGraph',
25 'c4',
26 'sankey',
27 'block',
28 'packet',
29 'architecture',
30 'radar',
31] as const;
32
33/**
34 * Generate default values from the JSON Schema.
35 *
36 * AJV does not support nested default values yet (or default values with $ref),
37 * so we need to manually find them (this may be fixed in ajv v9).
38 *
39 * @param mermaidConfigSchema - The Mermaid JSON Schema to use.
40 * @returns The default mermaid config object.
41 */
42function generateDefaults(mermaidConfigSchema: JSONSchemaType<MermaidConfig>) {
43 const ajv = new Ajv2019({
44 useDefaults: true,
45 allowUnionTypes: true,
46 strict: true,
47 });
48
49 ajv.addKeyword({
50 keyword: 'meta:enum', // used by jsonschema2md
51 errors: false,
52 });
53 ajv.addKeyword({
54 keyword: 'tsType', // used by json-schema-to-typescript
55 errors: false,
56 });
57
58 // ajv currently doesn't support nested default values, see https://github.com/ajv-validator/ajv/issues/1718
59 // (may be fixed in v9) so we need to manually use sub-schemas
60 const mermaidDefaultConfig = {};
61
62 assert.ok(mermaidConfigSchema.$defs);
63 const baseDiagramConfig = mermaidConfigSchema.$defs.BaseDiagramConfig;
64
65 for (const key of MERMAID_CONFIG_DIAGRAM_KEYS) {
66 const subSchemaRef = mermaidConfigSchema.properties[key].$ref;
67 const [root, defs, defName] = subSchemaRef.split('/');
68 assert.strictEqual(root, '#');
69 assert.strictEqual(defs, '$defs');
70 const subSchema = {
71 $schema: mermaidConfigSchema.$schema,
72 $defs: mermaidConfigSchema.$defs,
73 ...mermaidConfigSchema.$defs[defName],
74 } as JSONSchemaType<BaseDiagramConfig>;
75
76 const validate = ajv.compile(subSchema);
77
78 mermaidDefaultConfig[key] = {};
79
80 for (const required of subSchema.required ?? []) {
81 if (subSchema.properties[required] === undefined && baseDiagramConfig.properties[required]) {
82 mermaidDefaultConfig[key][required] = baseDiagramConfig.properties[required].default;
83 }
84 }
85 if (!validate(mermaidDefaultConfig[key])) {
86 throw new Error(
87 `schema for subconfig ${key} does not have valid defaults! Errors were ${JSON.stringify(
88 validate.errors,
89 undefined,
90 2
91 )}`
92 );
93 }
94 }
95
96 const validate = ajv.compile(mermaidConfigSchema);
97
98 if (!validate(mermaidDefaultConfig)) {
99 throw new Error(
100 `Mermaid config JSON Schema does not have valid defaults! Errors were ${JSON.stringify(
101 validate.errors,
102 undefined,
103 2
104 )}`
105 );
106 }
107
108 return mermaidDefaultConfig;
109}
110
111export const loadSchema = (src: string, filename: string): JSONSchemaType<MermaidConfig> => {
112 const jsonSchema = load(src, {
113 filename,
114 // only allow JSON types in our YAML doc (will probably be default in YAML 1.3)
115 // e.g. `true` will be parsed a boolean `true`, `True` will be parsed as string `"True"`.
116 schema: JSON_SCHEMA,
117 }) as JSONSchemaType<MermaidConfig>;
118 return jsonSchema;
119};
120
121export const getDefaults = (schema: JSONSchemaType<MermaidConfig>) => {
122 return `export default ${JSON.stringify(generateDefaults(schema), undefined, 2)};`;
123};
124
125export const getSchema = (schema: JSONSchemaType<MermaidConfig>) => {
126 return `export default ${JSON.stringify(schema, undefined, 2)};`;
127};
128