108 lines
3.6 KiB
JavaScript
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;
|
|
}
|