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.
@@ -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, "&amp;")
84
+ .replace(/</g, "&lt;")
85
+ .replace(/>/g, "&gt;")
86
+ .replace(/"/g, "&quot;")
87
+ .replace(/'/g, "&#039;")
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">&nbsp;</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
+ }