file-concat-tool
Version:
A tool for generating context windows for AI prompting by recursively searching for .ts and .tsx files and concatenating their contents into a text file.
116 lines (100 loc) • 3.9 kB
JavaScript
const fs = require("fs");
const path = require("path");
const chalk = require("chalk");
/**
* Recursively searches the root directory for source files with specified extensions,
* while ignoring the "node_modules" directory and files containing "config" in their name.
*
* @param {string} rootDir - The directory to search in.
* @returns {string[]} - An array of found file paths.
*/
function findSourceFiles(rootDir) {
const filesList = [];
const allowedExtensions = [
'.ts', '.tsx', // TypeScript
'.js', '.jsx', // JavaScript
'.html', // HTML
'.css', // CSS
'.scss', // Sass
// add more extensions here as needed (e.g., '.py', '.java', '.go')
];
function walk(dir) {
const entries = fs.readdirSync(dir, { withFileTypes: true });
for (const entry of entries) {
const fullPath = path.join(dir, entry.name);
if (entry.isDirectory()) {
if (entry.name === "node_modules") continue;
walk(fullPath);
} else if (entry.isFile()) {
const name = entry.name.toLowerCase();
if (name.includes("config")) continue;
const ext = path.extname(name);
if (allowedExtensions.includes(ext)) {
filesList.push(fullPath);
}
}
}
}
walk(rootDir);
return filesList;
}
/**
* Writes the paths and contents of found files into a text file.
*
* @param {string[]} filePaths - Array of file paths.
* @param {string} outputFile - Path to the output file.
*/
function writeFilesToTxt(filePaths, outputFile) {
let outputContent = `Generated by file-concat-tool by noluyorAbi\n`;
outputContent += `https://www.npmjs.com/package/file-concat-tool\n\n`;
filePaths.forEach((filePath) => {
try {
const content = fs.readFileSync(filePath, "utf8");
outputContent += `=== File: ${filePath} ===\n` + content + "\n\n";
console.log(chalk.gray(` • ${filePath}`));
} catch (error) {
console.error(chalk.red(`Error reading file ${filePath}: ${error.message}`));
}
});
fs.writeFileSync(outputFile, outputContent, "utf8");
console.log(chalk.green(`\n✔ Contents written to ${outputFile}`));
}
/**
* Updates the Git exclude file ('.git/info/exclude') to ignore the output file.
* Does nothing if the project is not a Git repo or the exclude file doesn't exist.
*
* @param {string} rootDir - The root directory of the project.
* @param {string} outputFile - The output file name to ignore.
*/
function updateGitExclude(rootDir, outputFile) {
const gitDir = path.join(rootDir, ".git");
const excludePath = path.join(gitDir, "info", "exclude");
if (!fs.existsSync(gitDir) || !fs.existsSync(excludePath)) {
console.log(chalk.yellow("No Git repository detected or exclude file missing, skipping exclude update."));
return;
}
let excludeContent = fs.readFileSync(excludePath, "utf8");
if (!excludeContent.includes(outputFile)) {
fs.appendFileSync(excludePath, `\n${outputFile}\n`, "utf8");
console.log(chalk.green(`✔ Added ${outputFile} to git exclude.`));
} else {
console.log(chalk.blue(`⚑ ${outputFile} already in git exclude.`));
}
}
/**
* Main function that searches the directory and writes the results to a text file.
*/
function main() {
const rootDirectory = process.cwd();
const outputTxt = "source_files_content.txt";
console.log(chalk.cyan.bold(`\n🕵️♂️ Searching for source files in:`), chalk.white(rootDirectory));
const files = findSourceFiles(rootDirectory);
console.log(chalk.cyan(`Found ${files.length} file${files.length === 1 ? '' : 's'}.`));
console.log(chalk.cyan(`\n✍️ Writing contents to ${outputTxt}...\n`));
writeFilesToTxt(files, outputTxt);
// Update Git exclude if applicable
updateGitExclude(rootDirectory, outputTxt);
console.log(chalk.magenta.bold(`\n🎉 Done!`));
}
main();