#!/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 [--cadam-root ]"; } 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; }