puppeteer/utils/node6-transform/TransformAsyncFunctions.js

114 lines
3.2 KiB
JavaScript
Raw Normal View History

/**
* Copyright 2017 Google Inc. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
const esprima = require('esprima');
const ESTreeWalker = require('../ESTreeWalker');
// This is converted from Babel's "transform-async-to-generator"
// https://babeljs.io/docs/plugins/transform-async-to-generator/
const asyncToGenerator = fn => {
const gen = fn.call(this);
return new Promise((resolve, reject) => {
function step(key, arg) {
let info, value;
try {
info = gen[key](arg);
value = info.value;
} catch (error) {
reject(error);
return;
}
if (info.done) {
resolve(value);
} else {
return Promise.resolve(value).then(
value => {
step('next', value);
},
err => {
step('throw', err);
});
}
}
return step('next');
});
};
/**
* @param {string} text
*/
function transformAsyncFunctions(text) {
const edits = [];
const ast = esprima.parseScript(text, {range: true});
const walker = new ESTreeWalker(node => {
if (node.type === 'FunctionExpression' || node.type === 'FunctionDeclaration' || node.type === 'ArrowFunctionExpression')
onFunction(node);
else if (node.type === 'AwaitExpression')
onAwait(node);
});
walker.walk(ast);
edits.sort((a, b) => b.from - a.from);
for (const {replacement, from, to} of edits)
text = text.substring(0, from) + replacement + text.substring(to);
return text;
/**
* @param {ESTree.Node} node
*/
function onFunction(node) {
if (!node.async) return;
let range;
if (node.parent.type === 'MethodDefinition')
range = node.parent.range;
else
range = node.range;
const index = text.substring(range[0], range[1]).indexOf('async') + range[0];
insertText(index, index + 'async'.length, '/* async */');
let before = `{return (${asyncToGenerator.toString()})(function*()`;
let after = `);}`;
if (node.body.type !== 'BlockStatement') {
before += `{ return `;
after = `; }` + after;
}
insertText(node.body.range[0], node.body.range[0], before);
insertText(node.body.range[1], node.body.range[1], after);
}
/**
* @param {ESTree.Node} node
*/
function onAwait(node) {
const index = text.substring(node.range[0], node.range[1]).indexOf('await') + node.range[0];
insertText(index, index + 'await'.length, '(yield');
insertText(node.range[1], node.range[1], ')');
}
/**
* @param {number} from
* @param {number} to
* @param {string} replacement
*/
function insertText(from, to, replacement) {
edits.push({from, to, replacement});
}
}
module.exports = transformAsyncFunctions;