#
# Jasy - Web Tooling Framework
# Copyright 2010-2012 Zynga Inc.
# Copyright 2013-2014 Sebastian Werner
#
import jasy.style.parse.Node as Node
import jasy.style.parse.ScopeScanner as ScopeScanner
import jasy.core.Console as Console
#
# Public API
#
[docs]class Error(Exception):
def __init__(self, name, line):
self.__name = name
self.__line = line
def __str__(self):
return "Unallowed private field access to %s at line %s!" % (self.__name, self.__line)
[docs]def cleanup(node):
""""""
if not hasattr(node, "variables"):
ScopeScanner.scan(node)
# Re cleanup until nothing to remove is found
iteration = 0
cleaned = False
Console.debug("Removing unused variables...")
Console.indent()
while True:
iteration += 1
modified = __cleanup(node)
if modified > 0:
Console.debug("Removed %s unused variables", modified)
ScopeScanner.scan(node)
cleaned = True
else:
break
Console.outdent()
return cleaned
#
# Implementation
#
def __cleanup(node):
"""The scanner part which looks for scopes with unused variables/params."""
cleaned = 0
for child in list(node):
if child is not None:
cleaned += __cleanup(child)
# Process any selector separately.
# Variables defined in there do not affect the outside
if node.type == "selector":
rules = node.rules
if rules.scope.unused:
cleaned += __recurser(node.rules, rules.scope.unused)
# Process global style sheet node
elif node.type == "sheet" and not hasattr(node, "parent"):
if node.scope.unused:
cleaned += __recurser(node, node.scope.unused)
return cleaned
def __recurser(node, unused):
"""The cleanup part which always processes one scope and cleans up params and variable definitions which are
unused."""
modified = 0
# Process children, but ignore all selector blocks as these should be processed separately
if node.type != "selector":
for child in reversed(node):
# None children are allowed sometimes e.g. during array_init like [1,2,,,7,8]
if child is not None:
modified += __recurser(child, unused)
if node.type == "mixin":
# Mixin with actual users aka extending customers
if getattr(node, "selector", None):
pass
# Remove full unused functions (when not in top-level scope)
elif node.name in unused:
Console.debug("Removing unused mixin %s at line %s" % (node.name, node.line))
node.parent.remove(node)
modified += 1
else:
# Remove unused parameters
params = getattr(node.parent, "params", None)
if params:
# Start from back, as we can only remove params as long
# as there is not a required one after the current one
for variable in reversed(params):
if variable.name in unused:
Console.debug("Removing unused parameter '%s' in line %s", variable.name, variable.line)
params.remove(variable)
modified += 1
else:
break
elif node.type == "declaration":
if node.name in unused:
if hasattr(node, "initializer"):
init = node.initializer
if init.type in ("null", "this", "true", "false", "identifier", "number", "string"):
Console.debug("Removing unused primitive variable %s at line %s" % (node.name, node.line))
node.parent.remove(node)
modified += 1
else:
Console.debug("Could not automatically remove unused variable %s at line %s without possible side-effects" % (node.name, node.line))
else:
node.parent.remove(node)
modified += 1
return modified