Flow enums parsing (#10344)

* Flow enums parsing

* Parse exporting enums

* Enums parsing remove lookahead, other improvements

* Enums: add EnumBody and EnumMember aliases, change boolean members to use BooleaLiteral value

* Fix enum member init flow type, now that boolean members have a BooleanLiteral value

* Flow enums: use contextual utils, change members length checks to use logic operators, remove reserved word logic modification

* Flow enums: remove unnecessary code in generator, fix error message
This commit is contained in:
George Zahariev 2019-10-29 14:55:12 -07:00 committed by Nicolò Ribaudo
parent 4b3a19ea9f
commit ec3345bb57
92 changed files with 3213 additions and 5 deletions

View File

@ -134,6 +134,92 @@ export function DeclareExportAllDeclaration(/*node: Object*/) {
ExportAllDeclaration.apply(this, arguments); ExportAllDeclaration.apply(this, arguments);
} }
export function EnumDeclaration(node: Object) {
const { id, body } = node;
this.word("enum");
this.space();
this.print(id, node);
this.print(body, node);
}
function enumExplicitType(
context: Object,
name: string,
hasExplicitType: boolean,
) {
if (hasExplicitType) {
context.space();
context.word("of");
context.space();
context.word(name);
}
context.space();
}
function enumBody(context: Object, node: Object) {
const { members } = node;
context.token("{");
context.indent();
context.newline();
for (const member of members) {
context.print(member, node);
context.newline();
}
context.dedent();
context.token("}");
}
export function EnumBooleanBody(node: Object) {
const { explicitType } = node;
enumExplicitType(this, "boolean", explicitType);
enumBody(this, node);
}
export function EnumNumberBody(node: Object) {
const { explicitType } = node;
enumExplicitType(this, "number", explicitType);
enumBody(this, node);
}
export function EnumStringBody(node: Object) {
const { explicitType } = node;
enumExplicitType(this, "string", explicitType);
enumBody(this, node);
}
export function EnumSymbolBody(node: Object) {
enumExplicitType(this, "symbol", true);
enumBody(this, node);
}
export function EnumDefaultedMember(node: Object) {
const { id } = node;
this.print(id, node);
this.token(",");
}
function enumInitializedMember(context: Object, node: Object) {
const { id, init } = node;
context.print(id, node);
context.space();
context.token("=");
context.space();
context.print(init, node);
context.token(",");
}
export function EnumBooleanMember(node: Object) {
enumInitializedMember(this, node);
}
export function EnumNumberMember(node: Object) {
enumInitializedMember(this, node);
}
export function EnumStringMember(node: Object) {
enumInitializedMember(this, node);
}
function FlowExportDeclaration(node: Object) { function FlowExportDeclaration(node: Object) {
if (node.declaration) { if (node.declaration) {
const declar = node.declaration; const declar = node.declaration;

View File

@ -0,0 +1,36 @@
enum E {
A = true,
B = false,
}
enum E of boolean {
A = true,
B = false,
}
enum E {
A = 1,
B = 2,
}
enum E of number {
A = 1,
B = 2,
}
enum E {
A,
B,
}
enum E of string {
A,
B,
}
enum E {
A = "a",
B = "b",
}
enum E of string {
A = "a",
B = "b",
}
enum E of symbol {
A,
B,
}

View File

@ -0,0 +1,3 @@
{
"plugins": [["flow", { "enums": true }]]
}

View File

@ -0,0 +1,36 @@
enum E {
A = true,
B = false,
}
enum E of boolean {
A = true,
B = false,
}
enum E {
A = 1,
B = 2,
}
enum E of number {
A = 1,
B = 2,
}
enum E {
A,
B,
}
enum E of string {
A,
B,
}
enum E {
A = "a",
B = "b",
}
enum E of string {
A = "a",
B = "b",
}
enum E of symbol {
A,
B,
}

View File

@ -81,6 +81,20 @@ function partition<T>(
const FLOW_PRAGMA_REGEX = /\*?\s*@((?:no)?flow)\b/; const FLOW_PRAGMA_REGEX = /\*?\s*@((?:no)?flow)\b/;
// Flow enums types
type EnumExplicitType = null | "boolean" | "number" | "string" | "symbol";
type EnumContext = {|
enumName: string,
explicitType: EnumExplicitType,
memberName: string,
|};
type EnumMemberInit =
| {| type: "number", pos: number, value: N.Node |}
| {| type: "string", pos: number, value: N.Node |}
| {| type: "boolean", pos: number, value: N.Node |}
| {| type: "invalid", pos: number |}
| {| type: "none", pos: number |};
export default (superClass: Class<Parser>): Class<Parser> => export default (superClass: Class<Parser>): Class<Parser> =>
class extends superClass { class extends superClass {
// The value of the @flow/@noflow pragma. Initially undefined, transitions // The value of the @flow/@noflow pragma. Initially undefined, transitions
@ -97,6 +111,10 @@ export default (superClass: Class<Parser>): Class<Parser> =>
return this.getPluginOption("flow", "all") || this.flowPragma === "flow"; return this.getPluginOption("flow", "all") || this.flowPragma === "flow";
} }
shouldParseEnums(): boolean {
return !!this.getPluginOption("flow", "enums");
}
finishToken(type: TokenType, val: any): void { finishToken(type: TokenType, val: any): void {
if ( if (
type !== tt.string && type !== tt.string &&
@ -1604,7 +1622,7 @@ export default (superClass: Class<Parser>): Class<Parser> =>
super.parseFunctionBodyAndFinish(node, type, isMethod); super.parseFunctionBodyAndFinish(node, type, isMethod);
} }
// interfaces // interfaces and enums
parseStatement(context: ?string, topLevel?: boolean): N.Statement { parseStatement(context: ?string, topLevel?: boolean): N.Statement {
// strict mode handling of `interface` since it's a reserved word // strict mode handling of `interface` since it's a reserved word
if ( if (
@ -1615,6 +1633,10 @@ export default (superClass: Class<Parser>): Class<Parser> =>
const node = this.startNode(); const node = this.startNode();
this.next(); this.next();
return this.flowParseInterface(node); return this.flowParseInterface(node);
} else if (this.shouldParseEnums() && this.isContextual("enum")) {
const node = this.startNode();
this.next();
return this.flowParseEnumDeclaration(node);
} else { } else {
const stmt = super.parseStatement(context, topLevel); const stmt = super.parseStatement(context, topLevel);
// We will parse a flow pragma in any comment before the first statement. // We will parse a flow pragma in any comment before the first statement.
@ -1661,6 +1683,7 @@ export default (superClass: Class<Parser>): Class<Parser> =>
this.isContextual("type") || this.isContextual("type") ||
this.isContextual("interface") || this.isContextual("interface") ||
this.isContextual("opaque") || this.isContextual("opaque") ||
(this.shouldParseEnums() && this.isContextual("enum")) ||
super.shouldParseExportDeclaration() super.shouldParseExportDeclaration()
); );
} }
@ -1670,7 +1693,8 @@ export default (superClass: Class<Parser>): Class<Parser> =>
this.match(tt.name) && this.match(tt.name) &&
(this.state.value === "type" || (this.state.value === "type" ||
this.state.value === "interface" || this.state.value === "interface" ||
this.state.value === "opaque") this.state.value === "opaque" ||
(this.shouldParseEnums() && this.state.value === "enum"))
) { ) {
return false; return false;
} }
@ -1678,6 +1702,15 @@ export default (superClass: Class<Parser>): Class<Parser> =>
return super.isExportDefaultSpecifier(); return super.isExportDefaultSpecifier();
} }
parseExportDefaultExpression(): N.Expression | N.Declaration {
if (this.shouldParseEnums() && this.isContextual("enum")) {
const node = this.startNode();
this.next();
return this.flowParseEnumDeclaration(node);
}
return super.parseExportDefaultExpression();
}
parseConditional( parseConditional(
expr: N.Expression, expr: N.Expression,
noIn: ?boolean, noIn: ?boolean,
@ -1935,6 +1968,11 @@ export default (superClass: Class<Parser>): Class<Parser> =>
const declarationNode = this.startNode(); const declarationNode = this.startNode();
this.next(); this.next();
return this.flowParseInterface(declarationNode); return this.flowParseInterface(declarationNode);
} else if (this.shouldParseEnums() && this.isContextual("enum")) {
node.exportKind = "value";
const declarationNode = this.startNode();
this.next();
return this.flowParseEnumDeclaration(declarationNode);
} else { } else {
return super.parseExportDeclaration(node); return super.parseExportDeclaration(node);
} }
@ -2839,4 +2877,418 @@ export default (superClass: Class<Parser>): Class<Parser> =>
this.raise(this.state.pos, "Unterminated comment"); this.raise(this.state.pos, "Unterminated comment");
} }
} }
// Flow enum parsing
flowEnumErrorBooleanMemberNotInitialized(
pos: number,
{ enumName, memberName }: { enumName: string, memberName: string },
): void {
this.raise(
pos,
`Boolean enum members need to be initialized. Use either \`${memberName} = true,\` ` +
`or \`${memberName} = false,\` in enum \`${enumName}\`.`,
);
}
flowEnumErrorInvalidMemberName(
pos: number,
{ enumName, memberName }: { enumName: string, memberName: string },
): void {
const suggestion = memberName[0].toUpperCase() + memberName.slice(1);
this.raise(
pos,
`Enum member names cannot start with lowercase 'a' through 'z'. Instead of using ` +
`\`${memberName}\`, consider using \`${suggestion}\`, in enum \`${enumName}\`.`,
);
}
flowEnumErrorDuplicateMemberName(
pos: number,
{ enumName, memberName }: { enumName: string, memberName: string },
): void {
this.raise(
pos,
`Enum member names need to be unique, but the name \`${memberName}\` has already been used ` +
`before in enum \`${enumName}\`.`,
);
}
flowEnumErrorInconsistentMemberValues(
pos: number,
{ enumName }: { enumName: string },
): void {
this.raise(
pos,
`Enum \`${enumName}\` has inconsistent member initializers. Either use no initializers, or ` +
`consistently use literals (either booleans, numbers, or strings) for all member initializers.`,
);
}
flowEnumErrorInvalidExplicitType(
pos: number,
{
enumName,
suppliedType,
}: { enumName: string, suppliedType: null | string },
): void {
const suggestion =
`Use one of \`boolean\`, \`number\`, \`string\`, or \`symbol\` in ` +
`enum \`${enumName}\`.`;
const message =
suppliedType === null
? `Supplied enum type is not valid. ${suggestion}`
: `Enum type \`${suppliedType}\` is not valid. ${suggestion}`;
this.raise(pos, message);
}
flowEnumErrorInvalidMemberInitializer(
pos: number,
{ enumName, explicitType, memberName }: EnumContext,
): void {
let message = null;
switch (explicitType) {
case "boolean":
case "number":
case "string":
message =
`Enum \`${enumName}\` has type \`${explicitType}\`, so the initializer of ` +
`\`${memberName}\` needs to be a ${explicitType} literal.`;
break;
case "symbol":
message =
`Symbol enum members cannot be initialized. Use \`${memberName},\` in ` +
`enum \`${enumName}\`.`;
break;
default:
// null
message =
`The enum member initializer for \`${memberName}\` needs to be a literal (either ` +
`a boolean, number, or string) in enum \`${enumName}\`.`;
}
this.raise(pos, message);
}
flowEnumErrorNumberMemberNotInitialized(
pos: number,
{ enumName, memberName }: { enumName: string, memberName: string },
): void {
this.raise(
pos,
`Number enum members need to be initialized, e.g. \`${memberName} = 1\` in enum \`${enumName}\`.`,
);
}
flowEnumErrorStringMemberInconsistentlyInitailized(
pos: number,
{ enumName }: { enumName: string },
): void {
this.raise(
pos,
`String enum members need to consistently either all use initializers, or use no initializers, ` +
`in enum \`${enumName}\`.`,
);
}
flowEnumMemberInit(): EnumMemberInit {
const startPos = this.state.start;
const endOfInit = () => this.match(tt.comma) || this.match(tt.braceR);
switch (this.state.type) {
case tt.num: {
const literal = this.parseLiteral(this.state.value, "NumericLiteral");
if (endOfInit()) {
return { type: "number", pos: literal.start, value: literal };
}
return { type: "invalid", pos: startPos };
}
case tt.string: {
const literal = this.parseLiteral(this.state.value, "StringLiteral");
if (endOfInit()) {
return { type: "string", pos: literal.start, value: literal };
}
return { type: "invalid", pos: startPos };
}
case tt._true:
case tt._false: {
const literal = this.parseBooleanLiteral();
if (endOfInit()) {
return {
type: "boolean",
pos: literal.start,
value: literal,
};
}
return { type: "invalid", pos: startPos };
}
default:
return { type: "invalid", pos: startPos };
}
}
flowEnumMemberRaw(): { id: N.Node, init: EnumMemberInit } {
const pos = this.state.start;
const id = this.parseIdentifier(true);
const init = this.eat(tt.eq)
? this.flowEnumMemberInit()
: { type: "none", pos };
return { id, init };
}
flowEnumCheckExplicitTypeMismatch(
pos: number,
context: EnumContext,
expectedType: EnumExplicitType,
): void {
const { explicitType } = context;
if (explicitType === null) {
return;
}
if (explicitType !== expectedType) {
this.flowEnumErrorInvalidMemberInitializer(pos, context);
}
}
flowEnumMembers({
enumName,
explicitType,
}: {
enumName: string,
explicitType: EnumExplicitType,
}): {|
booleanMembers: Array<N.Node>,
numberMembers: Array<N.Node>,
stringMembers: Array<N.Node>,
defaultedMembers: Array<N.Node>,
|} {
const seenNames = new Set();
const members = {
booleanMembers: [],
numberMembers: [],
stringMembers: [],
defaultedMembers: [],
};
while (!this.match(tt.braceR)) {
const memberNode = this.startNode();
const { id, init } = this.flowEnumMemberRaw();
const memberName = id.name;
if (memberName === "") {
continue;
}
if (/^[a-z]/.test(memberName)) {
this.flowEnumErrorInvalidMemberName(id.start, {
enumName,
memberName,
});
}
if (seenNames.has(memberName)) {
this.flowEnumErrorDuplicateMemberName(id.start, {
enumName,
memberName,
});
}
seenNames.add(memberName);
const context = { enumName, explicitType, memberName };
memberNode.id = id;
switch (init.type) {
case "boolean": {
this.flowEnumCheckExplicitTypeMismatch(
init.pos,
context,
"boolean",
);
memberNode.init = init.value;
members.booleanMembers.push(
this.finishNode(memberNode, "EnumBooleanMember"),
);
break;
}
case "number": {
this.flowEnumCheckExplicitTypeMismatch(init.pos, context, "number");
memberNode.init = init.value;
members.numberMembers.push(
this.finishNode(memberNode, "EnumNumberMember"),
);
break;
}
case "string": {
this.flowEnumCheckExplicitTypeMismatch(init.pos, context, "string");
memberNode.init = init.value;
members.stringMembers.push(
this.finishNode(memberNode, "EnumStringMember"),
);
break;
}
case "invalid": {
this.flowEnumErrorInvalidMemberInitializer(init.pos, context);
break;
}
case "none": {
switch (explicitType) {
case "boolean":
this.flowEnumErrorBooleanMemberNotInitialized(
init.pos,
context,
);
break;
case "number":
this.flowEnumErrorNumberMemberNotInitialized(init.pos, context);
break;
default:
members.defaultedMembers.push(
this.finishNode(memberNode, "EnumDefaultedMember"),
);
}
}
}
if (!this.match(tt.braceR)) {
this.expect(tt.comma);
}
}
return members;
}
flowEnumStringBody(
bodyNode: N.Node,
initializedMembers: Array<N.Node>,
defaultedMembers: Array<N.Node>,
{ enumName }: { enumName: string },
): N.Node {
if (initializedMembers.length === 0) {
bodyNode.members = defaultedMembers;
} else if (defaultedMembers.length === 0) {
bodyNode.members = initializedMembers;
} else if (defaultedMembers.length > initializedMembers.length) {
bodyNode.members = defaultedMembers;
for (const member of initializedMembers) {
this.flowEnumErrorStringMemberInconsistentlyInitailized(
member.start,
{ enumName },
);
}
} else {
bodyNode.members = initializedMembers;
for (const member of defaultedMembers) {
this.flowEnumErrorStringMemberInconsistentlyInitailized(
member.start,
{ enumName },
);
}
}
return this.finishNode(bodyNode, "EnumStringBody");
}
flowEnumParseExplicitType({
enumName,
}: {
enumName: string,
}): EnumExplicitType {
if (this.eatContextual("of")) {
if (this.match(tt.name)) {
switch (this.state.value) {
case "boolean":
case "number":
case "string":
case "symbol": {
const explicitType = this.state.value;
this.next();
return explicitType;
}
default:
this.flowEnumErrorInvalidExplicitType(this.state.start, {
enumName,
suppliedType: this.state.value,
});
}
} else {
this.flowEnumErrorInvalidExplicitType(this.state.start, {
enumName,
suppliedType: null,
});
}
}
return null;
}
flowParseEnumDeclaration(node: N.Node): N.Node {
const id = this.parseIdentifier();
node.id = id;
const enumName = id.name;
const explicitType = this.flowEnumParseExplicitType({ enumName });
this.expect(tt.braceL);
const bodyNode = this.startNode();
const members = this.flowEnumMembers({ enumName, explicitType });
switch (explicitType) {
case "boolean":
bodyNode.explicitType = true;
bodyNode.members = members.booleanMembers;
node.body = this.finishNode(bodyNode, "EnumBooleanBody");
break;
case "number":
bodyNode.explicitType = true;
bodyNode.members = members.numberMembers;
node.body = this.finishNode(bodyNode, "EnumNumberBody");
break;
case "string":
bodyNode.explicitType = true;
node.body = this.flowEnumStringBody(
bodyNode,
members.stringMembers,
members.defaultedMembers,
{ enumName },
);
break;
case "symbol":
bodyNode.members = members.defaultedMembers;
node.body = this.finishNode(bodyNode, "EnumSymbolBody");
break;
default: {
// null
const empty = () => {
bodyNode.members = [];
return this.finishNode(bodyNode, "EnumStringBody");
};
bodyNode.explicitType = false;
const boolsLen = members.booleanMembers.length;
const numsLen = members.numberMembers.length;
const strsLen = members.stringMembers.length;
const defaultedLen = members.defaultedMembers.length;
if (!boolsLen && !numsLen && !strsLen && !defaultedLen) {
node.body = empty();
} else if (!boolsLen && !numsLen) {
node.body = this.flowEnumStringBody(
bodyNode,
members.stringMembers,
members.defaultedMembers,
{ enumName },
);
} else if (!numsLen && !strsLen && boolsLen >= defaultedLen) {
bodyNode.members = members.booleanMembers;
node.body = this.finishNode(bodyNode, "EnumBooleanBody");
for (const member of members.defaultedMembers) {
this.flowEnumErrorBooleanMemberNotInitialized(member.start, {
enumName,
memberName: member.id.name,
});
}
} else if (!boolsLen && !strsLen && numsLen >= defaultedLen) {
bodyNode.members = members.numberMembers;
node.body = this.finishNode(bodyNode, "EnumNumberBody");
for (const member of members.defaultedMembers) {
this.flowEnumErrorNumberMemberNotInitialized(member.start, {
enumName,
memberName: member.id.name,
});
}
} else {
node.body = empty();
this.flowEnumErrorInconsistentMemberValues(id.start, { enumName });
}
}
}
this.expect(tt.braceR);
return this.finishNode(node, "EnumDeclaration");
}
}; };

