@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/README.md CHANGED
@@ -101,6 +101,8 @@ Options:
101
101
  -d, --max-depth <number> Maximum include depth (default: 10)
102
102
  -t, --timeout <ms> Rendering timeout in milliseconds (default: 5000)
103
103
  -v, --vars <json> Inline context variables as JSON
104
+ -r, --include-root <dir> Refuse {% include %} targets that resolve
105
+ (after symlinks) outside this directory
104
106
  -h, --help display help for command
105
107
  ```
106
108
 
@@ -129,6 +131,24 @@ minja README.md --context vars.json
129
131
  minja document.md --output docs/index.html
130
132
  ```
131
133
 
134
+ **Untrusted input — contain includes to a directory:**
135
+ ```bash
136
+ # Any {% include %} resolving (after symlink resolution) outside docs/
137
+ # is REFUSED and the render fails — without this, an include like
138
+ # "../secrets.txt" or "/etc/hostname" splices any readable file into
139
+ # the output. `..` and absolute paths that stay inside the root are fine.
140
+ minja docs/page.md --include-root docs/
141
+ ```
142
+
143
+ Programmatic equivalent:
144
+ ```ts
145
+ import { render, FileSystemLoader } from '@dogsbay/minja'
146
+ const out = await render(template, {
147
+ basePath: 'docs/',
148
+ loader: new FileSystemLoader('docs/', { root: 'docs/' }),
149
+ })
150
+ ```
151
+
132
152
  **Pipeline usage:**
