ts-transformer-minify-privates
Version:
A TypeScript custom transformer which minify names of private class members
184 lines (183 loc) • 7.72 kB
JavaScript
"use strict";
var __assign = (this && this.__assign) || function () {
__assign = Object.assign || function(t) {
for (var s, i = 1, n = arguments.length; i < n; i++) {
s = arguments[i];
for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p))
t[p] = s[p];
}
return t;
};
return __assign.apply(this, arguments);
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.PropertiesMinifier = void 0;
var ts = require("typescript");
var defaultOptions = {
prefix: '_private_',
};
var PropertiesMinifier = /** @class */ (function () {
function PropertiesMinifier(context, options) {
this.context = context;
this.options = __assign(__assign({}, defaultOptions), options);
}
PropertiesMinifier.prototype.visitSourceFile = function (node, program, context) {
var result = this.visitNodeAndChildren(node, program, context);
return result;
};
PropertiesMinifier.prototype.visitNodeAndChildren = function (node, program, context) {
var _this = this;
return ts.visitEachChild(this.visitNode(node, program), function (childNode) { return _this.visitNodeAndChildren(childNode, program, context); }, context);
};
PropertiesMinifier.prototype.visitNode = function (node, program) {
if (isAccessExpression(node)) {
return this.createNewAccessExpression(node, program);
}
else if (ts.isBindingElement(node)) {
return this.createNewBindingElement(node, program);
}
else if (isConstructorParameterReference(node, program)) {
return this.createNewNode(program, node, this.context.factory.createIdentifier);
}
return node;
};
PropertiesMinifier.prototype.createNewAccessExpression = function (node, program) {
var _this = this;
var typeChecker = program.getTypeChecker();
var accessName = ts.isPropertyAccessExpression(node) ? node.name : node.argumentExpression;
var symbol = typeChecker.getSymbolAtLocation(accessName);
if (!isPrivateNonStaticClassMember(symbol)) {
return node;
}
var propName;
var creator;
if (ts.isPropertyAccessExpression(node)) {
propName = node.name;
creator = function (newName) {
return _this.context.factory.createPropertyAccessExpression(node.expression, newName);
};
}
else {
if (!ts.isStringLiteral(node.argumentExpression)) {
return node;
}
propName = node.argumentExpression;
creator = function (newName) {
return _this.context.factory.createElementAccessExpression(node.expression, _this.context.factory.createStringLiteral(newName));
};
}
return this.createNewNode(program, propName, creator);
};
PropertiesMinifier.prototype.createNewBindingElement = function (node, program) {
var _this = this;
var typeChecker = program.getTypeChecker();
var propName;
var symbol;
if (node.propertyName === undefined) {
// if no property name is set (const { a } = foo)
// then node.propertyName is undefined and we need to find this property by yourself
// so let's use go-to-definition algorithm from TSServer
// see https://github.com/microsoft/TypeScript/blob/672b0e3e16ad18b422dbe0cec5a98fce49881b76/src/services/goToDefinition.ts#L58-L77
if (!ts.isObjectBindingPattern(node.parent)) {
return node;
}
var type = typeChecker.getTypeAtLocation(node.parent);
if (type.isUnion()) {
return node;
}
if (!ts.isIdentifier(node.name)) {
return node;
}
propName = node.name;
symbol = type.getProperty(ts.idText(propName));
}
else {
propName = node.propertyName;
symbol = typeChecker.getSymbolAtLocation(node.propertyName);
}
if (!isPrivateNonStaticClassMember(symbol)) {
return node;
}
return this.createNewNode(program, propName, function (newName) {
return _this.context.factory.createBindingElement(node.dotDotDotToken, newName, node.name, node.initializer);
});
};
PropertiesMinifier.prototype.createNewNode = function (program, oldProperty, createNode) {
var typeChecker = program.getTypeChecker();
var symbol = typeChecker.getSymbolAtLocation(oldProperty);
if (symbol === undefined) {
throw new Error("Cannot get symbol for node \"".concat(oldProperty.getText(), "\""));
}
var oldPropertyName = symbol.escapedName;
var newPropertyName = this.getNewName(oldPropertyName);
var newProperty = createNode(newPropertyName);
return newProperty;
};
PropertiesMinifier.prototype.getNewName = function (originalName) {
return "".concat(this.options.prefix).concat(originalName);
};
return PropertiesMinifier;
}());
exports.PropertiesMinifier = PropertiesMinifier;
function isPrivateNonStatic(node) {
return hasPrivateKeyword(node) && !hasModifier(node, ts.SyntaxKind.StaticKeyword);
}
function hasPrivateKeyword(node) {
return hasModifier(node, ts.SyntaxKind.PrivateKeyword);
}
function hasModifier(node, modifier) {
return getModifiers(node).some(function (mod) { return mod.kind === modifier; });
}
function isAccessExpression(node) {
return ts.isPropertyAccessExpression(node) || ts.isElementAccessExpression(node);
}
function isClassMember(node) {
return ts.isMethodDeclaration(node) || ts.isPropertyDeclaration(node);
}
function isConstructorParameter(node) {
return ts.isParameter(node) && ts.isConstructorDeclaration(node.parent);
}
function isConstructorParameterReference(node, program) {
if (!ts.isIdentifier(node)) {
return false;
}
var typeChecker = program.getTypeChecker();
var symbol = typeChecker.getSymbolAtLocation(node);
return isPrivateNonStaticClassMember(symbol);
}
function isPrivateNonStaticClassMember(symbol) {
// for some reason ts.Symbol.declarations can be undefined (for example in order to accessing to proto member)
if (symbol === undefined || symbol.declarations === undefined) {
return false;
}
return symbol.declarations.some(function (x) {
// terser / uglify property minifiers aren't able to handle decorators
return (isClassMember(x) && !hasDecorators(x) || isConstructorParameter(x)) && isPrivateNonStatic(x) && !hasModifier(x, ts.SyntaxKind.DeclareKeyword);
});
}
function hasDecorators(node) {
if (isBreakingTypeScriptApi(ts)) {
return ts.canHaveDecorators(node) && !!ts.getDecorators(node);
}
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore
// eslint-disable-next-line deprecation/deprecation, @typescript-eslint/no-unsafe-return
return !!node.decorators;
}
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore
function getModifiers(node) {
if (isBreakingTypeScriptApi(ts)) {
if (!ts.canHaveModifiers(node)) {
return [];
}
return ts.getModifiers(node) || [];
}
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore
// eslint-disable-next-line deprecation/deprecation, @typescript-eslint/no-unsafe-return
return node.modifiers || [];
}
function isBreakingTypeScriptApi(compiler) {
return 'canHaveDecorators' in ts;
}