View File

@ -0,0 +1,4 @@
enum E of boolean {
A = true,
B = false,
}

View File

@ -0,0 +1,181 @@
{
"type": "File",
"start": 0,
"end": 46,
"loc": {
"start": {
"line": 1,
"column": 0
},
"end": {
"line": 4,
"column": 1
}
},
"program": {
"type": "Program",
"start": 0,
"end": 46,
"loc": {
"start": {
"line": 1,
"column": 0
},
"end": {
"line": 4,
"column": 1
}
},
"sourceType": "script",
"interpreter": null,
"body": [
{
"type": "EnumDeclaration",
"start": 0,
"end": 46,
"loc": {
"start": {
"line": 1,
"column": 0
},
"end": {
"line": 4,
"column": 1
}
},
"id": {
"type": "Identifier",
"start": 5,
"end": 6,
"loc": {
"start": {
"line": 1,
"column": 5
},
"end": {
"line": 1,
"column": 6
},
"identifierName": "E"
},
"name": "E"
},
"body": {
"type": "EnumBooleanBody",
"start": 22,
"end": 44,
"loc": {
"start": {
"line": 2,
"column": 2
},
"end": {
"line": 3,
"column": 12
}
},
"explicitType": true,
"members": [
{
"type": "EnumBooleanMember",
"start": 22,
"end": 30,
"loc": {
"start": {
"line": 2,
"column": 2
},
"end": {
"line": 2,
"column": 10
}
},
"id": {
"type": "Identifier",
"start": 22,
"end": 23,
"loc": {
"start": {
"line": 2,
"column": 2
},
"end": {
"line": 2,
"column": 3
},
"identifierName": "A"
},
"name": "A"
},
"init": {
"type": "BooleanLiteral",
"start": 26,
"end": 30,
"loc": {
"start": {
"line": 2,
"column": 6
},
"end": {
"line": 2,
"column": 10
}
},
"value": true
}
},
{
"type": "EnumBooleanMember",
"start": 34,
"end": 43,
"loc": {
"start": {
"line": 3,
"column": 2
},
"end": {
"line": 3,
"column": 11
}
},
"id": {
"type": "Identifier",
"start": 34,
"end": 35,
"loc": {
"start": {
"line": 3,
"column": 2
},
"end": {
"line": 3,
"column": 3
},
"identifierName": "B"
},
"name": "B"
},
"init": {
"type": "BooleanLiteral",
"start": 38,
"end": 43,
"loc": {
"start": {
"line": 3,
"column": 6
},
"end": {
"line": 3,
"column": 11
}
},
"value": false
}
}
]
}
}
],
"directives": []
}
}

