added optimizer scripts and some code style improvements

This commit is contained in:
logsol 2015-03-09 04:56:28 +01:00
parent 6b472dc134
commit 5f5fec5b10
16 changed files with 397 additions and 36 deletions

218
scripts/optimize.js Normal file
View file

@ -0,0 +1,218 @@
/*
* This is supposed to be a code optimizer.
*
* usage:
* node scripts/optimize.js
*
* based on https://developers.google.com/speed/articles/optimizing-javascript
* I wanted to automatically replace properties as discribed under "Initializing instance variables"
*
* So far it is the only thing this script does, but the potential for more is there.
* Also it does not (yet) write to any files, only outputs the results in the console.
*
* The script fully disasembles the entire codebase (with esprima) and reconstructs (via escodegen) it
* with a few optimisations unfortunately it loses ambiguous new lines and such, so that it might
* not be entirely usable as a code replacing script - it could perhaps be used as a compile script in
* production
*
* But it has also nice things as at will add semicolons everywhere automatically and will
* definately keep a solid indentation style for everything. Since it lacks a lot of extra
* \n newlines it looks quite cluttered though.
*
*/
var fs = require('fs');
var util = require('util');
var esprima = require('esprima');
var escodegen = require('escodegen');
var info = [];
var generatorOption = {
format: {
indent: {
style: ' ',
base: 0,
adjustMultilineComment: true//false
},
newline: '\n',
space: ' ',
json: false,
renumber: false,
hexadecimal: false,
quotes: 'single',
escapeless: false,
compact: false,
parentheses: true,
semicolons: true,
safeConcatenation: true
},
moz: {
starlessGenerator: false,
parenthesizedComprehensionBlock: false,
comprehensionExpressionStartsWithAssignment: false
},
parse: null,
comment: true,
sourceMap: undefined,
sourceMapRoot: null,
sourceMapWithCode: false,
file: undefined,
directive: false,
verbatim: undefined
};
function readdir (path) {
var filesNames = fs.readdirSync(path)
for (var i = 0; i < filesNames.length; i++) {
var fileName = filesNames[i];
var fullPath = path + "/" + fileName;
if(path == "app/Lib/Vendor"){
continue;
}
if(fileName.indexOf(".js") == -1) {
var stats = fs.lstatSync(fullPath);
if (stats.isDirectory()) {
readdir(fullPath);
}
continue;
}
readFile(path, fileName);
};
}
function readFile (path, fileName) {
//console.log("+++++ " + moduleName + " ++++++");
var contents = fs.readFileSync(path + "/" + fileName);
contents = contents.toString();
optimize(path, fileName, contents);
}
function optimize (path, fileName, contents) {
var fullPath = path + "/" + fileName;
var moduleName = fileName.split(".js").join("");
var tree = esprima.parse(contents);
// find moduleId from return statement on module level
var module = tree.body[0].expression.arguments[1].body.body;
var moduleId = findModuleId(module);
if (!moduleId) {
info.push("could not find moduleId in: " + fullPath);
return;
}
if (moduleId == "Parent") {
info.push("not optimizing empty module (returning Parent) in: " + fullPath);
return;
}
// find constructor
var constructorPosition = findConstructorPosition(module, moduleId);
if (constructorPosition === false) {
info.push("could not find constructor in: " + fileName)
return;
}
var constructor = module[constructorPosition];
transformProps(tree, moduleId, constructor, constructorPosition);
console.log(escodegen.generate(tree, generatorOption));
}
function findModuleId (module) {
var moduleId = false;
for (var j = 0; j < module.length; j++) {
var expression = module[j];
//console.log(util.inspect(expression, { showHidden: true, depth: 4 }));
if (expression.type == "ReturnStatement") {
if(expression.argument.type == "Identifier") {
// for return Module;
moduleId = expression.argument.name;
break;
} else if (expression.argument.type == "NewExpression") {
// for return new Module;
moduleId = expression.argument.callee.name;
break;
} else {
info.push("Unexpected return type at module level. " + fullPath)
}
}
}
return moduleId;
};
function findConstructorPosition (module, moduleId) {
for (var j = 0; j < module.length; j++) {
var expression = module[j];
if (expression.type == "FunctionDeclaration" && expression.id.name == moduleId) {
return j;
}
}
return false;
}
function transformProps (tree, moduleId, constructor, constructorPosition) {
var props = [];
for (var k = constructor.body.body.length - 1; k >= 0; k--) {
var line = constructor.body.body[k];
if(line.expression
&& line.expression.type == "AssignmentExpression"
&& line.expression.operator == "="
&& line.expression.left.type == "MemberExpression"
&& line.expression.left.object.type == "ThisExpression"
&& line.expression.right.type == "Literal") {
// remove "this" properties with with value type from constructor
constructor.body.body.splice(k, 1);
//console.log(util.inspect(line, { showHidden: true, depth: 4 }));
props.push({
name: line.expression.left.property.name,
value: line.expression.right.value,
raw: line.expression.right.raw
});
}
};
// generate prototype properties
for (var l = 0; l < props.length; l++) {
var attributes = props[l];
var prop = { type: 'ExpressionStatement',
expression:
{ type: 'AssignmentExpression',
operator: '=',
left:
{ type: 'MemberExpression',
computed: false,
object:
{ type: 'MemberExpression',
computed: false,
object: { type: 'Identifier', name: moduleId },
property: { type: 'Identifier', name: 'prototype' } },
property: { type: 'Identifier', name: attributes.name } },
right: { type: 'Literal', value: attributes.value, raw: attributes.raw } } }
// place property after constructor
tree.body[0].expression.arguments[1].body.body.splice(constructorPosition+1, 0, prop);
};
}
readdir("app");
console.log(info)