mirror of
https://github.com/yamadashy/repomix.git
synced 2025-06-11 00:25:54 +03:00
Fixes from linter
This commit is contained in:
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "Repomix",
|
||||
"image": "mcr.microsoft.com/devcontainers/typescript-node:1-22-bullseye",
|
||||
"runArgs": ["--name", "repomix-devcontainer"],
|
||||
"postCreateCommand": "npm install"
|
||||
"name": "Repomix",
|
||||
"image": "mcr.microsoft.com/devcontainers/typescript-node:1-22-bullseye",
|
||||
"runArgs": ["--name", "repomix-devcontainer"],
|
||||
"postCreateCommand": "npm install"
|
||||
}
|
||||
|
||||
@@ -1,15 +1,12 @@
|
||||
import fs from "node:fs/promises";
|
||||
import path from "node:path";
|
||||
import { globby } from "globby";
|
||||
import { minimatch } from "minimatch";
|
||||
import type { RepomixConfigMerged } from "../../config/configSchema.js";
|
||||
import { defaultIgnoreList } from "../../config/defaultIgnore.js";
|
||||
import { logger } from "../../shared/logger.js";
|
||||
import { sortPaths } from "./filePathSort.js";
|
||||
import {
|
||||
PermissionError,
|
||||
checkDirectoryPermissions,
|
||||
} from "./permissionCheck.js";
|
||||
import fs from 'node:fs/promises';
|
||||
import path from 'node:path';
|
||||
import { globby } from 'globby';
|
||||
import { minimatch } from 'minimatch';
|
||||
import type { RepomixConfigMerged } from '../../config/configSchema.js';
|
||||
import { defaultIgnoreList } from '../../config/defaultIgnore.js';
|
||||
import { logger } from '../../shared/logger.js';
|
||||
import { sortPaths } from './filePathSort.js';
|
||||
import { PermissionError, checkDirectoryPermissions } from './permissionCheck.js';
|
||||
|
||||
export interface FileSearchResult {
|
||||
filePaths: string[];
|
||||
@@ -19,7 +16,7 @@ export interface FileSearchResult {
|
||||
const findEmptyDirectories = async (
|
||||
rootDir: string,
|
||||
directories: string[],
|
||||
ignorePatterns: string[]
|
||||
ignorePatterns: string[],
|
||||
): Promise<string[]> => {
|
||||
const emptyDirs: string[] = [];
|
||||
|
||||
@@ -27,15 +24,11 @@ const findEmptyDirectories = async (
|
||||
const fullPath = path.join(rootDir, dir);
|
||||
try {
|
||||
const entries = await fs.readdir(fullPath);
|
||||
const hasVisibleContents = entries.some(
|
||||
(entry) => !entry.startsWith(".")
|
||||
);
|
||||
const hasVisibleContents = entries.some((entry) => !entry.startsWith('.'));
|
||||
|
||||
if (!hasVisibleContents) {
|
||||
// This checks if the directory itself matches any ignore patterns
|
||||
const shouldIgnore = ignorePatterns.some(
|
||||
(pattern) => minimatch(dir, pattern) || minimatch(`${dir}/`, pattern)
|
||||
);
|
||||
const shouldIgnore = ignorePatterns.some((pattern) => minimatch(dir, pattern) || minimatch(`${dir}/`, pattern));
|
||||
|
||||
if (!shouldIgnore) {
|
||||
emptyDirs.push(dir);
|
||||
@@ -50,10 +43,7 @@ const findEmptyDirectories = async (
|
||||
};
|
||||
|
||||
// Get all file paths considering the config
|
||||
export const searchFiles = async (
|
||||
rootDir: string,
|
||||
config: RepomixConfigMerged
|
||||
): Promise<FileSearchResult> => {
|
||||
export const searchFiles = async (rootDir: string, config: RepomixConfigMerged): Promise<FileSearchResult> => {
|
||||
// First check directory permissions
|
||||
const permissionCheck = await checkDirectoryPermissions(rootDir);
|
||||
|
||||
@@ -61,12 +51,10 @@ export const searchFiles = async (
|
||||
if (permissionCheck.error instanceof PermissionError) {
|
||||
throw permissionCheck.error;
|
||||
}
|
||||
throw new Error(
|
||||
`Cannot access directory ${rootDir}: ${permissionCheck.error?.message}`
|
||||
);
|
||||
throw new Error(`Cannot access directory ${rootDir}: ${permissionCheck.error?.message}`);
|
||||
}
|
||||
|
||||
const includePatterns = config.include.length > 0 ? config.include : ["**/*"];
|
||||
const includePatterns = config.include.length > 0 ? config.include : ['**/*'];
|
||||
|
||||
try {
|
||||
const [ignorePatterns, ignoreFilePatterns] = await Promise.all([
|
||||
@@ -74,9 +62,9 @@ export const searchFiles = async (
|
||||
getIgnoreFilePatterns(config),
|
||||
]);
|
||||
|
||||
logger.trace("Include patterns:", includePatterns);
|
||||
logger.trace("Ignore patterns:", ignorePatterns);
|
||||
logger.trace("Ignore file patterns:", ignoreFilePatterns);
|
||||
logger.trace('Include patterns:', includePatterns);
|
||||
logger.trace('Ignore patterns:', ignorePatterns);
|
||||
logger.trace('Ignore file patterns:', ignoreFilePatterns);
|
||||
|
||||
const filePaths = await globby(includePatterns, {
|
||||
cwd: rootDir,
|
||||
@@ -88,10 +76,10 @@ export const searchFiles = async (
|
||||
followSymbolicLinks: false,
|
||||
}).catch((error) => {
|
||||
// Handle EPERM errors specifically
|
||||
if (error.code === "EPERM" || error.code === "EACCES") {
|
||||
if (error.code === 'EPERM' || error.code === 'EACCES') {
|
||||
throw new PermissionError(
|
||||
"Permission denied while scanning directory. Please check folder access permissions for your terminal app.",
|
||||
rootDir
|
||||
'Permission denied while scanning directory. Please check folder access permissions for your terminal app.',
|
||||
rootDir,
|
||||
);
|
||||
}
|
||||
throw error;
|
||||
@@ -109,11 +97,7 @@ export const searchFiles = async (
|
||||
followSymbolicLinks: false,
|
||||
});
|
||||
|
||||
emptyDirPaths = await findEmptyDirectories(
|
||||
rootDir,
|
||||
directories,
|
||||
ignorePatterns
|
||||
);
|
||||
emptyDirPaths = await findEmptyDirectories(rootDir, directories, ignorePatterns);
|
||||
}
|
||||
|
||||
logger.trace(`Filtered ${filePaths.length} files`);
|
||||
@@ -129,52 +113,45 @@ export const searchFiles = async (
|
||||
}
|
||||
|
||||
if (error instanceof Error) {
|
||||
logger.error("Error filtering files:", error.message);
|
||||
throw new Error(
|
||||
`Failed to filter files in directory ${rootDir}. Reason: ${error.message}`
|
||||
);
|
||||
logger.error('Error filtering files:', error.message);
|
||||
throw new Error(`Failed to filter files in directory ${rootDir}. Reason: ${error.message}`);
|
||||
}
|
||||
|
||||
logger.error("An unexpected error occurred:", error);
|
||||
throw new Error("An unexpected error occurred while filtering files.");
|
||||
logger.error('An unexpected error occurred:', error);
|
||||
throw new Error('An unexpected error occurred while filtering files.');
|
||||
}
|
||||
};
|
||||
|
||||
export const parseIgnoreContent = (content: string): string[] => {
|
||||
if (!content) return [];
|
||||
|
||||
return content.split("\n").reduce<string[]>((acc, line) => {
|
||||
return content.split('\n').reduce<string[]>((acc, line) => {
|
||||
const trimmedLine = line.trim();
|
||||
if (trimmedLine && !trimmedLine.startsWith("#")) {
|
||||
if (trimmedLine && !trimmedLine.startsWith('#')) {
|
||||
acc.push(trimmedLine);
|
||||
}
|
||||
return acc;
|
||||
}, []);
|
||||
};
|
||||
|
||||
export const getIgnoreFilePatterns = async (
|
||||
config: RepomixConfigMerged
|
||||
): Promise<string[]> => {
|
||||
export const getIgnoreFilePatterns = async (config: RepomixConfigMerged): Promise<string[]> => {
|
||||
const ignoreFilePatterns: string[] = [];
|
||||
|
||||
if (config.ignore.useGitignore) {
|
||||
ignoreFilePatterns.push("**/.gitignore");
|
||||
ignoreFilePatterns.push('**/.gitignore');
|
||||
}
|
||||
|
||||
ignoreFilePatterns.push("**/.repomixignore");
|
||||
ignoreFilePatterns.push('**/.repomixignore');
|
||||
|
||||
return ignoreFilePatterns;
|
||||
};
|
||||
|
||||
export const getIgnorePatterns = async (
|
||||
rootDir: string,
|
||||
config: RepomixConfigMerged
|
||||
): Promise<string[]> => {
|
||||
export const getIgnorePatterns = async (rootDir: string, config: RepomixConfigMerged): Promise<string[]> => {
|
||||
const ignorePatterns = new Set<string>();
|
||||
|
||||
// Add default ignore patterns
|
||||
if (config.ignore.useDefaultPatterns) {
|
||||
logger.trace("Adding default ignore patterns");
|
||||
logger.trace('Adding default ignore patterns');
|
||||
for (const pattern of defaultIgnoreList) {
|
||||
ignorePatterns.add(pattern);
|
||||
}
|
||||
@@ -182,19 +159,13 @@ export const getIgnorePatterns = async (
|
||||
|
||||
// Add repomix output file
|
||||
if (config.output.filePath) {
|
||||
logger.trace(
|
||||
"Adding output file to ignore patterns:",
|
||||
config.output.filePath
|
||||
);
|
||||
logger.trace('Adding output file to ignore patterns:', config.output.filePath);
|
||||
ignorePatterns.add(config.output.filePath);
|
||||
}
|
||||
|
||||
// Add custom ignore patterns
|
||||
if (config.ignore.customPatterns) {
|
||||
logger.trace(
|
||||
"Adding custom ignore patterns:",
|
||||
config.ignore.customPatterns
|
||||
);
|
||||
logger.trace('Adding custom ignore patterns:', config.ignore.customPatterns);
|
||||
for (const pattern of config.ignore.customPatterns) {
|
||||
ignorePatterns.add(pattern);
|
||||
}
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
import type { ProcessedFile } from "../file/fileTypes.js";
|
||||
import type { TokenCounter } from "../tokenCount/tokenCount.js";
|
||||
import type { FileMetrics } from "./calculateIndividualFileMetrics.js";
|
||||
import type { ProcessedFile } from '../file/fileTypes.js';
|
||||
import type { TokenCounter } from '../tokenCount/tokenCount.js';
|
||||
import type { FileMetrics } from './calculateIndividualFileMetrics.js';
|
||||
|
||||
export const aggregateMetrics = (
|
||||
fileMetrics: FileMetrics[],
|
||||
processedFiles: ProcessedFile[],
|
||||
output: string,
|
||||
tokenCounter: TokenCounter
|
||||
tokenCounter: TokenCounter,
|
||||
) => {
|
||||
const totalFiles = processedFiles.length;
|
||||
const totalCharacters = output.length;
|
||||
|
||||
@@ -1,30 +1,20 @@
|
||||
import pMap from "p-map";
|
||||
import { getProcessConcurrency } from "../../shared/processConcurrency.js";
|
||||
import type { RepomixProgressCallback } from "../../shared/types.js";
|
||||
import type { ProcessedFile } from "../file/fileTypes.js";
|
||||
import type { TokenCounter } from "../tokenCount/tokenCount.js";
|
||||
import {
|
||||
calculateIndividualFileMetrics,
|
||||
type FileMetrics,
|
||||
} from "./calculateIndividualFileMetrics.js";
|
||||
import pMap from 'p-map';
|
||||
import { getProcessConcurrency } from '../../shared/processConcurrency.js';
|
||||
import type { RepomixProgressCallback } from '../../shared/types.js';
|
||||
import type { ProcessedFile } from '../file/fileTypes.js';
|
||||
import type { TokenCounter } from '../tokenCount/tokenCount.js';
|
||||
import { type FileMetrics, calculateIndividualFileMetrics } from './calculateIndividualFileMetrics.js';
|
||||
|
||||
export const calculateAllFileMetrics = async (
|
||||
processedFiles: ProcessedFile[],
|
||||
tokenCounter: TokenCounter,
|
||||
progressCallback: RepomixProgressCallback
|
||||
progressCallback: RepomixProgressCallback,
|
||||
): Promise<FileMetrics[]> => {
|
||||
return await pMap(
|
||||
processedFiles,
|
||||
(file, index) =>
|
||||
calculateIndividualFileMetrics(
|
||||
file,
|
||||
index,
|
||||
processedFiles.length,
|
||||
tokenCounter,
|
||||
progressCallback
|
||||
),
|
||||
(file, index) => calculateIndividualFileMetrics(file, index, processedFiles.length, tokenCounter, progressCallback),
|
||||
{
|
||||
concurrency: getProcessConcurrency(),
|
||||
}
|
||||
},
|
||||
);
|
||||
};
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
import { setTimeout } from "node:timers/promises";
|
||||
import pc from "picocolors";
|
||||
import { RepomixProgressCallback } from "../../shared/types.js";
|
||||
import type { ProcessedFile } from "../file/fileTypes.js";
|
||||
import type { TokenCounter } from "../tokenCount/tokenCount.js";
|
||||
import { setTimeout } from 'node:timers/promises';
|
||||
import pc from 'picocolors';
|
||||
import type { RepomixProgressCallback } from '../../shared/types.js';
|
||||
import type { ProcessedFile } from '../file/fileTypes.js';
|
||||
import type { TokenCounter } from '../tokenCount/tokenCount.js';
|
||||
|
||||
export interface FileMetrics {
|
||||
path: string;
|
||||
@@ -15,14 +15,12 @@ export const calculateIndividualFileMetrics = async (
|
||||
index: number,
|
||||
totalFiles: number,
|
||||
tokenCounter: TokenCounter,
|
||||
progressCallback: RepomixProgressCallback
|
||||
progressCallback: RepomixProgressCallback,
|
||||
): Promise<FileMetrics> => {
|
||||
const charCount = file.content.length;
|
||||
const tokenCount = tokenCounter.countTokens(file.content, file.path);
|
||||
|
||||
progressCallback(
|
||||
`Calculating metrics... (${index + 1}/${totalFiles}) ${pc.dim(file.path)}`
|
||||
);
|
||||
progressCallback(`Calculating metrics... (${index + 1}/${totalFiles}) ${pc.dim(file.path)}`);
|
||||
|
||||
// Sleep for a short time to prevent blocking the event loop
|
||||
await setTimeout(1);
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
import { RepomixProgressCallback } from "../../shared/types.js";
|
||||
import { type ProcessedFile } from "../file/fileTypes.js";
|
||||
import { TokenCounter } from "../tokenCount/tokenCount.js";
|
||||
import { aggregateMetrics } from "./aggregateMetrics.js";
|
||||
import { calculateAllFileMetrics } from "./calculateAllFileMetrics.js";
|
||||
import type { RepomixProgressCallback } from '../../shared/types.js';
|
||||
import type { ProcessedFile } from '../file/fileTypes.js';
|
||||
import { TokenCounter } from '../tokenCount/tokenCount.js';
|
||||
import { aggregateMetrics } from './aggregateMetrics.js';
|
||||
import { calculateAllFileMetrics } from './calculateAllFileMetrics.js';
|
||||
|
||||
export interface CalculateMetricsResult {
|
||||
totalFiles: number;
|
||||
@@ -15,23 +15,14 @@ export interface CalculateMetricsResult {
|
||||
export const calculateMetrics = async (
|
||||
processedFiles: ProcessedFile[],
|
||||
output: string,
|
||||
progressCallback: RepomixProgressCallback
|
||||
progressCallback: RepomixProgressCallback,
|
||||
): Promise<CalculateMetricsResult> => {
|
||||
const tokenCounter = new TokenCounter();
|
||||
|
||||
progressCallback("Calculating metrics...");
|
||||
const fileMetrics = await calculateAllFileMetrics(
|
||||
processedFiles,
|
||||
tokenCounter,
|
||||
progressCallback
|
||||
);
|
||||
progressCallback('Calculating metrics...');
|
||||
const fileMetrics = await calculateAllFileMetrics(processedFiles, tokenCounter, progressCallback);
|
||||
|
||||
const result = aggregateMetrics(
|
||||
fileMetrics,
|
||||
processedFiles,
|
||||
output,
|
||||
tokenCounter
|
||||
);
|
||||
const result = aggregateMetrics(fileMetrics, processedFiles, output, tokenCounter);
|
||||
|
||||
tokenCounter.free();
|
||||
|
||||
|
||||
@@ -1,14 +1,14 @@
|
||||
import type { RepomixConfigMerged } from "../config/configSchema.js";
|
||||
import type { RepomixProgressCallback } from "../shared/types.js";
|
||||
import { collectFiles } from "./file/fileCollect.js";
|
||||
import { processFiles } from "./file/fileProcess.js";
|
||||
import { searchFiles } from "./file/fileSearch.js";
|
||||
import { calculateMetrics } from "./metrics/calculateMetrics.js";
|
||||
import { generateOutput } from "./output/outputGenerate.js";
|
||||
import { copyToClipboardIfEnabled } from "./packager/copyToClipboardIfEnabled.js";
|
||||
import { writeOutputToDisk } from "./packager/writeOutputToDisk.js";
|
||||
import type { SuspiciousFileResult } from "./security/securityCheck.js";
|
||||
import { validateFileSafety } from "./security/validateFileSafety.js";
|
||||
import type { RepomixConfigMerged } from '../config/configSchema.js';
|
||||
import type { RepomixProgressCallback } from '../shared/types.js';
|
||||
import { collectFiles } from './file/fileCollect.js';
|
||||
import { processFiles } from './file/fileProcess.js';
|
||||
import { searchFiles } from './file/fileSearch.js';
|
||||
import { calculateMetrics } from './metrics/calculateMetrics.js';
|
||||
import { generateOutput } from './output/outputGenerate.js';
|
||||
import { copyToClipboardIfEnabled } from './packager/copyToClipboardIfEnabled.js';
|
||||
import { writeOutputToDisk } from './packager/writeOutputToDisk.js';
|
||||
import type { SuspiciousFileResult } from './security/securityCheck.js';
|
||||
import { validateFileSafety } from './security/validateFileSafety.js';
|
||||
|
||||
export interface PackResult {
|
||||
totalFiles: number;
|
||||
@@ -32,39 +32,33 @@ export const pack = async (
|
||||
writeOutputToDisk,
|
||||
copyToClipboardIfEnabled,
|
||||
calculateMetrics,
|
||||
}
|
||||
},
|
||||
): Promise<PackResult> => {
|
||||
progressCallback("Searching for files...");
|
||||
progressCallback('Searching for files...');
|
||||
const { filePaths } = await deps.searchFiles(rootDir, config);
|
||||
|
||||
progressCallback("Collecting files...");
|
||||
progressCallback('Collecting files...');
|
||||
const rawFiles = await deps.collectFiles(filePaths, rootDir);
|
||||
|
||||
const { safeFilePaths, safeRawFiles, suspiciousFilesResults } =
|
||||
await deps.validateFileSafety(rawFiles, progressCallback, config);
|
||||
|
||||
// Process files (remove comments, etc.)
|
||||
progressCallback("Processing files...");
|
||||
const processedFiles = await deps.processFiles(safeRawFiles, config);
|
||||
|
||||
progressCallback("Generating output...");
|
||||
const output = await deps.generateOutput(
|
||||
rootDir,
|
||||
const { safeFilePaths, safeRawFiles, suspiciousFilesResults } = await deps.validateFileSafety(
|
||||
rawFiles,
|
||||
progressCallback,
|
||||
config,
|
||||
processedFiles,
|
||||
safeFilePaths
|
||||
);
|
||||
|
||||
progressCallback("Writing output file...");
|
||||
// Process files (remove comments, etc.)
|
||||
progressCallback('Processing files...');
|
||||
const processedFiles = await deps.processFiles(safeRawFiles, config);
|
||||
|
||||
progressCallback('Generating output...');
|
||||
const output = await deps.generateOutput(rootDir, config, processedFiles, safeFilePaths);
|
||||
|
||||
progressCallback('Writing output file...');
|
||||
await deps.writeOutputToDisk(output, config);
|
||||
|
||||
await deps.copyToClipboardIfEnabled(output, progressCallback, config);
|
||||
|
||||
const metrics = await deps.calculateMetrics(
|
||||
processedFiles,
|
||||
output,
|
||||
progressCallback
|
||||
);
|
||||
const metrics = await deps.calculateMetrics(processedFiles, output, progressCallback);
|
||||
|
||||
return {
|
||||
...metrics,
|
||||
|
||||
@@ -1,17 +1,17 @@
|
||||
import clipboard from "clipboardy";
|
||||
import { RepomixConfigMerged } from "../../config/configSchema.js";
|
||||
import { logger } from "../../shared/logger.js";
|
||||
import { type RepomixProgressCallback } from "../../shared/types.js";
|
||||
import clipboard from 'clipboardy';
|
||||
import type { RepomixConfigMerged } from '../../config/configSchema.js';
|
||||
import { logger } from '../../shared/logger.js';
|
||||
import type { RepomixProgressCallback } from '../../shared/types.js';
|
||||
|
||||
// Additionally copy to clipboard if flag is raised
|
||||
export const copyToClipboardIfEnabled = async (
|
||||
output: string,
|
||||
progressCallback: RepomixProgressCallback,
|
||||
config: RepomixConfigMerged
|
||||
config: RepomixConfigMerged,
|
||||
): Promise<undefined> => {
|
||||
if (config.output.copyToClipboard) {
|
||||
progressCallback("Copying to clipboard...");
|
||||
logger.trace("Copying output to clipboard");
|
||||
progressCallback('Copying to clipboard...');
|
||||
logger.trace('Copying output to clipboard');
|
||||
await clipboard.write(output);
|
||||
}
|
||||
};
|
||||
|
||||
@@ -1,13 +1,10 @@
|
||||
import path from "path";
|
||||
import fs from "node:fs/promises";
|
||||
import { RepomixConfigMerged } from "../../config/configSchema.js";
|
||||
import { logger } from "../../shared/logger.js";
|
||||
import fs from 'node:fs/promises';
|
||||
import path from 'path';
|
||||
import type { RepomixConfigMerged } from '../../config/configSchema.js';
|
||||
import { logger } from '../../shared/logger.js';
|
||||
|
||||
// Write output to file. path is relative to the cwd
|
||||
export const writeOutputToDisk = async (
|
||||
output: string,
|
||||
config: RepomixConfigMerged
|
||||
): Promise<undefined> => {
|
||||
export const writeOutputToDisk = async (output: string, config: RepomixConfigMerged): Promise<undefined> => {
|
||||
const outputPath = path.resolve(config.cwd, config.output.filePath);
|
||||
logger.trace(`Writing output to: ${outputPath}`);
|
||||
await fs.writeFile(outputPath, output);
|
||||
|
||||
@@ -1,11 +1,8 @@
|
||||
import { RawFile } from "../file/fileTypes.js";
|
||||
import { SuspiciousFileResult } from "./securityCheck.js";
|
||||
import type { RawFile } from '../file/fileTypes.js';
|
||||
import type { SuspiciousFileResult } from './securityCheck.js';
|
||||
|
||||
export const filterOutUntrustedFiles = (
|
||||
rawFiles: RawFile[],
|
||||
suspiciousFilesResults: SuspiciousFileResult[]
|
||||
suspiciousFilesResults: SuspiciousFileResult[],
|
||||
): RawFile[] =>
|
||||
rawFiles.filter(
|
||||
(rawFile) =>
|
||||
!suspiciousFilesResults.some((result) => result.filePath === rawFile.path)
|
||||
);
|
||||
rawFiles.filter((rawFile) => !suspiciousFilesResults.some((result) => result.filePath === rawFile.path));
|
||||
|
||||
@@ -1,16 +1,16 @@
|
||||
import { RepomixConfigMerged } from "../../config/configSchema.js";
|
||||
import { RepomixProgressCallback } from "../../shared/types.js";
|
||||
import { RawFile } from "../file/fileTypes.js";
|
||||
import { runSecurityCheck, SuspiciousFileResult } from "./securityCheck.js";
|
||||
import type { RepomixConfigMerged } from '../../config/configSchema.js';
|
||||
import type { RepomixProgressCallback } from '../../shared/types.js';
|
||||
import type { RawFile } from '../file/fileTypes.js';
|
||||
import { type SuspiciousFileResult, runSecurityCheck } from './securityCheck.js';
|
||||
|
||||
export const runSecurityCheckIfEnabled = async (
|
||||
rawFiles: RawFile[],
|
||||
config: RepomixConfigMerged,
|
||||
progressCallback: RepomixProgressCallback,
|
||||
checkSecurity = runSecurityCheck
|
||||
checkSecurity = runSecurityCheck,
|
||||
): Promise<SuspiciousFileResult[]> => {
|
||||
if (config.security.enableSecurityCheck) {
|
||||
progressCallback("Running security check...");
|
||||
progressCallback('Running security check...');
|
||||
return await checkSecurity(rawFiles, progressCallback);
|
||||
}
|
||||
return [];
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
import { type RepomixConfigMerged } from "../../config/configSchema.js";
|
||||
import { logger } from "../../shared/logger.js";
|
||||
import { type RepomixProgressCallback } from "../../shared/types.js";
|
||||
import { type RawFile } from "../file/fileTypes.js";
|
||||
import { filterOutUntrustedFiles } from "./filterOutUntrustedFiles.js";
|
||||
import { runSecurityCheckIfEnabled } from "./runSecurityCheckIfEnabled.js";
|
||||
import type { RepomixConfigMerged } from '../../config/configSchema.js';
|
||||
import { logger } from '../../shared/logger.js';
|
||||
import type { RepomixProgressCallback } from '../../shared/types.js';
|
||||
import type { RawFile } from '../file/fileTypes.js';
|
||||
import { filterOutUntrustedFiles } from './filterOutUntrustedFiles.js';
|
||||
import { runSecurityCheckIfEnabled } from './runSecurityCheckIfEnabled.js';
|
||||
|
||||
// marks which files are suspicious and which are safe
|
||||
export const validateFileSafety = async (
|
||||
@@ -13,19 +13,12 @@ export const validateFileSafety = async (
|
||||
deps = {
|
||||
runSecurityCheckIfEnabled,
|
||||
filterOutUntrustedFiles,
|
||||
}
|
||||
},
|
||||
) => {
|
||||
const suspiciousFilesResults = await deps.runSecurityCheckIfEnabled(
|
||||
rawFiles,
|
||||
config,
|
||||
progressCallback
|
||||
);
|
||||
const safeRawFiles = deps.filterOutUntrustedFiles(
|
||||
rawFiles,
|
||||
suspiciousFilesResults
|
||||
);
|
||||
const suspiciousFilesResults = await deps.runSecurityCheckIfEnabled(rawFiles, config, progressCallback);
|
||||
const safeRawFiles = deps.filterOutUntrustedFiles(rawFiles, suspiciousFilesResults);
|
||||
const safeFilePaths = safeRawFiles.map((file) => file.path);
|
||||
logger.trace("Safe files count:", safeRawFiles.length);
|
||||
logger.trace('Safe files count:', safeRawFiles.length);
|
||||
|
||||
return {
|
||||
safeRawFiles,
|
||||
|
||||
@@ -1,62 +1,49 @@
|
||||
import { describe, it, expect } from "vitest";
|
||||
import {
|
||||
aggregateMetrics,
|
||||
type FileMetrics,
|
||||
} from "../../../src/core/metrics/aggregateMetrics.js";
|
||||
import type { ProcessedFile } from "../../../src/core/file/fileTypes.js";
|
||||
import type { TokenCounter } from "../../../src/core/tokenCount/tokenCount.js";
|
||||
import { describe, expect, it } from 'vitest';
|
||||
import type { ProcessedFile } from '../../../src/core/file/fileTypes.js';
|
||||
import { type FileMetrics, aggregateMetrics } from '../../../src/core/metrics/aggregateMetrics.js';
|
||||
import type { TokenCounter } from '../../../src/core/tokenCount/tokenCount.js';
|
||||
|
||||
describe("aggregateMetrics", () => {
|
||||
it("should aggregate metrics correctly", () => {
|
||||
describe('aggregateMetrics', () => {
|
||||
it('should aggregate metrics correctly', () => {
|
||||
const fileMetrics: FileMetrics[] = [
|
||||
{ path: "file1.txt", charCount: 100, tokenCount: 10 },
|
||||
{ path: "file2.txt", charCount: 200, tokenCount: 20 },
|
||||
{ path: 'file1.txt', charCount: 100, tokenCount: 10 },
|
||||
{ path: 'file2.txt', charCount: 200, tokenCount: 20 },
|
||||
];
|
||||
const processedFiles: ProcessedFile[] = [
|
||||
{ path: "file1.txt", content: "a" },
|
||||
{ path: "file2.txt", content: "b".repeat(200) },
|
||||
{ path: 'file1.txt', content: 'a' },
|
||||
{ path: 'file2.txt', content: 'b'.repeat(200) },
|
||||
];
|
||||
const output = "a".repeat(300);
|
||||
const output = 'a'.repeat(300);
|
||||
const tokenCounter = {
|
||||
countTokens: (content: string) => content.length / 10,
|
||||
} as TokenCounter;
|
||||
|
||||
const result = aggregateMetrics(
|
||||
fileMetrics,
|
||||
processedFiles,
|
||||
output,
|
||||
tokenCounter
|
||||
);
|
||||
const result = aggregateMetrics(fileMetrics, processedFiles, output, tokenCounter);
|
||||
|
||||
expect(result).toEqual({
|
||||
totalFiles: 2,
|
||||
totalCharacters: 300,
|
||||
totalTokens: 30,
|
||||
fileCharCounts: {
|
||||
"file1.txt": 100,
|
||||
"file2.txt": 200,
|
||||
'file1.txt': 100,
|
||||
'file2.txt': 200,
|
||||
},
|
||||
fileTokenCounts: {
|
||||
"file1.txt": 10,
|
||||
"file2.txt": 20,
|
||||
'file1.txt': 10,
|
||||
'file2.txt': 20,
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
it("should handle empty file metrics", () => {
|
||||
it('should handle empty file metrics', () => {
|
||||
const fileMetrics: FileMetrics[] = [];
|
||||
const processedFiles: ProcessedFile[] = [];
|
||||
const output = "";
|
||||
const output = '';
|
||||
const tokenCounter = {
|
||||
countTokens: (content: string) => content.length / 10,
|
||||
} as TokenCounter;
|
||||
|
||||
const result = aggregateMetrics(
|
||||
fileMetrics,
|
||||
processedFiles,
|
||||
output,
|
||||
tokenCounter
|
||||
);
|
||||
const result = aggregateMetrics(fileMetrics, processedFiles, output, tokenCounter);
|
||||
|
||||
expect(result).toEqual({
|
||||
totalFiles: 0,
|
||||
|
||||
@@ -1,20 +1,20 @@
|
||||
import { describe, it, expect, vi, Mock } from "vitest";
|
||||
import type { ProcessedFile } from "../../../src/core/file/fileTypes.js";
|
||||
import { calculateAllFileMetrics } from "../../../src/core/metrics/calculateAllFileMetrics.js";
|
||||
import type { TokenCounter } from "../../../src/core/tokenCount/tokenCount.js";
|
||||
import type { RepomixProgressCallback } from "../../../src/shared/types.js";
|
||||
import { calculateIndividualFileMetrics } from "../../../src/core/metrics/calculateIndividualFileMetrics.js";
|
||||
import { type Mock, describe, expect, it, vi } from 'vitest';
|
||||
import type { ProcessedFile } from '../../../src/core/file/fileTypes.js';
|
||||
import { calculateAllFileMetrics } from '../../../src/core/metrics/calculateAllFileMetrics.js';
|
||||
import { calculateIndividualFileMetrics } from '../../../src/core/metrics/calculateIndividualFileMetrics.js';
|
||||
import type { TokenCounter } from '../../../src/core/tokenCount/tokenCount.js';
|
||||
import type { RepomixProgressCallback } from '../../../src/shared/types.js';
|
||||
|
||||
vi.mock("../../../src/core/metrics/calculateIndividualFileMetrics.js");
|
||||
vi.mock("../../shared/processConcurrency", () => ({
|
||||
vi.mock('../../../src/core/metrics/calculateIndividualFileMetrics.js');
|
||||
vi.mock('../../shared/processConcurrency', () => ({
|
||||
getProcessConcurrency: () => 1,
|
||||
}));
|
||||
|
||||
describe("calculateAllFileMetrics", () => {
|
||||
it("should calculate metrics for all files", async () => {
|
||||
describe('calculateAllFileMetrics', () => {
|
||||
it('should calculate metrics for all files', async () => {
|
||||
const processedFiles: ProcessedFile[] = [
|
||||
{ path: "file1.txt", content: "a".repeat(100) },
|
||||
{ path: "file2.txt", content: "b".repeat(200) },
|
||||
{ path: 'file1.txt', content: 'a'.repeat(100) },
|
||||
{ path: 'file2.txt', content: 'b'.repeat(200) },
|
||||
];
|
||||
const tokenCounter = {} as TokenCounter;
|
||||
const progressCallback: RepomixProgressCallback = vi.fn();
|
||||
@@ -26,19 +26,15 @@ describe("calculateAllFileMetrics", () => {
|
||||
charCount: file.content.length,
|
||||
tokenCount: file.content.length / 10,
|
||||
};
|
||||
}
|
||||
},
|
||||
);
|
||||
|
||||
const result = await calculateAllFileMetrics(
|
||||
processedFiles,
|
||||
tokenCounter,
|
||||
progressCallback
|
||||
);
|
||||
const result = await calculateAllFileMetrics(processedFiles, tokenCounter, progressCallback);
|
||||
|
||||
expect(calculateIndividualFileMetrics).toHaveBeenCalledTimes(2);
|
||||
expect(result).toEqual([
|
||||
{ path: "file1.txt", charCount: 100, tokenCount: 10 },
|
||||
{ path: "file2.txt", charCount: 200, tokenCount: 20 },
|
||||
{ path: 'file1.txt', charCount: 100, tokenCount: 10 },
|
||||
{ path: 'file2.txt', charCount: 200, tokenCount: 20 },
|
||||
]);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,13 +1,13 @@
|
||||
import pc from "picocolors";
|
||||
import { describe, expect, it, vi } from "vitest";
|
||||
import type { ProcessedFile } from "../../../src/core/file/fileTypes.js";
|
||||
import { calculateIndividualFileMetrics } from "../../../src/core/metrics/calculateIndividualFileMetrics.js";
|
||||
import type { TokenCounter } from "../../../src/core/tokenCount/tokenCount.js";
|
||||
import type { RepomixProgressCallback } from "../../../src/shared/types.js";
|
||||
import pc from 'picocolors';
|
||||
import { describe, expect, it, vi } from 'vitest';
|
||||
import type { ProcessedFile } from '../../../src/core/file/fileTypes.js';
|
||||
import { calculateIndividualFileMetrics } from '../../../src/core/metrics/calculateIndividualFileMetrics.js';
|
||||
import type { TokenCounter } from '../../../src/core/tokenCount/tokenCount.js';
|
||||
import type { RepomixProgressCallback } from '../../../src/shared/types.js';
|
||||
|
||||
describe("calculateIndividualFileMetrics", () => {
|
||||
it("should calculate file metrics and report progress", async () => {
|
||||
const file: ProcessedFile = { path: "file1.txt", content: "a".repeat(100) };
|
||||
describe('calculateIndividualFileMetrics', () => {
|
||||
it('should calculate file metrics and report progress', async () => {
|
||||
const file: ProcessedFile = { path: 'file1.txt', content: 'a'.repeat(100) };
|
||||
const index = 0;
|
||||
const totalFiles = 1;
|
||||
const tokenCounter = {
|
||||
@@ -15,23 +15,12 @@ describe("calculateIndividualFileMetrics", () => {
|
||||
} as unknown as TokenCounter;
|
||||
const progressCallback: RepomixProgressCallback = vi.fn();
|
||||
|
||||
const result = await calculateIndividualFileMetrics(
|
||||
file,
|
||||
index,
|
||||
totalFiles,
|
||||
tokenCounter,
|
||||
progressCallback
|
||||
);
|
||||
const result = await calculateIndividualFileMetrics(file, index, totalFiles, tokenCounter, progressCallback);
|
||||
|
||||
expect(tokenCounter.countTokens).toHaveBeenCalledWith(
|
||||
file.content,
|
||||
file.path
|
||||
);
|
||||
expect(progressCallback).toHaveBeenCalledWith(
|
||||
`Calculating metrics... (1/1) ${pc.dim("file1.txt")}`
|
||||
);
|
||||
expect(tokenCounter.countTokens).toHaveBeenCalledWith(file.content, file.path);
|
||||
expect(progressCallback).toHaveBeenCalledWith(`Calculating metrics... (1/1) ${pc.dim('file1.txt')}`);
|
||||
expect(result).toEqual({
|
||||
path: "file1.txt",
|
||||
path: 'file1.txt',
|
||||
charCount: 100,
|
||||
tokenCount: 10,
|
||||
});
|
||||
|
||||
@@ -1,35 +1,33 @@
|
||||
import { describe, it, expect, vi, Mock } from "vitest";
|
||||
import { ProcessedFile } from "../../../src/core/file/fileTypes.js";
|
||||
import { aggregateMetrics } from "../../../src/core/metrics/aggregateMetrics.js";
|
||||
import { calculateAllFileMetrics } from "../../../src/core/metrics/calculateAllFileMetrics.js";
|
||||
import { calculateMetrics } from "../../../src/core/metrics/calculateMetrics.js";
|
||||
import { TokenCounter } from "../../../src/core/tokenCount/tokenCount.js";
|
||||
import { RepomixProgressCallback } from "../../../src/shared/types.js";
|
||||
import { type Mock, describe, expect, it, vi } from 'vitest';
|
||||
import type { ProcessedFile } from '../../../src/core/file/fileTypes.js';
|
||||
import { aggregateMetrics } from '../../../src/core/metrics/aggregateMetrics.js';
|
||||
import { calculateAllFileMetrics } from '../../../src/core/metrics/calculateAllFileMetrics.js';
|
||||
import { calculateMetrics } from '../../../src/core/metrics/calculateMetrics.js';
|
||||
import { TokenCounter } from '../../../src/core/tokenCount/tokenCount.js';
|
||||
import type { RepomixProgressCallback } from '../../../src/shared/types.js';
|
||||
|
||||
vi.mock("../../../src/core/tokenCount/tokenCount.js");
|
||||
vi.mock("../../../src/core/metrics/aggregateMetrics.js");
|
||||
vi.mock("../../../src/core/metrics/calculateAllFileMetrics.js");
|
||||
vi.mock('../../../src/core/tokenCount/tokenCount.js');
|
||||
vi.mock('../../../src/core/metrics/aggregateMetrics.js');
|
||||
vi.mock('../../../src/core/metrics/calculateAllFileMetrics.js');
|
||||
|
||||
describe("calculateMetrics", () => {
|
||||
it("should calculate metrics and return the result", async () => {
|
||||
describe('calculateMetrics', () => {
|
||||
it('should calculate metrics and return the result', async () => {
|
||||
const processedFiles: ProcessedFile[] = [
|
||||
{ path: "file1.txt", content: "a".repeat(100) },
|
||||
{ path: "file2.txt", content: "b".repeat(200) },
|
||||
{ path: 'file1.txt', content: 'a'.repeat(100) },
|
||||
{ path: 'file2.txt', content: 'b'.repeat(200) },
|
||||
];
|
||||
const output = "a".repeat(300);
|
||||
const output = 'a'.repeat(300);
|
||||
const progressCallback: RepomixProgressCallback = vi.fn();
|
||||
|
||||
const mockTokenCounter = {
|
||||
countTokens: vi.fn(),
|
||||
free: vi.fn(),
|
||||
};
|
||||
(TokenCounter as unknown as Mock).mockImplementation(
|
||||
() => mockTokenCounter
|
||||
);
|
||||
(TokenCounter as unknown as Mock).mockImplementation(() => mockTokenCounter);
|
||||
|
||||
const fileMetrics = [
|
||||
{ path: "file1.txt", charCount: 100, tokenCount: 10 },
|
||||
{ path: "file2.txt", charCount: 200, tokenCount: 20 },
|
||||
{ path: 'file1.txt', charCount: 100, tokenCount: 10 },
|
||||
{ path: 'file2.txt', charCount: 200, tokenCount: 20 },
|
||||
];
|
||||
(calculateAllFileMetrics as unknown as Mock).mockResolvedValue(fileMetrics);
|
||||
|
||||
@@ -38,34 +36,21 @@ describe("calculateMetrics", () => {
|
||||
totalCharacters: 300,
|
||||
totalTokens: 30,
|
||||
fileCharCounts: {
|
||||
"file1.txt": 100,
|
||||
"file2.txt": 200,
|
||||
'file1.txt': 100,
|
||||
'file2.txt': 200,
|
||||
},
|
||||
fileTokenCounts: {
|
||||
"file1.txt": 10,
|
||||
"file2.txt": 20,
|
||||
'file1.txt': 10,
|
||||
'file2.txt': 20,
|
||||
},
|
||||
};
|
||||
(aggregateMetrics as unknown as vi.Mock).mockReturnValue(aggregatedResult);
|
||||
|
||||
const result = await calculateMetrics(
|
||||
processedFiles,
|
||||
output,
|
||||
progressCallback
|
||||
);
|
||||
const result = await calculateMetrics(processedFiles, output, progressCallback);
|
||||
|
||||
expect(progressCallback).toHaveBeenCalledWith("Calculating metrics...");
|
||||
expect(calculateAllFileMetrics).toHaveBeenCalledWith(
|
||||
processedFiles,
|
||||
mockTokenCounter,
|
||||
progressCallback
|
||||
);
|
||||
expect(aggregateMetrics).toHaveBeenCalledWith(
|
||||
fileMetrics,
|
||||
processedFiles,
|
||||
output,
|
||||
mockTokenCounter
|
||||
);
|
||||
expect(progressCallback).toHaveBeenCalledWith('Calculating metrics...');
|
||||
expect(calculateAllFileMetrics).toHaveBeenCalledWith(processedFiles, mockTokenCounter, progressCallback);
|
||||
expect(aggregateMetrics).toHaveBeenCalledWith(fileMetrics, processedFiles, output, mockTokenCounter);
|
||||
expect(mockTokenCounter.free).toHaveBeenCalled();
|
||||
expect(result).toEqual(aggregatedResult);
|
||||
});
|
||||
|
||||
@@ -1,39 +1,39 @@
|
||||
import path from "node:path";
|
||||
import { beforeEach, describe, expect, test, vi } from "vitest";
|
||||
import { pack } from "../../src/core/packager.js";
|
||||
import { TokenCounter } from "../../src/core/tokenCount/tokenCount.js";
|
||||
import { createMockConfig } from "../testing/testUtils.js";
|
||||
import path from 'node:path';
|
||||
import { beforeEach, describe, expect, test, vi } from 'vitest';
|
||||
import { pack } from '../../src/core/packager.js';
|
||||
import { TokenCounter } from '../../src/core/tokenCount/tokenCount.js';
|
||||
import { createMockConfig } from '../testing/testUtils.js';
|
||||
|
||||
vi.mock("node:fs/promises");
|
||||
vi.mock("fs/promises");
|
||||
vi.mock("../../src/core/tokenCount/tokenCount");
|
||||
vi.mock("clipboardy", () => ({
|
||||
vi.mock('node:fs/promises');
|
||||
vi.mock('fs/promises');
|
||||
vi.mock('../../src/core/tokenCount/tokenCount');
|
||||
vi.mock('clipboardy', () => ({
|
||||
default: {
|
||||
write: vi.fn(),
|
||||
},
|
||||
}));
|
||||
|
||||
describe("packager", () => {
|
||||
describe('packager', () => {
|
||||
beforeEach(() => {
|
||||
vi.resetAllMocks();
|
||||
});
|
||||
|
||||
test("pack should orchestrate packing files and generating output", async () => {
|
||||
const file2Path = path.join("dir1", "file2.txt");
|
||||
test('pack should orchestrate packing files and generating output', async () => {
|
||||
const file2Path = path.join('dir1', 'file2.txt');
|
||||
const mockRawFiles = [
|
||||
{ path: "file1.txt", content: "raw content 1" },
|
||||
{ path: file2Path, content: "raw content 2" },
|
||||
{ path: 'file1.txt', content: 'raw content 1' },
|
||||
{ path: file2Path, content: 'raw content 2' },
|
||||
];
|
||||
const mockSafeRawFiles = [
|
||||
{ path: "file1.txt", content: "safed content 1" },
|
||||
{ path: file2Path, content: "safed content 2" },
|
||||
{ path: 'file1.txt', content: 'safed content 1' },
|
||||
{ path: file2Path, content: 'safed content 2' },
|
||||
];
|
||||
const mockProcessedFiles = [
|
||||
{ path: "file1.txt", content: "processed content 1" },
|
||||
{ path: file2Path, content: "processed content 2" },
|
||||
{ path: 'file1.txt', content: 'processed content 1' },
|
||||
{ path: file2Path, content: 'processed content 2' },
|
||||
];
|
||||
const mockOutput = "mock output";
|
||||
const mockFilePaths = ["file1.txt", file2Path];
|
||||
const mockOutput = 'mock output';
|
||||
const mockFilePaths = ['file1.txt', file2Path];
|
||||
|
||||
const mockDeps = {
|
||||
searchFiles: vi.fn().mockResolvedValue({
|
||||
@@ -55,11 +55,11 @@ describe("packager", () => {
|
||||
totalCharacters: 11,
|
||||
totalTokens: 10,
|
||||
fileCharCounts: {
|
||||
"file1.txt": 19,
|
||||
'file1.txt': 19,
|
||||
[file2Path]: 19,
|
||||
},
|
||||
fileTokenCounts: {
|
||||
"file1.txt": 10,
|
||||
'file1.txt': 10,
|
||||
[file2Path]: 10,
|
||||
},
|
||||
}),
|
||||
@@ -69,56 +69,33 @@ describe("packager", () => {
|
||||
|
||||
const mockConfig = createMockConfig();
|
||||
const progressCallback = vi.fn();
|
||||
const result = await pack("root", mockConfig, progressCallback, mockDeps);
|
||||
const result = await pack('root', mockConfig, progressCallback, mockDeps);
|
||||
|
||||
expect(mockDeps.searchFiles).toHaveBeenCalledWith("root", mockConfig);
|
||||
expect(mockDeps.collectFiles).toHaveBeenCalledWith(mockFilePaths, "root");
|
||||
expect(mockDeps.searchFiles).toHaveBeenCalledWith('root', mockConfig);
|
||||
expect(mockDeps.collectFiles).toHaveBeenCalledWith(mockFilePaths, 'root');
|
||||
expect(mockDeps.validateFileSafety).toHaveBeenCalled();
|
||||
expect(mockDeps.processFiles).toHaveBeenCalled();
|
||||
expect(mockDeps.writeOutputToDisk).toHaveBeenCalled();
|
||||
expect(mockDeps.generateOutput).toHaveBeenCalled();
|
||||
expect(mockDeps.calculateMetrics).toHaveBeenCalled();
|
||||
|
||||
expect(mockDeps.validateFileSafety).toHaveBeenCalledWith(
|
||||
mockRawFiles,
|
||||
progressCallback,
|
||||
mockConfig
|
||||
);
|
||||
expect(mockDeps.processFiles).toHaveBeenCalledWith(
|
||||
mockSafeRawFiles,
|
||||
mockConfig
|
||||
);
|
||||
expect(mockDeps.generateOutput).toHaveBeenCalledWith(
|
||||
"root",
|
||||
mockConfig,
|
||||
mockProcessedFiles,
|
||||
mockFilePaths
|
||||
);
|
||||
expect(mockDeps.writeOutputToDisk).toHaveBeenCalledWith(
|
||||
mockOutput,
|
||||
mockConfig
|
||||
);
|
||||
expect(mockDeps.copyToClipboardIfEnabled).toHaveBeenCalledWith(
|
||||
mockOutput,
|
||||
progressCallback,
|
||||
mockConfig
|
||||
);
|
||||
expect(mockDeps.calculateMetrics).toHaveBeenCalledWith(
|
||||
mockProcessedFiles,
|
||||
mockOutput,
|
||||
progressCallback
|
||||
);
|
||||
expect(mockDeps.validateFileSafety).toHaveBeenCalledWith(mockRawFiles, progressCallback, mockConfig);
|
||||
expect(mockDeps.processFiles).toHaveBeenCalledWith(mockSafeRawFiles, mockConfig);
|
||||
expect(mockDeps.generateOutput).toHaveBeenCalledWith('root', mockConfig, mockProcessedFiles, mockFilePaths);
|
||||
expect(mockDeps.writeOutputToDisk).toHaveBeenCalledWith(mockOutput, mockConfig);
|
||||
expect(mockDeps.copyToClipboardIfEnabled).toHaveBeenCalledWith(mockOutput, progressCallback, mockConfig);
|
||||
expect(mockDeps.calculateMetrics).toHaveBeenCalledWith(mockProcessedFiles, mockOutput, progressCallback);
|
||||
|
||||
// Check the result of pack function
|
||||
expect(result.totalFiles).toBe(2);
|
||||
expect(result.totalCharacters).toBe(11);
|
||||
expect(result.totalTokens).toBe(10);
|
||||
expect(result.fileCharCounts).toEqual({
|
||||
"file1.txt": 19,
|
||||
'file1.txt': 19,
|
||||
[file2Path]: 19,
|
||||
});
|
||||
expect(result.fileTokenCounts).toEqual({
|
||||
"file1.txt": 10,
|
||||
'file1.txt': 10,
|
||||
[file2Path]: 10,
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,20 +1,20 @@
|
||||
import clipboard from "clipboardy";
|
||||
import { logger } from "handlebars";
|
||||
import { beforeEach, describe, expect, it, vi } from "vitest";
|
||||
import { type RepomixConfigMerged } from "../../../src/config/configSchema.js";
|
||||
import { copyToClipboardIfEnabled } from "../../../src/core/packager/copyToClipboardIfEnabled.js";
|
||||
import { type RepomixProgressCallback } from "../../../src/shared/types.js";
|
||||
import clipboard from 'clipboardy';
|
||||
import { logger } from 'handlebars';
|
||||
import { beforeEach, describe, expect, it, vi } from 'vitest';
|
||||
import type { RepomixConfigMerged } from '../../../src/config/configSchema.js';
|
||||
import { copyToClipboardIfEnabled } from '../../../src/core/packager/copyToClipboardIfEnabled.js';
|
||||
import type { RepomixProgressCallback } from '../../../src/shared/types.js';
|
||||
|
||||
vi.mock("clipboardy");
|
||||
vi.mock("../../shared/logger");
|
||||
vi.mock('clipboardy');
|
||||
vi.mock('../../shared/logger');
|
||||
|
||||
describe("copyToClipboardIfEnabled", () => {
|
||||
describe('copyToClipboardIfEnabled', () => {
|
||||
beforeEach(() => {
|
||||
vi.resetAllMocks();
|
||||
});
|
||||
|
||||
it("should copy output to clipboard if flag enabled in config", async () => {
|
||||
const output = "test output";
|
||||
it('should copy output to clipboard if flag enabled in config', async () => {
|
||||
const output = 'test output';
|
||||
const config: RepomixConfigMerged = {
|
||||
output: { copyToClipboard: true },
|
||||
} as RepomixConfigMerged;
|
||||
@@ -22,12 +22,12 @@ describe("copyToClipboardIfEnabled", () => {
|
||||
|
||||
await copyToClipboardIfEnabled(output, progressCallback, config);
|
||||
|
||||
expect(progressCallback).toHaveBeenCalledWith("Copying to clipboard...");
|
||||
expect(progressCallback).toHaveBeenCalledWith('Copying to clipboard...');
|
||||
expect(clipboard.write).toHaveBeenCalledWith(output);
|
||||
});
|
||||
|
||||
it("should not copy output to clipboard if flag disabled in config", async () => {
|
||||
const output = "test output";
|
||||
it('should not copy output to clipboard if flag disabled in config', async () => {
|
||||
const output = 'test output';
|
||||
const config: RepomixConfigMerged = {
|
||||
output: { copyToClipboard: false },
|
||||
} as RepomixConfigMerged;
|
||||
|
||||
@@ -1,18 +1,18 @@
|
||||
import { describe, it, expect, vi } from "vitest";
|
||||
import path from "path";
|
||||
import fs from "node:fs/promises";
|
||||
import { type RepomixConfigMerged } from "../../../src/config/configSchema.js";
|
||||
import { writeOutputToDisk } from "../../../src/core/packager/writeOutputToDisk.js";
|
||||
import fs from 'node:fs/promises';
|
||||
import path from 'path';
|
||||
import { describe, expect, it, vi } from 'vitest';
|
||||
import type { RepomixConfigMerged } from '../../../src/config/configSchema.js';
|
||||
import { writeOutputToDisk } from '../../../src/core/packager/writeOutputToDisk.js';
|
||||
|
||||
vi.mock("node:fs/promises");
|
||||
vi.mock("../../shared/logger");
|
||||
vi.mock('node:fs/promises');
|
||||
vi.mock('../../shared/logger');
|
||||
|
||||
describe("writeOutputToDisk", () => {
|
||||
it("should write output to the specified file path", async () => {
|
||||
const output = "test output";
|
||||
describe('writeOutputToDisk', () => {
|
||||
it('should write output to the specified file path', async () => {
|
||||
const output = 'test output';
|
||||
const config: RepomixConfigMerged = {
|
||||
cwd: "/test/directory",
|
||||
output: { filePath: "output.txt" },
|
||||
cwd: '/test/directory',
|
||||
output: { filePath: 'output.txt' },
|
||||
} as RepomixConfigMerged;
|
||||
|
||||
const outputPath = path.resolve(config.cwd, config.output.filePath);
|
||||
|
||||
@@ -1,17 +1,17 @@
|
||||
import { describe, expect, it } from "vitest";
|
||||
import { RawFile } from "../../../src/core/file/fileTypes.js";
|
||||
import { filterOutUntrustedFiles } from "../../../src/core/security/filterOutUntrustedFiles.js";
|
||||
import { SuspiciousFileResult } from "../../../src/core/security/securityCheck.js";
|
||||
import { describe, expect, it } from 'vitest';
|
||||
import type { RawFile } from '../../../src/core/file/fileTypes.js';
|
||||
import { filterOutUntrustedFiles } from '../../../src/core/security/filterOutUntrustedFiles.js';
|
||||
import type { SuspiciousFileResult } from '../../../src/core/security/securityCheck.js';
|
||||
|
||||
describe("filterOutUntrustedFiles", () => {
|
||||
it("should filter out untrusted files", () => {
|
||||
describe('filterOutUntrustedFiles', () => {
|
||||
it('should filter out untrusted files', () => {
|
||||
const rawFiles: RawFile[] = [
|
||||
{ path: "file1.txt", content: "content 1" },
|
||||
{ path: "file2.txt", content: "content 2" },
|
||||
{ path: "file3.txt", content: "content 3" },
|
||||
{ path: 'file1.txt', content: 'content 1' },
|
||||
{ path: 'file2.txt', content: 'content 2' },
|
||||
{ path: 'file3.txt', content: 'content 3' },
|
||||
];
|
||||
const suspiciousFilesResults: SuspiciousFileResult[] = [
|
||||
{ filePath: "file2.txt", messages: ["something suspicious."] },
|
||||
{ filePath: 'file2.txt', messages: ['something suspicious.'] },
|
||||
];
|
||||
const expectedGoodFiles = [rawFiles[0], rawFiles[2]];
|
||||
|
||||
@@ -20,11 +20,11 @@ describe("filterOutUntrustedFiles", () => {
|
||||
expect(result).toEqual(expectedGoodFiles);
|
||||
});
|
||||
|
||||
it("should return all files if no suspicious files", () => {
|
||||
it('should return all files if no suspicious files', () => {
|
||||
const rawFiles: RawFile[] = [
|
||||
{ path: "file1.txt", content: "content 1" },
|
||||
{ path: "file2.txt", content: "content 2" },
|
||||
{ path: "file3.txt", content: "content 3" },
|
||||
{ path: 'file1.txt', content: 'content 1' },
|
||||
{ path: 'file2.txt', content: 'content 2' },
|
||||
{ path: 'file3.txt', content: 'content 3' },
|
||||
];
|
||||
const suspiciousFilesResults: SuspiciousFileResult[] = [];
|
||||
|
||||
|
||||
@@ -1,40 +1,33 @@
|
||||
import { describe, it, expect, vi } from "vitest";
|
||||
import { RepomixConfigMerged } from "../../../src/config/configSchema.js";
|
||||
import { RawFile } from "../../../src/core/file/fileTypes.js";
|
||||
import { runSecurityCheckIfEnabled } from "../../../src/core/security/runSecurityCheckIfEnabled.js";
|
||||
import { SuspiciousFileResult } from "../../../src/core/security/securityCheck.js";
|
||||
import { RepomixProgressCallback } from "../../../src/shared/types.js";
|
||||
import { describe, expect, it, vi } from 'vitest';
|
||||
import type { RepomixConfigMerged } from '../../../src/config/configSchema.js';
|
||||
import type { RawFile } from '../../../src/core/file/fileTypes.js';
|
||||
import { runSecurityCheckIfEnabled } from '../../../src/core/security/runSecurityCheckIfEnabled.js';
|
||||
import type { SuspiciousFileResult } from '../../../src/core/security/securityCheck.js';
|
||||
import type { RepomixProgressCallback } from '../../../src/shared/types.js';
|
||||
|
||||
describe("runSecurityCheckIfEnabled", () => {
|
||||
it("should run security check if enabled in config", async () => {
|
||||
describe('runSecurityCheckIfEnabled', () => {
|
||||
it('should run security check if enabled in config', async () => {
|
||||
const rawFiles: RawFile[] = [
|
||||
{ path: "file1.txt", content: "contents1" },
|
||||
{ path: "file2.txt", content: "contents2" },
|
||||
{ path: 'file1.txt', content: 'contents1' },
|
||||
{ path: 'file2.txt', content: 'contents2' },
|
||||
];
|
||||
const config: RepomixConfigMerged = {
|
||||
security: { enableSecurityCheck: true },
|
||||
} as RepomixConfigMerged;
|
||||
const progressCallback: RepomixProgressCallback = vi.fn();
|
||||
const checkSecurity = vi
|
||||
.fn()
|
||||
.mockResolvedValue([{ filePath: "file1.txt" }] as SuspiciousFileResult[]);
|
||||
const checkSecurity = vi.fn().mockResolvedValue([{ filePath: 'file1.txt' }] as SuspiciousFileResult[]);
|
||||
|
||||
const result = await runSecurityCheckIfEnabled(
|
||||
rawFiles,
|
||||
config,
|
||||
progressCallback,
|
||||
checkSecurity
|
||||
);
|
||||
const result = await runSecurityCheckIfEnabled(rawFiles, config, progressCallback, checkSecurity);
|
||||
|
||||
expect(progressCallback).toHaveBeenCalledWith("Running security check...");
|
||||
expect(progressCallback).toHaveBeenCalledWith('Running security check...');
|
||||
expect(checkSecurity).toHaveBeenCalledWith(rawFiles, progressCallback);
|
||||
expect(result).toEqual([{ filePath: "file1.txt" }]);
|
||||
expect(result).toEqual([{ filePath: 'file1.txt' }]);
|
||||
});
|
||||
|
||||
it("should not run security check if disabled in config", async () => {
|
||||
it('should not run security check if disabled in config', async () => {
|
||||
const rawFiles: RawFile[] = [
|
||||
{ path: "file1.txt", content: "contents1" },
|
||||
{ path: "file2.txt", content: "contents2" },
|
||||
{ path: 'file1.txt', content: 'contents1' },
|
||||
{ path: 'file2.txt', content: 'contents2' },
|
||||
];
|
||||
const config: RepomixConfigMerged = {
|
||||
security: { enableSecurityCheck: false },
|
||||
@@ -42,12 +35,7 @@ describe("runSecurityCheckIfEnabled", () => {
|
||||
const progressCallback: RepomixProgressCallback = vi.fn();
|
||||
const checkSecurity = vi.fn();
|
||||
|
||||
const result = await runSecurityCheckIfEnabled(
|
||||
rawFiles,
|
||||
config,
|
||||
progressCallback,
|
||||
checkSecurity
|
||||
);
|
||||
const result = await runSecurityCheckIfEnabled(rawFiles, config, progressCallback, checkSecurity);
|
||||
|
||||
expect(progressCallback).not.toHaveBeenCalled();
|
||||
expect(checkSecurity).not.toHaveBeenCalled();
|
||||
|
||||
@@ -1,16 +1,16 @@
|
||||
import { describe, it, expect, vi } from "vitest";
|
||||
import { type RepomixConfigMerged } from "../../../src/config/configSchema.js";
|
||||
import { type RawFile } from "../../../src/core/file/fileTypes.js";
|
||||
import { type SuspiciousFileResult } from "../../../src/core/security/securityCheck.js";
|
||||
import { validateFileSafety } from "../../../src/core/security/validateFileSafety.js";
|
||||
import { type RepomixProgressCallback } from "../../../src/shared/types.js";
|
||||
import { describe, expect, it, vi } from 'vitest';
|
||||
import type { RepomixConfigMerged } from '../../../src/config/configSchema.js';
|
||||
import type { RawFile } from '../../../src/core/file/fileTypes.js';
|
||||
import type { SuspiciousFileResult } from '../../../src/core/security/securityCheck.js';
|
||||
import { validateFileSafety } from '../../../src/core/security/validateFileSafety.js';
|
||||
import type { RepomixProgressCallback } from '../../../src/shared/types.js';
|
||||
|
||||
describe("validateFileSafety", () => {
|
||||
it("should validate file safety and return safe files and paths", async () => {
|
||||
describe('validateFileSafety', () => {
|
||||
it('should validate file safety and return safe files and paths', async () => {
|
||||
const rawFiles: RawFile[] = [
|
||||
{ path: "file1.txt", content: "content1" },
|
||||
{ path: "file2.txt", content: "content2" },
|
||||
{ path: "file3.txt", content: "content3" },
|
||||
{ path: 'file1.txt', content: 'content1' },
|
||||
{ path: 'file2.txt', content: 'content2' },
|
||||
{ path: 'file3.txt', content: 'content3' },
|
||||
];
|
||||
const safeRawFiles = [rawFiles[0], rawFiles[1]];
|
||||
const config: RepomixConfigMerged = {
|
||||
@@ -18,34 +18,20 @@ describe("validateFileSafety", () => {
|
||||
} as RepomixConfigMerged;
|
||||
const progressCallback: RepomixProgressCallback = vi.fn();
|
||||
const suspiciousFilesResults: SuspiciousFileResult[] = [
|
||||
{ filePath: "file2.txt", messages: ["something suspicious."] },
|
||||
{ filePath: 'file2.txt', messages: ['something suspicious.'] },
|
||||
];
|
||||
const deps = {
|
||||
runSecurityCheckIfEnabled: vi
|
||||
.fn()
|
||||
.mockResolvedValue(suspiciousFilesResults),
|
||||
runSecurityCheckIfEnabled: vi.fn().mockResolvedValue(suspiciousFilesResults),
|
||||
filterOutUntrustedFiles: vi.fn().mockReturnValue(safeRawFiles),
|
||||
};
|
||||
|
||||
const result = await validateFileSafety(
|
||||
rawFiles,
|
||||
progressCallback,
|
||||
config,
|
||||
deps
|
||||
);
|
||||
const result = await validateFileSafety(rawFiles, progressCallback, config, deps);
|
||||
|
||||
expect(deps.runSecurityCheckIfEnabled).toHaveBeenCalledWith(
|
||||
rawFiles,
|
||||
config,
|
||||
progressCallback
|
||||
);
|
||||
expect(deps.filterOutUntrustedFiles).toHaveBeenCalledWith(
|
||||
rawFiles,
|
||||
suspiciousFilesResults
|
||||
);
|
||||
expect(deps.runSecurityCheckIfEnabled).toHaveBeenCalledWith(rawFiles, config, progressCallback);
|
||||
expect(deps.filterOutUntrustedFiles).toHaveBeenCalledWith(rawFiles, suspiciousFilesResults);
|
||||
expect(result).toEqual({
|
||||
safeRawFiles,
|
||||
safeFilePaths: ["file1.txt", "file2.txt"],
|
||||
safeFilePaths: ['file1.txt', 'file2.txt'],
|
||||
suspiciousFilesResults,
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user