diff --git a/lib/xjst/compiler/base.js b/lib/xjst/compiler/base.js index 6aecea8..bc0e2f7 100644 --- a/lib/xjst/compiler/base.js +++ b/lib/xjst/compiler/base.js @@ -34,6 +34,10 @@ function Compiler(options) { this.renderCacheMap = {}; this.sharedBodies = {}; this.maps = {}; + + // Context extensions from local({}) stmts + this.extensions = {}; + this.program = null; this.inputProgram = null; @@ -66,7 +70,11 @@ Compiler.prototype.generate = function generate(code) { ' return run(templates, ctx);\n' + '};\n' + 'try {\n' + - ' applyc({ $init:true, $exports: exports, $context: {} });\n' + + ' applyc({\n' + + ' $init:true,\n' + + ' $exports: exports,\n' + + ' $context: { recordExtensions: function() {} }\n' + + ' });\n' + '} catch (e) {\n' + ' // Just ignore any errors\n' + '}\n' + @@ -640,6 +648,50 @@ Compiler.prototype.sortGroup = function sortGroup(templates) { return out; }; +Compiler.prototype.registerExtension = function registerExtension(name) { + if (name !== '__proto__') + this.extensions[name] = true; +}; + +Compiler.prototype.getRecordExtensions = function getRecordExtensions() { + var ctx = { type: 'Identifier', name: 'ctx' }; + var body = []; + + Object.keys(this.extensions).forEach(function(name) { + body.push({ + type: 'ExpressionStatement', + expression: { + type: 'AssignmentExpression', + operator: '=', + left: { + type: 'MemberExpression', + computed: false, + object: ctx, + property: { type: 'Identifier', name: name } + }, + + // Apply flags should have boolean value + right: /^__\$a/.test(name) ? { type: 'Literal', value: false } : + { type: 'Identifier', name: 'undefined' } + } + }); + }); + + return { + type: 'FunctionExpression', + id: null, + params: [ ctx ], + defaults: [], + rest: null, + generator: false, + expression: false, + body: { + type: 'BlockStatement', + body: body + } + }; +}; + Compiler.prototype.render = function render(program, bodyOnly) { var stmts = [], initializers = program.init.slice(), @@ -750,7 +802,7 @@ Compiler.prototype.render = function render(program, bodyOnly) { stmts.push(applyc); // Call applyc once to allow users override exports - // [init functions].forEach(function(fn) { fn(exports) }); + // [init functions].forEach(function(fn) { fn(exports, this) }, ctx); stmts.push({ type: 'ExpressionStatement', expression: { @@ -788,7 +840,12 @@ Compiler.prototype.render = function render(program, bodyOnly) { } }, { type: 'ObjectExpression', - properties: [] + properties: [{ + type: 'Property', + key: { type: 'Literal', value: 'recordExtensions' }, + value: this.getRecordExtensions(), + kind: 'init' + }] }] } }); diff --git a/lib/xjst/compiler/entities/body.js b/lib/xjst/compiler/entities/body.js index cfe23ed..35b944c 100644 --- a/lib/xjst/compiler/entities/body.js +++ b/lib/xjst/compiler/entities/body.js @@ -240,6 +240,8 @@ Body.prototype.rollOutLocal = function rollOutLocal(ast, changes, body) { property: { type: 'Identifier', name: right } }; + self.compiler.registerExtension(right); + return sub; }, ctx);