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