sugar-high 0.3.1 → 0.4.2
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/README.md +14 -21
- package/lib/index.mjs +119 -48
- package/package.json +4 -2
package/README.md
CHANGED
|
@@ -35,26 +35,14 @@ Then make your own theme with customized colors by token type and put in global
|
|
|
35
35
|
* comment
|
|
36
36
|
* jsxliterals
|
|
37
37
|
*/
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
}
|
|
47
|
-
.sh__string {
|
|
48
|
-
color: #00a99a;
|
|
49
|
-
}
|
|
50
|
-
.sh__keyword {
|
|
51
|
-
color: #f47067;
|
|
52
|
-
}
|
|
53
|
-
.sh__comment {
|
|
54
|
-
color: #a19595;
|
|
55
|
-
}
|
|
56
|
-
.sh__jsxliterals {
|
|
57
|
-
color: #6266d1;
|
|
38
|
+
:root {
|
|
39
|
+
--sh-class: #2d5e9d;
|
|
40
|
+
--sh-identifier: #354150;
|
|
41
|
+
--sh-sign: #8996a3;
|
|
42
|
+
--sh-string: #00a99a;
|
|
43
|
+
--sh-keyword: #f47067;
|
|
44
|
+
--sh-comment: #a19595;
|
|
45
|
+
--sh-jsxliterals: #6266d1;
|
|
58
46
|
}
|
|
59
47
|
```
|
|
60
48
|
|
|
@@ -65,8 +53,13 @@ Then make your own theme with customized colors by token type and put in global
|
|
|
65
53
|
Sugar high provide `.sh_line` class name for each line. To display line number, define the `.sh_line::before` element with CSS will enable line numbers automatically.
|
|
66
54
|
|
|
67
55
|
```css
|
|
56
|
+
pre code {
|
|
57
|
+
counter-reset: sh-line-number;
|
|
58
|
+
}
|
|
59
|
+
|
|
68
60
|
.sh__line::before {
|
|
69
|
-
|
|
61
|
+
counter-increment: sh-line-number 1;
|
|
62
|
+
content: counter(sh-line-number);
|
|
70
63
|
margin-right: 24px;
|
|
71
64
|
text-align: right;
|
|
72
65
|
color: #a4a4a4;
|
package/lib/index.mjs
CHANGED
|
@@ -168,15 +168,21 @@ export function tokenize(code) {
|
|
|
168
168
|
/** @type {Array<[number, string]>} */
|
|
169
169
|
const tokens = []
|
|
170
170
|
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
171
|
+
let __jsxEnter = false
|
|
172
|
+
/**
|
|
173
|
+
* @type {0 | 1 | 2}
|
|
174
|
+
* 0 for not in jsx
|
|
175
|
+
* 1 for open jsx tag
|
|
176
|
+
* 2 for closing jsx tag
|
|
177
|
+
**/
|
|
178
|
+
let __jsxTag = 0
|
|
179
|
+
let __jsxExpr = false
|
|
177
180
|
|
|
178
|
-
|
|
179
|
-
|
|
181
|
+
// only match paired (open + close) tags, not self-closing tags
|
|
182
|
+
let __jsxStack = 0
|
|
183
|
+
const __jsxChild = () => __jsxEnter && !__jsxExpr && !__jsxTag
|
|
184
|
+
const inJsxTag = () => __jsxTag && !__jsxChild()
|
|
185
|
+
const inJsxLiterals = () => !__jsxTag && __jsxChild() && !__jsxExpr && __jsxStack > 0
|
|
180
186
|
|
|
181
187
|
/**
|
|
182
188
|
*
|
|
@@ -184,8 +190,11 @@ export function tokenize(code) {
|
|
|
184
190
|
* @returns {number}
|
|
185
191
|
*/
|
|
186
192
|
function classify(token) {
|
|
187
|
-
|
|
188
|
-
|
|
193
|
+
const isJsxLiterals = inJsxLiterals()
|
|
194
|
+
if (isJsxLiterals) {
|
|
195
|
+
return T_JSX_LITERALS
|
|
196
|
+
} else if (keywords.has(token)) {
|
|
197
|
+
return last[1] === '.' ? T_IDENTIFIER : T_KEYWORD
|
|
189
198
|
} else if (token === '\n') {
|
|
190
199
|
return T_BREAK
|
|
191
200
|
} else if (isSpaces(token)) {
|
|
@@ -193,11 +202,9 @@ export function tokenize(code) {
|
|
|
193
202
|
} else if (token.split('').every(ch => signs.has(ch))) {
|
|
194
203
|
return T_SIGN
|
|
195
204
|
} else if (isCls(token)) {
|
|
196
|
-
return
|
|
205
|
+
return inJsxTag() ? T_IDENTIFIER : T_CLS_NUMBER
|
|
197
206
|
} else {
|
|
198
|
-
return
|
|
199
|
-
? T_JSX_LITERALS
|
|
200
|
-
: isIdentifier(token) ? T_IDENTIFIER : T_STRING
|
|
207
|
+
return isIdentifier(token) ? T_IDENTIFIER : T_STRING
|
|
201
208
|
}
|
|
202
209
|
}
|
|
203
210
|
|
|
@@ -219,37 +226,96 @@ export function tokenize(code) {
|
|
|
219
226
|
const next = code[i + 1]
|
|
220
227
|
const p_c = prev + curr // previous and current
|
|
221
228
|
const c_n = curr + next // current and next
|
|
222
|
-
const isJsxLiterals = inJsxLiterals()
|
|
223
229
|
|
|
224
|
-
|
|
225
|
-
|
|
230
|
+
|
|
231
|
+
if (__jsxChild()) {
|
|
232
|
+
if (curr === '{') {
|
|
233
|
+
append()
|
|
234
|
+
current = curr
|
|
235
|
+
append(T_SIGN)
|
|
236
|
+
__jsxExpr = true
|
|
237
|
+
continue
|
|
238
|
+
}
|
|
226
239
|
}
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
240
|
+
|
|
241
|
+
if (__jsxEnter) {
|
|
242
|
+
// <: open tag sign
|
|
243
|
+
if (!__jsxTag && curr === '<') {
|
|
244
|
+
append()
|
|
245
|
+
__jsxTag = 1
|
|
246
|
+
current = curr
|
|
247
|
+
if (next === '/') {
|
|
248
|
+
__jsxTag = 2
|
|
249
|
+
current = c_n
|
|
250
|
+
i++
|
|
251
|
+
}
|
|
252
|
+
append(T_SIGN)
|
|
253
|
+
continue
|
|
254
|
+
}
|
|
255
|
+
if (__jsxTag) {
|
|
256
|
+
// >: open tag close sign or closing tag closing sign
|
|
257
|
+
if (curr === '>' && prev !== '/') {
|
|
258
|
+
append()
|
|
259
|
+
if (__jsxTag === 1) {
|
|
260
|
+
__jsxTag = 0
|
|
261
|
+
__jsxStack++
|
|
262
|
+
} else {
|
|
263
|
+
__jsxTag = 0
|
|
264
|
+
__jsxEnter = false
|
|
265
|
+
}
|
|
266
|
+
current = curr
|
|
267
|
+
append(T_SIGN)
|
|
268
|
+
continue
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
// >: tag self close sign or close tag sign
|
|
272
|
+
if (c_n === '/>' || c_n === '</') {
|
|
273
|
+
append()
|
|
274
|
+
if (c_n === '/>') {
|
|
275
|
+
__jsxTag = 0
|
|
276
|
+
}
|
|
277
|
+
if (c_n === '</') {
|
|
278
|
+
__jsxStack--
|
|
279
|
+
}
|
|
280
|
+
current = c_n
|
|
281
|
+
i++
|
|
282
|
+
append(T_SIGN)
|
|
283
|
+
continue
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
// <: open tag sign
|
|
287
|
+
if (curr === '<') {
|
|
288
|
+
append()
|
|
289
|
+
current = curr
|
|
290
|
+
append(T_SIGN)
|
|
291
|
+
continue
|
|
292
|
+
}
|
|
293
|
+
}
|
|
233
294
|
}
|
|
295
|
+
|
|
234
296
|
// if it's not in a jsx tag declaration or a string, close child if next is jsx close tag
|
|
235
|
-
if (!
|
|
236
|
-
|
|
237
|
-
|
|
297
|
+
if (!__jsxTag && (curr === '<' && isIdentifierChar(next) || c_n === '</')) {
|
|
298
|
+
__jsxTag = next === '/' ? 2 : 1
|
|
299
|
+
|
|
300
|
+
if (curr === '<' && next !== '/') {
|
|
301
|
+
__jsxEnter = true
|
|
302
|
+
}
|
|
238
303
|
}
|
|
239
304
|
|
|
240
305
|
const isQuotationChar = isStringQuotation(curr)
|
|
241
|
-
const isRegexChar = !
|
|
306
|
+
const isRegexChar = !__jsxEnter && isRegexStart(c_n)
|
|
307
|
+
const isJsxLiterals = inJsxLiterals()
|
|
242
308
|
if (isQuotationChar || isRegexChar) {
|
|
243
309
|
append()
|
|
244
310
|
const [lastType, lastToken] = last
|
|
245
311
|
// Special cases that are not considered as regex:
|
|
246
|
-
// * (expr1) / expr2: `
|
|
312
|
+
// * (expr1) / expr2: `)` before `/` operator is still in expression
|
|
247
313
|
// * <non comment start>/ expr: non comment start before `/` is not regex
|
|
248
314
|
if (
|
|
249
315
|
isRegexChar &&
|
|
250
316
|
lastType &&
|
|
251
317
|
!(
|
|
252
|
-
(lastType === T_SIGN &&
|
|
318
|
+
(lastType === T_SIGN && ')' !== lastToken) ||
|
|
253
319
|
lastType === T_COMMENT
|
|
254
320
|
)
|
|
255
321
|
) {
|
|
@@ -317,25 +383,38 @@ export function tokenize(code) {
|
|
|
317
383
|
)
|
|
318
384
|
) {
|
|
319
385
|
current += curr
|
|
386
|
+
if (next === '<') {
|
|
387
|
+
append()
|
|
388
|
+
}
|
|
320
389
|
} else {
|
|
321
390
|
append()
|
|
322
391
|
current = curr
|
|
323
392
|
append()
|
|
324
393
|
}
|
|
325
394
|
} else {
|
|
326
|
-
if (
|
|
395
|
+
if (__jsxExpr && curr === '}') {
|
|
396
|
+
append()
|
|
397
|
+
current = curr
|
|
398
|
+
append()
|
|
399
|
+
__jsxExpr = false
|
|
400
|
+
} else if (
|
|
401
|
+
// it's jsx literals and is not a jsx bracket
|
|
327
402
|
(isJsxLiterals && !jsxBrackets.has(curr)) ||
|
|
328
|
-
|
|
329
|
-
!signs.has(curr)
|
|
403
|
+
// same type char as previous one in current token
|
|
404
|
+
((isWord(curr) === isWord(current[current.length - 1]) || __jsxChild()) && !signs.has(curr))
|
|
330
405
|
) {
|
|
331
406
|
current += curr
|
|
332
|
-
if (next === '<') {
|
|
333
|
-
append();
|
|
334
|
-
}
|
|
335
407
|
} else {
|
|
408
|
+
if (p_c === '</') {
|
|
409
|
+
current = p_c
|
|
410
|
+
}
|
|
336
411
|
append()
|
|
337
|
-
|
|
338
|
-
if (
|
|
412
|
+
|
|
413
|
+
if (p_c !== '</') {
|
|
414
|
+
current = curr
|
|
415
|
+
|
|
416
|
+
}
|
|
417
|
+
if ((c_n === '</' || c_n === '/>')) {
|
|
339
418
|
current = c_n
|
|
340
419
|
append()
|
|
341
420
|
i++
|
|
@@ -343,13 +422,6 @@ export function tokenize(code) {
|
|
|
343
422
|
else if (jsxBrackets.has(curr)) append()
|
|
344
423
|
}
|
|
345
424
|
}
|
|
346
|
-
|
|
347
|
-
if (jsx.child && curr === '{') {
|
|
348
|
-
jsx.expr = true
|
|
349
|
-
}
|
|
350
|
-
if (jsx.child && jsx.expr && curr === '}') {
|
|
351
|
-
jsx.expr = false
|
|
352
|
-
}
|
|
353
425
|
}
|
|
354
426
|
|
|
355
427
|
append()
|
|
@@ -362,14 +434,13 @@ export function tokenize(code) {
|
|
|
362
434
|
* @return {Array<string>}
|
|
363
435
|
*/
|
|
364
436
|
function generate(tokens) {
|
|
365
|
-
let lineNo = 1
|
|
366
437
|
const linesHtml = []
|
|
367
|
-
const createLine = (content) => `<span
|
|
438
|
+
const createLine = (content) => `<span class="sh__line">${content}</span>`
|
|
368
439
|
|
|
369
440
|
function flushLine(tokens) {
|
|
370
441
|
linesHtml.push(createLine(
|
|
371
|
-
tokens.map(
|
|
372
|
-
`<span
|
|
442
|
+
tokens.map(([type, value]) => (
|
|
443
|
+
`<span style="color: var(--sh-${types[type]})">${encode(value)}</span>`
|
|
373
444
|
))
|
|
374
445
|
.join('')
|
|
375
446
|
))
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "sugar-high",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.4.2",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"exports": "./lib/index.mjs",
|
|
6
6
|
"description": "Super lightweight JSX syntax highlighter",
|
|
@@ -16,9 +16,11 @@
|
|
|
16
16
|
"build": "next build docs"
|
|
17
17
|
},
|
|
18
18
|
"devDependencies": {
|
|
19
|
-
"
|
|
19
|
+
"codice": "^0.0.9",
|
|
20
|
+
"next": "12.1.6-canary.6",
|
|
20
21
|
"react": "18.0.0",
|
|
21
22
|
"react-dom": "18.0.0",
|
|
23
|
+
"sugar-high": "link:./",
|
|
22
24
|
"vitest": "~0.6.0"
|
|
23
25
|
}
|
|
24
26
|
}
|