sugar-high 0.0.1
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/docs/index.html +72 -0
- package/docs/main.js +50 -0
- package/lib/index.js +203 -0
- package/package.json +14 -0
package/docs/index.html
ADDED
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
<!DOCTYPE html>
|
|
2
|
+
<html lang="en">
|
|
3
|
+
<head>
|
|
4
|
+
<meta charset="UTF-8">
|
|
5
|
+
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
|
6
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
7
|
+
<title>sugar high</title>
|
|
8
|
+
<style>
|
|
9
|
+
html {
|
|
10
|
+
font-family: "Inter",-apple-system,BlinkMacSystemFont,"Segoe UI","Roboto","Oxygen","Ubuntu","Cantarell","Fira Sans","Droid Sans","Helvetica Neue",sans-serif
|
|
11
|
+
}
|
|
12
|
+
body {
|
|
13
|
+
max-width: 1120px;
|
|
14
|
+
margin: auto;
|
|
15
|
+
padding: 0 10px;
|
|
16
|
+
}
|
|
17
|
+
#code {
|
|
18
|
+
font-family: Consolas, Monaco, monospace;
|
|
19
|
+
font-size: 16px;
|
|
20
|
+
display: block;
|
|
21
|
+
width: 100%;
|
|
22
|
+
min-height: 100px;
|
|
23
|
+
background-color: #fff;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
#output {
|
|
27
|
+
font-family: Consolas, Monaco, monospace;
|
|
28
|
+
font-size: 16px;
|
|
29
|
+
display: block;
|
|
30
|
+
min-height: 100px;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
.identifier {
|
|
34
|
+
color: #2d333b;
|
|
35
|
+
}
|
|
36
|
+
.sign {
|
|
37
|
+
color: #818c9b;
|
|
38
|
+
font-weight: bold;
|
|
39
|
+
}
|
|
40
|
+
.string {
|
|
41
|
+
color: #00a99a;
|
|
42
|
+
}
|
|
43
|
+
.keyword {
|
|
44
|
+
color: #f47067;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
.flex {
|
|
48
|
+
display: flex;
|
|
49
|
+
}
|
|
50
|
+
.flex-1 {
|
|
51
|
+
flex: 1 0;
|
|
52
|
+
margin: 8px;
|
|
53
|
+
padding: 12px 10px 12px 10px;
|
|
54
|
+
background-color: #f9f9f9;
|
|
55
|
+
border: 1px solid #d0d0d0;
|
|
56
|
+
border-radius: 4px;
|
|
57
|
+
}
|
|
58
|
+
.comment {
|
|
59
|
+
color: #818c9b;
|
|
60
|
+
}
|
|
61
|
+
</style>
|
|
62
|
+
</head>
|
|
63
|
+
<body>
|
|
64
|
+
<h1>Sugar High</h1>
|
|
65
|
+
<div class="flex">
|
|
66
|
+
<textarea class="flex-1" id="code"></textarea>
|
|
67
|
+
|
|
68
|
+
<pre class="flex-1"><code id="output"></code></pre>
|
|
69
|
+
</div>
|
|
70
|
+
<script type="module" src="./main.js"></script>
|
|
71
|
+
</body>
|
|
72
|
+
</html>
|
package/docs/main.js
ADDED
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import { tokenize, generate } from '../lib'
|
|
2
|
+
|
|
3
|
+
// console interactive API
|
|
4
|
+
window.tokenize = tokenize
|
|
5
|
+
window.generate = generate
|
|
6
|
+
|
|
7
|
+
const codeInput = document.getElementById('code')
|
|
8
|
+
const codeOutput = document.getElementById('output')
|
|
9
|
+
|
|
10
|
+
codeInput.addEventListener('input', () => {
|
|
11
|
+
update()
|
|
12
|
+
})
|
|
13
|
+
|
|
14
|
+
codeInput.value = `
|
|
15
|
+
// hello-world.js
|
|
16
|
+
import { planet } from '../space'
|
|
17
|
+
|
|
18
|
+
class SuperArray extends Array {
|
|
19
|
+
static core = planet
|
|
20
|
+
|
|
21
|
+
constructor(...args) {
|
|
22
|
+
super(...args)
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
bump() {
|
|
26
|
+
return this.map(x => x + 1)
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* @param {string} name
|
|
32
|
+
* @return {void}
|
|
33
|
+
*/
|
|
34
|
+
function hello(name) {
|
|
35
|
+
console.log('hello', name)
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
`.trim()
|
|
39
|
+
|
|
40
|
+
function update() {
|
|
41
|
+
const code = codeInput.value?.trim() || ''
|
|
42
|
+
const tokens = tokenize(code)
|
|
43
|
+
const output = generate(tokens).join('')
|
|
44
|
+
|
|
45
|
+
console.log(tokens)
|
|
46
|
+
|
|
47
|
+
codeOutput.innerHTML = output
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
update()
|
package/lib/index.js
ADDED
|
@@ -0,0 +1,203 @@
|
|
|
1
|
+
// @ts-check
|
|
2
|
+
|
|
3
|
+
const keywords = new Set([
|
|
4
|
+
'for',
|
|
5
|
+
'while',
|
|
6
|
+
'if',
|
|
7
|
+
'else',
|
|
8
|
+
'return',
|
|
9
|
+
'function',
|
|
10
|
+
'var',
|
|
11
|
+
'let',
|
|
12
|
+
'const',
|
|
13
|
+
'true',
|
|
14
|
+
'false',
|
|
15
|
+
'null',
|
|
16
|
+
'undefined',
|
|
17
|
+
'NaN',
|
|
18
|
+
'Infinity',
|
|
19
|
+
'this',
|
|
20
|
+
'new',
|
|
21
|
+
'delete',
|
|
22
|
+
'typeof',
|
|
23
|
+
'in',
|
|
24
|
+
'instanceof',
|
|
25
|
+
'void',
|
|
26
|
+
'break',
|
|
27
|
+
'continue',
|
|
28
|
+
'switch',
|
|
29
|
+
'case',
|
|
30
|
+
'default',
|
|
31
|
+
'throw',
|
|
32
|
+
'try',
|
|
33
|
+
'catch',
|
|
34
|
+
'finally',
|
|
35
|
+
'debugger',
|
|
36
|
+
'with',
|
|
37
|
+
'yield',
|
|
38
|
+
'async',
|
|
39
|
+
'await',
|
|
40
|
+
'class',
|
|
41
|
+
'extends',
|
|
42
|
+
'super',
|
|
43
|
+
'import',
|
|
44
|
+
'export',
|
|
45
|
+
'from',
|
|
46
|
+
'static',
|
|
47
|
+
])
|
|
48
|
+
|
|
49
|
+
const signs = new Set([
|
|
50
|
+
'+',
|
|
51
|
+
'-',
|
|
52
|
+
'*',
|
|
53
|
+
'/',
|
|
54
|
+
'%',
|
|
55
|
+
'=',
|
|
56
|
+
'!',
|
|
57
|
+
'>',
|
|
58
|
+
'<',
|
|
59
|
+
'&',
|
|
60
|
+
'|',
|
|
61
|
+
'^',
|
|
62
|
+
'~',
|
|
63
|
+
'!',
|
|
64
|
+
'?',
|
|
65
|
+
':',
|
|
66
|
+
'.',
|
|
67
|
+
',',
|
|
68
|
+
`'`,
|
|
69
|
+
'"',
|
|
70
|
+
'.',
|
|
71
|
+
'{',
|
|
72
|
+
'}',
|
|
73
|
+
'(',
|
|
74
|
+
')',
|
|
75
|
+
'[',
|
|
76
|
+
']',
|
|
77
|
+
'#',
|
|
78
|
+
'\\',
|
|
79
|
+
])
|
|
80
|
+
|
|
81
|
+
function encode(str) {
|
|
82
|
+
return str
|
|
83
|
+
.replace(/&/g, "&")
|
|
84
|
+
.replace(/</g, "<")
|
|
85
|
+
.replace(/>/g, ">")
|
|
86
|
+
.replace(/"/g, """)
|
|
87
|
+
.replace(/'/g, "'")
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
function isIdentifierChar(chr) {
|
|
91
|
+
return /[a-zA-Z0-9_$]/.test(chr)
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
function isQuotationMark(chr) {
|
|
95
|
+
return chr === '"' || chr === "'" || chr === '`'
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
/**
|
|
99
|
+
* @param {string} code
|
|
100
|
+
* @return {Array<string>}
|
|
101
|
+
*/
|
|
102
|
+
export function tokenize(code) {
|
|
103
|
+
let current = ''
|
|
104
|
+
const tokens = []
|
|
105
|
+
const string = { entered: false }
|
|
106
|
+
const comment = { entered: false, type: 0 }
|
|
107
|
+
for (let i = 0; i < code.length; i++) {
|
|
108
|
+
if (!string.entered && isQuotationMark(code[i])) {
|
|
109
|
+
string.entered = true
|
|
110
|
+
if (current) tokens.push(current)
|
|
111
|
+
current = code[i]
|
|
112
|
+
} else if (string.entered) {
|
|
113
|
+
current += code[i]
|
|
114
|
+
if (isQuotationMark(code[i])) {
|
|
115
|
+
string.entered = false
|
|
116
|
+
tokens.push(current)
|
|
117
|
+
current = ''
|
|
118
|
+
}
|
|
119
|
+
} else if (
|
|
120
|
+
!comment.entered &&
|
|
121
|
+
code[i] === '/' &&
|
|
122
|
+
(code[i + 1] === '/' || code[i + 1] === '*')
|
|
123
|
+
) {
|
|
124
|
+
comment.type = code[i + 1] === '/' ? 0 : 1
|
|
125
|
+
comment.entered = true
|
|
126
|
+
if (current) tokens.push(current)
|
|
127
|
+
current = code[i]
|
|
128
|
+
} else if (comment.entered) {
|
|
129
|
+
current += code[i]
|
|
130
|
+
if (comment.type === 0 && code[i + 1] === '\n') {
|
|
131
|
+
comment.entered = false
|
|
132
|
+
tokens.push(current)
|
|
133
|
+
current = ''
|
|
134
|
+
} else if (comment.type === 1 && code[i] === '*' && code[i + 1] === '/') {
|
|
135
|
+
comment.entered = false
|
|
136
|
+
tokens.push(current)
|
|
137
|
+
current = ''
|
|
138
|
+
tokens.push(code[i])
|
|
139
|
+
tokens.push(code[i + 1])
|
|
140
|
+
i++
|
|
141
|
+
}
|
|
142
|
+
} else if (code[i] === ' ' || code[i] === '\n') {
|
|
143
|
+
if (current.length > 0) {
|
|
144
|
+
tokens.push(current)
|
|
145
|
+
current = ''
|
|
146
|
+
}
|
|
147
|
+
tokens.push(code[i])
|
|
148
|
+
} else {
|
|
149
|
+
if (isIdentifierChar(code[i]) !== isIdentifierChar(current[current.length - 1])) {
|
|
150
|
+
if (current) tokens.push(current)
|
|
151
|
+
current = code[i]
|
|
152
|
+
} else {
|
|
153
|
+
current += code[i]
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
if (current) {
|
|
158
|
+
tokens.push(current)
|
|
159
|
+
current = ''
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
return tokens
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
|
|
166
|
+
function classify(token) {
|
|
167
|
+
if (token[0] === '/' && (token[1] === '*' || token[1] === '/')) {
|
|
168
|
+
return 'comment'
|
|
169
|
+
} else if (keywords.has(token)) {
|
|
170
|
+
return 'keyword'
|
|
171
|
+
} else if (token === '\n') {
|
|
172
|
+
return 'linebreak'
|
|
173
|
+
} else if (isQuotationMark(token[0]) && !isQuotationMark(token[1])) {
|
|
174
|
+
return 'string'
|
|
175
|
+
} else if (token === ' ') {
|
|
176
|
+
return 'space'
|
|
177
|
+
} else if (signs.has(token[0])) {
|
|
178
|
+
return 'sign'
|
|
179
|
+
} else {
|
|
180
|
+
return 'identifier'
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
/**
|
|
185
|
+
* @param {Array<string>} tokens
|
|
186
|
+
* @return {Array<string>}
|
|
187
|
+
*/
|
|
188
|
+
export function generate(tokens) {
|
|
189
|
+
let output = []
|
|
190
|
+
for (let i = 0; i < tokens.length; i++) {
|
|
191
|
+
const token = tokens[i]
|
|
192
|
+
const type = classify(token)
|
|
193
|
+
if (type === 'linebreak') {
|
|
194
|
+
output.push('<br>')
|
|
195
|
+
} else if (type === 'space') {
|
|
196
|
+
output.push(`<span class="space"> </span>`)
|
|
197
|
+
} else {
|
|
198
|
+
output.push(`<span class="${type}">${encode(token)}</span>`)
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
return output
|
|
202
|
+
}
|
|
203
|
+
|
package/package.json
ADDED
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "sugar-high",
|
|
3
|
+
"version": "0.0.1",
|
|
4
|
+
"exports": "./lib/index.js",
|
|
5
|
+
"description": "Super lightweight JavaScript syntax highlighter",
|
|
6
|
+
"license": "MIT",
|
|
7
|
+
"scripts": {
|
|
8
|
+
"dev": "vite docs",
|
|
9
|
+
"build": "vite build docs"
|
|
10
|
+
},
|
|
11
|
+
"devDependencies": {
|
|
12
|
+
"vite": "2.7.13"
|
|
13
|
+
}
|
|
14
|
+
}
|