UNPKG

sucrase

Version:

Super-fast alternative to Babel for when you can target modern JS runtimes

1,709 lines (1,352 loc) 53.9 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.default = void 0; var _types = require("../tokenizer/types"); var _context = require("../tokenizer/context"); var N = _interopRequireWildcard(require("../types")); var _parser = _interopRequireDefault(require("../parser")); function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } else { var newObj = {}; if (obj != null) { for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) { var desc = Object.defineProperty && Object.getOwnPropertyDescriptor ? Object.getOwnPropertyDescriptor(obj, key) : {}; if (desc.get || desc.set) { Object.defineProperty(newObj, key, desc); } else { newObj[key] = obj[key]; } } } } newObj.default = obj; return newObj; } } function nonNull(x) { if (x == null) { // $FlowIgnore throw new Error(`Unexpected ${x} value.`); } return x; } function assert(x) { if (!x) { throw new Error("Assert fail"); } } // Doesn't handle "void" or "null" because those are keywords, not identifiers. function keywordTypeFromName(value) { switch (value) { case "any": return "TSAnyKeyword"; case "boolean": return "TSBooleanKeyword"; case "never": return "TSNeverKeyword"; case "number": return "TSNumberKeyword"; case "object": return "TSObjectKeyword"; case "string": return "TSStringKeyword"; case "symbol": return "TSSymbolKeyword"; case "undefined": return "TSUndefinedKeyword"; default: return undefined; } } var _default = superClass => class extends superClass { tsIsIdentifier() { // TODO: actually a bit more complex in TypeScript, but shouldn't matter. // See https://github.com/Microsoft/TypeScript/issues/15008 return this.match(_types.types.name); } tsNextTokenCanFollowModifier() { // Note: TypeScript's implementation is much more complicated because // more things are considered modifiers there. // This implementation only handles modifiers not handled by babylon itself. And "static". // TODO: Would be nice to avoid lookahead. Want a hasLineBreakUpNext() method... this.next(); return !this.hasPrecedingLineBreak() && !this.match(_types.types.parenL) && !this.match(_types.types.colon) && !this.match(_types.types.eq) && !this.match(_types.types.question); } /** Parses a modifier matching one the given modifier names. */ tsParseModifier(allowedModifiers) { if (!this.match(_types.types.name)) { return undefined; } const modifier = this.state.value; if (allowedModifiers.indexOf(modifier) !== -1 && this.tsTryParse(this.tsNextTokenCanFollowModifier.bind(this))) { return modifier; } return undefined; } tsIsListTerminator(kind) { switch (kind) { case "EnumMembers": case "TypeMembers": return this.match(_types.types.braceR); case "HeritageClauseElement": return this.match(_types.types.braceL); case "TupleElementTypes": return this.match(_types.types.bracketR); case "TypeParametersOrArguments": return this.isRelational(">"); } throw new Error("Unreachable"); } tsParseList(kind, parseElement) { const result = []; while (!this.tsIsListTerminator(kind)) { // Skipping "parseListElement" from the TS source since that's just for error handling. result.push(parseElement()); } return result; } tsParseDelimitedList(kind, parseElement) { return nonNull(this.tsParseDelimitedListWorker(kind, parseElement, /* expectSuccess */ true)); } tsTryParseDelimitedList(kind, parseElement) { return this.tsParseDelimitedListWorker(kind, parseElement, /* expectSuccess */ false); } /** * If !expectSuccess, returns undefined instead of failing to parse. * If expectSuccess, parseElement should always return a defined value. */ tsParseDelimitedListWorker(kind, parseElement, expectSuccess) { const result = []; while (true) { if (this.tsIsListTerminator(kind)) { break; } const element = parseElement(); if (element == null) { return undefined; } result.push(element); if (this.eat(_types.types.comma)) { continue; } if (this.tsIsListTerminator(kind)) { break; } if (expectSuccess) { // This will fail with an error about a missing comma this.expect(_types.types.comma); } return undefined; } return result; } tsParseBracketedList(kind, parseElement, bracket, skipFirstToken) { if (!skipFirstToken) { if (bracket) { this.expect(_types.types.bracketL); } else { this.expectRelational("<"); } } const result = this.tsParseDelimitedList(kind, parseElement); if (bracket) { this.expect(_types.types.bracketR); } else { this.expectRelational(">"); } return result; } tsParseEntityName(allowReservedWords) { let entity = this.parseIdentifier(); while (this.eat(_types.types.dot)) { const node = this.startNodeAtNode(entity); node.left = entity; node.right = this.parseIdentifier(allowReservedWords); entity = this.finishNode(node, "TSQualifiedName"); } return entity; } tsParseTypeReference() { const node = this.startNode(); node.typeName = this.tsParseEntityName( /* allowReservedWords */ false); if (!this.hasPrecedingLineBreak() && this.isRelational("<")) { node.typeParameters = this.tsParseTypeArguments(); } return this.finishNode(node, "TSTypeReference"); } tsParseThisTypePredicate(lhs) { this.next(); const node = this.startNode(); node.parameterName = lhs; node.typeAnnotation = this.tsParseTypeAnnotation( /* eatColon */ false); return this.finishNode(node, "TSTypePredicate"); } tsParseThisTypeNode() { const node = this.startNode(); this.next(); return this.finishNode(node, "TSThisType"); } tsParseTypeQuery() { const node = this.startNode(); this.expect(_types.types._typeof); node.exprName = this.tsParseEntityName( /* allowReservedWords */ true); return this.finishNode(node, "TSTypeQuery"); } tsParseTypeParameter() { const node = this.startNode(); node.name = this.parseIdentifierName(node.start); if (this.eat(_types.types._extends)) { node.constraint = this.tsParseType(); } if (this.eat(_types.types.eq)) { node.default = this.tsParseType(); } return this.finishNode(node, "TSTypeParameter"); } tsTryParseTypeParameters() { if (this.isRelational("<")) { return this.tsParseTypeParameters(); } } tsParseTypeParameters() { const node = this.startNode(); if (this.isRelational("<") || this.match(_types.types.jsxTagStart)) { this.next(); } else { this.unexpected(); } node.params = this.tsParseBracketedList("TypeParametersOrArguments", this.tsParseTypeParameter.bind(this), /* bracket */ false, /* skipFirstToken */ true); return this.finishNode(node, "TSTypeParameterDeclaration"); } // Note: In TypeScript implementation we must provide `yieldContext` and `awaitContext`, // but here it's always false, because this is only used for types. tsFillSignature(returnToken, signature) { // Arrow fns *must* have return token (`=>`). Normal functions can omit it. const returnTokenRequired = returnToken === _types.types.arrow; signature.typeParameters = this.tsTryParseTypeParameters(); this.expect(_types.types.parenL); signature.parameters = this.tsParseBindingListForSignature(); if (returnTokenRequired) { signature.typeAnnotation = this.tsParseTypeOrTypePredicateAnnotation(returnToken); } else if (this.match(returnToken)) { signature.typeAnnotation = this.tsParseTypeOrTypePredicateAnnotation(returnToken); } } tsParseBindingListForSignature() { return this.parseBindingList(_types.types.parenR).map(pattern => { if (pattern.type !== "Identifier" && pattern.type !== "RestElement") { throw this.unexpected(pattern.start, "Name in a signature must be an Identifier."); } return pattern; }); } tsParseTypeMemberSemicolon() { if (!this.eat(_types.types.comma)) { this.semicolon(); } } tsParseSignatureMember(kind) { const node = this.startNode(); if (kind === "TSConstructSignatureDeclaration") { this.expect(_types.types._new); } this.tsFillSignature(_types.types.colon, node); this.tsParseTypeMemberSemicolon(); return this.finishNode(node, kind); } tsIsUnambiguouslyIndexSignature() { this.next(); // Skip '{' return this.eat(_types.types.name) && this.match(_types.types.colon); } tsTryParseIndexSignature(node) { if (!(this.match(_types.types.bracketL) && this.tsLookAhead(this.tsIsUnambiguouslyIndexSignature.bind(this)))) { return undefined; } this.expect(_types.types.bracketL); const id = this.parseIdentifier(); this.expect(_types.types.colon); id.typeAnnotation = this.tsParseTypeAnnotation( /* eatColon */ false); this.expect(_types.types.bracketR); node.parameters = [id]; const type = this.tsTryParseTypeAnnotation(); if (type) node.typeAnnotation = type; this.tsParseTypeMemberSemicolon(); return this.finishNode(node, "TSIndexSignature"); } tsParsePropertyOrMethodSignature(node, readonly) { this.parsePropertyName(node); if (this.eat(_types.types.question)) node.optional = true; const nodeAny = node; if (!readonly && (this.match(_types.types.parenL) || this.isRelational("<"))) { const method = nodeAny; this.tsFillSignature(_types.types.colon, method); this.tsParseTypeMemberSemicolon(); return this.finishNode(method, "TSMethodSignature"); } else { const property = nodeAny; if (readonly) property.readonly = true; const type = this.tsTryParseTypeAnnotation(); if (type) property.typeAnnotation = type; this.tsParseTypeMemberSemicolon(); return this.finishNode(property, "TSPropertySignature"); } } tsParseTypeMember() { if (this.match(_types.types.parenL) || this.isRelational("<")) { return this.tsParseSignatureMember("TSCallSignatureDeclaration"); } if (this.match(_types.types._new) && this.tsLookAhead(this.tsIsStartOfConstructSignature.bind(this))) { return this.tsParseSignatureMember("TSConstructSignatureDeclaration"); } // Instead of fullStart, we create a node here. const node = this.startNode(); const readonly = !!this.tsParseModifier(["readonly"]); const idx = this.tsTryParseIndexSignature(node); if (idx) { if (readonly) node.readonly = true; return idx; } return this.tsParsePropertyOrMethodSignature(node, readonly); } tsIsStartOfConstructSignature() { this.next(); return this.match(_types.types.parenL) || this.isRelational("<"); } tsParseTypeLiteral() { const node = this.startNode(); node.members = this.tsParseObjectTypeMembers(); return this.finishNode(node, "TSTypeLiteral"); } tsParseObjectTypeMembers() { this.expect(_types.types.braceL); const members = this.tsParseList("TypeMembers", this.tsParseTypeMember.bind(this)); this.expect(_types.types.braceR); return members; } tsIsStartOfMappedType() { this.next(); if (this.isContextual("readonly")) { this.next(); } if (!this.match(_types.types.bracketL)) { return false; } this.next(); if (!this.tsIsIdentifier()) { return false; } this.next(); return this.match(_types.types._in); } tsParseMappedTypeParameter() { const node = this.startNode(); node.name = this.parseIdentifierName(node.start); this.expect(_types.types._in); node.constraint = this.tsParseType(); return this.finishNode(node, "TSTypeParameter"); } tsParseMappedType() { const node = this.startNode(); this.expect(_types.types.braceL); if (this.eatContextual("readonly")) { node.readonly = true; } this.expect(_types.types.bracketL); node.typeParameter = this.tsParseMappedTypeParameter(); this.expect(_types.types.bracketR); if (this.eat(_types.types.question)) { node.optional = true; } node.typeAnnotation = this.tsTryParseType(); this.semicolon(); this.expect(_types.types.braceR); return this.finishNode(node, "TSMappedType"); } tsParseTupleType() { const node = this.startNode(); node.elementTypes = this.tsParseBracketedList("TupleElementTypes", this.tsParseType.bind(this), /* bracket */ true, /* skipFirstToken */ false); return this.finishNode(node, "TSTupleType"); } tsParseParenthesizedType() { const node = this.startNode(); this.expect(_types.types.parenL); node.typeAnnotation = this.tsParseType(); this.expect(_types.types.parenR); return this.finishNode(node, "TSParenthesizedType"); } tsParseFunctionOrConstructorType(type) { const node = this.startNode(); if (type === "TSConstructorType") { this.expect(_types.types._new); } this.tsFillSignature(_types.types.arrow, node); return this.finishNode(node, type); } tsParseLiteralTypeNode() { const node = this.startNode(); node.literal = (() => { switch (this.state.type) { case _types.types.num: return this.parseLiteral(this.state.value, "NumericLiteral"); case _types.types.string: return this.parseLiteral(this.state.value, "StringLiteral"); case _types.types._true: case _types.types._false: return this.parseBooleanLiteral(); default: throw this.unexpected(); } })(); return this.finishNode(node, "TSLiteralType"); } tsParseNonArrayType() { switch (this.state.type) { case _types.types.name: case _types.types._void: case _types.types._null: { const type = this.match(_types.types._void) ? "TSVoidKeyword" : this.match(_types.types._null) ? "TSNullKeyword" : keywordTypeFromName(this.state.value); if (type !== undefined && this.lookahead().type !== _types.types.dot) { const node = this.startNode(); this.next(); return this.finishNode(node, type); } return this.tsParseTypeReference(); } case _types.types.string: case _types.types.num: case _types.types._true: case _types.types._false: return this.tsParseLiteralTypeNode(); case _types.types.plusMin: if (this.state.value === "-") { const node = this.startNode(); this.next(); if (!this.match(_types.types.num)) { throw this.unexpected(); } node.literal = this.parseLiteral(-this.state.value, "NumericLiteral", node.start, node.loc.start); return this.finishNode(node, "TSLiteralType"); } break; case _types.types._this: { const thisKeyword = this.tsParseThisTypeNode(); if (this.isContextual("is") && !this.hasPrecedingLineBreak()) { return this.tsParseThisTypePredicate(thisKeyword); } else { return thisKeyword; } } case _types.types._typeof: return this.tsParseTypeQuery(); case _types.types.braceL: return this.tsLookAhead(this.tsIsStartOfMappedType.bind(this)) ? this.tsParseMappedType() : this.tsParseTypeLiteral(); case _types.types.bracketL: return this.tsParseTupleType(); case _types.types.parenL: return this.tsParseParenthesizedType(); } throw this.unexpected(); } tsParseArrayTypeOrHigher() { let type = this.tsParseNonArrayType(); while (!this.hasPrecedingLineBreak() && this.eat(_types.types.bracketL)) { if (this.match(_types.types.bracketR)) { const node = this.startNodeAtNode(type); node.elementType = type; this.expect(_types.types.bracketR); type = this.finishNode(node, "TSArrayType"); } else { const node = this.startNodeAtNode(type); node.objectType = type; node.indexType = this.tsParseType(); this.expect(_types.types.bracketR); type = this.finishNode(node, "TSIndexedAccessType"); } } return type; } tsParseTypeOperator(operator) { const node = this.startNode(); this.expectContextual(operator); node.operator = operator; node.typeAnnotation = this.tsParseTypeOperatorOrHigher(); return this.finishNode(node, "TSTypeOperator"); } tsParseTypeOperatorOrHigher() { if (this.isContextual("keyof")) { return this.tsParseTypeOperator("keyof"); } return this.tsParseArrayTypeOrHigher(); } tsParseUnionOrIntersectionType(kind, parseConstituentType, operator) { this.eat(operator); let type = parseConstituentType(); if (this.match(operator)) { const types = [type]; while (this.eat(operator)) { types.push(parseConstituentType()); } const node = this.startNodeAtNode(type); node.types = types; type = this.finishNode(node, kind); } return type; } tsParseIntersectionTypeOrHigher() { return this.tsParseUnionOrIntersectionType("TSIntersectionType", this.tsParseTypeOperatorOrHigher.bind(this), _types.types.bitwiseAND); } tsParseUnionTypeOrHigher() { return this.tsParseUnionOrIntersectionType("TSUnionType", this.tsParseIntersectionTypeOrHigher.bind(this), _types.types.bitwiseOR); } tsIsStartOfFunctionType() { if (this.isRelational("<")) { return true; } return this.match(_types.types.parenL) && this.tsLookAhead(this.tsIsUnambiguouslyStartOfFunctionType.bind(this)); } tsSkipParameterStart() { if (this.match(_types.types.name) || this.match(_types.types._this)) { this.next(); return true; } return false; } tsIsUnambiguouslyStartOfFunctionType() { this.next(); if (this.match(_types.types.parenR) || this.match(_types.types.ellipsis)) { // ( ) // ( ... return true; } if (this.tsSkipParameterStart()) { if (this.match(_types.types.colon) || this.match(_types.types.comma) || this.match(_types.types.question) || this.match(_types.types.eq)) { // ( xxx : // ( xxx , // ( xxx ? // ( xxx = return true; } if (this.match(_types.types.parenR)) { this.next(); if (this.match(_types.types.arrow)) { // ( xxx ) => return true; } } } return false; } tsParseTypeOrTypePredicateAnnotation(returnToken) { const t = this.startNode(); this.expect(returnToken); const typePredicateVariable = this.tsIsIdentifier() && this.tsTryParse(this.tsParseTypePredicatePrefix.bind(this)); if (!typePredicateVariable) { return this.tsParseTypeAnnotation( /* eatColon */ false, t); } const type = this.tsParseTypeAnnotation( /* eatColon */ false); const node = this.startNodeAtNode(typePredicateVariable); node.parameterName = typePredicateVariable; node.typeAnnotation = type; t.typeAnnotation = this.finishNode(node, "TSTypePredicate"); return this.finishNode(t, "TSTypeAnnotation"); } tsTryParseTypeOrTypePredicateAnnotation() { return this.match(_types.types.colon) ? this.tsParseTypeOrTypePredicateAnnotation(_types.types.colon) : undefined; } tsTryParseTypeAnnotation() { return this.match(_types.types.colon) ? this.tsParseTypeAnnotation() : undefined; } tsTryParseType() { return this.eat(_types.types.colon) ? this.tsParseType() : undefined; } tsParseTypePredicatePrefix() { const id = this.parseIdentifier(); if (this.isContextual("is") && !this.hasPrecedingLineBreak()) { this.next(); return id; } } tsParseTypeAnnotation(eatColon = true, t = this.startNode()) { if (eatColon) this.expect(_types.types.colon); t.typeAnnotation = this.tsParseType(); return this.finishNode(t, "TSTypeAnnotation"); } tsParseType() { // Need to set `state.inType` so that we don't parse JSX in a type context. const oldInType = this.state.inType; this.state.inType = true; try { if (this.tsIsStartOfFunctionType()) { return this.tsParseFunctionOrConstructorType("TSFunctionType"); } if (this.match(_types.types._new)) { // As in `new () => Date` return this.tsParseFunctionOrConstructorType("TSConstructorType"); } return this.tsParseUnionTypeOrHigher(); } finally { this.state.inType = oldInType; } } tsParseTypeAssertion() { const node = this.startNode(); node.typeAnnotation = this.tsParseType(); this.expectRelational(">"); node.expression = this.parseMaybeUnary(); return this.finishNode(node, "TSTypeAssertion"); } tsTryParseTypeArgumentsInExpression() { return this.tsTryParseAndCatch(() => { const res = this.startNode(); this.expectRelational("<"); const typeArguments = this.tsParseDelimitedList("TypeParametersOrArguments", this.tsParseType.bind(this)); this.expectRelational(">"); res.params = typeArguments; this.finishNode(res, "TSTypeParameterInstantiation"); this.expect(_types.types.parenL); return res; }); } tsParseHeritageClause() { return this.tsParseDelimitedList("HeritageClauseElement", this.tsParseExpressionWithTypeArguments.bind(this)); } tsParseExpressionWithTypeArguments() { const node = this.startNode(); // Note: TS uses parseLeftHandSideExpressionOrHigher, // then has grammar errors later if it's not an EntityName. node.expression = this.tsParseEntityName( /* allowReservedWords */ false); if (this.isRelational("<")) { node.typeParameters = this.tsParseTypeArguments(); } return this.finishNode(node, "TSExpressionWithTypeArguments"); } tsParseInterfaceDeclaration(node) { node.id = this.parseIdentifier(); node.typeParameters = this.tsTryParseTypeParameters(); if (this.eat(_types.types._extends)) { node.extends = this.tsParseHeritageClause(); } const body = this.startNode(); body.body = this.tsParseObjectTypeMembers(); node.body = this.finishNode(body, "TSInterfaceBody"); return this.finishNode(node, "TSInterfaceDeclaration"); } tsParseTypeAliasDeclaration(node) { node.id = this.parseIdentifier(); node.typeParameters = this.tsTryParseTypeParameters(); this.expect(_types.types.eq); node.typeAnnotation = this.tsParseType(); this.semicolon(); return this.finishNode(node, "TSTypeAliasDeclaration"); } tsParseEnumMember() { const node = this.startNode(); // Computed property names are grammar errors in an enum, so accept just string literal or identifier. node.id = this.match(_types.types.string) ? this.parseLiteral(this.state.value, "StringLiteral") : this.parseIdentifier( /* liberal */ true); if (this.eat(_types.types.eq)) { node.initializer = this.parseMaybeAssign(); } return this.finishNode(node, "TSEnumMember"); } tsParseEnumDeclaration(node, isConst) { if (isConst) node.const = true; node.id = this.parseIdentifier(); this.expect(_types.types.braceL); node.members = this.tsParseDelimitedList("EnumMembers", this.tsParseEnumMember.bind(this)); this.expect(_types.types.braceR); return this.finishNode(node, "TSEnumDeclaration"); } tsParseModuleBlock() { const node = this.startNode(); this.expect(_types.types.braceL); // Inside of a module block is considered "top-level", meaning it can have imports and exports. this.parseBlockOrModuleBlockBody(node.body = [], /* directives */ undefined, /* topLevel */ true, /* end */ _types.types.braceR); return this.finishNode(node, "TSModuleBlock"); } tsParseModuleOrNamespaceDeclaration(node) { node.id = this.parseIdentifier(); if (this.eat(_types.types.dot)) { const inner = this.startNode(); this.tsParseModuleOrNamespaceDeclaration(inner); node.body = inner; } else { node.body = this.tsParseModuleBlock(); } return this.finishNode(node, "TSModuleDeclaration"); } tsParseAmbientExternalModuleDeclaration(node) { if (this.isContextual("global")) { node.global = true; node.id = this.parseIdentifier(); } else if (this.match(_types.types.string)) { node.id = this.parseExprAtom(); } else { this.unexpected(); } if (this.match(_types.types.braceL)) { node.body = this.tsParseModuleBlock(); } else { this.semicolon(); } return this.finishNode(node, "TSModuleDeclaration"); } tsParseImportEqualsDeclaration(node, isExport) { node.isExport = isExport || false; node.id = this.parseIdentifier(); this.expect(_types.types.eq); node.moduleReference = this.tsParseModuleReference(); this.semicolon(); return this.finishNode(node, "TSImportEqualsDeclaration"); } tsIsExternalModuleReference() { return this.isContextual("require") && this.lookahead().type === _types.types.parenL; } tsParseModuleReference() { return this.tsIsExternalModuleReference() ? this.tsParseExternalModuleReference() : this.tsParseEntityName( /* allowReservedWords */ false); } tsParseExternalModuleReference() { const node = this.startNode(); this.expectContextual("require"); this.expect(_types.types.parenL); if (!this.match(_types.types.string)) { throw this.unexpected(); } node.expression = this.parseLiteral(this.state.value, "StringLiteral"); this.expect(_types.types.parenR); return this.finishNode(node, "TSExternalModuleReference"); } // Utilities tsLookAhead(f) { const state = this.state.clone(); const res = f(); this.state = state; return res; } tsTryParseAndCatch(f) { const state = this.state.clone(); try { return f(); } catch (e) { if (e instanceof SyntaxError) { this.state = state; return undefined; } throw e; } } tsTryParse(f) { const state = this.state.clone(); const result = f(); if (result !== undefined && result !== false) { return result; } else { this.state = state; return undefined; } } nodeWithSamePosition(original, type) { const node = this.startNodeAtNode(original); node.type = type; node.end = original.end; node.loc.end = original.loc.end; if (original.leadingComments) { node.leadingComments = original.leadingComments; } if (original.trailingComments) { node.trailingComments = original.trailingComments; } if (original.innerComments) node.innerComments = original.innerComments; return node; } tsTryParseDeclare(nany) { switch (this.state.type) { case _types.types._function: this.next(); return this.parseFunction(nany, /* isStatement */ true); case _types.types._class: return this.parseClass(nany, /* isStatement */ true, /* optionalId */ false); case _types.types._const: if (this.match(_types.types._const) && this.isLookaheadContextual("enum")) { // `const enum = 0;` not allowed because "enum" is a strict mode reserved word. this.expect(_types.types._const); this.expectContextual("enum"); return this.tsParseEnumDeclaration(nany, /* isConst */ true); } // falls through case _types.types._var: case _types.types._let: return this.parseVarStatement(nany, this.state.type); case _types.types.name: { const value = this.state.value; if (value === "global") { return this.tsParseAmbientExternalModuleDeclaration(nany); } else { return this.tsParseDeclaration(nany, value, /* next */ true); } } } } // Note: this won't be called unless the keyword is allowed in `shouldParseExportDeclaration`. tsTryParseExportDeclaration() { return this.tsParseDeclaration(this.startNode(), this.state.value, /* next */ true); } tsParseExpressionStatement(node, expr) { switch (expr.name) { case "declare": { const declaration = this.tsTryParseDeclare(node); if (declaration) { declaration.declare = true; return declaration; } break; } case "global": // `global { }` (with no `declare`) may appear inside an ambient module declaration. // Would like to use tsParseAmbientExternalModuleDeclaration here, but already ran past "global". if (this.match(_types.types.braceL)) { const mod = node; mod.global = true; mod.id = expr; mod.body = this.tsParseModuleBlock(); return this.finishNode(mod, "TSModuleDeclaration"); } break; default: return this.tsParseDeclaration(node, expr.name, /* next */ false); } } // Common to tsTryParseDeclare, tsTryParseExportDeclaration, and tsParseExpressionStatement. tsParseDeclaration(node, value, next) { switch (value) { case "abstract": if (next || this.match(_types.types._class)) { const cls = node; cls.abstract = true; if (next) this.next(); return this.parseClass(cls, /* isStatement */ true, /* optionalId */ false); } break; case "enum": if (next || this.match(_types.types.name)) { if (next) this.next(); return this.tsParseEnumDeclaration(node, /* isConst */ false); } break; case "interface": if (next || this.match(_types.types.name)) { if (next) this.next(); return this.tsParseInterfaceDeclaration(node); } break; case "module": if (next) this.next(); if (this.match(_types.types.string)) { return this.tsParseAmbientExternalModuleDeclaration(node); } else if (next || this.match(_types.types.name)) { return this.tsParseModuleOrNamespaceDeclaration(node); } break; case "namespace": if (next || this.match(_types.types.name)) { if (next) this.next(); return this.tsParseModuleOrNamespaceDeclaration(node); } break; case "type": if (next || this.match(_types.types.name)) { if (next) this.next(); return this.tsParseTypeAliasDeclaration(node); } break; } } tsTryParseGenericAsyncArrowFunction(startPos, startLoc) { const res = this.tsTryParseAndCatch(() => { const node = this.startNodeAt(startPos, startLoc); node.typeParameters = this.tsParseTypeParameters(); // Don't use overloaded parseFunctionParams which would look for "<" again. super.parseFunctionParams(node); node.returnType = this.tsTryParseTypeOrTypePredicateAnnotation(); this.expect(_types.types.arrow); return node; }); if (!res) { return undefined; } res.id = null; res.generator = false; res.expression = true; // May be set again by parseFunctionBody. res.async = true; this.parseFunctionBody(res, true); return this.finishNode(res, "ArrowFunctionExpression"); } tsParseTypeArguments() { const node = this.startNode(); this.expectRelational("<"); node.params = this.tsParseDelimitedList("TypeParametersOrArguments", this.tsParseType.bind(this)); this.expectRelational(">"); return this.finishNode(node, "TSTypeParameterInstantiation"); } tsIsDeclarationStart() { if (this.match(_types.types.name)) { switch (this.state.value) { case "abstract": case "declare": case "enum": case "interface": case "module": case "namespace": case "type": return true; } } return false; } // ====================================================== // OVERRIDES // ====================================================== isExportDefaultSpecifier() { if (this.tsIsDeclarationStart()) return false; return super.isExportDefaultSpecifier(); } parseAssignableListItem(allowModifiers, decorators) { let accessibility; let readonly = false; if (allowModifiers) { accessibility = this.parseAccessModifier(); readonly = !!this.tsParseModifier(["readonly"]); } const left = this.parseMaybeDefault(); this.parseAssignableListItemTypes(left); const elt = this.parseMaybeDefault(left.start, left.loc.start, left); if (accessibility || readonly) { const pp = this.startNodeAtNode(elt); if (decorators.length) { pp.decorators = decorators; } if (accessibility) pp.accessibility = accessibility; if (readonly) pp.readonly = readonly; if (elt.type !== "Identifier" && elt.type !== "AssignmentPattern") { throw this.raise(pp.start, "A parameter property may not be declared using a binding pattern."); } pp.parameter = elt; return this.finishNode(pp, "TSParameterProperty"); } else { if (decorators.length) { left.decorators = decorators; } return elt; } } parseFunctionBodyAndFinish(node, type, allowExpressionBody) { // For arrow functions, `parseArrow` handles the return type itself. if (!allowExpressionBody && this.match(_types.types.colon)) { node.returnType = this.tsParseTypeOrTypePredicateAnnotation(_types.types.colon); } const bodilessType = type === "FunctionDeclaration" ? "TSDeclareFunction" : type === "ClassMethod" ? "TSDeclareMethod" : undefined; if (bodilessType && !this.match(_types.types.braceL) && this.isLineTerminator()) { this.finishNode(node, bodilessType); return; } super.parseFunctionBodyAndFinish(node, type, allowExpressionBody); } parseSubscript(base, startPos, startLoc, noCalls, state) { if (!this.hasPrecedingLineBreak() && this.eat(_types.types.bang)) { const nonNullExpression = this.startNodeAt(startPos, startLoc); nonNullExpression.expression = base; return this.finishNode(nonNullExpression, "TSNonNullExpression"); } if (!noCalls && this.isRelational("<")) { if (this.atPossibleAsync(base)) { // Almost certainly this is a generic async function `async <T>() => ... // But it might be a call with a type argument `async<T>();` const asyncArrowFn = this.tsTryParseGenericAsyncArrowFunction(startPos, startLoc); if (asyncArrowFn) { return asyncArrowFn; } } const node = this.startNodeAt(startPos, startLoc); node.callee = base; // May be passing type arguments. But may just be the `<` operator. const typeArguments = this.tsTryParseTypeArgumentsInExpression(); // Also eats the "(" if (typeArguments) { // possibleAsync always false here, because we would have handled it above. // $FlowIgnore (won't be any undefined arguments) node.arguments = this.parseCallExpressionArguments(_types.types.parenR, /* possibleAsync */ false); node.typeParameters = typeArguments; return this.finishCallExpression(node); } } return super.parseSubscript(base, startPos, startLoc, noCalls, state); } parseNewArguments(node) { if (this.isRelational("<")) { // tsTryParseAndCatch is expensive, so avoid if not necessary. // 99% certain this is `new C<T>();`. But may be `new C < T;`, which is also legal. const typeParameters = this.tsTryParseAndCatch(() => { const args = this.tsParseTypeArguments(); if (!this.match(_types.types.parenL)) this.unexpected(); return args; }); if (typeParameters) { node.typeParameters = typeParameters; } } super.parseNewArguments(node); } parseExprOp(left, leftStartPos, leftStartLoc, minPrec, noIn) { if (nonNull(_types.types._in.binop) > minPrec && !this.hasPrecedingLineBreak() && this.eatContextual("as")) { const node = this.startNodeAt(leftStartPos, leftStartLoc); node.expression = left; node.typeAnnotation = this.tsParseType(); this.finishNode(node, "TSAsExpression"); return this.parseExprOp(node, leftStartPos, leftStartLoc, minPrec, noIn); } return super.parseExprOp(left, leftStartPos, leftStartLoc, minPrec, noIn); } checkReservedWord(word, startLoc, checkKeywords, // eslint-disable-next-line no-unused-vars isBinding) {} // Don't bother checking for TypeScript code. // Strict mode words may be allowed as in `declare namespace N { const static: number; }`. // And we have a type checker anyway, so don't bother having the parser do it. /* Don't bother doing this check in TypeScript code because: 1. We may have a nested export statement with the same name: export const x = 0; export namespace N { export const x = 1; } 2. We have a type checker to warn us about this sort of thing. */ checkDuplicateExports() {} parseImport(node) { if (this.match(_types.types.name) && this.lookahead().type === _types.types.eq) { return this.tsParseImportEqualsDeclaration(node); } return super.parseImport(node); } parseExport(node) { if (this.match(_types.types._import)) { // `export import A = B;` this.expect(_types.types._import); return this.tsParseImportEqualsDeclaration(node, /* isExport */ true); } else if (this.eat(_types.types.eq)) { // `export = x;` const assign = node; assign.expression = this.parseExpression(); this.semicolon(); return this.finishNode(assign, "TSExportAssignment"); } else if (this.eatContextual("as")) { // `export as namespace A;` const decl = node; // See `parseNamespaceExportDeclaration` in TypeScript's own parser this.expectContextual("namespace"); decl.id = this.parseIdentifier(); this.semicolon(); return this.finishNode(decl, "TSNamespaceExportDeclaration"); } else { return super.parseExport(node); } } parseStatementContent(declaration, topLevel) { if (this.state.type === _types.types._const) { const ahead = this.lookahead(); if (ahead.type === _types.types.name && ahead.value === "enum") { const node = this.startNode(); this.expect(_types.types._const); this.expectContextual("enum"); return this.tsParseEnumDeclaration(node, /* isConst */ true); } } return super.parseStatementContent(declaration, topLevel); } parseAccessModifier() { return this.tsParseModifier(["public", "protected", "private"]); } parseClassMember(classBody, member, state) { const accessibility = this.parseAccessModifier(); if (accessibility) member.accessibility = accessibility; super.parseClassMember(classBody, member, state); } parseClassMemberWithIsStatic(classBody, member, state, isStatic) { const methodOrProp = member; const prop = member; const propOrIdx = member; let abstract = false, readonly = false; const mod = this.tsParseModifier(["abstract", "readonly"]); switch (mod) { case "readonly": readonly = true; abstract = !!this.tsParseModifier(["abstract"]); break; case "abstract": abstract = true; readonly = !!this.tsParseModifier(["readonly"]); break; } if (abstract) methodOrProp.abstract = true; if (readonly) propOrIdx.readonly = true; if (!abstract && !isStatic && !methodOrProp.accessibility) { const idx = this.tsTryParseIndexSignature(member); if (idx) { classBody.body.push(idx); return; } } if (readonly) { // Must be a property (if not an index signature). methodOrProp.static = isStatic; this.parseClassPropertyName(prop); this.parsePostMemberNameModifiers(methodOrProp); this.pushClassProperty(classBody, prop); return; } super.parseClassMemberWithIsStatic(classBody, member, state, isStatic); } parsePostMemberNameModifiers(methodOrProp) { const optional = this.eat(_types.types.question); if (optional) methodOrProp.optional = true; } // Note: The reason we do this in `parseExpressionStatement` and not `parseStatement` // is that e.g. `type()` is valid JS, so we must try parsing that first. // If it's really a type, we will parse `type` as the statement, and can correct it here // by parsing the rest. parseExpressionStatement(node, expr) { const decl = expr.type === "Identifier" ? this.tsParseExpressionStatement(node, expr) : undefined; return decl || super.parseExpressionStatement(node, expr); } // export type // Should be true for anything parsed by `tsTryParseExportDeclaration`. shouldParseExportDeclaration() { if (this.tsIsDeclarationStart()) return true; return super.shouldParseExportDeclaration(); } // An apparent conditional expression could actually be an optional parameter in an arrow function. parseConditional(expr, noIn, startPos, startLoc, refNeedsArrowPos) { // only do the expensive clone if there is a question mark // and if we come from inside parens if (!refNeedsArrowPos || !this.match(_types.types.question)) { return super.parseConditional(expr, noIn, startPos, startLoc, refNeedsArrowPos); } const state = this.state.clone(); try { return super.parseConditional(expr, noIn, startPos, startLoc); } catch (err) { if (!(err instanceof SyntaxError)) { // istanbul ignore next: no such error is expected throw err; } this.state = state; refNeedsArrowPos.start = err.pos || this.state.start; return expr; } } // Note: These "type casts" are *not* valid TS expressions. // But we parse them here and change them when completing the arrow function. parseParenItem(node, startPos, startLoc) { node = super.parseParenItem(node, startPos, startLoc); if (this.eat(_types.types.question)) { node.optional = true; } if (this.match(_types.types.colon)) { const typeCastNode = this.startNodeAt(startPos, startLoc); typeCastNode.expression = node; typeCastNode.typeAnnotation = this.tsParseTypeAnnotation(); return this.finishNode(typeCastNode, "TSTypeCastExpression"); } return node; } parseExportDeclaration(node) { // "export declare" is equivalent to just "export". const isDeclare = this.eatContextual("declare"); let declaration; if (this.match(_types.types.name)) { declaration = this.tsTryParseExportDeclaration(); } if (!declaration) { declaration = super.parseExportDeclaration(node); } if (declaration && isDeclare) { declaration.declare = true; } return declaration; } parseClassId(node, isStatement, optionalId) { if ((!isStatement || optionalId) && this.isContextual("implements")) { return; } super.parseClassId(...arguments); const typeParameters = this.tsTryParseTypeParameters(); if (typeParameters) node.typeParameters = typeParameters; } parseClassProperty(node) { const type = this.tsTryParseTypeAnnotation(); if (type) node.typeAnnotation = type; return super.parseClassProperty(node); } pushClassMethod(classBody, method, isGenerator, isAsync, isConstructor) { const typeParameters = this.tsTryParseTypeParameters(); if (typeParameters) method.typeParameters = typeParameters; super.pushClassMethod(classBody, method, isGenerator, isAsync, isConstructor); } pushClassPrivateMethod(classBody, method, isGenerator, isAsync) { const typeParameters = this.tsTryParseTypeParameters(); if (typeParameters) method.typeParameters = typeParameters; super.pushClassPrivateMethod(classBody, method, isGenerator, isAsync); } parseClassSuper(node) { super.parseClassSuper(node); if (node.superClass && this.isRelational("<")) { node.superTypeParameters = this.tsParseTypeArguments(); } if (this.eatContextual("implements")) { node.implements = this.tsParseHeritageClause(); } } parseObjPropValue(prop, ...args) { if (this.isRelational("<")) { throw new Error("TODO"); } super.parseObjPropValue(prop, ...args); } parseFunctionParams(node, allowModifiers) { const typeParameters = this.tsTryParseTypeParameters(); if (typeParameters) node.typeParameters = typeParameters; super.parseFunctionParams(node, allowModifiers); } // `let x: number;` parseVarHead(decl) { super.parseVarHead(decl); const type = this.tsTryParseTypeAnnotation(); if (type) { decl.id.typeAnnotation = type; this.finishNode(decl.id, decl.id.type); // set end position to end of type } } // parse the return type of an async arrow function - let foo = (async (): number => {}); parseAsyncArrowFromCallExpression(node, call) { if (this.match(_types.types.colon)) { node.returnType = this.tsParseTypeAnnotation(); } return super.parseAsyncArrowFromCallExpression(node, call); } parseMaybeAssign(...args) { // Note: When the JSX plugin is on, type assertions (`<T> x`) aren't valid syntax. let jsxError; if (this.match(_types.types.jsxTagStart)) { const context = this.curContext(); assert(context === _context.types.j_oTag); // Only time j_oTag is pushed is right after j_expr. assert(this.state.context[this.state.context.length - 2] === _context.types.j_expr); // Prefer to parse JSX if possible. But may be an arrow fn. const state = this.state.clone(); try { return super.parseMaybeAssign(...args); } catch (err) { if (!(err instanceof SyntaxError)) { // istanbul ignore next: no such error is expected throw err; } this.state = state; // Pop the context added by the jsxTagStart. assert(this.curContext() === _context.types.j_oTag); this.state.context.pop(); assert(this.curContext() === _context.types.j_expr); this.state.context.pop(); jsxError = err; } } if (jsxError === undefined && !this.isRelational("<")) { return super.parseMaybeAssign(...args); } // Either way, we're looking at a '<': tt.jsxTagStart or relational. let arrowExpression; let typeParameters; const state = this.state.clone(); try { // This is similar to TypeScript's `tryParseParenthesizedArrowFunctionExpression`. typeParameters = this.tsParseTypeParameters(); arrowExpression = super.parseMaybeAssign(...args); if (arrowExpression.type !== "ArrowFunctionExpression") { this.unexpected(); // Go to the catch block (needs a SyntaxError). } } catch (err) { if (!(err instanceof SyntaxError)) { // istanbul ignore next: no such error is expected throw err; } if (jsxError) { throw jsxError; } // Try parsing a type cast instead of an arrow function. // This will never happen outside of JSX. // (Because in JSX the '<' should be a jsxTagStart and not a relational. assert(!this.hasPlugin("jsx")); // Parsing an arrow function failed, so try a type cast. this.state = state; // This will start with a type assertion (via parseMaybeUnary). // But don't directly call `this.tsParseTypeAssertion` because we want to handle any binary after it. return super.parseMaybeAssign(...args); } // Correct TypeScript code should have at least 1 type parameter, but don't crash on bad code. if (typeParameters && typeParameters.params.length !== 0) { this.resetStartLocationFromNode(arrowExpression, typeParameters.params[0]); } arrowExpression.typeParameters = typeParameters; return arrowExpression; } // Handle type assertions parseMaybeUnary(refShorthandDefaultPos) { if (!this.hasPlugin("jsx") && this.eatRelational("<")) { return this.tsParseTypeAssertion(); } else { return super.parseMaybeUnary(refShorthandDefaultPos); } } parseArrow(node) { if (this.match(_types.types.colon)) { // This is different from how the TS parser does it. // TS uses lookahead. Babylon parses it as a parenthesized expression and converts. const state = this.state.clone(); try { const returnType = this.tsParseTypeOrTypePredicateAnnotation(_types.types.colon); if (this.canInsertSemicolon()) this.unexpected(); if (!this.match(_types.types.arrow)) this.unexpected(); node.returnType = returnType; } catch (err) { if (err instanceof SyntaxError) { this.state = state; } else { // istanbul ignore next: no such error is expected throw err; } } } return super.parseArrow(node); } // Allow type annotations inside of a parameter list. parseAssignableListItemTypes(param) {