View File

@ -0,0 +1,4 @@
enum E {
A = true,
B = false,
}

View File

@ -0,0 +1,181 @@
{
"type": "File",
"start": 0,
"end": 35,
"loc": {
"start": {
"line": 1,
"column": 0
},
"end": {
"line": 4,
"column": 1
}
},
"program": {
"type": "Program",
"start": 0,
"end": 35,
"loc": {
"start": {
"line": 1,
"column": 0
},
"end": {
"line": 4,
"column": 1
}
},
"sourceType": "script",
"interpreter": null,
"body": [
{
"type": "EnumDeclaration",
"start": 0,
"end": 35,
"loc": {
"start": {
"line": 1,
"column": 0
},
"end": {
"line": 4,
"column": 1
}
},
"id": {
"type": "Identifier",
"start": 5,
"end": 6,
"loc": {
"start": {
"line": 1,
"column": 5
},
"end": {
"line": 1,
"column": 6
},
"identifierName": "E"
},
"name": "E"
},
"body": {
"type": "EnumBooleanBody",
"start": 11,
"end": 33,
"loc": {
"start": {
"line": 2,
"column": 2
},
"end": {
"line": 3,
"column": 12
}
},
"explicitType": false,
"members": [
{
"type": "EnumBooleanMember",
"start": 11,
"end": 19,
"loc": {
"start": {
"line": 2,
"column": 2
},
"end": {
"line": 2,
"column": 10
}
},
"id": {
"type": "Identifier",
"start": 11,
"end": 12,
"loc": {
"start": {
"line": 2,
"column": 2
},
"end": {
"line": 2,
"column": 3
},
"identifierName": "A"
},
"name": "A"
},
"init": {
"type": "BooleanLiteral",
"start": 15,
"end": 19,
"loc": {
"start": {
"line": 2,
"column": 6
},
"end": {
"line": 2,
"column": 10
}
},
"value": true
}
},
{
"type": "EnumBooleanMember",
"start": 23,
"end": 32,
"loc": {
"start": {
"line": 3,
"column": 2
},
"end": {
"line": 3,
"column": 11
}
},
"id": {
"type": "Identifier",
"start": 23,
"end": 24,
"loc": {
"start": {
"line": 3,
"column": 2
},
"end": {
"line": 3,
"column": 3
},
"identifierName": "B"
},
"name": "B"
},
"init": {
"type": "BooleanLiteral",
"start": 27,
"end": 32,
"loc": {
"start": {
"line": 3,
"column": 6
},
"end": {
"line": 3,
"column": 11
}
},
"value": false
}
}
]
}
}
],
"directives": []
}
}

View File

@ -0,0 +1,3 @@
enum E of boolean {
A,
}

View File

@ -0,0 +1,4 @@
{
"plugins": [["flow", { "enums": true }]],
"throws": "Boolean enum members need to be initialized. Use either `A = true,` or `A = false,` in enum `E`. (2:2)"
}

View File

@ -0,0 +1,4 @@
enum E {
A,
B = true,
}

View File

@ -0,0 +1,4 @@
{
"plugins": [["flow", { "enums": true }]],
"throws": "Boolean enum members need to be initialized. Use either `A = true,` or `A = false,` in enum `E`. (2:2)"
}

View File

@ -0,0 +1,4 @@
enum E {
A,
A,
}

View File

@ -0,0 +1,4 @@
{
"plugins": [["flow", { "enums": true }]],
"throws": "Enum member names need to be unique, but the name `A` has already been used before in enum `E`. (3:2)"
}

View File

@ -0,0 +1 @@
enum E { }

View File

