ts-comment-remover
Version:
TypeScript file compression tool that removes comments and unnecessary whitespace using AST
84 lines • 3.78 kB
JavaScript
import { createSourceFile, ScriptTarget, createPrinter } from 'typescript';
import { readFile } from 'node:fs/promises';
import { getTypeScriptFiles, calculateStats, getRelativePath, pipe, } from './utils.js';
export const removeCommentsWithAST = (content) => {
const sourceFile = createSourceFile('temp.ts', content, ScriptTarget.Latest, true);
const printer = createPrinter({
removeComments: true,
omitTrailingSemicolon: false,
});
return printer.printFile(sourceFile);
};
const compressionRules = [
[/\n\s*\n/g, '\n'],
[/^\s+/gm, ''],
[/\s+$/gm, ''],
[/\s*([{}:;,=()[\]<>])\s*/g, '$1'],
[/import\s*{/g, 'import{'],
[/}\s*from/g, '}from'],
[/export\s*{/g, 'export{'],
[/export\s+type/g, 'export type'],
[/export\s+const/g, 'export const'],
[/export\s+enum/g, 'export enum'],
[/export\s+interface/g, 'export interface'],
[/type\s+(\w+)\s*=/g, 'type $1='],
[/:\s*([A-Za-z])/g, ':$1'],
];
const applyRule = (content, [pattern, replacement]) => content.replace(pattern, replacement);
const processLines = (content) => content
.split('\n')
.map(line => line.trim())
.filter(line => line.length > 0)
.join('');
export const advancedCompress = (content) => {
const afterRules = compressionRules.reduce(applyRule, content);
return processLines(afterRules);
};
export const createCompressionPipeline = (...functions) => functions.reduce((acc, fn) => pipe(acc, fn));
export const defaultCompressionPipeline = createCompressionPipeline(removeCommentsWithAST, advancedCompress);
const readFileContent = (filePath) => async () => readFile(filePath, 'utf-8');
export const processFile = (filePath, compress = defaultCompressionPipeline) => async () => {
const originalContent = await readFileContent(filePath)();
const compressedContent = compress(originalContent);
const stats = calculateStats(originalContent.length, compressedContent.length);
return {
path: filePath,
originalContent,
compressedContent,
stats,
};
};
export const formatFileOutput = (file, baseDir, preserveStructure) => {
const relativePath = getRelativePath(file.path, baseDir);
return preserveStructure
? `\n/*=== ${relativePath} ===*/\n${file.compressedContent}\n`
: `/*${relativePath}*/${file.compressedContent}`;
};
export const aggregateStats = (files) => files.reduce((acc, file) => ({
totalOriginalSize: acc.totalOriginalSize + file.stats.originalSize,
totalCompressedSize: acc.totalCompressedSize + file.stats.compressedSize,
}), { totalOriginalSize: 0, totalCompressedSize: 0 });
export const generateOutput = (files, baseDir, preserveStructure) => files
.map(file => formatFileOutput(file, baseDir, preserveStructure))
.join(preserveStructure ? '\n' : '');
export const compressTypeScriptFiles = (options) => async () => {
const { targetDir, includePatterns, excludePatterns, preserveStructure = false, verbose = false, } = options;
const files = await getTypeScriptFiles(targetDir, includePatterns, excludePatterns)();
if (files.length === 0) {
throw new Error(`No TypeScript files found in ${targetDir}`);
}
if (verbose) {
console.log(`Found ${files.length} TypeScript files`);
}
const processedFiles = await Promise.all(files.map(file => processFile(file)()));
const output = generateOutput(processedFiles, targetDir, preserveStructure);
const { totalOriginalSize } = aggregateStats(processedFiles);
const totalCompressedSize = output.length;
const totalStats = calculateStats(totalOriginalSize, totalCompressedSize);
return {
files: processedFiles,
totalStats,
output,
};
};
//# sourceMappingURL=compressor.js.map