Code Monkey home page Code Monkey logo

ts-transformer-properties-rename's People

Contributors

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  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-properties-rename's Issues

Error: Cannot find source file

Bug report

Hello there, I'm tring to set up TypeScript function minification using this lib.

However for some reason it just won't "find" the entry source files I give to it....

My setup is using webpack. See relevant configs below.

I have already tried using the following methods in most combinations:

  • { options: { getCustomTransformers: program => ({ before: [ ... ] }) } }
  • { loader: require.resolve('ts-loader'), options: { compiler: 'ttypescript', } } and tsconfig.json setup...
  • { loader: require.resolve('awesome-typescript-loader'), } and tsconfig.json setup...
  • relative path to entrySourceFiles (from project root)
  • relative path to entrySourceFiles (from node module)
  • absolute path to entrySourceFiles

However, the error message always stays the same: Error: Cannot find source file.

The weird part is, that the .getSourceFile(...) method definetley exists on program when I do a console.log.
It just does not seem to return or actually find the file.

Am I missing something about the config perhaps?

Thanks in advance! I'm really at a loss with this...

tsconfig.json

{
	"compilerOptions": {
		"outDir": "./dist/tsc",
		"target": "es2020",
		"module": "commonjs", // commonjs | esnext
		"moduleResolution": "node",
		"esModuleInterop": true, // TODO
		"emitDecoratorMetadata": false,
		"sourceMap": false,
		"removeComments": true,
		"baseUrl": ".",
		"paths": {
			"three": [
				"node_modules/three/src/Three"
			]
		},
		"lib": [
			"dom",
			"ES5"
		],
		"plugins": [
			{ 
				"transform": "ts-transformer-properties-rename",
				"entrySourceFiles": ["./src/AudiOrbi.ts"]
			}
		]
	},
	"include": [
		"./src/**/*.ts"
	],
	"exclude": [
		"**/*.asc",
		"**/better-docs/**"
	]
}

webpack.config.js

const path = require('path');

const ForkTsCheckerWebpackPlugin = require('fork-ts-checker-webpack-plugin');

const TerserPlugin = require('terser-webpack-plugin');

const ENTRY_FILE = path.resolve(__dirname, 'src', 'AudiOrbi.ts');
const BUILD_PATH = path.resolve(__dirname, 'dist');

module.exports = (env) => {
	const prod = env.production || false;

	return {
		mode: 'production',
		entry: {
			audiorbits: ENTRY_FILE,
		},
		output: {
			chunkFilename: '[id].bundle.js',
			path: BUILD_PATH,
		},
		resolve: {
			extensions: ['.ts', '.js'],
		},
		module: {
			rules: [
				// TypeScript loader
				{
					test: /\.tsx?$/,
					loader: require.resolve('ts-loader'),
					options: {
						compiler: 'ttypescript',
						configFile: 'tsconfig.json',
						transpileOnly: true,
					},
				},
				......
			],
		},
		plugins: [
			// The TS Syntax checking
			new ForkTsCheckerWebpackPlugin({}),
			.......
		],
		// remove dead code in production
		optimization: prod ? {
			minimizer: [
				new TerserPlugin({
					// https://github.com/webpack-contrib/terser-webpack-plugin#terseroptions
					terserOptions: {
						parse: {},
						compress: {
							unsafe: true,
							pure_funcs: ['console.warn'], // ~40kb of three.js messages... errors will stil come through.
						},
						mangle: {
							properties: {
								regex: /^_(private|internal)_/, // the same prefixes like for custom transformer
							},
						},
						module: true,
						sourceMap: false,
					},
				}),
			],
		}: {},
	};
};

Error output

// place your code here
./src/AudiOrbi.ts 39 bytes [built] [code generated] [1 error]

