sugar-high 1.3.0 → 2.0.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.
@@ -0,0 +1,6 @@
1
+ // @ts-check
2
+ import { tokenize as tokenizeJavaScript } from './javascript-runtime.js'
3
+
4
+ // JavaScript includes JSX and preserves the lightweight TypeScript heuristic used by the default
5
+ // highlighter. Use the TypeScript preset to force TS/TSX behavior for ambiguous snippets.
6
+ export const tokenize = tokenizeJavaScript
@@ -1,4 +1,8 @@
1
1
  // @ts-check
2
2
 
3
+ import { onCommentEnd, onCommentStart } from './clike-base.js'
4
+
3
5
  export const keywords = new Set(['true', 'false', 'null'])
4
6
  export const quotedKeys = true
7
+ // JSONC is treated as the comment-tolerant JSON dialect rather than a separate language.
8
+ export { onCommentEnd, onCommentStart }
@@ -0,0 +1,5 @@
1
+ // @ts-check
2
+ import { onCommentEnd, onCommentStart } from './clike-base.js'
3
+ export const keywords = new Set(['as','break','by','catch','class','companion','const','constructor','continue','data','do','else','enum','false','finally','for','fun','get','if','import','in','infix','init','interface','internal','is','lateinit','noinline','null','object','open','operator','out','override','package','private','protected','public','reified','return','sealed','set','suspend','tailrec','this','throw','true','try','typealias','val','var','vararg','when','where','while'])
4
+ export const typeKeywords = new Set(['Any','Boolean','Byte','Char','Double','Float','Int','Long','Nothing','Short','String','Unit'])
5
+ export { onCommentEnd, onCommentStart }
@@ -0,0 +1,15 @@
1
+ // @ts-check
2
+ import { onCommentEnd, onCommentStart } from './plain-base.js'
3
+
4
+ export const keywords = new Set([])
5
+
6
+ export const annotateLine = (line) => {
7
+ let annotation = ''
8
+ if (/^#{1,6}\s/.test(line.value)) annotation = 'markdown-heading'
9
+ else if (/^\s*>/.test(line.value)) annotation = 'markdown-quote'
10
+ else if (/^\s*(?:[-*+] |\d+[.)] )/.test(line.value)) annotation = 'markdown-list'
11
+ else if (/^\s*```/.test(line.value)) annotation = 'markdown-fence'
12
+ if (annotation) line.annotations.push(annotation)
13
+ }
14
+
15
+ export { onCommentEnd, onCommentStart }
@@ -0,0 +1,5 @@
1
+ // @ts-check
2
+ export const keywords = new Set(['abstract','and','array','as','break','callable','case','catch','class','clone','const','continue','declare','default','do','echo','else','elseif','empty','enddeclare','endfor','endforeach','endif','endswitch','endwhile','enum','eval','exit','extends','false','final','finally','fn','for','foreach','from','function','global','goto','if','implements','include','include_once','instanceof','insteadof','interface','isset','list','match','namespace','new','null','or','print','private','protected','public','readonly','require','require_once','return','static','switch','throw','trait','true','try','unset','use','var','while','xor','yield'])
3
+ export const typeKeywords = new Set(['bool','float','int','iterable','mixed','never','object','string','void'])
4
+ export const onCommentStart = (curr, next) => curr === '#' ? 1 : curr + next === '//' ? 1 : curr + next === '/*' ? 2 : 0
5
+ export const onCommentEnd = (prev, curr) => curr === '\n' ? 1 : prev + curr === '*/' ? 2 : 0
@@ -0,0 +1,4 @@
1
+ // @ts-check
2
+
3
+ export const onCommentStart = () => 0
4
+ export const onCommentEnd = () => 0
@@ -0,0 +1,5 @@
1
+ // @ts-check
2
+ export const caseInsensitive = true
3
+ export const keywords = new Set(['begin','break','catch','class','continue','data','define','do','dynamicparam','else','elseif','end','enum','exit','filter','finally','for','foreach','from','function','if','in','param','process','return','switch','throw','trap','try','until','using','while'])
4
+ export const onCommentStart = (curr, next) => curr === '#' ? 1 : curr + next === '<#' ? 2 : 0
5
+ export const onCommentEnd = (prev, curr) => curr === '\n' ? 1 : prev + curr === '#>' ? 2 : 0
@@ -0,0 +1,10 @@
1
+ // @ts-check
2
+ import { onCommentEnd, onCommentStart } from './hash-comment-base.js'
3
+
4
+ export const keywords = new Set([
5
+ 'case', 'coproc', 'do', 'done', 'elif', 'else', 'esac', 'export', 'fi', 'for',
6
+ 'function', 'if', 'in', 'local', 'readonly', 'return', 'select', 'then', 'time',
7
+ 'until', 'while',
8
+ ])
9
+
10
+ export { onCommentEnd, onCommentStart }
@@ -0,0 +1,31 @@
1
+ // @ts-check
2
+
3
+ export const keywords = new Set([
4
+ 'add', 'all', 'alter', 'and', 'as', 'asc', 'between', 'by', 'case', 'check',
5
+ 'column', 'constraint', 'create', 'cross', 'database', 'default', 'delete', 'desc',
6
+ 'distinct', 'drop', 'else', 'end', 'exists', 'foreign', 'from', 'full', 'group',
7
+ 'having', 'in', 'index', 'inner', 'insert', 'into', 'is', 'join', 'key', 'left',
8
+ 'like', 'limit', 'not', 'null', 'offset', 'on', 'or', 'order', 'outer', 'primary',
9
+ 'references', 'right', 'select', 'set', 'table', 'then', 'union', 'unique', 'update',
10
+ 'values', 'view', 'when', 'where', 'with',
11
+ ])
12
+
13
+ export const typeKeywords = new Set([
14
+ 'bigint', 'binary', 'bit', 'blob', 'boolean', 'char', 'date', 'datetime', 'decimal',
15
+ 'double', 'float', 'int', 'integer', 'interval', 'json', 'numeric', 'real', 'smallint',
16
+ 'text', 'time', 'timestamp', 'uuid', 'varchar',
17
+ ])
18
+
19
+ export const caseInsensitive = true
20
+
21
+ export const onCommentStart = (currentChar, nextChar) => {
22
+ const pair = currentChar + nextChar
23
+ if (pair === '--') return 1
24
+ if (pair === '/*') return 2
25
+ return 0
26
+ }
27
+
28
+ export const onCommentEnd = (prevChar, currChar) => {
29
+ if (currChar === '\n') return 1
30
+ return prevChar + currChar === '*/' ? 2 : 0
31
+ }
@@ -0,0 +1,5 @@
1
+ // @ts-check
2
+ import { onCommentEnd, onCommentStart } from './clike-base.js'
3
+ export const keywords = new Set(['as','associatedtype','break','case','catch','class','continue','convenience','default','defer','deinit','didSet','do','dynamic','else','enum','extension','fallthrough','false','fileprivate','final','for','func','get','guard','if','import','in','indirect','infix','init','inout','internal','is','lazy','let','mutating','nil','nonmutating','open','operator','override','precedencegroup','private','protocol','public','repeat','required','rethrows','return','self','set','some','static','struct','subscript','super','switch','throw','throws','true','try','typealias','unowned','var','weak','where','while','willSet'])
4
+ export const typeKeywords = new Set(['Any','Bool','Character','Double','Float','Int','Never','String','UInt','Void'])
5
+ export { onCommentEnd, onCommentStart }
@@ -0,0 +1,5 @@
1
+ // @ts-check
2
+ import { onCommentEnd, onCommentStart } from './hash-comment-base.js'
3
+ export const keywords = new Set(['false','true'])
4
+ export const quotedKeys = true
5
+ export { onCommentEnd, onCommentStart }
@@ -0,0 +1,7 @@
1
+ // @ts-check
2
+ import { tokenize as tokenizeJavaScript } from './javascript-runtime.js'
3
+
4
+ export const tokenize = (code, options) => tokenizeJavaScript(code, {
5
+ ...options,
6
+ typescript: true,
7
+ })
@@ -0,0 +1,10 @@
1
+ // @ts-check
2
+ import { onCommentEnd, onCommentStart } from './hash-comment-base.js'
3
+
4
+ export const keywords = new Set([
5
+ 'false', 'False', 'FALSE', 'no', 'No', 'NO', 'null', 'Null', 'NULL',
6
+ 'off', 'Off', 'OFF', 'on', 'On', 'ON', 'true', 'True', 'TRUE', 'yes', 'Yes', 'YES',
7
+ ])
8
+
9
+ export const quotedKeys = true
10
+ export { onCommentEnd, onCommentStart }
package/lib/shared.js ADDED
@@ -0,0 +1,149 @@
1
+ // @ts-check
2
+
3
+ const TokenTypes = /** @type {const} */ ('identifier keyword string class property entity jsxliterals sign comment break space'.split(' '))
4
+ const [
5
+ T_IDENTIFIER, T_KEYWORD, T_STRING, T_CLASS, T_PROPERTY, T_ENTITY,
6
+ T_JSX_LITERALS, T_SIGN, T_COMMENT, T_BREAK, T_SPACE,
7
+ ] = TokenTypes.map((_, index) => index)
8
+
9
+ const SugarHigh = /** @type {const} */ ({
10
+ TokenTypes,
11
+ TokenMap: new Map(TokenTypes.map((type, index) => [type, index])),
12
+ })
13
+
14
+ /** @param {string} value @param {Array<[number, string]>} tokens */
15
+ function assemble(value, tokens) {
16
+ const lines = []
17
+ let lineIndex = 0
18
+ /** @type {Array<[number, string]>} */
19
+ const lineTokens = []
20
+ let lastWasBreak = false
21
+
22
+ /** @param {Array<[number, string]>} tokens */
23
+ function flushLine(tokens) {
24
+ lines.push({
25
+ index: lineIndex++,
26
+ value: tokens.map(([, tokenValue]) => tokenValue).join(''),
27
+ tokens: tokens.map(([type, tokenValue]) => ({
28
+ type: TokenTypes[type],
29
+ value: tokenValue,
30
+ })),
31
+ annotations: [],
32
+ })
33
+ }
34
+
35
+ for (let index = 0; index < tokens.length; index++) {
36
+ const token = tokens[index]
37
+ const [type, value] = token
38
+ if (type !== T_BREAK) {
39
+ if (value.includes('\n')) {
40
+ const values = value.split('\n')
41
+ for (let part = 0; part < values.length; part++) {
42
+ lineTokens.push([type, values[part]])
43
+ if (part < values.length - 1) {
44
+ flushLine(lineTokens)
45
+ lineTokens.length = 0
46
+ }
47
+ }
48
+ } else {
49
+ lineTokens.push(token)
50
+ }
51
+ lastWasBreak = false
52
+ } else {
53
+ if (lastWasBreak) flushLine([])
54
+ else {
55
+ flushLine(lineTokens)
56
+ lineTokens.length = 0
57
+ }
58
+ if (index === tokens.length - 1) flushLine([])
59
+ lastWasBreak = true
60
+ }
61
+ }
62
+
63
+ if (lineTokens.length) flushLine(lineTokens)
64
+ return { value, lines }
65
+ }
66
+
67
+ /**
68
+ * @param {import('./core.js').ParsedCode} parsed
69
+ * @param {import('./core.js').DisplayOptions | undefined} options
70
+ */
71
+ function generate(parsed, options) {
72
+ const cx = options?.cx
73
+ const mark = options?.mark
74
+ const markLine = options?.markLine
75
+
76
+ return parsed.lines.map((parsedLine) => {
77
+ const line = {
78
+ index: parsedLine.index,
79
+ value: parsedLine.value,
80
+ tokens: parsedLine.tokens,
81
+ annotations: parsedLine.annotations,
82
+ className: `sh__line${parsedLine.annotations.map(annotation => ` sh__line--${annotation}`).join('')}`,
83
+ style: {},
84
+ properties: {},
85
+ }
86
+ markLine?.(line)
87
+
88
+ return {
89
+ type: 'element',
90
+ tagName: 'span',
91
+ children: parsedLine.tokens.map(({ type, value }) => {
92
+ const extraClassName = cx?.[type]
93
+ const token = {
94
+ type,
95
+ value,
96
+ className: `sh__token--${type}${extraClassName ? ` ${extraClassName}` : ''}`,
97
+ style: { color: `var(--sh-${type})` },
98
+ properties: {},
99
+ }
100
+ mark?.(token)
101
+ return {
102
+ type: 'element',
103
+ tokenType: token.type,
104
+ tagName: 'span',
105
+ children: [{ type: 'text', value: token.value }],
106
+ properties: {
107
+ ...token.properties,
108
+ className: token.className,
109
+ style: token.style,
110
+ },
111
+ }
112
+ }),
113
+ properties: {
114
+ ...line.properties,
115
+ className: line.className,
116
+ style: line.style,
117
+ },
118
+ }
119
+ })
120
+ }
121
+
122
+ const entities = { '&': '&amp;', '<': '&lt;', '>': '&gt;', '"': '&quot;', "'": '&#039;' }
123
+ /** @param {string} value */
124
+ const encode = (value) => value.replace(/[&<>"']/g, character => entities[character])
125
+
126
+ /** @param {Record<string, any>} values */
127
+ function attributes(values) {
128
+ const style = Object.entries(values.style || {})
129
+ .map(([key, value]) => `${key.replace(/[A-Z]/g, match => `-${match.toLowerCase()}`)}:${value}`).join(';')
130
+ const properties = Object.entries(values)
131
+ .filter(([key, value]) => /^[\w:-]+$/.test(key) && key !== 'className' && key !== 'style' && value !== false && value != null)
132
+ .map(([key, value]) => value === true ? key : `${key}="${encode(String(value))}"`).join(' ')
133
+ return `class="${encode(values.className || '')}"${style ? ` style="${encode(style)}"` : ''}${properties ? ` ${properties}` : ''}`
134
+ }
135
+
136
+ /** @param {Array<any>} lines */
137
+ function toHtml(lines) {
138
+ return lines.map(line => {
139
+ const children = line.children.map(token => {
140
+ return `<${token.tagName} ${attributes(token.properties)}>${encode(token.children[0].value)}</${token.tagName}>`
141
+ }).join('')
142
+ return `<${line.tagName} ${attributes(line.properties)}>${children}</${line.tagName}>`
143
+ }).join('\n')
144
+ }
145
+
146
+ export {
147
+ assemble, encode, generate, SugarHigh, toHtml, TokenTypes, T_BREAK, T_CLASS, T_COMMENT, T_ENTITY, T_IDENTIFIER,
148
+ T_JSX_LITERALS, T_KEYWORD, T_PROPERTY, T_SIGN, T_SPACE, T_STRING,
149
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "sugar-high",
3
- "version": "1.3.0",
3
+ "version": "2.0.0",
4
4
  "repository": {
5
5
  "type": "git",
6
6
  "url": "git+https://github.com/huozhi/sugar-high.git",
@@ -14,23 +14,28 @@
14
14
  "types": "./lib/index.d.ts",
15
15
  "default": "./lib/index.js"
16
16
  },
17
- "./presets": {
18
- "types": "./lib/presets/index.d.ts",
19
- "default": "./lib/presets/index.js"
17
+ "./core": {
18
+ "types": "./lib/core.d.ts",
19
+ "default": "./lib/core.js"
20
+ },
21
+ "./lang": {
22
+ "types": "./lib/lang.d.ts",
23
+ "default": "./lib/lang.js"
20
24
  }
21
25
  },
22
- "description": "Super lightweight JSX syntax highlighter",
26
+ "description": "Lightweight, zero-dependency syntax highlighting for popular programming languages",
23
27
  "files": [
24
28
  "lib"
25
29
  ],
26
30
  "license": "MIT",
27
- "scripts": {
28
- "test": "vitest",
29
- "build": "echo 'package requires no build'"
30
- },
31
31
  "devDependencies": {
32
32
  "@types/node": "22.12.0",
33
33
  "typescript": "6.0.2",
34
34
  "vitest": "^3.0.2"
35
+ },
36
+ "scripts": {
37
+ "test": "vitest",
38
+ "build": "echo 'package requires no build'",
39
+ "benchmark": "node scripts/benchmark.mjs"
35
40
  }
36
- }
41
+ }
@@ -1,18 +0,0 @@
1
- type LanguageConfig = {
2
- keywords: Set<string>
3
- typeKeywords?: Set<string>
4
- onCommentStart?(curr: string, next: string): 0 | 1 | 2
5
- onCommentEnd?(prev: string, curr: string): 0 | 1 | 2
6
- onQuote?(curr: string, i: number, code: string): number | null | undefined
7
- quotedKeys?: boolean
8
- lineClassName?(line: string, index: number): string | null | undefined
9
- }
10
-
11
- export const css: LanguageConfig
12
- export const rust: LanguageConfig
13
- export const python: LanguageConfig
14
- export const c: LanguageConfig
15
- export const go: LanguageConfig
16
- export const java: LanguageConfig
17
- export const diff: LanguageConfig
18
- export const json: LanguageConfig
@@ -1,8 +0,0 @@
1
- export * as css from './lang/css.js'
2
- export * as rust from './lang/rust.js'
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'
8
- export * as json from './lang/json.js'