ember-cli-tree-shake
Advanced tools
Comparing version 0.0.2 to 0.0.3
@@ -1,23 +0,16 @@ | ||
var Controller = require('./controller'); | ||
let Controllers = require('./controllers'); | ||
let Component = require('./component'); | ||
const LAST_CALL_EXPRESSION = 'extend'; | ||
var Controllers = function(tree) { | ||
this.tree = tree; | ||
}; | ||
let Components = class Components extends Controllers { | ||
type() { | ||
return 'Component'; | ||
} | ||
Controllers.prototype.find = function() { | ||
let foundControllers = []; | ||
this.tree.selectNodesByType('CallExpression').forEach(node => { | ||
if (!node.callee.property || !node.callee.object.property) { | ||
return; | ||
} | ||
if (node.callee.property.name === LAST_CALL_EXPRESSION && node.callee.object.property.name === 'Component') { | ||
foundControllers.push(new Controller(node)); | ||
} | ||
}); | ||
return foundControllers; | ||
typeClass() { | ||
return Component; | ||
} | ||
}; | ||
module.exports = Controllers; | ||
module.exports = Components; |
@@ -1,66 +0,35 @@ | ||
var ComputedProperty = function(node) { | ||
this.node = node; | ||
}; | ||
let Property = require('./property'); | ||
ComputedProperty.prototype.name = function() { | ||
return this.node.key.name; | ||
} | ||
let ComputedProperty = class ComputedProperty extends Property { | ||
value() { | ||
return this.node.value; | ||
} | ||
ComputedProperty.prototype.value = function() { | ||
return this.node.value; | ||
} | ||
arguments() { | ||
if (!this.value().arguments) { | ||
return []; | ||
} | ||
ComputedProperty.prototype.arguments = function() { | ||
if (!this.value().arguments) { | ||
return []; | ||
return this.value().arguments; | ||
} | ||
return this.value().arguments; | ||
} | ||
get functionBody() { | ||
const args = this.arguments(); | ||
return args[args.length - 1]; | ||
} | ||
ComputedProperty.prototype.internalGetters = function() { | ||
const args = this.arguments(); | ||
const fn = args[args.length - 1]; | ||
const getters = []; | ||
dependants() { | ||
let dependants = []; | ||
this.arguments().forEach((argument) => { | ||
if (argument.type !== 'StringLiteral') { | ||
return; | ||
} | ||
var dfs = (el) => { | ||
if (el && el.childElements) { | ||
el.childElements.forEach((element) => { | ||
if (element.type == 'CallExpression' && element.callee.property && element.callee.property.name === 'get') { | ||
const dependant = element.arguments[0]; | ||
dependants.push(argument.value.split('.')[0]); | ||
}); | ||
if (dependant.value) { | ||
getters.push(dependant); | ||
} | ||
} | ||
dfs(element.nextSibling); | ||
dfs(element); | ||
}); | ||
} | ||
return dependants.concat(super.dependants()); | ||
} | ||
if (fn) { | ||
dfs(fn.body); | ||
} | ||
return getters; | ||
} | ||
}; | ||
ComputedProperty.prototype.dependants = function() { | ||
let dependants = []; | ||
this.arguments().forEach((argument) => { | ||
if (argument.type !== 'StringLiteral') { | ||
return; | ||
} | ||
dependants.push(argument.value.split('.')[0]); | ||
}); | ||
this.internalGetters().forEach((getter) => { | ||
dependants.push(getter.value.split('.')[0]); | ||
}); | ||
return dependants; | ||
} | ||
module.exports = ComputedProperty; |
@@ -1,2 +0,3 @@ | ||
var ComputedProperty = require('./computed-property'); | ||
let ComputedProperty = require('./computed-property'); | ||
let Property = require('./property'); | ||
@@ -14,76 +15,104 @@ const TYPE = 'ObjectExpression'; | ||
var Controller = function(node) { | ||
this.node = node; | ||
this._computedProperties = null; | ||
this._observerProperties = null; | ||
}; | ||
let Controller = class Controller { | ||
constructor(node) { | ||
this.node = node; | ||
this._computedProperties = null; | ||
this._observerProperties = null; | ||
} | ||
Controller.prototype._computed = function(type) { | ||
let computedProperties = []; | ||
this.node.arguments.forEach((argument) => { | ||
if (!argument.properties) { | ||
return; | ||
} | ||
templatePath() { | ||
return null; | ||
} | ||
argument.properties.forEach((property) => { | ||
if (!property.value) { | ||
methods() { | ||
let methods = []; | ||
this.node.arguments.forEach((argument) => { | ||
if (!argument.properties) { | ||
return; | ||
} | ||
if (property.value.type === 'CallExpression' && property.value.callee.type === 'MemberExpression') { | ||
let callee = property.value.callee | ||
argument.properties.forEach((property) => { | ||
if (property.type !== 'ObjectMethod') { | ||
return; | ||
} | ||
if ((callee.object.name === 'Ember' && callee.property.name === type) || COMPUTED_PROPERTY_MACROS.indexOf(callee.property.name) > 0) { | ||
var computedProperty = new ComputedProperty(property); | ||
computedProperties.push(computedProperty); | ||
} | ||
} | ||
var method = new Property(property); | ||
methods.push(method); | ||
}); | ||
return; | ||
}); | ||
return; | ||
}); | ||
return computedProperties; | ||
}; | ||
return methods; | ||
} | ||
Controller.prototype.computed = function() { | ||
if (this._computedProperties) { | ||
return this._computedProperties; | ||
computed() { | ||
if (this._computedProperties) { | ||
return this._computedProperties; | ||
} | ||
let computedProperties = this._computed('computed'); | ||
this._computedProperties = computedProperties; | ||
return computedProperties; | ||
} | ||
let computedProperties = this._computed('computed'); | ||
observer() { | ||
if (this._observerProperties) { | ||
return this._observerProperties; | ||
} | ||
this._computedProperties = computedProperties; | ||
return computedProperties; | ||
}; | ||
let observerProperties = this._computed('observer'); | ||
Controller.prototype.observer = function() { | ||
if (this._observerProperties) { | ||
return this._observerProperties; | ||
this._observerProperties = observerProperties; | ||
return observerProperties; | ||
} | ||
let observerProperties = this._computed('observer'); | ||
classNameBindings() { | ||
let classNameBindings = []; | ||
this._observerProperties = observerProperties; | ||
return observerProperties; | ||
}; | ||
this.node.arguments.forEach((argument) => { | ||
if (!argument.properties) { | ||
return; | ||
} | ||
argument.properties.forEach((property) => { | ||
if (property.key.name === 'classNameBindings' && property.value.elements) { | ||
classNameBindings = property.value.elements.map(function(element) { | ||
return element.value.split(':')[0]; | ||
}); | ||
} | ||
}); | ||
}); | ||
Controller.prototype.classNameBindings = function() { | ||
let classNameBindings = []; | ||
return classNameBindings; | ||
} | ||
this.node.arguments.forEach((argument) => { | ||
if (!argument.properties) { | ||
_computed(type) { | ||
let computedProperties = []; | ||
this.node.arguments.forEach((argument) => { | ||
if (!argument.properties) { | ||
return; | ||
} | ||
argument.properties.forEach((property) => { | ||
if (!property.value) { | ||
return; | ||
} | ||
if (property.value.type === 'CallExpression' && property.value.callee.type === 'MemberExpression') { | ||
let callee = property.value.callee | ||
if ((callee.object.name === 'Ember' && callee.property.name === type) || COMPUTED_PROPERTY_MACROS.indexOf(callee.property.name) > 0) { | ||
var computedProperty = new ComputedProperty(property); | ||
computedProperties.push(computedProperty); | ||
} | ||
} | ||
}); | ||
return; | ||
} | ||
argument.properties.forEach((property) => { | ||
if (property.key.name === 'classNameBindings' && property.value.elements) { | ||
classNameBindings = property.value.elements.map(function(element) { | ||
return element.value.split(':')[0]; | ||
}); | ||
} | ||
}); | ||
}); | ||
return classNameBindings; | ||
}; | ||
return computedProperties; | ||
} | ||
} | ||
module.exports = Controller; |
@@ -5,20 +5,31 @@ var Controller = require('./controller'); | ||
var Controllers = function(tree) { | ||
this.tree = tree; | ||
}; | ||
let Controllers = class Controllers { | ||
constructor(tree) { | ||
this.tree = tree; | ||
} | ||
Controllers.prototype.find = function() { | ||
let foundControllers = []; | ||
this.tree.selectNodesByType('CallExpression').forEach(node => { | ||
if (!node.callee.property || !node.callee.object.property) { | ||
return; | ||
} | ||
type() { | ||
return 'Controller'; | ||
} | ||
if (node.callee.property.name === LAST_CALL_EXPRESSION && node.callee.object.property.name === 'Controller') { | ||
foundControllers.push(new Controller(node)); | ||
} | ||
}); | ||
return foundControllers; | ||
}; | ||
typeClass() { | ||
return Controller; | ||
} | ||
find() { | ||
let foundControllers = []; | ||
this.tree.selectNodesByType('CallExpression').forEach(node => { | ||
if (!node.callee.property || !node.callee.object.property) { | ||
return; | ||
} | ||
if (node.callee.property.name === LAST_CALL_EXPRESSION && node.callee.object.property.name === this.type()) { | ||
let type = this.typeClass(); | ||
foundControllers.push(new type(node)); | ||
} | ||
}); | ||
return foundControllers; | ||
} | ||
} | ||
module.exports = Controllers; |
@@ -10,162 +10,170 @@ const Controllers = require('./controllers'); | ||
var DeadComputedProperties = function(tree, paths, filepath) { | ||
this.tree = tree; | ||
this.filepath = filepath; | ||
this.paths = paths; | ||
}; | ||
let DeadComputedProperties = class DeadComputedProperties { | ||
constructor(tree, paths, filepath) { | ||
this.tree = tree; | ||
this.filepath = filepath; | ||
this.paths = paths; | ||
} | ||
DeadComputedProperties.prototype.emit = function() { | ||
this.collect().forEach((property) => { | ||
console.log( | ||
colors.yellow('Warning:'), | ||
'Possibly dead computed property', | ||
'"' + colors.green(property.name) + '"', | ||
property.path | ||
); | ||
}); | ||
} | ||
emit() { | ||
this.collect().forEach((property) => { | ||
console.log( | ||
colors.yellow('Warning:'), | ||
'Possibly dead computed property', | ||
'"' + colors.green(property.name) + '"', | ||
property.path | ||
); | ||
}); | ||
} | ||
DeadComputedProperties.prototype.collect = function() { | ||
var possiblyDead = []; | ||
var controllers = new Controllers(this.tree).find(); | ||
var components = new Components(this.tree).find(); | ||
collect() { | ||
let possiblyDead = []; | ||
let controllers = new Controllers(this.tree).find(); | ||
let components = new Components(this.tree).find(); | ||
return this.collectFor(controllers, 'controllers').concat( | ||
this.collectFor(components, 'components') | ||
); | ||
}; | ||
return this.collectFor(controllers).concat( | ||
this.collectFor(components) | ||
); | ||
} | ||
DeadComputedProperties.prototype.collectFor = function(type, typePath) { | ||
var possiblyDead = []; | ||
collectFor(type) { | ||
let possiblyDead = []; | ||
type.forEach((node) => { | ||
let dependants = {}; | ||
node.computed().forEach((computed) => { | ||
computed.dependants().forEach((dep) => { | ||
dependants[dep] = computed; | ||
type.forEach((node) => { | ||
let dependants = {}; | ||
node.computed().forEach((computed) => { | ||
computed.dependants().forEach((dep) => { | ||
dependants[dep] = true; | ||
}); | ||
}); | ||
}); | ||
node.observer().forEach((observer) => { | ||
observer.dependants().forEach((dep) => { | ||
dependants[dep] = observer; | ||
node.observer().forEach((observer) => { | ||
observer.dependants().forEach((dep) => { | ||
dependants[dep] = true; | ||
}); | ||
}); | ||
}); | ||
node.classNameBindings().forEach((binding) => { | ||
dependants[binding] = binding; | ||
}); | ||
node.classNameBindings().forEach((binding) => { | ||
dependants[binding] = true; | ||
}); | ||
let jsPath = path.relative(this.paths.app + '/' + typePath, this.filepath); | ||
let controllerDirPath = path.dirname(jsPath); | ||
let templatePathPart = '/templates/'; | ||
node.methods().forEach((method) => { | ||
method.dependants().forEach((dep) => { | ||
dependants[dep] = true | ||
}); | ||
}) | ||
if (typePath !== 'controllers') { | ||
templatePathPart += typePath + '/'; | ||
} | ||
let jsPath = path.relative(this.paths.app + '/' + node.templatePath(), this.filepath); | ||
let controllerDirPath = path.dirname(jsPath); | ||
let templatePathPart = '/templates/'; | ||
let templatePath = path.join( | ||
this.paths.app, | ||
templatePathPart, | ||
path.basename(jsPath, '.js') + '.hbs' | ||
); | ||
let walker, htmlbars; | ||
try { | ||
if (fs.statSync(templatePath)) { | ||
let code = fs.readFileSync(templatePath, 'utf8'); | ||
htmlbars = HTMLBarsSyntax.parse(code); | ||
walker = new HTMLBarsSyntax.Walker(); | ||
if (node.templatePath()) { | ||
templatePathPart += node.templatePath() + '/'; | ||
} | ||
} catch (_) { | ||
} | ||
node.computed().forEach((computed) => { | ||
let maybeDead = true; | ||
let templatePath = path.join( | ||
this.paths.app, | ||
templatePathPart, | ||
path.basename(jsPath, '.js') + '.hbs' | ||
); | ||
let walker, htmlbars; | ||
if (dependants[computed.name()]) { | ||
maybeDead = false; | ||
try { | ||
if (fs.statSync(templatePath)) { | ||
let code = fs.readFileSync(templatePath, 'utf8'); | ||
htmlbars = HTMLBarsSyntax.parse(code); | ||
walker = new HTMLBarsSyntax.Walker(); | ||
} | ||
} catch (_) { | ||
} | ||
if (walker) { | ||
let visit = (walker, hbsTree) => { | ||
walker.visit(hbsTree, (hbsNode) => { | ||
if (hbsNode.original === computed.name() || (hbsNode.path && hbsNode.path.original === computed.name())) { | ||
maybeDead = false; | ||
} | ||
node.computed().forEach((computed) => { | ||
let maybeDead = true; | ||
if (hbsNode.attributes) { | ||
hbsNode.attributes.forEach((attribute) => { | ||
visit(walker, attribute); | ||
}) | ||
} | ||
if (dependants[computed.name()]) { | ||
maybeDead = false; | ||
} | ||
if (hbsNode.parts) { | ||
hbsNode.parts.forEach((part) => { | ||
visit(walker, part); | ||
}) | ||
} | ||
if (walker) { | ||
let visit = (walker, hbsTree) => { | ||
walker.visit(hbsTree, (hbsNode) => { | ||
if (hbsNode.original === computed.name() || (hbsNode.path && hbsNode.path.original === computed.name())) { | ||
maybeDead = false; | ||
} | ||
if (hbsNode.pairs) { | ||
hbsNode.pairs.forEach((pair) => { | ||
visit(walker, pair); | ||
}) | ||
} | ||
if (hbsNode.attributes) { | ||
hbsNode.attributes.forEach((attribute) => { | ||
visit(walker, attribute); | ||
}) | ||
} | ||
if (hbsNode.params) { | ||
hbsNode.params.forEach((param) => { | ||
visit(walker, param); | ||
}) | ||
} | ||
if (hbsNode.parts) { | ||
hbsNode.parts.forEach((part) => { | ||
visit(walker, part); | ||
}) | ||
} | ||
if (hbsNode.value) { | ||
visit(walker, hbsNode.value); | ||
} | ||
if (hbsNode.pairs) { | ||
hbsNode.pairs.forEach((pair) => { | ||
visit(walker, pair); | ||
}) | ||
} | ||
if (hbsNode.hash) { | ||
visit(walker, hbsNode.hash); | ||
} | ||
if (hbsNode.params) { | ||
hbsNode.params.forEach((param) => { | ||
visit(walker, param); | ||
}) | ||
} | ||
if (hbsNode.program) { | ||
visit(walker, hbsNode.program); | ||
} | ||
if (hbsNode.value) { | ||
visit(walker, hbsNode.value); | ||
} | ||
if (hbsNode.path && hbsNode.path.original === 'partial') { | ||
let jsPath = path.relative(this.paths.app + '/controllers', this.filepath); | ||
let templatePath = path.join( | ||
this.paths.app, | ||
'/templates/', | ||
hbsNode.params[0].original + '.hbs' | ||
); | ||
if (hbsNode.hash) { | ||
visit(walker, hbsNode.hash); | ||
} | ||
try { | ||
if (fs.statSync(templatePath)) { | ||
let code = fs.readFileSync(templatePath, 'utf8'); | ||
partialHtmlbars = HTMLBarsSyntax.parse(code); | ||
partialWalker = new HTMLBarsSyntax.Walker(); | ||
if (hbsNode.program) { | ||
visit(walker, hbsNode.program); | ||
} | ||
visit(partialWalker, partialHtmlbars); | ||
if (hbsNode.path && hbsNode.path.original === 'partial') { | ||
let jsPath = path.relative(this.paths.app + '/controllers', this.filepath); | ||
let templatePath = path.join( | ||
this.paths.app, | ||
'/templates/', | ||
hbsNode.params[0].original + '.hbs' | ||
); | ||
try { | ||
if (fs.statSync(templatePath)) { | ||
let code = fs.readFileSync(templatePath, 'utf8'); | ||
partialHtmlbars = HTMLBarsSyntax.parse(code); | ||
partialWalker = new HTMLBarsSyntax.Walker(); | ||
visit(partialWalker, partialHtmlbars); | ||
} | ||
} catch (_) { | ||
} | ||
} catch (_) { | ||
} | ||
} | ||
}); | ||
} | ||
visit(walker, htmlbars); | ||
} | ||
if (maybeDead) { | ||
possiblyDead.push({ | ||
'name': computed.name(), | ||
'path': this.filepath | ||
}); | ||
} | ||
visit(walker, htmlbars); | ||
} | ||
if (maybeDead) { | ||
possiblyDead.push({ | ||
'name': computed.name(), | ||
'path': this.filepath | ||
}); | ||
} | ||
}); | ||
}); | ||
}); | ||
return possiblyDead; | ||
return possiblyDead; | ||
} | ||
}; | ||
module.exports = DeadComputedProperties; |
{ | ||
"name": "ember-cli-tree-shake", | ||
"version": "0.0.2", | ||
"version": "0.0.3", | ||
"description": "Use tree shaking to discover dead code in your Ember app", | ||
@@ -5,0 +5,0 @@ "directories": { |
@@ -9,3 +9,2 @@ # ember-cli-tree-shake | ||
- Plain function's usage of computed property doesn't get considered as dependency | ||
- Mixins don't get considered when considering aliveness of a property | ||
@@ -12,0 +11,0 @@ |
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
14278
13
394
22