@samuelbines/nunjucks 0.0.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.
Files changed (66) hide show
  1. package/LICENSE +26 -0
  2. package/README.md +55 -0
  3. package/dist/scripts/smoke.d.ts +1 -0
  4. package/dist/scripts/smoke.js +95 -0
  5. package/dist/src/compiler.d.ts +12 -0
  6. package/dist/src/compiler.js +1050 -0
  7. package/dist/src/environment.d.ts +103 -0
  8. package/dist/src/environment.js +621 -0
  9. package/dist/src/express-app.d.ts +2 -0
  10. package/dist/src/express-app.js +33 -0
  11. package/dist/src/filters.d.ts +44 -0
  12. package/dist/src/filters.js +424 -0
  13. package/dist/src/globals.d.ts +14 -0
  14. package/dist/src/globals.js +342 -0
  15. package/dist/src/index.d.ts +28 -0
  16. package/dist/src/index.js +116 -0
  17. package/dist/src/interpreter.d.ts +16 -0
  18. package/dist/src/interpreter.js +489 -0
  19. package/dist/src/lexer.d.ts +72 -0
  20. package/dist/src/lexer.js +480 -0
  21. package/dist/src/lib.d.ts +74 -0
  22. package/dist/src/lib.js +237 -0
  23. package/dist/src/loader.d.ts +80 -0
  24. package/dist/src/loader.js +175 -0
  25. package/dist/src/nodes.d.ts +362 -0
  26. package/dist/src/nodes.js +894 -0
  27. package/dist/src/parser.d.ts +66 -0
  28. package/dist/src/parser.js +1068 -0
  29. package/dist/src/precompile.d.ts +15 -0
  30. package/dist/src/precompile.js +108 -0
  31. package/dist/src/runtime.d.ts +33 -0
  32. package/dist/src/runtime.js +314 -0
  33. package/dist/src/transformer.d.ts +3 -0
  34. package/dist/src/transformer.js +161 -0
  35. package/dist/src/types.d.ts +27 -0
  36. package/dist/src/types.js +2 -0
  37. package/dist/tests/compiler.test.d.ts +1 -0
  38. package/dist/tests/compiler.test.js +201 -0
  39. package/dist/tests/enviornment.test.d.ts +1 -0
  40. package/dist/tests/enviornment.test.js +279 -0
  41. package/dist/tests/express.test.d.ts +1 -0
  42. package/dist/tests/express.test.js +86 -0
  43. package/dist/tests/filters.test.d.ts +13 -0
  44. package/dist/tests/filters.test.js +286 -0
  45. package/dist/tests/globals.test.d.ts +1 -0
  46. package/dist/tests/globals.test.js +579 -0
  47. package/dist/tests/interpreter.test.d.ts +1 -0
  48. package/dist/tests/interpreter.test.js +208 -0
  49. package/dist/tests/lexer.test.d.ts +1 -0
  50. package/dist/tests/lexer.test.js +249 -0
  51. package/dist/tests/lib.test.d.ts +1 -0
  52. package/dist/tests/lib.test.js +236 -0
  53. package/dist/tests/loader.test.d.ts +1 -0
  54. package/dist/tests/loader.test.js +301 -0
  55. package/dist/tests/nodes.test.d.ts +1 -0
  56. package/dist/tests/nodes.test.js +137 -0
  57. package/dist/tests/parser.test.d.ts +1 -0
  58. package/dist/tests/parser.test.js +294 -0
  59. package/dist/tests/precompile.test.d.ts +1 -0
  60. package/dist/tests/precompile.test.js +224 -0
  61. package/dist/tests/runtime.test.d.ts +1 -0
  62. package/dist/tests/runtime.test.js +237 -0
  63. package/dist/tests/transformer.test.d.ts +1 -0
  64. package/dist/tests/transformer.test.js +125 -0
  65. package/dist/tsconfig.tsbuildinfo +1 -0
  66. package/package.json +59 -0
