Implement block decorators
These allow for a given block to be wrapped in helper methods or metadata and allow for more control over the current container and method before the code is run.
This commit is contained in:
@@ -64,6 +64,7 @@ JavaScriptCompiler.prototype = {
|
||||
this.name = this.environment.name;
|
||||
this.isChild = !!context;
|
||||
this.context = context || {
|
||||
decorators: [],
|
||||
programs: [],
|
||||
environments: []
|
||||
};
|
||||
@@ -81,7 +82,7 @@ JavaScriptCompiler.prototype = {
|
||||
|
||||
this.compileChildren(environment, options);
|
||||
|
||||
this.useDepths = this.useDepths || environment.useDepths || this.options.compat;
|
||||
this.useDepths = this.useDepths || environment.useDepths || environment.useDecorators || this.options.compat;
|
||||
this.useBlockParams = this.useBlockParams || environment.useBlockParams;
|
||||
|
||||
let opcodes = environment.opcodes,
|
||||
@@ -107,16 +108,43 @@ JavaScriptCompiler.prototype = {
|
||||
throw new Exception('Compile completed with content left on stack');
|
||||
}
|
||||
|
||||
if (!this.decorators.isEmpty()) {
|
||||
this.useDecorators = true;
|
||||
|
||||
this.decorators.prepend('var decorators = container.decorators;\n');
|
||||
this.decorators.push('return fn;');
|
||||
|
||||
if (asObject) {
|
||||
this.decorators = Function.apply(this, ['fn', 'props', 'container', 'depth0', 'data', 'blockParams', 'depths', this.decorators.merge()]);
|
||||
} else {
|
||||
this.decorators.prepend('function(fn, props, container, depth0, data, blockParams, depths) {\n');
|
||||
this.decorators.push('}\n');
|
||||
this.decorators = this.decorators.merge();
|
||||
}
|
||||
} else {
|
||||
this.decorators = undefined;
|
||||
}
|
||||
|
||||
let fn = this.createFunctionContext(asObject);
|
||||
if (!this.isChild) {
|
||||
let ret = {
|
||||
compiler: this.compilerInfo(),
|
||||
main: fn
|
||||
};
|
||||
let programs = this.context.programs;
|
||||
|
||||
if (this.decorators) {
|
||||
ret.main_d = this.decorators; // eslint-disable-line camelcase
|
||||
ret.useDecorators = true;
|
||||
}
|
||||
|
||||
let {programs, decorators} = this.context;
|
||||
for (i = 0, l = programs.length; i < l; i++) {
|
||||
if (programs[i]) {
|
||||
ret[i] = programs[i];
|
||||
if (decorators[i]) {
|
||||
ret[i + '_d'] = decorators[i];
|
||||
ret.useDecorators = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -163,6 +191,7 @@ JavaScriptCompiler.prototype = {
|
||||
// getContext opcode when it would be a noop
|
||||
this.lastContext = 0;
|
||||
this.source = new CodeGen(this.options.srcName);
|
||||
this.decorators = new CodeGen(this.options.srcName);
|
||||
},
|
||||
|
||||
createFunctionContext: function(asObject) {
|
||||
@@ -561,6 +590,24 @@ JavaScriptCompiler.prototype = {
|
||||
}
|
||||
},
|
||||
|
||||
// [registerDecorator]
|
||||
//
|
||||
// On stack, before: hash, program, params..., ...
|
||||
// On stack, after: ...
|
||||
//
|
||||
// Pops off the decorator's parameters, invokes the decorator,
|
||||
// and inserts the decorator into the decorators list.
|
||||
registerDecorator(paramSize, name) {
|
||||
let foundDecorator = this.nameLookup('decorators', name, 'decorator'),
|
||||
options = this.setupHelperArgs(name, paramSize);
|
||||
|
||||
this.decorators.push([
|
||||
'fn = ',
|
||||
this.decorators.functionCall(foundDecorator, '', ['fn', 'props', 'container', options]),
|
||||
' || fn;'
|
||||
]);
|
||||
},
|
||||
|
||||
// [invokeHelper]
|
||||
//
|
||||
// On stack, before: hash, inverse, program, params..., ...
|
||||
@@ -738,6 +785,7 @@ JavaScriptCompiler.prototype = {
|
||||
child.index = index;
|
||||
child.name = 'program' + index;
|
||||
this.context.programs[index] = compiler.compile(child, options, this.context, !this.precompile);
|
||||
this.context.decorators[index] = compiler.decorators;
|
||||
this.context.environments[index] = child;
|
||||
|
||||
this.useDepths = this.useDepths || compiler.useDepths;
|
||||
@@ -946,7 +994,16 @@ JavaScriptCompiler.prototype = {
|
||||
},
|
||||
|
||||
setupParams: function(helper, paramSize, params) {
|
||||
let options = {}, contexts = [], types = [], ids = [], param;
|
||||
let options = {},
|
||||
contexts = [],
|
||||
types = [],
|
||||
ids = [],
|
||||
objectArgs = !params,
|
||||
param;
|
||||
|
||||
if (objectArgs) {
|
||||
params = [];
|
||||
}
|
||||
|
||||
options.name = this.quotedString(helper);
|
||||
options.hash = this.popStack();
|
||||
@@ -985,6 +1042,10 @@ JavaScriptCompiler.prototype = {
|
||||
}
|
||||
}
|
||||
|
||||
if (objectArgs) {
|
||||
options.args = this.source.generateArray(params);
|
||||
}
|
||||
|
||||
if (this.trackIds) {
|
||||
options.ids = this.source.generateArray(ids);
|
||||
}
|
||||
@@ -1009,9 +1070,11 @@ JavaScriptCompiler.prototype = {
|
||||
this.useRegister('options');
|
||||
params.push('options');
|
||||
return ['options=', options];
|
||||
} else {
|
||||
} else if (params) {
|
||||
params.push(options);
|
||||
return '';
|
||||
} else {
|
||||
return options;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user