-
-
Notifications
You must be signed in to change notification settings - Fork 225
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Implemented transform-void-to-nothing plugin.
Removes the 'void 0' rval for let-/var-assignments.
- Loading branch information
Shine Wang
committed
Oct 19, 2016
1 parent
a7a890a
commit 2e62acf
Showing
4 changed files
with
237 additions
and
0 deletions.
There are no files selected for viewing
4 changes: 4 additions & 0 deletions
4
packages/babel-plugin-remove-undefined-if-possible/.npmignore
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,4 @@ | ||
src | ||
__tests__ | ||
node_modules | ||
*.log |
59 changes: 59 additions & 0 deletions
59
packages/babel-plugin-remove-undefined-if-possible/README.md
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,59 @@ | ||
# babel-plugin-remove-undefined-if-possible | ||
|
||
For variable assignments, this removes rvals that evaluate to `undefined` | ||
(`var`s in functions only). | ||
For functions, this removes return arguments that evaluate to `undefined`. | ||
|
||
## Example | ||
|
||
**In** | ||
|
||
```javascript | ||
let a = void 0; | ||
function foo() { | ||
var b = undefined; | ||
return undefined; | ||
} | ||
``` | ||
|
||
**Out** | ||
|
||
```javascript | ||
let a; | ||
function foo() { | ||
var b; | ||
return; | ||
} | ||
``` | ||
|
||
## Installation | ||
|
||
```sh | ||
$ npm install babel-plugin-remove-undefined-if-possible | ||
``` | ||
|
||
## Usage | ||
|
||
### Via `.babelrc` (Recommended) | ||
|
||
**.babelrc** | ||
|
||
```json | ||
{ | ||
"plugins": ["babel-plugin-remove-undefined-if-possible"] | ||
} | ||
``` | ||
|
||
### Via CLI | ||
|
||
```sh | ||
$ babel --plugins babel-plugin-remove-undefined-if-possible script.js | ||
``` | ||
|
||
### Via Node API | ||
|
||
```javascript | ||
require("babel-core").transform("code", { | ||
plugins: ["babel-plugin-remove-undefined-if-possible"] | ||
}); | ||
``` |
78 changes: 78 additions & 0 deletions
78
...-remove-undefined-if-possible/__tests__/babel-plugin-remove-undefined-if-possible-test.js
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,78 @@ | ||
jest.autoMockOff(); | ||
|
||
const babel = require("babel-core"); | ||
const plugin = require("../src/index"); | ||
|
||
function transform(code) { | ||
return babel.transform(code, { | ||
plugins: [plugin], | ||
}).code; | ||
} | ||
|
||
describe("remove-undefined-if-possible-plugin", () => { | ||
it("should remove multiple undefined assignments", () => { | ||
const source = "let a = undefined, b = 3, c = undefined, d;"; | ||
const expected = "let a,\n b = 3,\n c,\n d;"; | ||
expect(transform(source)).toBe(expected); | ||
}); | ||
|
||
it("should remove let-assignments to undefined", () => { | ||
const source = "let a = undefined;"; | ||
const expected = "let a;"; | ||
expect(transform(source)).toBe(expected); | ||
}); | ||
|
||
it("should remove let-assignments to void 0", () => { | ||
const source = "let a = void 0;"; | ||
const expected = "let a;"; | ||
expect(transform(source)).toBe(expected); | ||
}); | ||
|
||
it("should remove undefined return value", () => { | ||
const source = ` | ||
function foo() { | ||
const a = undefined; | ||
return undefined; | ||
}`; | ||
const expected = ` | ||
function foo() { | ||
const a = undefined; | ||
return; | ||
}`; | ||
expect(transform(source)).toBe(expected); | ||
}); | ||
|
||
it("should remove var declarations in functions", () => { | ||
const source = ` | ||
function foo() { | ||
var a = undefined; | ||
}`; | ||
const expected = ` | ||
function foo() { | ||
var a; | ||
}`; | ||
expect(transform(source)).toBe(expected); | ||
}); | ||
|
||
it("should not remove const-assignments to undefined", () => { | ||
const source = "const a = undefined;"; | ||
expect(transform(source)).toBe(source); | ||
}); | ||
|
||
it("should not remove var-assignments in loops", () => { | ||
const source = ` | ||
for (var a = undefined;;) { | ||
var b = undefined; | ||
}`; | ||
expect(transform(source)).toBe(source); | ||
}); | ||
|
||
it("should not remove var-assignments if referenced before", () => { | ||
const source = ` | ||
function foo() { | ||
a = 3; | ||
var a = undefined; | ||
}`; | ||
expect(transform(source)).toBe(source); | ||
}); | ||
}); |
96 changes: 96 additions & 0 deletions
96
packages/babel-plugin-remove-undefined-if-possible/src/index.js
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,96 @@ | ||
"use strict"; | ||
|
||
function isInLocalLoop(path, t) { | ||
if (path === null) { | ||
return false; | ||
} else if (t.isLoop(path)) { | ||
return true; | ||
} else if (t.isFunction(path)) { | ||
return false; | ||
} else { | ||
return isInLocalLoop(path.parentPath, t); | ||
} | ||
} | ||
|
||
function removeRvalIfUndefined(declaratorPath) { | ||
const rval = declaratorPath.get("init") | ||
.evaluate(); | ||
if (rval.confident === true && rval.value === undefined) { | ||
declaratorPath.node.init = null; | ||
} | ||
} | ||
|
||
function isAnyLvalReferencedBefore(declaratorPath, seenIds, t) { | ||
const id = declaratorPath.get("id"); | ||
if (t.isIdentifier(id)) { | ||
return seenIds.has(id.node.name); | ||
} | ||
let hasReference = false; | ||
id.traverse({ | ||
Identifier(path) { | ||
if (seenIds.has(path.node.name)) { | ||
hasReference = true; | ||
} | ||
} | ||
}); | ||
return hasReference; | ||
} | ||
|
||
function removeUndefinedAssignments(functionPath, t) { | ||
const seenIds = new Set(); | ||
functionPath.traverse({ | ||
Function(path) { | ||
path.skip(); | ||
}, | ||
Identifier(path) { | ||
// potential optimization: only store ids that are lvals of assignments. | ||
seenIds.add(path.node.name); | ||
}, | ||
VariableDeclaration(path) { | ||
switch (path.node.kind) { | ||
case "const": | ||
break; | ||
case "let": | ||
path.node.declarations.forEach((declarator, index) => { | ||
const declaratorPath = path.get("declarations")[index]; | ||
removeRvalIfUndefined(declaratorPath); | ||
}); | ||
break; | ||
case "var": | ||
if (!t.isFunction(functionPath)) { | ||
break; | ||
} | ||
if (isInLocalLoop(path.parentPath, t)) { | ||
break; | ||
} | ||
path.node.declarations.forEach((declarator, index) => { | ||
const declaratorPath = path.get("declarations")[index]; | ||
if (!isAnyLvalReferencedBefore(declaratorPath, seenIds, t)) { | ||
removeRvalIfUndefined(declaratorPath); | ||
} | ||
}); | ||
break; | ||
} | ||
}, | ||
}); | ||
} | ||
|
||
module.exports = function({ types: t }) { | ||
return { | ||
name: "remove-undefined-if-possible", | ||
visitor: { | ||
FunctionParent(path) { | ||
removeUndefinedAssignments(path, t); | ||
}, | ||
ReturnStatement(path) { | ||
if (path.node.argument !== null) { | ||
const rval = path.get("argument") | ||
.evaluate(); | ||
if (rval.confident === true && rval.value === undefined) { | ||
path.node.argument = null; | ||
} | ||
} | ||
}, | ||
}, | ||
}; | ||
}; |