You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
1527 lines
49 KiB
1527 lines
49 KiB
"use strict";
|
|
|
|
Object.defineProperty(exports, "__esModule", {
|
|
value: true
|
|
});
|
|
exports.default = void 0;
|
|
|
|
var N = _interopRequireWildcard(require("../types"));
|
|
|
|
var _types2 = require("../tokenizer/types");
|
|
|
|
var _expression = _interopRequireDefault(require("./expression"));
|
|
|
|
var _identifier = require("../util/identifier");
|
|
|
|
var _whitespace = require("../util/whitespace");
|
|
|
|
var _scopeflags = require("../util/scopeflags");
|
|
|
|
var _util = require("./util");
|
|
|
|
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
|
|
|
|
function _getRequireWildcardCache() { if (typeof WeakMap !== "function") return null; var cache = new WeakMap(); _getRequireWildcardCache = function () { return cache; }; return cache; }
|
|
|
|
function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } if (obj === null || typeof obj !== "object" && typeof obj !== "function") { return { default: obj }; } var cache = _getRequireWildcardCache(); if (cache && cache.has(obj)) { return cache.get(obj); } var newObj = {}; var hasPropertyDescriptor = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) { var desc = hasPropertyDescriptor ? Object.getOwnPropertyDescriptor(obj, key) : null; if (desc && (desc.get || desc.set)) { Object.defineProperty(newObj, key, desc); } else { newObj[key] = obj[key]; } } } newObj.default = obj; if (cache) { cache.set(obj, newObj); } return newObj; }
|
|
|
|
const loopLabel = {
|
|
kind: "loop"
|
|
},
|
|
switchLabel = {
|
|
kind: "switch"
|
|
};
|
|
const FUNC_NO_FLAGS = 0b000,
|
|
FUNC_STATEMENT = 0b001,
|
|
FUNC_HANGING_STATEMENT = 0b010,
|
|
FUNC_NULLABLE_ID = 0b100;
|
|
|
|
class StatementParser extends _expression.default {
|
|
parseTopLevel(file, program) {
|
|
program.sourceType = this.options.sourceType;
|
|
program.interpreter = this.parseInterpreterDirective();
|
|
this.parseBlockBody(program, true, true, _types2.types.eof);
|
|
|
|
if (this.inModule && !this.options.allowUndeclaredExports && this.scope.undefinedExports.size > 0) {
|
|
for (let _i = 0, _Array$from = Array.from(this.scope.undefinedExports); _i < _Array$from.length; _i++) {
|
|
const [name] = _Array$from[_i];
|
|
const pos = this.scope.undefinedExports.get(name);
|
|
this.raise(pos, `Export '${name}' is not defined`);
|
|
}
|
|
}
|
|
|
|
file.program = this.finishNode(program, "Program");
|
|
file.comments = this.state.comments;
|
|
if (this.options.tokens) file.tokens = this.tokens;
|
|
return this.finishNode(file, "File");
|
|
}
|
|
|
|
stmtToDirective(stmt) {
|
|
const expr = stmt.expression;
|
|
const directiveLiteral = this.startNodeAt(expr.start, expr.loc.start);
|
|
const directive = this.startNodeAt(stmt.start, stmt.loc.start);
|
|
const raw = this.input.slice(expr.start, expr.end);
|
|
const val = directiveLiteral.value = raw.slice(1, -1);
|
|
this.addExtra(directiveLiteral, "raw", raw);
|
|
this.addExtra(directiveLiteral, "rawValue", val);
|
|
directive.value = this.finishNodeAt(directiveLiteral, "DirectiveLiteral", expr.end, expr.loc.end);
|
|
return this.finishNodeAt(directive, "Directive", stmt.end, stmt.loc.end);
|
|
}
|
|
|
|
parseInterpreterDirective() {
|
|
if (!this.match(_types2.types.interpreterDirective)) {
|
|
return null;
|
|
}
|
|
|
|
const node = this.startNode();
|
|
node.value = this.state.value;
|
|
this.next();
|
|
return this.finishNode(node, "InterpreterDirective");
|
|
}
|
|
|
|
isLet(context) {
|
|
if (!this.isContextual("let")) {
|
|
return false;
|
|
}
|
|
|
|
const next = this.nextTokenStart();
|
|
const nextCh = this.input.charCodeAt(next);
|
|
if (nextCh === 91) return true;
|
|
if (context) return false;
|
|
if (nextCh === 123) return true;
|
|
|
|
if ((0, _identifier.isIdentifierStart)(nextCh)) {
|
|
let pos = next + 1;
|
|
|
|
while ((0, _identifier.isIdentifierChar)(this.input.charCodeAt(pos))) {
|
|
++pos;
|
|
}
|
|
|
|
const ident = this.input.slice(next, pos);
|
|
if (!_identifier.keywordRelationalOperator.test(ident)) return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
parseStatement(context, topLevel) {
|
|
if (this.match(_types2.types.at)) {
|
|
this.parseDecorators(true);
|
|
}
|
|
|
|
return this.parseStatementContent(context, topLevel);
|
|
}
|
|
|
|
parseStatementContent(context, topLevel) {
|
|
let starttype = this.state.type;
|
|
const node = this.startNode();
|
|
let kind;
|
|
|
|
if (this.isLet(context)) {
|
|
starttype = _types2.types._var;
|
|
kind = "let";
|
|
}
|
|
|
|
switch (starttype) {
|
|
case _types2.types._break:
|
|
case _types2.types._continue:
|
|
return this.parseBreakContinueStatement(node, starttype.keyword);
|
|
|
|
case _types2.types._debugger:
|
|
return this.parseDebuggerStatement(node);
|
|
|
|
case _types2.types._do:
|
|
return this.parseDoStatement(node);
|
|
|
|
case _types2.types._for:
|
|
return this.parseForStatement(node);
|
|
|
|
case _types2.types._function:
|
|
if (this.lookaheadCharCode() === 46) break;
|
|
|
|
if (context) {
|
|
if (this.state.strict) {
|
|
this.raise(this.state.start, "In strict mode code, functions can only be declared at top level or inside a block");
|
|
} else if (context !== "if" && context !== "label") {
|
|
this.raise(this.state.start, "In non-strict mode code, functions can only be declared at top level, " + "inside a block, or as the body of an if statement");
|
|
}
|
|
}
|
|
|
|
return this.parseFunctionStatement(node, false, !context);
|
|
|
|
case _types2.types._class:
|
|
if (context) this.unexpected();
|
|
return this.parseClass(node, true);
|
|
|
|
case _types2.types._if:
|
|
return this.parseIfStatement(node);
|
|
|
|
case _types2.types._return:
|
|
return this.parseReturnStatement(node);
|
|
|
|
case _types2.types._switch:
|
|
return this.parseSwitchStatement(node);
|
|
|
|
case _types2.types._throw:
|
|
return this.parseThrowStatement(node);
|
|
|
|
case _types2.types._try:
|
|
return this.parseTryStatement(node);
|
|
|
|
case _types2.types._const:
|
|
case _types2.types._var:
|
|
kind = kind || this.state.value;
|
|
|
|
if (context && kind !== "var") {
|
|
this.raise(this.state.start, "Lexical declaration cannot appear in a single-statement context");
|
|
}
|
|
|
|
return this.parseVarStatement(node, kind);
|
|
|
|
case _types2.types._while:
|
|
return this.parseWhileStatement(node);
|
|
|
|
case _types2.types._with:
|
|
return this.parseWithStatement(node);
|
|
|
|
case _types2.types.braceL:
|
|
return this.parseBlock();
|
|
|
|
case _types2.types.semi:
|
|
return this.parseEmptyStatement(node);
|
|
|
|
case _types2.types._export:
|
|
case _types2.types._import:
|
|
{
|
|
const nextTokenCharCode = this.lookaheadCharCode();
|
|
|
|
if (nextTokenCharCode === 40 || nextTokenCharCode === 46) {
|
|
break;
|
|
}
|
|
|
|
if (!this.options.allowImportExportEverywhere && !topLevel) {
|
|
this.raise(this.state.start, "'import' and 'export' may only appear at the top level");
|
|
}
|
|
|
|
this.next();
|
|
let result;
|
|
|
|
if (starttype === _types2.types._import) {
|
|
result = this.parseImport(node);
|
|
|
|
if (result.type === "ImportDeclaration" && (!result.importKind || result.importKind === "value")) {
|
|
this.sawUnambiguousESM = true;
|
|
}
|
|
} else {
|
|
result = this.parseExport(node);
|
|
|
|
if (result.type === "ExportNamedDeclaration" && (!result.exportKind || result.exportKind === "value") || result.type === "ExportAllDeclaration" && (!result.exportKind || result.exportKind === "value") || result.type === "ExportDefaultDeclaration") {
|
|
this.sawUnambiguousESM = true;
|
|
}
|
|
}
|
|
|
|
this.assertModuleNodeAllowed(node);
|
|
return result;
|
|
}
|
|
|
|
default:
|
|
{
|
|
if (this.isAsyncFunction()) {
|
|
if (context) {
|
|
this.raise(this.state.start, "Async functions can only be declared at the top level or inside a block");
|
|
}
|
|
|
|
this.next();
|
|
return this.parseFunctionStatement(node, true, !context);
|
|
}
|
|
}
|
|
}
|
|
|
|
const maybeName = this.state.value;
|
|
const expr = this.parseExpression();
|
|
|
|
if (starttype === _types2.types.name && expr.type === "Identifier" && this.eat(_types2.types.colon)) {
|
|
return this.parseLabeledStatement(node, maybeName, expr, context);
|
|
} else {
|
|
return this.parseExpressionStatement(node, expr);
|
|
}
|
|
}
|
|
|
|
assertModuleNodeAllowed(node) {
|
|
if (!this.options.allowImportExportEverywhere && !this.inModule) {
|
|
this.raise(node.start, `'import' and 'export' may appear only with 'sourceType: "module"'`, {
|
|
code: "BABEL_PARSER_SOURCETYPE_MODULE_REQUIRED"
|
|
});
|
|
}
|
|
}
|
|
|
|
takeDecorators(node) {
|
|
const decorators = this.state.decoratorStack[this.state.decoratorStack.length - 1];
|
|
|
|
if (decorators.length) {
|
|
node.decorators = decorators;
|
|
this.resetStartLocationFromNode(node, decorators[0]);
|
|
this.state.decoratorStack[this.state.decoratorStack.length - 1] = [];
|
|
}
|
|
}
|
|
|
|
canHaveLeadingDecorator() {
|
|
return this.match(_types2.types._class);
|
|
}
|
|
|
|
parseDecorators(allowExport) {
|
|
const currentContextDecorators = this.state.decoratorStack[this.state.decoratorStack.length - 1];
|
|
|
|
while (this.match(_types2.types.at)) {
|
|
const decorator = this.parseDecorator();
|
|
currentContextDecorators.push(decorator);
|
|
}
|
|
|
|
if (this.match(_types2.types._export)) {
|
|
if (!allowExport) {
|
|
this.unexpected();
|
|
}
|
|
|
|
if (this.hasPlugin("decorators") && !this.getPluginOption("decorators", "decoratorsBeforeExport")) {
|
|
this.raise(this.state.start, "Using the export keyword between a decorator and a class is not allowed. " + "Please use `export @dec class` instead.");
|
|
}
|
|
} else if (!this.canHaveLeadingDecorator()) {
|
|
throw this.raise(this.state.start, "Leading decorators must be attached to a class declaration");
|
|
}
|
|
}
|
|
|
|
parseDecorator() {
|
|
this.expectOnePlugin(["decorators-legacy", "decorators"]);
|
|
const node = this.startNode();
|
|
this.next();
|
|
|
|
if (this.hasPlugin("decorators")) {
|
|
this.state.decoratorStack.push([]);
|
|
const startPos = this.state.start;
|
|
const startLoc = this.state.startLoc;
|
|
let expr;
|
|
|
|
if (this.eat(_types2.types.parenL)) {
|
|
expr = this.parseExpression();
|
|
this.expect(_types2.types.parenR);
|
|
} else {
|
|
expr = this.parseIdentifier(false);
|
|
|
|
while (this.eat(_types2.types.dot)) {
|
|
const node = this.startNodeAt(startPos, startLoc);
|
|
node.object = expr;
|
|
node.property = this.parseIdentifier(true);
|
|
node.computed = false;
|
|
expr = this.finishNode(node, "MemberExpression");
|
|
}
|
|
}
|
|
|
|
node.expression = this.parseMaybeDecoratorArguments(expr);
|
|
this.state.decoratorStack.pop();
|
|
} else {
|
|
node.expression = this.parseExprSubscripts();
|
|
}
|
|
|
|
return this.finishNode(node, "Decorator");
|
|
}
|
|
|
|
parseMaybeDecoratorArguments(expr) {
|
|
if (this.eat(_types2.types.parenL)) {
|
|
const node = this.startNodeAtNode(expr);
|
|
node.callee = expr;
|
|
node.arguments = this.parseCallExpressionArguments(_types2.types.parenR, false);
|
|
this.toReferencedList(node.arguments);
|
|
return this.finishNode(node, "CallExpression");
|
|
}
|
|
|
|
return expr;
|
|
}
|
|
|
|
parseBreakContinueStatement(node, keyword) {
|
|
const isBreak = keyword === "break";
|
|
this.next();
|
|
|
|
if (this.isLineTerminator()) {
|
|
node.label = null;
|
|
} else {
|
|
node.label = this.parseIdentifier();
|
|
this.semicolon();
|
|
}
|
|
|
|
this.verifyBreakContinue(node, keyword);
|
|
return this.finishNode(node, isBreak ? "BreakStatement" : "ContinueStatement");
|
|
}
|
|
|
|
verifyBreakContinue(node, keyword) {
|
|
const isBreak = keyword === "break";
|
|
let i;
|
|
|
|
for (i = 0; i < this.state.labels.length; ++i) {
|
|
const lab = this.state.labels[i];
|
|
|
|
if (node.label == null || lab.name === node.label.name) {
|
|
if (lab.kind != null && (isBreak || lab.kind === "loop")) break;
|
|
if (node.label && isBreak) break;
|
|
}
|
|
}
|
|
|
|
if (i === this.state.labels.length) {
|
|
this.raise(node.start, "Unsyntactic " + keyword);
|
|
}
|
|
}
|
|
|
|
parseDebuggerStatement(node) {
|
|
this.next();
|
|
this.semicolon();
|
|
return this.finishNode(node, "DebuggerStatement");
|
|
}
|
|
|
|
parseHeaderExpression() {
|
|
this.expect(_types2.types.parenL);
|
|
const val = this.parseExpression();
|
|
this.expect(_types2.types.parenR);
|
|
return val;
|
|
}
|
|
|
|
parseDoStatement(node) {
|
|
this.next();
|
|
this.state.labels.push(loopLabel);
|
|
node.body = this.withTopicForbiddingContext(() => this.parseStatement("do"));
|
|
this.state.labels.pop();
|
|
this.expect(_types2.types._while);
|
|
node.test = this.parseHeaderExpression();
|
|
this.eat(_types2.types.semi);
|
|
return this.finishNode(node, "DoWhileStatement");
|
|
}
|
|
|
|
parseForStatement(node) {
|
|
this.next();
|
|
this.state.labels.push(loopLabel);
|
|
let awaitAt = -1;
|
|
|
|
if (this.isAwaitAllowed() && this.eatContextual("await")) {
|
|
awaitAt = this.state.lastTokStart;
|
|
}
|
|
|
|
this.scope.enter(_scopeflags.SCOPE_OTHER);
|
|
this.expect(_types2.types.parenL);
|
|
|
|
if (this.match(_types2.types.semi)) {
|
|
if (awaitAt > -1) {
|
|
this.unexpected(awaitAt);
|
|
}
|
|
|
|
return this.parseFor(node, null);
|
|
}
|
|
|
|
const isLet = this.isLet();
|
|
|
|
if (this.match(_types2.types._var) || this.match(_types2.types._const) || isLet) {
|
|
const init = this.startNode();
|
|
const kind = isLet ? "let" : this.state.value;
|
|
this.next();
|
|
this.parseVar(init, true, kind);
|
|
this.finishNode(init, "VariableDeclaration");
|
|
|
|
if ((this.match(_types2.types._in) || this.isContextual("of")) && init.declarations.length === 1) {
|
|
return this.parseForIn(node, init, awaitAt);
|
|
}
|
|
|
|
if (awaitAt > -1) {
|
|
this.unexpected(awaitAt);
|
|
}
|
|
|
|
return this.parseFor(node, init);
|
|
}
|
|
|
|
const refExpressionErrors = new _util.ExpressionErrors();
|
|
const init = this.parseExpression(true, refExpressionErrors);
|
|
|
|
if (this.match(_types2.types._in) || this.isContextual("of")) {
|
|
this.toAssignable(init);
|
|
const description = this.isContextual("of") ? "for-of statement" : "for-in statement";
|
|
this.checkLVal(init, undefined, undefined, description);
|
|
return this.parseForIn(node, init, awaitAt);
|
|
} else {
|
|
this.checkExpressionErrors(refExpressionErrors, true);
|
|
}
|
|
|
|
if (awaitAt > -1) {
|
|
this.unexpected(awaitAt);
|
|
}
|
|
|
|
return this.parseFor(node, init);
|
|
}
|
|
|
|
parseFunctionStatement(node, isAsync, declarationPosition) {
|
|
this.next();
|
|
return this.parseFunction(node, FUNC_STATEMENT | (declarationPosition ? 0 : FUNC_HANGING_STATEMENT), isAsync);
|
|
}
|
|
|
|
parseIfStatement(node) {
|
|
this.next();
|
|
node.test = this.parseHeaderExpression();
|
|
node.consequent = this.parseStatement("if");
|
|
node.alternate = this.eat(_types2.types._else) ? this.parseStatement("if") : null;
|
|
return this.finishNode(node, "IfStatement");
|
|
}
|
|
|
|
parseReturnStatement(node) {
|
|
if (!this.scope.inFunction && !this.options.allowReturnOutsideFunction) {
|
|
this.raise(this.state.start, "'return' outside of function");
|
|
}
|
|
|
|
this.next();
|
|
|
|
if (this.isLineTerminator()) {
|
|
node.argument = null;
|
|
} else {
|
|
node.argument = this.parseExpression();
|
|
this.semicolon();
|
|
}
|
|
|
|
return this.finishNode(node, "ReturnStatement");
|
|
}
|
|
|
|
parseSwitchStatement(node) {
|
|
this.next();
|
|
node.discriminant = this.parseHeaderExpression();
|
|
const cases = node.cases = [];
|
|
this.expect(_types2.types.braceL);
|
|
this.state.labels.push(switchLabel);
|
|
this.scope.enter(_scopeflags.SCOPE_OTHER);
|
|
let cur;
|
|
|
|
for (let sawDefault; !this.match(_types2.types.braceR);) {
|
|
if (this.match(_types2.types._case) || this.match(_types2.types._default)) {
|
|
const isCase = this.match(_types2.types._case);
|
|
if (cur) this.finishNode(cur, "SwitchCase");
|
|
cases.push(cur = this.startNode());
|
|
cur.consequent = [];
|
|
this.next();
|
|
|
|
if (isCase) {
|
|
cur.test = this.parseExpression();
|
|
} else {
|
|
if (sawDefault) {
|
|
this.raise(this.state.lastTokStart, "Multiple default clauses");
|
|
}
|
|
|
|
sawDefault = true;
|
|
cur.test = null;
|
|
}
|
|
|
|
this.expect(_types2.types.colon);
|
|
} else {
|
|
if (cur) {
|
|
cur.consequent.push(this.parseStatement(null));
|
|
} else {
|
|
this.unexpected();
|
|
}
|
|
}
|
|
}
|
|
|
|
this.scope.exit();
|
|
if (cur) this.finishNode(cur, "SwitchCase");
|
|
this.next();
|
|
this.state.labels.pop();
|
|
return this.finishNode(node, "SwitchStatement");
|
|
}
|
|
|
|
parseThrowStatement(node) {
|
|
this.next();
|
|
|
|
if (_whitespace.lineBreak.test(this.input.slice(this.state.lastTokEnd, this.state.start))) {
|
|
this.raise(this.state.lastTokEnd, "Illegal newline after throw");
|
|
}
|
|
|
|
node.argument = this.parseExpression();
|
|
this.semicolon();
|
|
return this.finishNode(node, "ThrowStatement");
|
|
}
|
|
|
|
parseTryStatement(node) {
|
|
this.next();
|
|
node.block = this.parseBlock();
|
|
node.handler = null;
|
|
|
|
if (this.match(_types2.types._catch)) {
|
|
const clause = this.startNode();
|
|
this.next();
|
|
|
|
if (this.match(_types2.types.parenL)) {
|
|
this.expect(_types2.types.parenL);
|
|
clause.param = this.parseBindingAtom();
|
|
const simple = clause.param.type === "Identifier";
|
|
this.scope.enter(simple ? _scopeflags.SCOPE_SIMPLE_CATCH : 0);
|
|
this.checkLVal(clause.param, _scopeflags.BIND_LEXICAL, null, "catch clause");
|
|
this.expect(_types2.types.parenR);
|
|
} else {
|
|
clause.param = null;
|
|
this.scope.enter(_scopeflags.SCOPE_OTHER);
|
|
}
|
|
|
|
clause.body = this.withTopicForbiddingContext(() => this.parseBlock(false, false));
|
|
this.scope.exit();
|
|
node.handler = this.finishNode(clause, "CatchClause");
|
|
}
|
|
|
|
node.finalizer = this.eat(_types2.types._finally) ? this.parseBlock() : null;
|
|
|
|
if (!node.handler && !node.finalizer) {
|
|
this.raise(node.start, "Missing catch or finally clause");
|
|
}
|
|
|
|
return this.finishNode(node, "TryStatement");
|
|
}
|
|
|
|
parseVarStatement(node, kind) {
|
|
this.next();
|
|
this.parseVar(node, false, kind);
|
|
this.semicolon();
|
|
return this.finishNode(node, "VariableDeclaration");
|
|
}
|
|
|
|
parseWhileStatement(node) {
|
|
this.next();
|
|
node.test = this.parseHeaderExpression();
|
|
this.state.labels.push(loopLabel);
|
|
node.body = this.withTopicForbiddingContext(() => this.parseStatement("while"));
|
|
this.state.labels.pop();
|
|
return this.finishNode(node, "WhileStatement");
|
|
}
|
|
|
|
parseWithStatement(node) {
|
|
if (this.state.strict) {
|
|
this.raise(this.state.start, "'with' in strict mode");
|
|
}
|
|
|
|
this.next();
|
|
node.object = this.parseHeaderExpression();
|
|
node.body = this.withTopicForbiddingContext(() => this.parseStatement("with"));
|
|
return this.finishNode(node, "WithStatement");
|
|
}
|
|
|
|
parseEmptyStatement(node) {
|
|
this.next();
|
|
return this.finishNode(node, "EmptyStatement");
|
|
}
|
|
|
|
parseLabeledStatement(node, maybeName, expr, context) {
|
|
for (let _i2 = 0, _this$state$labels = this.state.labels; _i2 < _this$state$labels.length; _i2++) {
|
|
const label = _this$state$labels[_i2];
|
|
|
|
if (label.name === maybeName) {
|
|
this.raise(expr.start, `Label '${maybeName}' is already declared`);
|
|
}
|
|
}
|
|
|
|
const kind = this.state.type.isLoop ? "loop" : this.match(_types2.types._switch) ? "switch" : null;
|
|
|
|
for (let i = this.state.labels.length - 1; i >= 0; i--) {
|
|
const label = this.state.labels[i];
|
|
|
|
if (label.statementStart === node.start) {
|
|
label.statementStart = this.state.start;
|
|
label.kind = kind;
|
|
} else {
|
|
break;
|
|
}
|
|
}
|
|
|
|
this.state.labels.push({
|
|
name: maybeName,
|
|
kind: kind,
|
|
statementStart: this.state.start
|
|
});
|
|
node.body = this.parseStatement(context ? context.indexOf("label") === -1 ? context + "label" : context : "label");
|
|
this.state.labels.pop();
|
|
node.label = expr;
|
|
return this.finishNode(node, "LabeledStatement");
|
|
}
|
|
|
|
parseExpressionStatement(node, expr) {
|
|
node.expression = expr;
|
|
this.semicolon();
|
|
return this.finishNode(node, "ExpressionStatement");
|
|
}
|
|
|
|
parseBlock(allowDirectives = false, createNewLexicalScope = true) {
|
|
const node = this.startNode();
|
|
this.expect(_types2.types.braceL);
|
|
|
|
if (createNewLexicalScope) {
|
|
this.scope.enter(_scopeflags.SCOPE_OTHER);
|
|
}
|
|
|
|
this.parseBlockBody(node, allowDirectives, false, _types2.types.braceR);
|
|
|
|
if (createNewLexicalScope) {
|
|
this.scope.exit();
|
|
}
|
|
|
|
return this.finishNode(node, "BlockStatement");
|
|
}
|
|
|
|
isValidDirective(stmt) {
|
|
return stmt.type === "ExpressionStatement" && stmt.expression.type === "StringLiteral" && !stmt.expression.extra.parenthesized;
|
|
}
|
|
|
|
parseBlockBody(node, allowDirectives, topLevel, end) {
|
|
const body = node.body = [];
|
|
const directives = node.directives = [];
|
|
this.parseBlockOrModuleBlockBody(body, allowDirectives ? directives : undefined, topLevel, end);
|
|
}
|
|
|
|
parseBlockOrModuleBlockBody(body, directives, topLevel, end) {
|
|
let parsedNonDirective = false;
|
|
let oldStrict;
|
|
let octalPosition;
|
|
|
|
while (!this.eat(end)) {
|
|
if (!parsedNonDirective && this.state.containsOctal && !octalPosition) {
|
|
octalPosition = this.state.octalPosition;
|
|
}
|
|
|
|
const stmt = this.parseStatement(null, topLevel);
|
|
|
|
if (directives && !parsedNonDirective && this.isValidDirective(stmt)) {
|
|
const directive = this.stmtToDirective(stmt);
|
|
directives.push(directive);
|
|
|
|
if (oldStrict === undefined && directive.value.value === "use strict") {
|
|
oldStrict = this.state.strict;
|
|
this.setStrict(true);
|
|
|
|
if (octalPosition) {
|
|
this.raise(octalPosition, "Octal literal in strict mode");
|
|
}
|
|
}
|
|
|
|
continue;
|
|
}
|
|
|
|
parsedNonDirective = true;
|
|
body.push(stmt);
|
|
}
|
|
|
|
if (oldStrict === false) {
|
|
this.setStrict(false);
|
|
}
|
|
}
|
|
|
|
parseFor(node, init) {
|
|
node.init = init;
|
|
this.expect(_types2.types.semi);
|
|
node.test = this.match(_types2.types.semi) ? null : this.parseExpression();
|
|
this.expect(_types2.types.semi);
|
|
node.update = this.match(_types2.types.parenR) ? null : this.parseExpression();
|
|
this.expect(_types2.types.parenR);
|
|
node.body = this.withTopicForbiddingContext(() => this.parseStatement("for"));
|
|
this.scope.exit();
|
|
this.state.labels.pop();
|
|
return this.finishNode(node, "ForStatement");
|
|
}
|
|
|
|
parseForIn(node, init, awaitAt) {
|
|
const isForIn = this.match(_types2.types._in);
|
|
this.next();
|
|
|
|
if (isForIn) {
|
|
if (awaitAt > -1) this.unexpected(awaitAt);
|
|
} else {
|
|
node.await = awaitAt > -1;
|
|
}
|
|
|
|
if (init.type === "VariableDeclaration" && init.declarations[0].init != null && (!isForIn || this.state.strict || init.kind !== "var" || init.declarations[0].id.type !== "Identifier")) {
|
|
this.raise(init.start, `${isForIn ? "for-in" : "for-of"} loop variable declaration may not have an initializer`);
|
|
} else if (init.type === "AssignmentPattern") {
|
|
this.raise(init.start, "Invalid left-hand side in for-loop");
|
|
}
|
|
|
|
node.left = init;
|
|
node.right = isForIn ? this.parseExpression() : this.parseMaybeAssign();
|
|
this.expect(_types2.types.parenR);
|
|
node.body = this.withTopicForbiddingContext(() => this.parseStatement("for"));
|
|
this.scope.exit();
|
|
this.state.labels.pop();
|
|
return this.finishNode(node, isForIn ? "ForInStatement" : "ForOfStatement");
|
|
}
|
|
|
|
parseVar(node, isFor, kind) {
|
|
const declarations = node.declarations = [];
|
|
const isTypescript = this.hasPlugin("typescript");
|
|
node.kind = kind;
|
|
|
|
for (;;) {
|
|
const decl = this.startNode();
|
|
this.parseVarId(decl, kind);
|
|
|
|
if (this.eat(_types2.types.eq)) {
|
|
decl.init = this.parseMaybeAssign(isFor);
|
|
} else {
|
|
if (kind === "const" && !(this.match(_types2.types._in) || this.isContextual("of"))) {
|
|
if (!isTypescript) {
|
|
this.unexpected();
|
|
}
|
|
} else if (decl.id.type !== "Identifier" && !(isFor && (this.match(_types2.types._in) || this.isContextual("of")))) {
|
|
this.raise(this.state.lastTokEnd, "Complex binding patterns require an initialization value");
|
|
}
|
|
|
|
decl.init = null;
|
|
}
|
|
|
|
declarations.push(this.finishNode(decl, "VariableDeclarator"));
|
|
if (!this.eat(_types2.types.comma)) break;
|
|
}
|
|
|
|
return node;
|
|
}
|
|
|
|
parseVarId(decl, kind) {
|
|
decl.id = this.parseBindingAtom();
|
|
this.checkLVal(decl.id, kind === "var" ? _scopeflags.BIND_VAR : _scopeflags.BIND_LEXICAL, undefined, "variable declaration", kind !== "var");
|
|
}
|
|
|
|
parseFunction(node, statement = FUNC_NO_FLAGS, isAsync = false) {
|
|
const isStatement = statement & FUNC_STATEMENT;
|
|
const isHangingStatement = statement & FUNC_HANGING_STATEMENT;
|
|
const requireId = !!isStatement && !(statement & FUNC_NULLABLE_ID);
|
|
this.initFunction(node, isAsync);
|
|
|
|
if (this.match(_types2.types.star) && isHangingStatement) {
|
|
this.raise(this.state.start, "Generators can only be declared at the top level or inside a block");
|
|
}
|
|
|
|
node.generator = this.eat(_types2.types.star);
|
|
|
|
if (isStatement) {
|
|
node.id = this.parseFunctionId(requireId);
|
|
}
|
|
|
|
const oldMaybeInArrowParameters = this.state.maybeInArrowParameters;
|
|
const oldYieldPos = this.state.yieldPos;
|
|
const oldAwaitPos = this.state.awaitPos;
|
|
this.state.maybeInArrowParameters = false;
|
|
this.state.yieldPos = -1;
|
|
this.state.awaitPos = -1;
|
|
this.scope.enter((0, _scopeflags.functionFlags)(node.async, node.generator));
|
|
|
|
if (!isStatement) {
|
|
node.id = this.parseFunctionId();
|
|
}
|
|
|
|
this.parseFunctionParams(node);
|
|
this.withTopicForbiddingContext(() => {
|
|
this.parseFunctionBodyAndFinish(node, isStatement ? "FunctionDeclaration" : "FunctionExpression");
|
|
});
|
|
this.scope.exit();
|
|
|
|
if (isStatement && !isHangingStatement) {
|
|
this.registerFunctionStatementId(node);
|
|
}
|
|
|
|
this.state.maybeInArrowParameters = oldMaybeInArrowParameters;
|
|
this.state.yieldPos = oldYieldPos;
|
|
this.state.awaitPos = oldAwaitPos;
|
|
return node;
|
|
}
|
|
|
|
parseFunctionId(requireId) {
|
|
return requireId || this.match(_types2.types.name) ? this.parseIdentifier() : null;
|
|
}
|
|
|
|
parseFunctionParams(node, allowModifiers) {
|
|
const oldInParameters = this.state.inParameters;
|
|
this.state.inParameters = true;
|
|
this.expect(_types2.types.parenL);
|
|
node.params = this.parseBindingList(_types2.types.parenR, 41, false, allowModifiers);
|
|
this.state.inParameters = oldInParameters;
|
|
this.checkYieldAwaitInDefaultParams();
|
|
}
|
|
|
|
registerFunctionStatementId(node) {
|
|
if (!node.id) return;
|
|
this.scope.declareName(node.id.name, this.state.strict || node.generator || node.async ? this.scope.treatFunctionsAsVar ? _scopeflags.BIND_VAR : _scopeflags.BIND_LEXICAL : _scopeflags.BIND_FUNCTION, node.id.start);
|
|
}
|
|
|
|
parseClass(node, isStatement, optionalId) {
|
|
this.next();
|
|
this.takeDecorators(node);
|
|
const oldStrict = this.state.strict;
|
|
this.state.strict = true;
|
|
this.parseClassId(node, isStatement, optionalId);
|
|
this.parseClassSuper(node);
|
|
node.body = this.parseClassBody(!!node.superClass);
|
|
this.state.strict = oldStrict;
|
|
return this.finishNode(node, isStatement ? "ClassDeclaration" : "ClassExpression");
|
|
}
|
|
|
|
isClassProperty() {
|
|
return this.match(_types2.types.eq) || this.match(_types2.types.semi) || this.match(_types2.types.braceR);
|
|
}
|
|
|
|
isClassMethod() {
|
|
return this.match(_types2.types.parenL);
|
|
}
|
|
|
|
isNonstaticConstructor(method) {
|
|
return !method.computed && !method.static && (method.key.name === "constructor" || method.key.value === "constructor");
|
|
}
|
|
|
|
parseClassBody(constructorAllowsSuper) {
|
|
this.classScope.enter();
|
|
const state = {
|
|
hadConstructor: false
|
|
};
|
|
let decorators = [];
|
|
const classBody = this.startNode();
|
|
classBody.body = [];
|
|
this.expect(_types2.types.braceL);
|
|
this.withTopicForbiddingContext(() => {
|
|
while (!this.eat(_types2.types.braceR)) {
|
|
if (this.eat(_types2.types.semi)) {
|
|
if (decorators.length > 0) {
|
|
throw this.raise(this.state.lastTokEnd, "Decorators must not be followed by a semicolon");
|
|
}
|
|
|
|
continue;
|
|
}
|
|
|
|
if (this.match(_types2.types.at)) {
|
|
decorators.push(this.parseDecorator());
|
|
continue;
|
|
}
|
|
|
|
const member = this.startNode();
|
|
|
|
if (decorators.length) {
|
|
member.decorators = decorators;
|
|
this.resetStartLocationFromNode(member, decorators[0]);
|
|
decorators = [];
|
|
}
|
|
|
|
this.parseClassMember(classBody, member, state, constructorAllowsSuper);
|
|
|
|
if (member.kind === "constructor" && member.decorators && member.decorators.length > 0) {
|
|
this.raise(member.start, "Decorators can't be used with a constructor. Did you mean '@dec class { ... }'?");
|
|
}
|
|
}
|
|
});
|
|
|
|
if (decorators.length) {
|
|
throw this.raise(this.state.start, "You have trailing decorators with no method");
|
|
}
|
|
|
|
this.classScope.exit();
|
|
return this.finishNode(classBody, "ClassBody");
|
|
}
|
|
|
|
parseClassMember(classBody, member, state, constructorAllowsSuper) {
|
|
let isStatic = false;
|
|
const containsEsc = this.state.containsEsc;
|
|
|
|
if (this.match(_types2.types.name) && this.state.value === "static") {
|
|
const key = this.parseIdentifier(true);
|
|
|
|
if (this.isClassMethod()) {
|
|
const method = member;
|
|
method.kind = "method";
|
|
method.computed = false;
|
|
method.key = key;
|
|
method.static = false;
|
|
this.pushClassMethod(classBody, method, false, false, false, false);
|
|
return;
|
|
} else if (this.isClassProperty()) {
|
|
const prop = member;
|
|
prop.computed = false;
|
|
prop.key = key;
|
|
prop.static = false;
|
|
classBody.body.push(this.parseClassProperty(prop));
|
|
return;
|
|
} else if (containsEsc) {
|
|
throw this.unexpected();
|
|
}
|
|
|
|
isStatic = true;
|
|
}
|
|
|
|
this.parseClassMemberWithIsStatic(classBody, member, state, isStatic, constructorAllowsSuper);
|
|
}
|
|
|
|
parseClassMemberWithIsStatic(classBody, member, state, isStatic, constructorAllowsSuper) {
|
|
const publicMethod = member;
|
|
const privateMethod = member;
|
|
const publicProp = member;
|
|
const privateProp = member;
|
|
const method = publicMethod;
|
|
const publicMember = publicMethod;
|
|
member.static = isStatic;
|
|
|
|
if (this.eat(_types2.types.star)) {
|
|
method.kind = "method";
|
|
this.parseClassPropertyName(method);
|
|
|
|
if (method.key.type === "PrivateName") {
|
|
this.pushClassPrivateMethod(classBody, privateMethod, true, false);
|
|
return;
|
|
}
|
|
|
|
if (this.isNonstaticConstructor(publicMethod)) {
|
|
this.raise(publicMethod.key.start, "Constructor can't be a generator");
|
|
}
|
|
|
|
this.pushClassMethod(classBody, publicMethod, true, false, false, false);
|
|
return;
|
|
}
|
|
|
|
const containsEsc = this.state.containsEsc;
|
|
const key = this.parseClassPropertyName(member);
|
|
const isPrivate = key.type === "PrivateName";
|
|
const isSimple = key.type === "Identifier";
|
|
const maybeQuestionTokenStart = this.state.start;
|
|
this.parsePostMemberNameModifiers(publicMember);
|
|
|
|
if (this.isClassMethod()) {
|
|
method.kind = "method";
|
|
|
|
if (isPrivate) {
|
|
this.pushClassPrivateMethod(classBody, privateMethod, false, false);
|
|
return;
|
|
}
|
|
|
|
const isConstructor = this.isNonstaticConstructor(publicMethod);
|
|
let allowsDirectSuper = false;
|
|
|
|
if (isConstructor) {
|
|
publicMethod.kind = "constructor";
|
|
|
|
if (state.hadConstructor && !this.hasPlugin("typescript")) {
|
|
this.raise(key.start, "Duplicate constructor in the same class");
|
|
}
|
|
|
|
state.hadConstructor = true;
|
|
allowsDirectSuper = constructorAllowsSuper;
|
|
}
|
|
|
|
this.pushClassMethod(classBody, publicMethod, false, false, isConstructor, allowsDirectSuper);
|
|
} else if (this.isClassProperty()) {
|
|
if (isPrivate) {
|
|
this.pushClassPrivateProperty(classBody, privateProp);
|
|
} else {
|
|
this.pushClassProperty(classBody, publicProp);
|
|
}
|
|
} else if (isSimple && key.name === "async" && !containsEsc && !this.isLineTerminator()) {
|
|
const isGenerator = this.eat(_types2.types.star);
|
|
|
|
if (publicMember.optional) {
|
|
this.unexpected(maybeQuestionTokenStart);
|
|
}
|
|
|
|
method.kind = "method";
|
|
this.parseClassPropertyName(method);
|
|
this.parsePostMemberNameModifiers(publicMember);
|
|
|
|
if (method.key.type === "PrivateName") {
|
|
this.pushClassPrivateMethod(classBody, privateMethod, isGenerator, true);
|
|
} else {
|
|
if (this.isNonstaticConstructor(publicMethod)) {
|
|
this.raise(publicMethod.key.start, "Constructor can't be an async function");
|
|
}
|
|
|
|
this.pushClassMethod(classBody, publicMethod, isGenerator, true, false, false);
|
|
}
|
|
} else if (isSimple && (key.name === "get" || key.name === "set") && !containsEsc && !(this.match(_types2.types.star) && this.isLineTerminator())) {
|
|
method.kind = key.name;
|
|
this.parseClassPropertyName(publicMethod);
|
|
|
|
if (method.key.type === "PrivateName") {
|
|
this.pushClassPrivateMethod(classBody, privateMethod, false, false);
|
|
} else {
|
|
if (this.isNonstaticConstructor(publicMethod)) {
|
|
this.raise(publicMethod.key.start, "Constructor can't have get/set modifier");
|
|
}
|
|
|
|
this.pushClassMethod(classBody, publicMethod, false, false, false, false);
|
|
}
|
|
|
|
this.checkGetterSetterParams(publicMethod);
|
|
} else if (this.isLineTerminator()) {
|
|
if (isPrivate) {
|
|
this.pushClassPrivateProperty(classBody, privateProp);
|
|
} else {
|
|
this.pushClassProperty(classBody, publicProp);
|
|
}
|
|
} else {
|
|
this.unexpected();
|
|
}
|
|
}
|
|
|
|
parseClassPropertyName(member) {
|
|
const key = this.parsePropertyName(member, true);
|
|
|
|
if (!member.computed && member.static && (key.name === "prototype" || key.value === "prototype")) {
|
|
this.raise(key.start, "Classes may not have static property named prototype");
|
|
}
|
|
|
|
if (key.type === "PrivateName" && key.id.name === "constructor") {
|
|
this.raise(key.start, "Classes may not have a private field named '#constructor'");
|
|
}
|
|
|
|
return key;
|
|
}
|
|
|
|
pushClassProperty(classBody, prop) {
|
|
if (!prop.computed && (prop.key.name === "constructor" || prop.key.value === "constructor")) {
|
|
this.raise(prop.key.start, "Classes may not have a field named 'constructor'");
|
|
}
|
|
|
|
classBody.body.push(this.parseClassProperty(prop));
|
|
}
|
|
|
|
pushClassPrivateProperty(classBody, prop) {
|
|
this.expectPlugin("classPrivateProperties", prop.key.start);
|
|
const node = this.parseClassPrivateProperty(prop);
|
|
classBody.body.push(node);
|
|
this.classScope.declarePrivateName(node.key.id.name, _scopeflags.CLASS_ELEMENT_OTHER, node.key.start);
|
|
}
|
|
|
|
pushClassMethod(classBody, method, isGenerator, isAsync, isConstructor, allowsDirectSuper) {
|
|
classBody.body.push(this.parseMethod(method, isGenerator, isAsync, isConstructor, allowsDirectSuper, "ClassMethod", true));
|
|
}
|
|
|
|
pushClassPrivateMethod(classBody, method, isGenerator, isAsync) {
|
|
this.expectPlugin("classPrivateMethods", method.key.start);
|
|
const node = this.parseMethod(method, isGenerator, isAsync, false, false, "ClassPrivateMethod", true);
|
|
classBody.body.push(node);
|
|
const kind = node.kind === "get" ? node.static ? _scopeflags.CLASS_ELEMENT_STATIC_GETTER : _scopeflags.CLASS_ELEMENT_INSTANCE_GETTER : node.kind === "set" ? node.static ? _scopeflags.CLASS_ELEMENT_STATIC_SETTER : _scopeflags.CLASS_ELEMENT_INSTANCE_SETTER : _scopeflags.CLASS_ELEMENT_OTHER;
|
|
this.classScope.declarePrivateName(node.key.id.name, kind, node.key.start);
|
|
}
|
|
|
|
parsePostMemberNameModifiers(methodOrProp) {}
|
|
|
|
parseAccessModifier() {
|
|
return undefined;
|
|
}
|
|
|
|
parseClassPrivateProperty(node) {
|
|
this.scope.enter(_scopeflags.SCOPE_CLASS | _scopeflags.SCOPE_SUPER);
|
|
node.value = this.eat(_types2.types.eq) ? this.parseMaybeAssign() : null;
|
|
this.semicolon();
|
|
this.scope.exit();
|
|
return this.finishNode(node, "ClassPrivateProperty");
|
|
}
|
|
|
|
parseClassProperty(node) {
|
|
if (!node.typeAnnotation) {
|
|
this.expectPlugin("classProperties");
|
|
}
|
|
|
|
this.scope.enter(_scopeflags.SCOPE_CLASS | _scopeflags.SCOPE_SUPER);
|
|
|
|
if (this.match(_types2.types.eq)) {
|
|
this.expectPlugin("classProperties");
|
|
this.next();
|
|
node.value = this.parseMaybeAssign();
|
|
} else {
|
|
node.value = null;
|
|
}
|
|
|
|
this.semicolon();
|
|
this.scope.exit();
|
|
return this.finishNode(node, "ClassProperty");
|
|
}
|
|
|
|
parseClassId(node, isStatement, optionalId, bindingType = _scopeflags.BIND_CLASS) {
|
|
if (this.match(_types2.types.name)) {
|
|
node.id = this.parseIdentifier();
|
|
|
|
if (isStatement) {
|
|
this.checkLVal(node.id, bindingType, undefined, "class name");
|
|
}
|
|
} else {
|
|
if (optionalId || !isStatement) {
|
|
node.id = null;
|
|
} else {
|
|
this.unexpected(null, "A class name is required");
|
|
}
|
|
}
|
|
}
|
|
|
|
parseClassSuper(node) {
|
|
node.superClass = this.eat(_types2.types._extends) ? this.parseExprSubscripts() : null;
|
|
}
|
|
|
|
parseExport(node) {
|
|
const hasDefault = this.maybeParseExportDefaultSpecifier(node);
|
|
const parseAfterDefault = !hasDefault || this.eat(_types2.types.comma);
|
|
const hasStar = parseAfterDefault && this.eatExportStar(node);
|
|
const hasNamespace = hasStar && this.maybeParseExportNamespaceSpecifier(node);
|
|
const parseAfterNamespace = parseAfterDefault && (!hasNamespace || this.eat(_types2.types.comma));
|
|
const isFromRequired = hasDefault || hasStar;
|
|
|
|
if (hasStar && !hasNamespace) {
|
|
if (hasDefault) this.unexpected();
|
|
this.parseExportFrom(node, true);
|
|
return this.finishNode(node, "ExportAllDeclaration");
|
|
}
|
|
|
|
const hasSpecifiers = this.maybeParseExportNamedSpecifiers(node);
|
|
|
|
if (hasDefault && parseAfterDefault && !hasStar && !hasSpecifiers || hasNamespace && parseAfterNamespace && !hasSpecifiers) {
|
|
throw this.unexpected(null, _types2.types.braceL);
|
|
}
|
|
|
|
let hasDeclaration;
|
|
|
|
if (isFromRequired || hasSpecifiers) {
|
|
hasDeclaration = false;
|
|
this.parseExportFrom(node, isFromRequired);
|
|
} else {
|
|
hasDeclaration = this.maybeParseExportDeclaration(node);
|
|
}
|
|
|
|
if (isFromRequired || hasSpecifiers || hasDeclaration) {
|
|
this.checkExport(node, true, false, !!node.source);
|
|
return this.finishNode(node, "ExportNamedDeclaration");
|
|
}
|
|
|
|
if (this.eat(_types2.types._default)) {
|
|
node.declaration = this.parseExportDefaultExpression();
|
|
this.checkExport(node, true, true);
|
|
return this.finishNode(node, "ExportDefaultDeclaration");
|
|
}
|
|
|
|
throw this.unexpected(null, _types2.types.braceL);
|
|
}
|
|
|
|
eatExportStar(node) {
|
|
return this.eat(_types2.types.star);
|
|
}
|
|
|
|
maybeParseExportDefaultSpecifier(node) {
|
|
if (this.isExportDefaultSpecifier()) {
|
|
this.expectPlugin("exportDefaultFrom");
|
|
const specifier = this.startNode();
|
|
specifier.exported = this.parseIdentifier(true);
|
|
node.specifiers = [this.finishNode(specifier, "ExportDefaultSpecifier")];
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
maybeParseExportNamespaceSpecifier(node) {
|
|
if (this.isContextual("as")) {
|
|
if (!node.specifiers) node.specifiers = [];
|
|
const specifier = this.startNodeAt(this.state.lastTokStart, this.state.lastTokStartLoc);
|
|
this.next();
|
|
specifier.exported = this.parseIdentifier(true);
|
|
node.specifiers.push(this.finishNode(specifier, "ExportNamespaceSpecifier"));
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
maybeParseExportNamedSpecifiers(node) {
|
|
if (this.match(_types2.types.braceL)) {
|
|
if (!node.specifiers) node.specifiers = [];
|
|
node.specifiers.push(...this.parseExportSpecifiers());
|
|
node.source = null;
|
|
node.declaration = null;
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
maybeParseExportDeclaration(node) {
|
|
if (this.shouldParseExportDeclaration()) {
|
|
if (this.isContextual("async")) {
|
|
const next = this.nextTokenStart();
|
|
|
|
if (!this.isUnparsedContextual(next, "function")) {
|
|
this.unexpected(next, `Unexpected token, expected "function"`);
|
|
}
|
|
}
|
|
|
|
node.specifiers = [];
|
|
node.source = null;
|
|
node.declaration = this.parseExportDeclaration(node);
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
isAsyncFunction() {
|
|
if (!this.isContextual("async")) return false;
|
|
const next = this.nextTokenStart();
|
|
return !_whitespace.lineBreak.test(this.input.slice(this.state.pos, next)) && this.isUnparsedContextual(next, "function");
|
|
}
|
|
|
|
parseExportDefaultExpression() {
|
|
const expr = this.startNode();
|
|
const isAsync = this.isAsyncFunction();
|
|
|
|
if (this.match(_types2.types._function) || isAsync) {
|
|
this.next();
|
|
|
|
if (isAsync) {
|
|
this.next();
|
|
}
|
|
|
|
return this.parseFunction(expr, FUNC_STATEMENT | FUNC_NULLABLE_ID, isAsync);
|
|
} else if (this.match(_types2.types._class)) {
|
|
return this.parseClass(expr, true, true);
|
|
} else if (this.match(_types2.types.at)) {
|
|
if (this.hasPlugin("decorators") && this.getPluginOption("decorators", "decoratorsBeforeExport")) {
|
|
this.raise(this.state.start, "Decorators must be placed *before* the 'export' keyword." + " You can set the 'decoratorsBeforeExport' option to false to use" + " the 'export @decorator class {}' syntax");
|
|
}
|
|
|
|
this.parseDecorators(false);
|
|
return this.parseClass(expr, true, true);
|
|
} else if (this.match(_types2.types._const) || this.match(_types2.types._var) || this.isLet()) {
|
|
throw this.raise(this.state.start, "Only expressions, functions or classes are allowed as the `default` export.");
|
|
} else {
|
|
const res = this.parseMaybeAssign();
|
|
this.semicolon();
|
|
return res;
|
|
}
|
|
}
|
|
|
|
parseExportDeclaration(node) {
|
|
return this.parseStatement(null);
|
|
}
|
|
|
|
isExportDefaultSpecifier() {
|
|
if (this.match(_types2.types.name)) {
|
|
return this.state.value !== "async" && this.state.value !== "let";
|
|
}
|
|
|
|
if (!this.match(_types2.types._default)) {
|
|
return false;
|
|
}
|
|
|
|
const next = this.nextTokenStart();
|
|
return this.input.charCodeAt(next) === 44 || this.isUnparsedContextual(next, "from");
|
|
}
|
|
|
|
parseExportFrom(node, expect) {
|
|
if (this.eatContextual("from")) {
|
|
node.source = this.parseImportSource();
|
|
this.checkExport(node);
|
|
} else {
|
|
if (expect) {
|
|
this.unexpected();
|
|
} else {
|
|
node.source = null;
|
|
}
|
|
}
|
|
|
|
this.semicolon();
|
|
}
|
|
|
|
shouldParseExportDeclaration() {
|
|
if (this.match(_types2.types.at)) {
|
|
this.expectOnePlugin(["decorators", "decorators-legacy"]);
|
|
|
|
if (this.hasPlugin("decorators")) {
|
|
if (this.getPluginOption("decorators", "decoratorsBeforeExport")) {
|
|
this.unexpected(this.state.start, "Decorators must be placed *before* the 'export' keyword." + " You can set the 'decoratorsBeforeExport' option to false to use" + " the 'export @decorator class {}' syntax");
|
|
} else {
|
|
return true;
|
|
}
|
|
}
|
|
}
|
|
|
|
return this.state.type.keyword === "var" || this.state.type.keyword === "const" || this.state.type.keyword === "function" || this.state.type.keyword === "class" || this.isLet() || this.isAsyncFunction();
|
|
}
|
|
|
|
checkExport(node, checkNames, isDefault, isFrom) {
|
|
if (checkNames) {
|
|
if (isDefault) {
|
|
this.checkDuplicateExports(node, "default");
|
|
} else if (node.specifiers && node.specifiers.length) {
|
|
for (let _i3 = 0, _node$specifiers = node.specifiers; _i3 < _node$specifiers.length; _i3++) {
|
|
const specifier = _node$specifiers[_i3];
|
|
this.checkDuplicateExports(specifier, specifier.exported.name);
|
|
|
|
if (!isFrom && specifier.local) {
|
|
this.checkReservedWord(specifier.local.name, specifier.local.start, true, false);
|
|
this.scope.checkLocalExport(specifier.local);
|
|
}
|
|
}
|
|
} else if (node.declaration) {
|
|
if (node.declaration.type === "FunctionDeclaration" || node.declaration.type === "ClassDeclaration") {
|
|
const id = node.declaration.id;
|
|
if (!id) throw new Error("Assertion failure");
|
|
this.checkDuplicateExports(node, id.name);
|
|
} else if (node.declaration.type === "VariableDeclaration") {
|
|
for (let _i4 = 0, _node$declaration$dec = node.declaration.declarations; _i4 < _node$declaration$dec.length; _i4++) {
|
|
const declaration = _node$declaration$dec[_i4];
|
|
this.checkDeclaration(declaration.id);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
const currentContextDecorators = this.state.decoratorStack[this.state.decoratorStack.length - 1];
|
|
|
|
if (currentContextDecorators.length) {
|
|
const isClass = node.declaration && (node.declaration.type === "ClassDeclaration" || node.declaration.type === "ClassExpression");
|
|
|
|
if (!node.declaration || !isClass) {
|
|
throw this.raise(node.start, "You can only use decorators on an export when exporting a class");
|
|
}
|
|
|
|
this.takeDecorators(node.declaration);
|
|
}
|
|
}
|
|
|
|
checkDeclaration(node) {
|
|
if (node.type === "Identifier") {
|
|
this.checkDuplicateExports(node, node.name);
|
|
} else if (node.type === "ObjectPattern") {
|
|
for (let _i5 = 0, _node$properties = node.properties; _i5 < _node$properties.length; _i5++) {
|
|
const prop = _node$properties[_i5];
|
|
this.checkDeclaration(prop);
|
|
}
|
|
} else if (node.type === "ArrayPattern") {
|
|
for (let _i6 = 0, _node$elements = node.elements; _i6 < _node$elements.length; _i6++) {
|
|
const elem = _node$elements[_i6];
|
|
|
|
if (elem) {
|
|
this.checkDeclaration(elem);
|
|
}
|
|
}
|
|
} else if (node.type === "ObjectProperty") {
|
|
this.checkDeclaration(node.value);
|
|
} else if (node.type === "RestElement") {
|
|
this.checkDeclaration(node.argument);
|
|
} else if (node.type === "AssignmentPattern") {
|
|
this.checkDeclaration(node.left);
|
|
}
|
|
}
|
|
|
|
checkDuplicateExports(node, name) {
|
|
if (this.state.exportedIdentifiers.indexOf(name) > -1) {
|
|
this.raise(node.start, name === "default" ? "Only one default export allowed per module." : `\`${name}\` has already been exported. Exported identifiers must be unique.`);
|
|
}
|
|
|
|
this.state.exportedIdentifiers.push(name);
|
|
}
|
|
|
|
parseExportSpecifiers() {
|
|
const nodes = [];
|
|
let first = true;
|
|
this.expect(_types2.types.braceL);
|
|
|
|
while (!this.eat(_types2.types.braceR)) {
|
|
if (first) {
|
|
first = false;
|
|
} else {
|
|
this.expect(_types2.types.comma);
|
|
if (this.eat(_types2.types.braceR)) break;
|
|
}
|
|
|
|
const node = this.startNode();
|
|
node.local = this.parseIdentifier(true);
|
|
node.exported = this.eatContextual("as") ? this.parseIdentifier(true) : node.local.__clone();
|
|
nodes.push(this.finishNode(node, "ExportSpecifier"));
|
|
}
|
|
|
|
return nodes;
|
|
}
|
|
|
|
parseImport(node) {
|
|
node.specifiers = [];
|
|
|
|
if (!this.match(_types2.types.string)) {
|
|
const hasDefault = this.maybeParseDefaultImportSpecifier(node);
|
|
const parseNext = !hasDefault || this.eat(_types2.types.comma);
|
|
const hasStar = parseNext && this.maybeParseStarImportSpecifier(node);
|
|
if (parseNext && !hasStar) this.parseNamedImportSpecifiers(node);
|
|
this.expectContextual("from");
|
|
}
|
|
|
|
node.source = this.parseImportSource();
|
|
this.semicolon();
|
|
return this.finishNode(node, "ImportDeclaration");
|
|
}
|
|
|
|
parseImportSource() {
|
|
if (!this.match(_types2.types.string)) this.unexpected();
|
|
return this.parseExprAtom();
|
|
}
|
|
|
|
shouldParseDefaultImport(node) {
|
|
return this.match(_types2.types.name);
|
|
}
|
|
|
|
parseImportSpecifierLocal(node, specifier, type, contextDescription) {
|
|
specifier.local = this.parseIdentifier();
|
|
this.checkLVal(specifier.local, _scopeflags.BIND_LEXICAL, undefined, contextDescription);
|
|
node.specifiers.push(this.finishNode(specifier, type));
|
|
}
|
|
|
|
maybeParseDefaultImportSpecifier(node) {
|
|
if (this.shouldParseDefaultImport(node)) {
|
|
this.parseImportSpecifierLocal(node, this.startNode(), "ImportDefaultSpecifier", "default import specifier");
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
maybeParseStarImportSpecifier(node) {
|
|
if (this.match(_types2.types.star)) {
|
|
const specifier = this.startNode();
|
|
this.next();
|
|
this.expectContextual("as");
|
|
this.parseImportSpecifierLocal(node, specifier, "ImportNamespaceSpecifier", "import namespace specifier");
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
parseNamedImportSpecifiers(node) {
|
|
let first = true;
|
|
this.expect(_types2.types.braceL);
|
|
|
|
while (!this.eat(_types2.types.braceR)) {
|
|
if (first) {
|
|
first = false;
|
|
} else {
|
|
if (this.eat(_types2.types.colon)) {
|
|
throw this.raise(this.state.start, "ES2015 named imports do not destructure. " + "Use another statement for destructuring after the import.");
|
|
}
|
|
|
|
this.expect(_types2.types.comma);
|
|
if (this.eat(_types2.types.braceR)) break;
|
|
}
|
|
|
|
this.parseImportSpecifier(node);
|
|
}
|
|
}
|
|
|
|
parseImportSpecifier(node) {
|
|
const specifier = this.startNode();
|
|
specifier.imported = this.parseIdentifier(true);
|
|
|
|
if (this.eatContextual("as")) {
|
|
specifier.local = this.parseIdentifier();
|
|
} else {
|
|
this.checkReservedWord(specifier.imported.name, specifier.start, true, true);
|
|
specifier.local = specifier.imported.__clone();
|
|
}
|
|
|
|
this.checkLVal(specifier.local, _scopeflags.BIND_LEXICAL, undefined, "import specifier");
|
|
node.specifiers.push(this.finishNode(specifier, "ImportSpecifier"));
|
|
}
|
|
|
|
}
|
|
|
|
exports.default = StatementParser; |