@hlang-tech/compiler
Compiles an hlang program (IGraph) ahead of time into a standalone ES module. The module runs the graph with the same behavior as @hlang-tech/interpreter, without the interpreter or the graph JSON at run time.
Install
npm install -D @hlang-tech/compiler
The same build is published as @hlang-org/compiler. ESM and CommonJS builds with type declarations.
Usage
import { writeFile } from 'node:fs/promises';
import { compile } from '@hlang-tech/compiler';
const { code, imports } = compile(graph, {
nodes: {
numbers: { from: './nodes/numbers.js' }, // default export
double: { from: '@acme/hlang-nodes', name: 'Double' }, // named export
log: { from: './nodes/log.js' },
},
});
await writeFile('program.mjs', code);
// `imports` lists every module the program imports, for example
// ['@hlang-tech/runtime', 'rxjs', './nodes/numbers.js', '@acme/hlang-nodes', './nodes/log.js'].
Run the compiled program:
import { run } from './program.mjs';
const handle = run({ env: 'prod', onError: ({ nodeId, error }) => console.error(nodeId, error) });
handle.nodes.numbers; // the constructed node
handle.dispose(); // stop long-running nodes
compile is a pure function. It writes no files and installs nothing.
Generated module
compile validates the graph with planGraph from @hlang-tech/ir, builds an ESTree program, and prints it with astring. Values are emitted as literals by the printer, so the output is valid JavaScript for any node id or parameter. The same graph always produces the same text.
// Generated by @hlang-tech/compiler. Do not edit.
import {Flow} from "@hlang-tech/runtime";
import {Subject} from "rxjs";
import Node0 from "./nodes/numbers.js";
// ...
export function run(options = {}) {
const flowId = options.flowId ?? "flow-" + Date.now().toString(36);
// ...
const nodes = {
numbers: new Node0({ nodeId: "numbers", flowId, env, middleware, config: { params: {} }, signal }),
// ...
};
nodes["numbers"].O("out").connect(nodes["double"].I("in"));
const flow = new Flow();
flow.run(nodes["numbers"]);
return { flowId, nodes, dispose: () => { /* ... */ } };
}
run(options) accepts flowId, env (default 'prod'), middleware, and onError. It returns { flowId, nodes, dispose }.
API
compile(graph, options)
| Option | Description |
|---|---|
nodes | Required. Maps each node code to { from, name? }: the module specifier and the export name (default export when name is omitted). |
runtime | Module that exports Flow. Default '@hlang-tech/runtime'. |
rxjs | Module that exports Subject. Default 'rxjs'. |
Returns { code, imports }.
Errors
GraphError(re-exported from@hlang-tech/ir) for an invalid graph.CompileErrorwithcode:unresolved-node: a nodecodehas no entry inoptions.nodes.invalid-import: an export name is not a valid identifier.unserializable-config: abizDatavalue is not JSON (for exampleundefined,NaN, a function, or aDate). The message names the path.
Migrating from 1.x
Version 2 is a rewrite. 1.x remains on npm, and ^1.0.0 ranges do not resolve to 2.x.
| 1.x | 2.x |
|---|---|
Default export build(graph) wrote a project to the temp directory and ran npm install. | compile(graph, options) returns { code, imports }. Write and install them yourself. |
Node modules came from each node's runtimeResource. | Map each code in options.nodes. |
The output depended on @hset/xlang-runtime-middleware, lodash, and child-process IPC. | The output depends only on the runtime, RxJS, and your node modules. Pass services as middleware. |
complier export. | Removed. Use compile. |
Source and documentation: github.com/hlang-tech/hlang.
License
MIT