babel-plugin-closure-elimination
Advanced tools
Comparing version 0.0.2 to 1.0.0
@@ -1,18 +0,16 @@ | ||
/** | ||
* # Closure Eliminator | ||
*/ | ||
"use strict"; | ||
var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }(); | ||
Object.defineProperty(exports, "__esModule", { | ||
value: true | ||
}); | ||
exports.default = build; | ||
var _createClass = (function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; })(); | ||
exports["default"] = build; | ||
function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } | ||
/** | ||
* # Closure Eliminator | ||
*/ | ||
function build(babel) { | ||
var Transformer = babel.Transformer; | ||
var t = babel.types; | ||
@@ -22,6 +20,8 @@ var traverse = babel.traverse; | ||
var referenceVisitor = { | ||
enter: function enter(node, parent, scope, state) { | ||
if (!this.isJSXIdentifier() && this.isIdentifier()) { | ||
enter: function enter(path, state) { | ||
var node = path.node, | ||
scope = path.scope; | ||
if (!path.isJSXIdentifier() && path.isIdentifier()) { | ||
// direct references that we need to track to hoist this to the highest scope we can | ||
if (this.isReferenced()) { | ||
if (path.isReferenced()) { | ||
var bindingInfo = scope.getBinding(node.name); | ||
@@ -39,3 +39,3 @@ | ||
state.foundIncompatible = true; | ||
this.stop(); | ||
path.stop(); | ||
} | ||
@@ -47,3 +47,3 @@ } | ||
var PathHoister = (function () { | ||
var PathHoister = function () { | ||
function PathHoister(path, scope) { | ||
@@ -112,4 +112,4 @@ _classCallCheck(this, PathHoister); | ||
value: function hasNonParamBindings() { | ||
for (var _name in this.bindings) { | ||
var binding = this.bindings[_name]; | ||
for (var name in this.bindings) { | ||
var binding = this.bindings[name]; | ||
if (binding.kind !== "param") { | ||
@@ -142,6 +142,7 @@ return true; | ||
path.insertBefore([node]); | ||
this.path.dangerouslyRemove(); | ||
this.path.remove(); | ||
} else { | ||
var uid = path.scope.generateUidIdentifier("ref"); | ||
var replacement = t.functionDeclaration(uid, node.params, normalizeFunctionBody(node.body)); | ||
replacement.loc = node.loc; | ||
replacement._hoisted = true; | ||
@@ -156,32 +157,38 @@ | ||
return PathHoister; | ||
})(); | ||
}(); | ||
return new Transformer("closure-elimination", { | ||
Function: { | ||
enter: function enter(node, parent, scope) { | ||
var parentScope = scope.parent.getFunctionParent(); | ||
if (parent.type === 'Program' || parentScope.block.type === 'Program' || parent.type === 'CallExpression' && parent.callee === node) { | ||
return; | ||
} | ||
if (node.type === 'ArrowFunctionExpression') { | ||
var isCompatible = true; | ||
this.traverse({ | ||
enter: function enter(node) { | ||
if (node.type === 'ThisExpression') { | ||
isCompatible = false; | ||
this.stop(); | ||
} else if (this.isFunction() && node.type !== 'ArrowFunctionExpression') { | ||
this.skip(); | ||
return { | ||
visitor: { | ||
Function: { | ||
enter: function enter(path) { | ||
var node = path.node, | ||
scope = path.scope, | ||
parent = path.parentPath.node, | ||
parentScope = scope.parent.getFunctionParent(); | ||
if (parent.type === 'Program' || parentScope.block.type === 'Program' || parent.type === 'CallExpression' && parent.callee === node) { | ||
return; | ||
} | ||
if (node.type === 'ArrowFunctionExpression') { | ||
var isCompatible = true; | ||
path.traverse({ | ||
enter: function enter(subPath) { | ||
var node = subPath.node; | ||
if (node.type === 'ThisExpression') { | ||
isCompatible = false; | ||
subPath.stop(); | ||
} else if (subPath.isFunction() && node.type !== 'ArrowFunctionExpression') { | ||
subPath.skip(); | ||
} | ||
} | ||
}); | ||
if (!isCompatible) { | ||
return; | ||
} | ||
}); | ||
if (!isCompatible) { | ||
return; | ||
} | ||
var hoister = new PathHoister(path, path.parentPath.scope); | ||
hoister.run(); | ||
} | ||
var hoister = new PathHoister(this, this.parentPath.scope); | ||
hoister.run(); | ||
} | ||
} | ||
}); | ||
}; | ||
@@ -198,4 +205,2 @@ /** | ||
} | ||
} | ||
module.exports = exports["default"]; | ||
} |
{ | ||
"name": "babel-plugin-closure-elimination", | ||
"version": "0.0.2", | ||
"version": "1.0.0", | ||
"description": "Removes closures from your JavaScript in the name of performance.", | ||
@@ -10,3 +10,3 @@ "main": "lib/index.js", | ||
"pretest": "npm run build", | ||
"test": "mocha", | ||
"test": "mocha --harmony", | ||
"watch": "mocha --watch" | ||
@@ -32,6 +32,9 @@ }, | ||
"devDependencies": { | ||
"babel-cli": "^6.4.0", | ||
"babel-core": "^6.4.0", | ||
"babel-plugin-transform-flow-strip-types": "^6.4.0", | ||
"babel-preset-es2015": "^6.3.13", | ||
"mocha": "^2.2.4", | ||
"should": "^6.0.1", | ||
"babel": "^5.5.0" | ||
"should": "^6.0.1" | ||
} | ||
} |
@@ -7,2 +7,4 @@ # Babel Closure Elimination | ||
> Note: Now requires Babel 6. | ||
# What? | ||
@@ -9,0 +11,0 @@ |
@@ -5,9 +5,11 @@ /** | ||
export default function build (babel: Object): Object { | ||
const {Transformer, types: t, traverse} = babel; | ||
const {types: t, traverse} = babel; | ||
const referenceVisitor = { | ||
enter (node, parent, scope, state) { | ||
if (!this.isJSXIdentifier() && this.isIdentifier()) { | ||
enter (path, state) { | ||
const node = path.node, | ||
scope = path.scope; | ||
if (!path.isJSXIdentifier() && path.isIdentifier()) { | ||
// direct references that we need to track to hoist this to the highest scope we can | ||
if (this.isReferenced()) { | ||
if (path.isReferenced()) { | ||
const bindingInfo = scope.getBinding(node.name); | ||
@@ -26,3 +28,3 @@ | ||
state.foundIncompatible = true; | ||
this.stop(); | ||
path.stop(); | ||
} | ||
@@ -125,3 +127,3 @@ } | ||
path.insertBefore([node]); | ||
this.path.dangerouslyRemove(); | ||
this.path.remove(); | ||
} | ||
@@ -135,2 +137,3 @@ else { | ||
); | ||
replacement.loc = node.loc; | ||
replacement._hoisted = true; | ||
@@ -145,35 +148,41 @@ | ||
return new Transformer("closure-elimination", { | ||
Function: { | ||
enter (node, parent, scope) { | ||
const parentScope = scope.parent.getFunctionParent(); | ||
if ( | ||
parent.type === 'Program' || | ||
parentScope.block.type === 'Program' || | ||
(parent.type === 'CallExpression' && parent.callee === node) | ||
) { | ||
return; | ||
} | ||
if (node.type === 'ArrowFunctionExpression') { | ||
let isCompatible = true; | ||
this.traverse({ | ||
enter (node) { | ||
if (node.type === 'ThisExpression') { | ||
isCompatible = false; | ||
this.stop(); | ||
return { | ||
visitor: { | ||
Function: { | ||
enter (path) { | ||
const node = path.node, | ||
scope = path.scope, | ||
parent = path.parentPath.node, | ||
parentScope = scope.parent.getFunctionParent(); | ||
if ( | ||
parent.type === 'Program' || | ||
parentScope.block.type === 'Program' || | ||
(parent.type === 'CallExpression' && parent.callee === node) | ||
) { | ||
return; | ||
} | ||
if (node.type === 'ArrowFunctionExpression') { | ||
let isCompatible = true; | ||
path.traverse({ | ||
enter (subPath) { | ||
const node = subPath.node; | ||
if (node.type === 'ThisExpression') { | ||
isCompatible = false; | ||
subPath.stop(); | ||
} | ||
else if (subPath.isFunction() && node.type !== 'ArrowFunctionExpression') { | ||
subPath.skip(); | ||
} | ||
} | ||
else if (this.isFunction() && node.type !== 'ArrowFunctionExpression') { | ||
this.skip(); | ||
} | ||
}); | ||
if (!isCompatible) { | ||
return; | ||
} | ||
}); | ||
if (!isCompatible) { | ||
return; | ||
} | ||
const hoister = new PathHoister(path, path.parentPath.scope); | ||
hoister.run(); | ||
} | ||
const hoister = new PathHoister(this, this.parentPath.scope); | ||
hoister.run(); | ||
} | ||
} | ||
}); | ||
}; | ||
@@ -180,0 +189,0 @@ |
@@ -1,3 +0,4 @@ | ||
require("babel/register")({ | ||
"stage": 0 | ||
require("babel-core/register")({ | ||
"presets": ["es2015"], | ||
"plugins": ["transform-flow-strip-types"] | ||
}); |
import Plugin from '../src'; | ||
import fs from 'fs'; | ||
import {parse, transform, traverse, types as t} from 'babel'; | ||
import {parse, transform, traverse, types as t} from 'babel-core'; | ||
function load (basename: string): string { | ||
function load (basename) { | ||
const filename = `${__dirname}/fixtures/${basename}.js`; | ||
@@ -14,5 +14,10 @@ return fs.readFileSync(filename, 'utf8'); | ||
traverse(ast, { | ||
enter (node: Object, parent: Object) { | ||
if (this.isFunction()) { | ||
collected[JSON.stringify(node.loc)] = extractPath(this.scope); | ||
enter (path) { | ||
const node = path.node; | ||
if (path.isFunction()) { | ||
if(node.loc) { | ||
collected[JSON.stringify(node.loc)] = extractPath(path.scope); | ||
} else { | ||
throw new Error('Wrong transformed function. maybe wrong sourcemap?'); | ||
} | ||
} | ||
@@ -24,3 +29,3 @@ } | ||
function countHoisted (oldAst: Object, newAst: Object): number { | ||
function countHoisted (oldAst, newAst) { | ||
const oldPositions = collectPositions(oldAst); | ||
@@ -32,2 +37,5 @@ const newPositions = collectPositions(newAst); | ||
let key = keys[i]; | ||
if(!newPositions[key]) { | ||
throw new Error('some missed function'); | ||
} | ||
if (oldPositions[key] !== newPositions[key]) { | ||
@@ -40,7 +48,7 @@ total++; | ||
function runTest (basename: string, numberToRemove: number): void { | ||
function runTest (basename, numberToRemove) { | ||
const source = load(basename); | ||
const transformedNaked = transform(source, {stage: 0}); | ||
const transformedNaked = transform(source, {plugins: ["transform-flow-strip-types"]}); | ||
//console.log(transformedNaked.code); | ||
const transformedWithPlugin = transform(source, {stage: 0, plugins: [Plugin]}); | ||
const transformedWithPlugin = transform(source, {plugins: [Plugin, "transform-flow-strip-types"]}); | ||
//console.log(transformedWithPlugin.code); | ||
@@ -51,3 +59,3 @@ const diff = countHoisted(transformedNaked.ast, transformedWithPlugin.ast); | ||
function eliminate (basename: string, numberToRemove: number): void { | ||
function eliminate (basename, numberToRemove) { | ||
it(`should eliminate ${numberToRemove} closure(s) from "${basename}"`, function () { | ||
@@ -58,3 +66,3 @@ runTest(basename, numberToRemove); | ||
eliminate.only = function (basename: string, numberToRemove: number): void { | ||
eliminate.only = function (basename: string, numberToRemove: number) { | ||
it.only(`should eliminate ${numberToRemove} closure(s) from "${basename}"`, function () { | ||
@@ -61,0 +69,0 @@ try { |
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
No v1
QualityPackage is not semver >=1. This means it is not stable and does not support ^ ranges.
Found 1 instance in 1 package
22921
26
632
0
55
6