├── .npmignore ├── test ├── fixtures │ ├── unused-with-duplicates │ │ ├── expected.js │ │ └── actual.js │ ├── node-env-inline │ │ ├── expected.js │ │ ├── options.json │ │ └── actual.js │ ├── unused-vars │ │ ├── expected.js │ │ └── actual.js │ ├── decorated-classes │ │ ├── actual.js │ │ ├── expected.js │ │ └── options.json │ ├── exported-anonymous-function │ │ ├── actual.js │ │ ├── expected.js │ │ └── options.json │ ├── if-statement-and-unreachable │ │ ├── expected.js │ │ └── actual.js │ ├── async-hoisting │ │ ├── options.json │ │ ├── actual.js │ │ └── expected.js │ ├── regression-7 │ │ ├── expected.js │ │ └── actual.js │ ├── conditional-expression │ │ ├── expected.js │ │ └── actual.js │ ├── unused-classes │ │ ├── expected.js │ │ └── actual.js │ ├── unused-functions │ │ ├── expected.js │ │ └── actual.js │ ├── regression-10 │ │ ├── expected.js │ │ └── actual.js │ ├── destructuring │ │ ├── actual.js │ │ ├── expected.js │ │ └── options.json │ ├── inlining │ │ ├── options.json │ │ ├── expected.js │ │ └── actual.js │ ├── if-statement │ │ ├── expected.js │ │ └── actual.js │ ├── inlining-bailout │ │ ├── options.json │ │ ├── expected.js │ │ └── actual.js │ ├── logical-expression │ │ ├── expected.js │ │ └── actual.js │ ├── unreachable │ │ ├── expected.js │ │ └── actual.js │ ├── exported │ │ ├── actual.js │ │ ├── options.json │ │ └── expected.js │ ├── inlining-disabled │ │ ├── actual.js │ │ └── expected.js │ └── regression-16 │ │ ├── actual.js │ │ └── expected.js └── test.js ├── .babelrc ├── .gitignore ├── README.md ├── .github └── workflows │ └── ci.yml ├── LICENSE ├── package.json └── src └── index.js /.npmignore: -------------------------------------------------------------------------------- 1 | * 2 | 3 | !lib 4 | -------------------------------------------------------------------------------- /test/fixtures/unused-with-duplicates/expected.js: -------------------------------------------------------------------------------- 1 | foo(); -------------------------------------------------------------------------------- /test/fixtures/node-env-inline/expected.js: -------------------------------------------------------------------------------- 1 | bar(); 2 | foobar(); 3 | -------------------------------------------------------------------------------- /test/fixtures/unused-vars/expected.js: -------------------------------------------------------------------------------- 1 | var foobar; 2 | foobar(); 3 | -------------------------------------------------------------------------------- /test/fixtures/decorated-classes/actual.js: -------------------------------------------------------------------------------- 1 | @decorator 2 | class Foo {} 3 | -------------------------------------------------------------------------------- /test/fixtures/decorated-classes/expected.js: -------------------------------------------------------------------------------- 1 | @decorator 2 | class Foo {} 3 | -------------------------------------------------------------------------------- /test/fixtures/exported-anonymous-function/actual.js: -------------------------------------------------------------------------------- 1 | export default function () {} 2 | -------------------------------------------------------------------------------- /test/fixtures/if-statement-and-unreachable/expected.js: -------------------------------------------------------------------------------- 1 | (function (x) { 2 | return x; 3 | })(); 4 | -------------------------------------------------------------------------------- /test/fixtures/unused-vars/actual.js: -------------------------------------------------------------------------------- 1 | var foo = 5; 2 | var bar = 'bar'; 3 | var foobar; 4 | foobar(); 5 | -------------------------------------------------------------------------------- /test/fixtures/decorated-classes/options.json: -------------------------------------------------------------------------------- 1 | { "plugins": ["syntax-decorators", "../../../lib/index.js"] } 2 | -------------------------------------------------------------------------------- /test/fixtures/async-hoisting/options.json: -------------------------------------------------------------------------------- 1 | { "plugins": ["transform-async-to-generator", "../../../lib/index.js"] } 2 | -------------------------------------------------------------------------------- /test/fixtures/node-env-inline/options.json: -------------------------------------------------------------------------------- 1 | { "plugins": ["transform-node-env-inline", "../../../lib/index.js"] } 2 | -------------------------------------------------------------------------------- /test/fixtures/regression-7/expected.js: -------------------------------------------------------------------------------- 1 | module.exports = rawAsap; 2 | function rawAsap(task) { 3 | true; 4 | } 5 | -------------------------------------------------------------------------------- /test/fixtures/conditional-expression/expected.js: -------------------------------------------------------------------------------- 1 | foo(); 2 | foo(); 3 | foo(); 4 | foo(); 5 | foo(); 6 | foo(); 7 | foo(); 8 | -------------------------------------------------------------------------------- /test/fixtures/unused-classes/expected.js: -------------------------------------------------------------------------------- 1 | class Foobar {} 2 | // used twice to avoid inlining 3 | new Foobar(); 4 | new Foobar(); 5 | -------------------------------------------------------------------------------- /test/fixtures/unused-functions/expected.js: -------------------------------------------------------------------------------- 1 | function foobar() {} 2 | // used twice to avoid inlining 3 | foobar(); 4 | foobar(); 5 | -------------------------------------------------------------------------------- /test/fixtures/async-hoisting/actual.js: -------------------------------------------------------------------------------- 1 | (() => { 2 | return foo(); 3 | 4 | async function foo() { 5 | return bar; 6 | } 7 | })(); 8 | -------------------------------------------------------------------------------- /test/fixtures/regression-10/expected.js: -------------------------------------------------------------------------------- 1 | module.exports = rawAsap; 2 | function rawAsap(task) { 3 | if (foo) true; 4 | foo(bar()); 5 | } 6 | -------------------------------------------------------------------------------- /test/fixtures/destructuring/actual.js: -------------------------------------------------------------------------------- 1 | var [a, b, c] = [1, 2, 3]; 2 | ({ a, b, c }); 3 | 4 | var [d, e, [f, g]] = someArray; 5 | ({ d, e, f, g }); 6 | -------------------------------------------------------------------------------- /test/fixtures/destructuring/expected.js: -------------------------------------------------------------------------------- 1 | var [a, b, c] = [1, 2, 3]; 2 | ({ a, b, c }); 3 | 4 | var [d, e, [f, g]] = someArray; 5 | ({ d, e, f, g }); 6 | -------------------------------------------------------------------------------- /test/fixtures/inlining/options.json: -------------------------------------------------------------------------------- 1 | { 2 | "plugins": [ 3 | ["../../../lib/index.js", { 4 | "experimentalInlining": true 5 | }] 6 | ] 7 | } 8 | -------------------------------------------------------------------------------- /test/fixtures/destructuring/options.json: -------------------------------------------------------------------------------- 1 | { 2 | "plugins": [ 3 | ["../../../lib/index.js", { 4 | "experimentalInlining": true 5 | }] 6 | ] 7 | } 8 | -------------------------------------------------------------------------------- /test/fixtures/if-statement-and-unreachable/actual.js: -------------------------------------------------------------------------------- 1 | (function (x) { 2 | if (true) { 3 | return x; 4 | } 5 | throw new Error('untrue'); 6 | })(); 7 | -------------------------------------------------------------------------------- /test/fixtures/regression-7/actual.js: -------------------------------------------------------------------------------- 1 | module.exports = rawAsap; 2 | function rawAsap(task) { 3 | flushing = true; 4 | } 5 | 6 | var flushing = false; 7 | -------------------------------------------------------------------------------- /.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "presets": ["es2015"], 3 | "env": { 4 | "development": { 5 | "sourceMaps": "inline" 6 | } 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /test/fixtures/if-statement/expected.js: -------------------------------------------------------------------------------- 1 | foo(); 2 | foobar(); 3 | baz(); 4 | if (foo) { 5 | foo(); 6 | } 7 | if (!bar) { 8 | bar(); 9 | } 10 | foo(); 11 | -------------------------------------------------------------------------------- /test/fixtures/inlining-bailout/options.json: -------------------------------------------------------------------------------- 1 | { 2 | "plugins": [ 3 | ["../../../lib/index.js", { 4 | "experimentalInlining": true 5 | }] 6 | ] 7 | } 8 | -------------------------------------------------------------------------------- /test/fixtures/inlining/expected.js: -------------------------------------------------------------------------------- 1 | var foo = function bar() {}(); 2 | 3 | var baz = new class Foo {}(); 4 | 5 | (function () { 6 | return function local() {}(1); 7 | })(); 8 | -------------------------------------------------------------------------------- /test/fixtures/logical-expression/expected.js: -------------------------------------------------------------------------------- 1 | foo(); 2 | foo(); 3 | false; 4 | 0; 5 | bar() && foo(); 6 | foo(); 7 | foo(); 8 | true; 9 | 1; 10 | bar() || foo(); 11 | -------------------------------------------------------------------------------- /test/fixtures/unreachable/expected.js: -------------------------------------------------------------------------------- 1 | (function () { 2 | // used twice to ensure `local` isn't inlined 3 | local(); 4 | return local(); 5 | function local() {} 6 | })(); 7 | -------------------------------------------------------------------------------- /test/fixtures/exported/actual.js: -------------------------------------------------------------------------------- 1 | export default 0; 2 | export const foo = 1; 3 | export let bar = 2; 4 | export var baz = 3; 5 | export class Foo {} 6 | export function foobar() {} 7 | -------------------------------------------------------------------------------- /test/fixtures/regression-10/actual.js: -------------------------------------------------------------------------------- 1 | module.exports = rawAsap; 2 | function rawAsap(task) { 3 | if (foo) flushing = true; 4 | foo(flushing = bar()); 5 | } 6 | 7 | var flushing = false; 8 | -------------------------------------------------------------------------------- /test/fixtures/exported-anonymous-function/expected.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | Object.defineProperty(exports, "__esModule", { 4 | value: true 5 | }); 6 | 7 | exports.default = function () {}; 8 | -------------------------------------------------------------------------------- /test/fixtures/exported/options.json: -------------------------------------------------------------------------------- 1 | { 2 | "plugins": [ 3 | "transform-es2015-modules-commonjs", 4 | ["../../../lib/index.js", { 5 | "experimentalInlining": true 6 | }] 7 | ] 8 | } 9 | -------------------------------------------------------------------------------- /test/fixtures/unused-with-duplicates/actual.js: -------------------------------------------------------------------------------- 1 | foo(); 2 | 3 | class bar {} 4 | function bar() {} 5 | 6 | function baz() {} 7 | function baz() {} 8 | 9 | var abc = 5; 10 | var abc = 6; 11 | -------------------------------------------------------------------------------- /test/fixtures/exported-anonymous-function/options.json: -------------------------------------------------------------------------------- 1 | { 2 | "plugins": [ 3 | "transform-es2015-modules-commonjs", 4 | ["../../../lib/index.js", { 5 | "experimentalInlining": true 6 | }] 7 | ] 8 | } 9 | -------------------------------------------------------------------------------- /test/fixtures/unused-functions/actual.js: -------------------------------------------------------------------------------- 1 | function foo() {} 2 | function bar() { 3 | baz(); 4 | foo = 5; 5 | } 6 | function foobar() {} 7 | // used twice to avoid inlining 8 | foobar(); 9 | foobar(); 10 | -------------------------------------------------------------------------------- /test/fixtures/node-env-inline/actual.js: -------------------------------------------------------------------------------- 1 | process.env.NODE_ENV === 'this will never be equal' ? foo() : bar(); 2 | 3 | if (process.env.NODE_ENV === 'neither will this') { 4 | baz(); 5 | } else { 6 | foobar(); 7 | } 8 | -------------------------------------------------------------------------------- /test/fixtures/unreachable/actual.js: -------------------------------------------------------------------------------- 1 | (function() { 2 | // used twice to ensure `local` isn't inlined 3 | local(); 4 | return local(); 5 | 6 | var foo = bar(); 7 | baz(); 8 | 9 | function local() {} 10 | })(); 11 | -------------------------------------------------------------------------------- /test/fixtures/unused-classes/actual.js: -------------------------------------------------------------------------------- 1 | class Foo {} 2 | class Bar { 3 | baz() {} 4 | foo() { 5 | foo = 5; 6 | } 7 | } 8 | class Foobar {} 9 | // used twice to avoid inlining 10 | new Foobar(); 11 | new Foobar(); 12 | -------------------------------------------------------------------------------- /test/fixtures/inlining-disabled/actual.js: -------------------------------------------------------------------------------- 1 | var foo = bar(); 2 | function bar() {} 3 | 4 | var baz = new Foo(); 5 | class Foo {} 6 | 7 | (() => { 8 | var foo = 1; 9 | return local(foo); 10 | function local() {} 11 | })(); 12 | -------------------------------------------------------------------------------- /test/fixtures/inlining/actual.js: -------------------------------------------------------------------------------- 1 | var foo = bar(); 2 | function bar() {} 3 | 4 | var baz = new Foo(); 5 | class Foo {} 6 | 7 | (function() { 8 | var foo = 1; 9 | return local(foo); 10 | function local() {} 11 | })(); 12 | -------------------------------------------------------------------------------- /test/fixtures/logical-expression/actual.js: -------------------------------------------------------------------------------- 1 | true && foo(); 2 | 1 && foo(); 3 | false && foo(); 4 | 0 && foo(); 5 | bar() && foo(); 6 | false || foo(); 7 | 0 || foo(); 8 | true || foo(); 9 | 1 || foo(); 10 | bar() || foo(); 11 | -------------------------------------------------------------------------------- /test/fixtures/inlining-disabled/expected.js: -------------------------------------------------------------------------------- 1 | var foo = bar(); 2 | function bar() {} 3 | 4 | var baz = new Foo(); 5 | class Foo {} 6 | 7 | (() => { 8 | var foo = 1; 9 | return local(foo); 10 | function local() {} 11 | })(); 12 | -------------------------------------------------------------------------------- /test/fixtures/conditional-expression/actual.js: -------------------------------------------------------------------------------- 1 | true ? foo() : bar(); 2 | false ? bar() : foo(); 3 | 'hi' ? foo() : bar(); 4 | null ? bar() : foo(); 5 | 'hi' === 'hi' ? foo() : bar(); 6 | 'hi' === 'bye' ? bar() : foo(); 7 | 'hi' !== 'hi' ? bar() : foo(); 8 | -------------------------------------------------------------------------------- /test/fixtures/regression-16/actual.js: -------------------------------------------------------------------------------- 1 | var MAX_INT = 2147483647; 2 | var MIN_INT = -2147483648; 3 | function coerceInt(value) { 4 | var num = Number(value); 5 | if (num === num && num <= MAX_INT && num >= MIN_INT) { 6 | return (num < 0 ? Math.ceil : Math.floor)(num); 7 | } 8 | return null; 9 | } 10 | 11 | coerceInt(); 12 | -------------------------------------------------------------------------------- /test/fixtures/regression-16/expected.js: -------------------------------------------------------------------------------- 1 | var MAX_INT = 2147483647; 2 | var MIN_INT = -2147483648; 3 | function coerceInt(value) { 4 | var num = Number(value); 5 | if (num === num && num <= MAX_INT && num >= MIN_INT) { 6 | return (num < 0 ? Math.ceil : Math.floor)(num); 7 | } 8 | return null; 9 | } 10 | 11 | coerceInt(); 12 | -------------------------------------------------------------------------------- /test/fixtures/exported/expected.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | Object.defineProperty(exports, "__esModule", { 4 | value: true 5 | }); 6 | 7 | exports.foobar = foobar; 8 | exports.default = 0; 9 | const foo = exports.foo = 1; 10 | let bar = exports.bar = 2; 11 | var baz = exports.baz = 3; 12 | class Foo {} 13 | exports.Foo = Foo; 14 | function foobar() {} 15 | -------------------------------------------------------------------------------- /test/fixtures/if-statement/actual.js: -------------------------------------------------------------------------------- 1 | if (true) { 2 | foo(); 3 | } else { 4 | bar(); 5 | } 6 | 7 | if ('foo') foobar(); 8 | 9 | if (false) { 10 | bar(); 11 | } else { 12 | baz(); 13 | } 14 | 15 | if (false) bar(); 16 | 17 | if (foo) { 18 | foo(); 19 | } else {} 20 | 21 | if (bar) {} 22 | else { 23 | bar(); 24 | } 25 | 26 | if ('hi' === 'hi') { 27 | foo(); 28 | } 29 | -------------------------------------------------------------------------------- /test/fixtures/inlining-bailout/expected.js: -------------------------------------------------------------------------------- 1 | export function foo() {} 2 | foo(); 3 | 4 | function bar() {} 5 | for (;;) { 6 | bar(); 7 | } 8 | 9 | function recursive() { 10 | recursive(); 11 | } 12 | 13 | // arg not inlined 14 | (function baz(arg) { 15 | return arg; 16 | })(); 17 | 18 | var multipleRefs = 'one'; 19 | multipleRefs = 'two'; 20 | something(multipleRefs); 21 | 22 | var impure = multipleRefs; 23 | something(impure); 24 | 25 | var itself = itself + 1; 26 | -------------------------------------------------------------------------------- /test/fixtures/inlining-bailout/actual.js: -------------------------------------------------------------------------------- 1 | export function foo() {} 2 | foo(); 3 | 4 | function bar() {} 5 | for (;;) { 6 | bar(); 7 | } 8 | 9 | function recursive() { 10 | recursive(); 11 | } 12 | 13 | // arg not inlined 14 | function baz(arg) { 15 | return arg; 16 | } 17 | baz(); 18 | 19 | var multipleRefs = 'one'; 20 | multipleRefs = 'two'; 21 | something(multipleRefs); 22 | 23 | var impure = multipleRefs; 24 | something(impure); 25 | 26 | var itself = itself + 1; 27 | -------------------------------------------------------------------------------- /test/fixtures/async-hoisting/expected.js: -------------------------------------------------------------------------------- 1 | function _asyncToGenerator(fn) { return function () { var gen = fn.apply(this, arguments); return new Promise(function (resolve, reject) { function step(key, arg) { try { var info = gen[key](arg); var value = info.value; } catch (error) { reject(error); return; } if (info.done) { resolve(value); } else { return Promise.resolve(value).then(function (value) { step("next", value); }, function (err) { step("throw", err); }); } } return step("next"); }); }; } 2 | 3 | (() => { 4 | let foo = (() => { 5 | var _ref = _asyncToGenerator(function* () { 6 | return bar; 7 | }); 8 | return function foo() { 9 | return _ref.apply(this, arguments); 10 | }; 11 | })(); 12 | return foo(); 13 | })(); 14 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | npm-debug.log* 5 | 6 | # Runtime data 7 | pids 8 | *.pid 9 | *.seed 10 | 11 | # Directory for instrumented libs generated by jscoverage/JSCover 12 | lib-cov 13 | 14 | # Coverage directory used by tools like istanbul 15 | coverage 16 | 17 | # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) 18 | .grunt 19 | 20 | # node-waf configuration 21 | .lock-wscript 22 | 23 | # Compiled binary addons (http://nodejs.org/api/addons.html) 24 | build/Release 25 | 26 | # Dependency directory 27 | node_modules 28 | 29 | # Optional npm cache directory 30 | .npm 31 | 32 | # Optional REPL history 33 | .node_repl_history 34 | 35 | # IntelliJ 36 | *.iml 37 | .idea 38 | 39 | # Build output 40 | lib 41 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # babel-plugin-transform-dead-code-elimination 2 | 3 | 4 | Babel 6 fork of babel-plugin-dead-code-elimination. 5 | 6 | Incorporates fixes from [achicu/babel-plugin-dead-code-elimination](https://github.com/achicu/babel-plugin-dead-code-elimination). 7 | 8 | ## Installation 9 | 10 | `npm install --save-dev babel-plugin-transform-dead-code-elimination` 11 | 12 | ## Usage 13 | 14 | **.babelrc:** 15 | 16 | ```json 17 | { 18 | "plugins": [ 19 | "transform-dead-code-elimination" 20 | ] 21 | } 22 | ``` 23 | 24 | Or, with options (note: `experimentalInlining` will almost definitely break your code): 25 | 26 | ```json 27 | { 28 | "plugins": [ 29 | ["transform-dead-code-elimination", { 30 | "experimentalInlining": true 31 | }] 32 | ] 33 | } 34 | ``` 35 | -------------------------------------------------------------------------------- /.github/workflows/ci.yml: -------------------------------------------------------------------------------- 1 | name: CI 2 | 3 | on: 4 | push: 5 | branches: 6 | - master 7 | tags: 8 | - v*.*.* 9 | pull_request: 10 | 11 | jobs: 12 | build: 13 | runs-on: ubuntu-latest 14 | steps: 15 | - uses: actions/checkout@v1 16 | - uses: actions/setup-node@v1 17 | with: 18 | node-version: '12.x' 19 | registry-url: 'https://registry.npmjs.org' 20 | - run: npm install 21 | - run: npm run test-coverage 22 | - run: npm run report-coverage -- --check-coverage --lines 100 23 | - uses: actions/upload-artifact@v1 24 | with: 25 | name: coverage 26 | path: coverage 27 | - run: npm run build 28 | - run: npm publish 29 | if: startsWith(github.ref, 'refs/tags/') 30 | env: 31 | NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }} 32 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2016 Erik Desjardins 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /test/test.js: -------------------------------------------------------------------------------- 1 | import test from 'ava'; 2 | import { transformFile } from 'babel-core'; 3 | import fs from 'fs'; 4 | import path from 'path'; 5 | import util from 'util'; 6 | import globby from 'globby'; 7 | 8 | const exists = util.promisify(fs.exists); 9 | const readFile = util.promisify(fs.readFile); 10 | 11 | async function transform(dir) { 12 | const optionsPath = path.join(__dirname, dir, 'options.json'); 13 | const hasOptions = await exists(optionsPath); 14 | const options = hasOptions ? require(optionsPath) : {}; 15 | 16 | options.plugins = options.plugins || [path.join(__dirname, '../lib/index.js')]; 17 | options.babelrc = false; 18 | 19 | return new Promise((resolve, reject) => { 20 | transformFile(path.join(dir, 'actual.js'), options, (err, result) => { 21 | err ? reject(err) : resolve(result.code); 22 | }); 23 | }); 24 | } 25 | 26 | function normalize(string) { 27 | // replace multiple newlines with one, normalize to unix line endings 28 | return string.trim().replace(/[\r\n]+/g, '\n'); 29 | } 30 | 31 | for (const dir of globby.sync('fixtures/*', { onlyDirectories: true })) { 32 | const name = path.basename(path.resolve(__dirname, 'fixtures', dir)); 33 | test(name, async t => { 34 | let [expected, transformed] = (await Promise.all([ 35 | readFile(path.join(dir, 'expected.js'), 'utf8'), 36 | transform(dir) 37 | ])).map(normalize); 38 | 39 | t.truthy(expected); 40 | t.truthy(transformed); 41 | t.is(expected, transformed); 42 | }); 43 | } 44 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "babel-plugin-transform-dead-code-elimination", 3 | "version": "2.2.3", 4 | "description": "Babel 6 fork of babel-plugin-dead-code-elimination.", 5 | "main": "lib/index.js", 6 | "scripts": { 7 | "prebuild": "rimraf lib", 8 | "build": "cross-env NODE_ENV=production babel src --out-dir lib", 9 | "pretest": "cross-env NODE_ENV=development babel src --out-dir lib", 10 | "test": "ava test/test.js", 11 | "test-coverage": "nyc npm test", 12 | "report-coverage": "nyc report --reporter=text --reporter=html" 13 | }, 14 | "repository": { 15 | "type": "git", 16 | "url": "git+https://github.com/erikdesjardins/babel-plugin-transform-dead-code-elimination.git" 17 | }, 18 | "keywords": [ 19 | "babel-plugin" 20 | ], 21 | "author": "Erik Desjardins", 22 | "license": "MIT", 23 | "bugs": { 24 | "url": "https://github.com/erikdesjardins/babel-plugin-transform-dead-code-elimination/issues" 25 | }, 26 | "homepage": "https://github.com/erikdesjardins/babel-plugin-transform-dead-code-elimination#readme", 27 | "devDependencies": { 28 | "ava": "^0.16.0", 29 | "babel-cli": "^6.26.0", 30 | "babel-core": "^6.26.3", 31 | "babel-plugin-syntax-decorators": "^6.13.0", 32 | "babel-plugin-transform-async-to-generator": "^6.24.1", 33 | "babel-plugin-transform-es2015-modules-commonjs": "^6.26.2", 34 | "babel-plugin-transform-node-env-inline": "^6.8.0", 35 | "babel-preset-es2015": "^6.24.1", 36 | "cross-env": "^6.0.3", 37 | "globby": "^10.0.1", 38 | "nyc": "^14.1.1", 39 | "rimraf": "^3.0.0" 40 | }, 41 | "dependencies": { 42 | "babel-types": "^6.13.0" 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /src/index.js: -------------------------------------------------------------------------------- 1 | import * as t from "babel-types"; 2 | 3 | export default function () { 4 | function toStatements(node) { 5 | if (t.isBlockStatement(node)) { 6 | var hasBlockScoped = false; 7 | 8 | for (var i = 0; i < node.body.length; i++) { 9 | var bodyNode = node.body[i]; 10 | if (t.isBlockScoped(bodyNode)) hasBlockScoped = true; 11 | } 12 | 13 | if (!hasBlockScoped) { 14 | return node.body; 15 | } 16 | } 17 | 18 | return node; 19 | } 20 | 21 | var visitor = { 22 | ReferencedIdentifier(path, state) { 23 | if (!state.opts.experimentalInlining) return; 24 | 25 | const { node, scope } = path; 26 | var binding = scope.getBinding(node.name); 27 | if (!binding || binding.references > 1 || !binding.constant) return; 28 | if (binding.kind === "param" || binding.kind === "module") return; 29 | 30 | // Do not remove exports like `export function t() { }`. 31 | if (t.isExportDeclaration(binding.path.parent)) return; 32 | 33 | var replacement = binding.path.node; 34 | if (t.isVariableDeclarator(replacement)) { 35 | if (t.isArrayPattern(replacement.id)) { 36 | // don't try to inline across array destructuring 37 | return; 38 | } 39 | 40 | replacement = replacement.init; 41 | } 42 | if (!replacement) return; 43 | 44 | // ensure it's a "pure" type 45 | if (!scope.isPure(replacement, true)) return; 46 | 47 | if (t.isClass(replacement) || t.isFunction(replacement)) { 48 | // don't change this if it's in a different scope, this can be bad 49 | // for performance since it may be inside a loop or deeply nested in 50 | // hot code 51 | if (binding.path.scope.parent !== scope) return; 52 | } 53 | 54 | if (path.findParent((path) => path.node === replacement)) { 55 | return; 56 | } 57 | 58 | t.toExpression(replacement); 59 | scope.removeBinding(node.name); 60 | binding.path.remove(); 61 | path.replaceWith(replacement); 62 | }, 63 | 64 | "ClassDeclaration|FunctionDeclaration"(path) { 65 | const { node, scope } = path; 66 | if (t.isClass(node) && node.decorators && node.decorators.length) { 67 | // We don't want to remove classes that have attached decorators. 68 | // The decorator itself is referencing the class and might have side effects, like 69 | // registering the class somewhere else. 70 | return; 71 | } 72 | // Anonymous functions will either be used immediately 73 | // or assigned to a variable, which will be DCE'd if it's unused. 74 | // Either way, we can't do anything here. 75 | if (!node.id) return; 76 | 77 | var binding = scope.getBinding(node.id.name); 78 | if (binding && !binding.referenced) { 79 | path.remove(); 80 | // binding might never be read (have no references) yet still be written to 81 | // so replace all constantViolations (assignments) with the assigned value 82 | for (const path of binding.constantViolations) { 83 | // constantViolations that aren't assignments should be duplicate function or var declarations 84 | // which will be removed when this visitor gets to them a second time 85 | if (t.isAssignmentExpression(path)) { 86 | path.replaceWith(path.node.right); 87 | } 88 | } 89 | } 90 | }, 91 | 92 | VariableDeclarator({ node, scope }) { 93 | if (!t.isIdentifier(node.id) || !scope.isPure(node.init, true)) return; 94 | visitor["ClassDeclaration|FunctionDeclaration"].apply(this, arguments); 95 | }, 96 | 97 | ConditionalExpression: { 98 | exit(path) { 99 | const { node } = path; 100 | var evaluateTest = path.get("test").evaluateTruthy(); 101 | if (evaluateTest === true) { 102 | path.replaceWith(node.consequent); 103 | } else if (evaluateTest === false) { 104 | path.replaceWith(node.alternate); 105 | } 106 | } 107 | }, 108 | 109 | LogicalExpression: { 110 | exit(path) { 111 | const { node } = path; 112 | var operator = path.get("operator").node; 113 | var leftOfOperatorTest = path.get("left").evaluateTruthy(); 114 | if (operator === "&&") { 115 | if (leftOfOperatorTest === true) { 116 | path.replaceWith(node.right); 117 | } else if (leftOfOperatorTest === false) { 118 | path.replaceWith(node.left); 119 | } 120 | } else if (operator === "||") { 121 | if (leftOfOperatorTest === true) { 122 | path.replaceWith(node.left); 123 | } else if (leftOfOperatorTest === false) { 124 | path.replaceWith(node.right); 125 | } 126 | } 127 | } 128 | }, 129 | 130 | BlockStatement: { 131 | exit(path) { 132 | var paths = path.get("body"); 133 | 134 | var purge = false; 135 | 136 | for (var i = 0; i < paths.length; i++) { 137 | let path = paths[i]; 138 | 139 | if (!purge && t.isCompletionStatement(path)) { 140 | purge = true; 141 | continue; 142 | } 143 | 144 | if (purge && !t.isFunctionDeclaration(path) && !path.node._blockHoist) { 145 | path.remove(); 146 | } 147 | } 148 | } 149 | }, 150 | 151 | IfStatement: { 152 | exit(path) { 153 | const { node } = path; 154 | var consequent = node.consequent; 155 | var alternate = node.alternate; 156 | var test = node.test; 157 | 158 | var evaluateTest = path.get("test").evaluateTruthy(); 159 | 160 | // we can check if a test will be truthy 100% and if so then we can inline 161 | // the consequent and completely ignore the alternate 162 | // 163 | // if (true) { foo; } -> { foo; } 164 | // if ("foo") { foo; } -> { foo; } 165 | // 166 | 167 | if (evaluateTest === true) { 168 | path.replaceWithMultiple(toStatements(consequent)); 169 | return; 170 | } 171 | 172 | // we can check if a test will be falsy 100% and if so we can inline the 173 | // alternate if there is one and completely remove the consequent 174 | // 175 | // if ("") { bar; } else { foo; } -> { foo; } 176 | // if ("") { bar; } -> 177 | // 178 | 179 | if (evaluateTest === false) { 180 | if (alternate) { 181 | path.replaceWithMultiple(toStatements(alternate)); 182 | } else { 183 | path.remove(); 184 | } 185 | return; 186 | } 187 | 188 | // remove alternate blocks that are empty 189 | // 190 | // if (foo) { foo; } else {} -> if (foo) { foo; } 191 | // 192 | 193 | if (t.isBlockStatement(alternate) && !alternate.body.length) { 194 | alternate = node.alternate = null; 195 | } 196 | 197 | // if the consequent block is empty turn alternate blocks into a consequent 198 | // and flip the test 199 | // 200 | // if (foo) {} else { bar; } -> if (!foo) { bar; } 201 | // 202 | 203 | if (t.isBlockStatement(consequent) && !consequent.body.length && t.isBlockStatement(alternate) && alternate.body.length) { 204 | node.consequent = node.alternate; 205 | node.alternate = null; 206 | node.test = t.unaryExpression("!", test, true); 207 | } 208 | } 209 | } 210 | }; 211 | 212 | return { visitor }; 213 | } 214 | --------------------------------------------------------------------------------