@v1hz/md2docx 2.3.0 → 2.5.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 -48
- package/config/config.json +5 -0
- package/config/config.schema.json +26 -0
- package/config/lua/limit-image-size.lua +93 -0
- package/config/style-config.json +23 -3
- package/config/style-config.schema.json +56 -4
- package/dist/index.js +343 -107
- package/package.json +1 -1
- /package/config/{style.json → style-raw.json} +0 -0
package/README.md
CHANGED
|
@@ -23,6 +23,7 @@
|
|
|
23
23
|
| 标题层级归一化 | 将最浅标题归一化为 H1,并修复标题层级跳跃 |
|
|
24
24
|
| 标题编号 | 生成 `1`、`1.1`、`1.1.1` 等编号,并可剥离常见的已有中英文编号 |
|
|
25
25
|
| 表格与图片编号 | 自动生成“表 1”“图 1:标题”等题注 |
|
|
26
|
+
| 图片尺寸限制 | 按自然尺寸等比缩小超过最大宽度或高度的图片 |
|
|
26
27
|
| Mermaid 图表 | 使用 beautiful-mermaid 和 resvg-wasm 将 Mermaid 渲染为高 DPI PNG |
|
|
27
28
|
| Word 样式 | 支持受控语义化配置和完整底层样式,也可从现有 DOCX 提取样式 |
|
|
28
29
|
| Markdown 格式化 | 可只运行预处理流水线,输出格式化后的 Markdown |
|
|
@@ -37,7 +38,7 @@
|
|
|
37
38
|
前置依赖:
|
|
38
39
|
|
|
39
40
|
- [Node.js 22.12+](https://nodejs.org/)
|
|
40
|
-
- [Pandoc](https://pandoc.org/installing.html),并确保 `pandoc` 可通过 `PATH`
|
|
41
|
+
- [Pandoc](https://pandoc.org/installing.html),并确保 `pandoc` 可通过 `PATH` 调用;图片尺寸限制需要 Pandoc 3.1.13+
|
|
41
42
|
|
|
42
43
|
```bash
|
|
43
44
|
npm install -g @v1hz/md2docx
|
|
@@ -58,26 +59,27 @@ npx @v1hz/md2docx report.md
|
|
|
58
59
|
md2docx report.md
|
|
59
60
|
|
|
60
61
|
# 使用任何转换选项时,输入必须通过 --file 指定
|
|
61
|
-
md2docx --file report.md --output output/report.docx
|
|
62
|
+
md2docx --file report.md --output output/report.docx
|
|
62
63
|
|
|
63
|
-
#
|
|
64
|
-
md2docx -f report.md -c config.json -
|
|
64
|
+
# 使用自定义配置、底层样式和语义化样式配置
|
|
65
|
+
md2docx -f report.md -c config.json --style-raw style-raw.json --style-config style-config.json
|
|
65
66
|
|
|
66
67
|
# 只执行 Markdown 预处理
|
|
67
68
|
md2docx format -f report.md
|
|
68
69
|
|
|
69
|
-
#
|
|
70
|
+
# 导出内置默认配置、底层样式和语义化样式配置
|
|
70
71
|
md2docx export config
|
|
71
|
-
md2docx export style
|
|
72
|
+
md2docx export style-raw
|
|
73
|
+
md2docx export style-config
|
|
72
74
|
|
|
73
75
|
# 从现有 DOCX 提取样式
|
|
74
|
-
md2docx export style -f template.docx
|
|
76
|
+
md2docx export style-raw -f template.docx
|
|
75
77
|
|
|
76
78
|
# 删除 ~/.md2docx 中的中间文件和缓存
|
|
77
79
|
md2docx clean
|
|
78
80
|
```
|
|
79
81
|
|
|
80
|
-
|
|
82
|
+
所有写文件命令默认覆盖已有输出。
|
|
81
83
|
|
|
82
84
|
## CLI 参考
|
|
83
85
|
|
|
@@ -86,29 +88,30 @@ md2docx <markdown>
|
|
|
86
88
|
md2docx -f <markdown> [转换选项]
|
|
87
89
|
md2docx format -f <markdown> [选项]
|
|
88
90
|
md2docx export config [选项]
|
|
89
|
-
md2docx export style [选项]
|
|
91
|
+
md2docx export style-raw [选项]
|
|
92
|
+
md2docx export style-config [选项]
|
|
90
93
|
md2docx clean
|
|
91
94
|
```
|
|
92
95
|
|
|
93
96
|
### 转换
|
|
94
97
|
|
|
95
|
-
| 参数
|
|
96
|
-
|
|
|
97
|
-
| `<markdown>`
|
|
98
|
-
| `-f, --file <path>`
|
|
99
|
-
| `-c, --config <path>`
|
|
100
|
-
|
|
|
101
|
-
|
|
|
102
|
-
|
|
|
103
|
-
| `-h, --help`
|
|
104
|
-
| `-v, --version`
|
|
98
|
+
| 参数 | 说明 |
|
|
99
|
+
| ----------------------- | ---------------------------------------- |
|
|
100
|
+
| `<markdown>` | 位置参数;仅在没有其他转换选项时允许使用 |
|
|
101
|
+
| `-f, --file <path>` | Markdown 输入文件 |
|
|
102
|
+
| `-c, --config <path>` | 自定义配置 JSON |
|
|
103
|
+
| `--style-raw <path>` | 完整底层 Word 样式 JSON |
|
|
104
|
+
| `--style-config <path>` | 受控语义化样式配置 JSON |
|
|
105
|
+
| `-o, --output <path>` | DOCX 输出路径 |
|
|
106
|
+
| `-h, --help` | 显示帮助 |
|
|
107
|
+
| `-v, --version` | 显示版本号 |
|
|
105
108
|
|
|
106
|
-
位置参数不能和 `--file`、`--config`、`--style`、`--
|
|
109
|
+
位置参数不能和 `--file`、`--config`、`--style-raw`、`--style-config` 或 `--output` 混用。例如:
|
|
107
110
|
|
|
108
111
|
```bash
|
|
109
112
|
md2docx report.md # 正确
|
|
110
|
-
md2docx report.md
|
|
111
|
-
md2docx -f report.md
|
|
113
|
+
md2docx report.md -o report.docx # 错误
|
|
114
|
+
md2docx -f report.md -o report.docx # 正确
|
|
112
115
|
```
|
|
113
116
|
|
|
114
117
|
### format
|
|
@@ -120,16 +123,16 @@ md2docx -f report.md --force # 正确
|
|
|
120
123
|
| `-f, --file <path>` | 必填,Markdown 输入文件 |
|
|
121
124
|
| `-c, --config <path>` | 自定义配置 JSON |
|
|
122
125
|
| `-o, --output <path>` | 输出 Markdown,默认 `<文件名>_formatted.md` |
|
|
123
|
-
| `--force` | 覆盖已有输出 |
|
|
124
126
|
|
|
125
127
|
### export
|
|
126
128
|
|
|
127
129
|
```bash
|
|
128
|
-
md2docx export config [-o config.json]
|
|
129
|
-
md2docx export style [-f template.docx] [-o style.json]
|
|
130
|
+
md2docx export config [-o config.json]
|
|
131
|
+
md2docx export style-raw [-f template.docx] [-o style-raw.json]
|
|
132
|
+
md2docx export style-config [-o style-config.json]
|
|
130
133
|
```
|
|
131
134
|
|
|
132
|
-
`export config`
|
|
135
|
+
`export config` 导出内置 Markdown 处理配置。`export style-raw` 不带 `--file` 时导出内置底层 Word 样式;指定 DOCX 时从该文档提取底层样式。`export style-config` 导出默认语义化样式配置。
|
|
133
136
|
|
|
134
137
|
### clean
|
|
135
138
|
|
|
@@ -153,8 +156,9 @@ md2docx report.md → ./report.docx
|
|
|
153
156
|
md2docx -f report.md → ./report.docx
|
|
154
157
|
md2docx format -f report.md → ./report_formatted.md
|
|
155
158
|
md2docx export config → ./config.json
|
|
156
|
-
md2docx export style → ./style.json
|
|
157
|
-
md2docx export style -f template.docx → ./template_style.json
|
|
159
|
+
md2docx export style-raw → ./style-raw.json
|
|
160
|
+
md2docx export style-raw -f template.docx → ./template_style-raw.json
|
|
161
|
+
md2docx export style-config → ./style-config.json
|
|
158
162
|
```
|
|
159
163
|
|
|
160
164
|
## 中间文件与缓存
|
|
@@ -170,8 +174,9 @@ md2docx export style -f template.docx → ./template_style.json
|
|
|
170
174
|
├── resources/
|
|
171
175
|
│ ├── config.json
|
|
172
176
|
│ ├── style-config.json
|
|
173
|
-
│ ├── style.json
|
|
174
|
-
│
|
|
177
|
+
│ ├── style-raw.json
|
|
178
|
+
│ ├── add-inline-code.lua
|
|
179
|
+
│ └── limit-image-size.lua
|
|
175
180
|
└── style/
|
|
176
181
|
└── <样式内容哈希>.docx
|
|
177
182
|
```
|
|
@@ -210,26 +215,51 @@ CLI 不支持覆盖单个配置项,所有配置都通过 JSON 文件管理。
|
|
|
210
215
|
| `renderMermaid.enabled` | 将 Mermaid 渲染为 PNG | `true` |
|
|
211
216
|
| `renderMermaid.theme` | beautiful-mermaid 主题 | `"tokyo-night-light"` |
|
|
212
217
|
| `renderMermaid.density` | PNG 输出 DPI,最小值 72 | `200` |
|
|
218
|
+
| `imageSize.enabled` | 等比缩小超过尺寸限制的图片 | `true` |
|
|
219
|
+
| `imageSize.maxWidthCm` | DOCX 图片最大宽度(厘米) | `12` |
|
|
220
|
+
| `imageSize.maxHeightCm` | DOCX 图片最大高度(厘米) | `12` |
|
|
213
221
|
| `removeThematicBreaks.enabled` | 移除 `---`、`***`、`___` 等分隔符行 | `true` |
|
|
214
222
|
|
|
223
|
+
图片尺寸限制在 Pandoc 生成 DOCX 前通过 Lua filter 应用。程序读取图片像素尺寸和 DPI,仅缩小超限图片,不会放大小图;宽度和高度使用同一缩放比例。Markdown 中已经显式设置 `width` 或 `height` 的图片视为用户覆盖,不应用全局限制。单张图片无法读取尺寸时会输出警告并继续转换。
|
|
224
|
+
|
|
215
225
|
## 样式定制
|
|
216
226
|
|
|
217
227
|
普通用户推荐使用受控的语义化样式配置,只修改程序明确开放的高频选项。其余颜色、尺寸、对齐方式、间距和 Word 样式继承关系继续由内置预设管理。
|
|
218
228
|
|
|
219
|
-
仓库中的 `config/style-config.json` 是可直接复制和修改的默认配置,`config/style-config.schema.json`
|
|
229
|
+
仓库中的 `config/style-config.json` 是可直接复制和修改的默认配置,`config/style-config.schema.json` 用于编辑器提示和校验。完整底层 Word 样式位于 `config/style-raw.json`。
|
|
220
230
|
|
|
221
231
|
```json
|
|
222
232
|
{
|
|
223
233
|
"$schema": "https://raw.githubusercontent.com/WXY-V1hZ/md2docx/main/config/style-config.schema.json",
|
|
224
234
|
"schemaVersion": 1,
|
|
225
|
-
"preset": "default",
|
|
226
235
|
"options": {
|
|
227
236
|
"body": {
|
|
228
|
-
"firstLineIndent": false
|
|
237
|
+
"firstLineIndent": false,
|
|
238
|
+
"lineSpacing": "onePointFive"
|
|
229
239
|
},
|
|
230
240
|
"headings": {
|
|
231
241
|
"1": {
|
|
232
|
-
"startOnNewPage": false
|
|
242
|
+
"startOnNewPage": false,
|
|
243
|
+
"alignment": "left",
|
|
244
|
+
"bold": true
|
|
245
|
+
},
|
|
246
|
+
"2": {
|
|
247
|
+
"bold": true
|
|
248
|
+
},
|
|
249
|
+
"3": {
|
|
250
|
+
"bold": true
|
|
251
|
+
},
|
|
252
|
+
"4": {
|
|
253
|
+
"bold": true,
|
|
254
|
+
"italic": false
|
|
255
|
+
},
|
|
256
|
+
"5": {
|
|
257
|
+
"bold": true,
|
|
258
|
+
"italic": false
|
|
259
|
+
},
|
|
260
|
+
"6": {
|
|
261
|
+
"bold": true,
|
|
262
|
+
"italic": false
|
|
233
263
|
}
|
|
234
264
|
},
|
|
235
265
|
"inlineCode": {
|
|
@@ -245,18 +275,27 @@ CLI 不支持覆盖单个配置项,所有配置都通过 JSON 文件管理。
|
|
|
245
275
|
将配置保存为 `style-config.json` 后使用:
|
|
246
276
|
|
|
247
277
|
```bash
|
|
248
|
-
md2docx -f report.md -
|
|
278
|
+
md2docx -f report.md --style-config style-config.json
|
|
249
279
|
```
|
|
250
280
|
|
|
251
|
-
|
|
281
|
+
字段缺失表示继承底层样式;`true` 表示显式启用完整效果;`false` 表示显式关闭。正文行距为浮点数倍数,例如 `1.5` 表示 1.5 倍行距;一级标题对齐只接受 `left`、`center`。当前还开放一至六级标题粗体,以及四至六级标题斜体。完整设计见 [`docs/style-config-design.md`](docs/style-config-design.md)。
|
|
282
|
+
|
|
283
|
+
转换时按以下规则选择输入:
|
|
252
284
|
|
|
253
|
-
|
|
285
|
+
| 参数 | 行为 |
|
|
286
|
+
| ------------------- | ----------------------------------- |
|
|
287
|
+
| 都不指定 | 默认 raw + 默认 config |
|
|
288
|
+
| 仅 `--style-raw` | 直接使用用户 raw,不应用默认 config |
|
|
289
|
+
| 仅 `--style-config` | 用户 config 应用到默认 raw |
|
|
290
|
+
| 两者都指定 | 用户 config 应用到用户 raw |
|
|
291
|
+
|
|
292
|
+
最终有效的底层样式用于生成 reference DOCX,并通过 Pandoc 的 `--reference-doc` 应用。`--style-raw` 和 `--style-config` 类型固定,不能互换。
|
|
254
293
|
|
|
255
294
|
### 从现有 DOCX 提取
|
|
256
295
|
|
|
257
296
|
```bash
|
|
258
|
-
md2docx export style -f template.docx
|
|
259
|
-
md2docx -f report.md -
|
|
297
|
+
md2docx export style-raw -f template.docx
|
|
298
|
+
md2docx -f report.md --style-raw template_style-raw.json
|
|
260
299
|
```
|
|
261
300
|
|
|
262
301
|
### 高级手动维护
|
|
@@ -323,7 +362,7 @@ numberPictures()
|
|
|
323
362
|
↓
|
|
324
363
|
生成或复用 reference DOCX
|
|
325
364
|
↓
|
|
326
|
-
Pandoc + Lua filter
|
|
365
|
+
Pandoc + 行内代码/图片尺寸 Lua filter
|
|
327
366
|
↓
|
|
328
367
|
DOCX
|
|
329
368
|
```
|
|
@@ -386,14 +425,6 @@ pandoc --version
|
|
|
386
425
|
|
|
387
426
|
如果命令不存在,请从 [Pandoc 官方安装页](https://pandoc.org/installing.html) 安装,并重新打开终端使 `PATH` 生效。
|
|
388
427
|
|
|
389
|
-
### 输出文件已存在
|
|
390
|
-
|
|
391
|
-
默认不会覆盖文件。确认目标可以覆盖后使用:
|
|
392
|
-
|
|
393
|
-
```bash
|
|
394
|
-
md2docx -f report.md --force
|
|
395
|
-
```
|
|
396
|
-
|
|
397
428
|
### 从其他目录转换时图片缺失
|
|
398
429
|
|
|
399
430
|
相对图片路径应以原始 Markdown 文件为基准:
|
package/config/config.json
CHANGED
|
@@ -117,6 +117,31 @@
|
|
|
117
117
|
},
|
|
118
118
|
"required": ["enabled", "theme", "density"]
|
|
119
119
|
},
|
|
120
|
+
"imageSize": {
|
|
121
|
+
"description": "DOCX 图片尺寸限制",
|
|
122
|
+
"type": "object",
|
|
123
|
+
"additionalProperties": false,
|
|
124
|
+
"properties": {
|
|
125
|
+
"enabled": {
|
|
126
|
+
"description": "按图片自然尺寸等比缩小超限图片",
|
|
127
|
+
"type": "boolean",
|
|
128
|
+
"default": true
|
|
129
|
+
},
|
|
130
|
+
"maxWidthCm": {
|
|
131
|
+
"description": "图片最大宽度(厘米)",
|
|
132
|
+
"type": "number",
|
|
133
|
+
"exclusiveMinimum": 0,
|
|
134
|
+
"default": 12
|
|
135
|
+
},
|
|
136
|
+
"maxHeightCm": {
|
|
137
|
+
"description": "图片最大高度(厘米)",
|
|
138
|
+
"type": "number",
|
|
139
|
+
"exclusiveMinimum": 0,
|
|
140
|
+
"default": 12
|
|
141
|
+
}
|
|
142
|
+
},
|
|
143
|
+
"required": ["enabled", "maxWidthCm", "maxHeightCm"]
|
|
144
|
+
},
|
|
120
145
|
"detectTitle": {
|
|
121
146
|
"description": "文档标题",
|
|
122
147
|
"type": "object",
|
|
@@ -162,6 +187,7 @@
|
|
|
162
187
|
"normalizeHeadings",
|
|
163
188
|
"numberHeadings",
|
|
164
189
|
"renderMermaid",
|
|
190
|
+
"imageSize",
|
|
165
191
|
"detectTitle",
|
|
166
192
|
"removeThematicBreaks"
|
|
167
193
|
]
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
local max_width_cm
|
|
2
|
+
local max_height_cm
|
|
3
|
+
local size_cache = {}
|
|
4
|
+
local warned = {}
|
|
5
|
+
|
|
6
|
+
local function warn_once(source, message)
|
|
7
|
+
if warned[source] then
|
|
8
|
+
return
|
|
9
|
+
end
|
|
10
|
+
warned[source] = true
|
|
11
|
+
local text = "无法限制图片尺寸 " .. source .. ":" .. message
|
|
12
|
+
if pandoc.log and pandoc.log.warn then
|
|
13
|
+
pandoc.log.warn(text)
|
|
14
|
+
else
|
|
15
|
+
io.stderr:write("[WARNING] " .. text .. "\n")
|
|
16
|
+
end
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
local function metadata_number(meta, key)
|
|
20
|
+
local value = meta[key]
|
|
21
|
+
if value == nil then
|
|
22
|
+
return nil
|
|
23
|
+
end
|
|
24
|
+
return tonumber(pandoc.utils.stringify(value))
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
local function read_config(meta)
|
|
28
|
+
max_width_cm = metadata_number(meta, "md2docx-image-max-width-cm")
|
|
29
|
+
max_height_cm = metadata_number(meta, "md2docx-image-max-height-cm")
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
local function image_size(source)
|
|
33
|
+
local cached = size_cache[source]
|
|
34
|
+
if cached ~= nil then
|
|
35
|
+
return cached or nil
|
|
36
|
+
end
|
|
37
|
+
if pandoc.image == nil or pandoc.image.size == nil then
|
|
38
|
+
warn_once(source, "当前 Pandoc 不支持 pandoc.image.size,请升级到 3.1.13 或更高版本")
|
|
39
|
+
size_cache[source] = false
|
|
40
|
+
return nil
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
local fetched, _, contents = pcall(pandoc.mediabag.fetch, source)
|
|
44
|
+
if not fetched or contents == nil then
|
|
45
|
+
warn_once(source, fetched and "未读取到图片内容" or tostring(_))
|
|
46
|
+
size_cache[source] = false
|
|
47
|
+
return nil
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
local measured, size = pcall(pandoc.image.size, contents, PANDOC_WRITER_OPTIONS)
|
|
51
|
+
if not measured then
|
|
52
|
+
warn_once(source, tostring(size))
|
|
53
|
+
size_cache[source] = false
|
|
54
|
+
return nil
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
size_cache[source] = size
|
|
58
|
+
return size
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
local function limit_image(image)
|
|
62
|
+
if max_width_cm == nil or max_height_cm == nil then
|
|
63
|
+
return nil
|
|
64
|
+
end
|
|
65
|
+
if image.attributes.width ~= nil or image.attributes.height ~= nil then
|
|
66
|
+
return nil
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
local size = image_size(image.src)
|
|
70
|
+
if size == nil then
|
|
71
|
+
return nil
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
local fallback_dpi = PANDOC_WRITER_OPTIONS.dpi or 96
|
|
75
|
+
local horizontal_dpi = size.dpi_horz > 0 and size.dpi_horz or fallback_dpi
|
|
76
|
+
local vertical_dpi = size.dpi_vert > 0 and size.dpi_vert or fallback_dpi
|
|
77
|
+
local width_cm = size.width / horizontal_dpi * 2.54
|
|
78
|
+
local height_cm = size.height / vertical_dpi * 2.54
|
|
79
|
+
local scale = math.min(1, max_width_cm / width_cm, max_height_cm / height_cm)
|
|
80
|
+
|
|
81
|
+
if scale >= 1 then
|
|
82
|
+
return nil
|
|
83
|
+
end
|
|
84
|
+
|
|
85
|
+
image.attributes.width = string.format("%.4fcm", width_cm * scale)
|
|
86
|
+
image.attributes.height = string.format("%.4fcm", height_cm * scale)
|
|
87
|
+
return image
|
|
88
|
+
end
|
|
89
|
+
|
|
90
|
+
return {
|
|
91
|
+
{ Meta = read_config },
|
|
92
|
+
{ Image = limit_image },
|
|
93
|
+
}
|
package/config/style-config.json
CHANGED
|
@@ -1,14 +1,34 @@
|
|
|
1
1
|
{
|
|
2
2
|
"$schema": "https://raw.githubusercontent.com/WXY-V1hZ/md2docx/main/config/style-config.schema.json",
|
|
3
3
|
"schemaVersion": 1,
|
|
4
|
-
"preset": "default",
|
|
5
4
|
"options": {
|
|
6
5
|
"body": {
|
|
7
|
-
"firstLineIndent": true
|
|
6
|
+
"firstLineIndent": true,
|
|
7
|
+
"lineSpacing": 1.5
|
|
8
8
|
},
|
|
9
9
|
"headings": {
|
|
10
10
|
"1": {
|
|
11
|
-
"startOnNewPage": false
|
|
11
|
+
"startOnNewPage": false,
|
|
12
|
+
"alignment": "left",
|
|
13
|
+
"bold": true
|
|
14
|
+
},
|
|
15
|
+
"2": {
|
|
16
|
+
"bold": true
|
|
17
|
+
},
|
|
18
|
+
"3": {
|
|
19
|
+
"bold": true
|
|
20
|
+
},
|
|
21
|
+
"4": {
|
|
22
|
+
"bold": true,
|
|
23
|
+
"italic": false
|
|
24
|
+
},
|
|
25
|
+
"5": {
|
|
26
|
+
"bold": true,
|
|
27
|
+
"italic": false
|
|
28
|
+
},
|
|
29
|
+
"6": {
|
|
30
|
+
"bold": true,
|
|
31
|
+
"italic": false
|
|
12
32
|
}
|
|
13
33
|
},
|
|
14
34
|
"inlineCode": {
|
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
"title": "md2docx 语义化样式配置",
|
|
5
5
|
"type": "object",
|
|
6
6
|
"additionalProperties": false,
|
|
7
|
-
"required": ["schemaVersion"
|
|
7
|
+
"required": ["schemaVersion"],
|
|
8
8
|
"properties": {
|
|
9
9
|
"$schema": {
|
|
10
10
|
"type": "string"
|
|
@@ -12,9 +12,6 @@
|
|
|
12
12
|
"schemaVersion": {
|
|
13
13
|
"const": 1
|
|
14
14
|
},
|
|
15
|
-
"preset": {
|
|
16
|
-
"const": "default"
|
|
17
|
-
},
|
|
18
15
|
"options": {
|
|
19
16
|
"type": "object",
|
|
20
17
|
"additionalProperties": false,
|
|
@@ -26,6 +23,11 @@
|
|
|
26
23
|
"firstLineIndent": {
|
|
27
24
|
"type": "boolean",
|
|
28
25
|
"description": "正文是否使用预设的首行缩进"
|
|
26
|
+
},
|
|
27
|
+
"lineSpacing": {
|
|
28
|
+
"type": "number",
|
|
29
|
+
"exclusiveMinimum": 0,
|
|
30
|
+
"description": "正文行距倍数,如 1.5 表示 1.5 倍行距"
|
|
29
31
|
}
|
|
30
32
|
}
|
|
31
33
|
},
|
|
@@ -40,8 +42,32 @@
|
|
|
40
42
|
"startOnNewPage": {
|
|
41
43
|
"type": "boolean",
|
|
42
44
|
"description": "一级标题是否另起一页"
|
|
45
|
+
},
|
|
46
|
+
"alignment": {
|
|
47
|
+
"type": "string",
|
|
48
|
+
"enum": ["left", "center"],
|
|
49
|
+
"description": "一级标题左对齐或居中"
|
|
50
|
+
},
|
|
51
|
+
"bold": {
|
|
52
|
+
"type": "boolean",
|
|
53
|
+
"description": "一级标题是否加粗"
|
|
43
54
|
}
|
|
44
55
|
}
|
|
56
|
+
},
|
|
57
|
+
"2": {
|
|
58
|
+
"$ref": "#/$defs/headingBold"
|
|
59
|
+
},
|
|
60
|
+
"3": {
|
|
61
|
+
"$ref": "#/$defs/headingBold"
|
|
62
|
+
},
|
|
63
|
+
"4": {
|
|
64
|
+
"$ref": "#/$defs/headingBoldItalic"
|
|
65
|
+
},
|
|
66
|
+
"5": {
|
|
67
|
+
"$ref": "#/$defs/headingBoldItalic"
|
|
68
|
+
},
|
|
69
|
+
"6": {
|
|
70
|
+
"$ref": "#/$defs/headingBoldItalic"
|
|
45
71
|
}
|
|
46
72
|
}
|
|
47
73
|
},
|
|
@@ -67,5 +93,31 @@
|
|
|
67
93
|
}
|
|
68
94
|
}
|
|
69
95
|
}
|
|
96
|
+
},
|
|
97
|
+
"$defs": {
|
|
98
|
+
"headingBold": {
|
|
99
|
+
"type": "object",
|
|
100
|
+
"additionalProperties": false,
|
|
101
|
+
"properties": {
|
|
102
|
+
"bold": {
|
|
103
|
+
"type": "boolean",
|
|
104
|
+
"description": "标题是否加粗"
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
},
|
|
108
|
+
"headingBoldItalic": {
|
|
109
|
+
"type": "object",
|
|
110
|
+
"additionalProperties": false,
|
|
111
|
+
"properties": {
|
|
112
|
+
"bold": {
|
|
113
|
+
"type": "boolean",
|
|
114
|
+
"description": "标题是否加粗"
|
|
115
|
+
},
|
|
116
|
+
"italic": {
|
|
117
|
+
"type": "boolean",
|
|
118
|
+
"description": "标题是否斜体"
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
}
|
|
70
122
|
}
|
|
71
123
|
}
|
package/dist/index.js
CHANGED
|
@@ -128401,7 +128401,7 @@ import { fileURLToPath as fileURLToPath3 } from "node:url";
|
|
|
128401
128401
|
// package.json
|
|
128402
128402
|
var package_default = {
|
|
128403
128403
|
name: "@v1hz/md2docx",
|
|
128404
|
-
version: "2.
|
|
128404
|
+
version: "2.5.0",
|
|
128405
128405
|
description: "基于 pandoc 的 Markdown 转 Word 工具,自动格式化 Markdown,支持自定义样式,AI 友好,用户友好。",
|
|
128406
128406
|
keywords: [
|
|
128407
128407
|
"converter",
|
|
@@ -128471,8 +128471,8 @@ var package_default = {
|
|
|
128471
128471
|
// src/cli.ts
|
|
128472
128472
|
function createProgram(version, actions) {
|
|
128473
128473
|
const program2 = new Command;
|
|
128474
|
-
program2.name("md2docx").description("将 Markdown 转换为 Word 文档").version(version, "-v, --version", "显示版本号").helpOption("-h, --help", "显示帮助").helpCommand(false).showHelpAfterError("使用 --help 查看帮助").showSuggestionAfterError().enablePositionalOptions().argument("[markdown]", "Markdown 文件(仅无其他转换选项时)").option("-f, --file <path>", "Markdown 文件").option("-c, --config <path>", "自定义配置文件").option("-
|
|
128475
|
-
const hasAdditionalOptions = options.config !== undefined || options.
|
|
128474
|
+
program2.name("md2docx").description("将 Markdown 转换为 Word 文档").version(version, "-v, --version", "显示版本号").helpOption("-h, --help", "显示帮助").helpCommand(false).showHelpAfterError("使用 --help 查看帮助").showSuggestionAfterError().enablePositionalOptions().argument("[markdown]", "Markdown 文件(仅无其他转换选项时)").option("-f, --file <path>", "Markdown 文件").option("-c, --config <path>", "自定义配置文件").option("--style-raw <path>", "自定义底层 Word 样式").option("--style-config <path>", "自定义语义化样式配置").option("-o, --output <path>", "输出 DOCX 文件").action(async function(markdown, options) {
|
|
128475
|
+
const hasAdditionalOptions = options.config !== undefined || options.styleRaw !== undefined || options.styleConfig !== undefined || options.output !== undefined;
|
|
128476
128476
|
if (markdown !== undefined) {
|
|
128477
128477
|
if (options.file !== undefined || hasAdditionalOptions) {
|
|
128478
128478
|
this.error("位置参数不能与转换选项同时使用;使用选项时请通过 -f, --file 指定 Markdown 文件");
|
|
@@ -128493,11 +128493,13 @@ function createProgram(version, actions) {
|
|
|
128493
128493
|
this.help({ error: true });
|
|
128494
128494
|
});
|
|
128495
128495
|
exportCommand.exitOverride();
|
|
128496
|
-
const exportConfigCommand = exportCommand.command("config").description("导出默认配置").helpOption("-h, --help", "显示帮助").option("-o, --output <path>", "输出 JSON 文件,默认 ./config.json").
|
|
128496
|
+
const exportConfigCommand = exportCommand.command("config").description("导出默认配置").helpOption("-h, --help", "显示帮助").option("-o, --output <path>", "输出 JSON 文件,默认 ./config.json").action(actions.exportConfig);
|
|
128497
128497
|
exportConfigCommand.exitOverride();
|
|
128498
|
-
const
|
|
128499
|
-
|
|
128500
|
-
const
|
|
128498
|
+
const exportStyleRawCommand = exportCommand.command("style-raw").description("导出默认底层 Word 样式,或从 DOCX 提取底层样式").helpOption("-h, --help", "显示帮助").option("-f, --file <path>", "用于提取底层样式的 DOCX 文件").option("-o, --output <path>", "输出 JSON 文件").action(actions.exportStyleRaw);
|
|
128499
|
+
exportStyleRawCommand.exitOverride();
|
|
128500
|
+
const exportStyleConfigCommand = exportCommand.command("style-config").description("导出默认语义化样式配置").helpOption("-h, --help", "显示帮助").option("-o, --output <path>", "输出 JSON 文件,默认 ./style-config.json").action(actions.exportStyleConfig);
|
|
128501
|
+
exportStyleConfigCommand.exitOverride();
|
|
128502
|
+
const formatCommand = program2.command("format").description("预处理并格式化 Markdown").helpOption("-h, --help", "显示帮助").requiredOption("-f, --file <path>", "Markdown 文件").option("-c, --config <path>", "自定义配置文件").option("-o, --output <path>", "输出 Markdown 文件").action(actions.format);
|
|
128501
128503
|
formatCommand.exitOverride();
|
|
128502
128504
|
const cleanCommand = program2.command("clean").description("清除 ~/.md2docx 中的预处理文件和样式缓存").helpOption("-h, --help", "显示帮助").action(async () => {
|
|
128503
128505
|
await actions.clean();
|
|
@@ -128595,6 +128597,10 @@ function validateConfig(value, path2) {
|
|
|
128595
128597
|
if (!Number.isInteger(mermaid.density) || mermaid.density < 72) {
|
|
128596
128598
|
invalidConfig(path2, "renderMermaid.density", "必须是不小于 72 的整数");
|
|
128597
128599
|
}
|
|
128600
|
+
const imageSize = expectRecord(root.imageSize, path2, "imageSize");
|
|
128601
|
+
expectBoolean(imageSize.enabled, path2, "imageSize.enabled");
|
|
128602
|
+
expectPositiveNumber(imageSize.maxWidthCm, path2, "imageSize.maxWidthCm");
|
|
128603
|
+
expectPositiveNumber(imageSize.maxHeightCm, path2, "imageSize.maxHeightCm");
|
|
128598
128604
|
validateEnabled(root.removeThematicBreaks, path2, "removeThematicBreaks");
|
|
128599
128605
|
const title = expectRecord(root.detectTitle, path2, "detectTitle");
|
|
128600
128606
|
expectBoolean(title.enabled, path2, "detectTitle.enabled");
|
|
@@ -128629,6 +128635,12 @@ function expectString(value, path2, field) {
|
|
|
128629
128635
|
invalidConfig(path2, field, "必须是字符串");
|
|
128630
128636
|
return value;
|
|
128631
128637
|
}
|
|
128638
|
+
function expectPositiveNumber(value, path2, field) {
|
|
128639
|
+
if (typeof value !== "number" || !Number.isFinite(value) || value <= 0) {
|
|
128640
|
+
invalidConfig(path2, field, "必须是大于 0 的有限数字");
|
|
128641
|
+
}
|
|
128642
|
+
return value;
|
|
128643
|
+
}
|
|
128632
128644
|
function invalidConfig(path2, field, reason) {
|
|
128633
128645
|
throw new Error(`配置文件无效:${path2}
|
|
128634
128646
|
位置:${field}
|
|
@@ -128652,11 +128664,7 @@ function resolveOutputPath(path2, defaultName, extensions, label) {
|
|
|
128652
128664
|
assertExtension(resolved, extensions, label);
|
|
128653
128665
|
return resolved;
|
|
128654
128666
|
}
|
|
128655
|
-
function prepareOutput(path2
|
|
128656
|
-
if (existsSync2(path2) && !force) {
|
|
128657
|
-
throw new Error(`输出文件已存在:${path2}
|
|
128658
|
-
使用 --force 覆盖现有文件`);
|
|
128659
|
-
}
|
|
128667
|
+
function prepareOutput(path2) {
|
|
128660
128668
|
mkdirSync(dirname2(path2), { recursive: true });
|
|
128661
128669
|
}
|
|
128662
128670
|
function assertExtension(path2, extensions, label) {
|
|
@@ -146108,6 +146116,11 @@ var config_default = `{
|
|
|
146108
146116
|
"theme": "tokyo-night-light",
|
|
146109
146117
|
"density": 200
|
|
146110
146118
|
},
|
|
146119
|
+
"imageSize": {
|
|
146120
|
+
"enabled": true,
|
|
146121
|
+
"maxWidthCm": 12,
|
|
146122
|
+
"maxHeightCm": 12
|
|
146123
|
+
},
|
|
146111
146124
|
"detectTitle": {
|
|
146112
146125
|
"enabled": true,
|
|
146113
146126
|
"strategy": "first-h1"
|
|
@@ -146118,6 +146131,102 @@ var config_default = `{
|
|
|
146118
146131
|
}
|
|
146119
146132
|
`;
|
|
146120
146133
|
|
|
146134
|
+
// config/lua/limit-image-size.lua
|
|
146135
|
+
var limit_image_size_default = `local max_width_cm
|
|
146136
|
+
local max_height_cm
|
|
146137
|
+
local size_cache = {}
|
|
146138
|
+
local warned = {}
|
|
146139
|
+
|
|
146140
|
+
local function warn_once(source, message)
|
|
146141
|
+
if warned[source] then
|
|
146142
|
+
return
|
|
146143
|
+
end
|
|
146144
|
+
warned[source] = true
|
|
146145
|
+
local text = "无法限制图片尺寸 " .. source .. ":" .. message
|
|
146146
|
+
if pandoc.log and pandoc.log.warn then
|
|
146147
|
+
pandoc.log.warn(text)
|
|
146148
|
+
else
|
|
146149
|
+
io.stderr:write("[WARNING] " .. text .. "\\n")
|
|
146150
|
+
end
|
|
146151
|
+
end
|
|
146152
|
+
|
|
146153
|
+
local function metadata_number(meta, key)
|
|
146154
|
+
local value = meta[key]
|
|
146155
|
+
if value == nil then
|
|
146156
|
+
return nil
|
|
146157
|
+
end
|
|
146158
|
+
return tonumber(pandoc.utils.stringify(value))
|
|
146159
|
+
end
|
|
146160
|
+
|
|
146161
|
+
local function read_config(meta)
|
|
146162
|
+
max_width_cm = metadata_number(meta, "md2docx-image-max-width-cm")
|
|
146163
|
+
max_height_cm = metadata_number(meta, "md2docx-image-max-height-cm")
|
|
146164
|
+
end
|
|
146165
|
+
|
|
146166
|
+
local function image_size(source)
|
|
146167
|
+
local cached = size_cache[source]
|
|
146168
|
+
if cached ~= nil then
|
|
146169
|
+
return cached or nil
|
|
146170
|
+
end
|
|
146171
|
+
if pandoc.image == nil or pandoc.image.size == nil then
|
|
146172
|
+
warn_once(source, "当前 Pandoc 不支持 pandoc.image.size,请升级到 3.1.13 或更高版本")
|
|
146173
|
+
size_cache[source] = false
|
|
146174
|
+
return nil
|
|
146175
|
+
end
|
|
146176
|
+
|
|
146177
|
+
local fetched, _, contents = pcall(pandoc.mediabag.fetch, source)
|
|
146178
|
+
if not fetched or contents == nil then
|
|
146179
|
+
warn_once(source, fetched and "未读取到图片内容" or tostring(_))
|
|
146180
|
+
size_cache[source] = false
|
|
146181
|
+
return nil
|
|
146182
|
+
end
|
|
146183
|
+
|
|
146184
|
+
local measured, size = pcall(pandoc.image.size, contents, PANDOC_WRITER_OPTIONS)
|
|
146185
|
+
if not measured then
|
|
146186
|
+
warn_once(source, tostring(size))
|
|
146187
|
+
size_cache[source] = false
|
|
146188
|
+
return nil
|
|
146189
|
+
end
|
|
146190
|
+
|
|
146191
|
+
size_cache[source] = size
|
|
146192
|
+
return size
|
|
146193
|
+
end
|
|
146194
|
+
|
|
146195
|
+
local function limit_image(image)
|
|
146196
|
+
if max_width_cm == nil or max_height_cm == nil then
|
|
146197
|
+
return nil
|
|
146198
|
+
end
|
|
146199
|
+
if image.attributes.width ~= nil or image.attributes.height ~= nil then
|
|
146200
|
+
return nil
|
|
146201
|
+
end
|
|
146202
|
+
|
|
146203
|
+
local size = image_size(image.src)
|
|
146204
|
+
if size == nil then
|
|
146205
|
+
return nil
|
|
146206
|
+
end
|
|
146207
|
+
|
|
146208
|
+
local fallback_dpi = PANDOC_WRITER_OPTIONS.dpi or 96
|
|
146209
|
+
local horizontal_dpi = size.dpi_horz > 0 and size.dpi_horz or fallback_dpi
|
|
146210
|
+
local vertical_dpi = size.dpi_vert > 0 and size.dpi_vert or fallback_dpi
|
|
146211
|
+
local width_cm = size.width / horizontal_dpi * 2.54
|
|
146212
|
+
local height_cm = size.height / vertical_dpi * 2.54
|
|
146213
|
+
local scale = math.min(1, max_width_cm / width_cm, max_height_cm / height_cm)
|
|
146214
|
+
|
|
146215
|
+
if scale >= 1 then
|
|
146216
|
+
return nil
|
|
146217
|
+
end
|
|
146218
|
+
|
|
146219
|
+
image.attributes.width = string.format("%.4fcm", width_cm * scale)
|
|
146220
|
+
image.attributes.height = string.format("%.4fcm", height_cm * scale)
|
|
146221
|
+
return image
|
|
146222
|
+
end
|
|
146223
|
+
|
|
146224
|
+
return {
|
|
146225
|
+
{ Meta = read_config },
|
|
146226
|
+
{ Image = limit_image },
|
|
146227
|
+
}
|
|
146228
|
+
`;
|
|
146229
|
+
|
|
146121
146230
|
// config/lua/add-inline-code.lua
|
|
146122
146231
|
var add_inline_code_default = `-- Author: Achuan-2
|
|
146123
146232
|
function Code(el)
|
|
@@ -146126,32 +146235,8 @@ function Code(el)
|
|
|
146126
146235
|
end
|
|
146127
146236
|
return { Code = Code }`;
|
|
146128
146237
|
|
|
146129
|
-
// config/style-
|
|
146130
|
-
var
|
|
146131
|
-
"$schema": "https://raw.githubusercontent.com/WXY-V1hZ/md2docx/main/config/style-config.schema.json",
|
|
146132
|
-
"schemaVersion": 1,
|
|
146133
|
-
"preset": "default",
|
|
146134
|
-
"options": {
|
|
146135
|
-
"body": {
|
|
146136
|
-
"firstLineIndent": true
|
|
146137
|
-
},
|
|
146138
|
-
"headings": {
|
|
146139
|
-
"1": {
|
|
146140
|
-
"startOnNewPage": false
|
|
146141
|
-
}
|
|
146142
|
-
},
|
|
146143
|
-
"inlineCode": {
|
|
146144
|
-
"background": true
|
|
146145
|
-
},
|
|
146146
|
-
"codeBlock": {
|
|
146147
|
-
"border": true
|
|
146148
|
-
}
|
|
146149
|
-
}
|
|
146150
|
-
}
|
|
146151
|
-
`;
|
|
146152
|
-
|
|
146153
|
-
// config/style.json
|
|
146154
|
-
var style_default = `{
|
|
146238
|
+
// config/style-raw.json
|
|
146239
|
+
var style_raw_default = `{
|
|
146155
146240
|
"default": {
|
|
146156
146241
|
"document": {
|
|
146157
146242
|
"run": {
|
|
@@ -147108,10 +147193,54 @@ var style_default = `{
|
|
|
147108
147193
|
}
|
|
147109
147194
|
`;
|
|
147110
147195
|
|
|
147196
|
+
// config/style-config.json
|
|
147197
|
+
var style_config_default = `{
|
|
147198
|
+
"$schema": "https://raw.githubusercontent.com/WXY-V1hZ/md2docx/main/config/style-config.schema.json",
|
|
147199
|
+
"schemaVersion": 1,
|
|
147200
|
+
"options": {
|
|
147201
|
+
"body": {
|
|
147202
|
+
"firstLineIndent": true,
|
|
147203
|
+
"lineSpacing": 1.5
|
|
147204
|
+
},
|
|
147205
|
+
"headings": {
|
|
147206
|
+
"1": {
|
|
147207
|
+
"startOnNewPage": false,
|
|
147208
|
+
"alignment": "left",
|
|
147209
|
+
"bold": true
|
|
147210
|
+
},
|
|
147211
|
+
"2": {
|
|
147212
|
+
"bold": true
|
|
147213
|
+
},
|
|
147214
|
+
"3": {
|
|
147215
|
+
"bold": true
|
|
147216
|
+
},
|
|
147217
|
+
"4": {
|
|
147218
|
+
"bold": true,
|
|
147219
|
+
"italic": false
|
|
147220
|
+
},
|
|
147221
|
+
"5": {
|
|
147222
|
+
"bold": true,
|
|
147223
|
+
"italic": false
|
|
147224
|
+
},
|
|
147225
|
+
"6": {
|
|
147226
|
+
"bold": true,
|
|
147227
|
+
"italic": false
|
|
147228
|
+
}
|
|
147229
|
+
},
|
|
147230
|
+
"inlineCode": {
|
|
147231
|
+
"background": true
|
|
147232
|
+
},
|
|
147233
|
+
"codeBlock": {
|
|
147234
|
+
"border": true
|
|
147235
|
+
}
|
|
147236
|
+
}
|
|
147237
|
+
}
|
|
147238
|
+
`;
|
|
147239
|
+
|
|
147111
147240
|
// src/resources.ts
|
|
147112
147241
|
var DEFAULT_CONFIG_TEXT = config_default;
|
|
147113
147242
|
var DEFAULT_STYLE_CONFIG_TEXT = style_config_default;
|
|
147114
|
-
var
|
|
147243
|
+
var DEFAULT_STYLE_RAW_TEXT = style_raw_default;
|
|
147115
147244
|
function materializeResource(name2, content3) {
|
|
147116
147245
|
const directory = join4(TMP_DIR, "resources");
|
|
147117
147246
|
const path3 = join4(directory, name2);
|
|
@@ -147124,41 +147253,30 @@ function materializeResource(name2, content3) {
|
|
|
147124
147253
|
function materializeDefaultConfig() {
|
|
147125
147254
|
return materializeResource("config.json", DEFAULT_CONFIG_TEXT);
|
|
147126
147255
|
}
|
|
147127
|
-
function
|
|
147128
|
-
return materializeResource("style.json",
|
|
147256
|
+
function materializeDefaultStyleRaw() {
|
|
147257
|
+
return materializeResource("style-raw.json", DEFAULT_STYLE_RAW_TEXT);
|
|
147129
147258
|
}
|
|
147130
147259
|
function materializeDefaultStyleConfig() {
|
|
147131
|
-
materializeDefaultStyle();
|
|
147132
147260
|
return materializeResource("style-config.json", DEFAULT_STYLE_CONFIG_TEXT);
|
|
147133
147261
|
}
|
|
147134
|
-
function
|
|
147262
|
+
function materializeInlineCodeFilter() {
|
|
147135
147263
|
return materializeResource("add-inline-code.lua", add_inline_code_default);
|
|
147136
147264
|
}
|
|
147137
|
-
|
|
147138
|
-
|
|
147139
|
-
|
|
147140
|
-
import { writeFile } from "node:fs/promises";
|
|
147141
|
-
import { createHash as createHash2 } from "node:crypto";
|
|
147142
|
-
import { dirname as dirname4 } from "path";
|
|
147265
|
+
function materializeImageSizeFilter() {
|
|
147266
|
+
return materializeResource("limit-image-size.lua", limit_image_size_default);
|
|
147267
|
+
}
|
|
147143
147268
|
|
|
147144
147269
|
// src/style/compiler.ts
|
|
147145
147270
|
import { readFileSync as readFileSync2 } from "node:fs";
|
|
147146
147271
|
|
|
147147
147272
|
// src/style/config.ts
|
|
147148
|
-
function isStyleConfig(value2) {
|
|
147149
|
-
if (!isRecord(value2))
|
|
147150
|
-
return false;
|
|
147151
|
-
return "schemaVersion" in value2 || "preset" in value2 || "options" in value2;
|
|
147152
|
-
}
|
|
147153
147273
|
function validateStyleConfig(value2, path3) {
|
|
147154
147274
|
const root2 = expectRecord2(value2, path3, "样式配置");
|
|
147155
|
-
rejectUnknown(root2, ["$schema", "schemaVersion", "
|
|
147275
|
+
rejectUnknown(root2, ["$schema", "schemaVersion", "options"], path3, "样式配置");
|
|
147156
147276
|
if (root2.$schema !== undefined)
|
|
147157
147277
|
expectString2(root2.$schema, path3, "$schema");
|
|
147158
147278
|
if (root2.schemaVersion !== 1)
|
|
147159
147279
|
invalidStyleConfig(path3, "schemaVersion", "目前只支持版本 1");
|
|
147160
|
-
if (root2.preset !== "default")
|
|
147161
|
-
invalidStyleConfig(path3, "preset", '目前只支持 "default"');
|
|
147162
147280
|
if (root2.options !== undefined)
|
|
147163
147281
|
validateOptions(root2.options, path3);
|
|
147164
147282
|
return root2;
|
|
@@ -147168,21 +147286,25 @@ function validateOptions(value2, path3) {
|
|
|
147168
147286
|
rejectUnknown(options, ["body", "headings", "inlineCode", "codeBlock"], path3, "options");
|
|
147169
147287
|
if (options.body !== undefined) {
|
|
147170
147288
|
const body = expectRecord2(options.body, path3, "options.body");
|
|
147171
|
-
rejectUnknown(body, ["firstLineIndent"], path3, "options.body");
|
|
147289
|
+
rejectUnknown(body, ["firstLineIndent", "lineSpacing"], path3, "options.body");
|
|
147172
147290
|
if (body.firstLineIndent !== undefined) {
|
|
147173
147291
|
expectBoolean2(body.firstLineIndent, path3, "options.body.firstLineIndent");
|
|
147174
147292
|
}
|
|
147293
|
+
if (body.lineSpacing !== undefined) {
|
|
147294
|
+
if (typeof body.lineSpacing !== "number" || !Number.isFinite(body.lineSpacing) || body.lineSpacing <= 0) {
|
|
147295
|
+
invalidStyleConfig(path3, "options.body.lineSpacing", "必须是大于 0 的有限数字");
|
|
147296
|
+
}
|
|
147297
|
+
}
|
|
147175
147298
|
}
|
|
147176
147299
|
if (options.headings !== undefined) {
|
|
147177
147300
|
const headings = expectRecord2(options.headings, path3, "options.headings");
|
|
147178
|
-
rejectUnknown(headings, ["1"], path3, "options.headings");
|
|
147179
|
-
|
|
147180
|
-
|
|
147181
|
-
|
|
147182
|
-
|
|
147183
|
-
|
|
147184
|
-
|
|
147185
|
-
}
|
|
147301
|
+
rejectUnknown(headings, ["1", "2", "3", "4", "5", "6"], path3, "options.headings");
|
|
147302
|
+
validateHeading(headings, "1", ["startOnNewPage", "alignment", "bold"], path3);
|
|
147303
|
+
validateHeading(headings, "2", ["bold"], path3);
|
|
147304
|
+
validateHeading(headings, "3", ["bold"], path3);
|
|
147305
|
+
validateHeading(headings, "4", ["bold", "italic"], path3);
|
|
147306
|
+
validateHeading(headings, "5", ["bold", "italic"], path3);
|
|
147307
|
+
validateHeading(headings, "6", ["bold", "italic"], path3);
|
|
147186
147308
|
}
|
|
147187
147309
|
if (options.inlineCode !== undefined) {
|
|
147188
147310
|
const inlineCode2 = expectRecord2(options.inlineCode, path3, "options.inlineCode");
|
|
@@ -147199,6 +147321,23 @@ function validateOptions(value2, path3) {
|
|
|
147199
147321
|
}
|
|
147200
147322
|
}
|
|
147201
147323
|
}
|
|
147324
|
+
function validateHeading(headings, level, allowed, path3) {
|
|
147325
|
+
if (headings[level] === undefined)
|
|
147326
|
+
return;
|
|
147327
|
+
const field = `options.headings["${level}"]`;
|
|
147328
|
+
const heading2 = expectRecord2(headings[level], path3, field);
|
|
147329
|
+
rejectUnknown(heading2, allowed, path3, field);
|
|
147330
|
+
if (heading2.startOnNewPage !== undefined) {
|
|
147331
|
+
expectBoolean2(heading2.startOnNewPage, path3, `${field}.startOnNewPage`);
|
|
147332
|
+
}
|
|
147333
|
+
if (heading2.alignment !== undefined) {
|
|
147334
|
+
expectEnum(heading2.alignment, ["left", "center"], path3, `${field}.alignment`);
|
|
147335
|
+
}
|
|
147336
|
+
if (heading2.bold !== undefined)
|
|
147337
|
+
expectBoolean2(heading2.bold, path3, `${field}.bold`);
|
|
147338
|
+
if (heading2.italic !== undefined)
|
|
147339
|
+
expectBoolean2(heading2.italic, path3, `${field}.italic`);
|
|
147340
|
+
}
|
|
147202
147341
|
function rejectUnknown(value2, allowed, path3, field) {
|
|
147203
147342
|
const unknown2 = Object.keys(value2).find((key) => !allowed.includes(key));
|
|
147204
147343
|
if (unknown2 !== undefined)
|
|
@@ -147220,6 +147359,11 @@ function expectString2(value2, path3, field) {
|
|
|
147220
147359
|
if (typeof value2 !== "string")
|
|
147221
147360
|
invalidStyleConfig(path3, field, "必须是字符串");
|
|
147222
147361
|
}
|
|
147362
|
+
function expectEnum(value2, allowed, path3, field) {
|
|
147363
|
+
if (typeof value2 !== "string" || !allowed.includes(value2)) {
|
|
147364
|
+
invalidStyleConfig(path3, field, `必须是 ${allowed.join("、")} 之一`);
|
|
147365
|
+
}
|
|
147366
|
+
}
|
|
147223
147367
|
function invalidStyleConfig(path3, field, reason) {
|
|
147224
147368
|
throw new Error(`样式配置无效:${path3}
|
|
147225
147369
|
位置:${field}
|
|
@@ -147227,25 +147371,64 @@ function invalidStyleConfig(path3, field, reason) {
|
|
|
147227
147371
|
}
|
|
147228
147372
|
|
|
147229
147373
|
// src/style/compiler.ts
|
|
147230
|
-
|
|
147231
|
-
|
|
147232
|
-
|
|
147233
|
-
|
|
147234
|
-
|
|
147235
|
-
|
|
147236
|
-
|
|
147237
|
-
|
|
147238
|
-
|
|
147239
|
-
const
|
|
147374
|
+
var HEADING_LEVELS = ["1", "2", "3", "4", "5", "6"];
|
|
147375
|
+
function resolveEffectiveStyles(sources, defaults) {
|
|
147376
|
+
const rawPath = sources.styleRawPath ?? defaults?.styleRawPath ?? materializeDefaultStyleRaw();
|
|
147377
|
+
const rawStyle = loadStyleRaw(rawPath);
|
|
147378
|
+
if (sources.styleConfigPath) {
|
|
147379
|
+
return compileStyleConfig(rawStyle, loadStyleConfig(sources.styleConfigPath));
|
|
147380
|
+
}
|
|
147381
|
+
if (sources.styleRawPath)
|
|
147382
|
+
return rawStyle;
|
|
147383
|
+
const defaultConfigPath = defaults?.styleConfigPath ?? materializeDefaultStyleConfig();
|
|
147384
|
+
return compileStyleConfig(rawStyle, loadStyleConfig(defaultConfigPath));
|
|
147385
|
+
}
|
|
147386
|
+
function loadStyleRaw(path3) {
|
|
147387
|
+
const source = parseJson(readFileSync2(path3, "utf-8"), path3, "底层样式文件");
|
|
147388
|
+
if (!isRecord2(source))
|
|
147389
|
+
throw new Error(`底层样式文件必须是 JSON 对象:${path3}`);
|
|
147390
|
+
if (looksLikeStyleConfig(source)) {
|
|
147391
|
+
throw new Error(`底层样式文件无效:${path3}
|
|
147392
|
+
当前文件看起来是语义化样式配置,请改用 --style-config`);
|
|
147393
|
+
}
|
|
147394
|
+
if (!looksLikeRawStyle(source)) {
|
|
147395
|
+
throw new Error(`底层样式文件无效:${path3}
|
|
147396
|
+
缺少 Word 样式定义`);
|
|
147397
|
+
}
|
|
147398
|
+
return source;
|
|
147399
|
+
}
|
|
147400
|
+
function loadStyleConfig(path3) {
|
|
147401
|
+
const source = parseJson(readFileSync2(path3, "utf-8"), path3, "语义化样式配置");
|
|
147402
|
+
if (isRecord2(source) && looksLikeRawStyle(source)) {
|
|
147403
|
+
throw new Error(`语义化样式配置无效:${path3}
|
|
147404
|
+
当前文件看起来是底层样式,请改用 --style-raw`);
|
|
147405
|
+
}
|
|
147406
|
+
return validateStyleConfig(source, path3);
|
|
147407
|
+
}
|
|
147408
|
+
function compileStyleConfig(rawStyle, config) {
|
|
147409
|
+
const styles = structuredClone(rawStyle);
|
|
147240
147410
|
const options = config.options;
|
|
147241
147411
|
if (!options)
|
|
147242
147412
|
return styles;
|
|
147243
147413
|
const firstLineIndent = options.body?.firstLineIndent;
|
|
147244
147414
|
if (firstLineIndent !== undefined)
|
|
147245
147415
|
applyBodyFirstLineIndent(styles, firstLineIndent);
|
|
147416
|
+
const lineSpacing = options.body?.lineSpacing;
|
|
147417
|
+
if (lineSpacing !== undefined)
|
|
147418
|
+
applyBodyLineSpacing(styles, lineSpacing);
|
|
147246
147419
|
const startOnNewPage = options.headings?.["1"]?.startOnNewPage;
|
|
147247
147420
|
if (startOnNewPage !== undefined)
|
|
147248
147421
|
applyHeading1PageBreak(styles, startOnNewPage);
|
|
147422
|
+
const heading1Alignment = options.headings?.["1"]?.alignment;
|
|
147423
|
+
if (heading1Alignment !== undefined)
|
|
147424
|
+
applyHeading1Alignment(styles, heading1Alignment);
|
|
147425
|
+
for (const level of HEADING_LEVELS) {
|
|
147426
|
+
const heading2 = options.headings?.[level];
|
|
147427
|
+
if (heading2?.bold !== undefined)
|
|
147428
|
+
applyHeadingBold(styles, level, heading2.bold);
|
|
147429
|
+
if (heading2?.italic !== undefined)
|
|
147430
|
+
applyHeadingItalic(styles, level, heading2.italic);
|
|
147431
|
+
}
|
|
147249
147432
|
const inlineCodeBackground = options.inlineCode?.background;
|
|
147250
147433
|
if (inlineCodeBackground !== undefined) {
|
|
147251
147434
|
applyInlineCodeBackground(styles, inlineCodeBackground);
|
|
@@ -147255,6 +147438,18 @@ function compileStyleConfig(preset, config) {
|
|
|
147255
147438
|
applyCodeBlockBorder(styles, codeBlockBorder);
|
|
147256
147439
|
return styles;
|
|
147257
147440
|
}
|
|
147441
|
+
function applyBodyLineSpacing(styles, multiplier) {
|
|
147442
|
+
const line = Math.round(multiplier * 240);
|
|
147443
|
+
for (const name2 of ["First Paragraph", "Body Text"]) {
|
|
147444
|
+
const style = findStyle(styles, "paragraphStyles", name2);
|
|
147445
|
+
const paragraph2 = ensureObject(style, "paragraph");
|
|
147446
|
+
paragraph2.spacing = {
|
|
147447
|
+
...optionalObject(paragraph2.spacing),
|
|
147448
|
+
line,
|
|
147449
|
+
lineRule: "auto"
|
|
147450
|
+
};
|
|
147451
|
+
}
|
|
147452
|
+
}
|
|
147258
147453
|
function applyBodyFirstLineIndent(styles, enabled) {
|
|
147259
147454
|
const firstParagraph = findStyle(styles, "paragraphStyles", "First Paragraph");
|
|
147260
147455
|
const bodyText = findStyle(styles, "paragraphStyles", "Body Text");
|
|
@@ -147271,6 +147466,22 @@ function applyHeading1PageBreak(styles, enabled) {
|
|
|
147271
147466
|
const heading1 = findStyle(styles, "paragraphStyles", "heading 1");
|
|
147272
147467
|
ensureObject(heading1, "paragraph").pageBreakBefore = enabled;
|
|
147273
147468
|
}
|
|
147469
|
+
function applyHeading1Alignment(styles, alignment) {
|
|
147470
|
+
const heading1 = findStyle(styles, "paragraphStyles", "heading 1");
|
|
147471
|
+
ensureObject(heading1, "paragraph").alignment = alignment;
|
|
147472
|
+
}
|
|
147473
|
+
function applyHeadingBold(styles, level, enabled) {
|
|
147474
|
+
const heading2 = findStyle(styles, "paragraphStyles", `heading ${level}`);
|
|
147475
|
+
const run = ensureObject(heading2, "run");
|
|
147476
|
+
run.bold = enabled;
|
|
147477
|
+
run.boldComplexScript = enabled;
|
|
147478
|
+
}
|
|
147479
|
+
function applyHeadingItalic(styles, level, enabled) {
|
|
147480
|
+
const heading2 = findStyle(styles, "paragraphStyles", `heading ${level}`);
|
|
147481
|
+
const run = ensureObject(heading2, "run");
|
|
147482
|
+
run.italics = enabled;
|
|
147483
|
+
run.italicsComplexScript = enabled;
|
|
147484
|
+
}
|
|
147274
147485
|
function applyInlineCodeBackground(styles, enabled) {
|
|
147275
147486
|
const inlineCode2 = findStyle(styles, "characterStyles", "Inline Code");
|
|
147276
147487
|
ensureObject(inlineCode2, "run").shading = {
|
|
@@ -147297,12 +147508,12 @@ function applyCodeBlockBorder(styles, enabled) {
|
|
|
147297
147508
|
function findStyle(styles, collectionName, name2) {
|
|
147298
147509
|
const collection = styles[collectionName];
|
|
147299
147510
|
if (!Array.isArray(collection)) {
|
|
147300
|
-
throw new Error(
|
|
147511
|
+
throw new Error(`底层样式缺少 ${collectionName},无法应用语义化样式配置`);
|
|
147301
147512
|
}
|
|
147302
147513
|
const normalizedName = name2.toLowerCase();
|
|
147303
147514
|
const style = collection.find((entry) => isRecord2(entry) && typeof entry.name === "string" && entry.name.toLowerCase() === normalizedName);
|
|
147304
147515
|
if (!style)
|
|
147305
|
-
throw new Error(
|
|
147516
|
+
throw new Error(`底层样式缺少“${name2}”,无法应用语义化样式配置`);
|
|
147306
147517
|
return style;
|
|
147307
147518
|
}
|
|
147308
147519
|
function ensureObject(parent, key) {
|
|
@@ -147316,25 +147527,30 @@ function ensureObject(parent, key) {
|
|
|
147316
147527
|
function optionalObject(value2) {
|
|
147317
147528
|
return isRecord2(value2) ? value2 : {};
|
|
147318
147529
|
}
|
|
147319
|
-
function parseJson(text5, path3) {
|
|
147530
|
+
function parseJson(text5, path3, kind) {
|
|
147320
147531
|
try {
|
|
147321
147532
|
return JSON.parse(text5);
|
|
147322
147533
|
} catch (error) {
|
|
147323
147534
|
const message = error instanceof Error ? error.message : String(error);
|
|
147324
|
-
throw new Error(
|
|
147535
|
+
throw new Error(`${kind}不是有效的 JSON:${path3}
|
|
147325
147536
|
${message}`);
|
|
147326
147537
|
}
|
|
147327
147538
|
}
|
|
147328
|
-
function
|
|
147329
|
-
|
|
147330
|
-
|
|
147331
|
-
|
|
147539
|
+
function looksLikeStyleConfig(value2) {
|
|
147540
|
+
return "schemaVersion" in value2 || "options" in value2 || "preset" in value2;
|
|
147541
|
+
}
|
|
147542
|
+
function looksLikeRawStyle(value2) {
|
|
147543
|
+
return ["default", "paragraphStyles", "characterStyles", "tableStyles", "tableStylesXml"].some((key) => (key in value2));
|
|
147332
147544
|
}
|
|
147333
147545
|
function isRecord2(value2) {
|
|
147334
147546
|
return typeof value2 === "object" && value2 !== null && !Array.isArray(value2);
|
|
147335
147547
|
}
|
|
147336
147548
|
|
|
147337
147549
|
// src/style/generate.ts
|
|
147550
|
+
import { existsSync as existsSync5, mkdirSync as mkdirSync4 } from "node:fs";
|
|
147551
|
+
import { writeFile } from "node:fs/promises";
|
|
147552
|
+
import { createHash as createHash2 } from "node:crypto";
|
|
147553
|
+
import { dirname as dirname4 } from "path";
|
|
147338
147554
|
async function generateTemplateDocxFromStyles(raw, outputPath) {
|
|
147339
147555
|
const { Document: Document2, Packer: Packer3 } = await Promise.resolve().then(() => (init_dist(), exports_dist));
|
|
147340
147556
|
const tableStylesXml = raw.tableStylesXml;
|
|
@@ -147361,14 +147577,16 @@ async function generateTemplateDocxFromStyles(raw, outputPath) {
|
|
|
147361
147577
|
await writeFile(outputPath, buf);
|
|
147362
147578
|
}
|
|
147363
147579
|
var generatePromises = new Map;
|
|
147364
|
-
|
|
147365
|
-
|
|
147366
|
-
|
|
147580
|
+
function styleCacheHash(rawStyle) {
|
|
147581
|
+
return createHash2("sha256").update(JSON.stringify(rawStyle)).digest("hex").slice(0, 16);
|
|
147582
|
+
}
|
|
147583
|
+
async function ensureTemplateDocx(rawStyle) {
|
|
147584
|
+
const hash = styleCacheHash(rawStyle);
|
|
147367
147585
|
const outputPath = styleTemplateDocx(hash);
|
|
147368
147586
|
if (!existsSync5(outputPath)) {
|
|
147369
147587
|
let promise = generatePromises.get(outputPath);
|
|
147370
147588
|
if (!promise) {
|
|
147371
|
-
promise = generateTemplateDocxFromStyles(
|
|
147589
|
+
promise = generateTemplateDocxFromStyles(rawStyle, outputPath).catch((error) => {
|
|
147372
147590
|
generatePromises.delete(outputPath);
|
|
147373
147591
|
throw error;
|
|
147374
147592
|
});
|
|
@@ -147385,25 +147603,29 @@ async function convertMarkdown(options) {
|
|
|
147385
147603
|
throw new Error("缺少必填参数:--file");
|
|
147386
147604
|
const input = resolveInputPath(options.file, [".md", ".markdown"], "Markdown 文件");
|
|
147387
147605
|
const configPath = options.config ? resolveInputPath(options.config, [".json"], "配置文件") : materializeDefaultConfig();
|
|
147388
|
-
const
|
|
147606
|
+
const styleRawPath = options.styleRaw ? resolveInputPath(options.styleRaw, [".json"], "底层样式文件") : undefined;
|
|
147607
|
+
const styleConfigPath = options.styleConfig ? resolveInputPath(options.styleConfig, [".json"], "语义化样式配置") : undefined;
|
|
147389
147608
|
const baseName = parse4(input).name;
|
|
147390
147609
|
const output = resolveOutputPath(options.output, `${baseName}.docx`, [".docx"], "DOCX 输出文件");
|
|
147391
|
-
prepareOutput(output
|
|
147610
|
+
prepareOutput(output);
|
|
147392
147611
|
const config = await loadConfig(configPath);
|
|
147393
147612
|
const outDir = preprocessDir(input);
|
|
147394
147613
|
mkdirSync5(outDir, { recursive: true });
|
|
147395
147614
|
const formatted = await preprocess2(input, config, outDir);
|
|
147396
147615
|
const markdownOutput = formattedMdPath(input);
|
|
147397
147616
|
writeFileSync3(markdownOutput, formatted, "utf-8");
|
|
147398
|
-
const
|
|
147399
|
-
const
|
|
147617
|
+
const effectiveStyles = resolveEffectiveStyles({ styleRawPath, styleConfigPath });
|
|
147618
|
+
const templatePath = await ensureTemplateDocx(effectiveStyles);
|
|
147619
|
+
const inlineCodeFilter = materializeInlineCodeFilter();
|
|
147620
|
+
const imageSizeArgs = config.imageSize.enabled ? buildImageSizePandocArgs(config.imageSize, materializeImageSizeFilter()) : [];
|
|
147400
147621
|
const pandocArgs = [
|
|
147401
147622
|
markdownOutput,
|
|
147402
147623
|
"-o",
|
|
147403
147624
|
output,
|
|
147404
147625
|
...buildPandocResourcePathArgs(input),
|
|
147405
147626
|
`--reference-doc=${templatePath}`,
|
|
147406
|
-
...existsSync6(
|
|
147627
|
+
...existsSync6(inlineCodeFilter) ? [`--lua-filter=${inlineCodeFilter}`] : [],
|
|
147628
|
+
...imageSizeArgs
|
|
147407
147629
|
];
|
|
147408
147630
|
const { exitCode, stderr } = await runProcess("pandoc", pandocArgs);
|
|
147409
147631
|
if (exitCode !== 0) {
|
|
@@ -147411,6 +147633,15 @@ async function convertMarkdown(options) {
|
|
|
147411
147633
|
}
|
|
147412
147634
|
console.log(`已生成:${output}`);
|
|
147413
147635
|
}
|
|
147636
|
+
function buildImageSizePandocArgs(config, filterPath) {
|
|
147637
|
+
if (!config.enabled)
|
|
147638
|
+
return [];
|
|
147639
|
+
return [
|
|
147640
|
+
`--metadata=md2docx-image-max-width-cm:${config.maxWidthCm}`,
|
|
147641
|
+
`--metadata=md2docx-image-max-height-cm:${config.maxHeightCm}`,
|
|
147642
|
+
`--lua-filter=${filterPath}`
|
|
147643
|
+
];
|
|
147644
|
+
}
|
|
147414
147645
|
function buildPandocResourcePathArgs(inputPath, workingDirectory = process.cwd()) {
|
|
147415
147646
|
const workingDir = resolve5(workingDirectory);
|
|
147416
147647
|
const sourceDir = dirname5(resolve5(inputPath));
|
|
@@ -148054,23 +148285,32 @@ function extractStylesFromDocx(docxPath) {
|
|
|
148054
148285
|
// src/commands/export.ts
|
|
148055
148286
|
async function exportConfig(options) {
|
|
148056
148287
|
const output = resolveOutputPath(options.output, "config.json", [".json"], "配置输出文件");
|
|
148057
|
-
prepareOutput(output
|
|
148288
|
+
prepareOutput(output);
|
|
148058
148289
|
writeFileSync4(output, DEFAULT_CONFIG_TEXT, "utf-8");
|
|
148059
148290
|
console.log(`已导出配置:${output}`);
|
|
148060
148291
|
}
|
|
148061
|
-
async function
|
|
148292
|
+
async function exportStyleRaw(options) {
|
|
148062
148293
|
const source = options.file ? resolveInputPath(options.file, [".docx"], "DOCX 文件") : undefined;
|
|
148063
|
-
const defaultName =
|
|
148064
|
-
const output = resolveOutputPath(options.output, defaultName, [".json"], "
|
|
148065
|
-
prepareOutput(output
|
|
148294
|
+
const defaultName = defaultStyleRawOutputName(source);
|
|
148295
|
+
const output = resolveOutputPath(options.output, defaultName, [".json"], "底层样式输出文件");
|
|
148296
|
+
prepareOutput(output);
|
|
148066
148297
|
if (source) {
|
|
148067
148298
|
const styles = extractStylesFromDocx(source);
|
|
148068
148299
|
writeFileSync4(output, `${JSON.stringify(styles, null, 2)}
|
|
148069
148300
|
`, "utf-8");
|
|
148070
148301
|
} else {
|
|
148071
|
-
writeFileSync4(output,
|
|
148302
|
+
writeFileSync4(output, DEFAULT_STYLE_RAW_TEXT, "utf-8");
|
|
148072
148303
|
}
|
|
148073
|
-
console.log(
|
|
148304
|
+
console.log(`已导出底层样式:${output}`);
|
|
148305
|
+
}
|
|
148306
|
+
function defaultStyleRawOutputName(source) {
|
|
148307
|
+
return source ? `${parse5(source).name}_style-raw.json` : "style-raw.json";
|
|
148308
|
+
}
|
|
148309
|
+
async function exportStyleConfig(options) {
|
|
148310
|
+
const output = resolveOutputPath(options.output, "style-config.json", [".json"], "语义化样式配置输出文件");
|
|
148311
|
+
prepareOutput(output);
|
|
148312
|
+
writeFileSync4(output, DEFAULT_STYLE_CONFIG_TEXT, "utf-8");
|
|
148313
|
+
console.log(`已导出语义化样式配置:${output}`);
|
|
148074
148314
|
}
|
|
148075
148315
|
|
|
148076
148316
|
// src/commands/format.ts
|
|
@@ -148081,17 +148321,12 @@ async function formatMarkdown(options) {
|
|
|
148081
148321
|
const configPath = options.config ? resolveInputPath(options.config, [".json"], "配置文件") : materializeDefaultConfig();
|
|
148082
148322
|
const defaultName = `${parse6(input).name}_formatted.md`;
|
|
148083
148323
|
const output = resolveOutputPath(options.output, defaultName, [".md", ".markdown"], "Markdown 输出文件");
|
|
148084
|
-
|
|
148085
|
-
prepareOutput(output, force);
|
|
148324
|
+
prepareOutput(output);
|
|
148086
148325
|
const config = await loadConfig(configPath);
|
|
148087
148326
|
const outputName = parse6(output).name;
|
|
148088
148327
|
const assetsName = `${outputName}_assets`;
|
|
148089
148328
|
const assetsDir = resolve6(dirname6(output), assetsName);
|
|
148090
148329
|
if (config.renderMermaid.enabled && existsSync7(assetsDir)) {
|
|
148091
|
-
if (!force) {
|
|
148092
|
-
throw new Error(`资源目录已存在:${assetsDir}
|
|
148093
|
-
使用 --force 覆盖现有目录`);
|
|
148094
|
-
}
|
|
148095
148330
|
const parent = resolve6(dirname6(output));
|
|
148096
148331
|
if (dirname6(assetsDir) !== parent)
|
|
148097
148332
|
throw new Error(`拒绝清理非输出目录:${assetsDir}`);
|
|
@@ -148108,7 +148343,8 @@ async function run(args) {
|
|
|
148108
148343
|
convert: convertMarkdown,
|
|
148109
148344
|
format: formatMarkdown,
|
|
148110
148345
|
exportConfig,
|
|
148111
|
-
|
|
148346
|
+
exportStyleRaw,
|
|
148347
|
+
exportStyleConfig,
|
|
148112
148348
|
clean: cleanIntermediateFiles
|
|
148113
148349
|
});
|
|
148114
148350
|
program2.exitOverride();
|
package/package.json
CHANGED
|
File without changes
|