From 6afc7ddab0cecf5a3be60cc1a2d3f1d8c161e56b Mon Sep 17 00:00:00 2001 From: Prajeeth H Date: Wed, 26 Aug 2026 18:24:48 +0530 Subject: [PATCH] fix(install): guard prepare script against missing husky (#11634) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Merged via /merge-batch (2026-08-26, v3.8.51). Validado: 5/5 testes passando, `npm install` limpo confirmando que o script prepare não quebra sem husky. Obrigado pela contribuição. --- package.json | 2 +- tests/unit/prepare-script-husky-guard.test.ts | 54 +++++++++++++++++++ 2 files changed, 55 insertions(+), 1 deletion(-) create mode 100644 tests/unit/prepare-script-husky-guard.test.ts diff --git a/package.json b/package.json index 1e013942cc..4ab0fab4c9 100644 --- a/package.json +++ b/package.json @@ -252,7 +252,7 @@ "postinstall": "node scripts/build/postinstall.mjs", "uninstall": "node scripts/build/uninstall.mjs", "uninstall:full": "node scripts/build/uninstall.mjs --full", - "prepare": "husky", + "prepare": "node -e \"try{require.resolve('husky')}catch(e){process.exit(0)};require('child_process').execSync('husky',{stdio:'inherit'})\"", "system-info": "node scripts/dev/system-info.mjs", "build:cli-api": "node --import tsx/esm scripts/cli/generate-api-commands.mjs", "postbuild": "node scripts/build/colocate-standalone.mjs", diff --git a/tests/unit/prepare-script-husky-guard.test.ts b/tests/unit/prepare-script-husky-guard.test.ts new file mode 100644 index 0000000000..9da5d9a94a --- /dev/null +++ b/tests/unit/prepare-script-husky-guard.test.ts @@ -0,0 +1,54 @@ +import { describe, it } from "node:test"; +import assert from "node:assert/strict"; +import { readFileSync } from "node:fs"; +import { execSync } from "node:child_process"; +import { join } from "node:path"; + +describe("package.json prepare script (#11571)", () => { + const pkg = JSON.parse( + readFileSync(join(import.meta.dirname, "../../package.json"), "utf8") + ); + + it("has a prepare script that guards against missing husky", () => { + const prepare = pkg.scripts?.prepare; + assert.ok(prepare, "prepare script must exist"); + assert.ok( + prepare.includes("require.resolve") || prepare.includes("existsSync"), + "prepare must check if husky is available before running it" + ); + }); + + it("does not hard-fail when husky is absent", () => { + const prepare = pkg.scripts?.prepare; + assert.ok( + !prepare.match(/^\s*husky\s*$/), + "prepare must not be a bare 'husky' call without a guard" + ); + }); + + it("exits cleanly without invoking husky when it is unresolvable", () => { + const prepare = pkg.scripts?.prepare; + assert.ok(prepare); + assert.ok( + !prepare.includes("&& husky") && !prepare.includes("|| husky"), + "husky must not appear as a separate shell command after the guard — " + + "process.exit(0) in the guard would still allow && to proceed" + ); + }); + + it("still calls husky when available", () => { + const prepare = pkg.scripts?.prepare; + assert.ok( + prepare.includes("husky"), + "prepare must still invoke husky when it is installed" + ); + }); + + it("exits 0 in an environment where husky is not resolvable", () => { + const result = execSync( + "node -e \"try{require.resolve('husky_nonexistent_pkg')}catch(e){process.exit(0)};process.exit(1)\"", + { encoding: "utf8", stdio: "pipe" } + ); + assert.equal(result, "", "should produce no output and exit 0"); + }); +});