3 * Copyright(c) 2011 LearnBoost <dev@learnboost.com>
11 var fs = require('fs')
12 , socket = require('../lib/io')
13 , uglify = require('uglify-js')
14 , activeXObfuscator = require('active-x-obfuscator');
22 var template = '/*! Socket.IO.%ext% build:' + socket.version + ', %type%. Copyright(c) 2011 LearnBoost <dev@learnboost.com> MIT Licensed */\n'
23 , development = template.replace('%type%', 'development').replace('%ext%', 'js')
24 , production = template.replace('%type%', 'production').replace('%ext%', 'min.js');
27 * If statements, these allows you to create serveride & client side compatible
28 * code using specially designed `if` statements that remove serverside
29 * designed code from the source files
34 var starttagIF = '// if node'
35 , endtagIF = '// end node';
38 * The modules that are required to create a base build of Socket.IO.
57 * The available transports for Socket.IO. These are mapped as:
59 * - `key` the name of the transport
60 * - `value` the dependencies for the transport
67 var baseTransports = {
68 'websocket': ['transports/websocket.js']
70 'transports/websocket.js'
71 , 'transports/flashsocket.js'
72 , 'vendor/web-socket-js/swfobject.js'
73 , 'vendor/web-socket-js/web_socket.js'
75 , 'htmlfile': ['transports/xhr.js', 'transports/htmlfile.js']
76 /* FIXME: re-enable me once we have multi-part support
77 , 'xhr-multipart': ['transports/xhr.js', 'transports/xhr-multipart.js'] */
78 , 'xhr-polling': ['transports/xhr.js', 'transports/xhr-polling.js']
81 , 'transports/xhr-polling.js'
82 , 'transports/jsonp-polling.js'
87 * Wrappers for client-side usage.
88 * This enables usage in top-level browser window, client-side CommonJS systems and AMD loaders.
89 * If doing a node build for server-side client, this wrapper is NOT included.
92 var wrapperPre = "\nvar io = ('undefined' === typeof module ? {} : module.exports);\n(function() {\n";
94 var wrapperPost = "\nif (typeof define === \"function\" && define.amd) {" +
95 "\n define([], function () { return io; });" +
100 * Builds a custom Socket.IO distribution based on the transports that you
101 * need. You can configure the build to create development build or production
104 * @param {Array} transports The transports that needs to be bundled.
105 * @param {Object} [options] Options to configure the building process.
106 * @param {Function} callback Last argument should always be the callback
107 * @callback {String|Boolean} err An optional argument, if it exists than an error
108 * occurred during the build process.
109 * @callback {String} result The result of the build process.
113 var builder = module.exports = function () {
114 var transports, options, callback, error = null
115 , args = Array.prototype.slice.call(arguments, 0)
122 // Fancy pancy argument support this makes any pattern possible mainly
123 // because we require only one of each type
124 args.forEach(function (arg) {
125 var type = Object.prototype.toString.call(arg)
126 .replace(/\[object\s(\w+)\]/gi , '$1' ).toLowerCase();
130 return transports = arg;
132 return options = arg;
134 return callback = arg;
139 options = options || {};
140 transports = transports || Object.keys(baseTransports);
143 for(var option in options) {
144 settings[option] = options[option];
147 // Start creating a dependencies chain with all the required files for the
148 // custom Socket.IO bundle.
150 base.forEach(function (file) {
151 files.push(__dirname + '/../lib/' + file);
154 transports.forEach(function (transport) {
155 var dependencies = baseTransports[transport];
157 error = 'Unsupported transport `' + transport + '` supplied as argument.';
161 // Add the files to the files list, but only if they are not added before
162 dependencies.forEach(function (file) {
163 var path = __dirname + '/../lib/' + file;
164 if (!~files.indexOf(path)) files.push(path);
168 // check to see if the files tree compilation generated any errors.
169 if (error) return callback(error);
172 files.forEach(function (file) {
173 fs.readFile(file, function (err, content) {
174 if (err) error = err;
175 results[file] = content;
177 // check if we are done yet, or not.. Just by checking the size of the result
179 if (Object.keys(results).length !== files.length) return;
181 // we are done, did we error?
182 if (error) return callback(error);
184 // start with the license header
185 var code = development
188 // pre-wrapper for non-server-side builds
189 if (!settings.node) code += wrapperPre;
191 // concatenate the file contents in order
192 files.forEach(function (file) {
193 code += results[file];
196 // check if we need to add custom code
197 if (settings.custom.length) {
198 settings.custom.forEach(function (content) {
203 // post-wrapper for non-server-side builds
204 if (!settings.node) {
208 code = activeXObfuscator(code);
210 // Search for conditional code blocks that need to be removed as they
211 // where designed for a server side env. but only if we don't want to
212 // make this build node compatible.
213 if (!settings.node) {
214 code = code.split('\n').filter(function (line) {
215 // check if there are tags in here
216 var start = line.indexOf(starttagIF) >= 0
217 , end = line.indexOf(endtagIF) >= 0
220 // ignore the current line
226 // stop ignoring the next line
235 // check if we need to process it any further
236 if (settings.minify) {
237 var ast = uglify.parser.parse(code);
238 ast = uglify.uglify.ast_mangle(ast);
239 ast = uglify.uglify.ast_squeeze(ast);
241 code = production + uglify.uglify.gen_code(ast, { ascii_only: true });
244 callback(error, code);
250 * Builder version is also the current client version
251 * this way we don't have to do another include for the
252 * clients version number and we can just include the builder.
258 builder.version = socket.version;
261 * A list of all build in transport types.
267 builder.transports = baseTransports;
270 * Command line support, this allows us to generate builds without having
271 * to load it as module.
275 // the first 2 are `node` and the path to this file, we don't need them
276 var args = process.argv.slice(2);
278 // build a development build
279 builder(args.length ? args : false, { minify:false }, function (err, content) {
280 if (err) return console.error(err);
283 fs.openSync(__dirname + '/../dist/socket.io.js', 'w')
288 console.log('Successfully generated the development build: socket.io.js');
291 // and build a production build
292 builder(args.length ? args : false, function (err, content) {
293 if (err) return console.error(err);
296 fs.openSync(__dirname + '/../dist/socket.io.min.js', 'w')
301 console.log('Successfully generated the production build: socket.io.min.js');