Coder Social home page Coder Social logo

timocov / ts-transformer-minify-privates Goto Github PK

View Code? Open in Web Editor NEW
52.0 5.0 5.0 82 KB

A TypeScript custom transformer which renames private class members

License: MIT License

TypeScript 42.22% JavaScript 57.12% Shell 0.66%
typescript minifying transformer-typescript typescript-transformer

ts-transformer-minify-privates's Introduction

ts-transformer-minify-privates

npm version CircleCI Downloads

A TypeScript custom transformer which minify names of private class members.

For now it just renames private members with prepending some prefix to name. For example, if you have privateMember, then after transformation the name will be _private_privateMember. After that you can use terser/uglify with mangle options to minify that members. Note, that private class members with decorators won't be prefixed and further minified.

Caution!!!

Before start using this transformer in the production, I strongly recommend you check that your code compiles successfully and all files has correct output. I would say check the whole project file-by-file and compare the input with the (expected) output.

I cannot guarantee you that the transformer covers all possible cases, but it has tests for the most popular ones, and if you catch a bug - please feel free to create an issue.

I've tested it for several projects and it works well.

Requirement

  • Make sure that noImplicitThis is enabled in your tsconfig.

Options

prefix

Default: _private_

The prefix which will be added to private member's name.

How to use the custom transformer

