#[derive(Debug)]
pub struct LauncherScript {
fail_open: bool,
}
impl LauncherScript {
pub fn new(fail_open: bool) -> Self {
Self { fail_open }
}
pub fn render(&self) -> String {
let on_missing = if self.fail_open {
" process.exit(0);"
} else {
" process.stderr.write(`npmgen launcher: ${reason}\\n`);\n process.exit(1);"
};
let failure_exit = if self.fail_open { "0" } else { "1" };
format!(
r#"// Generated by npmgen. Runs the platform-specific binary for this package.
import {{ spawnSync }} from "node:child_process";
import {{ createRequire }} from "node:module";
import {{ readFileSync }} from "node:fs";
import {{ fileURLToPath }} from "node:url";
import {{ dirname, join }} from "node:path";
const root = dirname(fileURLToPath(import.meta.url));
const key = `${{process.platform}}-${{process.arch}}`;
const ext = process.platform === "win32" ? ".exe" : "";
function missing(reason) {{
{on_missing}
}}
let pkg;
try {{
pkg = JSON.parse(readFileSync(join(root, "package.json"), "utf8"));
}} catch {{
missing("cannot read package.json");
}}
const dependency = Object.keys(pkg.optionalDependencies ?? {{}}).find((name) => name.endsWith(`-${{key}}`));
const binaryName = (pkg.name ?? "").split("/").pop();
if (!dependency || !binaryName) {{
missing(`no package for ${{key}}`);
}}
const require = createRequire(import.meta.url);
let binary;
try {{
binary = require.resolve(`${{dependency}}/${{binaryName}}${{ext}}`);
}} catch {{
missing(`binary for ${{key}} is not installed`);
}}
const result = spawnSync(binary, process.argv.slice(2), {{ stdio: "inherit" }});
if (result.error) {{
missing(`failed to run binary: ${{result.error.message}}`);
}}
// A signal death leaves status null; treat it as failure, never success.
process.exit(result.status ?? {failure_exit});
"#
)
}
}
#[cfg(test)]
mod tests {
use super::LauncherScript;
#[test]
fn fail_open_exits_zero_on_any_failure() {
let source = LauncherScript::new(true).render();
assert!(source.contains("process.exit(0);"));
assert!(!source.contains("process.exit(1);"));
assert!(source.contains("result.status ?? 0"));
assert!(source.contains("if (result.error)"));
assert!(source.contains("spawnSync"));
}
#[test]
fn fail_hard_reports_and_exits_nonzero() {
let source = LauncherScript::new(false).render();
assert!(source.contains("process.exit(1);"));
assert!(source.contains("process.stderr.write"));
assert!(source.contains("if (result.error)"));
assert!(source.contains("result.status ?? 1"));
assert!(!source.contains("result.status ?? 0"));
}
}