@ -0,0 +1,84 @@
{
"type": "File",
"start": 0,
"end": 10,
"loc": {
"start": {
"line": 1,
"column": 0
},
"end": {
"line": 1,
"column": 10
}
},
"program": {
"type": "Program",
"start": 0,
"end": 10,
"loc": {
"start": {
"line": 1,
"column": 0
},
"end": {
"line": 1,
"column": 10
}
},
"sourceType": "script",
"interpreter": null,
"body": [
{
"type": "EnumDeclaration",
"start": 0,
"end": 10,
"loc": {
"start": {
"line": 1,
"column": 0
},
"end": {
"line": 1,
"column": 10
}
},
"id": {
"type": "Identifier",
"start": 5,
"end": 6,
"loc": {
"start": {
"line": 1,
"column": 5
},
"end": {
"line": 1,
"column": 6
},
"identifierName": "E"
},
"name": "E"
},
"body": {
"type": "EnumStringBody",
"start": 9,
"end": 8,
"loc": {
"start": {
"line": 1,
"column": 9
},
"end": {
"line": 1,
"column": 8
}
},
"explicitType": false,
"members": []
}
}
],
"directives": []
}
}

View File

@ -0,0 +1 @@
enum

View File

@ -0,0 +1,4 @@
{
"plugins": [["flow", { "enums": true }]],
"throws": "Unexpected token (1:4)"
}

View File

@ -0,0 +1,2 @@
enum type {
}

View File

@ -0,0 +1,84 @@
{
"type": "File",
"start": 0,
"end": 13,
"loc": {
"start": {
"line": 1,
"column": 0
},
"end": {
"line": 2,
"column": 1
}
},
"program": {
"type": "Program",
"start": 0,
"end": 13,
"loc": {
"start": {
"line": 1,
"column": 0
},
"end": {
"line": 2,
"column": 1
}
},
"sourceType": "script",
"interpreter": null,
"body": [
{
"type": "EnumDeclaration",
"start": 0,
"end": 13,
"loc": {
"start": {
"line": 1,
"column": 0
},
"end": {
"line": 2,
"column": 1
}
},
"id": {
"type": "Identifier",
"start": 5,
"end": 9,
"loc": {
"start": {
"line": 1,
"column": 5
},
"end": {
"line": 1,
"column": 9
},
"identifierName": "type"
},
"name": "type"
},
"body": {
"type": "EnumStringBody",
"start": 12,
"end": 11,
"loc": {
"start": {
"line": 2,
"column": 0
},
"end": {
"line": 1,
"column": 11
}
},
"explicitType": false,
"members": []
}
}
],
"directives": []
}
}

View File

@ -0,0 +1,3 @@
export enum A {}
export default enum B {}

View File

@ -0,0 +1,11 @@
{
"plugins": [
[
"flow",
{
"enums": true
}
]
],
"sourceType": "module"
}

View File

@ -0,0 +1,166 @@
{
"type": "File",
"start": 0,
"end": 42,
"loc": {
"start": {
"line": 1,
"column": 0
},
"end": {
"line": 3,
"column": 24
}
},
"program": {
"type": "Program",
"start": 0,
"end": 42,
"loc": {
"start": {
"line": 1,
"column": 0
},
"end": {
"line": 3,
"column": 24
}
},
"sourceType": "module",
"interpreter": null,
"body": [
{
"type": "ExportNamedDeclaration",
"start": 0,
"end": 16,
"loc": {
"start": {
"line": 1,
"column": 0
},
"end": {
"line": 1,
"column": 16
}
},
"specifiers": [],
"source": null,
"exportKind": "value",
"declaration": {
"type": "EnumDeclaration",
"start": 7,
"end": 16,
"loc": {
"start": {
"line": 1,
"column": 7
},
"end": {
"line": 1,
"column": 16
}
},
"id": {
"type": "Identifier",
"start": 12,
"end": 13,
"loc": {
"start": {
"line": 1,
"column": 12
},
"end": {
"line": 1,
"column": 13
},
"identifierName": "A"
},
"name": "A"
},
"body": {
"type": "EnumStringBody",
"start": 15,
"end": 15,
"loc": {
"start": {
"line": 1,
"column": 15
},
"end": {
"line": 1,
"column": 15
}
},
"explicitType": false,
"members": []
}
}
},
{
"type": "ExportDefaultDeclaration",
"start": 18,
"end": 42,
"loc": {
"start": {
"line": 3,
"column": 0
},
"end": {
"line": 3,
"column": 24
}
},
"declaration": {
"type": "EnumDeclaration",
"start": 33,
"end": 42,
"loc": {
"start": {
"line": 3,
"column": 15
},
"end": {
"line": 3,
"column": 24
}
},
"id": {
"type": "Identifier",
"start": 38,
"end": 39,
"loc": {
"start": {
"line": 3,
"column": 20
},
"end": {
"line": 3,
"column": 21
},
"identifierName": "B"
},
"name": "B"
},
"body": {
"type": "EnumStringBody",
"start": 41,
"end": 41,
"loc": {
"start": {
"line": 3,
"column": 23
},
"end": {
"line": 3,
"column": 23
}
},
"explicitType": false,
"members": []
}
}
}
],
"directives": []
}
}

View File

@ -0,0 +1,5 @@
enum E {
A,
B,
C = 3,
}

View File

@ -0,0 +1,4 @@
{
"plugins": [["flow", { "enums": true }]],
"throws": "Enum `E` has inconsistent member initializers. Either use no initializers, or consistently use literals (either booleans, numbers, or strings) for all member initializers. (1:5)"
}

View File

@ -0,0 +1,4 @@
enum E {
A = 1,
B = true,
}

View File

@ -0,0 +1,4 @@
{
"plugins": [["flow", { "enums": true }]],
"throws": "Enum `E` has inconsistent member initializers. Either use no initializers, or consistently use literals (either booleans, numbers, or strings) for all member initializers. (1:5)"
}

View File

@ -0,0 +1,2 @@
enum E of [] {
}

View File

@ -0,0 +1,4 @@
{
"plugins": [["flow", { "enums": true }]],
"throws": "Supplied enum type is not valid. Use one of `boolean`, `number`, `string`, or `symbol` in enum `E`. (1:10)"
}

View File

@ -0,0 +1,2 @@
enum E of xxx {
}

View File

@ -0,0 +1,4 @@
{
"plugins": [["flow", { "enums": true }]],
"throws": "Enum type `xxx` is not valid. Use one of `boolean`, `number`, `string`, or `symbol` in enum `E`. (1:10)"
}

View File

@ -0,0 +1,3 @@
enum E of string {
A = true,
}

View File

@ -0,0 +1,4 @@
{
"plugins": [["flow", { "enums": true }]],
"throws": "Enum `E` has type `string`, so the initializer of `A` needs to be a string literal. (2:6)"
}

View File

@ -0,0 +1,3 @@
enum E of boolean {
A = 1 + 2,
}

View File

@ -0,0 +1,4 @@
{
"plugins": [["flow", { "enums": true }]],
"throws": "Enum `E` has type `boolean`, so the initializer of `A` needs to be a boolean literal. (2:6)"
}

View File

@ -0,0 +1,3 @@
enum E of number {
A = 1 + 2,
}

View File

@ -0,0 +1,4 @@
{
"plugins": [["flow", { "enums": true }]],
"throws": "Enum `E` has type `number`, so the initializer of `A` needs to be a number literal. (2:6)"
}

View File

@ -0,0 +1,3 @@
enum E of string {
A = 1 + 2,
}

View File

@ -0,0 +1,4 @@
{
"plugins": [["flow", { "enums": true }]],
"throws": "Enum `E` has type `string`, so the initializer of `A` needs to be a string literal. (2:6)"
}

View File

@ -0,0 +1,3 @@
enum E of symbol {
A = 1 + 2,
}

View File

@ -0,0 +1,4 @@
{
"plugins": [["flow", { "enums": true }]],
"throws": "Symbol enum members cannot be initialized. Use `A,` in enum `E`. (2:6)"
}

View File

@ -0,0 +1,4 @@
{
"plugins": [["flow", { "enums": true }]],
"throws": "The enum member initializer for `A` needs to be a literal (either a boolean, number, or string) in enum `E`. (2:6)"
}

View File

@ -0,0 +1,3 @@
enum E of symbol {
A = 1,
}

View File

@ -0,0 +1,4 @@
{
"plugins": [["flow", { "enums": true }]],
"throws": "Symbol enum members cannot be initialized. Use `A,` in enum `E`. (2:6)"
}

View File

@ -0,0 +1,3 @@
enum E of boolean {
A = 1,
}

View File

@ -0,0 +1,4 @@
{
"plugins": [["flow", { "enums": true }]],
"throws": "Enum `E` has type `boolean`, so the initializer of `A` needs to be a boolean literal. (2:6)"
}

View File

@ -0,0 +1,3 @@
enum E of string {
A = 1,
}

View File

@ -0,0 +1,4 @@
{
"plugins": [["flow", { "enums": true }]],
"throws": "Enum `E` has type `string`, so the initializer of `A` needs to be a string literal. (2:6)"
}

View File

