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).
771 lines (770 loc) • 44.3 kB
JavaScript
;
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.handleAutomaticOperation = handleAutomaticOperation;
const fs = __importStar(require("fs/promises"));
const path = __importStar(require("path"));
const child_process_1 = require("child_process");
const constants_1 = require("../constants");
const utils_1 = require("../utils");
const execOpts = {
encoding: 'utf8',
stdio: 'pipe',
};
async function findFilesRecursive(dir, ext, allFiles = [], excludeDirs, CWD_ABS) {
const entries = await fs.readdir(dir, { withFileTypes: true });
for (const entry of entries) {
const fullPath = path.join(dir, entry.name);
if (entry.isDirectory()) {
const relativePath = CWD_ABS
? path.relative(CWD_ABS, fullPath)
: entry.name;
if (entry.name !== '.git' &&
entry.name !== constants_1.TAYLORED_DIR_NAME &&
(!excludeDirs ||
!excludeDirs.some((excludedDir) => relativePath === excludedDir ||
relativePath.startsWith(excludedDir + path.sep)))) {
await findFilesRecursive(fullPath, ext, allFiles, excludeDirs, CWD_ABS);
}
}
else if (entry.isFile() && entry.name.endsWith(ext)) {
allFiles.push(fullPath);
}
}
return allFiles;
}
async function handleAutomaticOperation(extensionsInput, branchName, CWD, excludeDirs) {
let originalBranchName;
try {
originalBranchName = (0, child_process_1.execSync)('git rev-parse --abbrev-ref HEAD', {
cwd: CWD,
...execOpts,
}).trim();
if (originalBranchName === 'HEAD') {
const errorMessage = 'CRITICAL ERROR: Repository is in a detached HEAD state. Please checkout a branch.';
console.error(errorMessage);
throw new Error(errorMessage);
}
}
catch (error) {
const errorMessage = `CRITICAL ERROR: Failed to get current Git branch. Details: ${error.message}`;
console.error(errorMessage);
if (error.stderr)
console.error('STDERR:\n' + error.stderr);
if (error.stdout)
console.error('STDOUT:\n' + error.stdout);
throw new Error(errorMessage);
}
try {
const gitStatus = (0, child_process_1.execSync)('git status --porcelain', {
cwd: CWD,
...execOpts,
}).trim();
if (gitStatus) {
const errorMessage = 'CRITICAL ERROR: Uncommitted changes or untracked files in the repository. Please commit or stash them before running --automatic.';
console.error(errorMessage);
console.error('Details:\n' + gitStatus);
throw new Error(errorMessage);
}
}
catch (error) {
const errorMessage = `CRITICAL ERROR: Failed to check Git status. Details: ${error.message}`;
console.error(errorMessage);
if (error.stderr)
console.error('STDERR:\n' + error.stderr);
if (error.stdout)
console.error('STDOUT:\n' + error.stdout);
throw new Error(errorMessage);
}
console.log(`Starting automatic taylored block extraction for extensions '${extensionsInput}' in directory '${CWD}'. Original branch: '${originalBranchName}'`);
const tayloredDir = path.join(CWD, constants_1.TAYLORED_DIR_NAME);
try {
await fs.mkdir(tayloredDir, { recursive: true });
}
catch (error) {
const errorMessage = `CRITICAL ERROR: Could not create directory '${tayloredDir}'. Details: ${error.message}`;
console.error(errorMessage);
throw new Error(errorMessage);
}
const extensions = extensionsInput.split(',').map((ext) => ext.trim());
const allFilesToScan = [];
const CWD_ABS = path.resolve(CWD);
for (const ext of extensions) {
const normalizedExtension = ext.startsWith('.') ? ext : `.${ext}`;
try {
const filesForExtension = await findFilesRecursive(CWD_ABS, normalizedExtension, [], excludeDirs, CWD_ABS);
allFilesToScan.push(...filesForExtension);
}
catch (error) {
console.error(`Error while searching for files with extension '${normalizedExtension}': ${error.message}`);
}
}
if (allFilesToScan.length === 0) {
console.log(`No files found with specified extensions: ${extensionsInput}`);
return;
}
console.log(`Found ${allFilesToScan.length} file(s) with specified extensions. Processing...`);
const blockRegex = /[^\n]*?<taylored\s+number="(\d+)"([^>]*)>([\s\S]*?)[^\n]*?<\/taylored>/g;
const jsonBlockRegex = /(?:const\s+\w+\s*=\s*)?({(?:[^{}]|{[^{}]*})*?"taylored"\s*:\s*(\d+)(?:[^{}]|{[^{}]*})*?});?/g;
let totalBlocksProcessed = 0;
const asyncScriptPromises = [];
for (const originalFilePath of allFilesToScan) {
let fileContent;
try {
fileContent = await fs.readFile(originalFilePath, 'utf-8');
}
catch (readError) {
console.warn(`Warning: Error reading file '${originalFilePath}': ${readError.message}. Skipping this file.`);
continue;
}
const xmlMatchesRaw = Array.from(fileContent.matchAll(blockRegex));
const jsonMatchesRaw = Array.from(fileContent.matchAll(jsonBlockRegex));
const allMatches = [];
for (const match of xmlMatchesRaw) {
if (match.index !== undefined) {
allMatches.push({ type: 'xml', match, index: match.index });
}
}
for (const match of jsonMatchesRaw) {
if (match.index !== undefined) {
allMatches.push({ type: 'json', match, index: match.index });
}
}
allMatches.sort((a, b) => a.index - b.index);
if (allMatches.length === 0) {
continue;
}
for (const matchInfo of allMatches) {
let numero;
let attributesString;
let scriptContent;
let scriptContentWithTags;
let computeCharsToStrip;
let asyncFlag = false;
let isDisabled = false;
if (matchInfo.type === 'xml') {
const match = matchInfo.match;
numero = match[1];
attributesString = match[2];
scriptContentWithTags = match[0];
scriptContent = match[3];
const computeMatch = attributesString.match(/compute=["']([^"']*)["']/);
computeCharsToStrip = computeMatch ? computeMatch[1] : undefined;
const asyncMatch = attributesString.match(/async=["'](true|false)["']/);
asyncFlag = asyncMatch ? asyncMatch[1] === 'true' : false;
const disabledMatch = attributesString.match(/disabled=["'](true|false)["']/);
isDisabled = disabledMatch ? disabledMatch[1] === 'true' : false;
}
else {
scriptContentWithTags = matchInfo.match[0];
const jsonBlockText = matchInfo.match[1];
numero = matchInfo.match[2];
try {
const cleanedJsonText = jsonBlockText.replace(/\/\/.*$/gm, '');
const parsedJson = JSON.parse(cleanedJsonText);
if (typeof parsedJson.content !== 'string') {
console.warn(`Warning: JSON block ${numero} in ${originalFilePath} has invalid or missing 'content' string. Skipping.`);
continue;
}
scriptContent = parsedJson.content;
computeCharsToStrip =
typeof parsedJson.compute === 'string'
? parsedJson.compute
: undefined;
asyncFlag = parsedJson.async === true;
isDisabled = parsedJson.disabled === true;
}
catch (e) {
console.warn(`Warning: Parsing of JSON block in ${originalFilePath} ... Skipping.`);
continue;
}
}
if (isDisabled) {
console.log(`Skipping disabled block ${numero} from ${originalFilePath}.`);
continue;
}
const targetTayloredFileName = `${numero}${constants_1.TAYLORED_FILE_EXTENSION}`;
const targetTayloredFilePath = path.join(tayloredDir, targetTayloredFileName);
const intermediateMainTayloredPath = path.join(tayloredDir, `main${constants_1.TAYLORED_FILE_EXTENSION}`);
console.log(`Processing block ${numero} from ${originalFilePath}...`);
if (computeCharsToStrip !== undefined) {
const processComputeBlock = async (currentNumero, currentOriginalFilePath, currentScriptContent, currentComputeCharsToStrip, currentScriptContentWithTags, currentCWD, currentBranchName, currentOriginalBranchName, currentTargetTayloredFilePath) => {
console.log(`Asynchronously processing computed block ${currentNumero} from ${currentOriginalFilePath}...`);
try {
await fs.access(currentTargetTayloredFilePath);
const message = `CRITICAL ERROR: Target file ${currentTargetTayloredFilePath} for computed block already exists. Please remove or rename it.`;
console.error(message);
throw new Error(message);
}
catch (error) {
if (error.code !== 'ENOENT') {
throw error;
}
}
try {
await fs.access(intermediateMainTayloredPath);
const message = `CRITICAL ERROR: Intermediate file ${intermediateMainTayloredPath} exists for a compute block. This file should not be present. Please remove or rename it.`;
console.error(message);
throw new Error(message);
}
catch (error) {
if (error.code !== 'ENOENT') {
throw error;
}
}
let actualScriptContent;
if (currentComputeCharsToStrip.length > 0) {
let processedContent = currentScriptContent.trim();
const patterns = currentComputeCharsToStrip.split(',');
for (const pattern of patterns) {
const trimmedPattern = pattern.trim();
if (trimmedPattern.length > 0) {
processedContent = processedContent.replaceAll(trimmedPattern, '');
}
}
actualScriptContent = processedContent.trim();
}
else {
actualScriptContent = currentScriptContent.trim();
}
const tempScriptPath = path.join(currentCWD, `taylored-temp-script-${Date.now()}-${Math.random().toString(36).substring(2, 8)}`);
await fs.writeFile(tempScriptPath, actualScriptContent);
let scriptResult = '';
try {
await fs.chmod(tempScriptPath, 0o755);
scriptResult = await new Promise((resolve, reject) => {
const child = (0, child_process_1.spawn)(tempScriptPath, [], {
cwd: currentCWD,
stdio: 'pipe',
shell: true,
});
let scriptOutput = '';
let scriptErrorOutput = '';
child.stdout.on('data', (data) => {
scriptOutput += data.toString();
process.stdout.write(data);
});
child.stderr.on('data', (data) => {
scriptErrorOutput += data.toString();
process.stderr.write(data);
});
child.on('error', reject);
child.on('close', (code) => {
if (code === 0) {
resolve(scriptOutput);
}
else {
const error = new Error(`Script failed with code ${code}`);
error.status = code;
error.stdout = scriptOutput;
error.stderr = scriptErrorOutput;
reject(error);
}
});
});
}
catch (error) {
if (error.status !== undefined ||
error.stderr !== undefined ||
error.stdout !== undefined) {
console.error(`ERROR: Script execution failed for block ${currentNumero} in ${currentOriginalFilePath}. Error: ${error.message}`);
if (error.stderr)
console.error('STDERR:\n' + error.stderr);
if (error.stdout)
console.error('STDOUT:\n' + error.stdout);
}
else {
console.error(`ERROR: Failed to set execute permissions or other FS issue on temporary script file '${tempScriptPath}'. Details: ${error.message}`);
}
throw error;
}
finally {
try {
await fs.unlink(tempScriptPath);
}
catch (unlinkError) {
console.warn(`Warning: Failed to delete temporary script file '${tempScriptPath}' during cleanup. Details: ${unlinkError.message}`);
}
}
const relativeOriginalFilePath = path.relative(currentCWD, currentOriginalFilePath);
const tempComputeBranchName = `temp-taylored-compute-${currentNumero}-${Date.now()}`;
try {
(0, child_process_1.execSync)(`git checkout -b "${tempComputeBranchName}" "${currentOriginalBranchName}"`, { cwd: currentCWD, ...execOpts });
const gitignorePath = path.join(currentCWD, '.gitignore');
await fs.writeFile(gitignorePath, constants_1.TAYLORED_DIR_NAME + '\n');
(0, child_process_1.execSync)(`git add .gitignore`, { cwd: currentCWD, ...execOpts });
const contentOnTempBranch = await fs.readFile(currentOriginalFilePath, 'utf-8');
const contentWithScriptResult = contentOnTempBranch.replace(currentScriptContentWithTags, scriptResult);
await fs.writeFile(currentOriginalFilePath, contentWithScriptResult);
(0, child_process_1.execSync)(`git add "${relativeOriginalFilePath}"`, {
cwd: currentCWD,
...execOpts,
});
(0, child_process_1.execSync)(`git commit --no-verify -m "AUTO: Apply computed block ${currentNumero} for ${path.basename(currentOriginalFilePath)}"`, { cwd: currentCWD, ...execOpts });
const diffAgainstBranchCommand = `git diff --exit-code "${currentBranchName}" HEAD -- "${relativeOriginalFilePath}"`;
try {
(0, child_process_1.execSync)(diffAgainstBranchCommand, {
cwd: currentCWD,
encoding: 'utf8',
stdio: 'pipe',
});
await fs.writeFile(currentTargetTayloredFilePath, '');
console.log(`No difference found for computed block ${currentNumero} from ${currentOriginalFilePath} when compared against branch '${currentBranchName}'. Empty taylored file created: ${currentTargetTayloredFilePath}`);
}
catch (e) {
if (e.status === 1 && typeof e.stdout === 'string') {
await fs.writeFile(currentTargetTayloredFilePath, e.stdout);
console.log(`Successfully created ${currentTargetTayloredFilePath} for computed block ${currentNumero} from ${currentOriginalFilePath} (using branch diff against '${currentBranchName}')`);
}
else {
console.error(`CRITICAL ERROR: Failed to generate diff for computed block ${currentNumero} from ${currentOriginalFilePath} against branch '${currentBranchName}'.`);
if (e.message)
console.error(` Error message: ${e.message}`);
if (e.stderr)
console.error(' STDERR:\n' + e.stderr.toString().trim());
if (e.stdout)
console.error(' STDOUT:\n' + e.stdout.toString().trim());
throw e;
}
}
}
catch (error) {
console.error(`CRITICAL ERROR: Failed to process computed block ${currentNumero} from ${currentOriginalFilePath} using branch diff method.`);
if (error.message)
console.error(` Error message: ${error.message}`);
if (error.stderr)
console.error(' STDERR:\n' + error.stderr.toString().trim());
if (error.stdout)
console.error(' STDOUT:\n' + error.stdout.toString().trim());
throw error;
}
finally {
const currentBranchAfterOps = (0, child_process_1.execSync)('git rev-parse --abbrev-ref HEAD', { cwd: currentCWD, ...execOpts }).trim();
if (currentBranchAfterOps === tempComputeBranchName) {
(0, child_process_1.execSync)(`git checkout -q "${currentOriginalBranchName}"`, {
cwd: currentCWD,
stdio: 'ignore',
});
}
else if (currentBranchAfterOps !== currentOriginalBranchName) {
console.warn(`Warning: Unexpected current branch '${currentBranchAfterOps}' during cleanup for computed block. Attempting to return to '${currentOriginalBranchName}'.`);
try {
(0, child_process_1.execSync)(`git checkout -q "${currentOriginalBranchName}"`, {
cwd: currentCWD,
stdio: 'ignore',
});
}
catch (coErr) {
console.warn(`Warning: Failed to checkout original branch '${currentOriginalBranchName}' during cleanup. Current branch: ${currentBranchAfterOps}. Error: ${coErr.message}`);
}
}
try {
const branchesRaw = (0, child_process_1.execSync)('git branch', {
cwd: currentCWD,
...execOpts,
});
const branchesList = branchesRaw
.split('\n')
.map((b) => b.trim().replace(/^\* /, ''));
if (branchesList.includes(tempComputeBranchName)) {
(0, child_process_1.execSync)(`git branch -q -D "${tempComputeBranchName}"`, {
cwd: currentCWD,
stdio: 'ignore',
});
}
}
catch (deleteBranchError) {
console.warn(`Warning: Failed to delete temporary branch '${tempComputeBranchName}' during cleanup for computed block. May require manual cleanup. ${deleteBranchError.message}`);
}
}
};
if (asyncFlag) {
asyncScriptPromises.push(processComputeBlock(numero, originalFilePath, scriptContent, computeCharsToStrip, scriptContentWithTags, CWD, branchName, originalBranchName, targetTayloredFilePath));
totalBlocksProcessed++;
}
else {
try {
await fs.access(targetTayloredFilePath);
const message = `CRITICAL ERROR: Target file ${targetTayloredFilePath} for computed block already exists. Please remove or rename it.`;
console.error(message);
throw new Error(message);
}
catch (error) {
if (error.code !== 'ENOENT') {
throw error;
}
}
try {
await fs.access(intermediateMainTayloredPath);
const message = `CRITICAL ERROR: Intermediate file ${intermediateMainTayloredPath} exists for a compute block. This file should not be present. Please remove or rename it.`;
console.error(message);
throw new Error(message);
}
catch (error) {
if (error.code !== 'ENOENT') {
throw error;
}
}
let actualScriptContent;
if (computeCharsToStrip !== undefined &&
computeCharsToStrip.length > 0) {
let processedContent = scriptContent.trim();
const patterns = computeCharsToStrip.split(',');
for (const pattern of patterns) {
const trimmedPattern = pattern.trim();
if (trimmedPattern.length > 0) {
processedContent = processedContent.replaceAll(trimmedPattern, '');
}
}
actualScriptContent = processedContent.trim();
}
else {
actualScriptContent = scriptContent.trim();
}
const tempScriptPath = path.join(CWD, `taylored-temp-script-${Date.now()}-${Math.random().toString(36).substring(2, 8)}`);
await fs.writeFile(tempScriptPath, actualScriptContent);
let scriptResult = '';
try {
await fs.chmod(tempScriptPath, 0o755);
scriptResult = await new Promise((resolve, reject) => {
const child = (0, child_process_1.spawn)(tempScriptPath, [], {
cwd: CWD,
stdio: 'pipe',
shell: true,
});
let scriptOutput = '';
let scriptErrorOutput = '';
child.stdout.on('data', (data) => {
scriptOutput += data.toString();
process.stdout.write(data);
});
child.stderr.on('data', (data) => {
scriptErrorOutput += data.toString();
process.stderr.write(data);
});
child.on('error', (err) => {
reject(err);
});
child.on('close', (code) => {
if (code === 0) {
resolve(scriptOutput);
}
else {
const error = new Error(`Script failed with code ${code}`);
error.status = code;
error.stdout = scriptOutput;
error.stderr = scriptErrorOutput;
reject(error);
}
});
});
}
catch (error) {
if (error.status !== undefined ||
error.stderr !== undefined ||
error.stdout !== undefined) {
console.error(`ERROR: Script execution failed for block ${numero} in ${originalFilePath}. Error: ${error.message}`);
if (error.stderr)
console.error('STDERR:\n' + error.stderr);
if (error.stdout)
console.error('STDOUT:\n' + error.stdout);
}
else {
console.error(`ERROR: Failed to set execute permissions or other FS issue on temporary script file '${tempScriptPath}'. Details: ${error.message}`);
}
throw error;
}
finally {
try {
await fs.unlink(tempScriptPath);
}
catch (unlinkError) {
console.warn(`Warning: Failed to delete temporary script file '${tempScriptPath}' during cleanup. Details: ${unlinkError.message}`);
}
}
const relativeOriginalFilePath = path.relative(CWD, originalFilePath);
const tempComputeBranchName = `temp-taylored-compute-${numero}-${Date.now()}`;
try {
(0, child_process_1.execSync)(`git checkout -b "${tempComputeBranchName}" "${originalBranchName}"`, { cwd: CWD, ...execOpts });
const gitignorePath = path.join(CWD, '.gitignore');
await fs.writeFile(gitignorePath, constants_1.TAYLORED_DIR_NAME + '\n');
(0, child_process_1.execSync)(`git add .gitignore`, { cwd: CWD, ...execOpts });
const contentOnTempBranch = await fs.readFile(originalFilePath, 'utf-8');
const contentWithScriptResult = contentOnTempBranch.replace(scriptContentWithTags, scriptResult);
await fs.writeFile(originalFilePath, contentWithScriptResult);
(0, child_process_1.execSync)(`git add "${relativeOriginalFilePath}"`, {
cwd: CWD,
...execOpts,
});
(0, child_process_1.execSync)(`git commit --no-verify -m "AUTO: Apply computed block ${numero} for ${path.basename(originalFilePath)}"`, { cwd: CWD, ...execOpts });
const diffAgainstBranchCommand = `git diff --exit-code "${branchName}" HEAD -- "${relativeOriginalFilePath}"`;
let diffOutputCommandResult;
try {
diffOutputCommandResult = (0, child_process_1.execSync)(diffAgainstBranchCommand, {
cwd: CWD,
encoding: 'utf8',
stdio: 'pipe',
});
await fs.writeFile(targetTayloredFilePath, '');
console.log(`No difference found for computed block ${numero} from ${originalFilePath} when compared against branch '${branchName}'. Empty taylored file created: ${targetTayloredFilePath}`);
}
catch (e) {
if (e.status === 1 && typeof e.stdout === 'string') {
diffOutputCommandResult = e.stdout;
await fs.writeFile(targetTayloredFilePath, diffOutputCommandResult);
console.log(`Successfully created ${targetTayloredFilePath} for computed block ${numero} from ${originalFilePath} (using branch diff against '${branchName}')`);
}
else {
console.error(`CRITICAL ERROR: Failed to generate diff for computed block ${numero} from ${originalFilePath} against branch '${branchName}'.`);
if (e.message)
console.error(` Error message: ${e.message}`);
if (e.stderr)
console.error(' STDERR:\n' + e.stderr.toString().trim());
if (e.stdout)
console.error(' STDOUT:\n' + e.stdout.toString().trim());
throw e;
}
}
totalBlocksProcessed++;
}
catch (error) {
console.error(`CRITICAL ERROR: Failed to process computed block ${numero} from ${originalFilePath} using branch diff method.`);
if (error.message)
console.error(` Error message: ${error.message}`);
if (error.stderr)
console.error(' STDERR:\n' + error.stderr.toString().trim());
if (error.stdout)
console.error(' STDOUT:\n' + error.stdout.toString().trim());
throw error;
}
finally {
const currentBranchAfterOps = (0, child_process_1.execSync)('git rev-parse --abbrev-ref HEAD', { cwd: CWD, ...execOpts }).trim();
if (currentBranchAfterOps === tempComputeBranchName) {
(0, child_process_1.execSync)(`git checkout -q "${originalBranchName}"`, {
cwd: CWD,
stdio: 'ignore',
});
}
else if (currentBranchAfterOps !== originalBranchName) {
console.warn(`Warning: Unexpected current branch '${currentBranchAfterOps}' during cleanup for computed block. Attempting to return to '${originalBranchName}'.`);
try {
(0, child_process_1.execSync)(`git checkout -q "${originalBranchName}"`, {
cwd: CWD,
stdio: 'ignore',
});
}
catch (coErr) {
console.warn(`Warning: Failed to checkout original branch '${originalBranchName}' during cleanup. Current branch: ${currentBranchAfterOps}. Error: ${coErr.message}`);
}
}
try {
const branchesRaw = (0, child_process_1.execSync)('git branch', {
cwd: CWD,
...execOpts,
});
const branchesList = branchesRaw
.split('\n')
.map((b) => b.trim().replace(/^\* /, ''));
if (branchesList.includes(tempComputeBranchName)) {
(0, child_process_1.execSync)(`git branch -q -D "${tempComputeBranchName}"`, {
cwd: CWD,
stdio: 'ignore',
});
}
}
catch (deleteBranchError) {
console.warn(`Warning: Failed to delete temporary branch '${tempComputeBranchName}' during cleanup for computed block. May require manual cleanup. ${deleteBranchError.message}`);
}
}
}
}
else {
const actualIntermediateFileName = `${branchName.replace(/[/\\]/g, '-')}${constants_1.TAYLORED_FILE_EXTENSION}`;
const actualIntermediateFilePath = path.join(tayloredDir, actualIntermediateFileName);
try {
await fs.access(actualIntermediateFilePath);
const message = `CRITICAL ERROR: Intermediate file ${actualIntermediateFilePath} (derived from branch name '${branchName}') already exists. 'handleSaveOperation' would overwrite this file. Please remove or rename it to ensure a clean state.`;
console.error(message);
throw new Error(message);
}
catch (error) {
if (error.code !== 'ENOENT') {
throw error;
}
}
try {
await fs.access(targetTayloredFilePath);
const message = `CRITICAL ERROR: Target file ${targetTayloredFilePath} already exists. Please remove or rename it.`;
console.error(message);
throw new Error(message);
}
catch (error) {
if (error.code !== 'ENOENT') {
throw error;
}
}
const contentUpToMatch = fileContent.substring(0, matchInfo.index);
const startLineNum = contentUpToMatch.split('\n').length;
const matchLinesCount = scriptContentWithTags.split('\n').length;
const tempBranchName = `temp-taylored-${numero}-${Date.now()}`;
try {
(0, child_process_1.execSync)(`git checkout -b ${tempBranchName}`, {
cwd: CWD,
...execOpts,
});
const gitignorePath = path.join(CWD, '.gitignore');
await fs.writeFile(gitignorePath, constants_1.TAYLORED_DIR_NAME + '\n');
(0, child_process_1.execSync)(`git add .gitignore`, { cwd: CWD, ...execOpts });
const currentFileLines = (await fs.readFile(originalFilePath, 'utf-8')).split('\n');
currentFileLines.splice(startLineNum - 1, matchLinesCount);
await fs.writeFile(originalFilePath, currentFileLines.join('\n'));
(0, child_process_1.execSync)(`git add "${originalFilePath}"`, { cwd: CWD, ...execOpts });
(0, child_process_1.execSync)(`git commit -m "Temporary: Remove block ${numero} from ${path.basename(originalFilePath)}"`, { cwd: CWD, ...execOpts });
const relativeOriginalFilePath = path.relative(CWD, originalFilePath);
const diffCommand = `git diff --exit-code "${originalBranchName}" HEAD -- "${relativeOriginalFilePath}"`;
let diffContentForFile = '';
try {
(0, child_process_1.execSync)(diffCommand, {
cwd: CWD,
encoding: 'utf8',
stdio: 'pipe',
});
}
catch (e) {
if (e.status === 1 && typeof e.stdout === 'string') {
diffContentForFile = e.stdout;
}
else {
console.error(`CRITICAL ERROR: Failed to generate diff for non-compute block ${numero} (removal vs original branch '${originalBranchName}').`);
if (e.message)
console.error(` Error message: ${e.message}`);
if (e.stderr)
console.error(' STDERR:\n' + e.stderr.toString().trim());
if (e.stdout)
console.error(' STDOUT:\n' + e.stdout.toString().trim());
throw e;
}
}
const analysis = (0, utils_1.analyzeDiffContent)(diffContentForFile);
if (!analysis.success) {
console.error(`CRITICAL ERROR: Failed to analyze diff content for non-compute block ${numero}. ${analysis.errorMessage}`);
throw new Error(`Diff analysis failed for non-compute block ${numero}.`);
}
if (analysis.isPure &&
analysis.deletions > 0 &&
analysis.additions === 0) {
await fs.writeFile(targetTayloredFilePath, diffContentForFile);
console.log(`Successfully created ${targetTayloredFilePath} for block ${numero} from ${originalFilePath} (block removal vs original branch '${originalBranchName}')`);
}
else if (analysis.isPure &&
analysis.additions === 0 &&
analysis.deletions === 0) {
await fs.writeFile(targetTayloredFilePath, '');
console.log(`Block removal for ${numero} in ${originalFilePath} resulted in no textual changes against original branch '${originalBranchName}'. Empty taylored file created: ${targetTayloredFilePath}`);
}
else {
console.error(`CRITICAL ERROR: Diff for non-compute block ${numero} (removal vs original branch '${originalBranchName}') was not as expected (purely deletions or no change).`);
console.error(` Additions: ${analysis.additions}, Deletions: ${analysis.deletions}, IsPure: ${analysis.isPure}`);
const maxDiffPreviewLength = 1000;
const diffPreview = diffContentForFile.length > maxDiffPreviewLength
? diffContentForFile.substring(0, maxDiffPreviewLength) +
'\n... (diff truncated)'
: diffContentForFile;
if (diffContentForFile.trim())
console.error(` Diff content:\n${diffPreview}`);
throw new Error(`Unexpected diff characteristics for non-compute block ${numero}.`);
}
totalBlocksProcessed++;
}
catch (error) {
console.error(`CRITICAL ERROR: Failed to process block ${numero} from ${originalFilePath}.`);
if (error.message &&
!error.message.includes('Unexpected diff characteristics'))
console.error(`Error message: ${error.message}`);
throw error;
}
finally {
try {
(0, child_process_1.execSync)(`git checkout "${originalBranchName}"`, {
cwd: CWD,
stdio: 'ignore',
});
}
catch (checkoutError) {
console.warn(`Warning: Failed to checkout original branch '${originalBranchName}' during cleanup. May require manual cleanup. ${checkoutError.message}`);
}
try {
(0, child_process_1.execSync)(`git branch -D "${tempBranchName}"`, {
cwd: CWD,
stdio: 'ignore',
});
}
catch (deleteBranchError) {
console.warn(`Warning: Failed to delete temporary branch '${tempBranchName}' during cleanup. May require manual cleanup. ${deleteBranchError.message}`);
}
}
}
}
}
if (asyncScriptPromises.length > 0) {
console.log(`Executing ${asyncScriptPromises.length} asynchronous compute block(s) in parallel...`);
const results = await Promise.allSettled(asyncScriptPromises);
let succeededCount = 0;
let failedCount = 0;
results.forEach((result, index) => {
const blockIdentifier = `async block (index ${index})`;
if (result.status === 'fulfilled') {
console.log(`Asynchronous task for ${blockIdentifier} completed successfully.`);
succeededCount++;
}
else {
console.error(`Asynchronous task for ${blockIdentifier} failed: ${result.reason}`);
failedCount++;
}
});
console.log(`All asynchronous tasks have completed. Succeeded: ${succeededCount}, Failed: ${failedCount}.`);
}
if (totalBlocksProcessed === 0) {
console.log('No taylored blocks found matching the criteria in any of the scanned files.');
}
else {
if (asyncScriptPromises.length > 0) {
console.log(`Finished processing. Initiated ${totalBlocksProcessed} taylored block(s). See async summary for completion details.`);
}
else {
console.log(`Finished processing. Successfully created ${totalBlocksProcessed} taylored file(s).`);
}
}
}