/ cloudformation-templates / node_modules / aws-cdk / node_modules / degenerator / dist / src / index.js
index.js
1 "use strict"; 2 var __importDefault = (this && this.__importDefault) || function (mod) { 3 return (mod && mod.__esModule) ? mod : { "default": mod }; 4 }; 5 const util_1 = require("util"); 6 const escodegen_1 = require("escodegen"); 7 const esprima_1 = require("esprima"); 8 const ast_types_1 = require("ast-types"); 9 const vm_1 = require("vm"); 10 const supports_async_1 = __importDefault(require("./supports-async")); 11 const generator_to_promise_1 = __importDefault(require("./generator-to-promise")); 12 /** 13 * Compiles sync JavaScript code into JavaScript with async Functions. 14 * 15 * @param {String} code JavaScript string to convert 16 * @param {Array} names Array of function names to add `await` operators to 17 * @return {String} Converted JavaScript string with async/await injected 18 * @api public 19 */ 20 function degenerator(code, _names, { output = 'async' } = {}) { 21 if (!Array.isArray(_names)) { 22 throw new TypeError('an array of async function "names" is required'); 23 } 24 // Duplicate the `names` array since it's rude to augment the user args 25 const names = _names.slice(0); 26 const ast = esprima_1.parseScript(code); 27 // First pass is to find the `function` nodes and turn them into async or 28 // generator functions only if their body includes `CallExpressions` to 29 // function in `names`. We also add the names of the functions to the `names` 30 // array. We'll iterate several time, as every iteration might add new items 31 // to the `names` array, until no new names were added in the iteration. 32 let lastNamesLength = 0; 33 do { 34 lastNamesLength = names.length; 35 ast_types_1.visit(ast, { 36 visitVariableDeclaration(path) { 37 if (path.node.declarations) { 38 for (let i = 0; i < path.node.declarations.length; i++) { 39 const declaration = path.node.declarations[i]; 40 if (ast_types_1.namedTypes.VariableDeclarator.check(declaration) && 41 ast_types_1.namedTypes.Identifier.check(declaration.init) && 42 ast_types_1.namedTypes.Identifier.check(declaration.id) && 43 checkName(declaration.init.name, names) && 44 !checkName(declaration.id.name, names)) { 45 names.push(declaration.id.name); 46 } 47 } 48 } 49 return false; 50 }, 51 visitAssignmentExpression(path) { 52 if (ast_types_1.namedTypes.Identifier.check(path.node.left) && 53 ast_types_1.namedTypes.Identifier.check(path.node.right) && 54 checkName(path.node.right.name, names) && 55 !checkName(path.node.left.name, names)) { 56 names.push(path.node.left.name); 57 } 58 return false; 59 }, 60 visitFunction(path) { 61 if (path.node.id) { 62 let shouldDegenerate = false; 63 ast_types_1.visit(path.node, { 64 visitCallExpression(path) { 65 if (checkNames(path.node, names)) { 66 shouldDegenerate = true; 67 } 68 return false; 69 } 70 }); 71 if (!shouldDegenerate) { 72 return false; 73 } 74 // Got a "function" expression/statement, 75 // convert it into an async or generator function 76 if (output === 'async') { 77 path.node.async = true; 78 } 79 else if (output === 'generator') { 80 path.node.generator = true; 81 } 82 // Add function name to `names` array 83 if (!checkName(path.node.id.name, names)) { 84 names.push(path.node.id.name); 85 } 86 } 87 this.traverse(path); 88 } 89 }); 90 } while (lastNamesLength !== names.length); 91 // Second pass is for adding `await`/`yield` statements to any function 92 // invocations that match the given `names` array. 93 ast_types_1.visit(ast, { 94 visitCallExpression(path) { 95 if (checkNames(path.node, names)) { 96 // A "function invocation" expression, 97 // we need to inject a `AwaitExpression`/`YieldExpression` 98 const delegate = false; 99 const { name, parent: { node: pNode } } = path; 100 let expr; 101 if (output === 'async') { 102 expr = ast_types_1.builders.awaitExpression(path.node, delegate); 103 } 104 else if (output === 'generator') { 105 expr = ast_types_1.builders.yieldExpression(path.node, delegate); 106 } 107 else { 108 throw new Error('Only "async" and "generator" are allowd `output` values'); 109 } 110 if (ast_types_1.namedTypes.CallExpression.check(pNode)) { 111 pNode.arguments[name] = expr; 112 } 113 else { 114 pNode[name] = expr; 115 } 116 } 117 this.traverse(path); 118 } 119 }); 120 return escodegen_1.generate(ast); 121 } 122 (function (degenerator) { 123 degenerator.supportsAsync = supports_async_1.default; 124 function compile(code, returnName, names, options = {}) { 125 const output = supports_async_1.default ? 'async' : 'generator'; 126 const compiled = degenerator(code, names, Object.assign(Object.assign({}, options), { output })); 127 const fn = vm_1.runInNewContext(`${compiled};${returnName}`, options.sandbox, options); 128 if (typeof fn !== 'function') { 129 throw new Error(`Expected a "function" to be returned for \`${returnName}\`, but got "${typeof fn}"`); 130 } 131 if (isAsyncFunction(fn)) { 132 return fn; 133 } 134 else { 135 const rtn = generator_to_promise_1.default(fn); 136 Object.defineProperty(rtn, 'toString', { 137 value: fn.toString.bind(fn), 138 enumerable: false 139 }); 140 return rtn; 141 } 142 } 143 degenerator.compile = compile; 144 })(degenerator || (degenerator = {})); 145 function isAsyncFunction(fn) { 146 return typeof fn === 'function' && fn.constructor.name === 'AsyncFunction'; 147 } 148 /** 149 * Returns `true` if `node` has a matching name to one of the entries in the 150 * `names` array. 151 * 152 * @param {types.Node} node 153 * @param {Array} names Array of function names to return true for 154 * @return {Boolean} 155 * @api private 156 */ 157 function checkNames({ callee }, names) { 158 let name; 159 if (ast_types_1.namedTypes.Identifier.check(callee)) { 160 name = callee.name; 161 } 162 else if (ast_types_1.namedTypes.MemberExpression.check(callee)) { 163 if (ast_types_1.namedTypes.Identifier.check(callee.object) && 164 ast_types_1.namedTypes.Identifier.check(callee.property)) { 165 name = `${callee.object.name}.${callee.property.name}`; 166 } 167 else { 168 return false; 169 } 170 } 171 else if (ast_types_1.namedTypes.FunctionExpression.check(callee)) { 172 if (callee.id) { 173 name = callee.id.name; 174 } 175 else { 176 return false; 177 } 178 } 179 else { 180 throw new Error(`Don't know how to get name for: ${callee.type}`); 181 } 182 return checkName(name, names); 183 } 184 function checkName(name, names) { 185 // now that we have the `name`, check if any entries match in the `names` array 186 for (let i = 0; i < names.length; i++) { 187 const n = names[i]; 188 if (util_1.isRegExp(n)) { 189 if (n.test(name)) { 190 return true; 191 } 192 } 193 else if (name === n) { 194 return true; 195 } 196 } 197 return false; 198 } 199 module.exports = degenerator; 200 //# sourceMappingURL=index.js.map