#!/usr/bin/env node import fs from "node:fs/promises"; import path from "node:path"; import { fileURLToPath } from "node:url"; const REQUIRED_QUERY_COMPILERS = new Set(["query_compiler_fast_bg.postgresql.mjs", "query_compiler_fast_bg.postgresql.wasm-base64.mjs"]); const QUERY_COMPILER_IMPORT_PATTERN = /@prisma\/client\/runtime\/(query_compiler_[^"']+)/gu; export async function verifyGeneratedPrismaRuntimeContract(generatedClientPath) { const source = await fs.readFile(generatedClientPath, "utf8"); const imports = new Set([...source.matchAll(QUERY_COMPILER_IMPORT_PATTERN)].map((match) => match[1])); const missing = [...REQUIRED_QUERY_COMPILERS].filter((fileName) => !imports.has(fileName)); const unexpected = [...imports].filter((fileName) => !REQUIRED_QUERY_COMPILERS.has(fileName)); if (missing.length > 0 || unexpected.length > 0) { throw new Error(`generated Prisma runtime contract mismatch: missing=${missing.join(",") || "none"} unexpected=${unexpected.join(",") || "none"}`); } return [...imports].sort(); } export async function prunePrismaClientRuntime({ generatedClientPath, nodeModulesPath }) { await verifyGeneratedPrismaRuntimeContract(generatedClientPath); const virtualStorePath = path.join(nodeModulesPath, ".pnpm"); const entries = await fs.readdir(virtualStorePath, { withFileTypes: true }); const clientRoots = entries .filter((entry) => entry.isDirectory() && entry.name.startsWith("@prisma+client@")) .map((entry) => path.join(virtualStorePath, entry.name, "node_modules", "@prisma", "client")); if (clientRoots.length === 0) { throw new Error(`no @prisma/client runtime found below ${virtualStorePath}`); } let bytesRemoved = 0; let filesRemoved = 0; for (const clientRoot of clientRoots) { const runtimePath = path.join(clientRoot, "runtime"); for (const requiredFile of REQUIRED_QUERY_COMPILERS) { await fs.access(path.join(runtimePath, requiredFile)); } for (const entry of await fs.readdir(runtimePath, { withFileTypes: true })) { if (!entry.isFile() || !entry.name.startsWith("query_compiler_") || REQUIRED_QUERY_COMPILERS.has(entry.name)) continue; const filePath = path.join(runtimePath, entry.name); bytesRemoved += (await fs.stat(filePath)).size; await fs.rm(filePath); filesRemoved += 1; } const generatorPath = path.join(clientRoot, "generator-build"); const generatorStats = await directoryStats(generatorPath); bytesRemoved += generatorStats.bytes; filesRemoved += generatorStats.files; await fs.rm(generatorPath, { force: true, recursive: true }); } return { bytesRemoved, clientCount: clientRoots.length, filesRemoved }; } async function directoryStats(directoryPath) { let bytes = 0; let files = 0; let entries; try { entries = await fs.readdir(directoryPath, { withFileTypes: true }); } catch (error) { if (error?.code === "ENOENT") return { bytes, files }; throw error; } for (const entry of entries) { const entryPath = path.join(directoryPath, entry.name); if (entry.isDirectory()) { const nested = await directoryStats(entryPath); bytes += nested.bytes; files += nested.files; } else if (entry.isFile()) { bytes += (await fs.stat(entryPath)).size; files += 1; } } return { bytes, files }; } async function runCli() { const nodeModulesPath = path.resolve(process.argv[2] ?? "node_modules"); const generatedClientPath = path.resolve(process.argv[3] ?? "packages/db/generated/prisma/client/internal/class.ts"); const result = await prunePrismaClientRuntime({ generatedClientPath, nodeModulesPath }); console.log(`PRUNED prisma-clients=${result.clientCount} files=${result.filesRemoved} bytes=${result.bytesRemoved}`); } if (process.argv[1] && path.resolve(process.argv[1]) === fileURLToPath(import.meta.url)) { await runCli(); }