diff --git a/.github/workflows/deploy-example.yml b/.github/workflows/deploy-example.yml index b37df92..7102a3d 100644 --- a/.github/workflows/deploy-example.yml +++ b/.github/workflows/deploy-example.yml @@ -49,11 +49,6 @@ jobs: - name: Install dependencies run: npm ci - - name: Setup d2-diagrams directory - run: | - sudo mkdir /d2-diagrams - sudo chown -R $(whoami) /d2-diagrams - - name: Build with VitePress env: BASE_PATH: "/vitepress-plugin-d2/" diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml index 2acd2e3..25ca0c5 100644 --- a/.github/workflows/publish.yml +++ b/.github/workflows/publish.yml @@ -13,6 +13,7 @@ jobs: node-version: '20.x' registry-url: 'https://registry.npmjs.org' - run: npm ci + - run: npm run build - run: npm publish env: NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }} diff --git a/.gitignore b/.gitignore index 47c2b3d..dded91e 100644 --- a/.gitignore +++ b/.gitignore @@ -128,3 +128,5 @@ out .yarn/build-state.yml .yarn/install-state.gz .pnp.* + +dist \ No newline at end of file diff --git a/README.md b/README.md index d8dbb56..bd5be89 100644 --- a/README.md +++ b/README.md @@ -24,12 +24,6 @@ Once you have installed D2, you can check it is working by running the following d2 --version ``` -Depending on where you are running the build process (such as GitHub codespaces) you may need to grand permissions to the `d2-diagram` directory using something like the below command. - -```bash -sudo chown codespace:codespace /d2-diagrams/ -``` - ## Installation First, install the plugin from NPM. diff --git a/dist/config.d.ts b/dist/config.d.ts deleted file mode 100644 index 4be0acb..0000000 --- a/dist/config.d.ts +++ /dev/null @@ -1,104 +0,0 @@ -/** - * Enum defining D2 layouts. - */ -export declare enum Layout { - /** Dagre is D2's default layout engine */ - DAGRE = "DAGRE", - /** ELK is a mature, hierarchical layout, actively maintained by an academic research group at Christian Albrechts University in Kiel */ - ELK = "ELK", - /** Tala is a proprietary layout engine developed by Terrastruct, designed specifically for software architecture diagrams */ - TALA = "TALA" -} -/** - * Enum defining D2 themes. - */ -export declare enum Theme { - /** Neutral default */ - NEUTRAL_DEFAULT = 0, - /** Neutral Grey */ - NEUTRAL_GREY = 1, - /** Flagship Terrastruct */ - FLAGSHIP_TERRASTRUCT = 3, - /** Cool classics */ - COOL_CLASSICS = 4, - /** Mixed berry blue */ - MIXED_BERRY_BLUE = 5, - /** Grape soda */ - GRAPE_SODA = 6, - /** Aubergine */ - AUBERGINE = 7, - /** Colorblind clear */ - COLORBLIND_CLEAR = 8, - /** Vanilla nitro cola */ - VANILLA_NITRO_COLA = 100, - /** Orange creamsicle */ - ORANGE_CREAMSICLE = 101, - /** Shirley temple */ - SHIRLEY_TEMPLE = 102, - /** Earth tones */ - EARTH_TONES = 103, - /** Everglade green */ - EVERGLADE_GREEN = 104, - /** Buttered toast */ - BUTTERED_TOAST = 105, - /** Terminal */ - TERMINAL = 300, - /** Terminal Grayscale */ - TERMINAL_GRAYSCALE = 301, - /** Origami */ - ORIGAMI = 302, - /** Dark Mauve */ - DARK_MUAVE = 200, - /** Dark Flagship Terrastruct */ - DARK_FLAGSHIP_TERRASTRUCT = 201 -} -/** - * Enum defining D2 export file types. - */ -export declare enum FileType { - /** SVG is the default export format on the CLI. If you don't specify an output, the export file will be the input name as an SVG file. */ - SVG = "SVG", - /** PNG exports work by Playwright spinning up a headless browser, putting the SVG onto it, and taking a screenshot. The first invocation of Playwright will download its dependencies, if they don't already exist on the machine. */ - PNG = "PNG", - /** GIF export format is useful for giving presentations when used with short compositions. For example, show two Scenarios, show a couple of steps. Something that the audience can digest in a loop that lasts a couple of seconds without needing to flip through it manually. */ - GIF = "GIF" -} -/** - * Interface defining D2 configuration. - */ -export interface Config { - /** An appendix for tooltips and links is added to PNG exports since they are not interactive. Force appendix adds an appendix to SVG exports as well (default false) */ - forceAppendix?: boolean | undefined; - /** The layout engine used (default "dagre") */ - layout?: Layout | undefined; - /** The diagram theme ID (default NEUTRAL_DEFAULT) */ - theme?: Theme | undefined; - /** The theme to use when the viewer's browser is in dark mode. When left unset theme config is used for both light and dark mode. Be aware that explicit styles set in D2 code will still be applied and this may produce unexpected results. (default null) */ - darkTheme?: Theme | undefined; - /** Pixels padded around the rendered diagram (default 100) */ - padding?: number | undefined; - /** If given, multiple boards are packaged as 1 SVG which transitions through each board at the interval (in milliseconds). Can only be used with SVG exports. (default 0) */ - animateInterval?: number | undefined; - /** The maximum number of seconds that D2 runs for before timing out and exiting. When rendering a large diagram, it is recommended to increase this value (default 120) */ - timeout?: number | undefined; - /** Render the diagram to look like it was sketched by hand (default false) */ - sketch?: boolean | undefined; - /** Center the SVG in the containing viewbox, such as your browser screen (default false) */ - center?: boolean | undefined; - /** Scale the output. E.g., 0.5 to halve the default size. Default -1 means that SVG's will fit to screen and all others will use their default render size. Setting to 1 turns off SVG fitting to screen. (default -1) */ - scale?: number | undefined; - /** Target board to render. Pass an empty string to target root board. If target ends with '*', it will be rendered with all of its scenarios, steps, and layers. Otherwise, only the target board will be rendered. E.g. '' to render root board only or 'layers.x.*' to render layer 'x' with all of its children. (default "*") */ - target?: string | undefined; - /** Path to .ttf file to use for the regular font. If none provided, Source Sans Pro Regular is used. (default null) */ - fontRegular?: string | undefined; - /** Path to .ttf file to use for the italic font. If none provided, Source Sans Pro Regular-Italic is used. (default null) */ - fontItalic?: string | undefined; - /** Path to .ttf file to use for the bold font. If none provided, Source Sans Pro Bold is used. (default null) */ - fontBold?: string | undefined; - /** Path to .ttf file to use for the semibold font. If none provided, Source Sans Pro Semibold is used. (default null) */ - fontSemiBold?: string | undefined; - /** File type to export diagram images as, either SVG, PNG or GIF. (default SVG) */ - fileType?: FileType | undefined; - /** Directory to export temporary diagram files and images to. (default d2-diagrams) */ - directory?: string | undefined; -} diff --git a/dist/config.js b/dist/config.js deleted file mode 100644 index c55dc33..0000000 --- a/dist/config.js +++ /dev/null @@ -1,68 +0,0 @@ -/** - * Enum defining D2 layouts. - */ -export var Layout; -(function (Layout) { - /** Dagre is D2's default layout engine */ - Layout["DAGRE"] = "DAGRE"; - /** ELK is a mature, hierarchical layout, actively maintained by an academic research group at Christian Albrechts University in Kiel */ - Layout["ELK"] = "ELK"; - /** Tala is a proprietary layout engine developed by Terrastruct, designed specifically for software architecture diagrams */ - Layout["TALA"] = "TALA"; -})(Layout || (Layout = {})); -/** - * Enum defining D2 themes. - */ -export var Theme; -(function (Theme) { - /** Neutral default */ - Theme[Theme["NEUTRAL_DEFAULT"] = 0] = "NEUTRAL_DEFAULT"; - /** Neutral Grey */ - Theme[Theme["NEUTRAL_GREY"] = 1] = "NEUTRAL_GREY"; - /** Flagship Terrastruct */ - Theme[Theme["FLAGSHIP_TERRASTRUCT"] = 3] = "FLAGSHIP_TERRASTRUCT"; - /** Cool classics */ - Theme[Theme["COOL_CLASSICS"] = 4] = "COOL_CLASSICS"; - /** Mixed berry blue */ - Theme[Theme["MIXED_BERRY_BLUE"] = 5] = "MIXED_BERRY_BLUE"; - /** Grape soda */ - Theme[Theme["GRAPE_SODA"] = 6] = "GRAPE_SODA"; - /** Aubergine */ - Theme[Theme["AUBERGINE"] = 7] = "AUBERGINE"; - /** Colorblind clear */ - Theme[Theme["COLORBLIND_CLEAR"] = 8] = "COLORBLIND_CLEAR"; - /** Vanilla nitro cola */ - Theme[Theme["VANILLA_NITRO_COLA"] = 100] = "VANILLA_NITRO_COLA"; - /** Orange creamsicle */ - Theme[Theme["ORANGE_CREAMSICLE"] = 101] = "ORANGE_CREAMSICLE"; - /** Shirley temple */ - Theme[Theme["SHIRLEY_TEMPLE"] = 102] = "SHIRLEY_TEMPLE"; - /** Earth tones */ - Theme[Theme["EARTH_TONES"] = 103] = "EARTH_TONES"; - /** Everglade green */ - Theme[Theme["EVERGLADE_GREEN"] = 104] = "EVERGLADE_GREEN"; - /** Buttered toast */ - Theme[Theme["BUTTERED_TOAST"] = 105] = "BUTTERED_TOAST"; - /** Terminal */ - Theme[Theme["TERMINAL"] = 300] = "TERMINAL"; - /** Terminal Grayscale */ - Theme[Theme["TERMINAL_GRAYSCALE"] = 301] = "TERMINAL_GRAYSCALE"; - /** Origami */ - Theme[Theme["ORIGAMI"] = 302] = "ORIGAMI"; - /** Dark Mauve */ - Theme[Theme["DARK_MUAVE"] = 200] = "DARK_MUAVE"; - /** Dark Flagship Terrastruct */ - Theme[Theme["DARK_FLAGSHIP_TERRASTRUCT"] = 201] = "DARK_FLAGSHIP_TERRASTRUCT"; -})(Theme || (Theme = {})); -/** - * Enum defining D2 export file types. - */ -export var FileType; -(function (FileType) { - /** SVG is the default export format on the CLI. If you don't specify an output, the export file will be the input name as an SVG file. */ - FileType["SVG"] = "SVG"; - /** PNG exports work by Playwright spinning up a headless browser, putting the SVG onto it, and taking a screenshot. The first invocation of Playwright will download its dependencies, if they don't already exist on the machine. */ - FileType["PNG"] = "PNG"; - /** GIF export format is useful for giving presentations when used with short compositions. For example, show two Scenarios, show a couple of steps. Something that the audience can digest in a loop that lasts a couple of seconds without needing to flip through it manually. */ - FileType["GIF"] = "GIF"; -})(FileType || (FileType = {})); diff --git a/dist/index.d.ts b/dist/index.d.ts deleted file mode 100644 index 6ddaa25..0000000 --- a/dist/index.d.ts +++ /dev/null @@ -1,7 +0,0 @@ -import { Config } from "./config.js"; -/** - * D2 plugin to convert markdown D2 code blocks to images. - * @param md Markdown. - * @param defaultConfig Default D2 plugin config. - */ -export default function d2(md: any, defaultConfig?: Config): void; diff --git a/dist/index.js b/dist/index.js deleted file mode 100644 index 9020119..0000000 --- a/dist/index.js +++ /dev/null @@ -1,197 +0,0 @@ -import { spawnSync } from "child_process"; -import { writeFileSync, unlinkSync, existsSync, mkdirSync, readFileSync } from "fs"; -import path from "path"; -import { Layout, FileType, Theme } from "./config.js"; -/** - * Function to parse diagram config located in code block. - * @param code Diagram D2 code. - * @returns Configuration and code (config removed). - */ -function parseAndConvertConfig(code) { - // Define the regex for parsing the config - const configRegex = /:::config([\s\S]+?):::/g; - // Temporary storage for the string config - const userConfig = {}; - // Parse the config from the code block - const matches = code.matchAll(configRegex); - for (const match of matches) { - if (match[1]) { - const configLines = match[1].trim().split("\n"); - for (const line of configLines) { - const [key, value] = line.split(":").map((s) => s.trim()); - if (key && value !== undefined) { - userConfig[key] = value; - } - } - } - } - // Clean the diagram code by removing the config - code = code.replace(configRegex, "").trim(); - // Convert string config into actual config values - const config = {}; - for (const [key, stringValue] of Object.entries(userConfig)) { - switch (key) { - case "forceAppendix": - config.forceAppendix = stringValue === "true"; - break; - case "layout": - config.layout = Layout[stringValue]; - break; - case "theme": - config.theme = Theme[stringValue]; - break; - case "darkTheme": - config.darkTheme = Theme[stringValue]; - break; - case "padding": - config.padding = parseInt(stringValue, 10); - break; - case "animateInterval": - config.animateInterval = parseInt(stringValue, 10); - break; - case "timeout": - config.timeout = parseInt(stringValue, 10); - break; - case "sketch": - config.sketch = stringValue === "true"; - break; - case "center": - config.center = stringValue === "true"; - break; - case "scale": - config.scale = parseFloat(stringValue); - break; - case "target": - config.target = stringValue; - break; - case "fontRegular": - config.fontRegular = stringValue; - break; - case "fontItalic": - config.fontItalic = stringValue; - break; - case "fontBold": - config.fontBold = stringValue; - break; - case "fontSemiBold": - config.fontSemiBold = stringValue; - break; - case "fileType": - config.fileType = FileType[stringValue]; - break; - case "directory": - config.directory = stringValue; - break; - } - } - return { config, code }; -} -/** - * D2 plugin to convert markdown D2 code blocks to images. - * @param md Markdown. - * @param defaultConfig Default D2 plugin config. - */ -export default function d2(md, defaultConfig = {}) { - // Store original fence to return if no D2 diagram rendered - const originalFence = md.renderer.rules.fence.bind(md.renderer.rules); - md.renderer.rules.fence = (tokens, idx, options, env, slf) => { - const token = tokens[idx]; - const tokenInfo = token.info.toLowerCase(); - // If code fence is for D2 diagram - if (tokenInfo === "d2" || tokenInfo === "d2lang") { - const { config: diagramConfig, code: code } = parseAndConvertConfig(token.content); - // Merge default config with diagram config - const config = { ...defaultConfig, ...diagramConfig }; - // Create output directory if not exist - const outputDir = `/${config.directory ?? "d2-diagrams"}`; - if (!existsSync(outputDir)) { - mkdirSync(outputDir, { recursive: true }); - } - // Get filetype - const fileType = FileType[config.fileType ?? FileType.SVG]; - // Generate unique filename for diagram image output file - const imageFilePath = path.join(outputDir, `d2-diagram-${Date.now()}.${fileType.toLowerCase()}`); - // Write the D2 diagram code to a temporary .d2 file - const tempD2FilePath = path.join(outputDir, "temp.d2"); - writeFileSync(tempD2FilePath, code); - // Construct command line arguments from config - const args = [tempD2FilePath, imageFilePath]; - if (config.forceAppendix === true) { - args.push("--force-appendix"); - } - if (config.layout != null) { - args.push(`--layout=${Layout[config.layout].toLowerCase()}`); - } - if (config.theme != null) { - args.push(`--theme=${config.theme}`); - } - if (config.darkTheme != null) { - args.push(`--dark-theme=${config.darkTheme}`); - } - if (config.padding != null) { - args.push(`--pad=${config.padding}`); - } - if (config.animateInterval != null) { - args.push(`--animate-interval=${config.animateInterval}`); - } - if (config.timeout != null) { - args.push(`--timeout=${config.timeout}`); - } - if (config.sketch === true) { - args.push("--sketch"); - } - if (config.center === true) { - args.push("--center"); - } - if (config.scale != null) { - args.push(`--scale=${config.scale}`); - } - if (config.target != null) { - args.push(`--target=${config.target}`); - } - if (config.fontRegular != null) { - args.push(`--font-regular=${config.fontRegular}`); - } - if (config.fontItalic != null) { - args.push(`--font-italic=${config.fontItalic}`); - } - if (config.fontBold != null) { - args.push(`--font-bold=${config.fontBold}`); - } - if (config.fontSemiBold != null) { - args.push(`--font-semibold=${config.fontSemiBold}`); - } - // Run D2 command to generate output diagram image file - const command = spawnSync("d2", args, { encoding: "utf-8", stdio: "pipe" }); - // Log any error with D2 command - if (command.status !== 0) { - console.error(`Error: Failed to generate D2 diagram.\n${command.stderr}`); - } - // Get diagram image file content as a base64-encoded string - const imageContent = readFileSync(imageFilePath, { encoding: "base64" }); - // Get media type from file type - let mediaType; - switch (fileType) { - case FileType.SVG: - mediaType = "image/svg+xml"; - break; - case FileType.PNG: - mediaType = "image/png"; - break; - case FileType.GIF: - mediaType = "image/gif"; - break; - } - // Create data URI for diagram image in base64 format - const dataUri = `data:${mediaType};base64,${imageContent}`; - // Delete the image file as no longer required - unlinkSync(imageFilePath); - // Delete temporary D2 file - unlinkSync(tempD2FilePath); - // Return an image tag with the Data URI as the source - return `D2 Diagram`; - } - // For other languages return the original fence - return originalFence(tokens, idx, options, env, slf); - }; -} diff --git a/package.json b/package.json index 4fe2e93..a82bfeb 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "vitepress-plugin-d2", - "version": "1.0.0", + "version": "1.0.1", "description": "Plugin for VitePress to add support for rendering D2 diagrams.", "main": "dist/index.js", "types": "dist/index.d.ts", diff --git a/src/index.ts b/src/index.ts index e02b516..a671a7a 100644 --- a/src/index.ts +++ b/src/index.ts @@ -115,7 +115,7 @@ export default function d2(md: any, defaultConfig: Config = {}) { const config = { ...defaultConfig, ...diagramConfig }; // Create output directory if not exist - const outputDir = `/${config.directory ?? "d2-diagrams"}`; + const outputDir = `./${config.directory ?? "d2-diagrams"}`; if (!existsSync(outputDir)) { mkdirSync(outputDir, { recursive: true }); }