@@ -0,0 +1,424 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.nl2br = exports.lower = exports.last = exports.first = exports.trim = exports.int = exports.isFloat = exports.isInt = exports.wordcount = exports.isUpper = exports.upper = exports.capitalize = exports.title = exports.string = exports.sort = exports.select = exports.reject = exports.random = exports.safe = exports.e = exports._escape = exports.d = void 0;
4
+ exports.batch = batch;
5
+ exports.center = center;
6
+ exports.default_ = default_;
7
+ exports.dictsort = dictsort;
8
+ exports.forceescape = forceescape;
9
+ exports.groupby = groupby;
10
+ exports.indent = indent;
11
+ exports.join = join;
12
+ exports.length = length;
13
+ exports.list = list;
14
+ exports.rejectattr = rejectattr;
15
+ exports.selectattr = selectattr;
16
+ exports.replace = replace;
17
+ exports.reverse = reverse;
18
+ exports.round = round;
19
+ exports.slice = slice;
20
+ exports.sum = sum;
21
+ exports.striptags = striptags;
22
+ exports.truncate = truncate;
23
+ exports.urlencode = urlencode;
24
+ exports.urlize = urlize;
25
+ exports.float = float;
26
+ //TODO: Sun 4th Jan 2026 [Improve this code]
27
+ const lib_1 = require("./lib");
28
+ const runtime_1 = require("./runtime");
29
+ const normalize = (value, defaultValue = '') => !value ? defaultValue : value;
30
+ function batch(arr, lineCount, fillWith) {
31
+ let i = 0;
32
+ const res = [];
33
+ let tmp = [];
34
+ for (i < arr?.length; i++;) {
35
+ if (i % lineCount === 0 && tmp?.length) {
36
+ res?.push(tmp);
37
+ tmp = [];
38
+ }
39
+ tmp?.push(arr[i]);
40
+ }
41
+ if (tmp?.length) {
42
+ if (fillWith) {
43
+ for (i = tmp?.length; i < lineCount; i++) {
44
+ tmp?.push(fillWith);
45
+ }
46
+ }
47
+ res?.push(tmp);
48
+ }
49
+ return res;
50
+ }
51
+ function center(str = '', width = 80) {
52
+ if (str?.length >= width)
53
+ return str;
54
+ const spaces = width - str?.length;
55
+ const pre = (0, lib_1.repeat)(' ', spaces / 2 - (spaces % 2));
56
+ const post = (0, lib_1.repeat)(' ', spaces / 2);
57
+ return (0, runtime_1.copySafeness)(str, pre + str + post);
58
+ }
59
+ function default_(val, def, bool = false) {
60
+ if (bool)
61
+ return def || val;
62
+ return val || def;
63
+ }
64
+ exports.d = default_;
65
+ function dictsort(val, caseSensitive, by = 'value') {
66
+ if (!(0, lib_1.isObject)(val)) {
67
+ throw (0, lib_1.TemplateError)('dictsort filter: val must be an object');
68
+ }
69
+ let array = [];
70
+ // deliberately include properties from the object's prototype
71
+ for (let k in val) {
72
+ // @ts-ignore
73
+ array?.push([k, val[k]]);
74
+ }
75
+ let si;
76
+ if (by === undefined || by === 'key') {
77
+ si = 0;
78
+ }
79
+ else if (by === 'value') {
80
+ si = 1;
81
+ }
82
+ else {
83
+ throw (0, lib_1.TemplateError)('dictsort filter: You can only sort by either key or value');
84
+ }
85
+ array.sort((t1, t2) => {
86
+ var a = t1[si];
87
+ var b = t2[si];
88
+ if (!caseSensitive) {
89
+ if ((0, lib_1.isString)(a)) {
90
+ a = a.toUpperCase();
91
+ }
92
+ if ((0, lib_1.isString)(b)) {
93
+ b = b.toUpperCase();
94
+ }
95
+ }
96
+ return a > b ? 1 : a === b ? 0 : -1; // eslint-disable-line no-nested-ternary
97
+ });
98
+ return array;
99
+ }
100
+ const _escape = (str) => (0, runtime_1.markSafe)((0, lib_1.escape)(str.toString()));
101
+ exports._escape = _escape;
102
+ exports.e = lib_1.escape;
103
+ const safe = (str = '') => (0, runtime_1.markSafe)(str.toString());
104
+ exports.safe = safe;
105
+ function forceescape(str) {
106
+ str = str === null || str === undefined ? '' : str;
107
+ return (0, runtime_1.markSafe)((0, lib_1.escape)(str.toString()));
108
+ }
109
+ function groupby(arr, attr) {
110
+ return (0, lib_1.groupBy)(arr, attr, this.env.throwOnUndefined);
111
+ }
112
+ function indent(str = '', width = 4, indentfirst) {
113
+ if (str === '') {
114
+ return '';
115
+ }
116
+ // let res = '';
117
+ const lines = str.split('\n');
118
+ const sp = (0, lib_1.repeat)(' ', width);
119
+ const res = lines
120
+ .map((l, i) => {
121
+ return i === 0 && !indentfirst ? l : `${sp}${l}`;
122
+ })
123
+ .join('\n');
124
+ return (0, runtime_1.copySafeness)(str, res);
125
+ }
126
+ function join(arr, del = '', attr) {
127
+ if (attr)
128
+ arr = arr.map((v) => v[attr]);
129
+ return arr.join(del);
130
+ }
131
+ function length(str) {
132
+ if (Array.isArray(str) || (0, lib_1.isString)(str))
133
+ return str?.length;
134
+ if ((0, lib_1.isObject)(str))
135
+ Object.keys(str)?.length;
136
+ if (str instanceof Map || str instanceof Set)
137
+ return str.size;
138
+ return 0;
139
+ }
140
+ function list(val) {
141
+ if ((0, lib_1.isString)(val)) {
142
+ return val.split('');
143
+ }
144
+ else if ((0, lib_1.isObject)(val)) {
145
+ return Object.entries(val || {}).map(([key, value]) => ({ key, value }));
146
+ }
147
+ else if (Array.isArray(val)) {
148
+ return val;
149
+ }
150
+ throw (0, lib_1.TemplateError)('list filter: type not iterable'); //TODO: maybe error isn't useful here
151
+ }
152
+ const random = (arr) => arr[Math.floor(Math.random() * arr?.length)];
153
+ exports.random = random;
154
+ function getSelectOrReject(expectedTestResult = false) {
155
+ return function filter(arr, testName = 'truthy', secondArg) {
156
+ const context = this;
157
+ const test = context.env.getTest(testName);
158
+ return (0, lib_1.toArray)(arr).filter(function examineTestResult(item) {
159
+ return test.call(context, item, secondArg) === expectedTestResult;
160
+ });
161
+ };
162
+ }
163
+ exports.reject = getSelectOrReject();
164
+ exports.select = getSelectOrReject(true);
165
+ function rejectattr(arr, attr) {
166
+ return arr.filter((item) => !item[attr]);
167
+ }
168
+ function selectattr(arr, attr) {
169
+ return arr.filter((item) => !!item[attr]);
170
+ }
171
+ function replace(str, old, new_, maxCount) {
172
+ var originalStr = str;
173
+ if (old instanceof RegExp) {
174
+ if ((0, lib_1.isString)(str))
175
+ return str.replace(old, new_);
176
+ }
177
+ if (typeof maxCount === 'undefined') {
178
+ maxCount = -1;
179
+ }
180
+ let res = ''; // Output
181
+ if ((0, exports.isFloat)(old)) {
182
+ old = '' + old;
183
+ }
184
+ else if (!(0, lib_1.isString)(old)) {
185
+ return str;
186
+ }
187
+ if ((0, exports.isFloat)(str)) {
188
+ str = '' + str;
189
+ }
190
+ if (!(0, lib_1.isString)(str)) {
191
+ return str;
192
+ }
193
+ // ShortCircuits
194
+ if (old === '') {
195
+ res = new_ + str.split('').join(new_) + new_;
196
+ return (0, runtime_1.copySafeness)(str, res);
197
+ }
198
+ let nextIndex = str.indexOf(old);
199
+ if (maxCount === 0 || nextIndex === -1) {
200
+ return str;
201
+ }
202
+ let pos = 0;
203
+ let count = 0; // # of replacements made
204
+ while (nextIndex > -1 && (maxCount === -1 || count < maxCount)) {
205
+ // Grab the next chunk of src string and add it with the
206
+ // replacement, to the result
207
+ res += str.substring(pos, nextIndex) + new_;
208
+ // Increment our pointer in the src string
209
+ pos = nextIndex + old?.length;
210
+ count++;
211
+ // See if there are any more replacements to be made
212
+ nextIndex = str.indexOf(old, pos);
213
+ }
214
+ // We've either reached the end, or done the max # of
215
+ // replacements, tack on any remaining string
216
+ if (pos < str?.length) {
217
+ res += str.substring(pos);
218
+ }
219
+ return (0, runtime_1.copySafeness)(originalStr, res);
220
+ }
221
+ function reverse(val = []) {
222
+ let arr = val;
223
+ if ((0, lib_1.isString)(val)) {
224
+ arr = list(val);
225
+ arr.reverse();
226
+ return (0, runtime_1.copySafeness)(val, arr.join(''));
227
+ }
228
+ arr.reverse();
229
+ return arr;
230
+ }
231
+ function round(val, precision = 0, method = 'round') {
232
+ const factor = Math.pow(10, precision);
233
+ return ((() => {
234
+ if (method === 'ceil') {
235
+ return Math.ceil;
236
+ }
237
+ else if (method === 'floor') {
238
+ return Math.floor;
239
+ }
240
+ return Math.round;
241
+ })()(val * factor) / factor);
242
+ }
243
+ function slice(arr, slices, fillWith = false) {
244
+ const sliceLength = Math.floor(arr?.length / slices);
245
+ const extra = arr?.length % slices;
246
+ const res = [];
247
+ let offset = 0;
248
+ for (let i = 0; i < slices; i++) {
249
+ const start = offset + i * sliceLength;
250
+ if (i < extra) {
251
+ offset++;
252
+ }
253
+ const end = offset + (i + 1) * sliceLength;
254
+ const currSlice = arr.slice(start, end);
255
+ if (fillWith && i >= extra) {
256
+ currSlice?.push(fillWith);
257
+ }
258
+ res?.push(currSlice);
259
+ }
260
+ return res;
261
+ }
262
+ function sum(arr, attr, start = 0) {
263
+ if (attr) {
264
+ arr = arr.map((v) => v[attr]);
265
+ }
266
+ return start + arr.reduce((a, b) => a + b, 0);
267
+ }
268
+ exports.sort = (0, runtime_1.makeMacro)(['value', 'reverse', 'case_sensitive', 'attribute'], [], function sortFilter(arr, reversed = false, caseSens = false, attr) {
269
+ // Copy it
270
+ let array = arr.map((v) => v);
271
+ let getAttribute = (0, lib_1.getAttrGetter)(attr);
272
+ array.sort((a, b) => {
273
+ let x = attr ? getAttribute(a) : a;
274
+ let y = attr ? getAttribute(b) : b;
275
+ if (this.env.throwOnUndefined &&
276
+ attr &&
277
+ (x === undefined || y === undefined)) {
278
+ throw new TypeError(`sort: attribute "${attr}" resolved to undefined`);
279
+ }
280
+ if (!caseSens && (0, lib_1.isString)(x) && (0, lib_1.isString)(y)) {
281
+ x = (0, exports.lower)(x);
282
+ y = (0, exports.lower)(y);
283
+ }
284
+ if (x < y) {
285
+ return reversed ? 1 : -1;
286
+ }
287
+ else if (x > y) {
288
+ return reversed ? -1 : 1;
289
+ }
290
+ else {
291
+ return 0;
292
+ }
293
+ });
294
+ return array;
295
+ });
296
+ const string = (obj) => (0, runtime_1.copySafeness)(obj, obj);
297
+ exports.string = string;
298
+ function striptags(input = '', preserveLinebreaks = true) {
299
+ let tags = /<\/?([a-z][a-z0-9]*)\b[^>]*>|<!--[\s\S]*?-->/gi;
300
+ let trimmedInput = (0, exports.trim)(input.replace(tags, ''));
301
+ let res = '';
302
+ if (preserveLinebreaks) {
303
+ res = trimmedInput
304
+ .replace(/^ +| +$/gm, '') // remove leading and trailing spaces
305
+ .replace(/ +/g, ' ') // squash adjacent spaces
306
+ .replace(/(\r\n)/g, '\n') // normalize linebreaks (CRLF -> LF)
307
+ .replace(/\n\n\n+/g, '\n\n'); // squash abnormal adjacent linebreaks
308
+ }
309
+ else {
310
+ res = trimmedInput.replace(/\s+/gi, ' ');
311
+ }
312
+ return (0, runtime_1.copySafeness)(input, res);
313
+ }
314
+ const title = (str = '') => {
315
+ let words = str.split(' ').map((word) => (0, exports.capitalize)(word));
316
+ return (0, runtime_1.copySafeness)(str, words.join(' '));
317
+ };
318
+ exports.title = title;
319
+ function truncate(input = '', length = 255, killwords = false, end) {
320
+ let orig = input;
321
+ if (input?.length <= length) {
322
+ return input;
323
+ }
324
+ if (killwords) {
325
+ input = input.substring(0, length);
326
+ }
327
+ else {
328
+ let idx = input.lastIndexOf(' ', length);
329
+ if (idx === -1) {
330
+ idx = length;
331
+ }
332
+ input = input.substring(0, idx);
333
+ }
334
+ input += end || '...';
335
+ return (0, runtime_1.copySafeness)(orig, input);
336
+ }
337
+ // TODO: Pretty sure I don't need this
338
+ const capitalize = (str = '') => {
339
+ const ret = (0, exports.lower)(str);
340
+ return (0, runtime_1.copySafeness)(str, ret.charAt(0).toUpperCase() + ret.slice(1));
341
+ };
342
+ exports.capitalize = capitalize;
343
+ const upper = (str = '') => str.toUpperCase();
344
+ exports.upper = upper;
345
+ const isUpper = (str) => str.toUpperCase() === str;
346
+ exports.isUpper = isUpper;
347
+ function urlencode(obj) {
348
+ var enc = encodeURIComponent;
349
+ if ((0, lib_1.isString)(obj)) {
350
+ return enc(obj);
351
+ }
352
+ let keyvals = Array.isArray(obj) ? obj : Object.keys(obj);
353
+ lib_1.p.log(keyvals);
354
+ return keyvals.map(([k, v]) => `${enc(k)}=${enc(v)}`).join('&');
355
+ }
356
+ // For the jinja regexp, see
357
+ // https://github.com/mitsuhiko/jinja2/blob/f15b814dcba6aa12bc74d1f7d0c881d55f7126be/jinja2/utils.py#L20-L23
358
+ const puncRe = /^(?:\(|<|&lt;)?(.*?)(?:\.|,|\)|\n|&gt;)?$/;
359
+ // from http://blog.gerv.net/2011/05/html5_email_address_regexp/
360
+ const emailRe = /^[\w.!#$%&'*+\-\/=?\^`{|}~]+@[a-z\d\-]+(\.[a-z\d\-]+)+$/i;
361
+ const httpHttpsRe = /^https?:\/\/.*$/;
362
+ const wwwRe = /^www\./;
363
+ const tldRe = /\.(?:org|net|com)(?:\:|\/|$)/;
364
+ function urlize(str, length, nofollow) {
365
+ if (isNaN(length)) {
366
+ length = Infinity;
367
+ }
368
+ const noFollowAttr = nofollow === true ? ' rel="nofollow"' : '';
369
+ const words = str
370
+ .split(/(\s+)/)
371
+ .filter((word) => {
372
+ // If the word has no length, bail. This can happen for str with
373
+ // trailing whitespace.
374
+ return word && word?.length;
375
+ })
376
+ .map((word) => {
377
+ var matches = word.match(puncRe);
378
+ var possibleUrl = matches ? matches[1] : word;
379
+ var shortUrl = possibleUrl.substr(0, length);
380
+ // url that starts with http or https
381
+ if (httpHttpsRe.test(possibleUrl)) {
382
+ return `<a href="${possibleUrl}"${noFollowAttr}>${shortUrl}</a>`;
383
+ }
384
+ // url that starts with www.
385
+ if (wwwRe.test(possibleUrl)) {
386
+ return `<a href="http://${possibleUrl}"${noFollowAttr}>${shortUrl}</a>`;
387
+ }
388
+ // an email address of the form username@domain.tld
389
+ if (emailRe.test(possibleUrl)) {
390
+ return `<a href="mailto:${possibleUrl}">${possibleUrl}</a>`;
391
+ }
392
+ // url that ends in .com, .org or .net that is not an email address
393
+ if (tldRe.test(possibleUrl)) {
394
+ return `<a href="http://${possibleUrl}"${noFollowAttr}>${shortUrl}</a>`;
395
+ }
396
+ return word;
397
+ });
398
+ return words.join('');
399
+ }
400
+ const wordcount = (str = '') => str.match(/\w+/g)?.length || null;
401
+ exports.wordcount = wordcount;
402
+ function float(val, def) {
403
+ const res = parseFloat(val);
404
+ return isNaN(res) ? def : res;
405
+ }
406
+ const isInt = (n) => Number(n) === n && n % 1 === 0;
407
+ exports.isInt = isInt;
408
+ const isFloat = (n) => Number(n) === n && n % 1 !== 0;
409
+ exports.isFloat = isFloat;
410
+ exports.int = (0, runtime_1.makeMacro)(['value', 'default', 'base'], [], function doInt(value, defaultValue = 0, base = 10) {
411
+ var res = parseInt(value, base);
412
+ return isNaN(res) ? defaultValue : res;
413
+ });
414
+ const trim = (str) => (0, runtime_1.copySafeness)(str, str.replace(/^\s*|\s*$/g, ''));
415
+ exports.trim = trim;
416
+ // Aliases
417
+ const first = (arr = []) => arr[0];
418
+ exports.first = first;
419
+ const last = (arr = []) => arr[arr?.length - 1];
420
+ exports.last = last;
421
+ const lower = (str) => normalize(str).toLowerCase();
422
+ exports.lower = lower;
423
+ const nl2br = (str = '') => str ? (0, runtime_1.copySafeness)(str, str.replace(/\r\n|\n/g, '<br />\n')) : '';
424
+ exports.nl2br = nl2br;
@@ -0,0 +1,14 @@
1
+ export interface IGlobals {
2
+ range(start: number, stop: number, step?: number): any[];
3
+ cycler(...items: any[]): {
4
+ readonly current: any;
5
+ reset(): void;
6
+ next(): any;
7
+ };
8
+ joiner(sep?: string): () => string;
9
+ }
10
+ export declare function globals(): IGlobals;
11
+ export declare function precompileGlobal(templates: any[], opts?: {
12
+ isFunction: boolean;
13
+ }): string;
14
+ export declare function installCompat(): () => void;