@ -0,0 +1,4 @@
{
"plugins": [["flow", { "enums": true }]],
"throws": "The enum member initializer for `A` needs to be a literal (either a boolean, number, or string) in enum `E`. (2:6)"
}

View File

@ -0,0 +1,3 @@
enum E of boolean {
A = "hi",
}

View File

@ -0,0 +1,4 @@
{
"plugins": [["flow", { "enums": true }]],
"throws": "Enum `E` has type `boolean`, so the initializer of `A` needs to be a boolean literal. (2:6)"
}

View File

@ -0,0 +1,3 @@
enum E of number {
A = "hi",
}

View File

@ -0,0 +1,4 @@
{
"plugins": [["flow", { "enums": true }]],
"throws": "Enum `E` has type `number`, so the initializer of `A` needs to be a number literal. (2:6)"
}

View File

@ -0,0 +1,4 @@
enum E {
foo,
bar,
}

View File

@ -0,0 +1,4 @@
{
"plugins": [["flow", { "enums": true }]],
"throws": "Enum member names cannot start with lowercase 'a' through 'z'. Instead of using `foo`, consider using `Foo`, in enum `E`. (2:2)"
}

View File

@ -0,0 +1,3 @@
enum E {
A
}

View File

@ -0,0 +1,117 @@
{
"type": "File",
"start": 0,
"end": 14,
"loc": {
"start": {
"line": 1,
"column": 0
},
"end": {
"line": 3,
"column": 1
}
},
"program": {
"type": "Program",
"start": 0,
"end": 14,
"loc": {
"start": {
"line": 1,
"column": 0
},
"end": {
"line": 3,
"column": 1
}
},
"sourceType": "script",
"interpreter": null,
"body": [
{
"type": "EnumDeclaration",
"start": 0,
"end": 14,
"loc": {
"start": {
"line": 1,
"column": 0
},
"end": {
"line": 3,
"column": 1
}
},
"id": {
"type": "Identifier",
"start": 5,
"end": 6,
"loc": {
"start": {
"line": 1,
"column": 5
},
"end": {
"line": 1,
"column": 6
},
"identifierName": "E"
},
"name": "E"
},
"body": {
"type": "EnumStringBody",
"start": 11,
"end": 12,
"loc": {
"start": {
"line": 2,
"column": 2
},
"end": {
"line": 2,
"column": 3
}
},
"explicitType": false,
"members": [
{
"type": "EnumDefaultedMember",
"start": 11,
"end": 12,
"loc": {
"start": {
"line": 2,
"column": 2
},
"end": {
"line": 2,
"column": 3
}
},
"id": {
"type": "Identifier",
"start": 11,
"end": 12,
"loc": {
"start": {
"line": 2,
"column": 2
},
"end": {
"line": 2,
"column": 3
},
"identifierName": "A"
},
"name": "A"
}
}
]
}
}
],
"directives": []
}
}

View File

@ -0,0 +1,4 @@
enum E of number {
A = 1,
B = 2,
}

View File

@ -0,0 +1,189 @@
{
"type": "File",
"start": 0,
"end": 38,
"loc": {
"start": {
"line": 1,
"column": 0
},
"end": {
"line": 4,
"column": 1
}
},
"program": {
"type": "Program",
"start": 0,
"end": 38,
"loc": {
"start": {
"line": 1,
"column": 0
},
"end": {
"line": 4,
"column": 1
}
},
"sourceType": "script",
"interpreter": null,
"body": [
{
"type": "EnumDeclaration",
"start": 0,
"end": 38,
"loc": {
"start": {
"line": 1,
"column": 0
},
"end": {
"line": 4,
"column": 1
}
},
"id": {
"type": "Identifier",
"start": 5,
"end": 6,
"loc": {
"start": {
"line": 1,
"column": 5
},
"end": {
"line": 1,
"column": 6
},
"identifierName": "E"
},
"name": "E"
},
"body": {
"type": "EnumNumberBody",
"start": 21,
"end": 36,
"loc": {
"start": {
"line": 2,
"column": 2
},
"end": {
"line": 3,
"column": 8
}
},
"explicitType": true,
"members": [
{
"type": "EnumNumberMember",
"start": 21,
"end": 26,
"loc": {
"start": {
"line": 2,
"column": 2
},
"end": {
"line": 2,
"column": 7
}
},
"id": {
"type": "Identifier",
"start": 21,
"end": 22,
"loc": {
"start": {
"line": 2,
"column": 2
},
"end": {
"line": 2,
"column": 3
},
"identifierName": "A"
},
"name": "A"
},
"init": {
"type": "NumericLiteral",
"start": 25,
"end": 26,
"loc": {
"start": {
"line": 2,
"column": 6
},
"end": {
"line": 2,
"column": 7
}
},
"extra": {
"rawValue": 1,
"raw": "1"
},
"value": 1
}
},
{
"type": "EnumNumberMember",
"start": 30,
"end": 35,
"loc": {
"start": {
"line": 3,
"column": 2
},
"end": {
"line": 3,
"column": 7
}
},
"id": {
"type": "Identifier",
"start": 30,
"end": 31,
"loc": {
"start": {
"line": 3,
"column": 2
},
"end": {
"line": 3,
"column": 3
},
"identifierName": "B"
},
"name": "B"
},
"init": {
"type": "NumericLiteral",
"start": 34,
"end": 35,
"loc": {
"start": {
"line": 3,
"column": 6
},
"end": {
"line": 3,
"column": 7
}
},
"extra": {
"rawValue": 2,
"raw": "2"
},
"value": 2
}
}
]
}
}
],
"directives": []
}
}

View File

@ -0,0 +1,4 @@
enum E {
A = 1,
B = 2,
}

View File

@ -0,0 +1,189 @@
{
"type": "File",
"start": 0,
"end": 28,
"loc": {
"start": {
"line": 1,
"column": 0
},
"end": {
"line": 4,
"column": 1
}
},
"program": {
"type": "Program",
"start": 0,
"end": 28,
"loc": {
"start": {
"line": 1,
"column": 0
},
"end": {
"line": 4,
"column": 1
}
},
"sourceType": "script",
"interpreter": null,
"body": [
{
"type": "EnumDeclaration",
"start": 0,
"end": 28,
"loc": {
"start": {
"line": 1,
"column": 0
},
"end": {
"line": 4,
"column": 1
}
},
"id": {
"type": "Identifier",
"start": 5,
"end": 6,
"loc": {
"start": {
"line": 1,
"column": 5
},
"end": {
"line": 1,
"column": 6
},
"identifierName": "E"
},
"name": "E"
},
"body": {
"type": "EnumNumberBody",
"start": 11,
"end": 26,
"loc": {
"start": {
"line": 2,
"column": 2
},
"end": {
"line": 3,
"column": 8
}
},
"explicitType": false,
"members": [
{
"type": "EnumNumberMember",
"start": 11,
"end": 16,
"loc": {
"start": {
"line": 2,
"column": 2
},
"end": {
"line": 2,
"column": 7
}
},
"id": {
"type": "Identifier",
"start": 11,
"end": 12,
"loc": {
"start": {
"line": 2,
"column": 2
},
"end": {
"line": 2,
"column": 3
},
"identifierName": "A"
},
"name": "A"
},
"init": {
"type": "NumericLiteral",
"start": 15,
"end": 16,
"loc": {
"start": {
"line": 2,
"column": 6
},
"end": {
"line": 2,
"column": 7
}
},
"extra": {
"rawValue": 1,
"raw": "1"
},
"value": 1
}
},
{
"type": "EnumNumberMember",
"start": 20,
"end": 25,
"loc": {
"start": {
"line": 3,
"column": 2
},
"end": {
"line": 3,
"column": 7
}
},
"id": {
"type": "Identifier",
"start": 20,
"end": 21,
"loc": {
"start": {
"line": 3,
"column": 2
},
"end": {
"line": 3,
"column": 3
},
"identifierName": "B"
},
"name": "B"
},
"init": {
"type": "NumericLiteral",
"start": 24,
"end": 25,
"loc": {
"start": {
"line": 3,
"column": 6
},
"end": {
"line": 3,
"column": 7
}
},
"extra": {
"rawValue": 2,
"raw": "2"
},
"value": 2
}
}
]
}
}
],
"directives": []
}
}

View File

@ -0,0 +1,3 @@
enum E of number {
A,
}

View File

@ -0,0 +1,4 @@
{
"plugins": [["flow", { "enums": true }]],
"throws": "Number enum members need to be initialized, e.g. `A = 1` in enum `E`. (2:2)"
}

View File

@ -0,0 +1,4 @@
enum E {
A,
B = 1,
}

View File

@ -0,0 +1,4 @@
{
"plugins": [["flow", { "enums": true }]],
"throws": "Number enum members need to be initialized, e.g. `A = 1` in enum `E`. (2:2)"
}

