Skip to content

Commit

Permalink
Merge pull request #21 from Mermaid-Chart/fix/keep-comments-in-yaml
Browse files Browse the repository at this point in the history
fix(cli): keep comments in YAML frontmatter
  • Loading branch information
aloisklink authored Apr 22, 2024
2 parents ce94af6 + c0af0f6 commit ecd9e46
Show file tree
Hide file tree
Showing 5 changed files with 49 additions and 54 deletions.
4 changes: 2 additions & 2 deletions packages/cli/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -59,11 +59,11 @@
"@inquirer/select": "^1.3.1",
"@mermaidchart/sdk": "workspace:^",
"commander": "^11.1.0",
"js-yaml": "^4.1.0",
"remark": "^15.0.1",
"remark-frontmatter": "^5.0.0",
"remark-gfm": "^4.0.0",
"to-vfile": "^8.0.0",
"unist-util-visit": "^5.0.0"
"unist-util-visit": "^5.0.0",
"yaml": "^2.3.4"
}
}
7 changes: 7 additions & 0 deletions packages/cli/src/commander.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -435,6 +435,7 @@ describe('pull', () => {
const mockedDiagram = {
...mockedEmptyDiagram,
code: `---
# comments in YAML shouldn't be removed
title: My cool flowchart
---
flowchart TD
Expand All @@ -453,6 +454,7 @@ title: My cool flowchart
expect(diagramContents).toContain(
`id: https://test.mermaidchart.invalid/d/${mockedDiagram.documentID}`,
);
expect(diagramContents).toContain("# comments in YAML shouldn't be removed");
expect(diagramContents).toContain("flowchart TD\n A[I've been updated!]");
}
});
Expand Down Expand Up @@ -521,6 +523,11 @@ describe('push', () => {
code: expect.not.stringContaining('id:'),
}),
);
expect(vi.mocked(MermaidChart.prototype.setDocument)).toHaveBeenCalledWith(
expect.objectContaining({
code: expect.stringMatching(/^# This comment should be pushed to the server/m),
}),
);
});

it('should push documents from within markdown file', async () => {
Expand Down
57 changes: 20 additions & 37 deletions packages/cli/src/frontmatter.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,7 @@
/**
* Copied from https://github.com/mermaid-js/mermaid/blob/4a4e614b646bdb5f91f02d0483a7704b315d09fd/packages/mermaid/src/diagram-api/regexes.ts
*/

// The "* as yaml" part is necessary for tree-shaking
import * as yaml from 'js-yaml';
import { parseDocument, type Document, YAMLMap, isMap } from 'yaml';

const frontMatterRegex = /^-{3}\s*[\n\r](.*?)[\n\r]-{3}\s*[\n\r]+/s;
const urlIDRegex = /(?<baseURL>.*)\/d\/(?<documentID>[\w-]+)/;
Expand Down Expand Up @@ -59,20 +57,13 @@ function splitFrontMatter(text: string) {
}
}

function parseFrontMatterYAML(frontMatterYaml: string) {
let parsed: FrontMatterMetadata =
// TODO: replace with https://www.npmjs.com/package/yaml so that we can
// read/write comments too
yaml.load(frontMatterYaml, {
// To support config, we need JSON schema.
// https://www.yaml.org/spec/1.2/spec.html#id2803231
schema: yaml.JSON_SCHEMA,
}) ?? {};

// To handle runtime data type changes
parsed = typeof parsed === 'object' && !Array.isArray(parsed) ? parsed : {};
function parseFrontMatterYAML(frontMatterYaml: string): Document<YAMLMap, false> {
const document: Document = parseDocument(frontMatterYaml);
if (!isMap(document.contents)) {
document.contents = new YAMLMap();
}

return parsed;
return document as unknown as Document<YAMLMap, false>;
}

/**
Expand All @@ -85,7 +76,7 @@ function parseFrontMatterYAML(frontMatterYaml: string) {
export function extractFrontMatter(text: string): FrontMatterResult {
const { diagramText, frontMatter } = splitFrontMatter(text);

const parsed = parseFrontMatterYAML(frontMatter);
const parsed = parseFrontMatterYAML(frontMatter).toJSON();

const metadata: FrontMatterMetadata = {};

Expand Down Expand Up @@ -116,16 +107,16 @@ export function extractFrontMatter(text: string): FrontMatterResult {
* @param newMetadata - The metadata fields to update.
* @returns The text with the updated YAML frontmatter.
*/
export function injectFrontMatter(text: string, newMetadata: Partial<FrontMatterMetadata>) {
export function injectFrontMatter(text: string, newMetadata: Pick<FrontMatterMetadata, 'id'>) {
const { diagramText, frontMatter } = splitFrontMatter(text);

const parsed = parseFrontMatterYAML(frontMatter);
const document = parseFrontMatterYAML(frontMatter);

const mergedFrontmatter = { ...parsed, ...newMetadata };
for (const [key, value] of Object.entries(newMetadata)) {
document.contents.set(key, value);
}

return `---\n${yaml.dump(mergedFrontmatter, {
schema: yaml.JSON_SCHEMA,
})}---\n${diagramText}`;
return `---\n${document.toString()}---\n${diagramText}`;
}

/**
Expand All @@ -138,24 +129,16 @@ export function injectFrontMatter(text: string, newMetadata: Partial<FrontMatter
export function removeFrontMatterKeys(text: string, keysToRemove: Set<keyof FrontMatterMetadata>) {
const { diagramText, frontMatter } = splitFrontMatter(text);

const parsedFrontMatter = parseFrontMatterYAML(frontMatter);
const document = parseFrontMatterYAML(frontMatter);

const entries = Object.entries(parsedFrontMatter)
.map((val) => {
if (keysToRemove.has(val[0] as keyof FrontMatterMetadata)) {
return null;
} else {
return val;
}
})
.filter((val) => val) as [string, any][]; // eslint-disable-line @typescript-eslint/no-explicit-any
for (const key of keysToRemove) {
document.contents.delete(key);
}

if (entries.length === 0) {
if (document.contents.items.length === 0) {
// skip creating frontmatter if there is no frontmatter
return diagramText;
} else {
return `---\n${yaml.dump(Object.fromEntries(entries), {
schema: yaml.JSON_SCHEMA,
})}---\n${diagramText}`;
return `---\n${document.toString()}---\n${diagramText}`;
}
}
1 change: 1 addition & 0 deletions packages/cli/test/fixtures/connected-diagram.mmd
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
---
# This comment should be pushed to the server
title: My cool flowchart
id: https://test.mermaidchart.invalid/d/my-test-document-id
---
Expand Down
34 changes: 19 additions & 15 deletions pnpm-lock.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

0 comments on commit ecd9e46

Please sign in to comment.