"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"); };