Compiler
Print an IGraph as a standalone ES module, and migrate from compiler 1.x.
@hlang-org/compiler turns an IGraph into an ES module. It plans the graph with planGraph, builds an ESTree program from the plan and prints it. The module exports run(options).
import { mkdir, copyFile, writeFile } from 'node:fs/promises';
import { compile } from '@hlang-org/compiler';
import { graph } from './lib/graph.mjs';
// Each node code becomes an import from a module you choose.
const { code, imports } = compile(graph, {
nodes: {
greeting: { from: './nodes.mjs', name: 'Greeting' },
uppercase: { from: './nodes.mjs', name: 'Uppercase' },
print: { from: './nodes.mjs', name: 'Print' },
},
});
console.log(code);
console.log('imports:', imports.join(', '));
// Write the module next to the node classes and run it.
const dir = new URL('../../node_modules/.cache/hlang-examples/', import.meta.url);
await mkdir(dir, { recursive: true });
await copyFile(new URL('./lib/nodes.mjs', import.meta.url), new URL('nodes.mjs', dir));
await writeFile(new URL('program.mjs', dir), code);
const { run } = await import(new URL('program.mjs', dir).href);
const { dispose } = run();
dispose();The output below is the printed module, then the module's own output when the example imports and runs it:
// Generated by @hlang-org/compiler. Do not edit.
import {Flow} from "@hlang-org/runtime";
import {Subject} from "rxjs";
import {Greeting as Node0} from "./nodes.mjs";
import {Uppercase as Node1} from "./nodes.mjs";
import {Print as Node2} from "./nodes.mjs";
export function run(options = {}) {
const flowId = options.flowId ?? "flow-" + Date.now().toString(36);
const env = options.env ?? "prod";
const middleware = options.middleware ?? ({});
const stop = new Subject();
const stopCallbacks = [];
const signal = {
observable: stop.asObservable(),
onStop: callback => {
stopCallbacks.push(callback);
}
};
const nodes = {
a: new Node0({
nodeId: "a",
flowId,
env,
middleware,
config: {
params: {}
},
signal
}),
b: new Node1({
nodeId: "b",
flowId,
env,
middleware,
config: {
params: {}
},
signal
}),
c: new Node2({
nodeId: "c",
flowId,
env,
middleware,
config: {
params: {}
},
signal
})
};
if (options.onError) {
const onError = options.onError;
for (const nodeId of ["a", "b"]) {
nodes[nodeId].O("@node/error").receive(error => onError({
nodeId,
error
}));
}
}
nodes["a"].O("out").connect(nodes["b"].I("in"));
nodes["b"].O("out").connect(nodes["c"].I("in"));
const flow = new Flow();
flow.run(nodes["a"]);
return {
flowId,
nodes,
dispose: () => {
stop.next();
stop.complete();
for (const callback of stopCallbacks) {
callback();
}
}
};
}
imports: @hlang-org/runtime, rxjs, ./nodes.mjs, ./nodes.mjs, ./nodes.mjs
HELLO, HLANGcompile(graph, options)
| Option | Contents |
|---|---|
nodes | For every node code: { from, name? }, the module to import it from and its export name (the default export when name is omitted). |
runtime | The module that exports Flow. Defaults to @hlang-org/runtime. |
rxjs | The module that exports Subject. Defaults to rxjs. |
It returns { code, imports }: the module source and every module specifier it imports, in order.
The compiled run(options) accepts flowId, env (default 'prod'), middleware and onError, and returns { flowId, nodes, dispose }.
Errors
compile throws a GraphError for an invalid graph, the same as the interpreter, and a CompileError with one of these codes:
| Code | Meaning |
|---|---|
unresolved-node | A node code in the graph has no entry in options.nodes. |
invalid-import | An export name is not a valid JavaScript identifier. |
unserializable-config | A node's configuration holds a value that is not JSON (null, boolean, finite number, string, array or plain object), such as a function or a class instance. |
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 |
|---|---|
The 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. |
The complier export. | Removed. Use compile. |
The full API is in the compiler reference.
The examples on this page run in CI. Edit this page on GitHub