reporemix
Version:
A opiniated repomix tool for Rust and NextJS projects.
431 lines (418 loc) • 21.4 kB
JavaScript
"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;
};
})();
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
return new (P || (P = Promise))(function (resolve, reject) {
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
step((generator = generator.apply(thisArg, _arguments || [])).next());
});
};
Object.defineProperty(exports, "__esModule", { value: true });
const node_child_process_1 = require("node:child_process");
const fs = __importStar(require("node:fs/promises"));
const node_os_1 = require("node:os");
const path = __importStar(require("node:path"));
const node_util_1 = require("node:util");
const execPromise = (0, node_util_1.promisify)(node_child_process_1.exec);
// Path to the root of the project
const PROJECT_ROOT = path.resolve(__dirname, '..', '..');
// Path to our test output
const TEST_OUTPUT_PATH = path.join(PROJECT_ROOT, 'test-output.xml');
// Helper function to run the reporemix CLI command
function runReporemix(inputDir, outputFile, only) {
return __awaiter(this, void 0, void 0, function* () {
const cliPath = path.join(PROJECT_ROOT, 'dist', 'cli', 'index.js');
const onlyFlag = only ? `--only ${only}` : '';
try {
// Make sure the CLI is executable
yield execPromise(`chmod +x ${cliPath}`);
// Run the reporemix command
const { stdout, stderr } = yield execPromise(`node ${cliPath} ${inputDir} -o ${outputFile} ${onlyFlag}`);
if (stderr) {
console.error('Reporemix stderr:', stderr);
}
}
catch (error) {
console.error('Error running reporemix:', error);
throw error;
}
});
}
// Helper function to check if a file exists
function fileExists(filePath) {
return __awaiter(this, void 0, void 0, function* () {
try {
yield fs.access(filePath);
return true;
}
catch (_a) {
return false;
}
});
}
// Helper function to clean up test files
function cleanupTestFiles(testDir) {
return __awaiter(this, void 0, void 0, function* () {
try {
yield fs.rm(testDir, { recursive: true, force: true });
}
catch (error) {
console.error('Failed to clean up test files:', error);
}
});
}
// Helper to create test files with SVG, base64, and @xstate-layout content
function createTestFiles() {
return __awaiter(this, void 0, void 0, function* () {
// Create a unique temporary directory
const tmpDirPrefix = path.join((0, node_os_1.tmpdir)(), 'reporemix-test-');
const testDir = yield fs.mkdtemp(tmpDirPrefix);
try {
// Create a file with SVG content
const svgFile = path.join(testDir, 'icon.tsx');
const svgContent = `
import React from 'react';
const Icon = () => {
return (
<div>
<svg width="100" height="100" viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg">
<circle cx="50" cy="50" r="40" stroke="black" stroke-width="3" fill="red" />
</svg>
<p>This is a test SVG</p>
</div>
);
};
export default Icon;
`;
yield fs.writeFile(svgFile, svgContent);
// Create a file with simple base64 content
const base64File = path.join(testDir, 'image.tsx');
const base64Content = `
import React from 'react';
const Image = () => {
return (
<div>
<img src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mP8z8BQDwAEhQGAhKmMIQAAAABJRU5ErkJggg==" alt="Small PNG" />
<p>This is a test image with base64 data</p>
</div>
);
};
export default Image;
`;
yield fs.writeFile(base64File, base64Content);
// Create a file with @xstate-layout content
const xstateFile = path.join(testDir, 'machine.ts');
const xstateContent = `
import { createMachine } from 'xstate';
const toggleMachine = createMachine({
id: 'toggle',
initial: 'inactive',
states: {
inactive: {
on: { TOGGLE: 'active' }
},
active: {
on: { TOGGLE: 'inactive' }
}
}
// @xstate-layout: This is a comment that should be removed
// This is a normal comment that should be kept
});
export default toggleMachine;
`;
yield fs.writeFile(xstateFile, xstateContent);
// Create a JS file with long and short comments
const jsFile = path.join(testDir, 'test.js');
const jsContent = `
function shortComment() {
// This is a short comment.
console.log('Hello');
}
function longComment() {
// This is a very long single-line comment that definitely exceeds the seventy-five character limit imposed by our transformation script and should be truncated.
console.log('World');
}
/* This is a short multi-line comment. */
/*
* This is a very long multi-line comment that spans several lines and contains
* a lot of text, far exceeding the seventy-five character limit we have set.
* It should be significantly shortened by the script.
*/
function anotherFunction() {}
`;
yield fs.writeFile(jsFile, jsContent);
// Create a CSS file with long and short comments
const cssFile = path.join(testDir, 'test.css');
const cssContent = `
.short-comment {
/* Short comment */
color: blue;
}
.long-comment {
/* This is an extremely long CSS comment designed specifically to test the truncation functionality of the reporemix script, ensuring it correctly handles comments over 75 chars. */
color: red;
}
`;
yield fs.writeFile(cssFile, cssContent);
// Create an HTML file with long and short comments
const htmlFile = path.join(testDir, 'test.html');
const htmlContent = `
<!DOCTYPE html>
<html>
<head>
<title>Test Page</title>
<!-- Short comment -->
</head>
<body>
<h1>Hello</h1>
<!-- This is a very, very, very long HTML comment that goes on and on and on, well past the 75-character limit, so we expect it to be truncated. -->
<p>World</p>
</body>
</html>
`;
yield fs.writeFile(htmlFile, htmlContent);
// Create a Python file with long and short comments
const pyFile = path.join(testDir, 'test.py');
const pyContent = `
# Short Python comment
def short_comment():
print("Hello")
# This is an exceptionally long Python comment that rambles on quite a bit, far exceeding the seventy-five character threshold we have established for truncation testing purposes.
def long_comment():
print("World")
# Indented long comment that is very, very long and should be truncated while preserving the indent
# Another short one
`;
yield fs.writeFile(pyFile, pyContent);
// Create a Rust file
const rustFile = path.join(testDir, 'main.rs');
const rustContent = `
fn main() {
// A simple Rust program
println!("Hello, world!");
}
`;
yield fs.writeFile(rustFile, rustContent);
// Create a Cargo.toml file
const cargoFile = path.join(testDir, 'Cargo.toml');
const cargoContent = `
[package]
name = "test"
version = "0.1.0"
edition = "2021"
`;
yield fs.writeFile(cargoFile, cargoContent);
return testDir;
}
catch (error) {
// Clean up on failure
yield cleanupTestFiles(testDir);
throw error;
}
});
}
// Main test function
function runTest() {
return __awaiter(this, void 0, void 0, function* () {
let testsFailed = false;
let testDir = null;
try {
// Create test files
testDir = yield createTestFiles();
console.log(`Created test files in: ${testDir}`);
// Run reporemix on test files
yield runReporemix(testDir, TEST_OUTPUT_PATH);
// Read the output content
const outputContent = yield fs.readFile(TEST_OUTPUT_PATH, 'utf-8');
// --- SVG Replacement Tests ---
const svgReplacedCorrectly = outputContent.includes('>[Removed]</svg>') && !outputContent.includes('<circle cx="50" cy="50"');
// --- Base64 Shortening Tests ---
// Simple base64 check
const simpleBase64Original = 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mP8z8BQDwAEhQGAhKmMIQAAAABJRU5ErkJggg==';
const simpleBase64Correct = (outputContent.includes('data:image/png;base64,iVBOR...g==') ||
outputContent.includes('data:image/png;base64,iVBORw...ggg==')) &&
!outputContent.includes(simpleBase64Original);
// Complex base64 check (inside JSON)
const complexBase64Content = JSON.stringify({
image: 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mP8z8BQDwAEhQGAhKmMIQAAAABJRU5ErkJggg==',
});
const complexBase64Correct = !outputContent.includes(complexBase64Content);
// Combine Base64 checks
const base64ShortenedCorrectly = simpleBase64Correct && complexBase64Correct;
// Check for @xstate-layout removal
const xstateRemovedCorrectly = !outputContent.includes('@xstate-layout') &&
outputContent.includes('// This is a normal comment that should be kept');
// --- JS Checks --- (Check within test.js file block)
const jsFileBlockRegex = /<file path="test\.js">\s*<content><!\[CDATA\[([\s\S]*?)]]><\/content>/;
const jsMatch = outputContent.match(jsFileBlockRegex);
const jsContentOutput = jsMatch ? jsMatch[1] : '';
// Long single-line comment check (approximate length check + ellipsis)
const jsLongSingleLineOriginalContent = 'This is a very long single-line comment that definitely exceeds the seventy-five character limit imposed by our transformation script and should be truncated.';
const jsLongSingleLineOriginalFullLine = ' // This is a very long single-line comment that definitely exceeds the seve'; // More flexible start
const jsLongSingleLineCorrect = !jsContentOutput.includes(jsLongSingleLineOriginalContent) &&
jsContentOutput.includes('// This is a very long single-line comment that definitely exceeds the...');
// Short single-line comment check
const jsShortSingleLineOriginal = '// This is a short comment.';
const jsShortSingleLineCorrect = jsContentOutput.includes(jsShortSingleLineOriginal);
// Long multi-line comment check
const jsLongMultiLineOriginal = 'This is a very long multi-line comment that spans several lines and contains\n * a lot of text, far exceeding the seventy-five character limit we have set.';
const jsLongMultiLineCorrect = !jsContentOutput.includes(jsLongMultiLineOriginal) &&
jsContentOutput.includes(' * This is a very long multi-line comment that spans several lin...');
// Short multi-line comment check
const jsShortMultiLineOriginal = '/* This is a short multi-line comment. */';
const jsShortMultiLineCorrect = jsContentOutput.includes(jsShortMultiLineOriginal);
const jsCommentsCorrect = jsLongSingleLineCorrect && jsShortSingleLineCorrect && jsLongMultiLineCorrect && jsShortMultiLineCorrect;
// --- CSS Checks --- (Check within test.css file block)
const cssFileBlockRegex = /<file path="test\.css">\s*<content><!\[CDATA\[([\s\S]*?)]]><\/content>/;
const cssMatch = outputContent.match(cssFileBlockRegex);
const cssContentOutput = cssMatch ? cssMatch[1] : '';
const cssLongOriginal = '/* This is an extremely long CSS comment designed specifically to test the truncation functionality of the reporemix script, ensuring it correctly handles comments over 75 chars. */';
const cssShortOriginal = '/* Short comment */';
const cssCommentsCorrect = !cssContentOutput.includes(cssLongOriginal) &&
cssContentOutput.includes(cssShortOriginal) &&
cssContentOutput.includes('/* This is an extremely long CSS comment designed specifically to te...*/');
// --- HTML Checks --- (Check within test.html file block)
const htmlFileBlockRegex = /<file path="test\.html">\s*<content><!\[CDATA\[([\s\S]*?)]]><\/content>/;
const htmlMatch = outputContent.match(htmlFileBlockRegex);
const htmlContentOutput = htmlMatch ? htmlMatch[1] : '';
const htmlLongOriginal = '<!-- This is a very, very, very long HTML comment that goes on and on and on, well past the 75-character limit, so we expect it to be truncated. -->';
const htmlShortOriginal = '<!-- Short comment -->';
const htmlCommentsCorrect = !htmlContentOutput.includes(htmlLongOriginal) &&
htmlContentOutput.includes(htmlShortOriginal) &&
htmlContentOutput.includes('<!-- This is a very, very, very long HTML comment that goes on and... -->');
// --- Python Checks --- (Check within test.py file block)
const pyFileBlockRegex = /<file path="test\.py">\s*<content><!\[CDATA\[([\s\S]*?)\]\]><\/content>/;
const pyMatch = outputContent.match(pyFileBlockRegex);
const pyContentOutput = pyMatch ? pyMatch[1] : '';
const pyLongOriginal = '# This is an exceptionally long Python comment that rambles on quite a bit, far exceeding the seventy-five character threshold we have established for truncation testing purposes.';
const pyIndentedOriginal = ' # Indented long comment that is very, very long and should be truncated while preserving the indent';
const pyShortOriginal1 = '# Short Python comment';
const pyShortOriginal2 = '# Another short one';
const pyCommentsCorrect = !pyContentOutput.includes(pyLongOriginal) &&
!pyContentOutput.includes(pyIndentedOriginal) &&
pyContentOutput.includes(pyShortOriginal1) &&
pyContentOutput.includes(pyShortOriginal2) &&
pyContentOutput.includes('This is an exceptionally long Python comment that rambles on quite a b...') &&
pyContentOutput.includes('# Indented long comment that is very, very long and should be truncate...');
const allCommentsShortenedCorrectly = jsCommentsCorrect && cssCommentsCorrect && htmlCommentsCorrect && pyCommentsCorrect;
// --- --only flag test ---
const onlyTestOutputFile = path.join(PROJECT_ROOT, 'test-output-only.xml');
yield runReporemix(testDir, onlyTestOutputFile, 'rust');
const onlyOutputContent = yield fs.readFile(onlyTestOutputFile, 'utf-8');
const onlyRustCorrect = onlyOutputContent.includes('<file path="main.rs">') &&
onlyOutputContent.includes('<file path="Cargo.toml">') &&
!onlyOutputContent.includes('<file path="test.js">') &&
!onlyOutputContent.includes('<file path="test.py">');
if (yield fileExists(onlyTestOutputFile)) {
yield fs.unlink(onlyTestOutputFile);
}
// Create a file with Base64 in comment and in string
const base64TestFile = path.join(testDir, 'base64Test.js');
const base64TestContent = `
// data:image/png;base64,VERYLONGBASE64STRINGTHATISOVER75CHARACTERSANDMUCHMORETOMAKESUREITSLONGENOUGH==
const image = 'data:image/png;base64,VERYLONGBASE64STRINGTHATISOVER75CHARACTERSANDMUCHMORETOMAKESUREITSLONGENOUGH==';
console.log(image);
`;
yield fs.writeFile(base64TestFile, base64TestContent);
// Re-run to test the new file
yield runReporemix(testDir, TEST_OUTPUT_PATH);
const updatedOutputContent = yield fs.readFile(TEST_OUTPUT_PATH, 'utf-8');
// Check for Base64 in comment vs string
const base64TestBlockRegex = /<file path="base64Test\.js">\s*<content><!\[CDATA\[([\s\S]*?)\]\]><\/content>/;
const base64TestMatch = updatedOutputContent.match(base64TestBlockRegex);
const base64TestOutput = base64TestMatch ? base64TestMatch[1] : '';
const commentBase64ShortenedByCommentShortener = base64TestOutput.includes('// data:image/png;base64,VERYLONGBASE64STRINGTHATISOVER75CHARACTERSANDMU...');
const stringBase64Shortened = base64TestOutput.includes("'data:image/png;base64,VERYL...GH=='") &&
!base64TestOutput.includes('VERYLONGBASE64STRINGTHATISOVER75CHARACTERSANDMUCHMORETOMAKESUREITSLONGENOUGH==', base64TestOutput.indexOf('const image'));
const base64DetectionCorrect = commentBase64ShortenedByCommentShortener && stringBase64Shortened;
// Print test results
console.log('\n--- Test Results ---');
console.log(`SVG Replacement: ${svgReplacedCorrectly ? 'PASSED' : 'FAILED'}`);
console.log(`Simple Base64 Shortening: ${simpleBase64Correct ? 'PASSED' : 'FAILED'}`);
console.log(`Complex Base64 Shortening: ${complexBase64Correct ? 'PASSED' : 'FAILED'}`);
console.log(`@xstate-layout Removal: ${xstateRemovedCorrectly ? 'PASSED' : 'FAILED'}`);
console.log(`JS Comment Shortening: ${jsCommentsCorrect ? 'PASSED' : 'FAILED'}`);
console.log(`CSS Comment Shortening: ${cssCommentsCorrect ? 'PASSED' : 'FAILED'}`);
console.log(`HTML Comment Shortening: ${htmlCommentsCorrect ? 'PASSED' : 'FAILED'}`);
console.log(`Python Comment Shortening: ${pyCommentsCorrect ? 'PASSED' : 'FAILED'}`);
console.log(`Language Filtering (--only): ${onlyRustCorrect ? 'PASSED' : 'FAILED'}`);
console.log(`Base64 Detection (comment vs string): ${base64DetectionCorrect ? 'PASSED' : 'FAILED'}`);
if (svgReplacedCorrectly &&
base64ShortenedCorrectly &&
xstateRemovedCorrectly &&
allCommentsShortenedCorrectly &&
onlyRustCorrect &&
base64DetectionCorrect) {
console.log('\nALL TESTS PASSED!');
}
else {
console.log('\nSome tests FAILED');
console.log(`\nInspect the output file for details: ${TEST_OUTPUT_PATH}`);
testsFailed = true; // Set failure flag
}
}
catch (error) {
console.error('Test failed:', error);
console.log(`\nInspect the output file for details: ${TEST_OUTPUT_PATH}`);
testsFailed = true; // Set failure flag
}
finally {
// Clean up temporary test files
if (testDir) {
yield cleanupTestFiles(testDir);
}
else {
console.warn('No temporary directory was created, skipping cleanup.');
}
// Clean up the output file ONLY if all tests passed
try {
if (!testsFailed && (yield fileExists(TEST_OUTPUT_PATH))) {
yield fs.unlink(TEST_OUTPUT_PATH);
console.log('All tests passed, test output file cleaned up.');
}
}
catch (cleanupError) {
console.error('Failed to clean up output file:', cleanupError);
}
}
// Exit with error code if tests failed
if (testsFailed) {
process.exit(1);
}
});
}
runTest();