flash-theater-compiler v0.0.1 flash-theater-compiler
The .thr/.flsh → .xml/.brs compiler.
Parses with flash-parser's own vendored
grammar — never hand-parses BrightScript or XML itself — and generates static SceneGraph
markup plus generated observer wiring.
How it works
.thr source
field / derived / private-public function, template markup that is always valid XML.
flash-parser
Lossless CST + typed AST — the compiler's only parsing layer.
.xml + .brs
Static SceneGraph tree, generated init()/onChange handlers — zero vdom, zero diffing.
Real Roku device
Sideloaded and verified live — reactive fields update the screen with no hand-written observer.
Install
npm install --save-dev flash-theater-compilerRequires Node.js ≥ 24.
CLI
The primary way to use this package — compiles a whole project convention-based, like
tsc reading tsconfig.json,
no glob argument. Installing the package puts flash-theater on
your PATH (via npx flash-theater
or an npm script).
flash-theater compile [--check] [--src-dir <dir>] [--out-dir <dir>] [--env <name>]
flash-theater zip [--out-dir <dir>] [--env <name>] [--app-name <name>]compile
flash-theater compile
# Compiles every .thr/.flsh file under src/ into out/ (per
# flash-theater.config.json, or the srcDir/outDir defaults), copying
# every other src/ file (manifest, images/, source/Main.brs, ...)
# through untouched. Wipes and fully regenerates out/ every run.
flash-theater compile --check
# Same discovery/compile pass, but writes nothing — just reports
# "OK <path>" per file, or the first compile error. Use in CI.
flash-theater compile --src-dir client --out-dir build
# Override flash-theater.config.json's srcDir/outDir for this run.
flash-theater compile --env staging
# Loads environments/staging.config.json (+ an optional, git-ignored
# environments/staging.local.config.json layered on top), writes to
# out-staging/ instead of out/, and patches src/manifest's declared
# manifestOverrides into the copied manifest. Falls back to the
# FLASH_THEATER_ENV env var when --env isn't passed, so it flows
# through an npm script's "compile && zip" chain unchanged.zip
flash-theater zip
# Zips out/ into dist/<app-name>.zip — <app-name> defaults to the
# current directory's package.json "name" field, or its own directory
# name if there's no package.json.
flash-theater zip --app-name my-channel --out-dir build
# Explicit app name and a matching --out-dir (zip never reads srcDir —
# there's nothing under src/ it needs).
flash-theater zip --env staging
# Zips out-staging/ (matching a prior "compile --env staging") into
# dist/<app-name>-staging-<version>.zip.Configuration — flash-theater.config.json
The file itself is entirely optional — sibling of src/ and
out/. But once it exists, for any reason (even just to set
srcDir/outDir), designResolution
becomes mandatory in it — whether or not the project uses scale
anywhere. With no config file at all, defaults apply and using scale
anywhere becomes a compile error instead (dsl/scale-requires-config).
// flash-theater.config.json — sibling of src/, out/, package.json
// The file itself is entirely optional. But once it exists — for ANY
// reason, even just to set srcDir/outDir — "designResolution" becomes
// mandatory in it, whether or not this project uses "scale" anywhere.
{
"designResolution": "fhd", // "hd" | "fhd" — mandatory once this file exists
"srcDir": "src", // optional, defaults to "src"
"outDir": "out", // optional, defaults to "out"
"exclude": ["**/*.snap.thr"] // optional glob patterns, relative to srcDir
} environments/<name>.config.json (for --env)
is a separate, per-environment file — see
GRAMMAR.md's "Environments" section
for its own variables/manifestOverrides/exclude/include shape, and
GRAMMAR.md's "scale" section
for what designResolution actually controls.
Library API
For embedding the compiler directly — a build tool integration, a playground, a linter.
compileThrSource/compileFlshSource compile
one file in isolation and throw a CompileError on
any diagnostic (never a partial/best-effort result).
import { compileThrSource } from 'flash-theater-compiler';
import { CompileError } from 'flash-theater-compiler/dsl-ast';
try {
const { xml, brs, usesStore, usesFocusSystem, usesRouter } = compileThrSource(
`<script>
field count: integer = 0
derived doubled: integer = count * 2
</script>
<component>
<Label id="label" text="{doubled}" />
</component>`,
'DoubledCounter',
);
// xml — a static SceneGraph component definition
// brs — generated init()/setFields()/onChange handlers, no hand-written observers
// usesStore/usesFocusSystem/usesRouter/... — see "What compileThrSource does NOT do" below
} catch (err) {
if (err instanceof CompileError) {
// err.diagnostic — { code: string, message: string, span?: { line: number } }
console.error(`[${err.diagnostic.code}] ${err.diagnostic.message}`);
}
}What compileThrSource does NOT do
It does not copy the built-in runtime components (the focus manager, router, store, task
manager, or any of the Safe*/Scale/Stream/Http codegen
helpers) into your output — it only tells you which ones this one file needs, via the
usesStore/usesFocusSystem/usesRouter/usesTaskManager/usesComparisonHelper/usesSafeNotHelper/usesStreamHelper/usesHttpRequestHelper/usesScaleHelper/usesRelationalHelper
boolean flags on its return value. For a whole project's worth of files, compiled and wired
together the same way the CLI does it (this is exactly what runCompileCommand
calls), use compileApp(inputs, srcRoot, outRoot, config, envVariables) from
the same package instead — see
cli.ts
for the exact reference implementation (which runtime asset gets copied for which flag).
Browser-safe subpath
This site's own live playground on the homepage imports the compiler this way — no Node built-ins pulled into the browser bundle.
// A browser-safe subpath (no node:fs/node:path pulled in, unlike the
// package's main entry point which also re-exports the Node-only CLI) —
// what this site's own live playground imports.
import { compileThrSource } from 'flash-theater-compiler/compile';
import { CompileError } from 'flash-theater-compiler/dsl-ast';Documentation
- Getting started — project layout and the compile CLI.
- GRAMMAR.md — the exact grammar this package compiles.
- docs/features.md — full feature status.
- flash-parser — the parsing layer this compiler is built on.