sugar-high 0.1.2 → 0.2.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 +4 -2
  2. package/lib/index.mjs +45 -38
  3. package/package.json +4 -2
package/README.md CHANGED
@@ -33,7 +33,7 @@ Then make your own theme with customized colors by token type and put in global
33
33
  * Class, number and null
34
34
  * sign
35
35
  * comment
36
- *
36
+ * jsxliterals
37
37
  */
38
38
  .sh__class {
39
39
  color: #2d5e9d;
@@ -53,7 +53,9 @@ Then make your own theme with customized colors by token type and put in global
53
53
  .sh__comment {
54
54
  color: #a19595;
55
55
  }
56
-
56
+ .sh__jsxliterals {
57
+ color: #03066e;
58
+ }
57
59
  ```
58
60
 
59
61
  ### LICENSE
package/lib/index.mjs CHANGED
@@ -74,7 +74,7 @@ const signs = new Set([
74
74
  '\\',
75
75
  ])
76
76
 
77
- const types = [
77
+ export const types = [
78
78
  'identifier',
79
79
  'keyword',
80
80
  'string',
@@ -83,6 +83,7 @@ const types = [
83
83
  'comment',
84
84
  'break',
85
85
  'space',
86
+ 'jsxliterals'
86
87
  ]
87
88
 
88
89
  /**
@@ -95,6 +96,7 @@ const types = [
95
96
  * 5 - comment
96
97
  * 6 - break
97
98
  * 7 - space
99
+ * 8 - jsx literals
98
100
  *
99
101
  */
100
102
  const [
@@ -106,6 +108,7 @@ const [
106
108
  T_COMMENT,
107
109
  T_BREAK,
108
110
  T_SPACE,
111
+ T_JSX_LITERALS,
109
112
  ] = types.map((_, i) => i)
110
113
 
111
114
  function isSpaces(str) {
@@ -124,7 +127,11 @@ function encode(str) {
124
127
  }
125
128
 
126
129
  function isIdentifierChar(chr) {
127
- return /[a-zA-Z0-9_$]/.test(chr)
130
+ return /^[\w_$]$/.test(chr)
131
+ }
132
+
133
+ function isIdentifier(str) {
134
+ return /[a-zA-Z_$]/.test(str[0]) && (str.length === 1 || /^[\w_$]+$/.test(str.slice(1)))
128
135
  }
129
136
 
130
137
  function isStringQuotation(chr) {
@@ -160,27 +167,16 @@ export function tokenize(code) {
160
167
  const inJsxLiterals = () => !jsx.tag && jsx.child && !jsx.expr
161
168
 
162
169
  function classify(token) {
163
- const [chr0, chr1] = [token[0], token[1]]
164
- if (isCommentStart(chr0 + chr1)) {
165
- return T_COMMENT
166
- } else if (keywords.has(token)) {
170
+ const chr0 = token[0]
171
+ if (keywords.has(token)) {
167
172
  return T_KEYWORD
168
173
  } else if (token === '\n') {
169
174
  return T_BREAK
170
- } else if (token === ' ') {
175
+ } else if (isSpaces(token)) {
171
176
  return T_SPACE
172
177
  } else if (token.split('').every(ch => signs.has(ch))) {
173
178
  return T_SIGN
174
179
  } else if (
175
- (
176
- // is quoted string
177
- (isStringQuotation(chr0) && !isStringQuotation(chr1)) ||
178
- // is regex
179
- (!jsx.tag && isRegexStart(chr0 + chr1) && token[token.length - 1] === '/')
180
- )
181
- ) {
182
- return T_STRING
183
- } else if (
184
180
  !inJsxLiterals() &&
185
181
  (
186
182
  isIdentifierChar(chr0) &&
@@ -190,13 +186,13 @@ export function tokenize(code) {
190
186
  ) {
191
187
  return T_CLS_NUMBER
192
188
  } else {
193
- return T_IDENTIFIER
189
+ return isIdentifier(token) ? T_IDENTIFIER : inJsxLiterals() ? T_JSX_LITERALS : T_STRING
194
190
  }
195
191
  }
196
192
 
197
- const append = () => {
193
+ const append = (_type) => {
198
194
  if (current) {
199
- type = classify(current)
195
+ type = _type || classify(current)
200
196
  /** @type [number, string] */
201
197
  const pair = [type, current]
202
198
  if (type !== T_SPACE && type !== T_BREAK) {
@@ -226,40 +222,51 @@ export function tokenize(code) {
226
222
  jsx.child = false
227
223
  }
228
224
 
229
- if (
230
- (isStringQuotation(curr) || !jsx.tag && isRegexStart(c_n))
231
- ) {
225
+ const isQuotationChar = isStringQuotation(curr)
226
+ const isRegexChar = !jsx.tag && isRegexStart(c_n)
227
+ if (isQuotationChar || isRegexChar) {
232
228
  append()
233
229
  const [lastType, lastToken] = last
234
- if (!((lastType === T_SIGN && !'()[]'.includes(lastToken)) || lastType === T_COMMENT)) {
230
+ // Special cases that are not considered as regex:
231
+ // * (expr) / expr: `()[]` before `/` is still in expression
232
+ // * <non comment start>/ expr: non comment start before `/` is not regex
233
+ if (
234
+ isRegexChar &&
235
+ lastType &&
236
+ !(
237
+ (lastType === T_SIGN && !'()[]'.includes(lastToken)) ||
238
+ lastType === T_COMMENT
239
+ )
240
+ ) {
235
241
  current = curr
236
242
  append()
237
243
  continue
238
244
  }
239
- let j = i + 1
240
- let strEnd = i
241
- const inString = () => j < code.length && code[j] !== '\n'
245
+ const start = i++
246
+
247
+ const isInline = () => i < code.length && code[i] !== '\n'
242
248
  // string quotation
243
- if (isStringQuotation(curr)) {
244
- for (; inString(); j++) {
245
- if (isStringQuotation(code[j])) {
246
- strEnd = j
249
+ if (isQuotationChar) {
250
+ for (; isInline(); i++) {
251
+ if (isStringQuotation(code[i])) {
247
252
  break
248
253
  }
249
254
  }
250
255
  } else {
251
256
  // regex
252
- for (; inString(); j++) {
253
- if (code[j] === '/' && code[j - 1] !== '\\') {
254
- strEnd = j
257
+ for (; isInline(); i++) {
258
+ if (code[i] === '/' && code[i - 1] !== '\\') {
259
+ // append regex flags
260
+ while (start !== i && /^[a-z]$/.test(code[i + 1]) && isInline()) {
261
+ i++
262
+ };
255
263
  break
256
264
  }
257
265
  }
258
266
  }
259
- if (strEnd !== i) {
260
- current = code.slice(i, strEnd + 1)
261
- append()
262
- i = j
267
+ if (start !== i) {
268
+ current = code.slice(start, i + 1)
269
+ append(T_STRING)
263
270
  } else {
264
271
  current = curr
265
272
  append()
@@ -272,7 +279,7 @@ export function tokenize(code) {
272
279
  for (; i < code.length && code[i - 1] + code[i] !== '*/'; i++);
273
280
  }
274
281
  current = code.slice(start, i + 1)
275
- append()
282
+ append(T_COMMENT)
276
283
  } else if (curr === ' ' || curr === '\n') {
277
284
  if (
278
285
  curr === ' ' &&
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "sugar-high",
3
- "version": "0.1.2",
3
+ "version": "0.2.0",
4
4
  "type": "module",
5
5
  "exports": "./lib/index.mjs",
6
6
  "description": "Super lightweight JSX syntax highlighter",
@@ -11,10 +11,12 @@
11
11
  ],
12
12
  "license": "MIT",
13
13
  "scripts": {
14
+ "test": "vitest run",
14
15
  "dev": "vite docs",
15
16
  "build": "vite build docs"
16
17
  },
17
18
  "devDependencies": {
18
- "vite": "2.7.13"
19
+ "vite": "^2.8.6",
20
+ "vitest": "^0.5.9"
19
21
  }
20
22
  }