ERROR in ./src/AudiOrbi.ts
Module build failed (from ./node_modules/ts-loader/index.js):
Error: Cannot find source file ./src/AudiOrbi.ts
    at ExportsSymbolTree.computeTreeForExports (D:\MyProject\node_modules\ts-transformer-properties-rename\dist\exports-symbol-tree.js:27:23)
    at new ExportsSymbolTree (D:\MyProject\node_modules\ts-transformer-properties-rename\dist\exports-symbol-tree.js:10:14)
    at createTransformerFactory (D:\MyProject\node_modules\ts-transformer-properties-rename\dist\transformer.js:38:29)
    at propertiesRenameTransformer (D:\MyProject\node_modules\ts-transformer-properties-rename\dist\transformer.js:32:12)
    at createTransformerFromPattern (D:\MyProject\node_modules\ttypescript\lib\PluginCreator.js:40:19)
    at PluginCreator.createTransformers (D:\MyProject\node_modules\ttypescript\lib\PluginCreator.js:123:31)
    at Object.newEmit [as emit] (D:\MyProject\node_modules\ttypescript\lib\patchCreateProgram.js:68:52)
    at Object.transpileModule (D:\MyProject\node_modules\typescript\lib\typescript.js:131713:17)
    at getTranspilationEmit (D:\MyProject\node_modules\ts-loader\dist\index.js:388:75)
    at successLoader (D:\MyProject\node_modules\ts-loader\dist\index.js:37:11)

[feature] Lazily resolve TS Program

Currently the transformer factory requires a ts.Program as a parameter, but this might not be available yet. I would like it to accept an method that would return the ts.Program instance when needed.

Usecase
When I manipulate the Angular webpack compiler configuration to include this transformer it might not yet have created the ts.Program instance. If it would accept a lazy resolver method, this is not longer an issue:

propertiesRenameTransformer(() => angularCompilerPlugin._getTsProgram(), { entrySourceFiles: angularCompilerPlugin._rootNames })

I can create a PR that would accept both an instance or a method that would return an instance if that would be appreciated.

Wrong code for indexed generic in returned type

Bug report

Input code

export interface Ohlc {
	open: number;
	high: number;
	low: number;
	close: number;
}

export interface BarValueByType {
	Bar: Ohlc;
	Line: number;
}

export type SeriesType = keyof BarValueByType;

export function getBarValue<T extends SeriesType>(value: number, seriesType: T): BarValueByType[T] {
	if (seriesType === 'Bar') {
		return {
			open: value,
			high: value,
			low: value,
			close: value,
		} as BarValueByType[T];
	} else {
		return value as BarValueByType[T];
	}
}

Expected output

"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.getBarValue = void 0;
function getBarValue(value, seriesType) {
    if (seriesType === 'Bar') {
        return {
            open: value,
            high: value,
            low: value,
            close: value,
        };
    }
    else {
        return value;
    }
}
exports.getBarValue = getBarValue;

Actual output

"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.getBarValue = void 0;
function getBarValue(value, seriesType) {
    if (seriesType === 'Bar') {
        return {
            _internal_open: value,
            _internal_high: value,
            _internal_low: value,
            _internal_close: value,
        };
    }
    else {
        return value;
    }
}
exports.getBarValue = getBarValue;

Constructor parameters in internal classes get prefixed.

Bug report

Input code

class Measure {
    height: number;

    constructor(public width: number, public ascent: number, public descent: number) {
        this.height = ascent + descent;
    }
}

export class Foo {}

Expected output

class Measure {
      constructor(width, ascent, descent) {
              this._internal_width = width;
              this._internal_ascent = ascent;
              this._internal_descent = descent;
              this._internal_height = ascent + descent;
       }
}

export class Foo {
}

Actual output

class Measure {
      constructor(_internal_width, _internal_ascent, _internal_descent) {
              this._internal_width = _internal_width;
              this._internal_ascent = _internal_ascent;
              this._internal_descent = _internal_descent;
              this._internal_height = ascent + descent;
       }
}

export class Foo {
}

Additional context
Reproducible if the code above is added as input.ts to the test directory in this repo and npm run test is started.

Incorrect handling unknown/any casting

Bug report

Input code

function func(): unknown {
	const b = { value: 321 } as unknown;
	const c = { int: 222 } as unknown as InternalInterface;

	return {
		foo: 1,
		bar: b,
		baz: 3,
		c: c,
	};
}
function func2(): any {
	const b = { value: 321 } as any;
	const c = { field: 222 } as any as InternalInterface;
	return {
		foo: 1,
		bar: b,
		baz: 3,
		c: c,
	};
}

Expected output

