1 |
|
2 |
|
3 | console.error = ->
|
4 |
|
5 |
|
6 |
|
7 |
|
8 | prompt = require 'prompt'
|
9 | program = require('commander')
|
10 | colors = require('colors')
|
11 | fs = require('fs-extra')
|
12 | walk = require('walk')
|
13 | path = require('path')
|
14 | watcher = require('node-watch')
|
15 | Generate = require('./lib/Generate')
|
16 | CoffeeScript = require('coffee-script')
|
17 | uglify = require('uglify-js')
|
18 | restify = require('restify')
|
19 | CookieParser = require('restify-cookies')
|
20 | mkdirp = require('mkdirp')
|
21 | openurl = require('openurl')
|
22 | reload = require('reload')
|
23 |
|
24 |
|
25 |
|
26 |
|
27 | prompt.message = 'MagiX'
|
28 | reloadServer = undefined
|
29 |
|
30 |
|
31 |
|
32 |
|
33 | Array::move = (old_index, new_index) ->
|
34 | if new_index >= @length
|
35 | k = new_index - (@length)
|
36 | while k-- + 1
|
37 | @push undefined
|
38 | @splice new_index, 0, @splice(old_index, 1)[0]
|
39 | this
|
40 |
|
41 | reorderFiles = (files)->
|
42 | new_files = JSON.parse(JSON.stringify(files));
|
43 | no_match = []
|
44 | for item of files
|
45 | index = -1
|
46 | for item2 of new_files
|
47 | if new_files[item2].name is files[item].extends
|
48 | index = item2
|
49 | if index > -1
|
50 | if index > item
|
51 | new_files = new_files.move(index, item)
|
52 | else
|
53 | no_match.push files[item].id
|
54 |
|
55 | for id in no_match
|
56 | if id isnt undefined
|
57 | for item of new_files
|
58 | if new_files[item].id is id
|
59 | new_files.move(item, 0)
|
60 | return new_files
|
61 |
|
62 | capitalizeFirstLetter = (string) ->
|
63 | string.charAt(0).toUpperCase() + string.slice(1)
|
64 |
|
65 | getDirectories = (srcpath) ->
|
66 | fs.readdirSync(srcpath).filter (file) ->
|
67 | fs.statSync(path.join(srcpath, file)).isDirectory()
|
68 |
|
69 |
|
70 | indent = (str, numOfIndents, opt_spacesPerIndent) ->
|
71 | str = str.replace(/^(?=.)/gm, new Array(numOfIndents + 1).join('\t'));
|
72 | numOfIndents = new Array(opt_spacesPerIndent + 1 or 0).join(' ')
|
73 |
|
74 | if opt_spacesPerIndent then str.replace(/^\t+/g, ((tabs) ->
|
75 | tabs.replace /./g, numOfIndents
|
76 | )) else str
|
77 |
|
78 | deleteFolderRecursive = (path) ->
|
79 | if fs.existsSync(path)
|
80 | fs.readdirSync(path).forEach (file, index) ->
|
81 | curPath = path + '/' + file
|
82 | if fs.lstatSync(curPath).isDirectory()
|
83 |
|
84 | deleteFolderRecursive curPath
|
85 | else
|
86 |
|
87 | fs.unlinkSync curPath
|
88 | return
|
89 | fs.rmdirSync path
|
90 | return
|
91 |
|
92 | makeID = ->
|
93 | text = ''
|
94 | possible = 'abcdefghijklmnopqrstuvwxyz0123456789'
|
95 | i = 0
|
96 | while i < 8
|
97 | text += possible.charAt(Math.floor(Math.random() * possible.length))
|
98 | i++
|
99 | text
|
100 |
|
101 |
|
102 |
|
103 |
|
104 | about = ->
|
105 | console.log '\n'
|
106 | console.log ' d8bY88b d88P '
|
107 | console.log ' Y8P Y88b d88P '
|
108 | console.log ' Y88o88P '
|
109 | console.log '88888b.d88b. 8888b. .d88b. 888 Y888P '
|
110 | console.log '888 "888 "88b "88bd88P"88b888 d888b '
|
111 | console.log '888 888 888.d888888888 888888 d88888b '
|
112 | console.log '888 888 888888 888Y88b 888888 d88P Y88b '
|
113 | console.log '888 888 888"Y888888 "Y88888888d88P Y88b '
|
114 | console.log ' 888 '
|
115 | console.log ' Y8b d88P '
|
116 | console.log ' "Y88P" '
|
117 | console.log 'MagiX | magixjs.com'.green
|
118 | console.log 'Beyond magical.'.green
|
119 | console.log 'Created by Etienne Pinchon (@etiennepinchon)'.green
|
120 | console.log 'Copyright ©2016'.green
|
121 | console.log '\n'
|
122 | console.log 'Usage:'.green
|
123 | console.log '* create [name] | Create a new project.'
|
124 | console.log '* launch [dir] [port] | Create a new project.'
|
125 | console.log '* build [dir] [env] | Build a project.'
|
126 | console.log '* clean [dir] | Clear the build of a project.'
|
127 | console.log '* watch [dir] | Observe change on a project and compile on the fly.'
|
128 | console.log '\n'
|
129 | return
|
130 |
|
131 | create = (name) ->
|
132 |
|
133 | if not name or not /^[a-zA-Z0-9\_\s\-]{1,100}$/.test name
|
134 | console.log 'MagiX: [ERR] Name must be only letters, numbers, underscores, spaces or dashes.'.red
|
135 | return
|
136 |
|
137 | dir_project = './' + name
|
138 | if not fs.existsSync(dir_project)
|
139 | fs.mkdirSync(dir_project)
|
140 |
|
141 |
|
142 | appJS = Generate.JS()
|
143 | appCS = Generate.CS()
|
144 |
|
145 | done = ->
|
146 | process.chdir(dir_project)
|
147 | console.log 'MagiX: Project created successfully.'.green
|
148 |
|
149 | createJSON = ->
|
150 | packageFile =
|
151 | name: name
|
152 | version: '0.1.0'
|
153 | description: ''
|
154 | tags: ''
|
155 | created_at: new Date
|
156 |
|
157 | fs.writeFile dir_project + '/package.json', JSON.stringify(packageFile, null, 2), (err) ->
|
158 | return console.log(err) if err
|
159 | done()
|
160 |
|
161 | createIndexJS = ->
|
162 | indexJS = Generate.indexJS()
|
163 | fs.writeFile dir_project + '/build/index.js', indexJS, (err) ->
|
164 | return console.log(err) if err
|
165 | createCatalog()
|
166 |
|
167 | createCatalog = ->
|
168 | catalog = Generate.catalog()
|
169 | fs.writeFile dir_project + '/build/catalog.js', catalog, (err) ->
|
170 | return console.log(err) if err
|
171 | createJSON()
|
172 |
|
173 |
|
174 | dirBuild = dir_project + '/build'
|
175 | dirSources = dir_project + '/documents'
|
176 |
|
177 | fs.mkdirSync dir_project if not fs.existsSync(dir_project)
|
178 | fs.mkdirSync dirBuild if not fs.existsSync(dirBuild)
|
179 | fs.mkdirSync dirSources if not fs.existsSync(dirSources)
|
180 |
|
181 | htmlContent = Generate.HTML(name, '', '', no)
|
182 |
|
183 | appNameCS = '/App.coffee'
|
184 | appJS = Generate.appRunJS indent(appJS, 1)
|
185 |
|
186 |
|
187 | fs.writeFile dir_project + '/index.html', htmlContent, (err) ->
|
188 | return console.log(err) if err
|
189 |
|
190 |
|
191 | fs.writeFile dirBuild + '/App.js', appJS, (err) ->
|
192 | return console.log(err) if err
|
193 |
|
194 |
|
195 | fs.writeFile dirSources + appNameCS, appCS, (err) ->
|
196 | return console.log(err) if err
|
197 |
|
198 | createIndexJS()
|
199 | return
|
200 | return
|
201 | return
|
202 | return
|
203 |
|
204 | launch = (dir, server_port, env)->
|
205 |
|
206 |
|
207 |
|
208 |
|
209 | prod = ['production', 'prod', 'p']
|
210 | isProd = no
|
211 |
|
212 | if not env
|
213 | if prod.indexOf(dir) > -1
|
214 | isProd = yes
|
215 | dir = undefined
|
216 | else if prod.indexOf(server_port) > -1
|
217 | isProd = yes
|
218 | server_port = undefined
|
219 | else
|
220 | isProd = yes if prod.indexOf(env) > -1
|
221 |
|
222 |
|
223 |
|
224 | if not dir
|
225 | dir = undefined
|
226 |
|
227 | i = parseInt(dir)
|
228 | if Number.isInteger(i) and i > 0
|
229 | server_port = dir
|
230 | dir = undefined
|
231 |
|
232 | dir = process.cwd() if not dir
|
233 | if not fs.existsSync(dir)
|
234 | console.log 'MagiX: [ERR] Given folder does not exist.'.red
|
235 | return
|
236 |
|
237 |
|
238 | if dir.endsWith('/')
|
239 | dir = dir.slice(0, -1)
|
240 |
|
241 | server_port = 9000 if not server_port
|
242 |
|
243 |
|
244 |
|
245 |
|
246 | server = restify.createServer
|
247 | name: 'server'
|
248 | version: '1.0.0'
|
249 |
|
250 | server.use restify.acceptParser(server.acceptable)
|
251 | server.use restify.queryParser()
|
252 | server.use restify.bodyParser()
|
253 | server.use CookieParser.parse
|
254 | server.use restify.gzipResponse()
|
255 |
|
256 | server.get /^\/build\/?.*/, restify.serveStatic directory: dir
|
257 | server.get /^\/documents\/?.*/, restify.serveStatic directory: dir
|
258 |
|
259 | server.get /\/?/, (req, res, next) ->
|
260 | res.writeHead 200
|
261 | fs.createReadStream(dir + '/index.html').pipe res
|
262 | next()
|
263 |
|
264 |
|
265 | if not isProd
|
266 | reloadServer = reload(server, server, no)
|
267 |
|
268 | server.__port = server_port
|
269 | server.start = (message)->
|
270 | server.listen server.__port, 'localhost', ->
|
271 | if message
|
272 | url = server.url.replace('127.0.0.1', 'localhost')
|
273 |
|
274 | if not isProd
|
275 | console.log(('MagiX: Project launched! Running! Address ' + url).green)
|
276 | openurl.open(url)
|
277 | else
|
278 | console.log(('MagiX: Project launched in Production mode! Running! Address ' + url).green)
|
279 | return
|
280 |
|
281 | server.start(yes)
|
282 | if not isProd
|
283 | if fs.existsSync(dir + '/documents')
|
284 | watch(dir, server)
|
285 |
|
286 | build = (dir, env) ->
|
287 | prod = ['production', 'prod', 'p']
|
288 | isProd = no
|
289 |
|
290 | if not env
|
291 | if prod.indexOf(dir) > -1
|
292 | isProd = yes
|
293 | dir = undefined
|
294 | else
|
295 | isProd = yes if prod.indexOf(env) > -1
|
296 |
|
297 | dir = '.' if not dir
|
298 | return if not dirCheck dir
|
299 |
|
300 | files = []
|
301 | startA = +new Date()
|
302 |
|
303 |
|
304 | walker = walk.walk(dir + '/documents', followLinks: false)
|
305 | walker.on 'file', (root, stat, next) ->
|
306 | filename = stat.name
|
307 | if filename.endsWith('.coffee')
|
308 | files.push root + '/' + filename
|
309 | name = filename.split('/')
|
310 | name = name[name.length-1]
|
311 | name = name.replace('.coffee', '')
|
312 |
|
313 | compileFile name, root, next
|
314 |
|
315 |
|
316 | else if filename and (filename.endsWith('.js') or filename.endsWith('.css'))
|
317 |
|
318 | file = root + '/' + filename
|
319 | file_build = file.replace('documents', 'build')
|
320 | files.push file
|
321 |
|
322 | console.log ('MagiX: Copy ' + filename).magenta
|
323 | fs.copy file, file_build, (err) ->
|
324 | return console.error(err) if err
|
325 | next()
|
326 | else
|
327 | next()
|
328 | return
|
329 |
|
330 | walker.on 'end', ->
|
331 | buildAutoImport(dir)
|
332 |
|
333 | endA = +new Date()
|
334 | console.log "MagiX: Done: #{files.length} files built in #{(endA-startA)} ms.".green
|
335 |
|
336 |
|
337 | buildProduction(dir) if isProd
|
338 | return
|
339 |
|
340 | watch = (dir, server) ->
|
341 | dir = '.' if not dir
|
342 | return if not dirCheck dir
|
343 | console.log 'MagiX: Now observing changes in your project..'.green
|
344 |
|
345 | watcher dir + '/documents', (filename) ->
|
346 |
|
347 |
|
348 | if reloadServer
|
349 | reloadServer.reload()
|
350 |
|
351 |
|
352 | if filename and filename.endsWith('.coffee')
|
353 | name = filename.split('/')
|
354 | name = name[name.length-1]
|
355 | name = name.replace('.coffee', '')
|
356 |
|
357 | path = filename.split('/')
|
358 | path.pop()
|
359 | path = path.join('/')
|
360 |
|
361 | if fs.existsSync(filename)
|
362 | compileFile name, path, undefined, yes
|
363 | buildAutoImport(dir)
|
364 |
|
365 |
|
366 | else if filename and (filename.endsWith('.js') or filename.endsWith('.css'))
|
367 | name = filename.split('/')
|
368 | name = name[name.length-1]
|
369 |
|
370 | file_build = filename.replace('documents', 'build')
|
371 |
|
372 | if fs.existsSync(filename)
|
373 | console.log ('MagiX: Updating ' + name).magenta
|
374 | fs.copy filename, file_build, (err) ->
|
375 | return console.error(err) if err
|
376 | else if fs.existsSync(file_build)
|
377 | name = file_build.split('/')
|
378 | name = name[name.length-1]
|
379 | console.log ('MagiX: Removing ' + name).magenta
|
380 | fs.unlink file_build, (err) ->
|
381 | console.log err if err
|
382 |
|
383 |
|
384 |
|
385 | if server and server.close
|
386 | server.close()
|
387 | server.start(no)
|
388 |
|
389 | return
|
390 |
|
391 |
|
392 | clean = (dir) ->
|
393 | dir = '.' if not dir
|
394 | return if not dirCheck dir
|
395 |
|
396 | console.log 'MagiX: Cleaning..'.magenta
|
397 |
|
398 | pathBuild = dir + '/build'
|
399 | deleteFolderRecursive pathBuild
|
400 | build(dir)
|
401 | console.log "MagiX: Done: build cleaned.".green
|
402 | return
|
403 |
|
404 |
|
405 |
|
406 |
|
407 |
|
408 |
|
409 | dirCheck = (dir)->
|
410 | dir = '.' if not dir
|
411 | dir_build_check = no
|
412 | dir_documents_check = no
|
413 |
|
414 | if not fs.existsSync(dir)
|
415 | console.log 'MagiX: [ERR] Given folder does not exist.'.red
|
416 | return
|
417 |
|
418 | directories = getDirectories(dir)
|
419 | for directory in directories
|
420 | if directory is 'build'
|
421 | dir_build_check = yes
|
422 | else if directory is 'documents'
|
423 | dir_documents_check = yes
|
424 |
|
425 | if not dir_build_check or not dir_documents_check
|
426 | if not dir_build_check and not dir_documents_check
|
427 | console.log 'MagiX: [ERR] Cannot find the "documents" directory.'.red
|
428 | console.log 'MagiX: [HELP] Are you sure you are in the right folder? (cd magix-yourProjectName ;) ).'.magenta
|
429 | else
|
430 | if not dir_build_check
|
431 | dirBuild = __dirname + '/build'
|
432 | fs.mkdirSync dirBuild if not fs.existsSync(dirBuild)
|
433 | return yes
|
434 | if not dir_documents_check
|
435 | console.log 'MagiX: [ERR] Cannot find the "documents" directory.'.red
|
436 | return no
|
437 |
|
438 | return yes
|
439 |
|
440 | compileFile = (name, dir, next, notification)->
|
441 | console.log ('MagiX: Processing ' + name + ' ..').magenta
|
442 |
|
443 | fs.readFile dir + '/' + name + '.coffee', 'utf8', (err, data) ->
|
444 | return console.log(err) if err
|
445 |
|
446 | contentCopy = data
|
447 | file = {}
|
448 |
|
449 | if name isnt 'App'
|
450 | if /(Extends )\w+[ ]*\n/.test(contentCopy)
|
451 | contentCopy = contentCopy.replace /(Extends )\w+[ ]*\n/, (match) ->
|
452 | file.type = match.replace('Extends ', '')
|
453 | file.type = file.type.replace(/[ ]*\n/, '')
|
454 | return ''
|
455 |
|
456 | if /(Kind )([-a-zA-Z0-9])*\n/.test(contentCopy)
|
457 | contentCopy = params.contentCopy.replace /(Kind )([-a-zA-Z0-9])*\n/, (match) ->
|
458 | file.kind = match.replace('Kind ', '')
|
459 | file.kind = file.kind.replace(/\n/, '')
|
460 | return ''
|
461 |
|
462 | if /(Element )([-a-zA-Z0-9])*\n/.test(contentCopy)
|
463 | contentCopy = contentCopy.replace /(Element )([-a-zA-Z0-9])*\n/, (match) ->
|
464 | file.element = match.replace('Element ', '')
|
465 | file.element = file.element.replace(/\n/, '')
|
466 | return ''
|
467 |
|
468 |
|
469 | addClass = true
|
470 |
|
471 |
|
472 | contentCopy = indent(contentCopy, 2)
|
473 | classes = ['Page', 'View', 'Text', 'Button', 'Link', 'CheckBox', 'Dropdown', 'RadioButton', 'Image', 'List', 'ListItem', 'TextInput', 'SpeechRecognition', 'Say', 'FileInput', 'Player', 'Slider', 'ProgressBar', 'Canvas', 'WebView']
|
474 |
|
475 |
|
476 | if classes.indexOf(file.type) > -1
|
477 |
|
478 | classFile = 'class ' + name + ' extends ' + file.type + '\n\t'
|
479 | classFile += '_kind : "' + file.kind + '"\n\t' if file.kind
|
480 | classFile += '_elementType : "'+ file.element + '"\n\t' if file.element
|
481 | classFile += 'constructor: (options) ->\n\t\t\
|
482 | super\n\
|
483 | ' + contentCopy + '\n\t\t\
|
484 | if not @_didAppear and @parent and @didAppear\n\t\t\t\
|
485 | @_didAppear = true\n\t\t\t\
|
486 | @didAppear(@__options)'
|
487 |
|
488 | else
|
489 |
|
490 | if file.type is 'None'
|
491 | classFile = "class #{name}\n\t"
|
492 | classFile += '_kind : "' + file.kind + '"\n\t' if file.kind
|
493 | classFile += '_elementType : "'+ file.element + '"\n\t' if file.element
|
494 | classFile += "constructor: (options) ->\n\t\t\
|
495 | super\n\
|
496 | #{contentCopy}"
|
497 | else
|
498 |
|
499 | classFile = 'class ' + name + ' extends ' + file.type + '\n\t'
|
500 | classFile += '_kind : "' + file.kind + '"\n\t' if file.kind
|
501 | classFile += '_elementType : "'+ file.element + '"\n\t' if file.element
|
502 | classFile += 'constructor: (options) ->\n\t\t\
|
503 | super\n\
|
504 | ' + contentCopy + '\n\t\t\
|
505 | if not @_didAppear and @parent and @didAppear\n\t\t\t\
|
506 | @_didAppear = true\n\t\t\t\
|
507 | @didAppear(@__options)'
|
508 | else if /(class)\s+\w+\s+(extends)\s+\w+/.test(contentCopy)
|
509 | file.element = contentCopy.match(/(class)\s+\w+\s+(extends)\s+\w+/)[0].replace(/(class)\s+\w+\s+(extends)\s+/, '')
|
510 | classFile = contentCopy
|
511 | else
|
512 | classFile = contentCopy
|
513 | else
|
514 | classFile = contentCopy
|
515 |
|
516 |
|
517 | converted = null
|
518 | try
|
519 | converted = CoffeeScript.compile(classFile, 'bare': true)
|
520 | catch err
|
521 | convert_error = err
|
522 |
|
523 |
|
524 | dirBuild = dir.replace('documents', 'build')
|
525 |
|
526 | nextStep = ->
|
527 | filePathBuild = dirBuild + '/' + name + '.js'
|
528 |
|
529 | if converted
|
530 | if name is 'App'
|
531 | convertedFinal = Generate.appRunJS indent(converted, 1)
|
532 | else
|
533 | convertedFinal = converted
|
534 |
|
535 | fs.writeFile filePathBuild, convertedFinal, (err) ->
|
536 |
|
537 | console.log 'MagiX: ↳ success'.green
|
538 | next() if next
|
539 | else
|
540 | lines_info = String(convert_error).replace('[stdin]:', '').split(':')
|
541 | error = capitalizeFirstLetter "#{convert_error.message} at line #{lines_info[0]} column #{lines_info[1]}"
|
542 | console.log "MagiX: ↳ #{error}".red
|
543 |
|
544 |
|
545 | if notification
|
546 | notifier = require('node-notifier')
|
547 | path = require('path')
|
548 | notifier.notify {
|
549 | title: 'MagiX | Error on ' + name
|
550 | message: error
|
551 | icon: path.join(__dirname, 'images/icon.png')
|
552 | sound: no
|
553 | wait: no
|
554 | }, (err, response) ->
|
555 |
|
556 | return
|
557 |
|
558 | if not fs.existsSync(dirBuild)
|
559 | mkdirp dirBuild, (err) ->
|
560 | if err
|
561 | console.error err
|
562 | else
|
563 | nextStep()
|
564 | else
|
565 | nextStep()
|
566 |
|
567 | buildAutoImport = (dir)->
|
568 | autoImport = []
|
569 | catalog = []
|
570 |
|
571 |
|
572 | documents = []
|
573 |
|
574 |
|
575 |
|
576 |
|
577 | walker = walk.walk(dir + '/documents', followLinks: false)
|
578 | walker.on 'file', (root, stat, next) ->
|
579 | if stat.name.endsWith('.coffee')
|
580 |
|
581 | doc = {}
|
582 | doc.name = stat.name.split('/')
|
583 | doc.name = doc.name[doc.name.length-1]
|
584 | doc.name = doc.name.replace('.coffee', '')
|
585 |
|
586 | fs.readFile root + '/' + doc.name + '.coffee', 'utf8', (err, data) ->
|
587 | return console.log(err) if err
|
588 |
|
589 | if data[0] isnt '!'
|
590 | root = root.substring(root.indexOf("/documents") + 1)
|
591 | path = '/' + root.replace('documents', 'build') + '/' + stat.name.replace('coffee', 'js')
|
592 | if path isnt '/build/App.js'
|
593 |
|
594 | if /(Element )([-a-zA-Z0-9])*\n/.test(data)
|
595 | doc.extends = data.match(/(Element )([-a-zA-Z0-9])*\n/)[0].replace('Element ', '')
|
596 | doc.extends = doc.extends.replace(/\n/, '')
|
597 | else if /(class)\s+\w+\s+(extends)\s+\w+/.test(data)
|
598 | doc.extends = data.match(/(class)\s+\w+\s+(extends)\s+\w+/)[0].replace(/(class)\s+\w+\s+(extends)\s+/, '')
|
599 |
|
600 | else
|
601 | doc.extends = null
|
602 | doc.path = path
|
603 | doc.id = makeID()
|
604 |
|
605 | documents.push doc
|
606 | next()
|
607 | else if stat.name.endsWith('.png') or stat.name.endsWith('.svg') or stat.name.endsWith('.jpg') or stat.name.endsWith('.jpeg') or stat.name.endsWith('.gif') or stat.name.endsWith('.webm') or stat.name.endsWith('.ogg') or stat.name.endsWith('.mpeg') or stat.name.endsWith('.mp3') or stat.name.endsWith('.wav') or stat.name.endsWith('.webm') or stat.name.endsWith('.mp4') or stat.name.endsWith('.ogg')
|
608 | catalog.push (root.substring(root.indexOf("/documents") + 1)).replace('documents/', '') + '/' + stat.name
|
609 | next()
|
610 | else
|
611 | next()
|
612 | return
|
613 |
|
614 | walker.on 'end', ->
|
615 | documents = reorderFiles documents
|
616 |
|
617 | for file in documents
|
618 | autoImport.push file.path
|
619 |
|
620 | autoImport.push '/build/App.js'
|
621 | indexJS = Generate.indexJS(JSON.stringify(autoImport))
|
622 | fs.writeFile dir + '/build/index.js', indexJS, (err) ->
|
623 | return console.log(err) if err
|
624 | fs.writeFile dir + '/build/catalog.js', Generate.catalog(catalog), (err) ->
|
625 | return console.log(err) if err
|
626 | return
|
627 |
|
628 |
|
629 | buildProduction = (dir)->
|
630 |
|
631 | dir = process.cwd() if not dir
|
632 |
|
633 | dirName = dir.split('/')
|
634 | dirName = dirName[dirName.length-1]
|
635 |
|
636 | files = []
|
637 | folder = path.dirname(dir) + '/' + dirName
|
638 | paths_to_remove = []
|
639 |
|
640 | magixJSON = fs.readFileSync(folder + '/package.json', 'utf8')
|
641 | config = JSON.parse(magixJSON)
|
642 | if not config.name
|
643 | console.log 'MagiX: [ERR] Invalid JSON project file, name missing.'.red
|
644 | return
|
645 |
|
646 | prodFolder = folder + "/../magix-#{config.name}-production"
|
647 |
|
648 |
|
649 | scriptID = makeID()
|
650 |
|
651 |
|
652 |
|
653 |
|
654 | if not fs.existsSync(prodFolder)
|
655 | console.log 'MagiX: Cloning working project..'.magenta
|
656 | fs.mkdirSync prodFolder
|
657 |
|
658 |
|
659 | if fs.existsSync(prodFolder + '/build')
|
660 | deleteFolderRecursive prodFolder + '/build'
|
661 |
|
662 |
|
663 | fs.copy folder, prodFolder, (err) ->
|
664 | return console.error err if err
|
665 |
|
666 | console.log ('MagiX: Production path: ' + prodFolder).green
|
667 | fs.writeFile prodFolder + '/build/index.js', Generate.indexJS(JSON.stringify('/build/' + scriptID + '.js')), (err) ->
|
668 | return console.log(err) if err
|
669 | minify()
|
670 | return
|
671 | return
|
672 |
|
673 | minify = ->
|
674 |
|
675 | walker = walk.walk(prodFolder + '/documents', followLinks: false)
|
676 | walker.on 'file', (root, stat, next) ->
|
677 | name = stat.name.split('/')
|
678 | name = name[name.length-1]
|
679 |
|
680 |
|
681 | if stat.name.endsWith('.DS_Store')
|
682 | fs.unlinkSync root+'/'+stat.name
|
683 |
|
684 |
|
685 | if stat.name.endsWith('.coffee') or stat.name.endsWith('.js') or stat.name.endsWith('.css')
|
686 | name = name.replace('.coffee', '').replace('.js', '').replace('.css', '')
|
687 | if name isnt 'App'
|
688 | paths_to_remove.push root+'/'+stat.name
|
689 |
|
690 |
|
691 | if stat.name.endsWith('.coffee')
|
692 | name = name.replace('.coffee', '')
|
693 |
|
694 | fs.readFile root + '/' + name + '.coffee', 'utf8', (err, data) ->
|
695 | return console.log(err) if err
|
696 |
|
697 | path = root.replace('documents', 'build') + '/' + stat.name.replace('coffee', 'js')
|
698 |
|
699 | if data[0] isnt '!'
|
700 | if path isnt prodFolder + '/build/App.js'
|
701 | files.push path
|
702 | next()
|
703 | else
|
704 | next()
|
705 | else
|
706 | uglified = uglify.minify([path]).code
|
707 | fs.writeFile path, uglified, 'utf8', (err) ->
|
708 | return console.log(err) if err
|
709 | next()
|
710 | else
|
711 | next()
|
712 | return
|
713 |
|
714 | walker.on 'end', ->
|
715 | files.push prodFolder + '/build/App.js'
|
716 |
|
717 |
|
718 | uglified = '/* Made with MagiX (magixjs.com) and a smile :) */ ' + uglify.minify(files).code
|
719 | console.log 'MagiX: Minify script..'.magenta
|
720 |
|
721 | appPath = prodFolder + '/build/' + scriptID + '.js'
|
722 | fs.writeFile appPath, uglified, (err) ->
|
723 |
|
724 | console.log 'MagiX: Cleaning..'.magenta
|
725 | cleaning ->
|
726 | console.log "MagiX: Done: project built for production.".green
|
727 | return
|
728 |
|
729 | cleaning = (cb)->
|
730 |
|
731 |
|
732 | paths_to_remove = paths_to_remove.concat(files)
|
733 |
|
734 |
|
735 | try
|
736 | for item in paths_to_remove
|
737 | if fs.existsSync(item)
|
738 | fs.unlinkSync item
|
739 | catch e
|
740 | console.log e
|
741 |
|
742 | cleanEmptyFoldersRecursively = (folder) ->
|
743 | fs = require('fs')
|
744 | path = require('path')
|
745 | isDir = fs.statSync(folder).isDirectory()
|
746 | if !isDir
|
747 | return
|
748 | files = fs.readdirSync(folder)
|
749 | if files.length > 0
|
750 | files.forEach (file) ->
|
751 | fullPath = path.join(folder, file)
|
752 | cleanEmptyFoldersRecursively fullPath
|
753 | return
|
754 |
|
755 |
|
756 | files = fs.readdirSync(folder)
|
757 | if files.length == 0
|
758 | console.log ('MagiX: removing: '+folder).magenta
|
759 | fs.rmdirSync folder
|
760 | return
|
761 | return
|
762 |
|
763 | cleanEmptyFoldersRecursively prodFolder + '/documents/'
|
764 | cleanEmptyFoldersRecursively prodFolder + '/build/'
|
765 |
|
766 | cb() if cb
|
767 |
|
768 |
|
769 |
|
770 |
|
771 |
|
772 | program
|
773 | .command('about', {isDefault: yes})
|
774 | .description('About magiX.')
|
775 | .action about
|
776 |
|
777 | program
|
778 | .command('create [name]')
|
779 | .description('Create a new project.')
|
780 | .action create
|
781 |
|
782 | program
|
783 | .command('launch [dir] [port] [env]')
|
784 | .description('Launch a local server to help you code an magix project.')
|
785 | .action launch
|
786 |
|
787 |
|
788 |
|
789 |
|
790 |
|
791 |
|
792 |
|
793 |
|
794 |
|
795 |
|
796 |
|
797 |
|
798 | program
|
799 | .command('build [dir] [env]')
|
800 | .description('Build a project.')
|
801 | .action build
|
802 |
|
803 | program
|
804 | .command('clean [dir]')
|
805 | .description('Clear the build of a project.')
|
806 | .action clean
|
807 |
|
808 | program
|
809 | .command('watch [dir]')
|
810 | .description('Observe change on a project and compile on the fly.')
|
811 | .action watch
|
812 |
|
813 |
|
814 |
|
815 |
|
816 |
|
817 |
|
818 | program.parse process.argv
|