@ryanatkn/fuz_code
v0.21.0
Published
syntax styling utilities and components for TypeScript, Svelte, SvelteKit
Downloads
74
Readme
@ryanatkn/fuz_code
syntax styling utilities and components for TypeScript, Svelte, SvelteKit 🎨
fuz_code is a rework of Prism. The main changes:
- has a minimal and explicit API to generate stylized HTML, and knows nothing about the DOM
- uses stateless ES modules, instead of globals with side effects and pseudo-module behaviors
- has various incompatible changes, so using Prism grammars requires some tweaks
- smaller (by 7kB minified and 3kB gzipped, ~1/3 less) and faster
- written in TypeScript
Like Prism, there are zero dependencies (unless you count Prism's @types/prismjs
),
but there are two optional dependencies,
Svelte and
the color-scheme
-adaptive colors from Moss.
fuz_code provides optional builtin Svelte support
with a Svelte grammar
based on prism-svelte
and a Svelte component for convenient usage.
The default theme integrates with my CSS library Moss. A zero-dependency fuz_code theme is also provided with some caveats, see below for more.
Compared to Shiki,
this library is much lighter
(with its faster shiki/engine/javascript
, 503kB minified to 16kB, 63kb gzipped to 5.6kB),
and vastly faster
for runtime usage because it uses JS regexps instead of
the Onigurama regexp engine
used by TextMate grammars.
Shiki also has 38 dependencies instead of 0.
However this is not a fair comparison because Shiki is designed mainly for buildtime usage,
and Prism grammars are much simpler and less powerful than TextMate's.
Usage
npm i -D @ryanatkn/fuz_code
import {syntax_styler} from '@ryanatkn/fuz_code';
syntax_styler.stylize('<h1>hello world</h1>', 'svelte');
Themes are just CSS files, so they work with any JS framework.
With SvelteKit:
// +layout.svelte
import '@ryanatkn/fuz_code/theme.css';
The primary themes (currently just one) have a dependency on my CSS library Moss for color-scheme awareness. See the Moss docs for its usage.
A dependency-free version of the initial theme is provided,
but note that the colors are staticly defined instead of using
Moss' style variables.
They use light-dark()
which means they're hardcoded to the inferred color-scheme
,
rather than being settable by a user option unlike the Moss version.
// no dependencies:
import '@ryanatkn/fuz_code/theme_standalone.css';
Modules
- @ryanatkn/fuz_code - index with default grammars, use this as a guide if you want custom grammars
- @ryanatkn/fuz_code/syntax_styler.js - utilities for custom grammars
- @ryanatkn/fuz_code/theme.css - default theme that depends on Moss
- @ryanatkn/fuz_code/theme_standalone.css - default theme with no dependencies
- @ryanatkn/fuz_code/Code.svelte - Svelte component with a convenient API
I encourage you to poke around src/lib
if you're interested in using fuz_code.
Grammars
Enabled by default:
More
Docs are a work in progress:
- this readme has basic usage instructions
- code.fuz.dev has usage examples with the Svelte component
- samples on the website (also see the inputs and outputs)
- tests
Please open issues if you need any help.
Todo
- add builtin grammars for
markdown
andregexp
- lazy load the builtin grammars in
Code.svelte
- improve the default theme colors
- add more themes
- add a Vite plugin to do syntax styling at build-time for static cases
- add a worker helper module
- add some useful plugins, flesh out the API (start with line emphasis)
- improve the TypeScript grammar to tokenize types
- improve the grammars in subtle ways
Benchmarks
Performance is a high priority to best support runtime usage. This project is still early and there are more gains to be had.
Note that this benchmark is somewhat unfair to Shiki because it's not designed for runtime usage, and it probably does a significantly better job at the task at hand because it uses TextMate grammars.
Results styling the Svelte sample:
| Task name | Throughput average (ops/s) | Throughput median (ops/s) | Samples | | ----------------------- | -------------------------- | ------------------------- | ------- | | syntax_styler.stylize | 3149 ± 0.56% | 3333 | 6004 | | Prism.highlight | 2748 ± 0.51% | 2500 | 5293 | | Shiki engine/javascript | 69 ± 0.59% | 69 | 138 | | Shiki engine/oniguruma | 41 ± 0.27% | 40 | 82 |
Directly runnable benchmarks are not included yet - I don't know if I'll add them here or make a separate project.
To run the benchmarks yourself:
npm i -D shiki prismjs prism-svelte @types/prismjs @ryanatkn/fuz_code
Then add this file and import it somewhere like $routes/+page.svelte
:
// $lib/benchmark.ts
import {Bench} from 'tinybench';
import Prism from 'prismjs';
import 'prism-svelte';
import {createHighlighterCoreSync} from 'shiki/core';
import {createJavaScriptRegexEngine} from 'shiki/engine/javascript';
import svelte_shiki from 'shiki/langs/svelte.mjs';
import nord from 'shiki/themes/nord.mjs';
import {createOnigurumaEngine} from 'shiki/engine/oniguruma';
import {syntax_styler} from '$lib/index.js';
import {sample_svelte_code} from '$lib/code_sample_inputs.js';
console.log('benchmarking');
const bench = new Bench({name: 'syntax styling', time: 2000});
const shiki_highlighter_js = createHighlighterCoreSync({
themes: [nord],
langs: [svelte_shiki],
engine: createJavaScriptRegexEngine(),
});
const shiki_highlighter_builtin = createHighlighterCoreSync({
themes: [nord],
langs: [svelte_shiki],
engine: await createOnigurumaEngine(import('shiki/wasm')),
});
bench
.add('syntax_styler.stylize', () => {
syntax_styler.stylize(sample_svelte_code, 'svelte');
})
.add('Prism.highlight', () => {
Prism.highlight(sample_svelte_code, Prism.langs.svelte, 'svelte');
})
.add('Shiki engine/javascript', () => {
shiki_highlighter_js.codeToHtml(sample_svelte_code, {lang: 'svelte', theme: 'nord'});
})
.add('Shiki engine/oniguruma', () => {
shiki_highlighter_builtin.codeToHtml(sample_svelte_code, {lang: 'svelte', theme: 'nord'});
});
await bench.run();
console.log(bench.name);
console.table(bench.table());
License 🐦
the Svelte grammar
is based on prism-svelte
by @pngwn