LumisDocs
Advanced

Declaring parsers

Parsers are dependencies. How each runtime declares the languages it may load, and why nothing is fetched at runtime.

A parser is a dependency. You add it, your package manager pins it, and it is there — or you did not add it, and it is not.

Declaring parsers makes output reproducible. The set is in a file you already review, pinned by a lock you already have, so every machine renders the same document with the same parsers, and the lock records which parser produced it.

For an Elixir application that downloaded parsers before 0.9, see Upgrading to 0.9.

One rule, one place to look

Every runtime resolves parsers the same way: only the ones the project installed, with no fallback that fetches anything else. A parser is a dependency, declared where that runtime's dependencies already live.

RuntimeDeclares its parsers inPinned by
RustCargo.toml featuresCargo.lock
JavaScript / TypeScriptpackage.json dependenciesyour package manager's lockfile
JavaScript in a browserthe packages it importsthe lockfile behind your bundle
Elixirmix.exs dependenciesmix.lock
CLInothing—

Rust links parsers statically. JavaScript installs @lumis-sh/wasm-*, and a browser, which has no project to read, declares by what it imports. Elixir depends on lumis_wasm_*, whose priv/parsers holds the bytes — so they travel into a release like any other dependency's assets, with no data directory to prepare and nothing to copy.

The CLI is the exception, deliberately. It has no project and downloads what it needs, because a viewer is not the application whose output a declaration governs, and a highlighter that behaved differently depending on the directory you ran it in would be worse than one that does not.

Add the parsers you want to mix.exs. There are no Lumis-specific commands: mix deps.get, mix deps.update and mix.lock already are them.

mix.exs
defp deps do
  [
    {:lumis, "~> 0.10"},
    {:lumis_wasm_rust, "~> 0.26.0"},
    {:lumis_wasm_bundle_web, "~> 0.1"}
  ]
end

A bundle is a package whose dependencies are its members, so it widens when it gains a language and mix deps.update picks that up — a visible mix.lock diff, not a silent change.

Move a pinned version
mix deps.update lumis_wasm_rust
Stop using it
# remove the line from mix.exs, then
mix deps.unlock --unused

A project that vendors parsers rather than depending on them can point at the directories instead. Same layout; only how they got there differs.

config/config.exs
config :lumis, parser_dirs: ["priv/parsers"]

What a language you did not add does

Naming a language explicitly is your decision, so one you never depended on is an error rather than a silent download:

Lumis.Languages.load("haskell")
#=> {:error, :not_installed}

A language an injection names is content rather than a decision — a fenced block in a README, say — so that block renders plain and the document is fine. A missing parser costs one block, not the page.

Releases

Nothing to prepare. Parsers are dependencies, so mix release carries them the way it carries any dependency's priv/:

_build/prod/rel/my_app/lib/lumis_wasm_elixir-0.26.3/priv/parsers/
  elixir.lumis.json
  tree-sitter-elixir-0.26.3-....wasm

The released node reads them from there. There is no data directory to stage, no copy step, and no network at boot or at render time.

config :lumis, :data_dir still has a job, a smaller one: it holds the compiled WASM module cache, which is a speed-up rather than a source. Point it somewhere writable and persistent and a restart skips recompiling; lose it and the first render of each language is slower and nothing else changes.

Verification

npm and Hex check every package they fetch against its published checksum, which your lockfile records, so a parser that arrives through a package manager is already verified. Lumis loads it as installed, beside the lumis.json that describes it, and refuses a package outside the Tree-sitter series this Lumis speaks. It checks a parser against the SHA-256 in lumis.json only when it downloads the bytes itself, through a resolver or the CLI.

On this page