From 8e84f7cc6c113c1e62881bd14d2659bd7159829e Mon Sep 17 00:00:00 2001 From: John Chilton Date: Wed, 20 Dec 2023 12:10:43 -0500 Subject: [PATCH] Refactor Markdown parsing out into a seperate file for reuse. --- client/src/components/Markdown/Markdown.vue | 74 ++---------------- client/src/components/Markdown/parse.test.js | 10 +++ client/src/components/Markdown/parse.ts | 79 ++++++++++++++++++++ 3 files changed, 97 insertions(+), 66 deletions(-) create mode 100644 client/src/components/Markdown/parse.test.js create mode 100644 client/src/components/Markdown/parse.ts diff --git a/client/src/components/Markdown/Markdown.vue b/client/src/components/Markdown/Markdown.vue index 7fdbf5b15f71..fc71066bb11d 100644 --- a/client/src/components/Markdown/Markdown.vue +++ b/client/src/components/Markdown/Markdown.vue @@ -75,15 +75,12 @@ import Vue from "vue"; import { useWorkflowStore } from "@/stores/workflowStore"; +import { splitMarkdown as splitMarkdownUnrendered } from "./parse"; + import MarkdownContainer from "./MarkdownContainer.vue"; import LoadingSpan from "components/LoadingSpan.vue"; import StsDownloadButton from "components/StsDownloadButton.vue"; -const FUNCTION_VALUE_REGEX = `\\s*(?:[\\w_\\-]+|\\"[^\\"]+\\"|\\'[^\\']+\\')\\s*`; -const FUNCTION_CALL = `\\s*[\\w\\|]+\\s*=` + FUNCTION_VALUE_REGEX; -const FUNCTION_CALL_LINE = `\\s*(\\w+)\\s*\\(\\s*(?:(${FUNCTION_CALL})(,${FUNCTION_CALL})*)?\\s*\\)\\s*`; -const FUNCTION_CALL_LINE_TEMPLATE = new RegExp(FUNCTION_CALL_LINE, "m"); - const mdNewline = markdownItRegexp(/
/, () => { return "

"; }); @@ -197,70 +194,15 @@ export default { } }, splitMarkdown(markdown) { - const sections = []; - let digest = markdown; - while (digest.length > 0) { - const galaxyStart = digest.indexOf("```galaxy"); - if (galaxyStart != -1) { - const galaxyEnd = digest.substr(galaxyStart + 1).indexOf("```"); - if (galaxyEnd != -1) { - if (galaxyStart > 0) { - const defaultContent = digest.substr(0, galaxyStart).trim(); - if (defaultContent) { - sections.push({ - name: "default", - content: md.render(defaultContent), - }); - } - } - const galaxyEndIndex = galaxyEnd + 4; - const galaxySection = digest.substr(galaxyStart, galaxyEndIndex); - let args = null; - try { - args = this.getArgs(galaxySection); - sections.push(args); - } catch (e) { - this.markdownErrors.push({ - error: "Found an unresolved tag.", - line: galaxySection, - }); - } - digest = digest.substr(galaxyStart + galaxyEndIndex); - } else { - digest = digest.substr(galaxyStart + 1); - } - } else { - sections.push({ - name: "default", - content: md.render(digest), - }); - break; + const { sections, markdownErrors } = splitMarkdownUnrendered(markdown); + markdownErrors.forEach((error) => markdownErrors.push(error)); + sections.forEach((section) => { + if (section.name == "default") { + section.content = md.render(section.content); } - } + }); return sections; }, - getArgs(content) { - const galaxy_function = FUNCTION_CALL_LINE_TEMPLATE.exec(content); - const args = {}; - const function_name = galaxy_function[1]; - // we need [... ] to return empty string, if regex doesn't match - const function_arguments = [...content.matchAll(new RegExp(FUNCTION_CALL, "g"))]; - for (let i = 0; i < function_arguments.length; i++) { - if (function_arguments[i] === undefined) { - continue; - } - const arguments_str = function_arguments[i].toString().replace(/,/g, "").trim(); - if (arguments_str) { - const [key, val] = arguments_str.split("="); - args[key.trim()] = val.replace(/['"]+/g, "").trim(); - } - } - return { - name: function_name, - args: args, - content: content, - }; - }, }, }; diff --git a/client/src/components/Markdown/parse.test.js b/client/src/components/Markdown/parse.test.js new file mode 100644 index 000000000000..11248bc67d5e --- /dev/null +++ b/client/src/components/Markdown/parse.test.js @@ -0,0 +1,10 @@ +import { getArgs } from "./parse"; + +describe("parse.ts", () => { + describe("getArgs", async () => { + it("parses simple directive expression", () => { + const args = getArgs("job_metrics(job_id=THISFAKEID)"); + expect(args.name).toBe("job_metrics"); + }); + }); +}); diff --git a/client/src/components/Markdown/parse.ts b/client/src/components/Markdown/parse.ts new file mode 100644 index 000000000000..900bbd3135db --- /dev/null +++ b/client/src/components/Markdown/parse.ts @@ -0,0 +1,79 @@ +const FUNCTION_VALUE_REGEX = `\\s*(?:[\\w_\\-]+|\\"[^\\"]+\\"|\\'[^\\']+\\')\\s*`; +const FUNCTION_CALL = `\\s*[\\w\\|]+\\s*=` + FUNCTION_VALUE_REGEX; +const FUNCTION_CALL_LINE = `\\s*(\\w+)\\s*\\(\\s*(?:(${FUNCTION_CALL})(,${FUNCTION_CALL})*)?\\s*\\)\\s*`; +const FUNCTION_CALL_LINE_TEMPLATE = new RegExp(FUNCTION_CALL_LINE, "m"); + +export function splitMarkdown(markdown: string) { + const sections = []; + const markdownErrors = []; + let digest = markdown; + while (digest.length > 0) { + const galaxyStart = digest.indexOf("```galaxy"); + if (galaxyStart != -1) { + const galaxyEnd = digest.substr(galaxyStart + 1).indexOf("```"); + if (galaxyEnd != -1) { + if (galaxyStart > 0) { + const defaultContent = digest.substr(0, galaxyStart).trim(); + if (defaultContent) { + sections.push({ + name: "default", + content: defaultContent, + }); + } + } + const galaxyEndIndex = galaxyEnd + 4; + const galaxySection = digest.substr(galaxyStart, galaxyEndIndex); + let args = null; + try { + args = getArgs(galaxySection); + sections.push(args); + } catch (e) { + markdownErrors.push({ + error: "Found an unresolved tag.", + line: galaxySection, + }); + } + digest = digest.substr(galaxyStart + galaxyEndIndex); + } else { + digest = digest.substr(galaxyStart + 1); + } + } else { + sections.push({ + name: "default", + content: digest, + }); + break; + } + } + return { sections, markdownErrors }; +} + +export function getArgs(content: string) { + const galaxy_function = FUNCTION_CALL_LINE_TEMPLATE.exec(content); + if (galaxy_function == null) { + throw Error("Failed to parse galaxy directive"); + } + type ArgsType = { [key: string]: string }; + const args: ArgsType = {}; + const function_name = galaxy_function[1]; + // we need [... ] to return empty string, if regex doesn't match + const function_arguments = [...content.matchAll(new RegExp(FUNCTION_CALL, "g"))]; + for (let i = 0; i < function_arguments.length; i++) { + if (function_arguments[i] === undefined) { + continue; + } + const arguments_str = function_arguments[i]?.toString().replace(/,/g, "").trim(); + if (arguments_str) { + const [key, val] = arguments_str.split("="); + if (key == undefined || val == undefined) { + throw Error("Failed to parse galaxy directive"); + } + args[key.trim()] = val.replace(/['"]+/g, "").trim(); + } + } + return { + name: function_name, + args: args, + content: content, + }; +}