fix(node6): fix one line await arrow functions #2198
When the start of the function body was await, the async function transformer behaves non-deterministically and can break.
This commit is contained in:
parent
7d387d8d75
commit
d79eb70267
@ -52,18 +52,26 @@ const asyncToGenerator = fn => {
|
|||||||
* @return {string}
|
* @return {string}
|
||||||
*/
|
*/
|
||||||
function transformAsyncFunctions(text) {
|
function transformAsyncFunctions(text) {
|
||||||
|
/**
|
||||||
|
* @type {!Array<{from: number, to: number, replacement: string}>}
|
||||||
|
*/
|
||||||
const edits = [];
|
const edits = [];
|
||||||
|
|
||||||
const ast = esprima.parseScript(text, {range: true, tolerant: true});
|
const ast = esprima.parseScript(text, {range: true, tolerant: true});
|
||||||
const walker = new ESTreeWalker(node => {
|
const walker = new ESTreeWalker(node => {
|
||||||
if (node.type === 'FunctionExpression' || node.type === 'FunctionDeclaration' || node.type === 'ArrowFunctionExpression')
|
if (node.type === 'FunctionExpression' || node.type === 'FunctionDeclaration' || node.type === 'ArrowFunctionExpression')
|
||||||
onFunction(node);
|
onBeforeFunction(node);
|
||||||
else if (node.type === 'AwaitExpression')
|
else if (node.type === 'AwaitExpression')
|
||||||
onAwait(node);
|
onBeforeAwait(node);
|
||||||
|
}, node => {
|
||||||
|
if (node.type === 'FunctionExpression' || node.type === 'FunctionDeclaration' || node.type === 'ArrowFunctionExpression')
|
||||||
|
onAfterFunction(node);
|
||||||
|
else if (node.type === 'AwaitExpression')
|
||||||
|
onAfterAwait(node);
|
||||||
});
|
});
|
||||||
walker.walk(ast);
|
walker.walk(ast);
|
||||||
|
|
||||||
edits.sort((a, b) => b.from - a.from);
|
edits.reverse();
|
||||||
for (const {replacement, from, to} of edits)
|
for (const {replacement, from, to} of edits)
|
||||||
text = text.substring(0, from) + replacement + text.substring(to);
|
text = text.substring(0, from) + replacement + text.substring(to);
|
||||||
|
|
||||||
@ -72,7 +80,7 @@ function transformAsyncFunctions(text) {
|
|||||||
/**
|
/**
|
||||||
* @param {ESTree.Node} node
|
* @param {ESTree.Node} node
|
||||||
*/
|
*/
|
||||||
function onFunction(node) {
|
function onBeforeFunction(node) {
|
||||||
if (!node.async) return;
|
if (!node.async) return;
|
||||||
|
|
||||||
let range;
|
let range;
|
||||||
@ -84,40 +92,61 @@ function transformAsyncFunctions(text) {
|
|||||||
insertText(index, index + 'async'.length, '/* async */');
|
insertText(index, index + 'async'.length, '/* async */');
|
||||||
|
|
||||||
let before = `{return (${asyncToGenerator.toString()})(function*()`;
|
let before = `{return (${asyncToGenerator.toString()})(function*()`;
|
||||||
let after = `);}`;
|
|
||||||
if (node.body.type !== 'BlockStatement') {
|
if (node.body.type !== 'BlockStatement') {
|
||||||
before += `{ return `;
|
before += `{ return `;
|
||||||
after = `; }` + after;
|
|
||||||
|
|
||||||
// Remove parentheses that might wrap an arrow function
|
// Remove parentheses that might wrap an arrow function
|
||||||
const beforeBody = text.substring(node.range[0], node.body.range[0]);
|
const beforeBody = text.substring(node.range[0], node.body.range[0]);
|
||||||
if (/\(\s*$/.test(beforeBody)) {
|
if (/\(\s*$/.test(beforeBody)) {
|
||||||
const afterBody = text.substring(node.body.range[1], node.range[1]);
|
|
||||||
const openParen = node.range[0] + beforeBody.lastIndexOf('(');
|
const openParen = node.range[0] + beforeBody.lastIndexOf('(');
|
||||||
insertText(openParen, openParen + 1, ' ');
|
insertText(openParen, openParen + 1, ' ');
|
||||||
const closeParen = node.body.range[1] + afterBody.indexOf(')');
|
|
||||||
insertText(closeParen, closeParen + 1, ' ');
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
insertText(node.body.range[0], node.body.range[0], before);
|
insertText(node.body.range[0], node.body.range[0], before);
|
||||||
insertText(node.body.range[1], node.body.range[1], after);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param {ESTree.Node} node
|
* @param {ESTree.Node} node
|
||||||
*/
|
*/
|
||||||
function onAwait(node) {
|
function onAfterFunction(node) {
|
||||||
|
if (!node.async) return;
|
||||||
|
|
||||||
|
let after = `);}`;
|
||||||
|
if (node.body.type !== 'BlockStatement')
|
||||||
|
after = `; }` + after;
|
||||||
|
insertText(node.body.range[1], node.body.range[1], after);
|
||||||
|
|
||||||
|
if (node.body.type !== 'BlockStatement') {
|
||||||
|
// Remove parentheses that might wrap an arrow function
|
||||||
|
const beforeBody = text.substring(node.range[0], node.body.range[0]);
|
||||||
|
if (/\(\s*$/.test(beforeBody)) {
|
||||||
|
const afterBody = text.substring(node.body.range[1], node.range[1]);
|
||||||
|
const closeParen = node.body.range[1] + afterBody.indexOf(')');
|
||||||
|
insertText(closeParen, closeParen + 1, ' ');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {ESTree.Node} node
|
||||||
|
*/
|
||||||
|
function onBeforeAwait(node) {
|
||||||
const index = text.substring(node.range[0], node.range[1]).indexOf('await') + node.range[0];
|
const index = text.substring(node.range[0], node.range[1]).indexOf('await') + node.range[0];
|
||||||
insertText(index, index + 'await'.length, '(yield');
|
insertText(index, index + 'await'.length, '(yield');
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {ESTree.Node} node
|
||||||
|
*/
|
||||||
|
function onAfterAwait(node) {
|
||||||
insertText(node.range[1], node.range[1], ')');
|
insertText(node.range[1], node.range[1], ')');
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param {number} from
|
* @param {number} from
|
||||||
* @param {number} to
|
* @param {number} to
|
||||||
* @param {string} replacement
|
|
||||||
*/
|
*/
|
||||||
function insertText(from, to, replacement) {
|
function insertText(from, to, replacement) {
|
||||||
edits.push({from, to, replacement});
|
edits.push({from, to, replacement});
|
||||||
|
@ -86,6 +86,12 @@ describe('TransformAsyncFunctions', function() {
|
|||||||
expect(output instanceof Promise).toBe(true);
|
expect(output instanceof Promise).toBe(true);
|
||||||
output.then(result => expect(result).toBe(123)).then(done);
|
output.then(result => expect(result).toBe(123)).then(done);
|
||||||
});
|
});
|
||||||
|
it('should work async arrow with await', function(done) {
|
||||||
|
const input = `(async() => await 123)()`;
|
||||||
|
const output = eval(transformAsyncFunctions(input));
|
||||||
|
expect(output instanceof Promise).toBe(true);
|
||||||
|
output.then(result => expect(result).toBe(123)).then(done);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
runner.run();
|
runner.run();
|
||||||
|
Loading…
Reference in New Issue
Block a user