Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse files

Added better minification, obfuscation and better parsing

  • Loading branch information...
commit e82ad04d8bf6f1a43c69cb8d0d40f08c76b99624 1 parent d597d97
@3rd-Eden 3rd-Eden authored
View
24 Makefile
@@ -1,7 +1,24 @@
+# locations of the latest compilers
+YUIVERSION = yuicompressor-2.4.7
+LATESTCLOSURE= http://closure-compiler.googlecode.com/files/compiler-latest.zip
+LATESTYUI = http://yui.zenfs.com/releases/yuicompressor/$(YUIVERSION).zip
+
+# configuration for the test suite
ALL_TESTS = $(shell find tests/ -name '*.test.js')
REPORTER = spec
UI = exports
+# location of the vendor folder where all external tools are downloaded to
+VENDOR = ./vendor
+TMP = ./.build_tmp
+
+download:
+ @rm -Rf $(VENDOR)
+ @mkdir $(VENDOR) $(TMP)
+ @cd $(TMP) && curl $(LATESTCLOSURE) -o closure.zip && unzip closure.zip && mv compiler.jar .$(VENDOR)/closure.jar
+ @cd $(TMP) && curl $(LATESTYUI) -o yui.zip && unzip yui.zip && mv $(YUIVERSION)/build/$(YUIVERSION).jar .$(VENDOR)/yui.jar
+ @rm -Rf $(TMP)
+
test:
@./node_modules/.bin/mocha \
--require should \
@@ -11,4 +28,11 @@ test:
--growl \
$(ALL_TESTS)
+update:
+ @git submodule update --init --recursive
+ @npm install .
+
+install:
+ @npm link .
+
.PHONY: test
View
10 package.json
@@ -1,5 +1,5 @@
{
- "name": "oz"
+ "name": "ION"
, "description": "bundle, all the things"
, "version": "0.0.0"
, "author": "Arnout Kazemier <info@3rd-Eden.com>"
@@ -10,6 +10,10 @@
, "contextify": "0.0.7"
, "request": "2.2.9"
, "devnull": "0.0.x"
+ , "active-x-obfuscator": "0.0.1"
+ , "findit": "0.1.2"
+ , "watch": "0.5.0"
+ , "commander": "0.5.0"
}
, "devDependencies": {
"should": "0.3.2"
@@ -18,4 +22,8 @@
, "scripts": {
"test": "make test"
}
+ , "preferGlobal": true
+ , "bin": {
+ "ION": "./bin/ION"
+ }
}
View
25 plugins/crush.js
@@ -1,6 +1,9 @@
+"use strict";
+
var request = require('request')
, uglify = require('uglify-js')
, jscrush = require('./lib/jscrush')
+ , child = require('./lib/child')
, _ = require('underscore')._;
/**
@@ -84,7 +87,7 @@ module.exports = function setup (options) {
// start processing content
walk();
- }
+ };
};
/**
@@ -120,25 +123,7 @@ exports.jscrush = function jscrushit (content, aggressive, fn) {
*/
exports.closure = function googleclosure (content, aggressive, fn) {
- var fields = {
- output_format: 'text'
- , compilation_level: aggressive ? 'ADVANCED_OPTIMIZATIONS' : 'SIMPLE_OPTIMIZATIONS'
- , js_code: content
- , output_info: 'compiled_code'
- };
-
- // request
- request.post({
- url: 'http://closure-compiler.appspot.com/compile'
- , body: Object.keys(fields).map(function map (key) {
- return encodeURIComponent(key) + '=' + encodeURIComponent(fields[key]);
- }).join('&')
- }, function complete (err, response, data) {
- if (err) return fn(err, content);
- if (response.statusCode !== 200) return fn(new Error('invalid status'), content);
-
- fn(null, data);
- });
+ child.closure('js', content, {}, fn);
};
/**
View
196 plugins/lib/child.js
@@ -0,0 +1,196 @@
+"use strict";
+
+var spawn = require('child_process').spawn
+ , path = require('path')
+ , _ = require('underscore')._;
+
+/**
+ * Configures a new child process spawn that is used to minify files. We use
+ * new child processes for this as these kind of operations are CPU heavy and
+ * would block the Node.js event loop resulting in slower conversion rates. This
+ * setup also allows us to parallel convert code.
+ *
+ * @param {String} type which command should be spawned
+ * @param {Array} flags required configuration flags
+ * @param {Object} configuration default configuration
+ * @returns {Function} a configured spawn
+ * @api public
+ */
+
+function compressor (type, flags, configuration) {
+ /**
+ * Delegrate all the hard core processing to the vendor file.
+ *
+ * @param {String} extension file extenstion
+ * @param {String} content file contents
+ * @param {Object} options options
+ * @param {Function} fn error first callback
+ * @api public
+ */
+
+ function compile (extension, content, options, fn) {
+ // allow optional options argument
+ if (_.isFunction(options)) {
+ fn = options;
+ options = {};
+ }
+
+ var config = _.clone(compile.configuration)
+ , args = flags.slice(0)
+ , buffer = ''
+ , errors = ''
+ , compressor;
+
+ if (compile.configuration.type) {
+ config.type = extension;
+ }
+
+ // generate the --key value options, both the key and the value should added
+ // seperately to the `args` array or the child_process will chocke.
+ Object.keys(config).filter(function filter (option) {
+ return config[option];
+ }).forEach(function format (option) {
+ var bool = _.isBoolean(config[option]);
+
+ if (!bool || config[option]) {
+ args.push('--' + option);
+ if (!bool) args.push(config[option]);
+ }
+ });
+
+ // apply the configuration
+ _.extend(config, options);
+
+ // spawn the shit and set the correct encoding
+ compressor = spawn(type, args);
+ compressor.stdout.setEncoding('utf8');
+ compressor.stderr.setEncoding('utf8');
+
+ /**
+ * Buffer up the results so we can concat them once the compression is
+ * finished.
+ *
+ * @param {Buffer} chunk
+ * @api private
+ */
+
+ compressor.stdout.on('data', function data (chunk) {
+ buffer += chunk;
+ });
+
+ compressor.stderr.on('data', function data (err) {
+ errors += err;
+ });
+
+ /**
+ * The compressor has finished can we now process the data and see if it was
+ * a success.
+ *
+ * @param {Number} code
+ * @api private
+ */
+
+ compressor.on('exit', function exit (code) {
+ // invalid states
+ if (errors.length) return fn(new Error(errors));
+ if (code !== 0) return fn(new Error('process exited with code ' + code));
+ if (!buffer.length) return fn(new Error('no data returned'));
+
+ // correctly processed the data
+ fn(null, buffer);
+ });
+
+ // write out the content that needs to be minified
+ compressor.stdin.end(content);
+ }
+
+ /**
+ * Expose the configuration as global so you can configure the process at once
+ * instead of sending the same shitload of options each time.
+ *
+ * @type {Object}
+ * @api private
+ */
+
+ compile.configuration = configuration;
+ return compile;
+}
+/**
+ * The maxium amount of charactures that we allow one single line before line
+ * breaks will be injected. We have set this maxium to make it easier to debug
+ * minified code so it's easier to narrow down the location of the broken code.
+ *
+ * @type {Number}
+ * @api private
+ */
+
+compressor.maximum = 256;
+
+/**
+ * The paths of the executables and/or arguments.
+ *
+ * @type {Object}
+ * @api private
+ */
+
+compressor.paths = {
+ yui: path.join(__dirname, '../../vendor/yui.jar')
+ , closure: path.join(__dirname, '../../vendor/closure.jar')
+ , uglify: path.join(__dirname, '../../node_modules/.bin/uglifyjs')
+};
+
+/**
+ * Spawn a new YUI compressor process, this compressor allows you to compile
+ * both CSS and JavaScript.
+ *
+ * @api public
+ */
+
+compressor.yui = compressor('java', ['-jar', compressor.paths.yui], {
+ 'charset': 'ascii'
+ , 'type': 'js'
+ , 'line-break': compressor.maximum
+ , 'verbose': false
+});
+
+/**
+ * Spawn a new Google Closure compiler process, this compressor only allows you
+ * to compile JavaScript, but does a much better job then the YUI compressor in
+ * some cases. But this is the slowest compression you could possibly use.
+ *
+ * @api public
+ */
+
+var closure =
+compressor.closure = compressor('java', ['-jar', compressor.paths.closure], {
+ 'charset': 'ascii'
+ , 'compilation_level': 'SIMPLE_OPTIMIZATIONS'
+ , 'language_in': 'ECMASCRIPT5'
+ , 'warning_level': 'QUIET'
+ , 'jscomp_off': 'uselessCode'
+ , 'summary_detail_level': 0
+});
+
+/**
+ * Spawn a new Uglify process, this a next generation compressor complete
+ * writting in JavaScript that parses the AST from your JavaScript files.
+ * Because this is a fairly new compressor there might be some edgecases that
+ * could potentially break your code.
+ *
+ * @api public
+ */
+
+compressor.uglifyjs = compressor(compressor.paths.uglify, [], {
+ 'ascii': true
+ , 'unsafe': true
+ , 'lift-vars': true
+ , 'max-line-length': compressor.maximum
+});
+
+/**
+ * Expose the compressor interface.
+ *
+ * @api public
+ */
+
+module.exports = compressor;
View
15 plugins/lib/jscrush.js
@@ -1,9 +1,10 @@
// adapted from @aivopaas's jscrush
// http://www.iteral.com/jscrush/
-module.exports = function(code) {
- code = code.toString()
+module.exports = function jscrush (code) {
+ code = code.toString();
+ var i, $, s, c, n, t, o, m, S, M, x, j, e, I, str, map;
- $ = "~"
+ $ = "~";
for(
i=123;
@@ -11,7 +12,7 @@ module.exports = function(code) {
i-47||(i=38),i-63||(i=57),i-94||(i=90),i-10&&i-13&&i-34&&i-36&&($+=String.fromCharCode(i))
);
- $ = $.split("")
+ $ = $.split("");
for(s=code.replace(/\/\/.*\n|[\n\r]+[\t ]*/g,''),m=[],S=s.length;;){
for(c=M=0,i=99;!c&&i--;)!~s.indexOf($[i])&&(c=$[i]);
@@ -28,9 +29,9 @@ module.exports = function(code) {
m.push(c+I.replace(/\\/g,'\\\\'));
}
- str = '"' + s.replace(/["\\]/g,'\\$&') + '"'
- map = '"' + m.reverse().join('').replace(/"/g,'\\"') + '".split("")'
+ str = '"' + s.replace(/["\\]/g,'\\$&') + '"';
+ map = '"' + m.reverse().join('').replace(/"/g,'\\"') + '".split("")';
return '!function(D,B,n){for(n in B)D=D.replace(RegExp(B[n][0],"g"),B[n].slice(1));eval(D)}' +
- '(' + str + ',' + map + ')'
+ '(' + str + ',' + map + ')';
};
View
24 plugins/obfuscation.js
@@ -0,0 +1,24 @@
+"use strict";
+
+var ActiveX = require('active-x-obfuscator');
+
+/**
+ * Obfusticate JavaScript, there are firewalls that cannot handle ActiveX in the
+ * script body and drop the file as a result of this. This tool fixes that
+ * issue.
+ *
+ * @param {Object} options
+ * @returns {Function} middleware
+ * @api public
+ */
+
+module.exports = function setup (options) {
+ return function obfuscation (content, next) {
+ var obfused, err;
+
+ process.nextTick(function tick () {
+ try { return next(null, ActiveX(content)); }
+ catch (e) { return next(e, content); }
+ });
+ };
+};
View
BIN  vendor/closure.jar
Binary file not shown
View
BIN  vendor/yui.jar
Binary file not shown
Please sign in to comment.
Something went wrong with that request. Please try again.