Skip to content

Commit

Permalink
Merge pull request #49 from yamadashy/chore/more-coverage
Browse files Browse the repository at this point in the history
chore: refactor codes
  • Loading branch information
yamadashy authored Aug 12, 2024
2 parents 79e6d8d + 4502083 commit 617a633
Show file tree
Hide file tree
Showing 33 changed files with 763 additions and 341 deletions.
2 changes: 2 additions & 0 deletions eslint.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,8 @@ export default tseslint.config(
'import-x/no-duplicates': 'error',
'import-x/order': 'error',

'no-process-exit': 'error',

"prefer-arrow-functions/prefer-arrow-functions": [
"warn",
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,13 +11,13 @@ import { loadFileConfig, mergeConfigs } from '../../config/configLoader.js';
import { logger } from '../../shared/logger.js';
import { CliOptions } from '../cliRunner.js';
import { getVersion } from '../../core/file/packageJsonParser.js';
import Spinner from './../cliSpinner.js';
import { printSummary, printTopFiles, printCompletion, printSecurityCheck } from './../cliPrinter.js';
import Spinner from '../cliSpinner.js';
import { printSummary, printTopFiles, printCompletion, printSecurityCheck } from '../cliPrinter.js';

export const runDefaultCommand = async (directory: string, cwd: string, options: CliOptions): Promise<void> => {
export const runDefaultAction = async (directory: string, cwd: string, options: CliOptions): Promise<void> => {
const version = await getVersion();

console.log(pc.dim(`\n📦 Repopack v${version}\n`));
logger.log(pc.dim(`\n📦 Repopack v${version}\n`));

logger.setVerbose(options.verbose || false);
logger.trace('Loaded CLI options:', options);
Expand Down Expand Up @@ -71,15 +71,15 @@ export const runDefaultCommand = async (directory: string, cwd: string, options:
}

spinner.succeed('Packing completed successfully!');
console.log('');
logger.log('');

if (config.output.topFilesLength > 0) {
printTopFiles(packResult.fileCharCounts, packResult.fileTokenCounts, config.output.topFilesLength);
console.log('');
logger.log('');
}

printSecurityCheck(cwd, packResult.suspiciousFilesResults);
console.log('');
logger.log('');

printSummary(
cwd,
Expand All @@ -89,7 +89,7 @@ export const runDefaultCommand = async (directory: string, cwd: string, options:
config.output.filePath,
packResult.suspiciousFilesResults,
);
console.log('');
logger.log('');

printCompletion();
};
Original file line number Diff line number Diff line change
Expand Up @@ -6,14 +6,14 @@ import { logger } from '../../shared/logger.js';
import { RepopackConfigFile, RepopackOutputStyle } from '../../config/configTypes.js';
import { defaultConfig } from '../../config/defaultConfig.js';

export const runInitCommand = async (rootDir: string): Promise<void> => {
const configPath = path.join(rootDir, 'repopack.config.json');
export const runInitAction = async (rootDir: string): Promise<void> => {
const configPath = path.resolve(rootDir, 'repopack.config.json');

try {
// Check if the config file already exists
await fs.access(configPath);
console.log(pc.yellow('A repopack.config.json file already exists in this directory.'));
console.log(pc.yellow('If you want to create a new one, please delete or rename the existing file first.'));
logger.warn('A repopack.config.json file already exists in this directory.');
logger.warn('If you want to create a new one, please delete or rename the existing file first.');
return;
} catch {
// File doesn't exist, so we can proceed
Expand All @@ -22,6 +22,8 @@ export const runInitCommand = async (rootDir: string): Promise<void> => {
intro(pc.bold(pc.cyan('Welcome to Repopack!')));

try {
let isCancelled = false;

const options = await group(
{
outputFilePath: () =>
Expand All @@ -44,11 +46,15 @@ export const runInitCommand = async (rootDir: string): Promise<void> => {
{
onCancel: () => {
cancel('Configuration cancelled.');
process.exit(0);
isCancelled = true;
},
},
);

if (isCancelled) {
return;
}

const config: RepopackConfigFile = {
...defaultConfig,
output: {
Expand Down
7 changes: 7 additions & 0 deletions src/cli/actions/versionActionRunner.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
import { getVersion } from '../../core/file/packageJsonParser.js';
import { logger } from '../../shared/logger.js';

export const runVersionAction = async (): Promise<void> => {
const version = await getVersion();
logger.log(version);
};
49 changes: 24 additions & 25 deletions src/cli/cliPrinter.ts
Original file line number Diff line number Diff line change
@@ -1,14 +1,15 @@
import path from 'node:path';
import pc from 'picocolors';
import type { SecretLintCoreResult } from '@secretlint/types';
import { logger } from '../shared/logger.js';
import { SuspiciousFileResult } from '../core/security/securityCheckRunner.js';

export const printSummary = (
rootDir: string,
totalFiles: number,
totalCharacters: number,
totalTokens: number,
outputPath: string,
suspiciousFilesResults: SecretLintCoreResult[],
suspiciousFilesResults: SuspiciousFileResult[],
) => {
const relativeOutputPath = path.relative(rootDir, outputPath);

Expand All @@ -19,32 +20,30 @@ export const printSummary = (
securityCheckMessage = pc.white('✔ No suspicious files detected');
}

console.log(pc.white('📊 Pack Summary:'));
console.log(pc.dim('────────────────'));
console.log(`${pc.white(' Total Files:')} ${pc.white(totalFiles.toString())}`);
console.log(`${pc.white(' Total Chars:')} ${pc.white(totalCharacters.toString())}`);
console.log(`${pc.white(' Total Tokens:')} ${pc.white(totalTokens.toString())}`);
console.log(`${pc.white(' Output:')} ${pc.white(relativeOutputPath)}`);
console.log(`${pc.white(' Security:')} ${pc.white(securityCheckMessage)}`);
logger.log(pc.white('📊 Pack Summary:'));
logger.log(pc.dim('────────────────'));
logger.log(`${pc.white(' Total Files:')} ${pc.white(totalFiles.toString())}`);
logger.log(`${pc.white(' Total Chars:')} ${pc.white(totalCharacters.toString())}`);
logger.log(`${pc.white(' Total Tokens:')} ${pc.white(totalTokens.toString())}`);
logger.log(`${pc.white(' Output:')} ${pc.white(relativeOutputPath)}`);
logger.log(`${pc.white(' Security:')} ${pc.white(securityCheckMessage)}`);
};

export const printSecurityCheck = (rootDir: string, suspiciousFilesResults: SecretLintCoreResult[]) => {
console.log(pc.white('🔎 Security Check:'));
console.log(pc.dim('──────────────────'));
export const printSecurityCheck = (rootDir: string, suspiciousFilesResults: SuspiciousFileResult[]) => {
logger.log(pc.white('🔎 Security Check:'));
logger.log(pc.dim('──────────────────'));

if (suspiciousFilesResults.length === 0) {
console.log(pc.green('✔') + ' ' + pc.white('No suspicious files detected.'));
logger.log(pc.green('✔') + ' ' + pc.white('No suspicious files detected.'));
} else {
console.log(
pc.yellow(`${suspiciousFilesResults.length} suspicious file(s) detected and excluded from the output:`),
);
logger.log(pc.yellow(`${suspiciousFilesResults.length} suspicious file(s) detected and excluded from the output:`));
suspiciousFilesResults.forEach((suspiciousFilesResult, index) => {
const relativeFilePath = path.relative(rootDir, suspiciousFilesResult.filePath);
console.log(`${pc.white(`${index + 1}.`)} ${pc.white(relativeFilePath)}`);
console.log(pc.dim(' - ' + suspiciousFilesResult.messages.map((message) => message.message).join('\n - ')));
logger.log(`${pc.white(`${index + 1}.`)} ${pc.white(relativeFilePath)}`);
logger.log(pc.dim(' - ' + suspiciousFilesResult.messages.join('\n - ')));
});
console.log(pc.yellow('\nThese files have been excluded from the output for security reasons.'));
console.log(pc.yellow('Please review these files for potential sensitive information.'));
logger.log(pc.yellow('\nThese files have been excluded from the output for security reasons.'));
logger.log(pc.yellow('Please review these files for potential sensitive information.'));
}
};

Expand All @@ -53,8 +52,8 @@ export const printTopFiles = (
fileTokenCounts: Record<string, number>,
topFilesLength: number,
) => {
console.log(pc.white(`📈 Top ${topFilesLength} Files by Character Count and Token Count:`));
console.log(pc.dim('──────────────────────────────────────────────────────'));
logger.log(pc.white(`📈 Top ${topFilesLength} Files by Character Count and Token Count:`));
logger.log(pc.dim('──────────────────────────────────────────────────────'));

const topFiles = Object.entries(fileCharCounts)
.sort((a, b) => b[1] - a[1])
Expand All @@ -63,13 +62,13 @@ export const printTopFiles = (
topFiles.forEach(([filePath, charCount], index) => {
const tokenCount = fileTokenCounts[filePath];
const indexString = `${index + 1}.`.padEnd(3, ' ');
console.log(
logger.log(
`${pc.white(`${indexString}`)} ${pc.white(filePath)} ${pc.dim(`(${charCount} chars, ${tokenCount} tokens)`)}`,
);
});
};

export const printCompletion = () => {
console.log(pc.green('🎉 All Done!'));
console.log(pc.white('Your repository has been successfully packed.'));
logger.log(pc.green('🎉 All Done!'));
logger.log(pc.white('Your repository has been successfully packed.'));
};
12 changes: 6 additions & 6 deletions src/cli/cliRunner.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,9 @@ import { program, OptionValues } from 'commander';
import { RepopackOutputStyle } from '../config/configTypes.js';
import { getVersion } from '../core/file/packageJsonParser.js';
import { handleError } from '../shared/errorHandler.js';
import { runInitCommand } from './commands/initCommandRunner.js';
import { runVersionCommand } from './commands/versionCommandRunner.js';
import { runDefaultCommand } from './commands/defaultCommandRunner.js';
import { runInitAction } from './actions/initActionRunner.js';
import { runVersionAction } from './actions/versionActionRunner.js';
import { runDefaultAction } from './actions/defaultActionRunner.js';

export interface CliOptions extends OptionValues {
version?: boolean;
Expand Down Expand Up @@ -48,14 +48,14 @@ export async function run() {

const executeAction = async (directory: string, cwd: string, options: CliOptions) => {
if (options.version) {
await runVersionCommand();
await runVersionAction();
return;
}

if (options.init) {
await runInitCommand(cwd);
await runInitAction(cwd);
return;
}

await runDefaultCommand(directory, cwd, options);
await runDefaultAction(directory, cwd, options);
};
6 changes: 0 additions & 6 deletions src/cli/commands/versionCommandRunner.ts

This file was deleted.

47 changes: 47 additions & 0 deletions src/core/file/fileCollector.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
import * as fs from 'node:fs/promises';
import path from 'node:path';
import { isBinary } from 'istextorbinary';
import jschardet from 'jschardet';
import iconv from 'iconv-lite';
import { logger } from '../../shared/logger.js';
import { RawFile } from './fileTypes.js';

export const collectFiles = async (filePaths: string[], rootDir: string): Promise<RawFile[]> => {
const rawFiles: RawFile[] = [];

for (const filePath of filePaths) {
const fullPath = path.resolve(rootDir, filePath);
const content = await readRawFile(fullPath);
if (content) {
rawFiles.push({ path: filePath, content });
}
}

return rawFiles;
};

const readRawFile = async (filePath: string): Promise<string | null> => {
if (isBinary(filePath)) {
logger.debug(`Skipping binary file: ${filePath}`);
return null;
}

logger.trace(`Processing file: ${filePath}`);

try {
const buffer = await fs.readFile(filePath);

if (isBinary(null, buffer)) {
logger.debug(`Skipping binary file (content check): ${filePath}`);
return null;
}

const encoding = jschardet.detect(buffer).encoding || 'utf-8';
const content = iconv.decode(buffer, encoding);

return content;
} catch (error) {
logger.warn(`Failed to read file: ${filePath}`, error);
return null;
}
};
22 changes: 19 additions & 3 deletions src/core/file/fileManipulater.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,14 +3,29 @@ import strip from 'strip-comments';

interface FileManipulator {
removeComments(content: string): string;
removeEmptyLines(content: string): string;
}

const rtrimLines = (content: string): string => content.replace(/[ \t]+$/gm, '');

class StripCommentsManipulator implements FileManipulator {
class BaseManipulator implements FileManipulator {
removeComments(content: string): string {
return content;
}

removeEmptyLines(content: string): string {
return content
.split('\n')
.filter((line) => line.trim() !== '')
.join('\n');
}
}

class StripCommentsManipulator extends BaseManipulator {
private language: string;

constructor(language: string) {
super();
this.language = language;
}

Expand All @@ -20,7 +35,7 @@ class StripCommentsManipulator implements FileManipulator {
}
}

class PythonManipulator implements FileManipulator {
class PythonManipulator extends BaseManipulator {
removeComments(content: string): string {
// First, use strip-comments to remove standard comments
let result = strip(content, { language: 'python', preserveNewlines: true });
Expand All @@ -36,10 +51,11 @@ class PythonManipulator implements FileManipulator {
}
}

class CompositeManipulator implements FileManipulator {
class CompositeManipulator extends BaseManipulator {
private manipulators: FileManipulator[];

constructor(...manipulators: FileManipulator[]) {
super();
this.manipulators = manipulators;
}

Expand Down
27 changes: 27 additions & 0 deletions src/core/file/fileProcessor.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
import { RepopackConfigMerged } from '../../config/configTypes.js';
import { getFileManipulator } from './fileManipulater.js';
import { ProcessedFile, RawFile } from './fileTypes.js';

export const processFiles = (rawFiles: RawFile[], config: RepopackConfigMerged): ProcessedFile[] => {
return rawFiles.map((rawFile) => ({
path: rawFile.path,
content: processContent(rawFile.content, rawFile.path, config),
}));
};

export const processContent = (content: string, filePath: string, config: RepopackConfigMerged): string => {
let processedContent = content;
const manipulator = getFileManipulator(filePath);

if (config.output.removeComments && manipulator) {
processedContent = manipulator.removeComments(processedContent);
}

if (config.output.removeEmptyLines && manipulator) {
processedContent = manipulator.removeEmptyLines(processedContent);
}

processedContent = processedContent.trim();

return processedContent;
};
Loading

0 comments on commit 617a633

Please sign in to comment.