Skip to content

Commit e448d8b

Browse files
committed
Treat </ as a token in TSX files
1 parent 2555344 commit e448d8b

File tree

9 files changed

+76
-13
lines changed

9 files changed

+76
-13
lines changed

src/compiler/emitter.ts

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -53,12 +53,13 @@ var __param = (this && this.__param) || function (paramIndex, decorator) {
5353
let sourceMapDataList: SourceMapData[] = compilerOptions.sourceMap || compilerOptions.inlineSourceMap ? [] : undefined;
5454
let diagnostics: Diagnostic[] = [];
5555
let newLine = host.getNewLine();
56-
let jsxDesugaring = host.getCompilerOptions().jsx === JsxEmit.React;
56+
let jsxDesugaring = host.getCompilerOptions().jsx !== JsxEmit.Preserve;
57+
let shouldEmitJsx = (s: SourceFile) => (s.languageVariant === LanguageVariant.JSX && !jsxDesugaring);
5758

5859
if (targetSourceFile === undefined) {
5960
forEach(host.getSourceFiles(), sourceFile => {
6061
if (shouldEmitToOwnFile(sourceFile, compilerOptions)) {
61-
let jsFilePath = getOwnEmitOutputFilePath(sourceFile, host, (!sourceFile.isTSXFile || jsxDesugaring) ? ".js" : ".jsx");
62+
let jsFilePath = getOwnEmitOutputFilePath(sourceFile, host, shouldEmitJsx(sourceFile) ? ".jsx" : ".js");
6263
emitFile(jsFilePath, sourceFile);
6364
}
6465
});
@@ -70,7 +71,7 @@ var __param = (this && this.__param) || function (paramIndex, decorator) {
7071
else {
7172
// targetSourceFile is specified (e.g calling emitter from language service or calling getSemanticDiagnostic from language service)
7273
if (shouldEmitToOwnFile(targetSourceFile, compilerOptions)) {
73-
let jsFilePath = getOwnEmitOutputFilePath(targetSourceFile, host, (host.getSourceFiles().every(f => !f.isTSXFile) || jsxDesugaring) ? ".js" : ".jsx");
74+
let jsFilePath = getOwnEmitOutputFilePath(targetSourceFile, host, host.getSourceFiles().some(shouldEmitJsx) ? ".jsx" : ".js");
7475
emitFile(jsFilePath, targetSourceFile);
7576
}
7677
else if (!isDeclarationFile(targetSourceFile) && compilerOptions.out) {

src/compiler/parser.ts

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -544,6 +544,7 @@ namespace ts {
544544
scanner.setText(sourceText);
545545
scanner.setOnError(scanError);
546546
scanner.setScriptTarget(languageVersion);
547+
scanner.setLanguageVariant(isTsx(fileName) ? LanguageVariant.JSX : LanguageVariant.Standard);
547548
}
548549

549550
function clearState() {
@@ -656,7 +657,7 @@ namespace ts {
656657
sourceFile.languageVersion = languageVersion;
657658
sourceFile.fileName = normalizePath(fileName);
658659
sourceFile.flags = fileExtensionIs(sourceFile.fileName, ".d.ts") ? NodeFlags.DeclarationFile : 0;
659-
sourceFile.isTSXFile = fileExtensionIs(sourceFile.fileName, ".tsx");
660+
sourceFile.languageVariant = fileExtensionIs(sourceFile.fileName, ".tsx") ? LanguageVariant.JSX : LanguageVariant.Standard;
660661

661662
return sourceFile;
662663
}
@@ -2839,7 +2840,7 @@ namespace ts {
28392840
}
28402841

28412842
// JSX overrides
2842-
if (sourceFile.isTSXFile) {
2843+
if (sourceFile.languageVariant === LanguageVariant.JSX) {
28432844
let isArrowFunctionInJsx = lookAhead(() => {
28442845
let third = nextToken();
28452846
let fourth = nextToken();
@@ -3113,7 +3114,7 @@ namespace ts {
31133114
case SyntaxKind.VoidKeyword:
31143115
return parseVoidExpression();
31153116
case SyntaxKind.LessThanToken:
3116-
if (!sourceFile.isTSXFile) {
3117+
if (sourceFile.languageVariant !== LanguageVariant.JSX) {
31173118
return parseTypeAssertion();
31183119
}
31193120
if(lookAhead(nextTokenIsIdentifier)) {
@@ -5050,7 +5051,7 @@ namespace ts {
50505051
}
50515052

50525053
function processReferenceComments(sourceFile: SourceFile): void {
5053-
let triviaScanner = createScanner(sourceFile.languageVersion, /*skipTrivia*/false, sourceText);
5054+
let triviaScanner = createScanner(sourceFile.languageVersion, /*skipTrivia*/false, LanguageVariant.Standard, sourceText);
50545055
let referencedFiles: FileReference[] = [];
50555056
let amdDependencies: { path: string; name: string }[] = [];
50565057
let amdModuleName: string;

src/compiler/scanner.ts

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ namespace ts {
3030
setText(text: string, start?: number, length?: number): void;
3131
setOnError(onError: ErrorCallback): void;
3232
setScriptTarget(scriptTarget: ScriptTarget): void;
33+
setLanguageVariant(variant: LanguageVariant): void;
3334
setTextPos(textPos: number): void;
3435
// Invokes the provided callback then unconditionally restores the scanner to the state it
3536
// was in immediately prior to invoking the callback. The result of invoking the callback
@@ -630,6 +631,7 @@ namespace ts {
630631
// Creates a scanner over a (possibly unspecified) range of a piece of text.
631632
export function createScanner(languageVersion: ScriptTarget,
632633
skipTrivia: boolean,
634+
languageVariant = LanguageVariant.Standard,
633635
text?: string,
634636
onError?: ErrorCallback,
635637
start?: number,
@@ -675,6 +677,7 @@ namespace ts {
675677
scan,
676678
setText,
677679
setScriptTarget,
680+
setLanguageVariant,
678681
setOnError,
679682
setTextPos,
680683
tryScan,
@@ -1309,6 +1312,9 @@ namespace ts {
13091312
if (text.charCodeAt(pos + 1) === CharacterCodes.equals) {
13101313
return pos += 2, token = SyntaxKind.LessThanEqualsToken;
13111314
}
1315+
if (text.charCodeAt(pos + 1) === CharacterCodes.slash && languageVariant === LanguageVariant.JSX) {
1316+
return pos += 2, token = SyntaxKind.LessThanSlashToken;
1317+
}
13121318
return pos++, token = SyntaxKind.LessThanToken;
13131319
case CharacterCodes.equals:
13141320
if (isConflictMarkerTrivia(text, pos)) {
@@ -1588,6 +1594,10 @@ namespace ts {
15881594
languageVersion = scriptTarget;
15891595
}
15901596

1597+
function setLanguageVariant(variant: LanguageVariant) {
1598+
languageVariant = variant;
1599+
}
1600+
15911601
function setTextPos(textPos: number) {
15921602
Debug.assert(textPos >= 0);
15931603
pos = textPos;

src/compiler/types.ts

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1223,7 +1223,7 @@ namespace ts {
12231223
amdDependencies: {path: string; name: string}[];
12241224
moduleName: string;
12251225
referencedFiles: FileReference[];
1226-
isTSXFile: boolean;
1226+
languageVariant: LanguageVariant;
12271227

12281228
/**
12291229
* lib.d.ts should have a reference comment like
@@ -1988,6 +1988,11 @@ namespace ts {
19881988
Latest = ES6,
19891989
}
19901990

1991+
export const enum LanguageVariant {
1992+
Standard,
1993+
JSX
1994+
}
1995+
19911996
export interface ParsedCommandLine {
19921997
options: CompilerOptions;
19931998
fileNames: string[];

src/compiler/utilities.ts

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -274,7 +274,7 @@ namespace ts {
274274
}
275275

276276
export function getSpanOfTokenAtPosition(sourceFile: SourceFile, pos: number): TextSpan {
277-
let scanner = createScanner(sourceFile.languageVersion, /*skipTrivia*/ true, sourceFile.text, /*onError:*/ undefined, pos);
277+
let scanner = createScanner(sourceFile.languageVersion, /*skipTrivia*/ true, sourceFile.languageVariant, sourceFile.text, /*onError:*/ undefined, pos);
278278
scanner.scan();
279279
let start = scanner.getTokenPos();
280280
return createTextSpanFromBounds(start, scanner.getTextPos());
@@ -1957,6 +1957,10 @@ namespace ts {
19571957
return fileExtensionIs(fileName, ".js");
19581958
}
19591959

1960+
export function isTsx(fileName: string) {
1961+
return fileExtensionIs(fileName, ".tsx");
1962+
}
1963+
19601964
/**
19611965
* Replace each instance of non-ascii characters by one, two, three, or four escape sequences
19621966
* representing the UTF-8 encoding of the character, and return the expanded char code list.

src/services/services.ts

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -737,7 +737,6 @@ namespace ts {
737737
public amdDependencies: { name: string; path: string }[];
738738
public moduleName: string;
739739
public referencedFiles: FileReference[];
740-
public isTSXFile: boolean;
741740

742741
public syntacticDiagnostics: Diagnostic[];
743742
public referenceDiagnostics: Diagnostic[];
@@ -752,6 +751,7 @@ namespace ts {
752751
public symbolCount: number;
753752
public version: string;
754753
public languageVersion: ScriptTarget;
754+
public languageVariant: LanguageVariant;
755755
public identifiers: Map<string>;
756756
public nameTable: Map<string>;
757757

@@ -2916,7 +2916,7 @@ namespace ts {
29162916
else if (contextToken && contextToken.kind === SyntaxKind.DotToken && contextToken.parent.kind === SyntaxKind.QualifiedName) {
29172917
node = (<QualifiedName>contextToken.parent).left;
29182918
isRightOfDot = true;
2919-
} else if (contextToken && contextToken.kind === SyntaxKind.LessThanToken && sourceFile.isTSXFile) {
2919+
} else if (contextToken && contextToken.kind === SyntaxKind.LessThanToken && sourceFile.languageVariant === LanguageVariant.JSX) {
29202920
isRightOfOpenTag = true;
29212921
location = contextToken;
29222922
}
@@ -6167,8 +6167,8 @@ namespace ts {
61676167
let spanLength = span.length;
61686168

61696169
// Make a scanner we can get trivia from.
6170-
let triviaScanner = createScanner(ScriptTarget.Latest, /*skipTrivia:*/ false, sourceFile.text);
6171-
let mergeConflictScanner = createScanner(ScriptTarget.Latest, /*skipTrivia:*/ false, sourceFile.text);
6170+
let triviaScanner = createScanner(ScriptTarget.Latest, /*skipTrivia:*/ false, sourceFile.languageVariant, sourceFile.text);
6171+
let mergeConflictScanner = createScanner(ScriptTarget.Latest, /*skipTrivia:*/ false, sourceFile.languageVariant, sourceFile.text);
61726172

61736173
let result: number[] = [];
61746174
processElement(sourceFile);
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
tests/cases/conformance/jsx/tsxErrorRecovery1.tsx(5,19): error TS1109: Expression expected.
2+
3+
4+
==== tests/cases/conformance/jsx/tsxErrorRecovery1.tsx (1 errors) ====
5+
6+
declare namespace JSX { interface Element { } }
7+
8+
function foo() {
9+
var x = <div> { </div>
10+
~~
11+
!!! error TS1109: Expression expected.
12+
}
13+
// Shouldn't see any errors down here
14+
var y = { a: 1 };
15+
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
//// [tsxErrorRecovery1.tsx]
2+
3+
declare namespace JSX { interface Element { } }
4+
5+
function foo() {
6+
var x = <div> { </div>
7+
}
8+
// Shouldn't see any errors down here
9+
var y = { a: 1 };
10+
11+
12+
//// [tsxErrorRecovery1.jsx]
13+
function foo() {
14+
var x = <div> {} </div>;
15+
}
16+
// Shouldn't see any errors down here
17+
var y = { a: 1 };
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
//@filename: file.tsx
2+
//@jsx: preserve
3+
4+
declare namespace JSX { interface Element { } }
5+
6+
function foo() {
7+
var x = <div> { </div>
8+
}
9+
// Shouldn't see any errors down here
10+
var y = { a: 1 };

0 commit comments

Comments
 (0)