From de5984c67eec2e46d199067a6691557cd6923a21 Mon Sep 17 00:00:00 2001 From: TyrealHu Date: Fri, 20 Oct 2023 17:02:57 +0800 Subject: [PATCH] =?UTF-8?q?fix:=20=F0=9F=90=9Bbugs=20of=20left=20relationa?= =?UTF-8?q?l=20and=20right=20relational?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- __test__/__snapshot__/jsx/index.ts | 438 +++++++++++++++++++++++++++++ __test__/jsx/index.test.ts | 13 + src/index.ts | 44 +-- 3 files changed, 477 insertions(+), 18 deletions(-) diff --git a/__test__/__snapshot__/jsx/index.ts b/__test__/__snapshot__/jsx/index.ts index 2b152ad..7bd457f 100644 --- a/__test__/__snapshot__/jsx/index.ts +++ b/__test__/__snapshot__/jsx/index.ts @@ -3339,6 +3339,444 @@ const JSXSnapshot = { ], 'sourceType': 'module' }, + Issue46: { + 'type': 'Program', + 'start': 0, + 'end': 150, + 'loc': { + 'start': { + 'line': 1, + 'column': 0, + 'index': 0 + }, + 'end': { + 'line': 6, + 'column': 0, + 'index': 150 + } + }, + 'body': [ + { + 'type': 'ImportDeclaration', + 'start': 1, + 'end': 31, + 'loc': { + 'start': { + 'line': 2, + 'column': 0, + 'index': 1 + }, + 'end': { + 'line': 2, + 'column': 30, + 'index': 31 + } + }, + 'importKind': 'value', + 'specifiers': [ + { + 'type': 'ImportNamespaceSpecifier', + 'start': 8, + 'end': 18, + 'loc': { + 'start': { + 'line': 2, + 'column': 7, + 'index': 8 + }, + 'end': { + 'line': 2, + 'column': 17, + 'index': 18 + } + }, + 'local': { + 'type': 'Identifier', + 'start': 13, + 'end': 18, + 'loc': { + 'start': { + 'line': 2, + 'column': 12, + 'index': 13 + }, + 'end': { + 'line': 2, + 'column': 17, + 'index': 18 + } + }, + 'name': 'React' + } + } + ], + 'source': { + 'type': 'Literal', + 'start': 24, + 'end': 31, + 'loc': { + 'start': { + 'line': 2, + 'column': 23, + 'index': 24 + }, + 'end': { + 'line': 2, + 'column': 30, + 'index': 31 + } + }, + 'value': 'react', + 'raw': '\'react\'' + } + }, + { + 'type': 'ImportDeclaration', + 'start': 32, + 'end': 82, + 'loc': { + 'start': { + 'line': 3, + 'column': 0, + 'index': 32 + }, + 'end': { + 'line': 3, + 'column': 50, + 'index': 82 + } + }, + 'importKind': 'value', + 'specifiers': [ + { + 'type': 'ImportSpecifier', + 'start': 41, + 'end': 55, + 'loc': { + 'start': { + 'line': 3, + 'column': 9, + 'index': 41 + }, + 'end': { + 'line': 3, + 'column': 23, + 'index': 55 + } + }, + 'imported': { + 'type': 'Identifier', + 'start': 41, + 'end': 45, + 'loc': { + 'start': { + 'line': 3, + 'column': 9, + 'index': 41 + }, + 'end': { + 'line': 3, + 'column': 13, + 'index': 45 + } + }, + 'name': 'Link' + }, + 'local': { + 'type': 'Identifier', + 'start': 49, + 'end': 55, + 'loc': { + 'start': { + 'line': 3, + 'column': 17, + 'index': 49 + }, + 'end': { + 'line': 3, + 'column': 23, + 'index': 55 + } + }, + 'name': 'RRLink' + }, + 'importKind': 'value' + } + ], + 'source': { + 'type': 'Literal', + 'start': 63, + 'end': 81, + 'loc': { + 'start': { + 'line': 3, + 'column': 31, + 'index': 63 + }, + 'end': { + 'line': 3, + 'column': 49, + 'index': 81 + } + }, + 'value': 'react-router-dom', + 'raw': '\'react-router-dom\'' + } + }, + { + 'type': 'VariableDeclaration', + 'start': 84, + 'end': 149, + 'loc': { + 'start': { + 'line': 5, + 'column': 0, + 'index': 84 + }, + 'end': { + 'line': 5, + 'column': 65, + 'index': 149 + } + }, + 'declarations': [ + { + 'type': 'VariableDeclarator', + 'start': 90, + 'end': 149, + 'loc': { + 'start': { + 'line': 5, + 'column': 6, + 'index': 90 + }, + 'end': { + 'line': 5, + 'column': 65, + 'index': 149 + } + }, + 'id': { + 'type': 'Identifier', + 'start': 90, + 'end': 94, + 'loc': { + 'start': { + 'line': 5, + 'column': 6, + 'index': 90 + }, + 'end': { + 'line': 5, + 'column': 10, + 'index': 94 + } + }, + 'name': 'Link' + }, + 'init': { + 'type': 'ArrowFunctionExpression', + 'start': 97, + 'end': 149, + 'loc': { + 'start': { + 'line': 5, + 'column': 13, + 'index': 97 + }, + 'end': { + 'line': 5, + 'column': 65, + 'index': 149 + } + }, + 'id': null, + 'expression': true, + 'generator': false, + 'async': false, + 'params': [ + { + 'type': 'Identifier', + 'start': 98, + 'loc': { + 'start': { + 'line': 5, + 'column': 14, + 'index': 98 + }, + 'end': 140 + }, + 'name': 'props', + 'typeAnnotation': { + 'type': 'TSTypeAnnotation', + 'start': 103, + 'end': 140, + 'loc': { + 'start': { + 'line': 5, + 'column': 19, + 'index': 103 + }, + 'end': { + 'line': 5, + 'column': 56, + 'index': 140 + } + }, + 'typeAnnotation': { + 'type': 'TSTypeReference', + 'start': 105, + 'end': 140, + 'loc': { + 'start': { + 'line': 5, + 'column': 21, + 'index': 105 + }, + 'end': { + 'line': 5, + 'column': 56, + 'index': 140 + } + }, + 'typeName': { + 'type': 'TSQualifiedName', + 'start': 105, + 'end': 125, + 'loc': { + 'start': { + 'line': 5, + 'column': 21, + 'index': 105 + }, + 'end': { + 'line': 5, + 'column': 41, + 'index': 125 + } + }, + 'left': { + 'type': 'Identifier', + 'start': 105, + 'end': 110, + 'loc': { + 'start': { + 'line': 5, + 'column': 21, + 'index': 105 + }, + 'end': { + 'line': 5, + 'column': 26, + 'index': 110 + } + }, + 'name': 'React' + }, + 'right': { + 'type': 'Identifier', + 'start': 111, + 'end': 125, + 'loc': { + 'start': { + 'line': 5, + 'column': 27, + 'index': 111 + }, + 'end': { + 'line': 5, + 'column': 41, + 'index': 125 + } + }, + 'name': 'ComponentProps' + } + }, + 'typeParameters': { + 'type': 'TSTypeParameterInstantiation', + 'start': 125, + 'end': 140, + 'loc': { + 'start': { + 'line': 5, + 'column': 41, + 'index': 125 + }, + 'end': { + 'line': 5, + 'column': 56, + 'index': 140 + } + }, + 'params': [ + { + 'type': 'TSTypeQuery', + 'start': 126, + 'end': 139, + 'loc': { + 'start': { + 'line': 5, + 'column': 42, + 'index': 126 + }, + 'end': { + 'line': 5, + 'column': 55, + 'index': 139 + } + }, + 'exprName': { + 'type': 'Identifier', + 'start': 133, + 'end': 139, + 'loc': { + 'start': { + 'line': 5, + 'column': 49, + 'index': 133 + }, + 'end': { + 'line': 5, + 'column': 55, + 'index': 139 + } + }, + 'name': 'RRLink' + } + } + ] + } + } + } + } + ], + 'body': { + 'type': 'Literal', + 'start': 145, + 'end': 149, + 'loc': { + 'start': { + 'line': 5, + 'column': 61, + 'index': 145 + }, + 'end': { + 'line': 5, + 'column': 65, + 'index': 149 + } + }, + 'value': null, + 'raw': 'null' + } + } + } + ], + 'kind': 'const' + } + ], + 'sourceType': 'module' + }, Issue29Jsx: { 'type': 'Program', 'start': 0, diff --git a/__test__/jsx/index.test.ts b/__test__/jsx/index.test.ts index 7cd34ad..ad09a98 100644 --- a/__test__/jsx/index.test.ts +++ b/__test__/jsx/index.test.ts @@ -1,6 +1,13 @@ import { equalNode, generateSource, parseSource } from '../utils' import JSXSnapshot from '../__snapshot__/jsx' +const issue46File = ` +import * as React from 'react' +import { Link as RRLink } from 'react-router-dom'; + +const Link = (props: React.ComponentProps) => null +`; + describe('jsx', function() { it('simple', () => { const node = parseSource(generateSource([ @@ -94,4 +101,10 @@ describe('jsx', function() { equalNode(node, JSXSnapshot.Issue29Jsx) }) + + it('issue 46', () => { + const node = parseSource(issue46File) + + equalNode(node, JSXSnapshot.Issue46) + }) }) diff --git a/src/index.ts b/src/index.ts index 5c3fe6b..8eb8cb3 100644 --- a/src/index.ts +++ b/src/index.ts @@ -456,7 +456,7 @@ function tsPlugin(options?: { startLoc: Position, forInit: boolean ): any | undefined | null { - if (!this.match(tt.relational)) { + if (!this.tsMatchLeftRelational()) { return undefined } @@ -1112,7 +1112,7 @@ function tsPlugin(options?: { case 'TupleElementTypes': return this.match(tt.bracketR) case 'TypeParametersOrArguments': - return this.match(tt.relational) && this.value === '>' + return this.tsMatchRightRelational() } } @@ -1304,7 +1304,7 @@ function tsPlugin(options?: { } tsIsStartOfFunctionType() { - if (this.match(tt.relational)) { + if (this.tsMatchLeftRelational()) { return true } return ( @@ -1628,7 +1628,7 @@ function tsPlugin(options?: { // qualifier, so allow it to be a reserved word as well. node.qualifier = this.tsParseEntityName() } - if (this.match(tt.relational)) { + if (this.tsMatchLeftRelational()) { node.typeParameters = this.tsParseTypeArguments() } return this.finishNode(node, 'TSImportType') @@ -1642,7 +1642,7 @@ function tsPlugin(options?: { } else { node.exprName = this.tsParseEntityName() } - if (!this.hasPrecedingLineBreak() && this.match(tt.relational)) { + if (!this.hasPrecedingLineBreak() && this.tsMatchLeftRelational()) { node.typeParameters = this.tsParseTypeArguments() } return this.finishNode(node, 'TSTypeQuery') @@ -1791,12 +1791,20 @@ function tsPlugin(options?: { tsParseTypeReference(): any { const node = this.startNode() node.typeName = this.tsParseEntityName() - if (!this.hasPrecedingLineBreak() && this.match(tt.relational) && this.value === '<') { + if (!this.hasPrecedingLineBreak() && this.tsMatchLeftRelational()) { node.typeParameters = this.tsParseTypeArguments() } return this.finishNode(node, 'TSTypeReference') } + tsMatchLeftRelational() { + return this.match(tt.relational) && this.value === '<' + } + + tsMatchRightRelational() { + return this.match(tt.relational) && this.value === '>' + } + tsParseParenthesizedType(): any { const node = this.startNode() this.expect(tt.parenL) @@ -2055,7 +2063,7 @@ function tsPlugin(options?: { ) { const node = this.startNode() - if (this.match(tt.relational) || this.matchJsx('jsxTagStart')) { + if (this.tsMatchLeftRelational() || this.matchJsx('jsxTagStart')) { this.next() } else { this.unexpected() @@ -2082,7 +2090,7 @@ function tsPlugin(options?: { tsTryParseTypeParameters( parseModifiers?: ((node) => void) | null ) { - if (this.match(tt.relational)) { + if (this.tsMatchLeftRelational()) { return this.tsParseTypeParameters(parseModifiers) } } @@ -2321,7 +2329,7 @@ function tsPlugin(options?: { () => { const node = this.startNode() node.expression = this.tsParseEntityName() - if (this.match(tt.relational)) { + if (this.tsMatchLeftRelational()) { node.typeParameters = this.tsParseTypeArguments() } @@ -2373,12 +2381,12 @@ function tsPlugin(options?: { if (this.eat(tt.question)) node.optional = true const nodeAny: any = node - if (this.match(tt.parenL) || this.match(tt.relational)) { + if (this.match(tt.parenL) || this.tsMatchLeftRelational()) { if (readonly) { this.raise(node.start, TypeScriptError.ReadonlyForMethodSignature) } const method = nodeAny - if (method.kind && this.match(tt.relational)) { + if (method.kind && this.tsMatchLeftRelational()) { this.raise(this.start, TypeScriptError.AccesorCannotHaveTypeParameters) } this.tsFillSignature(tt.colon, method) @@ -2431,7 +2439,7 @@ function tsPlugin(options?: { tsParseTypeMember(): any { const node: any = this.startNode() - if (this.match(tt.parenL) || this.match(tt.relational)) { + if (this.match(tt.parenL) || this.tsMatchLeftRelational()) { return this.tsParseSignatureMember('TSCallSignatureDeclaration', node) } @@ -2439,7 +2447,7 @@ function tsPlugin(options?: { const id = this.startNode() this.next() - if (this.match(tt.parenL) || this.match(tt.relational)) { + if (this.match(tt.parenL) || this.tsMatchLeftRelational()) { return this.tsParseSignatureMember( 'TSConstructSignatureDeclaration', node @@ -3987,7 +3995,7 @@ function tsPlugin(options?: { parseClassSuper(node: any): void { super.parseClassSuper(node) // handle `extends f< - if (node.superClass && (this.match(tt.relational) || this.match(tt.bitShift))) { + if (node.superClass && (this.tsMatchLeftRelational() || this.match(tt.bitShift))) { node.superTypeParameters = this.tsParseTypeArgumentsInExpression() } if (this.eatContextual('implements')) { @@ -4131,7 +4139,7 @@ function tsPlugin(options?: { let typeCast if ( - this.matchJsx('jsxTagStart') || this.match(tt.relational) + this.matchJsx('jsxTagStart') || this.tsMatchLeftRelational() ) { // Prefer to parse JSX if possible. But may be an arrow fn. state = this.cloneCurLookaheadState() @@ -4159,7 +4167,7 @@ function tsPlugin(options?: { } } - if (!jsx?.error && !this.match(tt.relational)) { + if (!jsx?.error && !this.tsMatchLeftRelational()) { return this.parseMaybeAssignOrigin(forInit, refExpressionErrors, afterLeftParse) } @@ -4703,7 +4711,7 @@ function tsPlugin(options?: { } // handles 'f<' - if (this.match(tt.relational) || this.match(tt.bitShift)) { + if (this.tsMatchLeftRelational() || this.match(tt.bitShift)) { let missingParenErrorLoc // tsTryParseAndCatch is expensive, so avoid if not necessary. // There are number of things we are going to "maybe" parse, like type arguments on @@ -4765,7 +4773,7 @@ function tsPlugin(options?: { const tokenType = this.type if ( // a>c is not (a)>c, but a<(b>>c) - tokenType === tt.relational || + this.tsMatchRightRelational() || // a>>c is not (a)>>c, but a<(b>>>c) tokenType === tt.bitShift || // ac is (ac