View File

@ -0,0 +1,3 @@
{
"plugins": [["flow", { "enums": true }]]
}

View File

@ -0,0 +1,2 @@
enum class {
}

View File

@ -0,0 +1,4 @@
{
"plugins": [["flow", { "enums": true }]],
"throws": "Unexpected keyword 'class' (1:5)"
}

View File

@ -0,0 +1,4 @@
enum E of string {
A,
B,
}

View File

@ -0,0 +1,149 @@
{
"type": "File",
"start": 0,
"end": 30,
"loc": {
"start": {
"line": 1,
"column": 0
},
"end": {
"line": 4,
"column": 1
}
},
"program": {
"type": "Program",
"start": 0,
"end": 30,
"loc": {
"start": {
"line": 1,
"column": 0
},
"end": {
"line": 4,
"column": 1
}
},
"sourceType": "script",
"interpreter": null,
"body": [
{
"type": "EnumDeclaration",
"start": 0,
"end": 30,
"loc": {
"start": {
"line": 1,
"column": 0
},
"end": {
"line": 4,
"column": 1
}
},
"id": {
"type": "Identifier",
"start": 5,
"end": 6,
"loc": {
"start": {
"line": 1,
"column": 5
},
"end": {
"line": 1,
"column": 6
},
"identifierName": "E"
},
"name": "E"
},
"body": {
"type": "EnumStringBody",
"start": 21,
"end": 28,
"loc": {
"start": {
"line": 2,
"column": 2
},
"end": {
"line": 3,
"column": 4
}
},
"explicitType": true,
"members": [
{
"type": "EnumDefaultedMember",
"start": 21,
"end": 22,
"loc": {
"start": {
"line": 2,
"column": 2
},
"end": {
"line": 2,
"column": 3
}
},
"id": {
"type": "Identifier",
"start": 21,
"end": 22,
"loc": {
"start": {
"line": 2,
"column": 2
},
"end": {
"line": 2,
"column": 3
},
"identifierName": "A"
},
"name": "A"
}
},
{
"type": "EnumDefaultedMember",
"start": 26,
"end": 27,
"loc": {
"start": {
"line": 3,
"column": 2
},
"end": {
"line": 3,
"column": 3
}
},
"id": {
"type": "Identifier",
"start": 26,
"end": 27,
"loc": {
"start": {
"line": 3,
"column": 2
},
"end": {
"line": 3,
"column": 3
},
"identifierName": "B"
},
"name": "B"
}
}
]
}
}
],
"directives": []
}
}

View File

@ -0,0 +1,4 @@
enum E of string {
A = "a",
B = "b",
}

View File

@ -0,0 +1,189 @@
{
"type": "File",
"start": 0,
"end": 42,
"loc": {
"start": {
"line": 1,
"column": 0
},
"end": {
"line": 4,
"column": 1
}
},
"program": {
"type": "Program",
"start": 0,
"end": 42,
"loc": {
"start": {
"line": 1,
"column": 0
},
"end": {
"line": 4,
"column": 1
}
},
"sourceType": "script",
"interpreter": null,
"body": [
{
"type": "EnumDeclaration",
"start": 0,
"end": 42,
"loc": {
"start": {
"line": 1,
"column": 0
},
"end": {
"line": 4,
"column": 1
}
},
"id": {
"type": "Identifier",
"start": 5,
"end": 6,
"loc": {
"start": {
"line": 1,
"column": 5
},
"end": {
"line": 1,
"column": 6
},
"identifierName": "E"
},
"name": "E"
},
"body": {
"type": "EnumStringBody",
"start": 21,
"end": 40,
"loc": {
"start": {
"line": 2,
"column": 2
},
"end": {
"line": 3,
"column": 10
}
},
"explicitType": true,
"members": [
{
"type": "EnumStringMember",
"start": 21,
"end": 28,
"loc": {
"start": {
"line": 2,
"column": 2
},
"end": {
"line": 2,
"column": 9
}
},
"id": {
"type": "Identifier",
"start": 21,
"end": 22,
"loc": {
"start": {
"line": 2,
"column": 2
},
"end": {
"line": 2,
"column": 3
},
"identifierName": "A"
},
"name": "A"
},
"init": {
"type": "StringLiteral",
"start": 25,
"end": 28,
"loc": {
"start": {
"line": 2,
"column": 6
},
"end": {
"line": 2,
"column": 9
}
},
"extra": {
"rawValue": "a",
"raw": "\"a\""
},
"value": "a"
}
},
{
"type": "EnumStringMember",
"start": 32,
"end": 39,
"loc": {
"start": {
"line": 3,
"column": 2
},
"end": {
"line": 3,
"column": 9
}
},
"id": {
"type": "Identifier",
"start": 32,
"end": 33,
"loc": {
"start": {
"line": 3,
"column": 2
},
"end": {
"line": 3,
"column": 3
},
"identifierName": "B"
},
"name": "B"
},
"init": {
"type": "StringLiteral",
"start": 36,
"end": 39,
"loc": {
"start": {
"line": 3,
"column": 6
},
"end": {
"line": 3,
"column": 9
}
},
"extra": {
"rawValue": "b",
"raw": "\"b\""
},
"value": "b"
}
}
]
}
}
],
"directives": []
}
}

View File

@ -0,0 +1,4 @@
enum E {
A,
B,
}

View File

@ -0,0 +1,149 @@
{
"type": "File",
"start": 0,
"end": 20,
"loc": {
"start": {
"line": 1,
"column": 0
},
"end": {
"line": 4,
"column": 1
}
},
"program": {
"type": "Program",
"start": 0,
"end": 20,
"loc": {
"start": {
"line": 1,
"column": 0
},
"end": {
"line": 4,
"column": 1
}
},
"sourceType": "script",
"interpreter": null,
"body": [
{
"type": "EnumDeclaration",
"start": 0,
"end": 20,
"loc": {
"start": {
"line": 1,
"column": 0
},
"end": {
"line": 4,
"column": 1
}
},
"id": {
"type": "Identifier",
"start": 5,
"end": 6,
"loc": {
"start": {
"line": 1,
"column": 5
},
"end": {
"line": 1,
"column": 6
},
"identifierName": "E"
},
"name": "E"
},
"body": {
"type": "EnumStringBody",
"start": 11,
"end": 18,
"loc": {
"start": {
"line": 2,
"column": 2
},
"end": {
"line": 3,
"column": 4
}
},
"explicitType": false,
"members": [
{
"type": "EnumDefaultedMember",
"start": 11,
"end": 12,
"loc": {
"start": {
"line": 2,
"column": 2
},
"end": {
"line": 2,
"column": 3
}
},
"id": {
"type": "Identifier",
"start": 11,
"end": 12,
"loc": {
"start": {
"line": 2,
"column": 2
},
"end": {
"line": 2,
"column": 3
},
"identifierName": "A"
},
"name": "A"
}
},
{
"type": "EnumDefaultedMember",
"start": 16,
"end": 17,
"loc": {
"start": {
"line": 3,
"column": 2
},
"end": {
"line": 3,
"column": 3
}
},
"id": {
"type": "Identifier",
"start": 16,
"end": 17,
"loc": {
"start": {
"line": 3,
"column": 2
},
"end": {
"line": 3,
"column": 3
},
"identifierName": "B"
},
"name": "B"
}
}
]
}
}
],
"directives": []
}
}

View File

@ -0,0 +1,4 @@
enum E {
A = "a",
B = "b",
}

View File

@ -0,0 +1,189 @@
{
"type": "File",
"start": 0,
"end": 32,
"loc": {
"start": {
"line": 1,
"column": 0
},
"end": {
"line": 4,
"column": 1
}
},
"program": {
"type": "Program",
"start": 0,
"end": 32,
"loc": {
"start": {
"line": 1,
"column": 0
},
"end": {
"line": 4,
"column": 1
}
},
"sourceType": "script",
"interpreter": null,
"body": [
{
"type": "EnumDeclaration",
"start": 0,
"end": 32,
"loc": {
"start": {
"line": 1,
"column": 0
},
"end": {
"line": 4,
"column": 1
}
},
"id": {
"type": "Identifier",
"start": 5,
"end": 6,
"loc": {
"start": {
"line": 1,
"column": 5
},
"end": {
"line": 1,
"column": 6
},
"identifierName": "E"
},
"name": "E"
},
"body": {
"type": "EnumStringBody",
"start": 11,
"end": 30,
"loc": {
"start": {
"line": 2,
"column": 2
},
"end": {
"line": 3,
"column": 10
}
},
"explicitType": false,
"members": [
{
"type": "EnumStringMember",
"start": 11,
"end": 18,
"loc": {
"start": {
"line": 2,
"column": 2
},
"end": {
"line": 2,
"column": 9
}
},
"id": {
"type": "Identifier",
"start": 11,
"end": 12,
"loc": {
"start": {
"line": 2,
"column": 2
},
"end": {
"line": 2,
"column": 3
},
"identifierName": "A"
},
"name": "A"
},
"init": {
"type": "StringLiteral",
"start": 15,
"end": 18,
"loc": {
"start": {
"line": 2,
"column": 6
},
"end": {
"line": 2,
"column": 9
}
},
"extra": {
"rawValue": "a",
"raw": "\"a\""
},
"value": "a"
}
},
{
"type": "EnumStringMember",
"start": 22,
"end": 29,
"loc": {
"start": {
"line": 3,
"column": 2
},
"end": {
"line": 3,
"column": 9
}
},
"id": {
"type": "Identifier",
"start": 22,
"end": 23,
"loc": {
"start": {
"line": 3,
"column": 2
},
"end": {
"line": 3,
"column": 3
},
"identifierName": "B"
},
"name": "B"
},
"init": {
"type": "StringLiteral",
"start": 26,
"end": 29,
"loc": {
"start": {
"line": 3,
"column": 6
},
"end": {
"line": 3,
"column": 9
}
},
"extra": {
"rawValue": "b",
"raw": "\"b\""
},
"value": "b"
}
}
]
}
}
],
"directives": []
}
}

