source: frontend/node_modules/sucrase/dist/esm/parser/traverser/statement.js

Last change on this file was 9af201e, checked in by MBK <marija.karapandzova@…>, 2 weeks ago

Fix frontend appearance

  • Property mode set to 100644
File size: 33.4 KB
Line 
1/* eslint max-len: 0 */
2
3import {File} from "../index";
4import {
5 flowAfterParseClassSuper,
6 flowAfterParseVarHead,
7 flowParseExportDeclaration,
8 flowParseExportStar,
9 flowParseIdentifierStatement,
10 flowParseImportSpecifier,
11 flowParseTypeAnnotation,
12 flowParseTypeParameterDeclaration,
13 flowShouldDisallowExportDefaultSpecifier,
14 flowShouldParseExportDeclaration,
15 flowShouldParseExportStar,
16 flowStartParseFunctionParams,
17 flowStartParseImportSpecifiers,
18 flowTryParseExportDefaultExpression,
19 flowTryParseStatement,
20} from "../plugins/flow";
21import {
22 tsAfterParseClassSuper,
23 tsAfterParseVarHead,
24 tsIsDeclarationStart,
25 tsParseExportDeclaration,
26 tsParseExportSpecifier,
27 tsParseIdentifierStatement,
28 tsParseImportEqualsDeclaration,
29 tsParseImportSpecifier,
30 tsParseMaybeDecoratorArguments,
31 tsParseModifiers,
32 tsStartParseFunctionParams,
33 tsTryParseClassMemberWithIsStatic,
34 tsTryParseExport,
35 tsTryParseExportDefaultExpression,
36 tsTryParseStatementContent,
37 tsTryParseTypeAnnotation,
38 tsTryParseTypeParameters,
39} from "../plugins/typescript";
40import {
41 eat,
42 eatTypeToken,
43 IdentifierRole,
44 lookaheadType,
45 lookaheadTypeAndKeyword,
46 match,
47 next,
48 nextTokenStart,
49 nextTokenStartSince,
50 popTypeContext,
51 pushTypeContext,
52} from "../tokenizer";
53import {ContextualKeyword} from "../tokenizer/keywords";
54import {Scope} from "../tokenizer/state";
55import { TokenType as tt} from "../tokenizer/types";
56import {charCodes} from "../util/charcodes";
57import {getNextContextId, input, isFlowEnabled, isTypeScriptEnabled, state} from "./base";
58import {
59 parseCallExpressionArguments,
60 parseExprAtom,
61 parseExpression,
62 parseExprSubscripts,
63 parseFunctionBodyAndFinish,
64 parseIdentifier,
65 parseMaybeAssign,
66 parseMethod,
67 parseObj,
68 parseParenExpression,
69 parsePropertyName,
70} from "./expression";
71import {
72 parseBindingAtom,
73 parseBindingIdentifier,
74 parseBindingList,
75 parseImportedIdentifier,
76} from "./lval";
77import {
78 canInsertSemicolon,
79 eatContextual,
80 expect,
81 expectContextual,
82 hasFollowingLineBreak,
83 hasPrecedingLineBreak,
84 isContextual,
85 isLineTerminator,
86 isLookaheadContextual,
87 semicolon,
88 unexpected,
89} from "./util";
90
91export function parseTopLevel() {
92 parseBlockBody(tt.eof);
93 state.scopes.push(new Scope(0, state.tokens.length, true));
94 if (state.scopeDepth !== 0) {
95 throw new Error(`Invalid scope depth at end of file: ${state.scopeDepth}`);
96 }
97 return new File(state.tokens, state.scopes);
98}
99
100// Parse a single statement.
101//
102// If expecting a statement and finding a slash operator, parse a
103// regular expression literal. This is to handle cases like
104// `if (foo) /blah/.exec(foo)`, where looking at the previous token
105// does not help.
106
107export function parseStatement(declaration) {
108 if (isFlowEnabled) {
109 if (flowTryParseStatement()) {
110 return;
111 }
112 }
113 if (match(tt.at)) {
114 parseDecorators();
115 }
116 parseStatementContent(declaration);
117}
118
119function parseStatementContent(declaration) {
120 if (isTypeScriptEnabled) {
121 if (tsTryParseStatementContent()) {
122 return;
123 }
124 }
125
126 const starttype = state.type;
127
128 // Most types of statements are recognized by the keyword they
129 // start with. Many are trivial to parse, some require a bit of
130 // complexity.
131
132 switch (starttype) {
133 case tt._break:
134 case tt._continue:
135 parseBreakContinueStatement();
136 return;
137 case tt._debugger:
138 parseDebuggerStatement();
139 return;
140 case tt._do:
141 parseDoStatement();
142 return;
143 case tt._for:
144 parseForStatement();
145 return;
146 case tt._function:
147 if (lookaheadType() === tt.dot) break;
148 if (!declaration) unexpected();
149 parseFunctionStatement();
150 return;
151
152 case tt._class:
153 if (!declaration) unexpected();
154 parseClass(true);
155 return;
156
157 case tt._if:
158 parseIfStatement();
159 return;
160 case tt._return:
161 parseReturnStatement();
162 return;
163 case tt._switch:
164 parseSwitchStatement();
165 return;
166 case tt._throw:
167 parseThrowStatement();
168 return;
169 case tt._try:
170 parseTryStatement();
171 return;
172
173 case tt._let:
174 case tt._const:
175 if (!declaration) unexpected(); // NOTE: falls through to _var
176
177 case tt._var:
178 parseVarStatement(starttype !== tt._var);
179 return;
180
181 case tt._while:
182 parseWhileStatement();
183 return;
184 case tt.braceL:
185 parseBlock();
186 return;
187 case tt.semi:
188 parseEmptyStatement();
189 return;
190 case tt._export:
191 case tt._import: {
192 const nextType = lookaheadType();
193 if (nextType === tt.parenL || nextType === tt.dot) {
194 break;
195 }
196 next();
197 if (starttype === tt._import) {
198 parseImport();
199 } else {
200 parseExport();
201 }
202 return;
203 }
204 case tt.name:
205 if (state.contextualKeyword === ContextualKeyword._async) {
206 const functionStart = state.start;
207 // peek ahead and see if next token is a function
208 const snapshot = state.snapshot();
209 next();
210 if (match(tt._function) && !canInsertSemicolon()) {
211 expect(tt._function);
212 parseFunction(functionStart, true);
213 return;
214 } else {
215 state.restoreFromSnapshot(snapshot);
216 }
217 } else if (
218 state.contextualKeyword === ContextualKeyword._using &&
219 !hasFollowingLineBreak() &&
220 // Statements like `using[0]` and `using in foo` aren't actual using
221 // declarations.
222 lookaheadType() === tt.name
223 ) {
224 parseVarStatement(true);
225 return;
226 } else if (startsAwaitUsing()) {
227 expectContextual(ContextualKeyword._await);
228 parseVarStatement(true);
229 return;
230 }
231 default:
232 // Do nothing.
233 break;
234 }
235
236 // If the statement does not start with a statement keyword or a
237 // brace, it's an ExpressionStatement or LabeledStatement. We
238 // simply start parsing an expression, and afterwards, if the
239 // next token is a colon and the expression was a simple
240 // Identifier node, we switch to interpreting it as a label.
241 const initialTokensLength = state.tokens.length;
242 parseExpression();
243 let simpleName = null;
244 if (state.tokens.length === initialTokensLength + 1) {
245 const token = state.tokens[state.tokens.length - 1];
246 if (token.type === tt.name) {
247 simpleName = token.contextualKeyword;
248 }
249 }
250 if (simpleName == null) {
251 semicolon();
252 return;
253 }
254 if (eat(tt.colon)) {
255 parseLabeledStatement();
256 } else {
257 // This was an identifier, so we might want to handle flow/typescript-specific cases.
258 parseIdentifierStatement(simpleName);
259 }
260}
261
262/**
263 * Determine if we're positioned at an `await using` declaration.
264 *
265 * Note that this can happen either in place of a regular variable declaration
266 * or in a loop body, and in both places, there are similar-looking cases where
267 * we need to return false.
268 *
269 * Examples returning true:
270 * await using foo = bar();
271 * for (await using a of b) {}
272 *
273 * Examples returning false:
274 * await using
275 * await using + 1
276 * await using instanceof T
277 * for (await using;;) {}
278 *
279 * For now, we early return if we don't see `await`, then do a simple
280 * backtracking-based lookahead for the `using` and identifier tokens. In the
281 * future, this could be optimized with a character-based approach.
282 */
283function startsAwaitUsing() {
284 if (!isContextual(ContextualKeyword._await)) {
285 return false;
286 }
287 const snapshot = state.snapshot();
288 // await
289 next();
290 if (!isContextual(ContextualKeyword._using) || hasPrecedingLineBreak()) {
291 state.restoreFromSnapshot(snapshot);
292 return false;
293 }
294 // using
295 next();
296 if (!match(tt.name) || hasPrecedingLineBreak()) {
297 state.restoreFromSnapshot(snapshot);
298 return false;
299 }
300 state.restoreFromSnapshot(snapshot);
301 return true;
302}
303
304export function parseDecorators() {
305 while (match(tt.at)) {
306 parseDecorator();
307 }
308}
309
310function parseDecorator() {
311 next();
312 if (eat(tt.parenL)) {
313 parseExpression();
314 expect(tt.parenR);
315 } else {
316 parseIdentifier();
317 while (eat(tt.dot)) {
318 parseIdentifier();
319 }
320 parseMaybeDecoratorArguments();
321 }
322}
323
324function parseMaybeDecoratorArguments() {
325 if (isTypeScriptEnabled) {
326 tsParseMaybeDecoratorArguments();
327 } else {
328 baseParseMaybeDecoratorArguments();
329 }
330}
331
332export function baseParseMaybeDecoratorArguments() {
333 if (eat(tt.parenL)) {
334 parseCallExpressionArguments();
335 }
336}
337
338function parseBreakContinueStatement() {
339 next();
340 if (!isLineTerminator()) {
341 parseIdentifier();
342 semicolon();
343 }
344}
345
346function parseDebuggerStatement() {
347 next();
348 semicolon();
349}
350
351function parseDoStatement() {
352 next();
353 parseStatement(false);
354 expect(tt._while);
355 parseParenExpression();
356 eat(tt.semi);
357}
358
359function parseForStatement() {
360 state.scopeDepth++;
361 const startTokenIndex = state.tokens.length;
362 parseAmbiguousForStatement();
363 const endTokenIndex = state.tokens.length;
364 state.scopes.push(new Scope(startTokenIndex, endTokenIndex, false));
365 state.scopeDepth--;
366}
367
368/**
369 * Determine if this token is a `using` declaration (explicit resource
370 * management) as part of a loop.
371 * https://github.com/tc39/proposal-explicit-resource-management
372 */
373function isUsingInLoop() {
374 if (!isContextual(ContextualKeyword._using)) {
375 return false;
376 }
377 // This must be `for (using of`, where `using` is the name of the loop
378 // variable.
379 if (isLookaheadContextual(ContextualKeyword._of)) {
380 return false;
381 }
382 return true;
383}
384
385// Disambiguating between a `for` and a `for`/`in` or `for`/`of`
386// loop is non-trivial. Basically, we have to parse the init `var`
387// statement or expression, disallowing the `in` operator (see
388// the second parameter to `parseExpression`), and then check
389// whether the next token is `in` or `of`. When there is no init
390// part (semicolon immediately after the opening parenthesis), it
391// is a regular `for` loop.
392function parseAmbiguousForStatement() {
393 next();
394
395 let forAwait = false;
396 if (isContextual(ContextualKeyword._await)) {
397 forAwait = true;
398 next();
399 }
400 expect(tt.parenL);
401
402 if (match(tt.semi)) {
403 if (forAwait) {
404 unexpected();
405 }
406 parseFor();
407 return;
408 }
409
410 const isAwaitUsing = startsAwaitUsing();
411 if (isAwaitUsing || match(tt._var) || match(tt._let) || match(tt._const) || isUsingInLoop()) {
412 if (isAwaitUsing) {
413 expectContextual(ContextualKeyword._await);
414 }
415 next();
416 parseVar(true, state.type !== tt._var);
417 if (match(tt._in) || isContextual(ContextualKeyword._of)) {
418 parseForIn(forAwait);
419 return;
420 }
421 parseFor();
422 return;
423 }
424
425 parseExpression(true);
426 if (match(tt._in) || isContextual(ContextualKeyword._of)) {
427 parseForIn(forAwait);
428 return;
429 }
430 if (forAwait) {
431 unexpected();
432 }
433 parseFor();
434}
435
436function parseFunctionStatement() {
437 const functionStart = state.start;
438 next();
439 parseFunction(functionStart, true);
440}
441
442function parseIfStatement() {
443 next();
444 parseParenExpression();
445 parseStatement(false);
446 if (eat(tt._else)) {
447 parseStatement(false);
448 }
449}
450
451function parseReturnStatement() {
452 next();
453
454 // In `return` (and `break`/`continue`), the keywords with
455 // optional arguments, we eagerly look for a semicolon or the
456 // possibility to insert one.
457
458 if (!isLineTerminator()) {
459 parseExpression();
460 semicolon();
461 }
462}
463
464function parseSwitchStatement() {
465 next();
466 parseParenExpression();
467 state.scopeDepth++;
468 const startTokenIndex = state.tokens.length;
469 expect(tt.braceL);
470
471 // Don't bother validation; just go through any sequence of cases, defaults, and statements.
472 while (!match(tt.braceR) && !state.error) {
473 if (match(tt._case) || match(tt._default)) {
474 const isCase = match(tt._case);
475 next();
476 if (isCase) {
477 parseExpression();
478 }
479 expect(tt.colon);
480 } else {
481 parseStatement(true);
482 }
483 }
484 next(); // Closing brace
485 const endTokenIndex = state.tokens.length;
486 state.scopes.push(new Scope(startTokenIndex, endTokenIndex, false));
487 state.scopeDepth--;
488}
489
490function parseThrowStatement() {
491 next();
492 parseExpression();
493 semicolon();
494}
495
496function parseCatchClauseParam() {
497 parseBindingAtom(true /* isBlockScope */);
498
499 if (isTypeScriptEnabled) {
500 tsTryParseTypeAnnotation();
501 }
502}
503
504function parseTryStatement() {
505 next();
506
507 parseBlock();
508
509 if (match(tt._catch)) {
510 next();
511 let catchBindingStartTokenIndex = null;
512 if (match(tt.parenL)) {
513 state.scopeDepth++;
514 catchBindingStartTokenIndex = state.tokens.length;
515 expect(tt.parenL);
516 parseCatchClauseParam();
517 expect(tt.parenR);
518 }
519 parseBlock();
520 if (catchBindingStartTokenIndex != null) {
521 // We need a special scope for the catch binding which includes the binding itself and the
522 // catch block.
523 const endTokenIndex = state.tokens.length;
524 state.scopes.push(new Scope(catchBindingStartTokenIndex, endTokenIndex, false));
525 state.scopeDepth--;
526 }
527 }
528 if (eat(tt._finally)) {
529 parseBlock();
530 }
531}
532
533export function parseVarStatement(isBlockScope) {
534 next();
535 parseVar(false, isBlockScope);
536 semicolon();
537}
538
539function parseWhileStatement() {
540 next();
541 parseParenExpression();
542 parseStatement(false);
543}
544
545function parseEmptyStatement() {
546 next();
547}
548
549function parseLabeledStatement() {
550 parseStatement(true);
551}
552
553/**
554 * Parse a statement starting with an identifier of the given name. Subclasses match on the name
555 * to handle statements like "declare".
556 */
557function parseIdentifierStatement(contextualKeyword) {
558 if (isTypeScriptEnabled) {
559 tsParseIdentifierStatement(contextualKeyword);
560 } else if (isFlowEnabled) {
561 flowParseIdentifierStatement(contextualKeyword);
562 } else {
563 semicolon();
564 }
565}
566
567// Parse a semicolon-enclosed block of statements.
568export function parseBlock(isFunctionScope = false, contextId = 0) {
569 const startTokenIndex = state.tokens.length;
570 state.scopeDepth++;
571 expect(tt.braceL);
572 if (contextId) {
573 state.tokens[state.tokens.length - 1].contextId = contextId;
574 }
575 parseBlockBody(tt.braceR);
576 if (contextId) {
577 state.tokens[state.tokens.length - 1].contextId = contextId;
578 }
579 const endTokenIndex = state.tokens.length;
580 state.scopes.push(new Scope(startTokenIndex, endTokenIndex, isFunctionScope));
581 state.scopeDepth--;
582}
583
584export function parseBlockBody(end) {
585 while (!eat(end) && !state.error) {
586 parseStatement(true);
587 }
588}
589
590// Parse a regular `for` loop. The disambiguation code in
591// `parseStatement` will already have parsed the init statement or
592// expression.
593
594function parseFor() {
595 expect(tt.semi);
596 if (!match(tt.semi)) {
597 parseExpression();
598 }
599 expect(tt.semi);
600 if (!match(tt.parenR)) {
601 parseExpression();
602 }
603 expect(tt.parenR);
604 parseStatement(false);
605}
606
607// Parse a `for`/`in` and `for`/`of` loop, which are almost
608// same from parser's perspective.
609
610function parseForIn(forAwait) {
611 if (forAwait) {
612 eatContextual(ContextualKeyword._of);
613 } else {
614 next();
615 }
616 parseExpression();
617 expect(tt.parenR);
618 parseStatement(false);
619}
620
621// Parse a list of variable declarations.
622
623function parseVar(isFor, isBlockScope) {
624 while (true) {
625 parseVarHead(isBlockScope);
626 if (eat(tt.eq)) {
627 const eqIndex = state.tokens.length - 1;
628 parseMaybeAssign(isFor);
629 state.tokens[eqIndex].rhsEndIndex = state.tokens.length;
630 }
631 if (!eat(tt.comma)) {
632 break;
633 }
634 }
635}
636
637function parseVarHead(isBlockScope) {
638 parseBindingAtom(isBlockScope);
639 if (isTypeScriptEnabled) {
640 tsAfterParseVarHead();
641 } else if (isFlowEnabled) {
642 flowAfterParseVarHead();
643 }
644}
645
646// Parse a function declaration or literal (depending on the
647// `isStatement` parameter).
648
649export function parseFunction(
650 functionStart,
651 isStatement,
652 optionalId = false,
653) {
654 if (match(tt.star)) {
655 next();
656 }
657
658 if (isStatement && !optionalId && !match(tt.name) && !match(tt._yield)) {
659 unexpected();
660 }
661
662 let nameScopeStartTokenIndex = null;
663
664 if (match(tt.name)) {
665 // Expression-style functions should limit their name's scope to the function body, so we make
666 // a new function scope to enforce that.
667 if (!isStatement) {
668 nameScopeStartTokenIndex = state.tokens.length;
669 state.scopeDepth++;
670 }
671 parseBindingIdentifier(false);
672 }
673
674 const startTokenIndex = state.tokens.length;
675 state.scopeDepth++;
676 parseFunctionParams();
677 parseFunctionBodyAndFinish(functionStart);
678 const endTokenIndex = state.tokens.length;
679 // In addition to the block scope of the function body, we need a separate function-style scope
680 // that includes the params.
681 state.scopes.push(new Scope(startTokenIndex, endTokenIndex, true));
682 state.scopeDepth--;
683 if (nameScopeStartTokenIndex !== null) {
684 state.scopes.push(new Scope(nameScopeStartTokenIndex, endTokenIndex, true));
685 state.scopeDepth--;
686 }
687}
688
689export function parseFunctionParams(
690 allowModifiers = false,
691 funcContextId = 0,
692) {
693 if (isTypeScriptEnabled) {
694 tsStartParseFunctionParams();
695 } else if (isFlowEnabled) {
696 flowStartParseFunctionParams();
697 }
698
699 expect(tt.parenL);
700 if (funcContextId) {
701 state.tokens[state.tokens.length - 1].contextId = funcContextId;
702 }
703 parseBindingList(
704 tt.parenR,
705 false /* isBlockScope */,
706 false /* allowEmpty */,
707 allowModifiers,
708 funcContextId,
709 );
710 if (funcContextId) {
711 state.tokens[state.tokens.length - 1].contextId = funcContextId;
712 }
713}
714
715// Parse a class declaration or literal (depending on the
716// `isStatement` parameter).
717
718export function parseClass(isStatement, optionalId = false) {
719 // Put a context ID on the class keyword, the open-brace, and the close-brace, so that later
720 // code can easily navigate to meaningful points on the class.
721 const contextId = getNextContextId();
722
723 next();
724 state.tokens[state.tokens.length - 1].contextId = contextId;
725 state.tokens[state.tokens.length - 1].isExpression = !isStatement;
726 // Like with functions, we declare a special "name scope" from the start of the name to the end
727 // of the class, but only with expression-style classes, to represent the fact that the name is
728 // available to the body of the class but not an outer declaration.
729 let nameScopeStartTokenIndex = null;
730 if (!isStatement) {
731 nameScopeStartTokenIndex = state.tokens.length;
732 state.scopeDepth++;
733 }
734 parseClassId(isStatement, optionalId);
735 parseClassSuper();
736 const openBraceIndex = state.tokens.length;
737 parseClassBody(contextId);
738 if (state.error) {
739 return;
740 }
741 state.tokens[openBraceIndex].contextId = contextId;
742 state.tokens[state.tokens.length - 1].contextId = contextId;
743 if (nameScopeStartTokenIndex !== null) {
744 const endTokenIndex = state.tokens.length;
745 state.scopes.push(new Scope(nameScopeStartTokenIndex, endTokenIndex, false));
746 state.scopeDepth--;
747 }
748}
749
750function isClassProperty() {
751 return match(tt.eq) || match(tt.semi) || match(tt.braceR) || match(tt.bang) || match(tt.colon);
752}
753
754function isClassMethod() {
755 return match(tt.parenL) || match(tt.lessThan);
756}
757
758function parseClassBody(classContextId) {
759 expect(tt.braceL);
760
761 while (!eat(tt.braceR) && !state.error) {
762 if (eat(tt.semi)) {
763 continue;
764 }
765
766 if (match(tt.at)) {
767 parseDecorator();
768 continue;
769 }
770 const memberStart = state.start;
771 parseClassMember(memberStart, classContextId);
772 }
773}
774
775function parseClassMember(memberStart, classContextId) {
776 if (isTypeScriptEnabled) {
777 tsParseModifiers([
778 ContextualKeyword._declare,
779 ContextualKeyword._public,
780 ContextualKeyword._protected,
781 ContextualKeyword._private,
782 ContextualKeyword._override,
783 ]);
784 }
785 let isStatic = false;
786 if (match(tt.name) && state.contextualKeyword === ContextualKeyword._static) {
787 parseIdentifier(); // eats 'static'
788 if (isClassMethod()) {
789 parseClassMethod(memberStart, /* isConstructor */ false);
790 return;
791 } else if (isClassProperty()) {
792 parseClassProperty();
793 return;
794 }
795 // otherwise something static
796 state.tokens[state.tokens.length - 1].type = tt._static;
797 isStatic = true;
798
799 if (match(tt.braceL)) {
800 // This is a static block. Mark the word "static" with the class context ID for class element
801 // detection and parse as a regular block.
802 state.tokens[state.tokens.length - 1].contextId = classContextId;
803 parseBlock();
804 return;
805 }
806 }
807
808 parseClassMemberWithIsStatic(memberStart, isStatic, classContextId);
809}
810
811function parseClassMemberWithIsStatic(
812 memberStart,
813 isStatic,
814 classContextId,
815) {
816 if (isTypeScriptEnabled) {
817 if (tsTryParseClassMemberWithIsStatic(isStatic)) {
818 return;
819 }
820 }
821 if (eat(tt.star)) {
822 // a generator
823 parseClassPropertyName(classContextId);
824 parseClassMethod(memberStart, /* isConstructor */ false);
825 return;
826 }
827
828 // Get the identifier name so we can tell if it's actually a keyword like "async", "get", or
829 // "set".
830 parseClassPropertyName(classContextId);
831 let isConstructor = false;
832 const token = state.tokens[state.tokens.length - 1];
833 // We allow "constructor" as either an identifier or a string.
834 if (token.contextualKeyword === ContextualKeyword._constructor) {
835 isConstructor = true;
836 }
837 parsePostMemberNameModifiers();
838
839 if (isClassMethod()) {
840 parseClassMethod(memberStart, isConstructor);
841 } else if (isClassProperty()) {
842 parseClassProperty();
843 } else if (token.contextualKeyword === ContextualKeyword._async && !isLineTerminator()) {
844 state.tokens[state.tokens.length - 1].type = tt._async;
845 // an async method
846 const isGenerator = match(tt.star);
847 if (isGenerator) {
848 next();
849 }
850
851 // The so-called parsed name would have been "async": get the real name.
852 parseClassPropertyName(classContextId);
853 parsePostMemberNameModifiers();
854 parseClassMethod(memberStart, false /* isConstructor */);
855 } else if (
856 (token.contextualKeyword === ContextualKeyword._get ||
857 token.contextualKeyword === ContextualKeyword._set) &&
858 !(isLineTerminator() && match(tt.star))
859 ) {
860 if (token.contextualKeyword === ContextualKeyword._get) {
861 state.tokens[state.tokens.length - 1].type = tt._get;
862 } else {
863 state.tokens[state.tokens.length - 1].type = tt._set;
864 }
865 // `get\n*` is an uninitialized property named 'get' followed by a generator.
866 // a getter or setter
867 // The so-called parsed name would have been "get/set": get the real name.
868 parseClassPropertyName(classContextId);
869 parseClassMethod(memberStart, /* isConstructor */ false);
870 } else if (token.contextualKeyword === ContextualKeyword._accessor && !isLineTerminator()) {
871 parseClassPropertyName(classContextId);
872 parseClassProperty();
873 } else if (isLineTerminator()) {
874 // an uninitialized class property (due to ASI, since we don't otherwise recognize the next token)
875 parseClassProperty();
876 } else {
877 unexpected();
878 }
879}
880
881function parseClassMethod(functionStart, isConstructor) {
882 if (isTypeScriptEnabled) {
883 tsTryParseTypeParameters();
884 } else if (isFlowEnabled) {
885 if (match(tt.lessThan)) {
886 flowParseTypeParameterDeclaration();
887 }
888 }
889 parseMethod(functionStart, isConstructor);
890}
891
892// Return the name of the class property, if it is a simple identifier.
893export function parseClassPropertyName(classContextId) {
894 parsePropertyName(classContextId);
895}
896
897export function parsePostMemberNameModifiers() {
898 if (isTypeScriptEnabled) {
899 const oldIsType = pushTypeContext(0);
900 eat(tt.question);
901 popTypeContext(oldIsType);
902 }
903}
904
905export function parseClassProperty() {
906 if (isTypeScriptEnabled) {
907 eatTypeToken(tt.bang);
908 tsTryParseTypeAnnotation();
909 } else if (isFlowEnabled) {
910 if (match(tt.colon)) {
911 flowParseTypeAnnotation();
912 }
913 }
914
915 if (match(tt.eq)) {
916 const equalsTokenIndex = state.tokens.length;
917 next();
918 parseMaybeAssign();
919 state.tokens[equalsTokenIndex].rhsEndIndex = state.tokens.length;
920 }
921 semicolon();
922}
923
924function parseClassId(isStatement, optionalId = false) {
925 if (
926 isTypeScriptEnabled &&
927 (!isStatement || optionalId) &&
928 isContextual(ContextualKeyword._implements)
929 ) {
930 return;
931 }
932
933 if (match(tt.name)) {
934 parseBindingIdentifier(true);
935 }
936
937 if (isTypeScriptEnabled) {
938 tsTryParseTypeParameters();
939 } else if (isFlowEnabled) {
940 if (match(tt.lessThan)) {
941 flowParseTypeParameterDeclaration();
942 }
943 }
944}
945
946// Returns true if there was a superclass.
947function parseClassSuper() {
948 let hasSuper = false;
949 if (eat(tt._extends)) {
950 parseExprSubscripts();
951 hasSuper = true;
952 } else {
953 hasSuper = false;
954 }
955 if (isTypeScriptEnabled) {
956 tsAfterParseClassSuper(hasSuper);
957 } else if (isFlowEnabled) {
958 flowAfterParseClassSuper(hasSuper);
959 }
960}
961
962// Parses module export declaration.
963
964export function parseExport() {
965 const exportIndex = state.tokens.length - 1;
966 if (isTypeScriptEnabled) {
967 if (tsTryParseExport()) {
968 return;
969 }
970 }
971 // export * from '...'
972 if (shouldParseExportStar()) {
973 parseExportStar();
974 } else if (isExportDefaultSpecifier()) {
975 // export default from
976 parseIdentifier();
977 if (match(tt.comma) && lookaheadType() === tt.star) {
978 expect(tt.comma);
979 expect(tt.star);
980 expectContextual(ContextualKeyword._as);
981 parseIdentifier();
982 } else {
983 parseExportSpecifiersMaybe();
984 }
985 parseExportFrom();
986 } else if (eat(tt._default)) {
987 // export default ...
988 parseExportDefaultExpression();
989 } else if (shouldParseExportDeclaration()) {
990 parseExportDeclaration();
991 } else {
992 // export { x, y as z } [from '...']
993 parseExportSpecifiers();
994 parseExportFrom();
995 }
996 state.tokens[exportIndex].rhsEndIndex = state.tokens.length;
997}
998
999function parseExportDefaultExpression() {
1000 if (isTypeScriptEnabled) {
1001 if (tsTryParseExportDefaultExpression()) {
1002 return;
1003 }
1004 }
1005 if (isFlowEnabled) {
1006 if (flowTryParseExportDefaultExpression()) {
1007 return;
1008 }
1009 }
1010 const functionStart = state.start;
1011 if (eat(tt._function)) {
1012 parseFunction(functionStart, true, true);
1013 } else if (isContextual(ContextualKeyword._async) && lookaheadType() === tt._function) {
1014 // async function declaration
1015 eatContextual(ContextualKeyword._async);
1016 eat(tt._function);
1017 parseFunction(functionStart, true, true);
1018 } else if (match(tt._class)) {
1019 parseClass(true, true);
1020 } else if (match(tt.at)) {
1021 parseDecorators();
1022 parseClass(true, true);
1023 } else {
1024 parseMaybeAssign();
1025 semicolon();
1026 }
1027}
1028
1029function parseExportDeclaration() {
1030 if (isTypeScriptEnabled) {
1031 tsParseExportDeclaration();
1032 } else if (isFlowEnabled) {
1033 flowParseExportDeclaration();
1034 } else {
1035 parseStatement(true);
1036 }
1037}
1038
1039function isExportDefaultSpecifier() {
1040 if (isTypeScriptEnabled && tsIsDeclarationStart()) {
1041 return false;
1042 } else if (isFlowEnabled && flowShouldDisallowExportDefaultSpecifier()) {
1043 return false;
1044 }
1045 if (match(tt.name)) {
1046 return state.contextualKeyword !== ContextualKeyword._async;
1047 }
1048
1049 if (!match(tt._default)) {
1050 return false;
1051 }
1052
1053 const _next = nextTokenStart();
1054 const lookahead = lookaheadTypeAndKeyword();
1055 const hasFrom =
1056 lookahead.type === tt.name && lookahead.contextualKeyword === ContextualKeyword._from;
1057 if (lookahead.type === tt.comma) {
1058 return true;
1059 }
1060 // lookahead again when `export default from` is seen
1061 if (hasFrom) {
1062 const nextAfterFrom = input.charCodeAt(nextTokenStartSince(_next + 4));
1063 return nextAfterFrom === charCodes.quotationMark || nextAfterFrom === charCodes.apostrophe;
1064 }
1065 return false;
1066}
1067
1068function parseExportSpecifiersMaybe() {
1069 if (eat(tt.comma)) {
1070 parseExportSpecifiers();
1071 }
1072}
1073
1074export function parseExportFrom() {
1075 if (eatContextual(ContextualKeyword._from)) {
1076 parseExprAtom();
1077 maybeParseImportAttributes();
1078 }
1079 semicolon();
1080}
1081
1082function shouldParseExportStar() {
1083 if (isFlowEnabled) {
1084 return flowShouldParseExportStar();
1085 } else {
1086 return match(tt.star);
1087 }
1088}
1089
1090function parseExportStar() {
1091 if (isFlowEnabled) {
1092 flowParseExportStar();
1093 } else {
1094 baseParseExportStar();
1095 }
1096}
1097
1098export function baseParseExportStar() {
1099 expect(tt.star);
1100
1101 if (isContextual(ContextualKeyword._as)) {
1102 parseExportNamespace();
1103 } else {
1104 parseExportFrom();
1105 }
1106}
1107
1108function parseExportNamespace() {
1109 next();
1110 state.tokens[state.tokens.length - 1].type = tt._as;
1111 parseIdentifier();
1112 parseExportSpecifiersMaybe();
1113 parseExportFrom();
1114}
1115
1116function shouldParseExportDeclaration() {
1117 return (
1118 (isTypeScriptEnabled && tsIsDeclarationStart()) ||
1119 (isFlowEnabled && flowShouldParseExportDeclaration()) ||
1120 state.type === tt._var ||
1121 state.type === tt._const ||
1122 state.type === tt._let ||
1123 state.type === tt._function ||
1124 state.type === tt._class ||
1125 isContextual(ContextualKeyword._async) ||
1126 match(tt.at)
1127 );
1128}
1129
1130// Parses a comma-separated list of module exports.
1131export function parseExportSpecifiers() {
1132 let first = true;
1133
1134 // export { x, y as z } [from '...']
1135 expect(tt.braceL);
1136
1137 while (!eat(tt.braceR) && !state.error) {
1138 if (first) {
1139 first = false;
1140 } else {
1141 expect(tt.comma);
1142 if (eat(tt.braceR)) {
1143 break;
1144 }
1145 }
1146 parseExportSpecifier();
1147 }
1148}
1149
1150function parseExportSpecifier() {
1151 if (isTypeScriptEnabled) {
1152 tsParseExportSpecifier();
1153 return;
1154 }
1155 parseIdentifier();
1156 state.tokens[state.tokens.length - 1].identifierRole = IdentifierRole.ExportAccess;
1157 if (eatContextual(ContextualKeyword._as)) {
1158 parseIdentifier();
1159 }
1160}
1161
1162/**
1163 * Starting at the `module` token in an import, determine if it was truly an
1164 * import reflection token or just looks like one.
1165 *
1166 * Returns true for:
1167 * import module foo from "foo";
1168 * import module from from "foo";
1169 *
1170 * Returns false for:
1171 * import module from "foo";
1172 * import module, {bar} from "foo";
1173 */
1174function isImportReflection() {
1175 const snapshot = state.snapshot();
1176 expectContextual(ContextualKeyword._module);
1177 if (eatContextual(ContextualKeyword._from)) {
1178 if (isContextual(ContextualKeyword._from)) {
1179 state.restoreFromSnapshot(snapshot);
1180 return true;
1181 } else {
1182 state.restoreFromSnapshot(snapshot);
1183 return false;
1184 }
1185 } else if (match(tt.comma)) {
1186 state.restoreFromSnapshot(snapshot);
1187 return false;
1188 } else {
1189 state.restoreFromSnapshot(snapshot);
1190 return true;
1191 }
1192}
1193
1194/**
1195 * Eat the "module" token from the import reflection proposal.
1196 * https://github.com/tc39/proposal-import-reflection
1197 */
1198function parseMaybeImportReflection() {
1199 // isImportReflection does snapshot/restore, so only run it if we see the word
1200 // "module".
1201 if (isContextual(ContextualKeyword._module) && isImportReflection()) {
1202 next();
1203 }
1204}
1205
1206// Parses import declaration.
1207
1208export function parseImport() {
1209 if (isTypeScriptEnabled && match(tt.name) && lookaheadType() === tt.eq) {
1210 tsParseImportEqualsDeclaration();
1211 return;
1212 }
1213 if (isTypeScriptEnabled && isContextual(ContextualKeyword._type)) {
1214 const lookahead = lookaheadTypeAndKeyword();
1215 if (lookahead.type === tt.name && lookahead.contextualKeyword !== ContextualKeyword._from) {
1216 // One of these `import type` cases:
1217 // import type T = require('T');
1218 // import type A from 'A';
1219 expectContextual(ContextualKeyword._type);
1220 if (lookaheadType() === tt.eq) {
1221 tsParseImportEqualsDeclaration();
1222 return;
1223 }
1224 // If this is an `import type...from` statement, then we already ate the
1225 // type token, so proceed to the regular import parser.
1226 } else if (lookahead.type === tt.star || lookahead.type === tt.braceL) {
1227 // One of these `import type` cases, in which case we can eat the type token
1228 // and proceed as normal:
1229 // import type * as A from 'A';
1230 // import type {a} from 'A';
1231 expectContextual(ContextualKeyword._type);
1232 }
1233 // Otherwise, we are importing the name "type".
1234 }
1235
1236 // import '...'
1237 if (match(tt.string)) {
1238 parseExprAtom();
1239 } else {
1240 parseMaybeImportReflection();
1241 parseImportSpecifiers();
1242 expectContextual(ContextualKeyword._from);
1243 parseExprAtom();
1244 }
1245 maybeParseImportAttributes();
1246 semicolon();
1247}
1248
1249// eslint-disable-next-line no-unused-vars
1250function shouldParseDefaultImport() {
1251 return match(tt.name);
1252}
1253
1254function parseImportSpecifierLocal() {
1255 parseImportedIdentifier();
1256}
1257
1258// Parses a comma-separated list of module imports.
1259function parseImportSpecifiers() {
1260 if (isFlowEnabled) {
1261 flowStartParseImportSpecifiers();
1262 }
1263
1264 let first = true;
1265 if (shouldParseDefaultImport()) {
1266 // import defaultObj, { x, y as z } from '...'
1267 parseImportSpecifierLocal();
1268
1269 if (!eat(tt.comma)) return;
1270 }
1271
1272 if (match(tt.star)) {
1273 next();
1274 expectContextual(ContextualKeyword._as);
1275
1276 parseImportSpecifierLocal();
1277
1278 return;
1279 }
1280
1281 expect(tt.braceL);
1282 while (!eat(tt.braceR) && !state.error) {
1283 if (first) {
1284 first = false;
1285 } else {
1286 // Detect an attempt to deep destructure
1287 if (eat(tt.colon)) {
1288 unexpected(
1289 "ES2015 named imports do not destructure. Use another statement for destructuring after the import.",
1290 );
1291 }
1292
1293 expect(tt.comma);
1294 if (eat(tt.braceR)) {
1295 break;
1296 }
1297 }
1298
1299 parseImportSpecifier();
1300 }
1301}
1302
1303function parseImportSpecifier() {
1304 if (isTypeScriptEnabled) {
1305 tsParseImportSpecifier();
1306 return;
1307 }
1308 if (isFlowEnabled) {
1309 flowParseImportSpecifier();
1310 return;
1311 }
1312 parseImportedIdentifier();
1313 if (isContextual(ContextualKeyword._as)) {
1314 state.tokens[state.tokens.length - 1].identifierRole = IdentifierRole.ImportAccess;
1315 next();
1316 parseImportedIdentifier();
1317 }
1318}
1319
1320/**
1321 * Parse import attributes like `with {type: "json"}`, or the legacy form
1322 * `assert {type: "json"}`.
1323 *
1324 * Import attributes technically have their own syntax, but are always parseable
1325 * as a plain JS object, so just do that for simplicity.
1326 */
1327function maybeParseImportAttributes() {
1328 if (match(tt._with) || (isContextual(ContextualKeyword._assert) && !hasPrecedingLineBreak())) {
1329 next();
1330 parseObj(false, false);
1331 }
1332}
Note: See TracBrowser for help on using the repository browser.