@lotsa/verdant-js 0.7.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/LICENSE +373 -0
- package/README.md +39 -0
- package/package.json +54 -0
- package/pkg/index.d.ts +152 -0
- package/pkg/index.js +332 -0
- package/pkg/verdant-js.d.ts +28 -0
- package/pkg/verdant-js.js +2 -0
- package/pkg/verdant_js.wasm +0 -0
- package/src/index.ts +392 -0
package/src/index.ts
ADDED
|
@@ -0,0 +1,392 @@
|
|
|
1
|
+
import initModule, { type VerdantModule } from './verdant-js.js'
|
|
2
|
+
|
|
3
|
+
const PTR_SIZE = Float32Array.BYTES_PER_ELEMENT
|
|
4
|
+
|
|
5
|
+
let Module: VerdantModule = null as unknown as VerdantModule
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* A theme name to pass to {@link highlight} or {@link render}.
|
|
9
|
+
*/
|
|
10
|
+
export type BuiltinTheme = typeof BUILTIN_THEMES[number]
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* Source code with theme-independent style information attached.
|
|
14
|
+
*
|
|
15
|
+
* See {@link https://rubixdev.github.io/syntastica/syntastica/type.Highlights.html | the Rust definition}
|
|
16
|
+
* for more information.
|
|
17
|
+
*/
|
|
18
|
+
export type Highlights = [string, string | null][][]
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* A non-transparent color with red, green, and blue values between 0 and 255.
|
|
22
|
+
*
|
|
23
|
+
* Mirrors {@link https://rubixdev.github.io/syntastica/syntastica/style/type.Color.html | this Rust definition}.
|
|
24
|
+
*/
|
|
25
|
+
export interface Color {
|
|
26
|
+
red: number
|
|
27
|
+
green: number
|
|
28
|
+
blue: number
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* Defines how to style a region of text.
|
|
33
|
+
*
|
|
34
|
+
* Besides a main foreground {@link Color}, an optional background color and the following four booleans can be set:
|
|
35
|
+
*
|
|
36
|
+
* - underline
|
|
37
|
+
* - strikethrough
|
|
38
|
+
* - italic
|
|
39
|
+
* - bold
|
|
40
|
+
*
|
|
41
|
+
* Mirrors {@link https://rubixdev.github.io/syntastica/syntastica/style/struct.Style.html | this Rust definition}.
|
|
42
|
+
*/
|
|
43
|
+
export interface Style {
|
|
44
|
+
color: Color
|
|
45
|
+
bg: Color | null
|
|
46
|
+
underline: boolean
|
|
47
|
+
strikethrough: boolean
|
|
48
|
+
italic: boolean
|
|
49
|
+
bold: boolean
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
/**
|
|
53
|
+
* Defines how to style highlight captures.
|
|
54
|
+
*
|
|
55
|
+
* Mirrors {@link https://rubixdev.github.io/syntastica/syntastica/theme/struct.ResolvedTheme.html | this Rust definition}.
|
|
56
|
+
*/
|
|
57
|
+
export class Theme {
|
|
58
|
+
constructor(public styles: Record<string, Style>) {}
|
|
59
|
+
|
|
60
|
+
/**
|
|
61
|
+
* The default foreground color, if the theme defines one.
|
|
62
|
+
*/
|
|
63
|
+
get fg(): Color | null {
|
|
64
|
+
return this.styles['_normal']?.color || null
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
/**
|
|
68
|
+
* The default background color, if the theme defines one.
|
|
69
|
+
*/
|
|
70
|
+
get bg(): Color | null {
|
|
71
|
+
return this.styles['_normal']?.bg || null
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
/**
|
|
75
|
+
* Retreives the style map for a {@link BuiltinTheme}.
|
|
76
|
+
*/
|
|
77
|
+
static fromBuiltin(theme: BuiltinTheme): Theme {
|
|
78
|
+
checkModule()
|
|
79
|
+
const errmsgPtr = Module._malloc(PTR_SIZE)
|
|
80
|
+
const themePtr = Module.stringToNewUTF8(theme)
|
|
81
|
+
|
|
82
|
+
const resultPtr = Module._get_builtin_theme(errmsgPtr, themePtr)
|
|
83
|
+
if (resultPtr === 0) {
|
|
84
|
+
const errPtr = Module.getValue(errmsgPtr, 'i8*')
|
|
85
|
+
const err = Module.UTF8ToString(errPtr)
|
|
86
|
+
Module._free(errPtr)
|
|
87
|
+
Module._free(errmsgPtr)
|
|
88
|
+
Module._free(themePtr)
|
|
89
|
+
Module._free(resultPtr)
|
|
90
|
+
throw new Error(err)
|
|
91
|
+
}
|
|
92
|
+
const result = JSON.parse(Module.UTF8ToString(resultPtr)) as Record<string, Style>
|
|
93
|
+
|
|
94
|
+
Module._free(errmsgPtr)
|
|
95
|
+
Module._free(themePtr)
|
|
96
|
+
Module._free(resultPtr)
|
|
97
|
+
return new Theme(result)
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
/**
|
|
102
|
+
* Initialize the Wasm module.
|
|
103
|
+
*
|
|
104
|
+
* This function _must_ be called before any of the others
|
|
105
|
+
*/
|
|
106
|
+
export async function init(moduleOverrides?: any) {
|
|
107
|
+
if (Module === null) {
|
|
108
|
+
Module = await initModule(moduleOverrides)
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
function checkModule() {
|
|
113
|
+
if (Module === null) throw new Error('syntastica module is not initialized, call `init` first')
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
/**
|
|
117
|
+
* Load a language from a WebAssembly module.
|
|
118
|
+
*
|
|
119
|
+
* The module can be provided as a path to a file or as a buffer.
|
|
120
|
+
*/
|
|
121
|
+
export async function loadLanguage(input: string | Uint8Array): Promise<void> {
|
|
122
|
+
checkModule()
|
|
123
|
+
let bytes: Promise<Uint8Array>
|
|
124
|
+
if (input instanceof Uint8Array) {
|
|
125
|
+
bytes = Promise.resolve(input)
|
|
126
|
+
} else {
|
|
127
|
+
// @ts-expect-error
|
|
128
|
+
// eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
|
|
129
|
+
if (globalThis.process?.versions.node) {
|
|
130
|
+
// @ts-expect-error
|
|
131
|
+
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment, @typescript-eslint/no-require-imports
|
|
132
|
+
const fs: typeof import('fs/promises') = await import('fs/promises')
|
|
133
|
+
bytes = fs.readFile(input)
|
|
134
|
+
} else {
|
|
135
|
+
bytes = fetch(input)
|
|
136
|
+
.then(response =>
|
|
137
|
+
response.arrayBuffer()
|
|
138
|
+
.then(buffer => {
|
|
139
|
+
if (response.ok) {
|
|
140
|
+
return new Uint8Array(buffer)
|
|
141
|
+
} else {
|
|
142
|
+
const body = new TextDecoder('utf-8').decode(buffer)
|
|
143
|
+
throw new Error(`loadLanguage failed with status ${response.status}.\n\n${body}`)
|
|
144
|
+
}
|
|
145
|
+
})
|
|
146
|
+
)
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
const mod = await Module.loadWebAssemblyModule(await bytes, { loadAsync: true })
|
|
151
|
+
const symbolNames = Object.keys(mod)
|
|
152
|
+
const langFuncName = symbolNames.find(key => key.startsWith('verdant_lang_'))
|
|
153
|
+
if (!langFuncName) {
|
|
154
|
+
console.log(`Couldn't find language function in WASM file. Symbols:\n${JSON.stringify(symbolNames, null, 2)}`)
|
|
155
|
+
throw new Error('loadLanguage failed: no language function found in WASM file')
|
|
156
|
+
}
|
|
157
|
+
const langInfoPtr = mod[langFuncName]()
|
|
158
|
+
Module._add_language(langInfoPtr)
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
/**
|
|
162
|
+
* Highlight code and render to the requested format.
|
|
163
|
+
*
|
|
164
|
+
* If you plan to highlight the same input multiple times, use {@link process} and {@link render} instead.
|
|
165
|
+
*
|
|
166
|
+
* @param code - The code to highlight.
|
|
167
|
+
*
|
|
168
|
+
* @param language - The name of the code's language.
|
|
169
|
+
*
|
|
170
|
+
* The language must have been loaded previously by calling {@link loadLanguage}.
|
|
171
|
+
*
|
|
172
|
+
* @param theme - The name of the theme to use.
|
|
173
|
+
*
|
|
174
|
+
* All themes from {@link https://rubixdev.github.io/syntastica/syntastica_themes/ | the default collection}
|
|
175
|
+
* are supported. The theme name is equivalent to its Rust path specifier, so for example the gruvbox dark theme
|
|
176
|
+
* is named `gruvbox::dark`. Alternatively, a {@link Theme | custom theme} can be specified.
|
|
177
|
+
*
|
|
178
|
+
* @param renderer - The renderer to use.
|
|
179
|
+
*
|
|
180
|
+
* The renderer name is either `HTML` or `Terminal` in any casing. To specify a background color
|
|
181
|
+
* for the terminal renderer, append a hex color literal like `terminal#282828` or `Terminal#fff`.
|
|
182
|
+
*
|
|
183
|
+
* By default, the `HTML` renderer will be used.
|
|
184
|
+
*
|
|
185
|
+
* @returns The highlighted code as HTML code.
|
|
186
|
+
*
|
|
187
|
+
* See {@link https://rubixdev.github.io/syntastica/syntastica/renderer/struct.HtmlRenderer.html | here} for
|
|
188
|
+
* more information on the output.
|
|
189
|
+
*/
|
|
190
|
+
export function highlight(
|
|
191
|
+
code: string,
|
|
192
|
+
language: string,
|
|
193
|
+
theme: Theme | BuiltinTheme,
|
|
194
|
+
renderer: string = 'HTML',
|
|
195
|
+
): string {
|
|
196
|
+
checkModule()
|
|
197
|
+
const errmsgPtr = Module._malloc(PTR_SIZE)
|
|
198
|
+
const codePtr = Module.stringToNewUTF8(code)
|
|
199
|
+
const languagePtr = Module.stringToNewUTF8(language)
|
|
200
|
+
const themePtr = Module.stringToNewUTF8(
|
|
201
|
+
JSON.stringify(theme instanceof Theme ? { custom: theme.styles } : { builtin: theme }),
|
|
202
|
+
)
|
|
203
|
+
const rendererPtr = Module.stringToNewUTF8(renderer)
|
|
204
|
+
|
|
205
|
+
const resultPtr = Module._highlight(errmsgPtr, codePtr, languagePtr, themePtr, rendererPtr)
|
|
206
|
+
if (resultPtr === 0) {
|
|
207
|
+
const errPtr = Module.getValue(errmsgPtr, 'i8*')
|
|
208
|
+
const err = Module.UTF8ToString(errPtr)
|
|
209
|
+
Module._free(errPtr)
|
|
210
|
+
Module._free(errmsgPtr)
|
|
211
|
+
Module._free(codePtr)
|
|
212
|
+
Module._free(languagePtr)
|
|
213
|
+
Module._free(themePtr)
|
|
214
|
+
Module._free(rendererPtr)
|
|
215
|
+
Module._free(resultPtr)
|
|
216
|
+
throw new Error(err)
|
|
217
|
+
}
|
|
218
|
+
const result = Module.UTF8ToString(resultPtr)
|
|
219
|
+
|
|
220
|
+
Module._free(errmsgPtr)
|
|
221
|
+
Module._free(codePtr)
|
|
222
|
+
Module._free(languagePtr)
|
|
223
|
+
Module._free(themePtr)
|
|
224
|
+
Module._free(rendererPtr)
|
|
225
|
+
Module._free(resultPtr)
|
|
226
|
+
|
|
227
|
+
return result
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
/**
|
|
231
|
+
* Prepare code for rendering multiple times.
|
|
232
|
+
*
|
|
233
|
+
* @param code - The code to highlight.
|
|
234
|
+
*
|
|
235
|
+
* @param language - The name of the code's language.
|
|
236
|
+
*
|
|
237
|
+
* The language must have been loaded previously by calling {@link loadLanguage}.
|
|
238
|
+
*
|
|
239
|
+
* @returns Highlight information about the code to be used by {@link render}.
|
|
240
|
+
*/
|
|
241
|
+
export function process(code: string, language: string): Highlights {
|
|
242
|
+
checkModule()
|
|
243
|
+
const errmsgPtr = Module._malloc(PTR_SIZE)
|
|
244
|
+
const codePtr = Module.stringToNewUTF8(code)
|
|
245
|
+
const languagePtr = Module.stringToNewUTF8(language)
|
|
246
|
+
|
|
247
|
+
const resultPtr = Module._process(errmsgPtr, codePtr, languagePtr)
|
|
248
|
+
if (resultPtr === 0) {
|
|
249
|
+
const errPtr = Module.getValue(errmsgPtr, 'i8*')
|
|
250
|
+
const err = Module.UTF8ToString(errPtr)
|
|
251
|
+
Module._free(errPtr)
|
|
252
|
+
Module._free(errmsgPtr)
|
|
253
|
+
Module._free(codePtr)
|
|
254
|
+
Module._free(languagePtr)
|
|
255
|
+
Module._free(resultPtr)
|
|
256
|
+
throw new Error(err)
|
|
257
|
+
}
|
|
258
|
+
const result: Highlights = JSON.parse(Module.UTF8ToString(resultPtr))
|
|
259
|
+
|
|
260
|
+
Module._free(errmsgPtr)
|
|
261
|
+
Module._free(codePtr)
|
|
262
|
+
Module._free(languagePtr)
|
|
263
|
+
|
|
264
|
+
return result
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
/**
|
|
268
|
+
* Render code that was previously processed by calling {@link process}.
|
|
269
|
+
*
|
|
270
|
+
* @param highlights - The processed highlight information to render.
|
|
271
|
+
*
|
|
272
|
+
* @param theme - The name of the theme to use.
|
|
273
|
+
*
|
|
274
|
+
* All themes from {@link https://rubixdev.github.io/syntastica/syntastica_themes/ | the default collection}
|
|
275
|
+
* are supported. The theme name is equivalent to its Rust path specifier, so for example the gruvbox dark theme
|
|
276
|
+
* is named `gruvbox::dark`. Alternatively, a {@link Theme | custom theme} can be specified.
|
|
277
|
+
*
|
|
278
|
+
* @param renderer - The renderer to use.
|
|
279
|
+
*
|
|
280
|
+
* The renderer name is either `HTML` or `Terminal` in any casing. To specify a background color
|
|
281
|
+
* for the terminal renderer, append a hex color literal like `terminal#282828` or `Terminal#fff`.
|
|
282
|
+
*
|
|
283
|
+
* By default, the `HTML` renderer will be used.
|
|
284
|
+
*
|
|
285
|
+
* @returns The highlighted code in the requested format.
|
|
286
|
+
*/
|
|
287
|
+
export function render(highlights: Highlights, theme: Theme | BuiltinTheme, renderer: string = 'HTML'): string {
|
|
288
|
+
checkModule()
|
|
289
|
+
const errmsgPtr = Module._malloc(PTR_SIZE)
|
|
290
|
+
const themePtr = Module.stringToNewUTF8(
|
|
291
|
+
JSON.stringify(theme instanceof Theme ? { custom: theme.styles } : { builtin: theme }),
|
|
292
|
+
)
|
|
293
|
+
const rendererPtr = Module.stringToNewUTF8(renderer)
|
|
294
|
+
const highlightsPtr = Module.stringToNewUTF8(JSON.stringify(highlights))
|
|
295
|
+
|
|
296
|
+
const resultPtr = Module._render(errmsgPtr, highlightsPtr, themePtr, rendererPtr)
|
|
297
|
+
if (resultPtr === 0) {
|
|
298
|
+
const errPtr = Module.getValue(errmsgPtr, 'i8*')
|
|
299
|
+
const err = Module.UTF8ToString(errPtr)
|
|
300
|
+
Module._free(errPtr)
|
|
301
|
+
Module._free(errmsgPtr)
|
|
302
|
+
Module._free(themePtr)
|
|
303
|
+
Module._free(rendererPtr)
|
|
304
|
+
Module._free(highlightsPtr)
|
|
305
|
+
Module._free(resultPtr)
|
|
306
|
+
throw new Error(err)
|
|
307
|
+
}
|
|
308
|
+
const result = Module.UTF8ToString(resultPtr)
|
|
309
|
+
|
|
310
|
+
Module._free(errmsgPtr)
|
|
311
|
+
Module._free(themePtr)
|
|
312
|
+
Module._free(rendererPtr)
|
|
313
|
+
Module._free(highlightsPtr)
|
|
314
|
+
Module._free(resultPtr)
|
|
315
|
+
|
|
316
|
+
return result
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
export default { init, loadLanguage, highlight, process, render, Theme }
|
|
320
|
+
|
|
321
|
+
// DISCLAIMER: All code below this line is generated with `cargo xtask codegen js-list`
|
|
322
|
+
// in the syntastica workspace. Do not edit this code manually!
|
|
323
|
+
/**
|
|
324
|
+
* A list of all builtin themes.
|
|
325
|
+
*
|
|
326
|
+
* @see The {@link BuiltinTheme} type.
|
|
327
|
+
*/
|
|
328
|
+
export const BUILTIN_THEMES = [
|
|
329
|
+
'abscs::abscs',
|
|
330
|
+
'aurora::aurora',
|
|
331
|
+
'blue_moon::blue_moon',
|
|
332
|
+
'boo::boo',
|
|
333
|
+
'catppuccin::frappe',
|
|
334
|
+
'catppuccin::latte',
|
|
335
|
+
'catppuccin::macchiato',
|
|
336
|
+
'catppuccin::mocha',
|
|
337
|
+
'darcula::darcula',
|
|
338
|
+
'dracula::dracula',
|
|
339
|
+
'everblush::everblush',
|
|
340
|
+
'everforest::dark',
|
|
341
|
+
'everforest::light',
|
|
342
|
+
'falcon::falcon',
|
|
343
|
+
'github::dark',
|
|
344
|
+
'github::dark_colorblind',
|
|
345
|
+
'github::dark_default',
|
|
346
|
+
'github::dark_dimmed',
|
|
347
|
+
'github::dark_high_contrast',
|
|
348
|
+
'github::dark_tritanopia',
|
|
349
|
+
'github::light',
|
|
350
|
+
'github::light_colorblind',
|
|
351
|
+
'github::light_default',
|
|
352
|
+
'github::light_high_contrast',
|
|
353
|
+
'github::light_tritanopia',
|
|
354
|
+
'gruvbox::dark',
|
|
355
|
+
'gruvbox::light',
|
|
356
|
+
'melange::melange',
|
|
357
|
+
'minimal::minimal',
|
|
358
|
+
'monochrome::monochrome',
|
|
359
|
+
'monokai::monokai',
|
|
360
|
+
'monokai::pro',
|
|
361
|
+
'monokai::ristretto',
|
|
362
|
+
'monokai::soda',
|
|
363
|
+
'moonfly::moonfly',
|
|
364
|
+
'moonlight::moonlight',
|
|
365
|
+
'neon::dark',
|
|
366
|
+
'neon::default',
|
|
367
|
+
'neon::doom',
|
|
368
|
+
'neon::light',
|
|
369
|
+
'nightfly::nightfly',
|
|
370
|
+
'nord::nord',
|
|
371
|
+
'oceanicnext::dark',
|
|
372
|
+
'oceanicnext::light',
|
|
373
|
+
'omni::omni',
|
|
374
|
+
'one::cool',
|
|
375
|
+
'one::dark',
|
|
376
|
+
'one::darker',
|
|
377
|
+
'one::deep',
|
|
378
|
+
'one::light',
|
|
379
|
+
'one::warm',
|
|
380
|
+
'one::warmer',
|
|
381
|
+
'oxocarbon::dark',
|
|
382
|
+
'oxocarbon::light',
|
|
383
|
+
'solarized::dark',
|
|
384
|
+
'solarized::light',
|
|
385
|
+
'tokyo::day',
|
|
386
|
+
'tokyo::moon',
|
|
387
|
+
'tokyo::night',
|
|
388
|
+
'tokyo::storm',
|
|
389
|
+
'vscode::dark',
|
|
390
|
+
'vscode::light',
|
|
391
|
+
'zephyr::zephyr',
|
|
392
|
+
] as const
|