function func() {
    var b = { value: 321 };
    var c = { _internal_int: 222 };

    return {
        foo: 1,
        bar: b,
        baz: 3,
        c: c,
    };
}
function func2() {
    var b = { value: 321 };
    var c = { _internal_field: 222 };

    return {
        foo: 1,
        bar: b,
        baz: 3,
        c: c,
    };
}

Actual output

function func() {
    var b = { _internal_value: 321 };
    var c = { _internal_int: 222 };

    return {
        _internal_foo: 1,
        _internal_bar: b,
        _internal_baz: 3,
        _internal_c: c,
    };
}
function func2() {
    var b = { _internal_value: 321 };
    var c = { _internal_field: 222 };

    return {
        _internal_foo: 1,
        _internal_bar: b,
        _internal_baz: 3,
        _internal_c: c,
    };
}

Incorrect handling mapped types

Bug report

Input code

interface Foo {
	field: string;
	field2: number;
}

type ReadonlyFoo = Readonly<Foo>;

function bar(foo: ReadonlyFoo): void {
	console.log(foo.field, foo.field2);
}

function baz(): void {
	const foo: Foo = {
		field: '',
		field2: 0,
	};

	bar(foo);

	bar({
		field: '',
		field2: 0,
	});
}

Expected output

function bar(foo) {
    console.log(foo._internal_field, foo._internal_field2);
}
function baz() {
    var foo = {
        _internal_field: '',
        _internal_field2: 0,
    };
    bar(foo);
    bar({
        _internal_field: '',
        _internal_field2: 0,
    });
}

Actual output

function bar(foo) {
    console.log(foo.field, foo.field2);
}
function baz() {
    var foo = {
        _internal_field: '',
        _internal_field2: 0,
    };
    bar(foo);
    bar({
        field: '',
        field2: 0,
    });
}

Additional context

Broken in #15

Enums are not renamed causing runtime error

Enum members are not renamed, but member access is renamed resulting in potential runtime errors.

export enum TestEnum {
  Test1,
  Test2,
  Test3,
}

Results in:

export var TestEnum;
(function (TestEnum) {
    TestEnum[TestEnum["Test1"] = 0] = "Test1";
    TestEnum[TestEnum["Test2"] = 1] = "Test2";
    TestEnum[TestEnum["Test3"] = 2] = "Test3";
})(TestEnum || (TestEnum = {}));

Member access results in:

console.log(TestEnum._internal_Test1);

Which does not exist.

Incorrect handling generic type alias

Bug report

Input code

class Foo<Type extends string> {
	public search(): void {}
}

type StringFoo<T extends string> = Foo<T>;

class Bar {
	private foo: StringFoo<string> = new Foo();

	public doSomething(): void {
		this.foo.search();
	}
}

export interface FooBar {}

Expected output

"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
var Foo = /** @class */ (function () {
    function Foo() {
    }
    Foo.prototype._internal_search = function () { };
    return Foo;
}());
var Bar = /** @class */ (function () {
    function Bar() {
        this._private_foo = new Foo();
    }
    Bar.prototype._internal_doSomething = function () {
        this._private_foo._internal_search();
    };
    return Bar;
}());

Actual output

"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
var Foo = /** @class */ (function () {
    function Foo() {
    }
    Foo.prototype._internal_search = function () { };
    return Foo;
}());
var Bar = /** @class */ (function () {
    function Bar() {
        this._private_foo = new Foo();
    }
    Bar.prototype._internal_doSomething = function () {
        this._private_foo.search();
    };
    return Bar;
}());

Getters/Setters with Object.defineProperty

Bug report

Input code

// This will be an internal class determined by entry point
export class testProperties {
    private _salad: number = 0;
    public dressing: number = 0;
    public get salad(): number { return this._salad; }
    public set salad(val: number) { this._salad = val; }
}

var test = new testProperties();
test.salad = 0;
test.dressing = 0;
var totalSalad = test.salad + 1;
var totalDressing = test.dressing + 0;

Expected output

(function(exports){
exports.testProperties = void 0;
    var testProperties = (function () {
        function testProperties() {
            this._private__salad = 0;
            this._internal_dressing = 0;
        }
        Object.defineProperty(testProperties.prototype, "_internal_salad", {
            get: function () { return this._private__salad; },
            set: function (val) { this._private__salad = val; },
            enumerable: false,
            configurable: true
        });
        return testProperties;
    }());
    exports.testProperties = testProperties;
    var test = new testProperties();
    test._internal_salad = 0;
    test._internal_dressing = 0;
    var totalSalad = test._internal_salad + 1;
    var totalDressing = test._internal_dressing + 0;
})(output);

