Skip to content

Commit a1a2006

Browse files
authored
Merge pull request #15160 from Microsoft/master-jsxChildren
[Master] Type checking JSX children
2 parents d190530 + e7e13ec commit a1a2006

File tree

77 files changed

+3064
-239
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

77 files changed

+3064
-239
lines changed

src/compiler/checker.ts

Lines changed: 153 additions & 76 deletions
Large diffs are not rendered by default.

src/compiler/diagnosticMessages.json

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2119,6 +2119,10 @@
21192119
"category": "Error",
21202120
"code": 2709
21212121
},
2122+
"'{0}' are specified twice. The attribute named '{0}' will be overwritten.": {
2123+
"category": "Error",
2124+
"code": 2710
2125+
},
21222126

21232127
"Import declaration '{0}' is using private name '{1}'.": {
21242128
"category": "Error",

src/compiler/emitter.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2457,7 +2457,7 @@ namespace ts {
24572457
let indentation: number;
24582458
for (const line of lines) {
24592459
for (let i = 0; i < line.length && (indentation === undefined || i < indentation); i++) {
2460-
if (!isWhiteSpace(line.charCodeAt(i))) {
2460+
if (!isWhiteSpaceLike(line.charCodeAt(i))) {
24612461
if (indentation === undefined || i < indentation) {
24622462
indentation = i;
24632463
break;

src/compiler/parser.ts

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3828,13 +3828,15 @@ namespace ts {
38283828

38293829
function parseJsxText(): JsxText {
38303830
const node = <JsxText>createNode(SyntaxKind.JsxText, scanner.getStartPos());
3831+
node.containsOnlyWhiteSpaces = currentToken === SyntaxKind.JsxTextAllWhiteSpaces;
38313832
currentToken = scanner.scanJsxToken();
38323833
return finishNode(node);
38333834
}
38343835

38353836
function parseJsxChild(): JsxChild {
38363837
switch (token()) {
38373838
case SyntaxKind.JsxText:
3839+
case SyntaxKind.JsxTextAllWhiteSpaces:
38383840
return parseJsxText();
38393841
case SyntaxKind.OpenBraceToken:
38403842
return parseJsxExpression(/*inExpressionContext*/ false);
@@ -3864,7 +3866,10 @@ namespace ts {
38643866
else if (token() === SyntaxKind.ConflictMarkerTrivia) {
38653867
break;
38663868
}
3867-
result.push(parseJsxChild());
3869+
const child = parseJsxChild();
3870+
if (child) {
3871+
result.push(child);
3872+
}
38683873
}
38693874

38703875
result.end = scanner.getTokenPos();

src/compiler/scanner.ts

Lines changed: 24 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -367,7 +367,7 @@ namespace ts {
367367
return computeLineAndCharacterOfPosition(getLineStarts(sourceFile), position);
368368
}
369369

370-
export function isWhiteSpace(ch: number): boolean {
370+
export function isWhiteSpaceLike(ch: number): boolean {
371371
return isWhiteSpaceSingleLine(ch) || isLineBreak(ch);
372372
}
373373

@@ -512,7 +512,7 @@ namespace ts {
512512
break;
513513

514514
default:
515-
if (ch > CharacterCodes.maxAsciiCharacter && (isWhiteSpace(ch))) {
515+
if (ch > CharacterCodes.maxAsciiCharacter && (isWhiteSpaceLike(ch))) {
516516
pos++;
517517
continue;
518518
}
@@ -694,7 +694,7 @@ namespace ts {
694694
}
695695
break scan;
696696
default:
697-
if (ch > CharacterCodes.maxAsciiCharacter && (isWhiteSpace(ch))) {
697+
if (ch > CharacterCodes.maxAsciiCharacter && (isWhiteSpaceLike(ch))) {
698698
if (hasPendingCommentRange && isLineBreak(ch)) {
699699
pendingHasTrailingNewLine = true;
700700
}
@@ -1727,8 +1727,12 @@ namespace ts {
17271727
return token = SyntaxKind.OpenBraceToken;
17281728
}
17291729

1730+
// First non-whitespace character on this line.
1731+
let firstNonWhitespace = 0;
1732+
// These initial values are special because the first line is:
1733+
// firstNonWhitespace = 0 to indicate that we want leading whitspace,
1734+
17301735
while (pos < end) {
1731-
pos++;
17321736
char = text.charCodeAt(pos);
17331737
if (char === CharacterCodes.openBrace) {
17341738
break;
@@ -1740,8 +1744,23 @@ namespace ts {
17401744
}
17411745
break;
17421746
}
1747+
1748+
// FirstNonWhitespace is 0, then we only see whitespaces so far. If we see a linebreak, we want to ignore that whitespaces.
1749+
// i.e (- : whitespace)
1750+
// <div>----
1751+
// </div> becomes <div></div>
1752+
//
1753+
// <div>----</div> becomes <div>----</div>
1754+
if (isLineBreak(char) && firstNonWhitespace === 0) {
1755+
firstNonWhitespace = -1;
1756+
}
1757+
else if (!isWhiteSpaceLike(char)) {
1758+
firstNonWhitespace = pos;
1759+
}
1760+
pos++;
17431761
}
1744-
return token = SyntaxKind.JsxText;
1762+
1763+
return firstNonWhitespace === -1 ? SyntaxKind.JsxTextAllWhiteSpaces : SyntaxKind.JsxText;
17451764
}
17461765

17471766
// Scans a JSX identifier; these differ from normal identifiers in that

src/compiler/types.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ namespace ts {
1010

1111
/** ES6 Map interface. */
1212
export interface Map<T> {
13-
get(key: string): T;
13+
get(key: string): T | undefined;
1414
has(key: string): boolean;
1515
set(key: string, value: T): this;
1616
delete(key: string): boolean;
@@ -65,6 +65,7 @@ namespace ts {
6565
NumericLiteral,
6666
StringLiteral,
6767
JsxText,
68+
JsxTextAllWhiteSpaces,
6869
RegularExpressionLiteral,
6970
NoSubstitutionTemplateLiteral,
7071
// Pseudo-literals
@@ -1572,6 +1573,7 @@ namespace ts {
15721573

15731574
export interface JsxText extends Node {
15741575
kind: SyntaxKind.JsxText;
1576+
containsOnlyWhiteSpaces: boolean;
15751577
parent?: JsxElement;
15761578
}
15771579

src/services/formatting/smartIndenter.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,7 @@ namespace ts.formatting {
5454
let current = position;
5555
while (current > 0) {
5656
const char = sourceFile.text.charCodeAt(current);
57-
if (!isWhiteSpace(char)) {
57+
if (!isWhiteSpaceLike(char)) {
5858
break;
5959
}
6060
current--;

src/services/textChanges.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -608,7 +608,7 @@ namespace ts.textChanges {
608608
if (force || !isTrivia(s)) {
609609
this.lastNonTriviaPosition = this.writer.getTextPos();
610610
let i = 0;
611-
while (isWhiteSpace(s.charCodeAt(s.length - i - 1))) {
611+
while (isWhiteSpaceLike(s.charCodeAt(s.length - i - 1))) {
612612
i++;
613613
}
614614
// trim trailing whitespaces

src/services/utilities.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1380,7 +1380,7 @@ namespace ts {
13801380
}
13811381

13821382
export function getFirstNonSpaceCharacterPosition(text: string, position: number) {
1383-
while (isWhiteSpace(text.charCodeAt(position))) {
1383+
while (isWhiteSpaceLike(text.charCodeAt(position))) {
13841384
position += 1;
13851385
}
13861386
return position;
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
//// [file.tsx]
2+
import React = require('react');
3+
4+
interface Prop {
5+
a: number,
6+
b: string,
7+
children: string | JSX.Element
8+
}
9+
10+
function Comp(p: Prop) {
11+
return <div>{p.b}</div>;
12+
}
13+
14+
// OK
15+
let k = <Comp a={10} b="hi" children ="lol" />;
16+
let k1 =
17+
<Comp a={10} b="hi">
18+
hi hi hi!
19+
</Comp>;
20+
let k2 =
21+
<Comp a={10} b="hi">
22+
<div>hi hi hi!</div>
23+
</Comp>;
24+
25+
//// [file.jsx]
26+
"use strict";
27+
exports.__esModule = true;
28+
var React = require("react");
29+
function Comp(p) {
30+
return <div>{p.b}</div>;
31+
}
32+
// OK
33+
var k = <Comp a={10} b="hi" children="lol"/>;
34+
var k1 = <Comp a={10} b="hi">
35+
hi hi hi!
36+
</Comp>;
37+
var k2 = <Comp a={10} b="hi">
38+
<div>hi hi hi!</div>
39+
</Comp>;

0 commit comments

Comments
 (0)