md2gost 1.5.1 → 1.6.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +43 -1
- package/README.md +418 -19
- package/assets/styles.xml +126 -0
- package/assets/template.dotm +0 -0
- package/dist/index.d.ts +45 -0
- package/dist/index.js +2329 -415
- package/dist/index.js.map +3 -3
- package/dist/index.mjs +2281 -365
- package/dist/index.mjs.map +3 -3
- package/package.json +35 -7
package/dist/index.mjs
CHANGED
|
@@ -1,13 +1,18 @@
|
|
|
1
|
+
import { fileURLToPath as __md2gostFileURLToPath } from "node:url";
|
|
2
|
+
import { dirname as __md2gostPathDirname } from "node:path";
|
|
3
|
+
const __MD2GOST_DIRNAME__ = __md2gostPathDirname(__md2gostFileURLToPath(import.meta.url));
|
|
4
|
+
|
|
1
5
|
// src/index.ts
|
|
2
|
-
import
|
|
3
|
-
import { fileURLToPath } from "url";
|
|
6
|
+
import path6 from "path";
|
|
4
7
|
|
|
5
8
|
// src/main.ts
|
|
6
|
-
import
|
|
9
|
+
import path5 from "path";
|
|
7
10
|
|
|
8
11
|
// src/doc.ts
|
|
9
12
|
var Doc = class {
|
|
10
13
|
nodes = [];
|
|
14
|
+
header = { type: "none" };
|
|
15
|
+
footer = { type: "auto" };
|
|
11
16
|
rainbow = false;
|
|
12
17
|
numberingLazy = false;
|
|
13
18
|
numberingSections = false;
|
|
@@ -67,36 +72,66 @@ var Doc = class {
|
|
|
67
72
|
spacing: { before: void 0, after: void 0 },
|
|
68
73
|
text: { size: void 0 }
|
|
69
74
|
};
|
|
70
|
-
|
|
71
|
-
|
|
75
|
+
formula = {
|
|
76
|
+
spacing: { before: 8, after: 8 }
|
|
77
|
+
};
|
|
78
|
+
admonition = {
|
|
79
|
+
note: { title: "**\u041F\u0440\u0438\u043C\u0435\u0447\u0430\u043D\u0438\u0435**", indent: 1.25, spacing: { before: 8, after: 8 }, padding: { top: 6, right: 10, bottom: 6, left: 10 }, background: "f5f5f5", color: "666666", bar_width: 2.25, icon: true, icon_size: 16, title_color: true },
|
|
80
|
+
info: { title: "**\u041F\u0440\u0438\u043C\u0435\u0447\u0430\u043D\u0438\u0435**", indent: 1.25, spacing: { before: 8, after: 8 }, padding: { top: 6, right: 10, bottom: 6, left: 10 }, background: "f0f7ff", color: "1976d2", bar_width: 2.25, icon: true, icon_size: 16, title_color: true },
|
|
81
|
+
tip: { title: "**\u0420\u0435\u043A\u043E\u043C\u0435\u043D\u0434\u0430\u0446\u0438\u044F**", indent: 1.25, spacing: { before: 8, after: 8 }, padding: { top: 6, right: 10, bottom: 6, left: 10 }, background: "f2f8f2", color: "2e7d32", bar_width: 2.25, icon: true, icon_size: 16, title_color: true },
|
|
82
|
+
warning: { title: "**\u0412\u043D\u0438\u043C\u0430\u043D\u0438\u0435**", indent: 1.25, spacing: { before: 8, after: 8 }, padding: { top: 6, right: 10, bottom: 6, left: 10 }, background: "fff7ea", color: "b26a00", bar_width: 2.25, icon: true, icon_size: 16, title_color: true },
|
|
83
|
+
danger: { title: "**\u041A\u0440\u0438\u0442\u0438\u0447\u0435\u0441\u043A\u0438 \u0432\u0430\u0436\u043D\u043E**", indent: 1.25, spacing: { before: 8, after: 8 }, padding: { top: 6, right: 10, bottom: 6, left: 10 }, background: "fff0f0", color: "d32f2f", bar_width: 2.25, icon: true, icon_size: 16, title_color: true }
|
|
84
|
+
};
|
|
85
|
+
appendText(text, sourceLine) {
|
|
86
|
+
this.nodes.push({ type: "text", text, sourceLine });
|
|
72
87
|
}
|
|
73
|
-
appendTitle(text, level) {
|
|
74
|
-
this.nodes.push({ type: "title", text, level });
|
|
88
|
+
appendTitle(text, level, sourceLine) {
|
|
89
|
+
this.nodes.push({ type: "title", text, level, sourceLine });
|
|
75
90
|
}
|
|
76
91
|
appendNode(node) {
|
|
77
92
|
this.nodes.push(node);
|
|
78
93
|
}
|
|
94
|
+
lineSkips = [];
|
|
95
|
+
addLineSkip(line, delta) {
|
|
96
|
+
this.lineSkips.push({ ln: line, d: delta });
|
|
97
|
+
}
|
|
98
|
+
mapSourceLine(line) {
|
|
99
|
+
for (const { ln, d } of this.lineSkips)
|
|
100
|
+
if (ln <= line) line -= d;
|
|
101
|
+
return line;
|
|
102
|
+
}
|
|
79
103
|
};
|
|
80
|
-
function tableRow(...items) {
|
|
81
|
-
return items.map((v) => ({ type: "text", text: v }));
|
|
104
|
+
function tableRow(sourceLine, ...items) {
|
|
105
|
+
return items.map((v) => ({ type: "text", text: v, sourceLine }));
|
|
82
106
|
}
|
|
83
107
|
|
|
84
108
|
// src/parser.ts
|
|
85
|
-
import
|
|
109
|
+
import fs3 from "fs/promises";
|
|
86
110
|
|
|
87
111
|
// src/utils.ts
|
|
88
112
|
import fs from "fs/promises";
|
|
89
113
|
import fss from "fs";
|
|
90
114
|
import * as path from "node:path";
|
|
91
|
-
|
|
92
|
-
|
|
115
|
+
|
|
116
|
+
// src/errors.ts
|
|
117
|
+
var UserInputError = class extends Error {
|
|
118
|
+
constructor(message) {
|
|
119
|
+
super(message);
|
|
120
|
+
}
|
|
121
|
+
};
|
|
122
|
+
|
|
123
|
+
// src/utils.ts
|
|
124
|
+
function choice(first, ...options) {
|
|
125
|
+
const all = [first, ...options];
|
|
126
|
+
return all[randomInt(all.length)];
|
|
93
127
|
}
|
|
94
128
|
function randomInt(maxmin, max, rnd = Math.random) {
|
|
95
129
|
if (max != void 0)
|
|
96
|
-
return Math.floor(rnd() * (
|
|
130
|
+
return Math.floor(rnd() * (max - maxmin)) + maxmin;
|
|
97
131
|
return Math.floor(rnd() * maxmin);
|
|
98
132
|
}
|
|
99
133
|
function trimEnd(str, ...chs) {
|
|
134
|
+
chs = chs.filter((ch) => ch.length > 0);
|
|
100
135
|
if (!chs || chs.length == 0) return str.trimEnd();
|
|
101
136
|
let trimmed = false;
|
|
102
137
|
do {
|
|
@@ -110,6 +145,7 @@ function trimEnd(str, ...chs) {
|
|
|
110
145
|
return str;
|
|
111
146
|
}
|
|
112
147
|
function trimStart(str, ...chs) {
|
|
148
|
+
chs = chs.filter((ch) => ch.length > 0);
|
|
113
149
|
if (!chs || chs.length == 0) return str.trimStart();
|
|
114
150
|
let trimmed = false;
|
|
115
151
|
do {
|
|
@@ -125,10 +161,10 @@ function trimStart(str, ...chs) {
|
|
|
125
161
|
function toCapitalCase(str) {
|
|
126
162
|
return str.slice(0, 1).toUpperCase() + str.slice(1);
|
|
127
163
|
}
|
|
128
|
-
async function checkIfFileIsBlocked(
|
|
164
|
+
async function checkIfFileIsBlocked(path7) {
|
|
129
165
|
let file;
|
|
130
166
|
try {
|
|
131
|
-
file = await fs.open(
|
|
167
|
+
file = await fs.open(path7, "r+");
|
|
132
168
|
return false;
|
|
133
169
|
} catch (err) {
|
|
134
170
|
if (!err) return false;
|
|
@@ -181,22 +217,791 @@ function realpathAllowMissing(inputPath) {
|
|
|
181
217
|
function isMissingPathError(err) {
|
|
182
218
|
return err instanceof Error && "code" in err && (err.code === "ENOENT" || err.code === "ENOTDIR");
|
|
183
219
|
}
|
|
220
|
+
function getSafePathResolver(workdir, checkFilesIsInsidePath) {
|
|
221
|
+
const allowedRealPath = checkFilesIsInsidePath === false ? false : realpathAllowMissing(checkFilesIsInsidePath === "" ? workdir : checkFilesIsInsidePath);
|
|
222
|
+
const resolvePath = (fname, origin) => {
|
|
223
|
+
if (process.platform != "win32") fname = fname.replaceAll("\\", "/");
|
|
224
|
+
if (process.platform == "win32" && fname.startsWith("/")) fname = "." + fname;
|
|
225
|
+
const targetPath = path.isAbsolute(fname) ? path.resolve(fname) : path.resolve(origin || workdir, fname);
|
|
226
|
+
if (allowedRealPath !== false) {
|
|
227
|
+
const targetRealPath = realpathAllowMissing(targetPath);
|
|
228
|
+
const relative2 = path.relative(allowedRealPath, targetRealPath);
|
|
229
|
+
const isOutside = relative2 === ".." || relative2.startsWith(`..${path.sep}`) || path.isAbsolute(relative2);
|
|
230
|
+
if (isOutside) throw new UserInputError(`Access denied: "${fname}" resolves outside the working directory.`);
|
|
231
|
+
}
|
|
232
|
+
return targetPath;
|
|
233
|
+
};
|
|
234
|
+
const getRelative = (fname) => {
|
|
235
|
+
const targetPath = resolvePath(fname);
|
|
236
|
+
if (allowedRealPath !== false)
|
|
237
|
+
return path.relative(allowedRealPath, targetPath);
|
|
238
|
+
return path.relative(workdir, targetPath);
|
|
239
|
+
};
|
|
240
|
+
return { resolvePath, getRelative };
|
|
241
|
+
}
|
|
242
|
+
function isLocalFilePath(link) {
|
|
243
|
+
const value = link.trim();
|
|
244
|
+
if (value.startsWith("//")) return false;
|
|
245
|
+
if (path.win32.isAbsolute(value)) return true;
|
|
246
|
+
if (/^[a-zA-Z][a-zA-Z0-9+.-]*:/.test(value)) return false;
|
|
247
|
+
if (value.startsWith("#")) return false;
|
|
248
|
+
return true;
|
|
249
|
+
}
|
|
250
|
+
function deepOverwrite(target, source) {
|
|
251
|
+
const result = Object.assign(/* @__PURE__ */ Object.create(null), target);
|
|
252
|
+
for (const [key, sourceValue] of Object.entries(source)) {
|
|
253
|
+
if (key === "__proto__" || key === "prototype" || key === "constructor")
|
|
254
|
+
continue;
|
|
255
|
+
const targetValue = Object.hasOwn(result, key) ? result[key] : void 0;
|
|
256
|
+
result[key] = isJSONDict(targetValue) && isJSONDict(sourceValue) ? deepOverwrite(targetValue, sourceValue) : sourceValue;
|
|
257
|
+
}
|
|
258
|
+
return result;
|
|
259
|
+
function isJSONDict(value) {
|
|
260
|
+
return value !== null && typeof value === "object" && !Array.isArray(value);
|
|
261
|
+
}
|
|
262
|
+
}
|
|
184
263
|
|
|
185
264
|
// src/parser.ts
|
|
265
|
+
import path3 from "path";
|
|
266
|
+
|
|
267
|
+
// src/preprocessor.ts
|
|
268
|
+
import fs2 from "fs/promises";
|
|
186
269
|
import path2 from "path";
|
|
187
|
-
async function
|
|
188
|
-
const
|
|
270
|
+
async function preprocess(file, variables, checkFilesIsInsidePath, options, signal, logwarn = console.warn) {
|
|
271
|
+
const rootFile = path2.resolve(file);
|
|
272
|
+
const { resolvePath, getRelative } = getSafePathResolver(path2.dirname(rootFile), checkFilesIsInsidePath);
|
|
273
|
+
const ctx = {
|
|
274
|
+
options,
|
|
275
|
+
workdir: path2.dirname(rootFile),
|
|
276
|
+
includes: 0,
|
|
277
|
+
iterations: 0,
|
|
278
|
+
inputLength: 0,
|
|
279
|
+
generatedLength: 0,
|
|
280
|
+
signal: signal || void 0,
|
|
281
|
+
logwarn,
|
|
282
|
+
countAsOutput(v) {
|
|
283
|
+
ctx.generatedLength += v.length;
|
|
284
|
+
if (options.maxGeneratedLength !== void 0 && ctx.generatedLength > options.maxGeneratedLength)
|
|
285
|
+
throw new UserInputError(`Maximum output length exceeded (${options.maxGeneratedLength})`);
|
|
286
|
+
return v;
|
|
287
|
+
}
|
|
288
|
+
};
|
|
289
|
+
const rootNode = { type: "include", path: rootFile, dict: {}, trimEnd: false, trimStart: false };
|
|
290
|
+
return processInclude(rootNode, variables, resolvePath, getRelative, [], ctx);
|
|
291
|
+
}
|
|
292
|
+
async function processInclude(node, variables, resolvePath, getRelative, stack, ctx) {
|
|
293
|
+
ctx.signal?.throwIfAborted();
|
|
294
|
+
const fpath = resolvePath(node.path);
|
|
295
|
+
const relativePath = getRelative(fpath);
|
|
296
|
+
const dir = path2.dirname(fpath);
|
|
297
|
+
if (ctx.options.maxIncludeDepth !== void 0 && stack.length > ctx.options.maxIncludeDepth)
|
|
298
|
+
throw new UserInputError(`Maximum include depth exceeded while including "${relativePath}":
|
|
299
|
+
${stack.join(" -> ")}`);
|
|
300
|
+
if (stack.length > 0) {
|
|
301
|
+
ctx.includes++;
|
|
302
|
+
const maxIncludes = ctx.options.maxIncludes === void 0 ? 1e3 : ctx.options.maxIncludes;
|
|
303
|
+
if (ctx.includes > maxIncludes)
|
|
304
|
+
throw new UserInputError(`Maximum include count exceeded (${maxIncludes})`);
|
|
305
|
+
}
|
|
306
|
+
let rawContent;
|
|
307
|
+
try {
|
|
308
|
+
if (ctx.options.maxFileLength !== void 0) {
|
|
309
|
+
const size = (await fs2.stat(fpath)).size;
|
|
310
|
+
if (size > ctx.options.maxFileLength * 3)
|
|
311
|
+
throw new UserInputError(`File is too large (${size} > ${ctx.options.maxFileLength * 3}) "${relativePath}"`);
|
|
312
|
+
}
|
|
313
|
+
rawContent = await fs2.readFile(fpath, { encoding: "utf8", signal: ctx.signal });
|
|
314
|
+
if (ctx.options.maxFileLength !== void 0 && rawContent.length > ctx.options.maxFileLength)
|
|
315
|
+
throw new UserInputError(`File is too large (${rawContent.length} > ${ctx.options.maxFileLength}) "${relativePath}"`);
|
|
316
|
+
ctx.inputLength += rawContent.length;
|
|
317
|
+
if (ctx.options.maxTotalInputLength !== void 0 && ctx.inputLength > ctx.options.maxTotalInputLength)
|
|
318
|
+
throw new UserInputError(`Maximum total input length exceeded (${ctx.inputLength} > ${ctx.options.maxTotalInputLength})`);
|
|
319
|
+
} catch (err) {
|
|
320
|
+
ctx.signal?.throwIfAborted();
|
|
321
|
+
if (err instanceof UserInputError) throw err;
|
|
322
|
+
const reason = err instanceof Error ? err.message : String(err);
|
|
323
|
+
throw new UserInputError(`Failed to read included file "${relativePath}": ${reason}`);
|
|
324
|
+
}
|
|
325
|
+
let content = rawContent;
|
|
326
|
+
if (variables) {
|
|
327
|
+
variables = deepOverwrite(variables, node.dict);
|
|
328
|
+
const nodes = parseFExpr(rawContent);
|
|
329
|
+
nodes.forEach((n) => n.type == "expr" && (n.source.fname = relativePath));
|
|
330
|
+
content = processExpressions(nodes, variables || {}, ctx);
|
|
331
|
+
} else {
|
|
332
|
+
variables = {};
|
|
333
|
+
ctx.countAsOutput(content);
|
|
334
|
+
}
|
|
335
|
+
content = await fixLinks(content, fixLink);
|
|
336
|
+
if (!ctx.options.allowIncludes) return content;
|
|
337
|
+
const includes = await parseIncludes(content, ctx.logwarn);
|
|
338
|
+
let result = "";
|
|
339
|
+
for (const node2 of includes) {
|
|
340
|
+
if (node2.type == "text") result += node2.value;
|
|
341
|
+
else {
|
|
342
|
+
let r = await processInclude(node2, variables, resolvePath, getRelative, [...stack, relativePath], ctx);
|
|
343
|
+
if (node2.trimStart) r = r.trimStart();
|
|
344
|
+
if (node2.trimEnd) r = r.trimEnd();
|
|
345
|
+
result += r;
|
|
346
|
+
}
|
|
347
|
+
}
|
|
348
|
+
if (node.sourceLineCount) {
|
|
349
|
+
const delta = result.split("\n").length - node.sourceLineCount + 1;
|
|
350
|
+
result += "\n\uE100" + delta + "\uE101";
|
|
351
|
+
}
|
|
352
|
+
return result;
|
|
353
|
+
function fixLink(oldpath) {
|
|
354
|
+
oldpath = oldpath.trim();
|
|
355
|
+
oldpath = trimEnd(trimStart(oldpath, "<", '"'), ">", '"');
|
|
356
|
+
const newpath = !isLocalFilePath(oldpath) ? oldpath : path2.relative(ctx.workdir, resolvePath(oldpath, dir)).split(path2.sep).join("/");
|
|
357
|
+
if (newpath.includes(" ")) return `<${newpath}>`;
|
|
358
|
+
return newpath;
|
|
359
|
+
}
|
|
360
|
+
}
|
|
361
|
+
function parseFExpr(content) {
|
|
362
|
+
const nodes = [];
|
|
363
|
+
let textStart = 0;
|
|
364
|
+
let ln = 1;
|
|
365
|
+
function countLines(start, end) {
|
|
366
|
+
for (let i = start; i < end; i++)
|
|
367
|
+
if (content[i] == "\n") ln++;
|
|
368
|
+
}
|
|
369
|
+
function pushText(start, end) {
|
|
370
|
+
if (start >= end) return;
|
|
371
|
+
const value = content.slice(start, end);
|
|
372
|
+
const prev = nodes.at(-1);
|
|
373
|
+
countLines(start, end);
|
|
374
|
+
if (prev?.type == "text")
|
|
375
|
+
prev.value += value;
|
|
376
|
+
else
|
|
377
|
+
nodes.push({ type: "text", value });
|
|
378
|
+
}
|
|
379
|
+
function isEscaped(i) {
|
|
380
|
+
let slashes = 0;
|
|
381
|
+
for (i--; i >= 0 && content[i] == "\\"; i--)
|
|
382
|
+
slashes++;
|
|
383
|
+
return slashes % 2 != 0;
|
|
384
|
+
}
|
|
385
|
+
for (let i = 0; i < content.length; i++) {
|
|
386
|
+
if (content.startsWith("<!--", i)) {
|
|
387
|
+
const end2 = content.indexOf("-->", i + 4);
|
|
388
|
+
if (end2 < 0) break;
|
|
389
|
+
i = end2 + 2;
|
|
390
|
+
continue;
|
|
391
|
+
}
|
|
392
|
+
if (!content.startsWith("{{", i))
|
|
393
|
+
continue;
|
|
394
|
+
if (isEscaped(i)) {
|
|
395
|
+
pushText(textStart, i - 1);
|
|
396
|
+
pushText(i, i + 2);
|
|
397
|
+
textStart = i + 2;
|
|
398
|
+
i++;
|
|
399
|
+
continue;
|
|
400
|
+
}
|
|
401
|
+
const trimLeft = content[i + 2] == "-" && !/\d/.test(content[i + 3] || "");
|
|
402
|
+
const valueStart = i + 2 + (trimLeft ? 1 : 0);
|
|
403
|
+
let end = -1;
|
|
404
|
+
let inString = false;
|
|
405
|
+
let escaped = false;
|
|
406
|
+
for (let j = valueStart; j < content.length - 1; j++) {
|
|
407
|
+
const ch = content[j];
|
|
408
|
+
if (inString) {
|
|
409
|
+
if (escaped) escaped = false;
|
|
410
|
+
else if (ch === "\\") escaped = true;
|
|
411
|
+
else if (ch === '"') inString = false;
|
|
412
|
+
continue;
|
|
413
|
+
}
|
|
414
|
+
if (ch == '"') {
|
|
415
|
+
inString = true;
|
|
416
|
+
continue;
|
|
417
|
+
}
|
|
418
|
+
if (ch === "}" && content[j + 1] === "}") {
|
|
419
|
+
end = j;
|
|
420
|
+
break;
|
|
421
|
+
}
|
|
422
|
+
}
|
|
423
|
+
if (end < 0) continue;
|
|
424
|
+
const trimRight = end > valueStart && content[end - 1] == "-";
|
|
425
|
+
const valueEnd = trimRight ? end - 1 : end;
|
|
426
|
+
const hasQuotes = i > 0 && content[i - 1] == '"' && !isEscaped(i - 1) && content[end + 2] === '"';
|
|
427
|
+
let textEnd = hasQuotes ? i - 1 : i;
|
|
428
|
+
const untrimmedTextEnd = textEnd;
|
|
429
|
+
if (trimLeft)
|
|
430
|
+
while (textEnd > textStart && /\s/.test(content[textEnd - 1])) textEnd--;
|
|
431
|
+
pushText(textStart, textEnd);
|
|
432
|
+
countLines(textEnd, untrimmedTextEnd);
|
|
433
|
+
const lineStart = content.lastIndexOf("\n", i + 2);
|
|
434
|
+
const source = { ln, col: lineStart < 0 ? i + 1 : i - lineStart };
|
|
435
|
+
nodes.push({ type: "expr", value: content.slice(valueStart, valueEnd), hasQuotes, trimLeft, trimRight, source });
|
|
436
|
+
let next = hasQuotes ? end + 3 : end + 2;
|
|
437
|
+
if (trimRight) {
|
|
438
|
+
const untrimmedNext = next;
|
|
439
|
+
while (next < content.length && /\s/.test(content[next])) next++;
|
|
440
|
+
countLines(untrimmedNext, next);
|
|
441
|
+
}
|
|
442
|
+
textStart = next;
|
|
443
|
+
i = next - 1;
|
|
444
|
+
}
|
|
445
|
+
pushText(textStart, content.length);
|
|
446
|
+
return nodes;
|
|
447
|
+
}
|
|
448
|
+
function getByPath(value, path7) {
|
|
449
|
+
for (const key of path7) {
|
|
450
|
+
if (value === null || value === void 0)
|
|
451
|
+
return value;
|
|
452
|
+
const obj = Object(value);
|
|
453
|
+
if (!Object.hasOwn(obj, key))
|
|
454
|
+
return void 0;
|
|
455
|
+
value = obj[key];
|
|
456
|
+
}
|
|
457
|
+
return value;
|
|
458
|
+
}
|
|
459
|
+
function processExpressions(nodes, variables, ctx) {
|
|
460
|
+
const expr = parseExp(nodes, ctx);
|
|
461
|
+
const tree = buildExpTree(expr, ctx);
|
|
462
|
+
const res = execExpTree(tree, variables, ctx);
|
|
463
|
+
return res.join("");
|
|
464
|
+
}
|
|
465
|
+
function parseExp(nodes, ctx) {
|
|
466
|
+
const reVar = String.raw`[A-Za-z_$][A-Za-z0-9_$]*(?:\.[A-Za-z_$][A-Za-z0-9_$]*|\.\d+|\[\d+\])*`;
|
|
467
|
+
const reIdent = String.raw`[A-Za-z_$][A-Za-z0-9_$]*`;
|
|
468
|
+
const reStr = String.raw`"(?:\\.|[^"\\])*"`;
|
|
469
|
+
const reNum = String.raw`-?(?:0|[1-9]\d*)(?:\.\d+)?(?:[eE][+-]?\d+)?`;
|
|
470
|
+
const reVal = String.raw`(?:${reStr}|${reNum}|true|false|null)`;
|
|
471
|
+
const reOCompare = String.raw`(?:==|!=|<|<=|>|>=|\bin\b)`;
|
|
472
|
+
const reOMath = String.raw`(?:\+|-|/|//|%|\*|\*\*)`;
|
|
473
|
+
const reOLogical = String.raw`(?:and|or|&&|\|\||\?\?)`;
|
|
474
|
+
const re_var = new RegExp(String.raw`^\s*((?:${reVar})|${reVal})\s*(?:(${reOMath}|${reOLogical})\s*((?:${reVar})|${reVal}))?\s*$`);
|
|
475
|
+
const re_val = new RegExp(String.raw`^${reVal}$`);
|
|
476
|
+
const re_str = new RegExp(String.raw`^\s*(${reStr})\s*$`);
|
|
477
|
+
const re_else = /^:else\s*$/;
|
|
478
|
+
const re_for = new RegExp(String.raw`^#for\s+(${reIdent})(?:\s*,\s*(${reIdent}))?\s+in\s+(${reVar}|${reNum}|${reStr})\s*$`);
|
|
479
|
+
const re_forEnd = /^\/for\s*$/;
|
|
480
|
+
const re_if = new RegExp(String.raw`^#if\s+((?:${reVar})|${reVal})\s*(?:(${reOCompare}|${reOLogical})\s*((?:${reVar})|${reVal}))?\s*$`);
|
|
481
|
+
const re_ifEnd = /^\/if\s*$/;
|
|
482
|
+
const re_path = /[A-Za-z_$][A-Za-z0-9_$]*|\d+/g;
|
|
483
|
+
return nodes.map((node) => {
|
|
484
|
+
ctx.signal?.throwIfAborted();
|
|
485
|
+
if (node.type == "text") return { type: "text", value: node.value };
|
|
486
|
+
const enode = node;
|
|
487
|
+
const source = node.source;
|
|
488
|
+
const expression = `{{${node.trimLeft ? "-" : ""}${node.value}${node.trimRight ? "-" : ""}}}`;
|
|
489
|
+
const rawValue = node.hasQuotes ? `"${expression}"` : expression;
|
|
490
|
+
function retWarn(msg) {
|
|
491
|
+
ctx.logwarn(msg + pos(enode));
|
|
492
|
+
return { type: "text", value: rawValue };
|
|
493
|
+
}
|
|
494
|
+
const mvar = re_var.exec(node.value);
|
|
495
|
+
if (mvar) {
|
|
496
|
+
const left = parseOperand(mvar[1]);
|
|
497
|
+
if (!left) return retWarn("Unsupported expression: " + rawValue);
|
|
498
|
+
const operator = mvar[2];
|
|
499
|
+
if (!operator) return { type: "var", left, source, hasQuotes: node.hasQuotes };
|
|
500
|
+
const right = parseOperand(mvar[3]);
|
|
501
|
+
if (!right) return retWarn("Unsupported expression: " + rawValue);
|
|
502
|
+
return { type: "var", left, operator, right, source, hasQuotes: node.hasQuotes };
|
|
503
|
+
}
|
|
504
|
+
const mstr = re_str.exec(node.value);
|
|
505
|
+
if (mstr) {
|
|
506
|
+
const str = safeParse(node.value);
|
|
507
|
+
if (str === void 0) return retWarn("Cant parse string: " + node.value);
|
|
508
|
+
return { type: "text", value: node.hasQuotes ? JSON.stringify(str) : `${str}` };
|
|
509
|
+
}
|
|
510
|
+
const mfor = re_for.exec(node.value);
|
|
511
|
+
if (mfor) {
|
|
512
|
+
if (!ctx.options.allowLoops) return retWarn("Loop expressions are not allowed");
|
|
513
|
+
const varValue = mfor[1];
|
|
514
|
+
const varKey = mfor[2];
|
|
515
|
+
const array = parseOperand(mfor[3]);
|
|
516
|
+
if (!array) return retWarn("Unsupported expression: " + rawValue);
|
|
517
|
+
return { type: "for", varValue, varKey, array, source };
|
|
518
|
+
}
|
|
519
|
+
const mif = re_if.exec(node.value);
|
|
520
|
+
if (mif) {
|
|
521
|
+
const left = parseOperand(mif[1]);
|
|
522
|
+
if (!left) return retWarn("Unsupported expression: " + rawValue);
|
|
523
|
+
const operator = mif[2];
|
|
524
|
+
if (!operator) return { type: "if", left, source };
|
|
525
|
+
const right = parseOperand(mif[3]);
|
|
526
|
+
if (!right) return retWarn("Unsupported expression: " + rawValue);
|
|
527
|
+
return { type: "if", left, operator, right, source };
|
|
528
|
+
}
|
|
529
|
+
if (re_else.exec(node.value)) return { type: "else", source };
|
|
530
|
+
if (re_ifEnd.exec(node.value)) return { type: "ifEnd", source };
|
|
531
|
+
if (re_forEnd.exec(node.value)) {
|
|
532
|
+
if (!ctx.options.allowLoops) return retWarn("Loop expressions are not allowed");
|
|
533
|
+
return { type: "forEnd", source };
|
|
534
|
+
}
|
|
535
|
+
return retWarn("Unsupported expression: " + rawValue);
|
|
536
|
+
});
|
|
537
|
+
function pos(node) {
|
|
538
|
+
return ` [Ln ${node.source.ln}, Col ${node.source.col}] (${node.source.fname})`;
|
|
539
|
+
}
|
|
540
|
+
function safeParse(value) {
|
|
541
|
+
try {
|
|
542
|
+
return JSON.parse(value);
|
|
543
|
+
} catch {
|
|
544
|
+
return void 0;
|
|
545
|
+
}
|
|
546
|
+
}
|
|
547
|
+
function parsePath(value) {
|
|
548
|
+
const parts = value?.match(re_path);
|
|
549
|
+
if (!parts?.length) return void 0;
|
|
550
|
+
return parts.map((part) => /^\d+$/.test(part) ? Number(part) : part);
|
|
551
|
+
}
|
|
552
|
+
function parseOperand(value) {
|
|
553
|
+
if (!value) return void 0;
|
|
554
|
+
if (re_val.test(value)) {
|
|
555
|
+
const val = safeParse(value);
|
|
556
|
+
if (val === void 0) return null;
|
|
557
|
+
return { t: "val", val };
|
|
558
|
+
}
|
|
559
|
+
const path7 = parsePath(value);
|
|
560
|
+
if (!path7) return null;
|
|
561
|
+
return { t: "var", path: path7 };
|
|
562
|
+
}
|
|
563
|
+
}
|
|
564
|
+
function buildExpTree(nodes, ctx, depth = 0, source) {
|
|
565
|
+
ctx.signal?.throwIfAborted();
|
|
566
|
+
if (ctx.options.maxDepth !== void 0 && depth >= ctx.options.maxDepth) {
|
|
567
|
+
ctx.logwarn(`Maximum expression nesting depth exceeded (${ctx.options.maxDepth})${source ? pos({ source }) : ""}`);
|
|
568
|
+
return nodes.map((n) => {
|
|
569
|
+
if (n.type == "text" || n.type == "var") return n;
|
|
570
|
+
if (n.type == "else" || n.type == "forEnd" || n.type == "ifEnd") {
|
|
571
|
+
const str = n.type == "else" ? "{{:else}}" : n.type == "ifEnd" ? "{{/if}}" : "{{/for}}";
|
|
572
|
+
return { type: "text", value: str };
|
|
573
|
+
}
|
|
574
|
+
if (n.type == "for") {
|
|
575
|
+
const second = n.varKey ? `, ${n.varKey}` : "";
|
|
576
|
+
return { type: "text", value: `{{#for ${n.varValue}${second} in ${strOperand(n.array)}}}` };
|
|
577
|
+
}
|
|
578
|
+
if (n.type == "if") {
|
|
579
|
+
const right = n.right ? ` ${n.operator} ${strOperand(n.right)}` : "";
|
|
580
|
+
return { type: "text", value: `{{#if ${strOperand(n.left)}${right}}}` };
|
|
581
|
+
}
|
|
582
|
+
n;
|
|
583
|
+
return { type: "text", value: "" };
|
|
584
|
+
});
|
|
585
|
+
}
|
|
586
|
+
const tree = [];
|
|
587
|
+
for (let i = 0; i < nodes.length; i++) {
|
|
588
|
+
const node = nodes[i];
|
|
589
|
+
if (node.type == "text" || node.type == "var") {
|
|
590
|
+
tree.push(node);
|
|
591
|
+
}
|
|
592
|
+
if (node.type == "else" || node.type == "forEnd" || node.type == "ifEnd") {
|
|
593
|
+
const str = node.type == "else" ? "{{:else}}" : node.type == "ifEnd" ? "{{/if}}" : "{{/for}}";
|
|
594
|
+
ctx.logwarn(`Unexpected expression (${str})` + pos(node));
|
|
595
|
+
tree.push({ type: "text", value: str });
|
|
596
|
+
}
|
|
597
|
+
if (node.type == "for" || node.type == "if") {
|
|
598
|
+
const r = parseBlockWithElse(i);
|
|
599
|
+
if (!r) {
|
|
600
|
+
if (node.type == "for") {
|
|
601
|
+
const second = node.varKey ? `, ${node.varKey}` : "";
|
|
602
|
+
tree.push({ type: "text", value: `{{#for ${node.varValue}${second} in ${strOperand(node.array)}}}` });
|
|
603
|
+
} else if (node.type == "if") {
|
|
604
|
+
const right = node.right ? ` ${node.operator} ${strOperand(node.right)}` : "";
|
|
605
|
+
tree.push({ type: "text", value: `{{#if ${strOperand(node.left)}${right}}}` });
|
|
606
|
+
}
|
|
607
|
+
} else {
|
|
608
|
+
const { body, bodyElse, endI } = r;
|
|
609
|
+
i = endI;
|
|
610
|
+
tree.push({ ...node, body, bodyElse });
|
|
611
|
+
}
|
|
612
|
+
}
|
|
613
|
+
}
|
|
614
|
+
return tree;
|
|
615
|
+
function pos(node) {
|
|
616
|
+
return ` [Ln ${node.source.ln}, Col ${node.source.col}] (${node.source.fname})`;
|
|
617
|
+
}
|
|
618
|
+
function strOperand(v) {
|
|
619
|
+
return v.t == "val" ? JSON.stringify(v.val) : v.path.join(".");
|
|
620
|
+
}
|
|
621
|
+
function findBlockEnd(startI, type, skipElse) {
|
|
622
|
+
const stack = [];
|
|
623
|
+
for (let i = startI; i < nodes.length; i++) {
|
|
624
|
+
const node = nodes[i];
|
|
625
|
+
if (node.type == "if") stack.push("if");
|
|
626
|
+
else if (node.type == "for") stack.push("for");
|
|
627
|
+
else if (node.type == "forEnd" && stack.at(-1) == "for") stack.pop();
|
|
628
|
+
else if (node.type == "ifEnd" && stack.at(-1) == "if") stack.pop();
|
|
629
|
+
else if (stack.length == 0) {
|
|
630
|
+
if (node.type == "else" && !skipElse) return i;
|
|
631
|
+
if (node.type == "forEnd" && type == "for") return i;
|
|
632
|
+
if (node.type == "ifEnd" && type == "if") return i;
|
|
633
|
+
}
|
|
634
|
+
}
|
|
635
|
+
return -1;
|
|
636
|
+
}
|
|
637
|
+
function parseBlockWithElse(startI) {
|
|
638
|
+
const node = nodes[startI];
|
|
639
|
+
if (node.type != "for" && node.type != "if") return null;
|
|
640
|
+
const n = node;
|
|
641
|
+
const type = n.type;
|
|
642
|
+
const unclosed = (m) => ctx.logwarn(`Unclosed ${type} ${m}` + pos(n));
|
|
643
|
+
const bodyEnd = findBlockEnd(startI + 1, type, false);
|
|
644
|
+
if (bodyEnd < 0) {
|
|
645
|
+
unclosed("body");
|
|
646
|
+
return null;
|
|
647
|
+
}
|
|
648
|
+
const bodyNodes = nodes.slice(startI + 1, bodyEnd);
|
|
649
|
+
const body = buildExpTree(bodyNodes, ctx, depth + 1, n.source);
|
|
650
|
+
let endI = bodyEnd;
|
|
651
|
+
let bodyElse;
|
|
652
|
+
if (nodes[bodyEnd].type == "else") {
|
|
653
|
+
const bodyElseEnd = findBlockEnd(bodyEnd + 1, type, true);
|
|
654
|
+
if (bodyElseEnd < 0) {
|
|
655
|
+
unclosed("else block");
|
|
656
|
+
return null;
|
|
657
|
+
}
|
|
658
|
+
const bodyElseNodes = nodes.slice(bodyEnd + 1, bodyElseEnd);
|
|
659
|
+
bodyElse = buildExpTree(bodyElseNodes, ctx, depth + 1, n.source);
|
|
660
|
+
endI = bodyElseEnd;
|
|
661
|
+
}
|
|
662
|
+
return { body, bodyElse, endI };
|
|
663
|
+
}
|
|
664
|
+
}
|
|
665
|
+
function execExpTree(tree, variables, ctx) {
|
|
666
|
+
return tree.flatMap((node) => {
|
|
667
|
+
ctx.signal?.throwIfAborted();
|
|
668
|
+
if (node.type == "text") return ctx.countAsOutput(node.value);
|
|
669
|
+
if (node.type == "var") {
|
|
670
|
+
const n = node;
|
|
671
|
+
const value = execOperator(node.left, node.operator, node.right);
|
|
672
|
+
const rawValue = () => {
|
|
673
|
+
const right = n.right ? ` ${n.operator} ${strOperand(n.right)}` : "";
|
|
674
|
+
const value2 = `{{${strOperand(n.left)}${right}}}`;
|
|
675
|
+
return n.hasQuotes ? `"${value2}"` : value2;
|
|
676
|
+
};
|
|
677
|
+
if (value === void 0) ctx.logwarn("Undefined variable: " + rawValue() + pos(node));
|
|
678
|
+
const result = value === void 0 ? rawValue() : typeof value == "object" || node.hasQuotes ? JSON.stringify(value) : `${value}`;
|
|
679
|
+
return ctx.countAsOutput(result);
|
|
680
|
+
}
|
|
681
|
+
if (node.type == "if") {
|
|
682
|
+
const res = execOperator(node.left, node.operator, node.right);
|
|
683
|
+
const result = res && typeof res == "object" ? (Array.isArray(res) ? res : Object.keys(res)).length > 0 : !!res;
|
|
684
|
+
if (result) return execExpTree(node.body, variables, ctx);
|
|
685
|
+
if (node.bodyElse) return execExpTree(node.bodyElse, variables, ctx);
|
|
686
|
+
return "";
|
|
687
|
+
}
|
|
688
|
+
if (node.type == "for") {
|
|
689
|
+
if (!ctx.options.allowLoops) throw new UserInputError("Loop expressions are not allowed");
|
|
690
|
+
const arr = node.array.t == "val" ? node.array.val : getByPath(variables, node.array.path);
|
|
691
|
+
if (!arr || typeof arr == "object" && (Array.isArray(arr) ? arr : Object.keys(arr)).length == 0) {
|
|
692
|
+
if (node.bodyElse) return execExpTree(node.bodyElse, variables, ctx);
|
|
693
|
+
return "";
|
|
694
|
+
}
|
|
695
|
+
const values = (() => {
|
|
696
|
+
const n = node;
|
|
697
|
+
function checkLoopIterations(count, final = true) {
|
|
698
|
+
const max = ctx.options.maxLoopIterations === void 0 ? 1e3 : ctx.options.maxLoopIterations;
|
|
699
|
+
if (count > max)
|
|
700
|
+
throw new UserInputError(`Maximum loop iterations exceeded (${final ? count + " " : ""}> ${max})` + pos(n));
|
|
701
|
+
if (ctx.options.maxTotalIterations !== void 0 && ctx.iterations + count > ctx.options.maxTotalIterations)
|
|
702
|
+
throw new UserInputError(`Maximum total loop iterations exceeded (${ctx.options.maxTotalIterations})`);
|
|
703
|
+
}
|
|
704
|
+
if (typeof arr == "number" && Number.isInteger(arr)) {
|
|
705
|
+
if (arr < 0) {
|
|
706
|
+
ctx.logwarn(`For loop source is a negative integer (${arr})` + pos(node));
|
|
707
|
+
return [];
|
|
708
|
+
}
|
|
709
|
+
checkLoopIterations(arr);
|
|
710
|
+
return Array.from({ length: arr }, (_, i) => ({ k: i, v: i + 1 }));
|
|
711
|
+
}
|
|
712
|
+
if (typeof arr == "object") {
|
|
713
|
+
if (Array.isArray(arr)) {
|
|
714
|
+
checkLoopIterations(arr.length);
|
|
715
|
+
return arr.map((v, i) => ({ k: i, v }));
|
|
716
|
+
}
|
|
717
|
+
const keys = Object.keys(arr);
|
|
718
|
+
checkLoopIterations(keys.length);
|
|
719
|
+
return keys.map((k) => ({ k, v: arr[k] }));
|
|
720
|
+
}
|
|
721
|
+
if (typeof arr == "string") {
|
|
722
|
+
const values2 = [];
|
|
723
|
+
for (const v of arr) {
|
|
724
|
+
values2.push({ k: values2.length, v });
|
|
725
|
+
checkLoopIterations(values2.length, false);
|
|
726
|
+
}
|
|
727
|
+
return values2;
|
|
728
|
+
}
|
|
729
|
+
})();
|
|
730
|
+
if (!values) {
|
|
731
|
+
ctx.logwarn(`For loop source is not object, string or integer (${arr})` + pos(node));
|
|
732
|
+
return "";
|
|
733
|
+
}
|
|
734
|
+
return values.flatMap(({ k, v }) => {
|
|
735
|
+
ctx.signal?.throwIfAborted();
|
|
736
|
+
ctx.iterations++;
|
|
737
|
+
if (ctx.options.maxTotalIterations !== void 0 && ctx.iterations > ctx.options.maxTotalIterations)
|
|
738
|
+
throw new UserInputError(`Maximum total loop iterations exceeded (${ctx.options.maxTotalIterations})`);
|
|
739
|
+
const locals = Object.assign(/* @__PURE__ */ Object.create(null), variables);
|
|
740
|
+
locals[node.varValue] = v;
|
|
741
|
+
if (node.varKey) locals[node.varKey] = k;
|
|
742
|
+
return execExpTree(node.body, locals, ctx);
|
|
743
|
+
});
|
|
744
|
+
}
|
|
745
|
+
node;
|
|
746
|
+
return "";
|
|
747
|
+
});
|
|
748
|
+
function pos(node) {
|
|
749
|
+
return ` [Ln ${node.source.ln}, Col ${node.source.col}] (${node.source.fname})`;
|
|
750
|
+
}
|
|
751
|
+
function strOperand(v) {
|
|
752
|
+
return v.t == "val" ? JSON.stringify(v.val) : v.path.join(".");
|
|
753
|
+
}
|
|
754
|
+
function isEqual(obj1, obj2) {
|
|
755
|
+
if (obj1 === obj2) return true;
|
|
756
|
+
if (obj1 == null || obj2 == null || typeof obj1 !== "object" || typeof obj2 !== "object")
|
|
757
|
+
return obj1 == obj2;
|
|
758
|
+
const keys1 = Object.keys(obj1);
|
|
759
|
+
const keys2 = Object.keys(obj2);
|
|
760
|
+
if (keys1.length !== keys2.length) return false;
|
|
761
|
+
for (const key of keys1)
|
|
762
|
+
if (!Object.hasOwn(obj2, key) || !isEqual(obj1[key], obj2[key]))
|
|
763
|
+
return false;
|
|
764
|
+
return true;
|
|
765
|
+
}
|
|
766
|
+
function execOperator(left, operator, right) {
|
|
767
|
+
const leftV = left.t == "val" ? left.val : getByPath(variables, left.path);
|
|
768
|
+
if (!operator || !right) return leftV;
|
|
769
|
+
const rightV = right.t == "val" ? right.val : getByPath(variables, right.path);
|
|
770
|
+
if (operator == "==") return isEqual(leftV, rightV);
|
|
771
|
+
if (operator == "!=") return !isEqual(leftV, rightV);
|
|
772
|
+
if (operator == "<=") return leftV < rightV || isEqual(leftV, rightV);
|
|
773
|
+
if (operator == ">=") return leftV > rightV || isEqual(leftV, rightV);
|
|
774
|
+
if (operator == "<") return leftV < rightV;
|
|
775
|
+
if (operator == ">") return leftV > rightV;
|
|
776
|
+
if (operator == "in") return !!rightV && typeof rightV == "object" && (Array.isArray(rightV) ? rightV : Object.keys(rightV)).some((v) => isEqual(v, leftV));
|
|
777
|
+
if (operator == "and" || operator == "&&") return leftV && rightV;
|
|
778
|
+
if (operator == "or" || operator == "||") return leftV || rightV;
|
|
779
|
+
if (operator == "??") return leftV ?? rightV;
|
|
780
|
+
if (operator == "+") return leftV + rightV;
|
|
781
|
+
if (operator == "-") return leftV - rightV;
|
|
782
|
+
if (operator == "*") return leftV * rightV;
|
|
783
|
+
if (operator == "**") return Math.pow(leftV, rightV);
|
|
784
|
+
if (operator == "/") return rightV == 0 ? 0 : leftV / rightV;
|
|
785
|
+
if (operator == "//") return Math.floor(rightV == 0 ? 0 : leftV / rightV);
|
|
786
|
+
if (operator == "%") return rightV == 0 ? 0 : leftV % rightV;
|
|
787
|
+
operator;
|
|
788
|
+
return false;
|
|
789
|
+
}
|
|
790
|
+
}
|
|
791
|
+
function parseIncludePath(value) {
|
|
792
|
+
value = value.trim();
|
|
793
|
+
const trimLeft = value.startsWith("-");
|
|
794
|
+
if (trimLeft) value = value.slice(1).trimStart();
|
|
795
|
+
const trimLeftInner = value.startsWith("-");
|
|
796
|
+
if (trimLeftInner) value = value.slice(1).trimStart();
|
|
797
|
+
const trimRight = value.endsWith("-");
|
|
798
|
+
if (trimRight) value = value.slice(0, -1).trimEnd();
|
|
799
|
+
const trimRightInner = value.endsWith("-");
|
|
800
|
+
if (trimRightInner) value = value.slice(0, -1).trimEnd();
|
|
801
|
+
const path7 = trimEnd(trimStart(value, "<", '"'), ">", '"');
|
|
802
|
+
return { path: path7, trimLeft, trimLeftInner, trimRight, trimRightInner };
|
|
803
|
+
}
|
|
804
|
+
function findClosingBrace(content, start) {
|
|
805
|
+
let depth = 0;
|
|
806
|
+
let inString = false;
|
|
807
|
+
let lineComment = false;
|
|
808
|
+
let blockComment = false;
|
|
809
|
+
for (let i = start; i < content.length; i++) {
|
|
810
|
+
const ch = content[i];
|
|
811
|
+
if (lineComment) {
|
|
812
|
+
if (ch == "\n") lineComment = false;
|
|
813
|
+
continue;
|
|
814
|
+
}
|
|
815
|
+
if (blockComment) {
|
|
816
|
+
if (ch == "*" && content[i + 1] == "/") {
|
|
817
|
+
blockComment = false;
|
|
818
|
+
i++;
|
|
819
|
+
}
|
|
820
|
+
continue;
|
|
821
|
+
}
|
|
822
|
+
if (inString) {
|
|
823
|
+
if (ch == "\\") i++;
|
|
824
|
+
else if (ch == '"') inString = false;
|
|
825
|
+
continue;
|
|
826
|
+
}
|
|
827
|
+
if (ch == '"') {
|
|
828
|
+
inString = true;
|
|
829
|
+
continue;
|
|
830
|
+
}
|
|
831
|
+
if (ch == "/" && content[i + 1] == "/") {
|
|
832
|
+
lineComment = true;
|
|
833
|
+
i++;
|
|
834
|
+
continue;
|
|
835
|
+
}
|
|
836
|
+
if (ch == "/" && content[i + 1] == "*") {
|
|
837
|
+
blockComment = true;
|
|
838
|
+
i++;
|
|
839
|
+
continue;
|
|
840
|
+
}
|
|
841
|
+
if (ch == "{") depth++;
|
|
842
|
+
else if (ch == "}" && --depth == 0) return i;
|
|
843
|
+
}
|
|
844
|
+
return -1;
|
|
845
|
+
}
|
|
846
|
+
async function parseIncludes(content, logwarn) {
|
|
847
|
+
const { default: JSONC } = await import("jsonc-simple-parser");
|
|
848
|
+
const re_doc = /^!!\(([^{}]*)\)\s*{(.*)}$/s;
|
|
849
|
+
const re_codeFence = /^( {0,3})(`{3,})([^`]*)$/;
|
|
850
|
+
const nodes = [];
|
|
851
|
+
let textStart = 0;
|
|
852
|
+
function pushText(start, end) {
|
|
853
|
+
if (start >= end) return;
|
|
854
|
+
const value = content.slice(start, end);
|
|
855
|
+
const prev = nodes.at(-1);
|
|
856
|
+
if (prev?.type === "text")
|
|
857
|
+
prev.value += value;
|
|
858
|
+
else
|
|
859
|
+
nodes.push({ type: "text", value });
|
|
860
|
+
}
|
|
861
|
+
let codeFence = 0;
|
|
862
|
+
for (let i = 0; i < content.length; i++) {
|
|
863
|
+
if (i != 0 && content[i - 1] != "\n") continue;
|
|
864
|
+
const lineEnd = content.indexOf("\n", i);
|
|
865
|
+
const line = content.slice(i, lineEnd < 0 ? content.length : lineEnd).replace(/\r$/, "");
|
|
866
|
+
const m_fence = re_codeFence.exec(line);
|
|
867
|
+
if (m_fence) {
|
|
868
|
+
const ticks = m_fence[2].length;
|
|
869
|
+
const suffix = m_fence[3];
|
|
870
|
+
if (codeFence == 0) {
|
|
871
|
+
codeFence = ticks;
|
|
872
|
+
continue;
|
|
873
|
+
}
|
|
874
|
+
if (ticks >= codeFence && suffix.trim() == "") {
|
|
875
|
+
codeFence = 0;
|
|
876
|
+
continue;
|
|
877
|
+
}
|
|
878
|
+
}
|
|
879
|
+
if (codeFence != 0) continue;
|
|
880
|
+
if (content.slice(i, i + 2) !== "!!") continue;
|
|
881
|
+
const m_start = /^!!\([^{}]*\)\s*{/.exec(content.slice(i));
|
|
882
|
+
if (!m_start) continue;
|
|
883
|
+
const end = findClosingBrace(content, i + m_start[0].length - 1);
|
|
884
|
+
if (end < 0) continue;
|
|
885
|
+
const block = content.slice(i, end + 1);
|
|
886
|
+
const m_doc = re_doc.exec(block);
|
|
887
|
+
if (!m_doc) continue;
|
|
888
|
+
const { path: path7, trimLeft, trimLeftInner, trimRight, trimRightInner } = parseIncludePath(m_doc[1]);
|
|
889
|
+
if (!path7.toLowerCase().endsWith(".md")) continue;
|
|
890
|
+
let dict = {};
|
|
891
|
+
try {
|
|
892
|
+
dict = JSONC.parse(`{${m_doc[2]}}`);
|
|
893
|
+
} catch {
|
|
894
|
+
logwarn(`Can't parse include dict: {${m_doc[2].replaceAll("\n", " ")}}`);
|
|
895
|
+
continue;
|
|
896
|
+
}
|
|
897
|
+
let textEnd = i;
|
|
898
|
+
if (trimLeft)
|
|
899
|
+
while (textEnd > textStart && /\s/.test(content[textEnd - 1])) textEnd--;
|
|
900
|
+
pushText(textStart, textEnd);
|
|
901
|
+
nodes.push({ type: "include", path: path7, dict, sourceLineCount: block.split("\n").length, trimStart: trimLeftInner, trimEnd: trimRightInner });
|
|
902
|
+
let next = end + 1;
|
|
903
|
+
if (trimRight)
|
|
904
|
+
while (next < content.length && /\s/.test(content[next])) next++;
|
|
905
|
+
textStart = next;
|
|
906
|
+
i = next - 1;
|
|
907
|
+
}
|
|
908
|
+
pushText(textStart, content.length);
|
|
909
|
+
return nodes;
|
|
910
|
+
}
|
|
911
|
+
async function fixLinks(content, fixLink) {
|
|
912
|
+
const { default: JSONC } = await import("jsonc-simple-parser");
|
|
913
|
+
const re_codeFence = /^( {0,3})(`{3,})([^`]*)$/;
|
|
914
|
+
let result = "";
|
|
915
|
+
let textStart = 0;
|
|
916
|
+
function append(start, end) {
|
|
917
|
+
if (start >= end) return;
|
|
918
|
+
result += content.slice(start, end);
|
|
919
|
+
}
|
|
920
|
+
let codeFence = 0;
|
|
921
|
+
for (let i = 0; i < content.length; i++) {
|
|
922
|
+
if (i != 0 && content[i - 1] != "\n") continue;
|
|
923
|
+
const lineEnd = content.indexOf("\n", i);
|
|
924
|
+
const lineTextEnd = lineEnd < 0 ? content.length : lineEnd - (content[lineEnd - 1] == "\r" ? 1 : 0);
|
|
925
|
+
const line = content.slice(i, lineTextEnd);
|
|
926
|
+
const m_fence = re_codeFence.exec(line);
|
|
927
|
+
if (m_fence) {
|
|
928
|
+
const ticks = m_fence[2].length;
|
|
929
|
+
const suffix = m_fence[3];
|
|
930
|
+
if (codeFence == 0) {
|
|
931
|
+
codeFence = ticks;
|
|
932
|
+
continue;
|
|
933
|
+
}
|
|
934
|
+
if (ticks >= codeFence && suffix.trim() == "") {
|
|
935
|
+
codeFence = 0;
|
|
936
|
+
continue;
|
|
937
|
+
}
|
|
938
|
+
}
|
|
939
|
+
if (codeFence != 0) continue;
|
|
940
|
+
if (content.at(i) != "!") continue;
|
|
941
|
+
const m_img = /^!\[(.*)\]\((.*)\)({(.*)})?$/.exec(line);
|
|
942
|
+
if (m_img) {
|
|
943
|
+
append(textStart, i);
|
|
944
|
+
result += `![${m_img[1]}](${fixLink(m_img[2])})${m_img[3] || ""}`;
|
|
945
|
+
textStart = lineTextEnd;
|
|
946
|
+
i = lineEnd < 0 ? content.length : lineEnd;
|
|
947
|
+
continue;
|
|
948
|
+
}
|
|
949
|
+
const m_start = /^!!\([^{}]*\)\s*{/.exec(content.slice(i));
|
|
950
|
+
if (!m_start) continue;
|
|
951
|
+
const end = findClosingBrace(content, i + m_start[0].length - 1);
|
|
952
|
+
if (end < 0) continue;
|
|
953
|
+
const block = content.slice(i, end + 1);
|
|
954
|
+
const m_doc = /^!!\(([^{}]*)\)(\s*{(.*)})$/s.exec(block);
|
|
955
|
+
if (!m_doc) continue;
|
|
956
|
+
try {
|
|
957
|
+
JSONC.parse(m_doc[2].trim());
|
|
958
|
+
} catch {
|
|
959
|
+
continue;
|
|
960
|
+
}
|
|
961
|
+
const { path: path7, trimLeft, trimLeftInner, trimRight, trimRightInner } = parseIncludePath(m_doc[1]);
|
|
962
|
+
let fixedPath = fixLink(path7);
|
|
963
|
+
if (fixedPath.startsWith("-") || fixedPath.endsWith("-"))
|
|
964
|
+
fixedPath = `"${fixedPath}"`;
|
|
965
|
+
append(textStart, i);
|
|
966
|
+
result += `!!(${trimLeft ? "-" : ""}${trimLeftInner ? "-" : ""}${fixedPath}${trimRightInner ? "-" : ""}${trimRight ? "-" : ""})${m_doc[2]}`;
|
|
967
|
+
textStart = end + 1;
|
|
968
|
+
i = end;
|
|
969
|
+
}
|
|
970
|
+
append(textStart, content.length);
|
|
971
|
+
return result;
|
|
972
|
+
}
|
|
973
|
+
|
|
974
|
+
// src/parser.ts
|
|
975
|
+
async function parseMD(file, variables, templateOptions = {}, checkFilesIsInsidePath = false, signal, logwarn = console.warn) {
|
|
976
|
+
signal?.throwIfAborted();
|
|
977
|
+
const preprocessed = await preprocess(file, variables, checkFilesIsInsidePath, templateOptions, signal, logwarn);
|
|
978
|
+
signal?.throwIfAborted();
|
|
979
|
+
const lines = preprocessed.split("\n");
|
|
189
980
|
const doc = new Doc();
|
|
981
|
+
const warnAt = (line, message) => logwarn(`Line ${doc.mapSourceLine(line)}: ${message}`);
|
|
982
|
+
(function processLineSkips() {
|
|
983
|
+
for (let i = 0; i < lines.length; i++) {
|
|
984
|
+
const line = lines[i];
|
|
985
|
+
if (line.at(0) != "\uE100") continue;
|
|
986
|
+
const m = /\uE100(-?\d+)\uE101/.exec(line);
|
|
987
|
+
if (!m) {
|
|
988
|
+
lines[i] = line.replaceAll("\uE100", "").replaceAll("\uE101", "");
|
|
989
|
+
continue;
|
|
990
|
+
}
|
|
991
|
+
lines[i] = line.slice(m?.[0].length);
|
|
992
|
+
doc.addLineSkip(i + 1, parseInt(m[1]));
|
|
993
|
+
}
|
|
994
|
+
})();
|
|
190
995
|
try {
|
|
191
|
-
doc.title = toCapitalCase(trimEnd(
|
|
192
|
-
const stats = await
|
|
996
|
+
doc.title = toCapitalCase(trimEnd(path3.parse(file).name, ".g"));
|
|
997
|
+
const stats = await fs3.stat(file);
|
|
193
998
|
doc.ctime = stats.birthtime;
|
|
194
999
|
} catch {
|
|
195
1000
|
}
|
|
196
1001
|
function parseList(text, parts, ordered, level = 0) {
|
|
197
1002
|
const startIndex = isFinite(parseInt(parts[0])) ? parseInt(parts[0]) : 1;
|
|
198
1003
|
const mark = ordered ? parts[1] == "." ? "." : ")" : parts[1] == "*" ? "*" : "-";
|
|
199
|
-
const node = { type: "list", ordered, mark, startIndex, items: [{ type: "listItem", text }] };
|
|
1004
|
+
const node = { type: "list", ordered, mark, startIndex, sourceLine: lineI, items: [{ type: "listItem", text, sourceLine: lineI }] };
|
|
200
1005
|
const P = ordered ? "1)" : "*";
|
|
201
1006
|
function skipEmptyLines() {
|
|
202
1007
|
let emptyFound = false;
|
|
@@ -228,7 +1033,7 @@ async function parseMD(file, logwarn = console.warn) {
|
|
|
228
1033
|
if (ln.prefix == "" && last?.type == "listItem")
|
|
229
1034
|
last.text += "\n" + ln.text;
|
|
230
1035
|
else if (ln.prefix == P)
|
|
231
|
-
node.items.push({ type: "listItem", text: ln.text });
|
|
1036
|
+
node.items.push({ type: "listItem", text: ln.text, sourceLine: lineI + 1 });
|
|
232
1037
|
else break;
|
|
233
1038
|
} else if (ln.level > level) {
|
|
234
1039
|
if (ln.level == level + 1) ln = parseLine(ln.text);
|
|
@@ -257,19 +1062,19 @@ async function parseMD(file, logwarn = console.warn) {
|
|
|
257
1062
|
if (isFinite(h)) height = h;
|
|
258
1063
|
}
|
|
259
1064
|
const src = trimEnd(trimStart(parts[1], "<"), ">");
|
|
260
|
-
return { type: "image", text, src, width, height };
|
|
1065
|
+
return { type: "image", text, src, width, height, sourceLine: lineI };
|
|
261
1066
|
}
|
|
262
1067
|
function parseCode(text) {
|
|
263
1068
|
const header = text.split(" ");
|
|
264
1069
|
const title = header.slice(1).join(" ").trim() || void 0;
|
|
265
|
-
const node = { type: "code", lang: header[0], title, code: "" };
|
|
266
|
-
|
|
1070
|
+
const node = { type: "code", lang: header[0], title, code: "", sourceLine: lineI };
|
|
1071
|
+
const code = [];
|
|
267
1072
|
while (lineI < lines.length) {
|
|
268
1073
|
const ln = lines[lineI++];
|
|
269
1074
|
if (ln.startsWith("```")) break;
|
|
270
|
-
|
|
1075
|
+
code.push(ln);
|
|
271
1076
|
}
|
|
272
|
-
node.code =
|
|
1077
|
+
node.code = code.join("\n");
|
|
273
1078
|
return node;
|
|
274
1079
|
}
|
|
275
1080
|
function parseSection(text) {
|
|
@@ -282,9 +1087,78 @@ async function parseMD(file, logwarn = console.warn) {
|
|
|
282
1087
|
return {
|
|
283
1088
|
type: "sectionBreak",
|
|
284
1089
|
pageStart: unpaged ? -1 : isFinite(num) ? num : null,
|
|
285
|
-
orientation: portrait ? "portrait" : landscape ? "landscape" : null
|
|
1090
|
+
orientation: portrait ? "portrait" : landscape ? "landscape" : null,
|
|
1091
|
+
sourceLine: lineI
|
|
286
1092
|
};
|
|
287
1093
|
}
|
|
1094
|
+
function parseHeaderFooter(kind, options) {
|
|
1095
|
+
const value = options.trim().toLowerCase();
|
|
1096
|
+
if (value == "none" || kind == "footer" && value == "auto") {
|
|
1097
|
+
setHeaderFooter(kind, { type: value });
|
|
1098
|
+
return;
|
|
1099
|
+
}
|
|
1100
|
+
const align = value == "" ? "left" : /^align=(left|center|right)$/.exec(value)?.[1];
|
|
1101
|
+
if (!align) {
|
|
1102
|
+
warnAt(lineI, `Wrong ${kind} options: "${options}". Expected align=left, align=center, align=right${kind == "footer" ? ", none, or auto" : ", or none"}.`);
|
|
1103
|
+
return;
|
|
1104
|
+
}
|
|
1105
|
+
const end = `!!end${kind}`;
|
|
1106
|
+
const content = [];
|
|
1107
|
+
const initialLineI = lineI;
|
|
1108
|
+
let closed = false;
|
|
1109
|
+
while (lineI < lines.length) {
|
|
1110
|
+
const line = lines[lineI++];
|
|
1111
|
+
if (line.trim().toLowerCase() == end) {
|
|
1112
|
+
closed = true;
|
|
1113
|
+
break;
|
|
1114
|
+
}
|
|
1115
|
+
content.push(line);
|
|
1116
|
+
}
|
|
1117
|
+
if (!closed) {
|
|
1118
|
+
lineI = initialLineI;
|
|
1119
|
+
warnAt(initialLineI, `${kind} block is not closed`);
|
|
1120
|
+
return;
|
|
1121
|
+
}
|
|
1122
|
+
const nodes = [{ type: "text", text: content.join("\n").trim(), sourceLine: initialLineI + 1 }];
|
|
1123
|
+
findTables(nodes, false);
|
|
1124
|
+
const supported = nodes.filter((node) => node.type == "text" || node.type == "table");
|
|
1125
|
+
if (supported.length != 1 || supported[0]?.type == "text" && !supported[0].text) {
|
|
1126
|
+
warnAt(initialLineI, `${kind} must contain text or one table`);
|
|
1127
|
+
return;
|
|
1128
|
+
}
|
|
1129
|
+
setHeaderFooter(kind, { type: "content", align, nodes: supported });
|
|
1130
|
+
function setHeaderFooter(kind2, value2) {
|
|
1131
|
+
let section;
|
|
1132
|
+
for (let i = doc.nodes.length - 1; i >= 0; i--) {
|
|
1133
|
+
if (doc.nodes[i]?.type == "sectionBreak") {
|
|
1134
|
+
section = doc.nodes[i];
|
|
1135
|
+
break;
|
|
1136
|
+
}
|
|
1137
|
+
}
|
|
1138
|
+
if (section?.type == "sectionBreak") section[kind2] = value2;
|
|
1139
|
+
else doc[kind2] = value2;
|
|
1140
|
+
}
|
|
1141
|
+
}
|
|
1142
|
+
function parseAdmonition(parts) {
|
|
1143
|
+
const marker = parts[0];
|
|
1144
|
+
const type = parts[1];
|
|
1145
|
+
const title = parts[2];
|
|
1146
|
+
const attributes = parts[3];
|
|
1147
|
+
const content = [];
|
|
1148
|
+
const close = new RegExp(`^${marker}\\s*$`);
|
|
1149
|
+
const initialLineI = lineI;
|
|
1150
|
+
let closed = false;
|
|
1151
|
+
while (lineI < lines.length) {
|
|
1152
|
+
const line = lines[lineI++];
|
|
1153
|
+
if (close.test(line)) {
|
|
1154
|
+
closed = true;
|
|
1155
|
+
break;
|
|
1156
|
+
}
|
|
1157
|
+
content.push(line);
|
|
1158
|
+
}
|
|
1159
|
+
if (!closed) warnAt(initialLineI, `Admonition "${type}" is not closed`);
|
|
1160
|
+
return { type: "admonition", admonitionType: type, title, text: content.filter((t) => !!t.trim()).join("\n").trim(), attributes, sourceLine: initialLineI };
|
|
1161
|
+
}
|
|
288
1162
|
class RuleError extends Error {
|
|
289
1163
|
}
|
|
290
1164
|
;
|
|
@@ -354,9 +1228,43 @@ async function parseMD(file, logwarn = console.warn) {
|
|
|
354
1228
|
"text spacing after": (v) => doc.text.spacing.after = tryParseInt(v),
|
|
355
1229
|
"img text size": (v) => doc.img.text.size = tryParseInt(v),
|
|
356
1230
|
"img spacing before": (v) => doc.img.spacing.before = tryParseInt(v),
|
|
357
|
-
"img spacing after": (v) => doc.img.spacing.after = tryParseInt(v)
|
|
1231
|
+
"img spacing after": (v) => doc.img.spacing.after = tryParseInt(v),
|
|
1232
|
+
"formula spacing before": (v) => doc.formula.spacing.before = tryParseInt(v),
|
|
1233
|
+
"formula spacing after": (v) => doc.formula.spacing.after = tryParseInt(v)
|
|
358
1234
|
};
|
|
359
1235
|
const reRules = [
|
|
1236
|
+
{
|
|
1237
|
+
re: /^(admonition (all|note|info|tip|warning|danger) (spacing (before|after)|padding|indent|background|color|bar_width|icon size|icon|title_color|title))(.*)/,
|
|
1238
|
+
n: (m) => ({ rule: m[1], value: (m.at(-1) || "").trim() }),
|
|
1239
|
+
f(m) {
|
|
1240
|
+
const type = m[2];
|
|
1241
|
+
const property = m[3];
|
|
1242
|
+
const value = (m.at(-1) || "").trim();
|
|
1243
|
+
const valueKeepCase = text.slice(m[1].length).trim();
|
|
1244
|
+
const types = type == "all" ? ["note", "info", "tip", "warning", "danger"] : [type];
|
|
1245
|
+
for (const type2 of types) {
|
|
1246
|
+
const style = doc.admonition[type2];
|
|
1247
|
+
if (property == "spacing before") style.spacing.before = tryParseInt(value);
|
|
1248
|
+
else if (property == "spacing after") style.spacing.after = tryParseInt(value);
|
|
1249
|
+
else if (property == "indent") style.indent = tryParseFloat(value);
|
|
1250
|
+
else if (property == "padding") {
|
|
1251
|
+
const padding = value.split(" ").map(tryParseInt);
|
|
1252
|
+
if (padding.length != 2) throw new RuleError("Two values are required: horizontal vertical");
|
|
1253
|
+
style.padding = { top: padding[1], right: padding[0], bottom: padding[1], left: padding[0] };
|
|
1254
|
+
} else if (property == "background") style.background = tryParseColor(value);
|
|
1255
|
+
else if (property == "color") style.color = tryParseColor(value);
|
|
1256
|
+
else if (property == "bar_width") style.bar_width = tryParseFloat(value);
|
|
1257
|
+
else if (property == "icon size") style.icon_size = tryParseInt(value);
|
|
1258
|
+
else if (property == "icon") {
|
|
1259
|
+
choices(value, "on", "off");
|
|
1260
|
+
style.icon = value == "on";
|
|
1261
|
+
} else if (property == "title_color") {
|
|
1262
|
+
choices(value, "on", "off");
|
|
1263
|
+
style.title_color = value == "on";
|
|
1264
|
+
} else if (property == "title") style.title = valueKeepCase;
|
|
1265
|
+
}
|
|
1266
|
+
}
|
|
1267
|
+
},
|
|
360
1268
|
{
|
|
361
1269
|
re: /^(headings (h[1-6])(\+?) (size|spacing (before|after)|uppercase|indent))(.*)/,
|
|
362
1270
|
n: (m) => ({ rule: m[1], value: (m.at(-1) || "").trim() }),
|
|
@@ -394,11 +1302,11 @@ async function parseMD(file, logwarn = console.warn) {
|
|
|
394
1302
|
rule = n.rule;
|
|
395
1303
|
value = n.value;
|
|
396
1304
|
}
|
|
397
|
-
if (e instanceof RuleError)
|
|
1305
|
+
if (e instanceof RuleError) warnAt(lineI, `Rule "${rule}" wrong value: "${value}". ${e.message}`);
|
|
398
1306
|
else throw e;
|
|
399
1307
|
}
|
|
400
1308
|
} else {
|
|
401
|
-
|
|
1309
|
+
warnAt(lineI, `Wrong rule: "${text}"`);
|
|
402
1310
|
}
|
|
403
1311
|
function tryParseInt(v) {
|
|
404
1312
|
const num = parseInt(v);
|
|
@@ -415,6 +1323,11 @@ async function parseMD(file, logwarn = console.warn) {
|
|
|
415
1323
|
if (!isFinite(date.valueOf())) throw new RuleError("Only ISO 8601 date is allowed");
|
|
416
1324
|
return date;
|
|
417
1325
|
}
|
|
1326
|
+
function tryParseColor(v) {
|
|
1327
|
+
const color = /^#?([\da-f]{6})$/i.exec(v)?.[1];
|
|
1328
|
+
if (!color) throw new RuleError("Only #RRGGBB colors are allowed");
|
|
1329
|
+
return color;
|
|
1330
|
+
}
|
|
418
1331
|
function choices(v, ...vars) {
|
|
419
1332
|
if (vars.includes(v)) return v;
|
|
420
1333
|
throw new RuleError("Allowed: " + vars.join(", "));
|
|
@@ -428,66 +1341,85 @@ async function parseMD(file, logwarn = console.warn) {
|
|
|
428
1341
|
while (lineI < lines.length) {
|
|
429
1342
|
const line = lines[lineI++];
|
|
430
1343
|
const { prefix, text, parts } = parseLine(line);
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
1344
|
+
try {
|
|
1345
|
+
switch (prefix) {
|
|
1346
|
+
case "#":
|
|
1347
|
+
doc.appendTitle(text, 1, lineI);
|
|
1348
|
+
break;
|
|
1349
|
+
case "##":
|
|
1350
|
+
doc.appendTitle(text, 2, lineI);
|
|
1351
|
+
break;
|
|
1352
|
+
case "###":
|
|
1353
|
+
doc.appendTitle(text, 3, lineI);
|
|
1354
|
+
break;
|
|
1355
|
+
case "####":
|
|
1356
|
+
doc.appendTitle(text, 4, lineI);
|
|
1357
|
+
break;
|
|
1358
|
+
case "#####":
|
|
1359
|
+
doc.appendTitle(text, 5, lineI);
|
|
1360
|
+
break;
|
|
1361
|
+
case "######":
|
|
1362
|
+
doc.appendTitle(text, 6, lineI);
|
|
1363
|
+
break;
|
|
1364
|
+
case "*":
|
|
1365
|
+
doc.appendNode(parseList(text, parts, false));
|
|
1366
|
+
break;
|
|
1367
|
+
case "1)":
|
|
1368
|
+
doc.appendNode(parseList(text, parts, true));
|
|
1369
|
+
break;
|
|
1370
|
+
case "Img":
|
|
1371
|
+
doc.appendNode(parseImg(parts));
|
|
1372
|
+
break;
|
|
1373
|
+
case "Code":
|
|
1374
|
+
doc.appendNode(parseCode(text));
|
|
1375
|
+
break;
|
|
1376
|
+
case "Admonition":
|
|
1377
|
+
doc.appendNode(parseAdmonition(parts));
|
|
1378
|
+
break;
|
|
1379
|
+
case "Comment":
|
|
1380
|
+
skipComment();
|
|
1381
|
+
break;
|
|
1382
|
+
case "!!section":
|
|
1383
|
+
doc.appendNode(parseSection(text));
|
|
1384
|
+
break;
|
|
1385
|
+
case "!!header":
|
|
1386
|
+
parseHeaderFooter("header", text);
|
|
1387
|
+
break;
|
|
1388
|
+
case "!!footer":
|
|
1389
|
+
parseHeaderFooter("footer", text);
|
|
1390
|
+
break;
|
|
1391
|
+
case "!!rule":
|
|
1392
|
+
apllyRule(text);
|
|
1393
|
+
break;
|
|
1394
|
+
case "---":
|
|
1395
|
+
doc.appendNode({ type: "pageBreak", sourceLine: lineI });
|
|
1396
|
+
break;
|
|
1397
|
+
case "":
|
|
1398
|
+
case " ":
|
|
1399
|
+
const last = doc.nodes.at(-1);
|
|
1400
|
+
if (line.trim() != "" && last?.type == "text") {
|
|
1401
|
+
if (last.text == "") last.sourceLine = lineI;
|
|
1402
|
+
else last.text += "\n";
|
|
1403
|
+
last.text += line.trim();
|
|
1404
|
+
} else doc.appendText(line.trim(), lineI);
|
|
1405
|
+
break;
|
|
1406
|
+
default:
|
|
1407
|
+
prefix;
|
|
1408
|
+
throw new Error("switch default");
|
|
1409
|
+
}
|
|
1410
|
+
} catch (error) {
|
|
1411
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
1412
|
+
throw new Error(`Line ${lineI}: ${message}`, { cause: error });
|
|
485
1413
|
}
|
|
486
1414
|
}
|
|
1415
|
+
doc.nodes.forEach((node) => {
|
|
1416
|
+
if (node.type == "admonition" && node.title == "\n")
|
|
1417
|
+
node.title = doc.admonition[node.admonitionType].title;
|
|
1418
|
+
});
|
|
487
1419
|
doc.nodes = doc.nodes.filter((n) => n.type != "text" || n.text != "");
|
|
488
|
-
findDocs(doc.nodes,
|
|
1420
|
+
await findDocs(doc.nodes, warnAt);
|
|
1421
|
+
findMath(doc.nodes, warnAt);
|
|
489
1422
|
findTables(doc.nodes);
|
|
490
|
-
findPageBreaks(doc.nodes);
|
|
491
1423
|
return doc;
|
|
492
1424
|
}
|
|
493
1425
|
function parseLine(line) {
|
|
@@ -504,7 +1436,7 @@ function parseLine(line) {
|
|
|
504
1436
|
const text = splited.slice(1).join(" ");
|
|
505
1437
|
let parts = [];
|
|
506
1438
|
if (prefix.startsWith("!")) {
|
|
507
|
-
const m_img =
|
|
1439
|
+
const m_img = /^!\[(.*)\]\((.*)\)({(.*)})?/.exec(line);
|
|
508
1440
|
if (m_img) return {
|
|
509
1441
|
prefix: "Img",
|
|
510
1442
|
text: line.trim(),
|
|
@@ -524,29 +1456,37 @@ function parseLine(line) {
|
|
|
524
1456
|
}
|
|
525
1457
|
}
|
|
526
1458
|
if (prefix.startsWith("```")) return { prefix: "Code", text: line.trim().slice(3), level, parts };
|
|
1459
|
+
const m_admonition = /^(:{3,})(note|info|tip|warning|danger)(?:\[([^\]]*)\])?(?:\{([^}]*)\})?\s*$/i.exec(line.trim());
|
|
1460
|
+
if (m_admonition) return {
|
|
1461
|
+
prefix: "Admonition",
|
|
1462
|
+
text: "",
|
|
1463
|
+
level,
|
|
1464
|
+
parts: [m_admonition[1], m_admonition[2].toLowerCase(), m_admonition[3] ?? "\n", m_admonition[4] || ""]
|
|
1465
|
+
};
|
|
527
1466
|
if (prefix.startsWith("<!--")) return { prefix: "Comment", text: line.trim().slice("<!--".length).trim(), level, parts };
|
|
528
|
-
if (["#", "##", "###", "####", "#####", "######", "*", "1)", "!!section", "!!rule"].includes(prefix))
|
|
1467
|
+
if (["#", "##", "###", "####", "#####", "######", "*", "1)", "!!section", "!!header", "!!footer", "!!rule"].includes(prefix))
|
|
529
1468
|
return { prefix, text, level: 0, parts };
|
|
530
1469
|
return { prefix: "", text: line.trim(), level, parts };
|
|
531
1470
|
}
|
|
532
|
-
function findDocs(nodes,
|
|
1471
|
+
async function findDocs(nodes, warnAt) {
|
|
1472
|
+
const { default: JSONC } = await import("jsonc-simple-parser");
|
|
533
1473
|
const re_doc = /^!!\(([^{}]*)\)\s*{(.*)}$/s;
|
|
534
|
-
const re_remTrailingComma = /,(\s*[}\]])/g;
|
|
535
1474
|
for (let i = 0; i < nodes.length; i++) {
|
|
536
1475
|
const node = nodes[i];
|
|
537
1476
|
if (node.type != "text") continue;
|
|
538
|
-
const m_doc = re_doc.exec(node.text.trimEnd()
|
|
1477
|
+
const m_doc = re_doc.exec(node.text.trimEnd());
|
|
539
1478
|
if (!m_doc) continue;
|
|
540
1479
|
let dict = {};
|
|
541
1480
|
try {
|
|
542
|
-
dict =
|
|
1481
|
+
dict = JSONC.parse(`{${m_doc[2]}}`);
|
|
543
1482
|
} catch {
|
|
544
|
-
|
|
1483
|
+
warnAt(node.sourceLine ?? -1, `Cant parse doc dict: {${m_doc[2].replaceAll("\n", " ")}}`);
|
|
545
1484
|
}
|
|
546
1485
|
nodes.splice(i, 1, {
|
|
547
1486
|
type: "externalDoc",
|
|
548
1487
|
path: trimEnd(trimStart(m_doc[1], "<", '"'), ">", '"'),
|
|
549
|
-
dict: stringifyDict(dict)
|
|
1488
|
+
dict: stringifyDict(dict),
|
|
1489
|
+
sourceLine: node.sourceLine
|
|
550
1490
|
});
|
|
551
1491
|
}
|
|
552
1492
|
}
|
|
@@ -570,7 +1510,7 @@ function stringifyDict(dict) {
|
|
|
570
1510
|
}
|
|
571
1511
|
return r;
|
|
572
1512
|
}
|
|
573
|
-
function findTables(nodes) {
|
|
1513
|
+
function findTables(nodes, hasHeader = true) {
|
|
574
1514
|
const re_sep = /^(\s*:?-+:?\s*(?<!\\)\|)+\s*:?-+:?\s*$/;
|
|
575
1515
|
const re_sep_oneCol = /^\|\s*:?-+:?\s*\|$/;
|
|
576
1516
|
const trim = (line) => {
|
|
@@ -585,19 +1525,20 @@ function findTables(nodes) {
|
|
|
585
1525
|
const lines = node.text.split("\n");
|
|
586
1526
|
const offset = lines[0].includes("|") ? 0 : 1;
|
|
587
1527
|
if (lines.length < 2 + offset) continue;
|
|
588
|
-
const
|
|
589
|
-
if (!re_sep.test(
|
|
590
|
-
const cols =
|
|
1528
|
+
const sep2 = trim(lines[1 + offset]);
|
|
1529
|
+
if (!re_sep.test(sep2) && !re_sep_oneCol.test(lines[1 + offset])) continue;
|
|
1530
|
+
const cols = sep2.split("|");
|
|
591
1531
|
const header = trim(lines[offset]).split(/(?<!\\)\|/).map((v) => v.trim());
|
|
592
1532
|
if (header.length != cols.length) continue;
|
|
593
1533
|
const align = cols.map((v) => v.trim()).map((v) => v.startsWith(":") && v.endsWith(":") ? "c" : v.endsWith(":") ? "r" : "l");
|
|
594
|
-
|
|
1534
|
+
let sourceLine = node.sourceLine + offset;
|
|
1535
|
+
const rows = [tableRow(sourceLine++, ...header)];
|
|
595
1536
|
for (const line of lines.slice(2 + offset)) {
|
|
596
1537
|
const row = trim(line).split(/(?<!\\)\|/).map((v) => v.trim());
|
|
597
1538
|
while (row.length < cols.length) row.push("");
|
|
598
|
-
rows.push(tableRow(...row));
|
|
1539
|
+
rows.push(tableRow(++sourceLine, ...row));
|
|
599
1540
|
}
|
|
600
|
-
const table = { type: "table", align, rows };
|
|
1541
|
+
const table = { type: "table", align, rows, sourceLine: node.sourceLine, ...hasHeader ? {} : { header: false } };
|
|
601
1542
|
nodes.splice(i, 1, table);
|
|
602
1543
|
const prev = nodes[i - 1];
|
|
603
1544
|
if (offset > 0) {
|
|
@@ -609,43 +1550,133 @@ function findTables(nodes) {
|
|
|
609
1550
|
}
|
|
610
1551
|
}
|
|
611
1552
|
}
|
|
612
|
-
function
|
|
613
|
-
const re_sep = /^\s*---+\s*$/;
|
|
1553
|
+
function findMath(nodes, warnAt) {
|
|
614
1554
|
for (let i = 0; i < nodes.length; i++) {
|
|
615
1555
|
const node = nodes[i];
|
|
616
|
-
if (node
|
|
617
|
-
if (!node.text.includes("---")) continue;
|
|
1556
|
+
if (node?.type != "text") continue;
|
|
618
1557
|
const lines = node.text.split("\n");
|
|
619
|
-
const
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
1558
|
+
const replacement = [];
|
|
1559
|
+
let text = [];
|
|
1560
|
+
let textStart = 0;
|
|
1561
|
+
const flush = () => {
|
|
1562
|
+
if (text.length)
|
|
1563
|
+
replacement.push({ type: "text", text: text.join("\n"), sourceLine: (node.sourceLine ?? 0) + textStart });
|
|
1564
|
+
text = [];
|
|
1565
|
+
};
|
|
1566
|
+
for (let line = 0; line < lines.length; line++) {
|
|
1567
|
+
if (lines[line]?.trim() != "$$") {
|
|
1568
|
+
if (!text.length) textStart = line;
|
|
1569
|
+
text.push(lines[line]);
|
|
1570
|
+
continue;
|
|
626
1571
|
}
|
|
1572
|
+
const start = line;
|
|
1573
|
+
let end = line + 1;
|
|
1574
|
+
while (end < lines.length && lines[end]?.trim() != "$$") end++;
|
|
1575
|
+
if (end >= lines.length) {
|
|
1576
|
+
warnAt((node.sourceLine ?? 0) + start, "Formula is not closed");
|
|
1577
|
+
if (!text.length) textStart = line;
|
|
1578
|
+
text.push(lines[line]);
|
|
1579
|
+
continue;
|
|
1580
|
+
}
|
|
1581
|
+
let id;
|
|
1582
|
+
let manualNumber;
|
|
1583
|
+
const idMatch = /^\s*(\[[a-zA-Zа-яА-ЯёЁ_\d#]+\])\s*$/.exec(text.at(-1) || "");
|
|
1584
|
+
const numberMatch = /^\s*(\(.*\))\s*$/.exec(text.at(-1) || "");
|
|
1585
|
+
if (idMatch) {
|
|
1586
|
+
id = `(${idMatch[1]})`;
|
|
1587
|
+
text.pop();
|
|
1588
|
+
} else if (numberMatch) {
|
|
1589
|
+
manualNumber = numberMatch[1];
|
|
1590
|
+
text.pop();
|
|
1591
|
+
}
|
|
1592
|
+
flush();
|
|
1593
|
+
replacement.push({
|
|
1594
|
+
type: "math",
|
|
1595
|
+
latex: lines.slice(start + 1, end).join("\n").trim(),
|
|
1596
|
+
title: id ?? manualNumber,
|
|
1597
|
+
sourceLine: (node.sourceLine ?? 0) + start
|
|
1598
|
+
});
|
|
1599
|
+
line = end;
|
|
1600
|
+
textStart = end + 1;
|
|
1601
|
+
}
|
|
1602
|
+
flush();
|
|
1603
|
+
if (replacement.some((n) => n.type == "math")) {
|
|
1604
|
+
nodes.splice(i, 1, ...replacement);
|
|
1605
|
+
i += replacement.length - 1;
|
|
627
1606
|
}
|
|
628
|
-
nodes.splice(i, 1, ...newNodes);
|
|
629
|
-
i += newNodes.length - 1;
|
|
630
1607
|
}
|
|
631
1608
|
}
|
|
632
1609
|
function runifyDoc(doc) {
|
|
633
1610
|
function runifyNode(node) {
|
|
634
|
-
if ("text" in node && node.text)
|
|
1611
|
+
if ("text" in node && node.text !== void 0)
|
|
635
1612
|
node.text = runifyText(node.text, doc.rainbow);
|
|
636
|
-
if ("title" in node && node.title)
|
|
1613
|
+
if ("title" in node && node.title !== void 0)
|
|
637
1614
|
node.title = runifyText(node.title, doc.rainbow);
|
|
638
1615
|
if (node.type == "table")
|
|
639
1616
|
node.rows.forEach((row) => row.forEach(runifyNode));
|
|
640
1617
|
if (node.type == "list")
|
|
641
1618
|
node.items.forEach(runifyNode);
|
|
1619
|
+
if (node.type == "sectionBreak") {
|
|
1620
|
+
runifyHeaderFooter(node.header);
|
|
1621
|
+
runifyHeaderFooter(node.footer);
|
|
1622
|
+
}
|
|
642
1623
|
}
|
|
643
1624
|
doc.nodes.forEach(runifyNode);
|
|
1625
|
+
runifyHeaderFooter(doc.header);
|
|
1626
|
+
runifyHeaderFooter(doc.footer);
|
|
644
1627
|
return doc;
|
|
1628
|
+
function runifyHeaderFooter(value) {
|
|
1629
|
+
if (value?.type != "content") return;
|
|
1630
|
+
value.nodes.forEach(runifyNode);
|
|
1631
|
+
}
|
|
645
1632
|
}
|
|
646
1633
|
var rainbowI = 0;
|
|
647
1634
|
function runifyText(text, rainbow = false) {
|
|
648
|
-
|
|
1635
|
+
const formulas = [];
|
|
1636
|
+
const codeSpans = [];
|
|
1637
|
+
function findClosingBackticks(from, count) {
|
|
1638
|
+
for (let i = from; i < text.length; ) {
|
|
1639
|
+
const start = text.indexOf("`", i);
|
|
1640
|
+
if (start < 0) return -1;
|
|
1641
|
+
let end = start + 1;
|
|
1642
|
+
while (text[end] == "`") end++;
|
|
1643
|
+
if (end - start == count) return start;
|
|
1644
|
+
i = end;
|
|
1645
|
+
}
|
|
1646
|
+
return -1;
|
|
1647
|
+
}
|
|
1648
|
+
;
|
|
1649
|
+
let preprocessedText = "";
|
|
1650
|
+
for (let i = 0; i < text.length; ) {
|
|
1651
|
+
if (text[i] == "`") {
|
|
1652
|
+
let delimiterEnd = i + 1;
|
|
1653
|
+
while (text[delimiterEnd] == "`") delimiterEnd++;
|
|
1654
|
+
const delimiterLength = delimiterEnd - i;
|
|
1655
|
+
const end = findClosingBackticks(delimiterEnd, delimiterLength);
|
|
1656
|
+
if (end >= 0) {
|
|
1657
|
+
codeSpans.push(text.slice(delimiterEnd, end));
|
|
1658
|
+
preprocessedText += `\uE002${codeSpans.length - 1}\uE003`;
|
|
1659
|
+
i = end + delimiterLength;
|
|
1660
|
+
continue;
|
|
1661
|
+
}
|
|
1662
|
+
preprocessedText += text.slice(i, delimiterEnd);
|
|
1663
|
+
i = delimiterEnd;
|
|
1664
|
+
continue;
|
|
1665
|
+
}
|
|
1666
|
+
if (text[i] == "$" && text[i - 1] != "\\" && text[i + 1] != "$") {
|
|
1667
|
+
let end = i + 1;
|
|
1668
|
+
while (end < text.length && text[end] != "\n" && (text[end] != "$" || text[end - 1] == "\\")) end++;
|
|
1669
|
+
if (end < text.length && text[end] == "$") {
|
|
1670
|
+
formulas.push(text.slice(i + 1, end));
|
|
1671
|
+
preprocessedText += `\uE000${formulas.length - 1}\uE001`;
|
|
1672
|
+
i = end + 1;
|
|
1673
|
+
continue;
|
|
1674
|
+
}
|
|
1675
|
+
}
|
|
1676
|
+
preprocessedText += text[i++];
|
|
1677
|
+
}
|
|
1678
|
+
text = preprocessedText.replaceAll("\\$", "$");
|
|
1679
|
+
return replaceAmpCodes(text).replaceAll("\n", "	\n").replaceAll(/\s*<br>\s*/g, "\n").replaceAll("\u2014", "-").replaceAll(" - ", " \u2013 ").replaceAll(/(^|[^\p{L}\d_])"([\p{L}\d_])/gu, "$1\xAB$2").replaceAll(/([\p{L}\d_])"([^\p{L}\d_]|$)/gu, "$1\xBB$2").replaceAll(/(^|\s)(\*+)($|\s)/g, (sub) => sub.replaceAll("*", "⋆")).split(/(\[[^\]]*\]\([^)]*\))/g).map((p) => {
|
|
649
1680
|
const m = /\[([^\]]*)\]\(([^)]*)\)/.exec(p);
|
|
650
1681
|
if (!m) return { text: p };
|
|
651
1682
|
return { text: m[1], link: m[2] };
|
|
@@ -671,17 +1702,23 @@ function runifyText(text, rainbow = false) {
|
|
|
671
1702
|
link: rune.link,
|
|
672
1703
|
bold: rune.bold,
|
|
673
1704
|
italic: i % 2 == 1 && i != arr.length - 1 || rune.italic
|
|
674
|
-
}))).flat().map((rune) => rune.text.split(
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
|
|
1705
|
+
}))).flat().map((rune) => rune.text.split(/(\uE002\d+\uE003)/).map((p, i) => {
|
|
1706
|
+
const m = /^\uE002(\d+)\uE003$/.exec(p);
|
|
1707
|
+
return {
|
|
1708
|
+
...rune,
|
|
1709
|
+
text: m ? codeSpans[parseInt(m[1])] : p,
|
|
1710
|
+
mono: m ? true : rune.mono,
|
|
1711
|
+
linebreak: i == 0 && rune.linebreak
|
|
1712
|
+
};
|
|
1713
|
+
})).flat().map((rune) => rune.mono ? [rune] : rune.text.split(/(\uE000\d+\uE001)/).map((p, i) => {
|
|
1714
|
+
const m = /^\uE000(\d+)\uE001$/.exec(p);
|
|
1715
|
+
return {
|
|
1716
|
+
...rune,
|
|
1717
|
+
text: m ? formulas[parseInt(m[1])] : p,
|
|
1718
|
+
type: m ? "math" : rune.type,
|
|
1719
|
+
linebreak: i == 0 && rune.linebreak
|
|
1720
|
+
};
|
|
1721
|
+
})).flat().map((rune) => rune.link || rune.mono ? [rune] : rune.text.split(/(\[!?[a-zA-Zа-яА-ЯёЁ_\d#]+\s*[+-]?\s*\d*\])/g).map((p, i) => {
|
|
685
1722
|
const m = /\[(!?([a-zA-Zа-яА-ЯёЁ_\d]+|#)(\s*[-+]\s*\d+)?)\]/.exec(p);
|
|
686
1723
|
const v = m && m[1];
|
|
687
1724
|
const isVal = v?.at(0) == "!";
|
|
@@ -691,12 +1728,14 @@ function runifyText(text, rainbow = false) {
|
|
|
691
1728
|
type: v ? isVal ? "val" : "ref" : rune.type,
|
|
692
1729
|
linebreak: i == 0 && rune.linebreak
|
|
693
1730
|
};
|
|
694
|
-
})).flat().map((rune) => ({
|
|
1731
|
+
})).flat().map((rune, i, arr) => ({
|
|
695
1732
|
...rune,
|
|
696
1733
|
...!rune.type || rune.type == "text" ? {
|
|
697
|
-
text:
|
|
1734
|
+
text: formatTypography(
|
|
1735
|
+
arr[i + 1]?.type == "ref" || arr[i + 1]?.type == "val" ? rune.text.replace(/[ \t]+$/, NBSP) : rune.text
|
|
1736
|
+
).replaceAll("⋆", "*").replaceAll("​", "")
|
|
698
1737
|
} : {}
|
|
699
|
-
})).map((rune) => !rainbow || rune.type && rune.type != "text" ? [rune] : rune.text.split("").map((p, i) => ({
|
|
1738
|
+
})).map((rune) => !rainbow || rune.mono || rune.type && rune.type != "text" ? [rune] : rune.text.split("").map((p, i) => ({
|
|
700
1739
|
...rune,
|
|
701
1740
|
text: p,
|
|
702
1741
|
linebreak: i == 0 && rune.linebreak,
|
|
@@ -706,6 +1745,7 @@ function runifyText(text, rainbow = false) {
|
|
|
706
1745
|
function replaceAmpCodes(text) {
|
|
707
1746
|
const codes = {
|
|
708
1747
|
"|": "|",
|
|
1748
|
+
"&": "&",
|
|
709
1749
|
"­": "\xAD",
|
|
710
1750
|
"«": "\xAB",
|
|
711
1751
|
"»": "\xBB",
|
|
@@ -777,90 +1817,616 @@ function replaceAmpCodes(text) {
|
|
|
777
1817
|
}
|
|
778
1818
|
return text;
|
|
779
1819
|
}
|
|
1820
|
+
var NBSP = "\xA0";
|
|
1821
|
+
var NON_BREAKING_HYPHEN = "\u2011";
|
|
1822
|
+
var shortWords = ["\u0430", "\u0432", "\u0432\u043E", "\u0438", "\u043A", "\u043A\u043E", "\u043E", "\u043E\u0431", "\u043E\u0442", "\u0441", "\u0441\u043E", "\u0443", "\u0431\u0435\u0437", "\u0434\u043E", "\u0437\u0430", "\u0438\u0437", "\u043D\u0430", "\u043D\u0435", "\u043F\u043E", "\u043F\u043E\u0434", "\u043F\u0440\u0438", "\u043F\u0440\u043E", "\u0434\u043B\u044F"];
|
|
1823
|
+
var referenceTerms = [
|
|
1824
|
+
"\u0440\u0438\u0441\u0443\u043D\u043E\u043A",
|
|
1825
|
+
"\u0440\u0438\u0441\u0443\u043D\u043A\u0430",
|
|
1826
|
+
"\u0440\u0438\u0441\u0443\u043D\u043A\u0435",
|
|
1827
|
+
"\u0440\u0438\u0441\u0443\u043D\u043A\u0443",
|
|
1828
|
+
"\u0440\u0438\u0441\u0443\u043D\u043A\u043E\u043C",
|
|
1829
|
+
"\u0440\u0438\u0441\u0443\u043D\u043A\u0438",
|
|
1830
|
+
"\u0440\u0438\u0441\u0443\u043D\u043A\u043E\u0432",
|
|
1831
|
+
"\u0440\u0438\u0441\u0443\u043D\u043A\u0430\u0445",
|
|
1832
|
+
"\u0442\u0430\u0431\u043B\u0438\u0446\u0430",
|
|
1833
|
+
"\u0442\u0430\u0431\u043B\u0438\u0446\u044B",
|
|
1834
|
+
"\u0442\u0430\u0431\u043B\u0438\u0446\u0435",
|
|
1835
|
+
"\u0442\u0430\u0431\u043B\u0438\u0446\u0443",
|
|
1836
|
+
"\u0442\u0430\u0431\u043B\u0438\u0446\u0435\u0439",
|
|
1837
|
+
"\u0442\u0430\u0431\u043B\u0438\u0446",
|
|
1838
|
+
"\u0442\u0430\u0431\u043B\u0438\u0446\u0430\u0445",
|
|
1839
|
+
"\u043B\u0438\u0441\u0442\u0438\u043D\u0433",
|
|
1840
|
+
"\u043B\u0438\u0441\u0442\u0438\u043D\u0433\u0430",
|
|
1841
|
+
"\u043B\u0438\u0441\u0442\u0438\u043D\u0433\u0435",
|
|
1842
|
+
"\u043B\u0438\u0441\u0442\u0438\u043D\u0433\u0443",
|
|
1843
|
+
"\u043B\u0438\u0441\u0442\u0438\u043D\u0433\u043E\u043C",
|
|
1844
|
+
"\u043B\u0438\u0441\u0442\u0438\u043D\u0433\u0438",
|
|
1845
|
+
"\u043B\u0438\u0441\u0442\u0438\u043D\u0433\u043E\u0432",
|
|
1846
|
+
"\u043B\u0438\u0441\u0442\u0438\u043D\u0433\u0430\u0445",
|
|
1847
|
+
"\u0444\u043E\u0440\u043C\u0443\u043B\u0430",
|
|
1848
|
+
"\u0444\u043E\u0440\u043C\u0443\u043B\u044B",
|
|
1849
|
+
"\u0444\u043E\u0440\u043C\u0443\u043B\u0435",
|
|
1850
|
+
"\u0444\u043E\u0440\u043C\u0443\u043B\u0443",
|
|
1851
|
+
"\u0444\u043E\u0440\u043C\u0443\u043B\u043E\u0439",
|
|
1852
|
+
"\u0444\u043E\u0440\u043C\u0443\u043B",
|
|
1853
|
+
"\u0444\u043E\u0440\u043C\u0443\u043B\u0430\u0445",
|
|
1854
|
+
"\u0443\u0440\u0430\u0432\u043D\u0435\u043D\u0438\u0435",
|
|
1855
|
+
"\u0443\u0440\u0430\u0432\u043D\u0435\u043D\u0438\u044F",
|
|
1856
|
+
"\u0443\u0440\u0430\u0432\u043D\u0435\u043D\u0438\u0438",
|
|
1857
|
+
"\u0443\u0440\u0430\u0432\u043D\u0435\u043D\u0438\u044E",
|
|
1858
|
+
"\u0443\u0440\u0430\u0432\u043D\u0435\u043D\u0438\u0435\u043C",
|
|
1859
|
+
"\u0443\u0440\u0430\u0432\u043D\u0435\u043D\u0438\u0439",
|
|
1860
|
+
"\u0443\u0440\u0430\u0432\u043D\u0435\u043D\u0438\u044F\u0445",
|
|
1861
|
+
"\u0440\u0430\u0437\u0434\u0435\u043B",
|
|
1862
|
+
"\u0440\u0430\u0437\u0434\u0435\u043B\u0430",
|
|
1863
|
+
"\u0440\u0430\u0437\u0434\u0435\u043B\u0435",
|
|
1864
|
+
"\u0440\u0430\u0437\u0434\u0435\u043B\u0443",
|
|
1865
|
+
"\u0440\u0430\u0437\u0434\u0435\u043B\u043E\u043C",
|
|
1866
|
+
"\u0440\u0430\u0437\u0434\u0435\u043B\u044B",
|
|
1867
|
+
"\u0440\u0430\u0437\u0434\u0435\u043B\u043E\u0432",
|
|
1868
|
+
"\u0440\u0430\u0437\u0434\u0435\u043B\u0430\u0445",
|
|
1869
|
+
"\u0433\u043B\u0430\u0432\u0430",
|
|
1870
|
+
"\u0433\u043B\u0430\u0432\u044B",
|
|
1871
|
+
"\u0433\u043B\u0430\u0432\u0435",
|
|
1872
|
+
"\u0433\u043B\u0430\u0432\u0443",
|
|
1873
|
+
"\u0433\u043B\u0430\u0432\u043E\u0439",
|
|
1874
|
+
"\u0433\u043B\u0430\u0432",
|
|
1875
|
+
"\u0433\u043B\u0430\u0432\u0430\u0445",
|
|
1876
|
+
"\u043F\u0440\u0438\u043B\u043E\u0436\u0435\u043D\u0438\u0435",
|
|
1877
|
+
"\u043F\u0440\u0438\u043B\u043E\u0436\u0435\u043D\u0438\u044F",
|
|
1878
|
+
"\u043F\u0440\u0438\u043B\u043E\u0436\u0435\u043D\u0438\u0438",
|
|
1879
|
+
"\u043F\u0440\u0438\u043B\u043E\u0436\u0435\u043D\u0438\u044E",
|
|
1880
|
+
"\u043F\u0440\u0438\u043B\u043E\u0436\u0435\u043D\u0438\u0435\u043C",
|
|
1881
|
+
"\u043F\u0440\u0438\u043B\u043E\u0436\u0435\u043D\u0438\u0439",
|
|
1882
|
+
"\u043F\u0440\u0438\u043B\u043E\u0436\u0435\u043D\u0438\u044F\u0445",
|
|
1883
|
+
"\u043F\u0440\u0438\u043C\u0435\u0440",
|
|
1884
|
+
"\u043F\u0440\u0438\u043C\u0435\u0440\u0430",
|
|
1885
|
+
"\u043F\u0440\u0438\u043C\u0435\u0440\u0435",
|
|
1886
|
+
"\u043F\u0440\u0438\u043C\u0435\u0440\u0443",
|
|
1887
|
+
"\u043F\u0440\u0438\u043C\u0435\u0440\u043E\u043C",
|
|
1888
|
+
"\u043F\u0440\u0438\u043C\u0435\u0440\u044B",
|
|
1889
|
+
"\u043F\u0440\u0438\u043C\u0435\u0440\u043E\u0432",
|
|
1890
|
+
"\u043F\u0440\u0438\u043C\u0435\u0440\u0430\u0445",
|
|
1891
|
+
"\u0430\u043B\u0433\u043E\u0440\u0438\u0442\u043C",
|
|
1892
|
+
"\u0430\u043B\u0433\u043E\u0440\u0438\u0442\u043C\u0430",
|
|
1893
|
+
"\u0430\u043B\u0433\u043E\u0440\u0438\u0442\u043C\u0435",
|
|
1894
|
+
"\u0430\u043B\u0433\u043E\u0440\u0438\u0442\u043C\u0443",
|
|
1895
|
+
"\u0430\u043B\u0433\u043E\u0440\u0438\u0442\u043C\u043E\u043C",
|
|
1896
|
+
"\u0430\u043B\u0433\u043E\u0440\u0438\u0442\u043C\u044B",
|
|
1897
|
+
"\u0430\u043B\u0433\u043E\u0440\u0438\u0442\u043C\u043E\u0432",
|
|
1898
|
+
"\u0430\u043B\u0433\u043E\u0440\u0438\u0442\u043C\u0430\u0445",
|
|
1899
|
+
"\u0441\u0445\u0435\u043C\u0430",
|
|
1900
|
+
"\u0441\u0445\u0435\u043C\u044B",
|
|
1901
|
+
"\u0441\u0445\u0435\u043C\u0435",
|
|
1902
|
+
"\u0441\u0445\u0435\u043C\u0443",
|
|
1903
|
+
"\u0441\u0445\u0435\u043C\u043E\u0439",
|
|
1904
|
+
"\u0441\u0445\u0435\u043C",
|
|
1905
|
+
"\u0441\u0445\u0435\u043C\u0430\u0445"
|
|
1906
|
+
];
|
|
1907
|
+
var referenceAbbreviations = ["\u0440\u0438\u0441", "\u0442\u0430\u0431\u043B", "\u043B\u0438\u0441\u0442", "\u0444\u043E\u0440\u043C", "\u0443\u0440", "\u0440\u0430\u0437\u0434", "\u0433\u043B", "\u043F\u0440\u0438\u043B"];
|
|
1908
|
+
var units = ["%", "\xB0C", "\xB0F", "\u043C\u043C", "\u0441\u043C", "\u0434\u043C", "\u043A\u043C", "\u043C\u043A\u043C", "\u043D\u043C", "\u043C", "\u043C\u0433", "\u043A\u0433", "\u0433", "\u043C\u043B", "\u043B", "\u043C\u0441", "\u043C\u043A\u0441", "\u043D\u0441", "\u0441", "\u043C\u0438\u043D", "\u0447", "\u0413\u0446", "\u043A\u0413\u0446", "\u041C\u0413\u0446", "\u0413\u0413\u0446", "\u0412\u0442", "\u043A\u0412\u0442", "\u041C\u0412\u0442", "\u0412", "\u043A\u0412", "\u0410", "\u043C\u0410", "\u041E\u043C", "\u043A\u041E\u043C", "\u041C\u041E\u043C", "\u0411", "\u041A\u0411", "\u041C\u0411", "\u0413\u0411", "\u0422\u0411", "\u0431\u0438\u0442", "\u041A\u0431\u0438\u0442", "\u041C\u0431\u0438\u0442", "\u0413\u0431\u0438\u0442", "\u0431\u0438\u0442/\u0441", "\u041A\u0431\u0438\u0442/\u0441", "\u041C\u0431\u0438\u0442/\u0441", "\u0413\u0431\u0438\u0442/\u0441"];
|
|
1909
|
+
function escapeRegExp(value) {
|
|
1910
|
+
return value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
1911
|
+
}
|
|
1912
|
+
function createAlternation(values) {
|
|
1913
|
+
return [...values].sort((a, b) => b.length - a.length).map(escapeRegExp).join("|");
|
|
1914
|
+
}
|
|
1915
|
+
var shortWordsPattern = createAlternation(shortWords);
|
|
1916
|
+
var referenceTermsPattern = createAlternation(referenceTerms);
|
|
1917
|
+
var referenceAbbreviationsPattern = createAlternation(referenceAbbreviations);
|
|
1918
|
+
var unitsPattern = createAlternation(units);
|
|
1919
|
+
var referenceIdentifierPattern = String.raw`(?:\(\s*\d+(?:\.\d+)*\s*\)|\d+(?:\.\d+)*|[А-ЯЁA-Z](?:[^\p{L}]|$))`;
|
|
1920
|
+
function formatTypography(text) {
|
|
1921
|
+
return text.replace(
|
|
1922
|
+
new RegExp(`(^|[^\\p{L}\\p{N}_])(${shortWordsPattern})[ \\t]+(?=\\S)`, "giu"),
|
|
1923
|
+
(_, prefix, word) => `${prefix}${word}${NBSP}`
|
|
1924
|
+
).replace(
|
|
1925
|
+
new RegExp(`(^|[^\\p{L}\\p{N}_])(${referenceTermsPattern})[ \\t]+(?=${referenceIdentifierPattern})`, "giu"),
|
|
1926
|
+
(_, prefix, term) => `${prefix}${term}${NBSP}`
|
|
1927
|
+
).replace(
|
|
1928
|
+
new RegExp(`(^|[^\\p{L}\\p{N}_])(${referenceAbbreviationsPattern})\\.[ \\t]+(?=${referenceIdentifierPattern})`, "giu"),
|
|
1929
|
+
(_, prefix, abbreviation) => `${prefix}${abbreviation}.${NBSP}`
|
|
1930
|
+
).replace(/\b(п|пп|ч|ст|с|стр|абз)\.[ \t]+(?=\d)/giu, `$1.${NBSP}`).replace(/№[ \t]+(?=\d)/gu, `\u2116${NBSP}`).replace(/(§§?)[ \t]+(?=\d)/gu, `$1${NBSP}`).replace(/\b(ГОСТ)[ \t]+(?=Р(?:[ \t]+\d)|\d)/giu, `$1${NBSP}`).replace(
|
|
1931
|
+
/\b(ГОСТ\u00A0Р|ГОСТ Р)[ \t]+(?=\d)/giu,
|
|
1932
|
+
(_, standard) => `${standard.replace(" ", NBSP)}${NBSP}`
|
|
1933
|
+
).replace(/\b(ISO|IEC|IEEE|RFC|DIN|EN)[ \t]+(?=\d)/g, `$1${NBSP}`).replace(/\b(г|ул|пр|просп|пер|бул|наб|пл|д|корп|стр|кв)\.[ \t]+(?=\S)/giu, `$1.${NBSP}`).replace(/(\d{4})[ \t]+(?=гг?\.)/gu, `$1${NBSP}`).replace(/([А-ЯЁA-Z])\.[ \t]+([А-ЯЁA-Z])\.[ \t]+(?=[А-ЯЁA-Z][\p{L}-]+)/gu, `$1.${NBSP}$2.${NBSP}`).replace(/([А-ЯЁA-Z])\.[ \t]+(?=[А-ЯЁA-Z][\p{L}-]+)/gu, `$1.${NBSP}`).replace(new RegExp(`(\\d)[ \\t]+(?=(?:${unitsPattern})(?=$|[^\\p{L}]))`, "giu"), `$1${NBSP}`).replaceAll(/(?<=[\p{L}\p{N}])-(?=[\p{L}\p{N}])/gu, NON_BREAKING_HYPHEN);
|
|
1934
|
+
}
|
|
780
1935
|
|
|
781
1936
|
// src/serializer.ts
|
|
782
|
-
import * as
|
|
783
|
-
import { AlignmentType, Document, Footer, convertMillimetersToTwip, Packer, PageBreak, PageNumber, Paragraph, TableOfContents, TextRun, LevelFormat, Table, TableRow, TableCell, ImageRun, ExternalHyperlink, InternalHyperlink, Bookmark, XmlComponent, LineRuleType, PageOrientation } from "docx";
|
|
1937
|
+
import * as fs4 from "fs";
|
|
1938
|
+
import { AlignmentType, BorderStyle, Document, Footer, Header, convertMillimetersToTwip, Packer, PageBreak, PageNumber, Paragraph, TableOfContents, TextRun, LevelFormat, Table, TableRow, TableCell, ImageRun, ExternalHyperlink, InternalHyperlink, Bookmark, XmlComponent as XmlComponent2, LineRuleType, PageOrientation, VerticalAlignTable } from "docx";
|
|
784
1939
|
import { imageSize } from "image-size";
|
|
785
|
-
import
|
|
786
|
-
import Prism from "prismjs";
|
|
787
|
-
import "prismjs/components/prism-clike";
|
|
788
|
-
import "prismjs/components/prism-markup";
|
|
789
|
-
import "prismjs/components/prism-css";
|
|
790
|
-
import "prismjs/components/prism-javascript";
|
|
791
|
-
import "prismjs/components/prism-typescript";
|
|
792
|
-
import "prismjs/components/prism-jsx";
|
|
793
|
-
import "prismjs/components/prism-tsx";
|
|
794
|
-
import "prismjs/components/prism-json";
|
|
795
|
-
import "prismjs/components/prism-bash";
|
|
796
|
-
import "prismjs/components/prism-powershell";
|
|
797
|
-
import "prismjs/components/prism-python";
|
|
798
|
-
import "prismjs/components/prism-java";
|
|
799
|
-
import "prismjs/components/prism-c";
|
|
800
|
-
import "prismjs/components/prism-cpp";
|
|
801
|
-
import "prismjs/components/prism-csharp";
|
|
802
|
-
import "prismjs/components/prism-go";
|
|
803
|
-
import "prismjs/components/prism-rust";
|
|
804
|
-
import "prismjs/components/prism-php";
|
|
805
|
-
import "prismjs/components/prism-ruby";
|
|
806
|
-
import "prismjs/components/prism-swift";
|
|
807
|
-
import "prismjs/components/prism-kotlin";
|
|
808
|
-
import "prismjs/components/prism-sql";
|
|
809
|
-
import "prismjs/components/prism-yaml";
|
|
810
|
-
import "prismjs/components/prism-markdown";
|
|
811
|
-
import "prismjs/components/prism-docker";
|
|
812
|
-
import "prismjs/components/prism-nginx";
|
|
1940
|
+
import path4 from "path";
|
|
813
1941
|
|
|
814
|
-
// src/
|
|
815
|
-
|
|
816
|
-
|
|
817
|
-
|
|
1942
|
+
// src/math.ts
|
|
1943
|
+
import { XmlComponent } from "docx";
|
|
1944
|
+
var Omml = class extends XmlComponent {
|
|
1945
|
+
constructor(tag, children = [], attributes = {}) {
|
|
1946
|
+
super(tag);
|
|
1947
|
+
if (Object.keys(attributes).length) this.root.push({ _attr: attributes });
|
|
1948
|
+
this.root.push(...children);
|
|
1949
|
+
}
|
|
1950
|
+
};
|
|
1951
|
+
var symbols = {
|
|
1952
|
+
alpha: "\u03B1",
|
|
1953
|
+
beta: "\u03B2",
|
|
1954
|
+
gamma: "\u03B3",
|
|
1955
|
+
delta: "\u03B4",
|
|
1956
|
+
epsilon: "\u03B5",
|
|
1957
|
+
varepsilon: "\u03B5",
|
|
1958
|
+
zeta: "\u03B6",
|
|
1959
|
+
eta: "\u03B7",
|
|
1960
|
+
theta: "\u03B8",
|
|
1961
|
+
vartheta: "\u03D1",
|
|
1962
|
+
iota: "\u03B9",
|
|
1963
|
+
kappa: "\u03BA",
|
|
1964
|
+
lambda: "\u03BB",
|
|
1965
|
+
mu: "\u03BC",
|
|
1966
|
+
nu: "\u03BD",
|
|
1967
|
+
xi: "\u03BE",
|
|
1968
|
+
pi: "\u03C0",
|
|
1969
|
+
rho: "\u03C1",
|
|
1970
|
+
sigma: "\u03C3",
|
|
1971
|
+
tau: "\u03C4",
|
|
1972
|
+
upsilon: "\u03C5",
|
|
1973
|
+
phi: "\u03C6",
|
|
1974
|
+
varphi: "\u03D5",
|
|
1975
|
+
chi: "\u03C7",
|
|
1976
|
+
psi: "\u03C8",
|
|
1977
|
+
omega: "\u03C9",
|
|
1978
|
+
Gamma: "\u0393",
|
|
1979
|
+
Delta: "\u0394",
|
|
1980
|
+
Theta: "\u0398",
|
|
1981
|
+
Lambda: "\u039B",
|
|
1982
|
+
Xi: "\u039E",
|
|
1983
|
+
Pi: "\u03A0",
|
|
1984
|
+
Sigma: "\u03A3",
|
|
1985
|
+
Upsilon: "\u03A5",
|
|
1986
|
+
Phi: "\u03A6",
|
|
1987
|
+
Psi: "\u03A8",
|
|
1988
|
+
Omega: "\u03A9",
|
|
1989
|
+
cdot: "\xB7",
|
|
1990
|
+
times: "\xD7",
|
|
1991
|
+
div: "\xF7",
|
|
1992
|
+
pm: "\xB1",
|
|
1993
|
+
mp: "\u2213",
|
|
1994
|
+
ast: "\u2217",
|
|
1995
|
+
star: "\u22C6",
|
|
1996
|
+
circ: "\u2218",
|
|
1997
|
+
bullet: "\u2219",
|
|
1998
|
+
oplus: "\u2295",
|
|
1999
|
+
ominus: "\u2296",
|
|
2000
|
+
otimes: "\u2297",
|
|
2001
|
+
oslash: "\u2298",
|
|
2002
|
+
odot: "\u2299",
|
|
2003
|
+
setminus: "\u2216",
|
|
2004
|
+
wedge: "\u2227",
|
|
2005
|
+
vee: "\u2228",
|
|
2006
|
+
le: "\u2264",
|
|
2007
|
+
leq: "\u2264",
|
|
2008
|
+
ge: "\u2265",
|
|
2009
|
+
geq: "\u2265",
|
|
2010
|
+
neq: "\u2260",
|
|
2011
|
+
ne: "\u2260",
|
|
2012
|
+
approx: "\u2248",
|
|
2013
|
+
equiv: "\u2261",
|
|
2014
|
+
sim: "\u223C",
|
|
2015
|
+
simeq: "\u2243",
|
|
2016
|
+
cong: "\u2245",
|
|
2017
|
+
propto: "\u221D",
|
|
2018
|
+
ll: "\u226A",
|
|
2019
|
+
gg: "\u226B",
|
|
2020
|
+
infty: "\u221E",
|
|
2021
|
+
partial: "\u2202",
|
|
2022
|
+
nabla: "\u2207",
|
|
2023
|
+
in: "\u2208",
|
|
2024
|
+
notin: "\u2209",
|
|
2025
|
+
ni: "\u220B",
|
|
2026
|
+
subset: "\u2282",
|
|
2027
|
+
subseteq: "\u2286",
|
|
2028
|
+
superset: "\u2283",
|
|
2029
|
+
supseteq: "\u2287",
|
|
2030
|
+
emptyset: "\u2205",
|
|
2031
|
+
varnothing: "\u2205",
|
|
2032
|
+
cup: "\u222A",
|
|
2033
|
+
cap: "\u2229",
|
|
2034
|
+
perp: "\u22A5",
|
|
2035
|
+
parallel: "\u2225",
|
|
2036
|
+
mid: "\u2223",
|
|
2037
|
+
angle: "\u2220",
|
|
2038
|
+
therefore: "\u2234",
|
|
2039
|
+
because: "\u2235",
|
|
2040
|
+
to: "\u2192",
|
|
2041
|
+
rightarrow: "\u2192",
|
|
2042
|
+
leftarrow: "\u2190",
|
|
2043
|
+
leftrightarrow: "\u2194",
|
|
2044
|
+
mapsto: "\u21A6",
|
|
2045
|
+
Rightarrow: "\u21D2",
|
|
2046
|
+
Leftarrow: "\u21D0",
|
|
2047
|
+
Leftrightarrow: "\u21D4",
|
|
2048
|
+
uparrow: "\u2191",
|
|
2049
|
+
downarrow: "\u2193",
|
|
2050
|
+
updownarrow: "\u2195",
|
|
2051
|
+
forall: "\u2200",
|
|
2052
|
+
exists: "\u2203",
|
|
2053
|
+
neg: "\xAC",
|
|
2054
|
+
land: "\u2227",
|
|
2055
|
+
lor: "\u2228",
|
|
2056
|
+
degree: "\xB0",
|
|
2057
|
+
ell: "\u2113",
|
|
2058
|
+
prime: "\u2032",
|
|
2059
|
+
hbar: "\u210F",
|
|
2060
|
+
aleph: "\u2135",
|
|
2061
|
+
Re: "\u211C",
|
|
2062
|
+
Im: "\u2111",
|
|
2063
|
+
ldots: "\u2026",
|
|
2064
|
+
dots: "\u2026",
|
|
2065
|
+
cdots: "\u22EF",
|
|
2066
|
+
vdots: "\u22EE",
|
|
2067
|
+
ddots: "\u22F1"
|
|
2068
|
+
};
|
|
2069
|
+
var operators = { sin: "sin", cos: "cos", tan: "tan", cot: "cot", sec: "sec", csc: "csc", log: "log", ln: "ln", exp: "exp", max: "max", min: "min", gcd: "gcd", det: "det" };
|
|
2070
|
+
function run(text, style) {
|
|
2071
|
+
const value = style == "plain" ? "p" : style == "bold" ? "b" : style == "italic" ? "i" : void 0;
|
|
2072
|
+
return new Omml("m:r", [
|
|
2073
|
+
...style == "double-struck" || style == "script" ? [new Omml("m:rPr", [new Omml("m:scr", [], { "m:val": style }), new Omml("m:sty", [], { "m:val": "p" })])] : [],
|
|
2074
|
+
...value ? [new Omml("m:rPr", [new Omml("m:sty", [], { "m:val": value })])] : [],
|
|
2075
|
+
new Omml("m:t", [text], { "xml:space": "preserve" })
|
|
2076
|
+
]);
|
|
2077
|
+
}
|
|
2078
|
+
function arg(children) {
|
|
2079
|
+
return new Omml("m:e", children);
|
|
2080
|
+
}
|
|
2081
|
+
function delimiter(children, begin, end) {
|
|
2082
|
+
return new Omml("m:d", [
|
|
2083
|
+
new Omml("m:dPr", [new Omml("m:begChr", [], { "m:val": begin }), new Omml("m:endChr", [], { "m:val": end })]),
|
|
2084
|
+
arg(children)
|
|
2085
|
+
]);
|
|
2086
|
+
}
|
|
2087
|
+
function latexToOmml(latex, para = false) {
|
|
2088
|
+
const parser = new LatexParser(latex);
|
|
2089
|
+
const body = parser.sequence();
|
|
2090
|
+
if (!parser.done()) throw new Error(`unexpected "${parser.peek()}"`);
|
|
2091
|
+
const math = new Omml("m:oMath", body);
|
|
2092
|
+
return para ? new Omml("m:oMathPara", [math]) : math;
|
|
2093
|
+
}
|
|
2094
|
+
var LatexParser = class {
|
|
2095
|
+
constructor(source) {
|
|
2096
|
+
this.source = source;
|
|
2097
|
+
}
|
|
2098
|
+
source;
|
|
2099
|
+
i = 0;
|
|
2100
|
+
done() {
|
|
2101
|
+
return this.i >= this.source.length;
|
|
2102
|
+
}
|
|
2103
|
+
peek() {
|
|
2104
|
+
return this.source[this.i] || "";
|
|
2105
|
+
}
|
|
2106
|
+
take() {
|
|
2107
|
+
return this.source[this.i++] || "";
|
|
2108
|
+
}
|
|
2109
|
+
skipWhitespace() {
|
|
2110
|
+
while (/\s/.test(this.peek())) this.take();
|
|
2111
|
+
}
|
|
2112
|
+
command(name) {
|
|
2113
|
+
return this.source.slice(this.i).startsWith(`\\${name}`) && !/[A-Za-z]/.test(this.source[this.i + name.length + 1] || "");
|
|
2114
|
+
}
|
|
2115
|
+
sequence(stop, style, preserveWhitespace = false, boundary) {
|
|
2116
|
+
const result = [];
|
|
2117
|
+
while (!this.done() && this.peek() != stop && !boundary?.()) {
|
|
2118
|
+
if (/\s/.test(this.peek())) {
|
|
2119
|
+
this.take();
|
|
2120
|
+
if (preserveWhitespace) result.push(run(" ", style));
|
|
2121
|
+
continue;
|
|
2122
|
+
}
|
|
2123
|
+
if (this.command("displaystyle") || this.command("textstyle")) {
|
|
2124
|
+
this.take();
|
|
2125
|
+
while (/[A-Za-z]/.test(this.peek())) this.take();
|
|
2126
|
+
continue;
|
|
2127
|
+
}
|
|
2128
|
+
if (this.source.startsWith("\\!", this.i)) {
|
|
2129
|
+
this.i += 2;
|
|
2130
|
+
continue;
|
|
2131
|
+
}
|
|
2132
|
+
result.push(this.decoratedAtom(style, preserveWhitespace));
|
|
2133
|
+
}
|
|
2134
|
+
if (stop) {
|
|
2135
|
+
if (this.take() != stop) throw new Error(`missing "${stop}"`);
|
|
2136
|
+
}
|
|
2137
|
+
return result;
|
|
2138
|
+
}
|
|
2139
|
+
decoratedAtom(style, preserveWhitespace = false) {
|
|
2140
|
+
let value = this.atom(style, preserveWhitespace);
|
|
2141
|
+
let sub;
|
|
2142
|
+
let sup;
|
|
2143
|
+
while (true) {
|
|
2144
|
+
const beforeSpace = this.i;
|
|
2145
|
+
this.skipWhitespace();
|
|
2146
|
+
if (this.peek() != "_" && this.peek() != "^") {
|
|
2147
|
+
this.i = beforeSpace;
|
|
2148
|
+
break;
|
|
2149
|
+
}
|
|
2150
|
+
const marker = this.take();
|
|
2151
|
+
const script = this.groupOrAtom(style);
|
|
2152
|
+
if (marker == "_" ? sub : sup) throw new Error("duplicate script");
|
|
2153
|
+
if (marker == "_") sub = script;
|
|
2154
|
+
else sup = script;
|
|
2155
|
+
}
|
|
2156
|
+
if (sub && sup) value = new Omml("m:sSubSup", [new Omml("m:e", [value]), new Omml("m:sub", sub), new Omml("m:sup", sup)]);
|
|
2157
|
+
else if (sub) value = new Omml("m:sSub", [new Omml("m:e", [value]), new Omml("m:sub", sub)]);
|
|
2158
|
+
else if (sup) value = new Omml("m:sSup", [new Omml("m:e", [value]), new Omml("m:sup", sup)]);
|
|
2159
|
+
return value;
|
|
2160
|
+
}
|
|
2161
|
+
groupOrAtom(style, preserveWhitespace = false) {
|
|
2162
|
+
this.skipWhitespace();
|
|
2163
|
+
if (this.done() || ["}", "_", "^", "&"].includes(this.peek())) throw new Error("argument expected");
|
|
2164
|
+
if (this.peek() == "{") {
|
|
2165
|
+
this.take();
|
|
2166
|
+
return this.sequence("}", style, preserveWhitespace);
|
|
2167
|
+
}
|
|
2168
|
+
return [this.atom(style)];
|
|
2169
|
+
}
|
|
2170
|
+
atom(style, preserveWhitespace = false) {
|
|
2171
|
+
const ch = this.take();
|
|
2172
|
+
if (!ch || ["}", "_", "^", "&"].includes(ch)) throw new Error(`unexpected "${ch}"`);
|
|
2173
|
+
if (ch == "{") return new Omml("m:box", [arg(this.sequence("}", style, preserveWhitespace))]);
|
|
2174
|
+
if (ch != "\\") return run(ch, style);
|
|
2175
|
+
let name = "";
|
|
2176
|
+
while (/[A-Za-z]/.test(this.peek())) name += this.take();
|
|
2177
|
+
if (!name) {
|
|
2178
|
+
const escaped = this.take();
|
|
2179
|
+
if (!escaped) throw new Error("command expected");
|
|
2180
|
+
const spaces = { ",": "\u2009", ";": "\u2004", ":": "\u2005", " ": " " };
|
|
2181
|
+
if (spaces[escaped]) return run(spaces[escaped], style);
|
|
2182
|
+
if (escaped == "!") return run("", style);
|
|
2183
|
+
return run(escaped, style);
|
|
2184
|
+
}
|
|
2185
|
+
this.skipWhitespace();
|
|
2186
|
+
if (name == "frac" || name == "dfrac" || name == "tfrac") return new Omml("m:f", [new Omml("m:num", this.groupOrAtom(style)), new Omml("m:den", this.groupOrAtom(style))]);
|
|
2187
|
+
if (name == "sqrt") {
|
|
2188
|
+
let degree;
|
|
2189
|
+
if (this.peek() == "[") {
|
|
2190
|
+
this.take();
|
|
2191
|
+
degree = this.sequence("]", style);
|
|
2192
|
+
}
|
|
2193
|
+
return new Omml("m:rad", [new Omml("m:radPr", degree ? [] : [new Omml("m:degHide", [], { "m:val": "1" })]), new Omml("m:deg", degree || []), arg(this.groupOrAtom(style))]);
|
|
2194
|
+
}
|
|
2195
|
+
if (name == "text") return new Omml("m:box", [arg(this.groupOrAtom("plain", true))]);
|
|
2196
|
+
if (name == "mathrm" || name == "operatorname") return new Omml("m:box", [arg(this.groupOrAtom("plain"))]);
|
|
2197
|
+
if (name == "mathbf") return new Omml("m:box", [arg(this.groupOrAtom("bold"))]);
|
|
2198
|
+
if (name == "mathit") return new Omml("m:box", [arg(this.groupOrAtom("italic"))]);
|
|
2199
|
+
if (["mathbb", "mathcal"].includes(name)) return new Omml("m:box", [arg(this.groupOrAtom(name == "mathbb" ? "double-struck" : "script"))]);
|
|
2200
|
+
if (["hat", "widehat", "vec"].includes(name)) {
|
|
2201
|
+
const chr = name == "vec" ? "\u20D7" : "\u0302";
|
|
2202
|
+
return new Omml("m:acc", [new Omml("m:accPr", [new Omml("m:chr", [], { "m:val": chr })]), arg(this.groupOrAtom(style))]);
|
|
2203
|
+
}
|
|
2204
|
+
if (["overline", "bar", "underline"].includes(name)) {
|
|
2205
|
+
const pos = name == "underline" ? "bot" : "top";
|
|
2206
|
+
return new Omml("m:bar", [new Omml("m:barPr", [new Omml("m:pos", [], { "m:val": pos })]), arg(this.groupOrAtom(style))]);
|
|
2207
|
+
}
|
|
2208
|
+
if (name == "binom" || name == "dbinom") return delimiter([new Omml("m:f", [new Omml("m:fPr", [new Omml("m:type", [], { "m:val": "noBar" })]), new Omml("m:num", this.groupOrAtom(style)), new Omml("m:den", this.groupOrAtom(style))])], "(", ")");
|
|
2209
|
+
if (name == "lim") {
|
|
2210
|
+
let below = true;
|
|
2211
|
+
if (this.command("limits") || this.command("nolimits")) {
|
|
2212
|
+
below = this.command("limits");
|
|
2213
|
+
this.i += below ? 7 : 9;
|
|
2214
|
+
this.skipWhitespace();
|
|
2215
|
+
}
|
|
2216
|
+
let sub = [];
|
|
2217
|
+
if (this.peek() == "_") {
|
|
2218
|
+
this.take();
|
|
2219
|
+
sub = this.groupOrAtom(style);
|
|
2220
|
+
}
|
|
2221
|
+
if (!sub.length) return run("lim", "plain");
|
|
2222
|
+
return new Omml(below ? "m:limLow" : "m:sSub", [arg([run("lim", "plain")]), new Omml(below ? "m:lim" : "m:sub", sub)]);
|
|
2223
|
+
}
|
|
2224
|
+
if (["sum", "prod", "int", "iint", "iiint"].includes(name)) {
|
|
2225
|
+
const chr = name == "sum" ? "\u2211" : name == "prod" ? "\u220F" : name == "iint" ? "\u222C" : name == "iiint" ? "\u222D" : "\u222B";
|
|
2226
|
+
let sub = [], sup = [];
|
|
2227
|
+
let limitLocation;
|
|
2228
|
+
while (true) {
|
|
2229
|
+
this.skipWhitespace();
|
|
2230
|
+
if (this.command("limits") || this.command("nolimits")) {
|
|
2231
|
+
const limits = this.command("limits");
|
|
2232
|
+
this.i += limits ? 7 : 9;
|
|
2233
|
+
limitLocation = limits ? "undOvr" : "subSup";
|
|
2234
|
+
continue;
|
|
2235
|
+
}
|
|
2236
|
+
if (this.peek() != "_" && this.peek() != "^") break;
|
|
2237
|
+
const marker = this.take();
|
|
2238
|
+
const script = this.groupOrAtom(style);
|
|
2239
|
+
if (marker == "_" ? sub.length : sup.length) throw new Error("duplicate script");
|
|
2240
|
+
if (marker == "_") sub = script;
|
|
2241
|
+
else sup = script;
|
|
2242
|
+
this.skipWhitespace();
|
|
2243
|
+
}
|
|
2244
|
+
while (/\s/.test(this.peek())) this.take();
|
|
2245
|
+
const operand = this.done() || ["}", "]", ")", "&", "=", "<", ">"].includes(this.peek()) || this.command("right") || this.command("end") || this.source.startsWith("\\\\", this.i) ? [] : [this.decoratedAtom(style)];
|
|
2246
|
+
return new Omml("m:nary", [new Omml("m:naryPr", [
|
|
2247
|
+
new Omml("m:chr", [], { "m:val": chr }),
|
|
2248
|
+
...limitLocation ? [new Omml("m:limLoc", [], { "m:val": limitLocation })] : [],
|
|
2249
|
+
new Omml("m:subHide", [], { "m:val": sub.length ? "0" : "1" }),
|
|
2250
|
+
new Omml("m:supHide", [], { "m:val": sup.length ? "0" : "1" })
|
|
2251
|
+
]), new Omml("m:sub", sub), new Omml("m:sup", sup), arg(operand)]);
|
|
2252
|
+
}
|
|
2253
|
+
if (name == "left") {
|
|
2254
|
+
const begin = this.readDelimiter();
|
|
2255
|
+
const body = this.sequence(void 0, style, false, () => this.command("right"));
|
|
2256
|
+
if (!this.command("right")) throw new Error("missing \\right");
|
|
2257
|
+
this.i += 6;
|
|
2258
|
+
return delimiter(body, begin, this.readDelimiter());
|
|
2259
|
+
}
|
|
2260
|
+
if (name == "begin") return this.matrix(style);
|
|
2261
|
+
if (symbols[name]) return run(symbols[name], style);
|
|
2262
|
+
if (operators[name]) return run(operators[name], "plain");
|
|
2263
|
+
if (["quad", "qquad"].includes(name)) return run(name == "quad" ? "\u2003" : "\u2003\u2003", style);
|
|
2264
|
+
throw new Error(`unsupported command \\${name}`);
|
|
2265
|
+
}
|
|
2266
|
+
readDelimiter() {
|
|
2267
|
+
this.skipWhitespace();
|
|
2268
|
+
const ch = this.take();
|
|
2269
|
+
if (ch == ".") return "";
|
|
2270
|
+
if (ch && "()[]|{}".includes(ch)) return ch;
|
|
2271
|
+
if (ch == "\\") {
|
|
2272
|
+
let name = "";
|
|
2273
|
+
while (/[A-Za-z]/.test(this.peek())) name += this.take();
|
|
2274
|
+
if (!name) name = this.take();
|
|
2275
|
+
const delimiters = { "{": "{", "}": "}", "|": "\u2016", lbrace: "{", rbrace: "}", langle: "\u27E8", rangle: "\u27E9", lvert: "|", rvert: "|", vert: "|", lVert: "\u2016", rVert: "\u2016", Vert: "\u2016" };
|
|
2276
|
+
if (delimiters[name]) return delimiters[name];
|
|
2277
|
+
}
|
|
2278
|
+
throw new Error("delimiter expected");
|
|
2279
|
+
}
|
|
2280
|
+
matrix(style) {
|
|
2281
|
+
if (this.peek() != "{") throw new Error("environment name expected");
|
|
2282
|
+
this.take();
|
|
2283
|
+
let env = "";
|
|
2284
|
+
while (!this.done() && this.peek() != "}") env += this.take();
|
|
2285
|
+
this.take();
|
|
2286
|
+
if (!["matrix", "pmatrix", "bmatrix", "cases"].includes(env)) throw new Error(`unsupported environment ${env}`);
|
|
2287
|
+
const rows = [];
|
|
2288
|
+
let cells = [];
|
|
2289
|
+
while (true) {
|
|
2290
|
+
this.skipWhitespace();
|
|
2291
|
+
if (this.done()) throw new Error(`environment ${env} is not closed`);
|
|
2292
|
+
if (this.command("end")) {
|
|
2293
|
+
this.i += 4;
|
|
2294
|
+
this.skipWhitespace();
|
|
2295
|
+
const closing = `{${env}}`;
|
|
2296
|
+
if (!this.source.startsWith(closing, this.i)) throw new Error(`mismatched environment ${env}`);
|
|
2297
|
+
this.i += closing.length;
|
|
2298
|
+
if (cells.length) rows.push(new Omml("m:mr", cells));
|
|
2299
|
+
break;
|
|
2300
|
+
}
|
|
2301
|
+
cells.push(arg(this.sequence(void 0, style, false, () => this.peek() == "&" || this.source.startsWith("\\\\", this.i) || this.command("end"))));
|
|
2302
|
+
if (this.peek() == "&") {
|
|
2303
|
+
this.take();
|
|
2304
|
+
continue;
|
|
2305
|
+
}
|
|
2306
|
+
if (this.source.startsWith("\\\\", this.i)) {
|
|
2307
|
+
this.i += 2;
|
|
2308
|
+
rows.push(new Omml("m:mr", cells));
|
|
2309
|
+
cells = [];
|
|
2310
|
+
}
|
|
2311
|
+
}
|
|
2312
|
+
const matrix = new Omml("m:m", rows);
|
|
2313
|
+
if (env == "pmatrix") return delimiter([matrix], "(", ")");
|
|
2314
|
+
if (env == "bmatrix") return delimiter([matrix], "[", "]");
|
|
2315
|
+
if (env == "cases") return delimiter([matrix], "{", "");
|
|
2316
|
+
return matrix;
|
|
818
2317
|
}
|
|
819
2318
|
};
|
|
820
2319
|
|
|
821
2320
|
// src/serializer.ts
|
|
2321
|
+
import Prism from "prismjs";
|
|
2322
|
+
import "prismjs/components/prism-clike.js";
|
|
2323
|
+
import "prismjs/components/prism-markup.js";
|
|
2324
|
+
import "prismjs/components/prism-css.js";
|
|
2325
|
+
import "prismjs/components/prism-javascript.js";
|
|
2326
|
+
import "prismjs/components/prism-typescript.js";
|
|
2327
|
+
import "prismjs/components/prism-jsx.js";
|
|
2328
|
+
import "prismjs/components/prism-tsx.js";
|
|
2329
|
+
import "prismjs/components/prism-json.js";
|
|
2330
|
+
import "prismjs/components/prism-bash.js";
|
|
2331
|
+
import "prismjs/components/prism-powershell.js";
|
|
2332
|
+
import "prismjs/components/prism-python.js";
|
|
2333
|
+
import "prismjs/components/prism-java.js";
|
|
2334
|
+
import "prismjs/components/prism-c.js";
|
|
2335
|
+
import "prismjs/components/prism-cpp.js";
|
|
2336
|
+
import "prismjs/components/prism-csharp.js";
|
|
2337
|
+
import "prismjs/components/prism-go.js";
|
|
2338
|
+
import "prismjs/components/prism-rust.js";
|
|
2339
|
+
import "prismjs/components/prism-php.js";
|
|
2340
|
+
import "prismjs/components/prism-ruby.js";
|
|
2341
|
+
import "prismjs/components/prism-swift.js";
|
|
2342
|
+
import "prismjs/components/prism-kotlin.js";
|
|
2343
|
+
import "prismjs/components/prism-sql.js";
|
|
2344
|
+
import "prismjs/components/prism-yaml.js";
|
|
2345
|
+
import "prismjs/components/prism-markdown.js";
|
|
2346
|
+
import "prismjs/components/prism-docker.js";
|
|
2347
|
+
import "prismjs/components/prism-nginx.js";
|
|
822
2348
|
var STYLE_list = "afc";
|
|
823
2349
|
var STYLE_table_title = "TableCaption";
|
|
824
2350
|
var STYLE_code_title = "ListingCaption";
|
|
825
2351
|
var STYLE_code = "ListingCode";
|
|
826
|
-
async function serializeDocx(doc, fout, workdir, assets, checkFilesIsInsidePath) {
|
|
827
|
-
const
|
|
828
|
-
const getPath = (fname) => {
|
|
829
|
-
if (process.platform != "win32") fname = fname.replaceAll("\\", "/");
|
|
830
|
-
if (process.platform == "win32" && fname.startsWith("/")) fname = "." + fname;
|
|
831
|
-
const targetPath = path3.isAbsolute(fname) ? path3.resolve(fname) : path3.resolve(workdir, fname);
|
|
832
|
-
if (allowedRealPath !== false) {
|
|
833
|
-
const targetRealPath = realpathAllowMissing(targetPath);
|
|
834
|
-
const relative = path3.relative(allowedRealPath, targetRealPath);
|
|
835
|
-
const isOutside = relative === ".." || relative.startsWith(`..${path3.sep}`) || path3.isAbsolute(relative);
|
|
836
|
-
if (isOutside) throw new UserInputError(`Access denied: "${fname}" resolves outside the working directory.`);
|
|
837
|
-
}
|
|
838
|
-
return targetPath;
|
|
839
|
-
};
|
|
2352
|
+
async function serializeDocx(doc, fout, workdir, assets, checkFilesIsInsidePath, logwarn = console.warn) {
|
|
2353
|
+
const { resolvePath: getPath } = getSafePathResolver(workdir, checkFilesIsInsidePath);
|
|
840
2354
|
const sections = [];
|
|
841
2355
|
const numbering = [];
|
|
842
2356
|
const docSections = (function splitSections() {
|
|
843
|
-
|
|
2357
|
+
let header = doc.header;
|
|
2358
|
+
let footer = doc.footer;
|
|
2359
|
+
const sections2 = [{ displayPageNum: true, orientation: "portrait", pageStart: 1, header, footer, nodes: [] }];
|
|
844
2360
|
for (let j = 0; j < doc.nodes.length; j++) {
|
|
845
2361
|
const node = doc.nodes[j];
|
|
846
|
-
if (node.type == "sectionBreak")
|
|
2362
|
+
if (node.type == "sectionBreak") {
|
|
2363
|
+
header = node.header ?? header;
|
|
2364
|
+
footer = node.footer ?? footer;
|
|
847
2365
|
sections2.push({
|
|
848
|
-
displayPageNum: node.pageStart == null ? sections2.at(-1)?.displayPageNum
|
|
2366
|
+
displayPageNum: node.pageStart == null ? sections2.at(-1)?.displayPageNum ?? true : node.pageStart >= 0,
|
|
849
2367
|
orientation: node.orientation || sections2.at(-1)?.orientation || "portrait",
|
|
850
2368
|
pageStart: node.pageStart,
|
|
2369
|
+
header,
|
|
2370
|
+
footer,
|
|
851
2371
|
nodes: []
|
|
852
2372
|
});
|
|
853
|
-
else
|
|
2373
|
+
} else
|
|
854
2374
|
sections2.at(-1)?.nodes.push(node);
|
|
855
2375
|
}
|
|
856
2376
|
return sections2.filter((s) => s.nodes.length > 0);
|
|
857
2377
|
})();
|
|
858
2378
|
for (const section of docSections) {
|
|
859
|
-
let
|
|
2379
|
+
let renderHeaderFooter2 = function(value, isFooter) {
|
|
2380
|
+
if (value.type == "none") return void 0;
|
|
2381
|
+
if (value.type == "auto") {
|
|
2382
|
+
if (!isFooter) return void 0;
|
|
2383
|
+
return new Footer({
|
|
2384
|
+
children: [new Paragraph({
|
|
2385
|
+
alignment: AlignmentType.CENTER,
|
|
2386
|
+
children: [new TextRun({ children: section.displayPageNum ? [PageNumber.CURRENT] : [] })],
|
|
2387
|
+
indent: { firstLine: 0 },
|
|
2388
|
+
spacing: { after: 0, line: 240 }
|
|
2389
|
+
})]
|
|
2390
|
+
});
|
|
2391
|
+
}
|
|
2392
|
+
const contentValue = value;
|
|
2393
|
+
const content = contentValue.nodes.flatMap((node) => {
|
|
2394
|
+
if (node.type == "text") return [new Paragraph({
|
|
2395
|
+
children: renderText(node.text, node.sourceLine),
|
|
2396
|
+
alignment: contentValue.align,
|
|
2397
|
+
indent: { firstLine: 0 },
|
|
2398
|
+
spacing: isFooter ? { after: 0, line: 240 } : { line: 240 }
|
|
2399
|
+
})];
|
|
2400
|
+
return [renderHeaderFooterTable2(node)];
|
|
2401
|
+
});
|
|
2402
|
+
return isFooter ? new Footer({ children: content }) : new Header({ children: content });
|
|
2403
|
+
}, renderHeaderFooterTable2 = function(node) {
|
|
2404
|
+
return new Table({
|
|
2405
|
+
width: { type: "dxa", size: convertMillimetersToTwip(section.orientation == "landscape" ? 255.8 : 168.8) },
|
|
2406
|
+
borders: {
|
|
2407
|
+
top: { style: BorderStyle.SINGLE, size: 4, color: "808080" },
|
|
2408
|
+
bottom: { style: BorderStyle.SINGLE, size: 4, color: "808080" },
|
|
2409
|
+
left: { style: BorderStyle.SINGLE, size: 4, color: "808080" },
|
|
2410
|
+
right: { style: BorderStyle.SINGLE, size: 4, color: "808080" },
|
|
2411
|
+
insideHorizontal: { style: BorderStyle.SINGLE, size: 4, color: "808080" },
|
|
2412
|
+
insideVertical: { style: BorderStyle.SINGLE, size: 4, color: "808080" }
|
|
2413
|
+
},
|
|
2414
|
+
rows: node.rows.map((row) => new TableRow({
|
|
2415
|
+
children: row.map((cell, colI) => new TableCell({
|
|
2416
|
+
children: cell.type == "text" ? [new Paragraph({
|
|
2417
|
+
children: renderText(cell.text, node.sourceLine),
|
|
2418
|
+
alignment: node.align[colI] == "c" ? "center" : node.align[colI] == "r" ? "right" : "left",
|
|
2419
|
+
indent: { firstLine: 0 },
|
|
2420
|
+
spacing: { after: 0, line: 240 * 1.25 }
|
|
2421
|
+
})] : []
|
|
2422
|
+
}))
|
|
2423
|
+
}))
|
|
2424
|
+
});
|
|
2425
|
+
}, renderNode2 = function(node, prevChild, prevNode) {
|
|
860
2426
|
switch (node.type) {
|
|
861
2427
|
case "text":
|
|
862
2428
|
return new Paragraph({
|
|
863
|
-
children: renderText(node.text),
|
|
2429
|
+
children: renderText(node.text, node.sourceLine),
|
|
864
2430
|
indent: node.noIndent ? { firstLine: 0 } : {},
|
|
865
2431
|
...node.center ? { alignment: "center", indent: { firstLine: 0 } } : {},
|
|
866
2432
|
spacing: {
|
|
@@ -874,7 +2440,7 @@ async function serializeDocx(doc, fout, workdir, assets, checkFilesIsInsidePath)
|
|
|
874
2440
|
const styles = doc.headings[`h${level}`];
|
|
875
2441
|
if (styles.uppercase) node.text.forEach((r) => r.text = r.text.toUpperCase());
|
|
876
2442
|
return new Paragraph({
|
|
877
|
-
children: renderText(node.text, styles.size),
|
|
2443
|
+
children: renderText(node.text, node.sourceLine, styles.size),
|
|
878
2444
|
style: `${node.level}`,
|
|
879
2445
|
...node.center ? { alignment: "center", indent: { firstLine: 0 } } : {},
|
|
880
2446
|
...!node.center && node.level != 0 && styles.indent_full ? { indent: { firstLine: 0, left: convertMillimetersToTwip(12.5) } } : {},
|
|
@@ -898,7 +2464,7 @@ async function serializeDocx(doc, fout, workdir, assets, checkFilesIsInsidePath)
|
|
|
898
2464
|
if (item.type == "list") renderList2({ ...item, alternativeStyle: !!node2.alternativeStyle }, level2 + 1);
|
|
899
2465
|
else {
|
|
900
2466
|
items.push(new Paragraph({
|
|
901
|
-
children: renderText(item.text),
|
|
2467
|
+
children: renderText(item.text, item.sourceLine ?? node2.sourceLine),
|
|
902
2468
|
style: STYLE_list,
|
|
903
2469
|
numbering: { reference: id, level: level2 },
|
|
904
2470
|
spacing: {
|
|
@@ -922,13 +2488,13 @@ async function serializeDocx(doc, fout, workdir, assets, checkFilesIsInsidePath)
|
|
|
922
2488
|
renderList2(node);
|
|
923
2489
|
return items;
|
|
924
2490
|
case "table":
|
|
925
|
-
if (node.rows[0] && doc.table.heading.style == "italic") node.rows[0].forEach((n) => n.type == "text" ? n.text.forEach((r) => r.italic = true) : 0);
|
|
926
|
-
if (node.rows[0] && doc.table.heading.style == "bold") node.rows[0].forEach((n) => n.type == "text" ? n.text.forEach((r) => r.bold = true) : 0);
|
|
2491
|
+
if (node.header !== false && node.rows[0] && doc.table.heading.style == "italic") node.rows[0].forEach((n) => n.type == "text" ? n.text.forEach((r) => r.italic = true) : 0);
|
|
2492
|
+
if (node.header !== false && node.rows[0] && doc.table.heading.style == "bold") node.rows[0].forEach((n) => n.type == "text" ? n.text.forEach((r) => r.bold = true) : 0);
|
|
927
2493
|
const spacingInner = Math.max(doc.table.spacing.after ?? doc.text.spacing.after, doc.table.spacing.before ?? 0);
|
|
928
2494
|
return [
|
|
929
2495
|
...node.title ? [
|
|
930
2496
|
new Paragraph({
|
|
931
|
-
children: renderText(node.title, doc.table.title.size),
|
|
2497
|
+
children: renderText(node.title, node.sourceLine, doc.table.title.size),
|
|
932
2498
|
style: STYLE_table_title,
|
|
933
2499
|
...prevChild instanceof Table ? { spacing: { before: spacingInner * 20 } } : doc.table.spacing.before ? { spacing: { before: doc.table.spacing.before * 20 } } : {}
|
|
934
2500
|
})
|
|
@@ -944,13 +2510,13 @@ async function serializeDocx(doc, fout, workdir, assets, checkFilesIsInsidePath)
|
|
|
944
2510
|
size: convertMillimetersToTwip(section.orientation === "landscape" ? 255.8 : 168.8)
|
|
945
2511
|
},
|
|
946
2512
|
rows: node.rows.map((row, rowI) => new TableRow({
|
|
947
|
-
tableHeader: rowI == 0,
|
|
2513
|
+
tableHeader: node.header !== false && rowI == 0,
|
|
948
2514
|
cantSplit: true,
|
|
949
2515
|
children: row.map((item, colI) => new TableCell({
|
|
950
2516
|
children: item.type != "text" ? renderNodeL2(item) : [
|
|
951
2517
|
new Paragraph({
|
|
952
|
-
children: renderText(item.text, node.normalFontSize ? void 0 : doc.table.text.size),
|
|
953
|
-
alignment: rowI == 0 ? doc.table.heading.align : node.align[colI] == "c" ? "center" : node.align[colI] == "r" ? "right" : "left",
|
|
2518
|
+
children: renderText(item.text, node.sourceLine, node.normalFontSize ? void 0 : doc.table.text.size),
|
|
2519
|
+
alignment: node.header !== false && rowI == 0 ? doc.table.heading.align : node.align[colI] == "c" ? "center" : node.align[colI] == "r" ? "right" : "left",
|
|
954
2520
|
indent: { firstLine: 0 },
|
|
955
2521
|
spacing: { after: 0, line: 240 * doc.table.text.line_spacing }
|
|
956
2522
|
})
|
|
@@ -968,13 +2534,13 @@ async function serializeDocx(doc, fout, workdir, assets, checkFilesIsInsidePath)
|
|
|
968
2534
|
new Paragraph({ children: [new PageBreak()] })
|
|
969
2535
|
];
|
|
970
2536
|
case "image":
|
|
971
|
-
const type = node.src.split(".").at(-1) || "";
|
|
2537
|
+
const type = (node.src.split(".").at(-1) || "").toLowerCase();
|
|
972
2538
|
if (!["jpg", "png", "gif", "bmp", "svg"].includes(type))
|
|
973
2539
|
throw new UserInputError(`Unsupported image format: "${type}", file: ${node.src}`);
|
|
974
2540
|
const img_path = getPath(node.src);
|
|
975
|
-
if (!
|
|
2541
|
+
if (!fs4.existsSync(img_path))
|
|
976
2542
|
throw new UserInputError(`File not exist: ${node.src}`);
|
|
977
|
-
const data =
|
|
2543
|
+
const data = fs4.readFileSync(img_path);
|
|
978
2544
|
const dimensions = imageSize(data);
|
|
979
2545
|
const [MaxW, MaxH] = section.orientation == "landscape" ? [950, 600] : [600, 900];
|
|
980
2546
|
let [width, height] = [dimensions.width, dimensions.height];
|
|
@@ -1006,7 +2572,7 @@ async function serializeDocx(doc, fout, workdir, assets, checkFilesIsInsidePath)
|
|
|
1006
2572
|
}),
|
|
1007
2573
|
...node.text ? [
|
|
1008
2574
|
new Paragraph({
|
|
1009
|
-
children: renderText(node.text, doc.img.text.size),
|
|
2575
|
+
children: renderText(node.text, node.sourceLine, doc.img.text.size),
|
|
1010
2576
|
alignment: "center",
|
|
1011
2577
|
indent: { firstLine: 0 },
|
|
1012
2578
|
spacing: {
|
|
@@ -1021,7 +2587,7 @@ async function serializeDocx(doc, fout, workdir, assets, checkFilesIsInsidePath)
|
|
|
1021
2587
|
return [
|
|
1022
2588
|
...node.title ? [
|
|
1023
2589
|
new Paragraph({
|
|
1024
|
-
children: renderText(node.title, doc.code.title.size),
|
|
2590
|
+
children: renderText(node.title, node.sourceLine, doc.code.title.size),
|
|
1025
2591
|
style: STYLE_code_title
|
|
1026
2592
|
})
|
|
1027
2593
|
] : prevNode?.type == "code" ? [
|
|
@@ -1033,11 +2599,44 @@ async function serializeDocx(doc, fout, workdir, assets, checkFilesIsInsidePath)
|
|
|
1033
2599
|
...i == 0 && !node.title && doc.code.spacing.before && prevNode?.type != "code" ? { spacing: { before: doc.code.spacing.before * 20 } } : {}
|
|
1034
2600
|
}))
|
|
1035
2601
|
];
|
|
2602
|
+
case "math":
|
|
2603
|
+
const formulaWidth = convertMillimetersToTwip(section.orientation == "landscape" ? 255.8 : 168.8);
|
|
2604
|
+
const number = node.title?.map((rune) => rune.text).join("") || "";
|
|
2605
|
+
const sideWidth = Math.max(
|
|
2606
|
+
convertMillimetersToTwip(5),
|
|
2607
|
+
Math.ceil(estimateNumberWidthPt(number, doc.text.size) * 20 + convertMillimetersToTwip(3))
|
|
2608
|
+
);
|
|
2609
|
+
return [
|
|
2610
|
+
...prevNode?.type != "math" ? [
|
|
2611
|
+
new Paragraph({ indent: { firstLine: 0 }, spacing: { line: 20, after: doc.formula.spacing.before * 20 } })
|
|
2612
|
+
] : [],
|
|
2613
|
+
new Table({
|
|
2614
|
+
width: { type: "dxa", size: formulaWidth },
|
|
2615
|
+
borders: { top: { style: BorderStyle.NONE }, bottom: { style: BorderStyle.NONE }, left: { style: BorderStyle.NONE }, right: { style: BorderStyle.NONE }, insideHorizontal: { style: BorderStyle.NONE }, insideVertical: { style: BorderStyle.NONE } },
|
|
2616
|
+
rows: [new TableRow({
|
|
2617
|
+
children: [
|
|
2618
|
+
new TableCell({ width: { type: "dxa", size: sideWidth }, children: [new Paragraph({ indent: { firstLine: 0 }, spacing: { after: 0 } })] }),
|
|
2619
|
+
new TableCell({
|
|
2620
|
+
children: [
|
|
2621
|
+
new Paragraph({ alignment: AlignmentType.CENTER, indent: { firstLine: 0 }, spacing: { after: 0 }, children: [renderMath(node.latex, true, node.sourceLine)] })
|
|
2622
|
+
]
|
|
2623
|
+
}),
|
|
2624
|
+
new TableCell({ width: { type: "dxa", size: sideWidth }, children: [new Paragraph({
|
|
2625
|
+
indent: { firstLine: 0 },
|
|
2626
|
+
spacing: { line: 240, after: 0 },
|
|
2627
|
+
alignment: AlignmentType.RIGHT,
|
|
2628
|
+
children: renderText(node.title || [], node.sourceLine)
|
|
2629
|
+
})], verticalAlign: VerticalAlignTable.CENTER })
|
|
2630
|
+
]
|
|
2631
|
+
})]
|
|
2632
|
+
}),
|
|
2633
|
+
new Paragraph({ indent: { firstLine: 0 }, spacing: { line: 20, after: doc.formula.spacing.after * 20 } })
|
|
2634
|
+
];
|
|
1036
2635
|
case "externalDoc":
|
|
1037
2636
|
const doc_path = getPath(node.path);
|
|
1038
|
-
if (!
|
|
2637
|
+
if (!fs4.existsSync(doc_path))
|
|
1039
2638
|
throw new UserInputError(`File not exist: ${node.path}`);
|
|
1040
|
-
const ext =
|
|
2639
|
+
const ext = path4.extname(doc_path);
|
|
1041
2640
|
if (ext != ".docx" && ext != ".pdf")
|
|
1042
2641
|
throw new UserInputError(`File not .docx or .pdf: ${node.path}`);
|
|
1043
2642
|
return new Paragraph({
|
|
@@ -1045,6 +2644,39 @@ async function serializeDocx(doc, fout, workdir, assets, checkFilesIsInsidePath)
|
|
|
1045
2644
|
indent: { firstLine: 0 },
|
|
1046
2645
|
alignment: "left"
|
|
1047
2646
|
});
|
|
2647
|
+
case "admonition":
|
|
2648
|
+
const style = `xAdmonition${node.admonitionType == "note" ? "" : node.admonitionType}`;
|
|
2649
|
+
const admonition = doc.admonition[node.admonitionType];
|
|
2650
|
+
const color = "#" + admonition.color;
|
|
2651
|
+
const icon = admonition.icon ? [new ImageRun({
|
|
2652
|
+
type: "svg",
|
|
2653
|
+
data: Buffer.from(admonitionIcons[node.admonitionType](admonition.title_color ? color : "#000000")),
|
|
2654
|
+
transformation: { width: admonition.icon_size, height: admonition.icon_size },
|
|
2655
|
+
fallback: {
|
|
2656
|
+
data: Buffer.from((admonition.title_color ? admonitionIconsFallback : admonitionIconsFallbackBlack)[node.admonitionType], "base64"),
|
|
2657
|
+
type: "png"
|
|
2658
|
+
}
|
|
2659
|
+
}), new TextRun(" ")] : [];
|
|
2660
|
+
const prevIsSameType = prevNode?.type == "admonition" && prevNode.admonitionType == node.admonitionType;
|
|
2661
|
+
const showTitle = node.title.map((r) => r.text).join("") != "";
|
|
2662
|
+
return [
|
|
2663
|
+
...prevIsSameType ? [
|
|
2664
|
+
new Paragraph({ spacing: { line: 20, before: 0, after: 0 } })
|
|
2665
|
+
] : [],
|
|
2666
|
+
...showTitle ? [
|
|
2667
|
+
new Paragraph({
|
|
2668
|
+
style,
|
|
2669
|
+
children: [...icon, ...renderText(admonition.title_color ? node.title.map((r) => ({ ...r, color })) : node.title, node.sourceLine)],
|
|
2670
|
+
keepNext: true,
|
|
2671
|
+
...prevIsSameType ? { spacing: { before: 0 } } : {}
|
|
2672
|
+
})
|
|
2673
|
+
] : [],
|
|
2674
|
+
new Paragraph({
|
|
2675
|
+
style,
|
|
2676
|
+
children: renderText(node.text, node.sourceLine),
|
|
2677
|
+
...prevIsSameType && !showTitle ? { spacing: { before: 0 } } : {}
|
|
2678
|
+
})
|
|
2679
|
+
];
|
|
1048
2680
|
case "sectionBreak":
|
|
1049
2681
|
return [];
|
|
1050
2682
|
default:
|
|
@@ -1052,12 +2684,20 @@ async function serializeDocx(doc, fout, workdir, assets, checkFilesIsInsidePath)
|
|
|
1052
2684
|
throw new Error("switch default");
|
|
1053
2685
|
}
|
|
1054
2686
|
}, renderNodeL2 = function(node, prevChild, prevNode) {
|
|
1055
|
-
|
|
1056
|
-
|
|
1057
|
-
|
|
2687
|
+
try {
|
|
2688
|
+
const r = renderNode2(node, prevChild, prevNode);
|
|
2689
|
+
if (r instanceof Array) return r;
|
|
2690
|
+
return [r];
|
|
2691
|
+
} catch (error) {
|
|
2692
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
2693
|
+
if (/^Line -?\d+:/.test(message)) throw error;
|
|
2694
|
+
throw new Error(`${node.sourceLine ? `Line ${node.sourceLine}: ` : ""}${message}`, { cause: error });
|
|
2695
|
+
}
|
|
1058
2696
|
};
|
|
1059
|
-
var renderNode = renderNode2, renderNodeL = renderNodeL2;
|
|
2697
|
+
var renderHeaderFooter = renderHeaderFooter2, renderHeaderFooterTable = renderHeaderFooterTable2, renderNode = renderNode2, renderNodeL = renderNodeL2;
|
|
1060
2698
|
const children = [];
|
|
2699
|
+
const header = renderHeaderFooter2(section.header, false);
|
|
2700
|
+
const footer = renderHeaderFooter2(section.footer, true);
|
|
1061
2701
|
sections.push({
|
|
1062
2702
|
children,
|
|
1063
2703
|
properties: {
|
|
@@ -1085,20 +2725,8 @@ async function serializeDocx(doc, fout, workdir, assets, checkFilesIsInsidePath)
|
|
|
1085
2725
|
}
|
|
1086
2726
|
}
|
|
1087
2727
|
},
|
|
1088
|
-
|
|
1089
|
-
|
|
1090
|
-
children: [
|
|
1091
|
-
new Paragraph({
|
|
1092
|
-
alignment: AlignmentType.CENTER,
|
|
1093
|
-
children: [new TextRun({
|
|
1094
|
-
children: section.displayPageNum ? [PageNumber.CURRENT] : []
|
|
1095
|
-
})],
|
|
1096
|
-
indent: { firstLine: 0 },
|
|
1097
|
-
spacing: { line: 240 }
|
|
1098
|
-
})
|
|
1099
|
-
]
|
|
1100
|
-
})
|
|
1101
|
-
}
|
|
2728
|
+
...header ? { headers: { default: header } } : {},
|
|
2729
|
+
...footer ? { footers: { default: footer } } : {}
|
|
1102
2730
|
});
|
|
1103
2731
|
for (let i = 0; i < section.nodes.length; i++)
|
|
1104
2732
|
children.push(...renderNodeL2(section.nodes[i], children.at(-1), section.nodes[i - 1]));
|
|
@@ -1133,8 +2761,8 @@ async function serializeDocx(doc, fout, workdir, assets, checkFilesIsInsidePath)
|
|
|
1133
2761
|
})
|
|
1134
2762
|
}
|
|
1135
2763
|
]);
|
|
1136
|
-
|
|
1137
|
-
function renderText(text, size) {
|
|
2764
|
+
fs4.writeFileSync(fout, buffer);
|
|
2765
|
+
function renderText(text, sourceLine, size) {
|
|
1138
2766
|
function renderRune(rune, link = false) {
|
|
1139
2767
|
if (rune.anchor) return new Bookmark({
|
|
1140
2768
|
id: rune.anchor,
|
|
@@ -1144,8 +2772,12 @@ async function serializeDocx(doc, fout, workdir, assets, checkFilesIsInsidePath)
|
|
|
1144
2772
|
const children2 = [renderRune({ ...rune, link: void 0 }, true)];
|
|
1145
2773
|
return rune.link.startsWith("#") ? new InternalHyperlink({ children: children2, anchor: rune.link.slice(1) }) : new ExternalHyperlink({ children: children2, link: rune.link });
|
|
1146
2774
|
}
|
|
2775
|
+
if (rune.type == "math") return renderMath(rune.text, false, sourceLine);
|
|
1147
2776
|
let children = null;
|
|
1148
|
-
if (rune.type == "val" && rune.text == "
|
|
2777
|
+
if (rune.type == "val" && rune.text == "page") {
|
|
2778
|
+
rune.type = "text";
|
|
2779
|
+
children = [PageNumber.CURRENT];
|
|
2780
|
+
} else if (rune.type == "val" && rune.text == "pages") {
|
|
1149
2781
|
rune.type = "text";
|
|
1150
2782
|
children = doc.totalPagesOffset == 0 ? [PageNumber.TOTAL_PAGES] : [
|
|
1151
2783
|
// Complex Formula Field: { = { NUMPAGES } + 1 }
|
|
@@ -1229,7 +2861,36 @@ async function serializeDocx(doc, fout, workdir, assets, checkFilesIsInsidePath)
|
|
|
1229
2861
|
}
|
|
1230
2862
|
}
|
|
1231
2863
|
}
|
|
2864
|
+
function renderMath(latex, para, sourceLine) {
|
|
2865
|
+
try {
|
|
2866
|
+
return latexToOmml(latex, para);
|
|
2867
|
+
} catch (error) {
|
|
2868
|
+
logwarn(`${sourceLine ? `Line ${doc.mapSourceLine(sourceLine + 1)}: ` : ""}Formula was not converted: ${error instanceof Error ? error.message : String(error)}`);
|
|
2869
|
+
return new TextRun(latex);
|
|
2870
|
+
}
|
|
2871
|
+
}
|
|
1232
2872
|
}
|
|
2873
|
+
var admonitionIcons = {
|
|
2874
|
+
note: (c) => `<svg version="1.2" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="24" height="24"><style>.a{fill:${c}}</style><path class="a" d="m13 12q0-0.4-0.3-0.7-0.3-0.3-0.7-0.3-0.4 0-0.7 0.3-0.3 0.3-0.3 0.7v4q0 0.4 0.3 0.7 0.3 0.3 0.7 0.3 0.4 0 0.7-0.3 0.3-0.3 0.3-0.7zm-1-2.5c0.3 0 0.6-0.1 0.9-0.4 0.2-0.2 0.3-0.5 0.3-0.8q0-0.6-0.3-0.9c-0.3-0.3-0.6-0.4-0.9-0.4-0.3 0-0.6 0.1-0.9 0.4q-0.3 0.3-0.3 0.9c0 0.3 0.1 0.6 0.3 0.8 0.3 0.3 0.6 0.4 0.9 0.4z"/><path fill-rule="evenodd" class="a" d="m22 12c0 5.5-4.5 10-10 10-5.5 0-10-4.5-10-10 0-5.5 4.5-10 10-10 5.5 0 10 4.5 10 10zm-15.7 5.7c1.5 1.5 3.6 2.3 5.7 2.3 2.1 0 4.2-0.8 5.7-2.3 1.5-1.5 2.3-3.6 2.3-5.7 0-2.1-0.8-4.2-2.3-5.7-1.5-1.5-3.6-2.3-5.7-2.3-2.1 0-4.2 0.8-5.7 2.3-1.5 1.5-2.3 3.6-2.3 5.7 0 2.1 0.8 4.2 2.3 5.7z"/></svg>`,
|
|
2875
|
+
info: (c) => `<svg version="1.2" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="24" height="24"><style>.a{fill:${c}}</style><path class="a" d="m13 12q0-0.4-0.3-0.7-0.3-0.3-0.7-0.3-0.4 0-0.7 0.3-0.3 0.3-0.3 0.7v4q0 0.4 0.3 0.7 0.3 0.3 0.7 0.3 0.4 0 0.7-0.3 0.3-0.3 0.3-0.7zm-1-2.5c0.3 0 0.6-0.1 0.9-0.4 0.2-0.2 0.3-0.5 0.3-0.8q0-0.6-0.3-0.9c-0.3-0.3-0.6-0.4-0.9-0.4-0.3 0-0.6 0.1-0.9 0.4-0.2 0.2-0.3 0.5-0.3 0.9 0 0.3 0.1 0.6 0.3 0.8 0.3 0.3 0.6 0.4 0.9 0.4z"/><path fill-rule="evenodd" class="a" d="m22 12c0 5.5-4.5 10-10 10-5.5 0-10-4.5-10-10 0-5.5 4.5-10 10-10 5.5 0 10 4.5 10 10zm-15.7 5.7c1.5 1.5 3.6 2.3 5.7 2.3 2.1 0 4.2-0.8 5.7-2.3 1.5-1.5 2.3-3.6 2.3-5.7 0-2.1-0.8-4.2-2.3-5.7-1.5-1.5-3.6-2.3-5.7-2.3-2.1 0-4.2 0.8-5.7 2.3-1.5 1.5-2.3 3.6-2.3 5.7 0 2.1 0.8 4.2 2.3 5.7z"/></svg>`,
|
|
2876
|
+
tip: (c) => `<svg version="1.2" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="24" height="24"><style>.a{fill:${c}}</style><path class="a" d="m8.1 20c0.2 0.9 0.7 1.6 1.5 2.2 0.7 0.5 1.5 0.8 2.4 0.8 0.9 0 1.7-0.3 2.4-0.8 0.8-0.6 1.3-1.3 1.5-2.2q0.2 0 0.4-0.1 0.1-0.1 0.3-0.2 0.1-0.1 0.2-0.3 0.1-0.2 0.1-0.4v-2.5q0.8-0.6 1.5-1.3 0.7-0.8 1.1-1.7 0.5-0.9 0.8-1.9 0.2-1 0.2-2.1c0-4.7-3.8-8.5-8.5-8.5-4.7 0-8.5 3.8-8.5 8.5q0 1.1 0.2 2.1 0.3 1 0.8 1.9 0.4 0.9 1.1 1.7 0.7 0.7 1.5 1.3v2.5q0 0.2 0.1 0.4 0.1 0.2 0.2 0.3 0.2 0.1 0.3 0.2 0.2 0.1 0.4 0.1zm6.8-3v1h-5.8v-1zm-1.1 3q-0.3 0.5-0.7 0.7-0.5 0.3-1 0.3-0.6 0-1-0.3-0.5-0.2-0.8-0.7zm-1.7-17c3.6 0 6.5 2.9 6.5 6.5q0 0.8-0.2 1.6-0.2 0.8-0.6 1.5-0.4 0.8-0.9 1.4-0.5 0.6-1.2 1h-7.2q-0.6-0.4-1.2-1-0.5-0.6-0.9-1.4-0.4-0.7-0.6-1.5-0.2-0.8-0.2-1.6c0-3.6 2.9-6.5 6.5-6.5z"/></svg>`,
|
|
2877
|
+
warning: (c) => `<svg version="1.2" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="24" height="24"><style>.a{fill:${c}}</style><path class="a" d="m9.4 4.3c1.2-1.9 4-1.9 5.2 0l7.4 12.1c1.2 2-0.2 4.6-2.6 4.6h-14.8c-2.4 0-3.8-2.6-2.6-4.6zm3.4 1.1c-0.4-0.7-1.3-0.7-1.7 0l-7.4 12.1c-0.4 0.7 0.1 1.6 0.8 1.6h14.9c0.8 0 1.3-0.9 0.9-1.6zm-0.9 3.7c0.6 0 1 0.5 1 1v3c0 0.6-0.4 1-1 1-0.6 0-1-0.4-1-1v-3c0-0.5 0.4-1 1-1zm-1.1 7.5c0-0.6 0.5-1.1 1.1-1.1 0.6 0 1.2 0.5 1.2 1.1 0 0.7-0.6 1.2-1.2 1.2-0.6 0-1.1-0.5-1.1-1.2z"/></svg>`,
|
|
2878
|
+
danger: (c) => `<svg version="1.2" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="24" height="24"><style>.a{fill:${c}}.b{fill:none;stroke:${c};stroke-linecap:round;stroke-linejoin:round;stroke-width:2}</style><path class="a" d="m12 14.5c-0.3 0-0.6 0.1-0.9 0.4-0.2 0.2-0.3 0.5-0.3 0.8q0 0.6 0.3 0.9c0.3 0.3 0.6 0.4 0.9 0.4 0.3 0 0.6-0.1 0.9-0.4q0.3-0.3 0.3-0.9c0-0.3-0.1-0.6-0.3-0.8-0.3-0.3-0.6-0.4-0.9-0.4z"/><path fill-rule="evenodd" class="b" d="m12 8v4"/><path class="b" d="m15.3 2q0.2 0 0.4 0 0.2 0.1 0.4 0.2 0.2 0 0.3 0.1 0.2 0.1 0.3 0.3l4.7 4.7q0.2 0.1 0.3 0.3 0.1 0.1 0.1 0.3 0.1 0.2 0.2 0.4 0 0.2 0 0.4v6.6q0 0.2 0 0.4-0.1 0.2-0.2 0.4 0 0.2-0.1 0.3-0.1 0.2-0.3 0.3l-4.7 4.7q-0.1 0.2-0.3 0.3-0.1 0.1-0.3 0.1-0.2 0.1-0.4 0.2-0.2 0-0.4 0h-6.6q-0.2 0-0.4 0-0.2-0.1-0.4-0.2-0.2 0-0.3-0.1-0.2-0.1-0.3-0.3l-4.7-4.7q-0.2-0.1-0.3-0.3-0.1-0.1-0.1-0.3-0.1-0.2-0.2-0.4 0-0.2 0-0.4v-6.6q0-0.2 0-0.4 0.1-0.2 0.2-0.4 0-0.2 0.1-0.3 0.1-0.2 0.3-0.3l4.7-4.7q0.1-0.2 0.3-0.3 0.1-0.1 0.3-0.1 0.2-0.1 0.4-0.2 0.2 0 0.4 0z"/></svg>`
|
|
2879
|
+
};
|
|
2880
|
+
var admonitionIconsFallback = {
|
|
2881
|
+
note: `iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAMAAADXqc3KAAAAilBMVEUAAABmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmbNZZ8EAAAALnRSTlMA//BXB/7YePSoRaAMKt3USM91y08CZiK8tUzjLxM82zjGblsPPSRhBkJzadIoQt+BPAAAAMBJREFUeJydUVcOwjAUi9Mm3YsOoLRlb7j/9RDJK22EEAL/OLKd5A3G/oVtBbnLs8CyTd2PQYj9se4IzKuubffVDMIZ6eBpfy44Xk4q3GhIRa6gVLiApThJ1M9bSF2BhUTxAZg+uZQ6yDxc9dVupfkCT7HUwQENpGIOauqWad6BkxFqAaBqyJCITKOhpzxsTGNCn/fl9ka5pHJtapDla0XHvkFWC343RnL+NkQ99vrEmF2YY/+8KLXajLv522p/wANc8QfHdtgFgwAAAABJRU5ErkJggg==`,
|
|
2882
|
+
info: `iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAMAAADXqc3KAAAAilBMVEUAAAAZdtIZdtIZdtIZdtIZdtIZdtIZdtIZdtIZdtIZdtIZdtIZdtIZdtIZdtIZdtIZdtIZdtIZdtIZdtIZdtIZdtIZdtIZdtIZdtIZdtIZdtIZdtIZdtIZdtIZdtIZdtIZdtIZdtIZdtIZdtIZdtIZdtIZdtIZdtIZdtIZdtIZdtIZdtIZdtIZdtL9UeA9AAAALnRSTlMA//BXB/7YePSoRaAMKt3USM91y08CZiK8tUzjLxM82zjGblsPPSRhBkJzadIoQt+BPAAAAMBJREFUeJydUVcOwjAUi9Mm3YsOoLRlb7j/9RDJK22EEAL/OLKd5A3G/oVtBbnLs8CyTd2PQYj9se4IzKuubffVDMIZ6eBpfy44Xk4q3GhIRa6gVLiApThJ1M9bSF2BhUTxAZg+uZQ6yDxc9dVupfkCT7HUwQENpGIOauqWad6BkxFqAaBqyJCITKOhpzxsTGNCn/fl9ka5pHJtapDla0XHvkFWC343RnL+NkQ99vrEmF2YY/+8KLXajLv522p/wANc8QfHdtgFgwAAAABJRU5ErkJggg==`,
|
|
2883
|
+
tip: `iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAMAAADXqc3KAAAAllBMVEUAAAAufTIufTIufTIufTIufTIufTIufTIufTIufTIufTIufTIufTIufTIufTIufTIufTIufTIufTIufTIufTIufTIufTIufTIufTIufTIufTIufTIufTIufTIufTIufTIufTIufTIufTIufTIufTIufTIufTIufTIufTIufTIufTIufTIufTIufTIufTIufTIufTIufTLBWjFSAAAAMnRSTlMA//AF9MXq5ssCgtub1XmmJgghfRkRC+exGm0tRBX+i2c1Tt8pMHC0RwxdtsOoo64eS7UJBqAAAADPSURBVHichVLZEoIwENtU7luUUwUB79v//zkHpKWiM+Ypk+x2szsl+ofAUhlTrWAkR7cCHQonlvWwASvrR7Sc6XBXgx67sOc9vcCYCGMmdKKjijXnE4Z6aF+g4fQJ3ZQGGtj2bIernKTESYxwZCOHJzpS2bCQ9+wANiQkUrHkqbQhIVEFTUTxoEWcm4YYQTS1YWQ99eFK71bK+4QtFL5Fh1A4SijrRAm6a2XQP3VKeMfYOHPjY9cWKfarqYP7WKcFa+vZ5sugja8l/vgv/MALHToJTMdMgakAAAAASUVORK5CYII=`,
|
|
2884
|
+
warning: `iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAMAAADXqc3KAAAAllBMVEUAAACyagCyagCyagCyagCyagCyagCyagCyagCyagCyagCyagCyagCyagCyagCyagCyagCyagCyagCyagCyagCyagCyagCyagCyagCyagCyagCyagCyagCyagCyagCyagCyagCyagCyagCyagCyagCyagCyagCyagCyagCyagCyagCyagCyagCyagCyagCyagCyagCyagD2Sl3HAAAAMnRSTlMA/w3r5vwbWbsElS1drcsiElT5ArX2wo045GaR779hCUx02KB7BdPiPyiCaxZSMsa5W/G4KzAAAADCSURBVHictZDJEoJADES7GZBFdlABFdz39f9/zmIoCge9WeaS5HVNkmngD6F5pKd9EeYkaX3yJ+NDRJofgwQ3gEO9P2zKlQ1oOicqHxrM6hzROCrCnWvYuQekHL/zkrMQA+rAYES346FggUbAkqLb79BHK9g7Tlt+ozEEUIlH3WVk3QFIfPXG5j2AgvtKFudr81edpcwzLiQIGG9lETX73da55NQeOpeWmUx79tiWFC6i9luNPJSzx7GKYyvom/9TvACzaQfmlOkXxAAAAABJRU5ErkJggg==`,
|
|
2885
|
+
danger: `iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAMAAADXqc3KAAAAdVBMVEUAAADTLy+/ICDSLi7SLi7GHBzPKSnSLy/SLi7MGhrTLy/TLy/SLy/RLi7SLy/SLi7TLi7KLCzRLi7SLi7KKyvTLi7TLy/EJyfTLi6/KyvTLy/TLy/SLi7TLy/MKSnMKyvTLy/SLy/TLi7OJyfSLi7TLy/TLy/z9/9QAAAAJ3RSTlMA/wi9wgklCyEK7L/UFuXA4B0c6Rj+Vg2zDMXc2LkZHre0lhqmySRpaHOvAAAAtElEQVR4nKVRRxLDIBBbGdx77yX1/0/M2EBMHE8u0QWtBCwriH6juECiKXQ9DJQOBOGuzz1YK2jLMNyU7kRghioMhmQW1Mo1ncjgiKyVTB5iTSdyObz1BdeSu0LxfbHaXXknIqCWWwFJ6o29yyPDH4Yvm38ZO/BpVNXBkOM9AUcwczNGyAHtcbEF4WiIKPUQm8dI0vMQ8y3Ek9hlK6I0wSJvMxn6bD8+DedfS5Q9lN5p+0/xArzsBn7bxA9sAAAAAElFTkSuQmCC`
|
|
2886
|
+
};
|
|
2887
|
+
var admonitionIconsFallbackBlack = {
|
|
2888
|
+
note: `iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAMAAADXqc3KAAAAilBMVEUAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAATAggvAAAALnRSTlMA//AHeFjY9N2gDCpEqM3TSToi47XVR9D9Zky8L3RWPRNPxm4CUFsPJGFpKAbKjdKbTAAAAMRJREFUeJydUdkSgjAM7BbKfYjKKQp4n///e860AdpxfNC8bGY3bZINY/+GJfzE5aUvLJOPPFB4kc7bwLru+r6rN4Ct83wx5ic+K0XlhnNV6FZUFaywk5insvMZjppAIJd4BZYycSAkxrirp1mmcIuYClThFC0ciRy0VJoovICTECgCoGlIcBCaQktfxTiYwthcIDWEo0fjWrQge+0l3MYFWQP+0C3BZLBu4qCZqGxvnoxZg2k7Y8WXQ8nTltxNPk77Q7wBVQsHvaZMK8YAAAAASUVORK5CYII=`,
|
|
2889
|
+
info: `iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAMAAADXqc3KAAAAilBMVEUAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAATAggvAAAALnRSTlMA//AHeFjY9N2gDCpEqM3TSToi47XVR9D9Zky8L3RWPRNPxm4CUFsPJGFpKAbKjdKbTAAAAMRJREFUeJydUdkSgjAM7BbKfYjKKQp4n///e860AdpxfNC8bGY3bZINY/+GJfzE5aUvLJOPPFB4kc7bwLru+r6rN4Ct83wx5ic+K0XlhnNV6FZUFaywk5insvMZjppAIJd4BZYycSAkxrirp1mmcIuYClThFC0ciRy0VJoovICTECgCoGlIcBCaQktfxTiYwthcIDWEo0fjWrQge+0l3MYFWQP+0C3BZLBu4qCZqGxvnoxZg2k7Y8WXQ8nTltxNPk77Q7wBVQsHvaZMK8YAAAAASUVORK5CYII=`,
|
|
2890
|
+
tip: `iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAMAAADXqc3KAAAAh1BMVEUAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD3YishAAAALXRSTlMA/+gFxBrmCvAH9EXbmyd9sC+kedXxTckiEW3ryx8VZ4s1gt8rqHC1XQyDshgZvchdAAAAyUlEQVR4nIWS5w7CMAyEfR20dO/SSSdlvf/zIaokDQWJ+3XyZ8dDIfonb9BMUxu8XbiIwOQacrwZkXZZU9j+AUm7xY0EzpNZDbkqgA9H5/6W48y9aiLbyieM3GY4yA1rhMxdMMugw1W0cGVQIhYVgQzuKJmzYW4TEmmw+VTVNiFRD0WkxVAKAXLRgujooGYbHmck0ru9xW8IWHyLVSdBrJMcJ1KwvqV/HmEFTHuwcPDYAQqwtIb7eYNVYfrOT6cvQF5UKdH+L/zQCzA0CJnLbfrbAAAAAElFTkSuQmCC`,
|
|
2891
|
+
warning: `iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAMAAADXqc3KAAAAllBMVEUAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA6C80qAAAAMnRSTlMA/w3mA/xUu1kb610AyyKtEi35lbXvwvZhv2aPCTnYixagayh7TDKSBeLTdII/xrk2W23LF/kAAADFSURBVHictZDJcsIwEET7WbINosDGLGELS8Kanf//OUpyucAmN4q+jOZ1aUZq6QmKMsiif4wpQHrPzxTfCbTuBjm20gDTHDZnaaXIMK/zfsyrrwnxe834YSNrMmnI+JZPGOVqY6T2C50rzx0zlYY+cdf9A7qqDPvBoeJ74r6khfv13R/4zqvL6nZheV/SjNMiHN6S8q+GSagjdgH0KI7hkJT7O1Vy9qt66DRE1mLYiMemwVg7n3ddJg+zx0UdF2mvGf5DugCExweZt6hkrgAAAABJRU5ErkJggg==`,
|
|
2892
|
+
danger: `iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAMAAADXqc3KAAAAYFBMVEUAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD6T+iNAAAAIHRSTlMA/wi9Cr8lwgwY7Bwhw7cW29Tl4OkdVh6z/bmWpsXXyQwvE4cAAACqSURBVHicpVFHEsIwEJPiFKeRXgjt/79ksNfEBA8XdNJKnpFXC/xGvlKw5r7edE4nu2bXl5ZKW6oV29LpVU8VuSFSHBZL09HTgahmn5rcgomnA3HG4vWDC7PYKkkizu18BUBKLkgh2rD3eGT8w0gk/MvYwU9jng+GrJeSlSxvjM0tqLeTJXHGFcAUqmQKlziaEgO1SxQwDTyJE/mHAvJ7+LRA+XB67b0P4gn4WAUqr4Oj0wAAAABJRU5ErkJggg==`
|
|
2893
|
+
};
|
|
1233
2894
|
function addListItemLevel(levels, level, startIndex, itemCount, ordered, alternativeStyle, mark) {
|
|
1234
2895
|
if (levels.find((v) => v.level == level)) return;
|
|
1235
2896
|
levels.sort((a, b) => a.level - b.level);
|
|
@@ -1268,7 +2929,7 @@ function addListItemLevel(levels, level, startIndex, itemCount, ordered, alterna
|
|
|
1268
2929
|
}
|
|
1269
2930
|
});
|
|
1270
2931
|
}
|
|
1271
|
-
var RawXml = class extends
|
|
2932
|
+
var RawXml = class extends XmlComponent2 {
|
|
1272
2933
|
constructor(tag, attributes = {}, text = null) {
|
|
1273
2934
|
super(tag);
|
|
1274
2935
|
if (Object.keys(attributes).length > 0)
|
|
@@ -1278,60 +2939,158 @@ var RawXml = class extends XmlComponent {
|
|
|
1278
2939
|
}
|
|
1279
2940
|
};
|
|
1280
2941
|
function renderCodeHighlighting(code, lang) {
|
|
1281
|
-
const
|
|
2942
|
+
const language = lang.trim().toLowerCase();
|
|
2943
|
+
const grammar = Prism.languages[language];
|
|
1282
2944
|
if (!grammar) return null;
|
|
1283
2945
|
const themeColors = {
|
|
1284
2946
|
// shared
|
|
1285
2947
|
"plain": "000000",
|
|
1286
2948
|
"comment": "008000",
|
|
2949
|
+
"hashbang": "008000",
|
|
2950
|
+
"shebang": "008000",
|
|
1287
2951
|
"punctuation": "000000",
|
|
1288
2952
|
"operator": "000000",
|
|
2953
|
+
"script": "000000",
|
|
2954
|
+
"script-punctuation": "000000",
|
|
2955
|
+
"expression": "000000",
|
|
2956
|
+
"interpolation": "000000",
|
|
1289
2957
|
// keywords & control flow
|
|
1290
2958
|
"keyword": "0000ff",
|
|
2959
|
+
"control-flow": "af00db",
|
|
1291
2960
|
"boolean": "0000ff",
|
|
1292
|
-
"
|
|
2961
|
+
"null": "0000ff",
|
|
2962
|
+
"builtin": "267f99",
|
|
1293
2963
|
"important": "0000ff",
|
|
2964
|
+
// "instruction": "0000ff",
|
|
2965
|
+
// "directive": "0000ff",
|
|
2966
|
+
"directive-hash": "0000ff",
|
|
2967
|
+
"macro": "0000ff",
|
|
2968
|
+
"macro-name": "0000ff",
|
|
2969
|
+
"rule": "0000ff",
|
|
1294
2970
|
// literals
|
|
1295
|
-
"string":
|
|
1296
|
-
"
|
|
1297
|
-
"
|
|
1298
|
-
"
|
|
2971
|
+
"string": "a31515",
|
|
2972
|
+
"template-string": "a31515",
|
|
2973
|
+
"template-punctuation": "a31515",
|
|
2974
|
+
"string-literal": "a31515",
|
|
1299
2975
|
"char": "a31515",
|
|
2976
|
+
"number": "098658",
|
|
2977
|
+
"datetime": "098658",
|
|
2978
|
+
"constant": "0070c1",
|
|
2979
|
+
"symbol": "0070c1",
|
|
2980
|
+
"scalar": "0000ff",
|
|
2981
|
+
"conversion-option": "0000ff",
|
|
1300
2982
|
// identifiers
|
|
1301
|
-
"class-name":
|
|
1302
|
-
"
|
|
1303
|
-
"
|
|
1304
|
-
"
|
|
1305
|
-
"
|
|
2983
|
+
"class-name": "267f99",
|
|
2984
|
+
"class-name-definition": "267f99",
|
|
2985
|
+
"type-definition": "267f99",
|
|
2986
|
+
"return-type": "267f99",
|
|
2987
|
+
"attribute": "267f99",
|
|
2988
|
+
"attribute-class-name": "267f99",
|
|
2989
|
+
"annotation": "267f99",
|
|
2990
|
+
"decorator": "267f99",
|
|
2991
|
+
"namespace": "267f99",
|
|
2992
|
+
"double-colon": "267f99",
|
|
2993
|
+
"function": "795e26",
|
|
2994
|
+
"function-definition": "795e26",
|
|
2995
|
+
"method-definition": "795e26",
|
|
2996
|
+
"variable": "001080",
|
|
2997
|
+
"parameter": "001080",
|
|
2998
|
+
"environment": "001080",
|
|
2999
|
+
"assign-left": "001080",
|
|
3000
|
+
"property": language === "css" ? "e50000" : language === "json" ? "0451a5" : "001080",
|
|
3001
|
+
"key": "0451a5",
|
|
3002
|
+
"package": "000000",
|
|
3003
|
+
"lifetime-annotation": "0000ff",
|
|
1306
3004
|
// markup / HTML / XML
|
|
1307
|
-
"tag":
|
|
3005
|
+
"tag": "800000",
|
|
1308
3006
|
"selector": "800000",
|
|
1309
3007
|
"attr-name": "e50000",
|
|
1310
|
-
"attr-value": "
|
|
3008
|
+
"attr-value": "0000ff",
|
|
1311
3009
|
"entity": "800000",
|
|
1312
3010
|
"doctype": "808080",
|
|
3011
|
+
"doctype-tag": "808080",
|
|
3012
|
+
"name": "808080",
|
|
1313
3013
|
"cdata": "808080",
|
|
1314
3014
|
"prolog": "808080",
|
|
3015
|
+
"delimiter": language === "php" ? "800000" : "a31515",
|
|
1315
3016
|
// CSS specific
|
|
1316
3017
|
"atrule": "0000ff",
|
|
1317
3018
|
"url": "0451a5",
|
|
1318
3019
|
// regex
|
|
1319
|
-
"regex": "811f3f"
|
|
3020
|
+
"regex": "811f3f",
|
|
3021
|
+
"regex-delimiter": "811f3f",
|
|
3022
|
+
"regex-source": "811f3f",
|
|
3023
|
+
"regex-flags": "0000ff",
|
|
3024
|
+
"interpolation-punctuation": "0000ff",
|
|
3025
|
+
// Markdown
|
|
3026
|
+
"title": "800000",
|
|
3027
|
+
"blockquote": "0451a5",
|
|
3028
|
+
"list": "0451a5",
|
|
3029
|
+
"bold": "000080",
|
|
3030
|
+
"italic": "800080",
|
|
3031
|
+
"code": "800000",
|
|
3032
|
+
"code-snippet": "800000",
|
|
3033
|
+
"code-block": "800000",
|
|
3034
|
+
"code-language": "0000ff",
|
|
3035
|
+
"inserted": "098658",
|
|
3036
|
+
"deleted": "a31515",
|
|
3037
|
+
"strike": "000000",
|
|
3038
|
+
"hr": "800000"
|
|
1320
3039
|
};
|
|
3040
|
+
const controlFlowKeywords = /* @__PURE__ */ new Set([
|
|
3041
|
+
"if",
|
|
3042
|
+
"else",
|
|
3043
|
+
"elif",
|
|
3044
|
+
"elseif",
|
|
3045
|
+
"unless",
|
|
3046
|
+
"then",
|
|
3047
|
+
"switch",
|
|
3048
|
+
"case",
|
|
3049
|
+
"default",
|
|
3050
|
+
"when",
|
|
3051
|
+
"match",
|
|
3052
|
+
"for",
|
|
3053
|
+
"foreach",
|
|
3054
|
+
"while",
|
|
3055
|
+
"do",
|
|
3056
|
+
"until",
|
|
3057
|
+
"loop",
|
|
3058
|
+
"break",
|
|
3059
|
+
"continue",
|
|
3060
|
+
"return",
|
|
3061
|
+
"goto",
|
|
3062
|
+
"throw",
|
|
3063
|
+
"throws",
|
|
3064
|
+
"try",
|
|
3065
|
+
"catch",
|
|
3066
|
+
"except",
|
|
3067
|
+
"finally",
|
|
3068
|
+
"rescue",
|
|
3069
|
+
"ensure",
|
|
3070
|
+
"await",
|
|
3071
|
+
"yield",
|
|
3072
|
+
"new",
|
|
3073
|
+
"delete",
|
|
3074
|
+
"in",
|
|
3075
|
+
"of",
|
|
3076
|
+
"instanceof"
|
|
3077
|
+
]);
|
|
1321
3078
|
const tokens = Prism.tokenize(code, grammar);
|
|
1322
3079
|
return tokensToParagraphs(tokens);
|
|
1323
3080
|
function tokensToParagraphs(tokens2) {
|
|
1324
3081
|
const paragraphs = [];
|
|
1325
3082
|
let currentRuns = [];
|
|
1326
|
-
|
|
3083
|
+
const plainStyle = { color: themeColors.plain, bold: false, italics: false, strike: false };
|
|
3084
|
+
function addText(text, style) {
|
|
1327
3085
|
const lines = text.split("\n");
|
|
1328
3086
|
lines.forEach((line, index) => {
|
|
1329
3087
|
currentRuns.push(
|
|
1330
3088
|
new TextRun({
|
|
1331
3089
|
text: line,
|
|
1332
|
-
color:
|
|
1333
|
-
bold:
|
|
1334
|
-
italics:
|
|
3090
|
+
color: style.color,
|
|
3091
|
+
bold: style.bold,
|
|
3092
|
+
italics: style.italics,
|
|
3093
|
+
strike: style.strike
|
|
1335
3094
|
})
|
|
1336
3095
|
);
|
|
1337
3096
|
if (index < lines.length - 1) {
|
|
@@ -1340,15 +3099,28 @@ function renderCodeHighlighting(code, lang) {
|
|
|
1340
3099
|
}
|
|
1341
3100
|
});
|
|
1342
3101
|
}
|
|
1343
|
-
function process2(token,
|
|
3102
|
+
function process2(token, parentStyle = plainStyle) {
|
|
1344
3103
|
if (typeof token === "string")
|
|
1345
|
-
addText(token,
|
|
1346
|
-
else
|
|
1347
|
-
token.
|
|
1348
|
-
|
|
1349
|
-
|
|
1350
|
-
|
|
1351
|
-
|
|
3104
|
+
addText(token, parentStyle);
|
|
3105
|
+
else {
|
|
3106
|
+
const aliases = typeof token.alias == "string" ? [token.alias] : token.alias || [];
|
|
3107
|
+
const types = [token.type, ...aliases];
|
|
3108
|
+
const colorType = types.find((type) => themeColors[type]);
|
|
3109
|
+
const isControlFlow = token.type == "keyword" && typeof token.content == "string" && controlFlowKeywords.has(token.content);
|
|
3110
|
+
const inheritPunctuation = token.type == "punctuation" && parentStyle.color != themeColors.plain;
|
|
3111
|
+
const style = {
|
|
3112
|
+
color: isControlFlow ? themeColors["control-flow"] : inheritPunctuation ? parentStyle.color : colorType ? themeColors[colorType] : parentStyle.color,
|
|
3113
|
+
bold: parentStyle.bold || types.includes("bold") || types.includes("important") || types.includes("title"),
|
|
3114
|
+
italics: parentStyle.italics || types.includes("italic"),
|
|
3115
|
+
strike: parentStyle.strike || types.includes("strike")
|
|
3116
|
+
};
|
|
3117
|
+
if (Array.isArray(token.content))
|
|
3118
|
+
token.content.forEach((t) => process2(t, style));
|
|
3119
|
+
else if (typeof token.content === "string")
|
|
3120
|
+
addText(token.content, style);
|
|
3121
|
+
else
|
|
3122
|
+
process2(token.content, style);
|
|
3123
|
+
}
|
|
1352
3124
|
}
|
|
1353
3125
|
tokens2.forEach((t) => process2(t));
|
|
1354
3126
|
if (currentRuns.length > 0) {
|
|
@@ -1357,6 +3129,37 @@ function renderCodeHighlighting(code, lang) {
|
|
|
1357
3129
|
return paragraphs;
|
|
1358
3130
|
}
|
|
1359
3131
|
}
|
|
3132
|
+
function estimateNumberWidthPt(text, fontSizePt) {
|
|
3133
|
+
const charWidths = {
|
|
3134
|
+
".": 0.25,
|
|
3135
|
+
",": 0.25,
|
|
3136
|
+
"-": 0.333,
|
|
3137
|
+
"(": 0.333,
|
|
3138
|
+
")": 0.333,
|
|
3139
|
+
" ": 0.25,
|
|
3140
|
+
"a": 0.444,
|
|
3141
|
+
"b": 0.5,
|
|
3142
|
+
"c": 0.444,
|
|
3143
|
+
"d": 0.5,
|
|
3144
|
+
"e": 0.444,
|
|
3145
|
+
"A": 0.722,
|
|
3146
|
+
"B": 0.667,
|
|
3147
|
+
"C": 0.667,
|
|
3148
|
+
"D": 0.722,
|
|
3149
|
+
"E": 0.611,
|
|
3150
|
+
"\u0430": 0.5,
|
|
3151
|
+
"\u0431": 0.5,
|
|
3152
|
+
"\u0432": 0.5,
|
|
3153
|
+
"\u0433": 0.5,
|
|
3154
|
+
"\u0434": 0.5,
|
|
3155
|
+
"\u0410": 0.722,
|
|
3156
|
+
"\u0411": 0.667,
|
|
3157
|
+
"\u0412": 0.667,
|
|
3158
|
+
"\u0413": 0.611,
|
|
3159
|
+
"\u0414": 0.722
|
|
3160
|
+
};
|
|
3161
|
+
return [...text].reduce((width, char) => width + (charWidths[char] ?? 0.5) * fontSizePt, 0);
|
|
3162
|
+
}
|
|
1360
3163
|
function genXml_app({ totalTime }) {
|
|
1361
3164
|
return `<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
|
|
1362
3165
|
<Properties xmlns="http://schemas.openxmlformats.org/officeDocument/2006/extended-properties" xmlns:vt="http://schemas.openxmlformats.org/officeDocument/2006/docPropsVTypes">
|
|
@@ -1364,20 +3167,29 @@ function genXml_app({ totalTime }) {
|
|
|
1364
3167
|
</Properties>
|
|
1365
3168
|
`;
|
|
1366
3169
|
}
|
|
3170
|
+
function escapeXml(value) {
|
|
3171
|
+
return value.replaceAll(/[<>&"']/g, (ch) => ({
|
|
3172
|
+
"<": "<",
|
|
3173
|
+
">": ">",
|
|
3174
|
+
"&": "&",
|
|
3175
|
+
'"': """,
|
|
3176
|
+
"'": "'"
|
|
3177
|
+
})[ch]);
|
|
3178
|
+
}
|
|
1367
3179
|
function genXml_core({ title, creator, createdAt, modifiedAt }) {
|
|
1368
3180
|
if (!createdAt) createdAt = /* @__PURE__ */ new Date();
|
|
1369
3181
|
if (!modifiedAt) modifiedAt = /* @__PURE__ */ new Date();
|
|
1370
3182
|
return `<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
|
|
1371
3183
|
<cp:coreProperties xmlns:cp="http://schemas.openxmlformats.org/package/2006/metadata/core-properties" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:dcterms="http://purl.org/dc/terms/" xmlns:dcmitype="http://purl.org/dc/dcmitype/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
|
|
1372
|
-
<dc:title>${title || "Document"}</dc:title>
|
|
1373
|
-
<dc:creator>${creator || "Student"}</dc:creator>
|
|
1374
|
-
<cp:lastModifiedBy>${creator || "Student"}</cp:lastModifiedBy>
|
|
3184
|
+
<dc:title>${escapeXml(title || "Document")}</dc:title>
|
|
3185
|
+
<dc:creator>${escapeXml(creator || "Student")}</dc:creator>
|
|
3186
|
+
<cp:lastModifiedBy>${escapeXml(creator || "Student")}</cp:lastModifiedBy>
|
|
1375
3187
|
<dcterms:created xsi:type="dcterms:W3CDTF">${createdAt.toISOString()}</dcterms:created>
|
|
1376
3188
|
<dcterms:modified xsi:type="dcterms:W3CDTF">${modifiedAt.toISOString()}</dcterms:modified>
|
|
1377
3189
|
</cp:coreProperties>`;
|
|
1378
3190
|
}
|
|
1379
3191
|
function genXml_style(assets, doc) {
|
|
1380
|
-
let xml =
|
|
3192
|
+
let xml = fs4.readFileSync(path4.join(assets, "styles.xml"), { encoding: "utf8" });
|
|
1381
3193
|
xml = editStyle(`<w:style w:type="paragraph" w:default="1" w:styleId="a2">`, (xml2) => {
|
|
1382
3194
|
xml2 = replaceTag(xml2, `<w:spacing w:line="${doc.text.line_spacing * 240}" w:lineRule="auto" w:after="${doc.text.spacing.after * 20}"/>`);
|
|
1383
3195
|
xml2 = replaceTag(xml2, `<w:ind w:firstLine="${convertMillimetersToTwip(doc.text.indent * 10)}"/>`);
|
|
@@ -1402,7 +3214,33 @@ function genXml_style(assets, doc) {
|
|
|
1402
3214
|
xml2 = addrPrStyle(xml2, doc.table.title.style == "bold" ? "<w:b/>" : "<w:i/>");
|
|
1403
3215
|
return xml2;
|
|
1404
3216
|
});
|
|
3217
|
+
const admonitionTypes = ["note", "info", "tip", "warning", "danger"];
|
|
3218
|
+
for (const type of admonitionTypes) {
|
|
3219
|
+
const suffix = type == "note" ? "" : type;
|
|
3220
|
+
const styleId = `xAdmonition${suffix}`;
|
|
3221
|
+
const styleTag = `<w:style w:type="paragraph" w:customStyle="1" w:styleId="${styleId}">`;
|
|
3222
|
+
const style = doc.admonition[type];
|
|
3223
|
+
xml = editStyle(styleTag, (xml2) => {
|
|
3224
|
+
xml2 = replaceTagOrInsertAfter(xml2, "<w:pPr>", `<w:spacing w:before="${style.spacing.before * 20}" w:after="${style.spacing.after * 20}"/>`);
|
|
3225
|
+
xml2 = replaceTagOrInsertAfter(xml2, "<w:pPr>", `<w:ind w:left="${convertMillimetersToTwip(style.indent * 10)}" w:firstLine="0"/>`);
|
|
3226
|
+
xml2 = replaceBorder(xml2, "top", style.padding.top, "FFFFFF", 2);
|
|
3227
|
+
xml2 = replaceBorder(xml2, "right", style.padding.right, "FFFFFF", 2);
|
|
3228
|
+
xml2 = replaceBorder(xml2, "bottom", style.padding.bottom, "FFFFFF", 2);
|
|
3229
|
+
xml2 = replaceBorder(xml2, "left", style.padding.left, style.color, Math.max(1, Math.round(style.bar_width * 8)));
|
|
3230
|
+
xml2 = replaceTagOrInsertAfter(xml2, "<w:pPr>", `<w:shd w:val="clear" w:color="auto" w:fill="${style.background}"/>`);
|
|
3231
|
+
return xml2;
|
|
3232
|
+
});
|
|
3233
|
+
}
|
|
1405
3234
|
return xml;
|
|
3235
|
+
function replaceBorder(xml2, side, space, color, size) {
|
|
3236
|
+
return replaceTagOrInsertAfter(xml2, "<w:pBdr>", `<w:${side} w:val="single" w:sz="${size}" w:space="${space}" w:color="${color}"/>`);
|
|
3237
|
+
}
|
|
3238
|
+
function replaceTagOrInsertAfter(xml2, insertAfter2, tag) {
|
|
3239
|
+
const tagName = /<([^\s>]+)/.exec(tag)?.[1];
|
|
3240
|
+
if (!tagName) throw err();
|
|
3241
|
+
const regex = new RegExp(`<${tagName}\\b[^>]*/>`);
|
|
3242
|
+
return regex.test(xml2) ? xml2.replace(regex, tag) : xml2.replace(insertAfter2, `${insertAfter2}${tag}`);
|
|
3243
|
+
}
|
|
1406
3244
|
function replaceTag(xml2, tag) {
|
|
1407
3245
|
const tagName = /<([^\s]+)\b/.exec(tag)?.[1];
|
|
1408
3246
|
if (!tagName) throw err();
|
|
@@ -1442,13 +3280,14 @@ function genXml_style(assets, doc) {
|
|
|
1442
3280
|
|
|
1443
3281
|
// src/enricher.ts
|
|
1444
3282
|
function enrichDoc(doc, logwarn = console.warn) {
|
|
3283
|
+
const warnAt = (line, message) => logwarn(`Line ${doc.mapSourceLine(line)}: ${message}`);
|
|
1445
3284
|
for (let i = 0; i < doc.nodes.length; i++) {
|
|
1446
3285
|
const node = doc.nodes[i];
|
|
1447
|
-
if (node.type == "title" && node.text.toUpperCase().startsWith("\u0422\u0418\u0422\u0423\u041B\u042C\u041D\u0418\u041A")) {
|
|
1448
|
-
doc.nodes.splice(i, 1, { type: "text", text: node.text.toUpperCase() });
|
|
1449
|
-
doc.nodes.splice(i + 1, 0, { type: "pageBreak" });
|
|
3286
|
+
if (node.type == "title" && node.level == 1 && node.text.toUpperCase().startsWith("\u0422\u0418\u0422\u0423\u041B\u042C\u041D\u0418\u041A")) {
|
|
3287
|
+
doc.nodes.splice(i, 1, { type: "text", text: node.text.toUpperCase(), sourceLine: node.sourceLine });
|
|
3288
|
+
doc.nodes.splice(i + 1, 0, { type: "pageBreak", sourceLine: node.sourceLine });
|
|
1450
3289
|
i++;
|
|
1451
|
-
} else if (node.type == "title" && node.text.toUpperCase() == "\u0420\u0415\u0424\u0415\u0420\u0410\u0422") {
|
|
3290
|
+
} else if (node.type == "title" && node.level == 1 && node.text.toUpperCase() == "\u0420\u0415\u0424\u0415\u0420\u0410\u0422") {
|
|
1452
3291
|
node.level = 0;
|
|
1453
3292
|
node.text = node.text.toUpperCase();
|
|
1454
3293
|
const nextNode = doc.nodes[i + 1];
|
|
@@ -1456,81 +3295,83 @@ function enrichDoc(doc, logwarn = console.warn) {
|
|
|
1456
3295
|
nextNode.noIndent = true;
|
|
1457
3296
|
nextNode.text = trimEnd(nextNode.text, ".").toUpperCase();
|
|
1458
3297
|
}
|
|
1459
|
-
doc.nodes.splice(i + 1, 0, { type: "text", tags: ["synopsis"], text: "\u041E\u0442\u0447\u0435\u0442 [!pages] \u0441., [!imgs] \u0440\u0438\u0441., [!tables] \u0442\u0430\u0431\u043B., [!codes] \u043B\u0438\u0441\u0442., [!sources] \u0438\u0441\u0442\u043E\u0447\u043D." });
|
|
3298
|
+
doc.nodes.splice(i + 1, 0, { type: "text", tags: ["synopsis"], text: "\u041E\u0442\u0447\u0435\u0442 [!pages] \u0441., [!imgs] \u0440\u0438\u0441., [!tables] \u0442\u0430\u0431\u043B., [!codes] \u043B\u0438\u0441\u0442., [!sources] \u0438\u0441\u0442\u043E\u0447\u043D.", sourceLine: node.sourceLine });
|
|
1460
3299
|
while (i + 1 < doc.nodes.length && doc.nodes[i + 1]?.type != "title") i++;
|
|
1461
|
-
doc.nodes.splice(i + 1, 0, { type: "pageBreak" });
|
|
1462
|
-
} else if (node.type == "title" && node.text.toUpperCase() == "\u041E\u0413\u041B\u0410\u0412\u041B\u0415\u041D\u0418\u0415") {
|
|
3300
|
+
doc.nodes.splice(i + 1, 0, { type: "pageBreak", sourceLine: node.sourceLine });
|
|
3301
|
+
} else if (node.type == "title" && node.level == 1 && node.text.toUpperCase() == "\u041E\u0413\u041B\u0410\u0412\u041B\u0415\u041D\u0418\u0415") {
|
|
1463
3302
|
node.level = 0;
|
|
1464
3303
|
node.text = node.text.toUpperCase();
|
|
1465
|
-
doc.nodes.splice(i + 1, 0, { type: "tableOfContents" });
|
|
1466
|
-
} else if (node.type == "title" && node.text.toUpperCase() == "\u0422\u0415\u0420\u041C\u0418\u041D\u042B \u0418 \u041E\u041F\u0420\u0415\u0414\u0415\u041B\u0415\u041D\u0418\u042F") {
|
|
3304
|
+
doc.nodes.splice(i + 1, 0, { type: "tableOfContents", sourceLine: node.sourceLine });
|
|
3305
|
+
} else if (node.type == "title" && node.level == 1 && node.text.toUpperCase() == "\u0422\u0415\u0420\u041C\u0418\u041D\u042B \u0418 \u041E\u041F\u0420\u0415\u0414\u0415\u041B\u0415\u041D\u0418\u042F") {
|
|
1467
3306
|
node.center = true;
|
|
1468
3307
|
node.text = node.text.toUpperCase();
|
|
1469
|
-
const warn = () =>
|
|
3308
|
+
const warn = (ln) => warnAt(ln, "Wrong format of \u0422\u0415\u0420\u041C\u0418\u041D\u042B \u0418 \u041E\u041F\u0420\u0415\u0414\u0415\u041B\u0415\u041D\u0418\u042F");
|
|
1470
3309
|
const list = doc.nodes[i + 1];
|
|
1471
3310
|
if (list?.type != "list") {
|
|
1472
|
-
warn();
|
|
3311
|
+
warn(node.sourceLine);
|
|
1473
3312
|
continue;
|
|
1474
3313
|
}
|
|
1475
|
-
const rows = [tableRow("\u0422\u0435\u0440\u043C\u0438\u043D", "\u041E\u043F\u0440\u0435\u0434\u0435\u043B\u0435\u043D\u0438\u0435")];
|
|
3314
|
+
const rows = [tableRow(list.sourceLine, "\u0422\u0435\u0440\u043C\u0438\u043D", "\u041E\u043F\u0440\u0435\u0434\u0435\u043B\u0435\u043D\u0438\u0435")];
|
|
1476
3315
|
const align = ["l", "l"];
|
|
1477
3316
|
for (const item of list.items) {
|
|
1478
3317
|
if (item.type != "listItem") {
|
|
1479
|
-
warn();
|
|
3318
|
+
warn(item.sourceLine);
|
|
1480
3319
|
continue;
|
|
1481
3320
|
}
|
|
1482
3321
|
const i2 = item.text.indexOf(":");
|
|
1483
3322
|
if (i2 < 0) {
|
|
1484
|
-
warn();
|
|
3323
|
+
warn(item.sourceLine);
|
|
1485
3324
|
continue;
|
|
1486
3325
|
}
|
|
1487
|
-
rows.push(tableRow(item.text.slice(0, i2).trim(), item.text.slice(i2 + 1).trim()));
|
|
3326
|
+
rows.push(tableRow(item.sourceLine, item.text.slice(0, i2).trim(), item.text.slice(i2 + 1).trim()));
|
|
1488
3327
|
}
|
|
1489
|
-
doc.nodes.splice(i + 1, 1, { type: "text", text: "\u0412 \u043D\u0430\u0441\u0442\u043E\u044F\u0449\u0435\u043C \u043E\u0442\u0447\u0435\u0442\u0435 \u043F\u0440\u0438\u043C\u0435\u043D\u044F\u044E\u0442\u0441\u044F \u0441\u043B\u0435\u0434\u0443\u044E\u0449\u0438\u0435 \u0442\u0435\u0440\u043C\u0438\u043D\u044B \u0441 \u0441\u043E\u043E\u0442\u0432\u0435\u0442\u0441\u0442\u0432\u0443\u044E\u0449\u0438\u043C\u0438 \u043E\u043F\u0440\u0435\u0434\u0435\u043B\u0435\u043D\u0438\u044F\u043C\u0438." });
|
|
1490
|
-
doc.nodes.splice(i + 2, 0, { type: "table", align, rows, normalFontSize: true, tags: ["definitions_table"] });
|
|
1491
|
-
doc.nodes.splice(i + 3, 0, { type: "pageBreak" });
|
|
1492
|
-
} else if (node.type == "title" && node.text.toUpperCase() == "\u041F\u0415\u0420\u0415\u0427\u0415\u041D\u042C \u0421\u041E\u041A\u0420\u0410\u0429\u0415\u041D\u0418\u0419 \u0418 \u041E\u0411\u041E\u0417\u041D\u0410\u0427\u0415\u041D\u0418\u0419") {
|
|
3328
|
+
doc.nodes.splice(i + 1, 1, { type: "text", text: "\u0412 \u043D\u0430\u0441\u0442\u043E\u044F\u0449\u0435\u043C \u043E\u0442\u0447\u0435\u0442\u0435 \u043F\u0440\u0438\u043C\u0435\u043D\u044F\u044E\u0442\u0441\u044F \u0441\u043B\u0435\u0434\u0443\u044E\u0449\u0438\u0435 \u0442\u0435\u0440\u043C\u0438\u043D\u044B \u0441 \u0441\u043E\u043E\u0442\u0432\u0435\u0442\u0441\u0442\u0432\u0443\u044E\u0449\u0438\u043C\u0438 \u043E\u043F\u0440\u0435\u0434\u0435\u043B\u0435\u043D\u0438\u044F\u043C\u0438.", sourceLine: node.sourceLine });
|
|
3329
|
+
doc.nodes.splice(i + 2, 0, { type: "table", align, rows, normalFontSize: true, tags: ["definitions_table"], sourceLine: node.sourceLine });
|
|
3330
|
+
doc.nodes.splice(i + 3, 0, { type: "pageBreak", sourceLine: node.sourceLine });
|
|
3331
|
+
} else if (node.type == "title" && node.level == 1 && node.text.toUpperCase() == "\u041F\u0415\u0420\u0415\u0427\u0415\u041D\u042C \u0421\u041E\u041A\u0420\u0410\u0429\u0415\u041D\u0418\u0419 \u0418 \u041E\u0411\u041E\u0417\u041D\u0410\u0427\u0415\u041D\u0418\u0419") {
|
|
1493
3332
|
node.center = true;
|
|
1494
3333
|
node.text = node.text.toUpperCase();
|
|
1495
|
-
const warn = () =>
|
|
3334
|
+
const warn = (ln) => warnAt(ln, "Wrong format of \u041F\u0415\u0420\u0415\u0427\u0415\u041D\u042C \u0421\u041E\u041A\u0420\u0410\u0429\u0415\u041D\u0418\u0419 \u0418 \u041E\u0411\u041E\u0417\u041D\u0410\u0427\u0415\u041D\u0418\u0419");
|
|
1496
3335
|
const list = doc.nodes[i + 1];
|
|
1497
3336
|
if (list?.type != "list") {
|
|
1498
|
-
warn();
|
|
3337
|
+
warn(node.sourceLine);
|
|
1499
3338
|
continue;
|
|
1500
3339
|
}
|
|
1501
3340
|
const items = [];
|
|
1502
3341
|
for (const item of list.items) {
|
|
1503
3342
|
if (item.type != "listItem") {
|
|
1504
|
-
warn();
|
|
3343
|
+
warn(item.sourceLine);
|
|
1505
3344
|
continue;
|
|
1506
3345
|
}
|
|
1507
|
-
items.push({ type: "text", text: trimEnd(item.text, "."), noIndent: true, noMargin: true });
|
|
3346
|
+
items.push({ type: "text", text: trimEnd(item.text, "."), noIndent: true, noMargin: true, sourceLine: item.sourceLine });
|
|
1508
3347
|
}
|
|
1509
|
-
doc.nodes.splice(i + 1, 1, { type: "text", text: "\u0412 \u043D\u0430\u0441\u0442\u043E\u044F\u0449\u0435\u043C \u043E\u0442\u0447\u0435\u0442\u0435 \u043F\u0440\u0438\u043C\u0435\u043D\u044F\u044E\u0442 \u0441\u043B\u0435\u0434\u0443\u044E\u0449\u0438\u0435 \u0441\u043E\u043A\u0440\u0430\u0449\u0435\u043D\u0438\u044F \u0438 \u043E\u0431\u043E\u0437\u043D\u0430\u0447\u0435\u043D\u0438\u044F." });
|
|
1510
|
-
doc.nodes.splice(i + 2, 0, { type: "pageBreak" });
|
|
3348
|
+
doc.nodes.splice(i + 1, 1, { type: "text", text: "\u0412 \u043D\u0430\u0441\u0442\u043E\u044F\u0449\u0435\u043C \u043E\u0442\u0447\u0435\u0442\u0435 \u043F\u0440\u0438\u043C\u0435\u043D\u044F\u044E\u0442 \u0441\u043B\u0435\u0434\u0443\u044E\u0449\u0438\u0435 \u0441\u043E\u043A\u0440\u0430\u0449\u0435\u043D\u0438\u044F \u0438 \u043E\u0431\u043E\u0437\u043D\u0430\u0447\u0435\u043D\u0438\u044F.", sourceLine: node.sourceLine });
|
|
3349
|
+
doc.nodes.splice(i + 2, 0, { type: "pageBreak", sourceLine: node.sourceLine });
|
|
1511
3350
|
doc.nodes.splice(i + 2, 0, ...items);
|
|
1512
|
-
} else if (node.type == "title" && node.text.toUpperCase() == "\u0412\u0412\u0415\u0414\u0415\u041D\u0418\u0415") {
|
|
3351
|
+
} else if (node.type == "title" && node.level == 1 && node.text.toUpperCase() == "\u0412\u0412\u0415\u0414\u0415\u041D\u0418\u0415") {
|
|
1513
3352
|
node.center = true;
|
|
1514
3353
|
node.text = node.text.toUpperCase();
|
|
1515
3354
|
for (let j = i + 1; j < doc.nodes.length; j++) {
|
|
1516
3355
|
const node2 = doc.nodes[j];
|
|
1517
3356
|
if (node2?.type == "title") {
|
|
1518
|
-
doc.nodes.splice(j, 0, { type: "pageBreak" });
|
|
3357
|
+
doc.nodes.splice(j, 0, { type: "pageBreak", sourceLine: -1 });
|
|
1519
3358
|
break;
|
|
1520
3359
|
}
|
|
1521
3360
|
}
|
|
1522
|
-
} else if (node.type == "title" && node.text.toUpperCase() == "\u0417\u0410\u041A\u041B\u042E\u0427\u0415\u041D\u0418\u0415") {
|
|
3361
|
+
} else if (node.type == "title" && node.level == 1 && node.text.toUpperCase() == "\u0417\u0410\u041A\u041B\u042E\u0427\u0415\u041D\u0418\u0415") {
|
|
1523
3362
|
node.center = true;
|
|
1524
3363
|
node.text = node.text.toUpperCase();
|
|
1525
|
-
doc.nodes.splice(i, 0, { type: "pageBreak" });
|
|
3364
|
+
doc.nodes.splice(i, 0, { type: "pageBreak", sourceLine: node.sourceLine });
|
|
1526
3365
|
i++;
|
|
1527
|
-
} else if (node.type == "title" && node.text.toUpperCase() == "\u0421\u041F\u0418\u0421\u041E\u041A \u0418\u0421\u041F\u041E\u041B\u042C\u0417\u041E\u0412\u0410\u041D\u041D\u042B\u0425 \u0418\u0421\u0422\u041E\u0427\u041D\u0418\u041A\u041E\u0412") {
|
|
3366
|
+
} else if (node.type == "title" && node.level == 1 && node.text.toUpperCase() == "\u0421\u041F\u0418\u0421\u041E\u041A \u0418\u0421\u041F\u041E\u041B\u042C\u0417\u041E\u0412\u0410\u041D\u041D\u042B\u0425 \u0418\u0421\u0422\u041E\u0427\u041D\u0418\u041A\u041E\u0412") {
|
|
1528
3367
|
node.center = true;
|
|
1529
3368
|
node.text = node.text.toUpperCase();
|
|
1530
|
-
doc.nodes.splice(i++, 0, { type: "pageBreak" });
|
|
3369
|
+
doc.nodes.splice(i++, 0, { type: "pageBreak", sourceLine: node.sourceLine });
|
|
1531
3370
|
const nextNode = doc.nodes[i + 1];
|
|
1532
3371
|
if (nextNode?.type == "list") {
|
|
1533
3372
|
nextNode.alternativeStyle = true;
|
|
3373
|
+
nextNode.tags = nextNode.tags || [];
|
|
3374
|
+
nextNode.tags.push("sources");
|
|
1534
3375
|
i++;
|
|
1535
3376
|
continue;
|
|
1536
3377
|
}
|
|
@@ -1539,35 +3380,38 @@ function enrichDoc(doc, logwarn = console.warn) {
|
|
|
1539
3380
|
mark: ".",
|
|
1540
3381
|
startIndex: 1,
|
|
1541
3382
|
alternativeStyle: true,
|
|
3383
|
+
tags: ["sources"],
|
|
1542
3384
|
ordered: true,
|
|
1543
|
-
items: []
|
|
3385
|
+
items: [],
|
|
3386
|
+
sourceLine: node.sourceLine + 1
|
|
1544
3387
|
};
|
|
1545
3388
|
const startI = i + 1;
|
|
1546
3389
|
while (true) {
|
|
1547
3390
|
const textNode = doc.nodes[i + 1];
|
|
1548
3391
|
if (textNode?.type != "text") break;
|
|
1549
3392
|
const items = textNode.text.split(/\n\s*(?=\[[a-zA-Zа-яА-ЯёЁ_\d]+\])/);
|
|
3393
|
+
let sourceLine = textNode.sourceLine;
|
|
1550
3394
|
for (const item of items)
|
|
1551
|
-
list.items.push({ type: "listItem", text: item });
|
|
3395
|
+
list.items.push({ type: "listItem", text: item, sourceLine: sourceLine++ });
|
|
1552
3396
|
i++;
|
|
1553
3397
|
}
|
|
1554
3398
|
if (list.items.length > 0)
|
|
1555
3399
|
doc.nodes.splice(startI, i - startI + 1, list);
|
|
1556
|
-
} else if (node.type == "title" && node.text.toUpperCase() == "\u041F\u0420\u0418\u041B\u041E\u0416\u0415\u041D\u0418\u042F") {
|
|
3400
|
+
} else if (node.type == "title" && node.level == 1 && node.text.toUpperCase() == "\u041F\u0420\u0418\u041B\u041E\u0416\u0415\u041D\u0418\u042F") {
|
|
1557
3401
|
node.level = 0;
|
|
1558
3402
|
node.center = true;
|
|
1559
3403
|
node.text = node.text.toUpperCase();
|
|
1560
|
-
doc.nodes.splice(i++, 0, { type: "pageBreak" });
|
|
3404
|
+
doc.nodes.splice(i++, 0, { type: "pageBreak", sourceLine: node.sourceLine });
|
|
1561
3405
|
for (let j = i + 1; j < doc.nodes.length; j++) {
|
|
1562
3406
|
const node2 = doc.nodes[j];
|
|
1563
3407
|
if (node2.type == "title") {
|
|
1564
|
-
doc.nodes.splice(i++ + 1, 0, { type: "text", text: "\u041F\u0440\u0438\u043B\u043E\u0436\u0435\u043D\u0438\u0435 " + node2.text.replace("]", "].") });
|
|
3408
|
+
doc.nodes.splice(i++ + 1, 0, { type: "text", text: "\u041F\u0440\u0438\u043B\u043E\u0436\u0435\u043D\u0438\u0435 " + node2.text.replace("]", "]."), sourceLine: node2.sourceLine });
|
|
1565
3409
|
j++;
|
|
1566
3410
|
node2.level = 1;
|
|
1567
3411
|
const splitI = node2.text.indexOf("]");
|
|
1568
|
-
doc.nodes.splice(j++, 0, { type: "pageBreak" });
|
|
3412
|
+
doc.nodes.splice(j++, 0, { type: "pageBreak", sourceLine: node2.sourceLine });
|
|
1569
3413
|
if (splitI >= 0) {
|
|
1570
|
-
doc.nodes.splice(j++ + 1, 0, { type: "text", text: node2.text.slice(splitI + 1), center: true });
|
|
3414
|
+
doc.nodes.splice(j++ + 1, 0, { type: "text", text: node2.text.slice(splitI + 1), center: true, sourceLine: node2.sourceLine });
|
|
1571
3415
|
node2.text = "\u041F\u0420\u0418\u041B\u041E\u0416\u0415\u041D\u0418\u0415 " + node2.text.slice(0, splitI + 1);
|
|
1572
3416
|
node2.center = true;
|
|
1573
3417
|
}
|
|
@@ -1603,11 +3447,11 @@ function enrichDoc(doc, logwarn = console.warn) {
|
|
|
1603
3447
|
} else if (node.type == "externalDoc") {
|
|
1604
3448
|
const nextNode = doc.nodes[i + 1];
|
|
1605
3449
|
if (nextNode && nextNode?.type != "externalDoc" && nextNode?.type != "pageBreak" && nextNode?.type != "sectionBreak") {
|
|
1606
|
-
doc.nodes.splice(i + 1, 0, { type: "pageBreak" });
|
|
3450
|
+
doc.nodes.splice(i + 1, 0, { type: "pageBreak", sourceLine: node.sourceLine });
|
|
1607
3451
|
}
|
|
1608
3452
|
const prevNode = doc.nodes[i - 1];
|
|
1609
3453
|
if (prevNode && prevNode?.type != "externalDoc" && prevNode?.type != "pageBreak" && prevNode?.type != "sectionBreak") {
|
|
1610
|
-
doc.nodes.splice(i, 0, { type: "pageBreak" });
|
|
3454
|
+
doc.nodes.splice(i, 0, { type: "pageBreak", sourceLine: node.sourceLine });
|
|
1611
3455
|
i++;
|
|
1612
3456
|
}
|
|
1613
3457
|
}
|
|
@@ -1616,7 +3460,7 @@ function enrichDoc(doc, logwarn = console.warn) {
|
|
|
1616
3460
|
|
|
1617
3461
|
// src/main.ts
|
|
1618
3462
|
import { exec, spawn } from "child_process";
|
|
1619
|
-
import
|
|
3463
|
+
import fs5 from "fs/promises";
|
|
1620
3464
|
import { existsSync as existsSync2 } from "fs";
|
|
1621
3465
|
import { PDFDocument } from "pdf-lib";
|
|
1622
3466
|
|
|
@@ -1627,12 +3471,15 @@ function alchemist(doc, logwarn = console.warn) {
|
|
|
1627
3471
|
codesAll: 0,
|
|
1628
3472
|
imgsAll: 0,
|
|
1629
3473
|
tablesAll: 0,
|
|
3474
|
+
formulasAll: 0,
|
|
1630
3475
|
codes: 0,
|
|
1631
3476
|
imgs: 0,
|
|
1632
3477
|
tables: 0,
|
|
3478
|
+
formulas: 0,
|
|
1633
3479
|
titles: { l1: 0, l2: 0, l3: 0, l4: 0, l5: 0 }
|
|
1634
3480
|
};
|
|
1635
3481
|
const named = {};
|
|
3482
|
+
const warnAt = (line, message) => logwarn(`${line ? `Line ${doc.mapSourceLine(line)}: ` : ""}${message}`);
|
|
1636
3483
|
const vals = {};
|
|
1637
3484
|
let lastRefI = 0;
|
|
1638
3485
|
let prevNum = -1;
|
|
@@ -1649,11 +3496,13 @@ function alchemist(doc, logwarn = console.warn) {
|
|
|
1649
3496
|
let codes = counter.codes;
|
|
1650
3497
|
let imgs = counter.imgs;
|
|
1651
3498
|
let tables = counter.tables;
|
|
3499
|
+
let formulas = counter.formulas;
|
|
1652
3500
|
for (let j = i; j < doc.nodes.length && nextNum < 0; j++) {
|
|
1653
3501
|
const n = doc.nodes[j];
|
|
1654
3502
|
if (n.type == "code") nextNum = codes + 1;
|
|
1655
3503
|
if (n.type == "image") nextNum = imgs + 1;
|
|
1656
3504
|
if (n.type == "table") nextNum = tables + 1;
|
|
3505
|
+
if (n.type == "math") nextNum = formulas + 1;
|
|
1657
3506
|
if (n.type == "title" && n.level == 1 && doc.numberingSections) {
|
|
1658
3507
|
const num = getTitleNum(n);
|
|
1659
3508
|
if (num) l1 = num;
|
|
@@ -1661,6 +3510,7 @@ function alchemist(doc, logwarn = console.warn) {
|
|
|
1661
3510
|
codes = 0;
|
|
1662
3511
|
imgs = 0;
|
|
1663
3512
|
tables = 0;
|
|
3513
|
+
formulas = 0;
|
|
1664
3514
|
}
|
|
1665
3515
|
}
|
|
1666
3516
|
nextPrefix = l1 > 0 && doc.numberingSections ? `${l1}.` : "";
|
|
@@ -1683,13 +3533,14 @@ function alchemist(doc, logwarn = console.warn) {
|
|
|
1683
3533
|
counter.codes = 0;
|
|
1684
3534
|
counter.imgs = 0;
|
|
1685
3535
|
counter.tables = 0;
|
|
3536
|
+
counter.formulas = 0;
|
|
1686
3537
|
}
|
|
1687
3538
|
prefix = doc.numberingSections ? `${counter.titles.l1}.` : "";
|
|
1688
3539
|
if (appendix) prefix = APPENDIX_NAME[counter.titles.l1 - 1] + ".";
|
|
1689
3540
|
}
|
|
1690
3541
|
}
|
|
1691
3542
|
materializeNode(node);
|
|
1692
|
-
if (node.type == "code" || node.type == "image" || node.type == "table") {
|
|
3543
|
+
if (node.type == "code" || node.type == "image" || node.type == "table" || node.type == "math") {
|
|
1693
3544
|
if (node.type == "code") {
|
|
1694
3545
|
counter.codes++;
|
|
1695
3546
|
counter.codesAll++;
|
|
@@ -1702,18 +3553,27 @@ function alchemist(doc, logwarn = console.warn) {
|
|
|
1702
3553
|
counter.tables++;
|
|
1703
3554
|
counter.tablesAll++;
|
|
1704
3555
|
}
|
|
3556
|
+
if (node.type == "math") {
|
|
3557
|
+
counter.formulas++;
|
|
3558
|
+
counter.formulasAll++;
|
|
3559
|
+
}
|
|
1705
3560
|
prevNum = nextNum;
|
|
1706
3561
|
prevPrefix = prefix;
|
|
1707
3562
|
nextNum = -1;
|
|
1708
3563
|
}
|
|
1709
3564
|
});
|
|
3565
|
+
for (const value of [doc.header, doc.footer, ...doc.nodes.filter((node) => node.type == "sectionBreak").flatMap((node) => [node.header, node.footer])]) {
|
|
3566
|
+
if (value?.type != "content") continue;
|
|
3567
|
+
value.nodes.forEach(materializeNode);
|
|
3568
|
+
}
|
|
1710
3569
|
const sourcesCount = crystallizeSources();
|
|
1711
|
-
vals["codes"]?.forEach((f) => f(counter.
|
|
1712
|
-
vals["imgs"]?.forEach((f) => f(counter.
|
|
1713
|
-
vals["tables"]?.forEach((f) => f(counter.
|
|
3570
|
+
vals["codes"]?.forEach((f) => f(counter.codesAll));
|
|
3571
|
+
vals["imgs"]?.forEach((f) => f(counter.imgsAll));
|
|
3572
|
+
vals["tables"]?.forEach((f) => f(counter.tablesAll));
|
|
3573
|
+
vals["formulas"]?.forEach((f) => f(counter.formulasAll));
|
|
1714
3574
|
vals["sources"]?.forEach((f) => f(sourcesCount));
|
|
1715
3575
|
Object.entries(named).forEach(([k, v]) => {
|
|
1716
|
-
if (v instanceof Array)
|
|
3576
|
+
if (v instanceof Array) v.forEach((ref) => warnAt(ref.sourceLine, `\u041D\u0435\u0438\u0437\u0432\u0435\u0441\u0442\u043D\u0430\u044F \u0441\u0441\u044B\u043B\u043A\u0430 [${k}]`));
|
|
1717
3577
|
});
|
|
1718
3578
|
if (synopsis) {
|
|
1719
3579
|
synopsis.text = [
|
|
@@ -1747,7 +3607,7 @@ function alchemist(doc, logwarn = console.warn) {
|
|
|
1747
3607
|
function materializeList(node) {
|
|
1748
3608
|
node.items.forEach((item) => {
|
|
1749
3609
|
if (item.type == "list") materializeList(item);
|
|
1750
|
-
else materializeRunes(item.text, node);
|
|
3610
|
+
else materializeRunes(item.text, { ...node, sourceLine: item.sourceLine ?? node.sourceLine });
|
|
1751
3611
|
});
|
|
1752
3612
|
}
|
|
1753
3613
|
function materializeTable(node) {
|
|
@@ -1795,8 +3655,9 @@ function alchemist(doc, logwarn = console.warn) {
|
|
|
1795
3655
|
rune.text = repeat(node.level, (i2) => counter.titles[`l${i2 + 1}`]).join(".");
|
|
1796
3656
|
return;
|
|
1797
3657
|
}
|
|
1798
|
-
if (type == "code" || type == "image" || type == "table") {
|
|
1799
|
-
let { num, text } = type == "code" ? { num: counter.codes, text: "\u041B\u0438\u0441\u0442\u0438\u043D\u0433" } : type == "image" ? { num: counter.imgs, text: "\u0420\u0438\u0441\u0443\u043D\u043E\u043A" } : type == "table" ? { num: counter.tables, text: "\u0422\u0430\u0431\u043B\u0438\u0446\u0430" } : (() => {
|
|
3658
|
+
if (type == "code" || type == "image" || type == "table" || type == "math") {
|
|
3659
|
+
let { num, text } = type == "code" ? { num: counter.codes, text: "\u041B\u0438\u0441\u0442\u0438\u043D\u0433" } : type == "image" ? { num: counter.imgs, text: "\u0420\u0438\u0441\u0443\u043D\u043E\u043A" } : type == "table" ? { num: counter.tables, text: "\u0422\u0430\u0431\u043B\u0438\u0446\u0430" } : type == "math" ? { num: counter.formulas, text: "" } : (() => {
|
|
3660
|
+
type;
|
|
1800
3661
|
throw new Error("switch default");
|
|
1801
3662
|
})();
|
|
1802
3663
|
num++;
|
|
@@ -1813,9 +3674,10 @@ function alchemist(doc, logwarn = console.warn) {
|
|
|
1813
3674
|
nextRune.text = toCapitalCase(nextRune.text);
|
|
1814
3675
|
}
|
|
1815
3676
|
text = `${text} ${prefix}${num} \u2013 `;
|
|
3677
|
+
if (type == "math") text = `${prefix}${num}`;
|
|
1816
3678
|
} else text = `${prefix}${num}`;
|
|
1817
3679
|
if (v instanceof Array) v.forEach((fn) => fn.f(num, prefix));
|
|
1818
|
-
else if (v && tag != "#")
|
|
3680
|
+
else if (v && tag != "#") warnAt(node.sourceLine, `id [${tag}] ${type == "code" ? "\u043B\u0438\u0441\u0442\u0438\u043D\u0433\u0430" : type == "image" ? "\u0440\u0438\u0441\u0443\u043D\u043A\u0430" : type == "table" ? "\u0442\u0430\u0431\u043B\u0438\u0446\u044B" : type == "math" ? "\u0444\u043E\u0440\u043C\u0443\u043B\u044B" : ""} \u0443\u0436\u0435 \u0437\u0430\u043D\u044F\u0442\u043E \u0447\u0435\u043C-\u0442\u043E \u0434\u0440\u0443\u0433\u0438\u043C`);
|
|
1819
3681
|
named[tag] = { n: num, prefix };
|
|
1820
3682
|
rune.type = "text";
|
|
1821
3683
|
rune.text = text;
|
|
@@ -1835,8 +3697,8 @@ function alchemist(doc, logwarn = console.warn) {
|
|
|
1835
3697
|
rune.type = "text";
|
|
1836
3698
|
rune.text = n < 0 ? prefix2 : `${prefix2}${applyMath(n)}`;
|
|
1837
3699
|
};
|
|
1838
|
-
if (v) v.push({ f, i: lastRefI
|
|
1839
|
-
else named[tag] = [{ f, i: lastRefI
|
|
3700
|
+
if (v) v.push({ f, i: lastRefI++, sourceLine: node.sourceLine });
|
|
3701
|
+
else named[tag] = [{ f, i: lastRefI++, sourceLine: node.sourceLine }];
|
|
1840
3702
|
} else {
|
|
1841
3703
|
rune.type = "text";
|
|
1842
3704
|
rune.text = `${v.prefix}${applyMath(v.n)}`;
|
|
@@ -1844,21 +3706,22 @@ function alchemist(doc, logwarn = console.warn) {
|
|
|
1844
3706
|
});
|
|
1845
3707
|
}
|
|
1846
3708
|
function crystallizeSources() {
|
|
1847
|
-
const sourcesList = doc.nodes.find((node) => node.type == "list" && node.
|
|
3709
|
+
const sourcesList = doc.nodes.find((node) => node.type == "list" && node.tags?.includes("sources"));
|
|
1848
3710
|
if (!sourcesList || sourcesList.type != "list") return 0;
|
|
1849
|
-
const items = sourcesList.items.filter((v) => v.type == "listItem").map((v
|
|
3711
|
+
const items = sourcesList.items.filter((v) => v.type == "listItem").map((v, i) => {
|
|
3712
|
+
const item = v.text;
|
|
1850
3713
|
const refI = item[0]?.text ? 0 : 1;
|
|
1851
3714
|
const ref = item[refI];
|
|
1852
3715
|
if (ref?.type != "ref") {
|
|
1853
|
-
return { text: item, ref: "", i: 99999 + i };
|
|
3716
|
+
return { text: item, ref: "", i: 99999 + i, srcI: v.sourceLine };
|
|
1854
3717
|
}
|
|
1855
3718
|
const refs = named[ref.text];
|
|
1856
3719
|
if (refs && !(refs instanceof Array)) {
|
|
1857
|
-
|
|
1858
|
-
return { text: item.slice(refI + 1), ref: "", i: 99999 + i };
|
|
3720
|
+
warnAt(sourcesList.sourceLine, `id [${ref.text}] \u0438\u0441\u0442\u043E\u0447\u043D\u0438\u043A\u0430 \u0443\u0436\u0435 \u0437\u0430\u043D\u044F\u0442\u043E \u0447\u0435\u043C-\u0442\u043E \u0434\u0440\u0443\u0433\u0438\u043C`);
|
|
3721
|
+
return { text: item.slice(refI + 1), ref: "", i: 99999 + i, srcI: v.sourceLine };
|
|
1859
3722
|
}
|
|
1860
|
-
const firstOccurrenceI = Math.min(...refs.map((
|
|
1861
|
-
return { text: item.slice(refI + 1), ref: ref.text, i: firstOccurrenceI };
|
|
3723
|
+
const firstOccurrenceI = Math.min(...refs.map((v2) => v2.i));
|
|
3724
|
+
return { text: item.slice(refI + 1), ref: ref.text, i: firstOccurrenceI, srcI: v.sourceLine };
|
|
1862
3725
|
});
|
|
1863
3726
|
items.sort((a, b) => a.i - b.i);
|
|
1864
3727
|
const hasIdAny = items.some((it) => !!it.ref);
|
|
@@ -1868,16 +3731,17 @@ function alchemist(doc, logwarn = console.warn) {
|
|
|
1868
3731
|
named[key] = { prefix: "", n: i + 1 };
|
|
1869
3732
|
if (refs instanceof Array) {
|
|
1870
3733
|
if (hasIdAny && refs.length <= 1)
|
|
1871
|
-
|
|
3734
|
+
warnAt(item.srcI, `\u0412 \u0442\u0435\u043A\u0441\u0442\u0435 \u043E\u0442\u0441\u0443\u0442\u0441\u0442\u0432\u0443\u044E\u0442 \u0441\u0441\u044B\u043B\u043A\u0438 \u043D\u0430 \u0438\u0441\u0442\u043E\u0447\u043D\u0438\u043A: ${item.ref}`);
|
|
1872
3735
|
refs.forEach((r) => hasIdAny ? r.f(i + 1, "") : r.f(-1, `[${i + 1}]`));
|
|
1873
3736
|
}
|
|
1874
|
-
return { type: "listItem", text: item.text };
|
|
3737
|
+
return { type: "listItem", text: item.text, sourceLine: item.srcI };
|
|
1875
3738
|
});
|
|
1876
3739
|
return sourcesList.items.length;
|
|
1877
3740
|
}
|
|
1878
3741
|
}
|
|
1879
3742
|
function addLazyNumbering(doc) {
|
|
1880
3743
|
doc.nodes.forEach((node) => {
|
|
3744
|
+
if (node.type == "math" && !node.title) node.title = [{ text: "(", type: "text" }, { text: "#", type: "ref" }, { text: ")", type: "text" }];
|
|
1881
3745
|
const runes = node.type == "code" || node.type == "table" ? node.title : node.type == "image" ? node.text : null;
|
|
1882
3746
|
if (!runes) return;
|
|
1883
3747
|
if (runes.find((rune) => rune.type == "ref")) return;
|
|
@@ -1899,6 +3763,8 @@ async function render({
|
|
|
1899
3763
|
disableMacros = false,
|
|
1900
3764
|
useLibreOffice = false,
|
|
1901
3765
|
checkFilesIsInsidePath = "",
|
|
3766
|
+
templateOptions = { allowIncludes: true, allowLoops: true },
|
|
3767
|
+
variables,
|
|
1902
3768
|
logwarn = console.warn,
|
|
1903
3769
|
loginfo = console.info,
|
|
1904
3770
|
logPS = (msg) => console.log(`PS: ${msg}`),
|
|
@@ -1906,23 +3772,23 @@ async function render({
|
|
|
1906
3772
|
signal = null
|
|
1907
3773
|
}) {
|
|
1908
3774
|
signal?.throwIfAborted();
|
|
1909
|
-
const fin =
|
|
3775
|
+
const fin = path5.resolve(file);
|
|
1910
3776
|
progress(10, "\u041F\u044B\u0442\u0430\u0435\u043C\u0441\u044F \u043F\u043E\u043D\u044F\u0442\u044C, \u0447\u0442\u043E \u0432\u044B \u0442\u0443\u0442 \u043D\u0430\u043F\u0438\u0441\u0430\u043B\u0438...");
|
|
1911
3777
|
loginfo(`[!1] Parsing file ${fin}`);
|
|
1912
|
-
const doc = await parseMD(fin, logwarn);
|
|
3778
|
+
const doc = await parseMD(fin, variables, templateOptions, checkFilesIsInsidePath, signal, logwarn);
|
|
1913
3779
|
signal?.throwIfAborted();
|
|
1914
3780
|
progress(10, "\u041A\u043E\u043B\u0434\u0443\u0435\u043C \u043D\u0430\u0434 \u0441\u0438\u043D\u0442\u0430\u043A\u0441\u0438\u0441\u043E\u043C...");
|
|
1915
3781
|
loginfo("[!2] Processing document");
|
|
1916
3782
|
enrichDoc(doc, logwarn);
|
|
1917
3783
|
const runicDoc = runifyDoc(doc);
|
|
1918
3784
|
alchemist(runicDoc, logwarn);
|
|
1919
|
-
const pout = output &&
|
|
1920
|
-
if (pout) await
|
|
1921
|
-
const p =
|
|
3785
|
+
const pout = output && path5.parse(path5.resolve(output));
|
|
3786
|
+
if (pout) await fs5.mkdir(pout.dir, { recursive: true });
|
|
3787
|
+
const p = path5.parse(fin);
|
|
1922
3788
|
const fdir = p.dir;
|
|
1923
3789
|
const fname = trimEnd(p.name, ".g");
|
|
1924
|
-
const ftmp =
|
|
1925
|
-
const fout = pout ?
|
|
3790
|
+
const ftmp = path5.join(fdir, fname + ".tmp.docx");
|
|
3791
|
+
const fout = pout ? path5.join(pout.dir, pout.name + ".docx") : path5.join(fdir, fname + ".docx");
|
|
1926
3792
|
if (await checkIfFileIsBlocked(ftmp))
|
|
1927
3793
|
throw new Error(`\u0417\u0430\u043A\u0440\u043E\u0439\u0442\u0435 docx \u0444\u0430\u0439\u043B. Output file is busy or locked: ${ftmp}`);
|
|
1928
3794
|
if (await checkIfFileIsBlocked(fout))
|
|
@@ -1930,7 +3796,7 @@ async function render({
|
|
|
1930
3796
|
signal?.throwIfAborted();
|
|
1931
3797
|
progress(10, phrase_renderDocx());
|
|
1932
3798
|
loginfo("[!3] Serializing to docx");
|
|
1933
|
-
await serializeDocx(runicDoc, ftmp, fdir, assets, checkFilesIsInsidePath);
|
|
3799
|
+
await serializeDocx(runicDoc, ftmp, fdir, assets, checkFilesIsInsidePath, logwarn);
|
|
1934
3800
|
let willRunMacros = !disableMacros && (renderPDF || hasReasonForRunningMacros(runicDoc));
|
|
1935
3801
|
let limitedRender = false;
|
|
1936
3802
|
if (process.platform != "win32" && willRunMacros && !useLibreOffice) {
|
|
@@ -1941,31 +3807,31 @@ async function render({
|
|
|
1941
3807
|
signal?.throwIfAborted();
|
|
1942
3808
|
progress(20, phrase_runMacros());
|
|
1943
3809
|
loginfo("[!4] Starting macros");
|
|
1944
|
-
const tmpfolder =
|
|
1945
|
-
await
|
|
3810
|
+
const tmpfolder = path5.join(pout ? pout.dir : fdir, ".md2gost_out");
|
|
3811
|
+
await fs5.rm(tmpfolder, { recursive: true, force: true });
|
|
1946
3812
|
try {
|
|
1947
3813
|
const macroRunner = useLibreOffice ? runUnoScript : runDocxMacro;
|
|
1948
3814
|
const ok = await macroRunner(progress, logPS, logPSError, assets, fdir, ftmp, fout, renderPDF, signal);
|
|
1949
3815
|
if (!ok) {
|
|
1950
|
-
await
|
|
3816
|
+
await fs5.rename(ftmp, fout);
|
|
1951
3817
|
return { fout, err: "inPS" };
|
|
1952
3818
|
}
|
|
1953
3819
|
} catch (x) {
|
|
1954
3820
|
if (signal?.aborted) {
|
|
1955
|
-
await
|
|
3821
|
+
await fs5.unlink(ftmp).catch(() => {
|
|
1956
3822
|
});
|
|
1957
3823
|
throw x;
|
|
1958
3824
|
}
|
|
1959
|
-
await
|
|
3825
|
+
await fs5.rename(ftmp, fout);
|
|
1960
3826
|
return { fout, err: "noPS", errS: x };
|
|
1961
3827
|
}
|
|
1962
3828
|
if (existsSync2(ftmp))
|
|
1963
|
-
await
|
|
3829
|
+
await fs5.unlink(ftmp);
|
|
1964
3830
|
signal?.throwIfAborted();
|
|
1965
|
-
const errorTxt =
|
|
3831
|
+
const errorTxt = path5.join(tmpfolder, "error.txt");
|
|
1966
3832
|
if (existsSync2(errorTxt)) {
|
|
1967
|
-
const err = await
|
|
1968
|
-
await
|
|
3833
|
+
const err = await fs5.readFile(errorTxt);
|
|
3834
|
+
await fs5.rm(tmpfolder, { recursive: true, force: true });
|
|
1969
3835
|
return { fout, err: "vba", errS: `${err}` };
|
|
1970
3836
|
}
|
|
1971
3837
|
loginfo("[!5] Updating metadata");
|
|
@@ -1977,11 +3843,11 @@ async function render({
|
|
|
1977
3843
|
if (!existsSync2(tmpfolder))
|
|
1978
3844
|
return { fout, err: "pdf" };
|
|
1979
3845
|
try {
|
|
1980
|
-
const files = (await
|
|
1981
|
-
const pdf = pout ?
|
|
3846
|
+
const files = (await fs5.readdir(tmpfolder)).sort().map((f) => path5.join(tmpfolder, f));
|
|
3847
|
+
const pdf = pout ? path5.join(pout.dir, pout.name + ".pdf") : path5.join(fdir, fname + ".pdf");
|
|
1982
3848
|
await mergePDFs(files, pdf, doc, signal || void 0);
|
|
1983
|
-
await
|
|
1984
|
-
if (removeIntermediateDocx && existsSync2(fout)) await
|
|
3849
|
+
await fs5.rm(tmpfolder, { recursive: true, force: true });
|
|
3850
|
+
if (removeIntermediateDocx && existsSync2(fout)) await fs5.unlink(fout);
|
|
1985
3851
|
return { fout: pdf };
|
|
1986
3852
|
} catch (err) {
|
|
1987
3853
|
if (signal?.aborted) throw err;
|
|
@@ -1989,7 +3855,7 @@ async function render({
|
|
|
1989
3855
|
}
|
|
1990
3856
|
}
|
|
1991
3857
|
} else {
|
|
1992
|
-
await
|
|
3858
|
+
await fs5.rename(ftmp, fout);
|
|
1993
3859
|
}
|
|
1994
3860
|
loginfo(`[!7] Rendered to ${fout}`);
|
|
1995
3861
|
return { fout, err: limitedRender ? "noWin" : void 0 };
|
|
@@ -2023,8 +3889,8 @@ function killProcessTree(child, wordPid) {
|
|
|
2023
3889
|
}
|
|
2024
3890
|
}
|
|
2025
3891
|
function runDocxMacro(progress, log, logError, assets, cwd, fin, fout, renderPDF, signal) {
|
|
2026
|
-
const script =
|
|
2027
|
-
const templateMacro =
|
|
3892
|
+
const script = path5.join(assets, "run.ps1");
|
|
3893
|
+
const templateMacro = path5.join(assets, "template.dotm");
|
|
2028
3894
|
return new Promise((res, rej) => {
|
|
2029
3895
|
if (signal?.aborted)
|
|
2030
3896
|
return rej(signal.reason ?? new Error("Aborted before execution started"));
|
|
@@ -2100,7 +3966,7 @@ function getPythonExecutable() {
|
|
|
2100
3966
|
return process.env.UNO_PYTHON_PATH || "python3";
|
|
2101
3967
|
}
|
|
2102
3968
|
function runUnoScript(progress, log, logError, assets, cwd, fin, fout, renderPDF, signal) {
|
|
2103
|
-
const script = process.env.MACRO_ENGINE_PATH ||
|
|
3969
|
+
const script = process.env.MACRO_ENGINE_PATH || path5.join(assets, "macro_engine.py");
|
|
2104
3970
|
const pythonExec = getPythonExecutable();
|
|
2105
3971
|
return new Promise((res, rej) => {
|
|
2106
3972
|
if (signal?.aborted) {
|
|
@@ -2164,31 +4030,61 @@ function runUnoScript(progress, log, logError, assets, cwd, fin, fout, renderPDF
|
|
|
2164
4030
|
});
|
|
2165
4031
|
}
|
|
2166
4032
|
async function mergePDFs(files, fout, doc, signal) {
|
|
2167
|
-
const
|
|
4033
|
+
const pdfFiles = files.filter((file) => file.toLowerCase().endsWith(".pdf"));
|
|
4034
|
+
let mainPdfIndex = -1;
|
|
4035
|
+
for (let i = 0; i < pdfFiles.length; i++) {
|
|
4036
|
+
if (pdfFiles[i].toLowerCase().endsWith("-main.pdf"))
|
|
4037
|
+
mainPdfIndex = i;
|
|
4038
|
+
}
|
|
4039
|
+
let mergedPdf;
|
|
4040
|
+
if (mainPdfIndex >= 0) {
|
|
4041
|
+
const bytes = await fs5.readFile(pdfFiles[mainPdfIndex], { signal });
|
|
4042
|
+
mergedPdf = await PDFDocument.load(bytes);
|
|
4043
|
+
signal?.throwIfAborted();
|
|
4044
|
+
let insertionIndex = 0;
|
|
4045
|
+
for (const file of pdfFiles.slice(0, mainPdfIndex)) {
|
|
4046
|
+
const sourceBytes = await fs5.readFile(file, { signal });
|
|
4047
|
+
const pdf = await PDFDocument.load(sourceBytes);
|
|
4048
|
+
signal?.throwIfAborted();
|
|
4049
|
+
const pages = await mergedPdf.copyPages(pdf, pdf.getPageIndices());
|
|
4050
|
+
for (const page of pages)
|
|
4051
|
+
mergedPdf.insertPage(insertionIndex++, page);
|
|
4052
|
+
signal?.throwIfAborted();
|
|
4053
|
+
}
|
|
4054
|
+
for (const file of pdfFiles.slice(mainPdfIndex + 1)) {
|
|
4055
|
+
const sourceBytes = await fs5.readFile(file, { signal });
|
|
4056
|
+
const pdf = await PDFDocument.load(sourceBytes);
|
|
4057
|
+
signal?.throwIfAborted();
|
|
4058
|
+
const pages = await mergedPdf.copyPages(pdf, pdf.getPageIndices());
|
|
4059
|
+
pages.forEach((page) => mergedPdf.addPage(page));
|
|
4060
|
+
signal?.throwIfAborted();
|
|
4061
|
+
}
|
|
4062
|
+
} else {
|
|
4063
|
+
mergedPdf = await PDFDocument.create();
|
|
4064
|
+
for (const file of pdfFiles) {
|
|
4065
|
+
const bytes = await fs5.readFile(file, { signal });
|
|
4066
|
+
const pdf = await PDFDocument.load(bytes);
|
|
4067
|
+
signal?.throwIfAborted();
|
|
4068
|
+
const pages = await mergedPdf.copyPages(pdf, pdf.getPageIndices());
|
|
4069
|
+
pages.forEach((page) => mergedPdf.addPage(page));
|
|
4070
|
+
signal?.throwIfAborted();
|
|
4071
|
+
}
|
|
4072
|
+
}
|
|
2168
4073
|
if (doc?.title) mergedPdf.setTitle(doc.title, { showInWindowTitleBar: true });
|
|
2169
4074
|
if (doc?.author) mergedPdf.setAuthor(doc.author);
|
|
2170
4075
|
if (doc?.ctime) mergedPdf.setCreationDate(doc.ctime);
|
|
2171
4076
|
if (doc?.mtime) mergedPdf.setModificationDate(doc.mtime);
|
|
2172
|
-
for (const file of files) {
|
|
2173
|
-
if (!file.endsWith(".pdf")) continue;
|
|
2174
|
-
const bytes = await fs4.readFile(file, { signal });
|
|
2175
|
-
const pdf = await PDFDocument.load(bytes);
|
|
2176
|
-
signal?.throwIfAborted();
|
|
2177
|
-
const pages = await mergedPdf.copyPages(pdf, pdf.getPageIndices());
|
|
2178
|
-
pages.forEach((page) => mergedPdf.addPage(page));
|
|
2179
|
-
signal?.throwIfAborted();
|
|
2180
|
-
}
|
|
2181
4077
|
const pdfBytes = await mergedPdf.save();
|
|
2182
|
-
await
|
|
4078
|
+
await fs5.writeFile(fout, pdfBytes, { signal });
|
|
2183
4079
|
}
|
|
2184
4080
|
async function updateMetadata(docfile, doc) {
|
|
2185
|
-
const content = await
|
|
4081
|
+
const content = await fs5.readFile(docfile);
|
|
2186
4082
|
const zip = new PizZip(content);
|
|
2187
4083
|
function replaceTag(xml, tag, value) {
|
|
2188
4084
|
const regex = new RegExp(`(<${tag}\\b[^>]*>)(.*?)(</${tag}>)`, "gs");
|
|
2189
4085
|
return xml.replace(
|
|
2190
4086
|
regex,
|
|
2191
|
-
(m, otag, old, ctag) => `${otag}${
|
|
4087
|
+
(m, otag, old, ctag) => `${otag}${escapeXml2(value)}${ctag}`
|
|
2192
4088
|
);
|
|
2193
4089
|
}
|
|
2194
4090
|
let coreXml = zip.file("docProps/core.xml").asText();
|
|
@@ -2202,8 +4098,8 @@ async function updateMetadata(docfile, doc) {
|
|
|
2202
4098
|
appXml = replaceTag(appXml, "TotalTime", `${doc.etime || randomInt(30, 120)}`);
|
|
2203
4099
|
zip.file("docProps/app.xml", appXml);
|
|
2204
4100
|
const buffer = zip.generate({ type: "nodebuffer" });
|
|
2205
|
-
await
|
|
2206
|
-
function
|
|
4101
|
+
await fs5.writeFile(docfile, buffer);
|
|
4102
|
+
function escapeXml2(unsafe) {
|
|
2207
4103
|
return unsafe.replaceAll(/[<>&"']/g, (ch) => {
|
|
2208
4104
|
switch (ch) {
|
|
2209
4105
|
case "<":
|
|
@@ -2274,11 +4170,26 @@ async function renderMarkdown(config) {
|
|
|
2274
4170
|
assert(typeof config.useLibreOffice == "boolean" || typeof config.useLibreOffice == "undefined", "useLibreOffice must be a boolean or undefined.");
|
|
2275
4171
|
assert(typeof config.checkFilesIsInsidePath == "string" || config.checkFilesIsInsidePath === false || typeof config.checkFilesIsInsidePath == "undefined", "checkFilesIsInsidePath must be a string, false or undefined.");
|
|
2276
4172
|
assert(typeof config.progress == "function" || typeof config.progress == "undefined", "Progress callback must be a function or undefined.");
|
|
4173
|
+
assert(typeof config.variables == "object" && config.variables !== null && !Array.isArray(config.variables) || typeof config.variables == "undefined", "variables must be an object or undefined.");
|
|
4174
|
+
assert(typeof config.templateOptions == "object" && config.templateOptions !== null && !Array.isArray(config.templateOptions) || typeof config.templateOptions == "undefined", "templateOptions must be an object or undefined.");
|
|
4175
|
+
if (config.templateOptions) {
|
|
4176
|
+
const options = config.templateOptions;
|
|
4177
|
+
const booleanProps = ["allowLoops", "allowIncludes"];
|
|
4178
|
+
const limitProps = ["maxLoopIterations", "maxTotalIterations", "maxDepth", "maxIncludeDepth", "maxIncludes", "maxFileLength", "maxTotalInputLength", "maxGeneratedLength"];
|
|
4179
|
+
const extraProps = getExtraProperties(options, [...booleanProps, ...limitProps]);
|
|
4180
|
+
assert(extraProps.length == 0, `Found unknown properties in config.templateOptions: ${extraProps.join(", ")}`);
|
|
4181
|
+
booleanProps.forEach(
|
|
4182
|
+
(p) => assert(options[p] === void 0 || typeof options[p] == "boolean", `config.templateOptions.${p} must be a boolean or undefined.`)
|
|
4183
|
+
);
|
|
4184
|
+
limitProps.forEach(
|
|
4185
|
+
(p) => assert(options[p] === void 0 || Number.isSafeInteger(options[p]) && options[p] >= 0, `config.templateOptions.${p} must be a non-negative safe integer or undefined.`)
|
|
4186
|
+
);
|
|
4187
|
+
}
|
|
2277
4188
|
assert(config.abortSignal instanceof AbortSignal || typeof config.abortSignal == "undefined", "abortSignal must be an AbortSignal or undefined.");
|
|
2278
4189
|
{
|
|
2279
4190
|
const extraProps = getExtraProperties(
|
|
2280
4191
|
config,
|
|
2281
|
-
["input", "output", "format", "keepIntermediateDocx", "disableMacros", "useLibreOffice", "progress", "logger", "abortSignal", "checkFilesIsInsidePath"]
|
|
4192
|
+
["input", "output", "format", "keepIntermediateDocx", "disableMacros", "useLibreOffice", "progress", "logger", "abortSignal", "checkFilesIsInsidePath", "variables", "templateOptions"]
|
|
2282
4193
|
);
|
|
2283
4194
|
assert(extraProps.length == 0, `Found unknown properties in config: ${extraProps.join(", ")}`);
|
|
2284
4195
|
}
|
|
@@ -2292,20 +4203,25 @@ async function renderMarkdown(config) {
|
|
|
2292
4203
|
(p) => assert(typeof logger[p] == "function", `config.logger.${p} must be a function.`)
|
|
2293
4204
|
);
|
|
2294
4205
|
}
|
|
2295
|
-
const
|
|
4206
|
+
const outputExtension = config.output && path6.extname(config.output).toLowerCase();
|
|
4207
|
+
const renderPDF = config.format === "pdf" || !config.format && outputExtension === ".pdf";
|
|
2296
4208
|
const removeIntermediateDocx = !config.keepIntermediateDocx;
|
|
2297
4209
|
const disableMacros = !!config.disableMacros;
|
|
2298
4210
|
const useLibreOffice = !!config.useLibreOffice;
|
|
2299
4211
|
const checkFilesIsInsidePath = config.checkFilesIsInsidePath === void 0 ? "" : config.checkFilesIsInsidePath;
|
|
2300
4212
|
assert(!(disableMacros && renderPDF), "Macros must be enabled to render PDF output.");
|
|
2301
|
-
|
|
4213
|
+
if (outputExtension && outputExtension !== ".docx" && outputExtension !== ".pdf")
|
|
4214
|
+
throw new TypeError("Output file extension must be either '.docx' or '.pdf'.");
|
|
4215
|
+
if (outputExtension && outputExtension !== (renderPDF ? ".pdf" : ".docx"))
|
|
4216
|
+
throw new TypeError(`Output file extension must match the selected '${renderPDF ? "pdf" : "docx"}' format.`);
|
|
4217
|
+
const pin = path6.parse(path6.resolve(config.input));
|
|
2302
4218
|
const fname = pin.name.endsWith(".g") ? pin.name.slice(0, -2) : pin.name;
|
|
2303
4219
|
let resolvedOutput = config.output;
|
|
2304
4220
|
if (resolvedOutput) {
|
|
2305
|
-
const isDir = resolvedOutput.endsWith("/") || resolvedOutput.endsWith("\\") || !
|
|
4221
|
+
const isDir = resolvedOutput.endsWith("/") || resolvedOutput.endsWith("\\") || !path6.extname(resolvedOutput);
|
|
2306
4222
|
if (isDir) {
|
|
2307
4223
|
const ext = renderPDF ? ".pdf" : ".docx";
|
|
2308
|
-
resolvedOutput =
|
|
4224
|
+
resolvedOutput = path6.join(resolvedOutput, fname + ext);
|
|
2309
4225
|
}
|
|
2310
4226
|
}
|
|
2311
4227
|
const warnings = [];
|
|
@@ -2330,6 +4246,8 @@ async function renderMarkdown(config) {
|
|
|
2330
4246
|
disableMacros,
|
|
2331
4247
|
useLibreOffice,
|
|
2332
4248
|
checkFilesIsInsidePath,
|
|
4249
|
+
variables: config.variables,
|
|
4250
|
+
templateOptions: config.templateOptions,
|
|
2333
4251
|
loginfo: logger?.info,
|
|
2334
4252
|
logwarn: (msg) => {
|
|
2335
4253
|
logger?.warn(msg);
|
|
@@ -2345,7 +4263,6 @@ async function renderMarkdown(config) {
|
|
|
2345
4263
|
},
|
|
2346
4264
|
signal: config.abortSignal
|
|
2347
4265
|
});
|
|
2348
|
-
config.progress?.(100, "Done!");
|
|
2349
4266
|
if (useLibreOffice) {
|
|
2350
4267
|
if (err == "noPS") throwMDE2("noPS", `Failed to run the LibreOffice binary (soffice). Ensure LibreOffice is installed and added to your system's PATH environment variable.`);
|
|
2351
4268
|
if (err == "inPS") throwMDE2("inPS", `An unexpected error occurred executing the LibreOffice conversion command. Verify that LibreOffice is properly installed, up to date, and has permission to access the target files.`);
|
|
@@ -2356,10 +4273,11 @@ async function renderMarkdown(config) {
|
|
|
2356
4273
|
if (err == "vba") throwMDE2("vba", `Microsoft Word VBA Macro execution failed: ${errS}`);
|
|
2357
4274
|
if (err == "pdf") throwMDE2("pdf", `Failed to export document to PDF.`);
|
|
2358
4275
|
if (err == "noWin") throwMDE2("noWin", `Platform restriction: Extended formatting macros and PDF rendering are only supported natively on Windows systems.`);
|
|
4276
|
+
config.progress?.(100, "Done!");
|
|
2359
4277
|
let intermediateDocxPath;
|
|
2360
4278
|
if (renderPDF && !removeIntermediateDocx) {
|
|
2361
|
-
const pout = resolvedOutput ?
|
|
2362
|
-
intermediateDocxPath = pout ?
|
|
4279
|
+
const pout = resolvedOutput ? path6.parse(path6.resolve(resolvedOutput)) : void 0;
|
|
4280
|
+
intermediateDocxPath = pout ? path6.join(pout.dir, pout.name + ".docx") : path6.join(pin.dir, fname + ".docx");
|
|
2363
4281
|
}
|
|
2364
4282
|
return {
|
|
2365
4283
|
filePath: fout,
|
|
@@ -2417,10 +4335,7 @@ var MDRenderError = class _MDRenderError extends Error {
|
|
|
2417
4335
|
}
|
|
2418
4336
|
};
|
|
2419
4337
|
function getAssetsDir() {
|
|
2420
|
-
|
|
2421
|
-
return path5.resolve(__dirname, "../assets");
|
|
2422
|
-
const metaUrl = new Function("return import.meta.url")();
|
|
2423
|
-
return path5.resolve(path5.dirname(fileURLToPath(metaUrl)), "../assets");
|
|
4338
|
+
return path6.resolve(__MD2GOST_DIRNAME__, "../assets");
|
|
2424
4339
|
}
|
|
2425
4340
|
function getExtraProperties(configObj, allowedConfigKeys) {
|
|
2426
4341
|
return Object.keys(configObj).filter(
|
|
@@ -2429,6 +4344,7 @@ function getExtraProperties(configObj, allowedConfigKeys) {
|
|
|
2429
4344
|
}
|
|
2430
4345
|
export {
|
|
2431
4346
|
MDRenderError,
|
|
4347
|
+
UserInputError,
|
|
2432
4348
|
renderMarkdown as default
|
|
2433
4349
|
};
|
|
2434
4350
|
//# sourceMappingURL=index.mjs.map
|