Skip to content
Ferriki

Beautiful code. Head start included.

TextMate syntax highlighting for Node.js, Rust and Vite. Keep the grammars and themes your editor uses, with a native engine and prepared language packages that arrive ready to work.

A head start, built in.

Standard language packages include compiled regex programs and prepared scanners. Restore that work on startup, then highlight with native Rust.

How the preparation travels
Version
1.3.0

One file. Three languages.

HTML, CSS and JavaScript share a page. Ferriki follows each language into the next, down to the template literal inside the script.

index.html · source
<!doctype html>
<html lang="en">
  <head>
    <style>
      .card {
        color: rebeccapurple;
      }
    </style>
  </head>
  <body>
    <button class="card" data-name="Ferriki">Say hello</button>
    <script type="module">
      const button = document.querySelector(".card");
      button.addEventListener("click", () => {
        const name = button.dataset.name ?? "you";
        alert(`Hello, ${name}!`);
      });
    </script>
  </body>
</html>
Ferriki v1.3.0 · github-dark
<!doctype html>
<html lang="en">
  <head>
    <style>
      .card {
        color: rebeccapurple;
      }
    </style>
  </head>
  <body>
    <button class="card" data-name="Ferriki">Say hello</button>
    <script type="module">
      const button = document.querySelector(".card");
      button.addEventListener("click", () => {
        const name = button.dataset.name ?? "you";
        alert(`Hello, ${name}!`);
      });
    </script>
  </body>
</html>

The highlighted result comes straight from Ferriki. Its tokenizer keeps each embedded language in context, so a string inside JavaScript can receive a different treatment from the surrounding markup.

We ship the preparation.

Standard language packages carry compiled regex programs and prepared static scanners. They are ready for production and enabled by default.

From a syntax definition to a reusable language package
  1. Compile before delivery
    Ferroni compiles static regex programs and prepares Eager scanner automata.
  2. Deliver the package
    Requested grammars arrive with their reusable programs and scanner preparation.
  3. Keep the prepared work
    Verified downloads and unpacked packages stay in your disk cache across builds.
  4. Restore, then highlight
    A fresh process restores the requested scanners and starts on your code.

Matching runs in Ferroni’s native, Oniguruma-compatible engine, without translating patterns to JavaScript RegExp. A fresh process restores the prepared work, rather than rebuilding every static scanner.

The preparation adds download weight: Rust needs about 58 kB in total versus 18 kB for its grammar alone. TypeScript with required embeddings needs 4.65 MB versus 0.54 MB; Markdown needs 1.97 MB versus 61 kB. Decimal payload sizes, excluding themes and HTTP overhead. Keep the disk cache to reuse downloads; decoded data also takes disk space. Compare the download sizes.

Missing packages fall back to source. Capture-dependent end and while patterns stay dynamic to preserve TextMate behavior. Only requested languages and required dependencies load; optional code-fence languages stay optional.

Sponsored by Sebastian Software, which currently covers Ferriki’s CDN costs.

Ready before the page ships.

Keep an example beside the React component it explains. During a Vite 8 build, Ferriki turns that example into highlighted HTML and CSS.

Answer.tsx · Vite input
import { Code } from "@ferriki/core/react/macro";

export function Answer() {
  return (
    <Code
      language="ts"
      source={`export const answer = 42;`}
    />
  );
}
Prepared TypeScript · github-dark
export const answer = 42;

The prepared block is the adapter’s actual output. Your readers receive the finished example; the browser does not need to run a highlighter. Inspect the full transformed module.

Style once. Switch themes freely.

Class-based output keeps token roles in the markup and theme rules in a stylesheet. Changing a theme does not mean highlighting the code again.

The same token elements, restyled
"hello"
{"message":"hello", "escaped":"line\n"}

Change the theme or enable the CSS override. The highlighted token elements stay in place.

Familiar calls. Native Rust underneath.

Start with Shiki-style HTML calls in Node.js, or use the same TextMate engine directly from Rust.

example.mjs
import { createHighlighter } from "@ferriki/core";

const highlighter = await createHighlighter({
  langs: ["typescript"],
  themes: ["github-dark"],
});
try {
  const html = highlighter.codeToHtml("const answer = 42;", {
    lang: "typescript", theme: "github-dark",
  });
  console.log(html);
} finally {
  highlighter.dispose();
}

The Rust crate’s remote feature loads the release-pinned grammar and theme catalog. Both entry points use the same native engine.

Spend less time rendering code.

Load a highlighter once, then keep using it. On our fourteen-file repository corpus, Ferriki delivered identical HTML to Shiki/WASM on both measured hosts.

With the languages and themes already loaded, repeated HTML calls were 3.7–4.4× faster than Shiki/WASM across Linux x86-64 and macOS ARM64. These are highlighting measurements, rather than total build times; another workload or machine can give different results.

Prepared packages also used about 45% less whole-process RSS after 600 documents across twelve loaded languages on both hosts. Six isolated processes per contender produced identical HTML. The detailed report keeps the startup cost, download weight and measurement conditions alongside the results.

Good company.

Use Ferriki on its own, or pair it with another tool from the workshop. Each engine has its own job and API.

Ardo

A documentation framework for React

Built for the code you publish.

TypeScript and TSX. Vue, Svelte, Astro and Angular. Rust, Python, Go and C/C++. TextMate grammars follow embedded languages and component expressions.

Shiki-compatible HTML API subset

Tested

codeToHtml, reusable highlighters, the singleton, the themes map, language aliases, transformers and decorations.

Standard grammars and themes

94 grammars · 65 themes

67 standard language and format entries, plus 27 support grammars for embeddings and injections. Required dependencies load with the host; optional code-fence languages stay optional.

Custom TextMate grammars and themes

Covered

Register your own JSON grammars and themes, with embedded languages and injections.

Class-based output

Covered

Nested scope classes and themes as a stylesheet, through styleMode: "classes" or codeToHtmlWithCss. Switch themes with data-ferriki-theme, without highlighting again.

Build-time macros

Vite 8

code() and <Code /> become HTML and CSS during the build through @ferriki/vite. Other build tools have no adapter.

Rust crate and Ferromark

Native

cargo add ferriki gives Rust the same engine, with no Node.js in the process. Ferromark highlights Markdown and MDX code fences with it natively.

Linux, macOS, Windows

Native

Linux x64 and arm64 (glibc and musl), macOS on Apple Silicon, Windows x64 and arm64, on Node.js 22.13 or newer.

Server and build time

By design

Render in Node.js or Rust at build time or on the server. Pages receive HTML and CSS ready to display, with highlighting handled before the code reaches the browser.

Give your next build a head start.

Choose a language and a theme. Ferriki brings the prepared scanners and turns your code into HTML and CSS, ready for the page.

npm · Crates.io · GitHub · Integration and support