Actual output

(function(exports){
exports.testProperties = void 0;
    var testProperties = (function () {
        function testProperties() {
            this._private__salad = 0;
            this._internal_dressing = 0;
        }
        Object.defineProperty(testProperties.prototype, "salad", {
            get: function () { return this._private__salad; },
            set: function (val) { this._private__salad = val; },
            enumerable: false,
            configurable: true
        });
        return testProperties;
    }());
    exports.testProperties = testProperties;
    var test = new testProperties();
    test._internal_salad = 0;
    test._internal_dressing = 0;
    var totalSalad = test._internal_salad + 1;
    var totalDressing = test._internal_dressing + 0;
})(output);

Additional context
The "salad" argument of Object.defineProperty was not renamed to "_internal_salad"

Handle object literals with "erased" type

Bug report

I believe it's one of the optimisations TypeScript compiler is doing, like type interning, but the type of the object literal can be "erased" down to an anonymous type when used in combination with generic function.

The examples below give more context.

The Model interface is annotated as @public and should not be minified.
However, when the generic context function gets in the middle, the type checker ends up "erasing" the type of the object literal being returned, i.e. instead of type being Model it becomes an anonymous { param1: string, param2: string }.
As a result the minifier will now minify property names it should not, breaking the code.

Input code

/** @public */
interface Model {
    param1: string;
    param2: string;
}

export declare function context<Result>(name: string, producer: () => Result): Result;

export function exampleWithGeneric(param1: string, param2: string): Model {
    return context("contextName", () => {
        return {
            param1: param1,
            param2: param2,
        };
    });
}

export function exampleWithGenericAndAnnotation(param1: string, param2: string): Model {
    return context<Model>("contextName", () => {
        return {
            param1: param1,
            param2: param2,
        };
    });
}

export function exampleWithoutGeneric(param1: string, param2: string): Model {
    return {
        param1: param1,
        param2: param2,
    };
}

Expected output

/** @public */
interface Model {
    param1: string;
    param2: string;
}

export declare function context<Result>(name: string, producer: () => Result): Result;

export function exampleWithGeneric(param1: string, param2: string): Model {
    return context("contextName", () => {
        return {
            param1: param1,
            param2: param2,
        };
    });
}

export function exampleWithGenericAndAnnotation(param1: string, param2: string): Model {
    return context<Model>("contextName", () => {
        return {
            param1: param1,
            param2: param2,
        };
    });
}

export function exampleWithoutGeneric(param1: string, param2: string): Model {
    return {
        param1: param1,
        param2: param2,
    };
}

Actual output

/** @public */
interface Model {
    param1: string;
    param2: string;
}

export declare function context<Result>(name: string, producer: () => Result): Result;

export function exampleWithGeneric(param1: string, param2: string): Model {
    return context("contextName", () => {
        return {
            _internal_param1: param1,
            _internal_param2: param2,
        };
    });
}

export function exampleWithGenericAndAnnotation(param1: string, param2: string): Model {
    return context<Model>("contextName", () => {
        return {
            param1: param1,
            param2: param2,
        };
    });
}

export function exampleWithoutGeneric(param1: string, param2: string): Model {
    return {
        param1: param1,
        param2: param2,
    };
}

Additional context

It's worth noting that it seems to be how TypeScript compiler resolves types.
Even VS Code's LSP indicates it.

image

image

Annotating generic functions with type signature is an option, but not a very reliable one because I don't think there's a way to enforce it.
If there's such option with linter or compiler that'd be nice.

It feels like transformer plugin has to do the work compiler chose not to.
E.g. if object literal is part of return statement then the plugin would have to analyse return type of the parent function and then all the other parent functions all the way up to the top (source file) and check if any of those types is external or annotated with @public.

Mapped types do not respect @public jsdoc

Mapped types do not appear to respect @public jsdoc

/** @public */
export type TestKey = "a" | "b";
/** @public */
export type TestMappedType = {
  /** @public */
  [P in TestKey]?: {};
};

