Dev Build / cleanup (push) Canceled after 0s
Dev Build / typecheck (push) Canceled after 0s
Dev Build / build (linux-arm64, ubuntu-24.04-arm, linux-arm64) (push) Canceled after 0s
Dev Build / build (linux-x64, ubuntu-latest, linux-x64) (push) Canceled after 0s
Dev Build / build (win-x64, windows-latest, win32-x64) (push) Canceled after 0s
Dev Build / dispatch-dev-docker (push) Canceled after 0s
53 lines
1.5 KiB
Plaintext
53 lines
1.5 KiB
Plaintext
type ModeWriter = (doc: Doc, modes: { execution: "sync" | "async" }) => void;
|
|
|
|
export class Doc {
|
|
args: string[];
|
|
/** Bindings the compiled function closes over, by name. */
|
|
closed: Record<string, unknown>;
|
|
content: string[] = [];
|
|
indent = 0;
|
|
|
|
constructor(args: string[] = [], closed: Record<string, unknown> = {}) {
|
|
this.args = args;
|
|
this.closed = closed;
|
|
}
|
|
|
|
// the compiler catches a child's throw and keeps writing into this doc, so the indent has to unwind with it
|
|
indented(fn: (doc: Doc) => void) {
|
|
this.indent += 1;
|
|
try {
|
|
fn(this);
|
|
} finally {
|
|
this.indent -= 1;
|
|
}
|
|
}
|
|
|
|
write(fn: ModeWriter): void;
|
|
write(line: string): void;
|
|
write(arg: any) {
|
|
if (typeof arg === "function") {
|
|
(arg as ModeWriter)(this, { execution: "sync" });
|
|
(arg as ModeWriter)(this, { execution: "async" });
|
|
return;
|
|
}
|
|
|
|
const content = arg as string;
|
|
const lines = content.split("\n").filter((x) => x);
|
|
const minIndent = Math.min(...lines.map((x) => x.length - x.trimStart().length));
|
|
const dedented = lines.map((x) => x.slice(minIndent)).map((x) => " ".repeat(this.indent * 2) + x);
|
|
for (const line of dedented) {
|
|
this.content.push(line);
|
|
}
|
|
}
|
|
|
|
compile(): any {
|
|
const F = Function;
|
|
const content = this?.content ?? [``];
|
|
const factory = new F(
|
|
...Object.keys(this.closed),
|
|
`return function (${this.args.join(", ")}) {\n${content.join("\n")}\n};`
|
|
);
|
|
return factory(...Object.values(this.closed)) as any;
|
|
}
|
|
}
|