astro-d2 0.8.1 → 0.9.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/CHANGELOG.md +22 -0
- package/config.ts +27 -0
- package/index.ts +2 -2
- package/libs/attributes.ts +10 -3
- package/libs/d2.ts +111 -12
- package/libs/remark.ts +1 -1
- package/package.json +2 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,27 @@
|
|
|
1
1
|
# astro-d2
|
|
2
2
|
|
|
3
|
+
## 0.9.0
|
|
4
|
+
|
|
5
|
+
### Minor Changes
|
|
6
|
+
|
|
7
|
+
- [#49](https://github.com/HiDeoo/astro-d2/pull/49) [`68bfe04`](https://github.com/HiDeoo/astro-d2/commit/68bfe04b6098c748beb4ffc24c595419917d5a4c) Thanks [@HiDeoo](https://github.com/HiDeoo)! - Adds new `inline` attribute to override the global `inline` configuration for a specific diagram.
|
|
8
|
+
|
|
9
|
+
- [#49](https://github.com/HiDeoo/astro-d2/pull/49) [`68bfe04`](https://github.com/HiDeoo/astro-d2/commit/68bfe04b6098c748beb4ffc24c595419917d5a4c) Thanks [@HiDeoo](https://github.com/HiDeoo)! - Adds support for customizing the semibold font in diagrams.
|
|
10
|
+
|
|
11
|
+
- [#49](https://github.com/HiDeoo/astro-d2/pull/49) [`68bfe04`](https://github.com/HiDeoo/astro-d2/commit/68bfe04b6098c748beb4ffc24c595419917d5a4c) Thanks [@HiDeoo](https://github.com/HiDeoo)! - Adds experimental support for using [D2.js](https://www.npmjs.com/package/@terrastruct/d2) to render diagrams.
|
|
12
|
+
|
|
13
|
+
By default, the integration requires the D2 binary to be installed on the system to generate diagrams. Enabling this option allows generating diagrams using D2.js, a JavaScript wrapper around D2 to run it through WebAssembly.
|
|
14
|
+
|
|
15
|
+
To enable this feature, add the experimental flag in your Astro D2 integration configuration:
|
|
16
|
+
|
|
17
|
+
```js
|
|
18
|
+
astroD2({
|
|
19
|
+
experimental: {
|
|
20
|
+
useD2js: true,
|
|
21
|
+
},
|
|
22
|
+
})
|
|
23
|
+
```
|
|
24
|
+
|
|
3
25
|
## 0.8.1
|
|
4
26
|
|
|
5
27
|
### Patch Changes
|
package/config.ts
CHANGED
|
@@ -9,6 +9,25 @@ export const AstroD2ConfigSchema = z
|
|
|
9
9
|
* @see https://d2lang.com/tour/interactive/
|
|
10
10
|
*/
|
|
11
11
|
appendix: z.boolean().default(false),
|
|
12
|
+
/**
|
|
13
|
+
* Available experimental flags.
|
|
14
|
+
*
|
|
15
|
+
* Note that experimental flags are not guaranteed to be stable and may change or be removed in any future release.
|
|
16
|
+
*/
|
|
17
|
+
experimental: z
|
|
18
|
+
.object({
|
|
19
|
+
/**
|
|
20
|
+
* Whether to use D2.js to generate the diagrams instead of the D2 binary.
|
|
21
|
+
*
|
|
22
|
+
* By default, the integration requires the D2 binary to be installed on the system to generate diagrams.
|
|
23
|
+
* Enabling this option allows generating diagrams using D2.js, a JavaScript wrapper around D2 to run it through
|
|
24
|
+
* WebAssembly.
|
|
25
|
+
*
|
|
26
|
+
* @default false
|
|
27
|
+
*/
|
|
28
|
+
useD2js: z.boolean().default(false),
|
|
29
|
+
})
|
|
30
|
+
.default({}),
|
|
12
31
|
/**
|
|
13
32
|
* Defines the fonts to use for the generated diagrams.
|
|
14
33
|
*
|
|
@@ -28,6 +47,10 @@ export const AstroD2ConfigSchema = z
|
|
|
28
47
|
* The relative path from the project's root to the .ttf font file to use for the bold font.
|
|
29
48
|
*/
|
|
30
49
|
bold: z.string().optional(),
|
|
50
|
+
/**
|
|
51
|
+
* The relative path from the project's root to the .ttf font file to use for the semibold font.
|
|
52
|
+
*/
|
|
53
|
+
semibold: z.string().optional(),
|
|
31
54
|
})
|
|
32
55
|
.optional(),
|
|
33
56
|
/**
|
|
@@ -96,6 +119,10 @@ export const AstroD2ConfigSchema = z
|
|
|
96
119
|
})
|
|
97
120
|
.default({}),
|
|
98
121
|
})
|
|
122
|
+
.refine((config) => config.layout !== 'tala' || !config.experimental.useD2js, {
|
|
123
|
+
// TODO(HiDeoo) test
|
|
124
|
+
message: 'The `tala` layout engine is not supported when using the `experimental.useD2js` option.',
|
|
125
|
+
})
|
|
99
126
|
.default({})
|
|
100
127
|
|
|
101
128
|
export type AstroD2UserConfig = z.input<typeof AstroD2ConfigSchema>
|
package/index.ts
CHANGED
|
@@ -5,7 +5,7 @@ import type { AstroIntegration } from 'astro'
|
|
|
5
5
|
|
|
6
6
|
import { AstroD2ConfigSchema, type AstroD2UserConfig } from './config'
|
|
7
7
|
import { clearContentLayerCache } from './libs/astro'
|
|
8
|
-
import {
|
|
8
|
+
import { isD2BinaryInstalled } from './libs/d2'
|
|
9
9
|
import { throwErrorWithHint } from './libs/integration'
|
|
10
10
|
import { remarkAstroD2 } from './libs/remark'
|
|
11
11
|
|
|
@@ -33,7 +33,7 @@ export default function astroD2Integration(userConfig?: AstroD2UserConfig): Astr
|
|
|
33
33
|
if (config.skipGeneration) {
|
|
34
34
|
logger.warn("Skipping generation of D2 diagrams as the 'skipGeneration' option is enabled.")
|
|
35
35
|
} else {
|
|
36
|
-
if (!(await
|
|
36
|
+
if (!config.experimental.useD2js && !(await isD2BinaryInstalled())) {
|
|
37
37
|
throwErrorWithHint(
|
|
38
38
|
'Could not find D2. Please check the installation instructions at https://github.com/terrastruct/d2/blob/master/docs/INSTALL.md',
|
|
39
39
|
)
|
package/libs/attributes.ts
CHANGED
|
@@ -15,9 +15,9 @@ export const AttributesSchema = z
|
|
|
15
15
|
.optional()
|
|
16
16
|
.transform((value) => value === 'true'),
|
|
17
17
|
/**
|
|
18
|
-
* The dark theme to use for the
|
|
18
|
+
* The dark theme to use for the diagram when the user's system preference is set to dark mode.
|
|
19
19
|
*
|
|
20
|
-
* To disable the dark theme
|
|
20
|
+
* To disable the dark theme, set this attribute to `'false'`.
|
|
21
21
|
*
|
|
22
22
|
* @see https://d2lang.com/tour/themes
|
|
23
23
|
*/
|
|
@@ -25,6 +25,13 @@ export const AttributesSchema = z
|
|
|
25
25
|
.string()
|
|
26
26
|
.optional()
|
|
27
27
|
.transform((value) => (value === 'false' ? false : value)),
|
|
28
|
+
/**
|
|
29
|
+
* Overrides the global `inline` configuration for the diagram.
|
|
30
|
+
*/
|
|
31
|
+
inline: z
|
|
32
|
+
.union([z.literal('true'), z.literal('false')])
|
|
33
|
+
.optional()
|
|
34
|
+
.transform((value) => (value === undefined ? undefined : value === 'true')),
|
|
28
35
|
/**
|
|
29
36
|
* Overrides the global `layout` configuration for the diagram.
|
|
30
37
|
*/
|
|
@@ -57,7 +64,7 @@ export const AttributesSchema = z
|
|
|
57
64
|
*/
|
|
58
65
|
title: z.string().default('Diagram'),
|
|
59
66
|
/**
|
|
60
|
-
* The default theme to use for the
|
|
67
|
+
* The default theme to use for the diagram.
|
|
61
68
|
*
|
|
62
69
|
* @see https://d2lang.com/tour/themes
|
|
63
70
|
*/
|
package/libs/d2.ts
CHANGED
|
@@ -2,15 +2,20 @@ import fs from 'node:fs/promises'
|
|
|
2
2
|
import path from 'node:path'
|
|
3
3
|
import url from 'node:url'
|
|
4
4
|
|
|
5
|
+
import { D2, type CompileRequest } from '@terrastruct/d2'
|
|
6
|
+
|
|
5
7
|
import type { DiagramAttributes } from './attributes'
|
|
6
8
|
import { exec } from './exec'
|
|
7
9
|
import type { RemarkAstroD2Config } from './remark'
|
|
8
10
|
|
|
9
11
|
const viewBoxRegex = /viewBox="\d+ \d+ (?<width>\d+) (?<height>\d+)"/
|
|
10
12
|
|
|
11
|
-
|
|
13
|
+
// When using D2.js, we cache the loaded fonts to avoid reading them for each diagram.
|
|
14
|
+
const jsFonts: Partial<Record<D2Font, Uint8Array>> = {}
|
|
15
|
+
|
|
16
|
+
export async function isD2BinaryInstalled() {
|
|
12
17
|
try {
|
|
13
|
-
await
|
|
18
|
+
await getD2BinaryVersion()
|
|
14
19
|
|
|
15
20
|
return true
|
|
16
21
|
} catch {
|
|
@@ -24,6 +29,17 @@ export async function generateD2Diagram(
|
|
|
24
29
|
input: string,
|
|
25
30
|
outputPath: string,
|
|
26
31
|
cwd: string,
|
|
32
|
+
) {
|
|
33
|
+
const generateFn = config.experimental.useD2js ? generateD2jsDiagram : generateD2BinaryDiagram
|
|
34
|
+
return generateFn(config, attributes, input, outputPath, cwd)
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
async function generateD2BinaryDiagram(
|
|
38
|
+
config: RemarkAstroD2Config,
|
|
39
|
+
attributes: DiagramAttributes,
|
|
40
|
+
input: string,
|
|
41
|
+
outputPath: string,
|
|
42
|
+
cwd: string,
|
|
27
43
|
) {
|
|
28
44
|
const extraArgs = []
|
|
29
45
|
|
|
@@ -58,6 +74,12 @@ export async function generateD2Diagram(
|
|
|
58
74
|
extraArgs.push(`--font-bold=${path.relative(cwd, path.join(url.fileURLToPath(config.root), config.fonts.bold))}`)
|
|
59
75
|
}
|
|
60
76
|
|
|
77
|
+
if (config.fonts?.semibold) {
|
|
78
|
+
extraArgs.push(
|
|
79
|
+
`--font-semibold=${path.relative(cwd, path.join(url.fileURLToPath(config.root), config.fonts.semibold))}`,
|
|
80
|
+
)
|
|
81
|
+
}
|
|
82
|
+
|
|
61
83
|
if ((config.appendix && attributes.appendix !== false) || attributes.appendix === true) {
|
|
62
84
|
extraArgs.push(`--force-appendix`)
|
|
63
85
|
}
|
|
@@ -85,26 +107,90 @@ export async function generateD2Diagram(
|
|
|
85
107
|
return await getD2Diagram(outputPath)
|
|
86
108
|
}
|
|
87
109
|
|
|
88
|
-
|
|
110
|
+
async function generateD2jsDiagram(
|
|
111
|
+
config: RemarkAstroD2Config,
|
|
112
|
+
attributes: DiagramAttributes,
|
|
113
|
+
input: string,
|
|
114
|
+
outputPath: string,
|
|
115
|
+
) {
|
|
89
116
|
try {
|
|
90
|
-
const
|
|
91
|
-
|
|
92
|
-
|
|
117
|
+
const request: CompileRequest = {
|
|
118
|
+
fs: { [outputPath]: input },
|
|
119
|
+
inputPath: outputPath,
|
|
120
|
+
options: {
|
|
121
|
+
// @ts-expect-error - We enforce that the layout cannot be 'tala' when using D2.js when validating the config.
|
|
122
|
+
layout: attributes.layout ?? config.layout,
|
|
123
|
+
pad: attributes.pad ?? config.pad,
|
|
124
|
+
sketch: (attributes.sketch === 'true' ? true : attributes.sketch) ?? config.sketch,
|
|
125
|
+
themeID: Number.parseInt(attributes.theme ?? config.theme.default, 10),
|
|
126
|
+
},
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
const darkTheme = attributes.darkTheme ?? config.theme.dark
|
|
130
|
+
if (darkTheme !== false) request.options.darkThemeID = Number.parseInt(darkTheme, 10)
|
|
131
|
+
|
|
132
|
+
if (attributes.animateInterval) request.options.animateInterval = Number.parseInt(attributes.animateInterval, 10)
|
|
93
133
|
|
|
94
|
-
if (
|
|
95
|
-
|
|
134
|
+
if (attributes.target !== undefined) request.options.target = attributes.target
|
|
135
|
+
else if (attributes.animateInterval) request.options.target = '*'
|
|
136
|
+
|
|
137
|
+
if (config.fonts?.regular) {
|
|
138
|
+
request.options.fontRegular = await getD2jsFont('regular', config.root, config.fonts.regular)
|
|
96
139
|
}
|
|
97
140
|
|
|
98
|
-
|
|
99
|
-
|
|
141
|
+
if (config.fonts?.italic) {
|
|
142
|
+
request.options.fontItalic = await getD2jsFont('italic', config.root, config.fonts.italic)
|
|
143
|
+
}
|
|
100
144
|
|
|
101
|
-
|
|
145
|
+
if (config.fonts?.bold) {
|
|
146
|
+
request.options.fontBold = await getD2jsFont('bold', config.root, config.fonts.bold)
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
if (config.fonts?.semibold) {
|
|
150
|
+
request.options.fontSemibold = await getD2jsFont('semibold', config.root, config.fonts.semibold)
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
if ((config.appendix && attributes.appendix !== false) || attributes.appendix === true) {
|
|
154
|
+
request.options.forceAppendix = true
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
const d2 = new D2()
|
|
158
|
+
const response = await d2.compile(request)
|
|
159
|
+
const content = await d2.render(response.diagram, response.renderOptions)
|
|
160
|
+
|
|
161
|
+
await fs.mkdir(path.dirname(outputPath), { recursive: true })
|
|
162
|
+
await fs.writeFile(outputPath, content)
|
|
163
|
+
|
|
164
|
+
return getD2DiagramFromContent(content)
|
|
165
|
+
} catch (error) {
|
|
166
|
+
throw new Error('Failed to generate D2 diagram using D2.js.', { cause: error })
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
export async function getD2Diagram(diagramPath: string): Promise<D2Diagram | undefined> {
|
|
171
|
+
try {
|
|
172
|
+
const content = await fs.readFile(diagramPath, 'utf8')
|
|
173
|
+
return getD2DiagramFromContent(content)
|
|
102
174
|
} catch (error) {
|
|
103
175
|
throw new Error(`Failed to get D2 diagram size at '${diagramPath}'.`, { cause: error })
|
|
104
176
|
}
|
|
105
177
|
}
|
|
106
178
|
|
|
107
|
-
|
|
179
|
+
function getD2DiagramFromContent(content: string): D2Diagram | undefined {
|
|
180
|
+
const match = viewBoxRegex.exec(content)
|
|
181
|
+
const { height, width } = match?.groups ?? {}
|
|
182
|
+
|
|
183
|
+
if (!height || !width) {
|
|
184
|
+
return
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
const computedHeight = Number.parseInt(height, 10)
|
|
188
|
+
const computedWidth = Number.parseInt(width, 10)
|
|
189
|
+
|
|
190
|
+
return { content, size: { height: computedHeight, width: computedWidth } }
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
async function getD2BinaryVersion() {
|
|
108
194
|
try {
|
|
109
195
|
const [version] = await exec('d2', ['--version'])
|
|
110
196
|
|
|
@@ -118,6 +204,17 @@ async function getD2Version() {
|
|
|
118
204
|
}
|
|
119
205
|
}
|
|
120
206
|
|
|
207
|
+
async function getD2jsFont(font: D2Font, root: URL, fontPath: string): Promise<Uint8Array> {
|
|
208
|
+
if (jsFonts[font]) return jsFonts[font]
|
|
209
|
+
|
|
210
|
+
const buffer = await fs.readFile(path.join(url.fileURLToPath(root), fontPath))
|
|
211
|
+
|
|
212
|
+
// Necessary when crossing the JS/WASM boundary.
|
|
213
|
+
jsFonts[font] = [...buffer] as unknown as Uint8Array
|
|
214
|
+
|
|
215
|
+
return jsFonts[font]
|
|
216
|
+
}
|
|
217
|
+
|
|
121
218
|
export interface D2Diagram {
|
|
122
219
|
content: string
|
|
123
220
|
size: D2Size
|
|
@@ -129,3 +226,5 @@ export type D2Size =
|
|
|
129
226
|
width: number
|
|
130
227
|
}
|
|
131
228
|
| undefined
|
|
229
|
+
|
|
230
|
+
type D2Font = 'regular' | 'italic' | 'bold' | 'semibold'
|
package/libs/remark.ts
CHANGED
|
@@ -59,7 +59,7 @@ export function remarkAstroD2(config: RemarkAstroD2Config) {
|
|
|
59
59
|
parent.children.splice(
|
|
60
60
|
index,
|
|
61
61
|
1,
|
|
62
|
-
config.inline
|
|
62
|
+
(attributes.inline ?? config.inline)
|
|
63
63
|
? makeHtmlSvgNode(attributes, diagram)
|
|
64
64
|
: makeHtmlImgNode(attributes, outputPath.imgPath, diagram?.size),
|
|
65
65
|
)
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "astro-d2",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.9.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,6 +10,7 @@
|
|
|
10
10
|
"./package.json": "./package.json"
|
|
11
11
|
},
|
|
12
12
|
"dependencies": {
|
|
13
|
+
"@terrastruct/d2": "^0.1.33",
|
|
13
14
|
"hast-util-from-html": "^2.0.3",
|
|
14
15
|
"hast-util-to-html": "^9.0.4",
|
|
15
16
|
"unist-util-visit": "^5.0.0"
|