UNPKG

ts-transformer-minify-privates

Version:

A TypeScript custom transformer which minify names of private class members

184 lines (183 loc) 7.72 kB
"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; }