diff --git a/bin/check-and-run.js b/bin/check-and-run.js new file mode 100644 index 00000000..52e9cdb9 --- /dev/null +++ b/bin/check-and-run.js @@ -0,0 +1,20 @@ +#!/usr/bin/env node + +import semver from "semver"; + +const { default: packageJSON } = await import("../package.json", { + with: { type: "json" } +}); + +const currentVersion = process.versions.node; +const requiredRange = packageJSON.engines.node; + +if (!semver.satisfies(currentVersion, requiredRange)) { + console.error( + `\n @nodesecure/cli requires Node.js ${requiredRange}.` + + `\n Current version: v${currentVersion}\n` + ); + process.exit(1); +} + +await import("./index.js"); diff --git a/package.json b/package.json index 75220e5e..daa2a534 100644 --- a/package.json +++ b/package.json @@ -4,8 +4,8 @@ "description": "Node.js security CLI", "main": "./bin/index.js", "bin": { - "node-secure": "./bin/index.js", - "nsecure": "./bin/index.js" + "node-secure": "./bin/check-and-run.js", + "nsecure": "./bin/check-and-run.js" }, "type": "module", "engines": { diff --git a/test/commands/bin.test.js b/test/commands/bin.test.js new file mode 100644 index 00000000..ff39841a --- /dev/null +++ b/test/commands/bin.test.js @@ -0,0 +1,46 @@ +// Import Node.js Dependencies +import assert from "node:assert"; +import { fork } from "node:child_process"; +import path from "node:path"; +import { describe, it } from "node:test"; + +// CONSTANTS +const kProcessPath = path.join(import.meta.dirname, "..", "process", "check-and-run.js"); + +describe("bin/check-and-run.js", () => { + it("should exit with code 1 and print a friendly message for an unsupported Node version", async() => { + const { exitCode, stderr } = await runSubprocess("0.10.0"); + + assert.strictEqual(exitCode, 1, "should exit with code 1"); + assert.ok( + stderr.includes("@nodesecure/cli requires Node.js"), + "should print required Node.js version message" + ); + }); + + it("should pass the version gate when Node version satisfies engines range", async() => { + const { stderr } = await runSubprocess("24.0.0"); + + assert.ok( + !stderr.includes("@nodesecure/cli requires Node.js"), + "should not print a version mismatch message" + ); + }); +}); + +function runSubprocess(version) { + return new Promise((resolve) => { + const child = fork(kProcessPath, [version], { + stdio: ["ignore", "pipe", "pipe", "ipc"] + }); + + let stderr = ""; + child.stderr.on("data", (chunk) => { + stderr += chunk; + }); + + child.on("close", (code) => { + resolve({ exitCode: code, stderr }); + }); + }); +} diff --git a/test/process/check-and-run.js b/test/process/check-and-run.js new file mode 100644 index 00000000..e3f8a9ac --- /dev/null +++ b/test/process/check-and-run.js @@ -0,0 +1,9 @@ +const fakeVersion = process.argv[2]; + +if (fakeVersion) { + Object.defineProperty(process, "versions", { + value: { ...process.versions, node: fakeVersion } + }); +} + +await import("../../bin/check-and-run.js");