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 +6 -0
- package/libs/d2.ts +9 -4
- package/libs/exec.ts +5 -1
- package/libs/integration.ts +2 -2
- package/libs/remark.ts +55 -8
- package/package.json +4 -1
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
|
|
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
|
|
81
|
+
return await getD2Diagram(outputPath)
|
|
82
82
|
}
|
|
83
83
|
|
|
84
|
-
export async function
|
|
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
|
-
|
|
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
|
})
|
package/libs/integration.ts
CHANGED
|
@@ -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,
|
|
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
|
|
37
|
+
let diagram: D2Diagram | undefined = undefined
|
|
35
38
|
|
|
36
39
|
if (config.skipGeneration) {
|
|
37
|
-
|
|
40
|
+
diagram = await getD2Diagram(outputPath.fsPath)
|
|
38
41
|
} else {
|
|
39
42
|
try {
|
|
40
|
-
|
|
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(
|
|
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
|
|
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.
|
|
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",
|