astro-d2 0.5.1 → 0.6.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/config.ts CHANGED
@@ -23,6 +23,12 @@ export const AstroD2ConfigSchema = z
23
23
  bold: z.string().optional(),
24
24
  })
25
25
  .optional(),
26
+ /**
27
+ * Defines if the SVG diagrams should be inlined in the HTML output.
28
+ *
29
+ * By default, the diagrams are rendered using the `<img>` tag.
30
+ */
31
+ inline: z.boolean().default(false),
26
32
  /**
27
33
  * Defines the layout engine to use to generate the diagrams.
28
34
  *
package/libs/d2.ts CHANGED
@@ -39,7 +39,7 @@ export async function generateD2Diagram(
39
39
  }
40
40
 
41
41
  if (attributes.target !== undefined) {
42
- extraArgs.push(`--target='${attributes.target}'`)
42
+ extraArgs.push(`--target=${attributes.target}`)
43
43
  }
44
44
 
45
45
  if (config.fonts?.regular) {
@@ -78,10 +78,10 @@ export async function generateD2Diagram(
78
78
  throw new Error('Failed to generate D2 diagram.', { cause: error })
79
79
  }
80
80
 
81
- return await getD2DiagramSize(outputPath)
81
+ return await getD2Diagram(outputPath)
82
82
  }
83
83
 
84
- export async function getD2DiagramSize(diagramPath: string): Promise<D2Size> {
84
+ export async function getD2Diagram(diagramPath: string): Promise<D2Diagram | undefined> {
85
85
  try {
86
86
  const content = await fs.readFile(diagramPath, 'utf8')
87
87
  const match = content.match(viewBoxRegex)
@@ -94,7 +94,7 @@ export async function getD2DiagramSize(diagramPath: string): Promise<D2Size> {
94
94
  const computedHeight = Number.parseInt(height, 10)
95
95
  const computedWidth = Number.parseInt(width, 10)
96
96
 
97
- return { height: computedHeight, width: computedWidth }
97
+ return { content, size: { height: computedHeight, width: computedWidth } }
98
98
  } catch (error) {
99
99
  throw new Error(`Failed to get D2 diagram size at '${diagramPath}'.`, { cause: error })
100
100
  }
@@ -114,6 +114,11 @@ async function getD2Version() {
114
114
  }
115
115
  }
116
116
 
117
+ export interface D2Diagram {
118
+ content: string
119
+ size: D2Size
120
+ }
121
+
117
122
  export type D2Size =
118
123
  | {
119
124
  height: number
package/libs/exec.ts CHANGED
@@ -8,7 +8,7 @@ export function exec(command: string, args: string[], stdin?: string, cwd?: stri
8
8
  })
9
9
 
10
10
  const output: string[] = []
11
- const errorMessage = `Unable to run command: '${command} ${args.join(' ')}'.`
11
+ let errorMessage = `Unable to run command: '${command} ${args.join(' ')}'.`
12
12
 
13
13
  child.stdout.on('data', (data: Buffer) => {
14
14
  const lines = data
@@ -19,6 +19,10 @@ export function exec(command: string, args: string[], stdin?: string, cwd?: stri
19
19
  output.push(...lines)
20
20
  })
21
21
 
22
+ child.stderr.on('data', (data: Buffer) => {
23
+ errorMessage += `\n${data.toString()}`
24
+ })
25
+
22
26
  child.on('error', (error) => {
23
27
  reject(new Error(errorMessage, { cause: error }))
24
28
  })
@@ -1,8 +1,8 @@
1
1
  import { AstroError } from 'astro/errors'
2
2
 
3
- export function throwErrorWithHint(message: string): never {
3
+ export function throwErrorWithHint(message: string, cause?: Error): never {
4
4
  throw new AstroError(
5
- message,
5
+ message + (cause ? `\n\n${cause.message}` : ''),
6
6
  `See the error report above for more informations.\n\nIf you believe this is a bug, please file an issue at https://github.com/HiDeoo/astro-d2/issues/new/choose`,
7
7
  )
8
8
  }
package/libs/remark.ts CHANGED
@@ -1,14 +1,17 @@
1
1
  import path from 'node:path'
2
2
  import url from 'node:url'
3
3
 
4
+ import type { Element } from 'hast'
5
+ import { fromHtml } from 'hast-util-from-html'
6
+ import { toHtml } from 'hast-util-to-html'
4
7
  import type { Code, Html, Parent, Root } from 'mdast'
5
- import { SKIP, visit } from 'unist-util-visit'
8
+ import { CONTINUE, EXIT, SKIP, visit } from 'unist-util-visit'
6
9
  import type { VFile } from 'vfile'
7
10
 
8
11
  import type { AstroD2Config } from '../config'
9
12
 
10
13
  import { type DiagramAttributes, getAttributes } from './attributes'
11
- import { generateD2Diagram, type D2Size, getD2DiagramSize } from './d2'
14
+ import { generateD2Diagram, type D2Size, getD2Diagram, type D2Diagram } from './d2'
12
15
  import { throwErrorWithHint } from './integration'
13
16
 
14
17
  export function remarkAstroD2(config: RemarkAstroD2Config) {
@@ -31,35 +34,42 @@ export function remarkAstroD2(config: RemarkAstroD2Config) {
31
34
  d2Nodes.map(async ([node, { index, parent }], d2Index) => {
32
35
  const outputPath = getOutputPaths(config, file, d2Index)
33
36
  const attributes = getAttributes(node.meta)
34
- let size: D2Size = undefined
37
+ let diagram: D2Diagram | undefined = undefined
35
38
 
36
39
  if (config.skipGeneration) {
37
- size = await getD2DiagramSize(outputPath.fsPath)
40
+ diagram = await getD2Diagram(outputPath.fsPath)
38
41
  } else {
39
42
  try {
40
- size = await generateD2Diagram(
43
+ diagram = await generateD2Diagram(
41
44
  config,
42
45
  attributes,
43
46
  node.value,
44
47
  outputPath.fsPath,
45
48
  file.history[0] ? path.dirname(file.history[0]) : file.cwd,
46
49
  )
47
- } catch {
50
+ } catch (error) {
48
51
  throwErrorWithHint(
49
52
  `Failed to generate the D2 diagram at ${node.position?.start.line ?? 0}:${node.position?.start.column ?? 0}.`,
53
+ error instanceof Error ? (error.cause instanceof Error ? error.cause : error) : undefined,
50
54
  )
51
55
  }
52
56
  }
53
57
 
54
58
  if (parent && index !== undefined) {
55
- parent.children.splice(index, 1, makHtmlImgNode(attributes, outputPath.imgPath, size))
59
+ parent.children.splice(
60
+ index,
61
+ 1,
62
+ config.inline
63
+ ? makeHtmlSvgNode(attributes, diagram)
64
+ : makeHtmlImgNode(attributes, outputPath.imgPath, diagram?.size),
65
+ )
56
66
  }
57
67
  }),
58
68
  )
59
69
  }
60
70
  }
61
71
 
62
- function makHtmlImgNode(attributes: DiagramAttributes, imgPath: string, size: D2Size): Html {
72
+ function makeHtmlImgNode(attributes: DiagramAttributes, imgPath: string, size: D2Size): Html {
63
73
  const htmlAttributes: Record<string, string> = {
64
74
  alt: attributes.title,
65
75
  decoding: 'async',
@@ -77,6 +87,34 @@ function makHtmlImgNode(attributes: DiagramAttributes, imgPath: string, size: D2
77
87
  }
78
88
  }
79
89
 
90
+ function makeHtmlSvgNode(attributes: DiagramAttributes, diagram?: D2Diagram): Html {
91
+ if (!diagram) {
92
+ throwErrorWithHint('Failed to retrieve the D2 diagram content for inline rendering.')
93
+ }
94
+
95
+ const tree = fromHtml(diagram.content, { fragment: true })
96
+
97
+ visit(tree, 'element', (node) => {
98
+ if (node.tagName !== 'svg' || !('d2version' in node.properties)) return CONTINUE
99
+
100
+ computeSvgSize(node, attributes, diagram.size)
101
+
102
+ node.children.unshift({
103
+ type: 'element',
104
+ tagName: 'title',
105
+ properties: {},
106
+ children: [{ type: 'text', value: attributes.title }],
107
+ })
108
+
109
+ return EXIT
110
+ })
111
+
112
+ return {
113
+ type: 'html',
114
+ value: toHtml(tree),
115
+ }
116
+ }
117
+
80
118
  function getOutputPaths(config: RemarkAstroD2Config, file: VFile, nodeIndex: number) {
81
119
  const relativePath = path.relative(file.cwd, file.path).replace(/^src[/\\](content|pages)[/\\]/, '')
82
120
  const parsedRelativePath = path.parse(relativePath)
@@ -103,6 +141,15 @@ function computeImgSize(htmlAttributes: Record<string, string>, attributes: Diag
103
141
  }
104
142
  }
105
143
 
144
+ function computeSvgSize(node: Element, attributes: DiagramAttributes, size: D2Size) {
145
+ if (!attributes.width || !size) return
146
+
147
+ const aspectRatio = size.height / size.width
148
+
149
+ node.properties['width'] = String(attributes.width)
150
+ node.properties['height'] = String(Math.round(attributes.width * aspectRatio))
151
+ }
152
+
106
153
  interface VisitorContext {
107
154
  index: number | undefined
108
155
  parent: Parent | undefined
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "astro-d2",
3
- "version": "0.5.1",
3
+ "version": "0.6.0",
4
4
  "license": "MIT",
5
5
  "description": "Astro integration and remark plugin to transform D2 Markdown code blocks into diagrams.",
6
6
  "author": "HiDeoo <github@hideoo.dev> (https://hideoo.dev)",
@@ -10,9 +10,12 @@
10
10
  "./package.json": "./package.json"
11
11
  },
12
12
  "dependencies": {
13
+ "hast-util-from-html": "2.0.3",
14
+ "hast-util-to-html": "9.0.3",
13
15
  "unist-util-visit": "5.0.0"
14
16
  },
15
17
  "devDependencies": {
18
+ "@types/hast": "3.0.4",
16
19
  "@types/mdast": "4.0.3",
17
20
  "remark": "15.0.1",
18
21
  "vfile": "6.0.1",