@dogsbay/minja 0.2.0-beta.92 → 0.2.0-beta.98

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.umd.js CHANGED
@@ -1,5 +1,5 @@
1
1
  /**
2
- * minja v0.2.0-beta.92
2
+ * minja v0.2.0-beta.98
3
3
  * Minimal, secure Jinja2/Nunjucks subset for documentation preprocessing
4
4
  * @license MIT
5
5
  */
@@ -29,10 +29,16 @@ var minja = (() => {
29
29
  Context: () => Context,
30
30
  FetchLoader: () => FetchLoader,
31
31
  MemoryLoader: () => MemoryLoader,
32
+ commentRe: () => commentRe,
33
+ directiveTagRe: () => directiveTagRe,
32
34
  evaluateExpression: () => evaluateExpression,
35
+ includeDirectiveRe: () => includeDirectiveRe,
33
36
  parse: () => parse,
34
37
  parseExpression: () => parseExpression,
35
- render: () => render
38
+ rawBlockRe: () => rawBlockRe,
39
+ render: () => render,
40
+ scanIncludes: () => scanIncludes,
41
+ variableRe: () => variableRe
36
42
  });
37
43
 
38
44
  // src/evaluator.ts
@@ -195,6 +201,24 @@ var minja = (() => {
195
201
  }
196
202
 
197
203
  // src/parser.ts
204
+ var KNOWN_TAG_KEYWORDS = /* @__PURE__ */ new Set([
205
+ "set",
206
+ "if",
207
+ "include",
208
+ "switch",
209
+ "leveloffset",
210
+ "raw",
211
+ "endif",
212
+ "elif",
213
+ "else",
214
+ "endswitch",
215
+ "case",
216
+ "endleveloffset",
217
+ "endraw"
218
+ ]);
219
+ function stripBodyForEndTag(body, endTag) {
220
+ return endTag.startsWith("{%-") ? body.replace(/[ \t]*\n[ \t\n]*$/, "") : body;
221
+ }
198
222
  function parse(template) {
199
223
  const errors = [];
200
224
  const ast = [];
@@ -277,12 +301,57 @@ var minja = (() => {
277
301
  }
278
302
  }
279
303
  } else if (nearest.type === "tag") {
304
+ const tagStart2 = position;
280
305
  advance(2);
281
306
  const endPos = findNext("%}", position);
282
307
  if (endPos === -1) {
283
308
  errors.push(createError("Unclosed statement tag"));
284
309
  break;
285
310
  }
311
+ const peeked = template.substring(position, endPos).trim();
312
+ const peekKeyword = peeked.replace(/^-\s*/, "").replace(/\s*-$/, "").split(/\s+/)[0] ?? "";
313
+ if (peekKeyword === "raw") {
314
+ const openerLeftStrip = peeked.startsWith("-");
315
+ const openerRightStrip = peeked.endsWith("-");
316
+ if (openerLeftStrip && ast.length > 0 && ast[ast.length - 1].type === "text") {
317
+ const lastNode = ast[ast.length - 1];
318
+ lastNode.value = lastNode.value.replace(/[ \t]*\n[ \t\n]*$/, "");
319
+ }
320
+ extractTo(endPos);
321
+ advance(2);
322
+ if (openerRightStrip) {
323
+ while (position < template.length && /[ \t\n]/.test(template[position])) {
324
+ advance(1);
325
+ }
326
+ }
327
+ const endrawMatch = /\{%-?\s*endraw\s*-?%\}/.exec(template.substring(position));
328
+ if (!endrawMatch) {
329
+ errors.push(createError("Unclosed raw block"));
330
+ break;
331
+ }
332
+ const endrawAbs = position + endrawMatch.index;
333
+ let content = extractTo(endrawAbs);
334
+ const endrawTag = endrawMatch[0];
335
+ if (endrawTag.startsWith("{%-")) {
336
+ content = content.replace(/[ \t]*\n[ \t\n]*$/, "");
337
+ }
338
+ advance(endrawTag.length);
339
+ if (endrawTag.endsWith("-%}")) {
340
+ while (position < template.length && /[ \t\n]/.test(template[position])) {
341
+ advance(1);
342
+ }
343
+ }
344
+ if (content) {
345
+ ast.push({ type: "text", value: content });
346
+ }
347
+ continue;
348
+ }
349
+ if (!KNOWN_TAG_KEYWORDS.has(peekKeyword)) {
350
+ extractTo(endPos);
351
+ advance(2);
352
+ ast.push({ type: "text", value: template.substring(tagStart2, endPos + 2) });
353
+ continue;
354
+ }
286
355
  let statement = extractTo(endPos).trim();
287
356
  const hasLeftStrip = statement.startsWith("-");
288
357
  const hasRightStrip = statement.endsWith("-");
@@ -416,12 +485,13 @@ var minja = (() => {
416
485
  }
417
486
  return { ast, errors };
418
487
  function findEndTag(tagName) {
419
- const pattern = new RegExp(`{%\\s*${tagName}\\s*%}`);
488
+ const pattern = new RegExp(`{%-?\\s*${tagName}\\s*-?%}`);
420
489
  const match = pattern.exec(template.substring(position));
421
490
  if (match) {
422
491
  return {
423
492
  start: position + match.index,
424
- length: match[0].length
493
+ length: match[0].length,
494
+ tag: match[0]
425
495
  };
426
496
  }
427
497
  return null;
@@ -453,8 +523,12 @@ var minja = (() => {
453
523
  searchPos = nearest.pos + nearest.match[0].length;
454
524
  } else if (nearest.type === "endif") {
455
525
  if (depth === 0) {
456
- const bodyTemplate = extractTo(nearest.pos);
526
+ const bodyTemplate = stripBodyForEndTag(extractTo(nearest.pos), nearest.match[0]);
457
527
  advance(nearest.match[0].length);
528
+ if (nearest.match[0].endsWith("-%}")) {
529
+ while (position < template.length && /[ \t\n]/.test(template[position]))
530
+ advance(1);
531
+ }
458
532
  const bodyResult = parse(bodyTemplate);
459
533
  parseErrors.push(...bodyResult.errors);
460
534
  return {
@@ -468,7 +542,7 @@ var minja = (() => {
468
542
  searchPos = nearest.pos + nearest.match[0].length;
469
543
  }
470
544
  } else if (depth === 0 && (nearest.type === "elif" || nearest.type === "else")) {
471
- const bodyTemplate = extractTo(nearest.pos);
545
+ const bodyTemplate = stripBodyForEndTag(extractTo(nearest.pos), nearest.match[0]);
472
546
  const bodyResult = parse(bodyTemplate);
473
547
  if (elifBranches.length === 0 && elseBranch.length === 0) {
474
548
  parseErrors.push(...bodyResult.errors);
@@ -505,7 +579,10 @@ var minja = (() => {
505
579
  if (!endifMatch2) {
506
580
  parseErrors.push(createError("Missing endif after else"));
507
581
  } else {
508
- const elseBodyTemplate = extractTo(endifMatch2.start);
582
+ const elseBodyTemplate = stripBodyForEndTag(
583
+ extractTo(endifMatch2.start),
584
+ endifMatch2.tag
585
+ );
509
586
  advance(endifMatch2.length);
510
587
  const elseBodyResult = parse(elseBodyTemplate);
511
588
  parseErrors.push(...elseBodyResult.errors);
@@ -691,15 +768,38 @@ var minja = (() => {
691
768
  };
692
769
 
693
770
  // src/renderer.ts
694
- function transformHeadings(text, offset) {
771
+ function transformHeadings(text, offset, initialFence = null, fenceOut) {
695
772
  if (offset === 0) {
773
+ if (fenceOut)
774
+ fenceOut.fence = initialFence;
696
775
  return text;
697
776
  }
698
- return text.replace(/^(#{1,6})(\s+)/gm, (_match, hashes, space) => {
699
- const currentLevel = hashes.length;
700
- const newLevel = Math.max(1, Math.min(6, currentLevel + offset));
701
- return "#".repeat(newLevel) + space;
702
- });
777
+ const lines = text.split("\n");
778
+ let fence = initialFence;
779
+ for (let i = 0; i < lines.length; i++) {
780
+ const line = lines[i];
781
+ const fenceMatch = line.match(/^\s*(`{3,}|~{3,})/);
782
+ if (fenceMatch) {
783
+ const run = fenceMatch[1];
784
+ if (fence === null) {
785
+ fence = run;
786
+ } else if (run[0] === fence[0] && run.length >= fence.length && line.trim() === run) {
787
+ fence = null;
788
+ }
789
+ continue;
790
+ }
791
+ if (fence !== null) {
792
+ continue;
793
+ }
794
+ const m = line.match(/^(#{1,6})(\s+)/);
795
+ if (m) {
796
+ const newLevel = Math.max(1, Math.min(6, m[1].length + offset));
797
+ lines[i] = "#".repeat(newLevel) + line.slice(m[1].length);
798
+ }
799
+ }
800
+ if (fenceOut)
801
+ fenceOut.fence = fence;
802
+ return lines.join("\n");
703
803
  }
704
804
  function resolvePath(path, basePath) {
705
805
  if (path.startsWith("/")) {
@@ -770,7 +870,14 @@ var minja = (() => {
770
870
  async function renderNodes(nodes, options) {
771
871
  const parts = [];
772
872
  for (const node of nodes) {
773
- parts.push(await renderNode(node, options));
873
+ let rendered = await renderNode(node, options);
874
+ if (node.type === "include" && rendered && parts.length > 0) {
875
+ const tail = /(?:^|\n)([ \t]+)$/.exec(parts[parts.length - 1]);
876
+ if (tail) {
877
+ rendered = rendered.replace(/\n(?=[^\n])/g, "\n" + tail[1]);
878
+ }
879
+ }
880
+ parts.push(rendered);
774
881
  }
775
882
  return parts.join("");
776
883
  }
@@ -782,7 +889,15 @@ var minja = (() => {
782
889
  if (currentOffset === 0) {
783
890
  return node.value;
784
891
  }
785
- return transformHeadings(node.value, currentOffset);
892
+ const fenceOut = { fence: null };
893
+ const out = transformHeadings(
894
+ node.value,
895
+ currentOffset,
896
+ context.get("_levelOffsetFence") ?? null,
897
+ fenceOut
898
+ );
899
+ context.set("_levelOffsetFence", fenceOut.fence);
900
+ return out;
786
901
  }
787
902
  case "variable": {
788
903
  const value = context.get(node.name);
@@ -792,6 +907,8 @@ var minja = (() => {
792
907
  throw new Error(`Undefined variable: ${node.name}`);
793
908
  case "preserve":
794
909
  return `{{ ${node.name} }}`;
910
+ case "asciidoc-literal":
911
+ return `{${node.name}}`;
795
912
  case "empty":
796
913
  default:
797
914
  return "";
@@ -811,7 +928,7 @@ var minja = (() => {
811
928
  case "comment":
812
929
  return "";
813
930
  case "if": {
814
- if (options.undefinedBehavior === "preserve" && options.undefinedConditions !== "falsy") {
931
+ if ((options.undefinedBehavior === "preserve" || options.undefinedBehavior === "asciidoc-literal") && options.undefinedConditions !== "falsy") {
815
932
  const undefinedRefs = findUndefinedRefs(node.condition, context);
816
933
  if (undefinedRefs.length > 0) {
817
934
  return ifNodeToSource(node);
@@ -871,7 +988,10 @@ var minja = (() => {
871
988
  undefinedConditions: options.undefinedConditions
872
989
  });
873
990
  } catch (error) {
874
- if (options.undefinedBehavior === "preserve") {
991
+ if (error instanceof Error && error.name === "IncludeContainmentError") {
992
+ throw error;
993
+ }
994
+ if (options.undefinedBehavior === "preserve" || options.undefinedBehavior === "asciidoc-literal") {
875
995
  return `{% include "${node.path}" %}`;
876
996
  }
877
997
  const message = error instanceof Error ? error.message : String(error);
@@ -894,11 +1014,14 @@ var minja = (() => {
894
1014
  const newOffset = node.isRelative ? parentOffset + node.offset : node.offset;
895
1015
  const previousOffset = parentOffset;
896
1016
  context.set("_levelOffset", newOffset);
1017
+ const previousFence = context.get("_levelOffsetFence") ?? null;
1018
+ context.set("_levelOffsetFence", null);
897
1019
  try {
898
1020
  const result = await renderNodes(node.body, options);
899
1021
  return result;
900
1022
  } finally {
901
1023
  context.set("_levelOffset", previousOffset);
1024
+ context.set("_levelOffsetFence", previousFence);
902
1025
  }
903
1026
  }
904
1027
  default:
@@ -1024,6 +1147,14 @@ var minja = (() => {
1024
1147
  return content;
1025
1148
  }
1026
1149
  };
1150
+
1151
+ // src/scan.ts
1152
+ var includeDirectiveRe = () => /\{%-?\s*include\s+["']([^"']+)["']\s*-?%\}/g;
1153
+ var scanIncludes = (text) => [...text.matchAll(includeDirectiveRe())].map((match) => match[1]);
1154
+ var variableRe = () => /\{\{-?([\s\S]*?)-?\}\}/g;
1155
+ var directiveTagRe = () => /\{%-?\s*([A-Za-z_][A-Za-z0-9_]*)([\s\S]*?)-?%\}/g;
1156
+ var commentRe = () => /\{#[\s\S]*?#\}/g;
1157
+ var rawBlockRe = () => /\{%-?\s*raw\s*-?%\}([\s\S]*?)\{%-?\s*endraw\s*-?%\}/g;
1027
1158
  return __toCommonJS(browser_exports);
1028
1159
  })();
1029
1160
  //# sourceMappingURL=index.umd.js.map