mirror of
https://github.com/nodejs/node.git
synced 2024-12-01 16:10:02 +01:00
60a207f5f2
Includes support for bigint syntax so we can remove the acorn-bigint plugin. PR-URL: https://github.com/nodejs/node/pull/28649 Reviewed-By: Ruben Bridgewater <ruben@bridgewater.de> Reviewed-By: Richard Lau <riclau@uk.ibm.com> Reviewed-By: Colin Ihrig <cjihrig@gmail.com> Reviewed-By: Yongsheng Zhang <zyszys98@gmail.com>
152 lines
4.3 KiB
JavaScript
152 lines
4.3 KiB
JavaScript
'use strict';
|
|
|
|
const { Object } = primordials;
|
|
|
|
const acorn = require('internal/deps/acorn/acorn/dist/acorn');
|
|
const walk = require('internal/deps/acorn/acorn-walk/dist/walk');
|
|
const privateMethods =
|
|
require('internal/deps/acorn-plugins/acorn-private-methods/index');
|
|
const classFields =
|
|
require('internal/deps/acorn-plugins/acorn-class-fields/index');
|
|
const numericSeparator =
|
|
require('internal/deps/acorn-plugins/acorn-numeric-separator/index');
|
|
const staticClassFeatures =
|
|
require('internal/deps/acorn-plugins/acorn-static-class-features/index');
|
|
|
|
const parser = acorn.Parser.extend(
|
|
privateMethods,
|
|
classFields,
|
|
numericSeparator,
|
|
staticClassFeatures
|
|
);
|
|
|
|
const noop = () => {};
|
|
const visitorsWithoutAncestors = {
|
|
ClassDeclaration(node, state, c) {
|
|
if (state.ancestors[state.ancestors.length - 2] === state.body) {
|
|
state.prepend(node, `${node.id.name}=`);
|
|
}
|
|
walk.base.ClassDeclaration(node, state, c);
|
|
},
|
|
ForOfStatement(node, state, c) {
|
|
if (node.await === true) {
|
|
state.containsAwait = true;
|
|
}
|
|
walk.base.ForOfStatement(node, state, c);
|
|
},
|
|
FunctionDeclaration(node, state, c) {
|
|
state.prepend(node, `${node.id.name}=`);
|
|
},
|
|
FunctionExpression: noop,
|
|
ArrowFunctionExpression: noop,
|
|
MethodDefinition: noop,
|
|
AwaitExpression(node, state, c) {
|
|
state.containsAwait = true;
|
|
walk.base.AwaitExpression(node, state, c);
|
|
},
|
|
ReturnStatement(node, state, c) {
|
|
state.containsReturn = true;
|
|
walk.base.ReturnStatement(node, state, c);
|
|
},
|
|
VariableDeclaration(node, state, c) {
|
|
if (node.kind === 'var' ||
|
|
state.ancestors[state.ancestors.length - 2] === state.body) {
|
|
if (node.declarations.length === 1) {
|
|
state.replace(node.start, node.start + node.kind.length, 'void');
|
|
} else {
|
|
state.replace(node.start, node.start + node.kind.length, 'void (');
|
|
}
|
|
|
|
for (const decl of node.declarations) {
|
|
state.prepend(decl, '(');
|
|
state.append(decl, decl.init ? ')' : '=undefined)');
|
|
}
|
|
|
|
if (node.declarations.length !== 1) {
|
|
state.append(node.declarations[node.declarations.length - 1], ')');
|
|
}
|
|
}
|
|
|
|
walk.base.VariableDeclaration(node, state, c);
|
|
}
|
|
};
|
|
|
|
const visitors = {};
|
|
for (const nodeType of Object.keys(walk.base)) {
|
|
const callback = visitorsWithoutAncestors[nodeType] || walk.base[nodeType];
|
|
visitors[nodeType] = (node, state, c) => {
|
|
const isNew = node !== state.ancestors[state.ancestors.length - 1];
|
|
if (isNew) {
|
|
state.ancestors.push(node);
|
|
}
|
|
callback(node, state, c);
|
|
if (isNew) {
|
|
state.ancestors.pop();
|
|
}
|
|
};
|
|
}
|
|
|
|
function processTopLevelAwait(src) {
|
|
const wrapped = `(async () => { ${src} })()`;
|
|
const wrappedArray = wrapped.split('');
|
|
let root;
|
|
try {
|
|
root = parser.parse(wrapped, { ecmaVersion: 11 });
|
|
} catch {
|
|
return null;
|
|
}
|
|
const body = root.body[0].expression.callee.body;
|
|
const state = {
|
|
body,
|
|
ancestors: [],
|
|
replace(from, to, str) {
|
|
for (var i = from; i < to; i++) {
|
|
wrappedArray[i] = '';
|
|
}
|
|
if (from === to) str += wrappedArray[from];
|
|
wrappedArray[from] = str;
|
|
},
|
|
prepend(node, str) {
|
|
wrappedArray[node.start] = str + wrappedArray[node.start];
|
|
},
|
|
append(node, str) {
|
|
wrappedArray[node.end - 1] += str;
|
|
},
|
|
containsAwait: false,
|
|
containsReturn: false
|
|
};
|
|
|
|
walk.recursive(body, state, visitors);
|
|
|
|
// Do not transform if
|
|
// 1. False alarm: there isn't actually an await expression.
|
|
// 2. There is a top-level return, which is not allowed.
|
|
if (!state.containsAwait || state.containsReturn) {
|
|
return null;
|
|
}
|
|
|
|
const last = body.body[body.body.length - 1];
|
|
if (last.type === 'ExpressionStatement') {
|
|
// For an expression statement of the form
|
|
// ( expr ) ;
|
|
// ^^^^^^^^^^ // last
|
|
// ^^^^ // last.expression
|
|
//
|
|
// We do not want the left parenthesis before the `return` keyword;
|
|
// therefore we prepend the `return (` to `last`.
|
|
//
|
|
// On the other hand, we do not want the right parenthesis after the
|
|
// semicolon. Since there can only be more right parentheses between
|
|
// last.expression.end and the semicolon, appending one more to
|
|
// last.expression should be fine.
|
|
state.prepend(last, 'return (');
|
|
state.append(last.expression, ')');
|
|
}
|
|
|
|
return wrappedArray.join('');
|
|
}
|
|
|
|
module.exports = {
|
|
processTopLevelAwait
|
|
};
|