ts-highlight 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.github/workflows/ci.yaml +29 -0
- package/LICENSE +24 -0
- package/README.md +1 -0
- package/biome.json +30 -0
- package/package.json +41 -0
- package/rollup.config.js +34 -0
- package/src/__tests__/bench/tokenizerBench.ts +0 -0
- package/src/__tests__/tokenizer.test.ts +59 -0
- package/src/generator/constants.ts +25 -0
- package/src/generator/generate.ts +199 -0
- package/src/generator/index.ts +2 -0
- package/src/generator/types.ts +73 -0
- package/src/highlight/highlight.ts +54 -0
- package/src/highlight/index.ts +1 -0
- package/src/index.ts +2 -0
- package/src/tokenizer/constants.ts +152 -0
- package/src/tokenizer/index.ts +2 -0
- package/src/tokenizer/tokenize.ts +302 -0
- package/src/tokenizer/types.ts +150 -0
- package/tsconfig.json +28 -0
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
name: ci
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
branches: '**'
|
|
6
|
+
|
|
7
|
+
jobs:
|
|
8
|
+
ci:
|
|
9
|
+
name: ci
|
|
10
|
+
|
|
11
|
+
runs-on: ubuntu-latest
|
|
12
|
+
|
|
13
|
+
steps:
|
|
14
|
+
- uses: actions/checkout@v4
|
|
15
|
+
|
|
16
|
+
- name: setup bun
|
|
17
|
+
uses: oven-sh/setup-bun@v2
|
|
18
|
+
- name: install dependencies
|
|
19
|
+
run: bun install
|
|
20
|
+
|
|
21
|
+
- name: run linter
|
|
22
|
+
run: bun run lint
|
|
23
|
+
|
|
24
|
+
- name: run tests
|
|
25
|
+
run: bun test
|
|
26
|
+
|
|
27
|
+
- name: build
|
|
28
|
+
|
|
29
|
+
run: bun run build
|
package/LICENSE
ADDED
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
This is free and unencumbered software released into the public domain.
|
|
2
|
+
|
|
3
|
+
Anyone is free to copy, modify, publish, use, compile, sell, or
|
|
4
|
+
distribute this software, either in source code form or as a compiled
|
|
5
|
+
binary, for any purpose, commercial or non-commercial, and by any
|
|
6
|
+
means.
|
|
7
|
+
|
|
8
|
+
In jurisdictions that recognize copyright laws, the author or authors
|
|
9
|
+
of this software dedicate any and all copyright interest in the
|
|
10
|
+
software to the public domain. We make this dedication for the benefit
|
|
11
|
+
of the public at large and to the detriment of our heirs and
|
|
12
|
+
successors. We intend this dedication to be an overt act of
|
|
13
|
+
relinquishment in perpetuity of all present and future rights to this
|
|
14
|
+
software under copyright law.
|
|
15
|
+
|
|
16
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
|
17
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
|
18
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
|
19
|
+
IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR
|
|
20
|
+
OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
|
|
21
|
+
ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
|
|
22
|
+
OTHER DEALINGS IN THE SOFTWARE.
|
|
23
|
+
|
|
24
|
+
For more information, please refer to <https://unlicense.org>
|
package/README.md
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
# <p align='center'> **es-highlight** </p>
|
package/biome.json
ADDED
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "https://biomejs.dev/schemas/2.3.10/schema.json",
|
|
3
|
+
"vcs": {
|
|
4
|
+
"enabled": true,
|
|
5
|
+
"clientKind": "git"
|
|
6
|
+
},
|
|
7
|
+
|
|
8
|
+
"files": {
|
|
9
|
+
"ignoreUnknown": false,
|
|
10
|
+
"includes": ["*", "!node_modules", "!dist"]
|
|
11
|
+
},
|
|
12
|
+
|
|
13
|
+
"formatter": {
|
|
14
|
+
"enabled": false
|
|
15
|
+
},
|
|
16
|
+
"linter": {
|
|
17
|
+
"enabled": true,
|
|
18
|
+
"rules": {
|
|
19
|
+
"recommended": true
|
|
20
|
+
}
|
|
21
|
+
},
|
|
22
|
+
"assist": {
|
|
23
|
+
"enabled": true,
|
|
24
|
+
"actions": {
|
|
25
|
+
"source": {
|
|
26
|
+
"organizeImports": "off"
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
}
|
package/package.json
ADDED
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "ts-highlight",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"module": "./dist/index.js",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"private": false,
|
|
7
|
+
"license": "UNLICENSED",
|
|
8
|
+
"author": {
|
|
9
|
+
"name": "marigold",
|
|
10
|
+
"url": "https://github.com/a-marigold"
|
|
11
|
+
},
|
|
12
|
+
"repository": {
|
|
13
|
+
"type": "github",
|
|
14
|
+
"url": "https://github.com/a-marigold/es-higрlight"
|
|
15
|
+
},
|
|
16
|
+
"scripts": {
|
|
17
|
+
"lint": "biome check",
|
|
18
|
+
"build": "rollup -c",
|
|
19
|
+
"prepublishOnly": "bun run build",
|
|
20
|
+
"pshPub": "bun publish --access public"
|
|
21
|
+
},
|
|
22
|
+
"main": "./dist/index.js",
|
|
23
|
+
"types": "./dist/index.d.ts",
|
|
24
|
+
"keywords": [
|
|
25
|
+
"es-higlight",
|
|
26
|
+
"higlight",
|
|
27
|
+
"syntax-higlight"
|
|
28
|
+
],
|
|
29
|
+
"devDependencies": {
|
|
30
|
+
"@biomejs/biome": "^2.3.10",
|
|
31
|
+
"@rollup/plugin-terser": "^0.4.4",
|
|
32
|
+
"@rollup/plugin-typescript": "^12.3.0",
|
|
33
|
+
"@types/bun": "latest",
|
|
34
|
+
"rollup": "^4.54.0",
|
|
35
|
+
"rollup-plugin-dts": "^6.3.0",
|
|
36
|
+
"tslib": "^2.8.1"
|
|
37
|
+
},
|
|
38
|
+
"peerDependencies": {
|
|
39
|
+
"typescript": "^5"
|
|
40
|
+
}
|
|
41
|
+
}
|
package/rollup.config.js
ADDED
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import { defineConfig } from 'rollup';
|
|
2
|
+
|
|
3
|
+
import typescript from '@rollup/plugin-typescript';
|
|
4
|
+
import dts from 'rollup-plugin-dts';
|
|
5
|
+
|
|
6
|
+
import terser from '@rollup/plugin-terser';
|
|
7
|
+
|
|
8
|
+
export default defineConfig([
|
|
9
|
+
{
|
|
10
|
+
input: './src/index.ts',
|
|
11
|
+
|
|
12
|
+
external: ['./src/__tests__/**'],
|
|
13
|
+
|
|
14
|
+
output: {
|
|
15
|
+
file: './dist/index.js',
|
|
16
|
+
format: 'esm',
|
|
17
|
+
},
|
|
18
|
+
plugins: [typescript({ exclude: ['**__tests__/**'] }), terser()],
|
|
19
|
+
},
|
|
20
|
+
|
|
21
|
+
{
|
|
22
|
+
input: './src/index.ts',
|
|
23
|
+
|
|
24
|
+
external: ['./src/__tests_/**'],
|
|
25
|
+
|
|
26
|
+
output: {
|
|
27
|
+
file: './dist/index.d.ts',
|
|
28
|
+
|
|
29
|
+
format: 'esm',
|
|
30
|
+
},
|
|
31
|
+
|
|
32
|
+
plugins: [dts()],
|
|
33
|
+
},
|
|
34
|
+
]);
|
|
File without changes
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
import { describe, it, expect } from 'bun:test';
|
|
2
|
+
|
|
3
|
+
import { tokenize } from '../tokenizer';
|
|
4
|
+
|
|
5
|
+
describe('tokenizer', () => {
|
|
6
|
+
it('should return a valid tokens array', () => {
|
|
7
|
+
const source = `
|
|
8
|
+
let num = 10;
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
console.log(num++);
|
|
12
|
+
`;
|
|
13
|
+
|
|
14
|
+
const tokens = tokenize(source);
|
|
15
|
+
|
|
16
|
+
console.log(tokens);
|
|
17
|
+
expect(tokens.length).toBe(25);
|
|
18
|
+
expect(
|
|
19
|
+
tokens.some(
|
|
20
|
+
(token) => token.type === 'Operator' && token.value === '++'
|
|
21
|
+
)
|
|
22
|
+
).toBe(true);
|
|
23
|
+
});
|
|
24
|
+
|
|
25
|
+
it('should group `space` or `tab` order to one `WhiteSpace` token', () => {
|
|
26
|
+
const tabSource = `\t\t\t\t\t\t`;
|
|
27
|
+
const spaceSource = ' ';
|
|
28
|
+
|
|
29
|
+
const tabTokens = tokenize(tabSource);
|
|
30
|
+
const spaceTokens = tokenize(spaceSource);
|
|
31
|
+
|
|
32
|
+
expect(tabTokens.length).toBe(1);
|
|
33
|
+
expect(tabTokens[0].type).toBe('WhiteSpace');
|
|
34
|
+
expect(tabTokens[0].value.length).toBe(tabSource.length);
|
|
35
|
+
|
|
36
|
+
expect(spaceTokens.length).toBe(1);
|
|
37
|
+
expect(spaceTokens[0].type).toBe('WhiteSpace');
|
|
38
|
+
expect(spaceTokens[0].value.length).toBe(spaceSource.length);
|
|
39
|
+
});
|
|
40
|
+
|
|
41
|
+
it('should handle CRLF and LF line feeds to `LineDivision` token', () => {
|
|
42
|
+
const CRLFSource = '\r\n\r\n\r\n\r\n\r\n\r\n';
|
|
43
|
+
const LFSource = '\n\n\n\n\n\n';
|
|
44
|
+
|
|
45
|
+
const CRLFTokens = tokenize(CRLFSource);
|
|
46
|
+
const LFTokens = tokenize(LFSource);
|
|
47
|
+
|
|
48
|
+
expect(CRLFTokens.length).toBe(CRLFSource.length / 2);
|
|
49
|
+
for (let i = CRLFTokens.length - 1; i > 0; i--) {
|
|
50
|
+
expect(CRLFTokens[i].type).toBe('LineDivision');
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
expect(LFTokens.length).toBe(LFSource.length);
|
|
54
|
+
|
|
55
|
+
for (let i = 0; i < CRLFTokens.length; i++) {
|
|
56
|
+
expect(LFTokens[i].type).toBe('LineDivision');
|
|
57
|
+
}
|
|
58
|
+
});
|
|
59
|
+
});
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* String with opened `span` HTML element with opened `class` attribute.
|
|
3
|
+
*
|
|
4
|
+
* There is double quote `"` in `class` attribute
|
|
5
|
+
*
|
|
6
|
+
* @example
|
|
7
|
+
* ```typescript
|
|
8
|
+
* OPENED_SPAN_WITH_CLASS + 'text-class' + '">' + 'lorem ipsum' + '</span>';
|
|
9
|
+
* // Output: '<span class="text-class">lorem ipsum</span>'
|
|
10
|
+
* ```
|
|
11
|
+
*/
|
|
12
|
+
export const OPENED_SPAN_WITH_CLASS = '<span class="';
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
*
|
|
16
|
+
* String with closed `span` HTML element.
|
|
17
|
+
*
|
|
18
|
+
*
|
|
19
|
+
* @example
|
|
20
|
+
* ```typescript
|
|
21
|
+
* '<span class="' + 'text-class' + '">' + 'dolor' + CLOSED_SPAN;
|
|
22
|
+
* // Output: <span class="text-class">dolor</span>
|
|
23
|
+
* ```
|
|
24
|
+
*/
|
|
25
|
+
export const CLOSED_SPAN = '</span>';
|
|
@@ -0,0 +1,199 @@
|
|
|
1
|
+
import { OPENED_SPAN_WITH_CLASS, CLOSED_SPAN } from './constants';
|
|
2
|
+
|
|
3
|
+
import type { Token } from '../tokenizer';
|
|
4
|
+
import type { HighlightCSSClasses } from './types';
|
|
5
|
+
|
|
6
|
+
export const generate = (
|
|
7
|
+
tokens: Token[],
|
|
8
|
+
cssClasses: HighlightCSSClasses
|
|
9
|
+
): string => {
|
|
10
|
+
const tokensLength = tokens.length;
|
|
11
|
+
|
|
12
|
+
let generated: string =
|
|
13
|
+
'<pre class="' +
|
|
14
|
+
cssClasses.pre +
|
|
15
|
+
'"><code class="' +
|
|
16
|
+
cssClasses.code +
|
|
17
|
+
'"><div class="' +
|
|
18
|
+
cssClasses.line +
|
|
19
|
+
'">';
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
*
|
|
23
|
+
* Boolean flag that shows is the current line opened.
|
|
24
|
+
* Needed for closing opened line in the end of loop.
|
|
25
|
+
*/
|
|
26
|
+
let isLineOpened: boolean = true;
|
|
27
|
+
|
|
28
|
+
let tokenPos = 0;
|
|
29
|
+
while (tokenPos < tokensLength) {
|
|
30
|
+
const currentToken = tokens[tokenPos];
|
|
31
|
+
|
|
32
|
+
if (currentToken.type === 'WhiteSpace') {
|
|
33
|
+
generated +=
|
|
34
|
+
OPENED_SPAN_WITH_CLASS +
|
|
35
|
+
cssClasses.token +
|
|
36
|
+
' ' +
|
|
37
|
+
cssClasses.whitespace +
|
|
38
|
+
'">';
|
|
39
|
+
|
|
40
|
+
let charPos = 0;
|
|
41
|
+
while (charPos < currentToken.value.length) {
|
|
42
|
+
generated += ' ';
|
|
43
|
+
charPos++;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
generated += CLOSED_SPAN;
|
|
47
|
+
|
|
48
|
+
tokenPos++;
|
|
49
|
+
|
|
50
|
+
continue;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
if (currentToken.type === 'LineDivision') {
|
|
54
|
+
generated += '</div>';
|
|
55
|
+
|
|
56
|
+
if (tokenPos !== tokensLength - 1) {
|
|
57
|
+
generated += '<div class="' + cssClasses.line + '">';
|
|
58
|
+
isLineOpened = false;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
tokenPos++;
|
|
62
|
+
|
|
63
|
+
continue;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
if (currentToken.type === 'Operator') {
|
|
67
|
+
generated +=
|
|
68
|
+
OPENED_SPAN_WITH_CLASS +
|
|
69
|
+
cssClasses.token +
|
|
70
|
+
' ' +
|
|
71
|
+
cssClasses.operator +
|
|
72
|
+
'">' +
|
|
73
|
+
currentToken.value +
|
|
74
|
+
CLOSED_SPAN;
|
|
75
|
+
|
|
76
|
+
tokenPos++;
|
|
77
|
+
|
|
78
|
+
continue;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
if (currentToken.type === 'Identifier') {
|
|
82
|
+
generated +=
|
|
83
|
+
OPENED_SPAN_WITH_CLASS +
|
|
84
|
+
cssClasses.token +
|
|
85
|
+
' ' +
|
|
86
|
+
cssClasses.mutableIdentifier +
|
|
87
|
+
'">' +
|
|
88
|
+
currentToken.value +
|
|
89
|
+
CLOSED_SPAN;
|
|
90
|
+
|
|
91
|
+
tokenPos++;
|
|
92
|
+
|
|
93
|
+
continue;
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
if (currentToken.type === 'Keyword') {
|
|
97
|
+
generated +=
|
|
98
|
+
OPENED_SPAN_WITH_CLASS +
|
|
99
|
+
cssClasses.token +
|
|
100
|
+
' ' +
|
|
101
|
+
cssClasses.keyword +
|
|
102
|
+
'">' +
|
|
103
|
+
currentToken.value +
|
|
104
|
+
CLOSED_SPAN;
|
|
105
|
+
|
|
106
|
+
tokenPos++;
|
|
107
|
+
|
|
108
|
+
continue;
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
// literals
|
|
112
|
+
if (currentToken.type === 'StringLiteral') {
|
|
113
|
+
generated +=
|
|
114
|
+
OPENED_SPAN_WITH_CLASS +
|
|
115
|
+
cssClasses.token +
|
|
116
|
+
' ' +
|
|
117
|
+
cssClasses.stringLiteral +
|
|
118
|
+
'">' +
|
|
119
|
+
currentToken.value +
|
|
120
|
+
CLOSED_SPAN;
|
|
121
|
+
|
|
122
|
+
tokenPos++;
|
|
123
|
+
|
|
124
|
+
continue;
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
if (currentToken.type === 'NumberLiteral') {
|
|
128
|
+
generated +=
|
|
129
|
+
OPENED_SPAN_WITH_CLASS +
|
|
130
|
+
cssClasses.token +
|
|
131
|
+
' ' +
|
|
132
|
+
cssClasses.numberLiteral +
|
|
133
|
+
'">' +
|
|
134
|
+
currentToken.value +
|
|
135
|
+
CLOSED_SPAN;
|
|
136
|
+
|
|
137
|
+
tokenPos++;
|
|
138
|
+
|
|
139
|
+
continue;
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
if (currentToken.type === 'BooleanLiteral') {
|
|
143
|
+
generated +=
|
|
144
|
+
OPENED_SPAN_WITH_CLASS +
|
|
145
|
+
cssClasses.token +
|
|
146
|
+
' ' +
|
|
147
|
+
cssClasses.booleanLiteral +
|
|
148
|
+
'">' +
|
|
149
|
+
currentToken.value +
|
|
150
|
+
CLOSED_SPAN;
|
|
151
|
+
|
|
152
|
+
tokenPos++;
|
|
153
|
+
|
|
154
|
+
continue;
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
if (currentToken.type === 'NaNLiteral') {
|
|
158
|
+
generated +=
|
|
159
|
+
OPENED_SPAN_WITH_CLASS +
|
|
160
|
+
cssClasses.token +
|
|
161
|
+
' ' +
|
|
162
|
+
cssClasses.NaNLiteral +
|
|
163
|
+
'">' +
|
|
164
|
+
currentToken.value +
|
|
165
|
+
CLOSED_SPAN;
|
|
166
|
+
|
|
167
|
+
tokenPos++;
|
|
168
|
+
|
|
169
|
+
continue;
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
// comments
|
|
173
|
+
if (currentToken.type === 'Comment') {
|
|
174
|
+
generated +=
|
|
175
|
+
OPENED_SPAN_WITH_CLASS +
|
|
176
|
+
cssClasses.token +
|
|
177
|
+
' ' +
|
|
178
|
+
cssClasses.comment +
|
|
179
|
+
'">' +
|
|
180
|
+
currentToken.value +
|
|
181
|
+
CLOSED_SPAN;
|
|
182
|
+
|
|
183
|
+
tokenPos++;
|
|
184
|
+
|
|
185
|
+
continue;
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
// fallback
|
|
189
|
+
tokenPos++;
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
if (isLineOpened) {
|
|
193
|
+
generated += '</div>';
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
generated += '</code></pre>';
|
|
197
|
+
|
|
198
|
+
return generated;
|
|
199
|
+
};
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Type of CSS classes object.
|
|
3
|
+
*
|
|
4
|
+
* Classes are used in generated HTML.
|
|
5
|
+
*
|
|
6
|
+
* CSS modules also can be used
|
|
7
|
+
*
|
|
8
|
+
*
|
|
9
|
+
*
|
|
10
|
+
* Default generated HTML structure:
|
|
11
|
+
* ```html
|
|
12
|
+
* <pre>
|
|
13
|
+
* <code>
|
|
14
|
+
* <div> ... </div> <!-- line -->
|
|
15
|
+
*
|
|
16
|
+
* ...
|
|
17
|
+
* </code>
|
|
18
|
+
* </pre>
|
|
19
|
+
* ```
|
|
20
|
+
*
|
|
21
|
+
* @example
|
|
22
|
+
* const CSSClasses: HighlightCSSClasses = {
|
|
23
|
+
* token: 'my-token-classname',
|
|
24
|
+
* comment: 'my-comment-classname',
|
|
25
|
+
* ...
|
|
26
|
+
* };
|
|
27
|
+
*/
|
|
28
|
+
export type HighlightCSSClasses = Partial<{
|
|
29
|
+
/**
|
|
30
|
+
*
|
|
31
|
+
*
|
|
32
|
+
* `pre` class is used for root wrapper element
|
|
33
|
+
*/
|
|
34
|
+
pre: string;
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* `code` class is used for generated code text element
|
|
38
|
+
*/
|
|
39
|
+
code: string;
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* `line` class is used for every line of generated code
|
|
43
|
+
*/
|
|
44
|
+
line: string;
|
|
45
|
+
|
|
46
|
+
/**
|
|
47
|
+
*
|
|
48
|
+
*/
|
|
49
|
+
|
|
50
|
+
/**
|
|
51
|
+
* `token` class is used for every token of code, even whitespace
|
|
52
|
+
*/
|
|
53
|
+
token: string;
|
|
54
|
+
|
|
55
|
+
// trivia
|
|
56
|
+
whitespace: string;
|
|
57
|
+
comment: string;
|
|
58
|
+
|
|
59
|
+
keyword: string;
|
|
60
|
+
|
|
61
|
+
operator: string;
|
|
62
|
+
|
|
63
|
+
// identifiers
|
|
64
|
+
constantIdentifier: string;
|
|
65
|
+
mutableIdentifier: string;
|
|
66
|
+
|
|
67
|
+
// literals
|
|
68
|
+
numberLiteral: string;
|
|
69
|
+
stringLiteral: string;
|
|
70
|
+
booleanLiteral: string;
|
|
71
|
+
NaNLiteral: string;
|
|
72
|
+
bigintChar: string;
|
|
73
|
+
}>;
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
import { tokenize } from '../tokenizer';
|
|
2
|
+
import { generate } from '../generator';
|
|
3
|
+
import type { HighlightCSSClasses } from '../generator';
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
*
|
|
7
|
+
* Transforms received `source` string to HTML with higlighted source code.
|
|
8
|
+
*
|
|
9
|
+
* Classes could be divided with space as if they are in default HTML `class` attribute.
|
|
10
|
+
*
|
|
11
|
+
* @param {string} source JavaScript or TypeScript source code to highlight.
|
|
12
|
+
* @param {HiglightCSSClasses} cssClasses object with CSS classes.
|
|
13
|
+
*
|
|
14
|
+
* @returns {string} string with HTML of highlighted code.
|
|
15
|
+
*
|
|
16
|
+
* @example
|
|
17
|
+
* ```typescript
|
|
18
|
+
* const cssClasses: HighlightCSSClasses = {
|
|
19
|
+
* pre: 'pre-element-class second-class',
|
|
20
|
+
* code: 'my-code-element-class',
|
|
21
|
+
*
|
|
22
|
+
* line: 'line-class my-line third-class',
|
|
23
|
+
* token: 'token-class',
|
|
24
|
+
*
|
|
25
|
+
* operator: 'operator-class',
|
|
26
|
+
*
|
|
27
|
+
* keyword: 'keyword-class',
|
|
28
|
+
* stringLiteral: 'string-class',
|
|
29
|
+
*
|
|
30
|
+
* ...
|
|
31
|
+
* }
|
|
32
|
+
*
|
|
33
|
+
* highlight('let a = "hello";', cssClasses);
|
|
34
|
+
* ```
|
|
35
|
+
* Output will be:
|
|
36
|
+
* ```html
|
|
37
|
+
* <pre class="pre-element-class">
|
|
38
|
+
* <code class="code-element-class">
|
|
39
|
+
* <div class="line-class"> ... </div> <!-- code line with generated tokens inside -->
|
|
40
|
+
* </code>
|
|
41
|
+
* </pre>
|
|
42
|
+
* ```
|
|
43
|
+
*/
|
|
44
|
+
export const highlight = (
|
|
45
|
+
source: string,
|
|
46
|
+
|
|
47
|
+
cssClasses: HighlightCSSClasses
|
|
48
|
+
): string => {
|
|
49
|
+
const tokens = tokenize(source);
|
|
50
|
+
|
|
51
|
+
const generated = generate(tokens, cssClasses);
|
|
52
|
+
|
|
53
|
+
return generated;
|
|
54
|
+
};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { highlight } from './highlight';
|
package/src/index.ts
ADDED
|
@@ -0,0 +1,152 @@
|
|
|
1
|
+
import type {
|
|
2
|
+
TokenSet,
|
|
3
|
+
SingleOperators,
|
|
4
|
+
DoubleOperators,
|
|
5
|
+
TripleOperators,
|
|
6
|
+
QuadrupleOperator,
|
|
7
|
+
Keywords,
|
|
8
|
+
} from './types';
|
|
9
|
+
|
|
10
|
+
// operators
|
|
11
|
+
|
|
12
|
+
const singleOperatorsInit: SingleOperators = [
|
|
13
|
+
'=',
|
|
14
|
+
'+',
|
|
15
|
+
'-',
|
|
16
|
+
'*',
|
|
17
|
+
'/',
|
|
18
|
+
'%',
|
|
19
|
+
'~',
|
|
20
|
+
'^',
|
|
21
|
+
'.',
|
|
22
|
+
':',
|
|
23
|
+
'|',
|
|
24
|
+
'&',
|
|
25
|
+
'?',
|
|
26
|
+
'!',
|
|
27
|
+
'<',
|
|
28
|
+
'>',
|
|
29
|
+
'{',
|
|
30
|
+
'}',
|
|
31
|
+
'[',
|
|
32
|
+
']',
|
|
33
|
+
'(',
|
|
34
|
+
')',
|
|
35
|
+
';',
|
|
36
|
+
',',
|
|
37
|
+
];
|
|
38
|
+
const doubleOperatorsInit: DoubleOperators = [
|
|
39
|
+
'==',
|
|
40
|
+
'!=',
|
|
41
|
+
|
|
42
|
+
'<=',
|
|
43
|
+
'>=',
|
|
44
|
+
|
|
45
|
+
'++',
|
|
46
|
+
'--',
|
|
47
|
+
'**',
|
|
48
|
+
|
|
49
|
+
'*=',
|
|
50
|
+
'/=',
|
|
51
|
+
'%=',
|
|
52
|
+
|
|
53
|
+
'^=',
|
|
54
|
+
'&=',
|
|
55
|
+
'|=',
|
|
56
|
+
|
|
57
|
+
'&&',
|
|
58
|
+
'||',
|
|
59
|
+
'??',
|
|
60
|
+
];
|
|
61
|
+
const tripleOperatorsInit: TripleOperators = [
|
|
62
|
+
'===',
|
|
63
|
+
'!==',
|
|
64
|
+
|
|
65
|
+
'**=',
|
|
66
|
+
'<<=',
|
|
67
|
+
'>>=',
|
|
68
|
+
'>>>',
|
|
69
|
+
'&&=',
|
|
70
|
+
'||=',
|
|
71
|
+
'??=',
|
|
72
|
+
];
|
|
73
|
+
|
|
74
|
+
/**
|
|
75
|
+
* `Set` with all javascript one-symbol operators
|
|
76
|
+
*
|
|
77
|
+
* @example '='
|
|
78
|
+
*/
|
|
79
|
+
export const singleOperators: TokenSet = new Set(singleOperatorsInit);
|
|
80
|
+
|
|
81
|
+
/**
|
|
82
|
+
* `Set` with all javascript two-symbol operators
|
|
83
|
+
*
|
|
84
|
+
* @example '++'
|
|
85
|
+
*/
|
|
86
|
+
export const doubleOperators: TokenSet = new Set(doubleOperatorsInit);
|
|
87
|
+
|
|
88
|
+
/**
|
|
89
|
+
* `Set` with all javascript three-symbol operators
|
|
90
|
+
*
|
|
91
|
+
* @example '>>>'
|
|
92
|
+
*/
|
|
93
|
+
export const tripleOperators: TokenSet = new Set(tripleOperatorsInit);
|
|
94
|
+
|
|
95
|
+
/**
|
|
96
|
+
* The javascript four-symbol operator.
|
|
97
|
+
*
|
|
98
|
+
* Always equals '>>>=' on December 18, 2025 ECMAScript Specification
|
|
99
|
+
*/
|
|
100
|
+
export const quadrupleOperator: QuadrupleOperator = '>>>=';
|
|
101
|
+
|
|
102
|
+
// keywords
|
|
103
|
+
|
|
104
|
+
const keywordsInit: Keywords = [
|
|
105
|
+
'var',
|
|
106
|
+
'let',
|
|
107
|
+
'const',
|
|
108
|
+
'typeof',
|
|
109
|
+
'class',
|
|
110
|
+
|
|
111
|
+
'in',
|
|
112
|
+
|
|
113
|
+
'new',
|
|
114
|
+
|
|
115
|
+
'instanceof',
|
|
116
|
+
'function',
|
|
117
|
+
|
|
118
|
+
'void',
|
|
119
|
+
'delete',
|
|
120
|
+
'keyof',
|
|
121
|
+
'abstract',
|
|
122
|
+
'interface',
|
|
123
|
+
'enum',
|
|
124
|
+
'type',
|
|
125
|
+
'implements',
|
|
126
|
+
];
|
|
127
|
+
|
|
128
|
+
/**
|
|
129
|
+
* `Set` with javascript and typescript keywords
|
|
130
|
+
*/
|
|
131
|
+
export const keywords: TokenSet = new Set(keywordsInit);
|
|
132
|
+
|
|
133
|
+
// regular expresions (RegExp)
|
|
134
|
+
|
|
135
|
+
/**
|
|
136
|
+
* RegExp that is used to match javascript identifier start symbol
|
|
137
|
+
*
|
|
138
|
+
*/
|
|
139
|
+
|
|
140
|
+
export const IDENTIFIER_START_REGEXP: RegExp = /^[a-zA-Zа-яА-Я_$]$/;
|
|
141
|
+
|
|
142
|
+
/**
|
|
143
|
+
* RegExp that is used to match javascript identifier symbols after the first symbol
|
|
144
|
+
*/
|
|
145
|
+
|
|
146
|
+
export const IDENTIFIER_REGEXP: RegExp = /^[a-zA-Zа-яА-Я_$0-9]$/;
|
|
147
|
+
|
|
148
|
+
/**
|
|
149
|
+
*
|
|
150
|
+
* RegExp that is used to match any number
|
|
151
|
+
*/
|
|
152
|
+
export const NUMBER_REGEXP: RegExp = /^[0-9]$/;
|
|
@@ -0,0 +1,302 @@
|
|
|
1
|
+
import {
|
|
2
|
+
IDENTIFIER_START_REGEXP,
|
|
3
|
+
IDENTIFIER_REGEXP,
|
|
4
|
+
NUMBER_REGEXP,
|
|
5
|
+
singleOperators,
|
|
6
|
+
doubleOperators,
|
|
7
|
+
tripleOperators,
|
|
8
|
+
quadrupleOperator,
|
|
9
|
+
keywords,
|
|
10
|
+
} from './constants';
|
|
11
|
+
|
|
12
|
+
import type { Token } from './types';
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
*
|
|
16
|
+
* *Tokenizer* or *Lexer* function.
|
|
17
|
+
*
|
|
18
|
+
*
|
|
19
|
+
* Divides `source` to tokens.
|
|
20
|
+
*
|
|
21
|
+
* @param {string} source - javascript or typescript source code to tokenize.
|
|
22
|
+
*
|
|
23
|
+
* @returns {Token[]} array with tokens from `source`.
|
|
24
|
+
*/
|
|
25
|
+
export const tokenize = (source: string): Token[] => {
|
|
26
|
+
const tokens: Token[] = [];
|
|
27
|
+
|
|
28
|
+
const sourceLength = source.length;
|
|
29
|
+
|
|
30
|
+
let pos = 0;
|
|
31
|
+
main: while (pos < sourceLength) {
|
|
32
|
+
if (source[pos] === ' ' || source[pos] === '\t') {
|
|
33
|
+
const startPos = pos;
|
|
34
|
+
|
|
35
|
+
pos++;
|
|
36
|
+
|
|
37
|
+
while (
|
|
38
|
+
pos < sourceLength &&
|
|
39
|
+
(source[pos] === ' ' || source[pos] === '\t')
|
|
40
|
+
) {
|
|
41
|
+
pos++;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
tokens[tokens.length] = {
|
|
45
|
+
type: 'WhiteSpace',
|
|
46
|
+
|
|
47
|
+
value: source.slice(startPos, pos),
|
|
48
|
+
|
|
49
|
+
start: startPos,
|
|
50
|
+
|
|
51
|
+
end: pos,
|
|
52
|
+
};
|
|
53
|
+
|
|
54
|
+
continue main;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
if (source[pos] === '\n' || source[pos] === '\r') {
|
|
58
|
+
const startPos = pos;
|
|
59
|
+
|
|
60
|
+
if (source[pos] === '\r') {
|
|
61
|
+
pos++;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
pos++;
|
|
65
|
+
|
|
66
|
+
tokens[tokens.length] = {
|
|
67
|
+
type: 'LineDivision',
|
|
68
|
+
|
|
69
|
+
value: '\n',
|
|
70
|
+
|
|
71
|
+
start: startPos,
|
|
72
|
+
end: pos,
|
|
73
|
+
};
|
|
74
|
+
|
|
75
|
+
continue main;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
// literals
|
|
79
|
+
if (IDENTIFIER_START_REGEXP.test(source[pos])) {
|
|
80
|
+
const startPos = pos;
|
|
81
|
+
pos++;
|
|
82
|
+
|
|
83
|
+
while (pos < sourceLength && IDENTIFIER_REGEXP.test(source[pos])) {
|
|
84
|
+
pos++;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
const identifier = source.slice(startPos, pos);
|
|
88
|
+
|
|
89
|
+
tokens[tokens.length] = {
|
|
90
|
+
type: keywords.has(identifier) ? 'Keyword' : 'Identifier',
|
|
91
|
+
value: identifier,
|
|
92
|
+
start: startPos,
|
|
93
|
+
end: pos,
|
|
94
|
+
};
|
|
95
|
+
|
|
96
|
+
continue main;
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
if (source[pos] === "'" || source[pos] === '"' || source[pos] === '`') {
|
|
100
|
+
const startPos = pos;
|
|
101
|
+
const startQuote = source[pos];
|
|
102
|
+
|
|
103
|
+
pos++;
|
|
104
|
+
|
|
105
|
+
while (pos < sourceLength && source[pos] !== startQuote) {
|
|
106
|
+
pos++;
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
pos++;
|
|
110
|
+
|
|
111
|
+
tokens[tokens.length] = {
|
|
112
|
+
type: 'StringLiteral',
|
|
113
|
+
value: source.slice(startPos, pos),
|
|
114
|
+
start: startPos,
|
|
115
|
+
end: pos,
|
|
116
|
+
};
|
|
117
|
+
|
|
118
|
+
continue main;
|
|
119
|
+
}
|
|
120
|
+
if (NUMBER_REGEXP.test(source[pos])) {
|
|
121
|
+
const startPos = pos;
|
|
122
|
+
|
|
123
|
+
while (pos < sourceLength && NUMBER_REGEXP.test(source[pos])) {
|
|
124
|
+
pos++;
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
tokens[tokens.length] = {
|
|
128
|
+
type: 'NumberLiteral',
|
|
129
|
+
value: source.slice(startPos, pos),
|
|
130
|
+
start: startPos,
|
|
131
|
+
end: pos,
|
|
132
|
+
};
|
|
133
|
+
|
|
134
|
+
continue main;
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
// comments
|
|
138
|
+
if (source[pos] === '/') {
|
|
139
|
+
const startPos = pos;
|
|
140
|
+
|
|
141
|
+
pos++;
|
|
142
|
+
|
|
143
|
+
if (source[pos] === '/') {
|
|
144
|
+
pos++;
|
|
145
|
+
|
|
146
|
+
while (
|
|
147
|
+
pos < sourceLength &&
|
|
148
|
+
source[pos] !== '\r' &&
|
|
149
|
+
source[pos] !== '\n'
|
|
150
|
+
) {
|
|
151
|
+
pos++;
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
if (source[pos] === '\r') {
|
|
155
|
+
pos += 2;
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
tokens[tokens.length] = {
|
|
159
|
+
type: 'Comment',
|
|
160
|
+
|
|
161
|
+
value: source.slice(startPos, pos),
|
|
162
|
+
start: startPos,
|
|
163
|
+
end: pos,
|
|
164
|
+
};
|
|
165
|
+
|
|
166
|
+
tokens[tokens.length] = {
|
|
167
|
+
type: 'LineDivision',
|
|
168
|
+
|
|
169
|
+
value: '\n',
|
|
170
|
+
|
|
171
|
+
start: startPos,
|
|
172
|
+
end: pos,
|
|
173
|
+
};
|
|
174
|
+
|
|
175
|
+
continue main;
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
if (source[pos] === '*') {
|
|
179
|
+
pos++;
|
|
180
|
+
|
|
181
|
+
let lastCommentStart = startPos;
|
|
182
|
+
|
|
183
|
+
while (
|
|
184
|
+
pos < sourceLength &&
|
|
185
|
+
!(source[pos] === '*' && source[pos + 1] === '/')
|
|
186
|
+
) {
|
|
187
|
+
if (source[pos] === '\n' || source[pos] === '\r') {
|
|
188
|
+
tokens[tokens.length] = {
|
|
189
|
+
type: 'Comment',
|
|
190
|
+
|
|
191
|
+
value: source.slice(lastCommentStart, pos),
|
|
192
|
+
|
|
193
|
+
start: startPos,
|
|
194
|
+
end: pos,
|
|
195
|
+
};
|
|
196
|
+
tokens[tokens.length] = {
|
|
197
|
+
type: 'LineDivision',
|
|
198
|
+
|
|
199
|
+
value: '\n',
|
|
200
|
+
|
|
201
|
+
start: pos,
|
|
202
|
+
end: pos + 1,
|
|
203
|
+
};
|
|
204
|
+
|
|
205
|
+
if (source[pos] === '\r') {
|
|
206
|
+
pos++;
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
pos++;
|
|
210
|
+
lastCommentStart = pos;
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
pos++;
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
pos += 2;
|
|
217
|
+
|
|
218
|
+
tokens[tokens.length] = {
|
|
219
|
+
type: 'Comment',
|
|
220
|
+
value: source.slice(lastCommentStart, pos),
|
|
221
|
+
start: startPos,
|
|
222
|
+
end: pos,
|
|
223
|
+
};
|
|
224
|
+
|
|
225
|
+
continue main;
|
|
226
|
+
}
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
// operators
|
|
230
|
+
if (
|
|
231
|
+
source[pos] +
|
|
232
|
+
source[pos + 1] +
|
|
233
|
+
source[pos + 2] +
|
|
234
|
+
source[pos + 3] ===
|
|
235
|
+
quadrupleOperator
|
|
236
|
+
) {
|
|
237
|
+
const startPos = pos;
|
|
238
|
+
|
|
239
|
+
pos += 4;
|
|
240
|
+
|
|
241
|
+
tokens[tokens.length] = {
|
|
242
|
+
type: 'Operator',
|
|
243
|
+
value: source.slice(startPos, pos),
|
|
244
|
+
start: startPos,
|
|
245
|
+
end: pos,
|
|
246
|
+
};
|
|
247
|
+
|
|
248
|
+
continue main;
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
if (
|
|
252
|
+
tripleOperators.has(source[pos] + source[pos + 1] + source[pos + 2])
|
|
253
|
+
) {
|
|
254
|
+
const startPos = pos;
|
|
255
|
+
|
|
256
|
+
pos += 3;
|
|
257
|
+
|
|
258
|
+
tokens[tokens.length] = {
|
|
259
|
+
type: 'Operator',
|
|
260
|
+
value: source.slice(startPos, pos),
|
|
261
|
+
start: startPos,
|
|
262
|
+
end: pos,
|
|
263
|
+
};
|
|
264
|
+
|
|
265
|
+
continue main;
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
if (doubleOperators.has(source[pos] + source[pos + 1])) {
|
|
269
|
+
const startPos = pos;
|
|
270
|
+
|
|
271
|
+
pos += 2;
|
|
272
|
+
|
|
273
|
+
tokens[tokens.length] = {
|
|
274
|
+
type: 'Operator',
|
|
275
|
+
value: source.slice(startPos, pos),
|
|
276
|
+
start: startPos,
|
|
277
|
+
end: pos,
|
|
278
|
+
};
|
|
279
|
+
|
|
280
|
+
continue main;
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
if (singleOperators.has(source[pos])) {
|
|
284
|
+
const startPos = pos;
|
|
285
|
+
|
|
286
|
+
pos++;
|
|
287
|
+
|
|
288
|
+
tokens[tokens.length] = {
|
|
289
|
+
type: 'Operator',
|
|
290
|
+
value: source.slice(startPos, pos),
|
|
291
|
+
start: startPos,
|
|
292
|
+
end: pos,
|
|
293
|
+
};
|
|
294
|
+
|
|
295
|
+
continue main;
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
pos++;
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
return tokens;
|
|
302
|
+
};
|
|
@@ -0,0 +1,150 @@
|
|
|
1
|
+
type LiteralTokenType =
|
|
2
|
+
| 'NumberLiteral'
|
|
3
|
+
| 'StringLiteral'
|
|
4
|
+
| 'BooleanLiteral'
|
|
5
|
+
| 'NaNLiteral';
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Variety of `Token` types
|
|
9
|
+
*/
|
|
10
|
+
export type TokenType =
|
|
11
|
+
| 'Identifier'
|
|
12
|
+
| 'Keyword'
|
|
13
|
+
| 'Operator'
|
|
14
|
+
| 'WhiteSpace'
|
|
15
|
+
| 'Comment'
|
|
16
|
+
| 'LineDivision'
|
|
17
|
+
| LiteralTokenType;
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
*
|
|
21
|
+
*/
|
|
22
|
+
export type Token = {
|
|
23
|
+
type: TokenType;
|
|
24
|
+
value: string;
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* Start position of token value in source code
|
|
28
|
+
*/
|
|
29
|
+
start: number;
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* The end of token value in source code
|
|
33
|
+
*/
|
|
34
|
+
end: number;
|
|
35
|
+
};
|
|
36
|
+
|
|
37
|
+
export type SingleOperators = [
|
|
38
|
+
'=',
|
|
39
|
+
'+',
|
|
40
|
+
'-',
|
|
41
|
+
'*',
|
|
42
|
+
'/',
|
|
43
|
+
'%',
|
|
44
|
+
'~',
|
|
45
|
+
'^',
|
|
46
|
+
'.',
|
|
47
|
+
':',
|
|
48
|
+
'|',
|
|
49
|
+
'&',
|
|
50
|
+
'?',
|
|
51
|
+
'!',
|
|
52
|
+
'<',
|
|
53
|
+
'>',
|
|
54
|
+
|
|
55
|
+
'{',
|
|
56
|
+
'}',
|
|
57
|
+
'[',
|
|
58
|
+
']',
|
|
59
|
+
'(',
|
|
60
|
+
')',
|
|
61
|
+
|
|
62
|
+
';',
|
|
63
|
+
','
|
|
64
|
+
];
|
|
65
|
+
export type SingleOperator = SingleOperators[number];
|
|
66
|
+
|
|
67
|
+
export type DoubleOperators = [
|
|
68
|
+
'==',
|
|
69
|
+
|
|
70
|
+
'!=',
|
|
71
|
+
'<=',
|
|
72
|
+
'>=',
|
|
73
|
+
'++',
|
|
74
|
+
'--',
|
|
75
|
+
'**',
|
|
76
|
+
'*=',
|
|
77
|
+
'/=',
|
|
78
|
+
'%=',
|
|
79
|
+
'^=',
|
|
80
|
+
'&=',
|
|
81
|
+
'|=',
|
|
82
|
+
|
|
83
|
+
'&&',
|
|
84
|
+
'||',
|
|
85
|
+
'??'
|
|
86
|
+
];
|
|
87
|
+
export type DoubleOperator = DoubleOperators[number];
|
|
88
|
+
|
|
89
|
+
export type TripleOperators = [
|
|
90
|
+
'===',
|
|
91
|
+
'!==',
|
|
92
|
+
'**=',
|
|
93
|
+
|
|
94
|
+
'<<=',
|
|
95
|
+
'>>=',
|
|
96
|
+
'>>>',
|
|
97
|
+
|
|
98
|
+
'&&=',
|
|
99
|
+
'||=',
|
|
100
|
+
'??='
|
|
101
|
+
];
|
|
102
|
+
export type TripleOperator = TripleOperators[number];
|
|
103
|
+
|
|
104
|
+
export type QuadrupleOperator = '>>>=';
|
|
105
|
+
|
|
106
|
+
export type JSKeywords = [
|
|
107
|
+
'var',
|
|
108
|
+
'let',
|
|
109
|
+
|
|
110
|
+
'const',
|
|
111
|
+
|
|
112
|
+
'typeof',
|
|
113
|
+
|
|
114
|
+
'class',
|
|
115
|
+
'in',
|
|
116
|
+
|
|
117
|
+
'new',
|
|
118
|
+
'instanceof',
|
|
119
|
+
|
|
120
|
+
'function',
|
|
121
|
+
'void',
|
|
122
|
+
'delete'
|
|
123
|
+
];
|
|
124
|
+
export type JSKeyword = JSKeywords[number];
|
|
125
|
+
export type TSKeywords = [
|
|
126
|
+
'keyof',
|
|
127
|
+
'abstract',
|
|
128
|
+
'interface',
|
|
129
|
+
'enum',
|
|
130
|
+
'type',
|
|
131
|
+
'implements'
|
|
132
|
+
];
|
|
133
|
+
export type TSKeyword = TSKeywords[number];
|
|
134
|
+
|
|
135
|
+
export type Keywords = [...JSKeywords, ...TSKeywords];
|
|
136
|
+
export type Keyword = Keywords[number];
|
|
137
|
+
|
|
138
|
+
/**
|
|
139
|
+
*
|
|
140
|
+
* Type that contains `TokenType` values to be checked in tokenizer.
|
|
141
|
+
*
|
|
142
|
+
*
|
|
143
|
+
* @example
|
|
144
|
+
* ```typescript
|
|
145
|
+
* const singleOperatorsInit: SingleOperators = ['=', '+', '-' ...];
|
|
146
|
+
*
|
|
147
|
+
* const singleOperators: TokenSet = new Set(singleOperatorsInit);
|
|
148
|
+
* ```
|
|
149
|
+
*/
|
|
150
|
+
export type TokenSet = Set<string>;
|
package/tsconfig.json
ADDED
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
{
|
|
2
|
+
"compilerOptions": {
|
|
3
|
+
"lib": ["ESNext"],
|
|
4
|
+
"target": "ESNext",
|
|
5
|
+
"module": "esnext",
|
|
6
|
+
"moduleDetection": "force",
|
|
7
|
+
|
|
8
|
+
"moduleResolution": "bundler",
|
|
9
|
+
|
|
10
|
+
"verbatimModuleSyntax": true,
|
|
11
|
+
|
|
12
|
+
"noEmit": true,
|
|
13
|
+
|
|
14
|
+
"strict": true,
|
|
15
|
+
"skipLibCheck": true,
|
|
16
|
+
"noFallthroughCasesInSwitch": true,
|
|
17
|
+
"noUncheckedIndexedAccess": false,
|
|
18
|
+
"noPropertyAccessFromIndexSignature": true,
|
|
19
|
+
"noImplicitOverride": true,
|
|
20
|
+
|
|
21
|
+
"noUnusedLocals": true,
|
|
22
|
+
"noUnusedParameters": true,
|
|
23
|
+
|
|
24
|
+
"outDir": "dist"
|
|
25
|
+
},
|
|
26
|
+
|
|
27
|
+
"exclude": ["node_modules", "dist"]
|
|
28
|
+
}
|