lookatni-file-markers
Version:
Advanced file organization system with unique markers for code sharing and project management. Extract files from marked content, generate markers, and validate project integrity.
418 lines (417 loc) • 18.6 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.LookAtniGenerator = void 0;
const fs = __importStar(require("fs"));
const path = __importStar(require("path"));
const FS_CHAR = String.fromCharCode(28);
const MARKER_START = `//${FS_CHAR}/`;
const MARKER_END = `/${FS_CHAR}//`;
const colors = {
RED: '\x1b[0;31m',
GREEN: '\x1b[0;32m',
YELLOW: '\x1b[1;33m',
BLUE: '\x1b[0;34m',
PURPLE: '\x1b[0;35m',
CYAN: '\x1b[0;36m',
NC: '\x1b[0m'
};
class LookAtniGenerator {
constructor(options) {
this.options = options;
this.stats = {
totalFiles: 0,
processedFiles: 0,
skippedFiles: 0,
totalSize: 0,
outputSize: 0,
largestFile: { path: '', size: 0 }
};
}
printHelp() {
console.log(`${colors.CYAN}🔧 LookAtni Marker Generator v3.0${colors.NC}`);
console.log("================================================");
console.log("Gera arquivo com marcadores únicos a partir de estrutura existente");
console.log("");
console.log("Uso: tsx generateMarkers.ts [diretorio] [arquivo_saida] [opcoes]");
console.log("");
console.log("Parâmetros:");
console.log(" diretorio : Diretório para escanear (padrão: .)");
console.log(" arquivo_saida : Arquivo de saída (padrão: lookatni-code.txt)");
console.log("");
console.log("Opções:");
console.log(" -e, --exclude PATTERN : Excluir arquivos/diretórios (pode usar múltiplas vezes)");
console.log(" -i, --include PATTERN : Incluir apenas arquivos que correspondem ao padrão");
console.log(" -m, --max-size SIZE : Tamanho máximo do arquivo em KB (padrão: 1000)");
console.log(" -v, --verbose : Saída detalhada");
console.log(" -h, --help : Esta ajuda");
console.log("");
console.log("Exemplos:");
console.log(" tsx generateMarkers.ts ./src projeto.txt");
console.log(" tsx generateMarkers.ts . codigo.txt --exclude node_modules --exclude .git");
console.log(" tsx generateMarkers.ts ./meu-projeto saida.txt --include '*.js' --include '*.ts'");
console.log("");
console.log("💡 O arquivo gerado pode ser usado com:");
console.log(" tsx extractFiles.ts codigo.txt ./novo-projeto");
}
shouldExcludeFile(filePath) {
const fileName = path.basename(filePath);
const relativePath = filePath;
const defaultExclusions = [
'node_modules',
'.git',
'.vscode',
'.idea',
'dist',
'build',
'out',
'coverage',
'.nyc_output',
'package-lock.json',
'yarn.lock',
'pnpm-lock.yaml',
'.DS_Store',
'Thumbs.db',
'*.log',
'*.tmp',
'*.temp'
];
for (const pattern of defaultExclusions) {
if (pattern.includes('*')) {
const regex = new RegExp(pattern.replace(/\*/g, '.*'));
if (regex.test(fileName)) {
return true;
}
}
else if (relativePath.includes(pattern) || fileName === pattern) {
return true;
}
}
for (const pattern of this.options.excludePatterns) {
if (pattern.includes('*')) {
const regex = new RegExp(pattern.replace(/\*/g, '.*'));
if (regex.test(fileName) || regex.test(relativePath)) {
return true;
}
}
else if (relativePath.includes(pattern) || fileName === pattern) {
return true;
}
}
return false;
}
shouldIncludeFile(filePath) {
if (this.options.includePatterns.length === 0) {
return true;
}
const fileName = path.basename(filePath);
const relativePath = filePath;
for (const pattern of this.options.includePatterns) {
if (pattern.includes('*')) {
const regex = new RegExp(pattern.replace(/\*/g, '.*'));
if (regex.test(fileName) || regex.test(relativePath)) {
return true;
}
}
else if (relativePath.includes(pattern) || fileName === pattern) {
return true;
}
}
return false;
}
isTextFile(filePath) {
const textExtensions = [
'.txt', '.md', '.json', '.js', '.ts', '.tsx', '.jsx',
'.html', '.htm', '.css', '.scss', '.sass', '.less',
'.xml', '.yaml', '.yml', '.toml', '.ini', '.cfg',
'.py', '.java', '.c', '.cpp', '.h', '.hpp',
'.cs', '.php', '.rb', '.go', '.rs', '.swift',
'.kt', '.scala', '.clj', '.hs', '.elm', '.f#',
'.vb', '.sql', '.sh', '.bash', '.zsh', '.fish',
'.ps1', '.bat', '.cmd', '.dockerfile', '.gitignore',
'.gitattributes', '.editorconfig', '.prettierrc',
'.eslintrc', '.babelrc', '.stylelintrc'
];
const ext = path.extname(filePath).toLowerCase();
return textExtensions.includes(ext);
}
getAllFiles(dir, basePath = '') {
const files = [];
try {
const items = fs.readdirSync(dir, { withFileTypes: true });
for (const item of items) {
const fullPath = path.join(dir, item.name);
const relativePath = path.join(basePath, item.name);
if (this.shouldExcludeFile(relativePath)) {
if (this.options.verbose) {
console.log(`${colors.YELLOW}⏭️ Excluindo: ${relativePath}${colors.NC}`);
}
continue;
}
if (item.isDirectory()) {
files.push(...this.getAllFiles(fullPath, relativePath));
}
else if (item.isFile()) {
if (this.shouldIncludeFile(relativePath) && this.isTextFile(fullPath)) {
const stat = fs.statSync(fullPath);
const sizeKB = stat.size / 1024;
if (sizeKB <= this.options.maxSizeKB) {
files.push(relativePath);
this.stats.totalSize += stat.size;
if (stat.size > this.stats.largestFile.size) {
this.stats.largestFile = { path: relativePath, size: stat.size };
}
}
else {
if (this.options.verbose) {
console.log(`${colors.YELLOW}📏 Arquivo muito grande, pulando: ${relativePath} (${Math.round(sizeKB)}KB > ${this.options.maxSizeKB}KB)${colors.NC}`);
}
this.stats.skippedFiles++;
}
}
else if (this.options.verbose) {
console.log(`${colors.YELLOW}🚫 Não incluído: ${relativePath}${colors.NC}`);
}
}
}
}
catch (error) {
console.error(`${colors.RED}❌ Erro ao ler diretório ${dir}: ${error}${colors.NC}`);
}
return files;
}
async generateMarkers(sourceDir, outputFile) {
const startTime = Date.now();
console.log(`${colors.CYAN}🔧 LookAtni Marker Generator v3.0${colors.NC}`);
console.log("================================================");
if (!fs.existsSync(sourceDir)) {
throw new Error(`${colors.RED}❌ Erro: Diretório '${sourceDir}' não encontrado!${colors.NC}`);
}
if (!fs.statSync(sourceDir).isDirectory()) {
throw new Error(`${colors.RED}❌ Erro: '${sourceDir}' não é um diretório!${colors.NC}`);
}
console.log(`${colors.BLUE}📁 Diretório fonte: ${sourceDir}${colors.NC}`);
console.log(`${colors.BLUE}📄 Arquivo de saída: ${outputFile}${colors.NC}`);
console.log(`${colors.BLUE}📏 Tamanho máximo por arquivo: ${this.options.maxSizeKB}KB${colors.NC}`);
if (this.options.excludePatterns.length > 0) {
console.log(`${colors.BLUE}🚫 Padrões de exclusão: ${this.options.excludePatterns.join(', ')}${colors.NC}`);
}
if (this.options.includePatterns.length > 0) {
console.log(`${colors.BLUE}✅ Padrões de inclusão: ${this.options.includePatterns.join(', ')}${colors.NC}`);
}
console.log("");
console.log(`${colors.GREEN}🔍 Escaneando arquivos...${colors.NC}`);
const files = this.getAllFiles(sourceDir);
this.stats.totalFiles = files.length;
if (files.length === 0) {
throw new Error(`${colors.RED}❌ Nenhum arquivo encontrado para processar!${colors.NC}\n${colors.YELLOW}💡 Verifique os filtros de inclusão/exclusão${colors.NC}`);
}
console.log(`${colors.GREEN}📋 Encontrados ${files.length} arquivo(s) para processar${colors.NC}`);
console.log("");
if (this.options.verbose) {
console.log(`${colors.PURPLE}📁 Arquivos a serem processados:${colors.NC}`);
files.forEach((file, index) => {
console.log(` ${(index + 1).toString().padStart(3)}. ${file}`);
});
console.log("");
}
console.log(`${colors.GREEN}🚀 Gerando marcadores...${colors.NC}`);
let output = '';
output += `# LookAtni Code - Gerado automaticamente\n`;
output += `# Data: ${new Date().toISOString()}\n`;
output += `# Fonte: ${sourceDir}\n`;
output += `# Total de arquivos: ${files.length}\n`;
output += `\n`;
for (let i = 0; i < files.length; i++) {
const file = files[i];
const filePath = path.join(sourceDir, file);
console.log(`[${(i + 1).toString().padStart(3)}/${files.length}] 📄 ${file}`);
try {
const content = fs.readFileSync(filePath, 'utf-8');
output += `${MARKER_START} ${file} ${MARKER_END}\n`;
output += content;
if (!content.endsWith('\n')) {
output += '\n';
}
if (i < files.length - 1) {
output += '\n';
}
this.stats.processedFiles++;
if (this.options.verbose) {
const lines = content.split('\n').length;
const bytes = Buffer.byteLength(content, 'utf-8');
console.log(` ${colors.GREEN}✅ Adicionado (${lines} linhas, ${bytes} bytes)${colors.NC}`);
}
}
catch (error) {
console.log(` ${colors.RED}❌ Erro ao ler arquivo: ${error}${colors.NC}`);
this.stats.skippedFiles++;
}
}
try {
fs.writeFileSync(outputFile, output, 'utf-8');
this.stats.outputSize = Buffer.byteLength(output, 'utf-8');
console.log("");
console.log(`${colors.GREEN}💾 Arquivo salvo: ${outputFile}${colors.NC}`);
}
catch (error) {
throw new Error(`${colors.RED}❌ Erro ao salvar arquivo: ${error}${colors.NC}`);
}
const duration = Date.now() - startTime;
this.printSummary(outputFile, duration);
}
printSummary(outputFile, duration) {
console.log("");
console.log(`${colors.CYAN}🎉 Geração concluída em ${duration}ms!${colors.NC}`);
console.log("================================");
console.log(`${colors.PURPLE}📊 Resumo:${colors.NC}`);
console.log(` • ${colors.GREEN}✅ Processados: ${this.stats.processedFiles}${colors.NC}`);
console.log(` • ${colors.YELLOW}⏭️ Pulados: ${this.stats.skippedFiles}${colors.NC}`);
console.log(` • ${colors.BLUE}📁 Total encontrados: ${this.stats.totalFiles}${colors.NC}`);
console.log(` • ${colors.CYAN}💾 Tamanho de entrada: ${Math.round(this.stats.totalSize / 1024)}KB${colors.NC}`);
console.log(` • ${colors.CYAN}💾 Tamanho de saída: ${Math.round(this.stats.outputSize / 1024)}KB${colors.NC}`);
console.log(` • ${colors.PURPLE}📏 Maior arquivo: ${this.stats.largestFile.path} (${Math.round(this.stats.largestFile.size / 1024)}KB)${colors.NC}`);
console.log(` • ${colors.YELLOW}⏱️ Tempo: ${duration}ms${colors.NC}`);
console.log("");
console.log(`${colors.GREEN}🔍 Arquivo gerado: ${outputFile}${colors.NC}`);
console.log("");
console.log(`${colors.CYAN}🚀 Para extrair os arquivos:${colors.NC}`);
console.log(` ${colors.YELLOW}tsx extractFiles.ts ${outputFile} ./extracted${colors.NC}`);
console.log("");
console.log(`${colors.CYAN}🔧 Para visualizar marcadores:${colors.NC}`);
console.log(` ${colors.YELLOW}grep '^${MARKER_START.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')}' ${outputFile}${colors.NC}`);
console.log("");
console.log(`${colors.PURPLE}💡 Comandos úteis:${colors.NC}`);
console.log(` ${colors.YELLOW}# Contar marcadores:${colors.NC}`);
console.log(` grep '^${MARKER_START.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')}' ${outputFile} | wc -l`);
console.log(` ${colors.YELLOW}# Listar arquivos:${colors.NC}`);
console.log(` grep '^${MARKER_START.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')}' ${outputFile} | sed 's/^${MARKER_START.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')} \\(.*\\) ${MARKER_END.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')}$/\\1/'`);
console.log(` ${colors.YELLOW}# Validar formato:${colors.NC}`);
console.log(` tsx extractFiles.ts ${outputFile} --format`);
}
}
exports.LookAtniGenerator = LookAtniGenerator;
async function main() {
const args = process.argv.slice(2);
const options = {
excludePatterns: [],
includePatterns: [],
maxSizeKB: 1000,
verbose: false
};
let sourceDir = '.';
let outputFile = 'lookatni-code.txt';
for (let i = 0; i < args.length; i++) {
const arg = args[i];
switch (arg) {
case '-e':
case '--exclude':
if (i + 1 < args.length) {
options.excludePatterns.push(args[++i]);
}
else {
console.error(`❌ Padrão de exclusão necessário após ${arg}`);
process.exit(1);
}
break;
case '-i':
case '--include':
if (i + 1 < args.length) {
options.includePatterns.push(args[++i]);
}
else {
console.error(`❌ Padrão de inclusão necessário após ${arg}`);
process.exit(1);
}
break;
case '-m':
case '--max-size':
if (i + 1 < args.length) {
const size = parseInt(args[++i]);
if (isNaN(size) || size <= 0) {
console.error(`❌ Tamanho máximo inválido: ${args[i]}`);
process.exit(1);
}
options.maxSizeKB = size;
}
else {
console.error(`❌ Tamanho máximo necessário após ${arg}`);
process.exit(1);
}
break;
case '-v':
case '--verbose':
options.verbose = true;
break;
case '-h':
case '--help':
new LookAtniGenerator(options).printHelp();
process.exit(0);
break;
default:
if (arg.startsWith('-')) {
console.error(`❌ Opção desconhecida: ${arg}`);
console.error("Use --help para ajuda");
process.exit(1);
}
else {
if (sourceDir === '.') {
sourceDir = arg;
}
else if (outputFile === 'lookatni-code.txt') {
outputFile = arg;
}
else {
console.error("❌ Muitos argumentos!");
process.exit(1);
}
}
break;
}
}
try {
const generator = new LookAtniGenerator(options);
await generator.generateMarkers(sourceDir, outputFile);
process.exit(0);
}
catch (error) {
console.error(error instanceof Error ? error.message : String(error));
process.exit(1);
}
}
if (require.main === module) {
main().catch(console.error);
}