typescript2/pkg-grammar/README.md
Canonical TextMate grammar for the BAML language — the single source of truth for syntax highlighting, everywhere: in this repo, on npm, and on github.com.
The grammar is authored in TypeScript with
tmlanguage-generator
and compiled to JSON. You edit the typed sources, never the JSON.
src/baml.ts — the BAML grammar (source.baml), including prompt template
highlighting.language-configuration.json — brackets/comments/auto-close pairs for
editor integrations (this one is hand-edited; it has no typed source).syntaxes/baml.xml — hand-authored KDE KSyntaxHighlighting definition
(Kate, KDE apps, and Pandoc via skylighting). Rule-based, so it cannot be
generated from the TextMate source; update it alongside grammar changes.Generated artifacts (do not edit by hand):
baml.tmLanguage.json — emitted by the build, imported directly by consumers.baml.sublime-syntax — converted from the emitted JSON by
scripts/emit-sublime.ts; consumed by Sublime Text and syntect (bat,
delta).dist/index.js + dist/index.d.ts — ESM module with the grammar inlined as
a JS object literal, typed as a Shiki LanguageRegistration. This is what
npm consumers import, so they never need JSON import attributes
(with { type: "json" }), which some bundlers (Metro) can't parse.Ports of the grammar to engines that cannot consume TextMate live in sibling
packages, mirrored to their own read-only repos by the same
sync-grammar-mirror workflow:
pkg-grammar-hljs → BoundaryML/baml-highlightjs
(npm @boundaryml/baml-highlightjs) — highlight.js.pkg-grammar-treesitter → BoundaryML/baml-treesitter
— tree-sitter, for Neovim/Zed/Helix/Emacs.The conformance contract tying them together: every port's test suite runs
against this package's tests/fixtures/*.baml. When the language grows, add a
fixture here and every port's CI tells you which ports need updating.
tests/fixtures/showcase__golden_sample.baml is the canonical showcase sample
(shipped in the mirror as samples/baml.sample and used for registry
submissions); extend it when the language grows a new surface.
Add repository rules in src/*.ts and reference them from patterns with
{ include: "#<key>" }. tmlanguage-generator assembles the emitted
repository from every rule that carries a key and is reachable from
patterns. Then regenerate:
pnpm --filter @b/pkg-grammar build
build runs the generator (scripts/build.ts) and then sync to propagate
the JSON and language-configuration.json into the app-vscode-ext mirror.
app-promptfiddle / pkg-editor import the JSON directly
(import('@b/pkg-grammar/baml.tmLanguage.json')) — no local copy.
app-vscode-ext keeps committed mirrors (syntaxes/baml.tmLanguage.json,
language-configuration.json) because VS Code loads them from the physical
files named in contributes and the extension ships bundled (no
node_modules in the .vsix). The mirrors are regenerated by build and a
pre-commit hook (pnpm --filter @b/pkg-grammar check) fails if they drift.
BoundaryML/textMate-baml
is an external mirror assembled by scripts/assemble-mirror.mjs from the
templates in mirror/ and pushed by the sync-grammar-mirror workflow on
every grammar change on canary. It is never edited by hand. Every content
change gets a patch version and a v<version> git tag. It serves several
consumers at once:
@boundaryml/baml-grammar..baml highlighting on
github.com (picked up automatically at each Linguist release, roughly
quarterly).grammars/baml.sublime-syntax
(bat as a submodule pinned to the version tags).syntaxes/baml.xml is the staging copy for the
KSyntaxHighlighting upstream submission.The mirror's file paths and scopeName: source.baml are frozen API —
external registries fetch them by URL; renames are breaking changes.
Linguist compiles every vendored grammar with its own compiler, which converts
each Oniguruma regex to PCRE — regexes that work in Shiki/VS Code can fail
there. The linguist-compile-gate CI job runs that exact compiler against the
freshly built grammar, so a grammar change that would break github.com fails
the PR instead of surfacing months later at a Linguist release. Two invariants
Linguist pins that must never change: scopeName: source.baml and the .baml
file type (guarded in tests/package-artifacts.test.ts).