133
153
  ```bash
134
154
  # Generate docs from multiple sources
package/bin/minja.js CHANGED
@@ -1,6 +1,6 @@
1
1
  #!/usr/bin/env node
2
2
  /**
3
- * minja v0.2.0-beta.92
3
+ * minja v0.2.0-beta.98
4
4
  * Minimal, secure Jinja2/Nunjucks subset for documentation preprocessing
5
5
  * @license MIT
6
6
  */
@@ -171,6 +171,24 @@ function isTruthy(value) {
171
171
  }
172
172
 
173
173
  // src/parser.ts
174
+ var KNOWN_TAG_KEYWORDS = /* @__PURE__ */ new Set([
175
+ "set",
176
+ "if",
177
+ "include",
178
+ "switch",
179
+ "leveloffset",
180
+ "raw",
181
+ "endif",
182
+ "elif",
183
+ "else",
184
+ "endswitch",
185
+ "case",
186
+ "endleveloffset",
187
+ "endraw"
188
+ ]);
189
+ function stripBodyForEndTag(body, endTag) {
190
+ return endTag.startsWith("{%-") ? body.replace(/[ \t]*\n[ \t\n]*$/, "") : body;
191
+ }
174
192
  function parse(template) {
175
193
  const errors = [];
176
194
  const ast = [];
@@ -253,12 +271,57 @@ function parse(template) {
253
271
  }
254
272
  }
255
273
  } else if (nearest.type === "tag") {
274
+ const tagStart2 = position;
256
275
  advance(2);
257
276
  const endPos = findNext("%}", position);
258
277
  if (endPos === -1) {
259
278
  errors.push(createError("Unclosed statement tag"));
260
279
  break;
261
280
  }
281
+ const peeked = template.substring(position, endPos).trim();
282
+ const peekKeyword = peeked.replace(/^-\s*/, "").replace(/\s*-$/, "").split(/\s+/)[0] ?? "";
283
+ if (peekKeyword === "raw") {
284
+ const openerLeftStrip = peeked.startsWith("-");
285
+ const openerRightStrip = peeked.endsWith("-");
286
+ if (openerLeftStrip && ast.length > 0 && ast[ast.length - 1].type === "text") {
287
+ const lastNode = ast[ast.length - 1];
288
+ lastNode.value = lastNode.value.replace(/[ \t]*\n[ \t\n]*$/, "");
289
+ }
290
+ extractTo(endPos);
291
+ advance(2);
292
+ if (openerRightStrip) {
293
+ while (position < template.length && /[ \t\n]/.test(template[position])) {
294
+ advance(1);
295
+ }
296
+ }
297
+ const endrawMatch = /\{%-?\s*endraw\s*-?%\}/.exec(template.substring(position));
298
+ if (!endrawMatch) {
299
+ errors.push(createError("Unclosed raw block"));
300
+ break;
301
+ }
302
+ const endrawAbs = position + endrawMatch.index;
303
+ let content = extractTo(endrawAbs);
304
+ const endrawTag = endrawMatch[0];
305
+ if (endrawTag.startsWith("{%-")) {
306
+ content = content.replace(/[ \t]*\n[ \t\n]*$/, "");
307
+ }
308
+ advance(endrawTag.length);
309
+ if (endrawTag.endsWith("-%}")) {
310
+ while (position < template.length && /[ \t\n]/.test(template[position])) {
311
+ advance(1);
312
+ }
313
+ }
314
+ if (content) {
315
+ ast.push({ type: "text", value: content });
316
+ }
317
+ continue;
318
+ }
319
+ if (!KNOWN_TAG_KEYWORDS.has(peekKeyword)) {
320
+ extractTo(endPos);
321
+ advance(2);
322
+ ast.push({ type: "text", value: template.substring(tagStart2, endPos + 2) });
323
+ continue;
324
+ }
262
325
  let statement = extractTo(endPos).trim();
263
326
  const hasLeftStrip = statement.startsWith("-");
264
327
  const hasRightStrip = statement.endsWith("-");
@@ -392,12 +455,13 @@ function parse(template) {
392
455
  }
393
456
  return { ast, errors };
394
457
  function findEndTag(tagName) {
395
- const pattern = new RegExp(`{%\\s*${tagName}\\s*%}`);
458
+ const pattern = new RegExp(`{%-?\\s*${tagName}\\s*-?%}`);
396
459
  const match = pattern.exec(template.substring(position));
397
460
  if (match) {
398
461
  return {
399
462
  start: position + match.index,
400
- length: match[0].length
463
+ length: match[0].length,
464
+ tag: match[0]
401
465
  };
402
466
  }
403
467
  return null;
@@ -429,8 +493,12 @@ function parse(template) {
429
493
  searchPos = nearest.pos + nearest.match[0].length;
430
494
  } else if (nearest.type === "endif") {
431
495
  if (depth === 0) {
432
- const bodyTemplate = extractTo(nearest.pos);
496
+ const bodyTemplate = stripBodyForEndTag(extractTo(nearest.pos), nearest.match[0]);
433
497
  advance(nearest.match[0].length);
498
+ if (nearest.match[0].endsWith("-%}")) {
499
+ while (position < template.length && /[ \t\n]/.test(template[position]))
500
+ advance(1);
501
+ }
434
502
  const bodyResult = parse(bodyTemplate);
435
503
  parseErrors.push(...bodyResult.errors);
436
504
  return {
@@ -444,7 +512,7 @@ function parse(template) {
444
512
  searchPos = nearest.pos + nearest.match[0].length;
445
513
  }
446
514
  } else if (depth === 0 && (nearest.type === "elif" || nearest.type === "else")) {
447
- const bodyTemplate = extractTo(nearest.pos);
515
+ const bodyTemplate = stripBodyForEndTag(extractTo(nearest.pos), nearest.match[0]);
448
516
  const bodyResult = parse(bodyTemplate);
449
517
  if (elifBranches.length === 0 && elseBranch.length === 0) {
450
518
  parseErrors.push(...bodyResult.errors);
@@ -481,7 +549,10 @@ function parse(template) {
481
549
  if (!endifMatch2) {
482
550
  parseErrors.push(createError("Missing endif after else"));
483
551
  } else {
484
- const elseBodyTemplate = extractTo(endifMatch2.start);
552
+ const elseBodyTemplate = stripBodyForEndTag(
553
+ extractTo(endifMatch2.start),
554
+ endifMatch2.tag
555
+ );
485
556
  advance(endifMatch2.length);
486
557
  const elseBodyResult = parse(elseBodyTemplate);
487
558
  parseErrors.push(...elseBodyResult.errors);
@@ -667,15 +738,38 @@ var FetchLoader = class {
667
738
  };
668
739
 
669
740
  // src/renderer.ts
670
- function transformHeadings(text, offset) {
741
+ function transformHeadings(text, offset, initialFence = null, fenceOut) {
671
742
  if (offset === 0) {
743
+ if (fenceOut)
744
+ fenceOut.fence = initialFence;
672
745
  return text;
673
746
  }
674
- return text.replace(/^(#{1,6})(\s+)/gm, (_match, hashes, space) => {
675
- const currentLevel = hashes.length;
676
- const newLevel = Math.max(1, Math.min(6, currentLevel + offset));
677
- return "#".repeat(newLevel) + space;
678
- });
747
+ const lines = text.split("\n");
748
+ let fence = initialFence;
749
+ for (let i = 0; i < lines.length; i++) {
750
+ const line = lines[i];
751
+ const fenceMatch = line.match(/^\s*(`{3,}|~{3,})/);
752
+ if (fenceMatch) {
753
+ const run = fenceMatch[1];
754
+ if (fence === null) {
755
+ fence = run;
756
+ } else if (run[0] === fence[0] && run.length >= fence.length && line.trim() === run) {
757
+ fence = null;
758
+ }
759
+ continue;
760
+ }
761
+ if (fence !== null) {
762
+ continue;
763
+ }
764
+ const m = line.match(/^(#{1,6})(\s+)/);
765
+ if (m) {
766
+ const newLevel = Math.max(1, Math.min(6, m[1].length + offset));
767
+ lines[i] = "#".repeat(newLevel) + line.slice(m[1].length);
768
+ }
769
+ }
770
+ if (fenceOut)
771
+ fenceOut.fence = fence;
772
+ return lines.join("\n");
679
773
  }
