mutorjs 1.3.1 → 1.3.3
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/cli.cjs +22 -1760
- package/dist/cli.cjs.map +1 -1
- package/dist/index.cjs +13 -1349
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +13 -1326
- package/dist/index.js.map +1 -1
- package/dist/mutor.global.js +14 -0
- package/dist/mutor.global.js.map +1 -0
- package/dist/server.cjs +16 -1479
- package/dist/server.cjs.map +1 -1
- package/dist/server.js +16 -1462
- package/dist/server.js.map +1 -1
- package/package.json +2 -1
package/dist/cli.cjs
CHANGED
|
@@ -1,1547 +1,20 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
"use strict";
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
var
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
};
|
|
11
|
-
var
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
};
|
|
19
|
-
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
20
|
-
|
|
21
|
-
// src/bin/cli.ts
|
|
22
|
-
var cli_exports = {};
|
|
23
|
-
__export(cli_exports, {
|
|
24
|
-
handleBuildCommand: () => handleBuildCommand,
|
|
25
|
-
handleCompileCommand: () => handleCompileCommand,
|
|
26
|
-
handleRenderCommand: () => handleRenderCommand,
|
|
27
|
-
parseArgs: () => parseArgs,
|
|
28
|
-
safeParseJsonFile: () => safeParseJsonFile,
|
|
29
|
-
safeReadFile: () => safeReadFile,
|
|
30
|
-
safeWriteFile: () => safeWriteFile
|
|
31
|
-
});
|
|
32
|
-
module.exports = __toCommonJS(cli_exports);
|
|
33
|
-
var import_node_fs2 = require("fs");
|
|
34
|
-
var import_node_process = require("process");
|
|
35
|
-
|
|
36
|
-
// package.json
|
|
37
|
-
var version = "1.3.1";
|
|
38
|
-
|
|
39
|
-
// src/core/mutor.server.ts
|
|
40
|
-
var import_node_fs = require("fs");
|
|
41
|
-
var import_promises = require("fs/promises");
|
|
42
|
-
var import_node_path2 = require("path");
|
|
43
|
-
|
|
44
|
-
// src/utils/construct-pointer.ts
|
|
45
|
-
function constructPointer(pos, offset) {
|
|
46
|
-
return `${" ".repeat(pos + offset + 1)}^`;
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
// src/core/error.ts
|
|
50
|
-
var MutorError = class _MutorError extends Error {
|
|
51
|
-
constructor(message) {
|
|
52
|
-
super(message);
|
|
53
|
-
this.name = "MutorError";
|
|
54
|
-
Object.setPrototypeOf(this, _MutorError.prototype);
|
|
55
|
-
}
|
|
56
|
-
};
|
|
57
|
-
var MutorCompilerError = class _MutorCompilerError extends MutorError {
|
|
58
|
-
constructor(message, line, lineText, column, file) {
|
|
59
|
-
const gutterWidth = line.toString().length + 2;
|
|
60
|
-
let report = `${message}
|
|
61
|
-
|
|
62
|
-
`;
|
|
63
|
-
report += `at ${file}:${line}:${column + 1}
|
|
64
|
-
`;
|
|
65
|
-
if (line > 1) {
|
|
66
|
-
report += `${(line - 1).toString().padStart(gutterWidth - 2)} | ...
|
|
67
|
-
`;
|
|
68
|
-
}
|
|
69
|
-
report += `${line} | ${lineText}
|
|
70
|
-
`;
|
|
71
|
-
report += constructPointer(column, gutterWidth);
|
|
72
|
-
super(report);
|
|
73
|
-
this.name = "MutorCompilerError";
|
|
74
|
-
Object.setPrototypeOf(this, _MutorCompilerError.prototype);
|
|
75
|
-
}
|
|
76
|
-
};
|
|
77
|
-
|
|
78
|
-
// src/core/constants.ts
|
|
79
|
-
var keywords = /* @__PURE__ */ new Set([
|
|
80
|
-
"for",
|
|
81
|
-
"if",
|
|
82
|
-
"else",
|
|
83
|
-
"true",
|
|
84
|
-
"false",
|
|
85
|
-
"null",
|
|
86
|
-
"undefined",
|
|
87
|
-
"end",
|
|
88
|
-
"in",
|
|
89
|
-
"of"
|
|
90
|
-
]);
|
|
91
|
-
var operators = /* @__PURE__ */ new Set([
|
|
92
|
-
"::",
|
|
93
|
-
// Namespace access
|
|
94
|
-
"||",
|
|
95
|
-
// Or
|
|
96
|
-
"??",
|
|
97
|
-
// Nullish coalesce
|
|
98
|
-
"&&",
|
|
99
|
-
// And
|
|
100
|
-
"**",
|
|
101
|
-
// Power
|
|
102
|
-
"^",
|
|
103
|
-
// Bitwise XOr
|
|
104
|
-
"|",
|
|
105
|
-
// Bitwise Or
|
|
106
|
-
"&",
|
|
107
|
-
// Bitwise And
|
|
108
|
-
"!",
|
|
109
|
-
// Not
|
|
110
|
-
"-",
|
|
111
|
-
// Minus
|
|
112
|
-
"%",
|
|
113
|
-
// Modulus
|
|
114
|
-
"+",
|
|
115
|
-
// Plus
|
|
116
|
-
"*",
|
|
117
|
-
// Times
|
|
118
|
-
"/",
|
|
119
|
-
// Divide
|
|
120
|
-
">",
|
|
121
|
-
// Greater than
|
|
122
|
-
"<",
|
|
123
|
-
// Less than
|
|
124
|
-
">=",
|
|
125
|
-
// Greater or equal
|
|
126
|
-
"<=",
|
|
127
|
-
// Less or equal
|
|
128
|
-
"==",
|
|
129
|
-
// Strict equal
|
|
130
|
-
"!=",
|
|
131
|
-
// Strict not equal
|
|
132
|
-
">>",
|
|
133
|
-
// Bitwise right shift
|
|
134
|
-
"<<",
|
|
135
|
-
// Bitwise left shift
|
|
136
|
-
".",
|
|
137
|
-
// Property acess
|
|
138
|
-
"?.",
|
|
139
|
-
// Optional property access
|
|
140
|
-
"(",
|
|
141
|
-
// Open parentheses
|
|
142
|
-
")",
|
|
143
|
-
// Close parentheses
|
|
144
|
-
"[",
|
|
145
|
-
// Square open parentheses
|
|
146
|
-
"]",
|
|
147
|
-
// Square close parentheses
|
|
148
|
-
",",
|
|
149
|
-
// Comma
|
|
150
|
-
":",
|
|
151
|
-
// Column
|
|
152
|
-
"?"
|
|
153
|
-
// Ternary operator
|
|
154
|
-
]);
|
|
155
|
-
var equalityOperators = /* @__PURE__ */ new Set(["==", "!="]);
|
|
156
|
-
var comparisonOperators = /* @__PURE__ */ new Set([">", "<", ">=", "<="]);
|
|
157
|
-
var additiveOperators = /* @__PURE__ */ new Set(["+", "-"]);
|
|
158
|
-
var multiplicativeOperators = /* @__PURE__ */ new Set(["*", "/", "%"]);
|
|
159
|
-
var propertyAccessOperators = /* @__PURE__ */ new Set([".", "?.", "[", "::"]);
|
|
160
|
-
var unaryOperators = /* @__PURE__ */ new Set(["-", "+", "!"]);
|
|
161
|
-
var ESCAPE_MAP = {
|
|
162
|
-
"&": "&",
|
|
163
|
-
"<": "<",
|
|
164
|
-
">": ">",
|
|
165
|
-
'"': """,
|
|
166
|
-
"'": "'"
|
|
167
|
-
};
|
|
168
|
-
var defaultConfig = {
|
|
169
|
-
build: {
|
|
170
|
-
include: /* @__PURE__ */ new Set([".html", ".txt"]),
|
|
171
|
-
exclude: /* @__PURE__ */ new Set(["node_modules", ".git"])
|
|
172
|
-
},
|
|
173
|
-
autoEscape: true,
|
|
174
|
-
allowedProps: /* @__PURE__ */ new Set(),
|
|
175
|
-
forbiddenProps: /* @__PURE__ */ new Set(["__proto__", "constructor", "prototype"]),
|
|
176
|
-
allowFnCalls: false,
|
|
177
|
-
delimiters: {
|
|
178
|
-
closingTag: "}}",
|
|
179
|
-
openingTag: "{{",
|
|
180
|
-
openingTagEscape: "\\",
|
|
181
|
-
whitespaceTrim: "~",
|
|
182
|
-
commentTag: "#"
|
|
183
|
-
},
|
|
184
|
-
keepOpeningTagEscapeDelimiter: false,
|
|
185
|
-
cache: {
|
|
186
|
-
active: true,
|
|
187
|
-
maxSize: 50 * 1024 * 1024
|
|
188
|
-
// 50MB
|
|
189
|
-
}
|
|
190
|
-
};
|
|
191
|
-
var namespaces = {
|
|
192
|
-
JSON: {
|
|
193
|
-
stringify(value) {
|
|
194
|
-
try {
|
|
195
|
-
return JSON.stringify(value);
|
|
196
|
-
} catch {
|
|
197
|
-
throw new MutorError("JSON.stringify failed: invalid value");
|
|
198
|
-
}
|
|
199
|
-
},
|
|
200
|
-
parse(str) {
|
|
201
|
-
if (typeof str !== "string") {
|
|
202
|
-
throw new MutorError("JSON.parse expects a string");
|
|
203
|
-
}
|
|
204
|
-
try {
|
|
205
|
-
return JSON.parse(str);
|
|
206
|
-
} catch {
|
|
207
|
-
throw new MutorError("JSON.parse failed: invalid JSON string");
|
|
208
|
-
}
|
|
209
|
-
}
|
|
210
|
-
},
|
|
211
|
-
Object: {
|
|
212
|
-
keys(obj) {
|
|
213
|
-
if (!obj || typeof obj !== "object") {
|
|
214
|
-
throw new MutorError("Object.keys expects an object");
|
|
215
|
-
}
|
|
216
|
-
return Object.keys(obj);
|
|
217
|
-
},
|
|
218
|
-
values(obj) {
|
|
219
|
-
if (!obj || typeof obj !== "object") {
|
|
220
|
-
throw new MutorError("Object.values expects an object");
|
|
221
|
-
}
|
|
222
|
-
return Object.values(obj);
|
|
223
|
-
},
|
|
224
|
-
entries(obj) {
|
|
225
|
-
if (!obj || typeof obj !== "object") {
|
|
226
|
-
throw new MutorError("Object.entries expects an object");
|
|
227
|
-
}
|
|
228
|
-
return Object.entries(obj);
|
|
229
|
-
},
|
|
230
|
-
hasOwn(obj, key) {
|
|
231
|
-
if (!obj || typeof obj !== "object") {
|
|
232
|
-
throw new MutorError("Object.hasOwn expects an object");
|
|
233
|
-
}
|
|
234
|
-
return Object.hasOwn(obj, key);
|
|
235
|
-
},
|
|
236
|
-
freeze(obj) {
|
|
237
|
-
if (!obj || typeof obj !== "object") {
|
|
238
|
-
throw new MutorError("Object.freeze expects an object");
|
|
239
|
-
}
|
|
240
|
-
return Object.freeze(obj);
|
|
241
|
-
},
|
|
242
|
-
seal(obj) {
|
|
243
|
-
if (!obj || typeof obj !== "object") {
|
|
244
|
-
throw new MutorError("Object.seal expects an object");
|
|
245
|
-
}
|
|
246
|
-
return Object.seal(obj);
|
|
247
|
-
},
|
|
248
|
-
fromEntries(entries) {
|
|
249
|
-
if (!Array.isArray(entries)) {
|
|
250
|
-
throw new MutorError("Object.fromEntries expects an array");
|
|
251
|
-
}
|
|
252
|
-
return Object.fromEntries(entries);
|
|
253
|
-
}
|
|
254
|
-
},
|
|
255
|
-
Array: {
|
|
256
|
-
isArray(value) {
|
|
257
|
-
return Array.isArray(value);
|
|
258
|
-
},
|
|
259
|
-
from(value) {
|
|
260
|
-
return Array.from(value);
|
|
261
|
-
}
|
|
262
|
-
},
|
|
263
|
-
Number: {
|
|
264
|
-
isFinite(value) {
|
|
265
|
-
return Number.isFinite(value);
|
|
266
|
-
},
|
|
267
|
-
isNaN(value) {
|
|
268
|
-
return Number.isNaN(value);
|
|
269
|
-
},
|
|
270
|
-
parseInt(value, radix = 10) {
|
|
271
|
-
return Number.parseInt(value, radix);
|
|
272
|
-
},
|
|
273
|
-
parseFloat(value) {
|
|
274
|
-
return Number.parseFloat(value);
|
|
275
|
-
}
|
|
276
|
-
},
|
|
277
|
-
String: {
|
|
278
|
-
fromCharCode(...args) {
|
|
279
|
-
return String.fromCharCode(...args);
|
|
280
|
-
}
|
|
281
|
-
},
|
|
282
|
-
Math: {
|
|
283
|
-
abs(x) {
|
|
284
|
-
return Math.abs(x);
|
|
285
|
-
},
|
|
286
|
-
floor(x) {
|
|
287
|
-
return Math.floor(x);
|
|
288
|
-
},
|
|
289
|
-
ceil(x) {
|
|
290
|
-
return Math.ceil(x);
|
|
291
|
-
},
|
|
292
|
-
round(x) {
|
|
293
|
-
return Math.round(x);
|
|
294
|
-
},
|
|
295
|
-
max(...args) {
|
|
296
|
-
return Math.max(...args);
|
|
297
|
-
},
|
|
298
|
-
min(...args) {
|
|
299
|
-
return Math.min(...args);
|
|
300
|
-
},
|
|
301
|
-
random() {
|
|
302
|
-
return Math.random();
|
|
303
|
-
}
|
|
304
|
-
},
|
|
305
|
-
Date: {
|
|
306
|
-
now() {
|
|
307
|
-
return Date.now();
|
|
308
|
-
},
|
|
309
|
-
parse(str) {
|
|
310
|
-
if (typeof str !== "string") {
|
|
311
|
-
throw new MutorError("Date.parse expects a string");
|
|
312
|
-
}
|
|
313
|
-
return Date.parse(str);
|
|
314
|
-
}
|
|
315
|
-
},
|
|
316
|
-
Boolean: {
|
|
317
|
-
valueOf(value) {
|
|
318
|
-
return Boolean(value);
|
|
319
|
-
}
|
|
320
|
-
}
|
|
321
|
-
};
|
|
322
|
-
|
|
323
|
-
// src/utils/escape-fn.ts
|
|
324
|
-
function escapeFn(e) {
|
|
325
|
-
if (typeof e !== "string") return e;
|
|
326
|
-
return /[&<>"']/.test(e) ? e.replace(/[&<>"']/g, (char) => ESCAPE_MAP[char]) : e;
|
|
327
|
-
}
|
|
328
|
-
|
|
329
|
-
// src/utils/to-absolute-path.ts
|
|
330
|
-
var import_node_path = require("path");
|
|
331
|
-
function toAbsolutePath(basePath, ...relativePaths) {
|
|
332
|
-
const absoluteBase = (0, import_node_path.isAbsolute)(basePath) ? basePath : (0, import_node_path.resolve)(process.cwd(), basePath);
|
|
333
|
-
if (relativePaths.length) {
|
|
334
|
-
const baseDir = (0, import_node_path.dirname)(absoluteBase);
|
|
335
|
-
return (0, import_node_path.resolve)(baseDir, ...relativePaths);
|
|
336
|
-
}
|
|
337
|
-
return absoluteBase;
|
|
338
|
-
}
|
|
339
|
-
|
|
340
|
-
// src/utils/validate-computed-prop.ts
|
|
341
|
-
function validateComputedProp(r, allowedProps, forbiddenProps) {
|
|
342
|
-
if (forbiddenProps.has(r) && !allowedProps.has(r)) {
|
|
343
|
-
throw new MutorError(
|
|
344
|
-
`Forbidden property access. Access to this computed property "${r}" is forbidden.`
|
|
345
|
-
);
|
|
346
|
-
}
|
|
347
|
-
return r;
|
|
348
|
-
}
|
|
349
|
-
|
|
350
|
-
// src/utils/validate-context.ts
|
|
351
|
-
var OBJECT = "object";
|
|
352
|
-
var MUTOR_SAFE = /* @__PURE__ */ Symbol("__mutor_safe_context");
|
|
353
|
-
function validateContext(ctx) {
|
|
354
|
-
if (!ctx || typeof ctx !== OBJECT) {
|
|
355
|
-
return ctx;
|
|
356
|
-
}
|
|
357
|
-
if (MUTOR_SAFE in ctx) {
|
|
358
|
-
return ctx;
|
|
359
|
-
}
|
|
360
|
-
const seen = /* @__PURE__ */ new WeakSet();
|
|
361
|
-
function walk(value, path = "") {
|
|
362
|
-
if (!value || typeof value !== OBJECT) return value;
|
|
363
|
-
if (seen.has(value)) return value;
|
|
364
|
-
seen.add(value);
|
|
365
|
-
const proto = Object.getPrototypeOf(value);
|
|
366
|
-
if (proto && proto !== Object.prototype && proto !== Array.prototype) {
|
|
367
|
-
throw new MutorError(`Unsafe prototype detected at ${path || "root"}`);
|
|
368
|
-
}
|
|
369
|
-
if (Array.isArray(value)) {
|
|
370
|
-
for (let i = 0; i < value.length; i++) {
|
|
371
|
-
value[i] = walk(value[i], `${path}[${i}]`);
|
|
372
|
-
}
|
|
373
|
-
return value;
|
|
374
|
-
}
|
|
375
|
-
if (value instanceof Map) {
|
|
376
|
-
for (const [k, v] of value.entries()) {
|
|
377
|
-
if (typeof k === OBJECT) walk(k, `${path}.mapKey`);
|
|
378
|
-
value.set(k, walk(v, `${path}.mapValue`));
|
|
379
|
-
}
|
|
380
|
-
return value;
|
|
381
|
-
}
|
|
382
|
-
if (value instanceof Set) {
|
|
383
|
-
const next = /* @__PURE__ */ new Set();
|
|
384
|
-
for (const v of value.values()) {
|
|
385
|
-
next.add(walk(v, path));
|
|
386
|
-
}
|
|
387
|
-
value.clear();
|
|
388
|
-
for (const v of next) value.add(v);
|
|
389
|
-
return value;
|
|
390
|
-
}
|
|
391
|
-
const descriptors = Object.getOwnPropertyDescriptors(value);
|
|
392
|
-
for (const key of Object.keys(descriptors)) {
|
|
393
|
-
const desc = descriptors[key];
|
|
394
|
-
if (desc.get || desc.set) {
|
|
395
|
-
throw new MutorError(`Getter/setter not allowed: ${path}.${key}`);
|
|
396
|
-
}
|
|
397
|
-
const prop = value[key];
|
|
398
|
-
if (prop && typeof prop === OBJECT) {
|
|
399
|
-
value[key] = walk(prop, `${path}.${key}`);
|
|
400
|
-
}
|
|
401
|
-
}
|
|
402
|
-
return value;
|
|
403
|
-
}
|
|
404
|
-
const safeData = walk(ctx);
|
|
405
|
-
if (safeData && typeof safeData === OBJECT) {
|
|
406
|
-
Object.defineProperty(safeData, MUTOR_SAFE, {
|
|
407
|
-
value: true,
|
|
408
|
-
enumerable: false,
|
|
409
|
-
writable: false,
|
|
410
|
-
configurable: false
|
|
411
|
-
});
|
|
412
|
-
}
|
|
413
|
-
return safeData;
|
|
414
|
-
}
|
|
415
|
-
|
|
416
|
-
// src/utils/escape-raw-text.ts
|
|
417
|
-
function escapeRawText(text) {
|
|
418
|
-
return text.replace(/\\/g, "\\\\").replace(/`/g, "\\`").replace(/\$/g, "\\$");
|
|
419
|
-
}
|
|
420
|
-
|
|
421
|
-
// src/utils/get-line-and-column-nums.ts
|
|
422
|
-
function getLineAndColumnNumbers(str, idx) {
|
|
423
|
-
const lines = str.slice(0, idx).split("\n");
|
|
424
|
-
const line = lines.length;
|
|
425
|
-
const lineIndex = str.lastIndexOf("\n", idx - 1) + 1;
|
|
426
|
-
return { line, lineIndex };
|
|
427
|
-
}
|
|
428
|
-
|
|
429
|
-
// src/utils/get-line-snapshot.ts
|
|
430
|
-
function getLineSnapshot(str, lineIdx, idx) {
|
|
431
|
-
const nextNewlineIdx = str.indexOf("\n", lineIdx);
|
|
432
|
-
const line = str.slice(
|
|
433
|
-
lineIdx,
|
|
434
|
-
nextNewlineIdx === -1 ? void 0 : nextNewlineIdx
|
|
435
|
-
);
|
|
436
|
-
return { line, pos: idx - lineIdx };
|
|
437
|
-
}
|
|
438
|
-
|
|
439
|
-
// src/core/build.ts
|
|
440
|
-
function build(ast, context) {
|
|
441
|
-
const { scope, forbiddenProps, allowedProps } = context;
|
|
442
|
-
function prefixWithCtx(ident) {
|
|
443
|
-
return scope.includes(ident) ? ident : `ctx.${ident}`;
|
|
444
|
-
}
|
|
445
|
-
function buildExpr(expr) {
|
|
446
|
-
switch (expr.type) {
|
|
447
|
-
case 17 /* END */:
|
|
448
|
-
return "}";
|
|
449
|
-
case 5 /* NUMBER */:
|
|
450
|
-
return expr.value;
|
|
451
|
-
case 4 /* STRING */:
|
|
452
|
-
return `\`${/\$/.test(expr.value) ? expr.value.replaceAll("$", "\\$") : expr.value}\``;
|
|
453
|
-
case 10 /* BOOLEAN */:
|
|
454
|
-
return expr.true ? "true" : "false";
|
|
455
|
-
case 12 /* NULL */:
|
|
456
|
-
return "null";
|
|
457
|
-
case 11 /* UNDEFINED */:
|
|
458
|
-
return "undefined";
|
|
459
|
-
case 7 /* IDENT */:
|
|
460
|
-
return prefixWithCtx(expr.value);
|
|
461
|
-
case 9 /* GROUP */:
|
|
462
|
-
return `(${buildExpr(expr.expr)})`;
|
|
463
|
-
case 2 /* UNARY */:
|
|
464
|
-
return `${expr.operator}${buildExpr(expr.expr)}`;
|
|
465
|
-
case 0 /* BINARY */:
|
|
466
|
-
return `${buildExpr(expr.left)} ${expr.operator} ${buildExpr(expr.right)}`;
|
|
467
|
-
case 1 /* TERNARY */:
|
|
468
|
-
return `${buildExpr(expr.condition)} ? ${buildExpr(expr.left)} : ${buildExpr(expr.right)}`;
|
|
469
|
-
case 8 /* PROP_ACCESS */:
|
|
470
|
-
return buildPropAccess(expr);
|
|
471
|
-
case 3 /* CALL */:
|
|
472
|
-
return buildCall(expr);
|
|
473
|
-
case 6 /* NAMESPACE */:
|
|
474
|
-
return buildNamespace(expr);
|
|
475
|
-
case 13 /* FOR */:
|
|
476
|
-
return buildForLoop(expr);
|
|
477
|
-
case 16 /* ELSE */:
|
|
478
|
-
return "} else {";
|
|
479
|
-
case 14 /* IF */:
|
|
480
|
-
return buildIfBlock(expr);
|
|
481
|
-
case 15 /* ELSE_IF */:
|
|
482
|
-
return buildElseIfBlock(expr);
|
|
483
|
-
default:
|
|
484
|
-
throw new MutorError(
|
|
485
|
-
`Unsupported expression type: ${expr.type}`
|
|
486
|
-
);
|
|
487
|
-
}
|
|
488
|
-
}
|
|
489
|
-
function buildNamespace(expr) {
|
|
490
|
-
if (expr.left.type !== 7 /* IDENT */) {
|
|
491
|
-
throw {
|
|
492
|
-
message: "Invalid usage of namespace operator.",
|
|
493
|
-
pos: expr.pos
|
|
494
|
-
};
|
|
495
|
-
}
|
|
496
|
-
return `namespaces.${expr.left.value}.${expr.right.value}`;
|
|
497
|
-
}
|
|
498
|
-
function buildPropAccess(expr) {
|
|
499
|
-
const left = buildExpr(expr.left);
|
|
500
|
-
if (expr.bracketNotation) {
|
|
501
|
-
const right = buildExpr(expr.right);
|
|
502
|
-
const optionalChain = expr.optional ? "?." : "";
|
|
503
|
-
return expr.right.type === 4 /* STRING */ || expr.right.type === 5 /* NUMBER */ ? `${left}${optionalChain}[${right}]` : `${left}${optionalChain}[validateComputedProps(${right}, allowedProps, forbiddenProps)]`;
|
|
504
|
-
} else {
|
|
505
|
-
const propName = expr.right.value;
|
|
506
|
-
const optionalChain = expr.optional ? "?." : ".";
|
|
507
|
-
if (forbiddenProps.has(propName) && !allowedProps.has(propName)) {
|
|
508
|
-
throw { message: "Forbidden property access.", pos: expr.right.pos };
|
|
509
|
-
}
|
|
510
|
-
return `${left}${optionalChain}${propName}`;
|
|
511
|
-
}
|
|
512
|
-
}
|
|
513
|
-
function buildCall(expr) {
|
|
514
|
-
const func = buildExpr(expr.expr);
|
|
515
|
-
const optionalChain = expr.optional ? "?." : "";
|
|
516
|
-
const args = expr.args.map((arg) => buildExpr(arg)).join(", ");
|
|
517
|
-
return `${func}${optionalChain}(${args})`;
|
|
518
|
-
}
|
|
519
|
-
function buildForLoop(expr) {
|
|
520
|
-
const { iterable, loopType, variable } = expr;
|
|
521
|
-
return `for(const ${variable} ${loopType === 1 /* IN */ ? "in" : "of"} ${build(iterable, context)}){`;
|
|
522
|
-
}
|
|
523
|
-
function buildIfBlock(expr) {
|
|
524
|
-
const { condition } = expr;
|
|
525
|
-
return `if(${build(condition, context)}){`;
|
|
526
|
-
}
|
|
527
|
-
function buildElseIfBlock(expr) {
|
|
528
|
-
const { condition } = expr;
|
|
529
|
-
return `}else if(${build(condition, context)}){`;
|
|
530
|
-
}
|
|
531
|
-
return buildExpr(ast);
|
|
532
|
-
}
|
|
533
|
-
|
|
534
|
-
// src/utils/get-token-type-words.ts
|
|
535
|
-
function getTokenTypeWords(type) {
|
|
536
|
-
switch (type) {
|
|
537
|
-
case 0 /* IDENT */:
|
|
538
|
-
return "identifier";
|
|
539
|
-
case 1 /* KEYWORD */:
|
|
540
|
-
return "keyword";
|
|
541
|
-
case 2 /* NUMBER */:
|
|
542
|
-
return "number";
|
|
543
|
-
case 4 /* OPERATOR */:
|
|
544
|
-
return "operator";
|
|
545
|
-
case 3 /* STRING */:
|
|
546
|
-
return "string";
|
|
547
|
-
}
|
|
548
|
-
}
|
|
549
|
-
|
|
550
|
-
// src/core/generate-ast.ts
|
|
551
|
-
function generateAst(tokens, config) {
|
|
552
|
-
let cursor = 0, generatingNamespace = false;
|
|
553
|
-
function expectOrThrow(type, value) {
|
|
554
|
-
const token = tokens[cursor];
|
|
555
|
-
const lastToken = tokens[tokens.length - 1];
|
|
556
|
-
if (!token) {
|
|
557
|
-
throw {
|
|
558
|
-
message: `Unexpected end of expression. Expected ${value ? `'${value}'` : `${type === 0 /* IDENT */ ? "an" : "a"} ${getTokenTypeWords(type)}`}.`,
|
|
559
|
-
pos: lastToken.pos + lastToken.value.length - 1
|
|
560
|
-
};
|
|
561
|
-
}
|
|
562
|
-
if (token.type !== type) {
|
|
563
|
-
throw {
|
|
564
|
-
message: `Unexpected token type. Expected ${value ? `'${value}'` : getTokenTypeWords(type)} but got ${getTokenTypeWords(token.type)} instead.`,
|
|
565
|
-
pos: token.pos
|
|
566
|
-
};
|
|
567
|
-
}
|
|
568
|
-
if (value !== void 0 && token.value !== value) {
|
|
569
|
-
throw {
|
|
570
|
-
message: `Unexpected token '${token?.value}'. Expected ${type === 0 /* IDENT */ ? "an" : "a"} ${getTokenTypeWords(type)} instead.`,
|
|
571
|
-
pos: token.pos
|
|
572
|
-
};
|
|
573
|
-
}
|
|
574
|
-
return tokens[cursor++];
|
|
575
|
-
}
|
|
576
|
-
function parseForLoop() {
|
|
577
|
-
const pos = tokens[cursor - 1].pos;
|
|
578
|
-
const variable = expectOrThrow(0 /* IDENT */).value;
|
|
579
|
-
let token;
|
|
580
|
-
try {
|
|
581
|
-
token = expectOrThrow(1 /* KEYWORD */, "in");
|
|
582
|
-
} catch {
|
|
583
|
-
token = expectOrThrow(1 /* KEYWORD */, "of");
|
|
584
|
-
}
|
|
585
|
-
const loopType = token.value === "in" ? 1 /* IN */ : 0 /* OF */;
|
|
586
|
-
const iterable = parseTernaryExpr();
|
|
587
|
-
return { type: 13 /* FOR */, loopType, iterable, variable, pos };
|
|
588
|
-
}
|
|
589
|
-
function parseIfExpression() {
|
|
590
|
-
const condition = parseTernaryExpr();
|
|
591
|
-
return { condition, pos: condition.pos, type: 14 /* IF */ };
|
|
592
|
-
}
|
|
593
|
-
function parseElseExpression() {
|
|
594
|
-
const pos = tokens[cursor - 1].pos;
|
|
595
|
-
try {
|
|
596
|
-
expectOrThrow(1 /* KEYWORD */, "if");
|
|
597
|
-
return { ...parseIfExpression(), type: 15 /* ELSE_IF */, pos };
|
|
598
|
-
} catch {
|
|
599
|
-
return { type: 16 /* ELSE */, pos };
|
|
600
|
-
}
|
|
601
|
-
}
|
|
602
|
-
function extractFnArgs() {
|
|
603
|
-
const args = [];
|
|
604
|
-
const emptyArgs = tokens[cursor]?.type === 4 /* OPERATOR */ && tokens[cursor]?.value === ")";
|
|
605
|
-
if (emptyArgs) {
|
|
606
|
-
return args;
|
|
607
|
-
}
|
|
608
|
-
args.push(parseTernaryExpr());
|
|
609
|
-
while (tokens[cursor]?.type === 4 /* OPERATOR */ && tokens[cursor]?.value === "," && tokens[cursor]?.value !== ")") {
|
|
610
|
-
cursor++;
|
|
611
|
-
args.push(parseTernaryExpr());
|
|
612
|
-
}
|
|
613
|
-
return args;
|
|
614
|
-
}
|
|
615
|
-
function parsePrimaryExpr() {
|
|
616
|
-
const token = tokens[cursor++];
|
|
617
|
-
if (token?.type === 2 /* NUMBER */) {
|
|
618
|
-
return { type: 5 /* NUMBER */, value: token.value, pos: token.pos };
|
|
619
|
-
}
|
|
620
|
-
if (token?.type === 3 /* STRING */) {
|
|
621
|
-
return { type: 4 /* STRING */, value: token.value, pos: token.pos };
|
|
622
|
-
}
|
|
623
|
-
if (token?.type === 1 /* KEYWORD */) {
|
|
624
|
-
if (token.value === "for" && cursor === 1) {
|
|
625
|
-
return parseForLoop();
|
|
626
|
-
}
|
|
627
|
-
if (token.value === "true" || token.value === "false") {
|
|
628
|
-
return {
|
|
629
|
-
type: 10 /* BOOLEAN */,
|
|
630
|
-
true: token.value === "true",
|
|
631
|
-
pos: token.pos
|
|
632
|
-
};
|
|
633
|
-
}
|
|
634
|
-
if (token.value === "undefined") {
|
|
635
|
-
return { type: 11 /* UNDEFINED */, pos: token.pos };
|
|
636
|
-
}
|
|
637
|
-
if (token.value === "null") {
|
|
638
|
-
return { type: 12 /* NULL */, pos: token.pos };
|
|
639
|
-
}
|
|
640
|
-
if (token.value === "end" && tokens.length === 1) {
|
|
641
|
-
return { type: 17 /* END */, pos: token.pos };
|
|
642
|
-
}
|
|
643
|
-
if (token.value === "if" && cursor === 1) {
|
|
644
|
-
return parseIfExpression();
|
|
645
|
-
}
|
|
646
|
-
if (token.value === "else" && cursor === 1) {
|
|
647
|
-
return parseElseExpression();
|
|
648
|
-
}
|
|
649
|
-
}
|
|
650
|
-
if (token?.type === 0 /* IDENT */) {
|
|
651
|
-
return { type: 7 /* IDENT */, value: token.value, pos: token.pos };
|
|
652
|
-
}
|
|
653
|
-
if (token?.type === 4 /* OPERATOR */ && token.value === "(") {
|
|
654
|
-
const expr = parseTernaryExpr();
|
|
655
|
-
expectOrThrow(4 /* OPERATOR */, ")");
|
|
656
|
-
return { type: 9 /* GROUP */, expr, pos: token.pos };
|
|
657
|
-
}
|
|
658
|
-
if (token?.type === 4 /* OPERATOR */ && unaryOperators.has(token.value)) {
|
|
659
|
-
const operator = token.value;
|
|
660
|
-
const expr = parseTernaryExpr();
|
|
661
|
-
return { type: 2 /* UNARY */, operator, expr, pos: token.pos };
|
|
662
|
-
}
|
|
663
|
-
if (cursor > tokens.length) {
|
|
664
|
-
throw {
|
|
665
|
-
message: `Unexpected end of expression.`,
|
|
666
|
-
pos: tokens[tokens.length - 1].pos
|
|
667
|
-
};
|
|
668
|
-
}
|
|
669
|
-
throw {
|
|
670
|
-
message: `Unexpected token '${token?.value}'.`,
|
|
671
|
-
pos: token.pos
|
|
672
|
-
};
|
|
673
|
-
}
|
|
674
|
-
function parsePropertyAccess() {
|
|
675
|
-
let left = parsePrimaryExpr();
|
|
676
|
-
while (tokens[cursor]) {
|
|
677
|
-
const token = tokens[cursor];
|
|
678
|
-
if (token?.type === 4 /* OPERATOR */ && token?.value === "(") {
|
|
679
|
-
cursor++;
|
|
680
|
-
if (!generatingNamespace && !config.allowFnCalls) {
|
|
681
|
-
throw {
|
|
682
|
-
message: "Function calls are not allowed.",
|
|
683
|
-
pos: tokens[cursor - 1].pos
|
|
684
|
-
};
|
|
685
|
-
}
|
|
686
|
-
const args = extractFnArgs();
|
|
687
|
-
expectOrThrow(4 /* OPERATOR */, ")");
|
|
688
|
-
left = {
|
|
689
|
-
type: 3 /* CALL */,
|
|
690
|
-
expr: left,
|
|
691
|
-
args,
|
|
692
|
-
pos: tokens[cursor - 1].pos
|
|
693
|
-
};
|
|
694
|
-
} else if (token?.type === 4 /* OPERATOR */ && token?.value === "?." && tokens[cursor + 1]?.type === 4 /* OPERATOR */ && tokens[cursor + 1]?.value === "(") {
|
|
695
|
-
cursor++;
|
|
696
|
-
cursor++;
|
|
697
|
-
if (!generatingNamespace && !config.allowFnCalls) {
|
|
698
|
-
throw {
|
|
699
|
-
message: "Function calls are not allowed.",
|
|
700
|
-
pos: tokens[cursor - 1].pos
|
|
701
|
-
};
|
|
702
|
-
}
|
|
703
|
-
const args = extractFnArgs();
|
|
704
|
-
expectOrThrow(4 /* OPERATOR */, ")");
|
|
705
|
-
left = {
|
|
706
|
-
type: 3 /* CALL */,
|
|
707
|
-
expr: left,
|
|
708
|
-
args,
|
|
709
|
-
optional: true,
|
|
710
|
-
pos: tokens[cursor - 1].pos
|
|
711
|
-
};
|
|
712
|
-
} else if (token?.type === 4 /* OPERATOR */ && propertyAccessOperators.has(token?.value)) {
|
|
713
|
-
const isNamespace = token?.value === "::";
|
|
714
|
-
const isBracketNotation = token?.value === "[";
|
|
715
|
-
const isOptional = token?.value === "?.";
|
|
716
|
-
cursor++;
|
|
717
|
-
if (isNamespace && (tokens[cursor - 2]?.type !== 0 /* IDENT */ || tokens[cursor]?.type !== 0 /* IDENT */)) {
|
|
718
|
-
throw {
|
|
719
|
-
message: `Invalid namespaces access. Expected syntax <IDENTIFIER>::<IDENTIFIER>, but got '${tokens[cursor - 2]?.value}::${tokens[cursor]?.value}' instead.`,
|
|
720
|
-
pos: tokens[cursor]?.pos
|
|
721
|
-
};
|
|
722
|
-
}
|
|
723
|
-
if (isNamespace) {
|
|
724
|
-
generatingNamespace = true;
|
|
725
|
-
const right = parsePrimaryExpr();
|
|
726
|
-
left = {
|
|
727
|
-
type: 6 /* NAMESPACE */,
|
|
728
|
-
left,
|
|
729
|
-
right,
|
|
730
|
-
pos: tokens[cursor - 1].pos
|
|
731
|
-
};
|
|
732
|
-
} else if (isBracketNotation) {
|
|
733
|
-
const right = parseTernaryExpr();
|
|
734
|
-
expectOrThrow(4 /* OPERATOR */, "]");
|
|
735
|
-
left = {
|
|
736
|
-
type: 8 /* PROP_ACCESS */,
|
|
737
|
-
right,
|
|
738
|
-
left,
|
|
739
|
-
bracketNotation: true,
|
|
740
|
-
pos: tokens[cursor - 1].pos
|
|
741
|
-
};
|
|
742
|
-
} else if (isOptional) {
|
|
743
|
-
if (tokens[cursor]?.type === 4 /* OPERATOR */ && tokens[cursor]?.value === "[") {
|
|
744
|
-
cursor++;
|
|
745
|
-
const right = parseTernaryExpr();
|
|
746
|
-
expectOrThrow(4 /* OPERATOR */, "]");
|
|
747
|
-
left = {
|
|
748
|
-
type: 8 /* PROP_ACCESS */,
|
|
749
|
-
left,
|
|
750
|
-
right,
|
|
751
|
-
bracketNotation: true,
|
|
752
|
-
optional: true,
|
|
753
|
-
pos: tokens[cursor - 1].pos
|
|
754
|
-
};
|
|
755
|
-
} else {
|
|
756
|
-
const right = parsePrimaryExpr();
|
|
757
|
-
left = {
|
|
758
|
-
type: 8 /* PROP_ACCESS */,
|
|
759
|
-
left,
|
|
760
|
-
right,
|
|
761
|
-
optional: true,
|
|
762
|
-
pos: tokens[cursor - 1].pos
|
|
763
|
-
};
|
|
764
|
-
}
|
|
765
|
-
} else {
|
|
766
|
-
const right = parsePrimaryExpr();
|
|
767
|
-
left = {
|
|
768
|
-
type: 8 /* PROP_ACCESS */,
|
|
769
|
-
left,
|
|
770
|
-
right,
|
|
771
|
-
pos: tokens[cursor - 1].pos
|
|
772
|
-
};
|
|
773
|
-
}
|
|
774
|
-
} else {
|
|
775
|
-
break;
|
|
776
|
-
}
|
|
777
|
-
}
|
|
778
|
-
generatingNamespace = false;
|
|
779
|
-
return left;
|
|
780
|
-
}
|
|
781
|
-
function parseMultiplicativeExpr() {
|
|
782
|
-
let left = parsePropertyAccess();
|
|
783
|
-
while (tokens[cursor]?.type === 4 /* OPERATOR */ && multiplicativeOperators.has(tokens[cursor]?.value)) {
|
|
784
|
-
const operator = tokens[cursor++].value;
|
|
785
|
-
const right = parsePropertyAccess();
|
|
786
|
-
left = {
|
|
787
|
-
type: 0 /* BINARY */,
|
|
788
|
-
left,
|
|
789
|
-
right,
|
|
790
|
-
operator,
|
|
791
|
-
pos: tokens[cursor - 1].pos
|
|
792
|
-
};
|
|
793
|
-
}
|
|
794
|
-
return left;
|
|
795
|
-
}
|
|
796
|
-
function parseAdditiveExpr() {
|
|
797
|
-
let left = parseMultiplicativeExpr();
|
|
798
|
-
while (tokens[cursor]?.type === 4 /* OPERATOR */ && additiveOperators.has(tokens[cursor]?.value)) {
|
|
799
|
-
const operator = tokens[cursor++].value;
|
|
800
|
-
const right = parseMultiplicativeExpr();
|
|
801
|
-
left = {
|
|
802
|
-
type: 0 /* BINARY */,
|
|
803
|
-
left,
|
|
804
|
-
right,
|
|
805
|
-
operator,
|
|
806
|
-
pos: tokens[cursor - 1].pos
|
|
807
|
-
};
|
|
808
|
-
}
|
|
809
|
-
return left;
|
|
810
|
-
}
|
|
811
|
-
function parseBitwiseExpr() {
|
|
812
|
-
let left = parseAdditiveExpr();
|
|
813
|
-
while (tokens[cursor]?.type === 4 /* OPERATOR */ && comparisonOperators.has(tokens[cursor]?.value)) {
|
|
814
|
-
const operator = tokens[cursor++].value;
|
|
815
|
-
const right = parseAdditiveExpr();
|
|
816
|
-
left = {
|
|
817
|
-
type: 0 /* BINARY */,
|
|
818
|
-
left,
|
|
819
|
-
right,
|
|
820
|
-
operator,
|
|
821
|
-
pos: tokens[cursor - 1].pos
|
|
822
|
-
};
|
|
823
|
-
}
|
|
824
|
-
return left;
|
|
825
|
-
}
|
|
826
|
-
function parseComparisonExpr() {
|
|
827
|
-
let left = parseBitwiseExpr();
|
|
828
|
-
while (tokens[cursor]?.type === 4 /* OPERATOR */ && comparisonOperators.has(tokens[cursor]?.value)) {
|
|
829
|
-
const operator = tokens[cursor++].value;
|
|
830
|
-
const right = parseBitwiseExpr();
|
|
831
|
-
left = {
|
|
832
|
-
type: 0 /* BINARY */,
|
|
833
|
-
left,
|
|
834
|
-
right,
|
|
835
|
-
operator,
|
|
836
|
-
pos: tokens[cursor - 1].pos
|
|
837
|
-
};
|
|
838
|
-
}
|
|
839
|
-
return left;
|
|
840
|
-
}
|
|
841
|
-
function parseEqualityExpr() {
|
|
842
|
-
let left = parseComparisonExpr();
|
|
843
|
-
while (tokens[cursor]?.type === 4 /* OPERATOR */ && equalityOperators.has(tokens[cursor]?.value)) {
|
|
844
|
-
const operator = tokens[cursor++].value;
|
|
845
|
-
const right = parseComparisonExpr();
|
|
846
|
-
left = {
|
|
847
|
-
type: 0 /* BINARY */,
|
|
848
|
-
left,
|
|
849
|
-
right,
|
|
850
|
-
operator,
|
|
851
|
-
pos: tokens[cursor - 1].pos
|
|
852
|
-
};
|
|
853
|
-
}
|
|
854
|
-
return left;
|
|
855
|
-
}
|
|
856
|
-
function parseLogicalOrExpr() {
|
|
857
|
-
let left = parseLogicalAndExpr();
|
|
858
|
-
while (tokens[cursor]?.type === 4 /* OPERATOR */ && tokens[cursor]?.value === "||") {
|
|
859
|
-
const operator = tokens[cursor++].value;
|
|
860
|
-
const right = parseLogicalAndExpr();
|
|
861
|
-
left = {
|
|
862
|
-
type: 0 /* BINARY */,
|
|
863
|
-
left,
|
|
864
|
-
right,
|
|
865
|
-
operator,
|
|
866
|
-
pos: tokens[cursor - 1].pos
|
|
867
|
-
};
|
|
868
|
-
}
|
|
869
|
-
return left;
|
|
870
|
-
}
|
|
871
|
-
function parseLogicalAndExpr() {
|
|
872
|
-
let left = parseNullishCoalesceExpr();
|
|
873
|
-
while (tokens[cursor]?.type === 4 /* OPERATOR */ && tokens[cursor]?.value === "&&") {
|
|
874
|
-
const operator = tokens[cursor++].value;
|
|
875
|
-
const right = parseNullishCoalesceExpr();
|
|
876
|
-
left = {
|
|
877
|
-
type: 0 /* BINARY */,
|
|
878
|
-
left,
|
|
879
|
-
right,
|
|
880
|
-
operator,
|
|
881
|
-
pos: tokens[cursor - 1].pos
|
|
882
|
-
};
|
|
883
|
-
}
|
|
884
|
-
return left;
|
|
885
|
-
}
|
|
886
|
-
function parseNullishCoalesceExpr() {
|
|
887
|
-
let left = parseEqualityExpr();
|
|
888
|
-
while (tokens[cursor]?.type === 4 /* OPERATOR */ && tokens[cursor]?.value === "??") {
|
|
889
|
-
const operator = tokens[cursor++].value;
|
|
890
|
-
const right = parseEqualityExpr();
|
|
891
|
-
left = {
|
|
892
|
-
type: 0 /* BINARY */,
|
|
893
|
-
left,
|
|
894
|
-
right,
|
|
895
|
-
operator,
|
|
896
|
-
pos: tokens[cursor - 1].pos
|
|
897
|
-
};
|
|
898
|
-
}
|
|
899
|
-
return left;
|
|
900
|
-
}
|
|
901
|
-
function parseTernaryExpr() {
|
|
902
|
-
const condition = parseLogicalOrExpr();
|
|
903
|
-
if (tokens[cursor]?.type !== 4 /* OPERATOR */ || tokens[cursor]?.value !== "?") {
|
|
904
|
-
return condition;
|
|
905
|
-
}
|
|
906
|
-
cursor++;
|
|
907
|
-
const left = parseTernaryExpr();
|
|
908
|
-
expectOrThrow(4 /* OPERATOR */, ":");
|
|
909
|
-
const right = parseTernaryExpr();
|
|
910
|
-
return {
|
|
911
|
-
type: 1 /* TERNARY */,
|
|
912
|
-
left,
|
|
913
|
-
right,
|
|
914
|
-
condition,
|
|
915
|
-
pos: tokens[cursor - 1].pos
|
|
916
|
-
};
|
|
917
|
-
}
|
|
918
|
-
const ast = parseTernaryExpr();
|
|
919
|
-
return ast;
|
|
920
|
-
}
|
|
921
|
-
|
|
922
|
-
// src/core/parse.ts
|
|
923
|
-
function parse(templateBlock, { delimiters }) {
|
|
924
|
-
const openingTagWithWhitespaceCtrl = `${delimiters.openingTag}${delimiters.whitespaceTrim}`;
|
|
925
|
-
const closingTagWithWhitespaceCtrl = `${delimiters.whitespaceTrim}${delimiters.closingTag}`;
|
|
926
|
-
const leftTrim = templateBlock.startsWith(openingTagWithWhitespaceCtrl);
|
|
927
|
-
const rightTrim = templateBlock.endsWith(closingTagWithWhitespaceCtrl);
|
|
928
|
-
const isComment = templateBlock.startsWith(
|
|
929
|
-
leftTrim ? openingTagWithWhitespaceCtrl + delimiters.commentTag : delimiters.openingTag + delimiters.commentTag
|
|
930
|
-
);
|
|
931
|
-
if (isComment) {
|
|
932
|
-
return { isComment, leftTrim, rightTrim };
|
|
933
|
-
}
|
|
934
|
-
const inner = templateBlock.slice(
|
|
935
|
-
leftTrim ? openingTagWithWhitespaceCtrl.length : delimiters.openingTag.length,
|
|
936
|
-
templateBlock.length - (rightTrim ? closingTagWithWhitespaceCtrl.length : delimiters.closingTag.length)
|
|
937
|
-
);
|
|
938
|
-
const trimmed = inner.trim();
|
|
939
|
-
const isBlock = trimmed.startsWith("for") || trimmed.startsWith("if") || trimmed.startsWith("else");
|
|
940
|
-
const requiresBlockClose = trimmed.startsWith("for") || trimmed.startsWith("if");
|
|
941
|
-
const isBlockEnd = trimmed === "end";
|
|
942
|
-
const hasContext = trimmed.startsWith("for");
|
|
943
|
-
return {
|
|
944
|
-
leftTrim,
|
|
945
|
-
rightTrim,
|
|
946
|
-
inner,
|
|
947
|
-
isBlock,
|
|
948
|
-
isBlockEnd,
|
|
949
|
-
hasContext,
|
|
950
|
-
requiresBlockClose
|
|
951
|
-
};
|
|
952
|
-
}
|
|
953
|
-
|
|
954
|
-
// src/core/tokenize.ts
|
|
955
|
-
function tokenize(expr) {
|
|
956
|
-
let cursor = 0, char = "";
|
|
957
|
-
const tokens = [];
|
|
958
|
-
function accumulateKeywordOrIdentifier() {
|
|
959
|
-
let buffer = "";
|
|
960
|
-
if (/[a-zA-Z$_]/.test(char)) {
|
|
961
|
-
let j = cursor;
|
|
962
|
-
while (/[a-zA-Z$_0-9]/.test(expr[j]) && j < expr.length) {
|
|
963
|
-
buffer += expr[j];
|
|
964
|
-
j++;
|
|
965
|
-
}
|
|
966
|
-
tokens.push({
|
|
967
|
-
type: keywords.has(buffer) ? 1 /* KEYWORD */ : 0 /* IDENT */,
|
|
968
|
-
value: buffer,
|
|
969
|
-
pos: cursor
|
|
970
|
-
});
|
|
971
|
-
cursor = j;
|
|
972
|
-
char = expr[cursor];
|
|
973
|
-
}
|
|
974
|
-
}
|
|
975
|
-
function accumulateStr() {
|
|
976
|
-
let buffer = "";
|
|
977
|
-
if (char === '"' || char === "'" || char === "`") {
|
|
978
|
-
let j = cursor + 1;
|
|
979
|
-
while (expr[j] !== char && j < expr.length) {
|
|
980
|
-
buffer += expr[j];
|
|
981
|
-
j++;
|
|
982
|
-
}
|
|
983
|
-
if (j > expr.length) {
|
|
984
|
-
throw { pos: cursor, message: `Found string without closing quote.` };
|
|
985
|
-
}
|
|
986
|
-
tokens.push({ type: 3 /* STRING */, value: buffer, pos: cursor });
|
|
987
|
-
cursor = j;
|
|
988
|
-
}
|
|
989
|
-
}
|
|
990
|
-
function accumulateNumber() {
|
|
991
|
-
if (/[0-9]/.test(char)) {
|
|
992
|
-
let j = cursor, buffer = "";
|
|
993
|
-
while (/[0-9.oxe]/.test(expr[j]) && j < expr.length) {
|
|
994
|
-
buffer += expr[j];
|
|
995
|
-
j++;
|
|
996
|
-
}
|
|
997
|
-
const numVal = Number(buffer);
|
|
998
|
-
const isNan = Number.isNaN(numVal);
|
|
999
|
-
if (isNan) {
|
|
1000
|
-
throw { pos: cursor, message: "Found invalid number literal." };
|
|
1001
|
-
}
|
|
1002
|
-
tokens.push({ type: 2 /* NUMBER */, value: `${numVal}`, pos: cursor });
|
|
1003
|
-
cursor = j - 1;
|
|
1004
|
-
char = expr[cursor];
|
|
1005
|
-
}
|
|
1006
|
-
}
|
|
1007
|
-
function accumulateOperator() {
|
|
1008
|
-
const op = `${char}${expr[cursor + 1]}`;
|
|
1009
|
-
if (operators.has(op)) {
|
|
1010
|
-
tokens.push({ type: 4 /* OPERATOR */, value: op, pos: cursor });
|
|
1011
|
-
cursor++;
|
|
1012
|
-
return;
|
|
1013
|
-
}
|
|
1014
|
-
if (operators.has(char)) {
|
|
1015
|
-
tokens.push({ type: 4 /* OPERATOR */, value: char, pos: cursor });
|
|
1016
|
-
return;
|
|
1017
|
-
}
|
|
1018
|
-
}
|
|
1019
|
-
while (cursor < expr.length) {
|
|
1020
|
-
char = expr[cursor];
|
|
1021
|
-
accumulateNumber();
|
|
1022
|
-
accumulateKeywordOrIdentifier();
|
|
1023
|
-
accumulateStr();
|
|
1024
|
-
accumulateOperator();
|
|
1025
|
-
if (!/[a-zA-Z$_0-9\s\t\r\n'"`]/.test(char) && !operators.has(char) && !operators.has(expr[cursor - 1] + char)) {
|
|
1026
|
-
throw {
|
|
1027
|
-
message: `Unexpected token '${char}' in expression.`,
|
|
1028
|
-
pos: cursor
|
|
1029
|
-
};
|
|
1030
|
-
}
|
|
1031
|
-
cursor++;
|
|
1032
|
-
}
|
|
1033
|
-
return tokens;
|
|
1034
|
-
}
|
|
1035
|
-
|
|
1036
|
-
// src/core/compile.ts
|
|
1037
|
-
function compile(src, config, meta) {
|
|
1038
|
-
const scope = [];
|
|
1039
|
-
const blockOpeningStack = [];
|
|
1040
|
-
const {
|
|
1041
|
-
delimiters,
|
|
1042
|
-
keepOpeningTagEscapeDelimiter,
|
|
1043
|
-
allowFnCalls,
|
|
1044
|
-
allowedProps,
|
|
1045
|
-
forbiddenProps,
|
|
1046
|
-
autoEscape
|
|
1047
|
-
} = config;
|
|
1048
|
-
let trimNext = false, cursor = 0, body = `let acc="";`;
|
|
1049
|
-
while (cursor < src.length) {
|
|
1050
|
-
let isEscaped2 = function() {
|
|
1051
|
-
let j = templateOpenTagIdx, count = 0;
|
|
1052
|
-
while (j >= delimiters.openingTagEscape.length && src.slice(j - delimiters.openingTagEscape.length, j) === delimiters.openingTagEscape) {
|
|
1053
|
-
count++;
|
|
1054
|
-
j -= delimiters.openingTagEscape.length;
|
|
1055
|
-
}
|
|
1056
|
-
return count % 2 === 1;
|
|
1057
|
-
};
|
|
1058
|
-
var isEscaped = isEscaped2;
|
|
1059
|
-
const templateOpenTagIdx = src.indexOf(delimiters.openingTag, cursor);
|
|
1060
|
-
if (templateOpenTagIdx === -1) {
|
|
1061
|
-
let lastChunk = src.slice(cursor);
|
|
1062
|
-
if (trimNext) lastChunk = lastChunk.trimStart();
|
|
1063
|
-
if (lastChunk) body += `acc+=\`${escapeRawText(lastChunk)}\`;`;
|
|
1064
|
-
break;
|
|
1065
|
-
}
|
|
1066
|
-
if (isEscaped2()) {
|
|
1067
|
-
let escapedChunk = src.slice(
|
|
1068
|
-
cursor,
|
|
1069
|
-
keepOpeningTagEscapeDelimiter ? templateOpenTagIdx + delimiters.openingTagEscape.length + 1 : templateOpenTagIdx - delimiters.openingTag.length + 1
|
|
1070
|
-
);
|
|
1071
|
-
if (trimNext) {
|
|
1072
|
-
escapedChunk = escapedChunk.trimStart();
|
|
1073
|
-
trimNext = false;
|
|
1074
|
-
}
|
|
1075
|
-
body += `acc+=\`${escapeRawText(escapedChunk)}\`;`;
|
|
1076
|
-
if (!keepOpeningTagEscapeDelimiter)
|
|
1077
|
-
body += `acc+=\`${delimiters.openingTag}\`;`;
|
|
1078
|
-
cursor = templateOpenTagIdx + delimiters.openingTag.length;
|
|
1079
|
-
continue;
|
|
1080
|
-
}
|
|
1081
|
-
const templateEndTagIdx = src.indexOf(
|
|
1082
|
-
delimiters.closingTag,
|
|
1083
|
-
templateOpenTagIdx
|
|
1084
|
-
);
|
|
1085
|
-
if (templateEndTagIdx === -1) {
|
|
1086
|
-
const { line, lineIndex } = getLineAndColumnNumbers(
|
|
1087
|
-
src,
|
|
1088
|
-
templateOpenTagIdx
|
|
1089
|
-
);
|
|
1090
|
-
const { line: lineText, pos } = getLineSnapshot(
|
|
1091
|
-
src,
|
|
1092
|
-
lineIndex,
|
|
1093
|
-
templateOpenTagIdx
|
|
1094
|
-
);
|
|
1095
|
-
throw new MutorCompilerError(
|
|
1096
|
-
"No closing tag found.",
|
|
1097
|
-
line,
|
|
1098
|
-
lineText,
|
|
1099
|
-
pos,
|
|
1100
|
-
meta.path
|
|
1101
|
-
);
|
|
1102
|
-
}
|
|
1103
|
-
const template = src.slice(
|
|
1104
|
-
templateOpenTagIdx,
|
|
1105
|
-
templateEndTagIdx + delimiters.closingTag.length
|
|
1106
|
-
);
|
|
1107
|
-
const {
|
|
1108
|
-
inner,
|
|
1109
|
-
leftTrim,
|
|
1110
|
-
rightTrim,
|
|
1111
|
-
isBlock,
|
|
1112
|
-
isBlockEnd,
|
|
1113
|
-
hasContext,
|
|
1114
|
-
requiresBlockClose,
|
|
1115
|
-
isComment
|
|
1116
|
-
} = parse(template, { delimiters });
|
|
1117
|
-
let rawText = src.slice(cursor, templateOpenTagIdx);
|
|
1118
|
-
if (rawText) {
|
|
1119
|
-
if (trimNext) {
|
|
1120
|
-
rawText = rawText.trimStart();
|
|
1121
|
-
}
|
|
1122
|
-
if (leftTrim) {
|
|
1123
|
-
rawText = rawText.trimEnd();
|
|
1124
|
-
}
|
|
1125
|
-
if (rawText) {
|
|
1126
|
-
body += `acc+=\`${escapeRawText(rawText)}\`;`;
|
|
1127
|
-
}
|
|
1128
|
-
}
|
|
1129
|
-
trimNext = false;
|
|
1130
|
-
cursor = templateEndTagIdx + delimiters.closingTag.length;
|
|
1131
|
-
try {
|
|
1132
|
-
if (!isComment) {
|
|
1133
|
-
const tokens = tokenize(inner);
|
|
1134
|
-
const ast = generateAst(tokens, { allowFnCalls });
|
|
1135
|
-
if (isBlock && requiresBlockClose && hasContext) {
|
|
1136
|
-
scope.push(ast.variable);
|
|
1137
|
-
blockOpeningStack.push({
|
|
1138
|
-
type: 0 /* LOOP */,
|
|
1139
|
-
pos: templateOpenTagIdx
|
|
1140
|
-
});
|
|
1141
|
-
} else if (isBlock && requiresBlockClose && !hasContext) {
|
|
1142
|
-
blockOpeningStack.push({
|
|
1143
|
-
type: 1 /* NON_LOOP */,
|
|
1144
|
-
pos: templateOpenTagIdx
|
|
1145
|
-
});
|
|
1146
|
-
}
|
|
1147
|
-
if (isBlockEnd) {
|
|
1148
|
-
const lastBlockOpened = blockOpeningStack.pop();
|
|
1149
|
-
if (lastBlockOpened?.type === 0 /* LOOP */) scope.pop();
|
|
1150
|
-
if (lastBlockOpened === void 0)
|
|
1151
|
-
throw {
|
|
1152
|
-
message: "Unexpected end of block",
|
|
1153
|
-
pos: templateOpenTagIdx
|
|
1154
|
-
};
|
|
1155
|
-
}
|
|
1156
|
-
const js = build(ast, { allowedProps, forbiddenProps, scope });
|
|
1157
|
-
if (isBlock || isBlockEnd) {
|
|
1158
|
-
body += js;
|
|
1159
|
-
} else {
|
|
1160
|
-
body += autoEscape && !js.startsWith("namespaces.Mutor.include") ? `acc+=escapeFn(${js});` : `acc+=${js};`;
|
|
1161
|
-
}
|
|
1162
|
-
}
|
|
1163
|
-
if (rightTrim) trimNext = true;
|
|
1164
|
-
} catch (e) {
|
|
1165
|
-
const { message, pos: relPos } = e;
|
|
1166
|
-
const { line, lineIndex } = getLineAndColumnNumbers(
|
|
1167
|
-
src,
|
|
1168
|
-
templateOpenTagIdx
|
|
1169
|
-
);
|
|
1170
|
-
const { line: lineText, pos } = getLineSnapshot(
|
|
1171
|
-
src,
|
|
1172
|
-
lineIndex,
|
|
1173
|
-
templateOpenTagIdx
|
|
1174
|
-
);
|
|
1175
|
-
throw new MutorCompilerError(
|
|
1176
|
-
message,
|
|
1177
|
-
line,
|
|
1178
|
-
lineText,
|
|
1179
|
-
pos + relPos + (leftTrim ? delimiters.openingTag.length + delimiters.whitespaceTrim.length : delimiters.openingTag.length),
|
|
1180
|
-
meta.path
|
|
1181
|
-
);
|
|
1182
|
-
}
|
|
1183
|
-
}
|
|
1184
|
-
if (blockOpeningStack.length) {
|
|
1185
|
-
const lastPos = blockOpeningStack.pop()?.pos;
|
|
1186
|
-
const { line, lineIndex } = getLineAndColumnNumbers(src, lastPos);
|
|
1187
|
-
const { line: lineText, pos } = getLineSnapshot(src, lineIndex, lastPos);
|
|
1188
|
-
throw new MutorCompilerError(
|
|
1189
|
-
"Unclosed block detected.",
|
|
1190
|
-
line,
|
|
1191
|
-
lineText,
|
|
1192
|
-
pos + delimiters.openingTag.length,
|
|
1193
|
-
meta.path
|
|
1194
|
-
);
|
|
1195
|
-
}
|
|
1196
|
-
body += `return acc;`;
|
|
1197
|
-
return new Function(
|
|
1198
|
-
"ctx",
|
|
1199
|
-
"namespaces",
|
|
1200
|
-
"allowedProps",
|
|
1201
|
-
"forbiddenProps",
|
|
1202
|
-
"escapeFn",
|
|
1203
|
-
"validateComputedProps",
|
|
1204
|
-
body
|
|
1205
|
-
);
|
|
1206
|
-
}
|
|
1207
|
-
|
|
1208
|
-
// src/core/mutor.ts
|
|
1209
|
-
var Mutor = class {
|
|
1210
|
-
constructor(config = {}) {
|
|
1211
|
-
this.__currentRenderedPath = "";
|
|
1212
|
-
this.__includeStack = /* @__PURE__ */ new Set();
|
|
1213
|
-
this.__cacheSize = 0;
|
|
1214
|
-
this.__config = { ...defaultConfig };
|
|
1215
|
-
this.__compiledTemplatesMap = /* @__PURE__ */ new Map();
|
|
1216
|
-
this.__currentContext = null;
|
|
1217
|
-
this.__namespaces = {
|
|
1218
|
-
...namespaces,
|
|
1219
|
-
Mutor: {
|
|
1220
|
-
include: (path, ctx) => {
|
|
1221
|
-
if (this.__includeStack.has(path)) {
|
|
1222
|
-
throw new MutorError(
|
|
1223
|
-
`Circular include detected:
|
|
1224
|
-
${Array.from(this.__includeStack).join("\n")}
|
|
1225
|
-
${path}`
|
|
1226
|
-
);
|
|
1227
|
-
}
|
|
1228
|
-
try {
|
|
1229
|
-
this.__includeStack.add(path);
|
|
1230
|
-
return this.renderComponent(path, ctx ?? this.__currentContext);
|
|
1231
|
-
} finally {
|
|
1232
|
-
this.__includeStack.delete(path);
|
|
1233
|
-
}
|
|
1234
|
-
}
|
|
1235
|
-
}
|
|
1236
|
-
};
|
|
1237
|
-
this.addConfig(config);
|
|
1238
|
-
Object.defineProperty(this.__namespaces.Mutor, "$$context", {
|
|
1239
|
-
get: () => {
|
|
1240
|
-
return this.__currentContext;
|
|
1241
|
-
}
|
|
1242
|
-
});
|
|
1243
|
-
}
|
|
1244
|
-
addConfig(conf) {
|
|
1245
|
-
const {
|
|
1246
|
-
autoEscape,
|
|
1247
|
-
delimiters: overrideDelimeters,
|
|
1248
|
-
allowedProps,
|
|
1249
|
-
forbiddenProps,
|
|
1250
|
-
keepOpeningTagEscapeDelimiter,
|
|
1251
|
-
allowFnCalls,
|
|
1252
|
-
cache,
|
|
1253
|
-
build: build2
|
|
1254
|
-
} = conf;
|
|
1255
|
-
this.__config = {
|
|
1256
|
-
build: {
|
|
1257
|
-
include: /* @__PURE__ */ new Set([...build2?.include || defaultConfig.build.include]),
|
|
1258
|
-
exclude: /* @__PURE__ */ new Set([
|
|
1259
|
-
...defaultConfig.build.exclude,
|
|
1260
|
-
...build2?.exclude || []
|
|
1261
|
-
])
|
|
1262
|
-
},
|
|
1263
|
-
autoEscape: autoEscape === true ? true : autoEscape !== false,
|
|
1264
|
-
allowedProps: allowedProps || defaultConfig.allowedProps,
|
|
1265
|
-
allowFnCalls: !!allowFnCalls,
|
|
1266
|
-
cache: { ...defaultConfig.cache, ...cache || {} },
|
|
1267
|
-
forbiddenProps: /* @__PURE__ */ new Set([
|
|
1268
|
-
...defaultConfig.forbiddenProps,
|
|
1269
|
-
...forbiddenProps || []
|
|
1270
|
-
]),
|
|
1271
|
-
keepOpeningTagEscapeDelimiter: keepOpeningTagEscapeDelimiter === true ? true : keepOpeningTagEscapeDelimiter !== false,
|
|
1272
|
-
delimiters: {
|
|
1273
|
-
...defaultConfig.delimiters,
|
|
1274
|
-
...overrideDelimeters || {}
|
|
1275
|
-
}
|
|
1276
|
-
};
|
|
1277
|
-
return this.__config;
|
|
1278
|
-
}
|
|
1279
|
-
restoreDefaultConfig() {
|
|
1280
|
-
this.__config = { ...defaultConfig };
|
|
1281
|
-
}
|
|
1282
|
-
compile(template) {
|
|
1283
|
-
return compile(template, this.__config, {
|
|
1284
|
-
path: this.__currentRenderedPath || "anonymous"
|
|
1285
|
-
});
|
|
1286
|
-
}
|
|
1287
|
-
render(template, context) {
|
|
1288
|
-
const prevContext = this.__currentContext;
|
|
1289
|
-
if (prevContext !== context) {
|
|
1290
|
-
this.__currentContext = context;
|
|
1291
|
-
}
|
|
1292
|
-
const result = this.compile(template)(
|
|
1293
|
-
validateContext(context),
|
|
1294
|
-
this.__namespaces,
|
|
1295
|
-
this.__config.allowedProps,
|
|
1296
|
-
this.__config.forbiddenProps,
|
|
1297
|
-
escapeFn,
|
|
1298
|
-
validateComputedProp
|
|
1299
|
-
);
|
|
1300
|
-
this.__currentContext = prevContext;
|
|
1301
|
-
return result;
|
|
1302
|
-
}
|
|
1303
|
-
renderComponent(identifier, context) {
|
|
1304
|
-
if (!this.__compiledTemplatesMap.has(identifier)) {
|
|
1305
|
-
throw new MutorError(
|
|
1306
|
-
`No template exists with the identifier '${identifier}'`
|
|
1307
|
-
);
|
|
1308
|
-
}
|
|
1309
|
-
const prevRenderComponentIdentifier = this.__currentRenderedPath;
|
|
1310
|
-
const prevContext = this.__currentContext;
|
|
1311
|
-
const compiled = this.__compiledTemplatesMap.get(identifier);
|
|
1312
|
-
this.__currentContext = context;
|
|
1313
|
-
this.__currentRenderedPath = identifier;
|
|
1314
|
-
const result = compiled.fn(
|
|
1315
|
-
validateContext(context),
|
|
1316
|
-
this.__namespaces,
|
|
1317
|
-
this.__config.allowedProps,
|
|
1318
|
-
this.__config.forbiddenProps,
|
|
1319
|
-
escapeFn,
|
|
1320
|
-
validateComputedProp
|
|
1321
|
-
);
|
|
1322
|
-
this.__currentContext = prevContext;
|
|
1323
|
-
this.__currentRenderedPath = prevRenderComponentIdentifier;
|
|
1324
|
-
return result;
|
|
1325
|
-
}
|
|
1326
|
-
registerComponent(identifier, template) {
|
|
1327
|
-
const templateSize = template.length * 2 + 500;
|
|
1328
|
-
if (this.__cacheSize + templateSize > this.__config.cache.maxSize) {
|
|
1329
|
-
if (!this.createEntrySpaceForTemplate(templateSize)) {
|
|
1330
|
-
throw new MutorError(
|
|
1331
|
-
`The template for the component '${identifier}' is too large. Consider increasing 'cache.maxSize' in the config`
|
|
1332
|
-
);
|
|
1333
|
-
}
|
|
1334
|
-
}
|
|
1335
|
-
this.__cacheSize += template.length * 2 + 500;
|
|
1336
|
-
this.__compiledTemplatesMap.set(identifier, {
|
|
1337
|
-
fn: this.compile(template),
|
|
1338
|
-
size: templateSize
|
|
1339
|
-
});
|
|
1340
|
-
}
|
|
1341
|
-
reset() {
|
|
1342
|
-
this.__config = { ...defaultConfig };
|
|
1343
|
-
this.__compiledTemplatesMap.clear();
|
|
1344
|
-
this.__currentContext = null;
|
|
1345
|
-
this.__cacheSize = 0;
|
|
1346
|
-
}
|
|
1347
|
-
createEntrySpaceForTemplate(targetSize) {
|
|
1348
|
-
if (this.__cacheSize + targetSize < this.__config.cache.maxSize) {
|
|
1349
|
-
return true;
|
|
1350
|
-
}
|
|
1351
|
-
if (targetSize > this.__config.cache.maxSize) {
|
|
1352
|
-
return false;
|
|
1353
|
-
}
|
|
1354
|
-
const firstEntry = this.__compiledTemplatesMap.entries().next().value;
|
|
1355
|
-
if (firstEntry) {
|
|
1356
|
-
const [oldestKey, oldestData] = firstEntry;
|
|
1357
|
-
this.__compiledTemplatesMap.delete(oldestKey);
|
|
1358
|
-
this.__cacheSize -= oldestData.size;
|
|
1359
|
-
}
|
|
1360
|
-
return this.createEntrySpaceForTemplate(targetSize);
|
|
1361
|
-
}
|
|
1362
|
-
getDiagnostics() {
|
|
1363
|
-
const maxSize = this.__config.cache.maxSize;
|
|
1364
|
-
return {
|
|
1365
|
-
bytesUsed: this.__cacheSize,
|
|
1366
|
-
bytesMax: maxSize,
|
|
1367
|
-
readableUsed: `${(this.__cacheSize / 1024 / 1024).toFixed(2)} MB`,
|
|
1368
|
-
readableMax: `${(maxSize / 1024 / 1024).toFixed(2)} MB`,
|
|
1369
|
-
totalEntries: this.__compiledTemplatesMap.size,
|
|
1370
|
-
percentFull: maxSize > 0 ? Math.min(100, Math.round(this.__cacheSize / maxSize * 100)) : 0,
|
|
1371
|
-
avgTemplateSize: this.__compiledTemplatesMap.size > 0 ? Math.round(this.__cacheSize / this.__compiledTemplatesMap.size) : 0
|
|
1372
|
-
};
|
|
1373
|
-
}
|
|
1374
|
-
};
|
|
1375
|
-
|
|
1376
|
-
// src/core/mutor.server.ts
|
|
1377
|
-
var Mutor2 = class extends Mutor {
|
|
1378
|
-
constructor(config = {}) {
|
|
1379
|
-
super(config);
|
|
1380
|
-
this.__namespaces.Mutor.include = (path, context) => {
|
|
1381
|
-
const resolvedPath = toAbsolutePath(this.__currentRenderedPath, path);
|
|
1382
|
-
if (this.__includeStack.has(resolvedPath)) {
|
|
1383
|
-
throw new MutorError(
|
|
1384
|
-
`Circular include detected:
|
|
1385
|
-
${Array.from(this.__includeStack).join("\n")}
|
|
1386
|
-
${resolvedPath}`
|
|
1387
|
-
);
|
|
1388
|
-
}
|
|
1389
|
-
try {
|
|
1390
|
-
this.__includeStack.add(resolvedPath);
|
|
1391
|
-
return this.renderFile(resolvedPath, context ?? this.__currentContext);
|
|
1392
|
-
} finally {
|
|
1393
|
-
this.__includeStack.delete(resolvedPath);
|
|
1394
|
-
}
|
|
1395
|
-
};
|
|
1396
|
-
}
|
|
1397
|
-
renderFile(path, context) {
|
|
1398
|
-
const absolutePath = toAbsolutePath(path);
|
|
1399
|
-
let compiled;
|
|
1400
|
-
const prevContext = this.__currentContext;
|
|
1401
|
-
const prevRenderComponentIdentifier = this.__currentRenderedPath;
|
|
1402
|
-
this.__currentContext = context;
|
|
1403
|
-
this.__currentRenderedPath = path;
|
|
1404
|
-
if (this.__config.cache.active && this.__compiledTemplatesMap.has(absolutePath)) {
|
|
1405
|
-
compiled = this.__compiledTemplatesMap.get(absolutePath).fn;
|
|
1406
|
-
} else {
|
|
1407
|
-
const template = (0, import_node_fs.readFileSync)(absolutePath, "utf-8");
|
|
1408
|
-
compiled = this.compile(template);
|
|
1409
|
-
if (this.__config.cache.active) {
|
|
1410
|
-
const templateSize = template.length * 2 + 500;
|
|
1411
|
-
if (this.__cacheSize + templateSize > this.__config.cache.maxSize) {
|
|
1412
|
-
if (this.createEntrySpaceForTemplate(templateSize)) {
|
|
1413
|
-
this.__compiledTemplatesMap.set(absolutePath, {
|
|
1414
|
-
fn: compiled,
|
|
1415
|
-
size: templateSize
|
|
1416
|
-
});
|
|
1417
|
-
this.__cacheSize += templateSize;
|
|
1418
|
-
}
|
|
1419
|
-
} else {
|
|
1420
|
-
this.__compiledTemplatesMap.set(absolutePath, {
|
|
1421
|
-
fn: compiled,
|
|
1422
|
-
size: templateSize
|
|
1423
|
-
});
|
|
1424
|
-
this.__cacheSize += templateSize;
|
|
1425
|
-
}
|
|
1426
|
-
}
|
|
1427
|
-
}
|
|
1428
|
-
const result = compiled(
|
|
1429
|
-
validateContext(context),
|
|
1430
|
-
this.__namespaces,
|
|
1431
|
-
this.__config.allowedProps,
|
|
1432
|
-
this.__config.forbiddenProps,
|
|
1433
|
-
escapeFn,
|
|
1434
|
-
validateComputedProp
|
|
1435
|
-
);
|
|
1436
|
-
this.__currentContext = prevContext;
|
|
1437
|
-
this.__currentRenderedPath = prevRenderComponentIdentifier;
|
|
1438
|
-
return result;
|
|
1439
|
-
}
|
|
1440
|
-
async buildDir(src, destination, context) {
|
|
1441
|
-
const absoluteDestinationPath = toAbsolutePath(destination);
|
|
1442
|
-
const absoluteSrcPath = toAbsolutePath(src);
|
|
1443
|
-
await (0, import_promises.mkdir)(absoluteDestinationPath, { recursive: true });
|
|
1444
|
-
const entries = await (0, import_promises.readdir)(absoluteSrcPath, { withFileTypes: true });
|
|
1445
|
-
await Promise.all(
|
|
1446
|
-
entries.map(async (entry) => {
|
|
1447
|
-
const srcPath = (0, import_node_path2.join)(absoluteSrcPath, entry.name);
|
|
1448
|
-
const destinationPath = (0, import_node_path2.join)(absoluteDestinationPath, entry.name);
|
|
1449
|
-
if (this.__config.build.exclude.has(entry.name)) {
|
|
1450
|
-
return;
|
|
1451
|
-
}
|
|
1452
|
-
if (entry.isDirectory()) {
|
|
1453
|
-
return this.buildDir(srcPath, destinationPath, context);
|
|
1454
|
-
}
|
|
1455
|
-
const extension = (0, import_node_path2.extname)(srcPath);
|
|
1456
|
-
if (this.__config.build.include.has(extension)) {
|
|
1457
|
-
const rendered = this.renderFile(srcPath, context);
|
|
1458
|
-
await (0, import_promises.writeFile)(destinationPath, rendered, "utf-8");
|
|
1459
|
-
} else {
|
|
1460
|
-
return await (0, import_promises.copyFile)(srcPath, destinationPath);
|
|
1461
|
-
}
|
|
1462
|
-
})
|
|
1463
|
-
);
|
|
1464
|
-
}
|
|
1465
|
-
async compileDir(src) {
|
|
1466
|
-
const absolutePath = toAbsolutePath(src);
|
|
1467
|
-
const entries = await (0, import_promises.readdir)(absolutePath, { withFileTypes: true });
|
|
1468
|
-
await Promise.all(
|
|
1469
|
-
entries.map(async (entry) => {
|
|
1470
|
-
const absoluteSrcPath = (0, import_node_path2.join)(absolutePath, entry.name);
|
|
1471
|
-
if (this.__config.build.exclude.has(entry.name)) {
|
|
1472
|
-
return;
|
|
1473
|
-
}
|
|
1474
|
-
if (entry.isDirectory()) {
|
|
1475
|
-
return this.compileDir(absoluteSrcPath);
|
|
1476
|
-
}
|
|
1477
|
-
const extension = (0, import_node_path2.extname)(absoluteSrcPath);
|
|
1478
|
-
if (this.__config.build.include.has(extension)) {
|
|
1479
|
-
try {
|
|
1480
|
-
const template = await (0, import_promises.readFile)(absoluteSrcPath, "utf-8");
|
|
1481
|
-
this.registerComponent(absoluteSrcPath, template);
|
|
1482
|
-
} catch {
|
|
1483
|
-
}
|
|
1484
|
-
}
|
|
1485
|
-
})
|
|
1486
|
-
);
|
|
1487
|
-
}
|
|
1488
|
-
};
|
|
1489
|
-
|
|
1490
|
-
// src/bin/cli-errors.ts
|
|
1491
|
-
var ExitCodes = {
|
|
1492
|
-
Success: 0,
|
|
1493
|
-
RuntimeError: 1,
|
|
1494
|
-
ArgumentError: 2
|
|
1495
|
-
};
|
|
1496
|
-
var CliError = class extends Error {
|
|
1497
|
-
constructor(message, exitCode = ExitCodes.RuntimeError) {
|
|
1498
|
-
super(message);
|
|
1499
|
-
this.exitCode = exitCode;
|
|
1500
|
-
this.name = "CliError";
|
|
1501
|
-
}
|
|
1502
|
-
};
|
|
1503
|
-
var ArgumentError = class extends CliError {
|
|
1504
|
-
constructor(message) {
|
|
1505
|
-
super(message, ExitCodes.ArgumentError);
|
|
1506
|
-
this.name = "ArgumentError";
|
|
1507
|
-
}
|
|
1508
|
-
};
|
|
1509
|
-
var FileReadError = class extends CliError {
|
|
1510
|
-
constructor(filePath, cause) {
|
|
1511
|
-
const reason = cause instanceof Error ? cause.message : String(cause);
|
|
1512
|
-
super(
|
|
1513
|
-
`could not read file '${filePath}': ${reason}`,
|
|
1514
|
-
ExitCodes.RuntimeError
|
|
1515
|
-
);
|
|
1516
|
-
this.name = "FileReadError";
|
|
1517
|
-
}
|
|
1518
|
-
};
|
|
1519
|
-
var FileWriteError = class extends CliError {
|
|
1520
|
-
constructor(filePath, cause) {
|
|
1521
|
-
const reason = cause instanceof Error ? cause.message : String(cause);
|
|
1522
|
-
super(
|
|
1523
|
-
`could not write file '${filePath}': ${reason}`,
|
|
1524
|
-
ExitCodes.RuntimeError
|
|
1525
|
-
);
|
|
1526
|
-
this.name = "FileWriteError";
|
|
1527
|
-
}
|
|
1528
|
-
};
|
|
1529
|
-
var JsonParseError = class extends CliError {
|
|
1530
|
-
constructor(filePath, cause) {
|
|
1531
|
-
const reason = cause instanceof Error ? cause.message : String(cause);
|
|
1532
|
-
super(
|
|
1533
|
-
`failed to parse JSON in '${filePath}': ${reason}`,
|
|
1534
|
-
ExitCodes.RuntimeError
|
|
1535
|
-
);
|
|
1536
|
-
this.name = "JsonParseError";
|
|
1537
|
-
}
|
|
1538
|
-
};
|
|
1539
|
-
|
|
1540
|
-
// src/bin/cli.ts
|
|
1541
|
-
var COMMANDS = /* @__PURE__ */ new Set(["compile", "build", "render"]);
|
|
1542
|
-
var OPTIONS = /* @__PURE__ */ new Set(["--out", "--data", "--config"]);
|
|
1543
|
-
var VERSION = `Mutor.js v${version}`;
|
|
1544
|
-
var USAGE = `
|
|
2
|
+
"use strict";var pe=Object.defineProperty;var Le=Object.getOwnPropertyDescriptor;var We=Object.getOwnPropertyNames;var Ye=Object.prototype.hasOwnProperty;var Ge=(e,r)=>{for(var t in r)pe(e,t,{get:r[t],enumerable:!0})},Je=(e,r,t,s)=>{if(r&&typeof r=="object"||typeof r=="function")for(let o of We(r))!Ye.call(e,o)&&o!==t&&pe(e,o,{get:()=>r[o],enumerable:!(s=Le(r,o))||s.enumerable});return e};var qe=e=>Je(pe({},"__esModule",{value:!0}),e);var tt={};Ge(tt,{handleBuildCommand:()=>Fe,handleCompileCommand:()=>Me,handleRenderCommand:()=>je,parseArgs:()=>Ie,safeParseJsonFile:()=>ce,safeReadFile:()=>ae,safeWriteFile:()=>ge});module.exports=qe(tt);var $=require("fs"),F=require("process");var be="1.3.3";var Te=require("fs"),P=require("fs/promises"),B=require("path");function ue(e,r){return`${" ".repeat(e+r)}^`}var g=class e extends Error{constructor(t){super(t);this.name="MutorError";Object.setPrototypeOf(this,e.prototype)}},W=class e extends g{constructor(t,s,o,c,p){let m=s.toString().length+2,i=`${t}
|
|
3
|
+
|
|
4
|
+
`;i+=`at ${p}:${s}:${c+1}
|
|
5
|
+
`,s>1&&(i+=`${(s-1).toString().padStart(m-2)} | ...
|
|
6
|
+
`),i+=`${s} | ${o}
|
|
7
|
+
`,i+=ue(c,m+1);super(i);this.name="MutorCompilerError";Object.setPrototypeOf(this,e.prototype)}};var Oe=new Set(["for","if","else","true","false","null","undefined","end","in","of"]),k=new Set(["::","||","??","&&","**","^","|","&","!","-","%","+","*","/",">","<",">=","<=","==","!=",">>","<<",".","?.","(",")","[","]",",",":","?"]);var xe=new Set(["==","!="]),le=new Set([">","<",">=","<="]);var Se=new Set(["+","-"]),Re=new Set(["*","/","%"]),Ce=new Set([".","?.","[","::"]),Pe=new Set(["-","+","!"]),$e={"&":"&","<":"<",">":">",'"':""","'":"'"},N={build:{include:new Set([".html",".txt"]),exclude:new Set(["node_modules",".git"])},autoEscape:!0,allowedProps:new Set,forbiddenProps:new Set(["__proto__","constructor","prototype"]),allowFnCalls:!1,delimiters:{closingTag:"}}",openingTag:"{{",openingTagEscape:"\\",whitespaceTrim:"~",commentTag:"#"},keepOpeningTagEscapeDelimiter:!1,cache:{active:!0,maxSize:50*1024*1024}},Ne={JSON:{stringify(e){try{return JSON.stringify(e)}catch{throw new g("JSON.stringify failed: invalid value")}},parse(e){if(typeof e!="string")throw new g("JSON.parse expects a string");try{return JSON.parse(e)}catch{throw new g("JSON.parse failed: invalid JSON string")}}},Object:{keys(e){if(!e||typeof e!="object")throw new g("Object.keys expects an object");return Object.keys(e)},values(e){if(!e||typeof e!="object")throw new g("Object.values expects an object");return Object.values(e)},entries(e){if(!e||typeof e!="object")throw new g("Object.entries expects an object");return Object.entries(e)},hasOwn(e,r){if(!e||typeof e!="object")throw new g("Object.hasOwn expects an object");return Object.hasOwn(e,r)},freeze(e){if(!e||typeof e!="object")throw new g("Object.freeze expects an object");return Object.freeze(e)},seal(e){if(!e||typeof e!="object")throw new g("Object.seal expects an object");return Object.seal(e)},fromEntries(e){if(!Array.isArray(e))throw new g("Object.fromEntries expects an array");return Object.fromEntries(e)}},Array:{isArray(e){return Array.isArray(e)},from(e){return Array.from(e)}},Number:{isFinite(e){return Number.isFinite(e)},isNaN(e){return Number.isNaN(e)},parseInt(e,r=10){return Number.parseInt(e,r)},parseFloat(e){return Number.parseFloat(e)}},String:{fromCharCode(...e){return String.fromCharCode(...e)}},Math:{abs(e){return Math.abs(e)},floor(e){return Math.floor(e)},ceil(e){return Math.ceil(e)},round(e){return Math.round(e)},max(...e){return Math.max(...e)},min(...e){return Math.min(...e)},random(){return Math.random()}},Date:{now(){return Date.now()},parse(e){if(typeof e!="string")throw new g("Date.parse expects a string");return Date.parse(e)}},Boolean:{valueOf(e){return!!e}}};function Y(e){return typeof e!="string"?e:/[&<>"']/.test(e)?e.replace(/[&<>"']/g,r=>$e[r]):e}var D=require("path");function _(e,...r){let t=(0,D.isAbsolute)(e)?e:(0,D.resolve)(process.cwd(),e);if(r.length){let s=(0,D.dirname)(t);return(0,D.resolve)(s,...r)}return t}function G(e,r,t){if(t.has(e)&&!r.has(e))throw new g(`Forbidden property access. Access to this computed property "${e}" is forbidden.`);return e}var K="object",ve=Symbol("__mutor_safe_context");function J(e){if(!e||typeof e!==K||ve in e)return e;let r=new WeakSet;function t(o,c=""){if(!o||typeof o!==K||r.has(o))return o;r.add(o);let p=Object.getPrototypeOf(o);if(p&&p!==Object.prototype&&p!==Array.prototype)throw new g(`Unsafe prototype detected at ${c||"root"}`);if(Array.isArray(o)){for(let i=0;i<o.length;i++)o[i]=t(o[i],`${c}[${i}]`);return o}if(o instanceof Map){for(let[i,u]of o.entries())typeof i===K&&t(i,`${c}.mapKey`),o.set(i,t(u,`${c}.mapValue`));return o}if(o instanceof Set){let i=new Set;for(let u of o.values())i.add(t(u,c));o.clear();for(let u of i)o.add(u);return o}let m=Object.getOwnPropertyDescriptors(o);for(let i of Object.keys(m)){let u=m[i];if(u.get||u.set)throw new g(`Getter/setter not allowed: ${c}.${i}`);let h=o[i];h&&typeof h===K&&(o[i]=t(h,`${c}.${i}`))}return o}let s=t(e);return s&&typeof s===K&&Object.defineProperty(s,ve,{value:!0,enumerable:!1,writable:!1,configurable:!1}),s}function z(e){return e.replace(/\\/g,"\\\\").replace(/`/g,"\\`").replace(/\$/g,"\\$")}function V(e,r){let s=e.slice(0,r).split(`
|
|
8
|
+
`).length,o=e.lastIndexOf(`
|
|
9
|
+
`,r-1)+1;return[s,o]}function Z(e,r){let t=e.indexOf(`
|
|
10
|
+
`,r);return e.slice(r,t===-1?void 0:t).replaceAll(" "," ")}function q(e,r){let{scope:t,forbiddenProps:s,allowedProps:o}=r;function c(a){return t.includes(a)?a:`ctx.${a}`}function p(a){switch(a.type){case 17:return"}";case 5:return a.value;case 4:return`\`${/\$\\/.test(a.value)?z(a.value):a.value}\``;case 10:return a.true?"true":"false";case 12:return"null";case 11:return"undefined";case 7:return c(a.value);case 9:return`(${p(a.expr)})`;case 2:return`${a.operator}${p(a.expr)}`;case 0:return`${p(a.left)} ${a.operator} ${p(a.right)}`;case 1:return`${p(a.condition)} ? ${p(a.left)} : ${p(a.right)}`;case 8:return i(a);case 3:return u(a);case 6:return m(a);case 13:return h(a);case 16:return"} else {";case 14:return y(a);case 15:return w(a);default:throw new g(`Unsupported expression type: ${a.type}`)}}function m(a){if(a.left.type!==7)throw{message:"Invalid usage of namespace operator.",pos:a.pos};return`namespaces.${a.left.value}.${a.right.value}`}function i(a){let b=p(a.left);if(a.bracketNotation){let d=p(a.right),O=a.optional?"?.":"";return a.right.type===4||a.right.type===5?`${b}${O}[${d}]`:`${b}${O}[validateComputedProps(${d}, allowedProps, forbiddenProps)]`}else{let d=a.right.value,O=a.optional?"?.":".";if(s.has(d)&&!o.has(d))throw{message:"Forbidden property access.",pos:a.right.pos};return`${b}${O}${d}`}}function u(a){let b=p(a.expr),d=a.optional?"?.":"",O=a.args.map(R=>p(R)).join(", ");return`${b}${d}(${O})`}function h(a){let{iterable:b,loopType:d,variable:O}=a;return`for(const ${O} ${d===1?"in":"of"} ${q(b,r)}){`}function y(a){let{condition:b}=a;return`if(${q(b,r)}){`}function w(a){let{condition:b}=a;return`}else if(${q(b,r)}){`}return p(e)}function H(e){switch(e){case 0:return"identifier";case 1:return"keyword";case 2:return"number";case 4:return"operator";case 3:return"string"}}function fe(e,r){let t=0,s=!1;function o(n,l){let f=e[t],A=e[e.length-1];if(!f)throw{message:`Unexpected end of expression. Expected ${l?`'${l}'`:`${n===0?"an":"a"} ${H(n)}`}.`,pos:A.pos+A.value.length-1};if(f.type!==n)throw{message:`Unexpected token type. Expected ${l?`'${l}'`:H(n)} but got ${H(f.type)} instead.`,pos:f.pos};if(l!==void 0&&f.value!==l)throw{message:`Unexpected token '${f?.value}'. Expected ${n===0?"an":"a"} ${H(n)} instead.`,pos:f.pos};return e[t++]}function c(){let n=e[t-1].pos,l=o(0).value,f;try{f=o(1,"in")}catch{f=o(1,"of")}let A=f.value==="in"?1:0,L=S();return{type:13,loopType:A,iterable:L,variable:l,pos:n}}function p(){let n=S();return{condition:n,pos:n.pos,type:14}}function m(){let n=e[t-1].pos;try{return o(1,"if"),{...p(),type:15,pos:n}}catch{return{type:16,pos:n}}}function i(){let n=[];if(e[t]?.type===4&&e[t]?.value===")")return n;for(n.push(S());e[t]?.type===4&&e[t]?.value===","&&e[t]?.value!==")";)t++,n.push(S());return n}function u(){let n=e[t++];if(n?.type===2)return{type:5,value:n.value,pos:n.pos};if(n?.type===3)return{type:4,value:n.value,pos:n.pos};if(n?.type===1){if(n.value==="for"&&t===1)return c();if(n.value==="true"||n.value==="false")return{type:10,true:n.value==="true",pos:n.pos};if(n.value==="undefined")return{type:11,pos:n.pos};if(n.value==="null")return{type:12,pos:n.pos};if(n.value==="end"&&e.length===1)return{type:17,pos:n.pos};if(n.value==="if"&&t===1)return p();if(n.value==="else"&&t===1)return m()}if(n?.type===0)return{type:7,value:n.value,pos:n.pos};if(n?.type===4&&n.value==="("){let l=S();return o(4,")"),{type:9,expr:l,pos:n.pos}}if(n?.type===4&&Pe.has(n.value)){let l=n.value,f=S();return{type:2,operator:l,expr:f,pos:n.pos}}throw t>e.length?{message:"Unexpected end of expression.",pos:e[e.length-1].pos}:{message:`Unexpected token '${n?.value}'.`,pos:n.pos}}function h(){let n=u();for(;e[t];){let l=e[t];if(l?.type===4&&l?.value==="("){if(t++,!s&&!r.allowFnCalls)throw{message:"Function calls are not allowed.",pos:e[t-1].pos};let f=i();o(4,")"),n={type:3,expr:n,args:f,pos:e[t-1].pos}}else if(l?.type===4&&l?.value==="?."&&e[t+1]?.type===4&&e[t+1]?.value==="("){if(t++,t++,!s&&!r.allowFnCalls)throw{message:"Function calls are not allowed.",pos:e[t-1].pos};let f=i();o(4,")"),n={type:3,expr:n,args:f,optional:!0,pos:e[t-1].pos}}else if(l?.type===4&&Ce.has(l?.value)){let f=l?.value==="::",A=l?.value==="[",L=l?.value==="?.";if(t++,f&&(e[t-2]?.type!==0||e[t]?.type!==0))throw{message:`Invalid namespaces access. Expected syntax <IDENTIFIER>::<IDENTIFIER>, but got '${e[t-2]?.value}::${e[t]?.value}' instead.`,pos:e[t]?.pos};if(f){s=!0;let C=u();n={type:6,left:n,right:C,pos:e[t-1].pos}}else if(A){let C=S();o(4,"]"),n={type:8,right:C,left:n,bracketNotation:!0,pos:e[t-1].pos}}else if(L)if(e[t]?.type===4&&e[t]?.value==="["){t++;let C=S();o(4,"]"),n={type:8,left:n,right:C,bracketNotation:!0,optional:!0,pos:e[t-1].pos}}else{let C=u();n={type:8,left:n,right:C,optional:!0,pos:e[t-1].pos}}else{let C=u();n={type:8,left:n,right:C,pos:e[t-1].pos}}}else break}return s=!1,n}function y(){let n=h();for(;e[t]?.type===4&&Re.has(e[t]?.value);){let l=e[t++].value,f=h();n={type:0,left:n,right:f,operator:l,pos:e[t-1].pos}}return n}function w(){let n=y();for(;e[t]?.type===4&&Se.has(e[t]?.value);){let l=e[t++].value,f=y();n={type:0,left:n,right:f,operator:l,pos:e[t-1].pos}}return n}function a(){let n=w();for(;e[t]?.type===4&&le.has(e[t]?.value);){let l=e[t++].value,f=w();n={type:0,left:n,right:f,operator:l,pos:e[t-1].pos}}return n}function b(){let n=a();for(;e[t]?.type===4&&le.has(e[t]?.value);){let l=e[t++].value,f=a();n={type:0,left:n,right:f,operator:l,pos:e[t-1].pos}}return n}function d(){let n=b();for(;e[t]?.type===4&&xe.has(e[t]?.value);){let l=e[t++].value,f=b();n={type:0,left:n,right:f,operator:l,pos:e[t-1].pos}}return n}function O(){let n=R();for(;e[t]?.type===4&&e[t]?.value==="||";){let l=e[t++].value,f=R();n={type:0,left:n,right:f,operator:l,pos:e[t-1].pos}}return n}function R(){let n=U();for(;e[t]?.type===4&&e[t]?.value==="&&";){let l=e[t++].value,f=U();n={type:0,left:n,right:f,operator:l,pos:e[t-1].pos}}return n}function U(){let n=d();for(;e[t]?.type===4&&e[t]?.value==="??";){let l=e[t++].value,f=d();n={type:0,left:n,right:f,operator:l,pos:e[t-1].pos}}return n}function S(){let n=O();if(e[t]?.type!==4||e[t]?.value!=="?")return n;t++;let l=S();o(4,":");let f=S();return{type:1,left:l,right:f,condition:n,pos:e[t-1].pos}}let te=S();if(t!==e.length)throw{pos:e[t].pos,message:`Unexpected end of expression.
|
|
11
|
+
Expected end of expression but found "${e[t].value}".`};return te}function de(e,{delimiters:r}){let t=`${r.openingTag}${r.whitespaceTrim}`,s=`${r.whitespaceTrim}${r.closingTag}`,o=e.startsWith(t),c=e.endsWith(s),p=e.startsWith(o?t+r.commentTag:r.openingTag+r.commentTag);if(p)return{isComment:p,leftTrim:o,rightTrim:c};let m=e.slice(o?t.length:r.openingTag.length,e.length-(c?s.length:r.closingTag.length)),i=m.trim(),u=i.startsWith("for")||i.startsWith("if")||i.startsWith("else"),h=i.startsWith("for")||i.startsWith("if"),y=i==="end",w=i.startsWith("for");return{leftTrim:o,rightTrim:c,inner:m,isBlock:u,isBlockEnd:y,hasContext:w,requiresBlockClose:h}}function me(e){let r=0,t="",s=[];function o(){let i="";if(/[a-zA-Z$_]/.test(t)){let u=r;for(;/[a-zA-Z$_0-9]/.test(e[u])&&u<e.length;)i+=e[u],u++;s.push({type:Oe.has(i)?1:0,value:i,pos:r}),r=u,t=e[r]}}function c(){if(t!=='"'&&t!=="'"&&t!=="`")return!1;let i=t,u=r,h=r+1,y="";for(;h<e.length;){let w=e[h];if(w==="\\"){if(h+1>=e.length)throw{pos:h,message:"Unexpected end of string after escape character."};y+=w,y+=e[h+1],h+=2;continue}if(w===i)break;y+=w,h++}if(h>=e.length||e[h]!==i)throw{pos:u,message:`String literal missing closing ${i}.`};return s.push({type:3,value:y,pos:u}),r=h+1,!0}function p(){if(/[0-9]/.test(t)){let i=r,u="";for(;/[0-9.oxe]/.test(e[i])&&i<e.length;)u+=e[i],i++;let h=Number(u);if(Number.isNaN(h))throw{pos:r,message:"Found invalid number literal."};s.push({type:2,value:`${h}`,pos:r}),r=i-1,t=e[r]}}function m(){let i=`${t}${e[r+1]}`;if(k.has(i)){s.push({type:4,value:i,pos:r}),r++;return}if(k.has(t)){s.push({type:4,value:t,pos:r});return}}for(;r<e.length;){if(t=e[r],p(),o(),c(),m(),!/[a-zA-Z$_0-9\s\t\r\n'"`]/.test(t)&&!k.has(t)&&!k.has(e[r-1]+t))throw{message:`Unexpected token '${t}' in expression.`,pos:r};r++}return s}function he(e,r,t){let s=[],o=[],{delimiters:c,keepOpeningTagEscapeDelimiter:p,allowFnCalls:m,allowedProps:i,forbiddenProps:u,autoEscape:h}=r,y=!1,w=0,a='let acc="";';for(;w<e.length;){let O=function(){let E=d,T=0;for(;E>=c.openingTagEscape.length&&e.slice(E-c.openingTagEscape.length,E)===c.openingTagEscape;)T++,E-=c.openingTagEscape.length;return T%2===1};var b=O;let d=e.indexOf(c.openingTag,w);if(d===-1){let E=e.slice(w);y&&(E=E.trimStart()),E&&(a+=`acc+=\`${z(E)}\`;`);break}if(O()){let E=e.slice(w,p?d+c.openingTagEscape.length+1:d-c.openingTag.length+1);y&&(E=E.trimStart(),y=!1),a+=`acc+=\`${z(E)}\`;`,p||(a+=`acc+=\`${c.openingTag}\`;`),w=d+c.openingTag.length;continue}let R=e.indexOf(c.closingTag,d);if(R===-1){let[E,T]=V(e,d),I=Z(e,T);throw new W("No closing tag found.",E,I,d,t.path)}let U=e.slice(d,R+c.closingTag.length),{inner:S,leftTrim:te,rightTrim:n,isBlock:l,isBlockEnd:f,hasContext:A,requiresBlockClose:L,isComment:C}=de(U,{delimiters:c}),j=e.slice(w,d);j&&(y&&(j=j.trimStart()),te&&(j=j.trimEnd()),j&&(a+=`acc+=\`${z(j)}\`;`)),y=!1,w=R+c.closingTag.length;try{if(!C){let E=me(S),T=fe(E,{allowFnCalls:m});if(l&&L&&A?(s.push(T.variable),o.push({type:0,pos:d})):l&&L&&!A&&o.push({type:1,pos:d}),f){let re=o.pop();if(re?.type===0&&s.pop(),re===void 0)throw{message:"Unexpected end of block",pos:d}}let I=q(T,{allowedProps:i,forbiddenProps:u,scope:s});l||f?a+=I:a+=h&&!I.startsWith("namespaces.Mutor.include")?`acc+=escapeFn(${I});`:`acc+=${I};`}n&&(y=!0)}catch(E){let{message:T,pos:I}=E,re=te?c.whitespaceTrim.length+c.openingTag.length:c.openingTag.length,Ee=d+I+re,[Be,_e]=V(e,Ee),Ue=Z(e,_e);throw new W(T,Be,Ue,Ee-_e,t.path)}}if(o.length){let d=o.pop()?.pos,[O,R]=V(e,d),U=Z(e,R);throw new W("Unclosed block detected.",O,U,d-R,t.path)}return a+="return acc;",new Function("ctx","namespaces","allowedProps","forbiddenProps","escapeFn","validateComputedProps",a)}var Q=class{constructor(r={}){this.__currentRenderedPath="";this.__includeStack=new Set;this.__cacheSize=0;this.__config={...N};this.__compiledTemplatesMap=new Map;this.__currentContext=null;this.__namespaces={...Ne,Mutor:{include:(r,t)=>{if(this.__includeStack.has(r))throw new g(`Circular include detected:
|
|
12
|
+
${Array.from(this.__includeStack).join(`
|
|
13
|
+
`)}
|
|
14
|
+
${r}`);try{return this.__includeStack.add(r),this.renderComponent(r,t??this.__currentContext)}finally{this.__includeStack.delete(r)}}}};this.addConfig(r),Object.defineProperty(this.__namespaces.Mutor,"$$context",{get:()=>this.__currentContext})}addConfig(r){let{autoEscape:t,delimiters:s,allowedProps:o,forbiddenProps:c,keepOpeningTagEscapeDelimiter:p,allowFnCalls:m,cache:i,build:u}=r;return this.__config={build:{include:new Set([...u?.include||N.build.include]),exclude:new Set([...N.build.exclude,...u?.exclude||[]])},autoEscape:t===!0?!0:t!==!1,allowedProps:o||N.allowedProps,allowFnCalls:!!m,cache:{...N.cache,...i||{}},forbiddenProps:new Set([...N.forbiddenProps,...c||[]]),keepOpeningTagEscapeDelimiter:p===!0?!0:p!==!1,delimiters:{...N.delimiters,...s||{}}},this.__config}restoreDefaultConfig(){this.__config={...N}}compile(r){return he(r,this.__config,{path:this.__currentRenderedPath||"anonymous"})}render(r,t){let s=this.__currentContext;s!==t&&(this.__currentContext=t);let o=this.compile(r)(J(t),this.__namespaces,this.__config.allowedProps,this.__config.forbiddenProps,Y,G);return this.__currentContext=s,o}renderComponent(r,t){if(!this.__compiledTemplatesMap.has(r))throw new g(`No template exists with the identifier '${r}'`);let s=this.__currentRenderedPath,o=this.__currentContext,c=this.__compiledTemplatesMap.get(r);this.__currentContext=t,this.__currentRenderedPath=r;let p=c.fn(J(t),this.__namespaces,this.__config.allowedProps,this.__config.forbiddenProps,Y,G);return this.__currentContext=o,this.__currentRenderedPath=s,p}registerComponent(r,t){let s=t.length*2+500;if(this.__cacheSize+s>this.__config.cache.maxSize&&!this.createEntrySpaceForTemplate(s))throw new g(`The template for the component '${r}' is too large. Consider increasing 'cache.maxSize' in the config`);this.__cacheSize+=t.length*2+500,this.__compiledTemplatesMap.set(r,{fn:this.compile(t),size:s})}reset(){this.__config={...N},this.__compiledTemplatesMap.clear(),this.__currentContext=null,this.__cacheSize=0}createEntrySpaceForTemplate(r){if(this.__cacheSize+r<this.__config.cache.maxSize)return!0;if(r>this.__config.cache.maxSize)return!1;let t=this.__compiledTemplatesMap.entries().next().value;if(t){let[s,o]=t;this.__compiledTemplatesMap.delete(s),this.__cacheSize-=o.size}return this.createEntrySpaceForTemplate(r)}getDiagnostics(){let r=this.__config.cache.maxSize;return{bytesUsed:this.__cacheSize,bytesMax:r,readableUsed:`${(this.__cacheSize/1024/1024).toFixed(2)} MB`,readableMax:`${(r/1024/1024).toFixed(2)} MB`,totalEntries:this.__compiledTemplatesMap.size,percentFull:r>0?Math.min(100,Math.round(this.__cacheSize/r*100)):0,avgTemplateSize:this.__compiledTemplatesMap.size>0?Math.round(this.__cacheSize/this.__compiledTemplatesMap.size):0}}};var X=class extends Q{constructor(r={}){super(r),this.__namespaces.Mutor.include=(t,s)=>{let o=_(this.__currentRenderedPath,t);if(this.__includeStack.has(o))throw new g(`Circular include detected:
|
|
15
|
+
${Array.from(this.__includeStack).join(`
|
|
16
|
+
`)}
|
|
17
|
+
${o}`);try{return this.__includeStack.add(o),this.renderFile(o,s??this.__currentContext)}finally{this.__includeStack.delete(o)}}}renderFile(r,t){let s=_(r),o,c=this.__currentContext,p=this.__currentRenderedPath;if(this.__currentContext=t,this.__currentRenderedPath=r,this.__config.cache.active&&this.__compiledTemplatesMap.has(s))o=this.__compiledTemplatesMap.get(s).fn;else{let i=(0,Te.readFileSync)(s,"utf-8");if(o=this.compile(i),this.__config.cache.active){let u=i.length*2+500;this.__cacheSize+u>this.__config.cache.maxSize?this.createEntrySpaceForTemplate(u)&&(this.__compiledTemplatesMap.set(s,{fn:o,size:u}),this.__cacheSize+=u):(this.__compiledTemplatesMap.set(s,{fn:o,size:u}),this.__cacheSize+=u)}}let m=o(J(t),this.__namespaces,this.__config.allowedProps,this.__config.forbiddenProps,Y,G);return this.__currentContext=c,this.__currentRenderedPath=p,m}async buildDir(r,t,s){let o=_(t),c=_(r);await(0,P.mkdir)(o,{recursive:!0});let p=await(0,P.readdir)(c,{withFileTypes:!0});await Promise.all(p.map(async m=>{let i=(0,B.join)(c,m.name),u=(0,B.join)(o,m.name);if(this.__config.build.exclude.has(m.name))return;if(m.isDirectory())return this.buildDir(i,u,s);let h=(0,B.extname)(i);if(this.__config.build.include.has(h)){let y=this.renderFile(i,s);await(0,P.writeFile)(u,y,"utf-8")}else return await(0,P.copyFile)(i,u)}))}async compileDir(r){let t=_(r),s=await(0,P.readdir)(t,{withFileTypes:!0});await Promise.all(s.map(async o=>{let c=(0,B.join)(t,o.name);if(this.__config.build.exclude.has(o.name))return;if(o.isDirectory())return this.compileDir(c);let p=(0,B.extname)(c);if(this.__config.build.include.has(p))try{let m=await(0,P.readFile)(c,"utf-8");this.registerComponent(c,m)}catch{}}))}};var v={Success:0,RuntimeError:1,ArgumentError:2},M=class extends Error{constructor(t,s=v.RuntimeError){super(t);this.exitCode=s;this.name="CliError"}},x=class extends M{constructor(r){super(r,v.ArgumentError),this.name="ArgumentError"}},ne=class extends M{constructor(r,t){let s=t instanceof Error?t.message:String(t);super(`could not read file '${r}': ${s}`,v.RuntimeError),this.name="FileReadError"}},oe=class extends M{constructor(r,t){let s=t instanceof Error?t.message:String(t);super(`could not write file '${r}': ${s}`,v.RuntimeError),this.name="FileWriteError"}},se=class extends M{constructor(r,t){let s=t instanceof Error?t.message:String(t);super(`failed to parse JSON in '${r}': ${s}`,v.RuntimeError),this.name="JsonParseError"}};var Ze=new Set(["compile","build","render"]),He=new Set(["--out","--data","--config"]),Qe=`Mutor.js v${be}`,ie=`
|
|
1545
18
|
Usage: mutor <command> <input> [options]
|
|
1546
19
|
|
|
1547
20
|
Commands:
|
|
@@ -1560,224 +33,13 @@ Exit codes:
|
|
|
1560
33
|
0 success
|
|
1561
34
|
1 runtime error (I/O failure, render failure, etc.)
|
|
1562
35
|
2 argument error (bad flags, missing or wrong-typed values)
|
|
1563
|
-
`.trim();
|
|
1564
|
-
function safeReadFile(filePath) {
|
|
1565
|
-
try {
|
|
1566
|
-
return (0, import_node_fs2.readFileSync)(filePath, "utf-8");
|
|
1567
|
-
} catch (err) {
|
|
1568
|
-
throw new FileReadError(filePath, err);
|
|
1569
|
-
}
|
|
1570
|
-
}
|
|
1571
|
-
function safeWriteFile(filePath, content) {
|
|
1572
|
-
try {
|
|
1573
|
-
(0, import_node_fs2.writeFileSync)(filePath, content, "utf-8");
|
|
1574
|
-
} catch (err) {
|
|
1575
|
-
throw new FileWriteError(filePath, err);
|
|
1576
|
-
}
|
|
1577
|
-
}
|
|
1578
|
-
function safeParseJsonFile(filePath) {
|
|
1579
|
-
const raw = safeReadFile(filePath);
|
|
1580
|
-
try {
|
|
1581
|
-
return JSON.parse(raw);
|
|
1582
|
-
} catch (err) {
|
|
1583
|
-
throw new JsonParseError(filePath, err);
|
|
1584
|
-
}
|
|
1585
|
-
}
|
|
1586
|
-
function assertIsFile(filePath, flag) {
|
|
1587
|
-
let stat;
|
|
1588
|
-
try {
|
|
1589
|
-
stat = (0, import_node_fs2.statSync)(filePath);
|
|
1590
|
-
} catch {
|
|
1591
|
-
throw new ArgumentError(`${flag} path '${filePath}' does not exist`);
|
|
1592
|
-
}
|
|
1593
|
-
if (!stat.isFile()) {
|
|
1594
|
-
throw new ArgumentError(`${flag} path '${filePath}' is not a file`);
|
|
1595
|
-
}
|
|
1596
|
-
}
|
|
1597
|
-
function assertIsDirectory(dirPath, flag) {
|
|
1598
|
-
let stat;
|
|
1599
|
-
try {
|
|
1600
|
-
stat = (0, import_node_fs2.statSync)(dirPath);
|
|
1601
|
-
} catch {
|
|
1602
|
-
throw new ArgumentError(`${flag} path '${dirPath}' does not exist`);
|
|
1603
|
-
}
|
|
1604
|
-
if (!stat.isDirectory()) {
|
|
1605
|
-
throw new ArgumentError(`${flag} path '${dirPath}' is not a directory`);
|
|
1606
|
-
}
|
|
1607
|
-
}
|
|
1608
|
-
function assertExtension(filePath, flag, ...extensions) {
|
|
1609
|
-
const matches = extensions.some((ext) => filePath.endsWith(ext));
|
|
1610
|
-
if (!matches) {
|
|
1611
|
-
throw new ArgumentError(
|
|
1612
|
-
`${flag} expects a file with extension ${extensions.join(" or ")} \u2014 got '${filePath}'`
|
|
1613
|
-
);
|
|
1614
|
-
}
|
|
1615
|
-
}
|
|
1616
|
-
function parseArgs(rawArgs) {
|
|
1617
|
-
if (rawArgs.length === 0 || rawArgs[0] === "--help") {
|
|
1618
|
-
console.log(USAGE);
|
|
1619
|
-
(0, import_node_process.exit)(ExitCodes.Success);
|
|
1620
|
-
}
|
|
1621
|
-
if (rawArgs[0] === "--version") {
|
|
1622
|
-
console.log(VERSION);
|
|
1623
|
-
(0, import_node_process.exit)(ExitCodes.Success);
|
|
1624
|
-
}
|
|
1625
|
-
const command = rawArgs[0];
|
|
1626
|
-
if (!COMMANDS.has(command)) {
|
|
1627
|
-
throw new ArgumentError(`unknown command '${command}'
|
|
36
|
+
`.trim();function ae(e){try{return(0,$.readFileSync)(e,"utf-8")}catch(r){throw new ne(e,r)}}function ge(e,r){try{(0,$.writeFileSync)(e,r,"utf-8")}catch(t){throw new oe(e,t)}}function ce(e){let r=ae(e);try{return JSON.parse(r)}catch(t){throw new se(e,t)}}function ee(e,r){let t;try{t=(0,$.statSync)(e)}catch{throw new x(`${r} path '${e}' does not exist`)}if(!t.isFile())throw new x(`${r} path '${e}' is not a file`)}function Xe(e,r){let t;try{t=(0,$.statSync)(e)}catch{throw new x(`${r} path '${e}' does not exist`)}if(!t.isDirectory())throw new x(`${r} path '${e}' is not a directory`)}function ye(e,r,...t){if(!t.some(o=>e.endsWith(o)))throw new x(`${r} expects a file with extension ${t.join(" or ")} \u2014 got '${e}'`)}function Ie(e){(e.length===0||e[0]==="--help")&&(console.log(ie),(0,F.exit)(v.Success)),e[0]==="--version"&&(console.log(Qe),(0,F.exit)(v.Success));let r=e[0];if(!Ze.has(r))throw new x(`unknown command '${r}'
|
|
1628
37
|
|
|
1629
|
-
${
|
|
1630
|
-
}
|
|
1631
|
-
const commandData = rawArgs[1];
|
|
1632
|
-
if (!commandData || commandData.startsWith("--")) {
|
|
1633
|
-
throw new ArgumentError(
|
|
1634
|
-
`command '${command}' requires an input path as its first argument
|
|
38
|
+
${ie}`);let t=e[1];if(!t||t.startsWith("--"))throw new x(`command '${r}' requires an input path as its first argument
|
|
1635
39
|
|
|
1636
|
-
${
|
|
1637
|
-
);
|
|
1638
|
-
}
|
|
1639
|
-
const struct = { command, commandData };
|
|
1640
|
-
for (let i = 2; i < rawArgs.length; i++) {
|
|
1641
|
-
const flag = rawArgs[i];
|
|
1642
|
-
if (!OPTIONS.has(flag)) {
|
|
1643
|
-
throw new ArgumentError(`unknown option '${flag}'
|
|
40
|
+
${ie}`);let s={command:r,commandData:t};for(let o=2;o<e.length;o++){let c=e[o];if(!He.has(c))throw new x(`unknown option '${c}'
|
|
1644
41
|
|
|
1645
|
-
${
|
|
1646
|
-
|
|
1647
|
-
|
|
1648
|
-
if (!value || value.startsWith("--")) {
|
|
1649
|
-
throw new ArgumentError(`option '${flag}' requires a value`);
|
|
1650
|
-
}
|
|
1651
|
-
struct[flag] = value;
|
|
1652
|
-
i++;
|
|
1653
|
-
}
|
|
1654
|
-
return struct;
|
|
1655
|
-
}
|
|
1656
|
-
function handleCompileCommand(mutor, args) {
|
|
1657
|
-
const inputPath = toAbsolutePath(args.commandData);
|
|
1658
|
-
assertIsFile(inputPath, "<input>");
|
|
1659
|
-
const template = safeReadFile(inputPath);
|
|
1660
|
-
const compiled = mutor.compile(template);
|
|
1661
|
-
const output = compiled.toString();
|
|
1662
|
-
if (args["--out"]) {
|
|
1663
|
-
const outPath = toAbsolutePath(args["--out"]);
|
|
1664
|
-
registerCleanup(() => {
|
|
1665
|
-
try {
|
|
1666
|
-
(0, import_node_fs2.rmSync)(outPath, { force: true });
|
|
1667
|
-
} catch {
|
|
1668
|
-
}
|
|
1669
|
-
});
|
|
1670
|
-
safeWriteFile(outPath, output);
|
|
1671
|
-
console.log(`Compiled \u2192 ${args["--out"]}`);
|
|
1672
|
-
} else {
|
|
1673
|
-
console.log(output);
|
|
1674
|
-
}
|
|
1675
|
-
}
|
|
1676
|
-
async function handleBuildCommand(mutor, args) {
|
|
1677
|
-
if (!args["--data"]) {
|
|
1678
|
-
throw new ArgumentError("'build' requires a data source via --data");
|
|
1679
|
-
}
|
|
1680
|
-
if (!args["--out"]) {
|
|
1681
|
-
throw new ArgumentError("'build' requires an output directory via --out");
|
|
1682
|
-
}
|
|
1683
|
-
const inputPath = toAbsolutePath(args.commandData);
|
|
1684
|
-
const dataPath = toAbsolutePath(args["--data"]);
|
|
1685
|
-
const outPath = toAbsolutePath(args["--out"]);
|
|
1686
|
-
assertIsDirectory(inputPath, "<input>");
|
|
1687
|
-
assertIsFile(dataPath, "--data");
|
|
1688
|
-
assertExtension(dataPath, "--data", ".json");
|
|
1689
|
-
const context = safeParseJsonFile(dataPath);
|
|
1690
|
-
let buildStarted = false;
|
|
1691
|
-
registerCleanup(() => {
|
|
1692
|
-
if (buildStarted) {
|
|
1693
|
-
console.warn("\nBuild interrupted \u2014 removing partial output...");
|
|
1694
|
-
try {
|
|
1695
|
-
(0, import_node_fs2.rmSync)(outPath, { recursive: true, force: true });
|
|
1696
|
-
} catch {
|
|
1697
|
-
}
|
|
1698
|
-
}
|
|
1699
|
-
});
|
|
1700
|
-
buildStarted = true;
|
|
1701
|
-
await mutor.buildDir(inputPath, outPath, context);
|
|
1702
|
-
buildStarted = false;
|
|
1703
|
-
console.log(`Built \u2192 ${args["--out"]}`);
|
|
1704
|
-
}
|
|
1705
|
-
async function handleRenderCommand(mutor, args) {
|
|
1706
|
-
if (!args["--data"]) {
|
|
1707
|
-
throw new ArgumentError("'render' requires a data source via --data");
|
|
1708
|
-
}
|
|
1709
|
-
const inputPath = toAbsolutePath(args.commandData);
|
|
1710
|
-
const dataPath = toAbsolutePath(args["--data"]);
|
|
1711
|
-
assertIsFile(inputPath, "<input>");
|
|
1712
|
-
assertIsFile(dataPath, "--data");
|
|
1713
|
-
assertExtension(dataPath, "--data", ".json");
|
|
1714
|
-
const context = safeParseJsonFile(dataPath);
|
|
1715
|
-
const template = safeReadFile(inputPath);
|
|
1716
|
-
const output = mutor.render(template, context);
|
|
1717
|
-
if (args["--out"]) {
|
|
1718
|
-
const outPath = toAbsolutePath(args["--out"]);
|
|
1719
|
-
registerCleanup(() => {
|
|
1720
|
-
try {
|
|
1721
|
-
(0, import_node_fs2.rmSync)(outPath, { force: true });
|
|
1722
|
-
} catch {
|
|
1723
|
-
}
|
|
1724
|
-
});
|
|
1725
|
-
safeWriteFile(outPath, output);
|
|
1726
|
-
console.log(`Rendered \u2192 ${args["--out"]}`);
|
|
1727
|
-
} else {
|
|
1728
|
-
console.log(output);
|
|
1729
|
-
}
|
|
1730
|
-
}
|
|
1731
|
-
var cleanupFn = null;
|
|
1732
|
-
function registerCleanup(fn) {
|
|
1733
|
-
cleanupFn = fn;
|
|
1734
|
-
}
|
|
1735
|
-
function handleSignal(signal) {
|
|
1736
|
-
console.warn(`
|
|
1737
|
-
Received ${signal}.`);
|
|
1738
|
-
cleanupFn?.();
|
|
1739
|
-
(0, import_node_process.exit)(ExitCodes.RuntimeError);
|
|
1740
|
-
}
|
|
1741
|
-
process.on("SIGINT", () => handleSignal("SIGINT"));
|
|
1742
|
-
process.on("SIGTERM", () => handleSignal("SIGTERM"));
|
|
1743
|
-
async function main() {
|
|
1744
|
-
const args = parseArgs(import_node_process.argv.slice(2));
|
|
1745
|
-
const mutor = new Mutor2();
|
|
1746
|
-
if (args["--config"]) {
|
|
1747
|
-
const configPath = toAbsolutePath(args["--config"]);
|
|
1748
|
-
assertIsFile(configPath, "--config");
|
|
1749
|
-
assertExtension(configPath, "--config", ".json");
|
|
1750
|
-
const config = safeParseJsonFile(configPath);
|
|
1751
|
-
mutor.addConfig(config);
|
|
1752
|
-
}
|
|
1753
|
-
switch (args.command) {
|
|
1754
|
-
case "compile":
|
|
1755
|
-
handleCompileCommand(mutor, args);
|
|
1756
|
-
break;
|
|
1757
|
-
case "build":
|
|
1758
|
-
await handleBuildCommand(mutor, args);
|
|
1759
|
-
break;
|
|
1760
|
-
case "render":
|
|
1761
|
-
await handleRenderCommand(mutor, args);
|
|
1762
|
-
break;
|
|
1763
|
-
}
|
|
1764
|
-
}
|
|
1765
|
-
main().catch((err) => {
|
|
1766
|
-
if (err instanceof CliError) {
|
|
1767
|
-
console.error(`error: ${err.message}`);
|
|
1768
|
-
(0, import_node_process.exit)(err.exitCode);
|
|
1769
|
-
}
|
|
1770
|
-
console.error("unexpected error:", err);
|
|
1771
|
-
(0, import_node_process.exit)(ExitCodes.RuntimeError);
|
|
1772
|
-
});
|
|
1773
|
-
// Annotate the CommonJS export names for ESM import in node:
|
|
1774
|
-
0 && (module.exports = {
|
|
1775
|
-
handleBuildCommand,
|
|
1776
|
-
handleCompileCommand,
|
|
1777
|
-
handleRenderCommand,
|
|
1778
|
-
parseArgs,
|
|
1779
|
-
safeParseJsonFile,
|
|
1780
|
-
safeReadFile,
|
|
1781
|
-
safeWriteFile
|
|
1782
|
-
});
|
|
42
|
+
${ie}`);let p=e[o+1];if(!p||p.startsWith("--"))throw new x(`option '${c}' requires a value`);s[c]=p,o++}return s}function Me(e,r){let t=_(r.commandData);ee(t,"<input>");let s=ae(t),c=e.compile(s).toString();if(r["--out"]){let p=_(r["--out"]);we(()=>{try{(0,$.rmSync)(p,{force:!0})}catch{}}),ge(p,c),console.log(`Compiled \u2192 ${r["--out"]}`)}else console.log(c)}async function Fe(e,r){if(!r["--data"])throw new x("'build' requires a data source via --data");if(!r["--out"])throw new x("'build' requires an output directory via --out");let t=_(r.commandData),s=_(r["--data"]),o=_(r["--out"]);Xe(t,"<input>"),ee(s,"--data"),ye(s,"--data",".json");let c=ce(s),p=!1;we(()=>{if(p){console.warn(`
|
|
43
|
+
Build interrupted \u2014 removing partial output...`);try{(0,$.rmSync)(o,{recursive:!0,force:!0})}catch{}}}),p=!0,await e.buildDir(t,o,c),p=!1,console.log(`Built \u2192 ${r["--out"]}`)}async function je(e,r){if(!r["--data"])throw new x("'render' requires a data source via --data");let t=_(r.commandData),s=_(r["--data"]);ee(t,"<input>"),ee(s,"--data"),ye(s,"--data",".json");let o=ce(s),c=ae(t),p=e.render(c,o);if(r["--out"]){let m=_(r["--out"]);we(()=>{try{(0,$.rmSync)(m,{force:!0})}catch{}}),ge(m,p),console.log(`Rendered \u2192 ${r["--out"]}`)}else console.log(p)}var De=null;function we(e){De=e}function ze(e){console.warn(`
|
|
44
|
+
Received ${e}.`),De?.(),(0,F.exit)(v.RuntimeError)}process.on("SIGINT",()=>ze("SIGINT"));process.on("SIGTERM",()=>ze("SIGTERM"));async function et(){let e=Ie(F.argv.slice(2)),r=new X;if(e["--config"]){let t=_(e["--config"]);ee(t,"--config"),ye(t,"--config",".json");let s=ce(t);r.addConfig(s)}switch(e.command){case"compile":Me(r,e);break;case"build":await Fe(r,e);break;case"render":await je(r,e);break}}et().catch(e=>{e instanceof M&&(console.error(`error: ${e.message}`),(0,F.exit)(e.exitCode)),console.error("unexpected error:",e),(0,F.exit)(v.RuntimeError)});0&&(module.exports={handleBuildCommand,handleCompileCommand,handleRenderCommand,parseArgs,safeParseJsonFile,safeReadFile,safeWriteFile});
|
|
1783
45
|
//# sourceMappingURL=cli.cjs.map
|