Skip to content

Commit

Permalink
Minify more type constructors (#45)
Browse files Browse the repository at this point in the history
* Transform: Array() -> []

* Transform: Object() -> {}

* Transform: Array() -> []: added IE8 note
  • Loading branch information
bfred-it authored and kangax committed Jul 12, 2016
1 parent 791ffb8 commit d627c33
Show file tree
Hide file tree
Showing 4 changed files with 278 additions and 7 deletions.
2 changes: 2 additions & 0 deletions packages/babel-plugin-minify-type-constructors/README.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
# babel-plugin-minify-type-constructors

**Note:** Not recommended if full support for IE8 and lower is required. [Details](https://github.com/amasad/babel-minify/pull/45#discussion_r70181249)

## Installation

```sh
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,16 +29,195 @@ describe("type-constructors-plugin", () => {
expect(transform(source)).toBe(expected);
});

it("should turn Array() to []", () => {
const source = "Array();";
const expected = "[];";
expect(transform(source)).toBe(expected);
});

it("should turn new Array() to []", () => {
const source = "new Array();";
const expected = "[];";
expect(transform(source)).toBe(expected);
});

it("should turn Array(nonNumericValue) to [nonNumericValue]", () => {
const source = unpad(`
Array("Rome");
Array(false);
Array(null);
Array({});
Array([]);
`);
const expected = unpad(`
["Rome"];
[false];
[null];
[{}];
[[]];
`);
expect(transform(source)).toBe(expected);
});

it("should turn Array(number) to [,] only if number is <=6", () => {
const source = unpad(`
Array(0);
Array(1);
Array(6);
Array(7);
`);
const expected = unpad(`
[];
[,];
[,,,,,,];
Array(7);
`);
expect(transform(source)).toBe(expected);
});

it("should turn new Array(number) to Array(number) if number is >6", () => {
const source = unpad(`
new Array(6);
new Array(7);
`);
const expected = unpad(`
[,,,,,,];
Array(7);
`);
expect(transform(source)).toBe(expected);
});

it("should turn Array(value, value) to [value, value]", () => {
const source = unpad(`
Array("a", "b");
new Array("0", "1", {});
Array(10, Symbol(), foo());
`);
const expected = unpad(`
["a", "b"];
["0", "1", {}];
[10, Symbol(), foo()];
`);
expect(transform(source)).toBe(expected);
});

it("should turn Object() to {}", () => {
const source = "var x = Object();";
const expected = "var x = {};";
expect(transform(source)).toBe(expected);
});

it("should turn new Object() to {}", () => {
const source = "var x = new Object();";
const expected = "var x = {};";
expect(transform(source)).toBe(expected);
});

it("should change Object(null|undefined) to {}", () => {
const source = unpad(`
[
Object(null),
Object(undefined),
new Object(void 0)
]
`);
const expected = "[{}, {}, {}];";
expect(transform(source)).toBe(expected);
});

it("should change Object({a:b}) to {a:b}", () => {
const source = unpad(`
[
Object({}),
Object({a:b}),
Object({a:b, c:d}),
]
`);// todo: add Object(Array())
const expected = "[{}, { a: b }, { a: b, c: d }];";
expect(transform(source)).toBe(expected);
});

it("should change Object([]) to []", () => {
const source = unpad(`
[
Object([]),
Object([1]),
Object([1,2]),
new Object([null])
]
`);// todo: add Object(Array())
const expected = "[[], [1], [1, 2], [null]];";
expect(transform(source)).toBe(expected);
});

it("should change Object(localFn) to localFn", () => {
const source = unpad(`
function a() {};
[
Object(function () {}),
new Object(a),
Object(Array)
]
`);
const expected = unpad(`
function a() {};
[function () {}, a, Object(Array)];
`);
expect(transform(source)).toBe(expected);
});

it("shouldn't change Object(value) for unrecognized values", () => {
const source = unpad(`
[
Object("undefined"),
Object(nulled),
Object(0),
Object(false),
Object(stuff())
]
`);
const expected = "[Object(\"undefined\"), Object(nulled), Object(0), Object(false), Object(stuff())];";
expect(transform(source)).toBe(expected);
});

it("should change new Object(value) to Object(value) for unrecognized values", () => {
const source = unpad(`
[
new Object("function"),
new Object(Symbol),
new Object(true),
new Object(1),
new Object(call({ me: true }))
]
`);
const expected = "[Object(\"function\"), Object(Symbol), Object(true), Object(1), Object(call({ me: true }))];";
expect(transform(source)).toBe(expected);
});

it("should change Object() to ({}) in ambiguous contexts", () => {
const source = unpad(`
new Object();
var foo = () => Object();
var bar = () => Object({ baz: 3 });
`);
const expected = unpad(`
({});
var foo = () => ({});
var bar = () => ({ baz: 3 });
`);
expect(transform(source)).toBe(expected);
});

it("shouldn't change referenced identifiers", () => {
const source = unpad(`
(function (Boolean, String, Number) {
return Boolean(a), String(b), Number(c);
})(MyBoolean, MyString, MyNumber);
(function (Boolean, String, Number, Array, Object) {
return Boolean(a), String(b), Number(c), Array(d), Object(d);
})(MyBoolean, MyString, MyNumber, MyArray, MyObject);
`);
const expected = unpad(`
(function (Boolean, String, Number) {
return Boolean(a), String(b), Number(c);
})(MyBoolean, MyString, MyNumber);
(function (Boolean, String, Number, Array, Object) {
return Boolean(a), String(b), Number(c), Array(d), Object(d);
})(MyBoolean, MyString, MyNumber, MyArray, MyObject);
`);
expect(transform(source)).toBe(expected);
});
Expand Down
4 changes: 3 additions & 1 deletion packages/babel-plugin-minify-type-constructors/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@
"keywords": [
"babel-plugin"
],
"dependencies": {},
"dependencies": {
"babel-helper-is-void-0": "^0.0.0"
},
"devDependencies": {}
}
88 changes: 88 additions & 0 deletions packages/babel-plugin-minify-type-constructors/src/index.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,72 @@
"use strict";

function replaceArray(t, path) {
const { node } = path;
if (t.isIdentifier(node.callee, { name: "Array" }) &&
!path.scope.getBinding("Array")) {

// Array(5) -> [,,,,,]
if (node.arguments.length === 1 &&
typeof node.arguments[0].value === "number") {

// "Array(7)" is shorter than "[,,,,,,,]"
if (node.arguments[0].value <= 6) {
path.replaceWith(t.arrayExpression(Array(node.arguments[0].value).fill(null)));

// new Array(7) -> Array(7)
} else if (node.type === "NewExpression") {
path.replaceWith(t.callExpression(node.callee, node.arguments));
}

// Array("Hello") -> ["Hello"]
} else {
path.replaceWith(t.arrayExpression(node.arguments));
}
return true;
}
}

function replaceObject(t, path) {
const { node } = path;
if (t.isIdentifier(node.callee, { name: "Object" }) &&
!path.scope.getBinding("Object")) {

const isVoid0 = require("babel-helper-is-void-0")(t);
const arg = node.arguments[0];
const binding = arg && t.isIdentifier(arg) && path.scope.getBinding(arg.name);

// Object() -> {}
if (node.arguments.length === 0) {
path.replaceWith(t.objectExpression([]));

// Object([]) -> []
} else if (arg.type === "ArrayExpression" ||
t.isFunctionExpression(arg)) {
path.replaceWith(arg);

// Object(null) -> {}
} else if (isVoid0(arg) ||
arg.name === "undefined" ||
arg.type === "NullLiteral" ||
arg.type === "ObjectExpression" && arg.properties.length === 0) {
path.replaceWith(t.objectExpression([]));

// Object(localFn) -> localFn
} else if (binding && binding.path.isFunction()) {
path.replaceWith(arg);

// Object({a:b}) -> {a:b}
} else if (arg.type === "ObjectExpression") {
path.replaceWith(arg);

// new Object(a) -> Object(a)
} else if (node.type === "NewExpression") {
path.replaceWith(t.callExpression(node.callee, node.arguments));
}
return true;
}
}

module.exports = function({ types: t }) {
return {
visitor: {
Expand Down Expand Up @@ -29,6 +96,27 @@ module.exports = function({ types: t }) {
path.replaceWith(t.binaryExpression("+", node.arguments[0], t.stringLiteral("")));
return;
}

// Array() -> []
if (replaceArray(t, path)) {
return;
}

// Object() -> {}
if (replaceObject(t, path)) {
return;
}
},
NewExpression(path) {
// new Array() -> []
if (replaceArray(t, path)) {
return;
}

// new Object() -> {}
if (replaceObject(t, path)) {
return;
}
},
},
};
Expand Down

0 comments on commit d627c33

Please sign in to comment.