UNPKG

asterx

Version:

Javascript continuos-passing-style callback transformations.

155 lines (141 loc) 8.9 kB
// Generated by CoffeeScript 1.10.0 (function() { var _, ast, fs, source_map; fs = require('fs'); _ = require('lodash'); _.string = require('underscore.string'); _.mixin(_.string.exports()); ast = {}; ast.parse = require('acorn').parse; ast.format = require('escodegen').generate; ast.walk = require('ast-types').visit; ast.create = require('ast-types').builders; ast.types = require('ast-types').namedTypes; source_map = require("./source_map.js"); exports["transform"] = function(code, options, back) { var callback, err, error, output, transformed; try { if (_.isString(options.source_map)) { options.source_map = JSON.parse(options.source_map); } options.source_map_enabled = _.isObject(options.source_map || options.source_map === true); options.source_map_exists = options.source_map_enabled === true && _.has(options.source_map, 'mappings') && !_.isEmpty(options.source_map.mappings); code = ast.parse(code, { locations: options.source_map_enabled }); callback = {}; callback.value = ast.create.identifier(options.callback_value); callback.error_value = ast.create.identifier(options.callback_error_value); callback["null"] = ast.create.identifier('null'); callback.error = ast.create.identifier('error'); ast.walk(code, { visitFunction: function(path) { var declaration; declaration = {}; declaration.statements = path.node.body; declaration.parameters = path.node.params; callback.is_lazy = function() { return _.contains(_.pluck(declaration.parameters, 'name'), callback.value.name); }; callback.is_strict = function() { return _.contains(_.pluck(declaration.parameters, 'name'), callback.error_value.name); }; callback.name = callback.is_lazy() ? callback.value.name : callback.error_value.name; callback.position = _.findIndex(declaration.parameters, function(parameter) { return parameter.name === callback.name; }); if (callback.is_strict() || callback.is_lazy()) { ast.walk(path.node, { visitReturnStatement: function(path) { path.get('argument').replace(callback.is_strict() ? ast.create.callExpression(callback.value, [callback["null"], path.node.argument]) : ast.create.conditionalExpression(callback.value, ast.create.callExpression(callback.value, [callback["null"], path.node.argument]), path.node.argument)); return this.traverse(path); } }); if (callback.is_strict()) { path.get('params', callback.position).replace(callback.value); declaration.statements.body.unshift(ast.create.ifStatement(ast.create.binaryExpression('!==', ast.create.unaryExpression('typeof', callback.value), ast.create.literal('function')), ast.create.returnStatement(ast.create.callExpression(callback.error_value, [ast.create.newExpression(ast.create.identifier('Error'), [ast.create.literal('Missing callback.')])])))); } if (options.inject_try_catch === true) { path.get('body').replace(ast.create.blockStatement([ast.create.tryStatement(declaration.statements, ast.create.catchClause(callback.error, null, ast.create.blockStatement([ast.create.returnStatement(ast.create.conditionalExpression(callback.value, ast.create.callExpression(callback.value, [callback.error]), ast.create.callExpression(callback.error_value, [callback.error])))])))])); } } return this.traverse(path); } }); ast.walk(code, { visitExpression: function(path) { var expression; expression = {}; expression.is_assigned = function() { return ast.types.AssignmentExpression.check(path.node); }; expression.call = expression.is_assigned() ? path.node.right : path.node; expression["arguments"] = expression.call["arguments"]; expression.is_call = function() { return ast.types.CallExpression.check(expression.call); }; callback.is_lazy = function() { return _.contains(_.pluck(expression["arguments"], 'name'), callback.value.name); }; callback.is_strict = function() { return _.contains(_.pluck(expression["arguments"], 'name'), callback.error_value.name); }; if (expression.is_call() && (callback.is_lazy() || callback.is_strict())) { expression.node = path.parent; expression.recipient = expression.is_assigned() ? path.node.left : null; expression.path = expression.is_assigned() ? path.get('right') : path; expression.position = expression.node.name; expression.parent = expression.node.parentPath.value; callback.name = callback.is_lazy() ? callback.value.name : callback.error_value.name; callback.position = _.findIndex(expression["arguments"], function(arg) { return arg.name === callback.name; }); callback.marker = expression.path.get('arguments', callback.position); callback["arguments"] = expression.is_assigned() ? [callback.error, expression.recipient] : [callback.error]; callback.statements = _.rest(expression.parent, expression.position + 1); while (expression.parent.length > (expression.position + 1)) { expression.parent.pop(); } if (callback.is_lazy()) { callback.statements.unshift(ast.create.ifStatement(callback.error, ast.create.returnStatement(ast.create.conditionalExpression(callback.value, ast.create.callExpression(callback.value, [callback.error]), ast.create.callExpression(callback.error_value, [callback.error]))))); } if (expression.is_assigned() && callback.is_strict()) { callback.statements.unshift(ast.create.expressionStatement(ast.create.assignmentExpression('=', expression.recipient, ast.create.objectExpression([ast.create.property('init', ast.create.identifier('error'), callback.error), ast.create.property('init', ast.create.identifier('value'), expression.recipient)])))); } if (options.inject_try_catch === true) { callback.marker.replace(ast.create.functionExpression(null, callback["arguments"], ast.create.blockStatement([ast.create.tryStatement(ast.create.blockStatement(callback.statements), ast.create.catchClause(callback.error, null, ast.create.blockStatement([ast.create.returnStatement(ast.create.conditionalExpression(callback.value, ast.create.callExpression(callback.value, [callback.error]), ast.create.callExpression(callback.error_value, [callback.error])))])))]))); } else { callback.marker.replace(ast.create.functionExpression(null, callback["arguments"], ast.create.blockStatement(callback.statements))); } expression.node.replace(ast.create.returnStatement(expression.call)); } return this.traverse(path); } }); ast.walk(code, { visitProgram: function(path) { path.get('body').unshift(ast.create.variableDeclaration('var', [ast.create.variableDeclarator(callback.error_value, ast.create.functionExpression(null, [callback.error], ast.create.blockStatement([ast.create.variableDeclaration('var', [ast.create.variableDeclarator(ast.create.identifier('target'), ast.create.conditionalExpression(ast.create.binaryExpression('!==', ast.create.unaryExpression('typeof', ast.create.identifier('window')), ast.create.literal('undefined')), ast.create.identifier('window'), ast.create.identifier('global')))]), ast.create.ifStatement(ast.create.identifier('target.onError'), ast.create.returnStatement(ast.create.callExpression(ast.create.identifier('target.onError'), [callback.error])), ast.create.throwStatement(callback.error))])))])); return false; } }); output = {}; transformed = ast.format(code, { sourceMapWithCode: options.source_map_enabled, sourceMap: options.source_map_enabled === true ? options.source_map.sources[0] : void 0 }); output.code = transformed.code || transformed; if (options.source_map_enabled === true) { output.source_map = JSON.parse(transformed.map); output.source_map.file = options.source_map.file; output.source_map.sourceRoot = options.source_map.sourceRoot || ''; if (options.source_map_exists === true) { output.source_map = source_map.map_back(output.source_map, options.source_map); } } return back(null, output); } catch (error) { err = error; return back(err); } }; }).call(this);