@@ -4865,18 +4865,45 @@ namespace ts {
4865
4865
return finishNode ( factory . createPropertyAccessExpression ( expression , parseRightSideOfDot ( /*allowIdentifierNames*/ true , /*allowPrivateIdentifiers*/ true ) ) , pos ) ;
4866
4866
}
4867
4867
4868
- function parseJsxElementOrSelfClosingElementOrFragment ( inExpressionContext : boolean , topInvalidNodePosition ?: number ) : JsxElement | JsxSelfClosingElement | JsxFragment {
4868
+ function parseJsxElementOrSelfClosingElementOrFragment ( inExpressionContext : boolean , topInvalidNodePosition ?: number , openingTag ?: JsxOpeningElement | JsxOpeningFragment ) : JsxElement | JsxSelfClosingElement | JsxFragment {
4869
4869
const pos = getNodePos ( ) ;
4870
4870
const opening = parseJsxOpeningOrSelfClosingElementOrOpeningFragment ( inExpressionContext ) ;
4871
4871
let result : JsxElement | JsxSelfClosingElement | JsxFragment ;
4872
4872
if ( opening . kind === SyntaxKind . JsxOpeningElement ) {
4873
- const children = parseJsxChildren ( opening ) ;
4874
- const closingElement = parseJsxClosingElement ( inExpressionContext ) ;
4875
-
4876
- if ( ! tagNamesAreEquivalent ( opening . tagName , closingElement . tagName ) ) {
4877
- parseErrorAtRange ( closingElement , Diagnostics . Expected_corresponding_JSX_closing_tag_for_0 , getTextOfNodeFromSourceText ( sourceText , opening . tagName ) ) ;
4873
+ let children = parseJsxChildren ( opening ) ;
4874
+ let closingElement : JsxClosingElement ;
4875
+
4876
+ const lastChild : JsxChild | undefined = children [ children . length - 1 ] ;
4877
+ if ( lastChild ?. kind === SyntaxKind . JsxElement
4878
+ && ! tagNamesAreEquivalent ( lastChild . openingElement . tagName , lastChild . closingElement . tagName )
4879
+ && tagNamesAreEquivalent ( opening . tagName , lastChild . closingElement . tagName ) ) {
4880
+ // when an unclosed JsxOpeningElement incorrectly parses its parent's JsxClosingElement,
4881
+ // restructure (<div>(...<span></div>)) --> (<div>(...<span></span>)</div>)
4882
+ // (no need to error; the parent will error)
4883
+ const end = lastChild . openingElement . end ; // newly-created children and closing are both zero-width end/end
4884
+ const newLast = finishNode ( factory . createJsxElement (
4885
+ lastChild . openingElement ,
4886
+ createNodeArray ( [ ] , end , end ) ,
4887
+ finishNode ( factory . createJsxClosingElement ( finishNode ( factory . createIdentifier ( "" ) , end , end ) ) , end , end ) ) ,
4888
+ lastChild . openingElement . pos ,
4889
+ end ) ;
4890
+
4891
+ children = createNodeArray ( [ ...children . slice ( 0 , children . length - 1 ) , newLast ] , children . pos , end ) ;
4892
+ closingElement = lastChild . closingElement ;
4893
+ }
4894
+ else {
4895
+ closingElement = parseJsxClosingElement ( opening , inExpressionContext ) ;
4896
+ if ( ! tagNamesAreEquivalent ( opening . tagName , closingElement . tagName ) ) {
4897
+ if ( openingTag && isJsxOpeningElement ( openingTag ) && tagNamesAreEquivalent ( closingElement . tagName , openingTag . tagName ) ) {
4898
+ // opening incorrectly matched with its parent's closing -- put error on opening
4899
+ parseErrorAtRange ( opening . tagName , Diagnostics . JSX_element_0_has_no_corresponding_closing_tag , getTextOfNodeFromSourceText ( sourceText , opening . tagName ) ) ;
4900
+ }
4901
+ else {
4902
+ // other opening/closing mismatches -- put error on closing
4903
+ parseErrorAtRange ( closingElement . tagName , Diagnostics . Expected_corresponding_JSX_closing_tag_for_0 , getTextOfNodeFromSourceText ( sourceText , opening . tagName ) ) ;
4904
+ }
4905
+ }
4878
4906
}
4879
-
4880
4907
result = finishNode ( factory . createJsxElement ( opening , children , closingElement ) , pos ) ;
4881
4908
}
4882
4909
else if ( opening . kind === SyntaxKind . JsxOpeningFragment ) {
@@ -4941,7 +4968,7 @@ namespace ts {
4941
4968
case SyntaxKind . OpenBraceToken :
4942
4969
return parseJsxExpression ( /*inExpressionContext*/ false ) ;
4943
4970
case SyntaxKind . LessThanToken :
4944
- return parseJsxElementOrSelfClosingElementOrFragment ( /*inExpressionContext*/ false ) ;
4971
+ return parseJsxElementOrSelfClosingElementOrFragment ( /*inExpressionContext*/ false , /*topInvalidNodePosition*/ undefined , openingTag ) ;
4945
4972
default :
4946
4973
return Debug . assertNever ( token ) ;
4947
4974
}
@@ -4957,6 +4984,13 @@ namespace ts {
4957
4984
const child = parseJsxChild ( openingTag , currentToken = scanner . reScanJsxToken ( ) ) ;
4958
4985
if ( ! child ) break ;
4959
4986
list . push ( child ) ;
4987
+ if ( isJsxOpeningElement ( openingTag )
4988
+ && child ?. kind === SyntaxKind . JsxElement
4989
+ && ! tagNamesAreEquivalent ( child . openingElement . tagName , child . closingElement . tagName )
4990
+ && tagNamesAreEquivalent ( openingTag . tagName , child . closingElement . tagName ) ) {
4991
+ // stop after parsing a mismatched child like <div>...(<span></div>) in order to reattach the </div> higher
4992
+ break ;
4993
+ }
4960
4994
}
4961
4995
4962
4996
parsingContext = saveParsingContext ;
@@ -4978,7 +5012,6 @@ namespace ts {
4978
5012
scanJsxText ( ) ;
4979
5013
return finishNode ( factory . createJsxOpeningFragment ( ) , pos ) ;
4980
5014
}
4981
-
4982
5015
const tagName = parseJsxElementName ( ) ;
4983
5016
const typeArguments = ( contextFlags & NodeFlags . JavaScriptFile ) === 0 ? tryParseTypeArguments ( ) : undefined ;
4984
5017
const attributes = parseJsxAttributes ( ) ;
@@ -4994,12 +5027,14 @@ namespace ts {
4994
5027
}
4995
5028
else {
4996
5029
parseExpected ( SyntaxKind . SlashToken ) ;
4997
- if ( inExpressionContext ) {
4998
- parseExpected ( SyntaxKind . GreaterThanToken ) ;
4999
- }
5000
- else {
5001
- parseExpected ( SyntaxKind . GreaterThanToken , /*diagnostic*/ undefined , /*shouldAdvance*/ false ) ;
5002
- scanJsxText ( ) ;
5030
+ if ( parseExpected ( SyntaxKind . GreaterThanToken , /*diagnostic*/ undefined , /*shouldAdvance*/ false ) ) {
5031
+ // manually advance the scanner in order to look for jsx text inside jsx
5032
+ if ( inExpressionContext ) {
5033
+ nextToken ( ) ;
5034
+ }
5035
+ else {
5036
+ scanJsxText ( ) ;
5037
+ }
5003
5038
}
5004
5039
node = factory . createJsxSelfClosingElement ( tagName , typeArguments , attributes ) ;
5005
5040
}
@@ -5077,16 +5112,18 @@ namespace ts {
5077
5112
return finishNode ( factory . createJsxSpreadAttribute ( expression ) , pos ) ;
5078
5113
}
5079
5114
5080
- function parseJsxClosingElement ( inExpressionContext : boolean ) : JsxClosingElement {
5115
+ function parseJsxClosingElement ( open : JsxOpeningElement , inExpressionContext : boolean ) : JsxClosingElement {
5081
5116
const pos = getNodePos ( ) ;
5082
5117
parseExpected ( SyntaxKind . LessThanSlashToken ) ;
5083
5118
const tagName = parseJsxElementName ( ) ;
5084
- if ( inExpressionContext ) {
5085
- parseExpected ( SyntaxKind . GreaterThanToken ) ;
5086
- }
5087
- else {
5088
- parseExpected ( SyntaxKind . GreaterThanToken , /*diagnostic*/ undefined , /*shouldAdvance*/ false ) ;
5089
- scanJsxText ( ) ;
5119
+ if ( parseExpected ( SyntaxKind . GreaterThanToken , /*diagnostic*/ undefined , /*shouldAdvance*/ false ) ) {
5120
+ // manually advance the scanner in order to look for jsx text inside jsx
5121
+ if ( inExpressionContext || ! tagNamesAreEquivalent ( open . tagName , tagName ) ) {
5122
+ nextToken ( ) ;
5123
+ }
5124
+ else {
5125
+ scanJsxText ( ) ;
5126
+ }
5090
5127
}
5091
5128
return finishNode ( factory . createJsxClosingElement ( tagName ) , pos ) ;
5092
5129
}
@@ -5097,12 +5134,14 @@ namespace ts {
5097
5134
if ( tokenIsIdentifierOrKeyword ( token ( ) ) ) {
5098
5135
parseErrorAtRange ( parseJsxElementName ( ) , Diagnostics . Expected_corresponding_closing_tag_for_JSX_fragment ) ;
5099
5136
}
5100
- if ( inExpressionContext ) {
5101
- parseExpected ( SyntaxKind . GreaterThanToken ) ;
5102
- }
5103
- else {
5104
- parseExpected ( SyntaxKind . GreaterThanToken , /*diagnostic*/ undefined , /*shouldAdvance*/ false ) ;
5105
- scanJsxText ( ) ;
5137
+ if ( parseExpected ( SyntaxKind . GreaterThanToken , /*diagnostic*/ undefined , /*shouldAdvance*/ false ) ) {
5138
+ // manually advance the scanner in order to look for jsx text inside jsx
5139
+ if ( inExpressionContext ) {
5140
+ nextToken ( ) ;
5141
+ }
5142
+ else {
5143
+ scanJsxText ( ) ;
5144
+ }
5106
5145
}
5107
5146
return finishNode ( factory . createJsxJsxClosingFragment ( ) , pos ) ;
5108
5147
}
0 commit comments