ts-minifer
Version:
Advanced TypeScript code minification and compression tool
189 lines • 8.59 kB
JavaScript
;
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;
};
})();
Object.defineProperty(exports, "__esModule", { value: true });
exports.TypeScriptCompressor = void 0;
const ts = __importStar(require("typescript"));
const fs = __importStar(require("fs"));
const path = __importStar(require("path"));
const glob = __importStar(require("glob"));
const source_map_1 = require("source-map");
const types_1 = require("./types");
const logger_1 = require("./logger");
class TypeScriptCompressor {
constructor(verbose = false) {
this.logger = new logger_1.Logger(verbose);
}
compressFiles(inputPaths, options) {
this.logger.info(`Starting compression with level: ${options.level}`);
// Resolve input files using glob
const inputFiles = this.resolveInputFiles(inputPaths, options.excludePatterns);
this.logger.info(`Found ${inputFiles.length} files to compress`);
// Parse and transform TypeScript files
const transformedFiles = inputFiles.map((file, index) => this.compressFile(file, options, index, inputFiles.length));
// Combine or output files based on configuration
const outputResult = this.generateOutput(transformedFiles, options);
this.logger.info('Compression completed');
return outputResult;
}
resolveInputFiles(inputPaths, excludePatterns) {
let files = [];
inputPaths.forEach(inputPath => {
const stats = fs.statSync(inputPath);
if (stats.isDirectory()) {
const dirFiles = glob.sync(`${inputPath}/**/*.ts`, {
ignore: excludePatterns || []
});
files = [...files, ...dirFiles];
}
else if (stats.isFile() && path.extname(inputPath) === '.ts') {
files.push(inputPath);
}
});
return files;
}
compressFile(filePath, options, currentIndex, totalFiles) {
this.logger.progress(currentIndex + 1, totalFiles, `Compressing ${path.basename(filePath)}`);
const sourceCode = fs.readFileSync(filePath, 'utf-8');
const sourceFile = ts.createSourceFile(filePath, sourceCode, ts.ScriptTarget.Latest, true);
// Create source map generator
const sourceMapGenerator = new source_map_1.SourceMapGenerator({
file: path.basename(filePath)
});
// Transform based on compression level
const transformedContent = this.transformSourceFile(sourceFile, options, sourceMapGenerator);
return {
originalContent: sourceCode,
transformedContent,
sourceMap: options.generateSourceMaps
? sourceMapGenerator.toString()
: undefined,
originalPath: filePath
};
}
transformSourceFile(sourceFile, options, sourceMapGenerator) {
const transformer = this.createTransformer(options, sourceMapGenerator);
const result = ts.transform(sourceFile, [transformer]);
const printer = ts.createPrinter({
removeComments: options.level !== types_1.CompressionLevel.NONE
});
return printer.printFile(result.transformed[0]);
}
createTransformer(options, sourceMapGenerator) {
return (context) => {
return (sourceFile) => {
const visitor = (node) => {
// Implement compression logic based on compression level
switch (options.level) {
case types_1.CompressionLevel.MINIMAL:
// Remove comments, minimal transformations
return this.minimalCompression(node, context);
case types_1.CompressionLevel.AGGRESSIVE:
// Aggressive name shortening, more aggressive removals
return this.aggressiveCompression(node, context);
default:
return ts.visitEachChild(node, visitor, context);
}
};
return ts.visitNode(sourceFile, visitor);
};
};
}
minimalCompression(node, context) {
if (ts.isJSDocCommentContainingNode(node) || node.kind === ts.SyntaxKind.JSDoc) {
return node;
}
return ts.visitEachChild(node, child => this.minimalCompression(child, context), context);
}
aggressiveCompression(node, context) {
if (ts.isIdentifier(node) && ts.isVariableDeclaration(node.parent)) {
// Implement variable name shortening logic
return ts.factory.createIdentifier(this.shortenName(node.text));
}
return ts.visitEachChild(node, child => this.aggressiveCompression(child, context), context);
}
shortenName(name) {
// Simple implementation - could be made more sophisticated
return `_${name.length}`;
}
generateOutput(transformedFiles, options) {
const outputFiles = [];
const sourceMapFiles = [];
const totalOriginalSize = transformedFiles.reduce((sum, file) => sum + file.originalContent.length, 0);
const totalCompressedSize = transformedFiles.reduce((sum, file) => sum + file.transformedContent.length, 0);
const outputDir = path.join(process.cwd(), 'dist');
fs.mkdirSync(outputDir, { recursive: true });
if (options.outputFormat === 'single') {
// Combine all files into one
const combinedContent = transformedFiles
.map(file => file.transformedContent)
.join('\n');
const outputPath = path.join(outputDir, 'compressed.ts');
fs.writeFileSync(outputPath, combinedContent);
outputFiles.push(outputPath);
if (options.generateSourceMaps) {
const sourceMapPath = `${outputPath}.map`;
fs.writeFileSync(sourceMapPath, transformedFiles
.map(file => file.sourceMap)
.filter(Boolean)
.join('\n'));
sourceMapFiles.push(sourceMapPath);
}
}
else {
// Output multiple files with original names
transformedFiles.forEach((file, index) => {
const outputPath = path.join(outputDir, path.basename(file.originalPath || `compressed_${index}.ts`));
fs.writeFileSync(outputPath, file.transformedContent);
outputFiles.push(outputPath);
if (options.generateSourceMaps && file.sourceMap) {
const sourceMapPath = `${outputPath}.map`;
fs.writeFileSync(sourceMapPath, file.sourceMap);
sourceMapFiles.push(sourceMapPath);
}
});
}
return {
outputFiles,
sourceMapFiles: options.generateSourceMaps ? sourceMapFiles : undefined,
stats: {
originalSize: totalOriginalSize,
compressedSize: totalCompressedSize,
compressionRatio: 1 - (totalCompressedSize / totalOriginalSize)
}
};
}
}
exports.TypeScriptCompressor = TypeScriptCompressor;
//# sourceMappingURL=compressor.js.map