1 | _ = require 'underscore'
|
2 | logging = require './logging'
|
3 | pathLib = require 'path'
|
4 |
|
5 | String::isHTTP = ->
|
6 | @substring(0, 7) == 'http://' or @substring(0, 8) == 'https://'
|
7 |
|
8 | String::isScript = ->
|
9 | this.substring(this.length-2) is 'js' or this.substring(this.length-6) is 'coffee'
|
10 |
|
11 |
|
12 |
|
13 |
|
14 |
|
15 | coffee = require 'coffee-script'
|
16 | exec = require('child_process').exec
|
17 | flow = require 'flow'
|
18 | fs = require 'fs'
|
19 | restler = require 'restler'
|
20 | readDir = require './readdir'
|
21 | uglify = require 'uglify-js'
|
22 | yaml = require 'pyyaml'
|
23 |
|
24 |
|
25 |
|
26 | defaults =
|
27 | depends: []
|
28 | test_depends: []
|
29 | sources: []
|
30 | minify: false
|
31 | include_depends: false
|
32 | depends_folder: 'requires'
|
33 | test_build_source_file: 'auto-source.js'
|
34 | test_build_test_file: 'auto-test.js'
|
35 | spec_folder: 'specs'
|
36 | source_folder: 'src'
|
37 | build_output: 'output.js'
|
38 |
|
39 |
|
40 |
|
41 |
|
42 |
|
43 |
|
44 |
|
45 |
|
46 |
|
47 |
|
48 | class Package
|
49 |
|
50 | exitCode: 0
|
51 |
|
52 | exit: (code)->
|
53 | @exitCode = code
|
54 |
|
55 | |
56 |
|
57 | complete: ->
|
58 | process.nextTick =>
|
59 | process.exit @exitCode
|
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 | constructor: (opts={}, cmd={})->
|
85 | configs = @loadConfigs opts
|
86 | @opts = []
|
87 | _.extend @opts, defaults, configs, opts, cmd
|
88 |
|
89 |
|
90 | |
91 |
|
92 |
|
93 |
|
94 |
|
95 |
|
96 |
|
97 |
|
98 |
|
99 |
|
100 |
|
101 | loadConfigs: (opts)->
|
102 | path = opts.root+opts.path
|
103 | logging.debug "Parsing jspackle file: #{path}"
|
104 | try
|
105 | JSON.parse fs.readFileSync path
|
106 | catch e
|
107 | @error "ERROR opening config file '#{path}'"
|
108 |
|
109 | |
110 |
|
111 |
|
112 |
|
113 |
|
114 |
|
115 |
|
116 |
|
117 |
|
118 |
|
119 |
|
120 |
|
121 |
|
122 |
|
123 |
|
124 | error: (e, code=1)->
|
125 | logging.warn e
|
126 | @exit code
|
127 |
|
128 | build: ->
|
129 | _this = this
|
130 | sources = []
|
131 |
|
132 |
|
133 |
|
134 |
|
135 | loadSources = ->
|
136 | flow = this
|
137 | sources = []
|
138 | if _this.opts.include_depends
|
139 | sources = sources.concat _this.depends
|
140 | sources = sources.concat _this.sources
|
141 | for index, src of sources
|
142 |
|
143 |
|
144 |
|
145 |
|
146 | do ->
|
147 | i = index
|
148 | registered = flow.MULTI()
|
149 |
|
150 | if src.isHTTP()
|
151 | _this.httpGet src, (script)->
|
152 | sources[i] = script
|
153 | registered()
|
154 |
|
155 | else
|
156 |
|
157 |
|
158 |
|
159 | fs.readFile _this.opts.root+src, (err, script)->
|
160 | return _this.error err if err
|
161 | sources[i] = script
|
162 | registered()
|
163 |
|
164 |
|
165 |
|
166 |
|
167 |
|
168 | processSources = ->
|
169 | outputFile = _this._generateOutputPath()
|
170 | logging.info "Found #{sources.length} source file"
|
171 | logging.info "Writing processed sources to: '#{outputFile}'"
|
172 | output = sources.join "\n"
|
173 | if _this.opts.minify
|
174 | output = _this.minify output
|
175 | fs.writeFile outputFile, output, this
|
176 |
|
177 |
|
178 |
|
179 | finish = (err)->
|
180 | _this.exit if err then 1 else 0
|
181 |
|
182 | complete = ->
|
183 | _this.complete()
|
184 |
|
185 | flow.exec loadSources, processSources, finish, complete
|
186 |
|
187 | |
188 |
|
189 |
|
190 |
|
191 |
|
192 |
|
193 |
|
194 |
|
195 |
|
196 |
|
197 | test: ->
|
198 | cancel = false
|
199 | _this = this
|
200 |
|
201 |
|
202 | createFile = ->
|
203 | _this._createJsTestDriverFile this
|
204 |
|
205 |
|
206 | execute = (err)->
|
207 | cancel = err
|
208 | if cancel
|
209 | _this.exit 1
|
210 | return this()
|
211 | _this._executeTests this
|
212 |
|
213 |
|
214 | clean = (err)->
|
215 | flow = this
|
216 | cancel = cancel or err
|
217 | _this.clean flow
|
218 | _this.exit if cancel then (parseInt(cancel, 10) or 1) else 0
|
219 |
|
220 | complete = ->
|
221 | _this.complete()
|
222 |
|
223 | flow.exec createFile, execute, clean, complete
|
224 |
|
225 | |
226 |
|
227 |
|
228 |
|
229 |
|
230 |
|
231 |
|
232 |
|
233 |
|
234 |
|
235 | clean: (flow)->
|
236 | logging.info "Cleaning up after jspackle run..."
|
237 | fs.unlink "#{@opts.root}JsTestDriver.conf", flow.MULTI()
|
238 | fs.unlink @opts.test_build_source_file, flow.MULTI()
|
239 | fs.unlink @opts.test_build_test_file, flow.MULTI()
|
240 |
|
241 | |
242 |
|
243 |
|
244 |
|
245 |
|
246 |
|
247 |
|
248 |
|
249 |
|
250 |
|
251 | minify: (source)->
|
252 | logging.info "Minifying JavaScript source..."
|
253 | tokens = uglify.parser.parse source
|
254 | tokens = uglify.uglify.ast_mangle tokens
|
255 | tokens = uglify.uglify.ast_squeeze tokens
|
256 | uglify.uglify.gen_code tokens
|
257 |
|
258 | |
259 |
|
260 |
|
261 |
|
262 |
|
263 |
|
264 |
|
265 |
|
266 |
|
267 |
|
268 |
|
269 |
|
270 |
|
271 | httpGet: (url, callback)->
|
272 | resp = restler.get url
|
273 | resp.on 'complete', callback
|
274 | resp.on 'error', => @error "ERROR: Cannot get #{url}"
|
275 |
|
276 |
|
277 |
|
278 | _executeTests: (callback)->
|
279 | logging.debug "Executing tests: #{@testCmd}"
|
280 | exec @testCmd, (err, stdout, stderr)->
|
281 | msg = """
|
282 |
|
283 | Output:
|
284 |
|
285 | #{stdout}
|
286 | """
|
287 | if err
|
288 | logging.warn msg
|
289 | code = err.code
|
290 | else
|
291 | logging.info msg
|
292 | code = 0
|
293 | callback code
|
294 |
|
295 | _createJsTestDriverFile: (callback)->
|
296 | configs =
|
297 | server: @opts.test_server
|
298 | timeout: @opts.test_timeout
|
299 |
|
300 | configs.load = @depends.concat(@testDepends).concat(@sources)
|
301 | configs.test = @tests
|
302 |
|
303 | logging.info "Executing #{configs.test.length} specs"
|
304 |
|
305 | path = "#{@opts.root}JsTestDriver.conf"
|
306 | logging.debug "Dumping configs to: #{path}"
|
307 | yaml.dump configs, path, callback
|
308 |
|
309 | _coffeeCompile: (sources, path)->
|
310 | logging.info "Compiling coffee-script to '#{path}'"
|
311 | compiled = []
|
312 | paths = []
|
313 | for src in sources
|
314 | if src.isHTTP()
|
315 | paths.push src
|
316 | else
|
317 | try
|
318 | compiled.push coffee.compile fs.readFileSync(src).toString()
|
319 | catch e
|
320 | logging.critical "Cannot pase #{src} as valid CoffeeScript!"
|
321 | logging.critical e
|
322 | throw e
|
323 | fs.writeFileSync path, compiled.join "\n"
|
324 | paths.push path
|
325 | return paths
|
326 |
|
327 | _findTests: ->
|
328 | found = readDir @opts.root+@opts.spec_folder
|
329 | tests = []
|
330 | for file in found.files
|
331 | if file.isScript()
|
332 | logging.debug "Discovered test: '#{file}'"
|
333 | tests.push(file.replace(@opts.root+@opts.spec_folder+'/', ''))
|
334 | tests
|
335 |
|
336 | |
337 |
|
338 |
|
339 | _generateOutputPath: ->
|
340 | filePath = pathLib.join @opts.root, @opts.build_output
|
341 |
|
342 | for variable in ['name', 'version']
|
343 | re = new RegExp "{{#{variable}}}", 'g'
|
344 | filePath = filePath.replace re, @opts[variable]
|
345 | return filePath
|
346 |
|
347 | _process: (option, folder, compile=false)->
|
348 | root = folder+'/'
|
349 | sources = []
|
350 | if typeof option == 'string'
|
351 | paths = @opts[option]
|
352 | else
|
353 | paths = option
|
354 |
|
355 | for path in paths
|
356 | if path.isHTTP()
|
357 | sources.push path
|
358 | else
|
359 | sources.push root+path
|
360 | return sources if not (compile and @opts.coffee)
|
361 | @_coffeeCompile sources, compile
|
362 |
|
363 |
|
364 |
|
365 |
|
366 |
|
367 |
|
368 |
|
369 |
|
370 |
|
371 | Package.prototype.__defineGetter__ 'sources', ->
|
372 | @_process 'sources', @opts.source_folder, @opts.test_build_source_file
|
373 |
|
374 | Package.prototype.__defineGetter__ 'depends', ->
|
375 | @_process 'depends', @opts.depends_folder
|
376 |
|
377 | Package.prototype.__defineGetter__ 'testDepends', ->
|
378 | @_process 'test_depends', @opts.depends_folder
|
379 |
|
380 | Package.prototype.__defineGetter__ 'tests', ->
|
381 | @_process @_findTests(), @opts.spec_folder, @opts.test_build_test_file
|
382 |
|
383 | Package.prototype.__defineGetter__ 'testCmd', ->
|
384 | "js-test-driver --config ./JsTestDriver.conf --tests all --reset #{@opts.test_args}"
|
385 |
|
386 | module.exports = Package
|