@samuelbines/nunjucks 0.0.3 → 0.0.5

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.
@@ -1,237 +0,0 @@
1
- "use strict";
2
- var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
- if (k2 === undefined) k2 = k;
4
- var desc = Object.getOwnPropertyDescriptor(m, k);
5
- if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
- desc = { enumerable: true, get: function() { return m[k]; } };
7
- }
8
- Object.defineProperty(o, k2, desc);
9
- }) : (function(o, m, k, k2) {
10
- if (k2 === undefined) k2 = k;
11
- o[k2] = m[k];
12
- }));
13
- var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
- Object.defineProperty(o, "default", { enumerable: true, value: v });
15
- }) : function(o, v) {
16
- o["default"] = v;
17
- });
18
- var __importStar = (this && this.__importStar) || (function () {
19
- var ownKeys = function(o) {
20
- ownKeys = Object.getOwnPropertyNames || function (o) {
21
- var ar = [];
22
- for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
- return ar;
24
- };
25
- return ownKeys(o);
26
- };
27
- return function (mod) {
28
- if (mod && mod.__esModule) return mod;
29
- var result = {};
30
- if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
- __setModuleDefault(result, mod);
32
- return result;
33
- };
34
- })();
35
- Object.defineProperty(exports, "__esModule", { value: true });
36
- // test/runtime.test.ts
37
- const vitest_1 = require("vitest");
38
- // adjust paths to your repo layout
39
- const runtime_1 = require("../src/runtime");
40
- const runtime = __importStar(require("../src/runtime"));
41
- (0, vitest_1.describe)('runtime.ts', () => {
42
- (0, vitest_1.describe)('Frame', () => {
43
- (0, vitest_1.it)('set/get supports dotted paths (auto-creates nested objects)', () => {
44
- const f = new runtime_1.Frame();
45
- f.set('user.profile.name', 'Sam', false);
46
- (0, vitest_1.expect)(f.variables.user.profile.name).toBe('Sam');
47
- (0, vitest_1.expect)(f.get('user')).toEqual({ profile: { name: 'Sam' } });
48
- });
49
- (0, vitest_1.it)('push/pop sets parent and topLevel semantics', () => {
50
- const root = new runtime_1.Frame(null);
51
- (0, vitest_1.expect)(root.parent).toBe(null);
52
- (0, vitest_1.expect)(root.topLevel).toBe(true);
53
- const child = root.push(false);
54
- (0, vitest_1.expect)(child.parent).toBe(root);
55
- (0, vitest_1.expect)(child.topLevel).toBe(false);
56
- (0, vitest_1.expect)(child.pop()).toBe(root);
57
- });
58
- (0, vitest_1.it)('resolveUp writes into an ancestor frame when it already exists', () => {
59
- const parent = new runtime_1.Frame(null);
60
- parent.set('x', 1, false);
61
- const child = parent.push(false);
62
- child.set('x', 2, true); // resolveUp
63
- console.log('Parent is', parent, child, parent.get('x'), child.get('x'));
64
- (0, vitest_1.expect)(parent.get('x')).toBe(2);
65
- (0, vitest_1.expect)(child.get('x')).toBe(null); // because it wrote to parent
66
- });
67
- (0, vitest_1.it)('isolateWrites prevents resolveUp from writing into parent', () => {
68
- const parent = new runtime_1.Frame(null);
69
- parent.set('x', 1, false);
70
- const child = new runtime_1.Frame(parent, true); // isolateWrites=true
71
- child.set('x', 2, true);
72
- (0, vitest_1.expect)(parent.get('x')).toBe(1);
73
- (0, vitest_1.expect)(child.get('x')).toBe(2);
74
- });
75
- (0, vitest_1.it)('get returns null for unknown names', () => {
76
- const f = new runtime_1.Frame();
77
- (0, vitest_1.expect)(f.get('missing')).toBe(null);
78
- });
79
- });
80
- (0, vitest_1.describe)('ensureDefined', () => {
81
- (0, vitest_1.it)('throws TemplateError for null/undefined and increments lineno/colno', () => {
82
- (0, vitest_1.expect)(() => (0, runtime_1.ensureDefined)(undefined, 4, 9)).toThrowError();
83
- try {
84
- (0, runtime_1.ensureDefined)(null, 4, 9);
85
- }
86
- catch (e) {
87
- // Your TemplateError likely stores lineno/colno; we at least verify message and that it references +1
88
- (0, vitest_1.expect)(String(e.message || e)).toContain('attempted to output null or undefined value');
89
- // if TemplateError attaches these, assert them (won’t fail if absent)
90
- if ('lineno' in e)
91
- (0, vitest_1.expect)(e.lineno).toBe(5);
92
- if ('colno' in e)
93
- (0, vitest_1.expect)(e.colno).toBe(10);
94
- }
95
- });
96
- (0, vitest_1.it)('returns the value if defined', () => {
97
- (0, vitest_1.expect)((0, runtime_1.ensureDefined)(0)).toBe(0);
98
- (0, vitest_1.expect)((0, runtime_1.ensureDefined)('')).toBe('');
99
- (0, vitest_1.expect)((0, runtime_1.ensureDefined)(false)).toBe(false);
100
- });
101
- });
102
- (0, vitest_1.describe)('memberLookup', () => {
103
- (0, vitest_1.it)('returns undefined for null/undefined obj', () => {
104
- (0, vitest_1.expect)((0, runtime_1.memberLookup)(null, 'x')).toBe(undefined);
105
- (0, vitest_1.expect)((0, runtime_1.memberLookup)(undefined, 'x')).toBe(undefined);
106
- });
107
- (0, vitest_1.it)('returns property value for non-functions', () => {
108
- (0, vitest_1.expect)((0, runtime_1.memberLookup)({ a: 123 }, 'a')).toBe(123);
109
- });
110
- (0, vitest_1.it)('wraps functions so that "this" is bound to the object', () => {
111
- const obj = {
112
- x: 3,
113
- inc(n) {
114
- // @ts-ignore
115
- return this.x + n;
116
- },
117
- };
118
- const fn = (0, runtime_1.memberLookup)(obj, 'inc');
119
- (0, vitest_1.expect)(typeof fn).toBe('function');
120
- (0, vitest_1.expect)(fn(4)).toBe(7);
121
- });
122
- });
123
- (0, vitest_1.describe)('suppressValue', () => {
124
- (0, vitest_1.it)('escapes when autoescape=true, returns raw when false', () => {
125
- const val = '<b>&</b>';
126
- const escaped = (0, runtime_1.suppressValue)(val, true);
127
- const raw = (0, runtime_1.suppressValue)(val, false);
128
- (0, vitest_1.expect)(raw).toBe(val);
129
- // rely on lib.escape semantics (amp/lt/gt etc)
130
- (0, vitest_1.expect)(escaped).not.toBe(val);
131
- (0, vitest_1.expect)(escaped).toContain('&lt;');
132
- (0, vitest_1.expect)(escaped).toContain('&gt;');
133
- (0, vitest_1.expect)(escaped).toContain('&amp;');
134
- });
135
- });
136
- (0, vitest_1.describe)('fromIterator', () => {
137
- (0, vitest_1.it)('returns arrays as-is', () => {
138
- const a = [1, 2, 3];
139
- (0, vitest_1.expect)((0, runtime_1.fromIterator)(a)).toBe(a);
140
- });
141
- (0, vitest_1.it)('returns non-objects as-is', () => {
142
- (0, vitest_1.expect)((0, runtime_1.fromIterator)(123)).toBe(123);
143
- (0, vitest_1.expect)((0, runtime_1.fromIterator)('x')).toBe('x');
144
- (0, vitest_1.expect)((0, runtime_1.fromIterator)(null)).toBe(null);
145
- });
146
- (0, vitest_1.it)('converts iterables to arrays when Symbol.iterator is present', () => {
147
- // Set is iterable in Node
148
- const s = new Set([1, 2, 3]);
149
- const out = (0, runtime_1.fromIterator)(s);
150
- (0, vitest_1.expect)(Array.isArray(out)).toBe(true);
151
- (0, vitest_1.expect)(out).toEqual([1, 2, 3]);
152
- });
153
- (0, vitest_1.it)('returns plain objects unchanged', () => {
154
- const o = { a: 1 };
155
- (0, vitest_1.expect)((0, runtime_1.fromIterator)(o)).toBe(o);
156
- });
157
- });
158
- (0, vitest_1.describe)('asyncAll', () => {
159
- (0, vitest_1.it)('joins outputs in original index order (array)', async () => {
160
- const arr = ['a', 'b', 'c'];
161
- const p = new Promise((resolve, reject) => {
162
- (0, runtime_1.asyncAll)(arr, 1, (item, i, len, done) => {
163
- // finish out of order intentionally
164
- const delay = item === 'b' ? 10 : item === 'a' ? 30 : 0;
165
- setTimeout(() => done(i, item.toUpperCase()), delay);
166
- }, (err, res) => (err ? reject(err) : resolve(res)));
167
- });
168
- await (0, vitest_1.expect)(p).resolves.toBe('ABC');
169
- });
170
- (0, vitest_1.it)('returns empty string for empty array', async () => {
171
- const p = new Promise((resolve, reject) => {
172
- (0, runtime_1.asyncAll)([], 1, (_item, _i, _len, done) => done(0, ''), (err, res) => (err ? reject(err) : resolve(res)));
173
- });
174
- await (0, vitest_1.expect)(p).resolves.toBe('');
175
- });
176
- (0, vitest_1.it)('supports object iteration and preserves key order from Object.keys', async () => {
177
- const obj = { b: 'B', a: 'A' }; // insertion order in JS: b then a
178
- const keys = Object.keys(obj);
179
- const p = new Promise((resolve, reject) => {
180
- (0, runtime_1.asyncAll)(obj, 2, (k, v, i, len, done) => {
181
- // output includes key to verify mapping
182
- done(i, `${k}:${v};`);
183
- }, (err, res) => (err ? reject(err) : resolve(res)));
184
- });
185
- const out = await p;
186
- (0, vitest_1.expect)(out).toBe(keys.map((k) => `${k}:${obj[k]};`).join(''));
187
- });
188
- });
189
- (0, vitest_1.describe)('default export shape', () => {
190
- (0, vitest_1.it)('exposes expected helpers on default runtime export', () => {
191
- (0, vitest_1.expect)(runtime).toHaveProperty('Frame');
192
- (0, vitest_1.expect)(runtime).toHaveProperty('makeMacro');
193
- (0, vitest_1.expect)(runtime).toHaveProperty('makeKeywordArgs');
194
- (0, vitest_1.expect)(runtime).toHaveProperty('numArgs');
195
- (0, vitest_1.expect)(runtime).toHaveProperty('ensureDefined');
196
- (0, vitest_1.expect)(runtime).toHaveProperty('memberLookup');
197
- (0, vitest_1.expect)(runtime).toHaveProperty('contextOrFrameLookup');
198
- (0, vitest_1.expect)(runtime).toHaveProperty('callWrap');
199
- (0, vitest_1.expect)(runtime).toHaveProperty('handleError');
200
- (0, vitest_1.expect)(runtime).toHaveProperty('asyncEach');
201
- (0, vitest_1.expect)(runtime).toHaveProperty('asyncAll');
202
- (0, vitest_1.expect)(runtime).toHaveProperty('inOperator');
203
- (0, vitest_1.expect)(runtime).toHaveProperty('fromIterator');
204
- });
205
- });
206
- (0, vitest_1.describe)('makeMacro + keyword args semantics (smoke)', () => {
207
- (0, vitest_1.it)('makeKeywordArgs marks objects and makeMacro maps extra positionals to kwargs defaults', () => {
208
- // @ts-ignore – using runtime.default helpers
209
- const { makeMacro, makeKeywordArgs } = runtime;
210
- const fn = vitest_1.vi.fn((a, b, kwargs) => ({ a, b, kwargs }));
211
- const macro = makeMacro(['a', 'b'], ['x', 'y'], fn);
212
- // pass 4 positionals -> extra two become kwargs.x/y
213
- const res = macro(1, 2, 10, 20);
214
- (0, vitest_1.expect)(res).toEqual({ a: 1, b: 2, kwargs: { x: 10, y: 20 } });
215
- });
216
- (0, vitest_1.it)('makeMacro fills missing positionals from keyword args and removes consumed keys', () => {
217
- // @ts-ignore
218
- const { makeMacro, makeKeywordArgs } = runtime;
219
- const fn = vitest_1.vi.fn((a, b, kwargs) => ({ a, b, kwargs }));
220
- const macro = makeMacro(['a', 'b'], [], fn);
221
- const kw = makeKeywordArgs({ b: 42, extra: 'keep' });
222
- const res = macro(1, kw);
223
- (0, vitest_1.expect)(res).toEqual({
224
- a: 1,
225
- b: 42,
226
- kwargs: { extra: 'keep', __keywords: true },
227
- });
228
- });
229
- (0, vitest_1.it)('numArgs ignores trailing keyword args object', () => {
230
- // @ts-ignore
231
- const { numArgs, makeKeywordArgs } = runtime;
232
- (0, vitest_1.expect)(numArgs([1, 2])).toBe(2);
233
- (0, vitest_1.expect)(numArgs([1, makeKeywordArgs({})])).toBe(1);
234
- (0, vitest_1.expect)(numArgs([makeKeywordArgs({})])).toBe(0);
235
- });
236
- });
237
- });
@@ -1 +0,0 @@
1
- export {};
@@ -1,125 +0,0 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- // test/transformer.test.ts
4
- const vitest_1 = require("vitest");
5
- // Adjust these import paths to match your repo
6
- const transformer_1 = require("../src/transformer");
7
- const nodes_1 = require("../src/nodes");
8
- function asNodeList(n) {
9
- (0, vitest_1.expect)(n).toBeInstanceOf(nodes_1.NodeList);
10
- return n;
11
- }
12
- function asOutput(n) {
13
- (0, vitest_1.expect)(n).toBeInstanceOf(nodes_1.Output);
14
- return n;
15
- }
16
- function findAll(root, Ctor) {
17
- return root.findAll(Ctor, []);
18
- }
19
- (0, vitest_1.describe)('transformer.ts', () => {
20
- (0, vitest_1.it)('lifts async filters inside Output into a preceding FilterAsync + replaces expression with a symbol hole', () => {
21
- const filterName = new nodes_1.Symbol(0, 0, 'af');
22
- const filterArgs = new nodes_1.NodeList(0, 0, [new nodes_1.Literal(0, 0, 'x')]);
23
- const filt = new nodes_1.Filter(0, 0, filterName, filterArgs);
24
- const out = new nodes_1.Output(0, 0, [filt]);
25
- const ast = new nodes_1.Root(0, 0, [out]);
26
- const res = (0, transformer_1.transform)(ast, ['af']);
27
- // Root children[0] becomes a NodeList: [FilterAsync, Output-with-hole]
28
- const top0 = res.children[0];
29
- const lifted = asNodeList(top0);
30
- (0, vitest_1.expect)(lifted.children[0]).toBeInstanceOf(nodes_1.FilterAsync);
31
- (0, vitest_1.expect)(lifted.children[1]).toBeInstanceOf(nodes_1.Output);
32
- const fa = lifted.children[0];
33
- const out2 = lifted.children[1];
34
- // Output now contains a Symbol hole instead of the Filter node
35
- (0, vitest_1.expect)(out2.children[0]).toBeInstanceOf(nodes_1.Symbol);
36
- const hole = out2.children[0];
37
- (0, vitest_1.expect)(fa.symbol).toBeInstanceOf(nodes_1.Symbol);
38
- (0, vitest_1.expect)(fa.symbol.value).toBe(hole.value);
39
- });
40
- (0, vitest_1.it)('does NOT lift filters inside Block (Block is treated as a boundary)', () => {
41
- const filterName = new nodes_1.Symbol(0, 0, 'af');
42
- const filterArgs = new nodes_1.NodeList(0, 0, [new nodes_1.Literal(0, 0, 'x')]);
43
- const filt = new nodes_1.Filter(0, 0, filterName, filterArgs);
44
- const out = new nodes_1.Output(0, 0, [filt]);
45
- const blockBody = new nodes_1.NodeList(0, 0, [out]);
46
- const block = new nodes_1.Block(0, 0, new nodes_1.Symbol(0, 0, 'content'), blockBody);
47
- const ast = new nodes_1.Root(0, 0, [block]);
48
- const res = (0, transformer_1.transform)(ast, ['af']);
49
- (0, vitest_1.expect)(findAll(res, nodes_1.FilterAsync).length).toBe(0);
50
- (0, vitest_1.expect)(findAll(res, nodes_1.Filter).length).toBe(1);
51
- });
52
- (0, vitest_1.it)('lifts super() inside Block body into a Super node + replaces call with a symbol', () => {
53
- const superCall = new nodes_1.FunCall(0, 0, new nodes_1.Symbol(0, 0, 'super'), new nodes_1.NodeList(0, 0, []));
54
- const out = new nodes_1.Output(0, 0, [superCall]);
55
- const body = new nodes_1.NodeList(0, 0, [out]);
56
- const blockName = new nodes_1.Symbol(0, 0, 'content');
57
- const block = new nodes_1.Block(0, 0, blockName, body);
58
- const ast = new nodes_1.Root(0, 0, [block]);
59
- const res = (0, transformer_1.transform)(ast, []);
60
- const block2 = res.children[0];
61
- (0, vitest_1.expect)(block2).toBeInstanceOf(nodes_1.Block);
62
- // Super node should be inserted at beginning of block body
63
- (0, vitest_1.expect)(block2.body).toBeInstanceOf(nodes_1.NodeList);
64
- const b2 = block2.body;
65
- (0, vitest_1.expect)(b2.children[0]).toBeInstanceOf(nodes_1.Super);
66
- const superNode = b2.children[0];
67
- // Output should still exist after Super
68
- (0, vitest_1.expect)(b2.children[1]).toBeInstanceOf(nodes_1.Output);
69
- const out2 = b2.children[1];
70
- // super() call replaced with a Symbol that matches Super.symbol
71
- (0, vitest_1.expect)(out2.children[0]).toBeInstanceOf(nodes_1.Symbol);
72
- const sym = out2.children[0];
73
- (0, vitest_1.expect)(superNode.symbol).toBeInstanceOf(nodes_1.Symbol);
74
- (0, vitest_1.expect)(superNode.symbol.value).toBe(sym.value);
75
- // Super should reference the block name
76
- (0, vitest_1.expect)(superNode.blockName).toBeInstanceOf(nodes_1.Symbol);
77
- (0, vitest_1.expect)(superNode.blockName.value).toBe('content');
78
- });
79
- (0, vitest_1.it)('converts If -> IfAsync when the If subtree contains async work (FilterAsync / CallExtensionAsync / etc.)', () => {
80
- // Put async filter inside the IF BODY (not the cond), so convertStatements will mark it async
81
- const filterName = new nodes_1.Symbol(0, 0, 'af');
82
- const filterArgs = new nodes_1.NodeList(0, 0, [new nodes_1.Literal(0, 0, 'x')]);
83
- const filt = new nodes_1.Filter(0, 0, filterName, filterArgs);
84
- const out = new nodes_1.Output(0, 0, [filt]);
85
- const ifBody = new nodes_1.NodeList(0, 0, [out]);
86
- const ifNode = new nodes_1.If(0, 0, new nodes_1.Literal(0, 0, true), ifBody, null);
87
- const ast = new nodes_1.Root(0, 0, [ifNode]);
88
- const res = (0, transformer_1.transform)(ast, ['af']);
89
- // If should become IfAsync
90
- const top = res.children[0];
91
- (0, vitest_1.expect)(top).toBeInstanceOf(nodes_1.IfAsync);
92
- });
93
- (0, vitest_1.it)('converts For -> AsyncEach when the For subtree contains async work', () => {
94
- const filterName = new nodes_1.Symbol(0, 0, 'af');
95
- const filterArgs = new nodes_1.NodeList(0, 0, [new nodes_1.Literal(0, 0, 'x')]);
96
- const filt = new nodes_1.Filter(0, 0, filterName, filterArgs);
97
- const out = new nodes_1.Output(0, 0, [filt]);
98
- const forBody = new nodes_1.NodeList(0, 0, [out]);
99
- const forNode = new nodes_1.For(0, 0, new nodes_1.Symbol(0, 0, 'arr'), new nodes_1.Symbol(0, 0, 'item'), forBody, null);
100
- const ast = new nodes_1.Root(0, 0, [forNode]);
101
- const res = (0, transformer_1.transform)(ast, ['af']);
102
- const top = res.children[0];
103
- (0, vitest_1.expect)(top).toBeInstanceOf(nodes_1.AsyncEach);
104
- });
105
- (0, vitest_1.it)('is copy-on-write: if no transforms apply, it returns the same node references', () => {
106
- const out = new nodes_1.Output(0, 0, [new nodes_1.TemplateData(0, 0, 'hello')]);
107
- const ast = new nodes_1.Root(0, 0, [out]);
108
- const res = (0, transformer_1.transform)(ast, []);
109
- // No super, no filters lifted, no async conversion => should be same object graph
110
- (0, vitest_1.expect)(res).toBe(ast);
111
- (0, vitest_1.expect)(res.children[0]).toBe(out);
112
- });
113
- (0, vitest_1.it)('only lifts filters listed in asyncFilters (non-listed filters remain synchronous)', () => {
114
- const filterName = new nodes_1.Symbol(0, 0, 'syncFilter');
115
- const filterArgs = new nodes_1.NodeList(0, 0, [new nodes_1.Literal(0, 0, 'x')]);
116
- const filt = new nodes_1.Filter(0, 0, filterName, filterArgs);
117
- const out = new nodes_1.Output(0, 0, [filt]);
118
- const ast = new nodes_1.Root(0, 0, [out]);
119
- const res = (0, transformer_1.transform)(ast, ['af']);
120
- (0, vitest_1.expect)(findAll(res, nodes_1.FilterAsync).length).toBe(0);
121
- (0, vitest_1.expect)(findAll(res, nodes_1.Filter).length).toBe(1);
122
- // And Output should remain Output at top-level
123
- (0, vitest_1.expect)(res.children[0]).toBeInstanceOf(nodes_1.Output);
124
- });
125
- });