@barzhsieh/nuxt-content-mermaid 1.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/LICENSE +21 -0
- package/README.md +194 -0
- package/README.zh-TW.md +194 -0
- package/dist/module.d.mts +56 -0
- package/dist/module.json +12 -0
- package/dist/module.mjs +105 -0
- package/dist/runtime/components/Mermaid.d.vue.ts +21 -0
- package/dist/runtime/components/Mermaid.vue +241 -0
- package/dist/runtime/components/Mermaid.vue.d.ts +21 -0
- package/dist/runtime/components/Spinner.d.vue.ts +3 -0
- package/dist/runtime/components/Spinner.vue +25 -0
- package/dist/runtime/components/Spinner.vue.d.ts +3 -0
- package/dist/runtime/plugins/mermaid.client.d.ts +10 -0
- package/dist/runtime/plugins/mermaid.client.js +58 -0
- package/dist/runtime/server/tsconfig.json +3 -0
- package/dist/types.d.mts +3 -0
- package/package.json +65 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
# MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2025-PRESENT BarZ Hsieh<https://github.com/andy820621>
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,194 @@
|
|
|
1
|
+
[English](./README.md) | [中文](./README.zh-TW.md)
|
|
2
|
+
|
|
3
|
+
# nuxt-mermaid-content
|
|
4
|
+
|
|
5
|
+
[![npm version][npm-version-src]][npm-version-href]
|
|
6
|
+
[![npm downloads][npm-downloads-src]][npm-downloads-href]
|
|
7
|
+
[![License][license-src]][license-href]
|
|
8
|
+
[![Nuxt][nuxt-src]][nuxt-href]
|
|
9
|
+
[](https://mermaid.js.org/)
|
|
10
|
+
[](https://content.nuxt.com/)
|
|
11
|
+
|
|
12
|
+
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.
|
|
13
|
+
|
|
14
|
+
## Features
|
|
15
|
+
|
|
16
|
+
- **Automatic conversion**: Parses Markdown code blocks and replaces them with a `<Mermaid>` rendering component.
|
|
17
|
+
- **Performance friendly**: Supports lazy loading — Mermaid core and related resources are only loaded when the component mounts.
|
|
18
|
+
- **Theme integration**: Integrates with `@nuxtjs/color-mode` to automatically switch between light and dark Mermaid themes.
|
|
19
|
+
- **Highly customizable**: Allows custom wrapper components, loading spinners, and custom CDN or local import sources.
|
|
20
|
+
- **Runtime config**: Settings can be overridden at deployment time through runtime config / environment variables.
|
|
21
|
+
|
|
22
|
+
## Requirements
|
|
23
|
+
|
|
24
|
+
- Nuxt 4 (`nuxt@^4.0.0`)
|
|
25
|
+
- `@nuxt/content@^3.8.0`
|
|
26
|
+
|
|
27
|
+
## Quick Setup
|
|
28
|
+
|
|
29
|
+
### 1. Install the module
|
|
30
|
+
|
|
31
|
+
```bash
|
|
32
|
+
npx nuxi module add @barzhsieh/nuxt-content-mermaid
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
### 2. Configure `nuxt.config.ts`
|
|
36
|
+
|
|
37
|
+
Make sure the module is included in the `modules` array (if you used `nuxi module add`, this is added automatically):
|
|
38
|
+
|
|
39
|
+
```ts
|
|
40
|
+
export default defineNuxtConfig({
|
|
41
|
+
modules: ["@barzhsieh/nuxt-content-mermaid", "@nuxt/content"],
|
|
42
|
+
});
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
### 3. Use Mermaid in Markdown
|
|
46
|
+
|
|
47
|
+
Add Mermaid code blocks inside `.md` files under the `content/` directory:
|
|
48
|
+
|
|
49
|
+
````markdown
|
|
50
|
+
# Flowchart example
|
|
51
|
+
|
|
52
|
+
```mermaid
|
|
53
|
+
graph LR
|
|
54
|
+
A[Start] --> B{Is it working?}
|
|
55
|
+
B -- Yes --> C[Great!]
|
|
56
|
+
B -- No --> D[Debug]
|
|
57
|
+
```
|
|
58
|
+
````
|
|
59
|
+
|
|
60
|
+
The module will automatically transform the block into an SVG chart component.
|
|
61
|
+
|
|
62
|
+
## Configuration
|
|
63
|
+
|
|
64
|
+
You can configure the module globally through the `mermaidContent` option.
|
|
65
|
+
|
|
66
|
+
```ts
|
|
67
|
+
// nuxt.config.ts
|
|
68
|
+
export default defineNuxtConfig({
|
|
69
|
+
mermaidContent: {
|
|
70
|
+
enabled: true,
|
|
71
|
+
loader: {
|
|
72
|
+
importSource:
|
|
73
|
+
"https://cdn.jsdelivr.net/npm/mermaid@11/dist/mermaid.esm.min.mjs",
|
|
74
|
+
init: {
|
|
75
|
+
securityLevel: "strict",
|
|
76
|
+
// additional options passed to mermaid.initialize()
|
|
77
|
+
},
|
|
78
|
+
},
|
|
79
|
+
theme: {
|
|
80
|
+
useColorModeTheme: true,
|
|
81
|
+
light: "default",
|
|
82
|
+
dark: "dark",
|
|
83
|
+
},
|
|
84
|
+
components: {
|
|
85
|
+
renderer: undefined,
|
|
86
|
+
spinner: undefined,
|
|
87
|
+
},
|
|
88
|
+
},
|
|
89
|
+
});
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
### Options
|
|
93
|
+
|
|
94
|
+
**Top-level**
|
|
95
|
+
|
|
96
|
+
| Option | Type | Default | Description |
|
|
97
|
+
| :-------- | :-------- | :------ | :--------------------------------------------------- |
|
|
98
|
+
| `enabled` | `boolean` | `true` | Whether the module and its conversion logic are on. |
|
|
99
|
+
|
|
100
|
+
**loader**
|
|
101
|
+
|
|
102
|
+
| Option | Type | Default | Description |
|
|
103
|
+
| :--------------------- | :----------------- | :----------- | :--------------------------------------------------------------------- |
|
|
104
|
+
| `loader.importSource` | `string` | jsDelivr CDN | Source to load Mermaid ESM from (supports CDN or a local path). |
|
|
105
|
+
| `loader.init` | `MermaidConfig` | `{ startOnLoad: false }` | Raw options object passed to `mermaid.initialize`. |
|
|
106
|
+
|
|
107
|
+
**theme**
|
|
108
|
+
|
|
109
|
+
| Option | Type | Default | Description |
|
|
110
|
+
| :-------------------------- | :------ | :---------- | :-------------------------------------------------------------------------------------------------------- |
|
|
111
|
+
| `theme.useColorModeTheme` | boolean | `true` | If `@nuxtjs/color-mode` is installed, automatically follow its current theme. |
|
|
112
|
+
| `theme.light` | string | `'default'` | Mermaid theme to use when color mode is light (and as fallback when color-mode is off). |
|
|
113
|
+
| `theme.dark` | string | `'dark'` | Mermaid theme to use when color mode is dark (and as fallback when color-mode is off). |
|
|
114
|
+
|
|
115
|
+
**components**
|
|
116
|
+
|
|
117
|
+
| Option | Type | Default | Description |
|
|
118
|
+
| :------------------------ | :------- | :---------- | :----------------------------------------------------------------------- |
|
|
119
|
+
| `components.renderer` | `string` | `undefined` | Optional: custom Mermaid renderer component name. |
|
|
120
|
+
| `components.spinner` | `string` | `undefined` | Optional: global loading spinner component name. |
|
|
121
|
+
|
|
122
|
+
> **Note**: All options can be overridden at runtime via `runtimeConfig.public.mermaidContent`.
|
|
123
|
+
|
|
124
|
+
## Advanced Usage
|
|
125
|
+
|
|
126
|
+
### Theme & Color Mode
|
|
127
|
+
|
|
128
|
+
The module determines the active Mermaid theme with the following priority:
|
|
129
|
+
|
|
130
|
+
1. If `theme.useColorModeTheme: true` _and_ `@nuxtjs/color-mode` is available:
|
|
131
|
+
- `dark` → `theme.dark`
|
|
132
|
+
- `light` → `theme.light`
|
|
133
|
+
2. Otherwise (color-mode missing or `theme.useColorModeTheme` is `false`):
|
|
134
|
+
- Use `loader.init.theme` if provided.
|
|
135
|
+
- Else fall back to `theme.light`, then `theme.dark`.
|
|
136
|
+
|
|
137
|
+
### Custom Rendering Component
|
|
138
|
+
|
|
139
|
+
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`.
|
|
140
|
+
|
|
141
|
+
1. Specify the component name in `nuxt.config.ts`:
|
|
142
|
+
|
|
143
|
+
```ts
|
|
144
|
+
mermaidContent: {
|
|
145
|
+
components: {
|
|
146
|
+
renderer: "MyCustomMermaid",
|
|
147
|
+
},
|
|
148
|
+
}
|
|
149
|
+
```
|
|
150
|
+
|
|
151
|
+
2. Implement the component in `components/MyCustomMermaid.vue`:
|
|
152
|
+
|
|
153
|
+
```vue
|
|
154
|
+
<script setup lang="ts">
|
|
155
|
+
// You can consume slot content or invoke useMermaid() logic here
|
|
156
|
+
</script>
|
|
157
|
+
|
|
158
|
+
<template>
|
|
159
|
+
<div class="custom-wrapper border rounded p-4">
|
|
160
|
+
<Mermaid>
|
|
161
|
+
<slot />
|
|
162
|
+
</Mermaid>
|
|
163
|
+
</div>
|
|
164
|
+
</template>
|
|
165
|
+
```
|
|
166
|
+
|
|
167
|
+
## Contribution
|
|
168
|
+
|
|
169
|
+
<details>
|
|
170
|
+
<summary>Local Development Commands</summary>
|
|
171
|
+
|
|
172
|
+
```bash
|
|
173
|
+
pnpm install
|
|
174
|
+
pnpm dev:prepare
|
|
175
|
+
pnpm dev # Run playground
|
|
176
|
+
pnpm test # Run tests
|
|
177
|
+
```
|
|
178
|
+
|
|
179
|
+
</details>
|
|
180
|
+
|
|
181
|
+
## License
|
|
182
|
+
|
|
183
|
+
[MIT License](./LICENSE)
|
|
184
|
+
|
|
185
|
+
<!-- Badges -->
|
|
186
|
+
|
|
187
|
+
[npm-version-src]: https://img.shields.io/npm/v/@barzhsieh/nuxt-content-mermaid/latest.svg?style=flat&colorA=020420&colorB=00DC82
|
|
188
|
+
[npm-version-href]: https://npmjs.com/package/@barzhsieh/nuxt-content-mermaid
|
|
189
|
+
[npm-downloads-src]: https://img.shields.io/npm/dm/@barzhsieh/nuxt-content-mermaid.svg?style=flat&colorA=020420&colorB=00DC82
|
|
190
|
+
[npm-downloads-href]: https://npmjs.com/package/@barzhsieh/nuxt-content-mermaid
|
|
191
|
+
[license-src]: https://img.shields.io/npm/l/@barzhsieh/nuxt-content-mermaid.svg?style=flat&colorA=020420&colorB=00DC82
|
|
192
|
+
[license-href]: https://npmjs.com/package/@barzhsieh/nuxt-content-mermaid
|
|
193
|
+
[nuxt-src]: https://img.shields.io/badge/Nuxt-020420?logo=nuxt.js
|
|
194
|
+
[nuxt-href]: https://nuxt.com
|
package/README.zh-TW.md
ADDED
|
@@ -0,0 +1,194 @@
|
|
|
1
|
+
[English](./README.md) | [中文](./README.zh-TW.md)
|
|
2
|
+
|
|
3
|
+
# nuxt-mermaid-content
|
|
4
|
+
|
|
5
|
+
[![npm version][npm-version-src]][npm-version-href]
|
|
6
|
+
[![npm downloads][npm-downloads-src]][npm-downloads-href]
|
|
7
|
+
[![License][license-src]][license-href]
|
|
8
|
+
[![Nuxt][nuxt-src]][nuxt-href]
|
|
9
|
+
[](https://mermaid.js.org/)
|
|
10
|
+
[](https://content.nuxt.com/)
|
|
11
|
+
|
|
12
|
+
專為 Nuxt + `@nuxt/content` 設計的 Mermaid 整合模組。能自動將 Markdown 中的 ```mermaid 區塊轉換為響應式的圖表元件,並支援 Lazy Loading 與深色模式切換。
|
|
13
|
+
|
|
14
|
+
## Features
|
|
15
|
+
|
|
16
|
+
- **自動轉換**:解析 Markdown 代碼區塊並替換為 `<Mermaid>` 渲染元件。
|
|
17
|
+
- **效能優化**:支援 Lazy Loading,僅在元件掛載時載入 Mermaid 核心與資源。
|
|
18
|
+
- **主題整合**:無縫整合 `@nuxtjs/color-mode`,自動切換 Light/Dark 對應主題。
|
|
19
|
+
- **高度客製**:支援自訂渲染元件(Wrapper)、Loading Spinner 及 CDN 來源。
|
|
20
|
+
- **Runtime Config**:支援透過環境變數動態覆寫設定。
|
|
21
|
+
|
|
22
|
+
## 使用前提
|
|
23
|
+
|
|
24
|
+
- Nuxt 4 (`nuxt@^4.0.0`)
|
|
25
|
+
- `@nuxt/content@^3.8.0`(必須安裝)
|
|
26
|
+
|
|
27
|
+
## Quick Setup
|
|
28
|
+
|
|
29
|
+
### 1. 安裝模組
|
|
30
|
+
|
|
31
|
+
```bash
|
|
32
|
+
npx nuxi module add @barzhsieh/nuxt-content-mermaid
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
### 2. 配置 `nuxt.config.ts`
|
|
36
|
+
|
|
37
|
+
確保模組已加入 `modules` 清單(若使用 `nuxi module add` 則會自動加入):
|
|
38
|
+
|
|
39
|
+
```ts
|
|
40
|
+
export default defineNuxtConfig({
|
|
41
|
+
modules: ["@barzhsieh/nuxt-content-mermaid", "@nuxt/content"],
|
|
42
|
+
});
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
### 3. 在 Markdown 中使用
|
|
46
|
+
|
|
47
|
+
在 `content/` 目錄下的 `.md` 檔案中直接撰寫 Mermaid 語法:
|
|
48
|
+
|
|
49
|
+
````markdown
|
|
50
|
+
# 流程圖範例
|
|
51
|
+
|
|
52
|
+
```mermaid
|
|
53
|
+
graph LR
|
|
54
|
+
A[Start] --> B{Is it working?}
|
|
55
|
+
B -- Yes --> C[Great!]
|
|
56
|
+
B -- No --> D[Debug]
|
|
57
|
+
```
|
|
58
|
+
````
|
|
59
|
+
|
|
60
|
+
模組會自動將其轉換為 SVG 圖表。
|
|
61
|
+
|
|
62
|
+
## Configuration
|
|
63
|
+
|
|
64
|
+
你可以透過 `mermaidContent` 選項進行全域設定。
|
|
65
|
+
|
|
66
|
+
```ts
|
|
67
|
+
// nuxt.config.ts
|
|
68
|
+
export default defineNuxtConfig({
|
|
69
|
+
mermaidContent: {
|
|
70
|
+
enabled: true,
|
|
71
|
+
loader: {
|
|
72
|
+
importSource:
|
|
73
|
+
"https://cdn.jsdelivr.net/npm/mermaid@11/dist/mermaid.esm.min.mjs",
|
|
74
|
+
init: {
|
|
75
|
+
securityLevel: "strict",
|
|
76
|
+
// 其他傳遞給 mermaid.initialize() 的參數
|
|
77
|
+
},
|
|
78
|
+
},
|
|
79
|
+
theme: {
|
|
80
|
+
useColorModeTheme: true,
|
|
81
|
+
light: "default",
|
|
82
|
+
dark: "dark",
|
|
83
|
+
},
|
|
84
|
+
components: {
|
|
85
|
+
renderer: undefined,
|
|
86
|
+
spinner: undefined,
|
|
87
|
+
},
|
|
88
|
+
},
|
|
89
|
+
});
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
### 參數說明
|
|
93
|
+
|
|
94
|
+
**Top-level**
|
|
95
|
+
|
|
96
|
+
| 參數 | 類型 | 預設值 | 說明 |
|
|
97
|
+
| :-------- | :-------- | :----- | :----------------------------- |
|
|
98
|
+
| `enabled` | `boolean` | `true` | 是否啟用模組與轉換邏輯。 |
|
|
99
|
+
|
|
100
|
+
**loader**
|
|
101
|
+
|
|
102
|
+
| 參數 | 類型 | 預設值 | 說明 |
|
|
103
|
+
| :-------------------- | :-------------- | :---------------------- | :--------------------------------------------------------- |
|
|
104
|
+
| `loader.importSource` | `string` | jsDelivr CDN | 指定 Mermaid ESM 的載入來源(支援 CDN 或本地路徑)。 |
|
|
105
|
+
| `loader.init` | `MermaidConfig` | `{ startOnLoad: false }` | 直接傳遞給 `mermaid.initialize` 的原始設定。 |
|
|
106
|
+
|
|
107
|
+
**theme**
|
|
108
|
+
|
|
109
|
+
| 參數 | 類型 | 預設值 | 說明 |
|
|
110
|
+
| :-------------------------- | :------ | :---------- | :------------------------------------------------------------------------------------------------------ |
|
|
111
|
+
| `theme.useColorModeTheme` | boolean | `true` | 若安裝了 `@nuxtjs/color-mode`,是否自動跟隨其主題。 |
|
|
112
|
+
| `theme.light` | string | `'default'` | 當 `colorMode` 為 light 時的主題,也可作為 fallback。 |
|
|
113
|
+
| `theme.dark` | string | `'dark'` | 當 `colorMode` 為 dark 時的主題,也可作為 fallback。 |
|
|
114
|
+
|
|
115
|
+
**components**
|
|
116
|
+
|
|
117
|
+
| 參數 | 類型 | 預設值 | 說明 |
|
|
118
|
+
| :----------------------- | :------- | :--------- | :----------------------------------------------------- |
|
|
119
|
+
| `components.renderer` | `string` | `undefined` | 指定自訂的 Mermaid 實作元件名稱(見進階用法)。 |
|
|
120
|
+
| `components.spinner` | `string` | `undefined` | 指定全域的 Loading 元件名稱。 |
|
|
121
|
+
|
|
122
|
+
> **Note**: 所有設定皆可透過 `runtimeConfig.public.mermaidContent` 在部署時進行覆寫。
|
|
123
|
+
|
|
124
|
+
## Advanced Usage
|
|
125
|
+
|
|
126
|
+
### 主題與顏色模式 (Color Mode)
|
|
127
|
+
|
|
128
|
+
模組會依據以下優先順序決定主題:
|
|
129
|
+
|
|
130
|
+
1. 若 `theme.useColorModeTheme: true`,且專案有安裝 `@nuxtjs/color-mode`:
|
|
131
|
+
- 偵測為 `dark` → 使用 `theme.dark`
|
|
132
|
+
- 偵測為 `light` → 使用 `theme.light`
|
|
133
|
+
2. 否則(color-mode 未安裝或 `theme.useColorModeTheme` 為 `false`):
|
|
134
|
+
- 優先使用 `loader.init.theme`。
|
|
135
|
+
- 若未提供,依序回退至 `theme.light`,若沒有設置則會回退至 `theme.dark`。
|
|
136
|
+
|
|
137
|
+
### 自訂渲染元件 (Custom Component)
|
|
138
|
+
|
|
139
|
+
若需完全接管 Mermaid 的渲染行為(例如:加入外框、ZoomIn/Out 功能),可指定 `components.renderer`。
|
|
140
|
+
|
|
141
|
+
1. 在 `nuxt.config.ts` 中指定元件名稱:
|
|
142
|
+
|
|
143
|
+
```ts
|
|
144
|
+
mermaidContent: {
|
|
145
|
+
components: {
|
|
146
|
+
renderer: "MyCustomMermaid",
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
```
|
|
150
|
+
|
|
151
|
+
2. 在 `components/MyCustomMermaid.vue` 中實作:
|
|
152
|
+
|
|
153
|
+
```vue
|
|
154
|
+
<script setup lang="ts">
|
|
155
|
+
// 你可以在此使用 slot 內容或自行呼叫 useMermaid() 等邏輯
|
|
156
|
+
</script>
|
|
157
|
+
|
|
158
|
+
<template>
|
|
159
|
+
<div class="custom-wrapper border rounded p-4">
|
|
160
|
+
<Mermaid>
|
|
161
|
+
<slot />
|
|
162
|
+
</Mermaid>
|
|
163
|
+
</div>
|
|
164
|
+
</template>
|
|
165
|
+
```
|
|
166
|
+
|
|
167
|
+
## Contribution
|
|
168
|
+
|
|
169
|
+
<details>
|
|
170
|
+
<summary>Local Development Commands</summary>
|
|
171
|
+
|
|
172
|
+
```bash
|
|
173
|
+
pnpm install
|
|
174
|
+
pnpm dev:prepare
|
|
175
|
+
pnpm dev # Run playground
|
|
176
|
+
pnpm test # Run tests
|
|
177
|
+
```
|
|
178
|
+
|
|
179
|
+
</details>
|
|
180
|
+
|
|
181
|
+
## License
|
|
182
|
+
|
|
183
|
+
[MIT License](./LICENSE)
|
|
184
|
+
|
|
185
|
+
<!-- Badges -->
|
|
186
|
+
|
|
187
|
+
[npm-version-src]: https://img.shields.io/npm/v/@barzhsieh/nuxt-content-mermaid/latest.svg?style=flat&colorA=020420&colorB=00DC82
|
|
188
|
+
[npm-version-href]: https://npmjs.com/package/@barzhsieh/nuxt-content-mermaid
|
|
189
|
+
[npm-downloads-src]: https://img.shields.io/npm/dm/@barzhsieh/nuxt-content-mermaid.svg?style=flat&colorA=020420&colorB=00DC82
|
|
190
|
+
[npm-downloads-href]: https://npmjs.com/package/@barzhsieh/nuxt-content-mermaid
|
|
191
|
+
[license-src]: https://img.shields.io/npm/l/@barzhsieh/nuxt-content-mermaid.svg?style=flat&colorA=020420&colorB=00DC82
|
|
192
|
+
[license-href]: https://npmjs.com/package/@barzhsieh/nuxt-content-mermaid
|
|
193
|
+
[nuxt-src]: https://img.shields.io/badge/Nuxt-020420?logo=nuxt.js
|
|
194
|
+
[nuxt-href]: https://nuxt.com
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
import * as _nuxt_schema from '@nuxt/schema';
|
|
2
|
+
import { MermaidConfig } from 'mermaid';
|
|
3
|
+
|
|
4
|
+
interface ModuleOptions {
|
|
5
|
+
/**
|
|
6
|
+
* Whether to enable the entire Mermaid process
|
|
7
|
+
* @default true
|
|
8
|
+
*/
|
|
9
|
+
enabled?: boolean;
|
|
10
|
+
/**
|
|
11
|
+
* Options related to loading mermaid
|
|
12
|
+
*/
|
|
13
|
+
loader?: {
|
|
14
|
+
/**
|
|
15
|
+
* Mermaid ESM source, can be a CDN URL or local /public path
|
|
16
|
+
*/
|
|
17
|
+
importSource?: string;
|
|
18
|
+
/**
|
|
19
|
+
* Pass-through to `mermaid.initialize`
|
|
20
|
+
*/
|
|
21
|
+
init?: MermaidConfig;
|
|
22
|
+
};
|
|
23
|
+
/**
|
|
24
|
+
* Options related to theme handling
|
|
25
|
+
*/
|
|
26
|
+
theme?: {
|
|
27
|
+
/**
|
|
28
|
+
* Whether to sync Mermaid theme with @nuxtjs/color-mode
|
|
29
|
+
* @default true
|
|
30
|
+
*/
|
|
31
|
+
useColorModeTheme?: boolean;
|
|
32
|
+
light?: MermaidConfig['theme'];
|
|
33
|
+
dark?: MermaidConfig['theme'];
|
|
34
|
+
};
|
|
35
|
+
/**
|
|
36
|
+
* Names for custom implementation components
|
|
37
|
+
*/
|
|
38
|
+
components?: {
|
|
39
|
+
/**
|
|
40
|
+
* Custom Mermaid implementation component name (e.g., 'MyMermaid').
|
|
41
|
+
* The Markdown transformation phase always outputs `<Mermaid>`, this setting only affects
|
|
42
|
+
* which component implementation `Mermaid.vue` delegates to at runtime.
|
|
43
|
+
*/
|
|
44
|
+
renderer?: string;
|
|
45
|
+
/**
|
|
46
|
+
* Spinner component name to use while Mermaid is loading.
|
|
47
|
+
* This should be a globally available component name (e.g., under the project `components/` directory).
|
|
48
|
+
* If not provided, the module's built-in `Spinner.vue` will be used.
|
|
49
|
+
*/
|
|
50
|
+
spinner?: string;
|
|
51
|
+
};
|
|
52
|
+
}
|
|
53
|
+
declare const _default: _nuxt_schema.NuxtModule<ModuleOptions, ModuleOptions, false>;
|
|
54
|
+
|
|
55
|
+
export { _default as default };
|
|
56
|
+
export type { ModuleOptions };
|
package/dist/module.json
ADDED
package/dist/module.mjs
ADDED
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
import { defineNuxtModule, createResolver, addPlugin, addComponent, addTypeTemplate } from '@nuxt/kit';
|
|
2
|
+
import { defu } from 'defu';
|
|
3
|
+
|
|
4
|
+
const DEFAULT_IMPORT_SOURCE = "https://cdn.jsdelivr.net/npm/mermaid@11.12.1/dist/mermaid.esm.min.mjs";
|
|
5
|
+
const DEFAULT_LIGHT_THEME = "default";
|
|
6
|
+
const DEFAULT_DARK_THEME = "dark";
|
|
7
|
+
const DEFAULT_MERMAID_INIT = {
|
|
8
|
+
startOnLoad: false
|
|
9
|
+
};
|
|
10
|
+
|
|
11
|
+
const MERMAID_BLOCK = /```mermaid([\s\S]*?)```/gi;
|
|
12
|
+
const DEFAULTS = {
|
|
13
|
+
enabled: true,
|
|
14
|
+
loader: {
|
|
15
|
+
importSource: DEFAULT_IMPORT_SOURCE,
|
|
16
|
+
init: { ...DEFAULT_MERMAID_INIT }
|
|
17
|
+
},
|
|
18
|
+
theme: {
|
|
19
|
+
useColorModeTheme: true,
|
|
20
|
+
light: DEFAULT_LIGHT_THEME,
|
|
21
|
+
dark: DEFAULT_DARK_THEME
|
|
22
|
+
},
|
|
23
|
+
components: {
|
|
24
|
+
renderer: void 0,
|
|
25
|
+
spinner: void 0
|
|
26
|
+
}
|
|
27
|
+
};
|
|
28
|
+
const module$1 = defineNuxtModule({
|
|
29
|
+
meta: {
|
|
30
|
+
name: "@barzhsieh/nuxt-content-mermaid",
|
|
31
|
+
configKey: "mermaidContent",
|
|
32
|
+
compatibility: {
|
|
33
|
+
nuxt: "^4.0.0"
|
|
34
|
+
}
|
|
35
|
+
},
|
|
36
|
+
defaults: {
|
|
37
|
+
...DEFAULTS
|
|
38
|
+
},
|
|
39
|
+
setup(options, nuxt) {
|
|
40
|
+
const resolvedOptions = defu(options, DEFAULTS);
|
|
41
|
+
const resolver = createResolver(import.meta.url);
|
|
42
|
+
const runtimeDir = resolver.resolve("./runtime");
|
|
43
|
+
const publicRuntimeConfig = nuxt.options.runtimeConfig.public;
|
|
44
|
+
const runtimeOverrides = publicRuntimeConfig.mermaidContent || {};
|
|
45
|
+
const runtimeMermaidConfig = defu(
|
|
46
|
+
runtimeOverrides,
|
|
47
|
+
resolvedOptions
|
|
48
|
+
);
|
|
49
|
+
publicRuntimeConfig.mermaidContent = runtimeMermaidConfig;
|
|
50
|
+
const baseMermaidComponentName = "Mermaid";
|
|
51
|
+
const isEnabled = runtimeMermaidConfig.enabled !== false;
|
|
52
|
+
if (!isEnabled) return;
|
|
53
|
+
addPlugin({
|
|
54
|
+
src: resolver.resolve(runtimeDir, "plugins/mermaid.client"),
|
|
55
|
+
mode: "client"
|
|
56
|
+
});
|
|
57
|
+
addComponent({
|
|
58
|
+
name: baseMermaidComponentName,
|
|
59
|
+
filePath: resolver.resolve(runtimeDir, "components/Mermaid.vue")
|
|
60
|
+
});
|
|
61
|
+
addTypeTemplate({
|
|
62
|
+
filename: "types/mermaid-content.d.ts",
|
|
63
|
+
getContents: () => `
|
|
64
|
+
import type { Mermaid } from 'mermaid'
|
|
65
|
+
|
|
66
|
+
declare module '#app' {
|
|
67
|
+
interface NuxtApp {
|
|
68
|
+
$mermaid: () => Promise<Mermaid>
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
declare module 'vue' {
|
|
72
|
+
interface ComponentCustomProperties {
|
|
73
|
+
$mermaid: () => Promise<Mermaid>
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
export {}
|
|
77
|
+
`
|
|
78
|
+
});
|
|
79
|
+
nuxt.hook("content:file:beforeParse", (ctx) => {
|
|
80
|
+
const { file } = ctx;
|
|
81
|
+
if (!file.id?.endsWith(".md")) return;
|
|
82
|
+
if (!file.body.toLowerCase().includes("```mermaid")) return;
|
|
83
|
+
file.body = transformMermaidCodeBlocks(
|
|
84
|
+
file.body,
|
|
85
|
+
baseMermaidComponentName
|
|
86
|
+
);
|
|
87
|
+
});
|
|
88
|
+
}
|
|
89
|
+
});
|
|
90
|
+
function escapeHtml(text) {
|
|
91
|
+
return text.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">").replace(/"/g, """).replace(/'/g, "'");
|
|
92
|
+
}
|
|
93
|
+
function transformMermaidCodeBlocks(body, componentName) {
|
|
94
|
+
return body.replace(MERMAID_BLOCK, (_, rawCode = "") => {
|
|
95
|
+
const code = rawCode.trim();
|
|
96
|
+
if (!code) return _;
|
|
97
|
+
const escaped = escapeHtml(code);
|
|
98
|
+
return `<${componentName}>
|
|
99
|
+
<pre><code>${escaped}
|
|
100
|
+
</code></pre>
|
|
101
|
+
</${componentName}>`;
|
|
102
|
+
});
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
export { module$1 as default };
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
declare var __VLS_8: {}, __VLS_10: {}, __VLS_12: {}, __VLS_14: {}, __VLS_22: {};
|
|
2
|
+
type __VLS_Slots = {} & {
|
|
3
|
+
default?: (props: typeof __VLS_8) => any;
|
|
4
|
+
} & {
|
|
5
|
+
default?: (props: typeof __VLS_10) => any;
|
|
6
|
+
} & {
|
|
7
|
+
default?: (props: typeof __VLS_12) => any;
|
|
8
|
+
} & {
|
|
9
|
+
loading?: (props: typeof __VLS_14) => any;
|
|
10
|
+
} & {
|
|
11
|
+
default?: (props: typeof __VLS_22) => any;
|
|
12
|
+
};
|
|
13
|
+
declare const __VLS_base: import("vue").DefineComponent<{}, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {}, string, import("vue").PublicProps, Readonly<{}> & Readonly<{}>, {}, {}, {}, {}, string, import("vue").ComponentProvideOptions, true, {}, any>;
|
|
14
|
+
declare const __VLS_export: __VLS_WithSlots<typeof __VLS_base, __VLS_Slots>;
|
|
15
|
+
declare const _default: typeof __VLS_export;
|
|
16
|
+
export default _default;
|
|
17
|
+
type __VLS_WithSlots<T, S> = T & {
|
|
18
|
+
new (): {
|
|
19
|
+
$slots: S;
|
|
20
|
+
};
|
|
21
|
+
};
|
|
@@ -0,0 +1,241 @@
|
|
|
1
|
+
<script setup>
|
|
2
|
+
import { useNuxtApp, useRuntimeConfig } from "#app";
|
|
3
|
+
import {
|
|
4
|
+
ref,
|
|
5
|
+
onMounted,
|
|
6
|
+
onUnmounted,
|
|
7
|
+
nextTick,
|
|
8
|
+
computed,
|
|
9
|
+
watch,
|
|
10
|
+
shallowRef
|
|
11
|
+
} from "vue";
|
|
12
|
+
import Spinner from "./Spinner.vue";
|
|
13
|
+
const nuxtApp = useNuxtApp();
|
|
14
|
+
const runtimeConfig = useRuntimeConfig();
|
|
15
|
+
const mermaidRuntime = runtimeConfig.public?.mermaidContent || {};
|
|
16
|
+
const isEnabled = mermaidRuntime.enabled !== false;
|
|
17
|
+
const loaderOptions = mermaidRuntime.loader || {};
|
|
18
|
+
const themeOptions = mermaidRuntime.theme || {};
|
|
19
|
+
const componentOptions = mermaidRuntime.components || {};
|
|
20
|
+
const colorMode = nuxtApp.$colorMode;
|
|
21
|
+
const { $mermaid } = nuxtApp;
|
|
22
|
+
const mermaidContainer = ref(null);
|
|
23
|
+
const isMounted = ref(false);
|
|
24
|
+
const hasRenderedOnce = ref(false);
|
|
25
|
+
const isLoading = ref(false);
|
|
26
|
+
let mermaidDefinition = "";
|
|
27
|
+
let observer = null;
|
|
28
|
+
const baseMermaidInit = loaderOptions.init || {};
|
|
29
|
+
const useColorModeTheme = themeOptions.useColorModeTheme;
|
|
30
|
+
const lightTheme = themeOptions.light;
|
|
31
|
+
const darkTheme = themeOptions.dark;
|
|
32
|
+
const mermaidTheme = computed(() => {
|
|
33
|
+
if (useColorModeTheme && colorMode)
|
|
34
|
+
return colorMode.value === "dark" ? darkTheme : lightTheme;
|
|
35
|
+
return baseMermaidInit.theme ?? lightTheme ?? darkTheme;
|
|
36
|
+
});
|
|
37
|
+
const configuredSpinnerName = computed(
|
|
38
|
+
() => componentOptions.spinner?.trim() || ""
|
|
39
|
+
);
|
|
40
|
+
const customSpinner = shallowRef(null);
|
|
41
|
+
const configuredMermaidImplName = computed(
|
|
42
|
+
() => componentOptions.renderer?.trim() || ""
|
|
43
|
+
);
|
|
44
|
+
const customMermaidImpl = shallowRef(null);
|
|
45
|
+
function normalizeIdentifier(value) {
|
|
46
|
+
return value.replace(/\.vue$/i, "").split(/[\\/]/).pop()?.replace(/[\s_-]+/g, "").toLowerCase() || "";
|
|
47
|
+
}
|
|
48
|
+
async function resolveAppComponent(name, appComponents, label) {
|
|
49
|
+
const target = normalizeIdentifier(name);
|
|
50
|
+
const matchEntry = Object.entries(appComponents).find(([path]) => {
|
|
51
|
+
const base = path.split(/[\\/]/).pop() || "";
|
|
52
|
+
const normalized = normalizeIdentifier(base);
|
|
53
|
+
return normalized === target;
|
|
54
|
+
});
|
|
55
|
+
if (!matchEntry) {
|
|
56
|
+
console.warn(
|
|
57
|
+
`[nuxt-mermaid-content] Cannot find ${label} component:`,
|
|
58
|
+
name
|
|
59
|
+
);
|
|
60
|
+
return null;
|
|
61
|
+
}
|
|
62
|
+
try {
|
|
63
|
+
const [, loader] = matchEntry;
|
|
64
|
+
const mod = await loader();
|
|
65
|
+
return mod.default || mod;
|
|
66
|
+
} catch (error) {
|
|
67
|
+
console.error(
|
|
68
|
+
`[nuxt-mermaid-content] Failed to load ${label} component:`,
|
|
69
|
+
error
|
|
70
|
+
);
|
|
71
|
+
return null;
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
if (import.meta.client && isEnabled) {
|
|
75
|
+
const appComponents = import.meta.glob("~/components/**/*.{vue,js,ts}");
|
|
76
|
+
watch(
|
|
77
|
+
configuredSpinnerName,
|
|
78
|
+
async (name) => {
|
|
79
|
+
if (!name) {
|
|
80
|
+
customSpinner.value = null;
|
|
81
|
+
return;
|
|
82
|
+
}
|
|
83
|
+
customSpinner.value = await resolveAppComponent(
|
|
84
|
+
name,
|
|
85
|
+
appComponents,
|
|
86
|
+
"spinner"
|
|
87
|
+
);
|
|
88
|
+
},
|
|
89
|
+
{ immediate: true }
|
|
90
|
+
);
|
|
91
|
+
watch(
|
|
92
|
+
configuredMermaidImplName,
|
|
93
|
+
async (name) => {
|
|
94
|
+
if (!name) {
|
|
95
|
+
customMermaidImpl.value = null;
|
|
96
|
+
return;
|
|
97
|
+
}
|
|
98
|
+
customMermaidImpl.value = await resolveAppComponent(
|
|
99
|
+
name,
|
|
100
|
+
appComponents,
|
|
101
|
+
"mermaid"
|
|
102
|
+
);
|
|
103
|
+
},
|
|
104
|
+
{ immediate: true }
|
|
105
|
+
);
|
|
106
|
+
}
|
|
107
|
+
const spinnerComponent = computed(() => {
|
|
108
|
+
return customSpinner.value || Spinner;
|
|
109
|
+
});
|
|
110
|
+
function serializeMermaidFromNode(root) {
|
|
111
|
+
const TEXT_NODE = 3;
|
|
112
|
+
const ELEMENT_NODE = 1;
|
|
113
|
+
if (root.nodeType === TEXT_NODE) {
|
|
114
|
+
return root.data;
|
|
115
|
+
}
|
|
116
|
+
if (root.nodeType === ELEMENT_NODE) {
|
|
117
|
+
const el = root;
|
|
118
|
+
const tag = el.tagName?.toUpperCase();
|
|
119
|
+
if (tag === "BR") return "<br/>";
|
|
120
|
+
let out = "";
|
|
121
|
+
el.childNodes.forEach((child) => {
|
|
122
|
+
out += serializeMermaidFromNode(child);
|
|
123
|
+
});
|
|
124
|
+
return out;
|
|
125
|
+
}
|
|
126
|
+
return "";
|
|
127
|
+
}
|
|
128
|
+
async function renderMermaid() {
|
|
129
|
+
if (!mermaidContainer.value || !mermaidDefinition) return;
|
|
130
|
+
try {
|
|
131
|
+
isLoading.value = true;
|
|
132
|
+
const mermaid = await $mermaid();
|
|
133
|
+
const theme = mermaidTheme.value;
|
|
134
|
+
const initOptions = {
|
|
135
|
+
startOnLoad: false,
|
|
136
|
+
...baseMermaidInit,
|
|
137
|
+
theme
|
|
138
|
+
};
|
|
139
|
+
mermaid.initialize(initOptions);
|
|
140
|
+
mermaidContainer.value.removeAttribute("data-processed");
|
|
141
|
+
mermaidContainer.value.textContent = mermaidDefinition;
|
|
142
|
+
await nextTick();
|
|
143
|
+
await mermaid.run({
|
|
144
|
+
nodes: [mermaidContainer.value],
|
|
145
|
+
suppressErrors: true
|
|
146
|
+
});
|
|
147
|
+
hasRenderedOnce.value = true;
|
|
148
|
+
isLoading.value = false;
|
|
149
|
+
} catch (error) {
|
|
150
|
+
console.error("[nuxt-mermaid-content]", error);
|
|
151
|
+
if (mermaidContainer.value)
|
|
152
|
+
mermaidContainer.value.innerHTML = "\u26A0\uFE0F Mermaid Diagram Error";
|
|
153
|
+
isLoading.value = false;
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
function setupMermaidContainer() {
|
|
157
|
+
if (mermaidContainer.value) {
|
|
158
|
+
const pre = mermaidContainer.value.querySelector("pre");
|
|
159
|
+
const code = mermaidContainer.value.querySelector("code");
|
|
160
|
+
if (code) {
|
|
161
|
+
mermaidDefinition = code.textContent?.trim() || "";
|
|
162
|
+
} else if (pre) {
|
|
163
|
+
mermaidDefinition = pre.textContent?.trim() || "";
|
|
164
|
+
} else {
|
|
165
|
+
mermaidDefinition = serializeMermaidFromNode(
|
|
166
|
+
mermaidContainer.value
|
|
167
|
+
).trim();
|
|
168
|
+
}
|
|
169
|
+
observer = new IntersectionObserver(
|
|
170
|
+
(entries) => {
|
|
171
|
+
if (entries[0]?.isIntersecting && !hasRenderedOnce.value) {
|
|
172
|
+
renderMermaid();
|
|
173
|
+
if (observer) {
|
|
174
|
+
observer.disconnect();
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
},
|
|
178
|
+
{ threshold: 0.1 }
|
|
179
|
+
);
|
|
180
|
+
observer.observe(mermaidContainer.value);
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
onMounted(async () => {
|
|
184
|
+
if (!isEnabled) return;
|
|
185
|
+
isMounted.value = true;
|
|
186
|
+
await nextTick();
|
|
187
|
+
setupMermaidContainer();
|
|
188
|
+
});
|
|
189
|
+
onUnmounted(() => {
|
|
190
|
+
if (observer) observer.disconnect();
|
|
191
|
+
});
|
|
192
|
+
watch(mermaidTheme, () => {
|
|
193
|
+
if (!isEnabled) return;
|
|
194
|
+
if (hasRenderedOnce.value) renderMermaid();
|
|
195
|
+
});
|
|
196
|
+
</script>
|
|
197
|
+
|
|
198
|
+
<template>
|
|
199
|
+
<component
|
|
200
|
+
:is="customMermaidImpl"
|
|
201
|
+
v-if="customMermaidImpl"
|
|
202
|
+
>
|
|
203
|
+
<slot />
|
|
204
|
+
</component>
|
|
205
|
+
|
|
206
|
+
<div
|
|
207
|
+
v-else-if="!isEnabled"
|
|
208
|
+
class="mermaid"
|
|
209
|
+
>
|
|
210
|
+
<slot />
|
|
211
|
+
</div>
|
|
212
|
+
|
|
213
|
+
<div
|
|
214
|
+
v-else-if="isMounted"
|
|
215
|
+
class="mermaid-wrapper"
|
|
216
|
+
>
|
|
217
|
+
<div
|
|
218
|
+
ref="mermaidContainer"
|
|
219
|
+
class="mermaid"
|
|
220
|
+
>
|
|
221
|
+
<slot />
|
|
222
|
+
</div>
|
|
223
|
+
|
|
224
|
+
<template v-if="isLoading && !hasRenderedOnce">
|
|
225
|
+
<slot name="loading">
|
|
226
|
+
<component :is="spinnerComponent" />
|
|
227
|
+
</slot>
|
|
228
|
+
</template>
|
|
229
|
+
</div>
|
|
230
|
+
|
|
231
|
+
<div
|
|
232
|
+
v-else
|
|
233
|
+
class="mermaid"
|
|
234
|
+
>
|
|
235
|
+
<slot />
|
|
236
|
+
</div>
|
|
237
|
+
</template>
|
|
238
|
+
|
|
239
|
+
<style scoped>
|
|
240
|
+
.mermaid-wrapper{align-items:center;display:flex;justify-content:center;position:relative}.mermaid:not([data-processed]){color:transparent;min-height:10px}.mermaid{display:flex;justify-content:center}.mermaid .edgeLabel,.mermaid .label,.mermaid .nodeLabel{overflow:visible!important;white-space:normal!important}.mermaid foreignObject{overflow:visible!important}
|
|
241
|
+
</style>
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
declare var __VLS_8: {}, __VLS_10: {}, __VLS_12: {}, __VLS_14: {}, __VLS_22: {};
|
|
2
|
+
type __VLS_Slots = {} & {
|
|
3
|
+
default?: (props: typeof __VLS_8) => any;
|
|
4
|
+
} & {
|
|
5
|
+
default?: (props: typeof __VLS_10) => any;
|
|
6
|
+
} & {
|
|
7
|
+
default?: (props: typeof __VLS_12) => any;
|
|
8
|
+
} & {
|
|
9
|
+
loading?: (props: typeof __VLS_14) => any;
|
|
10
|
+
} & {
|
|
11
|
+
default?: (props: typeof __VLS_22) => any;
|
|
12
|
+
};
|
|
13
|
+
declare const __VLS_base: import("vue").DefineComponent<{}, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {}, string, import("vue").PublicProps, Readonly<{}> & Readonly<{}>, {}, {}, {}, {}, string, import("vue").ComponentProvideOptions, true, {}, any>;
|
|
14
|
+
declare const __VLS_export: __VLS_WithSlots<typeof __VLS_base, __VLS_Slots>;
|
|
15
|
+
declare const _default: typeof __VLS_export;
|
|
16
|
+
export default _default;
|
|
17
|
+
type __VLS_WithSlots<T, S> = T & {
|
|
18
|
+
new (): {
|
|
19
|
+
$slots: S;
|
|
20
|
+
};
|
|
21
|
+
};
|
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
declare const _default: typeof __VLS_export;
|
|
2
|
+
export default _default;
|
|
3
|
+
declare const __VLS_export: import("vue").DefineComponent<{}, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {}, string, import("vue").PublicProps, Readonly<{}> & Readonly<{}>, {}, {}, {}, {}, string, import("vue").ComponentProvideOptions, true, {}, any>;
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<svg
|
|
3
|
+
class="spinner"
|
|
4
|
+
viewBox="0 0 50 50"
|
|
5
|
+
>
|
|
6
|
+
<circle
|
|
7
|
+
class="path"
|
|
8
|
+
cx="25"
|
|
9
|
+
cy="25"
|
|
10
|
+
r="20"
|
|
11
|
+
fill="none"
|
|
12
|
+
stroke-width="5"
|
|
13
|
+
/>
|
|
14
|
+
</svg>
|
|
15
|
+
</template>
|
|
16
|
+
|
|
17
|
+
<script>
|
|
18
|
+
export default {
|
|
19
|
+
name: "Spinner"
|
|
20
|
+
};
|
|
21
|
+
</script>
|
|
22
|
+
|
|
23
|
+
<style>
|
|
24
|
+
.spinner{--radius:20px;animation:rotate 2s linear infinite;height:calc(var(--radius)*2);left:50%;margin:calc(var(--radius)*-1) 0 0 calc(var(--radius)*-1);position:absolute;top:50%;width:calc(var(--radius)*2);z-index:2}.spinner .path{animation:dash 1.5s ease-in-out infinite;stroke:#c5c5c5;stroke-linecap:round}@keyframes rotate{to{transform:rotate(1turn)}}@keyframes dash{0%{stroke-dasharray:1,150;stroke-dashoffset:0}50%{stroke-dasharray:90,150;stroke-dashoffset:-35}to{stroke-dasharray:90,150;stroke-dashoffset:-124}}
|
|
25
|
+
</style>
|
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
declare const _default: typeof __VLS_export;
|
|
2
|
+
export default _default;
|
|
3
|
+
declare const __VLS_export: import("vue").DefineComponent<{}, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {}, string, import("vue").PublicProps, Readonly<{}> & Readonly<{}>, {}, {}, {}, {}, string, import("vue").ComponentProvideOptions, true, {}, any>;
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import type { Mermaid } from 'mermaid';
|
|
2
|
+
declare global {
|
|
3
|
+
var __nuxtMermaidLoader__: Promise<Mermaid> | undefined;
|
|
4
|
+
}
|
|
5
|
+
declare const _default: import("nuxt/app").Plugin<{
|
|
6
|
+
mermaid: () => Promise<Mermaid>;
|
|
7
|
+
}> & import("nuxt/app").ObjectPlugin<{
|
|
8
|
+
mermaid: () => Promise<Mermaid>;
|
|
9
|
+
}>;
|
|
10
|
+
export default _default;
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
import { defineNuxtPlugin, useRuntimeConfig } from "#app";
|
|
2
|
+
import {
|
|
3
|
+
DEFAULT_IMPORT_SOURCE,
|
|
4
|
+
DEFAULT_MERMAID_INIT
|
|
5
|
+
} from "../../constants";
|
|
6
|
+
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
|
+
export default defineNuxtPlugin(() => {
|
|
23
|
+
const runtimeConfig = useRuntimeConfig();
|
|
24
|
+
const mermaidConfig = runtimeConfig.public?.mermaidContent;
|
|
25
|
+
if (mermaidConfig?.enabled === false) {
|
|
26
|
+
return {
|
|
27
|
+
provide: {
|
|
28
|
+
mermaid: async () => {
|
|
29
|
+
throw new Error(
|
|
30
|
+
"[nuxt-mermaid-content] Mermaid is disabled via config."
|
|
31
|
+
);
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
};
|
|
35
|
+
}
|
|
36
|
+
const loadMermaid = async () => {
|
|
37
|
+
if (globalWithLoader.__nuxtMermaidLoader__)
|
|
38
|
+
return globalWithLoader.__nuxtMermaidLoader__;
|
|
39
|
+
const importSource = mermaidConfig?.loader?.importSource || DEFAULT_IMPORT_SOURCE;
|
|
40
|
+
const initOptions = {
|
|
41
|
+
...DEFAULT_MERMAID_INIT,
|
|
42
|
+
...mermaidConfig?.loader?.init
|
|
43
|
+
};
|
|
44
|
+
globalWithLoader.__nuxtMermaidLoader__ = loadMermaidFrom(importSource).then((mermaid) => {
|
|
45
|
+
mermaid.initialize(initOptions);
|
|
46
|
+
return mermaid;
|
|
47
|
+
}).catch((error) => {
|
|
48
|
+
globalWithLoader.__nuxtMermaidLoader__ = void 0;
|
|
49
|
+
throw error;
|
|
50
|
+
});
|
|
51
|
+
return globalWithLoader.__nuxtMermaidLoader__;
|
|
52
|
+
};
|
|
53
|
+
return {
|
|
54
|
+
provide: {
|
|
55
|
+
mermaid: () => loadMermaid()
|
|
56
|
+
}
|
|
57
|
+
};
|
|
58
|
+
});
|
package/dist/types.d.mts
ADDED
package/package.json
ADDED
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@barzhsieh/nuxt-content-mermaid",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "My new Nuxt module",
|
|
5
|
+
"repository": {
|
|
6
|
+
"type": "git",
|
|
7
|
+
"url": "git+https://github.com/andy820621/nuxt-content-mermaid.git"
|
|
8
|
+
},
|
|
9
|
+
"license": "MIT",
|
|
10
|
+
"type": "module",
|
|
11
|
+
"exports": {
|
|
12
|
+
".": {
|
|
13
|
+
"types": "./dist/types.d.mts",
|
|
14
|
+
"import": "./dist/module.mjs"
|
|
15
|
+
}
|
|
16
|
+
},
|
|
17
|
+
"main": "./dist/module.mjs",
|
|
18
|
+
"typesVersions": {
|
|
19
|
+
"*": {
|
|
20
|
+
".": [
|
|
21
|
+
"./dist/types.d.mts"
|
|
22
|
+
]
|
|
23
|
+
}
|
|
24
|
+
},
|
|
25
|
+
"files": [
|
|
26
|
+
"dist"
|
|
27
|
+
],
|
|
28
|
+
"dependencies": {
|
|
29
|
+
"@nuxt/kit": "^4.2.1",
|
|
30
|
+
"defu": "^6.1.4"
|
|
31
|
+
},
|
|
32
|
+
"peerDependencies": {
|
|
33
|
+
"@nuxt/content": "^3.8.0"
|
|
34
|
+
},
|
|
35
|
+
"devDependencies": {
|
|
36
|
+
"@nuxt/content": "3.8.0",
|
|
37
|
+
"@nuxt/devtools": "^3.1.0",
|
|
38
|
+
"@nuxt/eslint-config": "^1.10.0",
|
|
39
|
+
"@nuxt/module-builder": "^1.0.2",
|
|
40
|
+
"@nuxt/schema": "^4.2.1",
|
|
41
|
+
"@nuxt/test-utils": "^3.20.1",
|
|
42
|
+
"@nuxtjs/color-mode": "^4.0.0",
|
|
43
|
+
"@types/node": "latest",
|
|
44
|
+
"changelogen": "^0.6.2",
|
|
45
|
+
"eslint": "^9.39.1",
|
|
46
|
+
"mermaid": "^11.12.1",
|
|
47
|
+
"nuxt": "^4.2.1",
|
|
48
|
+
"typescript": "~5.9.3",
|
|
49
|
+
"vitest": "^4.0.10",
|
|
50
|
+
"vue-tsc": "^3.1.4"
|
|
51
|
+
},
|
|
52
|
+
"volta": {
|
|
53
|
+
"node": "22.21.1"
|
|
54
|
+
},
|
|
55
|
+
"scripts": {
|
|
56
|
+
"dev": "npm run dev:prepare && nuxi dev playground",
|
|
57
|
+
"dev:build": "nuxi build playground",
|
|
58
|
+
"dev:prepare": "nuxt-module-build build --stub && nuxt-module-build prepare && nuxi prepare playground",
|
|
59
|
+
"release": "npm run lint && npm run test && npm run prepack && changelogen --release && npm publish && git push --follow-tags",
|
|
60
|
+
"lint": "eslint .",
|
|
61
|
+
"test": "vitest run",
|
|
62
|
+
"test:watch": "vitest watch",
|
|
63
|
+
"test:types": "vue-tsc --noEmit && cd playground && vue-tsc --noEmit"
|
|
64
|
+
}
|
|
65
|
+
}
|