From 5ededb59e9424807664eebafeeb9c8a8e15da9b9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Hu=C3=A1ng=20J=C3=B9nli=C3=A0ng?= Date: Mon, 2 Mar 2020 21:12:59 -0500 Subject: [PATCH] refactor: use error message template in typescript plugin --- packages/babel-parser/src/plugins/flow.js | 1 + .../src/plugins/typescript/index.js | 123 +++++++++--------- 2 files changed, 63 insertions(+), 61 deletions(-) diff --git a/packages/babel-parser/src/plugins/flow.js b/packages/babel-parser/src/plugins/flow.js index 75214dd3a9f7..9eaf3c9851a7 100644 --- a/packages/babel-parser/src/plugins/flow.js +++ b/packages/babel-parser/src/plugins/flow.js @@ -44,6 +44,7 @@ const reservedTypes = new Set([ ]); /* eslint sort-keys: "error" */ +// The Errors key follows https://github.com/facebook/flow/blob/master/src/parser/parse_error.ml unless it does not exist const flowErrors = Object.freeze({ AmbiguousConditionalArrow: "Ambiguous expression: wrap the arrow functions in parentheses to disambiguate.", diff --git a/packages/babel-parser/src/plugins/typescript/index.js b/packages/babel-parser/src/plugins/typescript/index.js index 53c16cfb85cd..d981058a079d 100644 --- a/packages/babel-parser/src/plugins/typescript/index.js +++ b/packages/babel-parser/src/plugins/typescript/index.js @@ -27,6 +27,7 @@ import TypeScriptScopeHandler from "./scope"; import * as charCodes from "charcodes"; import type { ExpressionErrors } from "../../parser/util"; import { PARAM } from "../../util/production-parameter"; +import { Errors } from "../../parser/location"; type TsModifier = | "readonly" @@ -58,6 +59,42 @@ type ParsingContext = | "TypeMembers" | "TypeParametersOrArguments"; +const tsErrors = Object.freeze({ + ClassMethodHasDeclare: "Class methods cannot have the 'declare' modifier", + ClassMethodHasReadonly: "Class methods cannot have the 'readonly' modifier", + DeclareClassFieldHasInitializer: + "'declare' class fields cannot have an initializer", + DuplicateModifier: "Duplicate modifier: '%0'", + EmptyHeritageClauseType: "'%0' list cannot be empty.", + IndexSignatureHasAbstract: + "Index signatures cannot have the 'abstract' modifier", + IndexSignatureHasAccessibility: + "Index signatures cannot have an accessibility modifier ('%0')", + IndexSignatureHasStatic: "Index signatures cannot have the 'static' modifier", + OptionalTypeBeforeRequired: + "A required element cannot follow an optional element.", + PatternIsOptional: + "A binding pattern parameter cannot be optional in an implementation signature.", + PrivateElementHasAbstract: + "Private elements cannot have the 'abstract' modifier.", + PrivateElementHasAccessibility: + "Private elements cannot have an accessibility modifier ('%0')", + TemplateTypeHasSubstitution: + "Template literal types cannot have any substitution", + TypeAnnotationAfterAssign: + "Type annotations must come before default assignments, e.g. instead of `age = 25: number` use `age: number = 25`", + UnexpectedReadonly: + "'readonly' type modifier is only permitted on array and tuple literal types.", + UnexpectedTypeAnnotation: "Did not expect a type annotation here.", + UnexpectedTypeCastInParameter: "Unexpected type cast in parameter position.", + UnsupportedImportTypeArgument: + "Argument in a type import must be a string literal", + UnsupportedParameterPropertyKind: + "A parameter property may not be declared using a binding pattern.", + UnsupportedSignatureParameterKind: + "Name in a signature must be an Identifier, ObjectPattern or ArrayPattern, instead got %0", +}); + // Doesn't handle "void" or "null" because those are keywords, not identifiers. function keywordTypeFromName( value: string, @@ -149,7 +186,7 @@ export default (superClass: Class): Class => if (!modifier) break; if (Object.hasOwnProperty.call(modified, modifier)) { - this.raise(startPos, `Duplicate modifier: '${modifier}'`); + this.raise(startPos, tsErrors.DuplicateModifier, modifier); } modified[modifier] = true; } @@ -263,10 +300,7 @@ export default (superClass: Class): Class => this.expect(tt._import); this.expect(tt.parenL); if (!this.match(tt.string)) { - this.raise( - this.state.start, - "Argument in a type import must be a string literal", - ); + this.raise(this.state.start, tsErrors.UnsupportedImportTypeArgument); } // For compatibility to estree we cannot call parseLiteral directly here @@ -402,8 +436,8 @@ export default (superClass: Class): Class => ) { this.raise( pattern.start, - "Name in a signature must be an Identifier, ObjectPattern or ArrayPattern," + - `instead got ${pattern.type}`, + tsErrors.UnsupportedSignatureParameterKind, + pattern.type, ); } return (pattern: any); @@ -601,10 +635,7 @@ export default (superClass: Class): Class => if (elementNode.type === "TSOptionalType") { seenOptionalElement = true; } else if (seenOptionalElement && elementNode.type !== "TSRestType") { - this.raise( - elementNode.start, - "A required element cannot follow an optional element.", - ); + this.raise(elementNode.start, tsErrors.OptionalTypeBeforeRequired); } }); @@ -678,7 +709,7 @@ export default (superClass: Class): Class => if (templateNode.expressions.length > 0) { this.raise( templateNode.expressions[0].start, - "Template literal types cannot have any substitution", + tsErrors.TemplateTypeHasSubstitution, ); } node.literal = templateNode; @@ -790,10 +821,7 @@ export default (superClass: Class): Class => case "TSArrayType": return; default: - this.raise( - node.start, - "'readonly' type modifier is only permitted on array and tuple literal types.", - ); + this.raise(node.start, tsErrors.UnexpectedReadonly); } } @@ -1031,7 +1059,8 @@ export default (superClass: Class): Class => if (containsEsc) { this.raise( this.state.lastTokStart, - "Escape sequence in keyword asserts", + Errors.InvalidEscapedReservedWord, + "asserts", ); } @@ -1098,7 +1127,7 @@ export default (superClass: Class): Class => ); if (!delimitedList.length) { - this.raise(originalStart, `'${descriptor}' list cannot be empty.`); + this.raise(originalStart, tsErrors.EmptyHeritageClauseType, descriptor); } return delimitedList; @@ -1639,10 +1668,7 @@ export default (superClass: Class): Class => if (accessibility) pp.accessibility = accessibility; if (readonly) pp.readonly = readonly; if (elt.type !== "Identifier" && elt.type !== "AssignmentPattern") { - this.raise( - pp.start, - "A parameter property may not be declared using a binding pattern.", - ); + this.raise(pp.start, tsErrors.UnsupportedParameterPropertyKind); } pp.parameter = ((elt: any): N.Identifier | N.AssignmentPattern); return this.finishNode(pp, "TSParameterProperty"); @@ -1941,23 +1967,16 @@ export default (superClass: Class): Class => classBody.body.push(idx); if ((member: any).abstract) { - this.raise( - member.start, - "Index signatures cannot have the 'abstract' modifier", - ); + this.raise(member.start, tsErrors.IndexSignatureHasAbstract); } if (isStatic) { - this.raise( - member.start, - "Index signatures cannot have the 'static' modifier", - ); + this.raise(member.start, tsErrors.IndexSignatureHasStatic); } if ((member: any).accessibility) { this.raise( member.start, - `Index signatures cannot have an accessibility modifier ('${ - (member: any).accessibility - }')`, + tsErrors.IndexSignatureHasAccessibility, + (member: any).accessibility, ); } @@ -1982,17 +2001,11 @@ export default (superClass: Class): Class => if (optional) methodOrProp.optional = true; if ((methodOrProp: any).readonly && this.match(tt.parenL)) { - this.raise( - methodOrProp.start, - "Class methods cannot have the 'readonly' modifier", - ); + this.raise(methodOrProp.start, tsErrors.ClassMethodHasReadonly); } if ((methodOrProp: any).declare && this.match(tt.parenL)) { - this.raise( - methodOrProp.start, - "Class methods cannot have the 'declare' modifier", - ); + this.raise(methodOrProp.start, tsErrors.ClassMethodHasDeclare); } } @@ -2142,10 +2155,7 @@ export default (superClass: Class): Class => this.parseClassPropertyAnnotation(node); if (node.declare && this.match(tt.equal)) { - this.raise( - this.state.start, - "'declare' class fields cannot have an initializer", - ); + this.raise(this.state.start, tsErrors.DeclareClassFieldHasInitializer); } return super.parseClassProperty(node); @@ -2156,17 +2166,15 @@ export default (superClass: Class): Class => ): N.ClassPrivateProperty { // $FlowIgnore if (node.abstract) { - this.raise( - node.start, - "Private elements cannot have the 'abstract' modifier.", - ); + this.raise(node.start, tsErrors.PrivateElementHasAbstract); } // $FlowIgnore if (node.accessibility) { this.raise( node.start, - `Private elements cannot have an accessibility modifier ('${node.accessibility}')`, + tsErrors.PrivateElementHasAccessibility, + node.accessibility, ); } @@ -2389,10 +2397,7 @@ export default (superClass: Class): Class => parseAssignableListItemTypes(param: N.Pattern) { if (this.eat(tt.question)) { if (param.type !== "Identifier") { - this.raise( - param.start, - "A binding pattern parameter cannot be optional in an implementation signature.", - ); + this.raise(param.start, tsErrors.PatternIsOptional); } ((param: any): N.Identifier).optional = true; @@ -2507,8 +2512,7 @@ export default (superClass: Class): Class => ) { this.raise( node.typeAnnotation.start, - "Type annotations must come before default assignments, " + - "e.g. instead of `age = 25: number` use `age: number = 25`", + tsErrors.TypeAnnotationAfterAssign, ); } @@ -2537,10 +2541,7 @@ export default (superClass: Class): Class => if (!this.state.maybeInArrowParameters) { exprList[i] = this.typeCastToParameter(expr); } else { - this.raise( - expr.start, - "Unexpected type cast in parameter position.", - ); + this.raise(expr.start, tsErrors.UnexpectedTypeCastInParameter); } break; } @@ -2567,7 +2568,7 @@ export default (superClass: Class): Class => for (let i = 0; i < exprList.length; i++) { const expr = exprList[i]; if (expr && expr.type === "TSTypeCastExpression") { - this.raise(expr.start, "Did not expect a type annotation here."); + this.raise(expr.start, tsErrors.UnexpectedTypeAnnotation); } }