Skip to content

Commit f5e24b9

Browse files
authored
enh(typescript,javascript) Build both JS and TS on a common foundation (#2569)
1 parent 00a6749 commit f5e24b9

13 files changed

+271
-365
lines changed

src/languages/javascript.js

+174-92
Large diffs are not rendered by default.

src/languages/typescript.js

+52-186
Original file line numberDiff line numberDiff line change
@@ -7,11 +7,25 @@ Website: https://www.typescriptlang.org
77
Category: common, scripting
88
*/
99

10-
import * as ECMAScript from './lib/ecmascript.js';
10+
import * as ECMAScript from "./lib/ecmascript.js";
11+
import javascript from "./javascript.js";
1112

13+
/** @type LanguageFn */
1214
export default function(hljs) {
13-
var IDENT_RE = ECMAScript.IDENT_RE;
14-
var TYPES = [
15+
const IDENT_RE = ECMAScript.IDENT_RE;
16+
const NAMESPACE = {
17+
beginKeywords: 'namespace', end: /\{/, excludeEnd: true
18+
};
19+
const INTERFACE = {
20+
beginKeywords: 'interface', end: /\{/, excludeEnd: true,
21+
keywords: 'interface extends'
22+
};
23+
const USE_STRICT = {
24+
className: 'meta',
25+
relevance: 10,
26+
begin: /^\s*['"]use strict['"]/
27+
};
28+
const TYPES = [
1529
"any",
1630
"void",
1731
"number",
@@ -21,7 +35,7 @@ export default function(hljs) {
2135
"never",
2236
"enum"
2337
];
24-
var TS_SPECIFIC_KEYWORDS = [
38+
const TS_SPECIFIC_KEYWORDS = [
2539
"type",
2640
"namespace",
2741
"typedef",
@@ -34,197 +48,49 @@ export default function(hljs) {
3448
"abstract",
3549
"readonly"
3650
];
37-
var KEYWORDS = {
51+
const KEYWORDS = {
3852
$pattern: ECMAScript.IDENT_RE,
3953
keyword: ECMAScript.KEYWORDS.concat(TS_SPECIFIC_KEYWORDS).join(" "),
4054
literal: ECMAScript.LITERALS.join(" "),
4155
built_in: ECMAScript.BUILT_INS.concat(TYPES).join(" ")
4256
};
43-
var DECORATOR = {
57+
const DECORATOR = {
4458
className: 'meta',
4559
begin: '@' + IDENT_RE,
4660
};
47-
var NUMBER = {
48-
className: 'number',
49-
variants: [
50-
{ begin: '\\b(0[bB][01]+)n?' },
51-
{ begin: '\\b(0[oO][0-7]+)n?' },
52-
{ begin: hljs.C_NUMBER_RE + 'n?' }
53-
],
54-
relevance: 0
55-
};
56-
var SUBST = {
57-
className: 'subst',
58-
begin: '\\$\\{', end: '\\}',
59-
keywords: KEYWORDS,
60-
contains: [] // defined later
61-
};
62-
var HTML_TEMPLATE = {
63-
begin: 'html`', end: '',
64-
starts: {
65-
end: '`', returnEnd: false,
66-
contains: [
67-
hljs.BACKSLASH_ESCAPE,
68-
SUBST
69-
],
70-
subLanguage: 'xml',
71-
}
72-
};
73-
var CSS_TEMPLATE = {
74-
begin: 'css`', end: '',
75-
starts: {
76-
end: '`', returnEnd: false,
77-
contains: [
78-
hljs.BACKSLASH_ESCAPE,
79-
SUBST
80-
],
81-
subLanguage: 'css',
82-
}
83-
};
84-
var TEMPLATE_STRING = {
85-
className: 'string',
86-
begin: '`', end: '`',
87-
contains: [
88-
hljs.BACKSLASH_ESCAPE,
89-
SUBST
90-
]
91-
};
92-
SUBST.contains = [
93-
hljs.APOS_STRING_MODE,
94-
hljs.QUOTE_STRING_MODE,
95-
HTML_TEMPLATE,
96-
CSS_TEMPLATE,
97-
TEMPLATE_STRING,
98-
NUMBER,
99-
hljs.REGEXP_MODE
100-
];
101-
var ARGUMENTS =
102-
{
103-
begin: '\\(',
104-
end: /\)/,
105-
keywords: KEYWORDS,
106-
contains: [
107-
'self',
108-
hljs.QUOTE_STRING_MODE,
109-
hljs.APOS_STRING_MODE,
110-
hljs.NUMBER_MODE
111-
]
112-
};
113-
var PARAMS = {
114-
className: 'params',
115-
begin: /\(/, end: /\)/,
116-
excludeBegin: true,
117-
excludeEnd: true,
118-
keywords: KEYWORDS,
119-
contains: [
120-
hljs.C_LINE_COMMENT_MODE,
121-
hljs.C_BLOCK_COMMENT_MODE,
122-
DECORATOR,
123-
ARGUMENTS
124-
]
61+
62+
const swapMode = (mode, label, replacement) => {
63+
const indx = mode.contains.findIndex(m => m.label === label);
64+
if (indx === -1) { throw new Error("can not find mode to replace"); };
65+
66+
mode.contains.splice(indx, 1, replacement);
12567
};
12668

127-
return {
69+
const tsLanguage = javascript(hljs);
70+
71+
// this should update anywhere keywords is used since
72+
// it will be the same actual JS object
73+
Object.assign(tsLanguage.keywords, KEYWORDS);
74+
75+
tsLanguage.exports.PARAMS_CONTAINS.push(DECORATOR);
76+
tsLanguage.contains = tsLanguage.contains.concat([
77+
DECORATOR,
78+
NAMESPACE,
79+
INTERFACE,
80+
]);
81+
82+
// TS gets a simpler shebang rule than JS
83+
swapMode(tsLanguage, "shebang", hljs.SHEBANG());
84+
// JS use strict rule purposely excludes `asm` which makes no sense
85+
swapMode(tsLanguage, "use_strict", USE_STRICT);
86+
87+
const functionDeclaration = tsLanguage.contains.find(m => m.className === "function");
88+
functionDeclaration.relevance = 0; // () => {} is more typical in TypeScript
89+
90+
Object.assign(tsLanguage, {
12891
name: 'TypeScript',
129-
aliases: ['ts'],
130-
keywords: KEYWORDS,
131-
contains: [
132-
hljs.SHEBANG(),
133-
{
134-
className: 'meta',
135-
begin: /^\s*['"]use strict['"]/
136-
},
137-
hljs.APOS_STRING_MODE,
138-
hljs.QUOTE_STRING_MODE,
139-
HTML_TEMPLATE,
140-
CSS_TEMPLATE,
141-
TEMPLATE_STRING,
142-
hljs.C_LINE_COMMENT_MODE,
143-
hljs.C_BLOCK_COMMENT_MODE,
144-
NUMBER,
145-
{ // "value" container
146-
begin: '(' + hljs.RE_STARTERS_RE + '|\\b(case|return|throw)\\b)\\s*',
147-
keywords: 'return throw case',
148-
contains: [
149-
hljs.C_LINE_COMMENT_MODE,
150-
hljs.C_BLOCK_COMMENT_MODE,
151-
hljs.REGEXP_MODE,
152-
{
153-
className: 'function',
154-
// we have to count the parens to make sure we actually have the
155-
// correct bounding ( ) before the =>. There could be any number of
156-
// sub-expressions inside also surrounded by parens.
157-
begin: '(\\([^(]*' +
158-
'(\\([^(]*' +
159-
'(\\([^(]*' +
160-
'\\))?' +
161-
'\\))?' +
162-
'\\)|' + hljs.UNDERSCORE_IDENT_RE + ')\\s*=>', returnBegin: true,
163-
end: '\\s*=>',
164-
contains: [
165-
{
166-
className: 'params',
167-
variants: [
168-
{
169-
begin: hljs.UNDERSCORE_IDENT_RE
170-
},
171-
{
172-
className: null,
173-
begin: /\(\s*\)/,
174-
skip: true
175-
},
176-
{
177-
begin: /\(/, end: /\)/,
178-
excludeBegin: true, excludeEnd: true,
179-
keywords: KEYWORDS,
180-
contains: ARGUMENTS.contains
181-
}
182-
]
183-
}
184-
]
185-
}
186-
],
187-
relevance: 0
188-
},
189-
{
190-
className: 'function',
191-
beginKeywords: 'function', end: /[\{;]/, excludeEnd: true,
192-
keywords: KEYWORDS,
193-
contains: [
194-
'self',
195-
hljs.inherit(hljs.TITLE_MODE, { begin: IDENT_RE }),
196-
PARAMS
197-
],
198-
illegal: /%/,
199-
relevance: 0 // () => {} is more typical in TypeScript
200-
},
201-
{
202-
beginKeywords: 'constructor', end: /[\{;]/, excludeEnd: true,
203-
contains: [
204-
'self',
205-
PARAMS
206-
]
207-
},
208-
{ // prevent references like module.id from being highlighted as module definitions
209-
begin: /module\./,
210-
keywords: { built_in: 'module' },
211-
relevance: 0
212-
},
213-
{
214-
beginKeywords: 'module', end: /\{/, excludeEnd: true
215-
},
216-
{
217-
beginKeywords: 'interface', end: /\{/, excludeEnd: true,
218-
keywords: 'interface extends'
219-
},
220-
{
221-
begin: /\$[(.]/ // relevance booster for a pattern common to JS libs: `$(something)` and `$.something`
222-
},
223-
{
224-
begin: '\\.' + hljs.IDENT_RE, relevance: 0 // hack: prevents detection of keywords after dots
225-
},
226-
DECORATOR,
227-
ARGUMENTS
228-
]
229-
};
92+
aliases: ['ts']
93+
});
94+
95+
return tsLanguage;
23096
}

test/markup/javascript/class.expect.txt

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">Car</span> <span class="hljs-keyword">extends</span> <span class="hljs-title">Vehicle</span> </span>{
2-
<span class="hljs-keyword">constructor</span>(speed, cost) {
2+
<span class="hljs-keyword">constructor</span>(<span class="hljs-params">speed, cost</span>) {
33
<span class="hljs-built_in">super</span>(speed);
44

55
<span class="hljs-keyword">var</span> c = <span class="hljs-built_in">Symbol</span>(<span class="hljs-string">&#x27;cost&#x27;</span>);

test/markup/typescript/class.expect.txt

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
<span class="hljs-keyword">class</span> Car <span class="hljs-keyword">extends</span> Vehicle {
1+
<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">Car</span> <span class="hljs-keyword">extends</span> <span class="hljs-title">Vehicle</span> </span>{
22
<span class="hljs-keyword">constructor</span>(<span class="hljs-params">speed, cost</span>) {
33
<span class="hljs-built_in">super</span>(speed);
44

+2-2
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
1-
<span class="hljs-keyword">export</span> <span class="hljs-keyword">declare</span> <span class="hljs-keyword">class</span> CommandHandler <span class="hljs-keyword">extends</span> EventEmitter {
1+
<span class="hljs-keyword">export</span> <span class="hljs-keyword">declare</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">CommandHandler</span> <span class="hljs-keyword">extends</span> <span class="hljs-title">EventEmitter</span> </span>{
22

33
<span class="hljs-keyword">constructor</span>(<span class="hljs-params">config: CommandHandlerConfig</span>);
44
<span class="hljs-comment">/**
55
* Install the handler onto Discord.js
6-
* @param client - Client to handle
6+
* <span class="hljs-doctag">@param <span class="hljs-variable">client</span></span> - Client to handle
77
*/</span>
88
}
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,13 @@
11
<span class="hljs-meta">@foo</span>(<span class="hljs-string">&#x27;foo&#x27;</span>)
2-
<span class="hljs-keyword">export</span> <span class="hljs-keyword">class</span> MyClass {
2+
<span class="hljs-keyword">export</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">MyClass</span> </span>{
33
<span class="hljs-meta">@baz</span>(<span class="hljs-number">123</span>)
44
<span class="hljs-keyword">private</span> myAttribute: <span class="hljs-built_in">string</span>;
55

6-
<span class="hljs-keyword">constructor</span>(<span class="hljs-params"><span class="hljs-meta">@bar</span>(<span class="hljs-literal">true</span>) <span class="hljs-keyword">private</span> x,
6+
<span class="hljs-keyword">constructor</span>(<span class="hljs-params"><span class="hljs-meta">@bar</span>(<span class="hljs-literal">true</span>) <span class="hljs-keyword">private</span> x,
77
<span class="hljs-meta">@bar</span>(qux(quux(<span class="hljs-literal">true</span>))) <span class="hljs-keyword">private</span> y</span>) { }
88

99
<span class="hljs-meta">@bar</span>()
1010
<span class="hljs-keyword">private</span> myMethod(<span class="hljs-meta">@bar</span>() z) {
1111
<span class="hljs-built_in">console</span>.log(<span class="hljs-string">&#x27;Hello world.&#x27;</span>);
1212
}
13-
}
13+
}

test/markup/typescript/decorator-factories.txt

+2-2
Original file line numberDiff line numberDiff line change
@@ -3,11 +3,11 @@ export class MyClass {
33
@baz(123)
44
private myAttribute: string;
55

6-
constructor(@bar(true) private x,
6+
constructor(@bar(true) private x,
77
@bar(qux(quux(true))) private y) { }
88

99
@bar()
1010
private myMethod(@bar() z) {
1111
console.log('Hello world.');
1212
}
13-
}
13+
}

test/markup/typescript/functions.expect.txt

+1-1
Original file line numberDiff line numberDiff line change
@@ -22,5 +22,5 @@
2222
<span class="hljs-keyword">const</span> bad = (<span class="hljs-function">(<span class="hljs-params">a, b</span>) =&gt;</span> [...a, b]);
2323
<span class="hljs-keyword">const</span> array = [<span class="hljs-number">1</span>, <span class="hljs-number">2</span>, <span class="hljs-number">3</span>].reduce&lt;<span class="hljs-built_in">number</span>[]&gt;(<span class="hljs-function">(<span class="hljs-params">acc, next</span>) =&gt;</span> [...acc, next], []);
2424
<span class="hljs-keyword">const</span> bad = (<span class="hljs-function">(<span class="hljs-params">a=<span class="hljs-number">2</span>, b=<span class="hljs-number">5</span></span>) =&gt;</span> [...a, b]);
25-
sides.every(<span class="hljs-function">(<span class="hljs-params">length,width=(<span class="hljs-params"><span class="hljs-number">3</span>+<span class="hljs-number">2</span>+(<span class="hljs-params"><span class="hljs-number">4</span>/<span class="hljs-number">5</span></span>)</span>)</span>) =&gt;</span> length &gt; <span class="hljs-number">0</span> );
25+
sides.every(<span class="hljs-function">(<span class="hljs-params">length,width=(<span class="hljs-number">3</span>+<span class="hljs-number">2</span>+(<span class="hljs-number">4</span>/<span class="hljs-number">5</span>))</span>) =&gt;</span> length &gt; <span class="hljs-number">0</span> );
2626

test/markup/typescript/jsx.expect.txt

+11-33
Original file line numberDiff line numberDiff line change
@@ -1,38 +1,16 @@
1-
<span class="hljs-keyword">export</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">getModuleInstanceState</span>(<span class="hljs-params">node: Node</span>): <span class="hljs-title">ModuleInstanceState</span> </span>{
2-
<span class="hljs-comment">// A module is uninstantiated if it contains only</span>
3-
<span class="hljs-comment">// 1. interface declarations, type alias declarations</span>
4-
<span class="hljs-keyword">if</span> (node.kind === SyntaxKind.InterfaceDeclaration || node.kind === SyntaxKind.TypeAliasDeclaration) {
5-
<span class="hljs-keyword">return</span> ModuleInstanceState.NonInstantiated;
6-
}
7-
<span class="hljs-comment">// 2. const enum declarations</span>
8-
<span class="hljs-keyword">else</span> <span class="hljs-keyword">if</span> (isConstEnumDeclaration(node)) {
9-
<span class="hljs-keyword">return</span> ModuleInstanceState.ConstEnumOnly;
10-
}
11-
<span class="hljs-comment">// 3. non-exported import declarations</span>
12-
<span class="hljs-keyword">else</span> <span class="hljs-keyword">if</span> ((node.kind === SyntaxKind.ImportDeclaration || node.kind === SyntaxKind.ImportEqualsDeclaration) &amp;&amp; !(node.flags &amp; NodeFlags.Export)) {
13-
<span class="hljs-keyword">return</span> ModuleInstanceState.NonInstantiated;
14-
}
15-
<span class="hljs-comment">// 4. other uninstantiated module declarations.</span>
16-
<span class="hljs-keyword">else</span> <span class="hljs-keyword">if</span> (node.kind === SyntaxKind.ModuleBlock) {
17-
<span class="hljs-keyword">let</span> state = ModuleInstanceState.NonInstantiated;
18-
forEachChild(node, <span class="hljs-function"><span class="hljs-params">n</span> =&gt;</span> {
19-
<span class="hljs-keyword">switch</span> (getModuleInstanceState(n)) {
20-
<span class="hljs-keyword">case</span> ModuleInstanceState.NonInstantiated:
21-
<span class="hljs-comment">// child is non-instantiated - continue searching</span>
22-
<span class="hljs-keyword">return</span> <span class="hljs-literal">false</span>;
23-
<span class="hljs-keyword">case</span> ModuleInstanceState.ConstEnumOnly:
24-
<span class="hljs-comment">// child is const enum only - record state and continue searching</span>
25-
state = ModuleInstanceState.ConstEnumOnly;
26-
<span class="hljs-keyword">return</span> <span class="hljs-literal">false</span>;
27-
<span class="hljs-keyword">case</span> ModuleInstanceState.Instantiated:
28-
<span class="hljs-comment">// child is instantiated - record state and stop</span>
29-
state = ModuleInstanceState.Instantiated;
30-
<span class="hljs-keyword">return</span> <span class="hljs-literal">true</span>;
31-
}
32-
});
33-
<span class="hljs-keyword">return</span> state;
1+
<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">MyComponent</span> <span class="hljs-keyword">extends</span> <span class="hljs-title">Component</span>&lt;<span class="hljs-title">Props</span>&gt; </span>{
2+
render() {
3+
<span class="hljs-keyword">let</span> a : <span class="hljs-built_in">Array</span>&lt;<span class="hljs-built_in">Array</span>&lt;<span class="hljs-built_in">number</span>&gt;&gt; = [[<span class="hljs-number">1</span>,<span class="hljs-number">2</span>]];
4+
<span class="hljs-keyword">let</span> b = <span class="hljs-keyword">new</span> <span class="hljs-built_in">Map</span>&lt;<span class="hljs-built_in">string</span>,<span class="hljs-built_in">number</span>&gt;();
5+
<span class="hljs-keyword">return</span> (
6+
<span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">div</span> /&gt;</span></span>
7+
);
348
}
9+
}
10+
11+
<span class="hljs-keyword">export</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">getModuleInstanceState</span>(<span class="hljs-params">node: Node</span>): <span class="hljs-title">ModuleInstanceState</span> </span>{
3512
<span class="hljs-keyword">else</span> <span class="hljs-keyword">if</span> (node.kind === SyntaxKind.ModuleDeclaration) {
13+
<span class="hljs-keyword">return</span> getModuleInstanceState((&lt;<span class="hljs-built_in">Array</span>&lt;<span class="hljs-built_in">Array</span>&lt;<span class="hljs-built_in">number</span>&gt;&gt;node).body);
3614
<span class="hljs-keyword">return</span> getModuleInstanceState((&lt;ModuleDeclaration&gt;node).body);
3715
}
3816
<span class="hljs-keyword">else</span> {

0 commit comments

Comments
 (0)