Files
cadSet/text-to-cad/skills/cad-router/scripts/cadam_compile.mjs
T
2026-07-22 19:38:36 +08:00

108 lines
3.6 KiB
JavaScript

#!/usr/bin/env node
import fs from "node:fs";
import path from "node:path";
import { fileURLToPath, pathToFileURL } from "node:url";
function usage() {
return "Usage: node cadam_compile.mjs <input.scad> <output.stl|output.dxf> [--cadam-root <path>]";
}
function parseArgs(argv) {
const positional = [];
let cadamRoot = String(process.env.CADAM_ROOT || "").trim();
for (let index = 0; index < argv.length; index += 1) {
const value = argv[index];
if (value === "--cadam-root") {
cadamRoot = String(argv[index + 1] || "").trim();
index += 1;
} else {
positional.push(value);
}
}
if (positional.length !== 2) {
throw new Error(usage());
}
return { inputPath: path.resolve(positional[0]), outputPath: path.resolve(positional[1]), cadamRoot };
}
function defaultCadamRoot() {
const scriptDirectory = path.dirname(fileURLToPath(import.meta.url));
return path.resolve(scriptDirectory, "../../../../CADAM");
}
function runtimePaths(configuredRoot) {
const root = path.resolve(configuredRoot || defaultCadamRoot());
const runtimeDirectory = path.join(root, "src", "vendor", "openscad-wasm");
const modulePath = path.join(runtimeDirectory, "openscad.js");
const wasmPath = path.join(runtimeDirectory, "openscad.wasm");
if (!fs.existsSync(modulePath) || !fs.existsSync(wasmPath)) {
throw new Error(
`CADAM OpenSCAD WASM runtime not found under ${runtimeDirectory}. ` +
"Set CADAM_ROOT or pass --cadam-root to an installed CADAM checkout."
);
}
return { root, modulePath, wasmPath };
}
async function compile({ inputPath, outputPath, cadamRoot }) {
if (path.extname(inputPath).toLowerCase() !== ".scad") {
throw new Error("CADAM input must be an OpenSCAD .scad source file");
}
const extension = path.extname(outputPath).toLowerCase();
if (!new Set([".stl", ".dxf"]).has(extension)) {
throw new Error("CADAM output must be .stl or .dxf");
}
if (!fs.existsSync(inputPath) || !fs.statSync(inputPath).isFile()) {
throw new Error(`CADAM input file not found: ${inputPath}`);
}
const runtime = runtimePaths(cadamRoot);
const logs = { stdout: [], stderr: [] };
const runtimeModule = await import(pathToFileURL(runtime.modulePath).href);
const instance = await runtimeModule.default({
noInitialRun: true,
wasmBinary: fs.readFileSync(runtime.wasmPath),
print: (line) => logs.stdout.push(String(line)),
printErr: (line) => logs.stderr.push(String(line))
});
const virtualInput = "/input.scad";
const virtualOutput = `/out${extension}`;
instance.FS.writeFile(virtualInput, fs.readFileSync(inputPath, "utf8"));
const format = extension === ".stl" ? "binstl" : "dxf";
const exitCode = instance.callMain([
virtualInput,
"-o",
virtualOutput,
`--export-format=${format}`,
"--backend=manifold",
"--enable=lazy-union"
]);
if (exitCode !== 0) {
throw new Error(`CADAM OpenSCAD compilation failed with exit code ${exitCode}: ${logs.stderr.join("\n")}`);
}
const output = instance.FS.readFile(virtualOutput, { encoding: "binary" });
fs.mkdirSync(path.dirname(outputPath), { recursive: true });
fs.writeFileSync(outputPath, output);
return {
backend: "cadam",
runtime: "CADAM OpenSCAD WASM",
cadam_root: runtime.root,
input: inputPath,
output: outputPath,
bytes: output.length,
exit_code: exitCode,
stderr_tail: logs.stderr.slice(-8)
};
}
try {
const result = await compile(parseArgs(process.argv.slice(2)));
process.stdout.write(`${JSON.stringify(result, null, 2)}\n`);
} catch (error) {
process.stderr.write(`${error instanceof Error ? error.message : String(error)}\n`);
process.exitCode = 1;
}