View File

@ -0,0 +1,5 @@
enum E of string {
A = "a",
B,
C,
}

View File

@ -0,0 +1,4 @@
{
"plugins": [["flow", { "enums": true }]],
"throws": "String enum members need to consistently either all use initializers, or use no initializers, in enum `E`. (2:2)"
}

View File

@ -0,0 +1,5 @@
enum E of string {
A,
B = "b",
C = "c",
}

View File

@ -0,0 +1,4 @@
{
"plugins": [["flow", { "enums": true }]],
"throws": "String enum members need to consistently either all use initializers, or use no initializers, in enum `E`. (2:2)"
}

View File

@ -0,0 +1,4 @@
enum E of symbol {
A,
B,
}

View File

@ -0,0 +1,148 @@
{
"type": "File",
"start": 0,
"end": 30,
"loc": {
"start": {
"line": 1,
"column": 0
},
"end": {
"line": 4,
"column": 1
}
},
"program": {
"type": "Program",
"start": 0,
"end": 30,
"loc": {
"start": {
"line": 1,
"column": 0
},
"end": {
"line": 4,
"column": 1
}
},
"sourceType": "script",
"interpreter": null,
"body": [
{
"type": "EnumDeclaration",
"start": 0,
"end": 30,
"loc": {
"start": {
"line": 1,
"column": 0
},
"end": {
"line": 4,
"column": 1
}
},
"id": {
"type": "Identifier",
"start": 5,
"end": 6,
"loc": {
"start": {
"line": 1,
"column": 5
},
"end": {
"line": 1,
"column": 6
},
"identifierName": "E"
},
"name": "E"
},
"body": {
"type": "EnumSymbolBody",
"start": 21,
"end": 28,
"loc": {
"start": {
"line": 2,
"column": 2
},
"end": {
"line": 3,
"column": 4
}
},
"members": [
{
"type": "EnumDefaultedMember",
"start": 21,
"end": 22,
"loc": {
"start": {
"line": 2,
"column": 2
},
"end": {
"line": 2,
"column": 3
}
},
"id": {
"type": "Identifier",
"start": 21,
"end": 22,
"loc": {
"start": {
"line": 2,
"column": 2
},
"end": {
"line": 2,
"column": 3
},
"identifierName": "A"
},
"name": "A"
}
},
{
"type": "EnumDefaultedMember",
"start": 26,
"end": 27,
"loc": {
"start": {
"line": 3,
"column": 2
},
"end": {
"line": 3,
"column": 3
}
},
"id": {
"type": "Identifier",
"start": 26,
"end": 27,
"loc": {
"start": {
"line": 3,
"column": 2
},
"end": {
"line": 3,
"column": 3
},
"identifierName": "B"
},
"name": "B"
}
}
]
}
}
],
"directives": []
}
}

View File

@ -5,12 +5,16 @@ export default declare((api, options) => {
// When enabled and plugins includes flow, all files should be parsed as if // When enabled and plugins includes flow, all files should be parsed as if
// the @flow pragma was provided. // the @flow pragma was provided.
const { all } = options; const { all, enums } = options;
if (typeof all !== "boolean" && typeof all !== "undefined") { if (typeof all !== "boolean" && typeof all !== "undefined") {
throw new Error(".all must be a boolean, or undefined"); throw new Error(".all must be a boolean, or undefined");
} }
if (typeof enums !== "boolean" && typeof enums !== "undefined") {
throw new Error(".enums must be a boolean, or undefined");
}
return { return {
name: "syntax-flow", name: "syntax-flow",
@ -25,7 +29,7 @@ export default declare((api, options) => {
return; return;
} }
parserOpts.plugins.push(["flow", { all }]); parserOpts.plugins.push(["flow", { all, enums }]);
}, },
}; };
}); });

View File

@ -584,6 +584,39 @@ export function assertVoidTypeAnnotation(
): void { ): void {
assert("VoidTypeAnnotation", node, opts); assert("VoidTypeAnnotation", node, opts);
} }
export function assertEnumDeclaration(node: Object, opts?: Object = {}): void {
assert("EnumDeclaration", node, opts);
}
export function assertEnumBooleanBody(node: Object, opts?: Object = {}): void {
assert("EnumBooleanBody", node, opts);
}
export function assertEnumNumberBody(node: Object, opts?: Object = {}): void {
assert("EnumNumberBody", node, opts);
}
export function assertEnumStringBody(node: Object, opts?: Object = {}): void {
assert("EnumStringBody", node, opts);
}
export function assertEnumSymbolBody(node: Object, opts?: Object = {}): void {
assert("EnumSymbolBody", node, opts);
}
export function assertEnumBooleanMember(
node: Object,
opts?: Object = {},
): void {
assert("EnumBooleanMember", node, opts);
}
export function assertEnumNumberMember(node: Object, opts?: Object = {}): void {
assert("EnumNumberMember", node, opts);
}
export function assertEnumStringMember(node: Object, opts?: Object = {}): void {
assert("EnumStringMember", node, opts);
}
export function assertEnumDefaultedMember(
node: Object,
opts?: Object = {},
): void {
assert("EnumDefaultedMember", node, opts);
}
export function assertJSXAttribute(node: Object, opts?: Object = {}): void { export function assertJSXAttribute(node: Object, opts?: Object = {}): void {
assert("JSXAttribute", node, opts); assert("JSXAttribute", node, opts);
} }
@ -1139,6 +1172,12 @@ export function assertFlowDeclaration(node: Object, opts?: Object = {}): void {
export function assertFlowPredicate(node: Object, opts?: Object = {}): void { export function assertFlowPredicate(node: Object, opts?: Object = {}): void {
assert("FlowPredicate", node, opts); assert("FlowPredicate", node, opts);
} }
export function assertEnumBody(node: Object, opts?: Object = {}): void {
assert("EnumBody", node, opts);
}
export function assertEnumMember(node: Object, opts?: Object = {}): void {
assert("EnumMember", node, opts);
}
export function assertJSX(node: Object, opts?: Object = {}): void { export function assertJSX(node: Object, opts?: Object = {}): void {
assert("JSX", node, opts); assert("JSX", node, opts);
} }

View File

@ -517,6 +517,42 @@ export function VoidTypeAnnotation(...args: Array<any>): Object {
return builder("VoidTypeAnnotation", ...args); return builder("VoidTypeAnnotation", ...args);
} }
export { VoidTypeAnnotation as voidTypeAnnotation }; export { VoidTypeAnnotation as voidTypeAnnotation };
export function EnumDeclaration(...args: Array<any>): Object {
return builder("EnumDeclaration", ...args);
}
export { EnumDeclaration as enumDeclaration };
export function EnumBooleanBody(...args: Array<any>): Object {
return builder("EnumBooleanBody", ...args);
}
export { EnumBooleanBody as enumBooleanBody };
export function EnumNumberBody(...args: Array<any>): Object {
return builder("EnumNumberBody", ...args);
}
export { EnumNumberBody as enumNumberBody };
export function EnumStringBody(...args: Array<any>): Object {
return builder("EnumStringBody", ...args);
}
export { EnumStringBody as enumStringBody };
export function EnumSymbolBody(...args: Array<any>): Object {
return builder("EnumSymbolBody", ...args);
}
export { EnumSymbolBody as enumSymbolBody };
export function EnumBooleanMember(...args: Array<any>): Object {
return builder("EnumBooleanMember", ...args);
}
export { EnumBooleanMember as enumBooleanMember };
export function EnumNumberMember(...args: Array<any>): Object {
return builder("EnumNumberMember", ...args);
}
export { EnumNumberMember as enumNumberMember };
export function EnumStringMember(...args: Array<any>): Object {
return builder("EnumStringMember", ...args);
}
export { EnumStringMember as enumStringMember };
export function EnumDefaultedMember(...args: Array<any>): Object {
return builder("EnumDefaultedMember", ...args);
}
export { EnumDefaultedMember as enumDefaultedMember };
export function JSXAttribute(...args: Array<any>): Object { export function JSXAttribute(...args: Array<any>): Object {
return builder("JSXAttribute", ...args); return builder("JSXAttribute", ...args);
} }

