UNPKG

35.4 kBJavaScriptView Raw
1import {
2 eat,
3 lookaheadType,
4 lookaheadTypeAndKeyword,
5 match,
6 next,
7 popTypeContext,
8 pushTypeContext,
9} from "../tokenizer/index";
10import {ContextualKeyword} from "../tokenizer/keywords";
11import { TokenType as tt} from "../tokenizer/types";
12import {isJSXEnabled, state} from "../traverser/base";
13import {
14 atPossibleAsync,
15 baseParseMaybeAssign,
16 baseParseSubscript,
17 parseCallExpressionArguments,
18 parseExprAtom,
19 parseExpression,
20 parseFunctionBody,
21 parseIdentifier,
22 parseLiteral,
23 parseMaybeAssign,
24 parseMaybeUnary,
25 parsePropertyName,
26 parseTemplate,
27
28} from "../traverser/expression";
29import {parseBindingList} from "../traverser/lval";
30import {
31 baseParseMaybeDecoratorArguments,
32 parseBlockBody,
33 parseClass,
34 parseClassProperty,
35 parseClassPropertyName,
36 parseFunction,
37 parseFunctionParams,
38 parsePostMemberNameModifiers,
39 parseStatement,
40 parseVarStatement,
41} from "../traverser/statement";
42import {
43 canInsertSemicolon,
44 eatContextual,
45 expect,
46 expectContextual,
47 hasPrecedingLineBreak,
48 isContextual,
49 isLineTerminator,
50 isLookaheadContextual,
51 semicolon,
52 unexpected,
53} from "../traverser/util";
54import {nextJSXTagToken} from "./jsx";
55
56function tsIsIdentifier() {
57 // TODO: actually a bit more complex in TypeScript, but shouldn't matter.
58 // See https://github.com/Microsoft/TypeScript/issues/15008
59 return match(tt.name);
60}
61
62function tsNextTokenCanFollowModifier() {
63 // Note: TypeScript's implementation is much more complicated because
64 // more things are considered modifiers there.
65 // This implementation only handles modifiers not handled by babylon itself. And "static".
66 // TODO: Would be nice to avoid lookahead. Want a hasLineBreakUpNext() method...
67 const snapshot = state.snapshot();
68
69 next();
70 const canFollowModifier =
71 !hasPrecedingLineBreak() &&
72 !match(tt.parenL) &&
73 !match(tt.parenR) &&
74 !match(tt.colon) &&
75 !match(tt.eq) &&
76 !match(tt.question);
77
78 if (canFollowModifier) {
79 return true;
80 } else {
81 state.restoreFromSnapshot(snapshot);
82 return false;
83 }
84}
85
86/** Parses a modifier matching one the given modifier names. */
87export function tsParseModifier(
88 allowedModifiers,
89) {
90 if (!match(tt.name)) {
91 return null;
92 }
93
94 const modifier = state.contextualKeyword;
95 if (allowedModifiers.indexOf(modifier) !== -1 && tsNextTokenCanFollowModifier()) {
96 switch (modifier) {
97 case ContextualKeyword._readonly:
98 state.tokens[state.tokens.length - 1].type = tt._readonly;
99 break;
100 case ContextualKeyword._abstract:
101 state.tokens[state.tokens.length - 1].type = tt._abstract;
102 break;
103 case ContextualKeyword._static:
104 state.tokens[state.tokens.length - 1].type = tt._static;
105 break;
106 case ContextualKeyword._public:
107 state.tokens[state.tokens.length - 1].type = tt._public;
108 break;
109 case ContextualKeyword._private:
110 state.tokens[state.tokens.length - 1].type = tt._private;
111 break;
112 case ContextualKeyword._protected:
113 state.tokens[state.tokens.length - 1].type = tt._protected;
114 break;
115 default:
116 break;
117 }
118 return modifier;
119 }
120 return null;
121}
122
123function tsParseEntityName() {
124 parseIdentifier();
125 while (eat(tt.dot)) {
126 parseIdentifier();
127 }
128}
129
130function tsParseTypeReference() {
131 tsParseEntityName();
132 if (!hasPrecedingLineBreak() && match(tt.lessThan)) {
133 tsParseTypeArguments();
134 }
135}
136
137function tsParseThisTypePredicate() {
138 next();
139 tsParseTypeAnnotation();
140}
141
142function tsParseThisTypeNode() {
143 next();
144}
145
146function tsParseTypeQuery() {
147 expect(tt._typeof);
148 if (match(tt._import)) {
149 tsParseImportType();
150 } else {
151 tsParseEntityName();
152 }
153}
154
155function tsParseImportType() {
156 expect(tt._import);
157 expect(tt.parenL);
158 expect(tt.string);
159 expect(tt.parenR);
160 if (eat(tt.dot)) {
161 tsParseEntityName();
162 }
163 if (match(tt.lessThan)) {
164 tsParseTypeArguments();
165 }
166}
167
168function tsParseTypeParameter() {
169 parseIdentifier();
170 if (eat(tt._extends)) {
171 tsParseType();
172 }
173 if (eat(tt.eq)) {
174 tsParseType();
175 }
176}
177
178export function tsTryParseTypeParameters() {
179 if (match(tt.lessThan)) {
180 tsParseTypeParameters();
181 }
182}
183
184function tsParseTypeParameters() {
185 const oldIsType = pushTypeContext(0);
186 if (match(tt.lessThan) || match(tt.typeParameterStart)) {
187 next();
188 } else {
189 unexpected();
190 }
191
192 while (!eat(tt.greaterThan) && !state.error) {
193 tsParseTypeParameter();
194 eat(tt.comma);
195 }
196 popTypeContext(oldIsType);
197}
198
199// Note: In TypeScript implementation we must provide `yieldContext` and `awaitContext`,
200// but here it's always false, because this is only used for types.
201function tsFillSignature(returnToken) {
202 // Arrow fns *must* have return token (`=>`). Normal functions can omit it.
203 const returnTokenRequired = returnToken === tt.arrow;
204 tsTryParseTypeParameters();
205 expect(tt.parenL);
206 tsParseBindingListForSignature(false /* isBlockScope */);
207 if (returnTokenRequired) {
208 tsParseTypeOrTypePredicateAnnotation(returnToken);
209 } else if (match(returnToken)) {
210 tsParseTypeOrTypePredicateAnnotation(returnToken);
211 }
212}
213
214function tsParseBindingListForSignature(isBlockScope) {
215 parseBindingList(tt.parenR, isBlockScope);
216}
217
218function tsParseTypeMemberSemicolon() {
219 if (!eat(tt.comma)) {
220 semicolon();
221 }
222}
223
224var SignatureMemberKind; (function (SignatureMemberKind) {
225 const TSCallSignatureDeclaration = 0; SignatureMemberKind[SignatureMemberKind["TSCallSignatureDeclaration"] = TSCallSignatureDeclaration] = "TSCallSignatureDeclaration";
226 const TSConstructSignatureDeclaration = TSCallSignatureDeclaration + 1; SignatureMemberKind[SignatureMemberKind["TSConstructSignatureDeclaration"] = TSConstructSignatureDeclaration] = "TSConstructSignatureDeclaration";
227})(SignatureMemberKind || (SignatureMemberKind = {}));
228
229function tsParseSignatureMember(kind) {
230 if (kind === SignatureMemberKind.TSConstructSignatureDeclaration) {
231 expect(tt._new);
232 }
233 tsFillSignature(tt.colon);
234 tsParseTypeMemberSemicolon();
235}
236
237function tsIsUnambiguouslyIndexSignature() {
238 const snapshot = state.snapshot();
239 next(); // Skip '{'
240 const isIndexSignature = eat(tt.name) && match(tt.colon);
241 state.restoreFromSnapshot(snapshot);
242 return isIndexSignature;
243}
244
245function tsTryParseIndexSignature() {
246 if (!(match(tt.bracketL) && tsIsUnambiguouslyIndexSignature())) {
247 return false;
248 }
249
250 const oldIsType = pushTypeContext(0);
251
252 expect(tt.bracketL);
253 parseIdentifier();
254 tsParseTypeAnnotation();
255 expect(tt.bracketR);
256
257 tsTryParseTypeAnnotation();
258 tsParseTypeMemberSemicolon();
259
260 popTypeContext(oldIsType);
261 return true;
262}
263
264function tsParsePropertyOrMethodSignature(isReadonly) {
265 parsePropertyName(-1 /* Types don't need context IDs. */);
266 eat(tt.question);
267
268 if (!isReadonly && (match(tt.parenL) || match(tt.lessThan))) {
269 tsFillSignature(tt.colon);
270 tsParseTypeMemberSemicolon();
271 } else {
272 tsTryParseTypeAnnotation();
273 tsParseTypeMemberSemicolon();
274 }
275}
276
277function tsParseTypeMember() {
278 if (match(tt.parenL) || match(tt.lessThan)) {
279 tsParseSignatureMember(SignatureMemberKind.TSCallSignatureDeclaration);
280 return;
281 }
282 if (match(tt._new) && tsIsStartOfConstructSignature()) {
283 tsParseSignatureMember(SignatureMemberKind.TSConstructSignatureDeclaration);
284 return;
285 }
286 const readonly = !!tsParseModifier([ContextualKeyword._readonly]);
287
288 const found = tsTryParseIndexSignature();
289 if (found) {
290 return;
291 }
292 tsParsePropertyOrMethodSignature(readonly);
293}
294
295function tsIsStartOfConstructSignature() {
296 const lookahead = lookaheadType();
297 return lookahead === tt.parenL || lookahead === tt.lessThan;
298}
299
300function tsParseTypeLiteral() {
301 tsParseObjectTypeMembers();
302}
303
304function tsParseObjectTypeMembers() {
305 expect(tt.braceL);
306 while (!eat(tt.braceR) && !state.error) {
307 tsParseTypeMember();
308 }
309}
310
311function tsLookaheadIsStartOfMappedType() {
312 const snapshot = state.snapshot();
313 const isStartOfMappedType = tsIsStartOfMappedType();
314 state.restoreFromSnapshot(snapshot);
315 return isStartOfMappedType;
316}
317
318function tsIsStartOfMappedType() {
319 next();
320 if (eat(tt.plus) || eat(tt.minus)) {
321 return isContextual(ContextualKeyword._readonly);
322 }
323 if (isContextual(ContextualKeyword._readonly)) {
324 next();
325 }
326 if (!match(tt.bracketL)) {
327 return false;
328 }
329 next();
330 if (!tsIsIdentifier()) {
331 return false;
332 }
333 next();
334 return match(tt._in);
335}
336
337function tsParseMappedTypeParameter() {
338 parseIdentifier();
339 expect(tt._in);
340 tsParseType();
341}
342
343function tsParseMappedType() {
344 expect(tt.braceL);
345 if (match(tt.plus) || match(tt.minus)) {
346 next();
347 expectContextual(ContextualKeyword._readonly);
348 } else {
349 eatContextual(ContextualKeyword._readonly);
350 }
351 expect(tt.bracketL);
352 tsParseMappedTypeParameter();
353 expect(tt.bracketR);
354 if (match(tt.plus) || match(tt.minus)) {
355 next();
356 expect(tt.question);
357 } else {
358 eat(tt.question);
359 }
360 tsTryParseType();
361 semicolon();
362 expect(tt.braceR);
363}
364
365function tsParseTupleType() {
366 expect(tt.bracketL);
367 while (!eat(tt.bracketR) && !state.error) {
368 tsParseTupleElementType();
369 eat(tt.comma);
370 }
371}
372
373function tsParseTupleElementType() {
374 // parses `...TsType[]`
375 if (eat(tt.ellipsis)) {
376 tsParseType();
377 return;
378 }
379 // parses `TsType?`
380 tsParseType();
381 eat(tt.question);
382}
383
384function tsParseParenthesizedType() {
385 expect(tt.parenL);
386 tsParseType();
387 expect(tt.parenR);
388}
389
390var FunctionType; (function (FunctionType) {
391 const TSFunctionType = 0; FunctionType[FunctionType["TSFunctionType"] = TSFunctionType] = "TSFunctionType";
392 const TSConstructorType = TSFunctionType + 1; FunctionType[FunctionType["TSConstructorType"] = TSConstructorType] = "TSConstructorType";
393})(FunctionType || (FunctionType = {}));
394
395function tsParseFunctionOrConstructorType(type) {
396 if (type === FunctionType.TSConstructorType) {
397 expect(tt._new);
398 }
399 tsFillSignature(tt.arrow);
400}
401
402function tsParseNonArrayType() {
403 switch (state.type) {
404 case tt.name:
405 tsParseTypeReference();
406 return;
407 case tt._void:
408 case tt._null:
409 next();
410 return;
411 case tt.string:
412 case tt.num:
413 case tt._true:
414 case tt._false:
415 parseLiteral();
416 return;
417 case tt.minus:
418 next();
419 parseLiteral();
420 return;
421 case tt._this: {
422 tsParseThisTypeNode();
423 if (isContextual(ContextualKeyword._is) && !hasPrecedingLineBreak()) {
424 tsParseThisTypePredicate();
425 }
426 return;
427 }
428 case tt._typeof:
429 tsParseTypeQuery();
430 return;
431 case tt._import:
432 tsParseImportType();
433 return;
434 case tt.braceL:
435 if (tsLookaheadIsStartOfMappedType()) {
436 tsParseMappedType();
437 } else {
438 tsParseTypeLiteral();
439 }
440 return;
441 case tt.bracketL:
442 tsParseTupleType();
443 return;
444 case tt.parenL:
445 tsParseParenthesizedType();
446 return;
447 default:
448 break;
449 }
450
451 unexpected();
452}
453
454function tsParseArrayTypeOrHigher() {
455 tsParseNonArrayType();
456 while (!hasPrecedingLineBreak() && eat(tt.bracketL)) {
457 if (!eat(tt.bracketR)) {
458 // If we hit ] immediately, this is an array type, otherwise it's an indexed access type.
459 tsParseType();
460 expect(tt.bracketR);
461 }
462 }
463}
464
465function tsParseInferType() {
466 expectContextual(ContextualKeyword._infer);
467 parseIdentifier();
468}
469
470function tsParseTypeOperatorOrHigher() {
471 if (isContextual(ContextualKeyword._keyof) || isContextual(ContextualKeyword._unique)) {
472 next();
473 tsParseTypeOperatorOrHigher();
474 } else if (isContextual(ContextualKeyword._infer)) {
475 tsParseInferType();
476 } else {
477 tsParseArrayTypeOrHigher();
478 }
479}
480
481function tsParseIntersectionTypeOrHigher() {
482 eat(tt.bitwiseAND);
483 tsParseTypeOperatorOrHigher();
484 if (match(tt.bitwiseAND)) {
485 while (eat(tt.bitwiseAND)) {
486 tsParseTypeOperatorOrHigher();
487 }
488 }
489}
490
491function tsParseUnionTypeOrHigher() {
492 eat(tt.bitwiseOR);
493 tsParseIntersectionTypeOrHigher();
494 if (match(tt.bitwiseOR)) {
495 while (eat(tt.bitwiseOR)) {
496 tsParseIntersectionTypeOrHigher();
497 }
498 }
499}
500
501function tsIsStartOfFunctionType() {
502 if (match(tt.lessThan)) {
503 return true;
504 }
505 return match(tt.parenL) && tsLookaheadIsUnambiguouslyStartOfFunctionType();
506}
507
508function tsSkipParameterStart() {
509 if (match(tt.name) || match(tt._this)) {
510 next();
511 return true;
512 }
513 // If this is a possible array/object destructure, walk to the matching bracket/brace.
514 // The next token after will tell us definitively whether this is a function param.
515 if (match(tt.braceL) || match(tt.bracketL)) {
516 let depth = 1;
517 next();
518 while (depth > 0 && !state.error) {
519 if (match(tt.braceL) || match(tt.bracketL)) {
520 depth++;
521 } else if (match(tt.braceR) || match(tt.bracketR)) {
522 depth--;
523 }
524 next();
525 }
526 return true;
527 }
528 return false;
529}
530
531function tsLookaheadIsUnambiguouslyStartOfFunctionType() {
532 const snapshot = state.snapshot();
533 const isUnambiguouslyStartOfFunctionType = tsIsUnambiguouslyStartOfFunctionType();
534 state.restoreFromSnapshot(snapshot);
535 return isUnambiguouslyStartOfFunctionType;
536}
537
538function tsIsUnambiguouslyStartOfFunctionType() {
539 next();
540 if (match(tt.parenR) || match(tt.ellipsis)) {
541 // ( )
542 // ( ...
543 return true;
544 }
545 if (tsSkipParameterStart()) {
546 if (match(tt.colon) || match(tt.comma) || match(tt.question) || match(tt.eq)) {
547 // ( xxx :
548 // ( xxx ,
549 // ( xxx ?
550 // ( xxx =
551 return true;
552 }
553 if (match(tt.parenR)) {
554 next();
555 if (match(tt.arrow)) {
556 // ( xxx ) =>
557 return true;
558 }
559 }
560 }
561 return false;
562}
563
564function tsParseTypeOrTypePredicateAnnotation(returnToken) {
565 const oldIsType = pushTypeContext(0);
566 expect(returnToken);
567 tsParseTypePredicatePrefix();
568 // Regardless of whether we found an "is" token, there's now just a regular type in front of
569 // us.
570 tsParseType();
571 popTypeContext(oldIsType);
572}
573
574function tsTryParseTypeOrTypePredicateAnnotation() {
575 if (match(tt.colon)) {
576 tsParseTypeOrTypePredicateAnnotation(tt.colon);
577 }
578}
579
580export function tsTryParseTypeAnnotation() {
581 if (match(tt.colon)) {
582 tsParseTypeAnnotation();
583 }
584}
585
586function tsTryParseType() {
587 if (eat(tt.colon)) {
588 tsParseType();
589 }
590}
591
592function tsParseTypePredicatePrefix() {
593 const snapshot = state.snapshot();
594 parseIdentifier();
595 if (isContextual(ContextualKeyword._is) && !hasPrecedingLineBreak()) {
596 next();
597 } else {
598 state.restoreFromSnapshot(snapshot);
599 }
600}
601
602export function tsParseTypeAnnotation() {
603 const oldIsType = pushTypeContext(0);
604 expect(tt.colon);
605 tsParseType();
606 popTypeContext(oldIsType);
607}
608
609export function tsParseType() {
610 tsParseNonConditionalType();
611 if (hasPrecedingLineBreak() || !eat(tt._extends)) {
612 return;
613 }
614 // extends type
615 tsParseNonConditionalType();
616 expect(tt.question);
617 // true type
618 tsParseType();
619 expect(tt.colon);
620 // false type
621 tsParseType();
622}
623
624export function tsParseNonConditionalType() {
625 if (tsIsStartOfFunctionType()) {
626 tsParseFunctionOrConstructorType(FunctionType.TSFunctionType);
627 return;
628 }
629 if (match(tt._new)) {
630 // As in `new () => Date`
631 tsParseFunctionOrConstructorType(FunctionType.TSConstructorType);
632 return;
633 }
634 tsParseUnionTypeOrHigher();
635}
636
637export function tsParseTypeAssertion() {
638 const oldIsType = pushTypeContext(1);
639 tsParseType();
640 expect(tt.greaterThan);
641 popTypeContext(oldIsType);
642 parseMaybeUnary();
643}
644
645export function tsTryParseJSXTypeArgument() {
646 if (eat(tt.jsxTagStart)) {
647 state.tokens[state.tokens.length - 1].type = tt.typeParameterStart;
648 const oldIsType = pushTypeContext(1);
649 while (!match(tt.greaterThan) && !state.error) {
650 tsParseType();
651 eat(tt.comma);
652 }
653 // Process >, but the one after needs to be parsed JSX-style.
654 nextJSXTagToken();
655 popTypeContext(oldIsType);
656 }
657}
658
659function tsParseHeritageClause() {
660 while (!match(tt.braceL) && !state.error) {
661 tsParseExpressionWithTypeArguments();
662 eat(tt.comma);
663 }
664}
665
666function tsParseExpressionWithTypeArguments() {
667 // Note: TS uses parseLeftHandSideExpressionOrHigher,
668 // then has grammar errors later if it's not an EntityName.
669 tsParseEntityName();
670 if (match(tt.lessThan)) {
671 tsParseTypeArguments();
672 }
673}
674
675function tsParseInterfaceDeclaration() {
676 parseIdentifier();
677 tsTryParseTypeParameters();
678 if (eat(tt._extends)) {
679 tsParseHeritageClause();
680 }
681 tsParseObjectTypeMembers();
682}
683
684function tsParseTypeAliasDeclaration() {
685 parseIdentifier();
686 tsTryParseTypeParameters();
687 expect(tt.eq);
688 tsParseType();
689 semicolon();
690}
691
692function tsParseEnumMember() {
693 // Computed property names are grammar errors in an enum, so accept just string literal or identifier.
694 if (match(tt.string)) {
695 parseLiteral();
696 } else {
697 parseIdentifier();
698 }
699 if (eat(tt.eq)) {
700 const eqIndex = state.tokens.length - 1;
701 parseMaybeAssign();
702 state.tokens[eqIndex].rhsEndIndex = state.tokens.length;
703 }
704}
705
706function tsParseEnumDeclaration() {
707 parseIdentifier();
708 expect(tt.braceL);
709 while (!eat(tt.braceR) && !state.error) {
710 tsParseEnumMember();
711 eat(tt.comma);
712 }
713}
714
715function tsParseModuleBlock() {
716 expect(tt.braceL);
717 parseBlockBody(/* end */ tt.braceR);
718}
719
720function tsParseModuleOrNamespaceDeclaration() {
721 parseIdentifier();
722 if (eat(tt.dot)) {
723 tsParseModuleOrNamespaceDeclaration();
724 } else {
725 tsParseModuleBlock();
726 }
727}
728
729function tsParseAmbientExternalModuleDeclaration() {
730 if (isContextual(ContextualKeyword._global)) {
731 parseIdentifier();
732 } else if (match(tt.string)) {
733 parseExprAtom();
734 } else {
735 unexpected();
736 }
737
738 if (match(tt.braceL)) {
739 tsParseModuleBlock();
740 } else {
741 semicolon();
742 }
743}
744
745export function tsParseImportEqualsDeclaration() {
746 parseIdentifier();
747 expect(tt.eq);
748 tsParseModuleReference();
749 semicolon();
750}
751
752function tsIsExternalModuleReference() {
753 return isContextual(ContextualKeyword._require) && lookaheadType() === tt.parenL;
754}
755
756function tsParseModuleReference() {
757 if (tsIsExternalModuleReference()) {
758 tsParseExternalModuleReference();
759 } else {
760 tsParseEntityName();
761 }
762}
763
764function tsParseExternalModuleReference() {
765 expectContextual(ContextualKeyword._require);
766 expect(tt.parenL);
767 if (!match(tt.string)) {
768 unexpected();
769 }
770 parseLiteral();
771 expect(tt.parenR);
772}
773
774// Utilities
775
776// Returns true if a statement matched.
777function tsTryParseDeclare() {
778 switch (state.type) {
779 case tt._function: {
780 const oldIsType = pushTypeContext(1);
781 next();
782 // We don't need to precisely get the function start here, since it's only used to mark
783 // the function as a type if it's bodiless, and it's already a type here.
784 const functionStart = state.start;
785 parseFunction(functionStart, /* isStatement */ true);
786 popTypeContext(oldIsType);
787 return true;
788 }
789 case tt._class: {
790 const oldIsType = pushTypeContext(1);
791 parseClass(/* isStatement */ true, /* optionalId */ false);
792 popTypeContext(oldIsType);
793 return true;
794 }
795 case tt._const: {
796 if (match(tt._const) && isLookaheadContextual(ContextualKeyword._enum)) {
797 const oldIsType = pushTypeContext(1);
798 // `const enum = 0;` not allowed because "enum" is a strict mode reserved word.
799 expect(tt._const);
800 expectContextual(ContextualKeyword._enum);
801 state.tokens[state.tokens.length - 1].type = tt._enum;
802 tsParseEnumDeclaration();
803 popTypeContext(oldIsType);
804 return true;
805 }
806 }
807 // falls through
808 case tt._var:
809 case tt._let: {
810 const oldIsType = pushTypeContext(1);
811 parseVarStatement(state.type);
812 popTypeContext(oldIsType);
813 return true;
814 }
815 case tt.name: {
816 const oldIsType = pushTypeContext(1);
817 const contextualKeyword = state.contextualKeyword;
818 let matched = false;
819 if (contextualKeyword === ContextualKeyword._global) {
820 tsParseAmbientExternalModuleDeclaration();
821 matched = true;
822 } else {
823 matched = tsParseDeclaration(contextualKeyword, /* isBeforeToken */ true);
824 }
825 popTypeContext(oldIsType);
826 return matched;
827 }
828 default:
829 return false;
830 }
831}
832
833// Note: this won't be called unless the keyword is allowed in `shouldParseExportDeclaration`.
834// Returns true if it matched a declaration.
835function tsTryParseExportDeclaration() {
836 return tsParseDeclaration(state.contextualKeyword, /* isBeforeToken */ true);
837}
838
839// Returns true if it matched a statement.
840function tsParseExpressionStatement(contextualKeyword) {
841 switch (contextualKeyword) {
842 case ContextualKeyword._declare: {
843 const declareTokenIndex = state.tokens.length - 1;
844 const matched = tsTryParseDeclare();
845 if (matched) {
846 state.tokens[declareTokenIndex].type = tt._declare;
847 return true;
848 }
849 break;
850 }
851 case ContextualKeyword._global:
852 // `global { }` (with no `declare`) may appear inside an ambient module declaration.
853 // Would like to use tsParseAmbientExternalModuleDeclaration here, but already ran past "global".
854 if (match(tt.braceL)) {
855 tsParseModuleBlock();
856 return true;
857 }
858 break;
859
860 default:
861 return tsParseDeclaration(contextualKeyword, /* isBeforeToken */ false);
862 }
863 return false;
864}
865
866// Common to tsTryParseDeclare, tsTryParseExportDeclaration, and tsParseExpressionStatement.
867// Returns true if it matched a declaration.
868function tsParseDeclaration(contextualKeyword, isBeforeToken) {
869 switch (contextualKeyword) {
870 case ContextualKeyword._abstract:
871 if (isBeforeToken || match(tt._class)) {
872 if (isBeforeToken) next();
873 state.tokens[state.tokens.length - 1].type = tt._abstract;
874 parseClass(/* isStatement */ true, /* optionalId */ false);
875 return true;
876 }
877 break;
878
879 case ContextualKeyword._enum:
880 if (isBeforeToken || match(tt.name)) {
881 if (isBeforeToken) next();
882 state.tokens[state.tokens.length - 1].type = tt._enum;
883 tsParseEnumDeclaration();
884 return true;
885 }
886 break;
887
888 case ContextualKeyword._interface:
889 if (isBeforeToken || match(tt.name)) {
890 // `next` is true in "export" and "declare" contexts, so we want to remove that token
891 // as well.
892 const oldIsType = pushTypeContext(1);
893 if (isBeforeToken) next();
894 tsParseInterfaceDeclaration();
895 popTypeContext(oldIsType);
896 return true;
897 }
898 break;
899
900 case ContextualKeyword._module:
901 if (isBeforeToken) next();
902 if (match(tt.string)) {
903 const oldIsType = pushTypeContext(isBeforeToken ? 2 : 1);
904 tsParseAmbientExternalModuleDeclaration();
905 popTypeContext(oldIsType);
906 return true;
907 } else if (match(tt.name)) {
908 const oldIsType = pushTypeContext(isBeforeToken ? 2 : 1);
909 tsParseModuleOrNamespaceDeclaration();
910 popTypeContext(oldIsType);
911 return true;
912 }
913 break;
914
915 case ContextualKeyword._namespace:
916 if (isBeforeToken || match(tt.name)) {
917 const oldIsType = pushTypeContext(1);
918 if (isBeforeToken) next();
919 tsParseModuleOrNamespaceDeclaration();
920 popTypeContext(oldIsType);
921 return true;
922 }
923 break;
924
925 case ContextualKeyword._type:
926 if (isBeforeToken || match(tt.name)) {
927 const oldIsType = pushTypeContext(1);
928 if (isBeforeToken) next();
929 tsParseTypeAliasDeclaration();
930 popTypeContext(oldIsType);
931 return true;
932 }
933 break;
934
935 default:
936 break;
937 }
938 return false;
939}
940
941// Returns true if there was a generic async arrow function.
942function tsTryParseGenericAsyncArrowFunction() {
943 const snapshot = state.snapshot();
944
945 tsParseTypeParameters();
946 parseFunctionParams();
947 tsTryParseTypeOrTypePredicateAnnotation();
948 expect(tt.arrow);
949
950 if (state.error) {
951 state.restoreFromSnapshot(snapshot);
952 return false;
953 }
954
955 // We don't need to be precise about the function start since it's only used if this is a
956 // bodiless function, which isn't valid here.
957 const functionStart = state.start;
958 parseFunctionBody(functionStart, false /* isGenerator */, true);
959 return true;
960}
961
962function tsParseTypeArguments() {
963 const oldIsType = pushTypeContext(0);
964 expect(tt.lessThan);
965 while (!eat(tt.greaterThan) && !state.error) {
966 tsParseType();
967 eat(tt.comma);
968 }
969 popTypeContext(oldIsType);
970}
971
972export function tsIsDeclarationStart() {
973 if (match(tt.name)) {
974 switch (state.contextualKeyword) {
975 case ContextualKeyword._abstract:
976 case ContextualKeyword._declare:
977 case ContextualKeyword._enum:
978 case ContextualKeyword._interface:
979 case ContextualKeyword._module:
980 case ContextualKeyword._namespace:
981 case ContextualKeyword._type:
982 return true;
983 default:
984 break;
985 }
986 }
987
988 return false;
989}
990
991// ======================================================
992// OVERRIDES
993// ======================================================
994
995export function tsParseFunctionBodyAndFinish(
996 functionStart,
997 isGenerator,
998 allowExpressionBody = false,
999 funcContextId,
1000) {
1001 // For arrow functions, `parseArrow` handles the return type itself.
1002 if (!allowExpressionBody && match(tt.colon)) {
1003 tsParseTypeOrTypePredicateAnnotation(tt.colon);
1004 }
1005
1006 // The original code checked the node type to make sure this function type allows a missing
1007 // body, but we skip that to avoid sending around the node type. We instead just use the
1008 // allowExpressionBody boolean to make sure it's not an arrow function.
1009 if (!allowExpressionBody && !match(tt.braceL) && isLineTerminator()) {
1010 // Retroactively mark the function declaration as a type.
1011 let i = state.tokens.length - 1;
1012 while (
1013 i >= 0 &&
1014 (state.tokens[i].start >= functionStart ||
1015 state.tokens[i].type === tt._default ||
1016 state.tokens[i].type === tt._export)
1017 ) {
1018 state.tokens[i].isType = true;
1019 i--;
1020 }
1021 return;
1022 }
1023
1024 parseFunctionBody(functionStart, isGenerator, allowExpressionBody, funcContextId);
1025}
1026
1027export function tsParseSubscript(startPos, noCalls, stopState) {
1028 if (!hasPrecedingLineBreak() && eat(tt.bang)) {
1029 state.tokens[state.tokens.length - 1].type = tt.nonNullAssertion;
1030 return;
1031 }
1032
1033 if (match(tt.lessThan)) {
1034 // There are number of things we are going to "maybe" parse, like type arguments on
1035 // tagged template expressions. If any of them fail, walk it back and continue.
1036 const snapshot = state.snapshot();
1037
1038 if (!noCalls && atPossibleAsync()) {
1039 // Almost certainly this is a generic async function `async <T>() => ...
1040 // But it might be a call with a type argument `async<T>();`
1041 const asyncArrowFn = tsTryParseGenericAsyncArrowFunction();
1042 if (asyncArrowFn) {
1043 return;
1044 }
1045 }
1046 tsParseTypeArguments();
1047 if (!noCalls && eat(tt.parenL)) {
1048 parseCallExpressionArguments();
1049 } else if (match(tt.backQuote)) {
1050 // Tagged template with a type argument.
1051 parseTemplate();
1052 }
1053
1054 if (state.error) {
1055 state.restoreFromSnapshot(snapshot);
1056 } else {
1057 return;
1058 }
1059 }
1060 baseParseSubscript(startPos, noCalls, stopState);
1061}
1062
1063export function tsStartParseNewArguments() {
1064 if (match(tt.lessThan)) {
1065 // 99% certain this is `new C<T>();`. But may be `new C < T;`, which is also legal.
1066 const snapshot = state.snapshot();
1067
1068 state.type = tt.typeParameterStart;
1069 tsParseTypeArguments();
1070 if (!match(tt.parenL)) {
1071 unexpected();
1072 }
1073
1074 if (state.error) {
1075 state.restoreFromSnapshot(snapshot);
1076 }
1077 }
1078}
1079
1080export function tsTryParseExport() {
1081 if (match(tt._import)) {
1082 // `export import A = B;`
1083 expect(tt._import);
1084 tsParseImportEqualsDeclaration();
1085 return true;
1086 } else if (eat(tt.eq)) {
1087 // `export = x;`
1088 parseExpression();
1089 semicolon();
1090 return true;
1091 } else if (eatContextual(ContextualKeyword._as)) {
1092 // `export as namespace A;`
1093 // See `parseNamespaceExportDeclaration` in TypeScript's own parser
1094 expectContextual(ContextualKeyword._namespace);
1095 parseIdentifier();
1096 semicolon();
1097 return true;
1098 } else {
1099 return false;
1100 }
1101}
1102
1103export function tsTryParseExportDefaultExpression() {
1104 if (isContextual(ContextualKeyword._abstract) && lookaheadType() === tt._class) {
1105 state.type = tt._abstract;
1106 next(); // Skip "abstract"
1107 parseClass(true, true);
1108 return true;
1109 }
1110 if (isContextual(ContextualKeyword._interface)) {
1111 // Make sure "export default" are considered type tokens so the whole thing is removed.
1112 const oldIsType = pushTypeContext(2);
1113 tsParseDeclaration(ContextualKeyword._interface, true);
1114 popTypeContext(oldIsType);
1115 return true;
1116 }
1117 return false;
1118}
1119
1120export function tsTryParseStatementContent() {
1121 if (state.type === tt._const) {
1122 const ahead = lookaheadTypeAndKeyword();
1123 if (ahead.type === tt.name && ahead.contextualKeyword === ContextualKeyword._enum) {
1124 expect(tt._const);
1125 expectContextual(ContextualKeyword._enum);
1126 state.tokens[state.tokens.length - 1].type = tt._enum;
1127 tsParseEnumDeclaration();
1128 return true;
1129 }
1130 }
1131 return false;
1132}
1133
1134export function tsParseAccessModifier() {
1135 tsParseModifier([
1136 ContextualKeyword._public,
1137 ContextualKeyword._protected,
1138 ContextualKeyword._private,
1139 ]);
1140}
1141
1142export function tsTryParseClassMemberWithIsStatic(
1143 isStatic,
1144 classContextId,
1145) {
1146 let isAbstract = false;
1147 let isReadonly = false;
1148
1149 const mod = tsParseModifier([ContextualKeyword._abstract, ContextualKeyword._readonly]);
1150 switch (mod) {
1151 case ContextualKeyword._readonly:
1152 isReadonly = true;
1153 isAbstract = !!tsParseModifier([ContextualKeyword._abstract]);
1154 break;
1155 case ContextualKeyword._abstract:
1156 isAbstract = true;
1157 isReadonly = !!tsParseModifier([ContextualKeyword._readonly]);
1158 break;
1159 default:
1160 break;
1161 }
1162
1163 // We no longer check for public/private/etc, but tsTryParseIndexSignature should just return
1164 // false in that case for valid code.
1165 if (!isAbstract && !isStatic) {
1166 const found = tsTryParseIndexSignature();
1167 if (found) {
1168 return true;
1169 }
1170 }
1171
1172 if (isReadonly) {
1173 // Must be a property (if not an index signature).
1174 parseClassPropertyName(classContextId);
1175 parsePostMemberNameModifiers();
1176 parseClassProperty();
1177 return true;
1178 }
1179 return false;
1180}
1181
1182// Note: The reason we do this in `parseIdentifierStatement` and not `parseStatement`
1183// is that e.g. `type()` is valid JS, so we must try parsing that first.
1184// If it's really a type, we will parse `type` as the statement, and can correct it here
1185// by parsing the rest.
1186export function tsParseIdentifierStatement(contextualKeyword) {
1187 const matched = tsParseExpressionStatement(contextualKeyword);
1188 if (!matched) {
1189 semicolon();
1190 }
1191}
1192
1193export function tsParseExportDeclaration() {
1194 // "export declare" is equivalent to just "export".
1195 const isDeclare = eatContextual(ContextualKeyword._declare);
1196 if (isDeclare) {
1197 state.tokens[state.tokens.length - 1].type = tt._declare;
1198 }
1199
1200 let matchedDeclaration = false;
1201 if (match(tt.name)) {
1202 if (isDeclare) {
1203 const oldIsType = pushTypeContext(2);
1204 matchedDeclaration = tsTryParseExportDeclaration();
1205 popTypeContext(oldIsType);
1206 } else {
1207 matchedDeclaration = tsTryParseExportDeclaration();
1208 }
1209 }
1210 if (!matchedDeclaration) {
1211 if (isDeclare) {
1212 const oldIsType = pushTypeContext(2);
1213 parseStatement(true);
1214 popTypeContext(oldIsType);
1215 } else {
1216 parseStatement(true);
1217 }
1218 }
1219}
1220
1221export function tsAfterParseClassSuper(hasSuper) {
1222 if (hasSuper && match(tt.lessThan)) {
1223 tsParseTypeArguments();
1224 }
1225 if (eatContextual(ContextualKeyword._implements)) {
1226 state.tokens[state.tokens.length - 1].type = tt._implements;
1227 const oldIsType = pushTypeContext(1);
1228 tsParseHeritageClause();
1229 popTypeContext(oldIsType);
1230 }
1231}
1232
1233export function tsStartParseObjPropValue() {
1234 tsTryParseTypeParameters();
1235}
1236
1237export function tsStartParseFunctionParams() {
1238 tsTryParseTypeParameters();
1239}
1240
1241// `let x: number;`
1242export function tsAfterParseVarHead() {
1243 const oldIsType = pushTypeContext(0);
1244 eat(tt.bang);
1245 tsTryParseTypeAnnotation();
1246 popTypeContext(oldIsType);
1247}
1248
1249// parse the return type of an async arrow function - let foo = (async (): number => {});
1250export function tsStartParseAsyncArrowFromCallExpression() {
1251 if (match(tt.colon)) {
1252 tsParseTypeAnnotation();
1253 }
1254}
1255
1256// Returns true if the expression was an arrow function.
1257export function tsParseMaybeAssign(noIn, isWithinParens) {
1258 // Note: When the JSX plugin is on, type assertions (`<T> x`) aren't valid syntax.
1259 if (isJSXEnabled) {
1260 return tsParseMaybeAssignWithJSX(noIn, isWithinParens);
1261 } else {
1262 return tsParseMaybeAssignWithoutJSX(noIn, isWithinParens);
1263 }
1264}
1265
1266export function tsParseMaybeAssignWithJSX(noIn, isWithinParens) {
1267 if (!match(tt.lessThan)) {
1268 return baseParseMaybeAssign(noIn, isWithinParens);
1269 }
1270
1271 // Prefer to parse JSX if possible. But may be an arrow fn.
1272 const snapshot = state.snapshot();
1273 let wasArrow = baseParseMaybeAssign(noIn, isWithinParens);
1274 if (state.error) {
1275 state.restoreFromSnapshot(snapshot);
1276 } else {
1277 return wasArrow;
1278 }
1279
1280 // Otherwise, try as type-parameterized arrow function.
1281 state.type = tt.typeParameterStart;
1282 // This is similar to TypeScript's `tryParseParenthesizedArrowFunctionExpression`.
1283 tsParseTypeParameters();
1284 wasArrow = baseParseMaybeAssign(noIn, isWithinParens);
1285 if (!wasArrow) {
1286 unexpected();
1287 }
1288
1289 return wasArrow;
1290}
1291
1292export function tsParseMaybeAssignWithoutJSX(noIn, isWithinParens) {
1293 if (!match(tt.lessThan)) {
1294 return baseParseMaybeAssign(noIn, isWithinParens);
1295 }
1296
1297 const snapshot = state.snapshot();
1298 // This is similar to TypeScript's `tryParseParenthesizedArrowFunctionExpression`.
1299 tsParseTypeParameters();
1300 const wasArrow = baseParseMaybeAssign(noIn, isWithinParens);
1301 if (!wasArrow) {
1302 unexpected();
1303 }
1304 if (state.error) {
1305 state.restoreFromSnapshot(snapshot);
1306 } else {
1307 return wasArrow;
1308 }
1309
1310 // Try parsing a type cast instead of an arrow function.
1311 // This will start with a type assertion (via parseMaybeUnary).
1312 // But don't directly call `tsParseTypeAssertion` because we want to handle any binary after it.
1313 return baseParseMaybeAssign(noIn, isWithinParens);
1314}
1315
1316export function tsParseArrow() {
1317 if (match(tt.colon)) {
1318 // This is different from how the TS parser does it.
1319 // TS uses lookahead. Babylon parses it as a parenthesized expression and converts.
1320 const snapshot = state.snapshot();
1321
1322 tsParseTypeOrTypePredicateAnnotation(tt.colon);
1323 if (canInsertSemicolon()) unexpected();
1324 if (!match(tt.arrow)) unexpected();
1325
1326 if (state.error) {
1327 state.restoreFromSnapshot(snapshot);
1328 }
1329 }
1330 return eat(tt.arrow);
1331}
1332
1333// Allow type annotations inside of a parameter list.
1334export function tsParseAssignableListItemTypes() {
1335 const oldIsType = pushTypeContext(0);
1336 eat(tt.question);
1337 tsTryParseTypeAnnotation();
1338 popTypeContext(oldIsType);
1339}
1340
1341export function tsParseMaybeDecoratorArguments() {
1342 if (match(tt.lessThan)) {
1343 tsParseTypeArguments();
1344 }
1345 baseParseMaybeDecoratorArguments();
1346}