From 92a292326e9e89efd945100cb713e2b1c52c12d3 Mon Sep 17 00:00:00 2001 From: simon busch Date: Tue, 24 Oct 2023 08:04:02 +0200 Subject: [PATCH 01/15] [ADD] basic functionality to parse an existing markdown, process it, add json, mermaid and svg blocks instead of threatdown --- lib/md-converter.ts | 118 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 118 insertions(+) create mode 100644 lib/md-converter.ts diff --git a/lib/md-converter.ts b/lib/md-converter.ts new file mode 100644 index 0000000..ee6f565 --- /dev/null +++ b/lib/md-converter.ts @@ -0,0 +1,118 @@ +import { parse } from "./parser/index"; +import { compileToMermaid } from "./compiler"; +import { renderMermaid } from "./renderer"; +import fs from "fs"; +// TODO +// remove this, local dev +const file = fs.readFileSync("./test.md", "utf8"); + +const getMermaid = async (mermaidFormatedData: string): Promise => { + const testSvg = await renderMermaid(mermaidFormatedData); + return testSvg; +}; + +// This function will actually crete the svg and json files +const getThreatdownContent = (trimmedContent: string) => { + const jsonFormatedData = parse(trimmedContent); + writeFileType(JSON.stringify(jsonFormatedData), "json"); + + const mermaidFormatedData = compileToMermaid(jsonFormatedData); + + // returns the svg + getMermaid(mermaidFormatedData) + .then((res: string) => { + writeFileType(res, "svg"); + }) + .catch((err) => { + console.log(err); + }); +}; + +// This function write the svg and json files +const writeFileType = (svg: string, type: string) => { + fs.writeFile(`threatdown-${Date.now()}.${type}`, svg, (err) => { + if (err) throw err; + console.log("The file has been saved!"); + }); +}; + +// Define an asynchronous function to process each match +const processMatchAsync = async (match: string) => { + const cleanMatch = match + .trim() + .replace(/^```threatdown/, "") + .replace(/```$/, ""); + const jsonFormatedData = parse(cleanMatch); + const mermaidData = compileToMermaid(jsonFormatedData); + + // Define an asynchronous function to get the Mermaid SVG + const getMermaidAsync = async (mermaidData: string) => { + try { + const res = await getMermaid(mermaidData); + return res; + } catch (err) { + console.error(err); + return ""; + } + }; + + const mermaidSvg = await getMermaidAsync(mermaidData); + + return ( + ` \n` + + "## JSON \n" + + // Render JSON + "```json\n" + + JSON.stringify(jsonFormatedData) + + "\n```\n" + + "## MERMAID \n" + + // Render Mermaid + "```mermaid\n" + + mermaidData + + "\n```\n" + + "## SVG \n" + + "\n" + + mermaidSvg + + "\n" + ); +}; + +// This function takes a file as input and update it adding the svg and json +const generateUpdateMd = (file: string) => { + const threatdownRegex = /```threatdown([\s\S]*?)```/g; + const threatdownMatches = file.match(threatdownRegex); + console.log(threatdownMatches); + + if (!threatdownMatches) { + throw new Error("No threatdown content found"); + } + + threatdownMatches.forEach((threatdownContent: string) => { + const trimmedContent = threatdownContent + .trim() + .replace(/^```threatdown/, "") + .replace(/```$/, ""); + console.log("trimmedContent", trimmedContent); + }); + + const newFilePromises = threatdownMatches.map(processMatchAsync); + Promise.all(newFilePromises) + .then((newFileContentArray) => { + let newFile = file; + for (let i = 0; i < threatdownMatches.length; i++) { + newFile = newFile.replace(threatdownMatches[i], newFileContentArray[i]); + } + + fs.writeFile(`test-update.md`, newFile, (err) => { + if (err) throw err; + console.log("The file has been saved!"); + }); + }) + .catch((err) => { + console.error(err); + }); +}; + +// TODO +// remove this, local dev +generateUpdateMd(file); From 3f46ffd0c77a631efc14e2029a1c82f4820c3a85 Mon Sep 17 00:00:00 2001 From: simon busch Date: Tue, 24 Oct 2023 08:24:45 +0200 Subject: [PATCH 02/15] [UPD] refactor and clean up function + add todo --- bin/threatdown.ts | 6 +++++- lib/md-converter.ts | 39 +++++++++------------------------------ lib/parser/helpers.ts | 2 ++ 3 files changed, 16 insertions(+), 31 deletions(-) diff --git a/bin/threatdown.ts b/bin/threatdown.ts index 4e1b253..2523a4a 100755 --- a/bin/threatdown.ts +++ b/bin/threatdown.ts @@ -39,7 +39,7 @@ async function main () { const inputFile = positionals.shift(); // non-null assertion safe because the outputType has a default // eslint-disable-next-line @typescript-eslint/no-non-null-assertion - if (!inputFile || !["json", "mermaid", "svg"].includes(values.type!)) { + if (!inputFile || !["json", "mermaid", "svg"].includes(values.type!)) { // TODO add md as option here usage(); } else { const fileContent = readFileSync(resolve(process.cwd(), inputFile), { encoding: "utf8" }); @@ -71,6 +71,10 @@ async function main () { console.log(svgContent); } } + + if (values.type === "md") { + // TODO + } } } diff --git a/lib/md-converter.ts b/lib/md-converter.ts index ee6f565..87fd6c2 100644 --- a/lib/md-converter.ts +++ b/lib/md-converter.ts @@ -2,16 +2,14 @@ import { parse } from "./parser/index"; import { compileToMermaid } from "./compiler"; import { renderMermaid } from "./renderer"; import fs from "fs"; -// TODO -// remove this, local dev -const file = fs.readFileSync("./test.md", "utf8"); +import { threatdownRegex } from "./parser/helpers"; const getMermaid = async (mermaidFormatedData: string): Promise => { const testSvg = await renderMermaid(mermaidFormatedData); return testSvg; }; -// This function will actually crete the svg and json files +// This function will actually crete the svg and json files and write to system const getThreatdownContent = (trimmedContent: string) => { const jsonFormatedData = parse(trimmedContent); writeFileType(JSON.stringify(jsonFormatedData), "json"); @@ -45,31 +43,21 @@ const processMatchAsync = async (match: string) => { const jsonFormatedData = parse(cleanMatch); const mermaidData = compileToMermaid(jsonFormatedData); - // Define an asynchronous function to get the Mermaid SVG - const getMermaidAsync = async (mermaidData: string) => { - try { - const res = await getMermaid(mermaidData); - return res; - } catch (err) { - console.error(err); - return ""; - } - }; - - const mermaidSvg = await getMermaidAsync(mermaidData); + const mermaidSvg = await getMermaid(mermaidData); return ( ` \n` + - "## JSON \n" + // Render JSON + "## JSON \n" + "```json\n" + JSON.stringify(jsonFormatedData) + "\n```\n" + - "## MERMAID \n" + // Render Mermaid + "## MERMAID \n" + "```mermaid\n" + mermaidData + "\n```\n" + + // Render SVG "## SVG \n" + "\n" + mermaidSvg + @@ -78,23 +66,14 @@ const processMatchAsync = async (match: string) => { }; // This function takes a file as input and update it adding the svg and json -const generateUpdateMd = (file: string) => { - const threatdownRegex = /```threatdown([\s\S]*?)```/g; +const generateUpdatedMd = (filePath: string) => { + const file = fs.readFileSync(filePath, "utf8"); const threatdownMatches = file.match(threatdownRegex); - console.log(threatdownMatches); if (!threatdownMatches) { throw new Error("No threatdown content found"); } - threatdownMatches.forEach((threatdownContent: string) => { - const trimmedContent = threatdownContent - .trim() - .replace(/^```threatdown/, "") - .replace(/```$/, ""); - console.log("trimmedContent", trimmedContent); - }); - const newFilePromises = threatdownMatches.map(processMatchAsync); Promise.all(newFilePromises) .then((newFileContentArray) => { @@ -115,4 +94,4 @@ const generateUpdateMd = (file: string) => { // TODO // remove this, local dev -generateUpdateMd(file); +generateUpdatedMd("./test.md"); diff --git a/lib/parser/helpers.ts b/lib/parser/helpers.ts index 358545b..c641120 100644 --- a/lib/parser/helpers.ts +++ b/lib/parser/helpers.ts @@ -182,3 +182,5 @@ export function parseLine (line: string | string[]): string { return line; } + +export const threatdownRegex = /```threatdown([\s\S]*?)```/g; From ad6572f501f4a446177a15d2f20a49ca33c42f3c Mon Sep 17 00:00:00 2001 From: simon busch Date: Tue, 24 Oct 2023 09:10:58 +0200 Subject: [PATCH 03/15] =?UTF-8?q?[ADD]=C2=A0new=20command=20in=20cli=20and?= =?UTF-8?q?=20update=20the=20existing=20code?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- bin/threatdown.ts | 14 +++++++--- lib/index.ts | 1 + lib/md-converter.ts | 61 +++++++++++++++++++++++++------------------ lib/parser/helpers.ts | 2 -- 4 files changed, 48 insertions(+), 30 deletions(-) diff --git a/bin/threatdown.ts b/bin/threatdown.ts index 2523a4a..cc58783 100755 --- a/bin/threatdown.ts +++ b/bin/threatdown.ts @@ -7,6 +7,7 @@ import { parse, compileToMermaid, renderMermaid, + generateUpdatedMd, } from "../lib"; const { @@ -31,15 +32,16 @@ function usage () { console.log(`Usage: threatdown --output Write result to file - --type Change output type, must be one of "json", "mermaid" or "svg" + --type Change output type, must be one of "json", "mermaid", "svg" or "md" `); } async function main () { + const inputFile = positionals.shift(); // non-null assertion safe because the outputType has a default // eslint-disable-next-line @typescript-eslint/no-non-null-assertion - if (!inputFile || !["json", "mermaid", "svg"].includes(values.type!)) { // TODO add md as option here + if (!inputFile || !["json", "mermaid", "svg", "md"].includes(values.type!)) { // TODO add md as option here usage(); } else { const fileContent = readFileSync(resolve(process.cwd(), inputFile), { encoding: "utf8" }); @@ -72,8 +74,14 @@ async function main () { } } + //!! untested yet + const updatedMarkdown = await generateUpdatedMd(inputFile); if (values.type === "md") { - // TODO + if (values.output) { + writeFileSync(resolve(process.cwd(), values.output), updatedMarkdown); + } else { + console.log(updatedMarkdown); + } } } } diff --git a/lib/index.ts b/lib/index.ts index cb1608b..26a0f4e 100644 --- a/lib/index.ts +++ b/lib/index.ts @@ -1,3 +1,4 @@ export { parse } from "./parser"; export { compileToMermaid } from "./compiler"; export { renderMermaid } from "./renderer"; +export { generateUpdatedMd } from "./md-converter"; diff --git a/lib/md-converter.ts b/lib/md-converter.ts index 87fd6c2..dd4bdee 100644 --- a/lib/md-converter.ts +++ b/lib/md-converter.ts @@ -2,8 +2,8 @@ import { parse } from "./parser/index"; import { compileToMermaid } from "./compiler"; import { renderMermaid } from "./renderer"; import fs from "fs"; -import { threatdownRegex } from "./parser/helpers"; +const threatdownRegex = /```threatdown([\s\S]*?)```/g; const getMermaid = async (mermaidFormatedData: string): Promise => { const testSvg = await renderMermaid(mermaidFormatedData); return testSvg; @@ -35,6 +35,7 @@ const writeFileType = (svg: string, type: string) => { }; // Define an asynchronous function to process each match +//TODO: Define if we want to add all type or dynamically chose const processMatchAsync = async (match: string) => { const cleanMatch = match .trim() @@ -65,33 +66,43 @@ const processMatchAsync = async (match: string) => { ); }; -// This function takes a file as input and update it adding the svg and json -const generateUpdatedMd = (filePath: string) => { - const file = fs.readFileSync(filePath, "utf8"); - const threatdownMatches = file.match(threatdownRegex); +// generate the updated markdown file +export const generateUpdatedMd = async (filePath: string) => { + try { + const file = await fs.promises.readFile(filePath, "utf8"); - if (!threatdownMatches) { - throw new Error("No threatdown content found"); - } + const threatdownMatches = file.match(threatdownRegex); - const newFilePromises = threatdownMatches.map(processMatchAsync); - Promise.all(newFilePromises) - .then((newFileContentArray) => { - let newFile = file; - for (let i = 0; i < threatdownMatches.length; i++) { - newFile = newFile.replace(threatdownMatches[i], newFileContentArray[i]); - } - - fs.writeFile(`test-update.md`, newFile, (err) => { - if (err) throw err; - console.log("The file has been saved!"); - }); - }) - .catch((err) => { - console.error(err); - }); + if (!threatdownMatches) { + throw new Error("No threatdown content found"); + } + + const newFilePromises = threatdownMatches.map(processMatchAsync); + const newFileContentArray = await Promise.all(newFilePromises); + + let newFile = file; + for (let i = 0; i < threatdownMatches.length; i++) { + newFile = newFile.replace(threatdownMatches[i], newFileContentArray[i]); + } + + return newFile; + } catch (err) { + console.error(err); + throw err; + } }; // TODO // remove this, local dev -generateUpdatedMd("./test.md"); +generateUpdatedMd("./test.md") + .then((res) => { + console.log(res); + + fs.writeFile("test-uodate.md", res, (err) => { + if (err) throw err; + console.log("The file has been saved!"); + }); + }) + .catch((err) => { + console.error(err); + }); diff --git a/lib/parser/helpers.ts b/lib/parser/helpers.ts index c641120..358545b 100644 --- a/lib/parser/helpers.ts +++ b/lib/parser/helpers.ts @@ -182,5 +182,3 @@ export function parseLine (line: string | string[]): string { return line; } - -export const threatdownRegex = /```threatdown([\s\S]*?)```/g; From e03573315fee5c88fd53dc7a9ab8b1e1c47c66d8 Mon Sep 17 00:00:00 2001 From: simon busch Date: Tue, 24 Oct 2023 09:51:28 +0200 Subject: [PATCH 04/15] [UPD] threatdown bin to pass dynamically the type we want embbeded in the md, create new param, update the md-converter accordingly --- bin/threatdown.ts | 17 ++++++++++++---- lib/md-converter.ts | 49 +++++++++++++++++++++++++-------------------- 2 files changed, 40 insertions(+), 26 deletions(-) diff --git a/bin/threatdown.ts b/bin/threatdown.ts index cc58783..b70922b 100755 --- a/bin/threatdown.ts +++ b/bin/threatdown.ts @@ -25,6 +25,10 @@ const { short: "t", default: "mermaid", }, + mdEmbeddedType: { + type: "string", + default: "mermaid", + } }, }); @@ -33,6 +37,7 @@ function usage () { --output Write result to file --type Change output type, must be one of "json", "mermaid", "svg" or "md" + --mdEmbeddedType Choose the mdEmbeddedType you want in the updated Markdown must be one of "json", "mermaid", "svg" `); } @@ -75,12 +80,16 @@ async function main () { } //!! untested yet - const updatedMarkdown = await generateUpdatedMd(inputFile); if (values.type === "md") { - if (values.output) { - writeFileSync(resolve(process.cwd(), values.output), updatedMarkdown); + if (!values.mdEmbeddedType || !["json", "mermaid", "svg"].includes(values.mdEmbeddedType)) { + usage(); } else { - console.log(updatedMarkdown); + const updatedMarkdown = await generateUpdatedMd(fileContent, values.mdEmbeddedType); + if (values.output) { + writeFileSync(resolve(process.cwd(), values.output), updatedMarkdown); + } else { + console.log(updatedMarkdown); + } } } } diff --git a/lib/md-converter.ts b/lib/md-converter.ts index dd4bdee..1121654 100644 --- a/lib/md-converter.ts +++ b/lib/md-converter.ts @@ -3,6 +3,10 @@ import { compileToMermaid } from "./compiler"; import { renderMermaid } from "./renderer"; import fs from "fs"; +// TODO/ remove this later: +import { readFileSync } from "node:fs"; +import { resolve } from "node:path"; + const threatdownRegex = /```threatdown([\s\S]*?)```/g; const getMermaid = async (mermaidFormatedData: string): Promise => { const testSvg = await renderMermaid(mermaidFormatedData); @@ -35,49 +39,49 @@ const writeFileType = (svg: string, type: string) => { }; // Define an asynchronous function to process each match -//TODO: Define if we want to add all type or dynamically chose -const processMatchAsync = async (match: string) => { +const processMatchAsync = async (match: string, mdEmbeddedType: string) => { const cleanMatch = match .trim() .replace(/^```threatdown/, "") .replace(/```$/, ""); const jsonFormatedData = parse(cleanMatch); const mermaidData = compileToMermaid(jsonFormatedData); - const mermaidSvg = await getMermaid(mermaidData); return ( ` \n` + // Render JSON - "## JSON \n" + - "```json\n" + - JSON.stringify(jsonFormatedData) + - "\n```\n" + + `${ + mdEmbeddedType === "json" + ? "## JSON \n" + + "```json\n" + + JSON.stringify(jsonFormatedData) + + "\n```\n" + : "" + }` + // Render Mermaid - "## MERMAID \n" + - "```mermaid\n" + - mermaidData + - "\n```\n" + + `${ + mdEmbeddedType === "mermaid" + ? "## MERMAID \n" + "```mermaid\n" + mermaidData + "\n```\n" + : "" + }` + // Render SVG - "## SVG \n" + - "\n" + - mermaidSvg + - "\n" + `${mdEmbeddedType === "svg" ? "## SVG \n" + "\n" + mermaidSvg + "\n" : ""}` ); }; // generate the updated markdown file -export const generateUpdatedMd = async (filePath: string) => { +export const generateUpdatedMd = async (file: string, mdEmbeddedType: string) => { try { - const file = await fs.promises.readFile(filePath, "utf8"); - const threatdownMatches = file.match(threatdownRegex); if (!threatdownMatches) { throw new Error("No threatdown content found"); } - const newFilePromises = threatdownMatches.map(processMatchAsync); + const newFilePromises = threatdownMatches.map((match) => + processMatchAsync(match, mdEmbeddedType) + ); const newFileContentArray = await Promise.all(newFilePromises); let newFile = file; @@ -94,10 +98,11 @@ export const generateUpdatedMd = async (filePath: string) => { // TODO // remove this, local dev -generateUpdatedMd("./test.md") +const fileContent = readFileSync(resolve(process.cwd(), "./test.md"), { + encoding: "utf8", +}); +generateUpdatedMd(fileContent, "mermaid") .then((res) => { - console.log(res); - fs.writeFile("test-uodate.md", res, (err) => { if (err) throw err; console.log("The file has been saved!"); From 9099bb236797774727e6ea674ed866e04061d60c Mon Sep 17 00:00:00 2001 From: simon busch Date: Tue, 24 Oct 2023 12:34:29 +0200 Subject: [PATCH 05/15] [UPD] clean up --- lib/md-converter.ts | 46 --------------------------------------------- 1 file changed, 46 deletions(-) diff --git a/lib/md-converter.ts b/lib/md-converter.ts index 1121654..c4f19a3 100644 --- a/lib/md-converter.ts +++ b/lib/md-converter.ts @@ -1,11 +1,6 @@ import { parse } from "./parser/index"; import { compileToMermaid } from "./compiler"; import { renderMermaid } from "./renderer"; -import fs from "fs"; - -// TODO/ remove this later: -import { readFileSync } from "node:fs"; -import { resolve } from "node:path"; const threatdownRegex = /```threatdown([\s\S]*?)```/g; const getMermaid = async (mermaidFormatedData: string): Promise => { @@ -13,31 +8,6 @@ const getMermaid = async (mermaidFormatedData: string): Promise => { return testSvg; }; -// This function will actually crete the svg and json files and write to system -const getThreatdownContent = (trimmedContent: string) => { - const jsonFormatedData = parse(trimmedContent); - writeFileType(JSON.stringify(jsonFormatedData), "json"); - - const mermaidFormatedData = compileToMermaid(jsonFormatedData); - - // returns the svg - getMermaid(mermaidFormatedData) - .then((res: string) => { - writeFileType(res, "svg"); - }) - .catch((err) => { - console.log(err); - }); -}; - -// This function write the svg and json files -const writeFileType = (svg: string, type: string) => { - fs.writeFile(`threatdown-${Date.now()}.${type}`, svg, (err) => { - if (err) throw err; - console.log("The file has been saved!"); - }); -}; - // Define an asynchronous function to process each match const processMatchAsync = async (match: string, mdEmbeddedType: string) => { const cleanMatch = match @@ -95,19 +65,3 @@ export const generateUpdatedMd = async (file: string, mdEmbeddedType: string) => throw err; } }; - -// TODO -// remove this, local dev -const fileContent = readFileSync(resolve(process.cwd(), "./test.md"), { - encoding: "utf8", -}); -generateUpdatedMd(fileContent, "mermaid") - .then((res) => { - fs.writeFile("test-uodate.md", res, (err) => { - if (err) throw err; - console.log("The file has been saved!"); - }); - }) - .catch((err) => { - console.error(err); - }); From b694d40b4a175391588e339e0997349ae405657d Mon Sep 17 00:00:00 2001 From: simon busch Date: Tue, 24 Oct 2023 12:43:56 +0200 Subject: [PATCH 06/15] remove json as potential option for md updating as it doesn't really make sense --- bin/threatdown.ts | 4 ++-- lib/md-converter.ts | 9 --------- 2 files changed, 2 insertions(+), 11 deletions(-) diff --git a/bin/threatdown.ts b/bin/threatdown.ts index b70922b..b0ecd07 100755 --- a/bin/threatdown.ts +++ b/bin/threatdown.ts @@ -37,7 +37,7 @@ function usage () { --output Write result to file --type Change output type, must be one of "json", "mermaid", "svg" or "md" - --mdEmbeddedType Choose the mdEmbeddedType you want in the updated Markdown must be one of "json", "mermaid", "svg" + --mdEmbeddedType Choose the mdEmbeddedType you want in the updated Markdown must be one of "mermaid", "svg" `); } @@ -81,7 +81,7 @@ async function main () { //!! untested yet if (values.type === "md") { - if (!values.mdEmbeddedType || !["json", "mermaid", "svg"].includes(values.mdEmbeddedType)) { + if (!values.mdEmbeddedType || !["mermaid", "svg"].includes(values.mdEmbeddedType)) { usage(); } else { const updatedMarkdown = await generateUpdatedMd(fileContent, values.mdEmbeddedType); diff --git a/lib/md-converter.ts b/lib/md-converter.ts index c4f19a3..041e21f 100644 --- a/lib/md-converter.ts +++ b/lib/md-converter.ts @@ -20,15 +20,6 @@ const processMatchAsync = async (match: string, mdEmbeddedType: string) => { return ( ` \n` + - // Render JSON - `${ - mdEmbeddedType === "json" - ? "## JSON \n" + - "```json\n" + - JSON.stringify(jsonFormatedData) + - "\n```\n" - : "" - }` + // Render Mermaid `${ mdEmbeddedType === "mermaid" From 8f3f1dffde37237cdfbd225058f9d33d88e314ce Mon Sep 17 00:00:00 2001 From: simon busch Date: Tue, 24 Oct 2023 13:19:59 +0200 Subject: [PATCH 07/15] [UPD] var & function names --- lib/md-converter.ts | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/lib/md-converter.ts b/lib/md-converter.ts index 041e21f..863ea22 100644 --- a/lib/md-converter.ts +++ b/lib/md-converter.ts @@ -3,7 +3,7 @@ import { compileToMermaid } from "./compiler"; import { renderMermaid } from "./renderer"; const threatdownRegex = /```threatdown([\s\S]*?)```/g; -const getMermaid = async (mermaidFormatedData: string): Promise => { +const getMermaidSvg = async (mermaidFormatedData: string): Promise => { const testSvg = await renderMermaid(mermaidFormatedData); return testSvg; }; @@ -15,15 +15,16 @@ const processMatchAsync = async (match: string, mdEmbeddedType: string) => { .replace(/^```threatdown/, "") .replace(/```$/, ""); const jsonFormatedData = parse(cleanMatch); - const mermaidData = compileToMermaid(jsonFormatedData); - const mermaidSvg = await getMermaid(mermaidData); + const mermaidRaw = compileToMermaid(jsonFormatedData); + const mermaidSvg = await getMermaidSvg(mermaidRaw); return ( ` \n` + + // Render Mermaid `${ mdEmbeddedType === "mermaid" - ? "## MERMAID \n" + "```mermaid\n" + mermaidData + "\n```\n" + ? "## MERMAID \n" + "```mermaid\n" + mermaidRaw + "\n```\n" : "" }` + // Render SVG From bbc393d7f6e53c0a7292ac63a92afb8722feacc4 Mon Sep 17 00:00:00 2001 From: simon busch Date: Tue, 24 Oct 2023 13:31:10 +0200 Subject: [PATCH 08/15] [UPD] logic so we don't render svg if we don't use it --- lib/md-converter.ts | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/lib/md-converter.ts b/lib/md-converter.ts index 863ea22..5c8d06a 100644 --- a/lib/md-converter.ts +++ b/lib/md-converter.ts @@ -16,7 +16,10 @@ const processMatchAsync = async (match: string, mdEmbeddedType: string) => { .replace(/```$/, ""); const jsonFormatedData = parse(cleanMatch); const mermaidRaw = compileToMermaid(jsonFormatedData); - const mermaidSvg = await getMermaidSvg(mermaidRaw); + let mermaidSvg = ""; + if (mdEmbeddedType === "svg") { + mermaidSvg = await getMermaidSvg(mermaidRaw); + } return ( ` \n` + From 8f91f0b5f690551466fbc0bba97f325dc3c68cdc Mon Sep 17 00:00:00 2001 From: simon busch Date: Tue, 24 Oct 2023 13:35:55 +0200 Subject: [PATCH 09/15] [UPD] clean up --- bin/threatdown.ts | 2 ++ lib/md-converter.ts | 4 ++-- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/bin/threatdown.ts b/bin/threatdown.ts index b0ecd07..4f9af0e 100755 --- a/bin/threatdown.ts +++ b/bin/threatdown.ts @@ -77,6 +77,7 @@ async function main () { } else { console.log(svgContent); } + return; } //!! untested yet @@ -91,6 +92,7 @@ async function main () { console.log(updatedMarkdown); } } + return; } } } diff --git a/lib/md-converter.ts b/lib/md-converter.ts index 5c8d06a..b19e0a5 100644 --- a/lib/md-converter.ts +++ b/lib/md-converter.ts @@ -27,11 +27,11 @@ const processMatchAsync = async (match: string, mdEmbeddedType: string) => { // Render Mermaid `${ mdEmbeddedType === "mermaid" - ? "## MERMAID \n" + "```mermaid\n" + mermaidRaw + "\n```\n" + ? "```mermaid\n" + mermaidRaw + "\n```\n" : "" }` + // Render SVG - `${mdEmbeddedType === "svg" ? "## SVG \n" + "\n" + mermaidSvg + "\n" : ""}` + `${mdEmbeddedType === "svg" ? "\n" + mermaidSvg + "\n" : ""}` ); }; From 80875a7e02a8b09dae9709c9ed8e6506d58b0b00 Mon Sep 17 00:00:00 2001 From: simon busch Date: Tue, 24 Oct 2023 14:22:09 +0200 Subject: [PATCH 10/15] [UPD] cli call --- bin/threatdown.ts | 59 ++++++++++++++++++++++++++--------------------- 1 file changed, 33 insertions(+), 26 deletions(-) diff --git a/bin/threatdown.ts b/bin/threatdown.ts index 4f9af0e..38d41c4 100755 --- a/bin/threatdown.ts +++ b/bin/threatdown.ts @@ -10,10 +10,7 @@ import { generateUpdatedMd, } from "../lib"; -const { - values, - positionals, -} = parseArgs({ +const { values, positionals } = parseArgs({ allowPositionals: true, options: { output: { @@ -28,11 +25,11 @@ const { mdEmbeddedType: { type: "string", default: "mermaid", - } + }, }, }); -function usage () { +function usage() { console.log(`Usage: threatdown --output Write result to file @@ -41,19 +38,44 @@ function usage () { `); } -async function main () { - +async function main() { const inputFile = positionals.shift(); // non-null assertion safe because the outputType has a default // eslint-disable-next-line @typescript-eslint/no-non-null-assertion - if (!inputFile || !["json", "mermaid", "svg", "md"].includes(values.type!)) { // TODO add md as option here + if (!inputFile || !["json", "mermaid", "svg", "md"].includes(values.type!)) { usage(); } else { - const fileContent = readFileSync(resolve(process.cwd(), inputFile), { encoding: "utf8" }); + const fileContent = readFileSync(resolve(process.cwd(), inputFile), { + encoding: "utf8", + }); + + if (values.type === "md") { + if ( + !values.mdEmbeddedType || + !["mermaid", "svg"].includes(values.mdEmbeddedType) + ) { + usage(); + } else { + const updatedMarkdown = await generateUpdatedMd( + fileContent, + values.mdEmbeddedType + ); + if (values.output) { + writeFileSync(resolve(process.cwd(), values.output), updatedMarkdown); + } else { + console.log(updatedMarkdown); + } + } + return; + } + const parsedContent = parse(fileContent); if (values.type === "json") { if (values.output) { - writeFileSync(resolve(process.cwd(), values.output), JSON.stringify(parsedContent, null, 2)); + writeFileSync( + resolve(process.cwd(), values.output), + JSON.stringify(parsedContent, null, 2) + ); } else { console.log(JSON.stringify(parsedContent, null, 2)); } @@ -79,21 +101,6 @@ async function main () { } return; } - - //!! untested yet - if (values.type === "md") { - if (!values.mdEmbeddedType || !["mermaid", "svg"].includes(values.mdEmbeddedType)) { - usage(); - } else { - const updatedMarkdown = await generateUpdatedMd(fileContent, values.mdEmbeddedType); - if (values.output) { - writeFileSync(resolve(process.cwd(), values.output), updatedMarkdown); - } else { - console.log(updatedMarkdown); - } - } - return; - } } } From 46b3486465f368a476fe1f0465e04b05c9e73218 Mon Sep 17 00:00:00 2001 From: simon busch Date: Tue, 24 Oct 2023 17:01:28 +0200 Subject: [PATCH 11/15] [UPD] clean up and improvement --- bin/threatdown.ts | 89 +++++++++------------------------------------ lib/md-converter.ts | 12 +++++- 2 files changed, 28 insertions(+), 73 deletions(-) diff --git a/bin/threatdown.ts b/bin/threatdown.ts index 38d41c4..6c94c1d 100755 --- a/bin/threatdown.ts +++ b/bin/threatdown.ts @@ -2,13 +2,7 @@ import { readFileSync, writeFileSync } from "node:fs"; import { resolve } from "node:path"; import { parseArgs } from "node:util"; - -import { - parse, - compileToMermaid, - renderMermaid, - generateUpdatedMd, -} from "../lib"; +import { generateUpdatedMd } from "../lib"; const { values, positionals } = parseArgs({ allowPositionals: true, @@ -22,10 +16,6 @@ const { values, positionals } = parseArgs({ short: "t", default: "mermaid", }, - mdEmbeddedType: { - type: "string", - default: "mermaid", - }, }, }); @@ -33,8 +23,7 @@ function usage() { console.log(`Usage: threatdown --output Write result to file - --type Change output type, must be one of "json", "mermaid", "svg" or "md" - --mdEmbeddedType Choose the mdEmbeddedType you want in the updated Markdown must be one of "mermaid", "svg" + --type Change output type, must be one of "json", "mermaid", "svg" `); } @@ -42,70 +31,28 @@ async function main() { const inputFile = positionals.shift(); // non-null assertion safe because the outputType has a default // eslint-disable-next-line @typescript-eslint/no-non-null-assertion - if (!inputFile || !["json", "mermaid", "svg", "md"].includes(values.type!)) { + if ( + !inputFile || + !values.type || + !["json", "mermaid", "svg"].includes(values.type) + ) { usage(); } else { const fileContent = readFileSync(resolve(process.cwd(), inputFile), { encoding: "utf8", }); - - if (values.type === "md") { - if ( - !values.mdEmbeddedType || - !["mermaid", "svg"].includes(values.mdEmbeddedType) - ) { - usage(); - } else { - const updatedMarkdown = await generateUpdatedMd( - fileContent, - values.mdEmbeddedType - ); - if (values.output) { - writeFileSync(resolve(process.cwd(), values.output), updatedMarkdown); - } else { - console.log(updatedMarkdown); - } - } - return; - } - - const parsedContent = parse(fileContent); - if (values.type === "json") { - if (values.output) { - writeFileSync( - resolve(process.cwd(), values.output), - JSON.stringify(parsedContent, null, 2) - ); - } else { - console.log(JSON.stringify(parsedContent, null, 2)); - } - return; - } - - const mermaidContent = compileToMermaid(parsedContent); - if (values.type === "mermaid") { - if (values.output) { - writeFileSync(resolve(process.cwd(), values.output), mermaidContent); - } else { - console.log(mermaidContent); - } - return; - } - - const svgContent = await renderMermaid(mermaidContent); - if (values.type === "svg") { - if (values.output) { - writeFileSync(resolve(process.cwd(), values.output), svgContent); - } else { - console.log(svgContent); - } - return; + const updatedMarkdown = await generateUpdatedMd(fileContent, values.type); + if (values.output) { + const safeOutput = values.output.includes(".md") ? values.output : `${values.output}.md`; + writeFileSync(resolve(process.cwd(), safeOutput), updatedMarkdown); + } else { + console.log(updatedMarkdown); } + return; } } -main() - .catch((err: Error) => { - process.exitCode = 1; - console.error(err.stack); - }); +main().catch((err: Error) => { + process.exitCode = 1; + console.error(err.stack); +}); diff --git a/lib/md-converter.ts b/lib/md-converter.ts index b19e0a5..a6161ef 100644 --- a/lib/md-converter.ts +++ b/lib/md-converter.ts @@ -23,7 +23,12 @@ const processMatchAsync = async (match: string, mdEmbeddedType: string) => { return ( ` \n` + - + // Render Mermaid + `${ + mdEmbeddedType === "json" + ? "```json\n" + JSON.stringify(jsonFormatedData) + "\n```\n" + : "" + }` + // Render Mermaid `${ mdEmbeddedType === "mermaid" @@ -36,7 +41,10 @@ const processMatchAsync = async (match: string, mdEmbeddedType: string) => { }; // generate the updated markdown file -export const generateUpdatedMd = async (file: string, mdEmbeddedType: string) => { +export const generateUpdatedMd = async ( + file: string, + mdEmbeddedType: string +) => { try { const threatdownMatches = file.match(threatdownRegex); From 1273fd56efc47bf2798a8bd89d25a254259dc21a Mon Sep 17 00:00:00 2001 From: simon busch Date: Tue, 24 Oct 2023 17:27:25 +0200 Subject: [PATCH 12/15] [UPD] split function --- lib/md-converter.ts | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/lib/md-converter.ts b/lib/md-converter.ts index a6161ef..a07ccc2 100644 --- a/lib/md-converter.ts +++ b/lib/md-converter.ts @@ -3,17 +3,25 @@ import { compileToMermaid } from "./compiler"; import { renderMermaid } from "./renderer"; const threatdownRegex = /```threatdown([\s\S]*?)```/g; + const getMermaidSvg = async (mermaidFormatedData: string): Promise => { const testSvg = await renderMermaid(mermaidFormatedData); return testSvg; }; -// Define an asynchronous function to process each match -const processMatchAsync = async (match: string, mdEmbeddedType: string) => { - const cleanMatch = match +const cleanThreatdownBlocks = (input: string) => { + return input .trim() .replace(/^```threatdown/, "") .replace(/```$/, ""); +}; + +// Define an asynchronous function to process each match +const processMatchAsync = async ( + match: string, + mdEmbeddedType: string +): Promise => { + const cleanMatch = cleanThreatdownBlocks(match); const jsonFormatedData = parse(cleanMatch); const mermaidRaw = compileToMermaid(jsonFormatedData); let mermaidSvg = ""; @@ -44,7 +52,7 @@ const processMatchAsync = async (match: string, mdEmbeddedType: string) => { export const generateUpdatedMd = async ( file: string, mdEmbeddedType: string -) => { +): Promise => { try { const threatdownMatches = file.match(threatdownRegex); From 64d230ba0c5168b86eb1cdc9d9c6e1a00413f570 Mon Sep 17 00:00:00 2001 From: simon busch Date: Tue, 24 Oct 2023 17:55:41 +0200 Subject: [PATCH 13/15] [UPD] wording --- bin/threatdown.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bin/threatdown.ts b/bin/threatdown.ts index 6c94c1d..dfdf08f 100755 --- a/bin/threatdown.ts +++ b/bin/threatdown.ts @@ -23,7 +23,7 @@ function usage() { console.log(`Usage: threatdown --output Write result to file - --type Change output type, must be one of "json", "mermaid", "svg" + --type Change output type, must be one of "json", "mermaid" or "svg" `); } From 0f17d05cbc683f12c5f9ff7ac34f9ba82cff6e55 Mon Sep 17 00:00:00 2001 From: simon busch Date: Thu, 26 Oct 2023 12:45:13 +0200 Subject: [PATCH 14/15] clarify that inputfile must be a markdown --- bin/threatdown.ts | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/bin/threatdown.ts b/bin/threatdown.ts index dfdf08f..20ff2a7 100755 --- a/bin/threatdown.ts +++ b/bin/threatdown.ts @@ -20,7 +20,7 @@ const { values, positionals } = parseArgs({ }); function usage() { - console.log(`Usage: threatdown + console.log(`Usage: threatdown ( must be a markdown file ) --output Write result to file --type Change output type, must be one of "json", "mermaid" or "svg" @@ -34,7 +34,8 @@ async function main() { if ( !inputFile || !values.type || - !["json", "mermaid", "svg"].includes(values.type) + !["json", "mermaid", "svg"].includes(values.type) || + !inputFile.includes(".md") ) { usage(); } else { From 950db85fcda39c7b06d97b401d1b7909de3a57bb Mon Sep 17 00:00:00 2001 From: nlf Date: Thu, 26 Oct 2023 09:22:08 -0700 Subject: [PATCH 15/15] let bin accept both markdown and threatdown files --- bin/threatdown.ts | 79 +++++++++++++++++++++++++++++++++-------- test/fixtures/sample.md | 18 ++++++++++ 2 files changed, 82 insertions(+), 15 deletions(-) create mode 100644 test/fixtures/sample.md diff --git a/bin/threatdown.ts b/bin/threatdown.ts index 20ff2a7..e95e85b 100755 --- a/bin/threatdown.ts +++ b/bin/threatdown.ts @@ -1,8 +1,14 @@ #!/usr/bin/env node import { readFileSync, writeFileSync } from "node:fs"; -import { resolve } from "node:path"; +import { extname, resolve } from "node:path"; import { parseArgs } from "node:util"; -import { generateUpdatedMd } from "../lib"; + +import { + parse, + compileToMermaid, + generateUpdatedMd, + renderMermaid, +} from "../lib"; const { values, positionals } = parseArgs({ allowPositionals: true, @@ -20,35 +26,78 @@ const { values, positionals } = parseArgs({ }); function usage() { - console.log(`Usage: threatdown ( must be a markdown file ) + console.log(`Usage: threatdown + Must have extension \`.td\` or \`.md\` --output Write result to file --type Change output type, must be one of "json", "mermaid" or "svg" `); } async function main() { - const inputFile = positionals.shift(); + if (positionals.length !== 1) { + return usage(); + } + + // non-null assertion safe because we already checked for length + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion + const inputFile = positionals.shift()!; + const inputFileExt = extname(inputFile); + // non-null assertion safe because the outputType has a default // eslint-disable-next-line @typescript-eslint/no-non-null-assertion if ( - !inputFile || !values.type || !["json", "mermaid", "svg"].includes(values.type) || - !inputFile.includes(".md") + ![".md", ".td"].includes(inputFileExt) ) { - usage(); - } else { - const fileContent = readFileSync(resolve(process.cwd(), inputFile), { - encoding: "utf8", - }); - const updatedMarkdown = await generateUpdatedMd(fileContent, values.type); + process.exitCode = 1; + return usage(); + } + + const fileContent = readFileSync(resolve(process.cwd(), inputFile), { + encoding: "utf8", + }); + + if (inputFileExt === ".md") { + const markdownContent = await generateUpdatedMd(fileContent, values.type); if (values.output) { - const safeOutput = values.output.includes(".md") ? values.output : `${values.output}.md`; - writeFileSync(resolve(process.cwd(), safeOutput), updatedMarkdown); + writeFileSync(resolve(process.cwd(), values.output), markdownContent); } else { - console.log(updatedMarkdown); + console.log(markdownContent); } + } else if (inputFileExt === ".td") { + const parsedContent = parse(fileContent); + if (values.type === "json") { + if (values.output) { + writeFileSync(resolve(process.cwd(), values.output), JSON.stringify(parsedContent, null, 2)); + } else { + console.log(JSON.stringify(parsedContent, null, 2)); + } + + return; + } + + const mermaidContent = compileToMermaid(parsedContent); + if (values.type === "mermaid") { + if (values.output) { + writeFileSync(resolve(process.cwd(), values.output), mermaidContent); + } else { + console.log(mermaidContent); + } + + return; + } + + const svgContent = await renderMermaid(mermaidContent); + if (values.type === "svg") { + if (values.output) { + writeFileSync(resolve(process.cwd(), values.output), svgContent); + } else { + console.log(svgContent); + } + } + return; } } diff --git a/test/fixtures/sample.md b/test/fixtures/sample.md new file mode 100644 index 0000000..6065f6e --- /dev/null +++ b/test/fixtures/sample.md @@ -0,0 +1,18 @@ +## This is a markdown document + +```threatdown +__Attacker's goal__ + - method which in order to be viable + + (high) requires this condition to be true + + and this condition which depends on either + - x to be true + - or y to be true + + hey this condition must be true too + - another method here too + - a condition which depends on assumptions + +? this might be a problem + +? but only if this happens + -? which assumes this also happens +``` + +### Above this is a graph