sugar-high 0.2.2 → 0.4.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.
Files changed (3) hide show
  1. package/README.md +27 -19
  2. package/lib/index.mjs +71 -28
  3. package/package.json +1 -1
package/README.md CHANGED
@@ -3,7 +3,7 @@
3
3
 
4
4
  Super lightweight JSX syntax highlighter, around 1KB after minified and gzipped
5
5
 
6
- ![img](https://repository-images.githubusercontent.com/453236442/82cf2807-78f0-4009-bbdf-d7e753f73cf4)
6
+ ![img](https://repository-images.githubusercontent.com/453236442/94994be5-72fa-479f-8b87-2dc14fe40329)
7
7
 
8
8
  ### Usage
9
9
 
@@ -35,26 +35,34 @@ 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;
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;
40
46
  }
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;
47
+ ```
48
+
49
+ ### Features
50
+
51
+ #### Line number
52
+
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.
54
+
55
+ ```css
56
+ pre code {
57
+ counter-reset: sh-line-number;
55
58
  }
56
- .sh__jsxliterals {
57
- color: #03066e;
59
+
60
+ .sh__line::before {
61
+ counter-increment: sh-line-number 1;
62
+ content: counter(sh-line-number);
63
+ margin-right: 24px;
64
+ text-align: right;
65
+ color: #a4a4a4;
58
66
  }
59
67
  ```
60
68
 
package/lib/index.mjs CHANGED
@@ -123,14 +123,19 @@ function encode(str) {
123
123
  .replace(/>/g, '>')
124
124
  .replace(/"/g, '"')
125
125
  .replace(/'/g, ''')
126
- // matches space but not new line
127
- .replace(/[^\S\r\n]/g, ' ')
128
126
  }
129
127
 
130
128
  function isWord(chr) {
131
129
  return /^[\w_]+$/.test(chr)
132
130
  }
133
131
 
132
+ function isCls(str) {
133
+ const chr0 = str[0]
134
+ return isWord(chr0) &&
135
+ chr0 === chr0.toUpperCase() ||
136
+ str === 'null'
137
+ }
138
+
134
139
  function isIdentifierChar(chr) {
135
140
  return /^[a-zA-Z_$]$/.test(chr)
136
141
  }
@@ -163,16 +168,22 @@ export function tokenize(code) {
163
168
  /** @type {Array<[number, string]>} */
164
169
  const tokens = []
165
170
 
166
- // jsx.tag for entering open or closed tag
171
+ // jsx.tag for entering open or closed tags
172
+ // jsx.close for is the closing sign of open or close tags
167
173
  // jsx.child for entering children
168
174
  // jsx.expr for entering {expression}
169
- /** @type {{ tag: boolean; child: boolean; expr: boolean }} */
170
- const jsx = { tag: false, child: false, expr: false }
175
+ /** @type {{ tag: boolean; close: boolean; child: boolean; expr: boolean }} */
176
+ const jsx = { tag: false, close: false, child: false, expr: false }
171
177
 
172
- const inJsxLiterals = () => !jsx.tag && jsx.child && !jsx.expr
178
+ const inJsxTag = () => jsx.tag || jsx.close
179
+ const inJsxLiterals = () => !inJsxTag() && jsx.child && !jsx.expr
173
180
 
181
+ /**
182
+ *
183
+ * @param {string} token
184
+ * @returns {number}
185
+ */
174
186
  function classify(token) {
175
- const chr0 = token[0]
176
187
  if (keywords.has(token)) {
177
188
  return T_KEYWORD
178
189
  } else if (token === '\n') {
@@ -181,17 +192,12 @@ export function tokenize(code) {
181
192
  return T_SPACE
182
193
  } else if (token.split('').every(ch => signs.has(ch))) {
183
194
  return T_SIGN
184
- } else if (
185
- !inJsxLiterals() &&
186
- (
187
- isWord(chr0) &&
188
- chr0 === chr0.toUpperCase() ||
189
- token === 'null'
190
- )
191
- ) {
192
- return T_CLS_NUMBER
195
+ } else if (isCls(token)) {
196
+ return inJsxLiterals() ? T_JSX_LITERALS : inJsxTag() ? T_IDENTIFIER : T_CLS_NUMBER
193
197
  } else {
194
- return isIdentifier(token) ? T_IDENTIFIER : inJsxLiterals() ? T_JSX_LITERALS : T_STRING
198
+ return inJsxLiterals()
199
+ ? T_JSX_LITERALS
200
+ : isIdentifier(token) ? T_IDENTIFIER : T_STRING
195
201
  }
196
202
  }
197
203
 
@@ -211,15 +217,19 @@ export function tokenize(code) {
211
217
  const curr = code[i]
212
218
  const prev = code[i - 1]
213
219
  const next = code[i + 1]
214
- const c_n = curr + next // current and next
215
220
  const p_c = prev + curr // previous and current
221
+ const c_n = curr + next // current and next
216
222
  const isJsxLiterals = inJsxLiterals()
217
223
 
224
+ if (jsx.close && (last[1] === '>' || last[1] === '/>')) {
225
+ jsx.close = false
226
+ }
218
227
  if (jsx.tag) {
219
228
  const isOpenElementEnd = curr === '>'
220
229
  const isCloseElementEnd = p_c === '/>'
221
- jsx.child = !isCloseElementEnd && !isCloseElementEnd
230
+ jsx.child = !isCloseElementEnd
222
231
  jsx.tag = !(isOpenElementEnd || isCloseElementEnd)
232
+ jsx.close = isOpenElementEnd || isCloseElementEnd
223
233
  }
224
234
  // if it's not in a jsx tag declaration or a string, close child if next is jsx close tag
225
235
  if (!jsx.tag && (curr === '<' && isIdentifierChar(next) || c_n === '</')) {
@@ -319,6 +329,9 @@ export function tokenize(code) {
319
329
  !signs.has(curr)
320
330
  ) {
321
331
  current += curr
332
+ if (next === '<') {
333
+ append();
334
+ }
322
335
  } else {
323
336
  append()
324
337
  current = curr
@@ -349,16 +362,46 @@ export function tokenize(code) {
349
362
  * @return {Array<string>}
350
363
  */
351
364
  function generate(tokens) {
352
- const output = []
365
+ const linesHtml = []
366
+ const createLine = (content) => `<span class="sh__line">${content}</span>`
367
+
368
+ function flushLine(tokens) {
369
+ linesHtml.push(createLine(
370
+ tokens.map(([type, value]) => (
371
+ `<span style="color: var(--sh-${types[type]})">${encode(value)}</span>`
372
+ ))
373
+ .join('')
374
+ ))
375
+ }
376
+ const lineTokens = []
353
377
  for (let i = 0; i < tokens.length; i++) {
354
- const [type, token] = tokens[i]
355
- output.push(
356
- type === T_BREAK
357
- ? '<br>'
358
- : `<span class="sh__${types[type]}">${encode(token)}</span>`
359
- )
378
+ const token = tokens[i]
379
+ const [type, value] = token
380
+ if (type !== T_BREAK) {
381
+ // Divide multi-line token into multi-line code
382
+ if (value.includes('\n')) {
383
+ const lines = value.split('\n')
384
+ for (let j = 0; j < lines.length; j++) {
385
+ lineTokens.push([type, lines[j]])
386
+ if (j < lines.length - 1) {
387
+ flushLine(lineTokens)
388
+ lineTokens.length = 0
389
+ }
390
+ }
391
+ } else {
392
+ lineTokens.push(token)
393
+ }
394
+ } else {
395
+ lineTokens.push([type, ''])
396
+ flushLine(lineTokens)
397
+ lineTokens.length = 0
398
+ }
360
399
  }
361
- return output
400
+
401
+ if (lineTokens.length)
402
+ flushLine(lineTokens)
403
+
404
+ return linesHtml
362
405
  }
363
406
 
364
407
  /**
@@ -368,6 +411,6 @@ function generate(tokens) {
368
411
  */
369
412
  export function highlight(code) {
370
413
  const tokens = tokenize(code)
371
- const output = generate(tokens).join('')
414
+ const output = generate(tokens).join('\n')
372
415
  return output
373
416
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "sugar-high",
3
- "version": "0.2.2",
3
+ "version": "0.4.0",
4
4
  "type": "module",
5
5
  "exports": "./lib/index.mjs",
6
6
  "description": "Super lightweight JSX syntax highlighter",