@dogsbay/minja 0.2.0-beta.93 → 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 +20 -0
- package/bin/minja.js +156 -15
- package/dist/browser.d.ts +1 -0
- package/dist/browser.d.ts.map +1 -1
- package/dist/browser.js +1 -0
- package/dist/browser.js.map +1 -1
- package/dist/cli.js +2 -1
- package/dist/cli.js.map +1 -1
- package/dist/index.d.ts +2 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -0
- package/dist/index.js.map +1 -1
- package/dist/index.umd.js +125 -13
- package/dist/index.umd.js.map +4 -4
- package/dist/index.umd.min.js +7 -6
- package/dist/index.umd.min.js.map +4 -4
- package/dist/loader.d.ts +20 -1
- package/dist/loader.d.ts.map +1 -1
- package/dist/loader.js +69 -1
- package/dist/loader.js.map +1 -1
- package/dist/parser.d.ts +0 -5
- package/dist/parser.d.ts.map +1 -1
- package/dist/parser.js +112 -4
- package/dist/parser.js.map +1 -1
- package/dist/renderer.d.ts.map +1 -1
- package/dist/renderer.js +53 -7
- package/dist/renderer.js.map +1 -1
- package/dist/scan.d.ts +46 -0
- package/dist/scan.d.ts.map +1 -0
- package/dist/scan.js +46 -0
- package/dist/scan.js.map +1 -0
- package/dist/types.d.ts +12 -2
- package/dist/types.d.ts.map +1 -1
- package/package.json +17 -7
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.
|
|
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(`{
|
|
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 =
|
|
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,12 +738,14 @@ 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
747
|
const lines = text.split("\n");
|
|
675
|
-
let fence =
|
|
748
|
+
let fence = initialFence;
|
|
676
749
|
for (let i = 0; i < lines.length; i++) {
|
|
677
750
|
const line = lines[i];
|
|
678
751
|
const fenceMatch = line.match(/^\s*(`{3,}|~{3,})/);
|
|
@@ -694,6 +767,8 @@ function transformHeadings(text, offset) {
|
|
|
694
767
|
lines[i] = "#".repeat(newLevel) + line.slice(m[1].length);
|
|
695
768
|
}
|
|
696
769
|
}
|
|
770
|
+
if (fenceOut)
|
|
771
|
+
fenceOut.fence = fence;
|
|
697
772
|
return lines.join("\n");
|
|
698
773
|
}
|
|
699
774
|
function resolvePath(path, basePath) {
|
|
@@ -765,7 +840,14 @@ async function renderInternal(template, options) {
|
|
|
765
840
|
async function renderNodes(nodes, options) {
|
|
766
841
|
const parts = [];
|
|
767
842
|
for (const node of nodes) {
|
|
768
|
-
|
|
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);
|
|
769
851
|
}
|
|
770
852
|
return parts.join("");
|
|
771
853
|
}
|
|
@@ -777,7 +859,15 @@ async function renderNode(node, options) {
|
|
|
777
859
|
if (currentOffset === 0) {
|
|
778
860
|
return node.value;
|
|
779
861
|
}
|
|
780
|
-
|
|
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;
|
|
781
871
|
}
|
|
782
872
|
case "variable": {
|
|
783
873
|
const value = context.get(node.name);
|
|
@@ -787,6 +877,8 @@ async function renderNode(node, options) {
|
|
|
787
877
|
throw new Error(`Undefined variable: ${node.name}`);
|
|
788
878
|
case "preserve":
|
|
789
879
|
return `{{ ${node.name} }}`;
|
|
880
|
+
case "asciidoc-literal":
|
|
881
|
+
return `{${node.name}}`;
|
|
790
882
|
case "empty":
|
|
791
883
|
default:
|
|
792
884
|
return "";
|
|
@@ -806,7 +898,7 @@ async function renderNode(node, options) {
|
|
|
806
898
|
case "comment":
|
|
807
899
|
return "";
|
|
808
900
|
case "if": {
|
|
809
|
-
if (options.undefinedBehavior === "preserve" && options.undefinedConditions !== "falsy") {
|
|
901
|
+
if ((options.undefinedBehavior === "preserve" || options.undefinedBehavior === "asciidoc-literal") && options.undefinedConditions !== "falsy") {
|
|
810
902
|
const undefinedRefs = findUndefinedRefs(node.condition, context);
|
|
811
903
|
if (undefinedRefs.length > 0) {
|
|
812
904
|
return ifNodeToSource(node);
|
|
@@ -866,7 +958,10 @@ async function renderNode(node, options) {
|
|
|
866
958
|
undefinedConditions: options.undefinedConditions
|
|
867
959
|
});
|
|
868
960
|
} catch (error) {
|
|
869
|
-
if (
|
|
961
|
+
if (error instanceof Error && error.name === "IncludeContainmentError") {
|
|
962
|
+
throw error;
|
|
963
|
+
}
|
|
964
|
+
if (options.undefinedBehavior === "preserve" || options.undefinedBehavior === "asciidoc-literal") {
|
|
870
965
|
return `{% include "${node.path}" %}`;
|
|
871
966
|
}
|
|
872
967
|
const message = error instanceof Error ? error.message : String(error);
|
|
@@ -889,11 +984,14 @@ async function renderNode(node, options) {
|
|
|
889
984
|
const newOffset = node.isRelative ? parentOffset + node.offset : node.offset;
|
|
890
985
|
const previousOffset = parentOffset;
|
|
891
986
|
context.set("_levelOffset", newOffset);
|
|
987
|
+
const previousFence = context.get("_levelOffsetFence") ?? null;
|
|
988
|
+
context.set("_levelOffsetFence", null);
|
|
892
989
|
try {
|
|
893
990
|
const result = await renderNodes(node.body, options);
|
|
894
991
|
return result;
|
|
895
992
|
} finally {
|
|
896
993
|
context.set("_levelOffset", previousOffset);
|
|
994
|
+
context.set("_levelOffsetFence", previousFence);
|
|
897
995
|
}
|
|
898
996
|
}
|
|
899
997
|
default:
|
|
@@ -994,9 +1092,11 @@ var FileSystemLoader = class {
|
|
|
994
1092
|
/**
|
|
995
1093
|
* Create a filesystem loader
|
|
996
1094
|
* @param basePath - Base directory for resolving relative paths
|
|
1095
|
+
* @param options - See {@link FileSystemLoaderOptions}
|
|
997
1096
|
*/
|
|
998
|
-
constructor(basePath = process.cwd()) {
|
|
1097
|
+
constructor(basePath = process.cwd(), options = {}) {
|
|
999
1098
|
this.basePath = basePath;
|
|
1099
|
+
this.root = options.root;
|
|
1000
1100
|
}
|
|
1001
1101
|
/**
|
|
1002
1102
|
* Load a file from the filesystem
|
|
@@ -1009,6 +1109,41 @@ var FileSystemLoader = class {
|
|
|
1009
1109
|
const pathModule = await import("path");
|
|
1010
1110
|
const resolvedBasePath = basePath || this.basePath;
|
|
1011
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
|
+
}
|
|
1012
1147
|
try {
|
|
1013
1148
|
return await fs.readFile(resolvedPath, "utf-8");
|
|
1014
1149
|
} catch (error) {
|
|
@@ -1020,7 +1155,10 @@ var FileSystemLoader = class {
|
|
|
1020
1155
|
|
|
1021
1156
|
// src/cli.ts
|
|
1022
1157
|
var program = new Command();
|
|
1023
|
-
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").
|
|
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) => {
|
|
1024
1162
|
try {
|
|
1025
1163
|
let template;
|
|
1026
1164
|
let baseDir = process.cwd();
|
|
@@ -1047,7 +1185,10 @@ program.name("minja").description("Minimal, secure Jinja2/Nunjucks subset for do
|
|
|
1047
1185
|
const inlineVars = JSON.parse(options.vars);
|
|
1048
1186
|
context = { ...context, ...inlineVars };
|
|
1049
1187
|
}
|
|
1050
|
-
const loader = new FileSystemLoader(
|
|
1188
|
+
const loader = new FileSystemLoader(
|
|
1189
|
+
baseDir,
|
|
1190
|
+
options.includeRoot ? { root: resolve(options.includeRoot) } : {}
|
|
1191
|
+
);
|
|
1051
1192
|
const result = await render(template, {
|
|
1052
1193
|
loader,
|
|
1053
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
|
package/dist/browser.d.ts.map
CHANGED
|
@@ -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
|
package/dist/browser.js.map
CHANGED
|
@@ -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":"AAAA;;GAEG;AAEH,OAAO,EAAE,QAAQ,EAAE,SAAS,EAAE,MAAM,aAAa,CAAA;AACjD,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,MAAM,MAAM,CAAA;AACvC,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAA;AACnC,OAAO,EAAE,KAAK,IAAI,SAAS,EAAE,MAAM,MAAM,CAAA;AACzC,OAAO,EAAE,MAAM,EAAE,MAAM,eAAe,CAAA;AACtC,OAAO,EAAE,gBAAgB,EAAE,MAAM,aAAa,CAAA;AAE9C,MAAM,OAAO,GAAG,IAAI,OAAO,EAAE,CAAA;AAE7B,OAAO;KACJ,IAAI,CAAC,OAAO,CAAC;KACb,WAAW,CAAC,wEAAwE,CAAC;KACrF,OAAO,CAAC,OAAO,CAAC;KAChB,QAAQ,CAAC,YAAY,EAAE,8CAA8C,CAAC;KACtE,MAAM,CAAC,sBAAsB,EAAE,6BAA6B,CAAC;KAC7D,MAAM,CAAC,qBAAqB,EAAE,+BAA+B,CAAC;KAC9D,MAAM,CAAC,0BAA0B,EAAE,uBAAuB,EAAE,IAAI,CAAC;KACjE,MAAM,CAAC,oBAAoB,EAAE,mCAAmC,EAAE,MAAM,CAAC;KACzE,MAAM,CAAC,mBAAmB,EAAE,kCAAkC,CAAC;KAC/D,MAAM,CAAC,KAAK,EAAE,YAAgC,EAAE,
|
|
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
|
package/dist/index.d.ts.map
CHANGED
|
@@ -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;
|
|
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"}
|