@wakaru/unminify
v0.2.2
Published
🔪📦 Unminify and beautify bundled code
Downloads
20
Readme
@wakaru/unminify
This package offers a comprehensive set of transformation rules designed to unminify and enhance the readability of code.
It covers most of the patterns that are used by the following tools:
- Terser (Check the Terser Progress)
- Babel (Check the Babel Progress)
- SWC (Check the SWC Progress)
- TypeScript
Table of Contents
Readability
un-boolean
Converts minified boolean
to simple true
/false
.
- !0
+ true
- !1
+ false
un-undefined
Converts void 0
to undefined
.
- if(input === void 0) {}
+ if(input === undefined) {}
un-infinity
Converts 1 / 0
to Infinity
.
- 1 / 0
+ Infinity
- -1 / 0
+ -Infinity
un-numeric-literal
Converts numeric literal to its decimal representation.
A comment will be added to indicate the original value.
- 1e3
+ 1000 /* 1e3 */
- 0b101010
+ 42 /* 0b101010 */
- 0x123
+ 291 /* 0x123 */
un-typeof
Converts minified typeof
to its long form.
- typeof x > "u"
+ typeof x === "undefined"
- typeof x < "u"
+ typeof x !== "undefined"
un-sequence-expression
Separate sequence expressions into multiple statements.
- a(), b(), c()
+ a()
+ b()
+ c()
- return a(), b()
+ a()
+ return b()
- while (a(), b(), c++ > 0) {}
+ a()
+ b()
+ while (c++ > 0) {}
un-variable-merging
Separate variable declarators into multiple statements.
- var a = 1, b = true, c = func(d):
+ var a = 1;
+ var b = true;
+ var c = func(d);
Separate variable declarators that are not used in for statements.
- for (var i = 0, j = 0, k = 0; j < 10; k++) {}
+ var i = 0
+ for (var j = 0, k = 0; j < 10; k++) {}
un-assignment-expression
Separate chained assignment into multiple statements.
- a = b = c = 1
+ a = 1
+ b = 1
+ c = 1
un-bracket-notation
Simplify bracket notation.
- obj['prop']
+ obj.prop
- obj['var']
+ obj['var']
un-while-loop
Converts for loop without init and update to while loops.
- for (;;) {}
+ while (true) {}
- for (; i < 10;) {
- console.log(i);
- }
+ while (i < 10) {
+ console.log(i);
+ }
un-flip-comparisons
Flips comparisons that are in the form of "literal comes first" to "literal comes second".
- if ("dark" === theme) {}
+ if (theme === "dark") {}
- while (10 < count) {}
+ while (count > 10) {}
un-conditionals
Unwraps nested ternary expressions and binary expression into if-else statements or switch statements.
If-Else
- a ? b() : c ? d() : e()
+ if (a) {
+ b();
+ } else if (c) {
+ d();
+ } else {
+ e();
+ }
This rule will try to adopt the Early Exit
strategy if possible.
function fn () {
- return a ? b() : c ? d() : e()
+ if (a) {
+ return b();
+ }
+ if (c) {
+ return d();
+ }
+ return e();
}
Switch
- foo == 'bar' ? bar() : foo == 'baz' ? baz() : foo == 'qux' || foo == 'quux' ? qux() : quux()
+ switch (foo) {
+ case 'bar':
+ bar()
+ break
+ case 'baz':
+ baz()
+ break
+ case 'qux':
+ case 'quux':
+ qux()
+ break
+ default:
+ quux()
+ }
un-return
Simplify the last return statements.
function foo() {
const a = 1
if (a) {
return a;
}
- return void 0;
}
const bar = () => {
- return void foo();
+ foo();
}
un-indirect-call
Converts indirect call expressions to direct call expressions.
- import s from 'react'
- (0, s.useRef)(0);
+ import { useRef } from 'react'
+ useRef(0);
var s = require('react')
- (0, s.useRef)(0);
+ const { useRef } = s
+ useRef(0);
un-type-constructor
(Unsafe)
Restore type constructors from minified code.
- +x;
+ Number(x);
- x + "";
+ String(x);
- [,,,];
+ Array(3);
Unsafe:
- BigInt:
+1n
will throwTypeError
- Symbol:
Symbol('foo') + ""
will throwTypeError
un-builtin-prototype
Convert function calls on instances of built-in objects to equivalent calls on their prototypes.
- [].splice.apply(a, [1, 2, b, c]);
+ Array.prototype.splice.apply(a, [1, 2, b, c]);
- (function() {}).call.apply(console.log, console, ["foo"]),
+ Function.prototype.call.apply(console.log, console, ["foo"]),
- 0..toFixed.call(Math.PI, 2);
+ Number.prototype.toFixed.call(Math.PI, 2);
- ({}).hasOwnProperty.call(d, "foo");
+ Object.prototype.hasOwnProperty.call(d, "foo");
- /t/.test.call(/foo/, "bar");
+ RegExp.prototype.test.call(/foo/, "bar");
- "".indexOf.call(e, "bar");
+ String.prototype.indexOf.call(e, "bar");
un-import-rename
Rename import specifier back to the original name
-import { foo as a } from 'bar';
- a();
+import { foo } from 'bar';
+ foo();
smart-rename
Rename minified identifiers with heuristic rules.
- const {
- gql: t,
- dispatchers: o,
- listener: i
- } = n;
- o.delete(t, i);
+ const {
+ gql,
+ dispatchers,
+ listener
+ } = n;
+ dispatchers.delete(gql, listener);
React Hooks:
- const th = createContext('light');
+ const ThContext = createContext('light');
- const [e, f] = useState(0);
+ const [e, setE] = useState(0);
- const g = useRef(null);
+ const gRef = useRef(null);
- const [e, f] = o.useReducer(reducer, initialArg, init?);
+ const [eState, fDispatch] = o.useReducer(reducer, initialArg, init?);
- const Z = o.forwardRef((e, t) => { ... })
+ const Z = o.forwardRef((props, ref) => { ... })
un-iife
Improve the readability of code inside IIFE. Useful for short code snippets / userscripts.
Rename the parameters and move the passed-in arguments to the top.
- (function(i, s, o, g, r, a, m) {
- i['GoogleAnalyticsObject'] = r;
- i[r].l = 1 * new Date();
- a = s.createElement(o);
- m = s.getElementsByTagName(o)[0];
- a.async = 1;
- a.src = g;
- m.parentNode.insertBefore(a, m);
- })(window, document, 'script', 'https://www.google-analytics.com/analytics.js', 'ga');
+ (function(window, document, a, m) {
+ const o = 'script';
+ const g = 'https://www.google-analytics.com/analytics.js';
+ const r = 'ga';
+ window['GoogleAnalyticsObject'] = r;
+ window[r].l = 1 * new Date();
+ a = document.createElement(o);
+ m = document.getElementsByTagName(o)[0];
+ a.async = 1;
+ a.src = g;
+ m.parentNode.insertBefore(a, m);
+ })(window, document);
Syntax Upgrade
un-template-literal
Restore template literal syntax from string concatenation.
- "the ".concat(first, " take the ").concat(second, " and ").concat(third);
+ `the ${first} take the ${second} and ${third}`
un-parameter
Restore parameters. Support normal parameters and default parameters.
- function foo() {
- var a = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : "foo";
- var b = arguments.length > 1 ? arguments[1] : undefined;
- if (c === void 0) c = "bar";
- }
+ function foo(a = "foo", b, c = "bar") {}
- function foo() {
- console.log(arguments);
- }
+ function foo(...args) {
+ console.log(args);
+ }
un-argument-spread
Transform fn.apply
calls to spread operator.
- fn.apply(void 0, arr);
+ fn(...arr);
- obj.fn.apply(obj, arr);
+ obj.fn(...arr);
un-enum
Restore TypeScript enum syntax.
- var Direction;
- (function (Direction) {
- Direction["Up"] = "UP";
- Direction[Direction["Down"] = 2] = "Down";
- })(Direction || (Direction = {}));
+ var Direction = {
+ Up: "UP",
+ Down: 2,
+ // reverse mapping
+ 2: "Down"
+ }
smart-inline
Converts object property accesses and array index accesses to destructuring.
- const t = e.x;
- const n = e.y;
- const r = e.color;
- console.log(t, n, r)
+ const { x, y, color } = e;
+ console.log(x, y, color)
- const t = e[0];
- const n = e[1];
- const r = e[2];
- console.log(t, n, r);
+ const [t, n, r] = e;
+ console.log(t, n, r);
Inline reassigned temp variables.
- const a = d;
- const b = a;
- const c = b;
+ const c = d;
Inline common global variable references.
- const d = document;
- const c = d.createElement('canvas');
+ const c = document.createElement('canvas');
Rename variables based on object property access.
- const t = s.target;
- const p = t.parent;
- const v = p.value;
+ const s_target = s.target;
+ const s_target_parent = s_target.parent;
+ const s_target_parent_value = s_target_parent.value;
un-optional-chaining
Restore optional chaining syntax.
Support output from TypeScript, Babel and SWC.
- (_foo = foo) === null || _foo === void 0 ? void 0 : _foo.bar;
+ foo?.bar;
- (_foo = foo) === null || _foo === void 0 ? void 0 : (_foo_bar = _foo.bar) === null || _foo_bar === void 0 ? void 0 : _foo_bar.baz;
+ foo?.bar?.baz;
un-nullish-coalescing
Restore nullish coalescing syntax.
Support output from TypeScript, Babel and SWC.
- (_ref = foo !== null && foo !== void 0 ? foo : bar) !== null && _ref !== void 0 ? _ref : "quz";
+ foo ?? bar ?? "quz";
un-esm
(Unsafe)
Converts CommonJS's require
and module.exports
to ES6's import
and export
.
- const foo = require('foo')
- var { bar } = require('bar')
- var baz = require('baz').baz
- require('side-effect')
+ import foo from 'foo'
+ import { bar } from 'bar'
+ import { baz } from 'baz'
+ import 'side-effect'
- module.exports.foo = 1
- exports.bar = bar
+ export const foo = 1
+ export { bar }
- module.exports.default = foo
+ export default foo
[!WARNING] Please aware that CJS and ESM are not fully compatible, and this transformation is not perfect. We have a comprehensive test suite to ensure the correctness, but there are still some edge cases that are not covered. Feel free to open an issue if you find any bugs.
Limitations:
require(dynamic)
is not supported as ESM does not support dynamic imports. Convert it toawait import()
is not appropriate as it require the whole execution context to be async.- Some packages require
import * as name from 'package'
instead ofimport name from 'package'
. We cannot detect this automatically, so you might need to fix it manually. - Currently, it won't aware the exports format of other files generated by the
unpacker
.
un-es6-class
Restore Class
definition from the constructor and the prototype.
Currently, this transformation only supports output from TypeScript.
Supported features:
- constructor
- instance properties
- instance methods
- static methods
- static properties
- getters and setters
- async method (share the same limitations from
un-async-await
)
Unsupported features:
- inheritance
- decorators
- private flag(#)
- var Foo = (function() {
- function t(name) {
- this.name = name;
- this.age = 18;
- }
- t.prototype.hi = function logger() {
- console.log("Hello", this.name);
- };
- t.staticMethod = function staticMethod() {
- console.log("static");
- };
- t.instance = new t("foo");
- return t;
- })();
+ class Foo {
+ constructor(name) {
+ this.name = name;
+ this.age = 18;
+ }
+ hi() {
+ console.log("Hello", this.name);
+ }
+ static staticMethod() {
+ console.log("static");
+ }
+ static instance = new Foo("foo");
+ }
un-async-await
(Experimental) (WIP)
Restore async/await from helper __awaiter
and __generator
.
Currently, this transformation only supports output from TypeScript.
And it does not handle control flow properly, as it needs graph analysis.
Please aware there are tons of edge cases that are not covered by this rule.
-function func() {
- return __awaiter(this, void 0, void 0, function () {
- var result, json;
- return __generator(this, function (_a) {
- switch (_a.label) {
- case 0:
- console.log('Before sleep');
- return [4 /*yield*/, sleep(1000)];
- case 1:
- _a.sent();
- return [4 /*yield*/, fetch('')];
- case 2:
- result = _a.sent();
- return [4 /*yield*/, result.json()];
- case 3:
- json = _a.sent();
- return [2 /*return*/, json];
- }
- });
- });
-}
+async function func() {
+ var result, json;
+ console.log('Before sleep');
+ await sleep(1000);
+ result = await fetch('');
+ json = await result.json();
+ return json;
+}
un-jsx
Converts React.createElement
and jsxRuntime.jsx
back to JSX.
- React.createElement("div", { className: "title" }, "Hello World");
+ <div className="title">Hello World</div>
Built-in pragma: jsx
, jsxs
, _jsx
, _jsxs
, jsxDEV
, jsxsDEV
and h
Pass pragma
option to specify the JSX pragma.
Pass pragmaFrag
option to specify the JSX fragment pragma.
// pragma: "jsx", pragmaFrag: "Fragment"
- jsx(React.Fragment, null, jsx("span", { className: "title" }, "Hello"), jsx("span", null, "World"));
+ <>
+ <span className="title">Hello</span>
+ <span>World</span>
+ </>
Component name will be guessed from the displayName
property automatically.
- var S = /*#__PURE__*/React.createElement("div", null);
- S.displayName = "Foo-Bar";
- var Bar = () => (
- <div>
- <S />
- </div>
- )
+ var FooBar = <div />;
+ FooBar.displayName = "Foo-Bar";
+ var Bar = () => (
+ <div>
+ <FooBar />
+ </div>
+ )
Clean Up
un-esmodule-flag
Removes the __esModule
flag from the module.
- Object.defineProperty(exports, "__esModule", { value: true });
un-use-strict
Removes the "use strict"
directive.
- "use strict";
Style
prettier
Formats the code with prettier.
Extra
lebab
Lebab transpiles your ES5 code to ES6/ES7. It does exactly the opposite of what Babel does.
We integrated part of rules from Lebab to unminify the code.
By utilizing Lebab, we can save the repetitive work of writing the same transformations ourselves.
TODO
- [ ]
un-string-literal
to decode printable unicode - [ ] Terser loops contains several useful patterns
- [ ]
let a; a = 1;
tolet a = 1;
- [ ] Support for Logical Assignment Operators (
a ||= b
,a &&= b
,a ??= b
) [ES2021]