#!/usr/bin/env node import { constants } from 'node:fs'; import { access } from 'node:fs/promises'; import { fileURLToPath } from 'node:url'; import path from 'node:path'; export const MISSING_DEPS_EXIT = 42; // The generated-state certification that used to live here (fingerprinting the // web source tree and certifying apps/web/.next) retired with the Next.js build // in Phase P5 (#1444): the Vite SPA has no generated tree that later gates // consume, so there is no stale-output class left to defend against. export async function runPreflight({ root = process.cwd() } = {}) { const binDir = path.join(root, 'node_modules', '.bin'); const requiredBinaries = ['eslint', 'husky', 'prettier', 'tsc', 'turbo', 'vitest']; const missingBinaries = []; for (const binary of requiredBinaries) { try { await access(path.join(binDir, binary), constants.X_OK); } catch { missingBinaries.push(binary); } } if (missingBinaries.length > 0) { return { code: MISSING_DEPS_EXIT, message: `MOSAIC_PREFLIGHT_MISSING_DEPS: dependency installation is missing ${missingBinaries.join(', ')}; run pnpm install --frozen-lockfile`, }; } return { code: 0, message: 'checkout preflight passed' }; } async function main() { const result = await runPreflight(); const stream = result.code === 0 ? process.stdout : process.stderr; stream.write(`${result.message}\n`); process.exitCode = result.code; } if (process.argv[1] && path.resolve(process.argv[1]) === fileURLToPath(import.meta.url)) { await main(); }