summaryrefslogtreecommitdiff
path: root/node_modules/sucrase/dist/esm/parser/traverser/statement.js
diff options
context:
space:
mode:
authorPhilipp Tanlak <philipp.tanlak@gmail.com>2025-11-24 20:54:57 +0100
committerPhilipp Tanlak <philipp.tanlak@gmail.com>2025-11-24 20:57:48 +0100
commitb1e2c8fd5cb5dfa46bc440a12eafaf56cd844b1c (patch)
tree49d360fd6cbc6a2754efe93524ac47ff0fbe0f7d /node_modules/sucrase/dist/esm/parser/traverser/statement.js
Docs
Diffstat (limited to 'node_modules/sucrase/dist/esm/parser/traverser/statement.js')
-rw-r--r--node_modules/sucrase/dist/esm/parser/traverser/statement.js1332
1 files changed, 1332 insertions, 0 deletions
diff --git a/node_modules/sucrase/dist/esm/parser/traverser/statement.js b/node_modules/sucrase/dist/esm/parser/traverser/statement.js
new file mode 100644
index 0000000..34a6511
--- /dev/null
+++ b/node_modules/sucrase/dist/esm/parser/traverser/statement.js
@@ -0,0 +1,1332 @@
+/* eslint max-len: 0 */
+
+import {File} from "../index";
+import {
+ flowAfterParseClassSuper,
+ flowAfterParseVarHead,
+ flowParseExportDeclaration,
+ flowParseExportStar,
+ flowParseIdentifierStatement,
+ flowParseImportSpecifier,
+ flowParseTypeAnnotation,
+ flowParseTypeParameterDeclaration,
+ flowShouldDisallowExportDefaultSpecifier,
+ flowShouldParseExportDeclaration,
+ flowShouldParseExportStar,
+ flowStartParseFunctionParams,
+ flowStartParseImportSpecifiers,
+ flowTryParseExportDefaultExpression,
+ flowTryParseStatement,
+} from "../plugins/flow";
+import {
+ tsAfterParseClassSuper,
+ tsAfterParseVarHead,
+ tsIsDeclarationStart,
+ tsParseExportDeclaration,
+ tsParseExportSpecifier,
+ tsParseIdentifierStatement,
+ tsParseImportEqualsDeclaration,
+ tsParseImportSpecifier,
+ tsParseMaybeDecoratorArguments,
+ tsParseModifiers,
+ tsStartParseFunctionParams,
+ tsTryParseClassMemberWithIsStatic,
+ tsTryParseExport,
+ tsTryParseExportDefaultExpression,
+ tsTryParseStatementContent,
+ tsTryParseTypeAnnotation,
+ tsTryParseTypeParameters,
+} from "../plugins/typescript";
+import {
+ eat,
+ eatTypeToken,
+ IdentifierRole,
+ lookaheadType,
+ lookaheadTypeAndKeyword,
+ match,
+ next,
+ nextTokenStart,
+ nextTokenStartSince,
+ popTypeContext,
+ pushTypeContext,
+} from "../tokenizer";
+import {ContextualKeyword} from "../tokenizer/keywords";
+import {Scope} from "../tokenizer/state";
+import { TokenType as tt} from "../tokenizer/types";
+import {charCodes} from "../util/charcodes";
+import {getNextContextId, input, isFlowEnabled, isTypeScriptEnabled, state} from "./base";
+import {
+ parseCallExpressionArguments,
+ parseExprAtom,
+ parseExpression,
+ parseExprSubscripts,
+ parseFunctionBodyAndFinish,
+ parseIdentifier,
+ parseMaybeAssign,
+ parseMethod,
+ parseObj,
+ parseParenExpression,
+ parsePropertyName,
+} from "./expression";
+import {
+ parseBindingAtom,
+ parseBindingIdentifier,
+ parseBindingList,
+ parseImportedIdentifier,
+} from "./lval";
+import {
+ canInsertSemicolon,
+ eatContextual,
+ expect,
+ expectContextual,
+ hasFollowingLineBreak,
+ hasPrecedingLineBreak,
+ isContextual,
+ isLineTerminator,
+ isLookaheadContextual,
+ semicolon,
+ unexpected,
+} from "./util";
+
+export function parseTopLevel() {
+ parseBlockBody(tt.eof);
+ state.scopes.push(new Scope(0, state.tokens.length, true));
+ if (state.scopeDepth !== 0) {
+ throw new Error(`Invalid scope depth at end of file: ${state.scopeDepth}`);
+ }
+ return new File(state.tokens, state.scopes);
+}
+
+// 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.
+
+export function parseStatement(declaration) {
+ if (isFlowEnabled) {
+ if (flowTryParseStatement()) {
+ return;
+ }
+ }
+ if (match(tt.at)) {
+ parseDecorators();
+ }
+ parseStatementContent(declaration);
+}
+
+function parseStatementContent(declaration) {
+ if (isTypeScriptEnabled) {
+ if (tsTryParseStatementContent()) {
+ return;
+ }
+ }
+
+ const starttype = state.type;
+
+ // 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 tt._break:
+ case tt._continue:
+ parseBreakContinueStatement();
+ return;
+ case tt._debugger:
+ parseDebuggerStatement();
+ return;
+ case tt._do:
+ parseDoStatement();
+ return;
+ case tt._for:
+ parseForStatement();
+ return;
+ case tt._function:
+ if (lookaheadType() === tt.dot) break;
+ if (!declaration) unexpected();
+ parseFunctionStatement();
+ return;
+
+ case tt._class:
+ if (!declaration) unexpected();
+ parseClass(true);
+ return;
+
+ case tt._if:
+ parseIfStatement();
+ return;
+ case tt._return:
+ parseReturnStatement();
+ return;
+ case tt._switch:
+ parseSwitchStatement();
+ return;
+ case tt._throw:
+ parseThrowStatement();
+ return;
+ case tt._try:
+ parseTryStatement();
+ return;
+
+ case tt._let:
+ case tt._const:
+ if (!declaration) unexpected(); // NOTE: falls through to _var
+
+ case tt._var:
+ parseVarStatement(starttype !== tt._var);
+ return;
+
+ case tt._while:
+ parseWhileStatement();
+ return;
+ case tt.braceL:
+ parseBlock();
+ return;
+ case tt.semi:
+ parseEmptyStatement();
+ return;
+ case tt._export:
+ case tt._import: {
+ const nextType = lookaheadType();
+ if (nextType === tt.parenL || nextType === tt.dot) {
+ break;
+ }
+ next();
+ if (starttype === tt._import) {
+ parseImport();
+ } else {
+ parseExport();
+ }
+ return;
+ }
+ case tt.name:
+ if (state.contextualKeyword === ContextualKeyword._async) {
+ const functionStart = state.start;
+ // peek ahead and see if next token is a function
+ const snapshot = state.snapshot();
+ next();
+ if (match(tt._function) && !canInsertSemicolon()) {
+ expect(tt._function);
+ parseFunction(functionStart, true);
+ return;
+ } else {
+ state.restoreFromSnapshot(snapshot);
+ }
+ } else if (
+ state.contextualKeyword === ContextualKeyword._using &&
+ !hasFollowingLineBreak() &&
+ // Statements like `using[0]` and `using in foo` aren't actual using
+ // declarations.
+ lookaheadType() === tt.name
+ ) {
+ parseVarStatement(true);
+ return;
+ } else if (startsAwaitUsing()) {
+ expectContextual(ContextualKeyword._await);
+ parseVarStatement(true);
+ return;
+ }
+ default:
+ // Do nothing.
+ break;
+ }
+
+ // 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.
+ const initialTokensLength = state.tokens.length;
+ parseExpression();
+ let simpleName = null;
+ if (state.tokens.length === initialTokensLength + 1) {
+ const token = state.tokens[state.tokens.length - 1];
+ if (token.type === tt.name) {
+ simpleName = token.contextualKeyword;
+ }
+ }
+ if (simpleName == null) {
+ semicolon();
+ return;
+ }
+ if (eat(tt.colon)) {
+ parseLabeledStatement();
+ } else {
+ // This was an identifier, so we might want to handle flow/typescript-specific cases.
+ parseIdentifierStatement(simpleName);
+ }
+}
+
+/**
+ * Determine if we're positioned at an `await using` declaration.
+ *
+ * Note that this can happen either in place of a regular variable declaration
+ * or in a loop body, and in both places, there are similar-looking cases where
+ * we need to return false.
+ *
+ * Examples returning true:
+ * await using foo = bar();
+ * for (await using a of b) {}
+ *
+ * Examples returning false:
+ * await using
+ * await using + 1
+ * await using instanceof T
+ * for (await using;;) {}
+ *
+ * For now, we early return if we don't see `await`, then do a simple
+ * backtracking-based lookahead for the `using` and identifier tokens. In the
+ * future, this could be optimized with a character-based approach.
+ */
+function startsAwaitUsing() {
+ if (!isContextual(ContextualKeyword._await)) {
+ return false;
+ }
+ const snapshot = state.snapshot();
+ // await
+ next();
+ if (!isContextual(ContextualKeyword._using) || hasPrecedingLineBreak()) {
+ state.restoreFromSnapshot(snapshot);
+ return false;
+ }
+ // using
+ next();
+ if (!match(tt.name) || hasPrecedingLineBreak()) {
+ state.restoreFromSnapshot(snapshot);
+ return false;
+ }
+ state.restoreFromSnapshot(snapshot);
+ return true;
+}
+
+export function parseDecorators() {
+ while (match(tt.at)) {
+ parseDecorator();
+ }
+}
+
+function parseDecorator() {
+ next();
+ if (eat(tt.parenL)) {
+ parseExpression();
+ expect(tt.parenR);
+ } else {
+ parseIdentifier();
+ while (eat(tt.dot)) {
+ parseIdentifier();
+ }
+ parseMaybeDecoratorArguments();
+ }
+}
+
+function parseMaybeDecoratorArguments() {
+ if (isTypeScriptEnabled) {
+ tsParseMaybeDecoratorArguments();
+ } else {
+ baseParseMaybeDecoratorArguments();
+ }
+}
+
+export function baseParseMaybeDecoratorArguments() {
+ if (eat(tt.parenL)) {
+ parseCallExpressionArguments();
+ }
+}
+
+function parseBreakContinueStatement() {
+ next();
+ if (!isLineTerminator()) {
+ parseIdentifier();
+ semicolon();
+ }
+}
+
+function parseDebuggerStatement() {
+ next();
+ semicolon();
+}
+
+function parseDoStatement() {
+ next();
+ parseStatement(false);
+ expect(tt._while);
+ parseParenExpression();
+ eat(tt.semi);
+}
+
+function parseForStatement() {
+ state.scopeDepth++;
+ const startTokenIndex = state.tokens.length;
+ parseAmbiguousForStatement();
+ const endTokenIndex = state.tokens.length;
+ state.scopes.push(new Scope(startTokenIndex, endTokenIndex, false));
+ state.scopeDepth--;
+}
+
+/**
+ * Determine if this token is a `using` declaration (explicit resource
+ * management) as part of a loop.
+ * https://github.com/tc39/proposal-explicit-resource-management
+ */
+function isUsingInLoop() {
+ if (!isContextual(ContextualKeyword._using)) {
+ return false;
+ }
+ // This must be `for (using of`, where `using` is the name of the loop
+ // variable.
+ if (isLookaheadContextual(ContextualKeyword._of)) {
+ return false;
+ }
+ return true;
+}
+
+// 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.
+function parseAmbiguousForStatement() {
+ next();
+
+ let forAwait = false;
+ if (isContextual(ContextualKeyword._await)) {
+ forAwait = true;
+ next();
+ }
+ expect(tt.parenL);
+
+ if (match(tt.semi)) {
+ if (forAwait) {
+ unexpected();
+ }
+ parseFor();
+ return;
+ }
+
+ const isAwaitUsing = startsAwaitUsing();
+ if (isAwaitUsing || match(tt._var) || match(tt._let) || match(tt._const) || isUsingInLoop()) {
+ if (isAwaitUsing) {
+ expectContextual(ContextualKeyword._await);
+ }
+ next();
+ parseVar(true, state.type !== tt._var);
+ if (match(tt._in) || isContextual(ContextualKeyword._of)) {
+ parseForIn(forAwait);
+ return;
+ }
+ parseFor();
+ return;
+ }
+
+ parseExpression(true);
+ if (match(tt._in) || isContextual(ContextualKeyword._of)) {
+ parseForIn(forAwait);
+ return;
+ }
+ if (forAwait) {
+ unexpected();
+ }
+ parseFor();
+}
+
+function parseFunctionStatement() {
+ const functionStart = state.start;
+ next();
+ parseFunction(functionStart, true);
+}
+
+function parseIfStatement() {
+ next();
+ parseParenExpression();
+ parseStatement(false);
+ if (eat(tt._else)) {
+ parseStatement(false);
+ }
+}
+
+function parseReturnStatement() {
+ next();
+
+ // In `return` (and `break`/`continue`), the keywords with
+ // optional arguments, we eagerly look for a semicolon or the
+ // possibility to insert one.
+
+ if (!isLineTerminator()) {
+ parseExpression();
+ semicolon();
+ }
+}
+
+function parseSwitchStatement() {
+ next();
+ parseParenExpression();
+ state.scopeDepth++;
+ const startTokenIndex = state.tokens.length;
+ expect(tt.braceL);
+
+ // Don't bother validation; just go through any sequence of cases, defaults, and statements.
+ while (!match(tt.braceR) && !state.error) {
+ if (match(tt._case) || match(tt._default)) {
+ const isCase = match(tt._case);
+ next();
+ if (isCase) {
+ parseExpression();
+ }
+ expect(tt.colon);
+ } else {
+ parseStatement(true);
+ }
+ }
+ next(); // Closing brace
+ const endTokenIndex = state.tokens.length;
+ state.scopes.push(new Scope(startTokenIndex, endTokenIndex, false));
+ state.scopeDepth--;
+}
+
+function parseThrowStatement() {
+ next();
+ parseExpression();
+ semicolon();
+}
+
+function parseCatchClauseParam() {
+ parseBindingAtom(true /* isBlockScope */);
+
+ if (isTypeScriptEnabled) {
+ tsTryParseTypeAnnotation();
+ }
+}
+
+function parseTryStatement() {
+ next();
+
+ parseBlock();
+
+ if (match(tt._catch)) {
+ next();
+ let catchBindingStartTokenIndex = null;
+ if (match(tt.parenL)) {
+ state.scopeDepth++;
+ catchBindingStartTokenIndex = state.tokens.length;
+ expect(tt.parenL);
+ parseCatchClauseParam();
+ expect(tt.parenR);
+ }
+ parseBlock();
+ if (catchBindingStartTokenIndex != null) {
+ // We need a special scope for the catch binding which includes the binding itself and the
+ // catch block.
+ const endTokenIndex = state.tokens.length;
+ state.scopes.push(new Scope(catchBindingStartTokenIndex, endTokenIndex, false));
+ state.scopeDepth--;
+ }
+ }
+ if (eat(tt._finally)) {
+ parseBlock();
+ }
+}
+
+export function parseVarStatement(isBlockScope) {
+ next();
+ parseVar(false, isBlockScope);
+ semicolon();
+}
+
+function parseWhileStatement() {
+ next();
+ parseParenExpression();
+ parseStatement(false);
+}
+
+function parseEmptyStatement() {
+ next();
+}
+
+function parseLabeledStatement() {
+ parseStatement(true);
+}
+
+/**
+ * Parse a statement starting with an identifier of the given name. Subclasses match on the name
+ * to handle statements like "declare".
+ */
+function parseIdentifierStatement(contextualKeyword) {
+ if (isTypeScriptEnabled) {
+ tsParseIdentifierStatement(contextualKeyword);
+ } else if (isFlowEnabled) {
+ flowParseIdentifierStatement(contextualKeyword);
+ } else {
+ semicolon();
+ }
+}
+
+// Parse a semicolon-enclosed block of statements.
+export function parseBlock(isFunctionScope = false, contextId = 0) {
+ const startTokenIndex = state.tokens.length;
+ state.scopeDepth++;
+ expect(tt.braceL);
+ if (contextId) {
+ state.tokens[state.tokens.length - 1].contextId = contextId;
+ }
+ parseBlockBody(tt.braceR);
+ if (contextId) {
+ state.tokens[state.tokens.length - 1].contextId = contextId;
+ }
+ const endTokenIndex = state.tokens.length;
+ state.scopes.push(new Scope(startTokenIndex, endTokenIndex, isFunctionScope));
+ state.scopeDepth--;
+}
+
+export function parseBlockBody(end) {
+ while (!eat(end) && !state.error) {
+ parseStatement(true);
+ }
+}
+
+// Parse a regular `for` loop. The disambiguation code in
+// `parseStatement` will already have parsed the init statement or
+// expression.
+
+function parseFor() {
+ expect(tt.semi);
+ if (!match(tt.semi)) {
+ parseExpression();
+ }
+ expect(tt.semi);
+ if (!match(tt.parenR)) {
+ parseExpression();
+ }
+ expect(tt.parenR);
+ parseStatement(false);
+}
+
+// Parse a `for`/`in` and `for`/`of` loop, which are almost
+// same from parser's perspective.
+
+function parseForIn(forAwait) {
+ if (forAwait) {
+ eatContextual(ContextualKeyword._of);
+ } else {
+ next();
+ }
+ parseExpression();
+ expect(tt.parenR);
+ parseStatement(false);
+}
+
+// Parse a list of variable declarations.
+
+function parseVar(isFor, isBlockScope) {
+ while (true) {
+ parseVarHead(isBlockScope);
+ if (eat(tt.eq)) {
+ const eqIndex = state.tokens.length - 1;
+ parseMaybeAssign(isFor);
+ state.tokens[eqIndex].rhsEndIndex = state.tokens.length;
+ }
+ if (!eat(tt.comma)) {
+ break;
+ }
+ }
+}
+
+function parseVarHead(isBlockScope) {
+ parseBindingAtom(isBlockScope);
+ if (isTypeScriptEnabled) {
+ tsAfterParseVarHead();
+ } else if (isFlowEnabled) {
+ flowAfterParseVarHead();
+ }
+}
+
+// Parse a function declaration or literal (depending on the
+// `isStatement` parameter).
+
+export function parseFunction(
+ functionStart,
+ isStatement,
+ optionalId = false,
+) {
+ if (match(tt.star)) {
+ next();
+ }
+
+ if (isStatement && !optionalId && !match(tt.name) && !match(tt._yield)) {
+ unexpected();
+ }
+
+ let nameScopeStartTokenIndex = null;
+
+ if (match(tt.name)) {
+ // Expression-style functions should limit their name's scope to the function body, so we make
+ // a new function scope to enforce that.
+ if (!isStatement) {
+ nameScopeStartTokenIndex = state.tokens.length;
+ state.scopeDepth++;
+ }
+ parseBindingIdentifier(false);
+ }
+
+ const startTokenIndex = state.tokens.length;
+ state.scopeDepth++;
+ parseFunctionParams();
+ parseFunctionBodyAndFinish(functionStart);
+ const endTokenIndex = state.tokens.length;
+ // In addition to the block scope of the function body, we need a separate function-style scope
+ // that includes the params.
+ state.scopes.push(new Scope(startTokenIndex, endTokenIndex, true));
+ state.scopeDepth--;
+ if (nameScopeStartTokenIndex !== null) {
+ state.scopes.push(new Scope(nameScopeStartTokenIndex, endTokenIndex, true));
+ state.scopeDepth--;
+ }
+}
+
+export function parseFunctionParams(
+ allowModifiers = false,
+ funcContextId = 0,
+) {
+ if (isTypeScriptEnabled) {
+ tsStartParseFunctionParams();
+ } else if (isFlowEnabled) {
+ flowStartParseFunctionParams();
+ }
+
+ expect(tt.parenL);
+ if (funcContextId) {
+ state.tokens[state.tokens.length - 1].contextId = funcContextId;
+ }
+ parseBindingList(
+ tt.parenR,
+ false /* isBlockScope */,
+ false /* allowEmpty */,
+ allowModifiers,
+ funcContextId,
+ );
+ if (funcContextId) {
+ state.tokens[state.tokens.length - 1].contextId = funcContextId;
+ }
+}
+
+// Parse a class declaration or literal (depending on the
+// `isStatement` parameter).
+
+export function parseClass(isStatement, optionalId = false) {
+ // Put a context ID on the class keyword, the open-brace, and the close-brace, so that later
+ // code can easily navigate to meaningful points on the class.
+ const contextId = getNextContextId();
+
+ next();
+ state.tokens[state.tokens.length - 1].contextId = contextId;
+ state.tokens[state.tokens.length - 1].isExpression = !isStatement;
+ // Like with functions, we declare a special "name scope" from the start of the name to the end
+ // of the class, but only with expression-style classes, to represent the fact that the name is
+ // available to the body of the class but not an outer declaration.
+ let nameScopeStartTokenIndex = null;
+ if (!isStatement) {
+ nameScopeStartTokenIndex = state.tokens.length;
+ state.scopeDepth++;
+ }
+ parseClassId(isStatement, optionalId);
+ parseClassSuper();
+ const openBraceIndex = state.tokens.length;
+ parseClassBody(contextId);
+ if (state.error) {
+ return;
+ }
+ state.tokens[openBraceIndex].contextId = contextId;
+ state.tokens[state.tokens.length - 1].contextId = contextId;
+ if (nameScopeStartTokenIndex !== null) {
+ const endTokenIndex = state.tokens.length;
+ state.scopes.push(new Scope(nameScopeStartTokenIndex, endTokenIndex, false));
+ state.scopeDepth--;
+ }
+}
+
+function isClassProperty() {
+ return match(tt.eq) || match(tt.semi) || match(tt.braceR) || match(tt.bang) || match(tt.colon);
+}
+
+function isClassMethod() {
+ return match(tt.parenL) || match(tt.lessThan);
+}
+
+function parseClassBody(classContextId) {
+ expect(tt.braceL);
+
+ while (!eat(tt.braceR) && !state.error) {
+ if (eat(tt.semi)) {
+ continue;
+ }
+
+ if (match(tt.at)) {
+ parseDecorator();
+ continue;
+ }
+ const memberStart = state.start;
+ parseClassMember(memberStart, classContextId);
+ }
+}
+
+function parseClassMember(memberStart, classContextId) {
+ if (isTypeScriptEnabled) {
+ tsParseModifiers([
+ ContextualKeyword._declare,
+ ContextualKeyword._public,
+ ContextualKeyword._protected,
+ ContextualKeyword._private,
+ ContextualKeyword._override,
+ ]);
+ }
+ let isStatic = false;
+ if (match(tt.name) && state.contextualKeyword === ContextualKeyword._static) {
+ parseIdentifier(); // eats 'static'
+ if (isClassMethod()) {
+ parseClassMethod(memberStart, /* isConstructor */ false);
+ return;
+ } else if (isClassProperty()) {
+ parseClassProperty();
+ return;
+ }
+ // otherwise something static
+ state.tokens[state.tokens.length - 1].type = tt._static;
+ isStatic = true;
+
+ if (match(tt.braceL)) {
+ // This is a static block. Mark the word "static" with the class context ID for class element
+ // detection and parse as a regular block.
+ state.tokens[state.tokens.length - 1].contextId = classContextId;
+ parseBlock();
+ return;
+ }
+ }
+
+ parseClassMemberWithIsStatic(memberStart, isStatic, classContextId);
+}
+
+function parseClassMemberWithIsStatic(
+ memberStart,
+ isStatic,
+ classContextId,
+) {
+ if (isTypeScriptEnabled) {
+ if (tsTryParseClassMemberWithIsStatic(isStatic)) {
+ return;
+ }
+ }
+ if (eat(tt.star)) {
+ // a generator
+ parseClassPropertyName(classContextId);
+ parseClassMethod(memberStart, /* isConstructor */ false);
+ return;
+ }
+
+ // Get the identifier name so we can tell if it's actually a keyword like "async", "get", or
+ // "set".
+ parseClassPropertyName(classContextId);
+ let isConstructor = false;
+ const token = state.tokens[state.tokens.length - 1];
+ // We allow "constructor" as either an identifier or a string.
+ if (token.contextualKeyword === ContextualKeyword._constructor) {
+ isConstructor = true;
+ }
+ parsePostMemberNameModifiers();
+
+ if (isClassMethod()) {
+ parseClassMethod(memberStart, isConstructor);
+ } else if (isClassProperty()) {
+ parseClassProperty();
+ } else if (token.contextualKeyword === ContextualKeyword._async && !isLineTerminator()) {
+ state.tokens[state.tokens.length - 1].type = tt._async;
+ // an async method
+ const isGenerator = match(tt.star);
+ if (isGenerator) {
+ next();
+ }
+
+ // The so-called parsed name would have been "async": get the real name.
+ parseClassPropertyName(classContextId);
+ parsePostMemberNameModifiers();
+ parseClassMethod(memberStart, false /* isConstructor */);
+ } else if (
+ (token.contextualKeyword === ContextualKeyword._get ||
+ token.contextualKeyword === ContextualKeyword._set) &&
+ !(isLineTerminator() && match(tt.star))
+ ) {
+ if (token.contextualKeyword === ContextualKeyword._get) {
+ state.tokens[state.tokens.length - 1].type = tt._get;
+ } else {
+ state.tokens[state.tokens.length - 1].type = tt._set;
+ }
+ // `get\n*` is an uninitialized property named 'get' followed by a generator.
+ // a getter or setter
+ // The so-called parsed name would have been "get/set": get the real name.
+ parseClassPropertyName(classContextId);
+ parseClassMethod(memberStart, /* isConstructor */ false);
+ } else if (token.contextualKeyword === ContextualKeyword._accessor && !isLineTerminator()) {
+ parseClassPropertyName(classContextId);
+ parseClassProperty();
+ } else if (isLineTerminator()) {
+ // an uninitialized class property (due to ASI, since we don't otherwise recognize the next token)
+ parseClassProperty();
+ } else {
+ unexpected();
+ }
+}
+
+function parseClassMethod(functionStart, isConstructor) {
+ if (isTypeScriptEnabled) {
+ tsTryParseTypeParameters();
+ } else if (isFlowEnabled) {
+ if (match(tt.lessThan)) {
+ flowParseTypeParameterDeclaration();
+ }
+ }
+ parseMethod(functionStart, isConstructor);
+}
+
+// Return the name of the class property, if it is a simple identifier.
+export function parseClassPropertyName(classContextId) {
+ parsePropertyName(classContextId);
+}
+
+export function parsePostMemberNameModifiers() {
+ if (isTypeScriptEnabled) {
+ const oldIsType = pushTypeContext(0);
+ eat(tt.question);
+ popTypeContext(oldIsType);
+ }
+}
+
+export function parseClassProperty() {
+ if (isTypeScriptEnabled) {
+ eatTypeToken(tt.bang);
+ tsTryParseTypeAnnotation();
+ } else if (isFlowEnabled) {
+ if (match(tt.colon)) {
+ flowParseTypeAnnotation();
+ }
+ }
+
+ if (match(tt.eq)) {
+ const equalsTokenIndex = state.tokens.length;
+ next();
+ parseMaybeAssign();
+ state.tokens[equalsTokenIndex].rhsEndIndex = state.tokens.length;
+ }
+ semicolon();
+}
+
+function parseClassId(isStatement, optionalId = false) {
+ if (
+ isTypeScriptEnabled &&
+ (!isStatement || optionalId) &&
+ isContextual(ContextualKeyword._implements)
+ ) {
+ return;
+ }
+
+ if (match(tt.name)) {
+ parseBindingIdentifier(true);
+ }
+
+ if (isTypeScriptEnabled) {
+ tsTryParseTypeParameters();
+ } else if (isFlowEnabled) {
+ if (match(tt.lessThan)) {
+ flowParseTypeParameterDeclaration();
+ }
+ }
+}
+
+// Returns true if there was a superclass.
+function parseClassSuper() {
+ let hasSuper = false;
+ if (eat(tt._extends)) {
+ parseExprSubscripts();
+ hasSuper = true;
+ } else {
+ hasSuper = false;
+ }
+ if (isTypeScriptEnabled) {
+ tsAfterParseClassSuper(hasSuper);
+ } else if (isFlowEnabled) {
+ flowAfterParseClassSuper(hasSuper);
+ }
+}
+
+// Parses module export declaration.
+
+export function parseExport() {
+ const exportIndex = state.tokens.length - 1;
+ if (isTypeScriptEnabled) {
+ if (tsTryParseExport()) {
+ return;
+ }
+ }
+ // export * from '...'
+ if (shouldParseExportStar()) {
+ parseExportStar();
+ } else if (isExportDefaultSpecifier()) {
+ // export default from
+ parseIdentifier();
+ if (match(tt.comma) && lookaheadType() === tt.star) {
+ expect(tt.comma);
+ expect(tt.star);
+ expectContextual(ContextualKeyword._as);
+ parseIdentifier();
+ } else {
+ parseExportSpecifiersMaybe();
+ }
+ parseExportFrom();
+ } else if (eat(tt._default)) {
+ // export default ...
+ parseExportDefaultExpression();
+ } else if (shouldParseExportDeclaration()) {
+ parseExportDeclaration();
+ } else {
+ // export { x, y as z } [from '...']
+ parseExportSpecifiers();
+ parseExportFrom();
+ }
+ state.tokens[exportIndex].rhsEndIndex = state.tokens.length;
+}
+
+function parseExportDefaultExpression() {
+ if (isTypeScriptEnabled) {
+ if (tsTryParseExportDefaultExpression()) {
+ return;
+ }
+ }
+ if (isFlowEnabled) {
+ if (flowTryParseExportDefaultExpression()) {
+ return;
+ }
+ }
+ const functionStart = state.start;
+ if (eat(tt._function)) {
+ parseFunction(functionStart, true, true);
+ } else if (isContextual(ContextualKeyword._async) && lookaheadType() === tt._function) {
+ // async function declaration
+ eatContextual(ContextualKeyword._async);
+ eat(tt._function);
+ parseFunction(functionStart, true, true);
+ } else if (match(tt._class)) {
+ parseClass(true, true);
+ } else if (match(tt.at)) {
+ parseDecorators();
+ parseClass(true, true);
+ } else {
+ parseMaybeAssign();
+ semicolon();
+ }
+}
+
+function parseExportDeclaration() {
+ if (isTypeScriptEnabled) {
+ tsParseExportDeclaration();
+ } else if (isFlowEnabled) {
+ flowParseExportDeclaration();
+ } else {
+ parseStatement(true);
+ }
+}
+
+function isExportDefaultSpecifier() {
+ if (isTypeScriptEnabled && tsIsDeclarationStart()) {
+ return false;
+ } else if (isFlowEnabled && flowShouldDisallowExportDefaultSpecifier()) {
+ return false;
+ }
+ if (match(tt.name)) {
+ return state.contextualKeyword !== ContextualKeyword._async;
+ }
+
+ if (!match(tt._default)) {
+ return false;
+ }
+
+ const _next = nextTokenStart();
+ const lookahead = lookaheadTypeAndKeyword();
+ const hasFrom =
+ lookahead.type === tt.name && lookahead.contextualKeyword === ContextualKeyword._from;
+ if (lookahead.type === tt.comma) {
+ return true;
+ }
+ // lookahead again when `export default from` is seen
+ if (hasFrom) {
+ const nextAfterFrom = input.charCodeAt(nextTokenStartSince(_next + 4));
+ return nextAfterFrom === charCodes.quotationMark || nextAfterFrom === charCodes.apostrophe;
+ }
+ return false;
+}
+
+function parseExportSpecifiersMaybe() {
+ if (eat(tt.comma)) {
+ parseExportSpecifiers();
+ }
+}
+
+export function parseExportFrom() {
+ if (eatContextual(ContextualKeyword._from)) {
+ parseExprAtom();
+ maybeParseImportAttributes();
+ }
+ semicolon();
+}
+
+function shouldParseExportStar() {
+ if (isFlowEnabled) {
+ return flowShouldParseExportStar();
+ } else {
+ return match(tt.star);
+ }
+}
+
+function parseExportStar() {
+ if (isFlowEnabled) {
+ flowParseExportStar();
+ } else {
+ baseParseExportStar();
+ }
+}
+
+export function baseParseExportStar() {
+ expect(tt.star);
+
+ if (isContextual(ContextualKeyword._as)) {
+ parseExportNamespace();
+ } else {
+ parseExportFrom();
+ }
+}
+
+function parseExportNamespace() {
+ next();
+ state.tokens[state.tokens.length - 1].type = tt._as;
+ parseIdentifier();
+ parseExportSpecifiersMaybe();
+ parseExportFrom();
+}
+
+function shouldParseExportDeclaration() {
+ return (
+ (isTypeScriptEnabled && tsIsDeclarationStart()) ||
+ (isFlowEnabled && flowShouldParseExportDeclaration()) ||
+ state.type === tt._var ||
+ state.type === tt._const ||
+ state.type === tt._let ||
+ state.type === tt._function ||
+ state.type === tt._class ||
+ isContextual(ContextualKeyword._async) ||
+ match(tt.at)
+ );
+}
+
+// Parses a comma-separated list of module exports.
+export function parseExportSpecifiers() {
+ let first = true;
+
+ // export { x, y as z } [from '...']
+ expect(tt.braceL);
+
+ while (!eat(tt.braceR) && !state.error) {
+ if (first) {
+ first = false;
+ } else {
+ expect(tt.comma);
+ if (eat(tt.braceR)) {
+ break;
+ }
+ }
+ parseExportSpecifier();
+ }
+}
+
+function parseExportSpecifier() {
+ if (isTypeScriptEnabled) {
+ tsParseExportSpecifier();
+ return;
+ }
+ parseIdentifier();
+ state.tokens[state.tokens.length - 1].identifierRole = IdentifierRole.ExportAccess;
+ if (eatContextual(ContextualKeyword._as)) {
+ parseIdentifier();
+ }
+}
+
+/**
+ * Starting at the `module` token in an import, determine if it was truly an
+ * import reflection token or just looks like one.
+ *
+ * Returns true for:
+ * import module foo from "foo";
+ * import module from from "foo";
+ *
+ * Returns false for:
+ * import module from "foo";
+ * import module, {bar} from "foo";
+ */
+function isImportReflection() {
+ const snapshot = state.snapshot();
+ expectContextual(ContextualKeyword._module);
+ if (eatContextual(ContextualKeyword._from)) {
+ if (isContextual(ContextualKeyword._from)) {
+ state.restoreFromSnapshot(snapshot);
+ return true;
+ } else {
+ state.restoreFromSnapshot(snapshot);
+ return false;
+ }
+ } else if (match(tt.comma)) {
+ state.restoreFromSnapshot(snapshot);
+ return false;
+ } else {
+ state.restoreFromSnapshot(snapshot);
+ return true;
+ }
+}
+
+/**
+ * Eat the "module" token from the import reflection proposal.
+ * https://github.com/tc39/proposal-import-reflection
+ */
+function parseMaybeImportReflection() {
+ // isImportReflection does snapshot/restore, so only run it if we see the word
+ // "module".
+ if (isContextual(ContextualKeyword._module) && isImportReflection()) {
+ next();
+ }
+}
+
+// Parses import declaration.
+
+export function parseImport() {
+ if (isTypeScriptEnabled && match(tt.name) && lookaheadType() === tt.eq) {
+ tsParseImportEqualsDeclaration();
+ return;
+ }
+ if (isTypeScriptEnabled && isContextual(ContextualKeyword._type)) {
+ const lookahead = lookaheadTypeAndKeyword();
+ if (lookahead.type === tt.name && lookahead.contextualKeyword !== ContextualKeyword._from) {
+ // One of these `import type` cases:
+ // import type T = require('T');
+ // import type A from 'A';
+ expectContextual(ContextualKeyword._type);
+ if (lookaheadType() === tt.eq) {
+ tsParseImportEqualsDeclaration();
+ return;
+ }
+ // If this is an `import type...from` statement, then we already ate the
+ // type token, so proceed to the regular import parser.
+ } else if (lookahead.type === tt.star || lookahead.type === tt.braceL) {
+ // One of these `import type` cases, in which case we can eat the type token
+ // and proceed as normal:
+ // import type * as A from 'A';
+ // import type {a} from 'A';
+ expectContextual(ContextualKeyword._type);
+ }
+ // Otherwise, we are importing the name "type".
+ }
+
+ // import '...'
+ if (match(tt.string)) {
+ parseExprAtom();
+ } else {
+ parseMaybeImportReflection();
+ parseImportSpecifiers();
+ expectContextual(ContextualKeyword._from);
+ parseExprAtom();
+ }
+ maybeParseImportAttributes();
+ semicolon();
+}
+
+// eslint-disable-next-line no-unused-vars
+function shouldParseDefaultImport() {
+ return match(tt.name);
+}
+
+function parseImportSpecifierLocal() {
+ parseImportedIdentifier();
+}
+
+// Parses a comma-separated list of module imports.
+function parseImportSpecifiers() {
+ if (isFlowEnabled) {
+ flowStartParseImportSpecifiers();
+ }
+
+ let first = true;
+ if (shouldParseDefaultImport()) {
+ // import defaultObj, { x, y as z } from '...'
+ parseImportSpecifierLocal();
+
+ if (!eat(tt.comma)) return;
+ }
+
+ if (match(tt.star)) {
+ next();
+ expectContextual(ContextualKeyword._as);
+
+ parseImportSpecifierLocal();
+
+ return;
+ }
+
+ expect(tt.braceL);
+ while (!eat(tt.braceR) && !state.error) {
+ if (first) {
+ first = false;
+ } else {
+ // Detect an attempt to deep destructure
+ if (eat(tt.colon)) {
+ unexpected(
+ "ES2015 named imports do not destructure. Use another statement for destructuring after the import.",
+ );
+ }
+
+ expect(tt.comma);
+ if (eat(tt.braceR)) {
+ break;
+ }
+ }
+
+ parseImportSpecifier();
+ }
+}
+
+function parseImportSpecifier() {
+ if (isTypeScriptEnabled) {
+ tsParseImportSpecifier();
+ return;
+ }
+ if (isFlowEnabled) {
+ flowParseImportSpecifier();
+ return;
+ }
+ parseImportedIdentifier();
+ if (isContextual(ContextualKeyword._as)) {
+ state.tokens[state.tokens.length - 1].identifierRole = IdentifierRole.ImportAccess;
+ next();
+ parseImportedIdentifier();
+ }
+}
+
+/**
+ * Parse import attributes like `with {type: "json"}`, or the legacy form
+ * `assert {type: "json"}`.
+ *
+ * Import attributes technically have their own syntax, but are always parseable
+ * as a plain JS object, so just do that for simplicity.
+ */
+function maybeParseImportAttributes() {
+ if (match(tt._with) || (isContextual(ContextualKeyword._assert) && !hasPrecedingLineBreak())) {
+ next();
+ parseObj(false, false);
+ }
+}