class TestMappedClass {
  /** @public */
  private testMapped: TestMappedType = {};
  public constructor() {
    this.testMapped.a = {};
  }
}

Results in

"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
var TestMappedClass = /** @class */ (function () {
    function TestMappedClass() {
        /** @public */
        this._private_testMapped = {};
        this._private_testMapped._internal_a = {};
    }
    return TestMappedClass;
}());

Note: I tried it using Record as well and it has the same issue

Fields declared with `declare` keyword are renamed but shouldn't

Bug report

Related to timocov/ts-transformer-minify-privates#24

Input code

Enable useDefineForClassFields compiler option.

class Class {
	public declare publicField: number;
	protected declare protectedField: number;
	private declare privateField: number;

	public method() {
		console.log(this.publicField, this.protectedField, this.privateField);
	}
}

Expected output

var Class = /** @class */ (function () {
    function Class() {
    }
    Object.defineProperty(Class.prototype, "_internal_method", {
        enumerable: false,
        configurable: true,
        writable: true,
        value: function () {
            console.log(this.publicField, this.protectedField, this.privateField);
        }
    });
    return Class;
}());

Actual output

var Class = /** @class */ (function () {
    function Class() {
        Object.defineProperty(this, "_internal_publicField", {
            enumerable: true,
            configurable: true,
            writable: true,
            value: void 0
        });
        Object.defineProperty(this, "_internal_protectedField", {
            enumerable: true,
            configurable: true,
            writable: true,
            value: void 0
        });
        Object.defineProperty(this, "_private_privateField", {
            enumerable: true,
            configurable: true,
            writable: true,
            value: void 0
        });
    }
    Object.defineProperty(Class.prototype, "_internal_method", {
        enumerable: false,
        configurable: true,
        writable: true,
        value: function () {
            console.log(this._internal_publicField, this._internal_protectedField, this._private_privateField);
        }
    });
    return Class;
}());

Handle Internal Class renames

It would be nice if this transformer could also determine internal class names via the entry points and mangle them, if mangling like terser is not feasible then possibly just map class names and the export.classname to a prefixed random set of characters, like cl$AZa

Computed or Invalid property names

Bug report

Input code

export class testProperties {
    public thank: number = 0;
    public you: number = 0;
    public painInTheButton0: number = 0;
    public painInTheButton1: number = 0;
    public painInTheButton2: number = 0;
}

var test = new testProperties();
test.thank = 0;
test.you = 0;
for (var i = 0; i < 3; i++)
    test['painInTheButton' + i] = i;
var totalThanks = test.thank + 1;
var totalForYou = test['you'] + 0;
var button1Result = test.painInTheButton1;
var button2Result = test['painInTheButton' + 2];
var button3Result = test['painInTheButton3'];

Expected output

error: testProperties.ts:13 Computed property, use /** @ignore-rename:[painInTheButton0,painInTheButton1,painInTheButton2] @ignore-computed-property */
error: testProperties.ts:18 Invalid property, use /** @ignore-invalid-property */

Actual output

(function(exports){
exports.testProperties = void 0;
    var testProperties = (function () {
        function testProperties() {
            this._internal_thank = 0;
            this._internal_you = 0;
            this._internal_painInTheButton0 = 0;
            this._internal_painInTheButton1 = 0;
            this._internal_painInTheButton2 = 0;
        }
        return testProperties;
    }());
    exports.testProperties = testProperties;
    var test = new testProperties();
    test._internal_thank = 0;
    test._internal_you = 0;
    for (var i = 0; i < 3; i++)
        test['painInTheButton' + i] = i;
    var totalThanks = test._internal_thank + 1;
    var totalForYou = test["_internal_you"] + 0;
    var button1Result = test._internal_painInTheButton1;
    var button2Result = test['painInTheButton' + 2];
    var button3Result = test['painInTheButton3'];
})(output);

Additional context
The two issues are...
1. 'painInTheButton' + i wont get renamed to '_internal_painInTheButton' + i, even if it did, terser wouldn't be able to rename that property to the mapped mangle.
2. 'painInTheButton3' doesn't exist on the object, since I only defined up to 2.

