@barzhsieh/nuxt-content-mermaid 1.0.3 → 2.0.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/README.md +79 -13
- package/README.zh-TW.md +84 -15
- package/dist/module.d.mts +8 -5
- package/dist/module.json +2 -2
- package/dist/module.mjs +25 -17
- package/dist/runtime/components/Mermaid.d.vue.ts +6 -1
- package/dist/runtime/components/Mermaid.vue +113 -31
- package/dist/runtime/components/Mermaid.vue.d.ts +6 -1
- package/dist/runtime/constants.d.ts +2 -2
- package/dist/runtime/constants.js +7 -3
- package/dist/runtime/mermaid-config.d.ts +17 -0
- package/dist/runtime/mermaid-config.js +36 -0
- package/dist/runtime/plugins/mermaid.client.js +21 -30
- package/dist/runtime/utils.d.ts +1 -1
- package/dist/runtime/utils.js +26 -3
- package/dist/types.d.mts +1 -1
- package/package.json +36 -37
package/README.md
CHANGED
|
@@ -22,7 +22,7 @@ It automatically converts `mermaid` code blocks in Markdown into responsive char
|
|
|
22
22
|
|
|
23
23
|
## Requirements
|
|
24
24
|
|
|
25
|
-
-
|
|
25
|
+
- `nuxt@^3.20.1 || ^4.1.0`
|
|
26
26
|
- `@nuxt/content@>=3.5.0`
|
|
27
27
|
|
|
28
28
|
## Quick Setup
|
|
@@ -79,20 +79,20 @@ The module will automatically transform the block into an SVG chart component.
|
|
|
79
79
|
|
|
80
80
|
## Configuration
|
|
81
81
|
|
|
82
|
-
You can configure the module globally through the `
|
|
82
|
+
You can configure the module globally through the `contentMermaid` option.
|
|
83
|
+
(`mermaidContent` is still accepted for backward compatibility, but will be removed in a future release.)
|
|
83
84
|
|
|
84
85
|
```ts
|
|
85
86
|
// nuxt.config.ts
|
|
86
87
|
export default defineNuxtConfig({
|
|
87
|
-
|
|
88
|
+
contentMermaid: {
|
|
88
89
|
enabled: true,
|
|
89
90
|
loader: {
|
|
90
|
-
importSource:
|
|
91
|
-
"https://cdn.jsdelivr.net/npm/mermaid@11/dist/mermaid.esm.min.mjs",
|
|
92
91
|
init: {
|
|
93
92
|
securityLevel: "strict",
|
|
94
93
|
// additional options passed to mermaid.initialize()
|
|
95
94
|
},
|
|
95
|
+
lazy: true,
|
|
96
96
|
},
|
|
97
97
|
theme: {
|
|
98
98
|
useColorModeTheme: true,
|
|
@@ -115,13 +115,14 @@ export default defineNuxtConfig({
|
|
|
115
115
|
| Option | Type | Default | Description |
|
|
116
116
|
| :-------- | :-------- | :------ | :--------------------------------------------------- |
|
|
117
117
|
| `enabled` | `boolean` | `true` | Whether the module and its conversion logic are on. |
|
|
118
|
+
| `debug` | `boolean` | `false` | Enable verbose diagnostics; see Debug section below. |
|
|
118
119
|
|
|
119
120
|
**loader**
|
|
120
121
|
|
|
121
|
-
| Option
|
|
122
|
-
|
|
|
123
|
-
| `loader.
|
|
124
|
-
| `loader.
|
|
122
|
+
| Option | Type | Default | Description |
|
|
123
|
+
| :---------------- | :------------------------------------ | :--------------------- | :------------------------------------------------------------------------------- |
|
|
124
|
+
| `loader.init` | `MermaidConfig` | `{ startOnLoad: false }` | Raw options object passed to `mermaid.initialize`. |
|
|
125
|
+
| `loader.lazy` | `boolean \| { threshold?: number }` | `true` | Lazy load Mermaid when the component enters the viewport; set `false` to preload. |
|
|
125
126
|
|
|
126
127
|
**theme**
|
|
127
128
|
|
|
@@ -139,10 +140,19 @@ export default defineNuxtConfig({
|
|
|
139
140
|
| `components.spinner` | `string` | `undefined` | Optional: global loading spinner component name. |
|
|
140
141
|
| `components.error` | `string` | `undefined` | Optional: global error component name when Mermaid rendering fails. |
|
|
141
142
|
|
|
142
|
-
> **Note**: All options can be overridden at runtime via `runtimeConfig.public.mermaidContent
|
|
143
|
+
> **Note**: All options can be overridden at runtime via `runtimeConfig.public.contentMermaid` (`runtimeConfig.public.mermaidContent` remains supported but deprecated).
|
|
143
144
|
|
|
144
145
|
## Advanced Usage
|
|
145
146
|
|
|
147
|
+
### Debug mode
|
|
148
|
+
|
|
149
|
+
**`contentMermaid.debug`** (default `false`):
|
|
150
|
+
- **Auto-config**: If you did **not** set `loader.init.logLevel` or `suppressErrorRendering`, debug defaults them to `logLevel: 1` and `suppressErrorRendering: false` (Mermaid shows errors in the DOM). If you set them explicitly, your values win.
|
|
151
|
+
- **Runtime behavior**:
|
|
152
|
+
- **Debug on**: `mermaid.run` uses `suppressErrors: false`, errors throw with full stack traces for debugging.
|
|
153
|
+
- **Debug off**: `mermaid.run` uses `suppressErrors: true`, so one failing chart won't block others.
|
|
154
|
+
- **Console output**: Adds queue diagnostics and render-time stats.
|
|
155
|
+
|
|
146
156
|
### Theme & Color Mode
|
|
147
157
|
|
|
148
158
|
The module determines the active Mermaid theme with the following priority:
|
|
@@ -154,6 +164,62 @@ The module determines the active Mermaid theme with the following priority:
|
|
|
154
164
|
- Use `loader.init.theme` if provided.
|
|
155
165
|
- Else fall back to `theme.light`, then `theme.dark`.
|
|
156
166
|
|
|
167
|
+
### Override Per-Page Settings with Frontmatter
|
|
168
|
+
|
|
169
|
+
Each Markdown file can override module settings by adding a `config` field in the frontmatter.
|
|
170
|
+
````markdown
|
|
171
|
+
---
|
|
172
|
+
title: Example of Overriding Mermaid Settings Per Page
|
|
173
|
+
config:
|
|
174
|
+
theme: forest
|
|
175
|
+
flowchart:
|
|
176
|
+
htmlLabels: false
|
|
177
|
+
---
|
|
178
|
+
```mermaid
|
|
179
|
+
flowchart LR
|
|
180
|
+
A["<b>Allow HTML labels?</b>"] --> B{Not allowed}
|
|
181
|
+
```
|
|
182
|
+
````
|
|
183
|
+
|
|
184
|
+
> **Important: If you want to control settings via frontmatter, declare it in the collection schema in `content.config.ts`, for example:**
|
|
185
|
+
>
|
|
186
|
+
> ```ts
|
|
187
|
+
> import { defineContentConfig, defineCollection, z } from '@nuxt/content'
|
|
188
|
+
>
|
|
189
|
+
> export default defineContentConfig({
|
|
190
|
+
> collections: {
|
|
191
|
+
> content: defineCollection({
|
|
192
|
+
> type: 'page',
|
|
193
|
+
> source: '**',
|
|
194
|
+
> schema: z.object({
|
|
195
|
+
> config: z.record(z.unknown()).optional(), // Declare config field
|
|
196
|
+
> }).passthrough(),
|
|
197
|
+
> }),
|
|
198
|
+
> },
|
|
199
|
+
> })
|
|
200
|
+
> ```
|
|
201
|
+
|
|
202
|
+
### Priority Order of `%%{init}%%` Syntax, Frontmatter, and Module Settings
|
|
203
|
+
|
|
204
|
+
Mermaid itself also supports overriding settings within diagrams using the `%%{init: ...}%%` syntax, for example:
|
|
205
|
+
````markdown
|
|
206
|
+
```mermaid
|
|
207
|
+
%%{init: { 'theme': 'forest', 'flowchart': { 'curve': 'step' } }}%%
|
|
208
|
+
graph TD
|
|
209
|
+
A[Input] --> B{Valid?}
|
|
210
|
+
B -- Yes --> C[Persist]
|
|
211
|
+
B -- No --> D[Error]
|
|
212
|
+
```
|
|
213
|
+
````
|
|
214
|
+
|
|
215
|
+
> For details, refer to the [official Mermaid documentation](https://mermaid.js.org/config/directives.html#declaring-directives)
|
|
216
|
+
|
|
217
|
+
The actual priority order when settings take effect is as follows:
|
|
218
|
+
|
|
219
|
+
1. **`%%{init: ...}%%` within the diagram** — Highest priority, processed directly by Mermaid.
|
|
220
|
+
2. **Frontmatter `config`** — merged on top of the module's `loader.init`.
|
|
221
|
+
3. **Module-level `contentMermaid.loader.init`** — Project default settings.
|
|
222
|
+
|
|
157
223
|
### Custom Rendering Component
|
|
158
224
|
|
|
159
225
|
If you want full control over rendering (for example to add a border, zoom controls, or other UI), you can provide a custom component via `components.renderer`.
|
|
@@ -161,7 +227,7 @@ If you want full control over rendering (for example to add a border, zoom contr
|
|
|
161
227
|
1. Specify the component name in `nuxt.config.ts`:
|
|
162
228
|
|
|
163
229
|
```ts
|
|
164
|
-
|
|
230
|
+
contentMermaid: {
|
|
165
231
|
components: {
|
|
166
232
|
renderer: "MyCustomMermaid",
|
|
167
233
|
},
|
|
@@ -193,7 +259,7 @@ Minimal example that uses the provided spinner while waiting for your own loadin
|
|
|
193
259
|
```ts
|
|
194
260
|
// nuxt.config.ts
|
|
195
261
|
export default defineNuxtConfig({
|
|
196
|
-
|
|
262
|
+
contentMermaid: {
|
|
197
263
|
components: {
|
|
198
264
|
renderer: 'MyCustomMermaid',
|
|
199
265
|
spinner: 'MySpinner', // optional: globally replace the default spinner
|
|
@@ -288,7 +354,7 @@ To reuse a global error view everywhere, register it once and reference it in co
|
|
|
288
354
|
```ts
|
|
289
355
|
// nuxt.config.ts
|
|
290
356
|
export default defineNuxtConfig({
|
|
291
|
-
|
|
357
|
+
contentMermaid: {
|
|
292
358
|
components: {
|
|
293
359
|
error: 'MermaidError', // globally registered component name
|
|
294
360
|
},
|
package/README.zh-TW.md
CHANGED
|
@@ -10,7 +10,7 @@
|
|
|
10
10
|
[](https://content.nuxt.com/)
|
|
11
11
|
|
|
12
12
|
專為 [**Nuxt Content v3**](https://content.nuxt.com/docs/getting-started) 整合 [**Mermaid**](https://mermaid.js.org/) 的模組。
|
|
13
|
-
能自動將 Markdown 中的
|
|
13
|
+
能自動將 Markdown 中的 \`\`\`mermaid 區塊轉換為響應式的圖表元件,並支援 Lazy Loading 與深色模式切換。
|
|
14
14
|
|
|
15
15
|
## Features
|
|
16
16
|
|
|
@@ -22,7 +22,7 @@
|
|
|
22
22
|
|
|
23
23
|
## 使用前提
|
|
24
24
|
|
|
25
|
-
-
|
|
25
|
+
- `nuxt@^3.20.1 || ^4.1.0`
|
|
26
26
|
- `@nuxt/content@>=3.5.0`
|
|
27
27
|
|
|
28
28
|
## Quick Setup
|
|
@@ -35,7 +35,7 @@
|
|
|
35
35
|
npx nuxi module add @barzhsieh/nuxt-content-mermaid
|
|
36
36
|
```
|
|
37
37
|
|
|
38
|
-
> Nuxt CLI 對第三方模組預設會裝到 `dependencies
|
|
38
|
+
> Nuxt CLI 對第三方模組預設會裝到 `dependencies`。若你偏好放在 `devDependencies`,請安裝完後移動或改用下方的方式手動安裝。
|
|
39
39
|
|
|
40
40
|
**裝到 `devDependencies`(需手動加入 `modules`):**
|
|
41
41
|
|
|
@@ -79,20 +79,20 @@ graph LR
|
|
|
79
79
|
|
|
80
80
|
## Configuration
|
|
81
81
|
|
|
82
|
-
你可以透過 `
|
|
82
|
+
你可以透過 `contentMermaid` 選項進行全域設定。
|
|
83
|
+
(`mermaidContent` 仍會被接受以維持相容,但已棄用,未來版本會移除。)
|
|
83
84
|
|
|
84
85
|
```ts
|
|
85
86
|
// nuxt.config.ts
|
|
86
87
|
export default defineNuxtConfig({
|
|
87
|
-
|
|
88
|
+
contentMermaid: {
|
|
88
89
|
enabled: true,
|
|
89
90
|
loader: {
|
|
90
|
-
importSource:
|
|
91
|
-
"https://cdn.jsdelivr.net/npm/mermaid@11/dist/mermaid.esm.min.mjs",
|
|
92
91
|
init: {
|
|
93
92
|
securityLevel: "strict",
|
|
94
93
|
// 其他傳遞給 mermaid.initialize() 的參數
|
|
95
94
|
},
|
|
95
|
+
lazy: true,
|
|
96
96
|
},
|
|
97
97
|
theme: {
|
|
98
98
|
useColorModeTheme: true,
|
|
@@ -115,13 +115,14 @@ export default defineNuxtConfig({
|
|
|
115
115
|
| 參數 | 類型 | 預設值 | 說明 |
|
|
116
116
|
| :-------- | :-------- | :----- | :----------------------------- |
|
|
117
117
|
| `enabled` | `boolean` | `true` | 是否啟用模組與轉換邏輯。 |
|
|
118
|
+
| `debug` | `boolean` | `false` | 啟用除錯模式;見下方 Debug 說明。 |
|
|
118
119
|
|
|
119
120
|
**loader**
|
|
120
121
|
|
|
121
|
-
| 參數
|
|
122
|
-
|
|
|
123
|
-
| `loader.
|
|
124
|
-
| `loader.
|
|
122
|
+
| 參數 | 類型 | 預設值 | 說明 |
|
|
123
|
+
| :--------------- | :------------------------------------- | :----------------------- | :---------------------------------------------------------------------- |
|
|
124
|
+
| `loader.init` | `MermaidConfig` | `{ startOnLoad: false }` | 直接傳遞給 `mermaid.initialize` 的原始設定。 |
|
|
125
|
+
| `loader.lazy` | `boolean \| { threshold?: number }` | `true` | 元件進入 viewport 時才載入 Mermaid;設為 `false` 會在前一刻就載入。 |
|
|
125
126
|
|
|
126
127
|
**theme**
|
|
127
128
|
|
|
@@ -139,10 +140,19 @@ export default defineNuxtConfig({
|
|
|
139
140
|
| `components.spinner` | `string` | `undefined` | 指定全域的 Loading 元件名稱。 |
|
|
140
141
|
| `components.error` | `string` | `undefined` | 指定全域的錯誤顯示元件名稱,渲染失敗時會使用。 |
|
|
141
142
|
|
|
142
|
-
> **Note**: 所有設定皆可透過 `runtimeConfig.public.mermaidContent`
|
|
143
|
+
> **Note**: 所有設定皆可透過 `runtimeConfig.public.contentMermaid` 在部署時進行覆寫(`runtimeConfig.public.mermaidContent` 仍支援但已棄用)。
|
|
143
144
|
|
|
144
145
|
## Advanced Usage
|
|
145
146
|
|
|
147
|
+
### Debug 模式
|
|
148
|
+
|
|
149
|
+
**`contentMermaid.debug`**(預設 `false`):
|
|
150
|
+
- **自動配置**:若未手動設定 `loader.init.logLevel` 或 `suppressErrorRendering`,開啟 debug 模式會自動將 `logLevel` 設為 `1` (Info),並將 `suppressErrorRendering` 設為 `false`(允許 Mermaid 在 DOM 中顯示錯誤訊息)。
|
|
151
|
+
- **執行行為**:
|
|
152
|
+
- **Debug 開啟**:`mermaid.run` 使用 `suppressErrors: false`,發生錯誤時會拋出完整堆疊以便除錯。
|
|
153
|
+
- **Debug 關閉**:`mermaid.run` 使用 `suppressErrors: true`,避免單一圖表錯誤中斷其他圖表的渲染。
|
|
154
|
+
- **主控台輸出**:模組會額外輸出渲染佇列的診斷資訊與執行時間統計。
|
|
155
|
+
|
|
146
156
|
### 主題與顏色模式 (Color Mode)
|
|
147
157
|
|
|
148
158
|
模組會依據以下優先順序決定主題:
|
|
@@ -154,6 +164,65 @@ export default defineNuxtConfig({
|
|
|
154
164
|
- 優先使用 `loader.init.theme`。
|
|
155
165
|
- 若未提供,依序回退至 `theme.light`,若沒有設置則會回退至 `theme.dark`。
|
|
156
166
|
|
|
167
|
+
### 以 frontmatter 覆寫單頁設定
|
|
168
|
+
|
|
169
|
+
每篇 Markdown 都可以透過於 frontmatter 中加入 `config` 欄位來覆寫模組設定。
|
|
170
|
+
|
|
171
|
+
````markdown
|
|
172
|
+
---
|
|
173
|
+
title: 單篇覆寫 Mermaid 設定範例
|
|
174
|
+
config:
|
|
175
|
+
theme: forest
|
|
176
|
+
flowchart:
|
|
177
|
+
htmlLabels: false
|
|
178
|
+
---
|
|
179
|
+
|
|
180
|
+
```mermaid
|
|
181
|
+
flowchart LR
|
|
182
|
+
A["<b>允許 HTML labels?</b>"] --> B{不允許}
|
|
183
|
+
```
|
|
184
|
+
````
|
|
185
|
+
|
|
186
|
+
> **重要:如果要使用 frontmatter 控制設定,請在 `content.config.ts` 的 collection schema 中宣告它,例如:**
|
|
187
|
+
>
|
|
188
|
+
> ```ts
|
|
189
|
+
> import { defineContentConfig, defineCollection, z } from '@nuxt/content'
|
|
190
|
+
>
|
|
191
|
+
> export default defineContentConfig({
|
|
192
|
+
> collections: {
|
|
193
|
+
> content: defineCollection({
|
|
194
|
+
> type: 'page',
|
|
195
|
+
> source: '**',
|
|
196
|
+
> schema: z.object({
|
|
197
|
+
> config: z.record(z.unknown()).optional(), // 宣告 config 欄位
|
|
198
|
+
> }).passthrough(),
|
|
199
|
+
> }),
|
|
200
|
+
> },
|
|
201
|
+
> })
|
|
202
|
+
> ```
|
|
203
|
+
|
|
204
|
+
### `%%{init}%%` 語法、frontmatter 與模組設定的優先順序
|
|
205
|
+
|
|
206
|
+
Mermaid 本身也支援在圖表內透過 `%%{init: ...}%%` 語法覆寫設定,例如:
|
|
207
|
+
|
|
208
|
+
````markdown
|
|
209
|
+
```mermaid
|
|
210
|
+
%%{init: { 'theme': 'forest', 'flowchart': { 'curve': 'step' } }}%%
|
|
211
|
+
graph TD
|
|
212
|
+
A[Input] --> B{Valid?}
|
|
213
|
+
B -- Yes --> C[Persist]
|
|
214
|
+
B -- No --> D[Error]
|
|
215
|
+
```
|
|
216
|
+
````
|
|
217
|
+
|
|
218
|
+
> 細節可參考 [Mermaid 官方文件](https://mermaid.js.org/config/directives.html#declaring-directives)
|
|
219
|
+
|
|
220
|
+
實際生效時的優先順序如下:
|
|
221
|
+
|
|
222
|
+
1. **圖表內的 `%%{init: ...}%%`** —— 最優先,直接由 Mermaid 處理。
|
|
223
|
+
2. **frontmatter `config`** —— 深度合併在模組的 `loader.init` 之上。
|
|
224
|
+
3. **模組層級的 `contentMermaid.loader.init`** —— 專案的全域預設。
|
|
225
|
+
|
|
157
226
|
### 自訂渲染元件 (Custom Component)
|
|
158
227
|
|
|
159
228
|
若需完全接管 Mermaid 的渲染行為(例如:加入外框、ZoomIn/Out 功能),可指定 `components.renderer`。
|
|
@@ -161,7 +230,7 @@ export default defineNuxtConfig({
|
|
|
161
230
|
1. 在 `nuxt.config.ts` 中指定元件名稱:
|
|
162
231
|
|
|
163
232
|
```ts
|
|
164
|
-
|
|
233
|
+
contentMermaid: {
|
|
165
234
|
components: {
|
|
166
235
|
renderer: "MyCustomMermaid",
|
|
167
236
|
}
|
|
@@ -193,7 +262,7 @@ export default defineNuxtConfig({
|
|
|
193
262
|
```ts
|
|
194
263
|
// nuxt.config.ts
|
|
195
264
|
export default defineNuxtConfig({
|
|
196
|
-
|
|
265
|
+
contentMermaid: {
|
|
197
266
|
components: {
|
|
198
267
|
renderer: 'MyCustomMermaid',
|
|
199
268
|
spinner: 'MySpinner', // 選填:改用自己的全域 Loading 元件
|
|
@@ -287,7 +356,7 @@ onMounted(() => { loading.value = false })
|
|
|
287
356
|
```ts
|
|
288
357
|
// nuxt.config.ts
|
|
289
358
|
export default defineNuxtConfig({
|
|
290
|
-
|
|
359
|
+
contentMermaid: {
|
|
291
360
|
components: {
|
|
292
361
|
error: 'MermaidError', // 全域註冊的元件名稱
|
|
293
362
|
},
|
package/dist/module.d.mts
CHANGED
|
@@ -7,14 +7,15 @@ interface ModuleOptions {
|
|
|
7
7
|
* @default true
|
|
8
8
|
*/
|
|
9
9
|
enabled?: boolean;
|
|
10
|
+
/**
|
|
11
|
+
* Enable debug mode for detailed logging and error reporting
|
|
12
|
+
* @default false
|
|
13
|
+
*/
|
|
14
|
+
debug?: boolean;
|
|
10
15
|
/**
|
|
11
16
|
* Options related to loading mermaid
|
|
12
17
|
*/
|
|
13
18
|
loader?: {
|
|
14
|
-
/**
|
|
15
|
-
* Mermaid ESM source, can be a CDN URL or local /public path
|
|
16
|
-
*/
|
|
17
|
-
importSource?: string;
|
|
18
19
|
/**
|
|
19
20
|
* Pass-through to `mermaid.initialize`
|
|
20
21
|
*/
|
|
@@ -66,5 +67,7 @@ interface ModuleOptions {
|
|
|
66
67
|
}
|
|
67
68
|
declare const _default: _nuxt_schema.NuxtModule<ModuleOptions, ModuleOptions, false>;
|
|
68
69
|
|
|
69
|
-
|
|
70
|
+
declare function transformMermaidCodeBlocks(body: string, componentName: string, frontmatterConfigKey?: string): string;
|
|
71
|
+
|
|
72
|
+
export { _default as default, transformMermaidCodeBlocks };
|
|
70
73
|
export type { ModuleOptions };
|
package/dist/module.json
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@barzhsieh/nuxt-content-mermaid",
|
|
3
|
-
"configKey": "
|
|
3
|
+
"configKey": "contentMermaid",
|
|
4
4
|
"compatibility": {
|
|
5
5
|
"nuxt": "^3.20.1 || ^4.1.0"
|
|
6
6
|
},
|
|
7
|
-
"version": "
|
|
7
|
+
"version": "2.0.0",
|
|
8
8
|
"builder": {
|
|
9
9
|
"@nuxt/module-builder": "1.0.2",
|
|
10
10
|
"unbuild": "3.6.1"
|
package/dist/module.mjs
CHANGED
|
@@ -1,13 +1,12 @@
|
|
|
1
|
-
import { defineNuxtModule, createResolver, addPlugin, addComponent, addTypeTemplate } from '@nuxt/kit';
|
|
1
|
+
import { defineNuxtModule, useLogger, createResolver, addPlugin, addComponent, addTypeTemplate } from '@nuxt/kit';
|
|
2
2
|
import { defu } from 'defu';
|
|
3
|
-
import {
|
|
3
|
+
import { DEFAULT_MERMAID_CONFIG, DEFAULT_DARK_THEME, DEFAULT_LIGHT_THEME, DEFAULT_FRONTMATTER_CONFIG_KEY } from '../dist/runtime/constants.js';
|
|
4
4
|
|
|
5
5
|
const MERMAID_BLOCK = /```mermaid([\s\S]*?)```/gi;
|
|
6
6
|
const DEFAULTS = {
|
|
7
7
|
enabled: true,
|
|
8
8
|
loader: {
|
|
9
|
-
|
|
10
|
-
init: { ...DEFAULT_MERMAID_INIT },
|
|
9
|
+
init: { ...DEFAULT_MERMAID_CONFIG },
|
|
11
10
|
lazy: true
|
|
12
11
|
},
|
|
13
12
|
theme: {
|
|
@@ -24,7 +23,7 @@ const DEFAULTS = {
|
|
|
24
23
|
const module$1 = defineNuxtModule({
|
|
25
24
|
meta: {
|
|
26
25
|
name: "@barzhsieh/nuxt-content-mermaid",
|
|
27
|
-
configKey: "
|
|
26
|
+
configKey: "contentMermaid",
|
|
28
27
|
compatibility: {
|
|
29
28
|
nuxt: "^3.20.1 || ^4.1.0"
|
|
30
29
|
}
|
|
@@ -33,15 +32,30 @@ const module$1 = defineNuxtModule({
|
|
|
33
32
|
...DEFAULTS
|
|
34
33
|
},
|
|
35
34
|
setup(options, nuxt) {
|
|
36
|
-
const
|
|
35
|
+
const logger = useLogger("nuxt-content-mermaid");
|
|
36
|
+
const warn = (message) => logger.warn(message);
|
|
37
|
+
const deprecatedOptions = nuxt.options.mermaidContent;
|
|
38
|
+
const hasDeprecatedOptions = deprecatedOptions && typeof deprecatedOptions === "object" && Object.keys(deprecatedOptions).length > 0;
|
|
39
|
+
if (hasDeprecatedOptions)
|
|
40
|
+
warn("[nuxt-content-mermaid] `mermaidContent` is deprecated, please switch to `contentMermaid`. The old key is still read for now but will be removed in a future release.");
|
|
41
|
+
const resolvedOptions = defu(
|
|
42
|
+
{},
|
|
43
|
+
options,
|
|
44
|
+
deprecatedOptions,
|
|
45
|
+
DEFAULTS
|
|
46
|
+
);
|
|
37
47
|
const resolver = createResolver(import.meta.url);
|
|
38
48
|
const runtimeDir = resolver.resolve("./runtime");
|
|
39
49
|
const publicRuntimeConfig = nuxt.options.runtimeConfig.public;
|
|
40
|
-
const runtimeOverrides = publicRuntimeConfig.mermaidContent || {};
|
|
50
|
+
const runtimeOverrides = publicRuntimeConfig.contentMermaid || publicRuntimeConfig.mermaidContent || {};
|
|
51
|
+
if (!publicRuntimeConfig.contentMermaid && publicRuntimeConfig.mermaidContent)
|
|
52
|
+
warn("[nuxt-content-mermaid] `runtimeConfig.public.mermaidContent` is deprecated, please use `runtimeConfig.public.contentMermaid` instead.");
|
|
41
53
|
const runtimeMermaidConfig = defu(
|
|
54
|
+
{},
|
|
42
55
|
runtimeOverrides,
|
|
43
56
|
resolvedOptions
|
|
44
57
|
);
|
|
58
|
+
publicRuntimeConfig.contentMermaid = runtimeMermaidConfig;
|
|
45
59
|
publicRuntimeConfig.mermaidContent = runtimeMermaidConfig;
|
|
46
60
|
const baseMermaidComponentName = "Mermaid";
|
|
47
61
|
const isEnabled = runtimeMermaidConfig.enabled !== false;
|
|
@@ -83,19 +97,13 @@ export {}
|
|
|
83
97
|
});
|
|
84
98
|
}
|
|
85
99
|
});
|
|
86
|
-
function
|
|
87
|
-
return text.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">").replace(/"/g, """).replace(/'/g, "'");
|
|
88
|
-
}
|
|
89
|
-
function transformMermaidCodeBlocks(body, componentName) {
|
|
100
|
+
function transformMermaidCodeBlocks(body, componentName, frontmatterConfigKey = DEFAULT_FRONTMATTER_CONFIG_KEY) {
|
|
90
101
|
return body.replace(MERMAID_BLOCK, (_, rawCode = "") => {
|
|
91
102
|
const code = rawCode.trim();
|
|
92
103
|
if (!code) return _;
|
|
93
|
-
const
|
|
94
|
-
return `<${componentName}
|
|
95
|
-
<pre><code>${escaped}
|
|
96
|
-
</code></pre>
|
|
97
|
-
</${componentName}>`;
|
|
104
|
+
const encoded = encodeURIComponent(code);
|
|
105
|
+
return `<${componentName} :config="${frontmatterConfigKey}" code="${encoded}"></${componentName}>`;
|
|
98
106
|
});
|
|
99
107
|
}
|
|
100
108
|
|
|
101
|
-
export { module$1 as default };
|
|
109
|
+
export { module$1 as default, transformMermaidCodeBlocks };
|
|
@@ -1,3 +1,8 @@
|
|
|
1
|
+
import type { MermaidConfig } from 'mermaid';
|
|
2
|
+
type __VLS_Props = {
|
|
3
|
+
config?: MermaidConfig | string | unknown;
|
|
4
|
+
code?: string;
|
|
5
|
+
};
|
|
1
6
|
declare var __VLS_1: {}, __VLS_9: {}, __VLS_11: {}, __VLS_13: {}, __VLS_21: {
|
|
2
7
|
error: unknown;
|
|
3
8
|
source: string;
|
|
@@ -13,7 +18,7 @@ type __VLS_Slots = {} & {
|
|
|
13
18
|
} & {
|
|
14
19
|
error?: (props: typeof __VLS_21) => any;
|
|
15
20
|
};
|
|
16
|
-
declare const __VLS_base: import("vue").DefineComponent<
|
|
21
|
+
declare const __VLS_base: import("vue").DefineComponent<__VLS_Props, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {}, string, import("vue").PublicProps, Readonly<__VLS_Props> & Readonly<{}>, {}, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
|
|
17
22
|
declare const __VLS_export: __VLS_WithSlots<typeof __VLS_base, __VLS_Slots>;
|
|
18
23
|
declare const _default: typeof __VLS_export;
|
|
19
24
|
export default _default;
|
|
@@ -10,15 +10,21 @@ import {
|
|
|
10
10
|
shallowRef,
|
|
11
11
|
useTemplateRef
|
|
12
12
|
} from "vue";
|
|
13
|
+
import { mergeMermaidConfig, resolveMermaidTheme } from "../mermaid-config";
|
|
13
14
|
import { enqueueRender } from "../utils";
|
|
14
15
|
import Spinner from "./Spinner.vue";
|
|
16
|
+
const props = defineProps({
|
|
17
|
+
config: { type: null, required: false },
|
|
18
|
+
code: { type: String, required: false }
|
|
19
|
+
});
|
|
15
20
|
const nuxtApp = useNuxtApp();
|
|
16
21
|
const runtimeConfig = useRuntimeConfig();
|
|
17
|
-
const
|
|
18
|
-
const isEnabled =
|
|
19
|
-
const
|
|
20
|
-
const
|
|
21
|
-
const
|
|
22
|
+
const contentMermaidOptions = runtimeConfig.public?.contentMermaid || runtimeConfig.public?.mermaidContent || {};
|
|
23
|
+
const isEnabled = contentMermaidOptions.enabled !== false;
|
|
24
|
+
const debug = contentMermaidOptions.debug || false;
|
|
25
|
+
const loaderOptions = contentMermaidOptions.loader || {};
|
|
26
|
+
const themeOptions = contentMermaidOptions.theme || {};
|
|
27
|
+
const componentOptions = contentMermaidOptions.components || {};
|
|
22
28
|
const lazyOption = loaderOptions.lazy;
|
|
23
29
|
const isLazy = lazyOption !== false;
|
|
24
30
|
const lazyThreshold = typeof lazyOption === "object" && typeof lazyOption.threshold === "number" ? lazyOption.threshold : 0.1;
|
|
@@ -33,12 +39,40 @@ const hasRenderedOnce = ref(false);
|
|
|
33
39
|
const isLoading = ref(false);
|
|
34
40
|
const hasError = ref(false);
|
|
35
41
|
const errorContent = shallowRef(null);
|
|
36
|
-
|
|
42
|
+
const decodedCode = computed(() => props.code ? decodeURIComponent(props.code) : "");
|
|
43
|
+
const mermaidDefinition = ref(decodedCode.value);
|
|
37
44
|
let observer = null;
|
|
45
|
+
const frontmatterConfig = computed(() => {
|
|
46
|
+
const value = props.config;
|
|
47
|
+
if (!value)
|
|
48
|
+
return void 0;
|
|
49
|
+
if (typeof value !== "object") {
|
|
50
|
+
if (import.meta.dev) {
|
|
51
|
+
console.warn(
|
|
52
|
+
"[nuxt-content-mermaid] Ignoring non-object `config` prop on <Mermaid> component. This usually means your `content.config.ts` collection schema did not declare `config` as a JSON field. See README section about per-page overrides via frontmatter.",
|
|
53
|
+
value
|
|
54
|
+
);
|
|
55
|
+
}
|
|
56
|
+
return void 0;
|
|
57
|
+
}
|
|
58
|
+
return value;
|
|
59
|
+
});
|
|
38
60
|
const mermaidTheme = computed(() => {
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
61
|
+
return resolveMermaidTheme({
|
|
62
|
+
useColorModeTheme,
|
|
63
|
+
colorModeValue: colorMode?.value,
|
|
64
|
+
frontmatterTheme: frontmatterConfig.value?.theme,
|
|
65
|
+
baseTheme: baseMermaidInit.theme,
|
|
66
|
+
lightTheme,
|
|
67
|
+
darkTheme
|
|
68
|
+
});
|
|
69
|
+
});
|
|
70
|
+
const effectiveMermaidInit = computed(() => {
|
|
71
|
+
return mergeMermaidConfig({
|
|
72
|
+
baseConfig: baseMermaidInit,
|
|
73
|
+
overrideConfig: frontmatterConfig.value,
|
|
74
|
+
theme: mermaidTheme.value
|
|
75
|
+
});
|
|
42
76
|
});
|
|
43
77
|
const configuredSpinnerName = computed(() => componentOptions.spinner?.trim() || "");
|
|
44
78
|
const customSpinner = shallowRef(null);
|
|
@@ -108,46 +142,78 @@ function serializeMermaidFromNode(root) {
|
|
|
108
142
|
if (root.nodeType === ELEMENT_NODE) {
|
|
109
143
|
const el = root;
|
|
110
144
|
const tag = el.tagName?.toUpperCase();
|
|
111
|
-
if (tag === "BR") return "
|
|
145
|
+
if (tag === "BR") return "\n";
|
|
146
|
+
const blockTags = [
|
|
147
|
+
"DIV",
|
|
148
|
+
"P",
|
|
149
|
+
"LI",
|
|
150
|
+
"TR",
|
|
151
|
+
"H1",
|
|
152
|
+
"H2",
|
|
153
|
+
"H3",
|
|
154
|
+
"H4",
|
|
155
|
+
"H5",
|
|
156
|
+
"H6",
|
|
157
|
+
"BLOCKQUOTE",
|
|
158
|
+
"PRE",
|
|
159
|
+
"UL",
|
|
160
|
+
"OL",
|
|
161
|
+
"DL",
|
|
162
|
+
"DT",
|
|
163
|
+
"DD",
|
|
164
|
+
"TABLE",
|
|
165
|
+
"THEAD",
|
|
166
|
+
"TBODY",
|
|
167
|
+
"TFOOT",
|
|
168
|
+
"SECTION",
|
|
169
|
+
"ARTICLE",
|
|
170
|
+
"ASIDE",
|
|
171
|
+
"HEADER",
|
|
172
|
+
"FOOTER",
|
|
173
|
+
"MAIN"
|
|
174
|
+
];
|
|
175
|
+
const isBlock = blockTags.includes(tag) || el.classList.contains("line") || el.classList.contains("code-line");
|
|
112
176
|
let out = "";
|
|
113
177
|
el.childNodes.forEach((child) => {
|
|
114
178
|
out += serializeMermaidFromNode(child);
|
|
115
179
|
});
|
|
180
|
+
if (isBlock) return "\n" + out + "\n";
|
|
116
181
|
return out;
|
|
117
182
|
}
|
|
118
183
|
return "";
|
|
119
184
|
}
|
|
120
185
|
function extractMermaidDefinition(container) {
|
|
186
|
+
const codeNodes = Array.from(container.querySelectorAll("code"));
|
|
187
|
+
if (codeNodes.length > 0) {
|
|
188
|
+
return codeNodes.map((node) => serializeMermaidFromNode(node)).join("\n").trim();
|
|
189
|
+
}
|
|
121
190
|
const pre = container.querySelector("pre");
|
|
122
|
-
|
|
123
|
-
if (code) return code.textContent?.trim() || "";
|
|
124
|
-
if (pre) return pre.textContent?.trim() || "";
|
|
191
|
+
if (pre) return serializeMermaidFromNode(pre).trim();
|
|
125
192
|
return serializeMermaidFromNode(container).trim();
|
|
126
193
|
}
|
|
127
194
|
async function renderMermaid() {
|
|
128
|
-
if (!mermaidContainer.value || !mermaidDefinition) return;
|
|
195
|
+
if (!mermaidContainer.value || !mermaidDefinition.value) return;
|
|
129
196
|
isLoading.value = true;
|
|
130
197
|
const performRender = async () => {
|
|
131
198
|
if (!mermaidContainer.value) return;
|
|
132
199
|
hasError.value = false;
|
|
133
200
|
errorContent.value = null;
|
|
201
|
+
const startTime = performance.now();
|
|
134
202
|
try {
|
|
135
203
|
const mermaid = await $mermaid();
|
|
136
|
-
|
|
137
|
-
startOnLoad: false,
|
|
138
|
-
...baseMermaidInit,
|
|
139
|
-
theme: mermaidTheme.value
|
|
140
|
-
};
|
|
141
|
-
mermaid.initialize(initOptions);
|
|
204
|
+
mermaid.initialize(effectiveMermaidInit.value);
|
|
142
205
|
mermaidContainer.value.removeAttribute("data-processed");
|
|
143
|
-
mermaidContainer.value.textContent = mermaidDefinition;
|
|
206
|
+
mermaidContainer.value.textContent = mermaidDefinition.value;
|
|
144
207
|
await nextTick();
|
|
145
208
|
await mermaid.run({
|
|
146
209
|
nodes: [mermaidContainer.value],
|
|
147
|
-
suppressErrors:
|
|
210
|
+
suppressErrors: !debug
|
|
148
211
|
});
|
|
149
212
|
hasRenderedOnce.value = true;
|
|
150
|
-
|
|
213
|
+
if (debug) {
|
|
214
|
+
const endTime = performance.now();
|
|
215
|
+
console.log(`[nuxt-content-mermaid] \u23F1\uFE0F Rendered in ${(endTime - startTime).toFixed(2)}ms`);
|
|
216
|
+
}
|
|
151
217
|
} catch (error) {
|
|
152
218
|
console.error("[nuxt-content-mermaid]", error);
|
|
153
219
|
hasError.value = true;
|
|
@@ -158,13 +224,11 @@ async function renderMermaid() {
|
|
|
158
224
|
isLoading.value = false;
|
|
159
225
|
}
|
|
160
226
|
};
|
|
161
|
-
await enqueueRender(performRender);
|
|
227
|
+
await enqueueRender(performRender, debug);
|
|
162
228
|
}
|
|
163
229
|
function setupMermaidContainer() {
|
|
164
230
|
const container = mermaidContainer.value;
|
|
165
231
|
if (!container) return;
|
|
166
|
-
if (!mermaidDefinition)
|
|
167
|
-
mermaidDefinition = extractMermaidDefinition(container);
|
|
168
232
|
if (!isLazy) {
|
|
169
233
|
renderMermaid();
|
|
170
234
|
return;
|
|
@@ -182,8 +246,8 @@ function setupMermaidContainer() {
|
|
|
182
246
|
}
|
|
183
247
|
onMounted(() => {
|
|
184
248
|
if (!isEnabled) return;
|
|
185
|
-
if (mermaidContainer.value)
|
|
186
|
-
mermaidDefinition = extractMermaidDefinition(mermaidContainer.value);
|
|
249
|
+
if (!mermaidDefinition.value && mermaidContainer.value)
|
|
250
|
+
mermaidDefinition.value = extractMermaidDefinition(mermaidContainer.value);
|
|
187
251
|
nextTick(() => setupMermaidContainer());
|
|
188
252
|
});
|
|
189
253
|
onUnmounted(() => {
|
|
@@ -193,6 +257,19 @@ watch(mermaidTheme, () => {
|
|
|
193
257
|
if (!isEnabled) return;
|
|
194
258
|
if (hasRenderedOnce.value) renderMermaid();
|
|
195
259
|
});
|
|
260
|
+
watch(
|
|
261
|
+
frontmatterConfig,
|
|
262
|
+
() => {
|
|
263
|
+
if (!isEnabled) return;
|
|
264
|
+
if (hasRenderedOnce.value) renderMermaid();
|
|
265
|
+
},
|
|
266
|
+
{ deep: true }
|
|
267
|
+
);
|
|
268
|
+
watch(decodedCode, (newCode) => {
|
|
269
|
+
if (!isEnabled) return;
|
|
270
|
+
mermaidDefinition.value = newCode;
|
|
271
|
+
if (hasRenderedOnce.value) renderMermaid();
|
|
272
|
+
});
|
|
196
273
|
</script>
|
|
197
274
|
|
|
198
275
|
<template>
|
|
@@ -210,8 +287,11 @@ watch(mermaidTheme, () => {
|
|
|
210
287
|
:is="customMermaidImpl"
|
|
211
288
|
v-else-if="customMermaidImpl"
|
|
212
289
|
:spinner="spinnerComponent"
|
|
290
|
+
:code="decodedCode"
|
|
213
291
|
>
|
|
214
|
-
<slot
|
|
292
|
+
<slot>
|
|
293
|
+
<pre v-if="decodedCode"><code>{{ decodedCode }}</code></pre>
|
|
294
|
+
</slot>
|
|
215
295
|
</component>
|
|
216
296
|
|
|
217
297
|
<div
|
|
@@ -220,7 +300,9 @@ watch(mermaidTheme, () => {
|
|
|
220
300
|
>
|
|
221
301
|
<div ref="mermaidContainer">
|
|
222
302
|
<!-- Initially show the slot's <pre><code> for SSR; client-side rendering will replace it with the mermaid SVG -->
|
|
223
|
-
<slot
|
|
303
|
+
<slot>
|
|
304
|
+
<pre v-if="decodedCode"><code>{{ decodedCode }}</code></pre>
|
|
305
|
+
</slot>
|
|
224
306
|
</div>
|
|
225
307
|
|
|
226
308
|
<template v-if="isLoading && !hasRenderedOnce">
|
|
@@ -254,5 +336,5 @@ watch(mermaidTheme, () => {
|
|
|
254
336
|
</template>
|
|
255
337
|
|
|
256
338
|
<style scoped>
|
|
257
|
-
.mermaid-wrapper{
|
|
339
|
+
.mermaid-wrapper{position:relative;text-align:center}.mermaid-wrapper :deep(code),.mermaid-wrapper :deep(pre){margin:0;max-height:160px;opacity:0;overflow:hidden;pointer-events:none;-webkit-user-select:none;-moz-user-select:none;user-select:none}.mermaid-error-default{text-align:center}.mermaid:not([data-processed]){color:transparent;min-height:10px}.mermaid-wrapper :deep(.edgeLabel),.mermaid-wrapper :deep(.label),.mermaid-wrapper :deep(.nodeLabel){overflow:visible!important;white-space:normal!important}.mermaid-wrapper :deep(foreignObject){overflow:visible!important}
|
|
258
340
|
</style>
|
|
@@ -1,3 +1,8 @@
|
|
|
1
|
+
import type { MermaidConfig } from 'mermaid';
|
|
2
|
+
type __VLS_Props = {
|
|
3
|
+
config?: MermaidConfig | string | unknown;
|
|
4
|
+
code?: string;
|
|
5
|
+
};
|
|
1
6
|
declare var __VLS_1: {}, __VLS_9: {}, __VLS_11: {}, __VLS_13: {}, __VLS_21: {
|
|
2
7
|
error: unknown;
|
|
3
8
|
source: string;
|
|
@@ -13,7 +18,7 @@ type __VLS_Slots = {} & {
|
|
|
13
18
|
} & {
|
|
14
19
|
error?: (props: typeof __VLS_21) => any;
|
|
15
20
|
};
|
|
16
|
-
declare const __VLS_base: import("vue").DefineComponent<
|
|
21
|
+
declare const __VLS_base: import("vue").DefineComponent<__VLS_Props, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {}, string, import("vue").PublicProps, Readonly<__VLS_Props> & Readonly<{}>, {}, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
|
|
17
22
|
declare const __VLS_export: __VLS_WithSlots<typeof __VLS_base, __VLS_Slots>;
|
|
18
23
|
declare const _default: typeof __VLS_export;
|
|
19
24
|
export default _default;
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { MermaidConfig } from 'mermaid';
|
|
2
|
-
export declare const DEFAULT_IMPORT_SOURCE = "https://cdn.jsdelivr.net/npm/mermaid@11.12.1/dist/mermaid.esm.min.mjs";
|
|
3
2
|
export declare const DEFAULT_LIGHT_THEME: MermaidConfig['theme'];
|
|
4
3
|
export declare const DEFAULT_DARK_THEME: MermaidConfig['theme'];
|
|
5
|
-
export declare const
|
|
4
|
+
export declare const DEFAULT_MERMAID_CONFIG: MermaidConfig;
|
|
5
|
+
export declare const DEFAULT_FRONTMATTER_CONFIG_KEY = "config";
|
|
@@ -1,6 +1,10 @@
|
|
|
1
|
-
export const DEFAULT_IMPORT_SOURCE = "https://cdn.jsdelivr.net/npm/mermaid@11.12.1/dist/mermaid.esm.min.mjs";
|
|
2
1
|
export const DEFAULT_LIGHT_THEME = "default";
|
|
3
2
|
export const DEFAULT_DARK_THEME = "dark";
|
|
4
|
-
export const
|
|
5
|
-
startOnLoad: false
|
|
3
|
+
export const DEFAULT_MERMAID_CONFIG = {
|
|
4
|
+
startOnLoad: false,
|
|
5
|
+
theme: "default",
|
|
6
|
+
fontFamily: "Arial, sans-serif, \u5FAE\u8EDF\u6B63\u9ED1\u9AD4",
|
|
7
|
+
securityLevel: "strict",
|
|
8
|
+
logLevel: "error"
|
|
6
9
|
};
|
|
10
|
+
export const DEFAULT_FRONTMATTER_CONFIG_KEY = "config";
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import type { MermaidConfig } from 'mermaid';
|
|
2
|
+
interface ThemeOptions {
|
|
3
|
+
useColorModeTheme?: boolean;
|
|
4
|
+
colorModeValue?: string;
|
|
5
|
+
baseTheme?: MermaidConfig['theme'];
|
|
6
|
+
lightTheme?: MermaidConfig['theme'];
|
|
7
|
+
darkTheme?: MermaidConfig['theme'];
|
|
8
|
+
frontmatterTheme?: MermaidConfig['theme'];
|
|
9
|
+
}
|
|
10
|
+
export declare function resolveMermaidTheme(options: ThemeOptions): "default" | "base" | "dark" | "forest" | "neutral" | "null" | undefined;
|
|
11
|
+
interface MergeOptions extends MermaidConfig {
|
|
12
|
+
baseConfig?: MermaidConfig;
|
|
13
|
+
overrideConfig?: MermaidConfig;
|
|
14
|
+
theme?: MermaidConfig['theme'];
|
|
15
|
+
}
|
|
16
|
+
export declare function mergeMermaidConfig(options: MergeOptions): MermaidConfig;
|
|
17
|
+
export {};
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import { defu } from "defu";
|
|
2
|
+
export function resolveMermaidTheme(options) {
|
|
3
|
+
const {
|
|
4
|
+
useColorModeTheme,
|
|
5
|
+
colorModeValue,
|
|
6
|
+
frontmatterTheme,
|
|
7
|
+
baseTheme,
|
|
8
|
+
lightTheme,
|
|
9
|
+
darkTheme
|
|
10
|
+
} = options;
|
|
11
|
+
if (frontmatterTheme) return frontmatterTheme;
|
|
12
|
+
if (useColorModeTheme && colorModeValue) {
|
|
13
|
+
if (colorModeValue === "dark" && darkTheme) return darkTheme;
|
|
14
|
+
if (colorModeValue !== "dark" && lightTheme) return lightTheme;
|
|
15
|
+
}
|
|
16
|
+
return baseTheme ?? lightTheme ?? darkTheme;
|
|
17
|
+
}
|
|
18
|
+
export function mergeMermaidConfig(options) {
|
|
19
|
+
const {
|
|
20
|
+
baseConfig,
|
|
21
|
+
overrideConfig,
|
|
22
|
+
theme,
|
|
23
|
+
...overrides
|
|
24
|
+
} = options;
|
|
25
|
+
const merged = defu(
|
|
26
|
+
{},
|
|
27
|
+
overrides || {},
|
|
28
|
+
overrideConfig || {},
|
|
29
|
+
baseConfig || {}
|
|
30
|
+
);
|
|
31
|
+
return {
|
|
32
|
+
startOnLoad: false,
|
|
33
|
+
...merged,
|
|
34
|
+
theme: theme ?? merged.theme
|
|
35
|
+
};
|
|
36
|
+
}
|
|
@@ -1,27 +1,9 @@
|
|
|
1
1
|
import { defineNuxtPlugin, useRuntimeConfig } from "#app";
|
|
2
|
-
import {
|
|
3
|
-
DEFAULT_IMPORT_SOURCE,
|
|
4
|
-
DEFAULT_MERMAID_INIT
|
|
5
|
-
} from "../constants.js";
|
|
2
|
+
import { DEFAULT_MERMAID_CONFIG } from "../constants.js";
|
|
6
3
|
const globalWithLoader = globalThis;
|
|
7
|
-
const ABSOLUTE_URL_REGEX = /^(?:https?:)?\/\//;
|
|
8
|
-
function resolveSource(source) {
|
|
9
|
-
if (ABSOLUTE_URL_REGEX.test(source)) return source;
|
|
10
|
-
if (source.startsWith("/")) return source;
|
|
11
|
-
if (typeof window !== "undefined")
|
|
12
|
-
return new URL(source, window.location.origin).href;
|
|
13
|
-
return source;
|
|
14
|
-
}
|
|
15
|
-
async function loadMermaidFrom(source) {
|
|
16
|
-
const mod = await import(
|
|
17
|
-
/* @vite-ignore */
|
|
18
|
-
resolveSource(source)
|
|
19
|
-
);
|
|
20
|
-
return mod.default ?? mod;
|
|
21
|
-
}
|
|
22
4
|
export default defineNuxtPlugin(() => {
|
|
23
5
|
const runtimeConfig = useRuntimeConfig();
|
|
24
|
-
const mermaidConfig = runtimeConfig.public?.mermaidContent;
|
|
6
|
+
const mermaidConfig = runtimeConfig.public?.contentMermaid ?? runtimeConfig.public?.mermaidContent ?? {};
|
|
25
7
|
if (mermaidConfig?.enabled === false) {
|
|
26
8
|
return {
|
|
27
9
|
provide: {
|
|
@@ -36,18 +18,27 @@ export default defineNuxtPlugin(() => {
|
|
|
36
18
|
const loadMermaid = async () => {
|
|
37
19
|
if (globalWithLoader.__nuxtMermaidLoader__)
|
|
38
20
|
return globalWithLoader.__nuxtMermaidLoader__;
|
|
39
|
-
const
|
|
21
|
+
const debug = mermaidConfig?.debug || false;
|
|
22
|
+
const userInit = mermaidConfig?.loader?.init || {};
|
|
23
|
+
const hasUserLogLevel = Object.prototype.hasOwnProperty.call(userInit, "logLevel");
|
|
24
|
+
const hasUserSuppressErrorRendering = Object.prototype.hasOwnProperty.call(userInit, "suppressErrorRendering");
|
|
40
25
|
const initOptions = {
|
|
41
|
-
...
|
|
42
|
-
...
|
|
26
|
+
...DEFAULT_MERMAID_CONFIG,
|
|
27
|
+
...userInit,
|
|
28
|
+
logLevel: hasUserLogLevel ? userInit.logLevel : debug ? 1 : 5,
|
|
29
|
+
suppressErrorRendering: hasUserSuppressErrorRendering ? userInit.suppressErrorRendering : !debug
|
|
43
30
|
};
|
|
44
|
-
globalWithLoader.__nuxtMermaidLoader__ =
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
31
|
+
globalWithLoader.__nuxtMermaidLoader__ = (async () => {
|
|
32
|
+
try {
|
|
33
|
+
const mermaid = await import("mermaid");
|
|
34
|
+
const mermaidInstance = mermaid.default ?? mermaid;
|
|
35
|
+
mermaidInstance.initialize(initOptions);
|
|
36
|
+
return mermaidInstance;
|
|
37
|
+
} catch (error) {
|
|
38
|
+
globalWithLoader.__nuxtMermaidLoader__ = void 0;
|
|
39
|
+
throw error;
|
|
40
|
+
}
|
|
41
|
+
})();
|
|
51
42
|
return globalWithLoader.__nuxtMermaidLoader__;
|
|
52
43
|
};
|
|
53
44
|
return {
|
package/dist/runtime/utils.d.ts
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export declare function enqueueRender(task: () => Promise<void
|
|
1
|
+
export declare function enqueueRender(task: () => Promise<void>, debug?: boolean): Promise<void>;
|
package/dist/runtime/utils.js
CHANGED
|
@@ -1,7 +1,30 @@
|
|
|
1
1
|
let renderQueue = Promise.resolve();
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
2
|
+
let queueSize = 0;
|
|
3
|
+
export function enqueueRender(task, debug = false) {
|
|
4
|
+
queueSize++;
|
|
5
|
+
if (debug) {
|
|
6
|
+
console.log(`[nuxt-content-mermaid] \u{1F535} Enqueueing task. Queue size: ${queueSize}`);
|
|
7
|
+
}
|
|
8
|
+
const wrappedTask = async () => {
|
|
9
|
+
if (debug) {
|
|
10
|
+
console.log(`[nuxt-content-mermaid] \u{1F7E2} Starting render task. Pending: ${queueSize - 1}`);
|
|
11
|
+
}
|
|
12
|
+
try {
|
|
13
|
+
await task();
|
|
14
|
+
} catch (e) {
|
|
15
|
+
console.error("[nuxt-content-mermaid] Render error:", e);
|
|
16
|
+
throw e;
|
|
17
|
+
} finally {
|
|
18
|
+
queueSize--;
|
|
19
|
+
if (debug) {
|
|
20
|
+
console.log(`[nuxt-content-mermaid] \u2705 Task finished. Remaining: ${queueSize}`);
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
};
|
|
24
|
+
renderQueue = renderQueue.then(wrappedTask).catch((e) => {
|
|
25
|
+
if (debug) {
|
|
26
|
+
console.error("[nuxt-content-mermaid] \u274C Queue chain caught error:", e);
|
|
27
|
+
}
|
|
5
28
|
});
|
|
6
29
|
return renderQueue;
|
|
7
30
|
}
|
package/dist/types.d.mts
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@barzhsieh/nuxt-content-mermaid",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "2.0.0",
|
|
4
4
|
"description": "A Nuxt module designed for integrating Mermaid with `@nuxt/content`. It automatically converts `mermaid` code blocks in Markdown into responsive chart components, and supports lazy loading and dark/light theme switching.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"nuxt",
|
|
@@ -35,48 +35,47 @@
|
|
|
35
35
|
"files": [
|
|
36
36
|
"dist"
|
|
37
37
|
],
|
|
38
|
-
"scripts": {
|
|
39
|
-
"prepack": "nuxt-module-build build",
|
|
40
|
-
"dev": "npm run dev:prepare && nuxi dev playground",
|
|
41
|
-
"dev:build": "nuxi build playground",
|
|
42
|
-
"dev:prepare": "nuxt-module-build build --stub && nuxt-module-build prepare && nuxi prepare playground",
|
|
43
|
-
"release:base": "npm run lint && npm run test && npm run prepack",
|
|
44
|
-
"release:patch": "npm run release:base && changelogen --release --patch --push --publish",
|
|
45
|
-
"release:minor": "npm run release:base && changelogen --release --minor --push --publish",
|
|
46
|
-
"release:major": "npm run release:base && changelogen --release --major --push --publish",
|
|
47
|
-
"release": "npm run release:patch",
|
|
48
|
-
"lint": "eslint .",
|
|
49
|
-
"test": "vitest run",
|
|
50
|
-
"test:watch": "vitest watch",
|
|
51
|
-
"test:types": "vue-tsc --noEmit && cd playground && vue-tsc --noEmit"
|
|
52
|
-
},
|
|
53
38
|
"dependencies": {
|
|
54
|
-
"@nuxt/kit": "
|
|
55
|
-
"defu": "
|
|
39
|
+
"@nuxt/kit": "4.2.1",
|
|
40
|
+
"defu": "^6.1.4",
|
|
41
|
+
"mermaid": "^11.12.2"
|
|
56
42
|
},
|
|
57
43
|
"peerDependencies": {
|
|
58
|
-
"nuxt": "
|
|
59
|
-
"
|
|
44
|
+
"@nuxt/content": ">=3.5.0",
|
|
45
|
+
"nuxt": "^3.20.1 || ^4.1.0"
|
|
60
46
|
},
|
|
61
47
|
"devDependencies": {
|
|
62
|
-
"@nuxt/content": "
|
|
63
|
-
"@nuxt/devtools": "
|
|
64
|
-
"@nuxt/eslint-config": "
|
|
65
|
-
"@nuxt/module-builder": "
|
|
66
|
-
"@nuxt/schema": "
|
|
67
|
-
"@nuxt/test-utils": "
|
|
68
|
-
"@nuxtjs/color-mode": "
|
|
69
|
-
"@types/node": "
|
|
70
|
-
"changelogen": "
|
|
71
|
-
"eslint": "
|
|
72
|
-
"
|
|
73
|
-
"
|
|
74
|
-
"typescript": "
|
|
75
|
-
"vitest": "
|
|
76
|
-
"vue-tsc": "
|
|
48
|
+
"@nuxt/content": "^3.9.0",
|
|
49
|
+
"@nuxt/devtools": "3.1.1",
|
|
50
|
+
"@nuxt/eslint-config": "1.11.0",
|
|
51
|
+
"@nuxt/module-builder": "1.0.2",
|
|
52
|
+
"@nuxt/schema": "4.2.1",
|
|
53
|
+
"@nuxt/test-utils": "^3.21.0",
|
|
54
|
+
"@nuxtjs/color-mode": "4.0.0",
|
|
55
|
+
"@types/node": "^24.10.1",
|
|
56
|
+
"changelogen": "^0.6.2",
|
|
57
|
+
"eslint": "^9.39.1",
|
|
58
|
+
"nuxt": "4.2.1",
|
|
59
|
+
"playwright": "^1.57.0",
|
|
60
|
+
"typescript": "~5.9.3",
|
|
61
|
+
"vitest": "^4.0.15",
|
|
62
|
+
"vue-tsc": "^3.1.7"
|
|
77
63
|
},
|
|
78
64
|
"volta": {
|
|
79
65
|
"node": "22.21.1"
|
|
80
66
|
},
|
|
81
|
-
"
|
|
82
|
-
|
|
67
|
+
"scripts": {
|
|
68
|
+
"dev": "npm run dev:prepare && nuxi dev playground",
|
|
69
|
+
"dev:build": "nuxi build playground",
|
|
70
|
+
"dev:prepare": "nuxt-module-build build --stub && nuxt-module-build prepare && nuxi prepare playground",
|
|
71
|
+
"release:base": "pnpm run lint && pnpm run test && pnpm run prepack",
|
|
72
|
+
"release:patch": "pnpm run release:base && changelogen --release --patch --push && pnpm publish",
|
|
73
|
+
"release:minor": "pnpm run release:base && changelogen --release --minor --push && pnpm publish",
|
|
74
|
+
"release:major": "pnpm run release:base && changelogen --release --major --push && pnpm publish",
|
|
75
|
+
"release": "pnpm run release:patch",
|
|
76
|
+
"lint": "eslint .",
|
|
77
|
+
"test": "vitest run",
|
|
78
|
+
"test:watch": "vitest watch",
|
|
79
|
+
"test:types": "vue-tsc --noEmit && cd playground && vue-tsc --noEmit"
|
|
80
|
+
}
|
|
81
|
+
}
|