Unfortunately, TypeScript itself does not currently provide any easy way to use custom transformers (see microsoft/TypeScript#14419). The followings are the example usage of the custom transformer.

webpack (with ts-loader or awesome-typescript-loader)

// webpack.config.js
const minifyPrivatesTransformer = require('ts-transformer-minify-privates').default;

module.exports = {
  // ...
  module: {
    rules: [
      {
        test: /\.ts$/,
        loader: 'ts-loader', // or 'awesome-typescript-loader'
        options: {
          getCustomTransformers: program => ({
              before: [
                  minifyPrivatesTransformer(program)
              ]
          })
        }
      }
    ]
  }
};

Rollup (with rollup-plugin-typescript2)

// rollup.config.js
import typescript from 'rollup-plugin-typescript2';
import minifyPrivatesTransformer from 'ts-transformer-minify-privates';

export default {
  // ...
  plugins: [
    typescript({ transformers: [service => ({
      before: [ minifyPrivatesTransformer(service.getProgram()) ],
      after: []
    })] })
  ]
};

ttypescript

See ttypescript's README for how to use this with module bundlers such as webpack or Rollup.

// tsconfig.json
{
  "compilerOptions": {
    // ...
    "plugins": [
      { "transform": "ts-transformer-minify-privates" }
    ]
  },
  // ...
}

Results

I've tested the transformer on lightweight-charts and the bundle size was reduced:

  • on ~15% min (from 186KB to 157KB)
  • on ~5% min.gz (from 43KB to 41KB)

ts-transformer-minify-privates's People

Contributors

mad-gooze avatar timocov avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar

ts-transformer-minify-privates's Issues

How to use this package with vite library mode?

Dear developers, I am using Vite library mode for my vanilla-ts front-end lib, and my typescript class contains too much private fields that causing minified umd js file too large. So I'm trying to use your package.
Vite basically uses rollup for building typescript files, so I follow your rollup guide and here is my vite.config.js file:

import { resolve } from 'path';
import { defineConfig } from "vite";
import typescript from 'rollup-plugin-typescript2';
import minifyPrivatesTransformer from 'ts-transformer-minify-privates';

export default defineConfig({
    server: {
        port: 3000
    },
    build: {
        lib: {
            entry: resolve(__dirname, 'src/lib/my-lib.ts'),
            name: 'MyLib',
            fileName: 'my-lib',
            formats: ['umd'],
        },
        rollupOptions: {
            output: {
                assetFileNames: (chunkInfo) => {
                    if (chunkInfo.name === 'style.css')
                        return 'my-lib.min.css';
                },
                entryFileNames: 'my-lib.min.js',
            },
            plugins: [
                typescript({
                    transformers: [service => ({
                        before: [minifyPrivatesTransformer(service.getProgram())],
                        after: []
                    })]
                })
            ]
        }
    }
})

But this basically not gonna work, build will failed with this configuration.
So could you plz tell me what should I do make it work? Or do you have any plan to support Vite library mode? Thank you!

Minifying protected and public members

Hey, have you thought about extending the transform to protected and public fields?

I understand that it's generally unsafe, but it will work for code which is not used externally and just bundled for a specific app.

This behaviour can be optional and enabled for classes with names matching a configurable regexp, a set of source files and etc.

Deprecated API warning: 'createIdentifier' has been deprecated since v4.0.0

DeprecationWarning: 'createIdentifier' has been deprecated since v4.0.0. Use the appropriate method on 'ts.factory' or the 'factory' supplied by your transformation context instead.

DeprecationWarning: '' has been deprecated since v4.0.0. Use the appropriate method on 'ts.factory' or the 'factory' supplied by your transformation context instead.

There's a deprecation warning Please solve it

Correct handle or fail build if access to this with runtime-based key

input.ts:

export class Class {
	private privateField: string = 'string-value';

	public constructor() {
		const that = this;
		that[Math.random() ? 'privateMethod' : 'publicMethod'](this.privateField);
		this[Math.random() ? 'privateMethod' : 'publicMethod'](this.privateField);
	}

	public publicMethod(): void {

	}

	private privateMethod(a: string): void {

	}
}

output.js:

"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
var Class = /** @class */ (function () {
    function Class() {
        this._private_privateField = 'string-value';
        var that = this;
        that[Math.random() ? '_private_privateMethod' : 'publicMethod'](this._private_privateField);
        this[Math.random() ? '_private_privateMethod' : 'publicMethod'](this._private_privateField);
    }
    Class.prototype.publicMethod = function () {
    };
    Class.prototype._private_privateMethod = function (a) {
    };
    return Class;
}());
exports.Class = Class;

or fail with error

Bugs when using decorators with private props/methods

There is a problem, when using decorators with private class members. This package is meant to be used with terser minify properties option, but for now terser isn't able to work with class members decorators (issue here). Seems, that can be fixed, when add additional check for decorators here. That would be smth like:

return (isClassMember(x) && !hasDecorators(x) || isConstructorParameter(x)) && isPrivateNonStatic(x);

where hasDecorators fn impl:

function hasDecorators(node: ts.Node): boolean {
    return !!node.decorators;
}

seems, this solution would fit for all cases now, since props or methods with decorators can't be minified anyway?
If any thoughts, i would be glad to contribute

Handle destructuring of this type

class FooBar {
	private privateField: string = 'sda';

	public publicMethod(): void {
		const { privateField } = this; // should correct handle this
		this.privateMethod(privateField);
	}

	private privateMethod(a: string): void {

	}
}

How set up with angular?

I'm tried:

npm install ts-transformer-minify-privates --save-dev
npm install @angular-builders/custom-webpack  --save-dev
npm install ts-loader  --save-dev

angular.json

  "projects": {
      "architect": {
        "build": {
          "builder": "@angular-builders/custom-webpack:browser",
          "options": {
            "customWebpackConfig": {
              "path": "custom-webpack.config.js",
              "replaceDuplicatePlugins": true
            }
        }
    }
  }

custom-webpack.config.js

// webpack.config.js
var minifyPrivatesTransformer = require('ts-transformer-minify-privates').default;

module.exports = (config, options) => {
  if (config.module && config.module.rules) {
    config.module.rules.push(
      {
        test: /\.ts$/,
        loader: 'ts-loader', // or 'awesome-typescript-loader'
        options: {
          getCustomTransformers: program => ({
            before: [
              minifyPrivatesTransformer(program)
            ]
          })
        }
      }
    );
  }
  return config;
}

tsconfig.ts

"compilerOptions": {
    "plugins": [
      {
        "transform": "ts-transformer-minify-privates"
      }
    ]
}

RUN BUILD

npm run build

but private field aren't minified... :(

Stable name generation

For now names are generated in random order aren't stable. Need to implement stable name generation.

Invalid declarations

Hi! It seems the private attributes are not updated in the declaration file. Do you know if it's possible?

input.ts:

export class Class {
	public publicField: number = 123;
	private privateField: string = 'string-value';

	public constructor() {
		this.privateMethod(this.privateField);
		this.privateMethod(this.publicField);

		this['privateMethod'](this.privateField);
	}

	private privateMethod(a: string | number): void { }
}

output.js:

"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.Class = void 0;
var Class = /** @class */ (function () {
    function Class() {
        this.publicField = 123;
        this._private_privateField = 'string-value';
        this._private_privateMethod(this._private_privateField);
        this._private_privateMethod(this.publicField);
        this["_private_privateMethod"](this._private_privateField);
    }
    Class.prototype._private_privateMethod = function (a) { };
    return Class;
}());
exports.Class = Class;

input.d.ts:

export declare class Class {
    publicField: number;
    private privateField;
    constructor();
    private privateMethod;
}

private function referenced in an anonymous function

Hi,

I found a case where the private function references are not handled which could cause runtime errors. Some code snippet.

private functionA {}

private functionB {
     document.getElementById("button").addEventListener("click", function(){
           this.functionA(); // not transformed
    }.bind(this));
}

The pattern is use .bind(this) for an anonymous function and call the private function within the anonymous function.
Workaround is to use arrow function or convert it to named function as another private members.

Thanks,
Weiwei

Error: "Cannot minify accessing for ..."

Hey, thanks for this project, looks very useful.

I'm getting the error specified in the title of this issue for some members, it's originating from this line:

if (!ts.isStringLiteral(node.argumentExpression)) {
    // it's access for private class' member - maybe need to warn here?
    throw new Error(`Cannot minify accessing for ${node.argumentExpression.getText()} property`);
}

Most other members minify fine, so it would be great if it was possible to ignore this error, instead of failing the build.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.