I would prefer these computed and invalid properties to throw an error when compiling, and if I want to ignore them have some option to do so. I would rather not find the bug through user report :(

The reason I would think the invalid property should error is to specifically review, and then can change to warn about it, or ignore it

Incorrect handing `in` operator with string literal

Bug report

Input code

export interface ExportedInterface {
	foo: number;
}

export interface AnotherExportedInterface {
	bar: number;
	isAnother: boolean;
}

export type ExportedType = ExportedInterface | AnotherExportedInterface;

interface NonExportedInterface {
	foo: number;
}

interface AnotherNonExportedInterface {
	bar: number;
	isAnother: boolean;
}

type NonExportedType = NonExportedInterface | AnotherNonExportedInterface;

declare function getNonExported(): NonExportedType;
declare const moveEvent: MouseEvent | TouchEvent;

export function func(type: ExportedType): void {
	if ('isAnother' in type) {
		console.log(type.bar);
	} else {
		console.log(type.foo);
	}

	const nonExportedVar = getNonExported();
	if ('isAnother' in nonExportedVar) {
		console.log(nonExportedVar.bar);
	} else {
		console.log(nonExportedVar.foo);
	}

	if ('onorientationchange' in window || 'button' in moveEvent) {
		console.log('check');
	}
}

Expected output

"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
function func(type) {
    if ('isAnother' in type) {
        console.log(type.bar);
    }
    else {
        console.log(type.foo);
    }
    var nonExportedVar = getNonExported();
    if ('_internal_isAnother' in nonExportedVar) {
        console.log(nonExportedVar._internal_bar);
    }
    else {
        console.log(nonExportedVar._internal_foo);
    }
    if ('onorientationchange' in window || 'button' in moveEvent) {
        console.log('check');
    }
}
exports.func = func;

Actual output

"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
function func(type) {
    if ('isAnother' in type) {
        console.log(type.bar);
    }
    else {
        console.log(type.foo);
    }
    var nonExportedVar = getNonExported();
    if ('isAnother' in nonExportedVar) {
        console.log(nonExportedVar._internal_bar);
    }
    else {
        console.log(nonExportedVar._internal_foo);
    }
    if ('onorientationchange' in window || 'button' in moveEvent) {
        console.log('check');
    }
}
exports.func = func;

Incorrect renaming `length` property of tuple

Bug report

Input code

type Tuple = [number, string] | [];

declare function getTuple(): Tuple;

export function doSomething(): void {
	console.log(getTuple().length);
}

Expected output

"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
function doSomething() {
    console.log(getTuple().length);
}
exports.doSomething = doSomething;

Actual output

"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
function doSomething() {
    console.log(getTuple()._internal_length);
}
exports.doSomething = doSomething;

Return type of public function is marked as internal

Bug report

When I do not specify return type to public functions the returning literal object is transformed to the object with internal properties.

Input code

function fn(input: { test1: number; test2: string }): { a: number; b: string } {
  return {
    a: 2,
    b: 'str',
  };
}

export function fn2(input: { test1: number; test2: string }) {
  const { a } = fn({ test1: 25, test2: 'test' });

  return {
    a,
    b: 'str',
  };
}

Expected output

function fn(input) {
    return {
        _internal_a: 2,
        _internal_b: 'str',
    };
}
function fn2(input) {
    const { _internal_a: a } = fn();
    return {
        a,
        b: 'str',
    };
}

export { fn2 };

Actual output

function fn(input) {
    return {
        _internal_a: 2,
        _internal_b: 'str',
    };
}
function fn2(input) {
    const { _internal_a: a } = fn();
    return {
        _internal_a: a,
        _internal_b: 'str',
    };
}

export { fn2 };

Additional context
If I specify return type explicitly it works as expected

function fn(input: { test1: number; test2: string }): { a: number; b: string } {
  return {
    a: 2,
    b: 'str',
  };
}

export function fn2(input: { test1: number; test2: string }): { a: number; b: string } { // this is the only change as return type is explicit now
  const { a } = fn({ test1: 25, test2: 'test' });

  return {
    a,
    b: 'str',
  };
}

Add loose mode

Currently the transformer trying to figure out visibility type of every particular node, what allows minify properties better.

Needs to add "loose" mode, what will use the only name of the property (rather that type/symbol) to detect visibility type.

For example,

export interface Foo {
  prop: number;
}

export class Bar implements Foo {
  prop: number = 123;
}

class Baz {
  prop: number = 321;
}

In the current mode we'll rename Baz::prop to Baz::_internal_prop, but Bar::prop will be unchanged.

In "loose" mode we won't rename Baz::prop, because there is another "public" property with the same name prop (but in Bar class). So, we'll just pop-up all "public" properties and use that set of names only.

Properly handle class members with decorators

The goal of this package is to mangle renamed properties by minifiers like 'terser', but there is an issue, when using class members with decorators. When transformer renames decorated class member with prefix, this class member is mangled by minifier, but it can't handle connection of this class member and decorator function.
Ommiting implementation of __decorate fn, decorator transform has the following output

// input
class Foo {
  @foo private field = 1;
}

// output
class Foo {
    constructor() {
        this.field = 1;
    }
}
__decorate([
    foo
], Foo.prototype, "field", void 0);

If use ts-transformer-properties-rename package, it will be transformed to

// input
class Foo {
  @foo private _private_field = 1;
}

// output
class Foo {
    constructor() {
        this._private_field = 1;
    }
}
__decorate([
    foo
], Foo.prototype, "_private_field", void 0);

But after terser's mangling it will be smth like that

// input
class Foo {
  @foo private i = 1;
}

// output
class Foo {
    constructor() {
        this.i = 1;
    }
}
__decorate([
    foo
], Foo.prototype, "_private_field", void 0);

There is an issue in terser repo, where contributors assumed, that it could be solved by mangling string literals by same prefix as well, but according issue is still unresolved

An object with quoted keys is not minified

I notice that if I quote the attribute it is not (and I like it!):

I'd say this is a bug tbh ๐Ÿ˜‚ I'll fix this later.

I think that this is a bug because this tool relies on the type information rather than the syntax so whatever syntax you use it should work the same IMO.

Originally posted by @timocov in #29 (comment)

Handle custom types in local folders

There is types and typesRoot compiler options, which allows you declare types from custom packages. Needs to change detecting whether the package is from external module to handle this.

Improvde DX and migration from `ts-transformer-minify-privates`

@timocov I've tried to use https://github.com/timocov/ts-transformer-properties-rename but couldn't make it work.

How I use ts-transformer-minify-privates in webpack ts-loader:

import:

import minifyPrivatesTransformer from 'ts-transformer-minify-privates';

use in ts-loader rule:

    {
        loader: 'ts-loader',
        options: {
            transpileOnly,
            onlyCompileBundledFiles: true,
            compilerOptions: {
                target: `es${targetEsVersion}`,
                importHelpers: true,
                jsx: 'preserve',
                noUnusedLocals: false
            },
            getCustomTransformers: transpileOnly ?
                undefined :
                (program: ts.Program) => ({
                    before: [
                        minifyPrivatesTransformer(program)
                    ]
                })
        }
    }

How I try to use ts-transformer-properties-rename:

const propertiesRenameTransformer = require('ts-transformer-properties-rename').default;

First of all, It's weird ts-transformer-properties-rename doesn't include d.ts for types.
Then I replace transform with the new one. Now I have to pass some entrySourceFiles (with ts-transformer-minify-privates I don't).
Ok, I try to pass the same entry file as I pass to webpack entry:

propertiesRenameTransformer(program, { entrySourceFiles: [entryFile] })

And get an error:
Error: Cannot find source file /Users/dfilatov/prjs/arcadia/maps/front/services/nmaps/src/client/pages/app-page/nk-app-page.ts

But file /Users/dfilatov/prjs/arcadia/maps/front/services/nmaps/src/client/pages/app-page/nk-app-page.ts actually exists:

โฏ ls -la /Users/dfilatov/prjs/arcadia/maps/front/services/nmaps/src/client/pages/app-page/nk-app-page.ts
-rw-rw-r--  1 dfilatov  LD\Domain Users  904 Mar  9  2022 /Users/dfilatov/prjs/arcadia/maps/front/services/nmaps/src/client/pages/app-page/nk-app-page.ts

Have no idea how to make it work. Please help.

Originally posted by @dfilatov in timocov/ts-transformer-minify-privates#25 (comment)

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.