UNPKG

7.36 kBJavaScriptView Raw
1/*!
2 * socket.io-node
3 * Copyright(c) 2011 LearnBoost <dev@learnboost.com>
4 * MIT Licensed
5 */
6
7/**
8 * Module dependencies.
9 */
10
11var fs = require('fs')
12 , socket = require('../lib/io')
13 , uglyfyParser = require('../lib/vendor/uglifyjs/lib/parse-js')
14 , uglyfyAst = require('../lib/vendor/uglifyjs/lib/process');
15
16/**
17 * License headers.
18 * @api private
19 */
20
21var template = '/*! Socket.IO.%ext% build:' + socket.version + ', %type%. Copyright(c) 2011 LearnBoost <dev@learnboost.com> MIT Licensed */\n'
22 , development = template.replace('%type%', 'development').replace('%ext%', 'js')
23 , production = template.replace('%type%', 'production').replace('%ext%', 'min.js');
24
25/**
26 * If statements, these allows you to create serveride & client side compatible code
27 * using specially designed `if` statements that remove serverside designed code from
28 * the source files
29 *
30 * @api private
31 */
32var starttagIF = '// if node'
33 , endtagIF = '// end node';
34
35/**
36 * The modules that are required to create a base build of Socket.IO.
37 *
38 * @const
39 * @type {Array}
40 * @api private
41 */
42
43var base = [
44 'io.js'
45 , 'util.js'
46 , 'events.js'
47 , 'json.js'
48 , 'parser.js'
49 , 'transport.js'
50 , 'socket.js'
51 , 'namespace.js'
52 ];
53
54/**
55 * The available transports for Socket.IO. These are mapped as:
56 *
57 * - `key` the name of the transport
58 * - `value` the dependencies for the transport
59 *
60 * @const
61 * @type {Object}
62 * @api public
63 */
64
65 var baseTransports = {
66 'websocket': ['transports/websocket.js']
67 , 'flashsocket': [
68 'transports/websocket.js'
69 , 'transports/flashsocket.js'
70 , 'vendor/web-socket-js/swfobject.js'
71 , 'vendor/web-socket-js/web_socket.js'
72 ]
73 , 'htmlfile': ['transports/xhr.js', 'transports/htmlfile.js']
74 /* FIXME: re-enable me once we have multi-part support
75 , 'xhr-multipart': ['transports/xhr.js', 'transports/xhr-multipart.js'] */
76 , 'xhr-polling': ['transports/xhr.js', 'transports/xhr-polling.js']
77 , 'jsonp-polling': ['transports/xhr.js', 'transports/jsonp-polling.js']
78 };
79
80/**
81 * Builds a custom Socket.IO distribution based on the transports that you need. You
82 * can configure the build to create development build or production build (minified).
83 *
84 * @param {Array} transports The transports that needs to be bundled.
85 * @param {Object} [options] Options to configure the building process.
86 * @param {Function} callback The argument is always the callback, because the options are.. optional:D.
87 * @callback {String|Boolean} err An optional argument, if it exists than an error occurred during the build process.
88 * @callback {String} result The result of the build process.
89 * @api public
90 */
91
92var builder = module.exports = function(){
93 var transports, options, callback, error = null
94 , args = Array.prototype.slice.call(arguments,0)
95 , settings = {
96 minify: true
97 , node: false
98 , custom: []
99 };
100
101 // Fancy pancy argument support
102 // this makes any pattern possible mainly because we require only one of each type
103 args.forEach(function(arg){
104 switch(Object.prototype.toString.call(arg).replace(/\[object\s(\w+)\]/gi , '$1' ).toLowerCase()){
105 case 'array':
106 return transports = arg;
107 case 'object':
108 return options = arg;
109 case 'function':
110 return callback = arg;
111 }
112 });
113
114 // Add defaults
115 options = options || {};
116 transports = transports || Object.keys(baseTransports);
117
118 // Merge the data
119 for(var option in options) settings[option] = options[option];
120
121 // Start creating a dependencies chain with all the required files for the custom Socket.IO bundle.
122 var files = [];
123 base.forEach(function(file){
124 files.push(__dirname + '/../lib/' + file);
125 });
126
127 transports.forEach(function(transport){
128 var dependencies = baseTransports[transport];
129 if (!dependencies) return error = 'Unsupported transport `' + transport + '` supplied as argument.';
130
131 // Add the files to the files list, but only if they are not added before
132 dependencies.forEach(function(file){
133 var path = __dirname + '/../lib/' + file;
134 if (!~files.indexOf(path)) files.push(path);
135 })
136 });
137
138 // check to see if the files tree compilation generated any errors.
139 if (error) return callback(error);
140
141 var results = {};
142 files.forEach(function(file){
143 fs.readFile(file, function(err, content){
144 if (err) error = err;
145 results[file] = content;
146
147 // check if we are done yet, or not.. Just by checking the size of the result
148 // object.
149 if (Object.keys(results).length !== files.length) return;
150
151 // we are done, did we error?
152 if (error) return callback(error);
153
154 // concatinate the file contents in order
155 var code = development
156 , ignore = 0;
157
158 files.forEach(function(file){
159 code += results[file];
160 });
161
162 // check if we need to add custom code
163 if (settings.custom.length){
164 settings.custom.forEach(function(content){
165 code += content;
166 })
167 }
168
169 // Search for conditional code blocks that need to be removed as they where designed for
170 // a server side env. but only if we don't want to make this build node compatible
171 if (!settings.node){
172 code = code.split('\n').filter(function(line){
173 // check if there are tags in here
174 var start = line.indexOf(starttagIF) >= 0
175 , end = line.indexOf(endtagIF) >= 0
176 , ret = ignore;
177
178 // ignore the current line
179 if (start) ignore++,ret = ignore;
180
181 // stop ignoring the next line
182 if (end) ignore--;
183
184 return ret == 0;
185 }).join('\n')
186 }
187
188 // check if we need to process it any further
189 if (settings.minify){
190 var ast = uglyfyParser.parse(code);
191 ast = uglyfyAst.ast_mangle(ast);
192 ast = uglyfyAst.ast_squeeze(ast);
193
194 code = production + uglyfyAst.gen_code(ast);
195 }
196
197 callback(error, code);
198 })
199 })
200};
201
202/**
203 * Builder version is also the current client version
204 * this way we don't have to do another include for the
205 * clients version number and we can just include the builder.
206 *
207 * @type {String}
208 * @api public
209 */
210
211builder.version = socket.version;
212
213/**
214 * A list of all build in transport types.
215 *
216 * @type {Object}
217 * @api public
218 */
219
220builder.transports = baseTransports;
221
222/**
223 * Command line support, this allows us to generate builds without having
224 * to load it as module.
225 */
226
227if (!module.parent){
228 var args = process.argv.slice(2); // the first 2 are `node` and the path to this file, we don't need them
229
230 // build a development build
231 builder(args.length ? args : false, {minify:false},function(err, content){
232 if (err) return console.error(err);
233
234 fs.write(fs.openSync(__dirname + '/../dist/socket.io.js', 'w'), content, 0, 'utf8');
235 console.log('Successfully generated the development build: socket.io.js');
236 });
237
238 // and build a production build
239 builder(args.length ? args : false, function(err, content){
240 if (err) return console.error(err);
241
242 fs.write(fs.openSync(__dirname + '/../dist/socket.io.min.js', 'w'), content, 0, 'utf8');
243 console.log('Successfully generated the production build: socket.io.min.js');
244 });
245}