>hello>;
diff --git a/autotests/html/test.ts.html b/autotests/html/test.ts.html
index 13d57c7..e8c88f6 100644
--- a/autotests/html/test.ts.html
+++ b/autotests/html/test.ts.html
@@ -1,232 +1,287 @@
test.ts
/// <reference types="node" />
// TypeScript Test
class Student {
fullName: string;
constructor(public firstName: string, public middleInitial: string, public lastName: string) {
this.fullName = firstName + " " + middleInitial + " " + lastName;
}
}
interface Person {
firstName: string;
lastName: string;
}
function greeter(person : Person) {
return "Hello, " + person.firstName + " " + person.lastName;
}
let user = new Student("Jane", "M.", "User");
// Built-in
document.body.innerHTML = greeter(user); to
JSON.stringify()
console.log("Hello world");
const os = require('os');
Math.LOG10E;
Number.MAX_SAFE_INTEGER;
import http = require("http");
import path = require("path");
import URL = url.URL;
import { Readable, Writable } from "stream";
import { isBuffer, isString, isObject } from "util";
const port = 8__88___8;
const baseUrl = new URL(`http://localhost:${port}/`);
const rootDir = path.dirname(__dirname);
const defaultBrowser = os.platform() === "win32" ? "edge" : "chrome";
let browser: "edge" | "chrome" | "none" = defaultBrowser;
let grep: string | undefined;
interface FileBasedTestConfiguration {
[setting: string]: string;
}
function swapCase(s: string): string {
return s.replace(/\w/g, (ch) => {
const up = ch.toUpperCase();
return ch === up ? ch.toLowerCase() : up;
});
}
for (var i in pieces) {
switch (true) {
case /^\"?Accession\"?/.test(pieces[i]):
numeration[0] = i;
break;
}
}
const enum ResponseCode {
Success = 200,
BadRequest = 400
}
// Substitutions
export function isInOpenInterval(start: number, end: number) {
return tersible(a => a > start && a < end, () => `(${start}...${end})`)
}
const bar = `${x} ${y}`;
// readonly
function f1(mt: [number, number], rt: readonly [number, number]) {
}
function f2(ma: string[], ra: readonly string[], mt: [string, string], rt: readonly [string, string]) {
}
type ReadWrite<T> = { -readonly [P in keyof T] : T[P] };
// const assertion
let obj = { x: 10, y: [20, 30], z: { a: { b: 42 } } } as const;
let r2 = { x: 20, y: 10 } as const;
let r2 = {...p} as const;
let p1 = { x: 10, y: 20 as const };
let p2 = { 10 as const, 'foo' as const };
// Definite assignment operator
class Cl {
one?: string;
two!: string;
}
let x! = 1;
// Function with multiple arguments
const children = [].map<Set<Map<number, string>>>(element => {
if (!this.identityProvider) {
return element;
}
return element;
});
// Numerics
var a = 0xA;
var b = 0b1;
var c = 0o7;
var d = 1.1E+3;
var e = 1.E+3;
var f = .1E+3;
var g = 1E+3;
var h = 1.1;
var i = 1.;
var j = .1;
var k = 1;
var l = 1__.e+3_22 | .2____e2 | 0o1_23 | 11__. ;
// Bigint
const binBig = 0b101n;
const octBig = 0o567n;
const hexBig = 0xC0Bn;
const decBig = 123n;
// Types
let a: null = null;
let b: number = 123;
let c: number = 123.456;
let d: string = `Geeks`;
let e: undefined = undefined;
let f: boolean = true;
let g: number = 0b111001; // Binary
let h: number = 0o436; // Octal
let i: number = 0xadf0d; // Hexa-Decimal
let j: bigint = 123n;
const query = query<[number], number>(`
SELECT *
FROM statistics
WHERE unit_id = $1`)
function runQuery() {
const query = createQuery<[number[]], Table<Columns>>(`
some SQL here
`)
return database.execute(query)
}
aa: <sdf/> string ?<ssd/> string
string // Don't highlight
aa: string assa |
string
string ;
string
aa: { string
string } // Don't highlight
aa: [ string
string ]
aa: ( string
string ) // Don't highlight
aa: string <string>
interface a {
aa: /* comment
*/ string,
bb: /* comment */
number,
cc: // comment
void,
dd:
any,
}
null, <{[key]: () => any}> null
null, <{[key]: () =>{a: number}}> null
// Correctly highlighting regular expressions
dt = ((dt[0] * 1e9 + dt[1]) / 1e6).toFixed(3).replace(/\.?0+$/, '') + 'ms';
(a[0] / 2) / (2)
// Type guards
function isNumber(x: any): x is number {
return typeof x === "number";
}
// Conditional expressions (highlight keywords before ":")
class C {
w = () =>
this.a() ? true : this.b() ? false : true;
z = () =>
this.b() ? hello : k;
}
function foo<T extends X>(arg: T): T extends B ? number : string {
if (arg === "A") return <T extends B ? number : never>111;
return <T extends B ? never : string>"returning a string";
}
+let x = (foo === null || foo === undefined) ?
+ undefined :
+ foo.bar.baz();
+let y = (foo === null || foo === undefined) ? /* comment */ undefined :
+ foo.bar.baz();
+
+// Operator ??
+let x = foo ?? bar();
+
// Types and logical `&&` and `||` operators after `as` expression
Date as any || null;
//Assertions
const assert: (value: unknown) => asserts value = value => {}
-declare function assertIsString(value: unknown): asserts value is string;
-declare function assertIsArrayOfStrings(value: unknown): asserts value is string[];
-declare function assertDefined<T>(value: T): asserts value is NonNullable<T>;
+declare function assertIsString(value: unknown): asserts value is string;
+declare function assertIsArrayOfStrings(value: unknown): asserts value is string[];
+declare function assertDefined<T>(value: T): asserts value is NonNullable<T>;
namespace Debug {
- export declare function assert(value: unknown, message?: string): asserts value;
- export declare function assertDefined<T>(value: T): asserts value is NonNullable<T>;
+ export declare function assert(value: unknown, message?: string): asserts value;
+ export declare function assertDefined<T>(value: T): asserts value is NonNullable<T>;
}
// Only highlight valid regular expressions, of a single line, after strings
// See: https://github.com/microsoft/TypeScript-TmLanguage/issues/786
"text" /No-Regex
"text" /Regex[:)]*/;
const a = "6" / 2; /*comment*/ const b = 5;
console.log("4" / "2"); // 2
// Single quote
const a = '6' / 2; /*comment*/ const b = 5;
console.log('4' / '2'); // 2
// Template
const a = `6` / 2; /*comment*/ const b = 5;
console.log(`4` / `2`); // 2
+
+ [1, 2, 3].reduce<readonly number[]>((previous) => previous, []);
+
+// Multiline arrow assignment
+const h = {
+ setSubProperty: <
+ T extends A,
+ K extends keyof T,
+ J extends keyof T[K]
+ >(
+ property: K,
+ key: J,
+ value: T[K][J]
+ ) => {}
+}
+
+// Private-Named Instance Fields
+class Greeter {
+ #name: string;
+ constructor(name: string) {
+ this.#name = name;
+ }
+ greet() {
+ console.log(`hello ${this.#name}`);
+ }
+}
+class Test {
+ #NAME1: () => string;
+ prop1 = x.#NAMEabc.#NAMEabc;
+ prop2 = x.#NAMEabc.#NAMEabc();
+ prop3 = this.#NAMEabc.#NAME();
+}
+
+// Type-only imports and exports
+import type T from "mod";
+import type { U, V } from "mod";
+import type * as types from "mod";
+import type from "mod";
+import type = require("mod");
+import type T = require("mod");
+
+export { a as b, x };
+export * from "AnotherModule";
+export { k as m, l } from "AnotherModule";
+export type { T };
+export type { U } from "AnotherModule";
diff --git a/autotests/html/test.tsx.html b/autotests/html/test.tsx.html
index e8557c8..b8008a4 100644
--- a/autotests/html/test.tsx.html
+++ b/autotests/html/test.tsx.html
@@ -1,160 +1,165 @@
test.tsx
// TypeScript React
/** @author Willy <willy@wmail.com>
* @url https://reactjs.org/ **/
import React from 'react';
import { PhotoStory, VideoStory } from './stories';
function Story(props) {
const SpecificStory = components[props.storyType];
return <SpecificStory story={ props.story } attr2="&ref;" attr3="Hello\n" />;
}
function
<Tag attr1={ <tag/> function <noTag/> return class var 0x123 { } &noRef; hello() React.Component() } attr2="&ref;">
/* no comment*/ function <tag/> return class var 0x123 &ref; hello() React.Component()
.<tag/> anyWord <tag/>
{ function <tag> return class var 0x123 hello() React.Component() }
</Tag>
<tag1> </tag1>
<tag1> </Tag$>
<Tag$> </tag>
<tag/*comment*/attr1/*comment*/= /*comment*/"value"/*comment*/attr2 /*comment*/attr3='a' key/*comment*/key2 />
// Detect Valid tags
/* comment */ <tag></tag>
{ /* comment
*/ <Tag />
word <noTag/> . <noTag/> } <noTag/>
return /* comment
multiline */ <tag/> /* comment */ <Tag/>
&& /*comment*/ <Tag/>
& /*comment*/ <noTag/>
<tag/>
{ <hello>Hello</hello> }
?<Tag />;
[ <tag /> ( <tag />
,<Tag/> =<Tag/>
&&<tag/> ||<tag/>
return <tag/> ;
default<tag/> ;
<Tag> <tag> <tag$/> </tag> return </Tag>
anyWord<noTag>
anyWord/*comment*/ <noTag/>
.<noTag>
&<notag> | <noTag/>
% /* comment*/ <noTag/>
// TODO: Fix this (comment before the tag name):
var x = </**/div></div>;
// Tag after ":"
annotation: <tag/>
annotation: text [ <tag/> ]
console.log("hello")
// Type assertion in tag
<C<number>/>
<C<number>> </C>
<C
<error />
// Non-ASCII tag name & attribute
<日本語></日本語>;
<Component 本本:本-本 aa本:本 aa:aa />
<aaaa:ñ />
<Namespace.DeepNamespace.Component />;
<Component { ... x } y
={2 } z />;
let k1 =
<Comp a={10} b="hi" {...o} >
hi hi hi!
</Comp>;
let k2 =
<Comp a={10} b="hi">
<div> My Div </div>
{(name: string) => <div> My name {name} </div>}
</Comp>;
let k3 = <GenericComponent initialValues={{ x: "y" }} nextValues={a => ({ x: a.x })} />; // No Error
// OK
let k1 = <Comp a={10} b="hi"><></><Button /><AnotherButton /></Comp>;
let k2 = <Comp a={10} b="hi"><><Button /></><AnotherButton /></Comp>;
let k3 = <Comp a={10} b="hi"><><Button /><AnotherButton /></></Comp>;
let k4 = <SingleChildComp a={10} b="hi"><><Button /><AnotherButton /></></SingleChildComp>;
// OK
let k1 = <div> <h2> Hello </h2> <h1> world </h1></div>;
let k2 = <div> <h2> Hello </h2> {(user: any) => <h2>{user.name}</h2>}</div>;
let k3 = <div> {1} {"That is a number"} </div>;
let k4 = <Button> <h2> Hello </h2> </Button>;
// Empty tags
hello<>
hello<string>
<></>; // no whitespace
< ></ >; // lots of whitespace
< /*starting wrap*/ ></ /*ending wrap*/>; // comments in the tags
<>hi</>; // text inside
<><span>hi</span><div>bye</div></>; // children
<><span>1</span><><span>2.1</span><span>2.2</span></><span>3</span></>; // nested fragments
<>#</>; // # would cause scanning error if not in jsxtext
// Tags after substitutions in templates
`aaa${<tag></tag>//comment
/*comment*/<A/>}`
// Don't highlight tags within type declaration
type T12 = ReturnType<(<T>() => T)>;
type T13 = ReturnType<(<T extends U, U extends number[]>() => T)>;
type T14 = ReturnType<typeof f1>;
type T15 = ReturnType<(s: string) => void>;
// Don't highlight tags within variable declaration
let myIdentity: <T>(arg: T) => T <noTag/> = <Tag />;
var myIdentity: <U>(arg: U) => U = identity;
const myIdentity: {<T>(arg: T): T} = identity;
// Don't highlight tags within interfaces and classes
interface GenericIdentityFn {
<T>(arg: T): T;
<noTag />
}
class Handler {
info: <T>(arg: T): T <noTag />;
<tag> </tag>
}
// Highlight "<T extends" as a type assertion, not as a tag
// (the "cast" expression works and isn't supported in the TSX file)
const goodHighlighting = <T extends I>(
arg: T
) => {
const a = arg
return a
}
<tag> text <T extends I/> </tag> // Here "<T extends I/>" is a tag
// Check character after tag name, do not highlight invalid tags
<noTag ?
<noTag ,
<noTag /* comment */ ?
<noTag#
<noTag/*comment*/#
+
+// Conditionals expressions
+let y = foo == null ? null : <Tag/>;
+let x = (foo === null || foo === undefined) ?
+ undefined :<tag>hello</tag>;
diff --git a/autotests/input/test.ts b/autotests/input/test.ts
index 0d385a1..53e94f0 100644
--- a/autotests/input/test.ts
+++ b/autotests/input/test.ts
@@ -1,225 +1,280 @@
///
// TypeScript Test
class Student {
fullName: string;
constructor(public firstName: string, public middleInitial: string, public lastName: string) {
this.fullName = firstName + " " + middleInitial + " " + lastName;
}
}
interface Person {
firstName: string;
lastName: string;
}
function greeter(person : Person) {
return "Hello, " + person.firstName + " " + person.lastName;
}
let user = new Student("Jane", "M.", "User");
// Built-in
document.body.innerHTML = greeter(user); to
JSON.stringify()
console.log("Hello world");
const os = require('os');
Math.LOG10E;
Number.MAX_SAFE_INTEGER;
import http = require("http");
import path = require("path");
import URL = url.URL;
import { Readable, Writable } from "stream";
import { isBuffer, isString, isObject } from "util";
const port = 8__88___8;
const baseUrl = new URL(`http://localhost:${port}/`);
const rootDir = path.dirname(__dirname);
const defaultBrowser = os.platform() === "win32" ? "edge" : "chrome";
let browser: "edge" | "chrome" | "none" = defaultBrowser;
let grep: string | undefined;
interface FileBasedTestConfiguration {
[setting: string]: string;
}
function swapCase(s: string): string {
return s.replace(/\w/g, (ch) => {
const up = ch.toUpperCase();
return ch === up ? ch.toLowerCase() : up;
});
}
for (var i in pieces) {
switch (true) {
case /^\"?Accession\"?/.test(pieces[i]):
numeration[0] = i;
break;
}
}
const enum ResponseCode {
Success = 200,
BadRequest = 400
}
// Substitutions
export function isInOpenInterval(start: number, end: number) {
return tersible(a => a > start && a < end, () => `(${start}...${end})`)
}
const bar = `${x} ${y}`;
// readonly
function f1(mt: [number, number], rt: readonly [number, number]) {
}
function f2(ma: string[], ra: readonly string[], mt: [string, string], rt: readonly [string, string]) {
}
type ReadWrite = { -readonly [P in keyof T] : T[P] };
// const assertion
let obj = { x: 10, y: [20, 30], z: { a: { b: 42 } } } as const;
let r2 = { x: 20, y: 10 } as const;
let r2 = {...p} as const;
let p1 = { x: 10, y: 20 as const };
let p2 = { 10 as const, 'foo' as const };
// Definite assignment operator
class Cl {
one?: string;
two!: string;
}
let x! = 1;
// Function with multiple arguments
const children = [].map>>(element => {
if (!this.identityProvider) {
return element;
}
return element;
});
// Numerics
var a = 0xA;
var b = 0b1;
var c = 0o7;
var d = 1.1E+3;
var e = 1.E+3;
var f = .1E+3;
var g = 1E+3;
var h = 1.1;
var i = 1.;
var j = .1;
var k = 1;
var l = 1__.e+3_22 | .2____e2 | 0o1_23 | 11__. ;
// Bigint
const binBig = 0b101n;
const octBig = 0o567n;
const hexBig = 0xC0Bn;
const decBig = 123n;
// Types
let a: null = null;
let b: number = 123;
let c: number = 123.456;
let d: string = `Geeks`;
let e: undefined = undefined;
let f: boolean = true;
let g: number = 0b111001; // Binary
let h: number = 0o436; // Octal
let i: number = 0xadf0d; // Hexa-Decimal
let j: bigint = 123n;
const query = query<[number], number>(`
SELECT *
FROM statistics
WHERE unit_id = $1`)
function runQuery() {
const query = createQuery<[number[]], Table>(`
some SQL here
`)
return database.execute(query)
}
aa: string ? string
string // Don't highlight
aa: string assa |
string
string ;
string
aa: { string
string } // Don't highlight
aa: [ string
string ]
aa: ( string
string ) // Don't highlight
aa: string
interface a {
aa: /* comment
*/ string,
bb: /* comment */
number,
cc: // comment
void,
dd:
any,
}
null, <{[key]: () => any}> null
null, <{[key]: () =>{a: number}}> null
// Correctly highlighting regular expressions
dt = ((dt[0] * 1e9 + dt[1]) / 1e6).toFixed(3).replace(/\.?0+$/, '') + 'ms';
(a[0] / 2) / (2)
// Type guards
function isNumber(x: any): x is number {
return typeof x === "number";
}
// Conditional expressions (highlight keywords before ":")
class C {
w = () =>
this.a() ? true : this.b() ? false : true;
z = () =>
this.b() ? hello : k;
}
function foo(arg: T): T extends B ? number : string {
if (arg === "A") return 111;
return "returning a string";
}
+let x = (foo === null || foo === undefined) ?
+ undefined :
+ foo.bar.baz();
+let y = (foo === null || foo === undefined) ? /* comment */ undefined :
+ foo.bar.baz();
+
+// Operator ??
+let x = foo ?? bar();
+
// Types and logical `&&` and `||` operators after `as` expression
Date as any || null;
//Assertions
const assert: (value: unknown) => asserts value = value => {}
declare function assertIsString(value: unknown): asserts value is string;
declare function assertIsArrayOfStrings(value: unknown): asserts value is string[];
declare function assertDefined(value: T): asserts value is NonNullable;
namespace Debug {
export declare function assert(value: unknown, message?: string): asserts value;
export declare function assertDefined(value: T): asserts value is NonNullable;
}
// Only highlight valid regular expressions, of a single line, after strings
// See: https://github.com/microsoft/TypeScript-TmLanguage/issues/786
"text" /No-Regex
"text" /Regex[:)]*/;
const a = "6" / 2; /*comment*/ const b = 5;
console.log("4" / "2"); // 2
// Single quote
const a = '6' / 2; /*comment*/ const b = 5;
console.log('4' / '2'); // 2
// Template
const a = `6` / 2; /*comment*/ const b = 5;
console.log(`4` / `2`); // 2
+
+ [1, 2, 3].reduce((previous) => previous, []);
+
+// Multiline arrow assignment
+const h = {
+ setSubProperty: <
+ T extends A,
+ K extends keyof T,
+ J extends keyof T[K]
+ >(
+ property: K,
+ key: J,
+ value: T[K][J]
+ ) => {}
+}
+
+// Private-Named Instance Fields
+class Greeter {
+ #name: string;
+ constructor(name: string) {
+ this.#name = name;
+ }
+ greet() {
+ console.log(`hello ${this.#name}`);
+ }
+}
+class Test {
+ #NAME1: () => string;
+ prop1 = x.#NAMEabc.#NAMEabc;
+ prop2 = x.#NAMEabc.#NAMEabc();
+ prop3 = this.#NAMEabc.#NAME();
+}
+
+// Type-only imports and exports
+import type T from "mod";
+import type { U, V } from "mod";
+import type * as types from "mod";
+import type from "mod";
+import type = require("mod");
+import type T = require("mod");
+
+export { a as b, x };
+export * from "AnotherModule";
+export { k as m, l } from "AnotherModule";
+export type { T };
+export type { U } from "AnotherModule";
diff --git a/autotests/input/test.tsx b/autotests/input/test.tsx
index 7484dcb..354f0bb 100644
--- a/autotests/input/test.tsx
+++ b/autotests/input/test.tsx
@@ -1,153 +1,158 @@
// TypeScript React
/** @author Willy
* @url https://reactjs.org/ **/
import React from 'react';
import { PhotoStory, VideoStory } from './stories';
function Story(props) {
const SpecificStory = components[props.storyType];
return ;
}
function
function return class var 0x123 { } &noRef; hello() React.Component() } attr2="&ref;">
/* no comment*/ function return class var 0x123 &ref; hello() React.Component()
. anyWord
{ function return class var 0x123 hello() React.Component() }