md2gost 1.5.0 → 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 +49 -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 +2332 -415
- package/dist/index.js.map +3 -3
- package/dist/index.mjs +2285 -366
- 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;
|
|
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();
|
|
626
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
|
})
|
|
@@ -939,15 +2505,18 @@ async function serializeDocx(doc, fout, workdir, assets, checkFilesIsInsidePath)
|
|
|
939
2505
|
] : [],
|
|
940
2506
|
new Table({
|
|
941
2507
|
// width: { type: "pct", size: 100 },
|
|
942
|
-
width: {
|
|
2508
|
+
width: {
|
|
2509
|
+
type: "dxa",
|
|
2510
|
+
size: convertMillimetersToTwip(section.orientation === "landscape" ? 255.8 : 168.8)
|
|
2511
|
+
},
|
|
943
2512
|
rows: node.rows.map((row, rowI) => new TableRow({
|
|
944
|
-
tableHeader: rowI == 0,
|
|
2513
|
+
tableHeader: node.header !== false && rowI == 0,
|
|
945
2514
|
cantSplit: true,
|
|
946
2515
|
children: row.map((item, colI) => new TableCell({
|
|
947
2516
|
children: item.type != "text" ? renderNodeL2(item) : [
|
|
948
2517
|
new Paragraph({
|
|
949
|
-
children: renderText(item.text, node.normalFontSize ? void 0 : doc.table.text.size),
|
|
950
|
-
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",
|
|
951
2520
|
indent: { firstLine: 0 },
|
|
952
2521
|
spacing: { after: 0, line: 240 * doc.table.text.line_spacing }
|
|
953
2522
|
})
|
|
@@ -965,13 +2534,13 @@ async function serializeDocx(doc, fout, workdir, assets, checkFilesIsInsidePath)
|
|
|
965
2534
|
new Paragraph({ children: [new PageBreak()] })
|
|
966
2535
|
];
|
|
967
2536
|
case "image":
|
|
968
|
-
const type = node.src.split(".").at(-1) || "";
|
|
2537
|
+
const type = (node.src.split(".").at(-1) || "").toLowerCase();
|
|
969
2538
|
if (!["jpg", "png", "gif", "bmp", "svg"].includes(type))
|
|
970
2539
|
throw new UserInputError(`Unsupported image format: "${type}", file: ${node.src}`);
|
|
971
2540
|
const img_path = getPath(node.src);
|
|
972
|
-
if (!
|
|
2541
|
+
if (!fs4.existsSync(img_path))
|
|
973
2542
|
throw new UserInputError(`File not exist: ${node.src}`);
|
|
974
|
-
const data =
|
|
2543
|
+
const data = fs4.readFileSync(img_path);
|
|
975
2544
|
const dimensions = imageSize(data);
|
|
976
2545
|
const [MaxW, MaxH] = section.orientation == "landscape" ? [950, 600] : [600, 900];
|
|
977
2546
|
let [width, height] = [dimensions.width, dimensions.height];
|
|
@@ -1003,7 +2572,7 @@ async function serializeDocx(doc, fout, workdir, assets, checkFilesIsInsidePath)
|
|
|
1003
2572
|
}),
|
|
1004
2573
|
...node.text ? [
|
|
1005
2574
|
new Paragraph({
|
|
1006
|
-
children: renderText(node.text, doc.img.text.size),
|
|
2575
|
+
children: renderText(node.text, node.sourceLine, doc.img.text.size),
|
|
1007
2576
|
alignment: "center",
|
|
1008
2577
|
indent: { firstLine: 0 },
|
|
1009
2578
|
spacing: {
|
|
@@ -1018,7 +2587,7 @@ async function serializeDocx(doc, fout, workdir, assets, checkFilesIsInsidePath)
|
|
|
1018
2587
|
return [
|
|
1019
2588
|
...node.title ? [
|
|
1020
2589
|
new Paragraph({
|
|
1021
|
-
children: renderText(node.title, doc.code.title.size),
|
|
2590
|
+
children: renderText(node.title, node.sourceLine, doc.code.title.size),
|
|
1022
2591
|
style: STYLE_code_title
|
|
1023
2592
|
})
|
|
1024
2593
|
] : prevNode?.type == "code" ? [
|
|
@@ -1030,11 +2599,44 @@ async function serializeDocx(doc, fout, workdir, assets, checkFilesIsInsidePath)
|
|
|
1030
2599
|
...i == 0 && !node.title && doc.code.spacing.before && prevNode?.type != "code" ? { spacing: { before: doc.code.spacing.before * 20 } } : {}
|
|
1031
2600
|
}))
|
|
1032
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
|
+
];
|
|
1033
2635
|
case "externalDoc":
|
|
1034
2636
|
const doc_path = getPath(node.path);
|
|
1035
|
-
if (!
|
|
2637
|
+
if (!fs4.existsSync(doc_path))
|
|
1036
2638
|
throw new UserInputError(`File not exist: ${node.path}`);
|
|
1037
|
-
const ext =
|
|
2639
|
+
const ext = path4.extname(doc_path);
|
|
1038
2640
|
if (ext != ".docx" && ext != ".pdf")
|
|
1039
2641
|
throw new UserInputError(`File not .docx or .pdf: ${node.path}`);
|
|
1040
2642
|
return new Paragraph({
|
|
@@ -1042,6 +2644,39 @@ async function serializeDocx(doc, fout, workdir, assets, checkFilesIsInsidePath)
|
|
|
1042
2644
|
indent: { firstLine: 0 },
|
|
1043
2645
|
alignment: "left"
|
|
1044
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
|
+
];
|
|
1045
2680
|
case "sectionBreak":
|
|
1046
2681
|
return [];
|
|
1047
2682
|
default:
|
|
@@ -1049,12 +2684,20 @@ async function serializeDocx(doc, fout, workdir, assets, checkFilesIsInsidePath)
|
|
|
1049
2684
|
throw new Error("switch default");
|
|
1050
2685
|
}
|
|
1051
2686
|
}, renderNodeL2 = function(node, prevChild, prevNode) {
|
|
1052
|
-
|
|
1053
|
-
|
|
1054
|
-
|
|
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
|
+
}
|
|
1055
2696
|
};
|
|
1056
|
-
var renderNode = renderNode2, renderNodeL = renderNodeL2;
|
|
2697
|
+
var renderHeaderFooter = renderHeaderFooter2, renderHeaderFooterTable = renderHeaderFooterTable2, renderNode = renderNode2, renderNodeL = renderNodeL2;
|
|
1057
2698
|
const children = [];
|
|
2699
|
+
const header = renderHeaderFooter2(section.header, false);
|
|
2700
|
+
const footer = renderHeaderFooter2(section.footer, true);
|
|
1058
2701
|
sections.push({
|
|
1059
2702
|
children,
|
|
1060
2703
|
properties: {
|
|
@@ -1082,20 +2725,8 @@ async function serializeDocx(doc, fout, workdir, assets, checkFilesIsInsidePath)
|
|
|
1082
2725
|
}
|
|
1083
2726
|
}
|
|
1084
2727
|
},
|
|
1085
|
-
|
|
1086
|
-
|
|
1087
|
-
children: [
|
|
1088
|
-
new Paragraph({
|
|
1089
|
-
alignment: AlignmentType.CENTER,
|
|
1090
|
-
children: [new TextRun({
|
|
1091
|
-
children: section.displayPageNum ? [PageNumber.CURRENT] : []
|
|
1092
|
-
})],
|
|
1093
|
-
indent: { firstLine: 0 },
|
|
1094
|
-
spacing: { line: 240 }
|
|
1095
|
-
})
|
|
1096
|
-
]
|
|
1097
|
-
})
|
|
1098
|
-
}
|
|
2728
|
+
...header ? { headers: { default: header } } : {},
|
|
2729
|
+
...footer ? { footers: { default: footer } } : {}
|
|
1099
2730
|
});
|
|
1100
2731
|
for (let i = 0; i < section.nodes.length; i++)
|
|
1101
2732
|
children.push(...renderNodeL2(section.nodes[i], children.at(-1), section.nodes[i - 1]));
|
|
@@ -1130,8 +2761,8 @@ async function serializeDocx(doc, fout, workdir, assets, checkFilesIsInsidePath)
|
|
|
1130
2761
|
})
|
|
1131
2762
|
}
|
|
1132
2763
|
]);
|
|
1133
|
-
|
|
1134
|
-
function renderText(text, size) {
|
|
2764
|
+
fs4.writeFileSync(fout, buffer);
|
|
2765
|
+
function renderText(text, sourceLine, size) {
|
|
1135
2766
|
function renderRune(rune, link = false) {
|
|
1136
2767
|
if (rune.anchor) return new Bookmark({
|
|
1137
2768
|
id: rune.anchor,
|
|
@@ -1141,8 +2772,12 @@ async function serializeDocx(doc, fout, workdir, assets, checkFilesIsInsidePath)
|
|
|
1141
2772
|
const children2 = [renderRune({ ...rune, link: void 0 }, true)];
|
|
1142
2773
|
return rune.link.startsWith("#") ? new InternalHyperlink({ children: children2, anchor: rune.link.slice(1) }) : new ExternalHyperlink({ children: children2, link: rune.link });
|
|
1143
2774
|
}
|
|
2775
|
+
if (rune.type == "math") return renderMath(rune.text, false, sourceLine);
|
|
1144
2776
|
let children = null;
|
|
1145
|
-
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") {
|
|
1146
2781
|
rune.type = "text";
|
|
1147
2782
|
children = doc.totalPagesOffset == 0 ? [PageNumber.TOTAL_PAGES] : [
|
|
1148
2783
|
// Complex Formula Field: { = { NUMPAGES } + 1 }
|
|
@@ -1226,7 +2861,36 @@ async function serializeDocx(doc, fout, workdir, assets, checkFilesIsInsidePath)
|
|
|
1226
2861
|
}
|
|
1227
2862
|
}
|
|
1228
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
|
+
}
|
|
1229
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
|
+
};
|
|
1230
2894
|
function addListItemLevel(levels, level, startIndex, itemCount, ordered, alternativeStyle, mark) {
|
|
1231
2895
|
if (levels.find((v) => v.level == level)) return;
|
|
1232
2896
|
levels.sort((a, b) => a.level - b.level);
|
|
@@ -1265,7 +2929,7 @@ function addListItemLevel(levels, level, startIndex, itemCount, ordered, alterna
|
|
|
1265
2929
|
}
|
|
1266
2930
|
});
|
|
1267
2931
|
}
|
|
1268
|
-
var RawXml = class extends
|
|
2932
|
+
var RawXml = class extends XmlComponent2 {
|
|
1269
2933
|
constructor(tag, attributes = {}, text = null) {
|
|
1270
2934
|
super(tag);
|
|
1271
2935
|
if (Object.keys(attributes).length > 0)
|
|
@@ -1275,60 +2939,158 @@ var RawXml = class extends XmlComponent {
|
|
|
1275
2939
|
}
|
|
1276
2940
|
};
|
|
1277
2941
|
function renderCodeHighlighting(code, lang) {
|
|
1278
|
-
const
|
|
2942
|
+
const language = lang.trim().toLowerCase();
|
|
2943
|
+
const grammar = Prism.languages[language];
|
|
1279
2944
|
if (!grammar) return null;
|
|
1280
2945
|
const themeColors = {
|
|
1281
2946
|
// shared
|
|
1282
2947
|
"plain": "000000",
|
|
1283
2948
|
"comment": "008000",
|
|
2949
|
+
"hashbang": "008000",
|
|
2950
|
+
"shebang": "008000",
|
|
1284
2951
|
"punctuation": "000000",
|
|
1285
2952
|
"operator": "000000",
|
|
2953
|
+
"script": "000000",
|
|
2954
|
+
"script-punctuation": "000000",
|
|
2955
|
+
"expression": "000000",
|
|
2956
|
+
"interpolation": "000000",
|
|
1286
2957
|
// keywords & control flow
|
|
1287
2958
|
"keyword": "0000ff",
|
|
2959
|
+
"control-flow": "af00db",
|
|
1288
2960
|
"boolean": "0000ff",
|
|
1289
|
-
"
|
|
2961
|
+
"null": "0000ff",
|
|
2962
|
+
"builtin": "267f99",
|
|
1290
2963
|
"important": "0000ff",
|
|
2964
|
+
// "instruction": "0000ff",
|
|
2965
|
+
// "directive": "0000ff",
|
|
2966
|
+
"directive-hash": "0000ff",
|
|
2967
|
+
"macro": "0000ff",
|
|
2968
|
+
"macro-name": "0000ff",
|
|
2969
|
+
"rule": "0000ff",
|
|
1291
2970
|
// literals
|
|
1292
|
-
"string":
|
|
1293
|
-
"
|
|
1294
|
-
"
|
|
1295
|
-
"
|
|
2971
|
+
"string": "a31515",
|
|
2972
|
+
"template-string": "a31515",
|
|
2973
|
+
"template-punctuation": "a31515",
|
|
2974
|
+
"string-literal": "a31515",
|
|
1296
2975
|
"char": "a31515",
|
|
2976
|
+
"number": "098658",
|
|
2977
|
+
"datetime": "098658",
|
|
2978
|
+
"constant": "0070c1",
|
|
2979
|
+
"symbol": "0070c1",
|
|
2980
|
+
"scalar": "0000ff",
|
|
2981
|
+
"conversion-option": "0000ff",
|
|
1297
2982
|
// identifiers
|
|
1298
|
-
"class-name":
|
|
1299
|
-
"
|
|
1300
|
-
"
|
|
1301
|
-
"
|
|
1302
|
-
"
|
|
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",
|
|
1303
3004
|
// markup / HTML / XML
|
|
1304
|
-
"tag":
|
|
3005
|
+
"tag": "800000",
|
|
1305
3006
|
"selector": "800000",
|
|
1306
3007
|
"attr-name": "e50000",
|
|
1307
|
-
"attr-value": "
|
|
3008
|
+
"attr-value": "0000ff",
|
|
1308
3009
|
"entity": "800000",
|
|
1309
3010
|
"doctype": "808080",
|
|
3011
|
+
"doctype-tag": "808080",
|
|
3012
|
+
"name": "808080",
|
|
1310
3013
|
"cdata": "808080",
|
|
1311
3014
|
"prolog": "808080",
|
|
3015
|
+
"delimiter": language === "php" ? "800000" : "a31515",
|
|
1312
3016
|
// CSS specific
|
|
1313
3017
|
"atrule": "0000ff",
|
|
1314
3018
|
"url": "0451a5",
|
|
1315
3019
|
// regex
|
|
1316
|
-
"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"
|
|
1317
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
|
+
]);
|
|
1318
3078
|
const tokens = Prism.tokenize(code, grammar);
|
|
1319
3079
|
return tokensToParagraphs(tokens);
|
|
1320
3080
|
function tokensToParagraphs(tokens2) {
|
|
1321
3081
|
const paragraphs = [];
|
|
1322
3082
|
let currentRuns = [];
|
|
1323
|
-
|
|
3083
|
+
const plainStyle = { color: themeColors.plain, bold: false, italics: false, strike: false };
|
|
3084
|
+
function addText(text, style) {
|
|
1324
3085
|
const lines = text.split("\n");
|
|
1325
3086
|
lines.forEach((line, index) => {
|
|
1326
3087
|
currentRuns.push(
|
|
1327
3088
|
new TextRun({
|
|
1328
3089
|
text: line,
|
|
1329
|
-
color:
|
|
1330
|
-
bold:
|
|
1331
|
-
italics:
|
|
3090
|
+
color: style.color,
|
|
3091
|
+
bold: style.bold,
|
|
3092
|
+
italics: style.italics,
|
|
3093
|
+
strike: style.strike
|
|
1332
3094
|
})
|
|
1333
3095
|
);
|
|
1334
3096
|
if (index < lines.length - 1) {
|
|
@@ -1337,15 +3099,28 @@ function renderCodeHighlighting(code, lang) {
|
|
|
1337
3099
|
}
|
|
1338
3100
|
});
|
|
1339
3101
|
}
|
|
1340
|
-
function process2(token,
|
|
3102
|
+
function process2(token, parentStyle = plainStyle) {
|
|
1341
3103
|
if (typeof token === "string")
|
|
1342
|
-
addText(token,
|
|
1343
|
-
else
|
|
1344
|
-
token.
|
|
1345
|
-
|
|
1346
|
-
|
|
1347
|
-
|
|
1348
|
-
|
|
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
|
+
}
|
|
1349
3124
|
}
|
|
1350
3125
|
tokens2.forEach((t) => process2(t));
|
|
1351
3126
|
if (currentRuns.length > 0) {
|
|
@@ -1354,6 +3129,37 @@ function renderCodeHighlighting(code, lang) {
|
|
|
1354
3129
|
return paragraphs;
|
|
1355
3130
|
}
|
|
1356
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
|
+
}
|
|
1357
3163
|
function genXml_app({ totalTime }) {
|
|
1358
3164
|
return `<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
|
|
1359
3165
|
<Properties xmlns="http://schemas.openxmlformats.org/officeDocument/2006/extended-properties" xmlns:vt="http://schemas.openxmlformats.org/officeDocument/2006/docPropsVTypes">
|
|
@@ -1361,20 +3167,29 @@ function genXml_app({ totalTime }) {
|
|
|
1361
3167
|
</Properties>
|
|
1362
3168
|
`;
|
|
1363
3169
|
}
|
|
3170
|
+
function escapeXml(value) {
|
|
3171
|
+
return value.replaceAll(/[<>&"']/g, (ch) => ({
|
|
3172
|
+
"<": "<",
|
|
3173
|
+
">": ">",
|
|
3174
|
+
"&": "&",
|
|
3175
|
+
'"': """,
|
|
3176
|
+
"'": "'"
|
|
3177
|
+
})[ch]);
|
|
3178
|
+
}
|
|
1364
3179
|
function genXml_core({ title, creator, createdAt, modifiedAt }) {
|
|
1365
3180
|
if (!createdAt) createdAt = /* @__PURE__ */ new Date();
|
|
1366
3181
|
if (!modifiedAt) modifiedAt = /* @__PURE__ */ new Date();
|
|
1367
3182
|
return `<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
|
|
1368
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">
|
|
1369
|
-
<dc:title>${title || "Document"}</dc:title>
|
|
1370
|
-
<dc:creator>${creator || "Student"}</dc:creator>
|
|
1371
|
-
<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>
|
|
1372
3187
|
<dcterms:created xsi:type="dcterms:W3CDTF">${createdAt.toISOString()}</dcterms:created>
|
|
1373
3188
|
<dcterms:modified xsi:type="dcterms:W3CDTF">${modifiedAt.toISOString()}</dcterms:modified>
|
|
1374
3189
|
</cp:coreProperties>`;
|
|
1375
3190
|
}
|
|
1376
3191
|
function genXml_style(assets, doc) {
|
|
1377
|
-
let xml =
|
|
3192
|
+
let xml = fs4.readFileSync(path4.join(assets, "styles.xml"), { encoding: "utf8" });
|
|
1378
3193
|
xml = editStyle(`<w:style w:type="paragraph" w:default="1" w:styleId="a2">`, (xml2) => {
|
|
1379
3194
|
xml2 = replaceTag(xml2, `<w:spacing w:line="${doc.text.line_spacing * 240}" w:lineRule="auto" w:after="${doc.text.spacing.after * 20}"/>`);
|
|
1380
3195
|
xml2 = replaceTag(xml2, `<w:ind w:firstLine="${convertMillimetersToTwip(doc.text.indent * 10)}"/>`);
|
|
@@ -1399,7 +3214,33 @@ function genXml_style(assets, doc) {
|
|
|
1399
3214
|
xml2 = addrPrStyle(xml2, doc.table.title.style == "bold" ? "<w:b/>" : "<w:i/>");
|
|
1400
3215
|
return xml2;
|
|
1401
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
|
+
}
|
|
1402
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
|
+
}
|
|
1403
3244
|
function replaceTag(xml2, tag) {
|
|
1404
3245
|
const tagName = /<([^\s]+)\b/.exec(tag)?.[1];
|
|
1405
3246
|
if (!tagName) throw err();
|
|
@@ -1439,13 +3280,14 @@ function genXml_style(assets, doc) {
|
|
|
1439
3280
|
|
|
1440
3281
|
// src/enricher.ts
|
|
1441
3282
|
function enrichDoc(doc, logwarn = console.warn) {
|
|
3283
|
+
const warnAt = (line, message) => logwarn(`Line ${doc.mapSourceLine(line)}: ${message}`);
|
|
1442
3284
|
for (let i = 0; i < doc.nodes.length; i++) {
|
|
1443
3285
|
const node = doc.nodes[i];
|
|
1444
|
-
if (node.type == "title" && node.text.toUpperCase().startsWith("\u0422\u0418\u0422\u0423\u041B\u042C\u041D\u0418\u041A")) {
|
|
1445
|
-
doc.nodes.splice(i, 1, { type: "text", text: node.text.toUpperCase() });
|
|
1446
|
-
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 });
|
|
1447
3289
|
i++;
|
|
1448
|
-
} 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") {
|
|
1449
3291
|
node.level = 0;
|
|
1450
3292
|
node.text = node.text.toUpperCase();
|
|
1451
3293
|
const nextNode = doc.nodes[i + 1];
|
|
@@ -1453,81 +3295,83 @@ function enrichDoc(doc, logwarn = console.warn) {
|
|
|
1453
3295
|
nextNode.noIndent = true;
|
|
1454
3296
|
nextNode.text = trimEnd(nextNode.text, ".").toUpperCase();
|
|
1455
3297
|
}
|
|
1456
|
-
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 });
|
|
1457
3299
|
while (i + 1 < doc.nodes.length && doc.nodes[i + 1]?.type != "title") i++;
|
|
1458
|
-
doc.nodes.splice(i + 1, 0, { type: "pageBreak" });
|
|
1459
|
-
} 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") {
|
|
1460
3302
|
node.level = 0;
|
|
1461
3303
|
node.text = node.text.toUpperCase();
|
|
1462
|
-
doc.nodes.splice(i + 1, 0, { type: "tableOfContents" });
|
|
1463
|
-
} 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") {
|
|
1464
3306
|
node.center = true;
|
|
1465
3307
|
node.text = node.text.toUpperCase();
|
|
1466
|
-
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");
|
|
1467
3309
|
const list = doc.nodes[i + 1];
|
|
1468
3310
|
if (list?.type != "list") {
|
|
1469
|
-
warn();
|
|
3311
|
+
warn(node.sourceLine);
|
|
1470
3312
|
continue;
|
|
1471
3313
|
}
|
|
1472
|
-
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")];
|
|
1473
3315
|
const align = ["l", "l"];
|
|
1474
3316
|
for (const item of list.items) {
|
|
1475
3317
|
if (item.type != "listItem") {
|
|
1476
|
-
warn();
|
|
3318
|
+
warn(item.sourceLine);
|
|
1477
3319
|
continue;
|
|
1478
3320
|
}
|
|
1479
3321
|
const i2 = item.text.indexOf(":");
|
|
1480
3322
|
if (i2 < 0) {
|
|
1481
|
-
warn();
|
|
3323
|
+
warn(item.sourceLine);
|
|
1482
3324
|
continue;
|
|
1483
3325
|
}
|
|
1484
|
-
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()));
|
|
1485
3327
|
}
|
|
1486
|
-
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." });
|
|
1487
|
-
doc.nodes.splice(i + 2, 0, { type: "table", align, rows, normalFontSize: true, tags: ["definitions_table"] });
|
|
1488
|
-
doc.nodes.splice(i + 3, 0, { type: "pageBreak" });
|
|
1489
|
-
} 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") {
|
|
1490
3332
|
node.center = true;
|
|
1491
3333
|
node.text = node.text.toUpperCase();
|
|
1492
|
-
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");
|
|
1493
3335
|
const list = doc.nodes[i + 1];
|
|
1494
3336
|
if (list?.type != "list") {
|
|
1495
|
-
warn();
|
|
3337
|
+
warn(node.sourceLine);
|
|
1496
3338
|
continue;
|
|
1497
3339
|
}
|
|
1498
3340
|
const items = [];
|
|
1499
3341
|
for (const item of list.items) {
|
|
1500
3342
|
if (item.type != "listItem") {
|
|
1501
|
-
warn();
|
|
3343
|
+
warn(item.sourceLine);
|
|
1502
3344
|
continue;
|
|
1503
3345
|
}
|
|
1504
|
-
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 });
|
|
1505
3347
|
}
|
|
1506
|
-
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." });
|
|
1507
|
-
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 });
|
|
1508
3350
|
doc.nodes.splice(i + 2, 0, ...items);
|
|
1509
|
-
} 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") {
|
|
1510
3352
|
node.center = true;
|
|
1511
3353
|
node.text = node.text.toUpperCase();
|
|
1512
3354
|
for (let j = i + 1; j < doc.nodes.length; j++) {
|
|
1513
3355
|
const node2 = doc.nodes[j];
|
|
1514
3356
|
if (node2?.type == "title") {
|
|
1515
|
-
doc.nodes.splice(j, 0, { type: "pageBreak" });
|
|
3357
|
+
doc.nodes.splice(j, 0, { type: "pageBreak", sourceLine: -1 });
|
|
1516
3358
|
break;
|
|
1517
3359
|
}
|
|
1518
3360
|
}
|
|
1519
|
-
} 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") {
|
|
1520
3362
|
node.center = true;
|
|
1521
3363
|
node.text = node.text.toUpperCase();
|
|
1522
|
-
doc.nodes.splice(i, 0, { type: "pageBreak" });
|
|
3364
|
+
doc.nodes.splice(i, 0, { type: "pageBreak", sourceLine: node.sourceLine });
|
|
1523
3365
|
i++;
|
|
1524
|
-
} 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") {
|
|
1525
3367
|
node.center = true;
|
|
1526
3368
|
node.text = node.text.toUpperCase();
|
|
1527
|
-
doc.nodes.splice(i++, 0, { type: "pageBreak" });
|
|
3369
|
+
doc.nodes.splice(i++, 0, { type: "pageBreak", sourceLine: node.sourceLine });
|
|
1528
3370
|
const nextNode = doc.nodes[i + 1];
|
|
1529
3371
|
if (nextNode?.type == "list") {
|
|
1530
3372
|
nextNode.alternativeStyle = true;
|
|
3373
|
+
nextNode.tags = nextNode.tags || [];
|
|
3374
|
+
nextNode.tags.push("sources");
|
|
1531
3375
|
i++;
|
|
1532
3376
|
continue;
|
|
1533
3377
|
}
|
|
@@ -1536,35 +3380,38 @@ function enrichDoc(doc, logwarn = console.warn) {
|
|
|
1536
3380
|
mark: ".",
|
|
1537
3381
|
startIndex: 1,
|
|
1538
3382
|
alternativeStyle: true,
|
|
3383
|
+
tags: ["sources"],
|
|
1539
3384
|
ordered: true,
|
|
1540
|
-
items: []
|
|
3385
|
+
items: [],
|
|
3386
|
+
sourceLine: node.sourceLine + 1
|
|
1541
3387
|
};
|
|
1542
3388
|
const startI = i + 1;
|
|
1543
3389
|
while (true) {
|
|
1544
3390
|
const textNode = doc.nodes[i + 1];
|
|
1545
3391
|
if (textNode?.type != "text") break;
|
|
1546
3392
|
const items = textNode.text.split(/\n\s*(?=\[[a-zA-Zа-яА-ЯёЁ_\d]+\])/);
|
|
3393
|
+
let sourceLine = textNode.sourceLine;
|
|
1547
3394
|
for (const item of items)
|
|
1548
|
-
list.items.push({ type: "listItem", text: item });
|
|
3395
|
+
list.items.push({ type: "listItem", text: item, sourceLine: sourceLine++ });
|
|
1549
3396
|
i++;
|
|
1550
3397
|
}
|
|
1551
3398
|
if (list.items.length > 0)
|
|
1552
3399
|
doc.nodes.splice(startI, i - startI + 1, list);
|
|
1553
|
-
} 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") {
|
|
1554
3401
|
node.level = 0;
|
|
1555
3402
|
node.center = true;
|
|
1556
3403
|
node.text = node.text.toUpperCase();
|
|
1557
|
-
doc.nodes.splice(i++, 0, { type: "pageBreak" });
|
|
3404
|
+
doc.nodes.splice(i++, 0, { type: "pageBreak", sourceLine: node.sourceLine });
|
|
1558
3405
|
for (let j = i + 1; j < doc.nodes.length; j++) {
|
|
1559
3406
|
const node2 = doc.nodes[j];
|
|
1560
3407
|
if (node2.type == "title") {
|
|
1561
|
-
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 });
|
|
1562
3409
|
j++;
|
|
1563
3410
|
node2.level = 1;
|
|
1564
3411
|
const splitI = node2.text.indexOf("]");
|
|
1565
|
-
doc.nodes.splice(j++, 0, { type: "pageBreak" });
|
|
3412
|
+
doc.nodes.splice(j++, 0, { type: "pageBreak", sourceLine: node2.sourceLine });
|
|
1566
3413
|
if (splitI >= 0) {
|
|
1567
|
-
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 });
|
|
1568
3415
|
node2.text = "\u041F\u0420\u0418\u041B\u041E\u0416\u0415\u041D\u0418\u0415 " + node2.text.slice(0, splitI + 1);
|
|
1569
3416
|
node2.center = true;
|
|
1570
3417
|
}
|
|
@@ -1600,11 +3447,11 @@ function enrichDoc(doc, logwarn = console.warn) {
|
|
|
1600
3447
|
} else if (node.type == "externalDoc") {
|
|
1601
3448
|
const nextNode = doc.nodes[i + 1];
|
|
1602
3449
|
if (nextNode && nextNode?.type != "externalDoc" && nextNode?.type != "pageBreak" && nextNode?.type != "sectionBreak") {
|
|
1603
|
-
doc.nodes.splice(i + 1, 0, { type: "pageBreak" });
|
|
3450
|
+
doc.nodes.splice(i + 1, 0, { type: "pageBreak", sourceLine: node.sourceLine });
|
|
1604
3451
|
}
|
|
1605
3452
|
const prevNode = doc.nodes[i - 1];
|
|
1606
3453
|
if (prevNode && prevNode?.type != "externalDoc" && prevNode?.type != "pageBreak" && prevNode?.type != "sectionBreak") {
|
|
1607
|
-
doc.nodes.splice(i, 0, { type: "pageBreak" });
|
|
3454
|
+
doc.nodes.splice(i, 0, { type: "pageBreak", sourceLine: node.sourceLine });
|
|
1608
3455
|
i++;
|
|
1609
3456
|
}
|
|
1610
3457
|
}
|
|
@@ -1613,7 +3460,7 @@ function enrichDoc(doc, logwarn = console.warn) {
|
|
|
1613
3460
|
|
|
1614
3461
|
// src/main.ts
|
|
1615
3462
|
import { exec, spawn } from "child_process";
|
|
1616
|
-
import
|
|
3463
|
+
import fs5 from "fs/promises";
|
|
1617
3464
|
import { existsSync as existsSync2 } from "fs";
|
|
1618
3465
|
import { PDFDocument } from "pdf-lib";
|
|
1619
3466
|
|
|
@@ -1624,12 +3471,15 @@ function alchemist(doc, logwarn = console.warn) {
|
|
|
1624
3471
|
codesAll: 0,
|
|
1625
3472
|
imgsAll: 0,
|
|
1626
3473
|
tablesAll: 0,
|
|
3474
|
+
formulasAll: 0,
|
|
1627
3475
|
codes: 0,
|
|
1628
3476
|
imgs: 0,
|
|
1629
3477
|
tables: 0,
|
|
3478
|
+
formulas: 0,
|
|
1630
3479
|
titles: { l1: 0, l2: 0, l3: 0, l4: 0, l5: 0 }
|
|
1631
3480
|
};
|
|
1632
3481
|
const named = {};
|
|
3482
|
+
const warnAt = (line, message) => logwarn(`${line ? `Line ${doc.mapSourceLine(line)}: ` : ""}${message}`);
|
|
1633
3483
|
const vals = {};
|
|
1634
3484
|
let lastRefI = 0;
|
|
1635
3485
|
let prevNum = -1;
|
|
@@ -1646,11 +3496,13 @@ function alchemist(doc, logwarn = console.warn) {
|
|
|
1646
3496
|
let codes = counter.codes;
|
|
1647
3497
|
let imgs = counter.imgs;
|
|
1648
3498
|
let tables = counter.tables;
|
|
3499
|
+
let formulas = counter.formulas;
|
|
1649
3500
|
for (let j = i; j < doc.nodes.length && nextNum < 0; j++) {
|
|
1650
3501
|
const n = doc.nodes[j];
|
|
1651
3502
|
if (n.type == "code") nextNum = codes + 1;
|
|
1652
3503
|
if (n.type == "image") nextNum = imgs + 1;
|
|
1653
3504
|
if (n.type == "table") nextNum = tables + 1;
|
|
3505
|
+
if (n.type == "math") nextNum = formulas + 1;
|
|
1654
3506
|
if (n.type == "title" && n.level == 1 && doc.numberingSections) {
|
|
1655
3507
|
const num = getTitleNum(n);
|
|
1656
3508
|
if (num) l1 = num;
|
|
@@ -1658,6 +3510,7 @@ function alchemist(doc, logwarn = console.warn) {
|
|
|
1658
3510
|
codes = 0;
|
|
1659
3511
|
imgs = 0;
|
|
1660
3512
|
tables = 0;
|
|
3513
|
+
formulas = 0;
|
|
1661
3514
|
}
|
|
1662
3515
|
}
|
|
1663
3516
|
nextPrefix = l1 > 0 && doc.numberingSections ? `${l1}.` : "";
|
|
@@ -1680,13 +3533,14 @@ function alchemist(doc, logwarn = console.warn) {
|
|
|
1680
3533
|
counter.codes = 0;
|
|
1681
3534
|
counter.imgs = 0;
|
|
1682
3535
|
counter.tables = 0;
|
|
3536
|
+
counter.formulas = 0;
|
|
1683
3537
|
}
|
|
1684
3538
|
prefix = doc.numberingSections ? `${counter.titles.l1}.` : "";
|
|
1685
3539
|
if (appendix) prefix = APPENDIX_NAME[counter.titles.l1 - 1] + ".";
|
|
1686
3540
|
}
|
|
1687
3541
|
}
|
|
1688
3542
|
materializeNode(node);
|
|
1689
|
-
if (node.type == "code" || node.type == "image" || node.type == "table") {
|
|
3543
|
+
if (node.type == "code" || node.type == "image" || node.type == "table" || node.type == "math") {
|
|
1690
3544
|
if (node.type == "code") {
|
|
1691
3545
|
counter.codes++;
|
|
1692
3546
|
counter.codesAll++;
|
|
@@ -1699,18 +3553,27 @@ function alchemist(doc, logwarn = console.warn) {
|
|
|
1699
3553
|
counter.tables++;
|
|
1700
3554
|
counter.tablesAll++;
|
|
1701
3555
|
}
|
|
3556
|
+
if (node.type == "math") {
|
|
3557
|
+
counter.formulas++;
|
|
3558
|
+
counter.formulasAll++;
|
|
3559
|
+
}
|
|
1702
3560
|
prevNum = nextNum;
|
|
1703
3561
|
prevPrefix = prefix;
|
|
1704
3562
|
nextNum = -1;
|
|
1705
3563
|
}
|
|
1706
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
|
+
}
|
|
1707
3569
|
const sourcesCount = crystallizeSources();
|
|
1708
|
-
vals["codes"]?.forEach((f) => f(counter.
|
|
1709
|
-
vals["imgs"]?.forEach((f) => f(counter.
|
|
1710
|
-
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));
|
|
1711
3574
|
vals["sources"]?.forEach((f) => f(sourcesCount));
|
|
1712
3575
|
Object.entries(named).forEach(([k, v]) => {
|
|
1713
|
-
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}]`));
|
|
1714
3577
|
});
|
|
1715
3578
|
if (synopsis) {
|
|
1716
3579
|
synopsis.text = [
|
|
@@ -1744,7 +3607,7 @@ function alchemist(doc, logwarn = console.warn) {
|
|
|
1744
3607
|
function materializeList(node) {
|
|
1745
3608
|
node.items.forEach((item) => {
|
|
1746
3609
|
if (item.type == "list") materializeList(item);
|
|
1747
|
-
else materializeRunes(item.text, node);
|
|
3610
|
+
else materializeRunes(item.text, { ...node, sourceLine: item.sourceLine ?? node.sourceLine });
|
|
1748
3611
|
});
|
|
1749
3612
|
}
|
|
1750
3613
|
function materializeTable(node) {
|
|
@@ -1792,8 +3655,9 @@ function alchemist(doc, logwarn = console.warn) {
|
|
|
1792
3655
|
rune.text = repeat(node.level, (i2) => counter.titles[`l${i2 + 1}`]).join(".");
|
|
1793
3656
|
return;
|
|
1794
3657
|
}
|
|
1795
|
-
if (type == "code" || type == "image" || type == "table") {
|
|
1796
|
-
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;
|
|
1797
3661
|
throw new Error("switch default");
|
|
1798
3662
|
})();
|
|
1799
3663
|
num++;
|
|
@@ -1810,9 +3674,10 @@ function alchemist(doc, logwarn = console.warn) {
|
|
|
1810
3674
|
nextRune.text = toCapitalCase(nextRune.text);
|
|
1811
3675
|
}
|
|
1812
3676
|
text = `${text} ${prefix}${num} \u2013 `;
|
|
3677
|
+
if (type == "math") text = `${prefix}${num}`;
|
|
1813
3678
|
} else text = `${prefix}${num}`;
|
|
1814
3679
|
if (v instanceof Array) v.forEach((fn) => fn.f(num, prefix));
|
|
1815
|
-
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`);
|
|
1816
3681
|
named[tag] = { n: num, prefix };
|
|
1817
3682
|
rune.type = "text";
|
|
1818
3683
|
rune.text = text;
|
|
@@ -1832,8 +3697,8 @@ function alchemist(doc, logwarn = console.warn) {
|
|
|
1832
3697
|
rune.type = "text";
|
|
1833
3698
|
rune.text = n < 0 ? prefix2 : `${prefix2}${applyMath(n)}`;
|
|
1834
3699
|
};
|
|
1835
|
-
if (v) v.push({ f, i: lastRefI
|
|
1836
|
-
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 }];
|
|
1837
3702
|
} else {
|
|
1838
3703
|
rune.type = "text";
|
|
1839
3704
|
rune.text = `${v.prefix}${applyMath(v.n)}`;
|
|
@@ -1841,21 +3706,22 @@ function alchemist(doc, logwarn = console.warn) {
|
|
|
1841
3706
|
});
|
|
1842
3707
|
}
|
|
1843
3708
|
function crystallizeSources() {
|
|
1844
|
-
const sourcesList = doc.nodes.find((node) => node.type == "list" && node.
|
|
3709
|
+
const sourcesList = doc.nodes.find((node) => node.type == "list" && node.tags?.includes("sources"));
|
|
1845
3710
|
if (!sourcesList || sourcesList.type != "list") return 0;
|
|
1846
|
-
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;
|
|
1847
3713
|
const refI = item[0]?.text ? 0 : 1;
|
|
1848
3714
|
const ref = item[refI];
|
|
1849
3715
|
if (ref?.type != "ref") {
|
|
1850
|
-
return { text: item, ref: "", i: 99999 + i };
|
|
3716
|
+
return { text: item, ref: "", i: 99999 + i, srcI: v.sourceLine };
|
|
1851
3717
|
}
|
|
1852
3718
|
const refs = named[ref.text];
|
|
1853
3719
|
if (refs && !(refs instanceof Array)) {
|
|
1854
|
-
|
|
1855
|
-
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 };
|
|
1856
3722
|
}
|
|
1857
|
-
const firstOccurrenceI = Math.min(...refs.map((
|
|
1858
|
-
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 };
|
|
1859
3725
|
});
|
|
1860
3726
|
items.sort((a, b) => a.i - b.i);
|
|
1861
3727
|
const hasIdAny = items.some((it) => !!it.ref);
|
|
@@ -1865,16 +3731,17 @@ function alchemist(doc, logwarn = console.warn) {
|
|
|
1865
3731
|
named[key] = { prefix: "", n: i + 1 };
|
|
1866
3732
|
if (refs instanceof Array) {
|
|
1867
3733
|
if (hasIdAny && refs.length <= 1)
|
|
1868
|
-
|
|
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}`);
|
|
1869
3735
|
refs.forEach((r) => hasIdAny ? r.f(i + 1, "") : r.f(-1, `[${i + 1}]`));
|
|
1870
3736
|
}
|
|
1871
|
-
return { type: "listItem", text: item.text };
|
|
3737
|
+
return { type: "listItem", text: item.text, sourceLine: item.srcI };
|
|
1872
3738
|
});
|
|
1873
3739
|
return sourcesList.items.length;
|
|
1874
3740
|
}
|
|
1875
3741
|
}
|
|
1876
3742
|
function addLazyNumbering(doc) {
|
|
1877
3743
|
doc.nodes.forEach((node) => {
|
|
3744
|
+
if (node.type == "math" && !node.title) node.title = [{ text: "(", type: "text" }, { text: "#", type: "ref" }, { text: ")", type: "text" }];
|
|
1878
3745
|
const runes = node.type == "code" || node.type == "table" ? node.title : node.type == "image" ? node.text : null;
|
|
1879
3746
|
if (!runes) return;
|
|
1880
3747
|
if (runes.find((rune) => rune.type == "ref")) return;
|
|
@@ -1896,6 +3763,8 @@ async function render({
|
|
|
1896
3763
|
disableMacros = false,
|
|
1897
3764
|
useLibreOffice = false,
|
|
1898
3765
|
checkFilesIsInsidePath = "",
|
|
3766
|
+
templateOptions = { allowIncludes: true, allowLoops: true },
|
|
3767
|
+
variables,
|
|
1899
3768
|
logwarn = console.warn,
|
|
1900
3769
|
loginfo = console.info,
|
|
1901
3770
|
logPS = (msg) => console.log(`PS: ${msg}`),
|
|
@@ -1903,23 +3772,23 @@ async function render({
|
|
|
1903
3772
|
signal = null
|
|
1904
3773
|
}) {
|
|
1905
3774
|
signal?.throwIfAborted();
|
|
1906
|
-
const fin =
|
|
3775
|
+
const fin = path5.resolve(file);
|
|
1907
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...");
|
|
1908
3777
|
loginfo(`[!1] Parsing file ${fin}`);
|
|
1909
|
-
const doc = await parseMD(fin, logwarn);
|
|
3778
|
+
const doc = await parseMD(fin, variables, templateOptions, checkFilesIsInsidePath, signal, logwarn);
|
|
1910
3779
|
signal?.throwIfAborted();
|
|
1911
3780
|
progress(10, "\u041A\u043E\u043B\u0434\u0443\u0435\u043C \u043D\u0430\u0434 \u0441\u0438\u043D\u0442\u0430\u043A\u0441\u0438\u0441\u043E\u043C...");
|
|
1912
3781
|
loginfo("[!2] Processing document");
|
|
1913
3782
|
enrichDoc(doc, logwarn);
|
|
1914
3783
|
const runicDoc = runifyDoc(doc);
|
|
1915
3784
|
alchemist(runicDoc, logwarn);
|
|
1916
|
-
const pout = output &&
|
|
1917
|
-
if (pout) await
|
|
1918
|
-
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);
|
|
1919
3788
|
const fdir = p.dir;
|
|
1920
3789
|
const fname = trimEnd(p.name, ".g");
|
|
1921
|
-
const ftmp =
|
|
1922
|
-
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");
|
|
1923
3792
|
if (await checkIfFileIsBlocked(ftmp))
|
|
1924
3793
|
throw new Error(`\u0417\u0430\u043A\u0440\u043E\u0439\u0442\u0435 docx \u0444\u0430\u0439\u043B. Output file is busy or locked: ${ftmp}`);
|
|
1925
3794
|
if (await checkIfFileIsBlocked(fout))
|
|
@@ -1927,7 +3796,7 @@ async function render({
|
|
|
1927
3796
|
signal?.throwIfAborted();
|
|
1928
3797
|
progress(10, phrase_renderDocx());
|
|
1929
3798
|
loginfo("[!3] Serializing to docx");
|
|
1930
|
-
await serializeDocx(runicDoc, ftmp, fdir, assets, checkFilesIsInsidePath);
|
|
3799
|
+
await serializeDocx(runicDoc, ftmp, fdir, assets, checkFilesIsInsidePath, logwarn);
|
|
1931
3800
|
let willRunMacros = !disableMacros && (renderPDF || hasReasonForRunningMacros(runicDoc));
|
|
1932
3801
|
let limitedRender = false;
|
|
1933
3802
|
if (process.platform != "win32" && willRunMacros && !useLibreOffice) {
|
|
@@ -1938,31 +3807,31 @@ async function render({
|
|
|
1938
3807
|
signal?.throwIfAborted();
|
|
1939
3808
|
progress(20, phrase_runMacros());
|
|
1940
3809
|
loginfo("[!4] Starting macros");
|
|
1941
|
-
const tmpfolder =
|
|
1942
|
-
await
|
|
3810
|
+
const tmpfolder = path5.join(pout ? pout.dir : fdir, ".md2gost_out");
|
|
3811
|
+
await fs5.rm(tmpfolder, { recursive: true, force: true });
|
|
1943
3812
|
try {
|
|
1944
3813
|
const macroRunner = useLibreOffice ? runUnoScript : runDocxMacro;
|
|
1945
3814
|
const ok = await macroRunner(progress, logPS, logPSError, assets, fdir, ftmp, fout, renderPDF, signal);
|
|
1946
3815
|
if (!ok) {
|
|
1947
|
-
await
|
|
3816
|
+
await fs5.rename(ftmp, fout);
|
|
1948
3817
|
return { fout, err: "inPS" };
|
|
1949
3818
|
}
|
|
1950
3819
|
} catch (x) {
|
|
1951
3820
|
if (signal?.aborted) {
|
|
1952
|
-
await
|
|
3821
|
+
await fs5.unlink(ftmp).catch(() => {
|
|
1953
3822
|
});
|
|
1954
3823
|
throw x;
|
|
1955
3824
|
}
|
|
1956
|
-
await
|
|
3825
|
+
await fs5.rename(ftmp, fout);
|
|
1957
3826
|
return { fout, err: "noPS", errS: x };
|
|
1958
3827
|
}
|
|
1959
3828
|
if (existsSync2(ftmp))
|
|
1960
|
-
await
|
|
3829
|
+
await fs5.unlink(ftmp);
|
|
1961
3830
|
signal?.throwIfAborted();
|
|
1962
|
-
const errorTxt =
|
|
3831
|
+
const errorTxt = path5.join(tmpfolder, "error.txt");
|
|
1963
3832
|
if (existsSync2(errorTxt)) {
|
|
1964
|
-
const err = await
|
|
1965
|
-
await
|
|
3833
|
+
const err = await fs5.readFile(errorTxt);
|
|
3834
|
+
await fs5.rm(tmpfolder, { recursive: true, force: true });
|
|
1966
3835
|
return { fout, err: "vba", errS: `${err}` };
|
|
1967
3836
|
}
|
|
1968
3837
|
loginfo("[!5] Updating metadata");
|
|
@@ -1974,11 +3843,11 @@ async function render({
|
|
|
1974
3843
|
if (!existsSync2(tmpfolder))
|
|
1975
3844
|
return { fout, err: "pdf" };
|
|
1976
3845
|
try {
|
|
1977
|
-
const files = (await
|
|
1978
|
-
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");
|
|
1979
3848
|
await mergePDFs(files, pdf, doc, signal || void 0);
|
|
1980
|
-
await
|
|
1981
|
-
if (removeIntermediateDocx && existsSync2(fout)) await
|
|
3849
|
+
await fs5.rm(tmpfolder, { recursive: true, force: true });
|
|
3850
|
+
if (removeIntermediateDocx && existsSync2(fout)) await fs5.unlink(fout);
|
|
1982
3851
|
return { fout: pdf };
|
|
1983
3852
|
} catch (err) {
|
|
1984
3853
|
if (signal?.aborted) throw err;
|
|
@@ -1986,7 +3855,7 @@ async function render({
|
|
|
1986
3855
|
}
|
|
1987
3856
|
}
|
|
1988
3857
|
} else {
|
|
1989
|
-
await
|
|
3858
|
+
await fs5.rename(ftmp, fout);
|
|
1990
3859
|
}
|
|
1991
3860
|
loginfo(`[!7] Rendered to ${fout}`);
|
|
1992
3861
|
return { fout, err: limitedRender ? "noWin" : void 0 };
|
|
@@ -2020,8 +3889,8 @@ function killProcessTree(child, wordPid) {
|
|
|
2020
3889
|
}
|
|
2021
3890
|
}
|
|
2022
3891
|
function runDocxMacro(progress, log, logError, assets, cwd, fin, fout, renderPDF, signal) {
|
|
2023
|
-
const script =
|
|
2024
|
-
const templateMacro =
|
|
3892
|
+
const script = path5.join(assets, "run.ps1");
|
|
3893
|
+
const templateMacro = path5.join(assets, "template.dotm");
|
|
2025
3894
|
return new Promise((res, rej) => {
|
|
2026
3895
|
if (signal?.aborted)
|
|
2027
3896
|
return rej(signal.reason ?? new Error("Aborted before execution started"));
|
|
@@ -2097,7 +3966,7 @@ function getPythonExecutable() {
|
|
|
2097
3966
|
return process.env.UNO_PYTHON_PATH || "python3";
|
|
2098
3967
|
}
|
|
2099
3968
|
function runUnoScript(progress, log, logError, assets, cwd, fin, fout, renderPDF, signal) {
|
|
2100
|
-
const script = process.env.MACRO_ENGINE_PATH ||
|
|
3969
|
+
const script = process.env.MACRO_ENGINE_PATH || path5.join(assets, "macro_engine.py");
|
|
2101
3970
|
const pythonExec = getPythonExecutable();
|
|
2102
3971
|
return new Promise((res, rej) => {
|
|
2103
3972
|
if (signal?.aborted) {
|
|
@@ -2161,31 +4030,61 @@ function runUnoScript(progress, log, logError, assets, cwd, fin, fout, renderPDF
|
|
|
2161
4030
|
});
|
|
2162
4031
|
}
|
|
2163
4032
|
async function mergePDFs(files, fout, doc, signal) {
|
|
2164
|
-
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
|
+
}
|
|
2165
4073
|
if (doc?.title) mergedPdf.setTitle(doc.title, { showInWindowTitleBar: true });
|
|
2166
4074
|
if (doc?.author) mergedPdf.setAuthor(doc.author);
|
|
2167
4075
|
if (doc?.ctime) mergedPdf.setCreationDate(doc.ctime);
|
|
2168
4076
|
if (doc?.mtime) mergedPdf.setModificationDate(doc.mtime);
|
|
2169
|
-
for (const file of files) {
|
|
2170
|
-
if (!file.endsWith(".pdf")) continue;
|
|
2171
|
-
const bytes = await fs4.readFile(file, { signal });
|
|
2172
|
-
const pdf = await PDFDocument.load(bytes);
|
|
2173
|
-
signal?.throwIfAborted();
|
|
2174
|
-
const pages = await mergedPdf.copyPages(pdf, pdf.getPageIndices());
|
|
2175
|
-
pages.forEach((page) => mergedPdf.addPage(page));
|
|
2176
|
-
signal?.throwIfAborted();
|
|
2177
|
-
}
|
|
2178
4077
|
const pdfBytes = await mergedPdf.save();
|
|
2179
|
-
await
|
|
4078
|
+
await fs5.writeFile(fout, pdfBytes, { signal });
|
|
2180
4079
|
}
|
|
2181
4080
|
async function updateMetadata(docfile, doc) {
|
|
2182
|
-
const content = await
|
|
4081
|
+
const content = await fs5.readFile(docfile);
|
|
2183
4082
|
const zip = new PizZip(content);
|
|
2184
4083
|
function replaceTag(xml, tag, value) {
|
|
2185
4084
|
const regex = new RegExp(`(<${tag}\\b[^>]*>)(.*?)(</${tag}>)`, "gs");
|
|
2186
4085
|
return xml.replace(
|
|
2187
4086
|
regex,
|
|
2188
|
-
(m, otag, old, ctag) => `${otag}${
|
|
4087
|
+
(m, otag, old, ctag) => `${otag}${escapeXml2(value)}${ctag}`
|
|
2189
4088
|
);
|
|
2190
4089
|
}
|
|
2191
4090
|
let coreXml = zip.file("docProps/core.xml").asText();
|
|
@@ -2199,8 +4098,8 @@ async function updateMetadata(docfile, doc) {
|
|
|
2199
4098
|
appXml = replaceTag(appXml, "TotalTime", `${doc.etime || randomInt(30, 120)}`);
|
|
2200
4099
|
zip.file("docProps/app.xml", appXml);
|
|
2201
4100
|
const buffer = zip.generate({ type: "nodebuffer" });
|
|
2202
|
-
await
|
|
2203
|
-
function
|
|
4101
|
+
await fs5.writeFile(docfile, buffer);
|
|
4102
|
+
function escapeXml2(unsafe) {
|
|
2204
4103
|
return unsafe.replaceAll(/[<>&"']/g, (ch) => {
|
|
2205
4104
|
switch (ch) {
|
|
2206
4105
|
case "<":
|
|
@@ -2271,11 +4170,26 @@ async function renderMarkdown(config) {
|
|
|
2271
4170
|
assert(typeof config.useLibreOffice == "boolean" || typeof config.useLibreOffice == "undefined", "useLibreOffice must be a boolean or undefined.");
|
|
2272
4171
|
assert(typeof config.checkFilesIsInsidePath == "string" || config.checkFilesIsInsidePath === false || typeof config.checkFilesIsInsidePath == "undefined", "checkFilesIsInsidePath must be a string, false or undefined.");
|
|
2273
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
|
+
}
|
|
2274
4188
|
assert(config.abortSignal instanceof AbortSignal || typeof config.abortSignal == "undefined", "abortSignal must be an AbortSignal or undefined.");
|
|
2275
4189
|
{
|
|
2276
4190
|
const extraProps = getExtraProperties(
|
|
2277
4191
|
config,
|
|
2278
|
-
["input", "output", "format", "keepIntermediateDocx", "disableMacros", "useLibreOffice", "progress", "logger", "abortSignal", "checkFilesIsInsidePath"]
|
|
4192
|
+
["input", "output", "format", "keepIntermediateDocx", "disableMacros", "useLibreOffice", "progress", "logger", "abortSignal", "checkFilesIsInsidePath", "variables", "templateOptions"]
|
|
2279
4193
|
);
|
|
2280
4194
|
assert(extraProps.length == 0, `Found unknown properties in config: ${extraProps.join(", ")}`);
|
|
2281
4195
|
}
|
|
@@ -2289,20 +4203,25 @@ async function renderMarkdown(config) {
|
|
|
2289
4203
|
(p) => assert(typeof logger[p] == "function", `config.logger.${p} must be a function.`)
|
|
2290
4204
|
);
|
|
2291
4205
|
}
|
|
2292
|
-
const
|
|
4206
|
+
const outputExtension = config.output && path6.extname(config.output).toLowerCase();
|
|
4207
|
+
const renderPDF = config.format === "pdf" || !config.format && outputExtension === ".pdf";
|
|
2293
4208
|
const removeIntermediateDocx = !config.keepIntermediateDocx;
|
|
2294
4209
|
const disableMacros = !!config.disableMacros;
|
|
2295
4210
|
const useLibreOffice = !!config.useLibreOffice;
|
|
2296
4211
|
const checkFilesIsInsidePath = config.checkFilesIsInsidePath === void 0 ? "" : config.checkFilesIsInsidePath;
|
|
2297
4212
|
assert(!(disableMacros && renderPDF), "Macros must be enabled to render PDF output.");
|
|
2298
|
-
|
|
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));
|
|
2299
4218
|
const fname = pin.name.endsWith(".g") ? pin.name.slice(0, -2) : pin.name;
|
|
2300
4219
|
let resolvedOutput = config.output;
|
|
2301
4220
|
if (resolvedOutput) {
|
|
2302
|
-
const isDir = resolvedOutput.endsWith("/") || resolvedOutput.endsWith("\\") || !
|
|
4221
|
+
const isDir = resolvedOutput.endsWith("/") || resolvedOutput.endsWith("\\") || !path6.extname(resolvedOutput);
|
|
2303
4222
|
if (isDir) {
|
|
2304
4223
|
const ext = renderPDF ? ".pdf" : ".docx";
|
|
2305
|
-
resolvedOutput =
|
|
4224
|
+
resolvedOutput = path6.join(resolvedOutput, fname + ext);
|
|
2306
4225
|
}
|
|
2307
4226
|
}
|
|
2308
4227
|
const warnings = [];
|
|
@@ -2327,6 +4246,8 @@ async function renderMarkdown(config) {
|
|
|
2327
4246
|
disableMacros,
|
|
2328
4247
|
useLibreOffice,
|
|
2329
4248
|
checkFilesIsInsidePath,
|
|
4249
|
+
variables: config.variables,
|
|
4250
|
+
templateOptions: config.templateOptions,
|
|
2330
4251
|
loginfo: logger?.info,
|
|
2331
4252
|
logwarn: (msg) => {
|
|
2332
4253
|
logger?.warn(msg);
|
|
@@ -2342,7 +4263,6 @@ async function renderMarkdown(config) {
|
|
|
2342
4263
|
},
|
|
2343
4264
|
signal: config.abortSignal
|
|
2344
4265
|
});
|
|
2345
|
-
config.progress?.(100, "Done!");
|
|
2346
4266
|
if (useLibreOffice) {
|
|
2347
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.`);
|
|
2348
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.`);
|
|
@@ -2353,10 +4273,11 @@ async function renderMarkdown(config) {
|
|
|
2353
4273
|
if (err == "vba") throwMDE2("vba", `Microsoft Word VBA Macro execution failed: ${errS}`);
|
|
2354
4274
|
if (err == "pdf") throwMDE2("pdf", `Failed to export document to PDF.`);
|
|
2355
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!");
|
|
2356
4277
|
let intermediateDocxPath;
|
|
2357
4278
|
if (renderPDF && !removeIntermediateDocx) {
|
|
2358
|
-
const pout = resolvedOutput ?
|
|
2359
|
-
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");
|
|
2360
4281
|
}
|
|
2361
4282
|
return {
|
|
2362
4283
|
filePath: fout,
|
|
@@ -2414,10 +4335,7 @@ var MDRenderError = class _MDRenderError extends Error {
|
|
|
2414
4335
|
}
|
|
2415
4336
|
};
|
|
2416
4337
|
function getAssetsDir() {
|
|
2417
|
-
|
|
2418
|
-
return path5.resolve(__dirname, "../assets");
|
|
2419
|
-
const metaUrl = new Function("return import.meta.url")();
|
|
2420
|
-
return path5.resolve(path5.dirname(fileURLToPath(metaUrl)), "../assets");
|
|
4338
|
+
return path6.resolve(__MD2GOST_DIRNAME__, "../assets");
|
|
2421
4339
|
}
|
|
2422
4340
|
function getExtraProperties(configObj, allowedConfigKeys) {
|
|
2423
4341
|
return Object.keys(configObj).filter(
|
|
@@ -2426,6 +4344,7 @@ function getExtraProperties(configObj, allowedConfigKeys) {
|
|
|
2426
4344
|
}
|
|
2427
4345
|
export {
|
|
2428
4346
|
MDRenderError,
|
|
4347
|
+
UserInputError,
|
|
2429
4348
|
renderMarkdown as default
|
|
2430
4349
|
};
|
|
2431
4350
|
//# sourceMappingURL=index.mjs.map
|