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.
Files changed (3) hide show
  1. package/README.md +14 -21
  2. package/lib/index.mjs +119 -48
  3. 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
- .sh__class {
39
- color: #2d5e9d;
40
- }
41
- .sh__identifier {
42
- color: #2d333b;
43
- }
44
- .sh__sign {
45
- color: #8996a3;
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
- content: attr(data-line-number);
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
- // jsx.tag for entering open or closed tags
172
- // jsx.close for is the closing sign of open or close tags
173
- // jsx.child for entering children
174
- // jsx.expr for entering {expression}
175
- /** @type {{ tag: boolean; close: boolean; child: boolean; expr: boolean }} */
176
- const jsx = { tag: false, close: false, child: false, expr: false }
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
- const inJsxTag = () => jsx.tag || jsx.close
179
- const inJsxLiterals = () => !inJsxTag() && jsx.child && !jsx.expr
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
- if (keywords.has(token)) {
188
- return T_KEYWORD
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 inJsxLiterals() ? T_JSX_LITERALS : inJsxTag() ? T_IDENTIFIER : T_CLS_NUMBER
205
+ return inJsxTag() ? T_IDENTIFIER : T_CLS_NUMBER
197
206
  } else {
198
- return inJsxLiterals()
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
- if (jsx.close && (last[1] === '>' || last[1] === '/>')) {
225
- jsx.close = false
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
- if (jsx.tag) {
228
- const isOpenElementEnd = curr === '>'
229
- const isCloseElementEnd = p_c === '/>'
230
- jsx.child = !isCloseElementEnd
231
- jsx.tag = !(isOpenElementEnd || isCloseElementEnd)
232
- jsx.close = isOpenElementEnd || isCloseElementEnd
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 (!jsx.tag && (curr === '<' && isIdentifierChar(next) || c_n === '</')) {
236
- jsx.tag = true
237
- jsx.child = false
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 = !jsx.tag && isRegexStart(c_n)
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: `()` before `/` operator is still in expression
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 && !'()'.includes(lastToken)) ||
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
- isWord(curr) === isWord(current[current.length - 1]) &&
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
- current = curr
338
- if (c_n === '</' || c_n === '/>') {
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 data-line-number="${lineNo++}" class="sh__line">${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(token => (
372
- `<span class="sh__${types[token[0]]}">${encode(token[1])}</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.1",
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
- "next": "12.1.4",
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
  }