ts-highlight 0.2.0 → 0.3.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/dist/index.d.ts +106 -0
- package/dist/index.js +1 -0
- package/package.json +4 -1
- package/.github/workflows/ci.yaml +0 -29
- package/assets/marigolds.webp +0 -0
- package/biome.json +0 -30
- package/rollup.config.js +0 -34
- package/src/__tests__/bench/tokenizerBench.ts +0 -0
- package/src/__tests__/tokenizer.test.ts +0 -59
- package/src/generator/constants.ts +0 -25
- package/src/generator/generate.ts +0 -214
- package/src/generator/index.ts +0 -2
- package/src/generator/types.ts +0 -75
- package/src/highlight/highlight.ts +0 -54
- package/src/highlight/index.ts +0 -1
- package/src/index.ts +0 -2
- package/src/tokenizer/constants.ts +0 -188
- package/src/tokenizer/index.ts +0 -2
- package/src/tokenizer/tokenize.ts +0 -304
- package/src/tokenizer/types.ts +0 -197
- package/tsconfig.json +0 -28
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,106 @@
|
|
|
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
|
+
type HighlightCSSClasses = Partial<{
|
|
29
|
+
/**
|
|
30
|
+
*
|
|
31
|
+
*
|
|
32
|
+
* `pre` class is used for root wrapper element
|
|
33
|
+
*/
|
|
34
|
+
pre: string;
|
|
35
|
+
/**
|
|
36
|
+
* `code` class is used for generated code text element
|
|
37
|
+
*/
|
|
38
|
+
code: string;
|
|
39
|
+
/**
|
|
40
|
+
* `line` class is used for every line of generated code
|
|
41
|
+
*/
|
|
42
|
+
line: string;
|
|
43
|
+
/**
|
|
44
|
+
*
|
|
45
|
+
*/
|
|
46
|
+
/**
|
|
47
|
+
* `token` class is used for every token of code, even whitespace
|
|
48
|
+
*/
|
|
49
|
+
token: string;
|
|
50
|
+
whitespace: string;
|
|
51
|
+
comment: string;
|
|
52
|
+
keyword: string;
|
|
53
|
+
instruction: string;
|
|
54
|
+
operator: string;
|
|
55
|
+
constantIdentifier: string;
|
|
56
|
+
mutableIdentifier: string;
|
|
57
|
+
numberLiteral: string;
|
|
58
|
+
stringLiteral: string;
|
|
59
|
+
booleanLiteral: string;
|
|
60
|
+
NaNLiteral: string;
|
|
61
|
+
bigintChar: string;
|
|
62
|
+
}>;
|
|
63
|
+
|
|
64
|
+
/**
|
|
65
|
+
*
|
|
66
|
+
* Transforms received `source` string to HTML with higlighted source code.
|
|
67
|
+
*
|
|
68
|
+
* Classes could be divided with space as if they are in default HTML `class` attribute.
|
|
69
|
+
*
|
|
70
|
+
* @param {string} source JavaScript or TypeScript source code to highlight.
|
|
71
|
+
* @param {HiglightCSSClasses} cssClasses object with CSS classes.
|
|
72
|
+
*
|
|
73
|
+
* @returns {string} string with HTML of highlighted code.
|
|
74
|
+
*
|
|
75
|
+
* @example
|
|
76
|
+
* ```typescript
|
|
77
|
+
* const cssClasses: HighlightCSSClasses = {
|
|
78
|
+
* pre: 'pre-element-class second-class',
|
|
79
|
+
* code: 'my-code-element-class',
|
|
80
|
+
*
|
|
81
|
+
* line: 'line-class my-line third-class',
|
|
82
|
+
* token: 'token-class',
|
|
83
|
+
*
|
|
84
|
+
* operator: 'operator-class',
|
|
85
|
+
*
|
|
86
|
+
* keyword: 'keyword-class',
|
|
87
|
+
* stringLiteral: 'string-class',
|
|
88
|
+
*
|
|
89
|
+
* ...
|
|
90
|
+
* }
|
|
91
|
+
*
|
|
92
|
+
* highlight('let a = "hello";', cssClasses);
|
|
93
|
+
* ```
|
|
94
|
+
* Output will be:
|
|
95
|
+
* ```html
|
|
96
|
+
* <pre class="pre-element-class">
|
|
97
|
+
* <code class="code-element-class">
|
|
98
|
+
* <div class="line-class"> ... </div> <!-- code line with generated tokens inside -->
|
|
99
|
+
* </code>
|
|
100
|
+
* </pre>
|
|
101
|
+
* ```
|
|
102
|
+
*/
|
|
103
|
+
declare const highlight: (source: string, cssClasses: HighlightCSSClasses) => string;
|
|
104
|
+
|
|
105
|
+
export { highlight };
|
|
106
|
+
export type { HighlightCSSClasses };
|
package/dist/index.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
const e=new Set(["=","+","-","*","/","%","~","^",".",":","|","&","?","!","<",">","{","}","[","]","(",")",";",","]),t=new Set(["==","!=","<=",">=","++","--","**","*=","/=","%=","^=","&=","|=","&&","||","??"]),n=new Set(["===","!==","**=","<<=",">>=",">>>","&&=","||=","??="]),o={NaN:"NaNLiteral",abstract:"Keyword",as:"Instruction",assert:"Instruction",asserts:"Instruction",async:"Instruction",await:"Instruction",break:"Instruction",catch:"Instruction",class:"Keyword",const:"Keyword",continue:"Instruction",debugger:"Keyword",declare:"Keyword",default:"Instruction",delete:"Keyword",do:"Instruction",enum:"Keyword",export:"Instruction",false:"BooleanLiteral",finally:"Instruction",for:"Instruction",function:"Keyword",implements:"Keyword",import:"Instruction",in:"Keyword",instanceof:"Keyword",interface:"Keyword",is:"Instruction",keyof:"Keyword",let:"Keyword",new:"Keyword",package:"Instruction",this:"Keyword",throw:"Instruction",true:"BooleanLiteral",try:"Instruction",type:"Keyword",typeof:"Keyword",var:"Keyword",void:"Keyword",while:"Instruction",with:"Instruction",yield:"Instruction"},r=/^[a-zA-Zа-яА-Я_$]$/,i=/^[a-zA-Zа-яА-Я_$0-9]$/,s=/^[0-9]$/,a='<span class="',l="</span>",c=(c,u)=>{const y=(a=>{const l=[],c=a.length;let u=0;e:for(;u<c;){if(" "===a[u]||"\t"===a[u]){const e=u;for(u++;u<c&&(" "===a[u]||"\t"===a[u]);)u++;l[l.length]={type:"WhiteSpace",value:a.slice(e,u),start:e,end:u};continue e}if("\n"===a[u]||"\r"===a[u]){const e=u;"\r"===a[u]&&u++,u++,l[l.length]={type:"LineDivision",value:"\n",start:e,end:u};continue e}if(r.test(a[u])){const e=u;for(u++;u<c&&i.test(a[u]);)u++;const t=a.slice(e,u);l[l.length]={type:o[t]??"Identifier",value:t,start:e,end:u};continue e}if("'"===a[u]||'"'===a[u]||"`"===a[u]){const e=u,t=a[u];for(u++;u<c&&a[u]!==t;)u++;u++,l[l.length]={type:"StringLiteral",value:a.slice(e,u),start:e,end:u};continue e}if(s.test(a[u])){const e=u;for(;u<c&&s.test(a[u]);)u++;l[l.length]={type:"NumberLiteral",value:a.slice(e,u),start:e,end:u};continue e}if("/"===a[u]){const e=u;if(u++,"/"===a[u]){for(u++;u<c&&"\r"!==a[u]&&"\n"!==a[u];)u++;"\r"===a[u]&&(u+=2),l[l.length]={type:"Comment",value:a.slice(e,u),start:e,end:u},l[l.length]={type:"LineDivision",value:"\n",start:e,end:u};continue e}if("*"===a[u]){u++;let t=e;for(;u<c&&("*"!==a[u]||"/"!==a[u+1]);)"\n"!==a[u]&&"\r"!==a[u]||(l[l.length]={type:"Comment",value:a.slice(t,u),start:e,end:u},l[l.length]={type:"LineDivision",value:"\n",start:u,end:u+1},"\r"===a[u]&&u++,u++,t=u),u++;u+=2,l[l.length]={type:"Comment",value:a.slice(t,u),start:e,end:u};continue e}}if(a[u]+a[u+1]+a[u+2]+a[u+3]===">>>="){const e=u;u+=4,l[l.length]={type:"Operator",value:a.slice(e,u),start:e,end:u};continue e}if(n.has(a[u]+a[u+1]+a[u+2])){const e=u;u+=3,l[l.length]={type:"Operator",value:a.slice(e,u),start:e,end:u};continue e}if(t.has(a[u]+a[u+1])){const e=u;u+=2,l[l.length]={type:"Operator",value:a.slice(e,u),start:e,end:u};continue e}if(e.has(a[u])){const e=u;u++,l[l.length]={type:"Operator",value:a.slice(e,u),start:e,end:u};continue e}u++}return l})(c),d=((e,t)=>{const n=e.length;let o='<pre class="'+t.pre+'"><code class="'+t.code+'"><div class="'+t.line+'">',r=!0,i=0;for(;i<n;){const s=e[i];if("WhiteSpace"===s.type){o+=a+t.token+" "+t.whitespace+'">';let e=0;for(;e<s.value.length;)o+=" ",e++;o+=l,i++;continue}"LineDivision"!==s.type?"Operator"!==s.type?"Identifier"!==s.type?"Keyword"!==s.type?"Instruction"!==s.type?"StringLiteral"!==s.type?"NumberLiteral"!==s.type?"BooleanLiteral"!==s.type?"NaNLiteral"!==s.type?("Comment"!==s.type||(o+=a+t.token+" "+t.comment+'">'+s.value+l),i++):(o+=a+t.token+" "+t.NaNLiteral+'">'+s.value+l,i++):(o+=a+t.token+" "+t.booleanLiteral+'">'+s.value+l,i++):(o+=a+t.token+" "+t.numberLiteral+'">'+s.value+l,i++):(o+=a+t.token+" "+t.stringLiteral+'">'+s.value+l,i++):(o+=a+t.token+" "+t.instruction+">"+s.value+l,i++):(o+=a+t.token+" "+t.keyword+'">'+s.value+l,i++):(o+=a+t.token+" "+t.mutableIdentifier+'">'+s.value+l,i++):(o+=a+t.token+" "+t.operator+'">'+s.value+l,i++):(o+="</div>",i!==n-1&&(o+='<div class="'+t.line+'">',r=!1),i++)}return r&&(o+="</div>"),o+="</code></pre>",o})(y,u);return d};export{c as highlight};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "ts-highlight",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.3.0",
|
|
4
4
|
"module": "./dist/index.js",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"private": false,
|
|
@@ -26,6 +26,9 @@
|
|
|
26
26
|
"higlight",
|
|
27
27
|
"syntax-higlight"
|
|
28
28
|
],
|
|
29
|
+
"files": [
|
|
30
|
+
"dist"
|
|
31
|
+
],
|
|
29
32
|
"devDependencies": {
|
|
30
33
|
"@biomejs/biome": "^2.3.10",
|
|
31
34
|
"@rollup/plugin-terser": "^0.4.4",
|
|
@@ -1,29 +0,0 @@
|
|
|
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/assets/marigolds.webp
DELETED
|
Binary file
|
package/biome.json
DELETED
|
@@ -1,30 +0,0 @@
|
|
|
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/rollup.config.js
DELETED
|
@@ -1,34 +0,0 @@
|
|
|
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
|
|
@@ -1,59 +0,0 @@
|
|
|
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
|
-
});
|
|
@@ -1,25 +0,0 @@
|
|
|
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>';
|
|
@@ -1,214 +0,0 @@
|
|
|
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
|
-
if (currentToken.type === 'Instruction') {
|
|
112
|
-
generated +=
|
|
113
|
-
OPENED_SPAN_WITH_CLASS +
|
|
114
|
-
cssClasses.token +
|
|
115
|
-
' ' +
|
|
116
|
-
cssClasses.instruction +
|
|
117
|
-
'>' +
|
|
118
|
-
currentToken.value +
|
|
119
|
-
CLOSED_SPAN;
|
|
120
|
-
|
|
121
|
-
tokenPos++;
|
|
122
|
-
|
|
123
|
-
continue;
|
|
124
|
-
}
|
|
125
|
-
|
|
126
|
-
// literals
|
|
127
|
-
if (currentToken.type === 'StringLiteral') {
|
|
128
|
-
generated +=
|
|
129
|
-
OPENED_SPAN_WITH_CLASS +
|
|
130
|
-
cssClasses.token +
|
|
131
|
-
' ' +
|
|
132
|
-
cssClasses.stringLiteral +
|
|
133
|
-
'">' +
|
|
134
|
-
currentToken.value +
|
|
135
|
-
CLOSED_SPAN;
|
|
136
|
-
|
|
137
|
-
tokenPos++;
|
|
138
|
-
|
|
139
|
-
continue;
|
|
140
|
-
}
|
|
141
|
-
|
|
142
|
-
if (currentToken.type === 'NumberLiteral') {
|
|
143
|
-
generated +=
|
|
144
|
-
OPENED_SPAN_WITH_CLASS +
|
|
145
|
-
cssClasses.token +
|
|
146
|
-
' ' +
|
|
147
|
-
cssClasses.numberLiteral +
|
|
148
|
-
'">' +
|
|
149
|
-
currentToken.value +
|
|
150
|
-
CLOSED_SPAN;
|
|
151
|
-
|
|
152
|
-
tokenPos++;
|
|
153
|
-
|
|
154
|
-
continue;
|
|
155
|
-
}
|
|
156
|
-
|
|
157
|
-
if (currentToken.type === 'BooleanLiteral') {
|
|
158
|
-
generated +=
|
|
159
|
-
OPENED_SPAN_WITH_CLASS +
|
|
160
|
-
cssClasses.token +
|
|
161
|
-
' ' +
|
|
162
|
-
cssClasses.booleanLiteral +
|
|
163
|
-
'">' +
|
|
164
|
-
currentToken.value +
|
|
165
|
-
CLOSED_SPAN;
|
|
166
|
-
|
|
167
|
-
tokenPos++;
|
|
168
|
-
|
|
169
|
-
continue;
|
|
170
|
-
}
|
|
171
|
-
|
|
172
|
-
if (currentToken.type === 'NaNLiteral') {
|
|
173
|
-
generated +=
|
|
174
|
-
OPENED_SPAN_WITH_CLASS +
|
|
175
|
-
cssClasses.token +
|
|
176
|
-
' ' +
|
|
177
|
-
cssClasses.NaNLiteral +
|
|
178
|
-
'">' +
|
|
179
|
-
currentToken.value +
|
|
180
|
-
CLOSED_SPAN;
|
|
181
|
-
|
|
182
|
-
tokenPos++;
|
|
183
|
-
|
|
184
|
-
continue;
|
|
185
|
-
}
|
|
186
|
-
|
|
187
|
-
// comments
|
|
188
|
-
if (currentToken.type === 'Comment') {
|
|
189
|
-
generated +=
|
|
190
|
-
OPENED_SPAN_WITH_CLASS +
|
|
191
|
-
cssClasses.token +
|
|
192
|
-
' ' +
|
|
193
|
-
cssClasses.comment +
|
|
194
|
-
'">' +
|
|
195
|
-
currentToken.value +
|
|
196
|
-
CLOSED_SPAN;
|
|
197
|
-
|
|
198
|
-
tokenPos++;
|
|
199
|
-
|
|
200
|
-
continue;
|
|
201
|
-
}
|
|
202
|
-
|
|
203
|
-
// fallback
|
|
204
|
-
tokenPos++;
|
|
205
|
-
}
|
|
206
|
-
|
|
207
|
-
if (isLineOpened) {
|
|
208
|
-
generated += '</div>';
|
|
209
|
-
}
|
|
210
|
-
|
|
211
|
-
generated += '</code></pre>';
|
|
212
|
-
|
|
213
|
-
return generated;
|
|
214
|
-
};
|
package/src/generator/index.ts
DELETED
package/src/generator/types.ts
DELETED
|
@@ -1,75 +0,0 @@
|
|
|
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
|
-
// identifier like token types ('Keyword', 'Instruction')
|
|
60
|
-
keyword: string;
|
|
61
|
-
instruction: string;
|
|
62
|
-
|
|
63
|
-
operator: string;
|
|
64
|
-
|
|
65
|
-
// identifiers
|
|
66
|
-
constantIdentifier: string;
|
|
67
|
-
mutableIdentifier: string;
|
|
68
|
-
|
|
69
|
-
// literals
|
|
70
|
-
numberLiteral: string;
|
|
71
|
-
stringLiteral: string;
|
|
72
|
-
booleanLiteral: string;
|
|
73
|
-
NaNLiteral: string;
|
|
74
|
-
bigintChar: string;
|
|
75
|
-
}>;
|
|
@@ -1,54 +0,0 @@
|
|
|
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
|
-
};
|
package/src/highlight/index.ts
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export { highlight } from './highlight';
|
package/src/index.ts
DELETED