sugar-high 1.0.0 → 1.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.
package/README.md CHANGED
@@ -4,7 +4,7 @@
4
4
 
5
5
  Super lightweight syntax highlighter for JavaScript and JSX—about **1 kB** minified and gzipped. Works in the browser or any JS runtime that can set HTML strings.
6
6
 
7
- ![Sugar High preview](https://repository-images.githubusercontent.com/453236442/e9c853e9-4af6-45c4-aa4a-c27afbf16a0d)
7
+ ![Sugar High preview](https://repository-images.githubusercontent.com/453236442/9aa2144a-3a4c-4a93-a87f-92ca6a37ded6)
8
8
 
9
9
  ## Install
10
10
 
@@ -33,6 +33,8 @@ import { rust } from 'sugar-high/presets'
33
33
  const html = highlight(source, { ...rust })
34
34
  ```
35
35
 
36
+ Available presets: `c`, `css`, `diff`, `go`, `java`, `python`, and `rust`.
37
+
36
38
  For more language presets and syntax color themes, see **[sugar-high.vercel.app](https://sugar-high.vercel.app/)**.
37
39
 
38
40
  ## Styling
package/lib/index.d.ts CHANGED
@@ -1,5 +1,9 @@
1
1
  type HighlightOptions = {
2
2
  keywords?: Set<string>
3
+ /**
4
+ * Highlighted as the `class` token type (e.g. built-in types). Checked before `keywords`.
5
+ */
6
+ typeKeywords?: Set<string>
3
7
  onCommentStart?: (curr: string, next: string) => number | boolean
4
8
  onCommentEnd?: (curr: string, prev: string) => number | boolean
5
9
  /**
@@ -7,11 +11,12 @@ type HighlightOptions = {
7
11
  * or null/undefined or a number below 1 for default JS single-quoted string rules.
8
12
  */
9
13
  onQuote?: (curr: string, i: number, code: string) => number | null | undefined
14
+ lineClassName?: (line: string, index: number) => string | null | undefined
10
15
  }
11
16
 
12
17
  export function highlight(code: string, options?: HighlightOptions): string
13
18
  export function tokenize(code: string, options?: HighlightOptions): Array<[number, string]>
14
- export function generate(tokens: Array<[number, string]>): Array<any>
19
+ export function generate(tokens: Array<[number, string]>, options?: Pick<HighlightOptions, 'lineClassName'>): Array<any>
15
20
  export const SugarHigh: {
16
21
  TokenTypes: {
17
22
  [key: number]: string
package/lib/index.js CHANGED
@@ -69,6 +69,13 @@ const Keywords_Ts = new Set([
69
69
  'unknown',
70
70
  'never',
71
71
  'any',
72
+ // Built-in type names (annotations / type positions)
73
+ 'number',
74
+ 'string',
75
+ 'boolean',
76
+ 'bigint',
77
+ 'symbol',
78
+ 'object',
72
79
  ])
73
80
 
74
81
  const Signs = new Set([
@@ -298,9 +305,11 @@ function isRegexStart(str) {
298
305
  * @param {string} code
299
306
  * @param {{
300
307
  * keywords?: Set<string>
308
+ * typeKeywords?: Set<string>
301
309
  * onCommentStart?: (curr: string, next: string) => number | boolean
302
310
  * onCommentEnd?: (prev: string, curr: string) => number | boolean
303
311
  * onQuote?: (curr: string, i: number, code: string) => number | null | undefined
312
+ * lineClassName?: (line: string, index: number) => string | null | undefined
304
313
  * } | undefined} options
305
314
  * Optional `onQuote(curr, i, code)` at `code[i] === "'"`: return length to consume from `i` (>= 1),
306
315
  * or null/undefined/below 1 for default JS single-quoted strings. No substring allocation.
@@ -319,6 +328,9 @@ function tokenize(code, options) {
319
328
  onCommentEnd,
320
329
  } = mergedOptions
321
330
 
331
+ const resolvedTypeKeywords =
332
+ mergedOptions.typeKeywords instanceof Set ? mergedOptions.typeKeywords : null
333
+
322
334
  let current = ''
323
335
  let type = -1
324
336
  /** @type {[number, string]} */
@@ -388,6 +400,8 @@ function tokenize(code, options) {
388
400
  // Determine strings first before other types
389
401
  if (inStringQuotes() || inStrTemplateLiterals()) {
390
402
  return T_STRING
403
+ } else if (resolvedTypeKeywords && resolvedTypeKeywords.has(token)) {
404
+ return last[1] === '.' ? T_IDENTIFIER : T_CLS_NUMBER
391
405
  } else if (resolvedKeywords.has(token)) {
392
406
  return last[1] === '.' ? T_IDENTIFIER : T_KEYWORD
393
407
  } else if (isLineBreak) {
@@ -800,29 +814,42 @@ function tokenize(code, options) {
800
814
 
801
815
  /**
802
816
  * @param {Array<[number, string]>} tokens
817
+ * @param {{
818
+ * lineClassName?: (line: string, index: number) => string | null | undefined
819
+ * } | undefined} options
803
820
  * @return {Array<{type: string, tagName: string, children: any[], properties: Record<string, string>}>}
804
821
  */
805
- function generate(tokens) {
822
+ function generate(tokens, options) {
806
823
  const lines = []
824
+ const lineClassName = options && typeof options.lineClassName === 'function'
825
+ ? options.lineClassName
826
+ : null
827
+ let lineIndex = 0
807
828
  /**
808
829
  * @param {any} children
830
+ * @param {string} text
809
831
  * @return {{type: string, tagName: string, children: any[], properties: Record<string, string>}}
810
832
  */
811
- const createLine = (children) =>
812
- ({
833
+ const createLine = (children, text) => {
834
+ const extraClassName = lineClassName ? lineClassName(text, lineIndex) : ''
835
+ lineIndex++
836
+
837
+ return ({
813
838
  type: 'element',
814
839
  tagName: 'span',
815
840
  children,
816
841
  properties: {
817
- className: 'sh__line',
842
+ className: extraClassName ? `sh__line ${extraClassName}` : 'sh__line',
818
843
  },
819
844
  })
845
+ }
820
846
 
821
847
  /**
822
848
  * @param {Array<[number, string]>} tokens
823
849
  * @returns {void}
824
850
  */
825
851
  function flushLine(tokens) {
852
+ const lineText = tokens.map(([, value]) => value).join('')
826
853
  /** @type {Array<any>} */
827
854
  const lineTokens = (
828
855
  tokens
@@ -842,7 +869,7 @@ function generate(tokens) {
842
869
  }
843
870
  })
844
871
  )
845
- lines.push(createLine(lineTokens))
872
+ lines.push(createLine(lineTokens, lineText))
846
873
  }
847
874
  /** @type {Array<[number, string]>} */
848
875
  const lineTokens = []
@@ -928,16 +955,18 @@ function toHtml(lines) {
928
955
  * @param {string} code
929
956
  * @param {{
930
957
  * keywords?: Set<string>
958
+ * typeKeywords?: Set<string>
931
959
  * onCommentStart?: (curr: string, next: string) => number | boolean
932
960
  * onCommentEnd?: (curr: string, prev: string) => number | boolean
933
961
  * onQuote?: (curr: string, i: number, code: string) => number | null | undefined
962
+ * lineClassName?: (line: string, index: number) => string | null | undefined
934
963
  * } | undefined} options
935
964
  * `onQuote` same as `tokenize`.
936
965
  * @returns {string}
937
966
  */
938
967
  function highlight(code, options) {
939
968
  const tokens = tokenize(code, options)
940
- const lines = generate(tokens)
969
+ const lines = generate(tokens, options)
941
970
  const output = toHtml(lines)
942
971
  return output
943
972
  }
@@ -1,10 +1,16 @@
1
1
  type LanguageConfig = {
2
2
  keywords: Set<string>
3
+ typeKeywords?: Set<string>
3
4
  onCommentStart?(curr: string, next: string): 0 | 1 | 2
4
5
  onCommentEnd?(prev: string, curr: string): 0 | 1 | 2
5
6
  onQuote?(curr: string, i: number, code: string): number | null | undefined
7
+ lineClassName?(line: string, index: number): string | null | undefined
6
8
  }
7
9
 
8
10
  export const css: LanguageConfig
9
11
  export const rust: LanguageConfig
10
12
  export const python: LanguageConfig
13
+ export const c: LanguageConfig
14
+ export const go: LanguageConfig
15
+ export const java: LanguageConfig
16
+ export const diff: LanguageConfig
@@ -1,3 +1,7 @@
1
1
  export * as css from './lang/css.js'
2
2
  export * as rust from './lang/rust.js'
3
3
  export * as python from './lang/python.js'
4
+ export * as c from './lang/c.js'
5
+ export * as go from './lang/go.js'
6
+ export * as java from './lang/java.js'
7
+ export * as diff from './lang/diff.js'
@@ -0,0 +1,17 @@
1
+ // @ts-check
2
+ import { onCommentStart, onCommentEnd } from './clike-base.js'
3
+
4
+ export const typeKeywords = new Set([
5
+ 'void', 'char', 'short', 'int', 'long', 'float', 'double', 'signed', 'unsigned',
6
+ '_Bool', '_Complex', '_Imaginary',
7
+ ])
8
+
9
+ export const keywords = new Set([
10
+ 'auto', 'break', 'case', 'const', 'continue', 'default', 'do', 'else', 'enum',
11
+ 'extern', 'for', 'goto', 'if', 'inline', 'register', 'restrict', 'return',
12
+ 'sizeof', 'static', 'struct', 'switch', 'typedef', 'union', 'volatile', 'while',
13
+ '_Alignas', '_Alignof', '_Atomic', '_Generic', '_Noreturn', '_Static_assert',
14
+ '_Thread_local',
15
+ ])
16
+
17
+ export { onCommentStart, onCommentEnd }
@@ -0,0 +1,12 @@
1
+ // @ts-check
2
+ export const onCommentStart = (currentChar, nextChar) => {
3
+ const pair = currentChar + nextChar
4
+ if (pair === '//') return 1
5
+ if (pair === '/*') return 1
6
+ return 0
7
+ }
8
+
9
+ export const onCommentEnd = (prevChar, currChar) => {
10
+ if (currChar === '\n') return 1
11
+ return prevChar + currChar === '*/' ? 1 : 0
12
+ }
@@ -0,0 +1,11 @@
1
+ // @ts-check
2
+
3
+ export const keywords = new Set([])
4
+
5
+ export const lineClassName = (line) => {
6
+ if (line.startsWith('+') && !line.startsWith('+++')) return 'sh__line--diff-add'
7
+ if (line.startsWith('-') && !line.startsWith('---')) return 'sh__line--diff-remove'
8
+ if (line.startsWith('@@')) return 'sh__line--diff-hunk'
9
+ if (/^(diff --git|index |--- |\+\+\+ )/.test(line)) return 'sh__line--diff-meta'
10
+ return undefined
11
+ }
@@ -0,0 +1,16 @@
1
+ // @ts-check
2
+ import { onCommentStart, onCommentEnd } from './clike-base.js'
3
+
4
+ export const typeKeywords = new Set([
5
+ 'bool', 'byte', 'complex64', 'complex128', 'error', 'float32', 'float64', 'int',
6
+ 'int8', 'int16', 'int32', 'int64', 'rune', 'string', 'uint', 'uint8', 'uint16',
7
+ 'uint32', 'uint64', 'uintptr',
8
+ ])
9
+
10
+ export const keywords = new Set([
11
+ 'break', 'case', 'chan', 'const', 'continue', 'default', 'defer', 'else',
12
+ 'fallthrough', 'for', 'func', 'go', 'goto', 'if', 'import', 'interface', 'map',
13
+ 'package', 'range', 'return', 'select', 'struct', 'switch', 'type', 'var',
14
+ ])
15
+
16
+ export { onCommentStart, onCommentEnd }
@@ -0,0 +1,17 @@
1
+ // @ts-check
2
+ import { onCommentStart, onCommentEnd } from './clike-base.js'
3
+
4
+ export const typeKeywords = new Set([
5
+ 'boolean', 'byte', 'char', 'double', 'float', 'int', 'long', 'short', 'void',
6
+ ])
7
+
8
+ export const keywords = new Set([
9
+ 'abstract', 'assert', 'break', 'case', 'catch', 'class', 'const', 'continue',
10
+ 'default', 'do', 'else', 'enum', 'extends', 'final', 'finally', 'for', 'goto',
11
+ 'if', 'implements', 'import', 'instanceof', 'interface', 'native', 'new',
12
+ 'package', 'private', 'protected', 'public', 'return', 'static', 'strictfp',
13
+ 'super', 'switch', 'synchronized', 'this', 'throw', 'throws', 'transient',
14
+ 'try', 'volatile', 'while',
15
+ ])
16
+
17
+ export { onCommentStart, onCommentEnd }
package/package.json CHANGED
@@ -1,6 +1,11 @@
1
1
  {
2
2
  "name": "sugar-high",
3
- "version": "1.0.0",
3
+ "version": "1.2.0",
4
+ "repository": {
5
+ "type": "git",
6
+ "url": "git+https://github.com/huozhi/sugar-high.git",
7
+ "directory": "packages/sugar-high"
8
+ },
4
9
  "type": "module",
5
10
  "types": "./lib/index.d.ts",
6
11
  "main": "./lib/index.js",
@@ -25,7 +30,7 @@
25
30
  },
26
31
  "devDependencies": {
27
32
  "@types/node": "22.12.0",
28
- "typescript": "5.7.3",
33
+ "typescript": "6.0.2",
29
34
  "vitest": "^3.0.2"
30
35
  }
31
36
  }