Skip to content

Commit

Permalink
feat(engine-js): improve performance for some languages (#897)
Browse files Browse the repository at this point in the history
  • Loading branch information
slevithan authored Jan 20, 2025
1 parent 97f7369 commit e86ca5f
Show file tree
Hide file tree
Showing 7 changed files with 54 additions and 53 deletions.
18 changes: 9 additions & 9 deletions docs/guide/best-performance.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ outline: deep

# Best Performance Practices

This guide will help you to improve the performance your Shiki usage.
This guide will help you to improve the performance of your Shiki usage.

## Cache the Highlighter Instance

Expand All @@ -23,19 +23,19 @@ export async function highlightCode(code: string, lang: string) {
}
```

When you no longer need a highlighter instance, you can call `dispose()` method to release the resources. (It can't not be GC-ed automatically, you need to do it explicitly)
When you no longer need a highlighter instance, you can call the `dispose()` method to release the resources. (It can't be GC-ed automatically, you need to do it explicitly.)

```ts
highlighter.dispose()
```

## Fine-Grained Bundle

The pre-built bundles are for easy usage, and mostly intended for Node.js environment where you don't worry too much about the bundle size. If you are building a web application, or in a resource-constrained environment, it's always better to use the fine-grained bundles to reduce the bundle size and memory usage.
The pre-built bundles are for easy usage, and mostly intended for a Node.js environment where you aren't worried about the bundle size. If you are building a web application or in a resource-constrained environment, it's always better to use the fine-grained bundles to reduce the bundle size and memory usage.

**Avoid importing `shiki`, `shiki/bundle/full`, `shiki/bundle/web` directly**.

Instead, import the fine-grained modules like `shiki/core`, `shiki/engine/javascript`, `@shikijs/langs/typescript`, `@shikijs/themes/dark-plus`, etc.
Instead, import fine-grained modules like `shiki/core`, `shiki/engine/javascript`, `@shikijs/langs/typescript`, `@shikijs/themes/dark-plus`, etc.

```ts
import { createHighlighterCore } from 'shiki/core'
Expand All @@ -56,13 +56,13 @@ const highlighter = await createHighlighterCore({
})
```

To compose the fine-grained bundles easily, we also provide the [`shiki-codegen`](/packages/codegen) tool to generate the fine-grained bundles for you.
To compose the fine-grained bundles easily, we also provide the [`shiki-codegen`](/packages/codegen) tool to generate fine-grained bundles for you.

Learn more about [Fine-Grained Bundles](/guide/bundles#fine-grained-bundle).

## Use Shorthands

`createHighlighter` and `createHighlighterCore` loads all the themes and languages **upfront** to ensure the subsequent highlight operations are synchronous. This can add a significant overhead to the startup time specially when you have a lot of themes and languages. Shorthands abstract the theme and language loading process and maintains an internal highlighter instance underneath, and only load the necessary themes and languages when needed. When your highlight process can be asynchronous, you can use the shorthands to reduce the startup time.
`createHighlighter` and `createHighlighterCore` load all the themes and languages **upfront** to ensure subsequent highlight operations are synchronous. This can add significant overhead to startup time, especially when you have a lot of themes and languages. Shorthands abstract the theme and language loading process and maintain an internal highlighter instance underneath, only loading the necessary themes and languages when needed. When your highlighting process can be asynchronous, you can use shorthands to reduce startup time.

```ts
import { codeToHtml } from 'shiki'
Expand All @@ -78,15 +78,15 @@ You can also create your own shorthands with fine-grained bundles. Check out the

## JavaScript Engine and Pre-compiled Languages

Shiki provides [two engines](/guide/regex-engines) for executing Regular Expressions: [`JavaScript`](/guide/regex-engines#javascript-regexp-engine) and [`Oniguruma`](/guide/regex-engines#oniguruma-engine). The Oniguruma engine is a WebAssembly-based that compiled from C++ code, and `JavaScript` is a pure JavaScript engine that translates Oniguruma-flavored regex to JavaScript regex.
Shiki provides [two engines](/guide/regex-engines) for executing regular expressions: [`JavaScript`](/guide/regex-engines#javascript-regexp-engine) and [`Oniguruma`](/guide/regex-engines#oniguruma-engine). The Oniguruma engine is WebAssembly-based and compiled from C code, and `JavaScript` is a pure JavaScript engine that translates Oniguruma-flavored regexes to JavaScript regexes.

If you are bundling Shiki for the web, using the JavaScript engine would be smaller in bundle size and faster in startup time. Meanwhile, the [precompiled languages](/guide/regex-engines#pre-compiled-languages) can also reduce the bundle size and startup time, if your target browsers support the latest RegExp features.
If you are bundling Shiki for the web, using the JavaScript engine results in a smaller bundle size and faster startup time. The [precompiled languages](/guide/regex-engines#pre-compiled-languages) can further reduce bundle size and startup time, if your target browsers support the latest RegExp features.

Check the [RegExp Engines](/guide/regex-engines) guide for more details.

## Use Workers

Shiki hightlights the code using Regular Expressions, which can be CPU-intensive. You can offload the highlighting work to a Web Worker/Node Worker to avoid blocking the main thread.
Shiki hightlights code using regular expressions, which can be CPU-intensive. You can offload the highlighting work to a Web Worker/Node Worker to avoid blocking the main thread.

::: info

Expand Down
10 changes: 5 additions & 5 deletions docs/guide/regex-engines.md
Original file line number Diff line number Diff line change
Expand Up @@ -85,13 +85,13 @@ const jsEngine = createJavaScriptRegexEngine({

### Pre-compiled Languages

Instead of compiling the regular expressions at on-the-fly, we also provided pre-compiled languages for the JavaScript engine to further reduce the startup time.
Instead of compiling regular expressions on-the-fly, we also provide pre-compiled languages for the JavaScript engine to further reduce startup time.

::: info
Pre-compiled languages requires RegExp uncoide sets support (`v` flag), which is targeting **ES2024** or Node.js 20+, and may not work in older environments. [Can I use](https://caniuse.com/mdn-javascript_builtins_regexp_unicode).
Pre-compiled languages require support for RegExp UnicodeSets (the `v` flag), which requires **ES2024** or Node.js 20+, and may not work in older environments. [Can I use](https://caniuse.com/mdn-javascript_builtins_regexp_unicodesets).
:::

You can install them with `@shikijs/langs-precompiled`, and change your `@shikijs/langs` imports to `@shikijs/langs-precompiled`:
You can install them with `@shikijs/langs-precompiled`, and then change your `@shikijs/langs` imports to `@shikijs/langs-precompiled`:

```ts
import { createHighlighterCore } from 'shiki/core'
Expand All @@ -113,6 +113,6 @@ const highlighter = await createHighlighterCore({
})
```

If you are not using the custom grammars that requires transpilation, you can use the `createJavaScriptRawEngine` to skip the transpilation step further reducing bundle size.
If you are not using custom grammars that require transpilation, you can use `createJavaScriptRawEngine` to skip the transpilation step, further reducing bundle size.

If you are using [`shiki-codegen`](/packages/codegen), you can generate the pre-compiled languages with the `--precompiled` and `--engine=javascript-raw` flags.
If you are using [`shiki-codegen`](/packages/codegen), you can generate pre-compiled languages with the `--precompiled` and `--engine=javascript-raw` flags.
6 changes: 3 additions & 3 deletions docs/references/engine-js-compat.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,9 @@

Compatibility reference of all built-in grammars with the [JavaScript RegExp engine](/guide/regex-engines#javascript-regexp-engine).

> Generated on Tuesday, January 14, 2025
> Generated on Monday, January 20, 2025
>
> Version `1.27.0`
> Version `1.27.2`
>
> Runtime: Node.js v22.11.0
Expand Down Expand Up @@ -262,5 +262,5 @@ Languages that throw with the JavaScript RegExp engine, either because they cont
| sass | ✅ OK | 67 | 2 | |
| purescript | ❌ Error | 72 | 1 | |
| csharp | ❌ Error | 310 | 3 | 137 |
| razor | ❌ Error | 959 | 3 | |
| swift | ❌ Error | 326 | 3 | |
| razor | ❌ Error | 957 | 5 | |
3 changes: 3 additions & 0 deletions packages/engine-javascript/src/engine-compile.ts
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,9 @@ export function defaultJavaScriptRegexConstructor(pattern: string, options?: Oni
// Oniguruma uses depth limit `20`; lowered here to keep regexes shorter and maybe
// sometimes faster, but can be increased if issues reported due to low limit
recursionLimit: 5,
// Oniguruma option for `^`->`\A`, `$`->`\Z`; improves search performance without any
// change in meaning since TM grammars search line by line
singleline: true,
},
...options,
},
Expand Down
Loading

0 comments on commit e86ca5f

Please sign in to comment.