UNPKG

taylo

Version:

Make changes to a branch a plugin. A command-line tool to manage and apply plugins '.taylored'. Supports applying, removing, verifying plugins, and generating them from branch (GIT).

248 lines (247 loc) 9.5 kB
"use strict"; var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { if (k2 === undefined) k2 = k; var desc = Object.getOwnPropertyDescriptor(m, k); if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { desc = { enumerable: true, get: function() { return m[k]; } }; } Object.defineProperty(o, k2, desc); }) : (function(o, m, k, k2) { if (k2 === undefined) k2 = k; o[k2] = m[k]; })); var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { Object.defineProperty(o, "default", { enumerable: true, value: v }); }) : function(o, v) { o["default"] = v; }); var __importStar = (this && this.__importStar) || (function () { var ownKeys = function(o) { ownKeys = Object.getOwnPropertyNames || function (o) { var ar = []; for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k; return ar; }; return ownKeys(o); }; return function (mod) { if (mod && mod.__esModule) return mod; var result = {}; if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]); __setModuleDefault(result, mod); return result; }; })(); Object.defineProperty(exports, "__esModule", { value: true }); exports.resolveTayloredFileName = resolveTayloredFileName; exports.printUsageAndExit = printUsageAndExit; exports.analyzeDiffContent = analyzeDiffContent; exports.getAndAnalyzeDiff = getAndAnalyzeDiff; exports.extractMessageFromPatch = extractMessageFromPatch; exports.findPatchesInDirectory = findPatchesInDirectory; exports.sortPatchesNumerically = sortPatchesNumerically; const child_process_1 = require("child_process"); const parseDiffModule = __importStar(require("parse-diff")); const fs = __importStar(require("fs/promises")); const path = __importStar(require("path")); const constants_1 = require("./constants"); function resolveTayloredFileName(userInputFileName) { if (userInputFileName.endsWith(constants_1.TAYLORED_FILE_EXTENSION)) { return userInputFileName; } return userInputFileName + constants_1.TAYLORED_FILE_EXTENSION; } function printUsageAndExit(message, printFullUsage = false) { if (message) { console.error(message); } if (printFullUsage || message) { console.log(` Usage: taylored <option> [arguments]`); console.log(` Core Patching Commands (require to be run in a Git repository root):`); console.log(` --add <taylored_file_name> Applies the patch.`); console.log(` --remove <taylored_file_name> Reverses the patch.`); console.log(` --verify-add <taylored_file_name> Verifies if the patch can be applied.`); console.log(` --verify-remove <taylored_file_name> Verifies if the patch can be reversed.`); console.log(` --save <branch_name> Creates a patch from changes in <branch_name>.`); console.log(` --list Lists all applied patches.`); console.log(` --offset <taylored_file_name> [BRANCH_NAME] Adjusts patch offsets based on current branch or specified BRANCH_NAME.`); console.log(` --automatic <EXTENSIONS> <branch_name> [--exclude <DIR_LIST>]`); console.log(` Automatically computes and applies line offsets for patches based on Git history.`); console.log(` --upgrade <patch_file> [target_file_path]`); console.log(` Analyzes patch frames against the target file (or inferred file).`); console.log(` If frames are intact, updates the patch content from the target file.`); } if (!message) { process.exit(0); } process.exit(1); } function analyzeDiffContent(diffOutput) { let additions = 0; let deletions = 0; let isPure = false; let success = true; let errorMessage; if (typeof diffOutput === 'string') { if (diffOutput.trim() === '') { isPure = true; } else { try { const parsedDiffFiles = parseDiffModule.default(diffOutput); for (const file of parsedDiffFiles) { additions += file.additions; deletions += file.deletions; } isPure = (additions > 0 && deletions === 0) || (deletions > 0 && additions === 0) || (additions === 0 && deletions === 0); } catch (parseError) { errorMessage = `Failed to parse diff output. Error: ${parseError.message}`; success = false; } } } else { errorMessage = `Diff output was unexpectedly undefined.`; success = false; } return { additions, deletions, isPure, success, errorMessage }; } function getAndAnalyzeDiff(branchName, CWD) { const command = `git diff HEAD "${branchName.replace(/"/g, '\\"')}"`; let diffOutput; let errorMessage; let commandSuccess = false; let additions = 0; let deletions = 0; let isPure = false; try { diffOutput = (0, child_process_1.execSync)(command, { encoding: 'utf8', cwd: CWD }); commandSuccess = true; } catch (error) { if (error.status === 1 && typeof error.stdout === 'string') { diffOutput = error.stdout; commandSuccess = true; } else { errorMessage = `CRITICAL ERROR: 'git diff' command failed for branch '${branchName}'.`; if (error.status) { errorMessage += ` Exit status: ${error.status}.`; } if (error.stderr && typeof error.stderr === 'string' && error.stderr.trim() !== '') { errorMessage += ` Git stderr: ${error.stderr.trim()}.`; } else if (error.message) { errorMessage += ` Error message: ${error.message}.`; } errorMessage += ` Attempted command: ${command}.`; commandSuccess = false; } } if (commandSuccess) { const analysis = analyzeDiffContent(diffOutput); if (analysis.success) { additions = analysis.additions; deletions = analysis.deletions; isPure = analysis.isPure; } else { errorMessage = (errorMessage ? errorMessage + '\n' : '') + `CRITICAL ERROR: Post-diff analysis failed. ${analysis.errorMessage}`; commandSuccess = false; } } return { diffOutput, additions, deletions, isPure, errorMessage, success: commandSuccess, }; } function extractMessageFromPatch(patchContent) { if (!patchContent || typeof patchContent !== 'string') { return null; } const lines = patchContent.split('\n'); for (const line of lines) { if (line.startsWith('Subject:')) { let message = line.substring('Subject:'.length).trim(); message = message.replace(/^\[PATCH(?:\s+\d+\/\d+)?\]\s*/, ''); if (message) { return message; } } } let inHeader = true; const potentialMessageLines = []; for (const line of lines) { if (line.startsWith('---') || line.startsWith('diff --git')) { inHeader = false; break; } if (inHeader && (line.startsWith('From:') || line.startsWith('Date:') || line.startsWith('Signed-off-by:'))) { continue; } if (inHeader && line.trim() !== '' && !line.startsWith(' ') && !line.startsWith('git') && !line.includes('/')) { potentialMessageLines.push(line.trim()); } } if (potentialMessageLines.length > 0) { return potentialMessageLines.slice(0, 3).join('\n'); } return null; } async function findPatchesInDirectory(directoryPath) { const allFiles = []; const entries = await fs.readdir(directoryPath, { withFileTypes: true }); for (const entry of entries) { const fullPath = path.join(directoryPath, entry.name); if (entry.isDirectory()) { allFiles.push(...(await findPatchesInDirectory(fullPath))); } else if (entry.isFile() && entry.name.endsWith(constants_1.TAYLORED_FILE_EXTENSION)) { allFiles.push(fullPath); } } return allFiles; } function sortPatchesNumerically(filePaths) { const extractNumber = (fileName) => { const match = path.basename(fileName).match(/^(\d+)/); return match ? parseInt(match[1], 10) : Infinity; }; return [...filePaths].sort((a, b) => { const numA = extractNumber(a); const numB = extractNumber(b); if (numA !== Infinity && numB !== Infinity) { return numA - numB; } else if (numA !== Infinity) { return -1; } else if (numB !== Infinity) { return 1; } else { return path.basename(a).localeCompare(path.basename(b)); } }); }