How to minify private methods in a TypeScript class?
Przemyslaw Jan Beigert
Posted on March 6, 2023
Introduction
As you may notice during minification your long and descriptive variable names are changed into e.g.: "a", "rd", "xd". However, when you minify class names of methods stay the same:
class Cls {
method() {
}
}
const cls = new Cls();
cls.method();
Will be compiled into
"use strict";(()=>{var c=class{method(){}},e=new c;e.method();})();
Is logical because method
has been used in other scripts. But what about private methods?
class Cls {
public publicMethod() {
this.privateMethod()
}
private privateMethod() {}
}
const cls = new Cls();
cls.publicMethod();
This code with be compiled into:
"use strict";(()=>{var t=class{publicMethod(){this.privateMethod()}privateMethod(){}},e=new t;e.publicMethod();})();
The private method stays the same. Is logical cause code is firstly transpile into JavaScript and then minified. During transpiration information about method privacy will be lost.
Terser
Terser is JavaScript compressor that can minified specific method names.
// terser.config.js
const fs = require('fs');
const terser = require('terser');
(async () => {
const file = fs.readFileSync('index.js', 'utf8')
const {code} = await terser.minify(file, {
mangle: {
properties: {
regex: `^(privateMethod)$`,
},
},
});
fs.writeFileSync('index.js', code)
})();
This code will create something like this:
"use strict";(new class{publicMethod(){this.t()}t(){}}).publicMethod();
Great success? Not yet. We can not manually paste every private method to the config file.
Find all private method
We can use bash to find all names of private methods.
- Print all TS files
cat src/**/*.ts
- Filter lines by pattern
private methodName(...) {
grep -E "^ private (.*?)\("
You can use prettier to be sure that all methods are formatted in the same way.
- Map lines like with
private methodsName(
intomethodName
by
awk '{split($2,a,"("); print a[1]}'
- Join each method with
|
separator by:
tr '\n' '|'
- Merge it with bash pipes:
cat src/**/*.ts | egrep " private (.*?)\(" | awk '{split($2,a,"("); print a[1]}' | tr '\n' '|'
The output should looks like this: privateMethod|privateMethod1|privateMethod2
. It exactly the same that what we can paste to regex in the terser.config
.
Fix collision
Unfortunately is public method will have the same name like a private one, both will be minified. How to fix that? Use previous script to find all public methods then just filter in out in the terser.config.js
const methodsToMinify = privateMethods
.split('|')
.filter(item => !publicMethods.includes(item))
.join('|');
Result
In my project helped me reduce bundle size by around 11%. It doesn't sound impressive, however applying this solution is relatively fast
Posted on March 6, 2023
Join Our Newsletter. No Spam, Only the good stuff.
Sign up to receive the latest update from our blog.
Related
October 31, 2024