123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111 |
- 'use strict';
- var detect = require('acorn-globals');
- var acorn = require('acorn');
- var walk = require('acorn/dist/walk');
- // hacky fix for https://github.com/marijnh/acorn/issues/227
- function reallyParse(source) {
- return acorn.parse(source, {
- ecmaVersion: 6,
- allowReturnOutsideFunction: true
- });
- }
- module.exports = addWith
- /**
- * Mimic `with` as far as possible but at compile time
- *
- * @param {String} obj The object part of a with expression
- * @param {String} src The body of the with expression
- * @param {Array.<String>} exclude A list of variable names to explicitly exclude
- */
- function addWith(obj, src, exclude) {
- obj = obj + ''
- src = src + ''
- exclude = exclude || []
- exclude = exclude.concat(detect(obj).map(function (global) { return global.name; }))
- var vars = detect(src).map(function (global) { return global.name; })
- .filter(function (v) {
- return exclude.indexOf(v) === -1
- && v !== 'undefined'
- && v !== 'this'
- })
- if (vars.length === 0) return src
- var declareLocal = ''
- var local = 'locals_for_with'
- var result = 'result_of_with'
- if (/^[a-zA-Z0-9$_]+$/.test(obj)) {
- local = obj
- } else {
- while (vars.indexOf(local) != -1 || exclude.indexOf(local) != -1) {
- local += '_'
- }
- declareLocal = 'var ' + local + ' = (' + obj + ')'
- }
- while (vars.indexOf(result) != -1 || exclude.indexOf(result) != -1) {
- result += '_'
- }
- var inputVars = vars.map(function (v) {
- return JSON.stringify(v) + ' in ' + local + '?' +
- local + '.' + v + ':' +
- 'typeof ' + v + '!=="undefined"?' + v + ':undefined'
- })
- src = '(function (' + vars.join(', ') + ') {' +
- src +
- '}.call(this' + inputVars.map(function (v) { return ',' + v; }).join('') + '))'
- return ';' + declareLocal + ';' + unwrapReturns(src, result) + ';'
- }
- /**
- * Take a self calling function, and unwrap it such that return inside the function
- * results in return outside the function
- *
- * @param {String} src Some JavaScript code representing a self-calling function
- * @param {String} result A temporary variable to store the result in
- */
- function unwrapReturns(src, result) {
- var originalSource = src
- var hasReturn = false
- var ast = reallyParse(src)
- var ref
- src = src.split('')
- // get a reference to the function that was inserted to add an inner context
- if ((ref = ast.body).length !== 1
- || (ref = ref[0]).type !== 'ExpressionStatement'
- || (ref = ref.expression).type !== 'CallExpression'
- || (ref = ref.callee).type !== 'MemberExpression' || ref.computed !== false || ref.property.name !== 'call'
- || (ref = ref.object).type !== 'FunctionExpression')
- throw new Error('AST does not seem to represent a self-calling function')
- var fn = ref
- walk.recursive(ast, null, {
- Function: function (node, st, c) {
- if (node === fn) {
- c(node.body, st, "ScopeBody");
- }
- },
- ReturnStatement: function (node) {
- hasReturn = true;
- replace(node, 'return {value: (' + (node.argument ? source(node.argument) : 'undefined') + ')};');
- }
- });
- function source(node) {
- return src.slice(node.start, node.end).join('')
- }
- function replace(node, str) {
- for (var i = node.start; i < node.end; i++) {
- src[i] = ''
- }
- src[node.start] = str
- }
- if (!hasReturn) return originalSource
- else return 'var ' + result + '=' + src.join('') + ';if (' + result + ') return ' + result + '.value'
- }
|