UNPKG

reporemix

Version:

A opiniated repomix tool for Rust and NextJS projects.

431 lines (418 loc) 21.4 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; }; })(); 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();