View File

@ -45,6 +45,8 @@ export const FLOWBASEANNOTATION_TYPES =
FLIPPED_ALIAS_KEYS["FlowBaseAnnotation"]; FLIPPED_ALIAS_KEYS["FlowBaseAnnotation"];
export const FLOWDECLARATION_TYPES = FLIPPED_ALIAS_KEYS["FlowDeclaration"]; export const FLOWDECLARATION_TYPES = FLIPPED_ALIAS_KEYS["FlowDeclaration"];
export const FLOWPREDICATE_TYPES = FLIPPED_ALIAS_KEYS["FlowPredicate"]; export const FLOWPREDICATE_TYPES = FLIPPED_ALIAS_KEYS["FlowPredicate"];
export const ENUMBODY_TYPES = FLIPPED_ALIAS_KEYS["EnumBody"];
export const ENUMMEMBER_TYPES = FLIPPED_ALIAS_KEYS["EnumMember"];
export const JSX_TYPES = FLIPPED_ALIAS_KEYS["JSX"]; export const JSX_TYPES = FLIPPED_ALIAS_KEYS["JSX"];
export const PRIVATE_TYPES = FLIPPED_ALIAS_KEYS["Private"]; export const PRIVATE_TYPES = FLIPPED_ALIAS_KEYS["Private"];
export const TSTYPEELEMENT_TYPES = FLIPPED_ALIAS_KEYS["TSTypeElement"]; export const TSTYPEELEMENT_TYPES = FLIPPED_ALIAS_KEYS["TSTypeElement"];

View File

@ -4,6 +4,7 @@ import defineType, {
assertOneOf, assertOneOf,
assertValueType, assertValueType,
validate, validate,
validateArrayOfType,
validateOptional, validateOptional,
validateOptionalType, validateOptionalType,
validateType, validateType,
@ -464,3 +465,88 @@ defineType("Variance", {
defineType("VoidTypeAnnotation", { defineType("VoidTypeAnnotation", {
aliases: ["Flow", "FlowType", "FlowBaseAnnotation"], aliases: ["Flow", "FlowType", "FlowBaseAnnotation"],
}); });
// Enums
defineType("EnumDeclaration", {
alises: ["Declaration"],
visitor: ["id", "body"],
fields: {
id: validateType("Identifier"),
body: validateType([
"EnumBooleanBody",
"EnumNumberBody",
"EnumStringBody",
"EnumSymbolBody",
]),
},
});
defineType("EnumBooleanBody", {
aliases: ["EnumBody"],
visitor: ["members"],
fields: {
explicit: validate(assertValueType("boolean")),
members: validateArrayOfType("EnumBooleanMember"),
},
});
defineType("EnumNumberBody", {
aliases: ["EnumBody"],
visitor: ["members"],
fields: {
explicit: validate(assertValueType("boolean")),
members: validateArrayOfType("EnumNumberMember"),
},
});
defineType("EnumStringBody", {
aliases: ["EnumBody"],
visitor: ["members"],
fields: {
explicit: validate(assertValueType("boolean")),
members: validateArrayOfType(["EnumStringMember", "EnumDefaultedMember"]),
},
});
defineType("EnumSymbolBody", {
aliases: ["EnumBody"],
visitor: ["members"],
fields: {
members: validateArrayOfType("EnumDefaultedMember"),
},
});
defineType("EnumBooleanMember", {
aliases: ["EnumMember"],
visitor: ["id"],
fields: {
id: validateType("Identifier"),
init: validateType("BooleanLiteral"),
},
});
defineType("EnumNumberMember", {
aliases: ["EnumMember"],
visitor: ["id", "init"],
fields: {
id: validateType("Identifier"),
init: validateType("NumericLiteral"),
},
});
defineType("EnumStringMember", {
aliases: ["EnumMember"],
visitor: ["id", "init"],
fields: {
id: validateType("Identifier"),
init: validateType("StringLiteral"),
},
});
defineType("EnumDefaultedMember", {
aliases: ["EnumMember"],
visitor: ["id"],
fields: {
id: validateType("Identifier"),
},
});

View File

@ -1866,6 +1866,132 @@ export function isVoidTypeAnnotation(node: ?Object, opts?: Object): boolean {
return false; return false;
} }
export function isEnumDeclaration(node: ?Object, opts?: Object): boolean {
if (!node) return false;
const nodeType = node.type;
if (nodeType === "EnumDeclaration") {
if (typeof opts === "undefined") {
return true;
} else {
return shallowEqual(node, opts);
}
}
return false;
}
export function isEnumBooleanBody(node: ?Object, opts?: Object): boolean {
if (!node) return false;
const nodeType = node.type;
if (nodeType === "EnumBooleanBody") {
if (typeof opts === "undefined") {
return true;
} else {
return shallowEqual(node, opts);
}
}
return false;
}
export function isEnumNumberBody(node: ?Object, opts?: Object): boolean {
if (!node) return false;
const nodeType = node.type;
if (nodeType === "EnumNumberBody") {
if (typeof opts === "undefined") {
return true;
} else {
return shallowEqual(node, opts);
}
}
return false;
}
export function isEnumStringBody(node: ?Object, opts?: Object): boolean {
if (!node) return false;
const nodeType = node.type;
if (nodeType === "EnumStringBody") {
if (typeof opts === "undefined") {
return true;
} else {
return shallowEqual(node, opts);
}
}
return false;
}
export function isEnumSymbolBody(node: ?Object, opts?: Object): boolean {
if (!node) return false;
const nodeType = node.type;
if (nodeType === "EnumSymbolBody") {
if (typeof opts === "undefined") {
return true;
} else {
return shallowEqual(node, opts);
}
}
return false;
}
export function isEnumBooleanMember(node: ?Object, opts?: Object): boolean {
if (!node) return false;
const nodeType = node.type;
if (nodeType === "EnumBooleanMember") {
if (typeof opts === "undefined") {
return true;
} else {
return shallowEqual(node, opts);
}
}
return false;
}
export function isEnumNumberMember(node: ?Object, opts?: Object): boolean {
if (!node) return false;
const nodeType = node.type;
if (nodeType === "EnumNumberMember") {
if (typeof opts === "undefined") {
return true;
} else {
return shallowEqual(node, opts);
}
}
return false;
}
export function isEnumStringMember(node: ?Object, opts?: Object): boolean {
if (!node) return false;
const nodeType = node.type;
if (nodeType === "EnumStringMember") {
if (typeof opts === "undefined") {
return true;
} else {
return shallowEqual(node, opts);
}
}
return false;
}
export function isEnumDefaultedMember(node: ?Object, opts?: Object): boolean {
if (!node) return false;
const nodeType = node.type;
if (nodeType === "EnumDefaultedMember") {
if (typeof opts === "undefined") {
return true;
} else {
return shallowEqual(node, opts);
}
}
return false;
}
export function isJSXAttribute(node: ?Object, opts?: Object): boolean { export function isJSXAttribute(node: ?Object, opts?: Object): boolean {
if (!node) return false; if (!node) return false;
@ -4276,6 +4402,46 @@ export function isFlowPredicate(node: ?Object, opts?: Object): boolean {
return false; return false;
} }
export function isEnumBody(node: ?Object, opts?: Object): boolean {
if (!node) return false;
const nodeType = node.type;
if (
nodeType === "EnumBody" ||
"EnumBooleanBody" === nodeType ||
"EnumNumberBody" === nodeType ||
"EnumStringBody" === nodeType ||
"EnumSymbolBody" === nodeType
) {
if (typeof opts === "undefined") {
return true;
} else {
return shallowEqual(node, opts);
}
}
return false;
}
export function isEnumMember(node: ?Object, opts?: Object): boolean {
if (!node) return false;
const nodeType = node.type;
if (
nodeType === "EnumMember" ||
"EnumBooleanMember" === nodeType ||
"EnumNumberMember" === nodeType ||
"EnumStringMember" === nodeType ||
"EnumDefaultedMember" === nodeType
) {
if (typeof opts === "undefined") {
return true;
} else {
return shallowEqual(node, opts);
}
}
return false;
}
export function isJSX(node: ?Object, opts?: Object): boolean { export function isJSX(node: ?Object, opts?: Object): boolean {
if (!node) return false; if (!node) return false;