UNPKG

@smushytaco/custompatch

Version:

Tool for patching buggy NPM packages instead of forking them

182 lines (179 loc) 7.46 kB
import fs from 'node:fs'; import path from 'pathe'; import { parsePatch, reversePatch, applyPatch, createTwoFilesPatch } from 'diff'; import pc from 'picocolors'; import { r as readFileContent, p as pathNormalize, e as ensureDirectoryExists } from './file-utilities.mjs'; import { a as patchDirectory, c as currentDirectory, t as temporaryDirectory, p as programOptions } from './variables.mjs'; import 'commander'; import 'node:os'; function createPatch(packageName, pathname, patch) { const newFile = path.join(currentDirectory, "node_modules", packageName, pathname); const oldFile = path.join(temporaryDirectory, packageName, pathname); const oldString = fs.existsSync(oldFile) ? readFileContent(oldFile) : ""; const newString = readFileContent(newFile); if (pathname === "package.json" && !programOptions.all) return; if (oldString !== newString) { patch.write(createTwoFilesPatch(oldFile.replace(temporaryDirectory, ""), newFile.replace(path.join(currentDirectory, "node_modules"), ""), oldString, newString)); } } function makePatchName(packageName, version) { return `${packageName.replaceAll("/", "+")}#${version}.patch`; } async function comparePackages(packageName, version) { const patchFile = makePatchName(packageName, version); ensureDirectoryExists(patchDirectory); const stream = fs.createWriteStream(path.join(patchDirectory, patchFile)); stream.on("error", (error) => { console.error(`${pc.redBright("ERROR:")} Failed to write to patch file - ${error.message}`); }); stream.cork(); scanFiles(packageName, "", stream); stream.uncork(); if (stream.write("")) { stream.end(); } else { stream.once("drain", () => { stream.end(); }); } console.log(`Successfully created ${pc.greenBright(patchFile)}`); } function scanFiles(packageName, source, patch) { const baseDirectory = path.join(currentDirectory, "node_modules", packageName); const stack = [source]; const visitedPaths = /* @__PURE__ */ new Set(); while (stack.length > 0) { const currentSource = stack.pop(); if (currentSource === void 0) continue; const directoryPath = path.join(baseDirectory, currentSource); let files; try { files = fs.readdirSync(directoryPath); } catch (error) { console.error(`${pc.redBright("ERROR:")} Failed to read directory ${directoryPath} - ${error instanceof Error ? error.message : String(error)}`); continue; } for (const item of files) { if (item === "node_modules") continue; const pathname = path.join(currentSource, item); const itemPath = path.join(baseDirectory, pathname); let stat; try { stat = fs.lstatSync(itemPath); } catch (error) { console.error(`${pc.redBright("ERROR:")} Failed to get stats for ${itemPath} - ${error instanceof Error ? error.message : String(error)}`); continue; } if (stat.isSymbolicLink()) { continue; } if (stat.isDirectory()) { const realPath = fs.realpathSync(itemPath); if (visitedPaths.has(realPath)) { continue; } visitedPaths.add(realPath); stack.push(pathname); } else { createPatch(packageName, pathname, patch); } } } } async function readPatch(packageName, version, reverse = false) { const thePackageName = packageName.replaceAll("+", path.sep); const config = getConfig(thePackageName); if (config) { const patchFile = makePatchName(packageName, version); const patchFilePath = path.join(patchDirectory, patchFile); if (!fs.existsSync(patchFilePath)) { console.warn(`${pc.yellowBright("WARNING:")} Patch file "${patchFile}" does not exist.`); return; } const patchContent = readFileContent(patchFilePath); const patches = parsePatch(patchContent); for (const patchItem of patches) { const filePath = patchItem.newFileName ?? patchItem.oldFileName; if (!filePath) { console.error(`${pc.redBright("ERROR:")} Patch item has no file names for package ${packageName}`); continue; } const normalizedPath = pathNormalize(filePath); const fileName = path.join(currentDirectory, "node_modules", normalizedPath); let fileContent = ""; if (fs.existsSync(fileName)) { fileContent = readFileContent(fileName); } else { console.warn(`${pc.yellowBright("WARNING:")} File "${fileName}" does not exist - skipping.`); continue; } if (reverse) { const reversedPatchText = reversePatch(patchItem); const reversePatchedContent = applyPatch(fileContent, reversedPatchText); if (reversePatchedContent === false) { const patchedContent = applyPatch(fileContent, patchItem); if (patchedContent === false) { console.warn(`${pc.yellowBright("WARNING:")} Failed to reverse patch for ${pc.redBright(fileName)}`); } else { console.log(`Patch already reversed for ${pc.greenBright(fileName)}`); } } else { try { fs.writeFileSync(fileName, reversePatchedContent, "utf8"); console.log(`Reversed patch for ${pc.greenBright(fileName)}`); } catch (error) { console.error(`${pc.redBright("ERROR:")} Could not write the new content for file ${fileName} - ${error instanceof Error ? error.message : String(error)}`); } } } else { const patchedContent = applyPatch(fileContent, patchItem); if (patchedContent === false) { const reversedPatchText = reversePatch(patchItem); const reversePatchedContent = applyPatch(fileContent, reversedPatchText); if (reversePatchedContent === false) { console.warn(`${pc.yellowBright("WARNING:")} Failed to apply patch to ${pc.redBright(fileName)}`); } else { console.log(`Patch already applied to ${pc.greenBright(fileName)}`); } } else { try { fs.writeFileSync(fileName, patchedContent, "utf8"); console.log(`Patched ${pc.greenBright(fileName)}`); } catch (error) { console.error(`${pc.redBright("ERROR:")} Could not write the new content for file ${fileName} - ${error instanceof Error ? error.message : String(error)}`); } } } } } else { console.error(`${pc.redBright("ERROR:")} Could not get config for package ${packageName}`); } } function getConfig(packageName) { const folder = path.join(currentDirectory, "node_modules", packageName); const configName = path.join(folder, "package.json"); if (!fs.existsSync(folder)) { console.error(`${pc.redBright("ERROR:")} Missing folder "${pc.whiteBright(folder)}"`); return false; } try { fs.accessSync(configName, fs.constants.R_OK); } catch { console.error(`${pc.redBright("ERROR:")} Cannot read "${pc.whiteBright(configName)}"`); return false; } const packageConfig = readFileContent(configName); let config; try { config = JSON.parse(packageConfig); } catch (error) { console.error(`${pc.redBright("ERROR:")} Could not parse "${pc.whiteBright("package.json")}" - ${pc.redBright(error instanceof Error ? error.message : String(error))}`); return false; } return config; } export { comparePackages as c, getConfig as g, readPatch as r }; //# sourceMappingURL=patch-utilities.mjs.map