UNPKG

23.3 kBtext/coffeescriptView Raw
1#!/usr/bin/env node
2
3console.error = ->
4
5##############################################################
6# REQUIRE
7
8prompt = require 'prompt'
9program = require('commander')
10colors = require('colors')
11fs = require('fs-extra')
12walk = require('walk')
13path = require('path')
14watcher = require('node-watch')
15Generate = require('./lib/Generate')
16CoffeeScript = require('coffee-script')
17uglify = require('uglify-js')
18restify = require('restify')
19CookieParser = require('restify-cookies')
20mkdirp = require('mkdirp')
21openurl = require('openurl')
22reload = require('reload')
23
24##############################################################
25# CONFIG
26
27prompt.message = 'MagiX'
28reloadServer = undefined
29
30##############################################################
31# HELPERS
32
33Array::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
41reorderFiles = (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
62capitalizeFirstLetter = (string) ->
63 string.charAt(0).toUpperCase() + string.slice(1)
64
65getDirectories = (srcpath) ->
66 fs.readdirSync(srcpath).filter (file) ->
67 fs.statSync(path.join(srcpath, file)).isDirectory()
68
69# Indent text
70indent = (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 # re-use
74 if opt_spacesPerIndent then str.replace(/^\t+/g, ((tabs) ->
75 tabs.replace /./g, numOfIndents
76 )) else str
77
78deleteFolderRecursive = (path) ->
79 if fs.existsSync(path)
80 fs.readdirSync(path).forEach (file, index) ->
81 curPath = path + '/' + file
82 if fs.lstatSync(curPath).isDirectory()
83 # recurse
84 deleteFolderRecursive curPath
85 else
86 # delete file
87 fs.unlinkSync curPath
88 return
89 fs.rmdirSync path
90 return
91
92makeID = ->
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# PROGRAMS FUNCTIONS
103
104about = ->
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
131create = (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 # Generate App content in both JS and CS
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 # Folders and files generation
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 # Write HTML content
187 fs.writeFile dir_project + '/index.html', htmlContent, (err) ->
188 return console.log(err) if err
189
190 # Write BUILD content
191 fs.writeFile dirBuild + '/App.js', appJS, (err) ->
192 return console.log(err) if err
193
194 # Write SOURCES content
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
204launch = (dir, server_port, env)->
205
206 ######################################################
207 # PROD CHECK
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 # If dir ends with / remove it
238 if dir.endsWith('/')
239 dir = dir.slice(0, -1)
240
241 server_port = 9000 if not server_port
242
243 ######################################################
244 # SERVER
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 # Reload server
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
286build = (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 # Walker options
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 # JS files
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 # Build production project
337 buildProduction(dir) if isProd
338 return
339
340watch = (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 # Fire server-side reload event
348 if reloadServer
349 reloadServer.reload()
350
351 # If file is coffeescript
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 # JS files
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 # If the path exist, simply copy the JS file to the build
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 #else if filename and filename.index#filename isnt '.DS_Store'
384
385 if server and server.close
386 server.close()
387 server.start(no)
388
389 return
390
391
392clean = (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# PROCESSES
408
409dirCheck = (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
440compileFile = (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 # We signal the code that we are about to add a class wrapper around the code
469 addClass = true
470
471 # We indent the code
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 # If extend framework, inject class with init
476 if classes.indexOf(file.type) > -1
477 # Create a class that extends another one
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 # Create an empty class
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 # Create an empty class
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 # Convert CS to JS
517 converted = null
518 try
519 converted = CoffeeScript.compile(classFile, 'bare': true)
520 catch err
521 convert_error = err
522
523 # Define paths
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 #console.log err if err
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 # Show user notification when watching changes
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 # Response is response from notification
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
567buildAutoImport = (dir)->
568 autoImport = []
569 catalog = []
570
571 # documents = reorderFiles documents
572 documents = []
573
574 # CREATE DOCUMENT ARRAY WITH FILE EXTENDS
575 # LOOP THROUGH DOCUMENTS AND REORDER
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
629buildProduction = (dir)->
630
631 dir = process.cwd() if not dir
632 #if dir is '.'
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 # Generate scriptID
649 scriptID = makeID()
650
651 # fs.copy folder, prodFolder, (err) ->
652 # return console.error err if err
653
654 if not fs.existsSync(prodFolder)
655 console.log 'MagiX: Cloning working project..'.magenta
656 fs.mkdirSync prodFolder
657
658 # Clean build
659 if fs.existsSync(prodFolder + '/build')
660 deleteFolderRecursive prodFolder + '/build'
661
662 # Copy project folder to production dir
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 # REMOVE DS STORE ON MAC OS
681 if stat.name.endsWith('.DS_Store')
682 fs.unlinkSync root+'/'+stat.name
683
684 # PUSH PATHS TO REMOVE LATER ON..
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 # IF COFFEE, PREPARE FOR MINIFING
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 # Minify files
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 # ADD BUILD FOLDER PATHS TO DOCUMENTS PATH
732 paths_to_remove = paths_to_remove.concat(files)
733
734 # CLEAN BUILD AND DOCUMENTS FOLDER
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 # re-evaluate files; after deleting subfolder
755 # we may have parent folder empty now
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# PROGRAMS
771
772program
773 .command('about', {isDefault: yes})
774 .description('About magiX.')
775 .action about
776
777program
778 .command('create [name]')
779 .description('Create a new project.')
780 .action create
781
782program
783 .command('launch [dir] [port] [env]')
784 .description('Launch a local server to help you code an magix project.')
785 .action launch
786
787# Maybe for later
788###
789program
790 .command('forever start [dir] [port]')
791 .description('Launch a local server that runs continuously.')
792 .action foreverStart
793program
794 .command('forever stop [dir] [port]')
795 .description('Launch a local server that runs continuously.')
796 .action foreverStop
797###
798program
799 .command('build [dir] [env]')
800 .description('Build a project.')
801 .action build
802
803program
804 .command('clean [dir]')
805 .description('Clear the build of a project.')
806 .action clean
807
808program
809 .command('watch [dir]')
810 .description('Observe change on a project and compile on the fly.')
811 .action watch
812
813# program
814# .command('install [name] [dir]')
815# .description('Add module to your project.')
816# .action install
817
818program.parse process.argv