handlebars-jaylinski 4.7.8
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/LICENSE +19 -0
- package/README.markdown +169 -0
- package/bin/.eslintrc.js +6 -0
- package/bin/handlebars +176 -0
- package/dist/amd/handlebars/base.js +106 -0
- package/dist/amd/handlebars/compiler/ast.js +31 -0
- package/dist/amd/handlebars/compiler/base.js +45 -0
- package/dist/amd/handlebars/compiler/code-gen.js +165 -0
- package/dist/amd/handlebars/compiler/compiler.js +562 -0
- package/dist/amd/handlebars/compiler/helpers.js +228 -0
- package/dist/amd/handlebars/compiler/javascript-compiler.js +1150 -0
- package/dist/amd/handlebars/compiler/parser.js +737 -0
- package/dist/amd/handlebars/compiler/printer.js +186 -0
- package/dist/amd/handlebars/compiler/visitor.js +138 -0
- package/dist/amd/handlebars/compiler/whitespace-control.js +219 -0
- package/dist/amd/handlebars/decorators/inline.js +25 -0
- package/dist/amd/handlebars/decorators.js +16 -0
- package/dist/amd/handlebars/exception.js +64 -0
- package/dist/amd/handlebars/helpers/block-helper-missing.js +35 -0
- package/dist/amd/handlebars/helpers/each.js +99 -0
- package/dist/amd/handlebars/helpers/helper-missing.js +22 -0
- package/dist/amd/handlebars/helpers/if.js +41 -0
- package/dist/amd/handlebars/helpers/log.js +24 -0
- package/dist/amd/handlebars/helpers/lookup.js +14 -0
- package/dist/amd/handlebars/helpers/with.js +38 -0
- package/dist/amd/handlebars/helpers.js +44 -0
- package/dist/amd/handlebars/internal/create-new-lookup-object.js +22 -0
- package/dist/amd/handlebars/internal/proto-access.js +71 -0
- package/dist/amd/handlebars/internal/wrapHelper.js +21 -0
- package/dist/amd/handlebars/logger.js +44 -0
- package/dist/amd/handlebars/no-conflict.js +28 -0
- package/dist/amd/handlebars/runtime.js +356 -0
- package/dist/amd/handlebars/safe-string.js +15 -0
- package/dist/amd/handlebars/utils.js +126 -0
- package/dist/amd/handlebars.js +52 -0
- package/dist/amd/handlebars.runtime.js +44 -0
- package/dist/amd/precompiler.js +314 -0
- package/dist/cjs/handlebars/base.js +116 -0
- package/dist/cjs/handlebars/compiler/ast.js +31 -0
- package/dist/cjs/handlebars/compiler/base.js +57 -0
- package/dist/cjs/handlebars/compiler/code-gen.js +168 -0
- package/dist/cjs/handlebars/compiler/compiler.js +566 -0
- package/dist/cjs/handlebars/compiler/helpers.js +228 -0
- package/dist/cjs/handlebars/compiler/javascript-compiler.js +1158 -0
- package/dist/cjs/handlebars/compiler/parser.js +737 -0
- package/dist/cjs/handlebars/compiler/printer.js +186 -0
- package/dist/cjs/handlebars/compiler/visitor.js +140 -0
- package/dist/cjs/handlebars/compiler/whitespace-control.js +221 -0
- package/dist/cjs/handlebars/decorators/inline.js +29 -0
- package/dist/cjs/handlebars/decorators.js +16 -0
- package/dist/cjs/handlebars/exception.js +64 -0
- package/dist/cjs/handlebars/helpers/block-helper-missing.js +39 -0
- package/dist/cjs/handlebars/helpers/each.js +104 -0
- package/dist/cjs/handlebars/helpers/helper-missing.js +25 -0
- package/dist/cjs/handlebars/helpers/if.js +46 -0
- package/dist/cjs/handlebars/helpers/log.js +26 -0
- package/dist/cjs/handlebars/helpers/lookup.js +16 -0
- package/dist/cjs/handlebars/helpers/with.js +43 -0
- package/dist/cjs/handlebars/helpers.js +56 -0
- package/dist/cjs/handlebars/internal/create-new-lookup-object.js +22 -0
- package/dist/cjs/handlebars/internal/proto-access.js +73 -0
- package/dist/cjs/handlebars/internal/wrapHelper.js +19 -0
- package/dist/cjs/handlebars/logger.js +47 -0
- package/dist/cjs/handlebars/no-conflict.js +30 -0
- package/dist/cjs/handlebars/runtime.js +372 -0
- package/dist/cjs/handlebars/safe-string.js +15 -0
- package/dist/cjs/handlebars/utils.js +124 -0
- package/dist/cjs/handlebars.js +66 -0
- package/dist/cjs/handlebars.runtime.js +66 -0
- package/dist/cjs/precompiler.js +328 -0
- package/dist/handlebars.amd.js +4639 -0
- package/dist/handlebars.amd.min.js +29 -0
- package/dist/handlebars.js +5972 -0
- package/dist/handlebars.min.js +29 -0
- package/dist/handlebars.runtime.amd.js +1302 -0
- package/dist/handlebars.runtime.amd.min.js +27 -0
- package/dist/handlebars.runtime.js +2563 -0
- package/dist/handlebars.runtime.min.js +27 -0
- package/lib/.eslintrc.js +8 -0
- package/lib/handlebars/base.js +94 -0
- package/lib/handlebars/compiler/ast.js +32 -0
- package/lib/handlebars/compiler/base.js +34 -0
- package/lib/handlebars/compiler/code-gen.js +171 -0
- package/lib/handlebars/compiler/compiler.js +594 -0
- package/lib/handlebars/compiler/helpers.js +219 -0
- package/lib/handlebars/compiler/javascript-compiler.js +1293 -0
- package/lib/handlebars/compiler/parser.js +622 -0
- package/lib/handlebars/compiler/printer.js +178 -0
- package/lib/handlebars/compiler/visitor.js +136 -0
- package/lib/handlebars/compiler/whitespace-control.js +234 -0
- package/lib/handlebars/decorators/inline.js +22 -0
- package/lib/handlebars/decorators.js +5 -0
- package/lib/handlebars/exception.js +68 -0
- package/lib/handlebars/helpers/block-helper-missing.js +35 -0
- package/lib/handlebars/helpers/each.js +101 -0
- package/lib/handlebars/helpers/helper-missing.js +15 -0
- package/lib/handlebars/helpers/if.js +33 -0
- package/lib/handlebars/helpers/log.js +19 -0
- package/lib/handlebars/helpers/lookup.js +9 -0
- package/lib/handlebars/helpers/with.js +39 -0
- package/lib/handlebars/helpers.js +26 -0
- package/lib/handlebars/internal/create-new-lookup-object.js +11 -0
- package/lib/handlebars/internal/proto-access.js +70 -0
- package/lib/handlebars/internal/wrapHelper.js +13 -0
- package/lib/handlebars/logger.js +39 -0
- package/lib/handlebars/no-conflict.js +23 -0
- package/lib/handlebars/runtime.js +450 -0
- package/lib/handlebars/safe-string.js +10 -0
- package/lib/handlebars/utils.js +116 -0
- package/lib/handlebars.js +46 -0
- package/lib/handlebars.runtime.js +37 -0
- package/lib/index.js +26 -0
- package/lib/precompiler.js +341 -0
- package/package.json +135 -0
- package/release-notes.md +1101 -0
- package/runtime.d.ts +5 -0
- package/runtime.js +3 -0
- package/types/index.d.ts +422 -0
|
@@ -0,0 +1,1293 @@
|
|
|
1
|
+
import { COMPILER_REVISION, REVISION_CHANGES } from '../base';
|
|
2
|
+
import Exception from '../exception';
|
|
3
|
+
import { isArray } from '../utils';
|
|
4
|
+
import CodeGen from './code-gen';
|
|
5
|
+
|
|
6
|
+
function Literal(value) {
|
|
7
|
+
this.value = value;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
function JavaScriptCompiler() {}
|
|
11
|
+
|
|
12
|
+
JavaScriptCompiler.prototype = {
|
|
13
|
+
// PUBLIC API: You can override these methods in a subclass to provide
|
|
14
|
+
// alternative compiled forms for name lookup and buffering semantics
|
|
15
|
+
nameLookup: function(parent, name /*, type */) {
|
|
16
|
+
return this.internalNameLookup(parent, name);
|
|
17
|
+
},
|
|
18
|
+
depthedLookup: function(name) {
|
|
19
|
+
return [
|
|
20
|
+
this.aliasable('container.lookup'),
|
|
21
|
+
'(depths, ',
|
|
22
|
+
JSON.stringify(name),
|
|
23
|
+
')'
|
|
24
|
+
];
|
|
25
|
+
},
|
|
26
|
+
|
|
27
|
+
compilerInfo: function() {
|
|
28
|
+
const revision = COMPILER_REVISION,
|
|
29
|
+
versions = REVISION_CHANGES[revision];
|
|
30
|
+
return [revision, versions];
|
|
31
|
+
},
|
|
32
|
+
|
|
33
|
+
appendToBuffer: function(source, location, explicit) {
|
|
34
|
+
// Force a source as this simplifies the merge logic.
|
|
35
|
+
if (!isArray(source)) {
|
|
36
|
+
source = [source];
|
|
37
|
+
}
|
|
38
|
+
source = this.source.wrap(source, location);
|
|
39
|
+
|
|
40
|
+
if (this.environment.isSimple) {
|
|
41
|
+
return ['return ', source, ';'];
|
|
42
|
+
} else if (explicit) {
|
|
43
|
+
// This is a case where the buffer operation occurs as a child of another
|
|
44
|
+
// construct, generally braces. We have to explicitly output these buffer
|
|
45
|
+
// operations to ensure that the emitted code goes in the correct location.
|
|
46
|
+
return ['buffer += ', source, ';'];
|
|
47
|
+
} else {
|
|
48
|
+
source.appendToBuffer = true;
|
|
49
|
+
return source;
|
|
50
|
+
}
|
|
51
|
+
},
|
|
52
|
+
|
|
53
|
+
initializeBuffer: function() {
|
|
54
|
+
return this.quotedString('');
|
|
55
|
+
},
|
|
56
|
+
// END PUBLIC API
|
|
57
|
+
internalNameLookup: function(parent, name) {
|
|
58
|
+
this.lookupPropertyFunctionIsUsed = true;
|
|
59
|
+
return ['lookupProperty(', parent, ',', JSON.stringify(name), ')'];
|
|
60
|
+
},
|
|
61
|
+
|
|
62
|
+
lookupPropertyFunctionIsUsed: false,
|
|
63
|
+
|
|
64
|
+
compile: function(environment, options, context, asObject) {
|
|
65
|
+
this.environment = environment;
|
|
66
|
+
this.options = options;
|
|
67
|
+
this.stringParams = this.options.stringParams;
|
|
68
|
+
this.trackIds = this.options.trackIds;
|
|
69
|
+
this.precompile = !asObject;
|
|
70
|
+
|
|
71
|
+
this.name = this.environment.name;
|
|
72
|
+
this.isChild = !!context;
|
|
73
|
+
this.context = context || {
|
|
74
|
+
decorators: [],
|
|
75
|
+
programs: [],
|
|
76
|
+
environments: []
|
|
77
|
+
};
|
|
78
|
+
|
|
79
|
+
this.preamble();
|
|
80
|
+
|
|
81
|
+
this.stackSlot = 0;
|
|
82
|
+
this.stackVars = [];
|
|
83
|
+
this.aliases = {};
|
|
84
|
+
this.registers = { list: [] };
|
|
85
|
+
this.hashes = [];
|
|
86
|
+
this.compileStack = [];
|
|
87
|
+
this.inlineStack = [];
|
|
88
|
+
this.blockParams = [];
|
|
89
|
+
|
|
90
|
+
this.compileChildren(environment, options);
|
|
91
|
+
|
|
92
|
+
this.useDepths =
|
|
93
|
+
this.useDepths ||
|
|
94
|
+
environment.useDepths ||
|
|
95
|
+
environment.useDecorators ||
|
|
96
|
+
this.options.compat;
|
|
97
|
+
this.useBlockParams = this.useBlockParams || environment.useBlockParams;
|
|
98
|
+
|
|
99
|
+
let opcodes = environment.opcodes,
|
|
100
|
+
opcode,
|
|
101
|
+
firstLoc,
|
|
102
|
+
i,
|
|
103
|
+
l;
|
|
104
|
+
|
|
105
|
+
for (i = 0, l = opcodes.length; i < l; i++) {
|
|
106
|
+
opcode = opcodes[i];
|
|
107
|
+
|
|
108
|
+
this.source.currentLocation = opcode.loc;
|
|
109
|
+
firstLoc = firstLoc || opcode.loc;
|
|
110
|
+
this[opcode.opcode].apply(this, opcode.args);
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
// Flush any trailing content that might be pending.
|
|
114
|
+
this.source.currentLocation = firstLoc;
|
|
115
|
+
this.pushSource('');
|
|
116
|
+
|
|
117
|
+
/* istanbul ignore next */
|
|
118
|
+
if (this.stackSlot || this.inlineStack.length || this.compileStack.length) {
|
|
119
|
+
throw new Exception('Compile completed with content left on stack');
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
if (!this.decorators.isEmpty()) {
|
|
123
|
+
this.useDecorators = true;
|
|
124
|
+
|
|
125
|
+
this.decorators.prepend([
|
|
126
|
+
'var decorators = container.decorators, ',
|
|
127
|
+
this.lookupPropertyFunctionVarDeclaration(),
|
|
128
|
+
';\n'
|
|
129
|
+
]);
|
|
130
|
+
this.decorators.push('return fn;');
|
|
131
|
+
|
|
132
|
+
if (asObject) {
|
|
133
|
+
this.decorators = Function.apply(this, [
|
|
134
|
+
'fn',
|
|
135
|
+
'props',
|
|
136
|
+
'container',
|
|
137
|
+
'depth0',
|
|
138
|
+
'data',
|
|
139
|
+
'blockParams',
|
|
140
|
+
'depths',
|
|
141
|
+
this.decorators.merge()
|
|
142
|
+
]);
|
|
143
|
+
} else {
|
|
144
|
+
this.decorators.prepend(
|
|
145
|
+
'function(fn, props, container, depth0, data, blockParams, depths) {\n'
|
|
146
|
+
);
|
|
147
|
+
this.decorators.push('}\n');
|
|
148
|
+
this.decorators = this.decorators.merge();
|
|
149
|
+
}
|
|
150
|
+
} else {
|
|
151
|
+
this.decorators = undefined;
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
let fn = this.createFunctionContext(asObject);
|
|
155
|
+
if (!this.isChild) {
|
|
156
|
+
let ret = {
|
|
157
|
+
compiler: this.compilerInfo(),
|
|
158
|
+
main: fn
|
|
159
|
+
};
|
|
160
|
+
|
|
161
|
+
if (this.decorators) {
|
|
162
|
+
ret.main_d = this.decorators; // eslint-disable-line camelcase
|
|
163
|
+
ret.useDecorators = true;
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
let { programs, decorators } = this.context;
|
|
167
|
+
for (i = 0, l = programs.length; i < l; i++) {
|
|
168
|
+
if (programs[i]) {
|
|
169
|
+
ret[i] = programs[i];
|
|
170
|
+
if (decorators[i]) {
|
|
171
|
+
ret[i + '_d'] = decorators[i];
|
|
172
|
+
ret.useDecorators = true;
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
if (this.environment.usePartial) {
|
|
178
|
+
ret.usePartial = true;
|
|
179
|
+
}
|
|
180
|
+
if (this.options.data) {
|
|
181
|
+
ret.useData = true;
|
|
182
|
+
}
|
|
183
|
+
if (this.useDepths) {
|
|
184
|
+
ret.useDepths = true;
|
|
185
|
+
}
|
|
186
|
+
if (this.useBlockParams) {
|
|
187
|
+
ret.useBlockParams = true;
|
|
188
|
+
}
|
|
189
|
+
if (this.options.compat) {
|
|
190
|
+
ret.compat = true;
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
if (!asObject) {
|
|
194
|
+
ret.compiler = JSON.stringify(ret.compiler);
|
|
195
|
+
|
|
196
|
+
this.source.currentLocation = { start: { line: 1, column: 0 } };
|
|
197
|
+
ret = this.objectLiteral(ret);
|
|
198
|
+
|
|
199
|
+
if (options.srcName) {
|
|
200
|
+
ret = ret.toStringWithSourceMap({ file: options.destName });
|
|
201
|
+
ret.map = ret.map && ret.map.toString();
|
|
202
|
+
} else {
|
|
203
|
+
ret = ret.toString();
|
|
204
|
+
}
|
|
205
|
+
} else {
|
|
206
|
+
ret.compilerOptions = this.options;
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
return ret;
|
|
210
|
+
} else {
|
|
211
|
+
return fn;
|
|
212
|
+
}
|
|
213
|
+
},
|
|
214
|
+
|
|
215
|
+
preamble: function() {
|
|
216
|
+
// track the last context pushed into place to allow skipping the
|
|
217
|
+
// getContext opcode when it would be a noop
|
|
218
|
+
this.lastContext = 0;
|
|
219
|
+
this.source = new CodeGen(this.options.srcName);
|
|
220
|
+
this.decorators = new CodeGen(this.options.srcName);
|
|
221
|
+
},
|
|
222
|
+
|
|
223
|
+
createFunctionContext: function(asObject) {
|
|
224
|
+
let varDeclarations = '';
|
|
225
|
+
|
|
226
|
+
let locals = this.stackVars.concat(this.registers.list);
|
|
227
|
+
if (locals.length > 0) {
|
|
228
|
+
varDeclarations += ', ' + locals.join(', ');
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
// Generate minimizer alias mappings
|
|
232
|
+
//
|
|
233
|
+
// When using true SourceNodes, this will update all references to the given alias
|
|
234
|
+
// as the source nodes are reused in situ. For the non-source node compilation mode,
|
|
235
|
+
// aliases will not be used, but this case is already being run on the client and
|
|
236
|
+
// we aren't concern about minimizing the template size.
|
|
237
|
+
let aliasCount = 0;
|
|
238
|
+
Object.keys(this.aliases).forEach(alias => {
|
|
239
|
+
let node = this.aliases[alias];
|
|
240
|
+
if (node.children && node.referenceCount > 1) {
|
|
241
|
+
varDeclarations += ', alias' + ++aliasCount + '=' + alias;
|
|
242
|
+
node.children[0] = 'alias' + aliasCount;
|
|
243
|
+
}
|
|
244
|
+
});
|
|
245
|
+
|
|
246
|
+
if (this.lookupPropertyFunctionIsUsed) {
|
|
247
|
+
varDeclarations += ', ' + this.lookupPropertyFunctionVarDeclaration();
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
let params = ['container', 'depth0', 'helpers', 'partials', 'data'];
|
|
251
|
+
|
|
252
|
+
if (this.useBlockParams || this.useDepths) {
|
|
253
|
+
params.push('blockParams');
|
|
254
|
+
}
|
|
255
|
+
if (this.useDepths) {
|
|
256
|
+
params.push('depths');
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
// Perform a second pass over the output to merge content when possible
|
|
260
|
+
let source = this.mergeSource(varDeclarations);
|
|
261
|
+
|
|
262
|
+
if (asObject) {
|
|
263
|
+
params.push(source);
|
|
264
|
+
|
|
265
|
+
return Function.apply(this, params);
|
|
266
|
+
} else {
|
|
267
|
+
return this.source.wrap([
|
|
268
|
+
'function(',
|
|
269
|
+
params.join(','),
|
|
270
|
+
') {\n ',
|
|
271
|
+
source,
|
|
272
|
+
'}'
|
|
273
|
+
]);
|
|
274
|
+
}
|
|
275
|
+
},
|
|
276
|
+
mergeSource: function(varDeclarations) {
|
|
277
|
+
let isSimple = this.environment.isSimple,
|
|
278
|
+
appendOnly = !this.forceBuffer,
|
|
279
|
+
appendFirst,
|
|
280
|
+
sourceSeen,
|
|
281
|
+
bufferStart,
|
|
282
|
+
bufferEnd;
|
|
283
|
+
this.source.each(line => {
|
|
284
|
+
if (line.appendToBuffer) {
|
|
285
|
+
if (bufferStart) {
|
|
286
|
+
line.prepend(' + ');
|
|
287
|
+
} else {
|
|
288
|
+
bufferStart = line;
|
|
289
|
+
}
|
|
290
|
+
bufferEnd = line;
|
|
291
|
+
} else {
|
|
292
|
+
if (bufferStart) {
|
|
293
|
+
if (!sourceSeen) {
|
|
294
|
+
appendFirst = true;
|
|
295
|
+
} else {
|
|
296
|
+
bufferStart.prepend('buffer += ');
|
|
297
|
+
}
|
|
298
|
+
bufferEnd.add(';');
|
|
299
|
+
bufferStart = bufferEnd = undefined;
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
sourceSeen = true;
|
|
303
|
+
if (!isSimple) {
|
|
304
|
+
appendOnly = false;
|
|
305
|
+
}
|
|
306
|
+
}
|
|
307
|
+
});
|
|
308
|
+
|
|
309
|
+
if (appendOnly) {
|
|
310
|
+
if (bufferStart) {
|
|
311
|
+
bufferStart.prepend('return ');
|
|
312
|
+
bufferEnd.add(';');
|
|
313
|
+
} else if (!sourceSeen) {
|
|
314
|
+
this.source.push('return "";');
|
|
315
|
+
}
|
|
316
|
+
} else {
|
|
317
|
+
varDeclarations +=
|
|
318
|
+
', buffer = ' + (appendFirst ? '' : this.initializeBuffer());
|
|
319
|
+
|
|
320
|
+
if (bufferStart) {
|
|
321
|
+
bufferStart.prepend('return buffer + ');
|
|
322
|
+
bufferEnd.add(';');
|
|
323
|
+
} else {
|
|
324
|
+
this.source.push('return buffer;');
|
|
325
|
+
}
|
|
326
|
+
}
|
|
327
|
+
|
|
328
|
+
if (varDeclarations) {
|
|
329
|
+
this.source.prepend(
|
|
330
|
+
'var ' + varDeclarations.substring(2) + (appendFirst ? '' : ';\n')
|
|
331
|
+
);
|
|
332
|
+
}
|
|
333
|
+
|
|
334
|
+
return this.source.merge();
|
|
335
|
+
},
|
|
336
|
+
|
|
337
|
+
lookupPropertyFunctionVarDeclaration: function() {
|
|
338
|
+
return `
|
|
339
|
+
lookupProperty = container.lookupProperty || function(parent, propertyName) {
|
|
340
|
+
if (Object.prototype.hasOwnProperty.call(parent, propertyName)) {
|
|
341
|
+
return parent[propertyName];
|
|
342
|
+
}
|
|
343
|
+
return undefined
|
|
344
|
+
}
|
|
345
|
+
`.trim();
|
|
346
|
+
},
|
|
347
|
+
|
|
348
|
+
// [blockValue]
|
|
349
|
+
//
|
|
350
|
+
// On stack, before: hash, inverse, program, value
|
|
351
|
+
// On stack, after: return value of blockHelperMissing
|
|
352
|
+
//
|
|
353
|
+
// The purpose of this opcode is to take a block of the form
|
|
354
|
+
// `{{#this.foo}}...{{/this.foo}}`, resolve the value of `foo`, and
|
|
355
|
+
// replace it on the stack with the result of properly
|
|
356
|
+
// invoking blockHelperMissing.
|
|
357
|
+
blockValue: function(name) {
|
|
358
|
+
let blockHelperMissing = this.aliasable(
|
|
359
|
+
'container.hooks.blockHelperMissing'
|
|
360
|
+
),
|
|
361
|
+
params = [this.contextName(0)];
|
|
362
|
+
this.setupHelperArgs(name, 0, params);
|
|
363
|
+
|
|
364
|
+
let blockName = this.popStack();
|
|
365
|
+
params.splice(1, 0, blockName);
|
|
366
|
+
|
|
367
|
+
this.push(this.source.functionCall(blockHelperMissing, 'call', params));
|
|
368
|
+
},
|
|
369
|
+
|
|
370
|
+
// [ambiguousBlockValue]
|
|
371
|
+
//
|
|
372
|
+
// On stack, before: hash, inverse, program, value
|
|
373
|
+
// Compiler value, before: lastHelper=value of last found helper, if any
|
|
374
|
+
// On stack, after, if no lastHelper: same as [blockValue]
|
|
375
|
+
// On stack, after, if lastHelper: value
|
|
376
|
+
ambiguousBlockValue: function() {
|
|
377
|
+
// We're being a bit cheeky and reusing the options value from the prior exec
|
|
378
|
+
let blockHelperMissing = this.aliasable(
|
|
379
|
+
'container.hooks.blockHelperMissing'
|
|
380
|
+
),
|
|
381
|
+
params = [this.contextName(0)];
|
|
382
|
+
this.setupHelperArgs('', 0, params, true);
|
|
383
|
+
|
|
384
|
+
this.flushInline();
|
|
385
|
+
|
|
386
|
+
let current = this.topStack();
|
|
387
|
+
params.splice(1, 0, current);
|
|
388
|
+
|
|
389
|
+
this.pushSource([
|
|
390
|
+
'if (!',
|
|
391
|
+
this.lastHelper,
|
|
392
|
+
') { ',
|
|
393
|
+
current,
|
|
394
|
+
' = ',
|
|
395
|
+
this.source.functionCall(blockHelperMissing, 'call', params),
|
|
396
|
+
'}'
|
|
397
|
+
]);
|
|
398
|
+
},
|
|
399
|
+
|
|
400
|
+
// [appendContent]
|
|
401
|
+
//
|
|
402
|
+
// On stack, before: ...
|
|
403
|
+
// On stack, after: ...
|
|
404
|
+
//
|
|
405
|
+
// Appends the string value of `content` to the current buffer
|
|
406
|
+
appendContent: function(content) {
|
|
407
|
+
if (this.pendingContent) {
|
|
408
|
+
content = this.pendingContent + content;
|
|
409
|
+
} else {
|
|
410
|
+
this.pendingLocation = this.source.currentLocation;
|
|
411
|
+
}
|
|
412
|
+
|
|
413
|
+
this.pendingContent = content;
|
|
414
|
+
},
|
|
415
|
+
|
|
416
|
+
// [append]
|
|
417
|
+
//
|
|
418
|
+
// On stack, before: value, ...
|
|
419
|
+
// On stack, after: ...
|
|
420
|
+
//
|
|
421
|
+
// Coerces `value` to a String and appends it to the current buffer.
|
|
422
|
+
//
|
|
423
|
+
// If `value` is truthy, or 0, it is coerced into a string and appended
|
|
424
|
+
// Otherwise, the empty string is appended
|
|
425
|
+
append: function() {
|
|
426
|
+
if (this.isInline()) {
|
|
427
|
+
this.replaceStack(current => [' != null ? ', current, ' : ""']);
|
|
428
|
+
|
|
429
|
+
this.pushSource(this.appendToBuffer(this.popStack()));
|
|
430
|
+
} else {
|
|
431
|
+
let local = this.popStack();
|
|
432
|
+
this.pushSource([
|
|
433
|
+
'if (',
|
|
434
|
+
local,
|
|
435
|
+
' != null) { ',
|
|
436
|
+
this.appendToBuffer(local, undefined, true),
|
|
437
|
+
' }'
|
|
438
|
+
]);
|
|
439
|
+
if (this.environment.isSimple) {
|
|
440
|
+
this.pushSource([
|
|
441
|
+
'else { ',
|
|
442
|
+
this.appendToBuffer("''", undefined, true),
|
|
443
|
+
' }'
|
|
444
|
+
]);
|
|
445
|
+
}
|
|
446
|
+
}
|
|
447
|
+
},
|
|
448
|
+
|
|
449
|
+
// [appendEscaped]
|
|
450
|
+
//
|
|
451
|
+
// On stack, before: value, ...
|
|
452
|
+
// On stack, after: ...
|
|
453
|
+
//
|
|
454
|
+
// Escape `value` and append it to the buffer
|
|
455
|
+
appendEscaped: function() {
|
|
456
|
+
this.pushSource(
|
|
457
|
+
this.appendToBuffer([
|
|
458
|
+
this.aliasable('container.escapeExpression'),
|
|
459
|
+
'(',
|
|
460
|
+
this.popStack(),
|
|
461
|
+
')'
|
|
462
|
+
])
|
|
463
|
+
);
|
|
464
|
+
},
|
|
465
|
+
|
|
466
|
+
// [getContext]
|
|
467
|
+
//
|
|
468
|
+
// On stack, before: ...
|
|
469
|
+
// On stack, after: ...
|
|
470
|
+
// Compiler value, after: lastContext=depth
|
|
471
|
+
//
|
|
472
|
+
// Set the value of the `lastContext` compiler value to the depth
|
|
473
|
+
getContext: function(depth) {
|
|
474
|
+
this.lastContext = depth;
|
|
475
|
+
},
|
|
476
|
+
|
|
477
|
+
// [pushContext]
|
|
478
|
+
//
|
|
479
|
+
// On stack, before: ...
|
|
480
|
+
// On stack, after: currentContext, ...
|
|
481
|
+
//
|
|
482
|
+
// Pushes the value of the current context onto the stack.
|
|
483
|
+
pushContext: function() {
|
|
484
|
+
this.pushStackLiteral(this.contextName(this.lastContext));
|
|
485
|
+
},
|
|
486
|
+
|
|
487
|
+
// [lookupOnContext]
|
|
488
|
+
//
|
|
489
|
+
// On stack, before: ...
|
|
490
|
+
// On stack, after: currentContext[name], ...
|
|
491
|
+
//
|
|
492
|
+
// Looks up the value of `name` on the current context and pushes
|
|
493
|
+
// it onto the stack.
|
|
494
|
+
lookupOnContext: function(parts, falsy, strict, scoped) {
|
|
495
|
+
let i = 0;
|
|
496
|
+
|
|
497
|
+
if (!scoped && this.options.compat && !this.lastContext) {
|
|
498
|
+
// The depthed query is expected to handle the undefined logic for the root level that
|
|
499
|
+
// is implemented below, so we evaluate that directly in compat mode
|
|
500
|
+
this.push(this.depthedLookup(parts[i++]));
|
|
501
|
+
} else {
|
|
502
|
+
this.pushContext();
|
|
503
|
+
}
|
|
504
|
+
|
|
505
|
+
this.resolvePath('context', parts, i, falsy, strict);
|
|
506
|
+
},
|
|
507
|
+
|
|
508
|
+
// [lookupBlockParam]
|
|
509
|
+
//
|
|
510
|
+
// On stack, before: ...
|
|
511
|
+
// On stack, after: blockParam[name], ...
|
|
512
|
+
//
|
|
513
|
+
// Looks up the value of `parts` on the given block param and pushes
|
|
514
|
+
// it onto the stack.
|
|
515
|
+
lookupBlockParam: function(blockParamId, parts) {
|
|
516
|
+
this.useBlockParams = true;
|
|
517
|
+
|
|
518
|
+
this.push(['blockParams[', blockParamId[0], '][', blockParamId[1], ']']);
|
|
519
|
+
this.resolvePath('context', parts, 1);
|
|
520
|
+
},
|
|
521
|
+
|
|
522
|
+
// [lookupData]
|
|
523
|
+
//
|
|
524
|
+
// On stack, before: ...
|
|
525
|
+
// On stack, after: data, ...
|
|
526
|
+
//
|
|
527
|
+
// Push the data lookup operator
|
|
528
|
+
lookupData: function(depth, parts, strict) {
|
|
529
|
+
if (!depth) {
|
|
530
|
+
this.pushStackLiteral('data');
|
|
531
|
+
} else {
|
|
532
|
+
this.pushStackLiteral('container.data(data, ' + depth + ')');
|
|
533
|
+
}
|
|
534
|
+
|
|
535
|
+
this.resolvePath('data', parts, 0, true, strict);
|
|
536
|
+
},
|
|
537
|
+
|
|
538
|
+
resolvePath: function(type, parts, i, falsy, strict) {
|
|
539
|
+
if (this.options.strict || this.options.assumeObjects) {
|
|
540
|
+
this.push(
|
|
541
|
+
strictLookup(this.options.strict && strict, this, parts, i, type)
|
|
542
|
+
);
|
|
543
|
+
return;
|
|
544
|
+
}
|
|
545
|
+
|
|
546
|
+
let len = parts.length;
|
|
547
|
+
for (; i < len; i++) {
|
|
548
|
+
/* eslint-disable no-loop-func */
|
|
549
|
+
this.replaceStack(current => {
|
|
550
|
+
let lookup = this.nameLookup(current, parts[i], type);
|
|
551
|
+
// We want to ensure that zero and false are handled properly if the context (falsy flag)
|
|
552
|
+
// needs to have the special handling for these values.
|
|
553
|
+
if (!falsy) {
|
|
554
|
+
return [' != null ? ', lookup, ' : ', current];
|
|
555
|
+
} else {
|
|
556
|
+
// Otherwise we can use generic falsy handling
|
|
557
|
+
return [' && ', lookup];
|
|
558
|
+
}
|
|
559
|
+
});
|
|
560
|
+
/* eslint-enable no-loop-func */
|
|
561
|
+
}
|
|
562
|
+
},
|
|
563
|
+
|
|
564
|
+
// [resolvePossibleLambda]
|
|
565
|
+
//
|
|
566
|
+
// On stack, before: value, ...
|
|
567
|
+
// On stack, after: resolved value, ...
|
|
568
|
+
//
|
|
569
|
+
// If the `value` is a lambda, replace it on the stack by
|
|
570
|
+
// the return value of the lambda
|
|
571
|
+
resolvePossibleLambda: function() {
|
|
572
|
+
this.push([
|
|
573
|
+
this.aliasable('container.lambda'),
|
|
574
|
+
'(',
|
|
575
|
+
this.popStack(),
|
|
576
|
+
', ',
|
|
577
|
+
this.contextName(0),
|
|
578
|
+
')'
|
|
579
|
+
]);
|
|
580
|
+
},
|
|
581
|
+
|
|
582
|
+
// [pushStringParam]
|
|
583
|
+
//
|
|
584
|
+
// On stack, before: ...
|
|
585
|
+
// On stack, after: string, currentContext, ...
|
|
586
|
+
//
|
|
587
|
+
// This opcode is designed for use in string mode, which
|
|
588
|
+
// provides the string value of a parameter along with its
|
|
589
|
+
// depth rather than resolving it immediately.
|
|
590
|
+
pushStringParam: function(string, type) {
|
|
591
|
+
this.pushContext();
|
|
592
|
+
this.pushString(type);
|
|
593
|
+
|
|
594
|
+
// If it's a subexpression, the string result
|
|
595
|
+
// will be pushed after this opcode.
|
|
596
|
+
if (type !== 'SubExpression') {
|
|
597
|
+
if (typeof string === 'string') {
|
|
598
|
+
this.pushString(string);
|
|
599
|
+
} else {
|
|
600
|
+
this.pushStackLiteral(string);
|
|
601
|
+
}
|
|
602
|
+
}
|
|
603
|
+
},
|
|
604
|
+
|
|
605
|
+
emptyHash: function(omitEmpty) {
|
|
606
|
+
if (this.trackIds) {
|
|
607
|
+
this.push('{}'); // hashIds
|
|
608
|
+
}
|
|
609
|
+
if (this.stringParams) {
|
|
610
|
+
this.push('{}'); // hashContexts
|
|
611
|
+
this.push('{}'); // hashTypes
|
|
612
|
+
}
|
|
613
|
+
this.pushStackLiteral(omitEmpty ? 'undefined' : '{}');
|
|
614
|
+
},
|
|
615
|
+
pushHash: function() {
|
|
616
|
+
if (this.hash) {
|
|
617
|
+
this.hashes.push(this.hash);
|
|
618
|
+
}
|
|
619
|
+
this.hash = { values: {}, types: [], contexts: [], ids: [] };
|
|
620
|
+
},
|
|
621
|
+
popHash: function() {
|
|
622
|
+
let hash = this.hash;
|
|
623
|
+
this.hash = this.hashes.pop();
|
|
624
|
+
|
|
625
|
+
if (this.trackIds) {
|
|
626
|
+
this.push(this.objectLiteral(hash.ids));
|
|
627
|
+
}
|
|
628
|
+
if (this.stringParams) {
|
|
629
|
+
this.push(this.objectLiteral(hash.contexts));
|
|
630
|
+
this.push(this.objectLiteral(hash.types));
|
|
631
|
+
}
|
|
632
|
+
|
|
633
|
+
this.push(this.objectLiteral(hash.values));
|
|
634
|
+
},
|
|
635
|
+
|
|
636
|
+
// [pushString]
|
|
637
|
+
//
|
|
638
|
+
// On stack, before: ...
|
|
639
|
+
// On stack, after: quotedString(string), ...
|
|
640
|
+
//
|
|
641
|
+
// Push a quoted version of `string` onto the stack
|
|
642
|
+
pushString: function(string) {
|
|
643
|
+
this.pushStackLiteral(this.quotedString(string));
|
|
644
|
+
},
|
|
645
|
+
|
|
646
|
+
// [pushLiteral]
|
|
647
|
+
//
|
|
648
|
+
// On stack, before: ...
|
|
649
|
+
// On stack, after: value, ...
|
|
650
|
+
//
|
|
651
|
+
// Pushes a value onto the stack. This operation prevents
|
|
652
|
+
// the compiler from creating a temporary variable to hold
|
|
653
|
+
// it.
|
|
654
|
+
pushLiteral: function(value) {
|
|
655
|
+
this.pushStackLiteral(value);
|
|
656
|
+
},
|
|
657
|
+
|
|
658
|
+
// [pushProgram]
|
|
659
|
+
//
|
|
660
|
+
// On stack, before: ...
|
|
661
|
+
// On stack, after: program(guid), ...
|
|
662
|
+
//
|
|
663
|
+
// Push a program expression onto the stack. This takes
|
|
664
|
+
// a compile-time guid and converts it into a runtime-accessible
|
|
665
|
+
// expression.
|
|
666
|
+
pushProgram: function(guid) {
|
|
667
|
+
if (guid != null) {
|
|
668
|
+
this.pushStackLiteral(this.programExpression(guid));
|
|
669
|
+
} else {
|
|
670
|
+
this.pushStackLiteral(null);
|
|
671
|
+
}
|
|
672
|
+
},
|
|
673
|
+
|
|
674
|
+
// [registerDecorator]
|
|
675
|
+
//
|
|
676
|
+
// On stack, before: hash, program, params..., ...
|
|
677
|
+
// On stack, after: ...
|
|
678
|
+
//
|
|
679
|
+
// Pops off the decorator's parameters, invokes the decorator,
|
|
680
|
+
// and inserts the decorator into the decorators list.
|
|
681
|
+
registerDecorator(paramSize, name) {
|
|
682
|
+
let foundDecorator = this.nameLookup('decorators', name, 'decorator'),
|
|
683
|
+
options = this.setupHelperArgs(name, paramSize);
|
|
684
|
+
|
|
685
|
+
this.decorators.push([
|
|
686
|
+
'fn = ',
|
|
687
|
+
this.decorators.functionCall(foundDecorator, '', [
|
|
688
|
+
'fn',
|
|
689
|
+
'props',
|
|
690
|
+
'container',
|
|
691
|
+
options
|
|
692
|
+
]),
|
|
693
|
+
' || fn;'
|
|
694
|
+
]);
|
|
695
|
+
},
|
|
696
|
+
|
|
697
|
+
// [invokeHelper]
|
|
698
|
+
//
|
|
699
|
+
// On stack, before: hash, inverse, program, params..., ...
|
|
700
|
+
// On stack, after: result of helper invocation
|
|
701
|
+
//
|
|
702
|
+
// Pops off the helper's parameters, invokes the helper,
|
|
703
|
+
// and pushes the helper's return value onto the stack.
|
|
704
|
+
//
|
|
705
|
+
// If the helper is not found, `helperMissing` is called.
|
|
706
|
+
invokeHelper: function(paramSize, name, isSimple) {
|
|
707
|
+
let nonHelper = this.popStack(),
|
|
708
|
+
helper = this.setupHelper(paramSize, name);
|
|
709
|
+
|
|
710
|
+
let possibleFunctionCalls = [];
|
|
711
|
+
|
|
712
|
+
if (isSimple) {
|
|
713
|
+
// direct call to helper
|
|
714
|
+
possibleFunctionCalls.push(helper.name);
|
|
715
|
+
}
|
|
716
|
+
// call a function from the input object
|
|
717
|
+
possibleFunctionCalls.push(nonHelper);
|
|
718
|
+
if (!this.options.strict) {
|
|
719
|
+
possibleFunctionCalls.push(
|
|
720
|
+
this.aliasable('container.hooks.helperMissing')
|
|
721
|
+
);
|
|
722
|
+
}
|
|
723
|
+
|
|
724
|
+
let functionLookupCode = [
|
|
725
|
+
'(',
|
|
726
|
+
this.itemsSeparatedBy(possibleFunctionCalls, '||'),
|
|
727
|
+
')'
|
|
728
|
+
];
|
|
729
|
+
let functionCall = this.source.functionCall(
|
|
730
|
+
functionLookupCode,
|
|
731
|
+
'call',
|
|
732
|
+
helper.callParams
|
|
733
|
+
);
|
|
734
|
+
this.push(functionCall);
|
|
735
|
+
},
|
|
736
|
+
|
|
737
|
+
itemsSeparatedBy: function(items, separator) {
|
|
738
|
+
let result = [];
|
|
739
|
+
result.push(items[0]);
|
|
740
|
+
for (let i = 1; i < items.length; i++) {
|
|
741
|
+
result.push(separator, items[i]);
|
|
742
|
+
}
|
|
743
|
+
return result;
|
|
744
|
+
},
|
|
745
|
+
// [invokeKnownHelper]
|
|
746
|
+
//
|
|
747
|
+
// On stack, before: hash, inverse, program, params..., ...
|
|
748
|
+
// On stack, after: result of helper invocation
|
|
749
|
+
//
|
|
750
|
+
// This operation is used when the helper is known to exist,
|
|
751
|
+
// so a `helperMissing` fallback is not required.
|
|
752
|
+
invokeKnownHelper: function(paramSize, name) {
|
|
753
|
+
let helper = this.setupHelper(paramSize, name);
|
|
754
|
+
this.push(this.source.functionCall(helper.name, 'call', helper.callParams));
|
|
755
|
+
},
|
|
756
|
+
|
|
757
|
+
// [invokeAmbiguous]
|
|
758
|
+
//
|
|
759
|
+
// On stack, before: hash, inverse, program, params..., ...
|
|
760
|
+
// On stack, after: result of disambiguation
|
|
761
|
+
//
|
|
762
|
+
// This operation is used when an expression like `{{foo}}`
|
|
763
|
+
// is provided, but we don't know at compile-time whether it
|
|
764
|
+
// is a helper or a path.
|
|
765
|
+
//
|
|
766
|
+
// This operation emits more code than the other options,
|
|
767
|
+
// and can be avoided by passing the `knownHelpers` and
|
|
768
|
+
// `knownHelpersOnly` flags at compile-time.
|
|
769
|
+
invokeAmbiguous: function(name, helperCall) {
|
|
770
|
+
this.useRegister('helper');
|
|
771
|
+
|
|
772
|
+
let nonHelper = this.popStack();
|
|
773
|
+
|
|
774
|
+
this.emptyHash();
|
|
775
|
+
let helper = this.setupHelper(0, name, helperCall);
|
|
776
|
+
|
|
777
|
+
let helperName = (this.lastHelper = this.nameLookup(
|
|
778
|
+
'helpers',
|
|
779
|
+
name,
|
|
780
|
+
'helper'
|
|
781
|
+
));
|
|
782
|
+
|
|
783
|
+
let lookup = ['(', '(helper = ', helperName, ' || ', nonHelper, ')'];
|
|
784
|
+
if (!this.options.strict) {
|
|
785
|
+
lookup[0] = '(helper = ';
|
|
786
|
+
lookup.push(
|
|
787
|
+
' != null ? helper : ',
|
|
788
|
+
this.aliasable('container.hooks.helperMissing')
|
|
789
|
+
);
|
|
790
|
+
}
|
|
791
|
+
|
|
792
|
+
this.push([
|
|
793
|
+
'(',
|
|
794
|
+
lookup,
|
|
795
|
+
helper.paramsInit ? ['),(', helper.paramsInit] : [],
|
|
796
|
+
'),',
|
|
797
|
+
'(typeof helper === ',
|
|
798
|
+
this.aliasable('"function"'),
|
|
799
|
+
' ? ',
|
|
800
|
+
this.source.functionCall('helper', 'call', helper.callParams),
|
|
801
|
+
' : helper))'
|
|
802
|
+
]);
|
|
803
|
+
},
|
|
804
|
+
|
|
805
|
+
// [invokePartial]
|
|
806
|
+
//
|
|
807
|
+
// On stack, before: context, ...
|
|
808
|
+
// On stack after: result of partial invocation
|
|
809
|
+
//
|
|
810
|
+
// This operation pops off a context, invokes a partial with that context,
|
|
811
|
+
// and pushes the result of the invocation back.
|
|
812
|
+
invokePartial: function(isDynamic, name, indent) {
|
|
813
|
+
let params = [],
|
|
814
|
+
options = this.setupParams(name, 1, params);
|
|
815
|
+
|
|
816
|
+
if (isDynamic) {
|
|
817
|
+
name = this.popStack();
|
|
818
|
+
delete options.name;
|
|
819
|
+
}
|
|
820
|
+
|
|
821
|
+
if (indent) {
|
|
822
|
+
options.indent = JSON.stringify(indent);
|
|
823
|
+
}
|
|
824
|
+
options.helpers = 'helpers';
|
|
825
|
+
options.partials = 'partials';
|
|
826
|
+
options.decorators = 'container.decorators';
|
|
827
|
+
|
|
828
|
+
if (!isDynamic) {
|
|
829
|
+
params.unshift(this.nameLookup('partials', name, 'partial'));
|
|
830
|
+
} else {
|
|
831
|
+
params.unshift(name);
|
|
832
|
+
}
|
|
833
|
+
|
|
834
|
+
if (this.options.compat) {
|
|
835
|
+
options.depths = 'depths';
|
|
836
|
+
}
|
|
837
|
+
options = this.objectLiteral(options);
|
|
838
|
+
params.push(options);
|
|
839
|
+
|
|
840
|
+
this.push(this.source.functionCall('container.invokePartial', '', params));
|
|
841
|
+
},
|
|
842
|
+
|
|
843
|
+
// [assignToHash]
|
|
844
|
+
//
|
|
845
|
+
// On stack, before: value, ..., hash, ...
|
|
846
|
+
// On stack, after: ..., hash, ...
|
|
847
|
+
//
|
|
848
|
+
// Pops a value off the stack and assigns it to the current hash
|
|
849
|
+
assignToHash: function(key) {
|
|
850
|
+
let value = this.popStack(),
|
|
851
|
+
context,
|
|
852
|
+
type,
|
|
853
|
+
id;
|
|
854
|
+
|
|
855
|
+
if (this.trackIds) {
|
|
856
|
+
id = this.popStack();
|
|
857
|
+
}
|
|
858
|
+
if (this.stringParams) {
|
|
859
|
+
type = this.popStack();
|
|
860
|
+
context = this.popStack();
|
|
861
|
+
}
|
|
862
|
+
|
|
863
|
+
let hash = this.hash;
|
|
864
|
+
if (context) {
|
|
865
|
+
hash.contexts[key] = context;
|
|
866
|
+
}
|
|
867
|
+
if (type) {
|
|
868
|
+
hash.types[key] = type;
|
|
869
|
+
}
|
|
870
|
+
if (id) {
|
|
871
|
+
hash.ids[key] = id;
|
|
872
|
+
}
|
|
873
|
+
hash.values[key] = value;
|
|
874
|
+
},
|
|
875
|
+
|
|
876
|
+
pushId: function(type, name, child) {
|
|
877
|
+
if (type === 'BlockParam') {
|
|
878
|
+
this.pushStackLiteral(
|
|
879
|
+
'blockParams[' +
|
|
880
|
+
name[0] +
|
|
881
|
+
'].path[' +
|
|
882
|
+
name[1] +
|
|
883
|
+
']' +
|
|
884
|
+
(child ? ' + ' + JSON.stringify('.' + child) : '')
|
|
885
|
+
);
|
|
886
|
+
} else if (type === 'PathExpression') {
|
|
887
|
+
this.pushString(name);
|
|
888
|
+
} else if (type === 'SubExpression') {
|
|
889
|
+
this.pushStackLiteral('true');
|
|
890
|
+
} else {
|
|
891
|
+
this.pushStackLiteral('null');
|
|
892
|
+
}
|
|
893
|
+
},
|
|
894
|
+
|
|
895
|
+
// HELPERS
|
|
896
|
+
|
|
897
|
+
compiler: JavaScriptCompiler,
|
|
898
|
+
|
|
899
|
+
compileChildren: function(environment, options) {
|
|
900
|
+
let children = environment.children,
|
|
901
|
+
child,
|
|
902
|
+
compiler;
|
|
903
|
+
|
|
904
|
+
for (let i = 0, l = children.length; i < l; i++) {
|
|
905
|
+
child = children[i];
|
|
906
|
+
compiler = new this.compiler(); // eslint-disable-line new-cap
|
|
907
|
+
|
|
908
|
+
let existing = this.matchExistingProgram(child);
|
|
909
|
+
|
|
910
|
+
if (existing == null) {
|
|
911
|
+
this.context.programs.push(''); // Placeholder to prevent name conflicts for nested children
|
|
912
|
+
let index = this.context.programs.length;
|
|
913
|
+
child.index = index;
|
|
914
|
+
child.name = 'program' + index;
|
|
915
|
+
this.context.programs[index] = compiler.compile(
|
|
916
|
+
child,
|
|
917
|
+
options,
|
|
918
|
+
this.context,
|
|
919
|
+
!this.precompile
|
|
920
|
+
);
|
|
921
|
+
this.context.decorators[index] = compiler.decorators;
|
|
922
|
+
this.context.environments[index] = child;
|
|
923
|
+
|
|
924
|
+
this.useDepths = this.useDepths || compiler.useDepths;
|
|
925
|
+
this.useBlockParams = this.useBlockParams || compiler.useBlockParams;
|
|
926
|
+
child.useDepths = this.useDepths;
|
|
927
|
+
child.useBlockParams = this.useBlockParams;
|
|
928
|
+
} else {
|
|
929
|
+
child.index = existing.index;
|
|
930
|
+
child.name = 'program' + existing.index;
|
|
931
|
+
|
|
932
|
+
this.useDepths = this.useDepths || existing.useDepths;
|
|
933
|
+
this.useBlockParams = this.useBlockParams || existing.useBlockParams;
|
|
934
|
+
}
|
|
935
|
+
}
|
|
936
|
+
},
|
|
937
|
+
matchExistingProgram: function(child) {
|
|
938
|
+
for (let i = 0, len = this.context.environments.length; i < len; i++) {
|
|
939
|
+
let environment = this.context.environments[i];
|
|
940
|
+
if (environment && environment.equals(child)) {
|
|
941
|
+
return environment;
|
|
942
|
+
}
|
|
943
|
+
}
|
|
944
|
+
},
|
|
945
|
+
|
|
946
|
+
programExpression: function(guid) {
|
|
947
|
+
let child = this.environment.children[guid],
|
|
948
|
+
programParams = [child.index, 'data', child.blockParams];
|
|
949
|
+
|
|
950
|
+
if (this.useBlockParams || this.useDepths) {
|
|
951
|
+
programParams.push('blockParams');
|
|
952
|
+
}
|
|
953
|
+
if (this.useDepths) {
|
|
954
|
+
programParams.push('depths');
|
|
955
|
+
}
|
|
956
|
+
|
|
957
|
+
return 'container.program(' + programParams.join(', ') + ')';
|
|
958
|
+
},
|
|
959
|
+
|
|
960
|
+
useRegister: function(name) {
|
|
961
|
+
if (!this.registers[name]) {
|
|
962
|
+
this.registers[name] = true;
|
|
963
|
+
this.registers.list.push(name);
|
|
964
|
+
}
|
|
965
|
+
},
|
|
966
|
+
|
|
967
|
+
push: function(expr) {
|
|
968
|
+
if (!(expr instanceof Literal)) {
|
|
969
|
+
expr = this.source.wrap(expr);
|
|
970
|
+
}
|
|
971
|
+
|
|
972
|
+
this.inlineStack.push(expr);
|
|
973
|
+
return expr;
|
|
974
|
+
},
|
|
975
|
+
|
|
976
|
+
pushStackLiteral: function(item) {
|
|
977
|
+
this.push(new Literal(item));
|
|
978
|
+
},
|
|
979
|
+
|
|
980
|
+
pushSource: function(source) {
|
|
981
|
+
if (this.pendingContent) {
|
|
982
|
+
this.source.push(
|
|
983
|
+
this.appendToBuffer(
|
|
984
|
+
this.source.quotedString(this.pendingContent),
|
|
985
|
+
this.pendingLocation
|
|
986
|
+
)
|
|
987
|
+
);
|
|
988
|
+
this.pendingContent = undefined;
|
|
989
|
+
}
|
|
990
|
+
|
|
991
|
+
if (source) {
|
|
992
|
+
this.source.push(source);
|
|
993
|
+
}
|
|
994
|
+
},
|
|
995
|
+
|
|
996
|
+
replaceStack: function(callback) {
|
|
997
|
+
let prefix = ['('],
|
|
998
|
+
stack,
|
|
999
|
+
createdStack,
|
|
1000
|
+
usedLiteral;
|
|
1001
|
+
|
|
1002
|
+
/* istanbul ignore next */
|
|
1003
|
+
if (!this.isInline()) {
|
|
1004
|
+
throw new Exception('replaceStack on non-inline');
|
|
1005
|
+
}
|
|
1006
|
+
|
|
1007
|
+
// We want to merge the inline statement into the replacement statement via ','
|
|
1008
|
+
let top = this.popStack(true);
|
|
1009
|
+
|
|
1010
|
+
if (top instanceof Literal) {
|
|
1011
|
+
// Literals do not need to be inlined
|
|
1012
|
+
stack = [top.value];
|
|
1013
|
+
prefix = ['(', stack];
|
|
1014
|
+
usedLiteral = true;
|
|
1015
|
+
} else {
|
|
1016
|
+
// Get or create the current stack name for use by the inline
|
|
1017
|
+
createdStack = true;
|
|
1018
|
+
let name = this.incrStack();
|
|
1019
|
+
|
|
1020
|
+
prefix = ['((', this.push(name), ' = ', top, ')'];
|
|
1021
|
+
stack = this.topStack();
|
|
1022
|
+
}
|
|
1023
|
+
|
|
1024
|
+
let item = callback.call(this, stack);
|
|
1025
|
+
|
|
1026
|
+
if (!usedLiteral) {
|
|
1027
|
+
this.popStack();
|
|
1028
|
+
}
|
|
1029
|
+
if (createdStack) {
|
|
1030
|
+
this.stackSlot--;
|
|
1031
|
+
}
|
|
1032
|
+
this.push(prefix.concat(item, ')'));
|
|
1033
|
+
},
|
|
1034
|
+
|
|
1035
|
+
incrStack: function() {
|
|
1036
|
+
this.stackSlot++;
|
|
1037
|
+
if (this.stackSlot > this.stackVars.length) {
|
|
1038
|
+
this.stackVars.push('stack' + this.stackSlot);
|
|
1039
|
+
}
|
|
1040
|
+
return this.topStackName();
|
|
1041
|
+
},
|
|
1042
|
+
topStackName: function() {
|
|
1043
|
+
return 'stack' + this.stackSlot;
|
|
1044
|
+
},
|
|
1045
|
+
flushInline: function() {
|
|
1046
|
+
let inlineStack = this.inlineStack;
|
|
1047
|
+
this.inlineStack = [];
|
|
1048
|
+
for (let i = 0, len = inlineStack.length; i < len; i++) {
|
|
1049
|
+
let entry = inlineStack[i];
|
|
1050
|
+
/* istanbul ignore if */
|
|
1051
|
+
if (entry instanceof Literal) {
|
|
1052
|
+
this.compileStack.push(entry);
|
|
1053
|
+
} else {
|
|
1054
|
+
let stack = this.incrStack();
|
|
1055
|
+
this.pushSource([stack, ' = ', entry, ';']);
|
|
1056
|
+
this.compileStack.push(stack);
|
|
1057
|
+
}
|
|
1058
|
+
}
|
|
1059
|
+
},
|
|
1060
|
+
isInline: function() {
|
|
1061
|
+
return this.inlineStack.length;
|
|
1062
|
+
},
|
|
1063
|
+
|
|
1064
|
+
popStack: function(wrapped) {
|
|
1065
|
+
let inline = this.isInline(),
|
|
1066
|
+
item = (inline ? this.inlineStack : this.compileStack).pop();
|
|
1067
|
+
|
|
1068
|
+
if (!wrapped && item instanceof Literal) {
|
|
1069
|
+
return item.value;
|
|
1070
|
+
} else {
|
|
1071
|
+
if (!inline) {
|
|
1072
|
+
/* istanbul ignore next */
|
|
1073
|
+
if (!this.stackSlot) {
|
|
1074
|
+
throw new Exception('Invalid stack pop');
|
|
1075
|
+
}
|
|
1076
|
+
this.stackSlot--;
|
|
1077
|
+
}
|
|
1078
|
+
return item;
|
|
1079
|
+
}
|
|
1080
|
+
},
|
|
1081
|
+
|
|
1082
|
+
topStack: function() {
|
|
1083
|
+
let stack = this.isInline() ? this.inlineStack : this.compileStack,
|
|
1084
|
+
item = stack[stack.length - 1];
|
|
1085
|
+
|
|
1086
|
+
/* istanbul ignore if */
|
|
1087
|
+
if (item instanceof Literal) {
|
|
1088
|
+
return item.value;
|
|
1089
|
+
} else {
|
|
1090
|
+
return item;
|
|
1091
|
+
}
|
|
1092
|
+
},
|
|
1093
|
+
|
|
1094
|
+
contextName: function(context) {
|
|
1095
|
+
if (this.useDepths && context) {
|
|
1096
|
+
return 'depths[' + context + ']';
|
|
1097
|
+
} else {
|
|
1098
|
+
return 'depth' + context;
|
|
1099
|
+
}
|
|
1100
|
+
},
|
|
1101
|
+
|
|
1102
|
+
quotedString: function(str) {
|
|
1103
|
+
return this.source.quotedString(str);
|
|
1104
|
+
},
|
|
1105
|
+
|
|
1106
|
+
objectLiteral: function(obj) {
|
|
1107
|
+
return this.source.objectLiteral(obj);
|
|
1108
|
+
},
|
|
1109
|
+
|
|
1110
|
+
aliasable: function(name) {
|
|
1111
|
+
let ret = this.aliases[name];
|
|
1112
|
+
if (ret) {
|
|
1113
|
+
ret.referenceCount++;
|
|
1114
|
+
return ret;
|
|
1115
|
+
}
|
|
1116
|
+
|
|
1117
|
+
ret = this.aliases[name] = this.source.wrap(name);
|
|
1118
|
+
ret.aliasable = true;
|
|
1119
|
+
ret.referenceCount = 1;
|
|
1120
|
+
|
|
1121
|
+
return ret;
|
|
1122
|
+
},
|
|
1123
|
+
|
|
1124
|
+
setupHelper: function(paramSize, name, blockHelper) {
|
|
1125
|
+
let params = [],
|
|
1126
|
+
paramsInit = this.setupHelperArgs(name, paramSize, params, blockHelper);
|
|
1127
|
+
let foundHelper = this.nameLookup('helpers', name, 'helper'),
|
|
1128
|
+
callContext = this.aliasable(
|
|
1129
|
+
`${this.contextName(0)} != null ? ${this.contextName(
|
|
1130
|
+
0
|
|
1131
|
+
)} : (container.nullContext || {})`
|
|
1132
|
+
);
|
|
1133
|
+
|
|
1134
|
+
return {
|
|
1135
|
+
params: params,
|
|
1136
|
+
paramsInit: paramsInit,
|
|
1137
|
+
name: foundHelper,
|
|
1138
|
+
callParams: [callContext].concat(params)
|
|
1139
|
+
};
|
|
1140
|
+
},
|
|
1141
|
+
|
|
1142
|
+
setupParams: function(helper, paramSize, params) {
|
|
1143
|
+
let options = {},
|
|
1144
|
+
contexts = [],
|
|
1145
|
+
types = [],
|
|
1146
|
+
ids = [],
|
|
1147
|
+
objectArgs = !params,
|
|
1148
|
+
param;
|
|
1149
|
+
|
|
1150
|
+
if (objectArgs) {
|
|
1151
|
+
params = [];
|
|
1152
|
+
}
|
|
1153
|
+
|
|
1154
|
+
options.name = this.quotedString(helper);
|
|
1155
|
+
options.hash = this.popStack();
|
|
1156
|
+
|
|
1157
|
+
if (this.trackIds) {
|
|
1158
|
+
options.hashIds = this.popStack();
|
|
1159
|
+
}
|
|
1160
|
+
if (this.stringParams) {
|
|
1161
|
+
options.hashTypes = this.popStack();
|
|
1162
|
+
options.hashContexts = this.popStack();
|
|
1163
|
+
}
|
|
1164
|
+
|
|
1165
|
+
let inverse = this.popStack(),
|
|
1166
|
+
program = this.popStack();
|
|
1167
|
+
|
|
1168
|
+
// Avoid setting fn and inverse if neither are set. This allows
|
|
1169
|
+
// helpers to do a check for `if (options.fn)`
|
|
1170
|
+
if (program || inverse) {
|
|
1171
|
+
options.fn = program || 'container.noop';
|
|
1172
|
+
options.inverse = inverse || 'container.noop';
|
|
1173
|
+
}
|
|
1174
|
+
|
|
1175
|
+
// The parameters go on to the stack in order (making sure that they are evaluated in order)
|
|
1176
|
+
// so we need to pop them off the stack in reverse order
|
|
1177
|
+
let i = paramSize;
|
|
1178
|
+
while (i--) {
|
|
1179
|
+
param = this.popStack();
|
|
1180
|
+
params[i] = param;
|
|
1181
|
+
|
|
1182
|
+
if (this.trackIds) {
|
|
1183
|
+
ids[i] = this.popStack();
|
|
1184
|
+
}
|
|
1185
|
+
if (this.stringParams) {
|
|
1186
|
+
types[i] = this.popStack();
|
|
1187
|
+
contexts[i] = this.popStack();
|
|
1188
|
+
}
|
|
1189
|
+
}
|
|
1190
|
+
|
|
1191
|
+
if (objectArgs) {
|
|
1192
|
+
options.args = this.source.generateArray(params);
|
|
1193
|
+
}
|
|
1194
|
+
|
|
1195
|
+
if (this.trackIds) {
|
|
1196
|
+
options.ids = this.source.generateArray(ids);
|
|
1197
|
+
}
|
|
1198
|
+
if (this.stringParams) {
|
|
1199
|
+
options.types = this.source.generateArray(types);
|
|
1200
|
+
options.contexts = this.source.generateArray(contexts);
|
|
1201
|
+
}
|
|
1202
|
+
|
|
1203
|
+
if (this.options.data) {
|
|
1204
|
+
options.data = 'data';
|
|
1205
|
+
}
|
|
1206
|
+
if (this.useBlockParams) {
|
|
1207
|
+
options.blockParams = 'blockParams';
|
|
1208
|
+
}
|
|
1209
|
+
return options;
|
|
1210
|
+
},
|
|
1211
|
+
|
|
1212
|
+
setupHelperArgs: function(helper, paramSize, params, useRegister) {
|
|
1213
|
+
let options = this.setupParams(helper, paramSize, params);
|
|
1214
|
+
options.loc = JSON.stringify(this.source.currentLocation);
|
|
1215
|
+
options = this.objectLiteral(options);
|
|
1216
|
+
if (useRegister) {
|
|
1217
|
+
this.useRegister('options');
|
|
1218
|
+
params.push('options');
|
|
1219
|
+
return ['options=', options];
|
|
1220
|
+
} else if (params) {
|
|
1221
|
+
params.push(options);
|
|
1222
|
+
return '';
|
|
1223
|
+
} else {
|
|
1224
|
+
return options;
|
|
1225
|
+
}
|
|
1226
|
+
}
|
|
1227
|
+
};
|
|
1228
|
+
|
|
1229
|
+
(function() {
|
|
1230
|
+
const reservedWords = (
|
|
1231
|
+
'break else new var' +
|
|
1232
|
+
' case finally return void' +
|
|
1233
|
+
' catch for switch while' +
|
|
1234
|
+
' continue function this with' +
|
|
1235
|
+
' default if throw' +
|
|
1236
|
+
' delete in try' +
|
|
1237
|
+
' do instanceof typeof' +
|
|
1238
|
+
' abstract enum int short' +
|
|
1239
|
+
' boolean export interface static' +
|
|
1240
|
+
' byte extends long super' +
|
|
1241
|
+
' char final native synchronized' +
|
|
1242
|
+
' class float package throws' +
|
|
1243
|
+
' const goto private transient' +
|
|
1244
|
+
' debugger implements protected volatile' +
|
|
1245
|
+
' double import public let yield await' +
|
|
1246
|
+
' null true false'
|
|
1247
|
+
).split(' ');
|
|
1248
|
+
|
|
1249
|
+
const compilerWords = (JavaScriptCompiler.RESERVED_WORDS = {});
|
|
1250
|
+
|
|
1251
|
+
for (let i = 0, l = reservedWords.length; i < l; i++) {
|
|
1252
|
+
compilerWords[reservedWords[i]] = true;
|
|
1253
|
+
}
|
|
1254
|
+
})();
|
|
1255
|
+
|
|
1256
|
+
/**
|
|
1257
|
+
* @deprecated May be removed in the next major version
|
|
1258
|
+
*/
|
|
1259
|
+
JavaScriptCompiler.isValidJavaScriptVariableName = function(name) {
|
|
1260
|
+
return (
|
|
1261
|
+
!JavaScriptCompiler.RESERVED_WORDS[name] &&
|
|
1262
|
+
/^[a-zA-Z_$][0-9a-zA-Z_$]*$/.test(name)
|
|
1263
|
+
);
|
|
1264
|
+
};
|
|
1265
|
+
|
|
1266
|
+
function strictLookup(requireTerminal, compiler, parts, i, type) {
|
|
1267
|
+
let stack = compiler.popStack(),
|
|
1268
|
+
len = parts.length;
|
|
1269
|
+
if (requireTerminal) {
|
|
1270
|
+
len--;
|
|
1271
|
+
}
|
|
1272
|
+
|
|
1273
|
+
for (; i < len; i++) {
|
|
1274
|
+
stack = compiler.nameLookup(stack, parts[i], type);
|
|
1275
|
+
}
|
|
1276
|
+
|
|
1277
|
+
if (requireTerminal) {
|
|
1278
|
+
return [
|
|
1279
|
+
compiler.aliasable('container.strict'),
|
|
1280
|
+
'(',
|
|
1281
|
+
stack,
|
|
1282
|
+
', ',
|
|
1283
|
+
compiler.quotedString(parts[i]),
|
|
1284
|
+
', ',
|
|
1285
|
+
JSON.stringify(compiler.source.currentLocation),
|
|
1286
|
+
' )'
|
|
1287
|
+
];
|
|
1288
|
+
} else {
|
|
1289
|
+
return stack;
|
|
1290
|
+
}
|
|
1291
|
+
}
|
|
1292
|
+
|
|
1293
|
+
export default JavaScriptCompiler;
|