Targets · JavaScript · Coding standards
Coding standards
The generated JavaScript is not free-form — it follows the same coding standards the Frenzi project holds its hand-written code to. There are two layers: a set of global rules that every target language obeys, and a set that is specific to JavaScript. Together they are why the output is plain, auditable, and sits line for line against the Program source it came from.
Contents
How a Program becomes JavaScript, in parts. Start with the introduction, then follow each part into the rendered output and the runtime it links against.
- Introduction — what the JavaScript target is, and how to read the rest of this section.
- Capabilities — which high-level features are possible, built in or through a library.
- Coding standards — the Frenzi coding standards the generated code follows, global and JavaScript-specific.
- Example — a full program using every keyword, and the JavaScript it renders to.
- Naming considerations — how Program's dotted names are carried into JavaScript.
- Entry point — the generated
main()that wires up the drivers and starts the reactive cycle. - Drivers — the native drivers that provide capabilities like the user interface and animation, draining each cycle's emissions.
- Runtime — the Reactive and Context machinery the rendered code links against.
- Native modules — the hand-written, per-platform JavaScript behind
NATIVEfunctions. - Keywords — how each of Program's keywords renders to JavaScript.
- Project setup — the files and commands to build and run the rendered program.
- Reserved words — the words JavaScript keeps for itself, and how a Program name that collides with one is escaped.
One statement per line, one name per result
The single rule that shapes the output more than any other: assign every result to a named local before you pass it on. A function call's arguments are only ever plain variables, constants, or closures — never another call nested inline. One operation per line; every step has a name.
// not allowed — a call nested inside another call's arguments
UiContent.Text(MathsInteger.ToString(count));
// the standard — each result gets its own variable, one call per line
var text = MathsInteger.ToString(count);
UiContent.Text(text);
This is exactly why the rendered code reads like the Program that produced it: Program itself has one
binding per step (LET), and the generator maps each binding to
one var and each call to one line. Nothing is ever folded together, so every intermediate
value is visible, nameable, and easy to step through in a debugger. A compound Program expression
becomes a straight run of named steps:
// Program: LET result = Process (Calculate input) offset ...etc
var calculatedValue = Calculate(input);
var adjustedValue = MathsInteger.Add(calculatedValue, offset);
var config = GetConfig();
var maxValue = Config.GetMaxValue(config);
var result = Process(adjustedValue, maxValue);
Global standards
These apply to every target language, so the same program produces structurally identical code in JavaScript, Dart, Swift and the rest. The ones that shape the JavaScript you see:
- One class or function per file. Each class and each standalone function lives in its own file, named exactly for what it contains. Index/entry files hold only includes and registration — never a definition.
- Data objects are immutable and hold only fields. No methods, not even serialisation — a record is data and nothing else. Anything about a record lives on a separate companion namespace (see TYPE).
- Constructors take positional arguments only — never a single options object, never named parameters. Optional fields become trailing optional positional parameters.
- Functions live in namespaces, never on data. A namespace is a bag of static functions and holds no instance state; a data object holds state and no functions. The two never mix.
- One statement per line, one name per result — the rule above.
- Optional values are named for it. A variable or field that may be absent is
prefixed
optional; a function that may return nothing starts withOptional(e.g.OptionalFind). - Security checks come first. Every function that takes reactive values validates
them with
ReactiveSecurity.Gettable(…)(orSettable/Binding) as the very first lines of the body — one per argument, before any other logic. - No native operators in reactive code. Inside reactive code a value may be a
loading or errored reactive value, so raw
+,==,if,&&and literals are avoided in favour of module functions (MathsInteger.Add,Text.Equal,Logic.If) that carry the loading/error state through. Native operators are confined to NATIVE bodies. - Line-for-line cross-platform congruence. The reactive calls must sit at exactly
the same positions in every target, because the runtime uses position to give each piece of
state a stable identity. A
Reactive.State()in the third slot must be the third slot everywhere; move it and its memory is lost. This is what keeps the JavaScript congruent to the Program — and to the Dart, Swift and PHP renders of the same program. - Context names are string literals. Channel names
(
'Ui.ComponentsContext','NetworkWeb.RequestContext') are written inline at each call, never stored in a constant.
JavaScript standards
On top of the global rules, the JavaScript target follows conventions chosen so the output is plain, broadly browser-compatible, and needs no build tooling to run:
- Standalone files, no module system. Rendered files use no
import,exportorrequire; each declares a global namespace object or a named function, and they are included with ordinary<script>tags in dependency order. See project setup. - Namespaces are nested objects; records are plain classes. A module renders to a
nested object (
var App = {}; App.Ui = {}; …) — see naming considerations — and a TYPE record to a plainclassholding only its fields. undefined, notnull, for an absent value in code (an absent option, a missing lookup). Values crossing the wire as JSON usenull; in rendered code the absent value isundefined.- Arrow functions for callbacks;
functionfor named things. Every closure — a HOLE body, a REVERSE body, a driver callback — is() => { … }. Named components and entry points keep thefunctionkeyword. - Assign to a
constbefore passing it. The JavaScript form of the one-name-per-result rule: a call's arguments areconstlocals, constants, or closures, one call per line. - Runtime type checks, not annotations. JavaScript is untyped, so validation is
done at runtime with
ReactiveSecurityguards andinstanceofrather than types. - Written for the baseline browser. The output avoids features that would narrow
browser support — no ES modules, no optional chaining
?., no nullish coalescing??, no private#fields, noasync/await(the reactive system is the async model). It uses classes,const/let, arrow functions, template literals and spread — features safe across current browsers. - Casing. Public members are
PascalCase(Reactive.State); private/generated members are_-prefixed (Reactive._runCallback) — and, because_is illegal in a Program identifier, the underscore is also what walls off generated helpers from anything the program can name.
Why it is written down here
An embedded Program is JavaScript you can open and read. Because it obeys these standards, that JavaScript is uniform from one program to the next: named intermediate values, positional constructors, namespaced functions, one operation per line. The pages that follow — keywords, runtime, drivers — all render to code in exactly this shape.