md2gost 1.5.1 → 1.6.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -1,3 +1,4 @@
1
+ const __MD2GOST_DIRNAME__ = __dirname;
1
2
  "use strict";
2
3
  var __create = Object.create;
3
4
  var __defProp = Object.defineProperty;
@@ -31,18 +32,20 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
31
32
  var index_exports = {};
32
33
  __export(index_exports, {
33
34
  MDRenderError: () => MDRenderError,
35
+ UserInputError: () => UserInputError,
34
36
  default: () => renderMarkdown
35
37
  });
36
38
  module.exports = __toCommonJS(index_exports);
37
- var import_path4 = __toESM(require("path"));
38
- var import_url = require("url");
39
+ var import_path5 = __toESM(require("path"));
39
40
 
40
41
  // src/main.ts
41
- var import_path3 = __toESM(require("path"));
42
+ var import_path4 = __toESM(require("path"));
42
43
 
43
44
  // src/doc.ts
44
45
  var Doc = class {
45
46
  nodes = [];
47
+ header = { type: "none" };
48
+ footer = { type: "auto" };
46
49
  rainbow = false;
47
50
  numberingLazy = false;
48
51
  numberingSections = false;
@@ -102,36 +105,66 @@ var Doc = class {
102
105
  spacing: { before: void 0, after: void 0 },
103
106
  text: { size: void 0 }
104
107
  };
105
- appendText(text) {
106
- this.nodes.push({ type: "text", text });
108
+ formula = {
109
+ spacing: { before: 8, after: 8 }
110
+ };
111
+ admonition = {
112
+ 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 },
113
+ 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 },
114
+ 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 },
115
+ 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 },
116
+ 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 }
117
+ };
118
+ appendText(text, sourceLine) {
119
+ this.nodes.push({ type: "text", text, sourceLine });
107
120
  }
108
- appendTitle(text, level) {
109
- this.nodes.push({ type: "title", text, level });
121
+ appendTitle(text, level, sourceLine) {
122
+ this.nodes.push({ type: "title", text, level, sourceLine });
110
123
  }
111
124
  appendNode(node) {
112
125
  this.nodes.push(node);
113
126
  }
127
+ lineSkips = [];
128
+ addLineSkip(line, delta) {
129
+ this.lineSkips.push({ ln: line, d: delta });
130
+ }
131
+ mapSourceLine(line) {
132
+ for (const { ln, d } of this.lineSkips)
133
+ if (ln <= line) line -= d;
134
+ return line;
135
+ }
114
136
  };