680
774
  function resolvePath(path, basePath) {
681
775
  if (path.startsWith("/")) {
@@ -746,7 +840,14 @@ async function renderInternal(template, options) {
746
840
  async function renderNodes(nodes, options) {
747
841
  const parts = [];
748
842
  for (const node of nodes) {
749
- parts.push(await renderNode(node, options));
843
+ let rendered = await renderNode(node, options);
844
+ if (node.type === "include" && rendered && parts.length > 0) {
845
+ const tail = /(?:^|\n)([ \t]+)$/.exec(parts[parts.length - 1]);
846
+ if (tail) {
847
+ rendered = rendered.replace(/\n(?=[^\n])/g, "\n" + tail[1]);
848
+ }
849
+ }
850
+ parts.push(rendered);
750
851
  }
751
852
  return parts.join("");
752
853
  }
@@ -758,7 +859,15 @@ async function renderNode(node, options) {
758
859
  if (currentOffset === 0) {
759
860
  return node.value;
760
861
  }
761
- return transformHeadings(node.value, currentOffset);
862
+ const fenceOut = { fence: null };
863
+ const out = transformHeadings(
864
+ node.value,
865
+ currentOffset,
866
+ context.get("_levelOffsetFence") ?? null,
867
+ fenceOut
868
+ );
869
+ context.set("_levelOffsetFence", fenceOut.fence);
870
+ return out;
762
871
  }
763
872
  case "variable": {
764
873
  const value = context.get(node.name);
@@ -768,6 +877,8 @@ async function renderNode(node, options) {
768
877
  throw new Error(`Undefined variable: ${node.name}`);
769
878
  case "preserve":
770
879
  return `{{ ${node.name} }}`;
880
+ case "asciidoc-literal":
881
+ return `{${node.name}}`;
771
882
  case "empty":
772
883
  default:
773
884
  return "";
@@ -787,7 +898,7 @@ async function renderNode(node, options) {
787
898
  case "comment":
788
899
  return "";
789
900
  case "if": {
790
- if (options.undefinedBehavior === "preserve" && options.undefinedConditions !== "falsy") {
901
+ if ((options.undefinedBehavior === "preserve" || options.undefinedBehavior === "asciidoc-literal") && options.undefinedConditions !== "falsy") {
791
902
  const undefinedRefs = findUndefinedRefs(node.condition, context);
792
903
  if (undefinedRefs.length > 0) {
793
904
  return ifNodeToSource(node);
@@ -847,7 +958,10 @@ async function renderNode(node, options) {
847
958
  undefinedConditions: options.undefinedConditions
848
959
  });
849
960
  } catch (error) {
850
- if (options.undefinedBehavior === "preserve") {
961
+ if (error instanceof Error && error.name === "IncludeContainmentError") {
962
+ throw error;
963
+ }
964
+ if (options.undefinedBehavior === "preserve" || options.undefinedBehavior === "asciidoc-literal") {
851
965
  return `{% include "${node.path}" %}`;
852
966
  }
853
967
  const message = error instanceof Error ? error.message : String(error);
@@ -870,11 +984,14 @@ async function renderNode(node, options) {
870
984
  const newOffset = node.isRelative ? parentOffset + node.offset : node.offset;
871
985
  const previousOffset = parentOffset;
872
986
  context.set("_levelOffset", newOffset);
987
+ const previousFence = context.get("_levelOffsetFence") ?? null;
988
+ context.set("_levelOffsetFence", null);
873
989
  try {
874
990
  const result = await renderNodes(node.body, options);
875
991
  return result;
876
992
  } finally {
877
993
  context.set("_levelOffset", previousOffset);
994
+ context.set("_levelOffsetFence", previousFence);
878
995
  }
879
996
  }
880
997
  default:
@@ -975,9 +1092,11 @@ var FileSystemLoader = class {
975
1092
  /**
976
1093
  * Create a filesystem loader
977
1094
  * @param basePath - Base directory for resolving relative paths
1095
+ * @param options - See {@link FileSystemLoaderOptions}
978
1096
  */
979
- constructor(basePath = process.cwd()) {
1097
+ constructor(basePath = process.cwd(), options = {}) {
980
1098
  this.basePath = basePath;
1099
+ this.root = options.root;
981
1100
  }
982
1101
  /**
983
1102
  * Load a file from the filesystem
@@ -990,6 +1109,41 @@ var FileSystemLoader = class {
990
1109
  const pathModule = await import("path");
991
1110
  const resolvedBasePath = basePath || this.basePath;
992
1111
  const resolvedPath = pathModule.isAbsolute(path) ? path : pathModule.resolve(resolvedBasePath, path);
1112
+ if (this.root !== void 0) {
1113
+ const refuse = (detail) => {
1114
+ const err = new Error(`Refusing include outside the include root: ${detail}`);
1115
+ err.name = "IncludeContainmentError";
1116
+ throw err;
1117
+ };
1118
+ if (this.realRoot === void 0) {
1119
+ try {
1120
+ this.realRoot = await fs.realpath(this.root);
1121
+ } catch (error) {
1122
+ const message = error instanceof Error ? error.message : String(error);
1123
+ refuse(`include root ${this.root} does not resolve (${message})`);
1124
+ }
1125
+ }
1126
+ const lexical = pathModule.resolve(resolvedPath);
1127
+ if (lexical !== this.realRoot && !lexical.startsWith(this.realRoot + pathModule.sep) && lexical !== this.root && !lexical.startsWith(pathModule.resolve(this.root) + pathModule.sep)) {
1128
+ refuse(`"${path}" resolves to ${lexical}, which is not inside ${this.realRoot}`);
1129
+ }
1130
+ let realTarget;
1131
+ try {
1132
+ realTarget = await fs.realpath(resolvedPath);
1133
+ } catch (error) {
1134
+ const message = error instanceof Error ? error.message : String(error);
1135
+ throw new Error(`Failed to load ${resolvedPath}: ${message}`);
1136
+ }
1137
+ if (realTarget !== this.realRoot && !realTarget.startsWith(this.realRoot + pathModule.sep)) {
1138
+ refuse(`"${path}" resolves to ${realTarget}, which is not inside ${this.realRoot}`);
1139
+ }
1140
+ try {
1141
+ return await fs.readFile(realTarget, "utf-8");
1142
+ } catch (error) {
1143
+ const message = error instanceof Error ? error.message : String(error);
1144
+ throw new Error(`Failed to load ${resolvedPath}: ${message}`);
1145
+ }
1146
+ }
993
1147
  try {
994
1148
  return await fs.readFile(resolvedPath, "utf-8");
995
1149
  } catch (error) {
@@ -1001,7 +1155,10 @@ var FileSystemLoader = class {
1001
1155
 
1002
1156
  // src/cli.ts
1003
1157
  var program = new Command();
1004
- program.name("minja").description("Minimal, secure Jinja2/Nunjucks subset for documentation preprocessing").version("0.1.0").argument("[template]", "Template file to render (or read from stdin)").option("-c, --context <file>", "Context file (JSON or YAML)").option("-o, --output <file>", "Output file (default: stdout)").option("-d, --max-depth <number>", "Maximum include depth", "10").option("-t, --timeout <ms>", "Rendering timeout in milliseconds", "5000").option("-v, --vars <json>", "Inline context variables as JSON").action(async (templatePath, options) => {
1158
+ program.name("minja").description("Minimal, secure Jinja2/Nunjucks subset for documentation preprocessing").version("0.1.0").argument("[template]", "Template file to render (or read from stdin)").option("-c, --context <file>", "Context file (JSON or YAML)").option("-o, --output <file>", "Output file (default: stdout)").option("-d, --max-depth <number>", "Maximum include depth", "10").option("-t, --timeout <ms>", "Rendering timeout in milliseconds", "5000").option("-v, --vars <json>", "Inline context variables as JSON").option(
1159
+ "-r, --include-root <dir>",
1160
+ "Refuse {% include %} targets that resolve (after symlinks) outside this directory"
1161
+ ).action(async (templatePath, options) => {
1005
1162
  try {
1006
1163
  let template;
1007
1164
  let baseDir = process.cwd();
@@ -1028,7 +1185,10 @@ program.name("minja").description("Minimal, secure Jinja2/Nunjucks subset for do
1028
1185
  const inlineVars = JSON.parse(options.vars);
1029
1186
  context = { ...context, ...inlineVars };
1030
1187
  }
1031
- const loader = new FileSystemLoader(baseDir);
1188
+ const loader = new FileSystemLoader(
1189
+ baseDir,
1190
+ options.includeRoot ? { root: resolve(options.includeRoot) } : {}
1191
+ );
1032
1192
  const result = await render(template, {
1033
1193
  loader,
1034
1194
  basePath: baseDir,
package/dist/browser.d.ts CHANGED
@@ -8,4 +8,5 @@ export { Context } from './context.js';
8
8
  export { FetchLoader } from './loader-fetch.js';
9
9
  export { MemoryLoader } from './loader-memory.js';
10
10
  export type { ASTNode, TextNode, VariableNode, SetNode, IfNode, IncludeNode, CommentNode, Expression, LiteralExpression, VariableExpression, BinaryExpression, UnaryExpression, RenderOptions, Loader, IContext, ParseResult, ParseError, } from './types.js';
11
+ export { includeDirectiveRe, scanIncludes, variableRe, directiveTagRe, commentRe, rawBlockRe } from './scan.js';
11
12
  //# sourceMappingURL=browser.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"browser.d.ts","sourceRoot":"","sources":["../src/browser.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,EAAE,MAAM,EAAE,MAAM,eAAe,CAAA;AACtC,OAAO,EAAE,KAAK,EAAE,MAAM,aAAa,CAAA;AACnC,OAAO,EAAE,eAAe,EAAE,kBAAkB,EAAE,MAAM,gBAAgB,CAAA;AACpE,OAAO,EAAE,OAAO,EAAE,MAAM,cAAc,CAAA;AACtC,OAAO,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAA;AAC/C,OAAO,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAA;AAEjD,YAAY,EAEV,OAAO,EACP,QAAQ,EACR,YAAY,EACZ,OAAO,EACP,MAAM,EACN,WAAW,EACX,WAAW,EAGX,UAAU,EACV,iBAAiB,EACjB,kBAAkB,EAClB,gBAAgB,EAChB,eAAe,EAGf,aAAa,EACb,MAAM,EACN,QAAQ,EACR,WAAW,EACX,UAAU,GACX,MAAM,YAAY,CAAA"}
1
+ {"version":3,"file":"browser.d.ts","sourceRoot":"","sources":["../src/browser.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,EAAE,MAAM,EAAE,MAAM,eAAe,CAAA;AACtC,OAAO,EAAE,KAAK,EAAE,MAAM,aAAa,CAAA;AACnC,OAAO,EAAE,eAAe,EAAE,kBAAkB,EAAE,MAAM,gBAAgB,CAAA;AACpE,OAAO,EAAE,OAAO,EAAE,MAAM,cAAc,CAAA;AACtC,OAAO,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAA;AAC/C,OAAO,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAA;AAEjD,YAAY,EAEV,OAAO,EACP,QAAQ,EACR,YAAY,EACZ,OAAO,EACP,MAAM,EACN,WAAW,EACX,WAAW,EAGX,UAAU,EACV,iBAAiB,EACjB,kBAAkB,EAClB,gBAAgB,EAChB,eAAe,EAGf,aAAa,EACb,MAAM,EACN,QAAQ,EACR,WAAW,EACX,UAAU,GACX,MAAM,YAAY,CAAA;AACnB,OAAO,EAAE,kBAAkB,EAAE,YAAY,EAAE,UAAU,EAAE,cAAc,EAAE,SAAS,EAAE,UAAU,EAAE,MAAM,WAAW,CAAA"}
package/dist/browser.js CHANGED
@@ -7,4 +7,5 @@ export { parseExpression, evaluateExpression } from './evaluator.js';
7
7
  export { Context } from './context.js';
8
8
  export { FetchLoader } from './loader-fetch.js';
9
9
  export { MemoryLoader } from './loader-memory.js';
10
+ export { includeDirectiveRe, scanIncludes, variableRe, directiveTagRe, commentRe, rawBlockRe } from './scan.js';
10
11
  //# sourceMappingURL=browser.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"browser.js","sourceRoot":"","sources":["../src/browser.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,EAAE,MAAM,EAAE,MAAM,eAAe,CAAA;AACtC,OAAO,EAAE,KAAK,EAAE,MAAM,aAAa,CAAA;AACnC,OAAO,EAAE,eAAe,EAAE,kBAAkB,EAAE,MAAM,gBAAgB,CAAA;AACpE,OAAO,EAAE,OAAO,EAAE,MAAM,cAAc,CAAA;AACtC,OAAO,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAA;AAC/C,OAAO,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAA"}
1
+ {"version":3,"file":"browser.js","sourceRoot":"","sources":["../src/browser.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,EAAE,MAAM,EAAE,MAAM,eAAe,CAAA;AACtC,OAAO,EAAE,KAAK,EAAE,MAAM,aAAa,CAAA;AACnC,OAAO,EAAE,eAAe,EAAE,kBAAkB,EAAE,MAAM,gBAAgB,CAAA;AACpE,OAAO,EAAE,OAAO,EAAE,MAAM,cAAc,CAAA;AACtC,OAAO,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAA;AAC/C,OAAO,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAA;AA0BjD,OAAO,EAAE,kBAAkB,EAAE,YAAY,EAAE,UAAU,EAAE,cAAc,EAAE,SAAS,EAAE,UAAU,EAAE,MAAM,WAAW,CAAA"}
package/dist/cli.js CHANGED
@@ -18,6 +18,7 @@ program
18
18
  .option('-d, --max-depth <number>', 'Maximum include depth', '10')
19
19
  .option('-t, --timeout <ms>', 'Rendering timeout in milliseconds', '5000')
20
20
  .option('-v, --vars <json>', 'Inline context variables as JSON')
21
+ .option('-r, --include-root <dir>', 'Refuse {% include %} targets that resolve (after symlinks) outside this directory')
21
22
  .action(async (templatePath, options) => {
22
23
  try {
23
24
  // Read template from file or stdin
@@ -53,7 +54,7 @@ program
53
54
  context = { ...context, ...inlineVars };
54
55
  }
55
56
  // Render template
56
- const loader = new FileSystemLoader(baseDir);
57
+ const loader = new FileSystemLoader(baseDir, options.includeRoot ? { root: resolve(options.includeRoot) } : {});
57
58
  const result = await render(template, {
58
59
  loader,
59
60
  basePath: baseDir,
package/dist/cli.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"cli.js","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"cli.js","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":"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"}
package/dist/index.d.ts CHANGED
@@ -14,5 +14,7 @@ export { parse } from './parser.js';
14
14
  export { parseExpression, evaluateExpression } from './evaluator.js';
15
15
  export { Context, type ContextOptions } from './context.js';
16
16
  export { FetchLoader, FileSystemLoader, MemoryLoader } from './loader.js';
17
+ export type { FileSystemLoaderOptions } from './loader.js';
18
+ export { includeDirectiveRe, scanIncludes, variableRe, directiveTagRe, commentRe, rawBlockRe } from './scan.js';
17
19
  export type { ASTNode, TextNode, VariableNode, SetNode, IfNode, IncludeNode, CommentNode, Expression, LiteralExpression, VariableExpression, BinaryExpression, UnaryExpression, RenderOptions, Loader, IContext, ParseResult, ParseError, } from './types.js';
18
20
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAEH,OAAO,EAAE,MAAM,EAAE,MAAM,eAAe,CAAA;AACtC,OAAO,EAAE,KAAK,EAAE,MAAM,aAAa,CAAA;AACnC,OAAO,EAAE,eAAe,EAAE,kBAAkB,EAAE,MAAM,gBAAgB,CAAA;AACpE,OAAO,EAAE,OAAO,EAAE,KAAK,cAAc,EAAE,MAAM,cAAc,CAAA;AAC3D,OAAO,EAAE,WAAW,EAAE,gBAAgB,EAAE,YAAY,EAAE,MAAM,aAAa,CAAA;AAEzE,YAAY,EAEV,OAAO,EACP,QAAQ,EACR,YAAY,EACZ,OAAO,EACP,MAAM,EACN,WAAW,EACX,WAAW,EAGX,UAAU,EACV,iBAAiB,EACjB,kBAAkB,EAClB,gBAAgB,EAChB,eAAe,EAGf,aAAa,EACb,MAAM,EACN,QAAQ,EACR,WAAW,EACX,UAAU,GACX,MAAM,YAAY,CAAA"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAEH,OAAO,EAAE,MAAM,EAAE,MAAM,eAAe,CAAA;AACtC,OAAO,EAAE,KAAK,EAAE,MAAM,aAAa,CAAA;AACnC,OAAO,EAAE,eAAe,EAAE,kBAAkB,EAAE,MAAM,gBAAgB,CAAA;AACpE,OAAO,EAAE,OAAO,EAAE,KAAK,cAAc,EAAE,MAAM,cAAc,CAAA;AAC3D,OAAO,EAAE,WAAW,EAAE,gBAAgB,EAAE,YAAY,EAAE,MAAM,aAAa,CAAA;AACzE,YAAY,EAAE,uBAAuB,EAAE,MAAM,aAAa,CAAA;AAC1D,OAAO,EAAE,kBAAkB,EAAE,YAAY,EAAE,UAAU,EAAE,cAAc,EAAE,SAAS,EAAE,UAAU,EAAE,MAAM,WAAW,CAAA;AAE/G,YAAY,EAEV,OAAO,EACP,QAAQ,EACR,YAAY,EACZ,OAAO,EACP,MAAM,EACN,WAAW,EACX,WAAW,EAGX,UAAU,EACV,iBAAiB,EACjB,kBAAkB,EAClB,gBAAgB,EAChB,eAAe,EAGf,aAAa,EACb,MAAM,EACN,QAAQ,EACR,WAAW,EACX,UAAU,GACX,MAAM,YAAY,CAAA"}
package/dist/index.js CHANGED
@@ -14,4 +14,5 @@ export { parse } from './parser.js';
14
14
  export { parseExpression, evaluateExpression } from './evaluator.js';
15
15
  export { Context } from './context.js';
16
16
  export { FetchLoader, FileSystemLoader, MemoryLoader } from './loader.js';
17
+ export { includeDirectiveRe, scanIncludes, variableRe, directiveTagRe, commentRe, rawBlockRe } from './scan.js';
17
18
  //# sourceMappingURL=index.js.map
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAEH,OAAO,EAAE,MAAM,EAAE,MAAM,eAAe,CAAA;AACtC,OAAO,EAAE,KAAK,EAAE,MAAM,aAAa,CAAA;AACnC,OAAO,EAAE,eAAe,EAAE,kBAAkB,EAAE,MAAM,gBAAgB,CAAA;AACpE,OAAO,EAAE,OAAO,EAAuB,MAAM,cAAc,CAAA;AAC3D,OAAO,EAAE,WAAW,EAAE,gBAAgB,EAAE,YAAY,EAAE,MAAM,aAAa,CAAA"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAEH,OAAO,EAAE,MAAM,EAAE,MAAM,eAAe,CAAA;AACtC,OAAO,EAAE,KAAK,EAAE,MAAM,aAAa,CAAA;AACnC,OAAO,EAAE,eAAe,EAAE,kBAAkB,EAAE,MAAM,gBAAgB,CAAA;AACpE,OAAO,EAAE,OAAO,EAAuB,MAAM,cAAc,CAAA;AAC3D,OAAO,EAAE,WAAW,EAAE,gBAAgB,EAAE,YAAY,EAAE,MAAM,aAAa,CAAA;AAEzE,OAAO,EAAE,kBAAkB,EAAE,YAAY,EAAE,UAAU,EAAE,cAAc,EAAE,SAAS,EAAE,UAAU,EAAE,MAAM,WAAW,CAAA"}