main
  1# ** Cakefile Template ** is a Template for a common Cakefile that you may use in a coffeescript nodejs project.
  2# 
  3# It comes baked in with 5 tasks:
  4#
  5# * build - compiles your src directory to your lib directory
  6# * watch - watches any changes in your src directory and automatically compiles to the lib directory
  7# * test  - runs mocha test framework, you can edit this task to use your favorite test framework
  8# * docs  - generates annotated documentation using docco
  9# * clean - clean generated .js files
 10files = [
 11  'lib'
 12  'src'
 13]
 14
 15fs = require 'fs'
 16{print} = require 'util'
 17{spawn, exec} = require 'child_process'
 18
 19try
 20  which = require('which').sync
 21catch err
 22  if process.platform.match(/^win/)?
 23    console.log 'WARNING: the which module is required for windows\ntry: npm install which'
 24  which = null
 25
 26# ANSI Terminal Colors
 27bold = '\x1b[0;1m'
 28green = '\x1b[0;32m'
 29reset = '\x1b[0m'
 30red = '\x1b[0;31m'
 31
 32# Cakefile Tasks
 33#
 34# ## *docs*
 35#
 36# Generate Annotated Documentation
 37#
 38# <small>Usage</small>
 39#
 40# ```
 41# cake docs
 42# ```
 43task 'docs', 'generate documentation', -> docco()
 44
 45# ## *build*
 46#
 47# Builds Source
 48#
 49# <small>Usage</small>
 50#
 51# ```
 52# cake build
 53# ```
 54task 'build', 'compile source', -> build -> log ":)", green
 55
 56# ## *watch*
 57#
 58# Builds your source whenever it changes
 59#
 60# <small>Usage</small>
 61#
 62# ```
 63# cake watch
 64# ```
 65task 'watch', 'compile and watch', -> build true, -> log ":-)", green
 66
 67# ## *test*
 68#
 69# Runs your test suite.
 70#
 71# <small>Usage</small>
 72#
 73# ```
 74# cake test
 75# ```
 76task 'test', 'run tests', -> build -> mocha -> log ":)", green
 77
 78# ## *clean*
 79#
 80# Cleans up generated js files
 81#
 82# <small>Usage</small>
 83#
 84# ```
 85# cake clean
 86# ```
 87task 'clean', 'clean generated files', -> clean -> log ";)", green
 88
 89
 90# Internal Functions
 91#
 92# ## *walk* 
 93#
 94# **given** string as dir which represents a directory in relation to local directory
 95# **and** callback as done in the form of (err, results)
 96# **then** recurse through directory returning an array of files
 97#
 98# Examples
 99#
100# ``` coffeescript
101# walk 'src', (err, results) -> console.log results
102# ```
103walk = (dir, done) ->
104  results = []
105  fs.readdir dir, (err, list) ->
106    return done(err, []) if err
107    pending = list.length
108    return done(null, results) unless pending
109    for name in list
110      file = "#{dir}/#{name}"
111      try
112        stat = fs.statSync file
113      catch err
114        stat = null
115      if stat?.isDirectory()
116        walk file, (err, res) ->
117          results.push name for name in res
118          done(null, results) unless --pending
119      else
120        results.push file
121        done(null, results) unless --pending
122
123# ## *log* 
124# 
125# **given** string as a message
126# **and** string as a color
127# **and** optional string as an explanation
128# **then** builds a statement and logs to console.
129# 
130log = (message, color, explanation) -> console.log color + message + reset + ' ' + (explanation or '')
131
132# ## *launch*
133#
134# **given** string as a cmd
135# **and** optional array and option flags
136# **and** optional callback
137# **then** spawn cmd with options
138# **and** pipe to process stdout and stderr respectively
139# **and** on child process exit emit callback if set and status is 0
140launch = (cmd, options=[], callback) ->
141  cmd = which(cmd) if which
142  app = spawn cmd, options
143  app.stdout.pipe(process.stdout)
144  app.stderr.pipe(process.stderr)
145  app.on 'exit', (status) -> callback?() if status is 0
146
147# ## *build*
148#
149# **given** optional boolean as watch
150# **and** optional function as callback
151# **then** invoke launch passing coffee command
152# **and** defaulted options to compile src to lib
153build = (watch, callback) ->
154  if typeof watch is 'function'
155    callback = watch
156    watch = false
157
158  options = ['-c', '-b', '-o' ]
159  options = options.concat files
160  options.unshift '-w' if watch
161  launch 'coffee', options, callback
162
163# ## *unlinkIfCoffeeFile*
164#
165# **given** string as file
166# **and** file ends in '.coffee'
167# **then** convert '.coffee' to '.js'
168# **and** remove the result
169unlinkIfCoffeeFile = (file) ->
170  if file.match /\.coffee$/
171    fs.unlink file.replace('src','lib').replace(/\.coffee$/, '.js'), ->
172    true
173  else false
174
175# ## *clean*
176#
177# **given** optional function as callback
178# **then** loop through files variable
179# **and** call unlinkIfCoffeeFile on each
180clean = (callback) ->
181  try
182    for file in files
183      unless unlinkIfCoffeeFile file
184        walk file, (err, results) ->
185          for f in results
186            unlinkIfCoffeeFile f
187
188    callback?()
189  catch err
190
191# ## *moduleExists*
192#
193# **given** name for module
194# **when** trying to require module
195# **and** not found
196# **then* print not found message with install helper in red
197# **and* return false if not found
198moduleExists = (name) ->
199  try 
200    require name 
201  catch err 
202    log "#{name} required: npm install #{name}", red
203    false
204
205
206# ## *mocha*
207#
208# **given** optional array of option flags
209# **and** optional function as callback
210# **then** invoke launch passing mocha command
211mocha = (options, callback) ->
212  #if moduleExists('mocha')
213  if typeof options is 'function'
214    callback = options
215    options = []
216  # add coffee directive
217  options.push '--compilers'
218  options.push 'coffee:coffee-script'
219  
220  launch 'mocha', options, callback
221
222# ## *docco*
223#
224# **given** optional function as callback
225# **then** invoke launch passing docco command
226docco = (callback) ->
227  #if moduleExists('docco')
228  walk 'src', (err, files) -> launch 'docco', files, callback
229