115
- function tableRow(...items) {
116
- return items.map((v) => ({ type: "text", text: v }));
137
+ function tableRow(sourceLine, ...items) {
138
+ return items.map((v) => ({ type: "text", text: v, sourceLine }));
117
139
  }
118
140
 
119
141
  // src/parser.ts
120
- var import_promises2 = __toESM(require("fs/promises"));
142
+ var import_promises3 = __toESM(require("fs/promises"));
121
143
 
122
144
  // src/utils.ts
123
145
  var import_promises = __toESM(require("fs/promises"));
124
146
  var import_fs = __toESM(require("fs"));
125
147
  var path = __toESM(require("node:path"));
126
- function choice(...options) {
127
- return options[randomInt(options.length)];
148
+
149
+ // src/errors.ts
150
+ var UserInputError = class extends Error {
151
+ constructor(message) {
152
+ super(message);
153
+ }
154
+ };
155
+
156
+ // src/utils.ts
157
+ function choice(first, ...options) {
158
+ const all = [first, ...options];
159
+ return all[randomInt(all.length)];
128
160
  }
129
161
  function randomInt(maxmin, max, rnd = Math.random) {
130
162
  if (max != void 0)
131
- return Math.floor(rnd() * (maxmin - max)) + max;
163
+ return Math.floor(rnd() * (max - maxmin)) + maxmin;
132
164
  return Math.floor(rnd() * maxmin);
133
165
  }
134
166
  function trimEnd(str, ...chs) {
167
+ chs = chs.filter((ch) => ch.length > 0);
135
168
  if (!chs || chs.length == 0) return str.trimEnd();
136
169
  let trimmed = false;
137
170
  do {
@@ -145,6 +178,7 @@ function trimEnd(str, ...chs) {
145
178
  return str;
146
179
  }
147
180
  function trimStart(str, ...chs) {
181
+ chs = chs.filter((ch) => ch.length > 0);
148
182
  if (!chs || chs.length == 0) return str.trimStart();
149
183
  let trimmed = false;
150
184
  do {
@@ -160,10 +194,10 @@ function trimStart(str, ...chs) {
160
194
  function toCapitalCase(str) {
161
195
  return str.slice(0, 1).toUpperCase() + str.slice(1);
162
196
  }
163
- async function checkIfFileIsBlocked(path6) {
197
+ async function checkIfFileIsBlocked(path7) {
164
198
  let file;
165
199
  try {
166
- file = await import_promises.default.open(path6, "r+");
200
+ file = await import_promises.default.open(path7, "r+");
167
201
  return false;
168
202
  } catch (err) {
169
203
  if (!err) return false;
@@ -216,22 +250,791 @@ function realpathAllowMissing(inputPath) {
216
250
  function isMissingPathError(err) {
217
251
  return err instanceof Error && "code" in err && (err.code === "ENOENT" || err.code === "ENOTDIR");
218
252
  }
253
+ function getSafePathResolver(workdir, checkFilesIsInsidePath) {
254
+ const allowedRealPath = checkFilesIsInsidePath === false ? false : realpathAllowMissing(checkFilesIsInsidePath === "" ? workdir : checkFilesIsInsidePath);
255
+ const resolvePath = (fname, origin) => {
256
+ if (process.platform != "win32") fname = fname.replaceAll("\\", "/");
257
+ if (process.platform == "win32" && fname.startsWith("/")) fname = "." + fname;
258
+ const targetPath = path.isAbsolute(fname) ? path.resolve(fname) : path.resolve(origin || workdir, fname);
259
+ if (allowedRealPath !== false) {
260
+ const targetRealPath = realpathAllowMissing(targetPath);
261
+ const relative2 = path.relative(allowedRealPath, targetRealPath);
262
+ const isOutside = relative2 === ".." || relative2.startsWith(`..${path.sep}`) || path.isAbsolute(relative2);
263
+ if (isOutside) throw new UserInputError(`Access denied: "${fname}" resolves outside the working directory.`);
264
+ }
265
+ return targetPath;
266
+ };
267
+ const getRelative = (fname) => {
268
+ const targetPath = resolvePath(fname);
269
+ if (allowedRealPath !== false)
270
+ return path.relative(allowedRealPath, targetPath);
271
+ return path.relative(workdir, targetPath);
272
+ };
273
+ return { resolvePath, getRelative };
274
+ }
275
+ function isLocalFilePath(link) {
276
+ const value = link.trim();
277
+ if (value.startsWith("//")) return false;
278
+ if (path.win32.isAbsolute(value)) return true;
279
+ if (/^[a-zA-Z][a-zA-Z0-9+.-]*:/.test(value)) return false;
280
+ if (value.startsWith("#")) return false;
281
+ return true;
282
+ }
283
+ function deepOverwrite(target, source) {
284
+ const result = Object.assign(/* @__PURE__ */ Object.create(null), target);
285
+ for (const [key, sourceValue] of Object.entries(source)) {
286
+ if (key === "__proto__" || key === "prototype" || key === "constructor")
287
+ continue;
288
+ const targetValue = Object.hasOwn(result, key) ? result[key] : void 0;
289
+ result[key] = isJSONDict(targetValue) && isJSONDict(sourceValue) ? deepOverwrite(targetValue, sourceValue) : sourceValue;
290
+ }
291
+ return result;
292
+ function isJSONDict(value) {
293
+ return value !== null && typeof value === "object" && !Array.isArray(value);
294
+ }
295
+ }
219
296
 
220
297
  // src/parser.ts
298
+ var import_path2 = __toESM(require("path"));
299
+
300
+ // src/preprocessor.ts
301
+ var import_promises2 = __toESM(require("fs/promises"));
221
302
  var import_path = __toESM(require("path"));
222
- async function parseMD(file, logwarn = console.warn) {
223
- const lines = (await import_promises2.default.readFile(file, { encoding: "utf8" })).split("\n");
303
+ async function preprocess(file, variables, checkFilesIsInsidePath, options, signal, logwarn = console.warn) {
304
+ const rootFile = import_path.default.resolve(file);
305
+ const { resolvePath, getRelative } = getSafePathResolver(import_path.default.dirname(rootFile), checkFilesIsInsidePath);
306
+ const ctx = {
307
+ options,
308
+ workdir: import_path.default.dirname(rootFile),
309
+ includes: 0,
310
+ iterations: 0,
311
+ inputLength: 0,
312
+ generatedLength: 0,
313
+ signal: signal || void 0,
314
+ logwarn,
315
+ countAsOutput(v) {
316
+ ctx.generatedLength += v.length;
317
+ if (options.maxGeneratedLength !== void 0 && ctx.generatedLength > options.maxGeneratedLength)
318
+ throw new UserInputError(`Maximum output length exceeded (${options.maxGeneratedLength})`);
319
+ return v;
320
+ }
321
+ };
322
+ const rootNode = { type: "include", path: rootFile, dict: {}, trimEnd: false, trimStart: false };
323
+ return processInclude(rootNode, variables, resolvePath, getRelative, [], ctx);
324
+ }
325
+ async function processInclude(node, variables, resolvePath, getRelative, stack, ctx) {
326
+ ctx.signal?.throwIfAborted();
327
+ const fpath = resolvePath(node.path);
328
+ const relativePath = getRelative(fpath);
329
+ const dir = import_path.default.dirname(fpath);
330
+ if (ctx.options.maxIncludeDepth !== void 0 && stack.length > ctx.options.maxIncludeDepth)
331
+ throw new UserInputError(`Maximum include depth exceeded while including "${relativePath}":
332
+ ${stack.join(" -> ")}`);
333
+ if (stack.length > 0) {
334
+ ctx.includes++;
335
+ const maxIncludes = ctx.options.maxIncludes === void 0 ? 1e3 : ctx.options.maxIncludes;
336
+ if (ctx.includes > maxIncludes)
337
+ throw new UserInputError(`Maximum include count exceeded (${maxIncludes})`);
338
+ }
339
+ let rawContent;
340
+ try {
341
+ if (ctx.options.maxFileLength !== void 0) {
342
+ const size = (await import_promises2.default.stat(fpath)).size;
343
+ if (size > ctx.options.maxFileLength * 3)
344
+ throw new UserInputError(`File is too large (${size} > ${ctx.options.maxFileLength * 3}) "${relativePath}"`);
345
+ }
346
+ rawContent = await import_promises2.default.readFile(fpath, { encoding: "utf8", signal: ctx.signal });
347
+ if (ctx.options.maxFileLength !== void 0 && rawContent.length > ctx.options.maxFileLength)
348
+ throw new UserInputError(`File is too large (${rawContent.length} > ${ctx.options.maxFileLength}) "${relativePath}"`);
349
+ ctx.inputLength += rawContent.length;
350
+ if (ctx.options.maxTotalInputLength !== void 0 && ctx.inputLength > ctx.options.maxTotalInputLength)
351
+ throw new UserInputError(`Maximum total input length exceeded (${ctx.inputLength} > ${ctx.options.maxTotalInputLength})`);
352
+ } catch (err) {
353
+ ctx.signal?.throwIfAborted();
354
+ if (err instanceof UserInputError) throw err;
355
+ const reason = err instanceof Error ? err.message : String(err);
356
+ throw new UserInputError(`Failed to read included file "${relativePath}": ${reason}`);
357
+ }
358
+ let content = rawContent;
359
+ if (variables) {
360
+ variables = deepOverwrite(variables, node.dict);
361
+ const nodes = parseFExpr(rawContent);
362
+ nodes.forEach((n) => n.type == "expr" && (n.source.fname = relativePath));
363
+ content = processExpressions(nodes, variables || {}, ctx);
364
+ } else {
365
+ variables = {};
366
+ ctx.countAsOutput(content);
367
+ }
368
+ content = await fixLinks(content, fixLink);
369
+ if (!ctx.options.allowIncludes) return content;
370
+ const includes = await parseIncludes(content, ctx.logwarn);
371
+ let result = "";
372
+ for (const node2 of includes) {
373
+ if (node2.type == "text") result += node2.value;
374
+ else {
375
+ let r = await processInclude(node2, variables, resolvePath, getRelative, [...stack, relativePath], ctx);
376
+ if (node2.trimStart) r = r.trimStart();
377
+ if (node2.trimEnd) r = r.trimEnd();
378
+ result += r;
379
+ }
380
+ }
381
+ if (node.sourceLineCount) {
382
+ const delta = result.split("\n").length - node.sourceLineCount + 1;
383
+ result += "\n\uE100" + delta + "\uE101";
384
+ }
385
+ return result;
386
+ function fixLink(oldpath) {
387
+ oldpath = oldpath.trim();
388
+ oldpath = trimEnd(trimStart(oldpath, "<", '"'), ">", '"');
389
+ const newpath = !isLocalFilePath(oldpath) ? oldpath : import_path.default.relative(ctx.workdir, resolvePath(oldpath, dir)).split(import_path.default.sep).join("/");
390
+ if (newpath.includes(" ")) return `<${newpath}>`;
391
+ return newpath;
392
+ }
393
+ }
394
+ function parseFExpr(content) {
395
+ const nodes = [];
396
+ let textStart = 0;
397
+ let ln = 1;
398
+ function countLines(start, end) {
399
+ for (let i = start; i < end; i++)
400
+ if (content[i] == "\n") ln++;
401
+ }
402
+ function pushText(start, end) {
403
+ if (start >= end) return;
404
+ const value = content.slice(start, end);
405
+ const prev = nodes.at(-1);
406
+ countLines(start, end);
407
+ if (prev?.type == "text")
408
+ prev.value += value;
409
+ else
410
+ nodes.push({ type: "text", value });
411
+ }
412
+ function isEscaped(i) {
413
+ let slashes = 0;
414
+ for (i--; i >= 0 && content[i] == "\\"; i--)
415
+ slashes++;
416
+ return slashes % 2 != 0;
417
+ }
418
+ for (let i = 0; i < content.length; i++) {
419
+ if (content.startsWith("<!--", i)) {
420
+ const end2 = content.indexOf("-->", i + 4);
421
+ if (end2 < 0) break;
422
+ i = end2 + 2;
423
+ continue;
424
+ }
425
+ if (!content.startsWith("{{", i))
426
+ continue;
427
+ if (isEscaped(i)) {
428
+ pushText(textStart, i - 1);
429
+ pushText(i, i + 2);
430
+ textStart = i + 2;
431
+ i++;
432
+ continue;
433
+ }
434
+ const trimLeft = content[i + 2] == "-" && !/\d/.test(content[i + 3] || "");
435
+ const valueStart = i + 2 + (trimLeft ? 1 : 0);
436
+ let end = -1;
437
+ let inString = false;
438
+ let escaped = false;
439
+ for (let j = valueStart; j < content.length - 1; j++) {
440
+ const ch = content[j];
441
+ if (inString) {
442
+ if (escaped) escaped = false;
443
+ else if (ch === "\\") escaped = true;
444
+ else if (ch === '"') inString = false;
445
+ continue;
446
+ }
447
+ if (ch == '"') {
448
+ inString = true;
449
+ continue;
450
+ }
451
+ if (ch === "}" && content[j + 1] === "}") {
452
+ end = j;
453
+ break;
454
+ }
455
+ }
456
+ if (end < 0) continue;
457
+ const trimRight = end > valueStart && content[end - 1] == "-";
458
+ const valueEnd = trimRight ? end - 1 : end;
459
+ const hasQuotes = i > 0 && content[i - 1] == '"' && !isEscaped(i - 1) && content[end + 2] === '"';
460
+ let textEnd = hasQuotes ? i - 1 : i;
461
+ const untrimmedTextEnd = textEnd;
462
+ if (trimLeft)
463
+ while (textEnd > textStart && /\s/.test(content[textEnd - 1])) textEnd--;
464
+ pushText(textStart, textEnd);
465
+ countLines(textEnd, untrimmedTextEnd);
466
+ const lineStart = content.lastIndexOf("\n", i + 2);
467
+ const source = { ln, col: lineStart < 0 ? i + 1 : i - lineStart };
468
+ nodes.push({ type: "expr", value: content.slice(valueStart, valueEnd), hasQuotes, trimLeft, trimRight, source });
469
+ let next = hasQuotes ? end + 3 : end + 2;
470
+ if (trimRight) {
471
+ const untrimmedNext = next;
472
+ while (next < content.length && /\s/.test(content[next])) next++;
473
+ countLines(untrimmedNext, next);
474
+ }
475
+ textStart = next;
476
+ i = next - 1;
477
+ }
478
+ pushText(textStart, content.length);
479
+ return nodes;
480
+ }
481
+ function getByPath(value, path7) {
482
+ for (const key of path7) {
483
+ if (value === null || value === void 0)
484
+ return value;
485
+ const obj = Object(value);
486
+ if (!Object.hasOwn(obj, key))
487
+ return void 0;
488
+ value = obj[key];
489
+ }
490
+ return value;
491
+ }
492
+ function processExpressions(nodes, variables, ctx) {
493
+ const expr = parseExp(nodes, ctx);
494
+ const tree = buildExpTree(expr, ctx);
495
+ const res = execExpTree(tree, variables, ctx);
496
+ return res.join("");
497
+ }
498
+ function parseExp(nodes, ctx) {
499
+ const reVar = String.raw`[A-Za-z_$][A-Za-z0-9_$]*(?:\.[A-Za-z_$][A-Za-z0-9_$]*|\.\d+|\[\d+\])*`;
500
+ const reIdent = String.raw`[A-Za-z_$][A-Za-z0-9_$]*`;
501
+ const reStr = String.raw`"(?:\\.|[^"\\])*"`;
502
+ const reNum = String.raw`-?(?:0|[1-9]\d*)(?:\.\d+)?(?:[eE][+-]?\d+)?`;
503
+ const reVal = String.raw`(?:${reStr}|${reNum}|true|false|null)`;
504
+ const reOCompare = String.raw`(?:==|!=|<|<=|>|>=|\bin\b)`;
505
+ const reOMath = String.raw`(?:\+|-|/|//|%|\*|\*\*)`;
506
+ const reOLogical = String.raw`(?:and|or|&&|\|\||\?\?)`;
507
+ const re_var = new RegExp(String.raw`^\s*((?:${reVar})|${reVal})\s*(?:(${reOMath}|${reOLogical})\s*((?:${reVar})|${reVal}))?\s*$`);
508
+ const re_val = new RegExp(String.raw`^${reVal}$`);
509
+ const re_str = new RegExp(String.raw`^\s*(${reStr})\s*$`);
510
+ const re_else = /^:else\s*$/;
511
+ const re_for = new RegExp(String.raw`^#for\s+(${reIdent})(?:\s*,\s*(${reIdent}))?\s+in\s+(${reVar}|${reNum}|${reStr})\s*$`);
512
+ const re_forEnd = /^\/for\s*$/;
513
+ const re_if = new RegExp(String.raw`^#if\s+((?:${reVar})|${reVal})\s*(?:(${reOCompare}|${reOLogical})\s*((?:${reVar})|${reVal}))?\s*$`);
514
+ const re_ifEnd = /^\/if\s*$/;
515
+ const re_path = /[A-Za-z_$][A-Za-z0-9_$]*|\d+/g;
516
+ return nodes.map((node) => {
517
+ ctx.signal?.throwIfAborted();
518
+ if (node.type == "text") return { type: "text", value: node.value };
519
+ const enode = node;
520
+ const source = node.source;
521
+ const expression = `{{${node.trimLeft ? "-" : ""}${node.value}${node.trimRight ? "-" : ""}}}`;
522
+ const rawValue = node.hasQuotes ? `"${expression}"` : expression;
523
+ function retWarn(msg) {
524
+ ctx.logwarn(msg + pos(enode));
525
+ return { type: "text", value: rawValue };
526
+ }
527
+ const mvar = re_var.exec(node.value);
528
+ if (mvar) {
529
+ const left = parseOperand(mvar[1]);
530
+ if (!left) return retWarn("Unsupported expression: " + rawValue);
531
+ const operator = mvar[2];
532
+ if (!operator) return { type: "var", left, source, hasQuotes: node.hasQuotes };
533
+ const right = parseOperand(mvar[3]);
534
+ if (!right) return retWarn("Unsupported expression: " + rawValue);
535
+ return { type: "var", left, operator, right, source, hasQuotes: node.hasQuotes };
536
+ }
537
+ const mstr = re_str.exec(node.value);
538
+ if (mstr) {
539
+ const str = safeParse(node.value);
540
+ if (str === void 0) return retWarn("Cant parse string: " + node.value);
541
+ return { type: "text", value: node.hasQuotes ? JSON.stringify(str) : `${str}` };
542
+ }
543
+ const mfor = re_for.exec(node.value);
544
+ if (mfor) {
545
+ if (!ctx.options.allowLoops) return retWarn("Loop expressions are not allowed");
546
+ const varValue = mfor[1];
547
+ const varKey = mfor[2];
548
+ const array = parseOperand(mfor[3]);
549
+ if (!array) return retWarn("Unsupported expression: " + rawValue);
550
+ return { type: "for", varValue, varKey, array, source };
551
+ }
552
+ const mif = re_if.exec(node.value);
553
+ if (mif) {
554
+ const left = parseOperand(mif[1]);
555
+ if (!left) return retWarn("Unsupported expression: " + rawValue);
556
+ const operator = mif[2];
557
+ if (!operator) return { type: "if", left, source };
558
+ const right = parseOperand(mif[3]);
559
+ if (!right) return retWarn("Unsupported expression: " + rawValue);
560
+ return { type: "if", left, operator, right, source };
561
+ }
562
+ if (re_else.exec(node.value)) return { type: "else", source };
563
+ if (re_ifEnd.exec(node.value)) return { type: "ifEnd", source };
564
+ if (re_forEnd.exec(node.value)) {
565
+ if (!ctx.options.allowLoops) return retWarn("Loop expressions are not allowed");
566
+ return { type: "forEnd", source };
567
+ }
568
+ return retWarn("Unsupported expression: " + rawValue);
569
+ });
570
+ function pos(node) {
571
+ return ` [Ln ${node.source.ln}, Col ${node.source.col}] (${node.source.fname})`;
572
+ }
573
+ function safeParse(value) {
574
+ try {
575
+ return JSON.parse(value);
576
+ } catch {
577
+ return void 0;
578
+ }
579
+ }
580
+ function parsePath(value) {
581
+ const parts = value?.match(re_path);
582
+ if (!parts?.length) return void 0;
583
+ return parts.map((part) => /^\d+$/.test(part) ? Number(part) : part);
584
+ }
585
+ function parseOperand(value) {
586
+ if (!value) return void 0;
587
+ if (re_val.test(value)) {
588
+ const val = safeParse(value);
589
+ if (val === void 0) return null;
590
+ return { t: "val", val };
591
+ }
592
+ const path7 = parsePath(value);
593
+ if (!path7) return null;
594
+ return { t: "var", path: path7 };
595
+ }
596
+ }
597
+ function buildExpTree(nodes, ctx, depth = 0, source) {
598
+ ctx.signal?.throwIfAborted();
599
+ if (ctx.options.maxDepth !== void 0 && depth >= ctx.options.maxDepth) {
600
+ ctx.logwarn(`Maximum expression nesting depth exceeded (${ctx.options.maxDepth})${source ? pos({ source }) : ""}`);
601
+ return nodes.map((n) => {
602
+ if (n.type == "text" || n.type == "var") return n;
603
+ if (n.type == "else" || n.type == "forEnd" || n.type == "ifEnd") {
604
+ const str = n.type == "else" ? "{{:else}}" : n.type == "ifEnd" ? "{{/if}}" : "{{/for}}";
605
+ return { type: "text", value: str };
606
+ }
607
+ if (n.type == "for") {
608
+ const second = n.varKey ? `, ${n.varKey}` : "";
609
+ return { type: "text", value: `{{#for ${n.varValue}${second} in ${strOperand(n.array)}}}` };
610
+ }
611
+ if (n.type == "if") {
612
+ const right = n.right ? ` ${n.operator} ${strOperand(n.right)}` : "";
613
+ return { type: "text", value: `{{#if ${strOperand(n.left)}${right}}}` };
614
+ }
615
+ n;
616
+ return { type: "text", value: "" };
617
+ });
618
+ }
619
+ const tree = [];
620
+ for (let i = 0; i < nodes.length; i++) {
621
+ const node = nodes[i];
622
+ if (node.type == "text" || node.type == "var") {
623
+ tree.push(node);
624
+ }
625
+ if (node.type == "else" || node.type == "forEnd" || node.type == "ifEnd") {
626
+ const str = node.type == "else" ? "{{:else}}" : node.type == "ifEnd" ? "{{/if}}" : "{{/for}}";
627
+ ctx.logwarn(`Unexpected expression (${str})` + pos(node));
628
+ tree.push({ type: "text", value: str });
629
+ }
630
+ if (node.type == "for" || node.type == "if") {
631
+ const r = parseBlockWithElse(i);
632
+ if (!r) {
633
+ if (node.type == "for") {
634
+ const second = node.varKey ? `, ${node.varKey}` : "";
635
+ tree.push({ type: "text", value: `{{#for ${node.varValue}${second} in ${strOperand(node.array)}}}` });
636
+ } else if (node.type == "if") {
637
+ const right = node.right ? ` ${node.operator} ${strOperand(node.right)}` : "";
638
+ tree.push({ type: "text", value: `{{#if ${strOperand(node.left)}${right}}}` });
639
+ }
640
+ } else {
641
+ const { body, bodyElse, endI } = r;
642
+ i = endI;
643
+ tree.push({ ...node, body, bodyElse });
644
+ }
645
+ }
646
+ }
647
+ return tree;
648
+ function pos(node) {
649
+ return ` [Ln ${node.source.ln}, Col ${node.source.col}] (${node.source.fname})`;
650
+ }
651
+ function strOperand(v) {
652
+ return v.t == "val" ? JSON.stringify(v.val) : v.path.join(".");
653
+ }
654
+ function findBlockEnd(startI, type, skipElse) {
655
+ const stack = [];
656
+ for (let i = startI; i < nodes.length; i++) {
657
+ const node = nodes[i];
658
+ if (node.type == "if") stack.push("if");
659
+ else if (node.type == "for") stack.push("for");
660
+ else if (node.type == "forEnd" && stack.at(-1) == "for") stack.pop();
661
+ else if (node.type == "ifEnd" && stack.at(-1) == "if") stack.pop();
662
+ else if (stack.length == 0) {
663
+ if (node.type == "else" && !skipElse) return i;
664
+ if (node.type == "forEnd" && type == "for") return i;
665
+ if (node.type == "ifEnd" && type == "if") return i;
666
+ }
667
+ }
668
+ return -1;
669
+ }
670
+ function parseBlockWithElse(startI) {
671
+ const node = nodes[startI];
672
+ if (node.type != "for" && node.type != "if") return null;
673
+ const n = node;
674
+ const type = n.type;
675
+ const unclosed = (m) => ctx.logwarn(`Unclosed ${type} ${m}` + pos(n));
676
+ const bodyEnd = findBlockEnd(startI + 1, type, false);
677
+ if (bodyEnd < 0) {
678
+ unclosed("body");
679
+ return null;
680
+ }
681
+ const bodyNodes = nodes.slice(startI + 1, bodyEnd);
682
+ const body = buildExpTree(bodyNodes, ctx, depth + 1, n.source);
683
+ let endI = bodyEnd;
684
+ let bodyElse;
685
+ if (nodes[bodyEnd].type == "else") {
686
+ const bodyElseEnd = findBlockEnd(bodyEnd + 1, type, true);
687
+ if (bodyElseEnd < 0) {
688
+ unclosed("else block");
689
+ return null;
690
+ }
691
+ const bodyElseNodes = nodes.slice(bodyEnd + 1, bodyElseEnd);
692
+ bodyElse = buildExpTree(bodyElseNodes, ctx, depth + 1, n.source);
693
+ endI = bodyElseEnd;
694
+ }
695
+ return { body, bodyElse, endI };
696
+ }
697
+ }
698
+ function execExpTree(tree, variables, ctx) {
699
+ return tree.flatMap((node) => {
700
+ ctx.signal?.throwIfAborted();
701
+ if (node.type == "text") return ctx.countAsOutput(node.value);
702
+ if (node.type == "var") {
703
+ const n = node;
704
+ const value = execOperator(node.left, node.operator, node.right);
705
+ const rawValue = () => {
706
+ const right = n.right ? ` ${n.operator} ${strOperand(n.right)}` : "";
707
+ const value2 = `{{${strOperand(n.left)}${right}}}`;
708
+ return n.hasQuotes ? `"${value2}"` : value2;
709
+ };
710
+ if (value === void 0) ctx.logwarn("Undefined variable: " + rawValue() + pos(node));
711
+ const result = value === void 0 ? rawValue() : typeof value == "object" || node.hasQuotes ? JSON.stringify(value) : `${value}`;
712
+ return ctx.countAsOutput(result);
713
+ }
714
+ if (node.type == "if") {
715
+ const res = execOperator(node.left, node.operator, node.right);
716
+ const result = res && typeof res == "object" ? (Array.isArray(res) ? res : Object.keys(res)).length > 0 : !!res;
717
+ if (result) return execExpTree(node.body, variables, ctx);
718
+ if (node.bodyElse) return execExpTree(node.bodyElse, variables, ctx);
719
+ return "";
720
+ }
721
+ if (node.type == "for") {
722
+ if (!ctx.options.allowLoops) throw new UserInputError("Loop expressions are not allowed");
723
+ const arr = node.array.t == "val" ? node.array.val : getByPath(variables, node.array.path);
724
+ if (!arr || typeof arr == "object" && (Array.isArray(arr) ? arr : Object.keys(arr)).length == 0) {
725
+ if (node.bodyElse) return execExpTree(node.bodyElse, variables, ctx);
726
+ return "";
727
+ }
728
+ const values = (() => {
729
+ const n = node;
730
+ function checkLoopIterations(count, final = true) {
731
+ const max = ctx.options.maxLoopIterations === void 0 ? 1e3 : ctx.options.maxLoopIterations;
732
+ if (count > max)
733
+ throw new UserInputError(`Maximum loop iterations exceeded (${final ? count + " " : ""}> ${max})` + pos(n));
734
+ if (ctx.options.maxTotalIterations !== void 0 && ctx.iterations + count > ctx.options.maxTotalIterations)
735
+ throw new UserInputError(`Maximum total loop iterations exceeded (${ctx.options.maxTotalIterations})`);
736
+ }
737
+ if (typeof arr == "number" && Number.isInteger(arr)) {
738
+ if (arr < 0) {
739
+ ctx.logwarn(`For loop source is a negative integer (${arr})` + pos(node));
740
+ return [];
741
+ }
742
+ checkLoopIterations(arr);
743
+ return Array.from({ length: arr }, (_, i) => ({ k: i, v: i + 1 }));
744
+ }
745
+ if (typeof arr == "object") {
746
+ if (Array.isArray(arr)) {
747
+ checkLoopIterations(arr.length);
748
+ return arr.map((v, i) => ({ k: i, v }));
749
+ }
750
+ const keys = Object.keys(arr);
751
+ checkLoopIterations(keys.length);
752
+ return keys.map((k) => ({ k, v: arr[k] }));
753
+ }
754
+ if (typeof arr == "string") {
755
+ const values2 = [];
756
+ for (const v of arr) {
757
+ values2.push({ k: values2.length, v });
758
+ checkLoopIterations(values2.length, false);
759
+ }
760
+ return values2;
761
+ }
762
+ })();
763
+ if (!values) {
764
+ ctx.logwarn(`For loop source is not object, string or integer (${arr})` + pos(node));
765
+ return "";
766
+ }
767
+ return values.flatMap(({ k, v }) => {
768
+ ctx.signal?.throwIfAborted();
769
+ ctx.iterations++;
770
+ if (ctx.options.maxTotalIterations !== void 0 && ctx.iterations > ctx.options.maxTotalIterations)
771
+ throw new UserInputError(`Maximum total loop iterations exceeded (${ctx.options.maxTotalIterations})`);
772
+ const locals = Object.assign(/* @__PURE__ */ Object.create(null), variables);
773
+ locals[node.varValue] = v;
774
+ if (node.varKey) locals[node.varKey] = k;
775
+ return execExpTree(node.body, locals, ctx);
776
+ });
777
+ }
778
+ node;
779
+ return "";
780
+ });
781
+ function pos(node) {
782
+ return ` [Ln ${node.source.ln}, Col ${node.source.col}] (${node.source.fname})`;
783
+ }
784
+ function strOperand(v) {
785
+ return v.t == "val" ? JSON.stringify(v.val) : v.path.join(".");
786
+ }
787
+ function isEqual(obj1, obj2) {
788
+ if (obj1 === obj2) return true;
789
+ if (obj1 == null || obj2 == null || typeof obj1 !== "object" || typeof obj2 !== "object")
790
+ return obj1 == obj2;
791
+ const keys1 = Object.keys(obj1);
792
+ const keys2 = Object.keys(obj2);
793
+ if (keys1.length !== keys2.length) return false;
794
+ for (const key of keys1)
795
+ if (!Object.hasOwn(obj2, key) || !isEqual(obj1[key], obj2[key]))
796
+ return false;
797
+ return true;
798
+ }
799
+ function execOperator(left, operator, right) {
800
+ const leftV = left.t == "val" ? left.val : getByPath(variables, left.path);
801
+ if (!operator || !right) return leftV;
802
+ const rightV = right.t == "val" ? right.val : getByPath(variables, right.path);
803
+ if (operator == "==") return isEqual(leftV, rightV);
804
+ if (operator == "!=") return !isEqual(leftV, rightV);
805
+ if (operator == "<=") return leftV < rightV || isEqual(leftV, rightV);
806
+ if (operator == ">=") return leftV > rightV || isEqual(leftV, rightV);
807
+ if (operator == "<") return leftV < rightV;
808
+ if (operator == ">") return leftV > rightV;
809
+ if (operator == "in") return !!rightV && typeof rightV == "object" && (Array.isArray(rightV) ? rightV : Object.keys(rightV)).some((v) => isEqual(v, leftV));
810
+ if (operator == "and" || operator == "&&") return leftV && rightV;
811
+ if (operator == "or" || operator == "||") return leftV || rightV;
812
+ if (operator == "??") return leftV ?? rightV;
813
+ if (operator == "+") return leftV + rightV;
814
+ if (operator == "-") return leftV - rightV;
815
+ if (operator == "*") return leftV * rightV;
816
+ if (operator == "**") return Math.pow(leftV, rightV);
817
+ if (operator == "/") return rightV == 0 ? 0 : leftV / rightV;
818
+ if (operator == "//") return Math.floor(rightV == 0 ? 0 : leftV / rightV);
819
+ if (operator == "%") return rightV == 0 ? 0 : leftV % rightV;
820
+ operator;
821
+ return false;
822
+ }
823
+ }
824
+ function parseIncludePath(value) {
825
+ value = value.trim();
826
+ const trimLeft = value.startsWith("-");
827
+ if (trimLeft) value = value.slice(1).trimStart();
828
+ const trimLeftInner = value.startsWith("-");
829
+ if (trimLeftInner) value = value.slice(1).trimStart();
830
+ const trimRight = value.endsWith("-");
831
+ if (trimRight) value = value.slice(0, -1).trimEnd();
832
+ const trimRightInner = value.endsWith("-");
833
+ if (trimRightInner) value = value.slice(0, -1).trimEnd();
834
+ const path7 = trimEnd(trimStart(value, "<", '"'), ">", '"');
835
+ return { path: path7, trimLeft, trimLeftInner, trimRight, trimRightInner };
836
+ }
837
+ function findClosingBrace(content, start) {
838
+ let depth = 0;
839
+ let inString = false;
840
+ let lineComment = false;
841
+ let blockComment = false;
842
+ for (let i = start; i < content.length; i++) {
843
+ const ch = content[i];
844
+ if (lineComment) {
845
+ if (ch == "\n") lineComment = false;
846
+ continue;
847
+ }
848
+ if (blockComment) {
849
+ if (ch == "*" && content[i + 1] == "/") {
850
+ blockComment = false;
851
+ i++;
852
+ }
853
+ continue;
854
+ }
855
+ if (inString) {
856
+ if (ch == "\\") i++;
857
+ else if (ch == '"') inString = false;
858
+ continue;
859
+ }
860
+ if (ch == '"') {
861
+ inString = true;
862
+ continue;
863
+ }
864
+ if (ch == "/" && content[i + 1] == "/") {
865
+ lineComment = true;
866
+ i++;
867
+ continue;
868
+ }
869
+ if (ch == "/" && content[i + 1] == "*") {
870
+ blockComment = true;
871
+ i++;
872
+ continue;
873
+ }
874
+ if (ch == "{") depth++;
875
+ else if (ch == "}" && --depth == 0) return i;
876
+ }
877
+ return -1;
878
+ }
879
+ async function parseIncludes(content, logwarn) {
880
+ const { default: JSONC } = await import("jsonc-simple-parser");
881
+ const re_doc = /^!!\(([^{}]*)\)\s*{(.*)}$/s;
882
+ const re_codeFence = /^( {0,3})(`{3,})([^`]*)$/;
883
+ const nodes = [];
884
+ let textStart = 0;
885
+ function pushText(start, end) {
886
+ if (start >= end) return;
887
+ const value = content.slice(start, end);
888
+ const prev = nodes.at(-1);
889
+ if (prev?.type === "text")
890
+ prev.value += value;
891
+ else
892
+ nodes.push({ type: "text", value });
893
+ }
894
+ let codeFence = 0;
895
+ for (let i = 0; i < content.length; i++) {
896
+ if (i != 0 && content[i - 1] != "\n") continue;
897
+ const lineEnd = content.indexOf("\n", i);
898
+ const line = content.slice(i, lineEnd < 0 ? content.length : lineEnd).replace(/\r$/, "");
899
+ const m_fence = re_codeFence.exec(line);
900
+ if (m_fence) {
901
+ const ticks = m_fence[2].length;
902
+ const suffix = m_fence[3];
903
+ if (codeFence == 0) {
904
+ codeFence = ticks;
905
+ continue;
906
+ }
907
+ if (ticks >= codeFence && suffix.trim() == "") {
908
+ codeFence = 0;
909
+ continue;
910
+ }
911
+ }
912
+ if (codeFence != 0) continue;
913
+ if (content.slice(i, i + 2) !== "!!") continue;
914
+ const m_start = /^!!\([^{}]*\)\s*{/.exec(content.slice(i));
915
+ if (!m_start) continue;
916
+ const end = findClosingBrace(content, i + m_start[0].length - 1);
917
+ if (end < 0) continue;
918
+ const block = content.slice(i, end + 1);
919
+ const m_doc = re_doc.exec(block);
920
+ if (!m_doc) continue;
921
+ const { path: path7, trimLeft, trimLeftInner, trimRight, trimRightInner } = parseIncludePath(m_doc[1]);
922
+ if (!path7.toLowerCase().endsWith(".md")) continue;
923
+ let dict = {};
924
+ try {
925
+ dict = JSONC.parse(`{${m_doc[2]}}`);
926
+ } catch {
927
+ logwarn(`Can't parse include dict: {${m_doc[2].replaceAll("\n", " ")}}`);
928
+ continue;
929
+ }
930
+ let textEnd = i;
931
+ if (trimLeft)
932
+ while (textEnd > textStart && /\s/.test(content[textEnd - 1])) textEnd--;
933
+ pushText(textStart, textEnd);
934
+ nodes.push({ type: "include", path: path7, dict, sourceLineCount: block.split("\n").length, trimStart: trimLeftInner, trimEnd: trimRightInner });
935
+ let next = end + 1;
936
+ if (trimRight)
937
+ while (next < content.length && /\s/.test(content[next])) next++;
938
+ textStart = next;
939
+ i = next - 1;
940
+ }
941
+ pushText(textStart, content.length);
942
+ return nodes;
943
+ }
944
+ async function fixLinks(content, fixLink) {
945
+ const { default: JSONC } = await import("jsonc-simple-parser");
946
+ const re_codeFence = /^( {0,3})(`{3,})([^`]*)$/;
947
+ let result = "";
948
+ let textStart = 0;
949
+ function append(start, end) {
950
+ if (start >= end) return;
951
+ result += content.slice(start, end);
952
+ }
953
+ let codeFence = 0;
954
+ for (let i = 0; i < content.length; i++) {
955
+ if (i != 0 && content[i - 1] != "\n") continue;
956
+ const lineEnd = content.indexOf("\n", i);
957
+ const lineTextEnd = lineEnd < 0 ? content.length : lineEnd - (content[lineEnd - 1] == "\r" ? 1 : 0);
958
+ const line = content.slice(i, lineTextEnd);
959
+ const m_fence = re_codeFence.exec(line);
960
+ if (m_fence) {
961
+ const ticks = m_fence[2].length;
962
+ const suffix = m_fence[3];
963
+ if (codeFence == 0) {
964
+ codeFence = ticks;
965
+ continue;
966
+ }
967
+ if (ticks >= codeFence && suffix.trim() == "") {
968
+ codeFence = 0;
969
+ continue;
970
+ }
971
+ }
972
+ if (codeFence != 0) continue;
973
+ if (content.at(i) != "!") continue;
974
+ const m_img = /^!\[(.*)\]\((.*)\)({(.*)})?$/.exec(line);
975
+ if (m_img) {
976
+ append(textStart, i);
977
+ result += `![${m_img[1]}](${fixLink(m_img[2])})${m_img[3] || ""}`;
978
+ textStart = lineTextEnd;
979
+ i = lineEnd < 0 ? content.length : lineEnd;
980
+ continue;
981
+ }
982
+ const m_start = /^!!\([^{}]*\)\s*{/.exec(content.slice(i));
983
+ if (!m_start) continue;
984
+ const end = findClosingBrace(content, i + m_start[0].length - 1);
985
+ if (end < 0) continue;
986
+ const block = content.slice(i, end + 1);
987
+ const m_doc = /^!!\(([^{}]*)\)(\s*{(.*)})$/s.exec(block);
988
+ if (!m_doc) continue;
989
+ try {
990
+ JSONC.parse(m_doc[2].trim());
991
+ } catch {
992
+ continue;
993
+ }
994
+ const { path: path7, trimLeft, trimLeftInner, trimRight, trimRightInner } = parseIncludePath(m_doc[1]);
995
+ let fixedPath = fixLink(path7);
996
+ if (fixedPath.startsWith("-") || fixedPath.endsWith("-"))
997
+ fixedPath = `"${fixedPath}"`;
998
+ append(textStart, i);
999
+ result += `!!(${trimLeft ? "-" : ""}${trimLeftInner ? "-" : ""}${fixedPath}${trimRightInner ? "-" : ""}${trimRight ? "-" : ""})${m_doc[2]}`;
1000
+ textStart = end + 1;
1001
+ i = end;
1002
+ }
1003
+ append(textStart, content.length);
1004
+ return result;
1005
+ }
1006
+
1007
+ // src/parser.ts
1008
+ async function parseMD(file, variables, templateOptions = {}, checkFilesIsInsidePath = false, signal, logwarn = console.warn) {
1009
+ signal?.throwIfAborted();
1010
+ const preprocessed = await preprocess(file, variables, checkFilesIsInsidePath, templateOptions, signal, logwarn);
1011
+ signal?.throwIfAborted();
1012
+ const lines = preprocessed.split("\n");
224
1013
  const doc = new Doc();
1014
+ const warnAt = (line, message) => logwarn(`Line ${doc.mapSourceLine(line)}: ${message}`);
1015
+ (function processLineSkips() {
1016
+ for (let i = 0; i < lines.length; i++) {
1017
+ const line = lines[i];
1018
+ if (line.at(0) != "\uE100") continue;
1019
+ const m = /\uE100(-?\d+)\uE101/.exec(line);
1020
+ if (!m) {
1021
+ lines[i] = line.replaceAll("\uE100", "").replaceAll("\uE101", "");
1022
+ continue;
1023
+ }
1024
+ lines[i] = line.slice(m?.[0].length);
1025
+ doc.addLineSkip(i + 1, parseInt(m[1]));
1026
+ }
1027
+ })();
225
1028
  try {
226
- doc.title = toCapitalCase(trimEnd(import_path.default.parse(file).name, ".g"));
227
- const stats = await import_promises2.default.stat(file);
1029
+ doc.title = toCapitalCase(trimEnd(import_path2.default.parse(file).name, ".g"));
1030
+ const stats = await import_promises3.default.stat(file);
228
1031
  doc.ctime = stats.birthtime;
229
1032
  } catch {
230
1033
  }
231
1034
  function parseList(text, parts, ordered, level = 0) {
232
1035
  const startIndex = isFinite(parseInt(parts[0])) ? parseInt(parts[0]) : 1;
233
1036
  const mark = ordered ? parts[1] == "." ? "." : ")" : parts[1] == "*" ? "*" : "-";
234
- const node = { type: "list", ordered, mark, startIndex, items: [{ type: "listItem", text }] };
1037
+ const node = { type: "list", ordered, mark, startIndex, sourceLine: lineI, items: [{ type: "listItem", text, sourceLine: lineI }] };
235
1038
  const P = ordered ? "1)" : "*";
236
1039
  function skipEmptyLines() {
237
1040
  let emptyFound = false;
@@ -263,7 +1066,7 @@ async function parseMD(file, logwarn = console.warn) {
263
1066
  if (ln.prefix == "" && last?.type == "listItem")
264
1067
  last.text += "\n" + ln.text;
265
1068
  else if (ln.prefix == P)
266
- node.items.push({ type: "listItem", text: ln.text });
1069
+ node.items.push({ type: "listItem", text: ln.text, sourceLine: lineI + 1 });
267
1070
  else break;
268
1071
  } else if (ln.level > level) {
269
1072
  if (ln.level == level + 1) ln = parseLine(ln.text);
@@ -292,19 +1095,19 @@ async function parseMD(file, logwarn = console.warn) {
292
1095
  if (isFinite(h)) height = h;
293
1096
  }
294
1097
  const src = trimEnd(trimStart(parts[1], "<"), ">");
295
- return { type: "image", text, src, width, height };
1098
+ return { type: "image", text, src, width, height, sourceLine: lineI };
296
1099
  }
297
1100
  function parseCode(text) {
298
1101
  const header = text.split(" ");
299
1102
  const title = header.slice(1).join(" ").trim() || void 0;
300
- const node = { type: "code", lang: header[0], title, code: "" };
301
- text = "";
1103
+ const node = { type: "code", lang: header[0], title, code: "", sourceLine: lineI };
1104
+ const code = [];
302
1105
  while (lineI < lines.length) {
303
1106
  const ln = lines[lineI++];
304
1107
  if (ln.startsWith("```")) break;
305
- text += "\n" + ln;
1108
+ code.push(ln);
306
1109
  }
307
- node.code = text.trim();
1110
+ node.code = code.join("\n");
308
1111
  return node;
309
1112
  }
310
1113
  function parseSection(text) {
@@ -317,9 +1120,78 @@ async function parseMD(file, logwarn = console.warn) {
317
1120
  return {
318
1121
  type: "sectionBreak",
319
1122
  pageStart: unpaged ? -1 : isFinite(num) ? num : null,
320
- orientation: portrait ? "portrait" : landscape ? "landscape" : null
1123
+ orientation: portrait ? "portrait" : landscape ? "landscape" : null,
1124
+ sourceLine: lineI
321
1125
  };
322
1126
  }
1127
+ function parseHeaderFooter(kind, options) {
1128
+ const value = options.trim().toLowerCase();
1129
+ if (value == "none" || kind == "footer" && value == "auto") {
1130
+ setHeaderFooter(kind, { type: value });
1131
+ return;
1132
+ }
1133
+ const align = value == "" ? "left" : /^align=(left|center|right)$/.exec(value)?.[1];
1134
+ if (!align) {
1135
+ warnAt(lineI, `Wrong ${kind} options: "${options}". Expected align=left, align=center, align=right${kind == "footer" ? ", none, or auto" : ", or none"}.`);
1136
+ return;
1137
+ }
1138
+ const end = `!!end${kind}`;
1139
+ const content = [];
1140
+ const initialLineI = lineI;
1141
+ let closed = false;
1142
+ while (lineI < lines.length) {
1143
+ const line = lines[lineI++];
1144
+ if (line.trim().toLowerCase() == end) {
1145
+ closed = true;
1146
+ break;
1147
+ }
1148
+ content.push(line);
1149
+ }
1150
+ if (!closed) {
1151
+ lineI = initialLineI;
1152
+ warnAt(initialLineI, `${kind} block is not closed`);
1153
+ return;
1154
+ }
1155
+ const nodes = [{ type: "text", text: content.join("\n").trim(), sourceLine: initialLineI + 1 }];
1156
+ findTables(nodes, false);
1157
+ const supported = nodes.filter((node) => node.type == "text" || node.type == "table");
1158
+ if (supported.length != 1 || supported[0]?.type == "text" && !supported[0].text) {
1159
+ warnAt(initialLineI, `${kind} must contain text or one table`);
1160
+ return;
1161
+ }
1162
+ setHeaderFooter(kind, { type: "content", align, nodes: supported });
1163
+ function setHeaderFooter(kind2, value2) {
1164
+ let section;
1165
+ for (let i = doc.nodes.length - 1; i >= 0; i--) {
1166
+ if (doc.nodes[i]?.type == "sectionBreak") {
1167
+ section = doc.nodes[i];
1168
+ break;
1169
+ }
1170
+ }
1171
+ if (section?.type == "sectionBreak") section[kind2] = value2;
1172
+ else doc[kind2] = value2;
1173
+ }
1174
+ }
1175
+ function parseAdmonition(parts) {
1176
+ const marker = parts[0];
1177
+ const type = parts[1];
1178
+ const title = parts[2];
1179
+ const attributes = parts[3];
1180
+ const content = [];
1181
+ const close = new RegExp(`^${marker}\\s*$`);
1182
+ const initialLineI = lineI;
1183
+ let closed = false;
1184
+ while (lineI < lines.length) {
1185
+ const line = lines[lineI++];
1186
+ if (close.test(line)) {
1187
+ closed = true;
1188
+ break;
1189
+ }
1190
+ content.push(line);
1191
+ }
1192
+ if (!closed) warnAt(initialLineI, `Admonition "${type}" is not closed`);
1193
+ return { type: "admonition", admonitionType: type, title, text: content.filter((t) => !!t.trim()).join("\n").trim(), attributes, sourceLine: initialLineI };
1194
+ }
323
1195
  class RuleError extends Error {
324
1196
  }
325
1197
  ;
@@ -389,9 +1261,43 @@ async function parseMD(file, logwarn = console.warn) {
389
1261
  "text spacing after": (v) => doc.text.spacing.after = tryParseInt(v),
390
1262
  "img text size": (v) => doc.img.text.size = tryParseInt(v),
391
1263
  "img spacing before": (v) => doc.img.spacing.before = tryParseInt(v),
392
- "img spacing after": (v) => doc.img.spacing.after = tryParseInt(v)
1264
+ "img spacing after": (v) => doc.img.spacing.after = tryParseInt(v),
1265
+ "formula spacing before": (v) => doc.formula.spacing.before = tryParseInt(v),
1266
+ "formula spacing after": (v) => doc.formula.spacing.after = tryParseInt(v)
393
1267
  };
394
1268
  const reRules = [
1269
+ {
1270
+ re: /^(admonition (all|note|info|tip|warning|danger) (spacing (before|after)|padding|indent|background|color|bar_width|icon size|icon|title_color|title))(.*)/,
1271
+ n: (m) => ({ rule: m[1], value: (m.at(-1) || "").trim() }),
1272
+ f(m) {
1273
+ const type = m[2];
1274
+ const property = m[3];
1275
+ const value = (m.at(-1) || "").trim();
1276
+ const valueKeepCase = text.slice(m[1].length).trim();
1277
+ const types = type == "all" ? ["note", "info", "tip", "warning", "danger"] : [type];
1278
+ for (const type2 of types) {
1279
+ const style = doc.admonition[type2];
1280
+ if (property == "spacing before") style.spacing.before = tryParseInt(value);
1281
+ else if (property == "spacing after") style.spacing.after = tryParseInt(value);
1282
+ else if (property == "indent") style.indent = tryParseFloat(value);
1283
+ else if (property == "padding") {
1284
+ const padding = value.split(" ").map(tryParseInt);
1285
+ if (padding.length != 2) throw new RuleError("Two values are required: horizontal vertical");
1286
+ style.padding = { top: padding[1], right: padding[0], bottom: padding[1], left: padding[0] };
1287
+ } else if (property == "background") style.background = tryParseColor(value);
1288
+ else if (property == "color") style.color = tryParseColor(value);
1289
+ else if (property == "bar_width") style.bar_width = tryParseFloat(value);
1290
+ else if (property == "icon size") style.icon_size = tryParseInt(value);
1291
+ else if (property == "icon") {
1292
+ choices(value, "on", "off");
1293
+ style.icon = value == "on";
1294
+ } else if (property == "title_color") {
1295
+ choices(value, "on", "off");
1296
+ style.title_color = value == "on";
1297
+ } else if (property == "title") style.title = valueKeepCase;
1298
+ }
1299
+ }
1300
+ },
395
1301
  {
396
1302
  re: /^(headings (h[1-6])(\+?) (size|spacing (before|after)|uppercase|indent))(.*)/,
397
1303
  n: (m) => ({ rule: m[1], value: (m.at(-1) || "").trim() }),
@@ -429,11 +1335,11 @@ async function parseMD(file, logwarn = console.warn) {
429
1335
  rule = n.rule;
430
1336
  value = n.value;
431
1337
  }
432
- if (e instanceof RuleError) logwarn(`Rule "${rule}" wrong value: "${value}". ${e.message}`);
1338
+ if (e instanceof RuleError) warnAt(lineI, `Rule "${rule}" wrong value: "${value}". ${e.message}`);
433
1339
  else throw e;
434
1340
  }
435
1341
  } else {
436
- logwarn(`Wrong rule: "${text}"`);
1342
+ warnAt(lineI, `Wrong rule: "${text}"`);
437
1343
  }
438
1344
  function tryParseInt(v) {
439
1345
  const num = parseInt(v);
@@ -450,6 +1356,11 @@ async function parseMD(file, logwarn = console.warn) {
450
1356
  if (!isFinite(date.valueOf())) throw new RuleError("Only ISO 8601 date is allowed");
451
1357
  return date;
452
1358
  }
1359
+ function tryParseColor(v) {
1360
+ const color = /^#?([\da-f]{6})$/i.exec(v)?.[1];
1361
+ if (!color) throw new RuleError("Only #RRGGBB colors are allowed");
1362
+ return color;
1363
+ }
453
1364
  function choices(v, ...vars) {
454
1365
  if (vars.includes(v)) return v;
455
1366
  throw new RuleError("Allowed: " + vars.join(", "));
@@ -463,66 +1374,85 @@ async function parseMD(file, logwarn = console.warn) {
463
1374
  while (lineI < lines.length) {
464
1375
  const line = lines[lineI++];
465
1376
  const { prefix, text, parts } = parseLine(line);
466
- switch (prefix) {
467
- case "#":
468
- doc.appendTitle(text, 1);
469
- break;
470
- case "##":
471
- doc.appendTitle(text, 2);
472
- break;
473
- case "###":
474
- doc.appendTitle(text, 3);
475
- break;
476
- case "####":
477
- doc.appendTitle(text, 4);
478
- break;
479
- case "#####":
480
- doc.appendTitle(text, 5);
481
- break;
482
- case "######":
483
- doc.appendTitle(text, 6);
484
- break;
485
- case "*":
486
- doc.appendNode(parseList(text, parts, false));
487
- break;
488
- case "1)":
489
- doc.appendNode(parseList(text, parts, true));
490
- break;
491
- case "Img":
492
- doc.appendNode(parseImg(parts));
493
- break;
494
- case "Code":
495
- doc.appendNode(parseCode(text));
496
- break;
497
- case "Comment":
498
- skipComment();
499
- break;
500
- case "!!section":
501
- doc.appendNode(parseSection(text));
502
- break;
503
- case "!!rule":
504
- apllyRule(text);
505
- break;
506
- case "---":
507
- doc.appendNode({ type: "pageBreak" });
508
- break;
509
- case "":
510
- case " ":
511
- const last = doc.nodes.at(-1);
512
- if (line.trim() != "" && last?.type == "text") {
513
- if (last.text != "") last.text += "\n";
514
- last.text += line.trim();
515
- } else doc.appendText(line.trim());
516
- break;
517
- default:
518
- prefix;
519
- throw new Error("switch default");
1377
+ try {
1378
+ switch (prefix) {
1379
+ case "#":
1380
+ doc.appendTitle(text, 1, lineI);
1381
+ break;
1382
+ case "##":
1383
+ doc.appendTitle(text, 2, lineI);
1384
+ break;
1385
+ case "###":
1386
+ doc.appendTitle(text, 3, lineI);
1387
+ break;
1388
+ case "####":
1389
+ doc.appendTitle(text, 4, lineI);
1390
+ break;
1391
+ case "#####":
1392
+ doc.appendTitle(text, 5, lineI);
1393
+ break;
1394
+ case "######":
1395
+ doc.appendTitle(text, 6, lineI);
1396
+ break;
1397
+ case "*":
1398
+ doc.appendNode(parseList(text, parts, false));
1399
+ break;
1400
+ case "1)":
1401
+ doc.appendNode(parseList(text, parts, true));
1402
+ break;
1403
+ case "Img":
1404
+ doc.appendNode(parseImg(parts));
1405
+ break;
1406
+ case "Code":
1407
+ doc.appendNode(parseCode(text));
1408
+ break;
1409
+ case "Admonition":
1410
+ doc.appendNode(parseAdmonition(parts));
1411
+ break;
1412
+ case "Comment":
1413
+ skipComment();
1414
+ break;
1415
+ case "!!section":
1416
+ doc.appendNode(parseSection(text));
1417
+ break;
1418
+ case "!!header":
1419
+ parseHeaderFooter("header", text);
1420
+ break;
1421
+ case "!!footer":
1422
+ parseHeaderFooter("footer", text);
1423
+ break;
1424
+ case "!!rule":
1425
+ apllyRule(text);
1426
+ break;
1427
+ case "---":
1428
+ doc.appendNode({ type: "pageBreak", sourceLine: lineI });
1429
+ break;
1430
+ case "":
1431
+ case " ":
1432
+ const last = doc.nodes.at(-1);
1433
+ if (line.trim() != "" && last?.type == "text") {
1434
+ if (last.text == "") last.sourceLine = lineI;
1435
+ else last.text += "\n";
1436
+ last.text += line.trim();
1437
+ } else doc.appendText(line.trim(), lineI);
1438
+ break;
1439
+ default:
1440
+ prefix;
1441
+ throw new Error("switch default");
1442
+ }
1443
+ } catch (error) {
1444
+ const message = error instanceof Error ? error.message : String(error);
1445
+ throw new Error(`Line ${lineI}: ${message}`, { cause: error });
520
1446
  }
521
1447
  }
1448
+ doc.nodes.forEach((node) => {
1449
+ if (node.type == "admonition" && node.title == "\n")
1450
+ node.title = doc.admonition[node.admonitionType].title;
1451
+ });
522
1452
  doc.nodes = doc.nodes.filter((n) => n.type != "text" || n.text != "");
523
- findDocs(doc.nodes, logwarn);
1453
+ await findDocs(doc.nodes, warnAt);
1454
+ findMath(doc.nodes, warnAt);
524
1455
  findTables(doc.nodes);
525
- findPageBreaks(doc.nodes);
526
1456
  return doc;
527
1457
  }
528
1458
  function parseLine(line) {
@@ -539,7 +1469,7 @@ function parseLine(line) {
539
1469
  const text = splited.slice(1).join(" ");
540
1470
  let parts = [];
541
1471
  if (prefix.startsWith("!")) {
542
- const m_img = /!\[(.*)\]\((.*)\)({(.*)})?/.exec(line);
1472
+ const m_img = /^!\[(.*)\]\((.*)\)({(.*)})?/.exec(line);
543
1473
  if (m_img) return {
544
1474
  prefix: "Img",
545
1475
  text: line.trim(),
@@ -559,29 +1489,37 @@ function parseLine(line) {
559
1489
  }
560
1490
  }
561
1491
  if (prefix.startsWith("```")) return { prefix: "Code", text: line.trim().slice(3), level, parts };
1492
+ const m_admonition = /^(:{3,})(note|info|tip|warning|danger)(?:\[([^\]]*)\])?(?:\{([^}]*)\})?\s*$/i.exec(line.trim());
1493
+ if (m_admonition) return {
1494
+ prefix: "Admonition",
1495
+ text: "",
1496
+ level,
1497
+ parts: [m_admonition[1], m_admonition[2].toLowerCase(), m_admonition[3] ?? "\n", m_admonition[4] || ""]
1498
+ };
562
1499
  if (prefix.startsWith("<!--")) return { prefix: "Comment", text: line.trim().slice("<!--".length).trim(), level, parts };
563
- if (["#", "##", "###", "####", "#####", "######", "*", "1)", "!!section", "!!rule"].includes(prefix))
1500
+ if (["#", "##", "###", "####", "#####", "######", "*", "1)", "!!section", "!!header", "!!footer", "!!rule"].includes(prefix))
564
1501
  return { prefix, text, level: 0, parts };
565
1502
  return { prefix: "", text: line.trim(), level, parts };
566
1503
  }
567
- function findDocs(nodes, logwarn = console.warn) {
1504
+ async function findDocs(nodes, warnAt) {
1505
+ const { default: JSONC } = await import("jsonc-simple-parser");
568
1506
  const re_doc = /^!!\(([^{}]*)\)\s*{(.*)}$/s;
569
- const re_remTrailingComma = /,(\s*[}\]])/g;
570
1507
  for (let i = 0; i < nodes.length; i++) {
571
1508
  const node = nodes[i];
572
1509
  if (node.type != "text") continue;
573
- const m_doc = re_doc.exec(node.text.trimEnd().replaceAll(re_remTrailingComma, "$1"));
1510
+ const m_doc = re_doc.exec(node.text.trimEnd());
574
1511
  if (!m_doc) continue;
575
1512
  let dict = {};
576
1513
  try {
577
- dict = JSON.parse(`{${m_doc[2]}}`);
1514
+ dict = JSONC.parse(`{${m_doc[2]}}`);
578
1515
  } catch {
579
- logwarn(`Cant parse doc dict: {${m_doc[2].replaceAll("\n", " ")}}`);
1516
+ warnAt(node.sourceLine ?? -1, `Cant parse doc dict: {${m_doc[2].replaceAll("\n", " ")}}`);
580
1517
  }
581
1518
  nodes.splice(i, 1, {
582
1519
  type: "externalDoc",
583
1520
  path: trimEnd(trimStart(m_doc[1], "<", '"'), ">", '"'),
584
- dict: stringifyDict(dict)
1521
+ dict: stringifyDict(dict),
1522
+ sourceLine: node.sourceLine
585
1523
  });
586
1524
  }
587
1525
  }
@@ -605,7 +1543,7 @@ function stringifyDict(dict) {
605
1543
  }
606
1544
  return r;
607
1545
  }
608
- function findTables(nodes) {
1546
+ function findTables(nodes, hasHeader = true) {
609
1547
  const re_sep = /^(\s*:?-+:?\s*(?<!\\)\|)+\s*:?-+:?\s*$/;
610
1548
  const re_sep_oneCol = /^\|\s*:?-+:?\s*\|$/;
611
1549
  const trim = (line) => {
@@ -620,19 +1558,20 @@ function findTables(nodes) {
620
1558
  const lines = node.text.split("\n");
621
1559
  const offset = lines[0].includes("|") ? 0 : 1;
622
1560
  if (lines.length < 2 + offset) continue;
623
- const sep = trim(lines[1 + offset]);
624
- if (!re_sep.test(sep) && !re_sep_oneCol.test(lines[1 + offset])) continue;
625
- const cols = sep.split("|");
1561
+ const sep2 = trim(lines[1 + offset]);
1562
+ if (!re_sep.test(sep2) && !re_sep_oneCol.test(lines[1 + offset])) continue;
1563
+ const cols = sep2.split("|");
626
1564
  const header = trim(lines[offset]).split(/(?<!\\)\|/).map((v) => v.trim());
627
1565
  if (header.length != cols.length) continue;
628
1566
  const align = cols.map((v) => v.trim()).map((v) => v.startsWith(":") && v.endsWith(":") ? "c" : v.endsWith(":") ? "r" : "l");
629
- const rows = [tableRow(...header)];
1567
+ let sourceLine = node.sourceLine + offset;
1568
+ const rows = [tableRow(sourceLine++, ...header)];
630
1569
  for (const line of lines.slice(2 + offset)) {
631
1570
  const row = trim(line).split(/(?<!\\)\|/).map((v) => v.trim());
632
1571
  while (row.length < cols.length) row.push("");
633
- rows.push(tableRow(...row));
1572
+ rows.push(tableRow(++sourceLine, ...row));
634
1573
  }
635
- const table = { type: "table", align, rows };
1574
+ const table = { type: "table", align, rows, sourceLine: node.sourceLine, ...hasHeader ? {} : { header: false } };
636
1575
  nodes.splice(i, 1, table);
637
1576
  const prev = nodes[i - 1];
638
1577
  if (offset > 0) {
@@ -644,43 +1583,133 @@ function findTables(nodes) {
644
1583
  }
645
1584
  }
646
1585
  }
647
- function findPageBreaks(nodes) {
648
- const re_sep = /^\s*---+\s*$/;
1586
+ function findMath(nodes, warnAt) {
649
1587
  for (let i = 0; i < nodes.length; i++) {
650
1588
  const node = nodes[i];
651
- if (node.type != "text") continue;
652
- if (!node.text.includes("---")) continue;
1589
+ if (node?.type != "text") continue;
653
1590
  const lines = node.text.split("\n");
654
- const newNodes = [];
655
- for (const line of lines) {
656
- if (re_sep.test(line)) newNodes.push({ type: "pageBreak" });
657
- else {
658
- const last = newNodes.at(-1);
659
- if (last?.type == "text") last.text += "\n" + line;
660
- else newNodes.push({ type: "text", text: line, noIndent: node.noIndent, noMargin: node.noMargin });
1591
+ const replacement = [];
1592
+ let text = [];
1593
+ let textStart = 0;
1594
+ const flush = () => {
1595
+ if (text.length)
1596
+ replacement.push({ type: "text", text: text.join("\n"), sourceLine: (node.sourceLine ?? 0) + textStart });
1597
+ text = [];
1598
+ };
1599
+ for (let line = 0; line < lines.length; line++) {
1600
+ if (lines[line]?.trim() != "$$") {
1601
+ if (!text.length) textStart = line;
1602
+ text.push(lines[line]);
1603
+ continue;
661
1604
  }
1605
+ const start = line;
1606
+ let end = line + 1;
1607
+ while (end < lines.length && lines[end]?.trim() != "$$") end++;
1608
+ if (end >= lines.length) {
1609
+ warnAt((node.sourceLine ?? 0) + start, "Formula is not closed");
1610
+ if (!text.length) textStart = line;
1611
+ text.push(lines[line]);
1612
+ continue;
1613
+ }
1614
+ let id;
1615
+ let manualNumber;
1616
+ const idMatch = /^\s*(\[[a-zA-Zа-яА-ЯёЁ_\d#]+\])\s*$/.exec(text.at(-1) || "");
1617
+ const numberMatch = /^\s*(\(.*\))\s*$/.exec(text.at(-1) || "");
1618
+ if (idMatch) {
1619
+ id = `(${idMatch[1]})`;
1620
+ text.pop();
1621
+ } else if (numberMatch) {
1622
+ manualNumber = numberMatch[1];
1623
+ text.pop();
1624
+ }
1625
+ flush();
1626
+ replacement.push({
1627
+ type: "math",
1628
+ latex: lines.slice(start + 1, end).join("\n").trim(),
1629
+ title: id ?? manualNumber,
1630
+ sourceLine: (node.sourceLine ?? 0) + start
1631
+ });
1632
+ line = end;
1633
+ textStart = end + 1;
1634
+ }
1635
+ flush();
1636
+ if (replacement.some((n) => n.type == "math")) {
1637
+ nodes.splice(i, 1, ...replacement);
1638
+ i += replacement.length - 1;
662
1639
  }
663
- nodes.splice(i, 1, ...newNodes);
664
- i += newNodes.length - 1;
665
1640
  }
666
1641
  }
667
1642
  function runifyDoc(doc) {
668
1643
  function runifyNode(node) {
669
- if ("text" in node && node.text)
1644
+ if ("text" in node && node.text !== void 0)
670
1645
  node.text = runifyText(node.text, doc.rainbow);
671
- if ("title" in node && node.title)
1646
+ if ("title" in node && node.title !== void 0)
672
1647
  node.title = runifyText(node.title, doc.rainbow);
673
1648
  if (node.type == "table")
674
1649
  node.rows.forEach((row) => row.forEach(runifyNode));
675
1650
  if (node.type == "list")
676
1651
  node.items.forEach(runifyNode);
1652
+ if (node.type == "sectionBreak") {
1653
+ runifyHeaderFooter(node.header);
1654
+ runifyHeaderFooter(node.footer);
1655
+ }
677
1656
  }
678
1657
  doc.nodes.forEach(runifyNode);
1658
+ runifyHeaderFooter(doc.header);
1659
+ runifyHeaderFooter(doc.footer);
679
1660
  return doc;
1661
+ function runifyHeaderFooter(value) {
1662
+ if (value?.type != "content") return;
1663
+ value.nodes.forEach(runifyNode);
1664
+ }
680
1665
  }
681
1666
  var rainbowI = 0;
682
1667
  function runifyText(text, rainbow = false) {
683
- return replaceAmpCodes(text).replaceAll("\n", "&Tab;\n").replaceAll(/\s*<br>\s*/g, "\n").replaceAll("\u2014", "-").replaceAll(" - ", " \u2013 ").replaceAll(/(?<=[\p{L}\p{N}])-(?=[\p{L}\p{N}])/gu, "\u2011").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("*", "&Star;")).split(/(\[[^\]]*\]\([^)]*\))/g).map((p) => {
1668
+ const formulas = [];
1669
+ const codeSpans = [];
1670
+ function findClosingBackticks(from, count) {
1671
+ for (let i = from; i < text.length; ) {
1672
+ const start = text.indexOf("`", i);
1673
+ if (start < 0) return -1;
1674
+ let end = start + 1;
1675
+ while (text[end] == "`") end++;
1676
+ if (end - start == count) return start;
1677
+ i = end;
1678
+ }
1679
+ return -1;
1680
+ }
1681
+ ;
1682
+ let preprocessedText = "";
1683
+ for (let i = 0; i < text.length; ) {
1684
+ if (text[i] == "`") {
1685
+ let delimiterEnd = i + 1;
1686
+ while (text[delimiterEnd] == "`") delimiterEnd++;
1687
+ const delimiterLength = delimiterEnd - i;
1688
+ const end = findClosingBackticks(delimiterEnd, delimiterLength);
1689
+ if (end >= 0) {
1690
+ codeSpans.push(text.slice(delimiterEnd, end));
1691
+ preprocessedText += `\uE002${codeSpans.length - 1}\uE003`;
1692
+ i = end + delimiterLength;
1693
+ continue;
1694
+ }
1695
+ preprocessedText += text.slice(i, delimiterEnd);
1696
+ i = delimiterEnd;
1697
+ continue;
1698
+ }
1699
+ if (text[i] == "$" && text[i - 1] != "\\" && text[i + 1] != "$") {
1700
+ let end = i + 1;
1701
+ while (end < text.length && text[end] != "\n" && (text[end] != "$" || text[end - 1] == "\\")) end++;
1702
+ if (end < text.length && text[end] == "$") {
1703
+ formulas.push(text.slice(i + 1, end));
1704
+ preprocessedText += `\uE000${formulas.length - 1}\uE001`;
1705
+ i = end + 1;
1706
+ continue;
1707
+ }
1708
+ }
1709
+ preprocessedText += text[i++];
1710
+ }
1711
+ text = preprocessedText.replaceAll("\\$", "$");
1712
+ return replaceAmpCodes(text).replaceAll("\n", "&Tab;\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("*", "&Star;")).split(/(\[[^\]]*\]\([^)]*\))/g).map((p) => {
684
1713
  const m = /\[([^\]]*)\]\(([^)]*)\)/.exec(p);
685
1714
  if (!m) return { text: p };
686
1715
  return { text: m[1], link: m[2] };
@@ -706,17 +1735,23 @@ function runifyText(text, rainbow = false) {
706
1735
  link: rune.link,
707
1736
  bold: rune.bold,
708
1737
  italic: i % 2 == 1 && i != arr.length - 1 || rune.italic
709
- }))).flat().map((rune) => rune.text.split("```").map((p, i, arr) => ({
710
- ...rune,
711
- text: p,
712
- linebreak: i == 0 && rune.linebreak,
713
- mono: i % 2 == 1 && i != arr.length - 1
714
- }))).flat().map((rune) => rune.mono ? rune : rune.text.split("`").map((p, i, arr) => ({
715
- ...rune,
716
- text: p,
717
- linebreak: i == 0 && rune.linebreak,
718
- mono: i % 2 == 1 && i != arr.length - 1
719
- }))).flat().map((rune) => rune.link ? [rune] : rune.text.split(/(\[!?[a-zA-Zа-яА-ЯёЁ_\d#]+\s*[+-]?\s*\d*\])/g).map((p, i) => {
1738
+ }))).flat().map((rune) => rune.text.split(/(\uE002\d+\uE003)/).map((p, i) => {
1739
+ const m = /^\uE002(\d+)\uE003$/.exec(p);
1740
+ return {
1741
+ ...rune,
1742
+ text: m ? codeSpans[parseInt(m[1])] : p,
1743
+ mono: m ? true : rune.mono,
1744
+ linebreak: i == 0 && rune.linebreak
1745
+ };
1746
+ })).flat().map((rune) => rune.mono ? [rune] : rune.text.split(/(\uE000\d+\uE001)/).map((p, i) => {
1747
+ const m = /^\uE000(\d+)\uE001$/.exec(p);
1748
+ return {
1749
+ ...rune,
1750
+ text: m ? formulas[parseInt(m[1])] : p,
1751
+ type: m ? "math" : rune.type,
1752
+ linebreak: i == 0 && rune.linebreak
1753
+ };
1754
+ })).flat().map((rune) => rune.link || rune.mono ? [rune] : rune.text.split(/(\[!?[a-zA-Zа-яА-ЯёЁ_\d#]+\s*[+-]?\s*\d*\])/g).map((p, i) => {
720
1755
  const m = /\[(!?([a-zA-Zа-яА-ЯёЁ_\d]+|#)(\s*[-+]\s*\d+)?)\]/.exec(p);
721
1756
  const v = m && m[1];
722
1757
  const isVal = v?.at(0) == "!";
@@ -726,12 +1761,14 @@ function runifyText(text, rainbow = false) {
726
1761
  type: v ? isVal ? "val" : "ref" : rune.type,
727
1762
  linebreak: i == 0 && rune.linebreak
728
1763
  };
729
- })).flat().map((rune) => ({
1764
+ })).flat().map((rune, i, arr) => ({
730
1765
  ...rune,
731
1766
  ...!rune.type || rune.type == "text" ? {
732
- text: rune.text.replaceAll("&Star;", "*").replaceAll("&#x200B;", "")
1767
+ text: formatTypography(
1768
+ arr[i + 1]?.type == "ref" || arr[i + 1]?.type == "val" ? rune.text.replace(/[ \t]+$/, NBSP) : rune.text
1769
+ ).replaceAll("&Star;", "*").replaceAll("&#x200B;", "")
733
1770
  } : {}
734
- })).map((rune) => !rainbow || rune.type && rune.type != "text" ? [rune] : rune.text.split("").map((p, i) => ({
1771
+ })).map((rune) => !rainbow || rune.mono || rune.type && rune.type != "text" ? [rune] : rune.text.split("").map((p, i) => ({
735
1772
  ...rune,
736
1773
  text: p,
737
1774
  linebreak: i == 0 && rune.linebreak,
@@ -741,6 +1778,7 @@ function runifyText(text, rainbow = false) {
741
1778
  function replaceAmpCodes(text) {
742
1779
  const codes = {
743
1780
  "&#124;": "|",
1781
+ "&amp;": "&",
744
1782
  "&shy;": "\xAD",
745
1783
  "&laquo;": "\xAB",
746
1784
  "&raquo;": "\xBB",
@@ -812,95 +1850,621 @@ function replaceAmpCodes(text) {
812
1850
  }
813
1851
  return text;
814
1852
  }
1853
+ var NBSP = "\xA0";
1854
+ var NON_BREAKING_HYPHEN = "\u2011";
1855
+ 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"];
1856
+ var referenceTerms = [
1857
+ "\u0440\u0438\u0441\u0443\u043D\u043E\u043A",
1858
+ "\u0440\u0438\u0441\u0443\u043D\u043A\u0430",
1859
+ "\u0440\u0438\u0441\u0443\u043D\u043A\u0435",
1860
+ "\u0440\u0438\u0441\u0443\u043D\u043A\u0443",
1861
+ "\u0440\u0438\u0441\u0443\u043D\u043A\u043E\u043C",
1862
+ "\u0440\u0438\u0441\u0443\u043D\u043A\u0438",
1863
+ "\u0440\u0438\u0441\u0443\u043D\u043A\u043E\u0432",
1864
+ "\u0440\u0438\u0441\u0443\u043D\u043A\u0430\u0445",
1865
+ "\u0442\u0430\u0431\u043B\u0438\u0446\u0430",
1866
+ "\u0442\u0430\u0431\u043B\u0438\u0446\u044B",
1867
+ "\u0442\u0430\u0431\u043B\u0438\u0446\u0435",
1868
+ "\u0442\u0430\u0431\u043B\u0438\u0446\u0443",
1869
+ "\u0442\u0430\u0431\u043B\u0438\u0446\u0435\u0439",
1870
+ "\u0442\u0430\u0431\u043B\u0438\u0446",
1871
+ "\u0442\u0430\u0431\u043B\u0438\u0446\u0430\u0445",
1872
+ "\u043B\u0438\u0441\u0442\u0438\u043D\u0433",
1873
+ "\u043B\u0438\u0441\u0442\u0438\u043D\u0433\u0430",
1874
+ "\u043B\u0438\u0441\u0442\u0438\u043D\u0433\u0435",
1875
+ "\u043B\u0438\u0441\u0442\u0438\u043D\u0433\u0443",
1876
+ "\u043B\u0438\u0441\u0442\u0438\u043D\u0433\u043E\u043C",
1877
+ "\u043B\u0438\u0441\u0442\u0438\u043D\u0433\u0438",
1878
+ "\u043B\u0438\u0441\u0442\u0438\u043D\u0433\u043E\u0432",
1879
+ "\u043B\u0438\u0441\u0442\u0438\u043D\u0433\u0430\u0445",
1880
+ "\u0444\u043E\u0440\u043C\u0443\u043B\u0430",
1881
+ "\u0444\u043E\u0440\u043C\u0443\u043B\u044B",
1882
+ "\u0444\u043E\u0440\u043C\u0443\u043B\u0435",
1883
+ "\u0444\u043E\u0440\u043C\u0443\u043B\u0443",
1884
+ "\u0444\u043E\u0440\u043C\u0443\u043B\u043E\u0439",
1885
+ "\u0444\u043E\u0440\u043C\u0443\u043B",
1886
+ "\u0444\u043E\u0440\u043C\u0443\u043B\u0430\u0445",
1887
+ "\u0443\u0440\u0430\u0432\u043D\u0435\u043D\u0438\u0435",
1888
+ "\u0443\u0440\u0430\u0432\u043D\u0435\u043D\u0438\u044F",
1889
+ "\u0443\u0440\u0430\u0432\u043D\u0435\u043D\u0438\u0438",
1890
+ "\u0443\u0440\u0430\u0432\u043D\u0435\u043D\u0438\u044E",
1891
+ "\u0443\u0440\u0430\u0432\u043D\u0435\u043D\u0438\u0435\u043C",
1892
+ "\u0443\u0440\u0430\u0432\u043D\u0435\u043D\u0438\u0439",
1893
+ "\u0443\u0440\u0430\u0432\u043D\u0435\u043D\u0438\u044F\u0445",
1894
+ "\u0440\u0430\u0437\u0434\u0435\u043B",
1895
+ "\u0440\u0430\u0437\u0434\u0435\u043B\u0430",
1896
+ "\u0440\u0430\u0437\u0434\u0435\u043B\u0435",
1897
+ "\u0440\u0430\u0437\u0434\u0435\u043B\u0443",
1898
+ "\u0440\u0430\u0437\u0434\u0435\u043B\u043E\u043C",
1899
+ "\u0440\u0430\u0437\u0434\u0435\u043B\u044B",
1900
+ "\u0440\u0430\u0437\u0434\u0435\u043B\u043E\u0432",
1901
+ "\u0440\u0430\u0437\u0434\u0435\u043B\u0430\u0445",
1902
+ "\u0433\u043B\u0430\u0432\u0430",
1903
+ "\u0433\u043B\u0430\u0432\u044B",
1904
+ "\u0433\u043B\u0430\u0432\u0435",
1905
+ "\u0433\u043B\u0430\u0432\u0443",
1906
+ "\u0433\u043B\u0430\u0432\u043E\u0439",
1907
+ "\u0433\u043B\u0430\u0432",
1908
+ "\u0433\u043B\u0430\u0432\u0430\u0445",
1909
+ "\u043F\u0440\u0438\u043B\u043E\u0436\u0435\u043D\u0438\u0435",
1910
+ "\u043F\u0440\u0438\u043B\u043E\u0436\u0435\u043D\u0438\u044F",
1911
+ "\u043F\u0440\u0438\u043B\u043E\u0436\u0435\u043D\u0438\u0438",
1912
+ "\u043F\u0440\u0438\u043B\u043E\u0436\u0435\u043D\u0438\u044E",
1913
+ "\u043F\u0440\u0438\u043B\u043E\u0436\u0435\u043D\u0438\u0435\u043C",
1914
+ "\u043F\u0440\u0438\u043B\u043E\u0436\u0435\u043D\u0438\u0439",
1915
+ "\u043F\u0440\u0438\u043B\u043E\u0436\u0435\u043D\u0438\u044F\u0445",
1916
+ "\u043F\u0440\u0438\u043C\u0435\u0440",
1917
+ "\u043F\u0440\u0438\u043C\u0435\u0440\u0430",
1918
+ "\u043F\u0440\u0438\u043C\u0435\u0440\u0435",
1919
+ "\u043F\u0440\u0438\u043C\u0435\u0440\u0443",
1920
+ "\u043F\u0440\u0438\u043C\u0435\u0440\u043E\u043C",
1921
+ "\u043F\u0440\u0438\u043C\u0435\u0440\u044B",
1922
+ "\u043F\u0440\u0438\u043C\u0435\u0440\u043E\u0432",
1923
+ "\u043F\u0440\u0438\u043C\u0435\u0440\u0430\u0445",
1924
+ "\u0430\u043B\u0433\u043E\u0440\u0438\u0442\u043C",
1925
+ "\u0430\u043B\u0433\u043E\u0440\u0438\u0442\u043C\u0430",
1926
+ "\u0430\u043B\u0433\u043E\u0440\u0438\u0442\u043C\u0435",
1927
+ "\u0430\u043B\u0433\u043E\u0440\u0438\u0442\u043C\u0443",
1928
+ "\u0430\u043B\u0433\u043E\u0440\u0438\u0442\u043C\u043E\u043C",
1929
+ "\u0430\u043B\u0433\u043E\u0440\u0438\u0442\u043C\u044B",
1930
+ "\u0430\u043B\u0433\u043E\u0440\u0438\u0442\u043C\u043E\u0432",
1931
+ "\u0430\u043B\u0433\u043E\u0440\u0438\u0442\u043C\u0430\u0445",
1932
+ "\u0441\u0445\u0435\u043C\u0430",
1933
+ "\u0441\u0445\u0435\u043C\u044B",
1934
+ "\u0441\u0445\u0435\u043C\u0435",
1935
+ "\u0441\u0445\u0435\u043C\u0443",
1936
+ "\u0441\u0445\u0435\u043C\u043E\u0439",
1937
+ "\u0441\u0445\u0435\u043C",
1938
+ "\u0441\u0445\u0435\u043C\u0430\u0445"
1939
+ ];
1940
+ 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"];
1941
+ 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"];
1942
+ function escapeRegExp(value) {
1943
+ return value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
1944
+ }
1945
+ function createAlternation(values) {
1946
+ return [...values].sort((a, b) => b.length - a.length).map(escapeRegExp).join("|");
1947
+ }
1948
+ var shortWordsPattern = createAlternation(shortWords);
1949
+ var referenceTermsPattern = createAlternation(referenceTerms);
1950
+ var referenceAbbreviationsPattern = createAlternation(referenceAbbreviations);
1951
+ var unitsPattern = createAlternation(units);
1952
+ var referenceIdentifierPattern = String.raw`(?:\(\s*\d+(?:\.\d+)*\s*\)|\d+(?:\.\d+)*|[А-ЯЁA-Z](?:[^\p{L}]|$))`;
1953
+ function formatTypography(text) {
1954
+ return text.replace(
1955
+ new RegExp(`(^|[^\\p{L}\\p{N}_])(${shortWordsPattern})[ \\t]+(?=\\S)`, "giu"),
1956
+ (_, prefix, word) => `${prefix}${word}${NBSP}`
1957
+ ).replace(
1958
+ new RegExp(`(^|[^\\p{L}\\p{N}_])(${referenceTermsPattern})[ \\t]+(?=${referenceIdentifierPattern})`, "giu"),
1959
+ (_, prefix, term) => `${prefix}${term}${NBSP}`
1960
+ ).replace(
1961
+ new RegExp(`(^|[^\\p{L}\\p{N}_])(${referenceAbbreviationsPattern})\\.[ \\t]+(?=${referenceIdentifierPattern})`, "giu"),
1962
+ (_, prefix, abbreviation) => `${prefix}${abbreviation}.${NBSP}`
1963
+ ).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(
1964
+ /\b(ГОСТ\u00A0Р|ГОСТ Р)[ \t]+(?=\d)/giu,
1965
+ (_, standard) => `${standard.replace(" ", NBSP)}${NBSP}`
1966
+ ).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);
1967
+ }
815
1968
 
816
1969
  // src/serializer.ts
817
- var fs3 = __toESM(require("fs"));
818
- var import_docx = require("docx");
1970
+ var fs4 = __toESM(require("fs"));
1971
+ var import_docx2 = require("docx");
819
1972
  var import_image_size = require("image-size");
820
- var import_path2 = __toESM(require("path"));
821
- var import_prismjs = __toESM(require("prismjs"));
822
- var import_prism_clike = require("prismjs/components/prism-clike");
823
- var import_prism_markup = require("prismjs/components/prism-markup");
824
- var import_prism_css = require("prismjs/components/prism-css");
825
- var import_prism_javascript = require("prismjs/components/prism-javascript");
826
- var import_prism_typescript = require("prismjs/components/prism-typescript");
827
- var import_prism_jsx = require("prismjs/components/prism-jsx");
828
- var import_prism_tsx = require("prismjs/components/prism-tsx");
829
- var import_prism_json = require("prismjs/components/prism-json");
830
- var import_prism_bash = require("prismjs/components/prism-bash");
831
- var import_prism_powershell = require("prismjs/components/prism-powershell");
832
- var import_prism_python = require("prismjs/components/prism-python");
833
- var import_prism_java = require("prismjs/components/prism-java");
834
- var import_prism_c = require("prismjs/components/prism-c");
835
- var import_prism_cpp = require("prismjs/components/prism-cpp");
836
- var import_prism_csharp = require("prismjs/components/prism-csharp");
837
- var import_prism_go = require("prismjs/components/prism-go");
838
- var import_prism_rust = require("prismjs/components/prism-rust");
839
- var import_prism_php = require("prismjs/components/prism-php");
840
- var import_prism_ruby = require("prismjs/components/prism-ruby");
841
- var import_prism_swift = require("prismjs/components/prism-swift");
842
- var import_prism_kotlin = require("prismjs/components/prism-kotlin");
843
- var import_prism_sql = require("prismjs/components/prism-sql");
844
- var import_prism_yaml = require("prismjs/components/prism-yaml");
845
- var import_prism_markdown = require("prismjs/components/prism-markdown");
846
- var import_prism_docker = require("prismjs/components/prism-docker");
847
- var import_prism_nginx = require("prismjs/components/prism-nginx");
1973
+ var import_path3 = __toESM(require("path"));
848
1974
 
849
- // src/errors.ts
850
- var UserInputError = class extends Error {
851
- constructor(message) {
852
- super(message);
1975
+ // src/math.ts
1976
+ var import_docx = require("docx");
1977
+ var Omml = class extends import_docx.XmlComponent {
1978
+ constructor(tag, children = [], attributes = {}) {
1979
+ super(tag);
1980
+ if (Object.keys(attributes).length) this.root.push({ _attr: attributes });
1981
+ this.root.push(...children);
1982
+ }
1983
+ };
1984
+ var symbols = {
1985
+ alpha: "\u03B1",
1986
+ beta: "\u03B2",
1987
+ gamma: "\u03B3",
1988
+ delta: "\u03B4",
1989
+ epsilon: "\u03B5",
1990
+ varepsilon: "\u03B5",
1991
+ zeta: "\u03B6",
1992
+ eta: "\u03B7",
1993
+ theta: "\u03B8",
1994
+ vartheta: "\u03D1",
1995
+ iota: "\u03B9",
1996
+ kappa: "\u03BA",
1997
+ lambda: "\u03BB",
1998
+ mu: "\u03BC",
1999
+ nu: "\u03BD",
2000
+ xi: "\u03BE",
2001
+ pi: "\u03C0",
2002
+ rho: "\u03C1",
2003
+ sigma: "\u03C3",
2004
+ tau: "\u03C4",
2005
+ upsilon: "\u03C5",
2006
+ phi: "\u03C6",
2007
+ varphi: "\u03D5",
2008
+ chi: "\u03C7",
2009
+ psi: "\u03C8",
2010
+ omega: "\u03C9",
2011
+ Gamma: "\u0393",
2012
+ Delta: "\u0394",
2013
+ Theta: "\u0398",
2014
+ Lambda: "\u039B",
2015
+ Xi: "\u039E",
2016
+ Pi: "\u03A0",
2017
+ Sigma: "\u03A3",
2018
+ Upsilon: "\u03A5",
2019
+ Phi: "\u03A6",
2020
+ Psi: "\u03A8",
2021
+ Omega: "\u03A9",
2022
+ cdot: "\xB7",
2023
+ times: "\xD7",
2024
+ div: "\xF7",
2025
+ pm: "\xB1",
2026
+ mp: "\u2213",
2027
+ ast: "\u2217",
2028
+ star: "\u22C6",
2029
+ circ: "\u2218",
2030
+ bullet: "\u2219",
2031
+ oplus: "\u2295",
2032
+ ominus: "\u2296",
2033
+ otimes: "\u2297",
2034
+ oslash: "\u2298",
2035
+ odot: "\u2299",
2036
+ setminus: "\u2216",
2037
+ wedge: "\u2227",
2038
+ vee: "\u2228",
2039
+ le: "\u2264",
2040
+ leq: "\u2264",
2041
+ ge: "\u2265",
2042
+ geq: "\u2265",
2043
+ neq: "\u2260",
2044
+ ne: "\u2260",
2045
+ approx: "\u2248",
2046
+ equiv: "\u2261",
2047
+ sim: "\u223C",
2048
+ simeq: "\u2243",
2049
+ cong: "\u2245",
2050
+ propto: "\u221D",
2051
+ ll: "\u226A",
2052
+ gg: "\u226B",
2053
+ infty: "\u221E",
2054
+ partial: "\u2202",
2055
+ nabla: "\u2207",
2056
+ in: "\u2208",
2057
+ notin: "\u2209",
2058
+ ni: "\u220B",
2059
+ subset: "\u2282",
2060
+ subseteq: "\u2286",
2061
+ superset: "\u2283",
2062
+ supseteq: "\u2287",
2063
+ emptyset: "\u2205",
2064
+ varnothing: "\u2205",
2065
+ cup: "\u222A",
2066
+ cap: "\u2229",
2067
+ perp: "\u22A5",
2068
+ parallel: "\u2225",
2069
+ mid: "\u2223",
2070
+ angle: "\u2220",
2071
+ therefore: "\u2234",
2072
+ because: "\u2235",
2073
+ to: "\u2192",
2074
+ rightarrow: "\u2192",
2075
+ leftarrow: "\u2190",
2076
+ leftrightarrow: "\u2194",
2077
+ mapsto: "\u21A6",
2078
+ Rightarrow: "\u21D2",
2079
+ Leftarrow: "\u21D0",
2080
+ Leftrightarrow: "\u21D4",
2081
+ uparrow: "\u2191",
2082
+ downarrow: "\u2193",
2083
+ updownarrow: "\u2195",
2084
+ forall: "\u2200",
2085
+ exists: "\u2203",
2086
+ neg: "\xAC",
2087
+ land: "\u2227",
2088
+ lor: "\u2228",
2089
+ degree: "\xB0",
2090
+ ell: "\u2113",
2091
+ prime: "\u2032",
2092
+ hbar: "\u210F",
2093
+ aleph: "\u2135",
2094
+ Re: "\u211C",
2095
+ Im: "\u2111",
2096
+ ldots: "\u2026",
2097
+ dots: "\u2026",
2098
+ cdots: "\u22EF",
2099
+ vdots: "\u22EE",
2100
+ ddots: "\u22F1"
2101
+ };
2102
+ 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" };
2103
+ function run(text, style) {
2104
+ const value = style == "plain" ? "p" : style == "bold" ? "b" : style == "italic" ? "i" : void 0;
2105
+ return new Omml("m:r", [
2106
+ ...style == "double-struck" || style == "script" ? [new Omml("m:rPr", [new Omml("m:scr", [], { "m:val": style }), new Omml("m:sty", [], { "m:val": "p" })])] : [],
2107
+ ...value ? [new Omml("m:rPr", [new Omml("m:sty", [], { "m:val": value })])] : [],
2108
+ new Omml("m:t", [text], { "xml:space": "preserve" })
2109
+ ]);
2110
+ }
2111
+ function arg(children) {
2112
+ return new Omml("m:e", children);
2113
+ }
2114
+ function delimiter(children, begin, end) {
2115
+ return new Omml("m:d", [
2116
+ new Omml("m:dPr", [new Omml("m:begChr", [], { "m:val": begin }), new Omml("m:endChr", [], { "m:val": end })]),
2117
+ arg(children)
2118
+ ]);
2119
+ }
2120
+ function latexToOmml(latex, para = false) {
2121
+ const parser = new LatexParser(latex);
2122
+ const body = parser.sequence();
2123
+ if (!parser.done()) throw new Error(`unexpected "${parser.peek()}"`);
2124
+ const math = new Omml("m:oMath", body);
2125
+ return para ? new Omml("m:oMathPara", [math]) : math;
2126
+ }
2127
+ var LatexParser = class {
2128
+ constructor(source) {
2129
+ this.source = source;
2130
+ }
2131
+ source;
2132
+ i = 0;
2133
+ done() {
2134
+ return this.i >= this.source.length;
2135
+ }
2136
+ peek() {
2137
+ return this.source[this.i] || "";
2138
+ }
2139
+ take() {
2140
+ return this.source[this.i++] || "";
2141
+ }
2142
+ skipWhitespace() {
2143
+ while (/\s/.test(this.peek())) this.take();
2144
+ }
2145
+ command(name) {
2146
+ return this.source.slice(this.i).startsWith(`\\${name}`) && !/[A-Za-z]/.test(this.source[this.i + name.length + 1] || "");
2147
+ }
2148
+ sequence(stop, style, preserveWhitespace = false, boundary) {
2149
+ const result = [];
2150
+ while (!this.done() && this.peek() != stop && !boundary?.()) {
2151
+ if (/\s/.test(this.peek())) {
2152
+ this.take();
2153
+ if (preserveWhitespace) result.push(run(" ", style));
2154
+ continue;
2155
+ }
2156
+ if (this.command("displaystyle") || this.command("textstyle")) {
2157
+ this.take();
2158
+ while (/[A-Za-z]/.test(this.peek())) this.take();
2159
+ continue;
2160
+ }
2161
+ if (this.source.startsWith("\\!", this.i)) {
2162
+ this.i += 2;
2163
+ continue;
2164
+ }
2165
+ result.push(this.decoratedAtom(style, preserveWhitespace));
2166
+ }
2167
+ if (stop) {
2168
+ if (this.take() != stop) throw new Error(`missing "${stop}"`);
2169
+ }
2170
+ return result;
2171
+ }
2172
+ decoratedAtom(style, preserveWhitespace = false) {
2173
+ let value = this.atom(style, preserveWhitespace);
2174
+ let sub;
2175
+ let sup;
2176
+ while (true) {
2177
+ const beforeSpace = this.i;
2178
+ this.skipWhitespace();
2179
+ if (this.peek() != "_" && this.peek() != "^") {
2180
+ this.i = beforeSpace;
2181
+ break;
2182
+ }
2183
+ const marker = this.take();
2184
+ const script = this.groupOrAtom(style);
2185
+ if (marker == "_" ? sub : sup) throw new Error("duplicate script");
2186
+ if (marker == "_") sub = script;
2187
+ else sup = script;
2188
+ }
2189
+ if (sub && sup) value = new Omml("m:sSubSup", [new Omml("m:e", [value]), new Omml("m:sub", sub), new Omml("m:sup", sup)]);
2190
+ else if (sub) value = new Omml("m:sSub", [new Omml("m:e", [value]), new Omml("m:sub", sub)]);
2191
+ else if (sup) value = new Omml("m:sSup", [new Omml("m:e", [value]), new Omml("m:sup", sup)]);
2192
+ return value;
2193
+ }
2194
+ groupOrAtom(style, preserveWhitespace = false) {
2195
+ this.skipWhitespace();
2196
+ if (this.done() || ["}", "_", "^", "&"].includes(this.peek())) throw new Error("argument expected");
2197
+ if (this.peek() == "{") {
2198
+ this.take();
2199
+ return this.sequence("}", style, preserveWhitespace);
2200
+ }
2201
+ return [this.atom(style)];
2202
+ }
2203
+ atom(style, preserveWhitespace = false) {
2204
+ const ch = this.take();
2205
+ if (!ch || ["}", "_", "^", "&"].includes(ch)) throw new Error(`unexpected "${ch}"`);
2206
+ if (ch == "{") return new Omml("m:box", [arg(this.sequence("}", style, preserveWhitespace))]);
2207
+ if (ch != "\\") return run(ch, style);
2208
+ let name = "";
2209
+ while (/[A-Za-z]/.test(this.peek())) name += this.take();
2210
+ if (!name) {
2211
+ const escaped = this.take();
2212
+ if (!escaped) throw new Error("command expected");
2213
+ const spaces = { ",": "\u2009", ";": "\u2004", ":": "\u2005", " ": " " };
2214
+ if (spaces[escaped]) return run(spaces[escaped], style);
2215
+ if (escaped == "!") return run("", style);
2216
+ return run(escaped, style);
2217
+ }
2218
+ this.skipWhitespace();
2219
+ 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))]);
2220
+ if (name == "sqrt") {
2221
+ let degree;
2222
+ if (this.peek() == "[") {
2223
+ this.take();
2224
+ degree = this.sequence("]", style);
2225
+ }
2226
+ 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))]);
2227
+ }
2228
+ if (name == "text") return new Omml("m:box", [arg(this.groupOrAtom("plain", true))]);
2229
+ if (name == "mathrm" || name == "operatorname") return new Omml("m:box", [arg(this.groupOrAtom("plain"))]);
2230
+ if (name == "mathbf") return new Omml("m:box", [arg(this.groupOrAtom("bold"))]);
2231
+ if (name == "mathit") return new Omml("m:box", [arg(this.groupOrAtom("italic"))]);
2232
+ if (["mathbb", "mathcal"].includes(name)) return new Omml("m:box", [arg(this.groupOrAtom(name == "mathbb" ? "double-struck" : "script"))]);
2233
+ if (["hat", "widehat", "vec"].includes(name)) {
2234
+ const chr = name == "vec" ? "\u20D7" : "\u0302";
2235
+ return new Omml("m:acc", [new Omml("m:accPr", [new Omml("m:chr", [], { "m:val": chr })]), arg(this.groupOrAtom(style))]);
2236
+ }
2237
+ if (["overline", "bar", "underline"].includes(name)) {
2238
+ const pos = name == "underline" ? "bot" : "top";
2239
+ return new Omml("m:bar", [new Omml("m:barPr", [new Omml("m:pos", [], { "m:val": pos })]), arg(this.groupOrAtom(style))]);
2240
+ }
2241
+ 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))])], "(", ")");
2242
+ if (name == "lim") {
2243
+ let below = true;
2244
+ if (this.command("limits") || this.command("nolimits")) {
2245
+ below = this.command("limits");
2246
+ this.i += below ? 7 : 9;
2247
+ this.skipWhitespace();
2248
+ }
2249
+ let sub = [];
2250
+ if (this.peek() == "_") {
2251
+ this.take();
2252
+ sub = this.groupOrAtom(style);
2253
+ }
2254
+ if (!sub.length) return run("lim", "plain");
2255
+ return new Omml(below ? "m:limLow" : "m:sSub", [arg([run("lim", "plain")]), new Omml(below ? "m:lim" : "m:sub", sub)]);
2256
+ }
2257
+ if (["sum", "prod", "int", "iint", "iiint"].includes(name)) {
2258
+ const chr = name == "sum" ? "\u2211" : name == "prod" ? "\u220F" : name == "iint" ? "\u222C" : name == "iiint" ? "\u222D" : "\u222B";
2259
+ let sub = [], sup = [];
2260
+ let limitLocation;
2261
+ while (true) {
2262
+ this.skipWhitespace();
2263
+ if (this.command("limits") || this.command("nolimits")) {
2264
+ const limits = this.command("limits");
2265
+ this.i += limits ? 7 : 9;
2266
+ limitLocation = limits ? "undOvr" : "subSup";
2267
+ continue;
2268
+ }
2269
+ if (this.peek() != "_" && this.peek() != "^") break;
2270
+ const marker = this.take();
2271
+ const script = this.groupOrAtom(style);
2272
+ if (marker == "_" ? sub.length : sup.length) throw new Error("duplicate script");
2273
+ if (marker == "_") sub = script;
2274
+ else sup = script;
2275
+ this.skipWhitespace();
2276
+ }
2277
+ while (/\s/.test(this.peek())) this.take();
2278
+ const operand = this.done() || ["}", "]", ")", "&", "=", "<", ">"].includes(this.peek()) || this.command("right") || this.command("end") || this.source.startsWith("\\\\", this.i) ? [] : [this.decoratedAtom(style)];
2279
+ return new Omml("m:nary", [new Omml("m:naryPr", [
2280
+ new Omml("m:chr", [], { "m:val": chr }),
2281
+ ...limitLocation ? [new Omml("m:limLoc", [], { "m:val": limitLocation })] : [],
2282
+ new Omml("m:subHide", [], { "m:val": sub.length ? "0" : "1" }),
2283
+ new Omml("m:supHide", [], { "m:val": sup.length ? "0" : "1" })
2284
+ ]), new Omml("m:sub", sub), new Omml("m:sup", sup), arg(operand)]);
2285
+ }
2286
+ if (name == "left") {
2287
+ const begin = this.readDelimiter();
2288
+ const body = this.sequence(void 0, style, false, () => this.command("right"));
2289
+ if (!this.command("right")) throw new Error("missing \\right");
2290
+ this.i += 6;
2291
+ return delimiter(body, begin, this.readDelimiter());
2292
+ }
2293
+ if (name == "begin") return this.matrix(style);
2294
+ if (symbols[name]) return run(symbols[name], style);
2295
+ if (operators[name]) return run(operators[name], "plain");
2296
+ if (["quad", "qquad"].includes(name)) return run(name == "quad" ? "\u2003" : "\u2003\u2003", style);
2297
+ throw new Error(`unsupported command \\${name}`);
2298
+ }
2299
+ readDelimiter() {
2300
+ this.skipWhitespace();
2301
+ const ch = this.take();
2302
+ if (ch == ".") return "";
2303
+ if (ch && "()[]|{}".includes(ch)) return ch;
2304
+ if (ch == "\\") {
2305
+ let name = "";
2306
+ while (/[A-Za-z]/.test(this.peek())) name += this.take();
2307
+ if (!name) name = this.take();
2308
+ const delimiters = { "{": "{", "}": "}", "|": "\u2016", lbrace: "{", rbrace: "}", langle: "\u27E8", rangle: "\u27E9", lvert: "|", rvert: "|", vert: "|", lVert: "\u2016", rVert: "\u2016", Vert: "\u2016" };
2309
+ if (delimiters[name]) return delimiters[name];
2310
+ }
2311
+ throw new Error("delimiter expected");
2312
+ }
2313
+ matrix(style) {
2314
+ if (this.peek() != "{") throw new Error("environment name expected");
2315
+ this.take();
2316
+ let env = "";
2317
+ while (!this.done() && this.peek() != "}") env += this.take();
2318
+ this.take();
2319
+ if (!["matrix", "pmatrix", "bmatrix", "cases"].includes(env)) throw new Error(`unsupported environment ${env}`);
2320
+ const rows = [];
2321
+ let cells = [];
2322
+ while (true) {
2323
+ this.skipWhitespace();
2324
+ if (this.done()) throw new Error(`environment ${env} is not closed`);
2325
+ if (this.command("end")) {
2326
+ this.i += 4;
2327
+ this.skipWhitespace();
2328
+ const closing = `{${env}}`;
2329
+ if (!this.source.startsWith(closing, this.i)) throw new Error(`mismatched environment ${env}`);
2330
+ this.i += closing.length;
2331
+ if (cells.length) rows.push(new Omml("m:mr", cells));
2332
+ break;
2333
+ }
2334
+ cells.push(arg(this.sequence(void 0, style, false, () => this.peek() == "&" || this.source.startsWith("\\\\", this.i) || this.command("end"))));
2335
+ if (this.peek() == "&") {
2336
+ this.take();
2337
+ continue;
2338
+ }
2339
+ if (this.source.startsWith("\\\\", this.i)) {
2340
+ this.i += 2;
2341
+ rows.push(new Omml("m:mr", cells));
2342
+ cells = [];
2343
+ }
2344
+ }
2345
+ const matrix = new Omml("m:m", rows);
2346
+ if (env == "pmatrix") return delimiter([matrix], "(", ")");
2347
+ if (env == "bmatrix") return delimiter([matrix], "[", "]");
2348
+ if (env == "cases") return delimiter([matrix], "{", "");
2349
+ return matrix;
853
2350
  }
854
2351
  };
855
2352
 
856
2353
  // src/serializer.ts
2354
+ var import_prismjs = __toESM(require("prismjs"));
2355
+ var import_prism_clike = require("prismjs/components/prism-clike.js");
2356
+ var import_prism_markup = require("prismjs/components/prism-markup.js");
2357
+ var import_prism_css = require("prismjs/components/prism-css.js");
2358
+ var import_prism_javascript = require("prismjs/components/prism-javascript.js");
2359
+ var import_prism_typescript = require("prismjs/components/prism-typescript.js");
2360
+ var import_prism_jsx = require("prismjs/components/prism-jsx.js");
2361
+ var import_prism_tsx = require("prismjs/components/prism-tsx.js");
2362
+ var import_prism_json = require("prismjs/components/prism-json.js");
2363
+ var import_prism_bash = require("prismjs/components/prism-bash.js");
2364
+ var import_prism_powershell = require("prismjs/components/prism-powershell.js");
2365
+ var import_prism_python = require("prismjs/components/prism-python.js");
2366
+ var import_prism_java = require("prismjs/components/prism-java.js");
2367
+ var import_prism_c = require("prismjs/components/prism-c.js");
2368
+ var import_prism_cpp = require("prismjs/components/prism-cpp.js");
2369
+ var import_prism_csharp = require("prismjs/components/prism-csharp.js");
2370
+ var import_prism_go = require("prismjs/components/prism-go.js");
2371
+ var import_prism_rust = require("prismjs/components/prism-rust.js");
2372
+ var import_prism_php = require("prismjs/components/prism-php.js");
2373
+ var import_prism_ruby = require("prismjs/components/prism-ruby.js");
2374
+ var import_prism_swift = require("prismjs/components/prism-swift.js");
2375
+ var import_prism_kotlin = require("prismjs/components/prism-kotlin.js");
2376
+ var import_prism_sql = require("prismjs/components/prism-sql.js");
2377
+ var import_prism_yaml = require("prismjs/components/prism-yaml.js");
2378
+ var import_prism_markdown = require("prismjs/components/prism-markdown.js");
2379
+ var import_prism_docker = require("prismjs/components/prism-docker.js");
2380
+ var import_prism_nginx = require("prismjs/components/prism-nginx.js");
857
2381
  var STYLE_list = "afc";
858
2382
  var STYLE_table_title = "TableCaption";
859
2383
  var STYLE_code_title = "ListingCaption";
860
2384
  var STYLE_code = "ListingCode";
861
- async function serializeDocx(doc, fout, workdir, assets, checkFilesIsInsidePath) {
862
- const allowedRealPath = checkFilesIsInsidePath === false ? false : realpathAllowMissing(checkFilesIsInsidePath === "" ? workdir : checkFilesIsInsidePath);
863
- const getPath = (fname) => {
864
- if (process.platform != "win32") fname = fname.replaceAll("\\", "/");
865
- if (process.platform == "win32" && fname.startsWith("/")) fname = "." + fname;
866
- const targetPath = import_path2.default.isAbsolute(fname) ? import_path2.default.resolve(fname) : import_path2.default.resolve(workdir, fname);
867
- if (allowedRealPath !== false) {
868
- const targetRealPath = realpathAllowMissing(targetPath);
869
- const relative = import_path2.default.relative(allowedRealPath, targetRealPath);
870
- const isOutside = relative === ".." || relative.startsWith(`..${import_path2.default.sep}`) || import_path2.default.isAbsolute(relative);
871
- if (isOutside) throw new UserInputError(`Access denied: "${fname}" resolves outside the working directory.`);
872
- }
873
- return targetPath;
874
- };
2385
+ async function serializeDocx(doc, fout, workdir, assets, checkFilesIsInsidePath, logwarn = console.warn) {
2386
+ const { resolvePath: getPath } = getSafePathResolver(workdir, checkFilesIsInsidePath);
875
2387
  const sections = [];
876
2388
  const numbering = [];
877
2389
  const docSections = (function splitSections() {
878
- const sections2 = [{ displayPageNum: true, orientation: "portrait", pageStart: 1, nodes: [] }];
2390
+ let header = doc.header;
2391
+ let footer = doc.footer;
2392
+ const sections2 = [{ displayPageNum: true, orientation: "portrait", pageStart: 1, header, footer, nodes: [] }];
879
2393
  for (let j = 0; j < doc.nodes.length; j++) {
880
2394
  const node = doc.nodes[j];
881
- if (node.type == "sectionBreak")
2395
+ if (node.type == "sectionBreak") {
2396
+ header = node.header ?? header;
2397
+ footer = node.footer ?? footer;
882
2398
  sections2.push({
883
- displayPageNum: node.pageStart == null ? sections2.at(-1)?.displayPageNum || true : node.pageStart >= 0,
2399
+ displayPageNum: node.pageStart == null ? sections2.at(-1)?.displayPageNum ?? true : node.pageStart >= 0,
884
2400
  orientation: node.orientation || sections2.at(-1)?.orientation || "portrait",
885
2401
  pageStart: node.pageStart,
2402
+ header,
2403
+ footer,
886
2404
  nodes: []
887
2405
  });
888
- else
2406
+ } else
889
2407
  sections2.at(-1)?.nodes.push(node);
890
2408
  }
891
2409
  return sections2.filter((s) => s.nodes.length > 0);
892
2410
  })();
893
2411
  for (const section of docSections) {
894
- let renderNode2 = function(node, prevChild, prevNode) {
2412
+ let renderHeaderFooter2 = function(value, isFooter) {
2413
+ if (value.type == "none") return void 0;
2414
+ if (value.type == "auto") {
2415
+ if (!isFooter) return void 0;
2416
+ return new import_docx2.Footer({
2417
+ children: [new import_docx2.Paragraph({
2418
+ alignment: import_docx2.AlignmentType.CENTER,
2419
+ children: [new import_docx2.TextRun({ children: section.displayPageNum ? [import_docx2.PageNumber.CURRENT] : [] })],
2420
+ indent: { firstLine: 0 },
2421
+ spacing: { after: 0, line: 240 }
2422
+ })]
2423
+ });
2424
+ }
2425
+ const contentValue = value;
2426
+ const content = contentValue.nodes.flatMap((node) => {
2427
+ if (node.type == "text") return [new import_docx2.Paragraph({
2428
+ children: renderText(node.text, node.sourceLine),
2429
+ alignment: contentValue.align,
2430
+ indent: { firstLine: 0 },
2431
+ spacing: isFooter ? { after: 0, line: 240 } : { line: 240 }
2432
+ })];
2433
+ return [renderHeaderFooterTable2(node)];
2434
+ });
2435
+ return isFooter ? new import_docx2.Footer({ children: content }) : new import_docx2.Header({ children: content });
2436
+ }, renderHeaderFooterTable2 = function(node) {
2437
+ return new import_docx2.Table({
2438
+ width: { type: "dxa", size: (0, import_docx2.convertMillimetersToTwip)(section.orientation == "landscape" ? 255.8 : 168.8) },
2439
+ borders: {
2440
+ top: { style: import_docx2.BorderStyle.SINGLE, size: 4, color: "808080" },
2441
+ bottom: { style: import_docx2.BorderStyle.SINGLE, size: 4, color: "808080" },
2442
+ left: { style: import_docx2.BorderStyle.SINGLE, size: 4, color: "808080" },
2443
+ right: { style: import_docx2.BorderStyle.SINGLE, size: 4, color: "808080" },
2444
+ insideHorizontal: { style: import_docx2.BorderStyle.SINGLE, size: 4, color: "808080" },
2445
+ insideVertical: { style: import_docx2.BorderStyle.SINGLE, size: 4, color: "808080" }
2446
+ },
2447
+ rows: node.rows.map((row) => new import_docx2.TableRow({
2448
+ children: row.map((cell, colI) => new import_docx2.TableCell({
2449
+ children: cell.type == "text" ? [new import_docx2.Paragraph({
2450
+ children: renderText(cell.text, node.sourceLine),
2451
+ alignment: node.align[colI] == "c" ? "center" : node.align[colI] == "r" ? "right" : "left",
2452
+ indent: { firstLine: 0 },
2453
+ spacing: { after: 0, line: 240 * 1.25 }
2454
+ })] : []
2455
+ }))
2456
+ }))
2457
+ });
2458
+ }, renderNode2 = function(node, prevChild, prevNode) {
895
2459
  switch (node.type) {
896
2460
  case "text":
897
- return new import_docx.Paragraph({
898
- children: renderText(node.text),
2461
+ return new import_docx2.Paragraph({
2462
+ children: renderText(node.text, node.sourceLine),
899
2463
  indent: node.noIndent ? { firstLine: 0 } : {},
900
2464
  ...node.center ? { alignment: "center", indent: { firstLine: 0 } } : {},
901
2465
  spacing: {
902
2466
  ...node.noMargin ? { after: 0 } : {},
903
- ...prevChild instanceof import_docx.Table ? { before: (doc.table.spacing.after ?? doc.text.spacing.after) * 20 } : {}
2467
+ ...prevChild instanceof import_docx2.Table ? { before: (doc.table.spacing.after ?? doc.text.spacing.after) * 20 } : {}
904
2468
  }
905
2469
  });
906
2470
  case "title":
@@ -908,20 +2472,20 @@ async function serializeDocx(doc, fout, workdir, assets, checkFilesIsInsidePath)
908
2472
  const level = node.level == 0 ? 1 : node.level;
909
2473
  const styles = doc.headings[`h${level}`];
910
2474
  if (styles.uppercase) node.text.forEach((r) => r.text = r.text.toUpperCase());
911
- return new import_docx.Paragraph({
912
- children: renderText(node.text, styles.size),
2475
+ return new import_docx2.Paragraph({
2476
+ children: renderText(node.text, node.sourceLine, styles.size),
913
2477
  style: `${node.level}`,
914
2478
  ...node.center ? { alignment: "center", indent: { firstLine: 0 } } : {},
915
- ...!node.center && node.level != 0 && styles.indent_full ? { indent: { firstLine: 0, left: (0, import_docx.convertMillimetersToTwip)(12.5) } } : {},
2479
+ ...!node.center && node.level != 0 && styles.indent_full ? { indent: { firstLine: 0, left: (0, import_docx2.convertMillimetersToTwip)(12.5) } } : {},
916
2480
  spacing: {
917
2481
  before: styles.spacing.before * 20,
918
2482
  after: styles.spacing.after * 20
919
2483
  }
920
2484
  });
921
2485
  case "pageBreak":
922
- if (!(prevChild instanceof import_docx.Paragraph))
923
- return new import_docx.Paragraph({ children: [new import_docx.PageBreak()] });
924
- prevChild.addChildElement(new import_docx.PageBreak());
2486
+ if (!(prevChild instanceof import_docx2.Paragraph))
2487
+ return new import_docx2.Paragraph({ children: [new import_docx2.PageBreak()] });
2488
+ prevChild.addChildElement(new import_docx2.PageBreak());
925
2489
  return [];
926
2490
  case "list":
927
2491
  let renderList2 = function(node2, level2 = 0) {
@@ -932,8 +2496,8 @@ async function serializeDocx(doc, fout, workdir, assets, checkFilesIsInsidePath)
932
2496
  for (const item of node2.items) {
933
2497
  if (item.type == "list") renderList2({ ...item, alternativeStyle: !!node2.alternativeStyle }, level2 + 1);
934
2498
  else {
935
- items.push(new import_docx.Paragraph({
936
- children: renderText(item.text),
2499
+ items.push(new import_docx2.Paragraph({
2500
+ children: renderText(item.text, item.sourceLine ?? node2.sourceLine),
937
2501
  style: STYLE_list,
938
2502
  numbering: { reference: id, level: level2 },
939
2503
  spacing: {
@@ -953,39 +2517,39 @@ async function serializeDocx(doc, fout, workdir, assets, checkFilesIsInsidePath)
953
2517
  };
954
2518
  numbering.push(list);
955
2519
  const items = [];
956
- let addMargin = prevChild instanceof import_docx.Table;
2520
+ let addMargin = prevChild instanceof import_docx2.Table;
957
2521
  renderList2(node);
958
2522
  return items;
959
2523
  case "table":
960
- 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);
961
- 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);
2524
+ 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);
2525
+ 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);
962
2526
  const spacingInner = Math.max(doc.table.spacing.after ?? doc.text.spacing.after, doc.table.spacing.before ?? 0);
963
2527
  return [
964
2528
  ...node.title ? [
965
- new import_docx.Paragraph({
966
- children: renderText(node.title, doc.table.title.size),
2529
+ new import_docx2.Paragraph({
2530
+ children: renderText(node.title, node.sourceLine, doc.table.title.size),
967
2531
  style: STYLE_table_title,
968
- ...prevChild instanceof import_docx.Table ? { spacing: { before: spacingInner * 20 } } : doc.table.spacing.before ? { spacing: { before: doc.table.spacing.before * 20 } } : {}
2532
+ ...prevChild instanceof import_docx2.Table ? { spacing: { before: spacingInner * 20 } } : doc.table.spacing.before ? { spacing: { before: doc.table.spacing.before * 20 } } : {}
969
2533
  })
970
- ] : prevChild instanceof import_docx.Table ? [
971
- new import_docx.Paragraph({ spacing: { line: 20, before: spacingInner * 20, after: 0 } })
2534
+ ] : prevChild instanceof import_docx2.Table ? [
2535
+ new import_docx2.Paragraph({ spacing: { line: 20, before: spacingInner * 20, after: 0 } })
972
2536
  ] : doc.table.spacing.before ? [
973
- new import_docx.Paragraph({ spacing: { line: 20, before: doc.table.spacing.before * 20, after: 0 } })
2537
+ new import_docx2.Paragraph({ spacing: { line: 20, before: doc.table.spacing.before * 20, after: 0 } })
974
2538
  ] : [],
975
- new import_docx.Table({
2539
+ new import_docx2.Table({
976
2540
  // width: { type: "pct", size: 100 },
977
2541
  width: {
978
2542
  type: "dxa",
979
- size: (0, import_docx.convertMillimetersToTwip)(section.orientation === "landscape" ? 255.8 : 168.8)
2543
+ size: (0, import_docx2.convertMillimetersToTwip)(section.orientation === "landscape" ? 255.8 : 168.8)
980
2544
  },
981
- rows: node.rows.map((row, rowI) => new import_docx.TableRow({
982
- tableHeader: rowI == 0,
2545
+ rows: node.rows.map((row, rowI) => new import_docx2.TableRow({
2546
+ tableHeader: node.header !== false && rowI == 0,
983
2547
  cantSplit: true,
984
- children: row.map((item, colI) => new import_docx.TableCell({
2548
+ children: row.map((item, colI) => new import_docx2.TableCell({
985
2549
  children: item.type != "text" ? renderNodeL2(item) : [
986
- new import_docx.Paragraph({
987
- children: renderText(item.text, node.normalFontSize ? void 0 : doc.table.text.size),
988
- alignment: rowI == 0 ? doc.table.heading.align : node.align[colI] == "c" ? "center" : node.align[colI] == "r" ? "right" : "left",
2550
+ new import_docx2.Paragraph({
2551
+ children: renderText(item.text, node.sourceLine, node.normalFontSize ? void 0 : doc.table.text.size),
2552
+ alignment: node.header !== false && rowI == 0 ? doc.table.heading.align : node.align[colI] == "c" ? "center" : node.align[colI] == "r" ? "right" : "left",
989
2553
  indent: { firstLine: 0 },
990
2554
  spacing: { after: 0, line: 240 * doc.table.text.line_spacing }
991
2555
  })
@@ -996,20 +2560,20 @@ async function serializeDocx(doc, fout, workdir, assets, checkFilesIsInsidePath)
996
2560
  ];
997
2561
  case "tableOfContents":
998
2562
  return [
999
- new import_docx.TableOfContents("\u041E\u0433\u043B\u0430\u0432\u043B\u0435\u043D\u0438\u0435", {
2563
+ new import_docx2.TableOfContents("\u041E\u0433\u043B\u0430\u0432\u043B\u0435\u043D\u0438\u0435", {
1000
2564
  hyperlink: true,
1001
2565
  headingStyleRange: "1-3"
1002
2566
  }),
1003
- new import_docx.Paragraph({ children: [new import_docx.PageBreak()] })
2567
+ new import_docx2.Paragraph({ children: [new import_docx2.PageBreak()] })
1004
2568
  ];
1005
2569
  case "image":
1006
- const type = node.src.split(".").at(-1) || "";
2570
+ const type = (node.src.split(".").at(-1) || "").toLowerCase();
1007
2571
  if (!["jpg", "png", "gif", "bmp", "svg"].includes(type))
1008
2572
  throw new UserInputError(`Unsupported image format: "${type}", file: ${node.src}`);
1009
2573
  const img_path = getPath(node.src);
1010
- if (!fs3.existsSync(img_path))
2574
+ if (!fs4.existsSync(img_path))
1011
2575
  throw new UserInputError(`File not exist: ${node.src}`);
1012
- const data = fs3.readFileSync(img_path);
2576
+ const data = fs4.readFileSync(img_path);
1013
2577
  const dimensions = (0, import_image_size.imageSize)(data);
1014
2578
  const [MaxW, MaxH] = section.orientation == "landscape" ? [950, 600] : [600, 900];
1015
2579
  let [width, height] = [dimensions.width, dimensions.height];
@@ -1019,19 +2583,19 @@ async function serializeDocx(doc, fout, workdir, assets, checkFilesIsInsidePath)
1019
2583
  if (height > MaxH) [width, height] = [width / height * MaxH, MaxH];
1020
2584
  if (width > MaxW) [width, height] = [MaxW, height / width * MaxW];
1021
2585
  return [
1022
- new import_docx.Paragraph({
2586
+ new import_docx2.Paragraph({
1023
2587
  alignment: "center",
1024
2588
  indent: { firstLine: 0 },
1025
2589
  spacing: {
1026
2590
  line: 240,
1027
- lineRule: import_docx.LineRuleType.AT_LEAST,
2591
+ lineRule: import_docx2.LineRuleType.AT_LEAST,
1028
2592
  // For LibreOffice to not shrink image
1029
2593
  ...!node.text && doc.img.spacing.after !== void 0 ? { after: doc.img.spacing.after * 20 } : {},
1030
2594
  ...doc.img.spacing.before !== void 0 ? { before: doc.img.spacing.before * 20 } : {}
1031
2595
  },
1032
2596
  keepNext: !!node.text,
1033
2597
  children: [
1034
- new import_docx.ImageRun({
2598
+ new import_docx2.ImageRun({
1035
2599
  type,
1036
2600
  data,
1037
2601
  transformation: { width, height },
@@ -1040,8 +2604,8 @@ async function serializeDocx(doc, fout, workdir, assets, checkFilesIsInsidePath)
1040
2604
  ]
1041
2605
  }),
1042
2606
  ...node.text ? [
1043
- new import_docx.Paragraph({
1044
- children: renderText(node.text, doc.img.text.size),
2607
+ new import_docx2.Paragraph({
2608
+ children: renderText(node.text, node.sourceLine, doc.img.text.size),
1045
2609
  alignment: "center",
1046
2610
  indent: { firstLine: 0 },
1047
2611
  spacing: {
@@ -1055,31 +2619,97 @@ async function serializeDocx(doc, fout, workdir, assets, checkFilesIsInsidePath)
1055
2619
  const code = doc.code.highlight && renderCodeHighlighting(node.code, node.lang);
1056
2620
  return [
1057
2621
  ...node.title ? [
1058
- new import_docx.Paragraph({
1059
- children: renderText(node.title, doc.code.title.size),
2622
+ new import_docx2.Paragraph({
2623
+ children: renderText(node.title, node.sourceLine, doc.code.title.size),
1060
2624
  style: STYLE_code_title
1061
2625
  })
1062
2626
  ] : prevNode?.type == "code" ? [
1063
- new import_docx.Paragraph({ spacing: { line: 20 } })
2627
+ new import_docx2.Paragraph({ spacing: { line: 20 } })
1064
2628
  ] : [],
1065
- ...code ? code : node.code.split("\n").map((p, i) => new import_docx.Paragraph({
1066
- children: [new import_docx.TextRun(p)],
2629
+ ...code ? code : node.code.split("\n").map((p, i) => new import_docx2.Paragraph({
2630
+ children: [new import_docx2.TextRun(p)],
1067
2631
  style: STYLE_code,
1068
2632
  ...i == 0 && !node.title && doc.code.spacing.before && prevNode?.type != "code" ? { spacing: { before: doc.code.spacing.before * 20 } } : {}
1069
2633
  }))
1070
2634
  ];
2635
+ case "math":
2636
+ const formulaWidth = (0, import_docx2.convertMillimetersToTwip)(section.orientation == "landscape" ? 255.8 : 168.8);
2637
+ const number = node.title?.map((rune) => rune.text).join("") || "";
2638
+ const sideWidth = Math.max(
2639
+ (0, import_docx2.convertMillimetersToTwip)(5),
2640
+ Math.ceil(estimateNumberWidthPt(number, doc.text.size) * 20 + (0, import_docx2.convertMillimetersToTwip)(3))
2641
+ );
2642
+ return [
2643
+ ...prevNode?.type != "math" ? [
2644
+ new import_docx2.Paragraph({ indent: { firstLine: 0 }, spacing: { line: 20, after: doc.formula.spacing.before * 20 } })
2645
+ ] : [],
2646
+ new import_docx2.Table({
2647
+ width: { type: "dxa", size: formulaWidth },
2648
+ borders: { top: { style: import_docx2.BorderStyle.NONE }, bottom: { style: import_docx2.BorderStyle.NONE }, left: { style: import_docx2.BorderStyle.NONE }, right: { style: import_docx2.BorderStyle.NONE }, insideHorizontal: { style: import_docx2.BorderStyle.NONE }, insideVertical: { style: import_docx2.BorderStyle.NONE } },
2649
+ rows: [new import_docx2.TableRow({
2650
+ children: [
2651
+ new import_docx2.TableCell({ width: { type: "dxa", size: sideWidth }, children: [new import_docx2.Paragraph({ indent: { firstLine: 0 }, spacing: { after: 0 } })] }),
2652
+ new import_docx2.TableCell({
2653
+ children: [
2654
+ new import_docx2.Paragraph({ alignment: import_docx2.AlignmentType.CENTER, indent: { firstLine: 0 }, spacing: { after: 0 }, children: [renderMath(node.latex, true, node.sourceLine)] })
2655
+ ]
2656
+ }),
2657
+ new import_docx2.TableCell({ width: { type: "dxa", size: sideWidth }, children: [new import_docx2.Paragraph({
2658
+ indent: { firstLine: 0 },
2659
+ spacing: { line: 240, after: 0 },
2660
+ alignment: import_docx2.AlignmentType.RIGHT,
2661
+ children: renderText(node.title || [], node.sourceLine)
2662
+ })], verticalAlign: import_docx2.VerticalAlignTable.CENTER })
2663
+ ]
2664
+ })]
2665
+ }),
2666
+ new import_docx2.Paragraph({ indent: { firstLine: 0 }, spacing: { line: 20, after: doc.formula.spacing.after * 20 } })
2667
+ ];
1071
2668
  case "externalDoc":
1072
2669
  const doc_path = getPath(node.path);
1073
- if (!fs3.existsSync(doc_path))
2670
+ if (!fs4.existsSync(doc_path))
1074
2671
  throw new UserInputError(`File not exist: ${node.path}`);
1075
- const ext = import_path2.default.extname(doc_path);
2672
+ const ext = import_path3.default.extname(doc_path);
1076
2673
  if (ext != ".docx" && ext != ".pdf")
1077
2674
  throw new UserInputError(`File not .docx or .pdf: ${node.path}`);
1078
- return new import_docx.Paragraph({
2675
+ return new import_docx2.Paragraph({
1079
2676
  text: `!!(${doc_path})${JSON.stringify(node.dict)}`,
1080
2677
  indent: { firstLine: 0 },
1081
2678
  alignment: "left"
1082
2679
  });
2680
+ case "admonition":
2681
+ const style = `xAdmonition${node.admonitionType == "note" ? "" : node.admonitionType}`;
2682
+ const admonition = doc.admonition[node.admonitionType];
2683
+ const color = "#" + admonition.color;
2684
+ const icon = admonition.icon ? [new import_docx2.ImageRun({
2685
+ type: "svg",
2686
+ data: Buffer.from(admonitionIcons[node.admonitionType](admonition.title_color ? color : "#000000")),
2687
+ transformation: { width: admonition.icon_size, height: admonition.icon_size },
2688
+ fallback: {
2689
+ data: Buffer.from((admonition.title_color ? admonitionIconsFallback : admonitionIconsFallbackBlack)[node.admonitionType], "base64"),
2690
+ type: "png"
2691
+ }
2692
+ }), new import_docx2.TextRun(" ")] : [];
2693
+ const prevIsSameType = prevNode?.type == "admonition" && prevNode.admonitionType == node.admonitionType;
2694
+ const showTitle = node.title.map((r) => r.text).join("") != "";
2695
+ return [
2696
+ ...prevIsSameType ? [
2697
+ new import_docx2.Paragraph({ spacing: { line: 20, before: 0, after: 0 } })
2698
+ ] : [],
2699
+ ...showTitle ? [
2700
+ new import_docx2.Paragraph({
2701
+ style,
2702
+ children: [...icon, ...renderText(admonition.title_color ? node.title.map((r) => ({ ...r, color })) : node.title, node.sourceLine)],
2703
+ keepNext: true,
2704
+ ...prevIsSameType ? { spacing: { before: 0 } } : {}
2705
+ })
2706
+ ] : [],
2707
+ new import_docx2.Paragraph({
2708
+ style,
2709
+ children: renderText(node.text, node.sourceLine),
2710
+ ...prevIsSameType && !showTitle ? { spacing: { before: 0 } } : {}
2711
+ })
2712
+ ];
1083
2713
  case "sectionBreak":
1084
2714
  return [];
1085
2715
  default:
@@ -1087,12 +2717,20 @@ async function serializeDocx(doc, fout, workdir, assets, checkFilesIsInsidePath)
1087
2717
  throw new Error("switch default");
1088
2718
  }
1089
2719
  }, renderNodeL2 = function(node, prevChild, prevNode) {
1090
- const r = renderNode2(node, prevChild, prevNode);
1091
- if (r instanceof Array) return r;
1092
- return [r];
2720
+ try {
2721
+ const r = renderNode2(node, prevChild, prevNode);
2722
+ if (r instanceof Array) return r;
2723
+ return [r];
2724
+ } catch (error) {
2725
+ const message = error instanceof Error ? error.message : String(error);
2726
+ if (/^Line -?\d+:/.test(message)) throw error;
2727
+ throw new Error(`${node.sourceLine ? `Line ${node.sourceLine}: ` : ""}${message}`, { cause: error });
2728
+ }
1093
2729
  };
1094
- var renderNode = renderNode2, renderNodeL = renderNodeL2;
2730
+ var renderHeaderFooter = renderHeaderFooter2, renderHeaderFooterTable = renderHeaderFooterTable2, renderNode = renderNode2, renderNodeL = renderNodeL2;
1095
2731
  const children = [];
2732
+ const header = renderHeaderFooter2(section.header, false);
2733
+ const footer = renderHeaderFooter2(section.footer, true);
1096
2734
  sections.push({
1097
2735
  children,
1098
2736
  properties: {
@@ -1107,38 +2745,26 @@ async function serializeDocx(doc, fout, workdir, assets, checkFilesIsInsidePath)
1107
2745
  // height: convertMillimetersToTwip(297),
1108
2746
  // },
1109
2747
  size: {
1110
- orientation: section.orientation == "landscape" ? import_docx.PageOrientation.LANDSCAPE : import_docx.PageOrientation.PORTRAIT
2748
+ orientation: section.orientation == "landscape" ? import_docx2.PageOrientation.LANDSCAPE : import_docx2.PageOrientation.PORTRAIT
1111
2749
  },
1112
2750
  pageNumbers: !section.pageStart ? {} : {
1113
2751
  start: section.pageStart
1114
2752
  },
1115
2753
  margin: {
1116
- top: (0, import_docx.convertMillimetersToTwip)(20),
1117
- left: (0, import_docx.convertMillimetersToTwip)(30),
1118
- right: (0, import_docx.convertMillimetersToTwip)(15),
1119
- bottom: (0, import_docx.convertMillimetersToTwip)(20)
2754
+ top: (0, import_docx2.convertMillimetersToTwip)(20),
2755
+ left: (0, import_docx2.convertMillimetersToTwip)(30),
2756
+ right: (0, import_docx2.convertMillimetersToTwip)(15),
2757
+ bottom: (0, import_docx2.convertMillimetersToTwip)(20)
1120
2758
  }
1121
2759
  }
1122
2760
  },
1123
- footers: {
1124
- default: new import_docx.Footer({
1125
- children: [
1126
- new import_docx.Paragraph({
1127
- alignment: import_docx.AlignmentType.CENTER,
1128
- children: [new import_docx.TextRun({
1129
- children: section.displayPageNum ? [import_docx.PageNumber.CURRENT] : []
1130
- })],
1131
- indent: { firstLine: 0 },
1132
- spacing: { line: 240 }
1133
- })
1134
- ]
1135
- })
1136
- }
2761
+ ...header ? { headers: { default: header } } : {},
2762
+ ...footer ? { footers: { default: footer } } : {}
1137
2763
  });
1138
2764
  for (let i = 0; i < section.nodes.length; i++)
1139
2765
  children.push(...renderNodeL2(section.nodes[i], children.at(-1), section.nodes[i - 1]));
1140
2766
  }
1141
- const docx = new import_docx.Document({
2767
+ const docx = new import_docx2.Document({
1142
2768
  sections,
1143
2769
  externalStyles: genXml_style(assets, doc),
1144
2770
  // features: { updateFields: true },
@@ -1156,7 +2782,7 @@ async function serializeDocx(doc, fout, workdir, assets, checkFilesIsInsidePath)
1156
2782
  // creator: doc.author,
1157
2783
  // lastModifiedBy: doc.author,
1158
2784
  });
1159
- const buffer = await import_docx.Packer.toBuffer(docx, void 0, [
2785
+ const buffer = await import_docx2.Packer.toBuffer(docx, void 0, [
1160
2786
  { path: "docProps/app.xml", data: genXml_app({ totalTime: doc.etime }) },
1161
2787
  {
1162
2788
  path: "docProps/core.xml",
@@ -1168,21 +2794,25 @@ async function serializeDocx(doc, fout, workdir, assets, checkFilesIsInsidePath)
1168
2794
  })
1169
2795
  }
1170
2796
  ]);
1171
- fs3.writeFileSync(fout, buffer);
1172
- function renderText(text, size) {
2797
+ fs4.writeFileSync(fout, buffer);
2798
+ function renderText(text, sourceLine, size) {
1173
2799
  function renderRune(rune, link = false) {
1174
- if (rune.anchor) return new import_docx.Bookmark({
2800
+ if (rune.anchor) return new import_docx2.Bookmark({
1175
2801
  id: rune.anchor,
1176
2802
  children: [renderRune({ ...rune, anchor: void 0 })]
1177
2803
  });
1178
2804
  if (rune.link) {
1179
2805
  const children2 = [renderRune({ ...rune, link: void 0 }, true)];
1180
- return rune.link.startsWith("#") ? new import_docx.InternalHyperlink({ children: children2, anchor: rune.link.slice(1) }) : new import_docx.ExternalHyperlink({ children: children2, link: rune.link });
2806
+ return rune.link.startsWith("#") ? new import_docx2.InternalHyperlink({ children: children2, anchor: rune.link.slice(1) }) : new import_docx2.ExternalHyperlink({ children: children2, link: rune.link });
1181
2807
  }
2808
+ if (rune.type == "math") return renderMath(rune.text, false, sourceLine);
1182
2809
  let children = null;
1183
- if (rune.type == "val" && rune.text == "pages") {
2810
+ if (rune.type == "val" && rune.text == "page") {
1184
2811
  rune.type = "text";
1185
- children = doc.totalPagesOffset == 0 ? [import_docx.PageNumber.TOTAL_PAGES] : [
2812
+ children = [import_docx2.PageNumber.CURRENT];
2813
+ } else if (rune.type == "val" && rune.text == "pages") {
2814
+ rune.type = "text";
2815
+ children = doc.totalPagesOffset == 0 ? [import_docx2.PageNumber.TOTAL_PAGES] : [
1186
2816
  // Complex Formula Field: { = { NUMPAGES } + 1 }
1187
2817
  // Outer field start: "="
1188
2818
  new RawXml("w:fldChar", { "w:fldCharType": "begin" }),
@@ -1203,7 +2833,7 @@ async function serializeDocx(doc, fout, workdir, assets, checkFilesIsInsidePath)
1203
2833
  new RawXml("w:fldChar", { "w:fldCharType": "end" })
1204
2834
  ];
1205
2835
  }
1206
- return new import_docx.TextRun({
2836
+ return new import_docx2.TextRun({
1207
2837
  ...children ? { children } : { text: rune.text },
1208
2838
  language: { value: rune.lang == "en" ? "en-US" : "ru-RU" },
1209
2839
  ...rune.linebreak ? { break: 1 } : {},
@@ -1264,12 +2894,41 @@ async function serializeDocx(doc, fout, workdir, assets, checkFilesIsInsidePath)
1264
2894
  }
1265
2895
  }
1266
2896
  }
2897
+ function renderMath(latex, para, sourceLine) {
2898
+ try {
2899
+ return latexToOmml(latex, para);
2900
+ } catch (error) {
2901
+ logwarn(`${sourceLine ? `Line ${doc.mapSourceLine(sourceLine + 1)}: ` : ""}Formula was not converted: ${error instanceof Error ? error.message : String(error)}`);
2902
+ return new import_docx2.TextRun(latex);
2903
+ }
2904
+ }
1267
2905
  }
2906
+ var admonitionIcons = {
2907
+ 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>`,
2908
+ 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>`,
2909
+ 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>`,
2910
+ 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>`,
2911
+ 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>`
2912
+ };
2913
+ var admonitionIconsFallback = {
2914
+ note: `iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAMAAADXqc3KAAAAilBMVEUAAABmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmbNZZ8EAAAALnRSTlMA//BXB/7YePSoRaAMKt3USM91y08CZiK8tUzjLxM82zjGblsPPSRhBkJzadIoQt+BPAAAAMBJREFUeJydUVcOwjAUi9Mm3YsOoLRlb7j/9RDJK22EEAL/OLKd5A3G/oVtBbnLs8CyTd2PQYj9se4IzKuubffVDMIZ6eBpfy44Xk4q3GhIRa6gVLiApThJ1M9bSF2BhUTxAZg+uZQ6yDxc9dVupfkCT7HUwQENpGIOauqWad6BkxFqAaBqyJCITKOhpzxsTGNCn/fl9ka5pHJtapDla0XHvkFWC343RnL+NkQ99vrEmF2YY/+8KLXajLv522p/wANc8QfHdtgFgwAAAABJRU5ErkJggg==`,
2915
+ info: `iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAMAAADXqc3KAAAAilBMVEUAAAAZdtIZdtIZdtIZdtIZdtIZdtIZdtIZdtIZdtIZdtIZdtIZdtIZdtIZdtIZdtIZdtIZdtIZdtIZdtIZdtIZdtIZdtIZdtIZdtIZdtIZdtIZdtIZdtIZdtIZdtIZdtIZdtIZdtIZdtIZdtIZdtIZdtIZdtIZdtIZdtIZdtIZdtIZdtIZdtIZdtL9UeA9AAAALnRSTlMA//BXB/7YePSoRaAMKt3USM91y08CZiK8tUzjLxM82zjGblsPPSRhBkJzadIoQt+BPAAAAMBJREFUeJydUVcOwjAUi9Mm3YsOoLRlb7j/9RDJK22EEAL/OLKd5A3G/oVtBbnLs8CyTd2PQYj9se4IzKuubffVDMIZ6eBpfy44Xk4q3GhIRa6gVLiApThJ1M9bSF2BhUTxAZg+uZQ6yDxc9dVupfkCT7HUwQENpGIOauqWad6BkxFqAaBqyJCITKOhpzxsTGNCn/fl9ka5pHJtapDla0XHvkFWC343RnL+NkQ99vrEmF2YY/+8KLXajLv522p/wANc8QfHdtgFgwAAAABJRU5ErkJggg==`,
2916
+ tip: `iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAMAAADXqc3KAAAAllBMVEUAAAAufTIufTIufTIufTIufTIufTIufTIufTIufTIufTIufTIufTIufTIufTIufTIufTIufTIufTIufTIufTIufTIufTIufTIufTIufTIufTIufTIufTIufTIufTIufTIufTIufTIufTIufTIufTIufTIufTIufTIufTIufTIufTIufTIufTIufTIufTIufTIufTIufTLBWjFSAAAAMnRSTlMA//AF9MXq5ssCgtub1XmmJgghfRkRC+exGm0tRBX+i2c1Tt8pMHC0RwxdtsOoo64eS7UJBqAAAADPSURBVHichVLZEoIwENtU7luUUwUB79v//zkHpKWiM+Ypk+x2szsl+ofAUhlTrWAkR7cCHQonlvWwASvrR7Sc6XBXgx67sOc9vcCYCGMmdKKjijXnE4Z6aF+g4fQJ3ZQGGtj2bIernKTESYxwZCOHJzpS2bCQ9+wANiQkUrHkqbQhIVEFTUTxoEWcm4YYQTS1YWQ99eFK71bK+4QtFL5Fh1A4SijrRAm6a2XQP3VKeMfYOHPjY9cWKfarqYP7WKcFa+vZ5sugja8l/vgv/MALHToJTMdMgakAAAAASUVORK5CYII=`,
2917
+ warning: `iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAMAAADXqc3KAAAAllBMVEUAAACyagCyagCyagCyagCyagCyagCyagCyagCyagCyagCyagCyagCyagCyagCyagCyagCyagCyagCyagCyagCyagCyagCyagCyagCyagCyagCyagCyagCyagCyagCyagCyagCyagCyagCyagCyagCyagCyagCyagCyagCyagCyagCyagCyagCyagCyagCyagCyagCyagD2Sl3HAAAAMnRSTlMA/w3r5vwbWbsElS1drcsiElT5ArX2wo045GaR779hCUx02KB7BdPiPyiCaxZSMsa5W/G4KzAAAADCSURBVHictZDJEoJADES7GZBFdlABFdz39f9/zmIoCge9WeaS5HVNkmngD6F5pKd9EeYkaX3yJ+NDRJofgwQ3gEO9P2zKlQ1oOicqHxrM6hzROCrCnWvYuQekHL/zkrMQA+rAYES346FggUbAkqLb79BHK9g7Tlt+ozEEUIlH3WVk3QFIfPXG5j2AgvtKFudr81edpcwzLiQIGG9lETX73da55NQeOpeWmUx79tiWFC6i9luNPJSzx7GKYyvom/9TvACzaQfmlOkXxAAAAABJRU5ErkJggg==`,
2918
+ 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`
2919
+ };
2920
+ var admonitionIconsFallbackBlack = {
2921
+ note: `iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAMAAADXqc3KAAAAilBMVEUAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAATAggvAAAALnRSTlMA//AHeFjY9N2gDCpEqM3TSToi47XVR9D9Zky8L3RWPRNPxm4CUFsPJGFpKAbKjdKbTAAAAMRJREFUeJydUdkSgjAM7BbKfYjKKQp4n///e860AdpxfNC8bGY3bZINY/+GJfzE5aUvLJOPPFB4kc7bwLru+r6rN4Ct83wx5ic+K0XlhnNV6FZUFaywk5insvMZjppAIJd4BZYycSAkxrirp1mmcIuYClThFC0ciRy0VJoovICTECgCoGlIcBCaQktfxTiYwthcIDWEo0fjWrQge+0l3MYFWQP+0C3BZLBu4qCZqGxvnoxZg2k7Y8WXQ8nTltxNPk77Q7wBVQsHvaZMK8YAAAAASUVORK5CYII=`,
2922
+ info: `iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAMAAADXqc3KAAAAilBMVEUAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAATAggvAAAALnRSTlMA//AHeFjY9N2gDCpEqM3TSToi47XVR9D9Zky8L3RWPRNPxm4CUFsPJGFpKAbKjdKbTAAAAMRJREFUeJydUdkSgjAM7BbKfYjKKQp4n///e860AdpxfNC8bGY3bZINY/+GJfzE5aUvLJOPPFB4kc7bwLru+r6rN4Ct83wx5ic+K0XlhnNV6FZUFaywk5insvMZjppAIJd4BZYycSAkxrirp1mmcIuYClThFC0ciRy0VJoovICTECgCoGlIcBCaQktfxTiYwthcIDWEo0fjWrQge+0l3MYFWQP+0C3BZLBu4qCZqGxvnoxZg2k7Y8WXQ8nTltxNPk77Q7wBVQsHvaZMK8YAAAAASUVORK5CYII=`,
2923
+ tip: `iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAMAAADXqc3KAAAAh1BMVEUAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD3YishAAAALXRSTlMA/+gFxBrmCvAH9EXbmyd9sC+kedXxTckiEW3ryx8VZ4s1gt8rqHC1XQyDshgZvchdAAAAyUlEQVR4nIWS5w7CMAyEfR20dO/SSSdlvf/zIaokDQWJ+3XyZ8dDIfonb9BMUxu8XbiIwOQacrwZkXZZU9j+AUm7xY0EzpNZDbkqgA9H5/6W48y9aiLbyieM3GY4yA1rhMxdMMugw1W0cGVQIhYVgQzuKJmzYW4TEmmw+VTVNiFRD0WkxVAKAXLRgujooGYbHmck0ru9xW8IWHyLVSdBrJMcJ1KwvqV/HmEFTHuwcPDYAQqwtIb7eYNVYfrOT6cvQF5UKdH+L/zQCzA0CJnLbfrbAAAAAElFTkSuQmCC`,
2924
+ warning: `iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAMAAADXqc3KAAAAllBMVEUAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA6C80qAAAAMnRSTlMA/w3mA/xUu1kb610AyyKtEi35lbXvwvZhv2aPCTnYixagayh7TDKSBeLTdII/xrk2W23LF/kAAADFSURBVHictZDJcsIwEET7WbINosDGLGELS8Kanf//OUpyucAmN4q+jOZ1aUZq6QmKMsiif4wpQHrPzxTfCbTuBjm20gDTHDZnaaXIMK/zfsyrrwnxe834YSNrMmnI+JZPGOVqY6T2C50rzx0zlYY+cdf9A7qqDPvBoeJ74r6khfv13R/4zqvL6nZheV/SjNMiHN6S8q+GSagjdgH0KI7hkJT7O1Vy9qt66DRE1mLYiMemwVg7n3ddJg+zx0UdF2mvGf5DugCExweZt6hkrgAAAABJRU5ErkJggg==`,
2925
+ danger: `iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAMAAADXqc3KAAAAYFBMVEUAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD6T+iNAAAAIHRSTlMA/wi9Cr8lwgwY7Bwhw7cW29Tl4OkdVh6z/bmWpsXXyQwvE4cAAACqSURBVHicpVFHEsIwEJPiFKeRXgjt/79ksNfEBA8XdNJKnpFXC/xGvlKw5r7edE4nu2bXl5ZKW6oV29LpVU8VuSFSHBZL09HTgahmn5rcgomnA3HG4vWDC7PYKkkizu18BUBKLkgh2rD3eGT8w0gk/MvYwU9jng+GrJeSlSxvjM0tqLeTJXHGFcAUqmQKlziaEgO1SxQwDTyJE/mHAvJ7+LRA+XB67b0P4gn4WAUqr4Oj0wAAAABJRU5ErkJggg==`
2926
+ };
1268
2927
  function addListItemLevel(levels, level, startIndex, itemCount, ordered, alternativeStyle, mark) {
1269
2928
  if (levels.find((v) => v.level == level)) return;
1270
2929
  levels.sort((a, b) => a.level - b.level);
1271
2930
  let indent = level;
1272
- const format = ordered ? import_docx.LevelFormat.DECIMAL : import_docx.LevelFormat.BULLET;
2931
+ const format = ordered ? import_docx2.LevelFormat.DECIMAL : import_docx2.LevelFormat.BULLET;
1273
2932
  for (let i = 1; i < levels.length + 1; i++) {
1274
2933
  const prevprevformat = levels[i - 2]?.format;
1275
2934
  const prevformat = levels[i - 1]?.format;
@@ -1295,15 +2954,15 @@ function addListItemLevel(levels, level, startIndex, itemCount, ordered, alterna
1295
2954
  } : {},
1296
2955
  paragraph: {
1297
2956
  indent: {
1298
- left: (0, import_docx.convertMillimetersToTwip)(left + 5 * indent),
1299
- hanging: (0, import_docx.convertMillimetersToTwip)(longList && !alternativeStyle ? 8 : 5),
1300
- ...alternativeStyle ? { firstLine: (0, import_docx.convertMillimetersToTwip)(12.5) } : {}
2957
+ left: (0, import_docx2.convertMillimetersToTwip)(left + 5 * indent),
2958
+ hanging: (0, import_docx2.convertMillimetersToTwip)(longList && !alternativeStyle ? 8 : 5),
2959
+ ...alternativeStyle ? { firstLine: (0, import_docx2.convertMillimetersToTwip)(12.5) } : {}
1301
2960
  }
1302
2961
  }
1303
2962
  }
1304
2963
  });
1305
2964
  }
1306
- var RawXml = class extends import_docx.XmlComponent {
2965
+ var RawXml = class extends import_docx2.XmlComponent {
1307
2966
  constructor(tag, attributes = {}, text = null) {
1308
2967
  super(tag);
1309
2968
  if (Object.keys(attributes).length > 0)
@@ -1313,85 +2972,227 @@ var RawXml = class extends import_docx.XmlComponent {
1313
2972
  }
1314
2973
  };
1315
2974
  function renderCodeHighlighting(code, lang) {
1316
- const grammar = import_prismjs.default.languages[lang.trim().toLowerCase()];
2975
+ const language = lang.trim().toLowerCase();
2976
+ const grammar = import_prismjs.default.languages[language];
1317
2977
  if (!grammar) return null;
1318
2978
  const themeColors = {
1319
2979
  // shared
1320
2980
  "plain": "000000",
1321
2981
  "comment": "008000",
2982
+ "hashbang": "008000",
2983
+ "shebang": "008000",
1322
2984
  "punctuation": "000000",
1323
2985
  "operator": "000000",
2986
+ "script": "000000",
2987
+ "script-punctuation": "000000",
2988
+ "expression": "000000",
2989
+ "interpolation": "000000",
1324
2990
  // keywords & control flow
1325
2991
  "keyword": "0000ff",
2992
+ "control-flow": "af00db",
1326
2993
  "boolean": "0000ff",
1327
- "builtin": "0000ff",
2994
+ "null": "0000ff",
2995
+ "builtin": "267f99",
1328
2996
  "important": "0000ff",
2997
+ // "instruction": "0000ff",
2998
+ // "directive": "0000ff",
2999
+ "directive-hash": "0000ff",
3000
+ "macro": "0000ff",
3001
+ "macro-name": "0000ff",
3002
+ "rule": "0000ff",
1329
3003
  // literals
1330
- "string": lang === "css" ? "0451a5" : lang === "json" ? "a31515" : "a31515",
1331
- "number": "098658",
1332
- "constant": "0000ff",
1333
- "symbol": "0000ff",
3004
+ "string": "a31515",
3005
+ "template-string": "a31515",
3006
+ "template-punctuation": "a31515",
3007
+ "string-literal": "a31515",
1334
3008
  "char": "a31515",
3009
+ "number": "098658",
3010
+ "datetime": "098658",
3011
+ "constant": "0070c1",
3012
+ "symbol": "0070c1",
3013
+ "scalar": "0000ff",
3014
+ "conversion-option": "0000ff",
1335
3015
  // identifiers
1336
- "class-name": lang === "js" || lang === "ts" ? "267f99" : "0451a5",
1337
- "function": "0451a5",
1338
- "variable": "000000",
1339
- "namespace": "000000",
1340
- "property": lang === "css" ? "e50000" : "0451a5",
3016
+ "class-name": "267f99",
3017
+ "class-name-definition": "267f99",
3018
+ "type-definition": "267f99",
3019
+ "return-type": "267f99",
3020
+ "attribute": "267f99",
3021
+ "attribute-class-name": "267f99",
3022
+ "annotation": "267f99",
3023
+ "decorator": "267f99",
3024
+ "namespace": "267f99",
3025
+ "double-colon": "267f99",
3026
+ "function": "795e26",
3027
+ "function-definition": "795e26",
3028
+ "method-definition": "795e26",
3029
+ "variable": "001080",
3030
+ "parameter": "001080",
3031
+ "environment": "001080",
3032
+ "assign-left": "001080",
3033
+ "property": language === "css" ? "e50000" : language === "json" ? "0451a5" : "001080",
3034
+ "key": "0451a5",
3035
+ "package": "000000",
3036
+ "lifetime-annotation": "0000ff",
1341
3037
  // markup / HTML / XML
1342
- "tag": lang === "html" || lang === "xml" ? "800000" : "0000ff",
3038
+ "tag": "800000",
1343
3039
  "selector": "800000",
1344
3040
  "attr-name": "e50000",
1345
- "attr-value": "a31515",
3041
+ "attr-value": "0000ff",
1346
3042
  "entity": "800000",
1347
3043
  "doctype": "808080",
3044
+ "doctype-tag": "808080",
3045
+ "name": "808080",
1348
3046
  "cdata": "808080",
1349
3047
  "prolog": "808080",
3048
+ "delimiter": language === "php" ? "800000" : "a31515",
1350
3049
  // CSS specific
1351
3050
  "atrule": "0000ff",
1352
3051
  "url": "0451a5",
1353
3052
  // regex
1354
- "regex": "811f3f"
3053
+ "regex": "811f3f",
3054
+ "regex-delimiter": "811f3f",
3055
+ "regex-source": "811f3f",
3056
+ "regex-flags": "0000ff",
3057
+ "interpolation-punctuation": "0000ff",
3058
+ // Markdown
3059
+ "title": "800000",
3060
+ "blockquote": "0451a5",
3061
+ "list": "0451a5",
3062
+ "bold": "000080",
3063
+ "italic": "800080",
3064
+ "code": "800000",
3065
+ "code-snippet": "800000",
3066
+ "code-block": "800000",
3067
+ "code-language": "0000ff",
3068
+ "inserted": "098658",
3069
+ "deleted": "a31515",
3070
+ "strike": "000000",
3071
+ "hr": "800000"
1355
3072
  };
3073
+ const controlFlowKeywords = /* @__PURE__ */ new Set([
3074
+ "if",
3075
+ "else",
3076
+ "elif",
3077
+ "elseif",
3078
+ "unless",
3079
+ "then",
3080
+ "switch",
3081
+ "case",
3082
+ "default",
3083
+ "when",
3084
+ "match",
3085
+ "for",
3086
+ "foreach",
3087
+ "while",
3088
+ "do",
3089
+ "until",
3090
+ "loop",
3091
+ "break",
3092
+ "continue",
3093
+ "return",
3094
+ "goto",
3095
+ "throw",
3096
+ "throws",
3097
+ "try",
3098
+ "catch",
3099
+ "except",
3100
+ "finally",
3101
+ "rescue",
3102
+ "ensure",
3103
+ "await",
3104
+ "yield",
3105
+ "new",
3106
+ "delete",
3107
+ "in",
3108
+ "of",
3109
+ "instanceof"
3110
+ ]);
1356
3111
  const tokens = import_prismjs.default.tokenize(code, grammar);
1357
3112
  return tokensToParagraphs(tokens);
1358
3113
  function tokensToParagraphs(tokens2) {
1359
3114
  const paragraphs = [];
1360
3115
  let currentRuns = [];
1361
- function addText(text, type) {
3116
+ const plainStyle = { color: themeColors.plain, bold: false, italics: false, strike: false };
3117
+ function addText(text, style) {
1362
3118
  const lines = text.split("\n");
1363
3119
  lines.forEach((line, index) => {
1364
3120
  currentRuns.push(
1365
- new import_docx.TextRun({
3121
+ new import_docx2.TextRun({
1366
3122
  text: line,
1367
- color: themeColors[type] || themeColors["plain"],
1368
- bold: type === "bold",
1369
- italics: type === "italic"
3123
+ color: style.color,
3124
+ bold: style.bold,
3125
+ italics: style.italics,
3126
+ strike: style.strike
1370
3127
  })
1371
3128
  );
1372
3129
  if (index < lines.length - 1) {
1373
- paragraphs.push(new import_docx.Paragraph({ children: currentRuns, style: STYLE_code }));
3130
+ paragraphs.push(new import_docx2.Paragraph({ children: currentRuns, style: STYLE_code }));
1374
3131
  currentRuns = [];
1375
3132
  }
1376
3133
  });
1377
3134
  }
1378
- function process2(token, parentType = "plain") {
3135
+ function process2(token, parentStyle = plainStyle) {
1379
3136
  if (typeof token === "string")
1380
- addText(token, parentType);
1381
- else if (Array.isArray(token.content))
1382
- token.content.forEach((t) => process2(t, token.type));
1383
- else if (typeof token.content === "string")
1384
- addText(token.content, token.type);
1385
- else
1386
- process2(token.content, token.type);
3137
+ addText(token, parentStyle);
3138
+ else {
3139
+ const aliases = typeof token.alias == "string" ? [token.alias] : token.alias || [];
3140
+ const types = [token.type, ...aliases];
3141
+ const colorType = types.find((type) => themeColors[type]);
3142
+ const isControlFlow = token.type == "keyword" && typeof token.content == "string" && controlFlowKeywords.has(token.content);
3143
+ const inheritPunctuation = token.type == "punctuation" && parentStyle.color != themeColors.plain;
3144
+ const style = {
3145
+ color: isControlFlow ? themeColors["control-flow"] : inheritPunctuation ? parentStyle.color : colorType ? themeColors[colorType] : parentStyle.color,
3146
+ bold: parentStyle.bold || types.includes("bold") || types.includes("important") || types.includes("title"),
3147
+ italics: parentStyle.italics || types.includes("italic"),
3148
+ strike: parentStyle.strike || types.includes("strike")
3149
+ };
3150
+ if (Array.isArray(token.content))
3151
+ token.content.forEach((t) => process2(t, style));
3152
+ else if (typeof token.content === "string")
3153
+ addText(token.content, style);
3154
+ else
3155
+ process2(token.content, style);
3156
+ }
1387
3157
  }
1388
3158
  tokens2.forEach((t) => process2(t));
1389
3159
  if (currentRuns.length > 0) {
1390
- paragraphs.push(new import_docx.Paragraph({ children: currentRuns, style: STYLE_code }));
3160
+ paragraphs.push(new import_docx2.Paragraph({ children: currentRuns, style: STYLE_code }));
1391
3161
  }
1392
3162
  return paragraphs;
1393
3163
  }
1394
3164
  }
3165
+ function estimateNumberWidthPt(text, fontSizePt) {
3166
+ const charWidths = {
3167
+ ".": 0.25,
3168
+ ",": 0.25,
3169
+ "-": 0.333,
3170
+ "(": 0.333,
3171
+ ")": 0.333,
3172
+ " ": 0.25,
3173
+ "a": 0.444,
3174
+ "b": 0.5,
3175
+ "c": 0.444,
3176
+ "d": 0.5,
3177
+ "e": 0.444,
3178
+ "A": 0.722,
3179
+ "B": 0.667,
3180
+ "C": 0.667,
3181
+ "D": 0.722,
3182
+ "E": 0.611,
3183
+ "\u0430": 0.5,
3184
+ "\u0431": 0.5,
3185
+ "\u0432": 0.5,
3186
+ "\u0433": 0.5,
3187
+ "\u0434": 0.5,
3188
+ "\u0410": 0.722,
3189
+ "\u0411": 0.667,
3190
+ "\u0412": 0.667,
3191
+ "\u0413": 0.611,
3192
+ "\u0414": 0.722
3193
+ };
3194
+ return [...text].reduce((width, char) => width + (charWidths[char] ?? 0.5) * fontSizePt, 0);
3195
+ }
1395
3196
  function genXml_app({ totalTime }) {
1396
3197
  return `<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
1397
3198
  <Properties xmlns="http://schemas.openxmlformats.org/officeDocument/2006/extended-properties" xmlns:vt="http://schemas.openxmlformats.org/officeDocument/2006/docPropsVTypes">
@@ -1399,23 +3200,32 @@ function genXml_app({ totalTime }) {
1399
3200
  </Properties>
1400
3201
  `;
1401
3202
  }
3203
+ function escapeXml(value) {
3204
+ return value.replaceAll(/[<>&"']/g, (ch) => ({
3205
+ "<": "&lt;",
3206
+ ">": "&gt;",
3207
+ "&": "&amp;",
3208
+ '"': "&quot;",
3209
+ "'": "&apos;"
3210
+ })[ch]);
3211
+ }
1402
3212
  function genXml_core({ title, creator, createdAt, modifiedAt }) {
1403
3213
  if (!createdAt) createdAt = /* @__PURE__ */ new Date();
1404
3214
  if (!modifiedAt) modifiedAt = /* @__PURE__ */ new Date();
1405
3215
  return `<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
1406
3216
  <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">
1407
- <dc:title>${title || "Document"}</dc:title>
1408
- <dc:creator>${creator || "Student"}</dc:creator>
1409
- <cp:lastModifiedBy>${creator || "Student"}</cp:lastModifiedBy>
3217
+ <dc:title>${escapeXml(title || "Document")}</dc:title>
3218
+ <dc:creator>${escapeXml(creator || "Student")}</dc:creator>
3219
+ <cp:lastModifiedBy>${escapeXml(creator || "Student")}</cp:lastModifiedBy>
1410
3220
  <dcterms:created xsi:type="dcterms:W3CDTF">${createdAt.toISOString()}</dcterms:created>
1411
3221
  <dcterms:modified xsi:type="dcterms:W3CDTF">${modifiedAt.toISOString()}</dcterms:modified>
1412
3222
  </cp:coreProperties>`;
1413
3223
  }
1414
3224
  function genXml_style(assets, doc) {
1415
- let xml = fs3.readFileSync(import_path2.default.join(assets, "styles.xml"), { encoding: "utf8" });
3225
+ let xml = fs4.readFileSync(import_path3.default.join(assets, "styles.xml"), { encoding: "utf8" });
1416
3226
  xml = editStyle(`<w:style w:type="paragraph" w:default="1" w:styleId="a2">`, (xml2) => {
1417
3227
  xml2 = replaceTag(xml2, `<w:spacing w:line="${doc.text.line_spacing * 240}" w:lineRule="auto" w:after="${doc.text.spacing.after * 20}"/>`);
1418
- xml2 = replaceTag(xml2, `<w:ind w:firstLine="${(0, import_docx.convertMillimetersToTwip)(doc.text.indent * 10)}"/>`);
3228
+ xml2 = replaceTag(xml2, `<w:ind w:firstLine="${(0, import_docx2.convertMillimetersToTwip)(doc.text.indent * 10)}"/>`);
1419
3229
  xml2 = replaceTag(xml2, `<w:sz w:val="${doc.text.size * 2}"/>`);
1420
3230
  return xml2;
1421
3231
  });
@@ -1437,7 +3247,33 @@ function genXml_style(assets, doc) {
1437
3247
  xml2 = addrPrStyle(xml2, doc.table.title.style == "bold" ? "<w:b/>" : "<w:i/>");
1438
3248
  return xml2;
1439
3249
  });
3250
+ const admonitionTypes = ["note", "info", "tip", "warning", "danger"];
3251
+ for (const type of admonitionTypes) {
3252
+ const suffix = type == "note" ? "" : type;
3253
+ const styleId = `xAdmonition${suffix}`;
3254
+ const styleTag = `<w:style w:type="paragraph" w:customStyle="1" w:styleId="${styleId}">`;
3255
+ const style = doc.admonition[type];
3256
+ xml = editStyle(styleTag, (xml2) => {
3257
+ xml2 = replaceTagOrInsertAfter(xml2, "<w:pPr>", `<w:spacing w:before="${style.spacing.before * 20}" w:after="${style.spacing.after * 20}"/>`);
3258
+ xml2 = replaceTagOrInsertAfter(xml2, "<w:pPr>", `<w:ind w:left="${(0, import_docx2.convertMillimetersToTwip)(style.indent * 10)}" w:firstLine="0"/>`);
3259
+ xml2 = replaceBorder(xml2, "top", style.padding.top, "FFFFFF", 2);
3260
+ xml2 = replaceBorder(xml2, "right", style.padding.right, "FFFFFF", 2);
3261
+ xml2 = replaceBorder(xml2, "bottom", style.padding.bottom, "FFFFFF", 2);
3262
+ xml2 = replaceBorder(xml2, "left", style.padding.left, style.color, Math.max(1, Math.round(style.bar_width * 8)));
3263
+ xml2 = replaceTagOrInsertAfter(xml2, "<w:pPr>", `<w:shd w:val="clear" w:color="auto" w:fill="${style.background}"/>`);
3264
+ return xml2;
3265
+ });
3266
+ }
1440
3267
  return xml;
3268
+ function replaceBorder(xml2, side, space, color, size) {
3269
+ return replaceTagOrInsertAfter(xml2, "<w:pBdr>", `<w:${side} w:val="single" w:sz="${size}" w:space="${space}" w:color="${color}"/>`);
3270
+ }
3271
+ function replaceTagOrInsertAfter(xml2, insertAfter2, tag) {
3272
+ const tagName = /<([^\s>]+)/.exec(tag)?.[1];
3273
+ if (!tagName) throw err();
3274
+ const regex = new RegExp(`<${tagName}\\b[^>]*/>`);
3275
+ return regex.test(xml2) ? xml2.replace(regex, tag) : xml2.replace(insertAfter2, `${insertAfter2}${tag}`);
3276
+ }
1441
3277
  function replaceTag(xml2, tag) {
1442
3278
  const tagName = /<([^\s]+)\b/.exec(tag)?.[1];
1443
3279
  if (!tagName) throw err();
@@ -1477,13 +3313,14 @@ function genXml_style(assets, doc) {
1477
3313
 
1478
3314
  // src/enricher.ts
1479
3315
  function enrichDoc(doc, logwarn = console.warn) {
3316
+ const warnAt = (line, message) => logwarn(`Line ${doc.mapSourceLine(line)}: ${message}`);
1480
3317
  for (let i = 0; i < doc.nodes.length; i++) {
1481
3318
  const node = doc.nodes[i];
1482
- if (node.type == "title" && node.text.toUpperCase().startsWith("\u0422\u0418\u0422\u0423\u041B\u042C\u041D\u0418\u041A")) {
1483
- doc.nodes.splice(i, 1, { type: "text", text: node.text.toUpperCase() });
1484
- doc.nodes.splice(i + 1, 0, { type: "pageBreak" });
3319
+ if (node.type == "title" && node.level == 1 && node.text.toUpperCase().startsWith("\u0422\u0418\u0422\u0423\u041B\u042C\u041D\u0418\u041A")) {
3320
+ doc.nodes.splice(i, 1, { type: "text", text: node.text.toUpperCase(), sourceLine: node.sourceLine });
3321
+ doc.nodes.splice(i + 1, 0, { type: "pageBreak", sourceLine: node.sourceLine });
1485
3322
  i++;
1486
- } else if (node.type == "title" && node.text.toUpperCase() == "\u0420\u0415\u0424\u0415\u0420\u0410\u0422") {
3323
+ } else if (node.type == "title" && node.level == 1 && node.text.toUpperCase() == "\u0420\u0415\u0424\u0415\u0420\u0410\u0422") {
1487
3324
  node.level = 0;
1488
3325
  node.text = node.text.toUpperCase();
1489
3326
  const nextNode = doc.nodes[i + 1];
@@ -1491,81 +3328,83 @@ function enrichDoc(doc, logwarn = console.warn) {
1491
3328
  nextNode.noIndent = true;
1492
3329
  nextNode.text = trimEnd(nextNode.text, ".").toUpperCase();
1493
3330
  }
1494
- 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." });
3331
+ 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 });
1495
3332
  while (i + 1 < doc.nodes.length && doc.nodes[i + 1]?.type != "title") i++;
1496
- doc.nodes.splice(i + 1, 0, { type: "pageBreak" });
1497
- } else if (node.type == "title" && node.text.toUpperCase() == "\u041E\u0413\u041B\u0410\u0412\u041B\u0415\u041D\u0418\u0415") {
3333
+ doc.nodes.splice(i + 1, 0, { type: "pageBreak", sourceLine: node.sourceLine });
3334
+ } else if (node.type == "title" && node.level == 1 && node.text.toUpperCase() == "\u041E\u0413\u041B\u0410\u0412\u041B\u0415\u041D\u0418\u0415") {
1498
3335
  node.level = 0;
1499
3336
  node.text = node.text.toUpperCase();
1500
- doc.nodes.splice(i + 1, 0, { type: "tableOfContents" });
1501
- } 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") {
3337
+ doc.nodes.splice(i + 1, 0, { type: "tableOfContents", sourceLine: node.sourceLine });
3338
+ } 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") {
1502
3339
  node.center = true;
1503
3340
  node.text = node.text.toUpperCase();
1504
- const warn = () => logwarn("Wrong format of \u0422\u0415\u0420\u041C\u0418\u041D\u042B \u0418 \u041E\u041F\u0420\u0415\u0414\u0415\u041B\u0415\u041D\u0418\u042F");
3341
+ 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");
1505
3342
  const list = doc.nodes[i + 1];
1506
3343
  if (list?.type != "list") {
1507
- warn();
3344
+ warn(node.sourceLine);
1508
3345
  continue;
1509
3346
  }
1510
- const rows = [tableRow("\u0422\u0435\u0440\u043C\u0438\u043D", "\u041E\u043F\u0440\u0435\u0434\u0435\u043B\u0435\u043D\u0438\u0435")];
3347
+ const rows = [tableRow(list.sourceLine, "\u0422\u0435\u0440\u043C\u0438\u043D", "\u041E\u043F\u0440\u0435\u0434\u0435\u043B\u0435\u043D\u0438\u0435")];
1511
3348
  const align = ["l", "l"];
1512
3349
  for (const item of list.items) {
1513
3350
  if (item.type != "listItem") {
1514
- warn();
3351
+ warn(item.sourceLine);
1515
3352
  continue;
1516
3353
  }
1517
3354
  const i2 = item.text.indexOf(":");
1518
3355
  if (i2 < 0) {
1519
- warn();
3356
+ warn(item.sourceLine);
1520
3357
  continue;
1521
3358
  }
1522
- rows.push(tableRow(item.text.slice(0, i2).trim(), item.text.slice(i2 + 1).trim()));
3359
+ rows.push(tableRow(item.sourceLine, item.text.slice(0, i2).trim(), item.text.slice(i2 + 1).trim()));
1523
3360
  }
1524
- 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." });
1525
- doc.nodes.splice(i + 2, 0, { type: "table", align, rows, normalFontSize: true, tags: ["definitions_table"] });
1526
- doc.nodes.splice(i + 3, 0, { type: "pageBreak" });
1527
- } 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") {
3361
+ 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 });
3362
+ doc.nodes.splice(i + 2, 0, { type: "table", align, rows, normalFontSize: true, tags: ["definitions_table"], sourceLine: node.sourceLine });
3363
+ doc.nodes.splice(i + 3, 0, { type: "pageBreak", sourceLine: node.sourceLine });
3364
+ } 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") {
1528
3365
  node.center = true;
1529
3366
  node.text = node.text.toUpperCase();
1530
- const warn = () => logwarn("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");
3367
+ 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");
1531
3368
  const list = doc.nodes[i + 1];
1532
3369
  if (list?.type != "list") {
1533
- warn();
3370
+ warn(node.sourceLine);
1534
3371
  continue;
1535
3372
  }
1536
3373
  const items = [];
1537
3374
  for (const item of list.items) {
1538
3375
  if (item.type != "listItem") {
1539
- warn();
3376
+ warn(item.sourceLine);
1540
3377
  continue;
1541
3378
  }
1542
- items.push({ type: "text", text: trimEnd(item.text, "."), noIndent: true, noMargin: true });
3379
+ items.push({ type: "text", text: trimEnd(item.text, "."), noIndent: true, noMargin: true, sourceLine: item.sourceLine });
1543
3380
  }
1544
- 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." });
1545
- doc.nodes.splice(i + 2, 0, { type: "pageBreak" });
3381
+ 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 });
3382
+ doc.nodes.splice(i + 2, 0, { type: "pageBreak", sourceLine: node.sourceLine });
1546
3383
  doc.nodes.splice(i + 2, 0, ...items);
1547
- } else if (node.type == "title" && node.text.toUpperCase() == "\u0412\u0412\u0415\u0414\u0415\u041D\u0418\u0415") {
3384
+ } else if (node.type == "title" && node.level == 1 && node.text.toUpperCase() == "\u0412\u0412\u0415\u0414\u0415\u041D\u0418\u0415") {
1548
3385
  node.center = true;
1549
3386
  node.text = node.text.toUpperCase();
1550
3387
  for (let j = i + 1; j < doc.nodes.length; j++) {
1551
3388
  const node2 = doc.nodes[j];
1552
3389
  if (node2?.type == "title") {
1553
- doc.nodes.splice(j, 0, { type: "pageBreak" });
3390
+ doc.nodes.splice(j, 0, { type: "pageBreak", sourceLine: -1 });
1554
3391
  break;
1555
3392
  }
1556
3393
  }
1557
- } else if (node.type == "title" && node.text.toUpperCase() == "\u0417\u0410\u041A\u041B\u042E\u0427\u0415\u041D\u0418\u0415") {
3394
+ } else if (node.type == "title" && node.level == 1 && node.text.toUpperCase() == "\u0417\u0410\u041A\u041B\u042E\u0427\u0415\u041D\u0418\u0415") {
1558
3395
  node.center = true;
1559
3396
  node.text = node.text.toUpperCase();
1560
- doc.nodes.splice(i, 0, { type: "pageBreak" });
3397
+ doc.nodes.splice(i, 0, { type: "pageBreak", sourceLine: node.sourceLine });
1561
3398
  i++;
1562
- } 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") {
3399
+ } 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") {
1563
3400
  node.center = true;
1564
3401
  node.text = node.text.toUpperCase();
1565
- doc.nodes.splice(i++, 0, { type: "pageBreak" });
3402
+ doc.nodes.splice(i++, 0, { type: "pageBreak", sourceLine: node.sourceLine });
1566
3403
  const nextNode = doc.nodes[i + 1];
1567
3404
  if (nextNode?.type == "list") {
1568
3405
  nextNode.alternativeStyle = true;
3406
+ nextNode.tags = nextNode.tags || [];
3407
+ nextNode.tags.push("sources");
1569
3408
  i++;
1570
3409
  continue;
1571
3410
  }
@@ -1574,35 +3413,38 @@ function enrichDoc(doc, logwarn = console.warn) {
1574
3413
  mark: ".",
1575
3414
  startIndex: 1,
1576
3415
  alternativeStyle: true,
3416
+ tags: ["sources"],
1577
3417
  ordered: true,
1578
- items: []
3418
+ items: [],
3419
+ sourceLine: node.sourceLine + 1
1579
3420
  };
1580
3421
  const startI = i + 1;
1581
3422
  while (true) {
1582
3423
  const textNode = doc.nodes[i + 1];
1583
3424
  if (textNode?.type != "text") break;
1584
3425
  const items = textNode.text.split(/\n\s*(?=\[[a-zA-Zа-яА-ЯёЁ_\d]+\])/);
3426
+ let sourceLine = textNode.sourceLine;
1585
3427
  for (const item of items)
1586
- list.items.push({ type: "listItem", text: item });
3428
+ list.items.push({ type: "listItem", text: item, sourceLine: sourceLine++ });
1587
3429
  i++;
1588
3430
  }
1589
3431
  if (list.items.length > 0)
1590
3432
  doc.nodes.splice(startI, i - startI + 1, list);
1591
- } else if (node.type == "title" && node.text.toUpperCase() == "\u041F\u0420\u0418\u041B\u041E\u0416\u0415\u041D\u0418\u042F") {
3433
+ } else if (node.type == "title" && node.level == 1 && node.text.toUpperCase() == "\u041F\u0420\u0418\u041B\u041E\u0416\u0415\u041D\u0418\u042F") {
1592
3434
  node.level = 0;
1593
3435
  node.center = true;
1594
3436
  node.text = node.text.toUpperCase();
1595
- doc.nodes.splice(i++, 0, { type: "pageBreak" });
3437
+ doc.nodes.splice(i++, 0, { type: "pageBreak", sourceLine: node.sourceLine });
1596
3438
  for (let j = i + 1; j < doc.nodes.length; j++) {
1597
3439
  const node2 = doc.nodes[j];
1598
3440
  if (node2.type == "title") {
1599
- doc.nodes.splice(i++ + 1, 0, { type: "text", text: "\u041F\u0440\u0438\u043B\u043E\u0436\u0435\u043D\u0438\u0435 " + node2.text.replace("]", "].") });
3441
+ 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 });
1600
3442
  j++;
1601
3443
  node2.level = 1;
1602
3444
  const splitI = node2.text.indexOf("]");
1603
- doc.nodes.splice(j++, 0, { type: "pageBreak" });
3445
+ doc.nodes.splice(j++, 0, { type: "pageBreak", sourceLine: node2.sourceLine });
1604
3446
  if (splitI >= 0) {
1605
- doc.nodes.splice(j++ + 1, 0, { type: "text", text: node2.text.slice(splitI + 1), center: true });
3447
+ doc.nodes.splice(j++ + 1, 0, { type: "text", text: node2.text.slice(splitI + 1), center: true, sourceLine: node2.sourceLine });
1606
3448
  node2.text = "\u041F\u0420\u0418\u041B\u041E\u0416\u0415\u041D\u0418\u0415 " + node2.text.slice(0, splitI + 1);
1607
3449
  node2.center = true;
1608
3450
  }
@@ -1638,11 +3480,11 @@ function enrichDoc(doc, logwarn = console.warn) {
1638
3480
  } else if (node.type == "externalDoc") {
1639
3481
  const nextNode = doc.nodes[i + 1];
1640
3482
  if (nextNode && nextNode?.type != "externalDoc" && nextNode?.type != "pageBreak" && nextNode?.type != "sectionBreak") {
1641
- doc.nodes.splice(i + 1, 0, { type: "pageBreak" });
3483
+ doc.nodes.splice(i + 1, 0, { type: "pageBreak", sourceLine: node.sourceLine });
1642
3484
  }
1643
3485
  const prevNode = doc.nodes[i - 1];
1644
3486
  if (prevNode && prevNode?.type != "externalDoc" && prevNode?.type != "pageBreak" && prevNode?.type != "sectionBreak") {
1645
- doc.nodes.splice(i, 0, { type: "pageBreak" });
3487
+ doc.nodes.splice(i, 0, { type: "pageBreak", sourceLine: node.sourceLine });
1646
3488
  i++;
1647
3489
  }
1648
3490
  }
@@ -1651,7 +3493,7 @@ function enrichDoc(doc, logwarn = console.warn) {
1651
3493
 
1652
3494
  // src/main.ts
1653
3495
  var import_child_process = require("child_process");
1654
- var import_promises3 = __toESM(require("fs/promises"));
3496
+ var import_promises4 = __toESM(require("fs/promises"));
1655
3497
  var import_fs2 = require("fs");
1656
3498
  var import_pdf_lib = require("pdf-lib");
1657
3499
 
@@ -1662,12 +3504,15 @@ function alchemist(doc, logwarn = console.warn) {
1662
3504
  codesAll: 0,
1663
3505
  imgsAll: 0,
1664
3506
  tablesAll: 0,
3507
+ formulasAll: 0,
1665
3508
  codes: 0,
1666
3509
  imgs: 0,
1667
3510
  tables: 0,
3511
+ formulas: 0,
1668
3512
  titles: { l1: 0, l2: 0, l3: 0, l4: 0, l5: 0 }
1669
3513
  };
1670
3514
  const named = {};
3515
+ const warnAt = (line, message) => logwarn(`${line ? `Line ${doc.mapSourceLine(line)}: ` : ""}${message}`);
1671
3516
  const vals = {};
1672
3517
  let lastRefI = 0;
1673
3518
  let prevNum = -1;
@@ -1684,11 +3529,13 @@ function alchemist(doc, logwarn = console.warn) {
1684
3529
  let codes = counter.codes;
1685
3530
  let imgs = counter.imgs;
1686
3531
  let tables = counter.tables;
3532
+ let formulas = counter.formulas;
1687
3533
  for (let j = i; j < doc.nodes.length && nextNum < 0; j++) {
1688
3534
  const n = doc.nodes[j];
1689
3535
  if (n.type == "code") nextNum = codes + 1;
1690
3536
  if (n.type == "image") nextNum = imgs + 1;
1691
3537
  if (n.type == "table") nextNum = tables + 1;
3538
+ if (n.type == "math") nextNum = formulas + 1;
1692
3539
  if (n.type == "title" && n.level == 1 && doc.numberingSections) {
1693
3540
  const num = getTitleNum(n);
1694
3541
  if (num) l1 = num;
@@ -1696,6 +3543,7 @@ function alchemist(doc, logwarn = console.warn) {
1696
3543
  codes = 0;
1697
3544
  imgs = 0;
1698
3545
  tables = 0;
3546
+ formulas = 0;
1699
3547
  }
1700
3548
  }
1701
3549
  nextPrefix = l1 > 0 && doc.numberingSections ? `${l1}.` : "";
@@ -1718,13 +3566,14 @@ function alchemist(doc, logwarn = console.warn) {
1718
3566
  counter.codes = 0;
1719
3567
  counter.imgs = 0;
1720
3568
  counter.tables = 0;
3569
+ counter.formulas = 0;
1721
3570
  }
1722
3571
  prefix = doc.numberingSections ? `${counter.titles.l1}.` : "";
1723
3572
  if (appendix) prefix = APPENDIX_NAME[counter.titles.l1 - 1] + ".";
1724
3573
  }
1725
3574
  }
1726
3575
  materializeNode(node);
1727
- if (node.type == "code" || node.type == "image" || node.type == "table") {
3576
+ if (node.type == "code" || node.type == "image" || node.type == "table" || node.type == "math") {
1728
3577
  if (node.type == "code") {
1729
3578
  counter.codes++;
1730
3579
  counter.codesAll++;
@@ -1737,18 +3586,27 @@ function alchemist(doc, logwarn = console.warn) {
1737
3586
  counter.tables++;
1738
3587
  counter.tablesAll++;
1739
3588
  }
3589
+ if (node.type == "math") {
3590
+ counter.formulas++;
3591
+ counter.formulasAll++;
3592
+ }
1740
3593
  prevNum = nextNum;
1741
3594
  prevPrefix = prefix;
1742
3595
  nextNum = -1;
1743
3596
  }
1744
3597
  });
3598
+ for (const value of [doc.header, doc.footer, ...doc.nodes.filter((node) => node.type == "sectionBreak").flatMap((node) => [node.header, node.footer])]) {
3599
+ if (value?.type != "content") continue;
3600
+ value.nodes.forEach(materializeNode);
3601
+ }
1745
3602
  const sourcesCount = crystallizeSources();
1746
- vals["codes"]?.forEach((f) => f(counter.codes));
1747
- vals["imgs"]?.forEach((f) => f(counter.imgs));
1748
- vals["tables"]?.forEach((f) => f(counter.tables));
3603
+ vals["codes"]?.forEach((f) => f(counter.codesAll));
3604
+ vals["imgs"]?.forEach((f) => f(counter.imgsAll));
3605
+ vals["tables"]?.forEach((f) => f(counter.tablesAll));
3606
+ vals["formulas"]?.forEach((f) => f(counter.formulasAll));
1749
3607
  vals["sources"]?.forEach((f) => f(sourcesCount));
1750
3608
  Object.entries(named).forEach(([k, v]) => {
1751
- if (v instanceof Array) logwarn(`\u041D\u0435\u0438\u0437\u0432\u0435\u0441\u0442\u043D\u0430\u044F \u0441\u0441\u044B\u043B\u043A\u0430 [${k}]`);
3609
+ 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}]`));
1752
3610
  });
1753
3611
  if (synopsis) {
1754
3612
  synopsis.text = [
@@ -1782,7 +3640,7 @@ function alchemist(doc, logwarn = console.warn) {
1782
3640
  function materializeList(node) {
1783
3641
  node.items.forEach((item) => {
1784
3642
  if (item.type == "list") materializeList(item);
1785
- else materializeRunes(item.text, node);
3643
+ else materializeRunes(item.text, { ...node, sourceLine: item.sourceLine ?? node.sourceLine });
1786
3644
  });
1787
3645
  }
1788
3646
  function materializeTable(node) {
@@ -1830,8 +3688,9 @@ function alchemist(doc, logwarn = console.warn) {
1830
3688
  rune.text = repeat(node.level, (i2) => counter.titles[`l${i2 + 1}`]).join(".");
1831
3689
  return;
1832
3690
  }
1833
- if (type == "code" || type == "image" || type == "table") {
1834
- 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" } : (() => {
3691
+ if (type == "code" || type == "image" || type == "table" || type == "math") {
3692
+ 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: "" } : (() => {
3693
+ type;
1835
3694
  throw new Error("switch default");
1836
3695
  })();
1837
3696
  num++;
@@ -1848,9 +3707,10 @@ function alchemist(doc, logwarn = console.warn) {
1848
3707
  nextRune.text = toCapitalCase(nextRune.text);
1849
3708
  }
1850
3709
  text = `${text} ${prefix}${num} \u2013 `;
3710
+ if (type == "math") text = `${prefix}${num}`;
1851
3711
  } else text = `${prefix}${num}`;
1852
3712
  if (v instanceof Array) v.forEach((fn) => fn.f(num, prefix));
1853
- else if (v && tag != "#") logwarn(`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" : ""} \u0443\u0436\u0435 \u0437\u0430\u043D\u044F\u0442\u043E \u0447\u0435\u043C-\u0442\u043E \u0434\u0440\u0443\u0433\u0438\u043C`);
3713
+ 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`);
1854
3714
  named[tag] = { n: num, prefix };
1855
3715
  rune.type = "text";
1856
3716
  rune.text = text;
@@ -1870,8 +3730,8 @@ function alchemist(doc, logwarn = console.warn) {
1870
3730
  rune.type = "text";
1871
3731
  rune.text = n < 0 ? prefix2 : `${prefix2}${applyMath(n)}`;
1872
3732
  };
1873
- if (v) v.push({ f, i: lastRefI++ });
1874
- else named[tag] = [{ f, i: lastRefI++ }];
3733
+ if (v) v.push({ f, i: lastRefI++, sourceLine: node.sourceLine });
3734
+ else named[tag] = [{ f, i: lastRefI++, sourceLine: node.sourceLine }];
1875
3735
  } else {
1876
3736
  rune.type = "text";
1877
3737
  rune.text = `${v.prefix}${applyMath(v.n)}`;
@@ -1879,21 +3739,22 @@ function alchemist(doc, logwarn = console.warn) {
1879
3739
  });
1880
3740
  }
1881
3741
  function crystallizeSources() {
1882
- const sourcesList = doc.nodes.find((node) => node.type == "list" && node.alternativeStyle);
3742
+ const sourcesList = doc.nodes.find((node) => node.type == "list" && node.tags?.includes("sources"));
1883
3743
  if (!sourcesList || sourcesList.type != "list") return 0;
1884
- const items = sourcesList.items.filter((v) => v.type == "listItem").map((v) => v.text).map((item, i) => {
3744
+ const items = sourcesList.items.filter((v) => v.type == "listItem").map((v, i) => {
3745
+ const item = v.text;
1885
3746
  const refI = item[0]?.text ? 0 : 1;
1886
3747
  const ref = item[refI];
1887
3748
  if (ref?.type != "ref") {
1888
- return { text: item, ref: "", i: 99999 + i };
3749
+ return { text: item, ref: "", i: 99999 + i, srcI: v.sourceLine };
1889
3750
  }
1890
3751
  const refs = named[ref.text];
1891
3752
  if (refs && !(refs instanceof Array)) {
1892
- logwarn(`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`);
1893
- return { text: item.slice(refI + 1), ref: "", i: 99999 + i };
3753
+ 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`);
3754
+ return { text: item.slice(refI + 1), ref: "", i: 99999 + i, srcI: v.sourceLine };
1894
3755
  }
1895
- const firstOccurrenceI = Math.min(...refs.map((v) => v.i));
1896
- return { text: item.slice(refI + 1), ref: ref.text, i: firstOccurrenceI };
3756
+ const firstOccurrenceI = Math.min(...refs.map((v2) => v2.i));
3757
+ return { text: item.slice(refI + 1), ref: ref.text, i: firstOccurrenceI, srcI: v.sourceLine };
1897
3758
  });
1898
3759
  items.sort((a, b) => a.i - b.i);
1899
3760
  const hasIdAny = items.some((it) => !!it.ref);
@@ -1903,16 +3764,17 @@ function alchemist(doc, logwarn = console.warn) {
1903
3764
  named[key] = { prefix: "", n: i + 1 };
1904
3765
  if (refs instanceof Array) {
1905
3766
  if (hasIdAny && refs.length <= 1)
1906
- logwarn(`\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}`);
3767
+ 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}`);
1907
3768
  refs.forEach((r) => hasIdAny ? r.f(i + 1, "") : r.f(-1, `[${i + 1}]`));
1908
3769
  }
1909
- return { type: "listItem", text: item.text };
3770
+ return { type: "listItem", text: item.text, sourceLine: item.srcI };
1910
3771
  });
1911
3772
  return sourcesList.items.length;
1912
3773
  }
1913
3774
  }
1914
3775
  function addLazyNumbering(doc) {
1915
3776
  doc.nodes.forEach((node) => {
3777
+ if (node.type == "math" && !node.title) node.title = [{ text: "(", type: "text" }, { text: "#", type: "ref" }, { text: ")", type: "text" }];
1916
3778
  const runes = node.type == "code" || node.type == "table" ? node.title : node.type == "image" ? node.text : null;
1917
3779
  if (!runes) return;
1918
3780
  if (runes.find((rune) => rune.type == "ref")) return;
@@ -1934,6 +3796,8 @@ async function render({
1934
3796
  disableMacros = false,
1935
3797
  useLibreOffice = false,
1936
3798
  checkFilesIsInsidePath = "",
3799
+ templateOptions = { allowIncludes: true, allowLoops: true },
3800
+ variables,
1937
3801
  logwarn = console.warn,
1938
3802
  loginfo = console.info,
1939
3803
  logPS = (msg) => console.log(`PS: ${msg}`),
@@ -1941,23 +3805,23 @@ async function render({
1941
3805
  signal = null
1942
3806
  }) {
1943
3807
  signal?.throwIfAborted();
1944
- const fin = import_path3.default.resolve(file);
3808
+ const fin = import_path4.default.resolve(file);
1945
3809
  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...");
1946
3810
  loginfo(`[!1] Parsing file ${fin}`);
1947
- const doc = await parseMD(fin, logwarn);
3811
+ const doc = await parseMD(fin, variables, templateOptions, checkFilesIsInsidePath, signal, logwarn);
1948
3812
  signal?.throwIfAborted();
1949
3813
  progress(10, "\u041A\u043E\u043B\u0434\u0443\u0435\u043C \u043D\u0430\u0434 \u0441\u0438\u043D\u0442\u0430\u043A\u0441\u0438\u0441\u043E\u043C...");
1950
3814
  loginfo("[!2] Processing document");
1951
3815
  enrichDoc(doc, logwarn);
1952
3816
  const runicDoc = runifyDoc(doc);
1953
3817
  alchemist(runicDoc, logwarn);
1954
- const pout = output && import_path3.default.parse(import_path3.default.resolve(output));
1955
- if (pout) await import_promises3.default.mkdir(pout.dir, { recursive: true });
1956
- const p = import_path3.default.parse(fin);
3818
+ const pout = output && import_path4.default.parse(import_path4.default.resolve(output));
3819
+ if (pout) await import_promises4.default.mkdir(pout.dir, { recursive: true });
3820
+ const p = import_path4.default.parse(fin);
1957
3821
  const fdir = p.dir;
1958
3822
  const fname = trimEnd(p.name, ".g");
1959
- const ftmp = import_path3.default.join(fdir, fname + ".tmp.docx");
1960
- const fout = pout ? import_path3.default.join(pout.dir, pout.name + ".docx") : import_path3.default.join(fdir, fname + ".docx");
3823
+ const ftmp = import_path4.default.join(fdir, fname + ".tmp.docx");
3824
+ const fout = pout ? import_path4.default.join(pout.dir, pout.name + ".docx") : import_path4.default.join(fdir, fname + ".docx");
1961
3825
  if (await checkIfFileIsBlocked(ftmp))
1962
3826
  throw new Error(`\u0417\u0430\u043A\u0440\u043E\u0439\u0442\u0435 docx \u0444\u0430\u0439\u043B. Output file is busy or locked: ${ftmp}`);
1963
3827
  if (await checkIfFileIsBlocked(fout))
@@ -1965,7 +3829,7 @@ async function render({
1965
3829
  signal?.throwIfAborted();
1966
3830
  progress(10, phrase_renderDocx());
1967
3831
  loginfo("[!3] Serializing to docx");
1968
- await serializeDocx(runicDoc, ftmp, fdir, assets, checkFilesIsInsidePath);
3832
+ await serializeDocx(runicDoc, ftmp, fdir, assets, checkFilesIsInsidePath, logwarn);
1969
3833
  let willRunMacros = !disableMacros && (renderPDF || hasReasonForRunningMacros(runicDoc));
1970
3834
  let limitedRender = false;
1971
3835
  if (process.platform != "win32" && willRunMacros && !useLibreOffice) {
@@ -1976,31 +3840,31 @@ async function render({
1976
3840
  signal?.throwIfAborted();
1977
3841
  progress(20, phrase_runMacros());
1978
3842
  loginfo("[!4] Starting macros");
1979
- const tmpfolder = import_path3.default.join(pout ? pout.dir : fdir, ".md2gost_out");
1980
- await import_promises3.default.rm(tmpfolder, { recursive: true, force: true });
3843
+ const tmpfolder = import_path4.default.join(pout ? pout.dir : fdir, ".md2gost_out");
3844
+ await import_promises4.default.rm(tmpfolder, { recursive: true, force: true });
1981
3845
  try {
1982
3846
  const macroRunner = useLibreOffice ? runUnoScript : runDocxMacro;
1983
3847
  const ok = await macroRunner(progress, logPS, logPSError, assets, fdir, ftmp, fout, renderPDF, signal);
1984
3848
  if (!ok) {
1985
- await import_promises3.default.rename(ftmp, fout);
3849
+ await import_promises4.default.rename(ftmp, fout);
1986
3850
  return { fout, err: "inPS" };
1987
3851
  }
1988
3852
  } catch (x) {
1989
3853
  if (signal?.aborted) {
1990
- await import_promises3.default.unlink(ftmp).catch(() => {
3854
+ await import_promises4.default.unlink(ftmp).catch(() => {
1991
3855
  });
1992
3856
  throw x;
1993
3857
  }
1994
- await import_promises3.default.rename(ftmp, fout);
3858
+ await import_promises4.default.rename(ftmp, fout);
1995
3859
  return { fout, err: "noPS", errS: x };
1996
3860
  }
1997
3861
  if ((0, import_fs2.existsSync)(ftmp))
1998
- await import_promises3.default.unlink(ftmp);
3862
+ await import_promises4.default.unlink(ftmp);
1999
3863
  signal?.throwIfAborted();
2000
- const errorTxt = import_path3.default.join(tmpfolder, "error.txt");
3864
+ const errorTxt = import_path4.default.join(tmpfolder, "error.txt");
2001
3865
  if ((0, import_fs2.existsSync)(errorTxt)) {
2002
- const err = await import_promises3.default.readFile(errorTxt);
2003
- await import_promises3.default.rm(tmpfolder, { recursive: true, force: true });
3866
+ const err = await import_promises4.default.readFile(errorTxt);
3867
+ await import_promises4.default.rm(tmpfolder, { recursive: true, force: true });
2004
3868
  return { fout, err: "vba", errS: `${err}` };
2005
3869
  }
2006
3870
  loginfo("[!5] Updating metadata");
@@ -2012,11 +3876,11 @@ async function render({
2012
3876
  if (!(0, import_fs2.existsSync)(tmpfolder))
2013
3877
  return { fout, err: "pdf" };
2014
3878
  try {
2015
- const files = (await import_promises3.default.readdir(tmpfolder)).sort().map((f) => import_path3.default.join(tmpfolder, f));
2016
- const pdf = pout ? import_path3.default.join(pout.dir, pout.base) : import_path3.default.join(fdir, fname + ".pdf");
3879
+ const files = (await import_promises4.default.readdir(tmpfolder)).sort().map((f) => import_path4.default.join(tmpfolder, f));
3880
+ const pdf = pout ? import_path4.default.join(pout.dir, pout.name + ".pdf") : import_path4.default.join(fdir, fname + ".pdf");
2017
3881
  await mergePDFs(files, pdf, doc, signal || void 0);
2018
- await import_promises3.default.rm(tmpfolder, { recursive: true, force: true });
2019
- if (removeIntermediateDocx && (0, import_fs2.existsSync)(fout)) await import_promises3.default.unlink(fout);
3882
+ await import_promises4.default.rm(tmpfolder, { recursive: true, force: true });
3883
+ if (removeIntermediateDocx && (0, import_fs2.existsSync)(fout)) await import_promises4.default.unlink(fout);
2020
3884
  return { fout: pdf };
2021
3885
  } catch (err) {
2022
3886
  if (signal?.aborted) throw err;
@@ -2024,7 +3888,7 @@ async function render({
2024
3888
  }
2025
3889
  }
2026
3890
  } else {
2027
- await import_promises3.default.rename(ftmp, fout);
3891
+ await import_promises4.default.rename(ftmp, fout);
2028
3892
  }
2029
3893
  loginfo(`[!7] Rendered to ${fout}`);
2030
3894
  return { fout, err: limitedRender ? "noWin" : void 0 };
@@ -2058,8 +3922,8 @@ function killProcessTree(child, wordPid) {
2058
3922
  }
2059
3923
  }
2060
3924
  function runDocxMacro(progress, log, logError, assets, cwd, fin, fout, renderPDF, signal) {
2061
- const script = import_path3.default.join(assets, "run.ps1");
2062
- const templateMacro = import_path3.default.join(assets, "template.dotm");
3925
+ const script = import_path4.default.join(assets, "run.ps1");
3926
+ const templateMacro = import_path4.default.join(assets, "template.dotm");
2063
3927
  return new Promise((res, rej) => {
2064
3928
  if (signal?.aborted)
2065
3929
  return rej(signal.reason ?? new Error("Aborted before execution started"));
@@ -2135,7 +3999,7 @@ function getPythonExecutable() {
2135
3999
  return process.env.UNO_PYTHON_PATH || "python3";
2136
4000
  }
2137
4001
  function runUnoScript(progress, log, logError, assets, cwd, fin, fout, renderPDF, signal) {
2138
- const script = process.env.MACRO_ENGINE_PATH || import_path3.default.join(assets, "macro_engine.py");
4002
+ const script = process.env.MACRO_ENGINE_PATH || import_path4.default.join(assets, "macro_engine.py");
2139
4003
  const pythonExec = getPythonExecutable();
2140
4004
  return new Promise((res, rej) => {
2141
4005
  if (signal?.aborted) {
@@ -2199,31 +4063,61 @@ function runUnoScript(progress, log, logError, assets, cwd, fin, fout, renderPDF
2199
4063
  });
2200
4064
  }
2201
4065
  async function mergePDFs(files, fout, doc, signal) {
2202
- const mergedPdf = await import_pdf_lib.PDFDocument.create();
4066
+ const pdfFiles = files.filter((file) => file.toLowerCase().endsWith(".pdf"));
4067
+ let mainPdfIndex = -1;
4068
+ for (let i = 0; i < pdfFiles.length; i++) {
4069
+ if (pdfFiles[i].toLowerCase().endsWith("-main.pdf"))
4070
+ mainPdfIndex = i;
4071
+ }
4072
+ let mergedPdf;
4073
+ if (mainPdfIndex >= 0) {
4074
+ const bytes = await import_promises4.default.readFile(pdfFiles[mainPdfIndex], { signal });
4075
+ mergedPdf = await import_pdf_lib.PDFDocument.load(bytes);
4076
+ signal?.throwIfAborted();
4077
+ let insertionIndex = 0;
4078
+ for (const file of pdfFiles.slice(0, mainPdfIndex)) {
4079
+ const sourceBytes = await import_promises4.default.readFile(file, { signal });
4080
+ const pdf = await import_pdf_lib.PDFDocument.load(sourceBytes);
4081
+ signal?.throwIfAborted();
4082
+ const pages = await mergedPdf.copyPages(pdf, pdf.getPageIndices());
4083
+ for (const page of pages)
4084
+ mergedPdf.insertPage(insertionIndex++, page);
4085
+ signal?.throwIfAborted();
4086
+ }
4087
+ for (const file of pdfFiles.slice(mainPdfIndex + 1)) {
4088
+ const sourceBytes = await import_promises4.default.readFile(file, { signal });
4089
+ const pdf = await import_pdf_lib.PDFDocument.load(sourceBytes);
4090
+ signal?.throwIfAborted();
4091
+ const pages = await mergedPdf.copyPages(pdf, pdf.getPageIndices());
4092
+ pages.forEach((page) => mergedPdf.addPage(page));
4093
+ signal?.throwIfAborted();
4094
+ }
4095
+ } else {
4096
+ mergedPdf = await import_pdf_lib.PDFDocument.create();
4097
+ for (const file of pdfFiles) {
4098
+ const bytes = await import_promises4.default.readFile(file, { signal });
4099
+ const pdf = await import_pdf_lib.PDFDocument.load(bytes);
4100
+ signal?.throwIfAborted();
4101
+ const pages = await mergedPdf.copyPages(pdf, pdf.getPageIndices());
4102
+ pages.forEach((page) => mergedPdf.addPage(page));
4103
+ signal?.throwIfAborted();
4104
+ }
4105
+ }
2203
4106
  if (doc?.title) mergedPdf.setTitle(doc.title, { showInWindowTitleBar: true });
2204
4107
  if (doc?.author) mergedPdf.setAuthor(doc.author);
2205
4108
  if (doc?.ctime) mergedPdf.setCreationDate(doc.ctime);
2206
4109
  if (doc?.mtime) mergedPdf.setModificationDate(doc.mtime);
2207
- for (const file of files) {
2208
- if (!file.endsWith(".pdf")) continue;
2209
- const bytes = await import_promises3.default.readFile(file, { signal });
2210
- const pdf = await import_pdf_lib.PDFDocument.load(bytes);
2211
- signal?.throwIfAborted();
2212
- const pages = await mergedPdf.copyPages(pdf, pdf.getPageIndices());
2213
- pages.forEach((page) => mergedPdf.addPage(page));
2214
- signal?.throwIfAborted();
2215
- }
2216
4110
  const pdfBytes = await mergedPdf.save();
2217
- await import_promises3.default.writeFile(fout, pdfBytes, { signal });
4111
+ await import_promises4.default.writeFile(fout, pdfBytes, { signal });
2218
4112
  }
2219
4113
  async function updateMetadata(docfile, doc) {
2220
- const content = await import_promises3.default.readFile(docfile);
4114
+ const content = await import_promises4.default.readFile(docfile);
2221
4115
  const zip = new import_pizzip.default(content);
2222
4116
  function replaceTag(xml, tag, value) {
2223
4117
  const regex = new RegExp(`(<${tag}\\b[^>]*>)(.*?)(</${tag}>)`, "gs");
2224
4118
  return xml.replace(
2225
4119
  regex,
2226
- (m, otag, old, ctag) => `${otag}${escapeXml(value)}${ctag}`
4120
+ (m, otag, old, ctag) => `${otag}${escapeXml2(value)}${ctag}`
2227
4121
  );
2228
4122
  }
2229
4123
  let coreXml = zip.file("docProps/core.xml").asText();
@@ -2237,8 +4131,8 @@ async function updateMetadata(docfile, doc) {
2237
4131
  appXml = replaceTag(appXml, "TotalTime", `${doc.etime || randomInt(30, 120)}`);
2238
4132
  zip.file("docProps/app.xml", appXml);
2239
4133
  const buffer = zip.generate({ type: "nodebuffer" });
2240
- await import_promises3.default.writeFile(docfile, buffer);
2241
- function escapeXml(unsafe) {
4134
+ await import_promises4.default.writeFile(docfile, buffer);
4135
+ function escapeXml2(unsafe) {
2242
4136
  return unsafe.replaceAll(/[<>&"']/g, (ch) => {
2243
4137
  switch (ch) {
2244
4138
  case "<":
@@ -2309,11 +4203,26 @@ async function renderMarkdown(config) {
2309
4203
  assert(typeof config.useLibreOffice == "boolean" || typeof config.useLibreOffice == "undefined", "useLibreOffice must be a boolean or undefined.");
2310
4204
  assert(typeof config.checkFilesIsInsidePath == "string" || config.checkFilesIsInsidePath === false || typeof config.checkFilesIsInsidePath == "undefined", "checkFilesIsInsidePath must be a string, false or undefined.");
2311
4205
  assert(typeof config.progress == "function" || typeof config.progress == "undefined", "Progress callback must be a function or undefined.");
4206
+ assert(typeof config.variables == "object" && config.variables !== null && !Array.isArray(config.variables) || typeof config.variables == "undefined", "variables must be an object or undefined.");
4207
+ assert(typeof config.templateOptions == "object" && config.templateOptions !== null && !Array.isArray(config.templateOptions) || typeof config.templateOptions == "undefined", "templateOptions must be an object or undefined.");
4208
+ if (config.templateOptions) {
4209
+ const options = config.templateOptions;
4210
+ const booleanProps = ["allowLoops", "allowIncludes"];
4211
+ const limitProps = ["maxLoopIterations", "maxTotalIterations", "maxDepth", "maxIncludeDepth", "maxIncludes", "maxFileLength", "maxTotalInputLength", "maxGeneratedLength"];
4212
+ const extraProps = getExtraProperties(options, [...booleanProps, ...limitProps]);
4213
+ assert(extraProps.length == 0, `Found unknown properties in config.templateOptions: ${extraProps.join(", ")}`);
4214
+ booleanProps.forEach(
4215
+ (p) => assert(options[p] === void 0 || typeof options[p] == "boolean", `config.templateOptions.${p} must be a boolean or undefined.`)
4216
+ );
4217
+ limitProps.forEach(
4218
+ (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.`)
4219
+ );
4220
+ }
2312
4221
  assert(config.abortSignal instanceof AbortSignal || typeof config.abortSignal == "undefined", "abortSignal must be an AbortSignal or undefined.");
2313
4222
  {
2314
4223
  const extraProps = getExtraProperties(
2315
4224
  config,
2316
- ["input", "output", "format", "keepIntermediateDocx", "disableMacros", "useLibreOffice", "progress", "logger", "abortSignal", "checkFilesIsInsidePath"]
4225
+ ["input", "output", "format", "keepIntermediateDocx", "disableMacros", "useLibreOffice", "progress", "logger", "abortSignal", "checkFilesIsInsidePath", "variables", "templateOptions"]
2317
4226
  );
2318
4227
  assert(extraProps.length == 0, `Found unknown properties in config: ${extraProps.join(", ")}`);
2319
4228
  }
@@ -2327,20 +4236,25 @@ async function renderMarkdown(config) {
2327
4236
  (p) => assert(typeof logger[p] == "function", `config.logger.${p} must be a function.`)
2328
4237
  );
2329
4238
  }
2330
- const renderPDF = config.format === "pdf" || !config.format && !!config.output?.endsWith(".pdf");
4239
+ const outputExtension = config.output && import_path5.default.extname(config.output).toLowerCase();
4240
+ const renderPDF = config.format === "pdf" || !config.format && outputExtension === ".pdf";
2331
4241
  const removeIntermediateDocx = !config.keepIntermediateDocx;
2332
4242
  const disableMacros = !!config.disableMacros;
2333
4243
  const useLibreOffice = !!config.useLibreOffice;
2334
4244
  const checkFilesIsInsidePath = config.checkFilesIsInsidePath === void 0 ? "" : config.checkFilesIsInsidePath;
2335
4245
  assert(!(disableMacros && renderPDF), "Macros must be enabled to render PDF output.");
2336
- const pin = import_path4.default.parse(import_path4.default.resolve(config.input));
4246
+ if (outputExtension && outputExtension !== ".docx" && outputExtension !== ".pdf")
4247
+ throw new TypeError("Output file extension must be either '.docx' or '.pdf'.");
4248
+ if (outputExtension && outputExtension !== (renderPDF ? ".pdf" : ".docx"))
4249
+ throw new TypeError(`Output file extension must match the selected '${renderPDF ? "pdf" : "docx"}' format.`);
4250
+ const pin = import_path5.default.parse(import_path5.default.resolve(config.input));
2337
4251
  const fname = pin.name.endsWith(".g") ? pin.name.slice(0, -2) : pin.name;
2338
4252
  let resolvedOutput = config.output;
2339
4253
  if (resolvedOutput) {
2340
- const isDir = resolvedOutput.endsWith("/") || resolvedOutput.endsWith("\\") || !import_path4.default.extname(resolvedOutput);
4254
+ const isDir = resolvedOutput.endsWith("/") || resolvedOutput.endsWith("\\") || !import_path5.default.extname(resolvedOutput);
2341
4255
  if (isDir) {
2342
4256
  const ext = renderPDF ? ".pdf" : ".docx";
2343
- resolvedOutput = import_path4.default.join(resolvedOutput, fname + ext);
4257
+ resolvedOutput = import_path5.default.join(resolvedOutput, fname + ext);
2344
4258
  }
2345
4259
  }
2346
4260
  const warnings = [];
@@ -2365,6 +4279,8 @@ async function renderMarkdown(config) {
2365
4279
  disableMacros,
2366
4280
  useLibreOffice,
2367
4281
  checkFilesIsInsidePath,
4282
+ variables: config.variables,
4283
+ templateOptions: config.templateOptions,
2368
4284
  loginfo: logger?.info,
2369
4285
  logwarn: (msg) => {
2370
4286
  logger?.warn(msg);
@@ -2380,7 +4296,6 @@ async function renderMarkdown(config) {
2380
4296
  },
2381
4297
  signal: config.abortSignal
2382
4298
  });
2383
- config.progress?.(100, "Done!");
2384
4299
  if (useLibreOffice) {
2385
4300
  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.`);
2386
4301
  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.`);
@@ -2391,10 +4306,11 @@ async function renderMarkdown(config) {
2391
4306
  if (err == "vba") throwMDE2("vba", `Microsoft Word VBA Macro execution failed: ${errS}`);
2392
4307
  if (err == "pdf") throwMDE2("pdf", `Failed to export document to PDF.`);
2393
4308
  if (err == "noWin") throwMDE2("noWin", `Platform restriction: Extended formatting macros and PDF rendering are only supported natively on Windows systems.`);
4309
+ config.progress?.(100, "Done!");
2394
4310
  let intermediateDocxPath;
2395
4311
  if (renderPDF && !removeIntermediateDocx) {
2396
- const pout = resolvedOutput ? import_path4.default.parse(import_path4.default.resolve(resolvedOutput)) : void 0;
2397
- intermediateDocxPath = pout ? import_path4.default.join(pout.dir, pout.name + ".docx") : import_path4.default.join(pin.dir, fname + ".docx");
4312
+ const pout = resolvedOutput ? import_path5.default.parse(import_path5.default.resolve(resolvedOutput)) : void 0;
4313
+ intermediateDocxPath = pout ? import_path5.default.join(pout.dir, pout.name + ".docx") : import_path5.default.join(pin.dir, fname + ".docx");
2398
4314
  }
2399
4315
  return {
2400
4316
  filePath: fout,
@@ -2452,10 +4368,7 @@ var MDRenderError = class _MDRenderError extends Error {
2452
4368
  }
2453
4369
  };
2454
4370
  function getAssetsDir() {
2455
- if (typeof __dirname !== "undefined")
2456
- return import_path4.default.resolve(__dirname, "../assets");
2457
- const metaUrl = new Function("return import.meta.url")();
2458
- return import_path4.default.resolve(import_path4.default.dirname((0, import_url.fileURLToPath)(metaUrl)), "../assets");
4371
+ return import_path5.default.resolve(__MD2GOST_DIRNAME__, "../assets");
2459
4372
  }
2460
4373
  function getExtraProperties(configObj, allowedConfigKeys) {
2461
4374
  return Object.keys(configObj).filter(
@@ -2464,6 +4377,7 @@ function getExtraProperties(configObj, allowedConfigKeys) {
2464
4377
  }
2465
4378
  // Annotate the CommonJS export names for ESM import in node:
2466
4379
  0 && (module.exports = {
2467
- MDRenderError
4380
+ MDRenderError,
4381
+ UserInputError
2468
4382
  });
2469
4383
  //# sourceMappingURL=index.js.map