UNPKG

astro-d2

Version:

Astro integration and remark plugin to transform D2 Markdown code blocks into diagrams.

132 lines (108 loc) 3.37 kB
import fs from 'node:fs/promises' import path from 'node:path' import url from 'node:url' import type { DiagramAttributes } from './attributes' import { exec } from './exec' import type { RemarkAstroD2Config } from './remark' const viewBoxRegex = /viewBox="\d+ \d+ (?<width>\d+) (?<height>\d+)"/ export async function isD2Installed() { try { await getD2Version() return true } catch { return false } } export async function generateD2Diagram( config: RemarkAstroD2Config, attributes: DiagramAttributes, input: string, outputPath: string, cwd: string, ) { const extraArgs = [] if ( (config.theme.dark !== false && attributes.darkTheme !== false) || (attributes.darkTheme !== undefined && attributes.darkTheme !== false) ) { extraArgs.push(`--dark-theme=${attributes.darkTheme ?? config.theme.dark}`) } if (attributes.animateInterval) { extraArgs.push(`--animate-interval=${attributes.animateInterval}`) } if (attributes.target !== undefined) { extraArgs.push(`--target=${attributes.target}`) } if (config.fonts?.regular) { extraArgs.push( `--font-regular=${path.relative(cwd, path.join(url.fileURLToPath(config.root), config.fonts.regular))}`, ) } if (config.fonts?.italic) { extraArgs.push( `--font-italic=${path.relative(cwd, path.join(url.fileURLToPath(config.root), config.fonts.italic))}`, ) } if (config.fonts?.bold) { extraArgs.push(`--font-bold=${path.relative(cwd, path.join(url.fileURLToPath(config.root), config.fonts.bold))}`) } if ((config.appendix && attributes.appendix !== false) || attributes.appendix === true) { extraArgs.push(`--force-appendix`) } try { // The `-` argument is used to read from stdin instead of a file. await exec( 'd2', [ `--layout=${attributes.layout ?? config.layout}`, `--theme=${attributes.theme ?? config.theme.default}`, `--sketch=${attributes.sketch ?? config.sketch}`, `--pad=${attributes.pad ?? config.pad}`, ...extraArgs, '-', outputPath, ], input, cwd, ) } catch (error) { throw new Error('Failed to generate D2 diagram.', { cause: error }) } return await getD2Diagram(outputPath) } export async function getD2Diagram(diagramPath: string): Promise<D2Diagram | undefined> { try { const content = await fs.readFile(diagramPath, 'utf8') const match = viewBoxRegex.exec(content) const { height, width } = match?.groups ?? {} if (!height || !width) { return } const computedHeight = Number.parseInt(height, 10) const computedWidth = Number.parseInt(width, 10) return { content, size: { height: computedHeight, width: computedWidth } } } catch (error) { throw new Error(`Failed to get D2 diagram size at '${diagramPath}'.`, { cause: error }) } } async function getD2Version() { try { const [version] = await exec('d2', ['--version']) if (!version || !/^v?\d+\.\d+\.\d+/.test(version)) { throw new Error(`Invalid D2 version, got '${version}'.`) } return version } catch (error) { throw new Error('Failed to get D2 version.', { cause: error }) } } export interface D2Diagram { content: string size: D2Size } export type D2Size = | { height: number width: number } | undefined