aboutsummaryrefslogtreecommitdiff
path: root/node_modules/babylon/lib/parser/statement.js
diff options
context:
space:
mode:
Diffstat (limited to 'node_modules/babylon/lib/parser/statement.js')
-rw-r--r--node_modules/babylon/lib/parser/statement.js1107
1 files changed, 1107 insertions, 0 deletions
diff --git a/node_modules/babylon/lib/parser/statement.js b/node_modules/babylon/lib/parser/statement.js
new file mode 100644
index 000000000..3aa2f7882
--- /dev/null
+++ b/node_modules/babylon/lib/parser/statement.js
@@ -0,0 +1,1107 @@
+"use strict";
+
+var _types = require("../tokenizer/types");
+
+var _index = require("./index");
+
+var _index2 = _interopRequireDefault(_index);
+
+var _whitespace = require("../util/whitespace");
+
+function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
+
+var pp = _index2.default.prototype;
+
+// ### Statement parsing
+
+// Parse a program. Initializes the parser, reads any number of
+// statements, and wraps them in a Program node. Optionally takes a
+// `program` argument. If present, the statements will be appended
+// to its body instead of creating a new node.
+
+/* eslint indent: 0 */
+/* eslint max-len: 0 */
+
+pp.parseTopLevel = function (file, program) {
+ program.sourceType = this.options.sourceType;
+
+ this.parseBlockBody(program, true, true, _types.types.eof);
+
+ file.program = this.finishNode(program, "Program");
+ file.comments = this.state.comments;
+ file.tokens = this.state.tokens;
+
+ return this.finishNode(file, "File");
+};
+
+var loopLabel = { kind: "loop" },
+ switchLabel = { kind: "switch" };
+
+// TODO
+
+pp.stmtToDirective = function (stmt) {
+ var expr = stmt.expression;
+
+ var directiveLiteral = this.startNodeAt(expr.start, expr.loc.start);
+ var directive = this.startNodeAt(stmt.start, stmt.loc.start);
+
+ var raw = this.input.slice(expr.start, expr.end);
+ var val = directiveLiteral.value = raw.slice(1, -1); // remove quotes
+
+ 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);
+};
+
+// Parse a single statement.
+//
+// If expecting a statement and finding a slash operator, parse a
+// regular expression literal. This is to handle cases like
+// `if (foo) /blah/.exec(foo)`, where looking at the previous token
+// does not help.
+
+pp.parseStatement = function (declaration, topLevel) {
+ if (this.match(_types.types.at)) {
+ this.parseDecorators(true);
+ }
+
+ var starttype = this.state.type,
+ node = this.startNode();
+
+ // Most types of statements are recognized by the keyword they
+ // start with. Many are trivial to parse, some require a bit of
+ // complexity.
+
+ switch (starttype) {
+ case _types.types._break:case _types.types._continue:
+ return this.parseBreakContinueStatement(node, starttype.keyword);
+ case _types.types._debugger:
+ return this.parseDebuggerStatement(node);
+ case _types.types._do:
+ return this.parseDoStatement(node);
+ case _types.types._for:
+ return this.parseForStatement(node);
+ case _types.types._function:
+ if (!declaration) this.unexpected();
+ return this.parseFunctionStatement(node);
+
+ case _types.types._class:
+ if (!declaration) this.unexpected();
+ this.takeDecorators(node);
+ return this.parseClass(node, true);
+
+ case _types.types._if:
+ return this.parseIfStatement(node);
+ case _types.types._return:
+ return this.parseReturnStatement(node);
+ case _types.types._switch:
+ return this.parseSwitchStatement(node);
+ case _types.types._throw:
+ return this.parseThrowStatement(node);
+ case _types.types._try:
+ return this.parseTryStatement(node);
+
+ case _types.types._let:
+ case _types.types._const:
+ if (!declaration) this.unexpected(); // NOTE: falls through to _var
+
+ case _types.types._var:
+ return this.parseVarStatement(node, starttype);
+
+ case _types.types._while:
+ return this.parseWhileStatement(node);
+ case _types.types._with:
+ return this.parseWithStatement(node);
+ case _types.types.braceL:
+ return this.parseBlock();
+ case _types.types.semi:
+ return this.parseEmptyStatement(node);
+ case _types.types._export:
+ case _types.types._import:
+ if (!this.options.allowImportExportEverywhere) {
+ if (!topLevel) {
+ this.raise(this.state.start, "'import' and 'export' may only appear at the top level");
+ }
+
+ if (!this.inModule) {
+ this.raise(this.state.start, "'import' and 'export' may appear only with 'sourceType: module'");
+ }
+ }
+ return starttype === _types.types._import ? this.parseImport(node) : this.parseExport(node);
+
+ case _types.types.name:
+ if (this.state.value === "async") {
+ // peek ahead and see if next token is a function
+ var state = this.state.clone();
+ this.next();
+ if (this.match(_types.types._function) && !this.canInsertSemicolon()) {
+ this.expect(_types.types._function);
+ return this.parseFunction(node, true, false, true);
+ } else {
+ this.state = state;
+ }
+ }
+ }
+
+ // If the statement does not start with a statement keyword or a
+ // brace, it's an ExpressionStatement or LabeledStatement. We
+ // simply start parsing an expression, and afterwards, if the
+ // next token is a colon and the expression was a simple
+ // Identifier node, we switch to interpreting it as a label.
+ var maybeName = this.state.value;
+ var expr = this.parseExpression();
+
+ if (starttype === _types.types.name && expr.type === "Identifier" && this.eat(_types.types.colon)) {
+ return this.parseLabeledStatement(node, maybeName, expr);
+ } else {
+ return this.parseExpressionStatement(node, expr);
+ }
+};
+
+pp.takeDecorators = function (node) {
+ if (this.state.decorators.length) {
+ node.decorators = this.state.decorators;
+ this.state.decorators = [];
+ }
+};
+
+pp.parseDecorators = function (allowExport) {
+ while (this.match(_types.types.at)) {
+ this.state.decorators.push(this.parseDecorator());
+ }
+
+ if (allowExport && this.match(_types.types._export)) {
+ return;
+ }
+
+ if (!this.match(_types.types._class)) {
+ this.raise(this.state.start, "Leading decorators must be attached to a class declaration");
+ }
+};
+
+pp.parseDecorator = function () {
+ if (!this.hasPlugin("decorators")) {
+ this.unexpected();
+ }
+ var node = this.startNode();
+ this.next();
+ node.expression = this.parseMaybeAssign();
+ return this.finishNode(node, "Decorator");
+};
+
+pp.parseBreakContinueStatement = function (node, keyword) {
+ var isBreak = keyword === "break";
+ this.next();
+
+ if (this.isLineTerminator()) {
+ node.label = null;
+ } else if (!this.match(_types.types.name)) {
+ this.unexpected();
+ } else {
+ node.label = this.parseIdentifier();
+ this.semicolon();
+ }
+
+ // Verify that there is an actual destination to break or
+ // continue to.
+ var i = void 0;
+ for (i = 0; i < this.state.labels.length; ++i) {
+ var 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);
+ return this.finishNode(node, isBreak ? "BreakStatement" : "ContinueStatement");
+};
+
+pp.parseDebuggerStatement = function (node) {
+ this.next();
+ this.semicolon();
+ return this.finishNode(node, "DebuggerStatement");
+};
+
+pp.parseDoStatement = function (node) {
+ this.next();
+ this.state.labels.push(loopLabel);
+ node.body = this.parseStatement(false);
+ this.state.labels.pop();
+ this.expect(_types.types._while);
+ node.test = this.parseParenExpression();
+ this.eat(_types.types.semi);
+ return this.finishNode(node, "DoWhileStatement");
+};
+
+// Disambiguating between a `for` and a `for`/`in` or `for`/`of`
+// loop is non-trivial. Basically, we have to parse the init `var`
+// statement or expression, disallowing the `in` operator (see
+// the second parameter to `parseExpression`), and then check
+// whether the next token is `in` or `of`. When there is no init
+// part (semicolon immediately after the opening parenthesis), it
+// is a regular `for` loop.
+
+pp.parseForStatement = function (node) {
+ this.next();
+ this.state.labels.push(loopLabel);
+
+ var forAwait = false;
+ if (this.hasPlugin("asyncGenerators") && this.state.inAsync && this.isContextual("await")) {
+ forAwait = true;
+ this.next();
+ }
+ this.expect(_types.types.parenL);
+
+ if (this.match(_types.types.semi)) {
+ if (forAwait) {
+ this.unexpected();
+ }
+ return this.parseFor(node, null);
+ }
+
+ if (this.match(_types.types._var) || this.match(_types.types._let) || this.match(_types.types._const)) {
+ var _init = this.startNode(),
+ varKind = this.state.type;
+ this.next();
+ this.parseVar(_init, true, varKind);
+ this.finishNode(_init, "VariableDeclaration");
+
+ if (this.match(_types.types._in) || this.isContextual("of")) {
+ if (_init.declarations.length === 1 && !_init.declarations[0].init) {
+ return this.parseForIn(node, _init, forAwait);
+ }
+ }
+ if (forAwait) {
+ this.unexpected();
+ }
+ return this.parseFor(node, _init);
+ }
+
+ var refShorthandDefaultPos = { start: 0 };
+ var init = this.parseExpression(true, refShorthandDefaultPos);
+ if (this.match(_types.types._in) || this.isContextual("of")) {
+ var description = this.isContextual("of") ? "for-of statement" : "for-in statement";
+ this.toAssignable(init, undefined, description);
+ this.checkLVal(init, undefined, undefined, description);
+ return this.parseForIn(node, init, forAwait);
+ } else if (refShorthandDefaultPos.start) {
+ this.unexpected(refShorthandDefaultPos.start);
+ }
+ if (forAwait) {
+ this.unexpected();
+ }
+ return this.parseFor(node, init);
+};
+
+pp.parseFunctionStatement = function (node) {
+ this.next();
+ return this.parseFunction(node, true);
+};
+
+pp.parseIfStatement = function (node) {
+ this.next();
+ node.test = this.parseParenExpression();
+ node.consequent = this.parseStatement(false);
+ node.alternate = this.eat(_types.types._else) ? this.parseStatement(false) : null;
+ return this.finishNode(node, "IfStatement");
+};
+
+pp.parseReturnStatement = function (node) {
+ if (!this.state.inFunction && !this.options.allowReturnOutsideFunction) {
+ this.raise(this.state.start, "'return' outside of function");
+ }
+
+ this.next();
+
+ // In `return` (and `break`/`continue`), the keywords with
+ // optional arguments, we eagerly look for a semicolon or the
+ // possibility to insert one.
+
+ if (this.isLineTerminator()) {
+ node.argument = null;
+ } else {
+ node.argument = this.parseExpression();
+ this.semicolon();
+ }
+
+ return this.finishNode(node, "ReturnStatement");
+};
+
+pp.parseSwitchStatement = function (node) {
+ this.next();
+ node.discriminant = this.parseParenExpression();
+ node.cases = [];
+ this.expect(_types.types.braceL);
+ this.state.labels.push(switchLabel);
+
+ // Statements under must be grouped (by label) in SwitchCase
+ // nodes. `cur` is used to keep the node that we are currently
+ // adding statements to.
+
+ var cur = void 0;
+ for (var sawDefault; !this.match(_types.types.braceR);) {
+ if (this.match(_types.types._case) || this.match(_types.types._default)) {
+ var isCase = this.match(_types.types._case);
+ if (cur) this.finishNode(cur, "SwitchCase");
+ node.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(_types.types.colon);
+ } else {
+ if (cur) {
+ cur.consequent.push(this.parseStatement(true));
+ } else {
+ this.unexpected();
+ }
+ }
+ }
+ if (cur) this.finishNode(cur, "SwitchCase");
+ this.next(); // Closing brace
+ this.state.labels.pop();
+ return this.finishNode(node, "SwitchStatement");
+};
+
+pp.parseThrowStatement = function (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");
+};
+
+// Reused empty array added for node fields that are always empty.
+
+var empty = [];
+
+pp.parseTryStatement = function (node) {
+ this.next();
+
+ node.block = this.parseBlock();
+ node.handler = null;
+
+ if (this.match(_types.types._catch)) {
+ var clause = this.startNode();
+ this.next();
+
+ this.expect(_types.types.parenL);
+ clause.param = this.parseBindingAtom();
+ this.checkLVal(clause.param, true, Object.create(null), "catch clause");
+ this.expect(_types.types.parenR);
+
+ clause.body = this.parseBlock();
+ node.handler = this.finishNode(clause, "CatchClause");
+ }
+
+ node.guardedHandlers = empty;
+ node.finalizer = this.eat(_types.types._finally) ? this.parseBlock() : null;
+
+ if (!node.handler && !node.finalizer) {
+ this.raise(node.start, "Missing catch or finally clause");
+ }
+
+ return this.finishNode(node, "TryStatement");
+};
+
+pp.parseVarStatement = function (node, kind) {
+ this.next();
+ this.parseVar(node, false, kind);
+ this.semicolon();
+ return this.finishNode(node, "VariableDeclaration");
+};
+
+pp.parseWhileStatement = function (node) {
+ this.next();
+ node.test = this.parseParenExpression();
+ this.state.labels.push(loopLabel);
+ node.body = this.parseStatement(false);
+ this.state.labels.pop();
+ return this.finishNode(node, "WhileStatement");
+};
+
+pp.parseWithStatement = function (node) {
+ if (this.state.strict) this.raise(this.state.start, "'with' in strict mode");
+ this.next();
+ node.object = this.parseParenExpression();
+ node.body = this.parseStatement(false);
+ return this.finishNode(node, "WithStatement");
+};
+
+pp.parseEmptyStatement = function (node) {
+ this.next();
+ return this.finishNode(node, "EmptyStatement");
+};
+
+pp.parseLabeledStatement = function (node, maybeName, expr) {
+ for (var _iterator = this.state.labels, _isArray = Array.isArray(_iterator), _i = 0, _iterator = _isArray ? _iterator : _iterator[Symbol.iterator]();;) {
+ var _ref;
+
+ if (_isArray) {
+ if (_i >= _iterator.length) break;
+ _ref = _iterator[_i++];
+ } else {
+ _i = _iterator.next();
+ if (_i.done) break;
+ _ref = _i.value;
+ }
+
+ var _label = _ref;
+
+ if (_label.name === maybeName) {
+ this.raise(expr.start, "Label '" + maybeName + "' is already declared");
+ }
+ }
+
+ var kind = this.state.type.isLoop ? "loop" : this.match(_types.types._switch) ? "switch" : null;
+ for (var i = this.state.labels.length - 1; i >= 0; i--) {
+ var 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(true);
+ this.state.labels.pop();
+ node.label = expr;
+ return this.finishNode(node, "LabeledStatement");
+};
+
+pp.parseExpressionStatement = function (node, expr) {
+ node.expression = expr;
+ this.semicolon();
+ return this.finishNode(node, "ExpressionStatement");
+};
+
+// Parse a semicolon-enclosed block of statements, handling `"use
+// strict"` declarations when `allowStrict` is true (used for
+// function bodies).
+
+pp.parseBlock = function (allowDirectives) {
+ var node = this.startNode();
+ this.expect(_types.types.braceL);
+ this.parseBlockBody(node, allowDirectives, false, _types.types.braceR);
+ return this.finishNode(node, "BlockStatement");
+};
+
+// TODO
+
+pp.parseBlockBody = function (node, allowDirectives, topLevel, end) {
+ node.body = [];
+ node.directives = [];
+
+ var parsedNonDirective = false;
+ var oldStrict = void 0;
+ var octalPosition = void 0;
+
+ while (!this.eat(end)) {
+ if (!parsedNonDirective && this.state.containsOctal && !octalPosition) {
+ octalPosition = this.state.octalPosition;
+ }
+
+ var stmt = this.parseStatement(true, topLevel);
+
+ if (allowDirectives && !parsedNonDirective && stmt.type === "ExpressionStatement" && stmt.expression.type === "StringLiteral" && !stmt.expression.extra.parenthesized) {
+ var directive = this.stmtToDirective(stmt);
+ node.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;
+ node.body.push(stmt);
+ }
+
+ if (oldStrict === false) {
+ this.setStrict(false);
+ }
+};
+
+// Parse a regular `for` loop. The disambiguation code in
+// `parseStatement` will already have parsed the init statement or
+// expression.
+
+pp.parseFor = function (node, init) {
+ node.init = init;
+ this.expect(_types.types.semi);
+ node.test = this.match(_types.types.semi) ? null : this.parseExpression();
+ this.expect(_types.types.semi);
+ node.update = this.match(_types.types.parenR) ? null : this.parseExpression();
+ this.expect(_types.types.parenR);
+ node.body = this.parseStatement(false);
+ this.state.labels.pop();
+ return this.finishNode(node, "ForStatement");
+};
+
+// Parse a `for`/`in` and `for`/`of` loop, which are almost
+// same from parser's perspective.
+
+pp.parseForIn = function (node, init, forAwait) {
+ var type = void 0;
+ if (forAwait) {
+ this.eatContextual("of");
+ type = "ForAwaitStatement";
+ } else {
+ type = this.match(_types.types._in) ? "ForInStatement" : "ForOfStatement";
+ this.next();
+ }
+ node.left = init;
+ node.right = this.parseExpression();
+ this.expect(_types.types.parenR);
+ node.body = this.parseStatement(false);
+ this.state.labels.pop();
+ return this.finishNode(node, type);
+};
+
+// Parse a list of variable declarations.
+
+pp.parseVar = function (node, isFor, kind) {
+ node.declarations = [];
+ node.kind = kind.keyword;
+ for (;;) {
+ var decl = this.startNode();
+ this.parseVarHead(decl);
+ if (this.eat(_types.types.eq)) {
+ decl.init = this.parseMaybeAssign(isFor);
+ } else if (kind === _types.types._const && !(this.match(_types.types._in) || this.isContextual("of"))) {
+ this.unexpected();
+ } else if (decl.id.type !== "Identifier" && !(isFor && (this.match(_types.types._in) || this.isContextual("of")))) {
+ this.raise(this.state.lastTokEnd, "Complex binding patterns require an initialization value");
+ } else {
+ decl.init = null;
+ }
+ node.declarations.push(this.finishNode(decl, "VariableDeclarator"));
+ if (!this.eat(_types.types.comma)) break;
+ }
+ return node;
+};
+
+pp.parseVarHead = function (decl) {
+ decl.id = this.parseBindingAtom();
+ this.checkLVal(decl.id, true, undefined, "variable declaration");
+};
+
+// Parse a function declaration or literal (depending on the
+// `isStatement` parameter).
+
+pp.parseFunction = function (node, isStatement, allowExpressionBody, isAsync, optionalId) {
+ var oldInMethod = this.state.inMethod;
+ this.state.inMethod = false;
+
+ this.initFunction(node, isAsync);
+
+ if (this.match(_types.types.star)) {
+ if (node.async && !this.hasPlugin("asyncGenerators")) {
+ this.unexpected();
+ } else {
+ node.generator = true;
+ this.next();
+ }
+ }
+
+ if (isStatement && !optionalId && !this.match(_types.types.name) && !this.match(_types.types._yield)) {
+ this.unexpected();
+ }
+
+ if (this.match(_types.types.name) || this.match(_types.types._yield)) {
+ node.id = this.parseBindingIdentifier();
+ }
+
+ this.parseFunctionParams(node);
+ this.parseFunctionBody(node, allowExpressionBody);
+
+ this.state.inMethod = oldInMethod;
+
+ return this.finishNode(node, isStatement ? "FunctionDeclaration" : "FunctionExpression");
+};
+
+pp.parseFunctionParams = function (node) {
+ this.expect(_types.types.parenL);
+ node.params = this.parseBindingList(_types.types.parenR);
+};
+
+// Parse a class declaration or literal (depending on the
+// `isStatement` parameter).
+
+pp.parseClass = function (node, isStatement, optionalId) {
+ this.next();
+ this.parseClassId(node, isStatement, optionalId);
+ this.parseClassSuper(node);
+ this.parseClassBody(node);
+ return this.finishNode(node, isStatement ? "ClassDeclaration" : "ClassExpression");
+};
+
+pp.isClassProperty = function () {
+ return this.match(_types.types.eq) || this.isLineTerminator();
+};
+
+pp.isClassMutatorStarter = function () {
+ return false;
+};
+
+pp.parseClassBody = function (node) {
+ // class bodies are implicitly strict
+ var oldStrict = this.state.strict;
+ this.state.strict = true;
+
+ var hadConstructorCall = false;
+ var hadConstructor = false;
+ var decorators = [];
+ var classBody = this.startNode();
+
+ classBody.body = [];
+
+ this.expect(_types.types.braceL);
+
+ while (!this.eat(_types.types.braceR)) {
+ if (this.eat(_types.types.semi)) {
+ continue;
+ }
+
+ if (this.match(_types.types.at)) {
+ decorators.push(this.parseDecorator());
+ continue;
+ }
+
+ var method = this.startNode();
+
+ // steal the decorators if there are any
+ if (decorators.length) {
+ method.decorators = decorators;
+ decorators = [];
+ }
+
+ var isConstructorCall = false;
+ var isMaybeStatic = this.match(_types.types.name) && this.state.value === "static";
+ var isGenerator = this.eat(_types.types.star);
+ var isGetSet = false;
+ var isAsync = false;
+
+ this.parsePropertyName(method);
+
+ method.static = isMaybeStatic && !this.match(_types.types.parenL);
+ if (method.static) {
+ if (isGenerator) this.unexpected();
+ isGenerator = this.eat(_types.types.star);
+ this.parsePropertyName(method);
+ }
+
+ if (!isGenerator) {
+ if (this.isClassProperty()) {
+ classBody.body.push(this.parseClassProperty(method));
+ continue;
+ }
+
+ if (method.key.type === "Identifier" && !method.computed && this.hasPlugin("classConstructorCall") && method.key.name === "call" && this.match(_types.types.name) && this.state.value === "constructor") {
+ isConstructorCall = true;
+ this.parsePropertyName(method);
+ }
+ }
+
+ var isAsyncMethod = !this.match(_types.types.parenL) && !method.computed && method.key.type === "Identifier" && method.key.name === "async";
+ if (isAsyncMethod) {
+ if (this.hasPlugin("asyncGenerators") && this.eat(_types.types.star)) isGenerator = true;
+ isAsync = true;
+ this.parsePropertyName(method);
+ }
+
+ method.kind = "method";
+
+ if (!method.computed) {
+ var key = method.key;
+
+ // handle get/set methods
+ // eg. class Foo { get bar() {} set bar() {} }
+
+ if (!isAsync && !isGenerator && !this.isClassMutatorStarter() && key.type === "Identifier" && !this.match(_types.types.parenL) && (key.name === "get" || key.name === "set")) {
+ isGetSet = true;
+ method.kind = key.name;
+ key = this.parsePropertyName(method);
+ }
+
+ // disallow invalid constructors
+ var isConstructor = !isConstructorCall && !method.static && (key.type === "Identifier" && key.name === "constructor" || key.type === "StringLiteral" && key.value === "constructor");
+ if (isConstructor) {
+ if (hadConstructor) this.raise(key.start, "Duplicate constructor in the same class");
+ if (isGetSet) this.raise(key.start, "Constructor can't have get/set modifier");
+ if (isGenerator) this.raise(key.start, "Constructor can't be a generator");
+ if (isAsync) this.raise(key.start, "Constructor can't be an async function");
+ method.kind = "constructor";
+ hadConstructor = true;
+ }
+
+ // disallow static prototype method
+ var isStaticPrototype = method.static && (key.type === "Identifier" && key.name === "prototype" || key.type === "StringLiteral" && key.value === "prototype");
+ if (isStaticPrototype) {
+ this.raise(key.start, "Classes may not have static property named prototype");
+ }
+ }
+
+ // convert constructor to a constructor call
+ if (isConstructorCall) {
+ if (hadConstructorCall) this.raise(method.start, "Duplicate constructor call in the same class");
+ method.kind = "constructorCall";
+ hadConstructorCall = true;
+ }
+
+ // disallow decorators on class constructors
+ if ((method.kind === "constructor" || method.kind === "constructorCall") && method.decorators) {
+ this.raise(method.start, "You can't attach decorators to a class constructor");
+ }
+
+ this.parseClassMethod(classBody, method, isGenerator, isAsync);
+
+ // get methods aren't allowed to have any parameters
+ // set methods must have exactly 1 parameter
+ if (isGetSet) {
+ var paramCount = method.kind === "get" ? 0 : 1;
+ if (method.params.length !== paramCount) {
+ var start = method.start;
+ if (method.kind === "get") {
+ this.raise(start, "getter should have no params");
+ } else {
+ this.raise(start, "setter should have exactly one param");
+ }
+ }
+ }
+ }
+
+ if (decorators.length) {
+ this.raise(this.state.start, "You have trailing decorators with no method");
+ }
+
+ node.body = this.finishNode(classBody, "ClassBody");
+
+ this.state.strict = oldStrict;
+};
+
+pp.parseClassProperty = function (node) {
+ if (this.match(_types.types.eq)) {
+ if (!this.hasPlugin("classProperties")) this.unexpected();
+ this.next();
+ node.value = this.parseMaybeAssign();
+ } else {
+ node.value = null;
+ }
+ this.semicolon();
+ return this.finishNode(node, "ClassProperty");
+};
+
+pp.parseClassMethod = function (classBody, method, isGenerator, isAsync) {
+ this.parseMethod(method, isGenerator, isAsync);
+ classBody.body.push(this.finishNode(method, "ClassMethod"));
+};
+
+pp.parseClassId = function (node, isStatement, optionalId) {
+ if (this.match(_types.types.name)) {
+ node.id = this.parseIdentifier();
+ } else {
+ if (optionalId || !isStatement) {
+ node.id = null;
+ } else {
+ this.unexpected();
+ }
+ }
+};
+
+pp.parseClassSuper = function (node) {
+ node.superClass = this.eat(_types.types._extends) ? this.parseExprSubscripts() : null;
+};
+
+// Parses module export declaration.
+
+pp.parseExport = function (node) {
+ this.next();
+ // export * from '...'
+ if (this.match(_types.types.star)) {
+ var specifier = this.startNode();
+ this.next();
+ if (this.hasPlugin("exportExtensions") && this.eatContextual("as")) {
+ specifier.exported = this.parseIdentifier();
+ node.specifiers = [this.finishNode(specifier, "ExportNamespaceSpecifier")];
+ this.parseExportSpecifiersMaybe(node);
+ this.parseExportFrom(node, true);
+ } else {
+ this.parseExportFrom(node, true);
+ return this.finishNode(node, "ExportAllDeclaration");
+ }
+ } else if (this.hasPlugin("exportExtensions") && this.isExportDefaultSpecifier()) {
+ var _specifier = this.startNode();
+ _specifier.exported = this.parseIdentifier(true);
+ node.specifiers = [this.finishNode(_specifier, "ExportDefaultSpecifier")];
+ if (this.match(_types.types.comma) && this.lookahead().type === _types.types.star) {
+ this.expect(_types.types.comma);
+ var _specifier2 = this.startNode();
+ this.expect(_types.types.star);
+ this.expectContextual("as");
+ _specifier2.exported = this.parseIdentifier();
+ node.specifiers.push(this.finishNode(_specifier2, "ExportNamespaceSpecifier"));
+ } else {
+ this.parseExportSpecifiersMaybe(node);
+ }
+ this.parseExportFrom(node, true);
+ } else if (this.eat(_types.types._default)) {
+ // export default ...
+ var expr = this.startNode();
+ var needsSemi = false;
+ if (this.eat(_types.types._function)) {
+ expr = this.parseFunction(expr, true, false, false, true);
+ } else if (this.match(_types.types._class)) {
+ expr = this.parseClass(expr, true, true);
+ } else {
+ needsSemi = true;
+ expr = this.parseMaybeAssign();
+ }
+ node.declaration = expr;
+ if (needsSemi) this.semicolon();
+ this.checkExport(node, true, true);
+ return this.finishNode(node, "ExportDefaultDeclaration");
+ } else if (this.state.type.keyword || this.shouldParseExportDeclaration()) {
+ node.specifiers = [];
+ node.source = null;
+ node.declaration = this.parseExportDeclaration(node);
+ } else {
+ // export { x, y as z } [from '...']
+ node.declaration = null;
+ node.specifiers = this.parseExportSpecifiers();
+ this.parseExportFrom(node);
+ }
+ this.checkExport(node, true);
+ return this.finishNode(node, "ExportNamedDeclaration");
+};
+
+pp.parseExportDeclaration = function () {
+ return this.parseStatement(true);
+};
+
+pp.isExportDefaultSpecifier = function () {
+ if (this.match(_types.types.name)) {
+ return this.state.value !== "type" && this.state.value !== "async" && this.state.value !== "interface";
+ }
+
+ if (!this.match(_types.types._default)) {
+ return false;
+ }
+
+ var lookahead = this.lookahead();
+ return lookahead.type === _types.types.comma || lookahead.type === _types.types.name && lookahead.value === "from";
+};
+
+pp.parseExportSpecifiersMaybe = function (node) {
+ if (this.eat(_types.types.comma)) {
+ node.specifiers = node.specifiers.concat(this.parseExportSpecifiers());
+ }
+};
+
+pp.parseExportFrom = function (node, expect) {
+ if (this.eatContextual("from")) {
+ node.source = this.match(_types.types.string) ? this.parseExprAtom() : this.unexpected();
+ this.checkExport(node);
+ } else {
+ if (expect) {
+ this.unexpected();
+ } else {
+ node.source = null;
+ }
+ }
+
+ this.semicolon();
+};
+
+pp.shouldParseExportDeclaration = function () {
+ return this.isContextual("async");
+};
+
+pp.checkExport = function (node, checkNames, isDefault) {
+ if (checkNames) {
+ // Check for duplicate exports
+ if (isDefault) {
+ // Default exports
+ this.checkDuplicateExports(node, "default", isDefault);
+ } else if (node.specifiers && node.specifiers.length) {
+ // Named exports
+ for (var _iterator2 = node.specifiers, _isArray2 = Array.isArray(_iterator2), _i2 = 0, _iterator2 = _isArray2 ? _iterator2 : _iterator2[Symbol.iterator]();;) {
+ var _ref2;
+
+ if (_isArray2) {
+ if (_i2 >= _iterator2.length) break;
+ _ref2 = _iterator2[_i2++];
+ } else {
+ _i2 = _iterator2.next();
+ if (_i2.done) break;
+ _ref2 = _i2.value;
+ }
+
+ var specifier = _ref2;
+
+ var name = specifier.exported.name;
+ if (name === "default") isDefault = true;
+ this.checkDuplicateExports(specifier, name, isDefault);
+ }
+ } else if (node.declaration) {
+ // Exported declarations
+ if (node.declaration.type === "FunctionDeclaration" || node.declaration.type === "ClassDeclaration") {
+ this.checkDuplicateExports(node, node.declaration.id.name, isDefault);
+ } else if (node.declaration.type === "VariableDeclaration") {
+ for (var _iterator3 = node.declaration.declarations, _isArray3 = Array.isArray(_iterator3), _i3 = 0, _iterator3 = _isArray3 ? _iterator3 : _iterator3[Symbol.iterator]();;) {
+ var _ref3;
+
+ if (_isArray3) {
+ if (_i3 >= _iterator3.length) break;
+ _ref3 = _iterator3[_i3++];
+ } else {
+ _i3 = _iterator3.next();
+ if (_i3.done) break;
+ _ref3 = _i3.value;
+ }
+
+ var declaration = _ref3;
+
+ if (declaration.id.name) {
+ this.checkDuplicateExports(declaration, declaration.id.name, isDefault);
+ }
+ }
+ }
+ }
+ }
+
+ if (this.state.decorators.length) {
+ var isClass = node.declaration && (node.declaration.type === "ClassDeclaration" || node.declaration.type === "ClassExpression");
+ if (!node.declaration || !isClass) {
+ this.raise(node.start, "You can only use decorators on an export when exporting a class");
+ }
+ this.takeDecorators(node.declaration);
+ }
+};
+
+pp.checkDuplicateExports = function (node, name, isDefault) {
+ if (this.state.exportedIdentifiers.indexOf(name) > -1) {
+ this.raiseDuplicateExportError(node, name, isDefault);
+ }
+ this.state.exportedIdentifiers.push(name);
+};
+
+pp.raiseDuplicateExportError = function (node, name, isDefault) {
+ this.raise(node.start, isDefault ? "Only one default export allowed per module." : "`" + name + "` has already been exported. Exported identifiers must be unique.");
+};
+
+// Parses a comma-separated list of module exports.
+
+pp.parseExportSpecifiers = function () {
+ var nodes = [];
+ var first = true;
+ var needsFrom = void 0;
+
+ // export { x, y as z } [from '...']
+ this.expect(_types.types.braceL);
+
+ while (!this.eat(_types.types.braceR)) {
+ if (first) {
+ first = false;
+ } else {
+ this.expect(_types.types.comma);
+ if (this.eat(_types.types.braceR)) break;
+ }
+
+ var isDefault = this.match(_types.types._default);
+ if (isDefault && !needsFrom) needsFrom = true;
+
+ var node = this.startNode();
+ node.local = this.parseIdentifier(isDefault);
+ node.exported = this.eatContextual("as") ? this.parseIdentifier(true) : node.local.__clone();
+ nodes.push(this.finishNode(node, "ExportSpecifier"));
+ }
+
+ // https://github.com/ember-cli/ember-cli/pull/3739
+ if (needsFrom && !this.isContextual("from")) {
+ this.unexpected();
+ }
+
+ return nodes;
+};
+
+// Parses import declaration.
+
+pp.parseImport = function (node) {
+ this.next();
+
+ // import '...'
+ if (this.match(_types.types.string)) {
+ node.specifiers = [];
+ node.source = this.parseExprAtom();
+ } else {
+ node.specifiers = [];
+ this.parseImportSpecifiers(node);
+ this.expectContextual("from");
+ node.source = this.match(_types.types.string) ? this.parseExprAtom() : this.unexpected();
+ }
+ this.semicolon();
+ return this.finishNode(node, "ImportDeclaration");
+};
+
+// Parses a comma-separated list of module imports.
+
+pp.parseImportSpecifiers = function (node) {
+ var first = true;
+ if (this.match(_types.types.name)) {
+ // import defaultObj, { x, y as z } from '...'
+ var startPos = this.state.start,
+ startLoc = this.state.startLoc;
+ node.specifiers.push(this.parseImportSpecifierDefault(this.parseIdentifier(), startPos, startLoc));
+ if (!this.eat(_types.types.comma)) return;
+ }
+
+ if (this.match(_types.types.star)) {
+ var specifier = this.startNode();
+ this.next();
+ this.expectContextual("as");
+ specifier.local = this.parseIdentifier();
+ this.checkLVal(specifier.local, true, undefined, "import namespace specifier");
+ node.specifiers.push(this.finishNode(specifier, "ImportNamespaceSpecifier"));
+ return;
+ }
+
+ this.expect(_types.types.braceL);
+ while (!this.eat(_types.types.braceR)) {
+ if (first) {
+ first = false;
+ } else {
+ this.expect(_types.types.comma);
+ if (this.eat(_types.types.braceR)) break;
+ }
+
+ var _specifier3 = this.startNode();
+ _specifier3.imported = this.parseIdentifier(true);
+ _specifier3.local = this.eatContextual("as") ? this.parseIdentifier() : _specifier3.imported.__clone();
+ this.checkLVal(_specifier3.local, true, undefined, "import specifier");
+ node.specifiers.push(this.finishNode(_specifier3, "ImportSpecifier"));
+ }
+};
+
+pp.parseImportSpecifierDefault = function (id, startPos, startLoc) {
+ var node = this.startNodeAt(startPos, startLoc);
+ node.local = id;
+ this.checkLVal(node.local, true, undefined, "default import specifier");
+ return this.finishNode(node, "ImportDefaultSpecifier");
+}; \ No newline at end of file