3.8 KiB
Conventions
This file outlines the code conventions that we're applying to code within the generator modules. These conventions supplement the conventions in the contributing documentation.
Important
If you are using a programming assistant, please include this file in the prompt's context when generating code.
Modules
Every source folder is treated as a module, and must have a barrel file. It may import from its ancestors and its children. Modules should not import from their cousins or grandchildren. Do not import barrel files from parent scopes.
✅ Do:
import foo from "../../foo";
import bar from "../bar";
import baz from "../baz.ts";
import biz from "./biz";
❌ Do not:
import foo from "../";
import bar from "../../foo/bar";
import baz from "../bar/baz.ts";
import buz from "./baz/biz/buz";
Barrel files (ADR-0002)
The root barrel file contains all public exports of the module. All other barrel files are internal to the module. This is presently enforced with a lint.
Common files
The following files may be used to consolidate low-level module items.
data.ts<- constants; may only depend on external modules.index.ts<- exports; may define aggregates and apply type assertions to improve module DevEx.type.ts<- type definitions; may only depend ondata.tsand external modules.util.ts<- utility functions; may only depend ondata.ts,type.ts, and external modules.
Tip
Implementing the
const objectpattern (ADR-0025):
- Write the const object into
data.ts- Derive type definitions from the const object in
type.ts- Import types and data into
index.ts, perform type assertions, and re-export the data.
Rx (ADR-0015)
Reactive code should be written functionally, with liberal use of observable injection. Core rx code should not depend on services. It should depend solely on reactive objects and functions.
Services and other long-lived components compose the rx logic for injection into other contexts. They read observables from their dependencies and inject them into the Core rx code.
Logging
The generator's reactivity model is time-sensitive, which makes identifying and diagnosing runtime
behaviors difficult. Consider, for example, interactively debugging an observable subject to
timeout(). Because the computer's clock keeps running when the debugger is paused, stopping a
program subject to this operation can exhaust the timeout, resulting in
heisenbugs. The generator's permanent runtime logging
facilities decrease this complexity of debugging by writing structured logs using the
SemanticLogger.
When a generator creates a logger, it sets the log's type parameter. This can be filtered by
editing XYZ.
Caution
The
SemanticLoggerwrites arbitrary runtime information into the console. It is automatically disabled outside of development environments to mitigate data leaks.