From 50ae16de38150caf84988b80098df93173f718b9 Mon Sep 17 00:00:00 2001 From: Andy Date: Tue, 13 Jun 2017 15:26:12 -0700 Subject: [PATCH 1/5] Factor parseSubscript out of parseSubscripts (#576) --- src/parser/expression.js | 164 +++++++++++++++++++++------------------ 1 file changed, 87 insertions(+), 77 deletions(-) diff --git a/src/parser/expression.js b/src/parser/expression.js index 44a9051a7e..16ca598bbd 100644 --- a/src/parser/expression.js +++ b/src/parser/expression.js @@ -294,99 +294,109 @@ export default class ExpressionParser extends LValParser { return this.parseSubscripts(expr, startPos, startLoc); } - parseSubscripts(base: N.Expression, startPos: number, startLoc: Position, noCalls?: boolean): N.Expression { - for (;;) { - if (!noCalls && this.eat(tt.doubleColon)) { - const node = this.startNodeAt(startPos, startLoc); - node.object = base; - node.callee = this.parseNoCallExpr(); - return this.parseSubscripts(this.finishNode(node, "BindExpression"), startPos, startLoc, noCalls); + parseSubscripts(base: N.Expression, startPos: number, startLoc: Position, noCalls?: ?boolean) { + const state = { stop: false }; + do { + base = this.parseSubscript(base, startPos, startLoc, noCalls, state); + } while (!state.stop); + return base; + } - } else if (this.match(tt.questionDot)) { - if (!this.hasPlugin("optionalChaining")) { - this.raise(startPos, "You can only use optional-chaining when the 'optionalChaining' plugin is enabled."); - } + /** @param state Set 'state.stop = true' to indicate that we should stop parsing subscripts. */ + parseSubscript(base: N.Expression, startPos: number, startLoc: Position, noCalls: ?boolean, state: { stop: boolean }): N.Expression { + if (!noCalls && this.eat(tt.doubleColon)) { + const node = this.startNodeAt(startPos, startLoc); + node.object = base; + node.callee = this.parseNoCallExpr(); + state.stop = true; + return this.parseSubscripts(this.finishNode(node, "BindExpression"), startPos, startLoc, noCalls); - if (noCalls && this.lookahead().type == tt.parenL) { - return base; - } - this.next(); + } else if (this.match(tt.questionDot)) { + if (!this.hasPlugin("optionalChaining")) { + this.raise(startPos, "You can only use optional-chaining when the 'optionalChaining' plugin is enabled."); + } - const node = this.startNodeAt(startPos, startLoc); + if (noCalls && this.lookahead().type == tt.parenL) { + state.stop = true; + return base; + } + this.next(); - if (this.eat(tt.bracketL)) { - node.object = base; - node.property = this.parseExpression(); - node.computed = true; - node.optional = true; - this.expect(tt.bracketR); - base = this.finishNode(node, "MemberExpression"); - } else if (this.eat(tt.parenL)) { - const possibleAsync = this.state.potentialArrowAt === base.start && - base.type === "Identifier" && - base.name === "async" && - !this.canInsertSemicolon(); + const node = this.startNodeAt(startPos, startLoc); - node.callee = base; - node.arguments = this.parseCallExpressionArguments(tt.parenR, possibleAsync); - node.optional = true; - - base = this.finishNode(node, "CallExpression"); - } else { - node.object = base; - node.property = this.parseIdentifier(true); - node.computed = false; - node.optional = true; - base = this.finishNode(node, "MemberExpression"); - } - } else if (this.eat(tt.dot)) { - const node = this.startNodeAt(startPos, startLoc); - node.object = base; - node.property = this.hasPlugin("classPrivateProperties") ? this.parseMaybePrivateName() : this.parseIdentifier(true); - node.computed = false; - base = this.finishNode(node, "MemberExpression"); - } else if (this.eat(tt.bracketL)) { - const node = this.startNodeAt(startPos, startLoc); + if (this.eat(tt.bracketL)) { node.object = base; node.property = this.parseExpression(); node.computed = true; + node.optional = true; this.expect(tt.bracketR); - base = this.finishNode(node, "MemberExpression"); - } else if (!noCalls && this.match(tt.parenL)) { - const possibleAsync = this.state.potentialArrowAt === base.start && base.type === "Identifier" && base.name === "async" && !this.canInsertSemicolon(); - this.next(); + return this.finishNode(node, "MemberExpression"); + } else if (this.eat(tt.parenL)) { + const possibleAsync = this.state.potentialArrowAt === base.start && + base.type === "Identifier" && + base.name === "async" && + !this.canInsertSemicolon(); - const node = this.startNodeAt(startPos, startLoc); node.callee = base; node.arguments = this.parseCallExpressionArguments(tt.parenR, possibleAsync); - if (node.callee.type === "Import") { - if (node.arguments.length !== 1) { - this.raise(node.start, "import() requires exactly one argument"); - } + node.optional = true; - const importArg = node.arguments[0]; - if (importArg && importArg.type === "SpreadElement") { - this.raise(importArg.start, "... is not allowed in import()"); - } - } - base = this.finishNode(node, "CallExpression"); - - if (possibleAsync && this.shouldParseAsyncArrow()) { - return this.parseAsyncArrowFromCallExpression(this.startNodeAt(startPos, startLoc), node); - } else { - this.toReferencedList(node.arguments); - } - } else if (this.match(tt.backQuote)) { - const node = this.startNodeAt(startPos, startLoc); - node.tag = base; - node.quasi = this.parseTemplate(true); - base = this.finishNode(node, "TaggedTemplateExpression"); + return this.finishNode(node, "CallExpression"); } else { - return base; + node.object = base; + node.property = this.parseIdentifier(true); + node.computed = false; + node.optional = true; + return this.finishNode(node, "MemberExpression"); } + } else if (this.eat(tt.dot)) { + const node = this.startNodeAt(startPos, startLoc); + node.object = base; + node.property = this.hasPlugin("classPrivateProperties") ? this.parseMaybePrivateName() : this.parseIdentifier(true); + node.computed = false; + return this.finishNode(node, "MemberExpression"); + } else if (this.eat(tt.bracketL)) { + const node = this.startNodeAt(startPos, startLoc); + node.object = base; + node.property = this.parseExpression(); + node.computed = true; + this.expect(tt.bracketR); + return this.finishNode(node, "MemberExpression"); + } else if (!noCalls && this.match(tt.parenL)) { + const possibleAsync = this.state.potentialArrowAt === base.start && base.type === "Identifier" && base.name === "async" && !this.canInsertSemicolon(); + this.next(); + + const node = this.startNodeAt(startPos, startLoc); + node.callee = base; + node.arguments = this.parseCallExpressionArguments(tt.parenR, possibleAsync); + if (node.callee.type === "Import") { + if (node.arguments.length !== 1) { + this.raise(node.start, "import() requires exactly one argument"); + } + + const importArg = node.arguments[0]; + if (importArg && importArg.type === "SpreadElement") { + this.raise(importArg.start, "... is not allowed in import()"); + } + } + this.finishNode(node, "CallExpression"); + + if (possibleAsync && this.shouldParseAsyncArrow()) { + state.stop = true; + return this.parseAsyncArrowFromCallExpression(this.startNodeAt(startPos, startLoc), node); + } else { + this.toReferencedList(node.arguments); + } + return node; + } else if (this.match(tt.backQuote)) { + const node = this.startNodeAt(startPos, startLoc); + node.tag = base; + node.quasi = this.parseTemplate(true); + return this.finishNode(node, "TaggedTemplateExpression"); + } else { + state.stop = true; + return base; } - // istanbul ignore next - throw new Error("Unreachable"); } parseCallExpressionArguments(close: TokenType, possibleAsyncArrow: boolean): $ReadOnlyArray { From 6b4fba4deb9d4e2261c0514db6d45e068eb53592 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nicol=C3=B2=20Ribaudo?= Date: Thu, 15 Jun 2017 16:39:39 +0200 Subject: [PATCH 2/5] The { after a function generic type annotation is a statement (#578) This would have previously been interpreted as an expression, because usually after ">" there can't be a statement. Fixes #36 --- src/plugins/flow.js | 1 + .../actual.js | 1 + .../expected.json | 281 ++++++++++++++++++ 3 files changed, 283 insertions(+) create mode 100644 test/fixtures/flow/type-annotations/function-expression-inside-jsx-attr/actual.js create mode 100644 test/fixtures/flow/type-annotations/function-expression-inside-jsx-attr/expected.json diff --git a/src/plugins/flow.js b/src/plugins/flow.js index 5329fef875..164ea6ae41 100644 --- a/src/plugins/flow.js +++ b/src/plugins/flow.js @@ -958,6 +958,7 @@ export default (superClass: Class): Class => class extends super this.state.inType = true; const type = this.flowParseUnionType(); this.state.inType = oldInType; + this.state.exprAllowed = false; return type; } diff --git a/test/fixtures/flow/type-annotations/function-expression-inside-jsx-attr/actual.js b/test/fixtures/flow/type-annotations/function-expression-inside-jsx-attr/actual.js new file mode 100644 index 0000000000..a4cfe8e70f --- /dev/null +++ b/test/fixtures/flow/type-annotations/function-expression-inside-jsx-attr/actual.js @@ -0,0 +1 @@ + {}} /> \ No newline at end of file diff --git a/test/fixtures/flow/type-annotations/function-expression-inside-jsx-attr/expected.json b/test/fixtures/flow/type-annotations/function-expression-inside-jsx-attr/expected.json new file mode 100644 index 0000000000..75d1eb85f5 --- /dev/null +++ b/test/fixtures/flow/type-annotations/function-expression-inside-jsx-attr/expected.json @@ -0,0 +1,281 @@ +{ + "type": "File", + "start": 0, + "end": 42, + "loc": { + "start": { + "line": 1, + "column": 0 + }, + "end": { + "line": 1, + "column": 42 + } + }, + "program": { + "type": "Program", + "start": 0, + "end": 42, + "loc": { + "start": { + "line": 1, + "column": 0 + }, + "end": { + "line": 1, + "column": 42 + } + }, + "sourceType": "module", + "body": [ + { + "type": "ExpressionStatement", + "start": 0, + "end": 42, + "loc": { + "start": { + "line": 1, + "column": 0 + }, + "end": { + "line": 1, + "column": 42 + } + }, + "expression": { + "type": "JSXElement", + "start": 0, + "end": 42, + "loc": { + "start": { + "line": 1, + "column": 0 + }, + "end": { + "line": 1, + "column": 42 + } + }, + "openingElement": { + "type": "JSXOpeningElement", + "start": 0, + "end": 42, + "loc": { + "start": { + "line": 1, + "column": 0 + }, + "end": { + "line": 1, + "column": 42 + } + }, + "attributes": [ + { + "type": "JSXAttribute", + "start": 5, + "end": 39, + "loc": { + "start": { + "line": 1, + "column": 5 + }, + "end": { + "line": 1, + "column": 39 + } + }, + "name": { + "type": "JSXIdentifier", + "start": 5, + "end": 6, + "loc": { + "start": { + "line": 1, + "column": 5 + }, + "end": { + "line": 1, + "column": 6 + } + }, + "name": "x" + }, + "value": { + "type": "JSXExpressionContainer", + "start": 7, + "end": 39, + "loc": { + "start": { + "line": 1, + "column": 7 + }, + "end": { + "line": 1, + "column": 39 + } + }, + "expression": { + "type": "FunctionExpression", + "start": 8, + "end": 38, + "loc": { + "start": { + "line": 1, + "column": 8 + }, + "end": { + "line": 1, + "column": 38 + } + }, + "id": null, + "generator": false, + "expression": false, + "async": false, + "params": [ + { + "type": "Identifier", + "start": 18, + "end": 19, + "loc": { + "start": { + "line": 1, + "column": 18 + }, + "end": { + "line": 1, + "column": 19 + }, + "identifierName": "x" + }, + "name": "x" + } + ], + "predicate": null, + "returnType": { + "type": "TypeAnnotation", + "start": 20, + "end": 35, + "loc": { + "start": { + "line": 1, + "column": 20 + }, + "end": { + "line": 1, + "column": 35 + } + }, + "typeAnnotation": { + "type": "GenericTypeAnnotation", + "start": 22, + "end": 35, + "loc": { + "start": { + "line": 1, + "column": 22 + }, + "end": { + "line": 1, + "column": 35 + } + }, + "typeParameters": { + "type": "TypeParameterInstantiation", + "start": 27, + "end": 35, + "loc": { + "start": { + "line": 1, + "column": 27 + }, + "end": { + "line": 1, + "column": 35 + } + }, + "params": [ + { + "type": "StringTypeAnnotation", + "start": 28, + "end": 34, + "loc": { + "start": { + "line": 1, + "column": 28 + }, + "end": { + "line": 1, + "column": 34 + } + } + } + ] + }, + "id": { + "type": "Identifier", + "start": 22, + "end": 27, + "loc": { + "start": { + "line": 1, + "column": 22 + }, + "end": { + "line": 1, + "column": 27 + }, + "identifierName": "Array" + }, + "name": "Array" + } + } + }, + "body": { + "type": "BlockStatement", + "start": 36, + "end": 38, + "loc": { + "start": { + "line": 1, + "column": 36 + }, + "end": { + "line": 1, + "column": 38 + } + }, + "body": [], + "directives": [] + } + } + } + } + ], + "name": { + "type": "JSXIdentifier", + "start": 1, + "end": 4, + "loc": { + "start": { + "line": 1, + "column": 1 + }, + "end": { + "line": 1, + "column": 4 + } + }, + "name": "bar" + }, + "selfClosing": true + }, + "closingElement": null, + "children": [] + } + } + ], + "directives": [] + } +} \ No newline at end of file From e11794c7359bcd54c08f39567d64f3cd90af58f3 Mon Sep 17 00:00:00 2001 From: Daniel Tschinder Date: Sat, 17 Jun 2017 01:05:32 +0200 Subject: [PATCH 3/5] Add back shorthand field to Property (#580) Also add some better types for estree --- src/plugins/estree.js | 17 ++++++++--------- src/types.js | 15 +++++++++++++++ .../estree/object-method/basic/expected.json | 3 ++- 3 files changed, 25 insertions(+), 10 deletions(-) diff --git a/src/plugins/estree.js b/src/plugins/estree.js index 61e2a8e69c..179f16f963 100644 --- a/src/plugins/estree.js +++ b/src/plugins/estree.js @@ -221,16 +221,15 @@ export default (superClass: Class): Class => class extends super isAsync: boolean, isPattern: boolean ): ?N.ObjectMethod { - const node = super.parseObjectMethod(prop, isGenerator, isAsync, isPattern); + const node: N.EstreeProperty = (super.parseObjectMethod(prop, isGenerator, isAsync, isPattern): any); if (node) { - // $FlowIgnore - if (node.kind === "method") node.kind = "init"; - // $FlowIgnore node.type = "Property"; + if (node.kind === "method") node.kind = "init"; + node.shorthand = false; } - return node; + return (node: any); } parseObjectProperty( @@ -240,16 +239,16 @@ export default (superClass: Class): Class => class extends super isPattern: boolean, refShorthandDefaultPos: ?Pos ): ?N.ObjectProperty { - const node = super.parseObjectProperty(prop, startPos, startLoc, isPattern, refShorthandDefaultPos); + const node: N.EstreeProperty = ( + super.parseObjectProperty(prop, startPos, startLoc, isPattern, refShorthandDefaultPos): any + ); if (node) { - // $FlowIgnore node.kind = "init"; - // $FlowIgnore node.type = "Property"; } - return node; + return (node: any); } toAssignable( diff --git a/src/types.js b/src/types.js index 950c2fdc6f..d4c1041ae8 100644 --- a/src/types.js +++ b/src/types.js @@ -724,3 +724,18 @@ export type FlowFunctionTypeParam = Node; export type FlowTypeAnnotation = Node; export type FlowVariance = Node; export type FlowClassImplements = Node; + + +// estree + +export type EstreeProperty = NodeBase & { + type: "Property"; + shorthand: boolean; + key: Expression; + computed: boolean; + value: Expression; + decorators: $ReadOnlyArray; + kind?: "get" | "set" | "init"; + + variance?: ?FlowVariance; +}; diff --git a/test/fixtures/estree/object-method/basic/expected.json b/test/fixtures/estree/object-method/basic/expected.json index b432d8ad2d..de9baf8dff 100644 --- a/test/fixtures/estree/object-method/basic/expected.json +++ b/test/fixtures/estree/object-method/basic/expected.json @@ -158,7 +158,8 @@ }, "body": [] } - } + }, + "shorthand": false } ] } From e982c0652c0a33a4fd4c2e20938bb03ffc8054b2 Mon Sep 17 00:00:00 2001 From: Daniel Tschinder Date: Sat, 17 Jun 2017 17:41:23 +0200 Subject: [PATCH 4/5] Fix v8 deopts (#581) --- src/tokenizer/index.js | 10 ++++++---- src/tokenizer/state.js | 2 ++ src/util/location.js | 1 + 3 files changed, 9 insertions(+), 4 deletions(-) diff --git a/src/tokenizer/index.js b/src/tokenizer/index.js index 460538f838..9854031814 100644 --- a/src/tokenizer/index.js +++ b/src/tokenizer/index.js @@ -78,6 +78,7 @@ export default class Tokenizer extends LocationParser { super(); this.state = new State; this.state.init(options, input); + this.isLookahead = false; } // Move to the next token @@ -127,7 +128,7 @@ export default class Tokenizer extends LocationParser { this.next(); this.isLookahead = false; - const curr = this.state.clone(true); + const curr = this.state; this.state = old; return curr; } @@ -225,9 +226,10 @@ export default class Tokenizer extends LocationParser { const start = this.state.pos; const startLoc = this.state.curPosition(); let ch = this.input.charCodeAt(this.state.pos += startSkip); - while (this.state.pos < this.input.length && ch !== 10 && ch !== 13 && ch !== 8232 && ch !== 8233) { - ++this.state.pos; - ch = this.input.charCodeAt(this.state.pos); + if (this.state.pos < this.input.length) { + while (ch !== 10 && ch !== 13 && ch !== 8232 && ch !== 8233 && ++this.state.pos < this.input.length) { + ch = this.input.charCodeAt(this.state.pos); + } } this.pushComment(false, this.input.slice(start + startSkip, this.state.pos), start, this.state.pos, startLoc, this.state.curPosition()); diff --git a/src/tokenizer/state.js b/src/tokenizer/state.js index cb1fedf74c..0a16182af8 100644 --- a/src/tokenizer/state.js +++ b/src/tokenizer/state.js @@ -40,6 +40,8 @@ export default class State { this.trailingComments = []; this.leadingComments = []; this.commentStack = []; + // $FlowIgnore + this.commentPreviousNode = null; this.pos = this.lineStart = 0; this.curLine = options.startLine; diff --git a/src/util/location.js b/src/util/location.js index aaa4ae531c..948c91770d 100644 --- a/src/util/location.js +++ b/src/util/location.js @@ -23,6 +23,7 @@ export class SourceLocation { start: Position; end: Position; filename: string; + identifierName: ?string; constructor(start: Position, end?: Position) { this.start = start; From e1e2c32bf32701a990369d98814a5e63d0652b55 Mon Sep 17 00:00:00 2001 From: Daniel Tschinder Date: Sat, 17 Jun 2017 21:43:31 +0200 Subject: [PATCH 5/5] Correctly put typeParameters on FunctionExpression (#585) --- src/plugins/estree.js | 5 + .../estree/class-method/flow/actual.js | 5 + .../estree/class-method/flow/expected.json | 400 ++++++++++++++++++ 3 files changed, 410 insertions(+) create mode 100644 test/fixtures/estree/class-method/flow/actual.js create mode 100644 test/fixtures/estree/class-method/flow/expected.json diff --git a/src/plugins/estree.js b/src/plugins/estree.js index 179f16f963..807fd9f340 100644 --- a/src/plugins/estree.js +++ b/src/plugins/estree.js @@ -161,6 +161,11 @@ export default (superClass: Class): Class => class extends super isAsync: boolean ): void { this.parseMethod(method, isGenerator, isAsync); + if (method.typeParameters) { + // $FlowIgnore + method.value.typeParameters = method.typeParameters; + delete method.typeParameters; + } classBody.body.push(this.finishNode(method, "MethodDefinition")); } diff --git a/test/fixtures/estree/class-method/flow/actual.js b/test/fixtures/estree/class-method/flow/actual.js new file mode 100644 index 0000000000..b251728341 --- /dev/null +++ b/test/fixtures/estree/class-method/flow/actual.js @@ -0,0 +1,5 @@ +class NodeUtils extends UtilParser { + finishNodeAt(node: T): T { + return node; + } +} diff --git a/test/fixtures/estree/class-method/flow/expected.json b/test/fixtures/estree/class-method/flow/expected.json new file mode 100644 index 0000000000..ff4523d0f1 --- /dev/null +++ b/test/fixtures/estree/class-method/flow/expected.json @@ -0,0 +1,400 @@ +{ + "type": "File", + "start": 0, + "end": 102, + "loc": { + "start": { + "line": 1, + "column": 0 + }, + "end": { + "line": 5, + "column": 1 + } + }, + "program": { + "type": "Program", + "start": 0, + "end": 102, + "loc": { + "start": { + "line": 1, + "column": 0 + }, + "end": { + "line": 5, + "column": 1 + } + }, + "sourceType": "script", + "body": [ + { + "type": "ClassDeclaration", + "start": 0, + "end": 102, + "loc": { + "start": { + "line": 1, + "column": 0 + }, + "end": { + "line": 5, + "column": 1 + } + }, + "id": { + "type": "Identifier", + "start": 6, + "end": 15, + "loc": { + "start": { + "line": 1, + "column": 6 + }, + "end": { + "line": 1, + "column": 15 + }, + "identifierName": "NodeUtils" + }, + "name": "NodeUtils" + }, + "superClass": { + "type": "Identifier", + "start": 24, + "end": 34, + "loc": { + "start": { + "line": 1, + "column": 24 + }, + "end": { + "line": 1, + "column": 34 + }, + "identifierName": "UtilParser" + }, + "name": "UtilParser" + }, + "body": { + "type": "ClassBody", + "start": 35, + "end": 102, + "loc": { + "start": { + "line": 1, + "column": 35 + }, + "end": { + "line": 5, + "column": 1 + } + }, + "body": [ + { + "type": "MethodDefinition", + "start": 39, + "end": 100, + "loc": { + "start": { + "line": 2, + "column": 2 + }, + "end": { + "line": 4, + "column": 3 + } + }, + "static": false, + "computed": false, + "key": { + "type": "Identifier", + "start": 39, + "end": 51, + "loc": { + "start": { + "line": 2, + "column": 2 + }, + "end": { + "line": 2, + "column": 14 + }, + "identifierName": "finishNodeAt" + }, + "name": "finishNodeAt" + }, + "kind": "method", + "value": { + "type": "FunctionExpression", + "start": 65, + "end": 100, + "loc": { + "start": { + "line": 2, + "column": 28 + }, + "end": { + "line": 4, + "column": 3 + } + }, + "id": null, + "generator": false, + "expression": false, + "async": false, + "params": [ + { + "type": "Identifier", + "start": 66, + "end": 73, + "loc": { + "start": { + "line": 2, + "column": 29 + }, + "end": { + "line": 2, + "column": 36 + }, + "identifierName": "node" + }, + "name": "node", + "typeAnnotation": { + "type": "TypeAnnotation", + "start": 70, + "end": 73, + "loc": { + "start": { + "line": 2, + "column": 33 + }, + "end": { + "line": 2, + "column": 36 + } + }, + "typeAnnotation": { + "type": "GenericTypeAnnotation", + "start": 72, + "end": 73, + "loc": { + "start": { + "line": 2, + "column": 35 + }, + "end": { + "line": 2, + "column": 36 + } + }, + "typeParameters": null, + "id": { + "type": "Identifier", + "start": 72, + "end": 73, + "loc": { + "start": { + "line": 2, + "column": 35 + }, + "end": { + "line": 2, + "column": 36 + }, + "identifierName": "T" + }, + "name": "T" + } + } + } + } + ], + "predicate": null, + "returnType": { + "type": "TypeAnnotation", + "start": 74, + "end": 77, + "loc": { + "start": { + "line": 2, + "column": 37 + }, + "end": { + "line": 2, + "column": 40 + } + }, + "typeAnnotation": { + "type": "GenericTypeAnnotation", + "start": 76, + "end": 77, + "loc": { + "start": { + "line": 2, + "column": 39 + }, + "end": { + "line": 2, + "column": 40 + } + }, + "typeParameters": null, + "id": { + "type": "Identifier", + "start": 76, + "end": 77, + "loc": { + "start": { + "line": 2, + "column": 39 + }, + "end": { + "line": 2, + "column": 40 + }, + "identifierName": "T" + }, + "name": "T" + } + } + }, + "body": { + "type": "BlockStatement", + "start": 78, + "end": 100, + "loc": { + "start": { + "line": 2, + "column": 41 + }, + "end": { + "line": 4, + "column": 3 + } + }, + "body": [ + { + "type": "ReturnStatement", + "start": 84, + "end": 96, + "loc": { + "start": { + "line": 3, + "column": 4 + }, + "end": { + "line": 3, + "column": 16 + } + }, + "argument": { + "type": "Identifier", + "start": 91, + "end": 95, + "loc": { + "start": { + "line": 3, + "column": 11 + }, + "end": { + "line": 3, + "column": 15 + }, + "identifierName": "node" + }, + "name": "node" + } + } + ] + }, + "typeParameters": { + "type": "TypeParameterDeclaration", + "start": 51, + "end": 65, + "loc": { + "start": { + "line": 2, + "column": 14 + }, + "end": { + "line": 2, + "column": 28 + } + }, + "params": [ + { + "type": "TypeParameter", + "start": 52, + "end": 64, + "loc": { + "start": { + "line": 2, + "column": 15 + }, + "end": { + "line": 2, + "column": 27 + } + }, + "name": "T", + "variance": null, + "bound": { + "type": "TypeAnnotation", + "start": 54, + "end": 64, + "loc": { + "start": { + "line": 2, + "column": 17 + }, + "end": { + "line": 2, + "column": 27 + } + }, + "typeAnnotation": { + "type": "GenericTypeAnnotation", + "start": 56, + "end": 64, + "loc": { + "start": { + "line": 2, + "column": 19 + }, + "end": { + "line": 2, + "column": 27 + } + }, + "typeParameters": null, + "id": { + "type": "Identifier", + "start": 56, + "end": 64, + "loc": { + "start": { + "line": 2, + "column": 19 + }, + "end": { + "line": 2, + "column": 27 + }, + "identifierName": "NodeType" + }, + "name": "NodeType" + } + } + } + } + ] + } + } + } + ] + } + } + ] + } +} \ No newline at end of file