Build composable Camino packages with explicit type and source dependencies
This commit is contained in:
@@ -0,0 +1,88 @@
|
||||
import fs from "node:fs/promises";
|
||||
import path from "node:path";
|
||||
import { execFileSync } from "node:child_process";
|
||||
|
||||
export async function buildService({
|
||||
config,
|
||||
compiler,
|
||||
pkg,
|
||||
output,
|
||||
coreServer,
|
||||
sharedFiles,
|
||||
implementations,
|
||||
recordFile,
|
||||
}) {
|
||||
const relative = compiler.artifactPath;
|
||||
if (pkg.exports.length || config.service.entry) {
|
||||
await fs.mkdir(path.join(output, "bin"), { recursive: true });
|
||||
await fs.mkdir(path.join(output, "share/quixos/service"), { recursive: true });
|
||||
let serviceEntry;
|
||||
if (config.service.entry) {
|
||||
if (!/\.m?js$/.test(config.service.entry))
|
||||
throw Error("Custom service entry must be JavaScript; use a separate typed/language build before packaging");
|
||||
serviceEntry = path.join(config.source, relative(config.service.entry));
|
||||
} else {
|
||||
const modules = implementations.filter((implementation) => implementation.target === "server");
|
||||
if (pkg.exports.some((entry) => !modules.some((module) => module.exports.includes(entry.id))))
|
||||
throw Error("Generated service does not cover every package export; provide a custom service");
|
||||
if (!modules.length) throw Error("Generated service requires a checked registry");
|
||||
serviceEntry = path.resolve("service-entry.mjs");
|
||||
const imports = modules
|
||||
.map(
|
||||
(module, index) =>
|
||||
`import { ${module.registryExport} as registry${index} } from ${JSON.stringify(path.join(output, module.path))};`,
|
||||
)
|
||||
.join("\n");
|
||||
await fs.writeFile(
|
||||
serviceEntry,
|
||||
imports +
|
||||
`\nimport {servePortableRegistry} from ${JSON.stringify(config.serviceAdapter)};
|
||||
const registries = [${modules.map((_, index) => "registry" + index).join(",")}];
|
||||
const owners = new Map();
|
||||
for (const registry of registries) for (const id of registry.exports) {
|
||||
if (owners.has(id)) throw Error("Duplicate registry export " + id);
|
||||
owners.set(id, registry);
|
||||
}
|
||||
servePortableRegistry({exports: [...owners.keys()], invoke(request, channel, context) {
|
||||
const registry = owners.get(request.exportId);
|
||||
if (!registry) throw Error("Unknown export");
|
||||
return registry.invoke(request, channel, context);
|
||||
}});\n`,
|
||||
);
|
||||
}
|
||||
const serviceModule = "share/quixos/service/main.mjs";
|
||||
execFileSync(
|
||||
config.esbuild,
|
||||
[
|
||||
serviceEntry,
|
||||
"--bundle",
|
||||
"--platform=node",
|
||||
"--format=esm",
|
||||
"--minify-whitespace",
|
||||
"--target=es2024",
|
||||
`--external:${coreServer}`,
|
||||
`--outfile=${path.join(output, serviceModule)}`,
|
||||
],
|
||||
{ stdio: "inherit" },
|
||||
);
|
||||
await fs.writeFile(
|
||||
path.join(output, "bin/service"),
|
||||
`#!${config.shell}\nservice_root=\${0%/*}/..\nexec ${config.node} "$service_root/${serviceModule}" "$@"\n`,
|
||||
{ mode: 0o755 },
|
||||
);
|
||||
const serviceBody = {
|
||||
id: "service",
|
||||
target: "server",
|
||||
compatibility: "quixos-service-v1",
|
||||
path: "bin/service",
|
||||
ports: "quixos-transaction-ports-v1",
|
||||
exports: pkg.exports.map((e) => e.id),
|
||||
files: [
|
||||
await recordFile("bin/service", "text/x-shellscript"),
|
||||
await recordFile(serviceModule, "text/javascript"),
|
||||
...sharedFiles,
|
||||
],
|
||||
};
|
||||
implementations.unshift({ ...serviceBody, contentDigest: compiler.implementationDigest(serviceBody) });
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user