Back to Baml

@b/pkg-grammar

typescript2/pkg-grammar/README.md

0.226.15.0 KB
Original Source

@b/pkg-grammar

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.

The grammar family

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:

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.

Authoring

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:

sh
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.

Consumers

  • 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:

    • npm: the mirror's own publish workflow releases it as @boundaryml/baml-grammar.
    • GitHub Linguist vendors it as a submodule for .baml highlighting on github.com (picked up automatically at each Linguist release, roughly quarterly).
    • Shiki's grammar registry fetches the raw grammar from it weekly.
    • bat / Sublime Package Control consume grammars/baml.sublime-syntax (bat as a submodule pinned to the version tags).
    • KDE / Pandoc: 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.

Don't break github.com

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).