Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
Browse files

Merge pull request #775 from mklabs/cake

build script: as a cake!
  • Loading branch information...
commit e47f81a79e836474880f57938d29df56cc32b899 2 parents 994cd08 + 75fff4b
@KushalP KushalP authored
View
5 build/cake/.gitignore
@@ -0,0 +1,5 @@
+html5-boilerplate
+intermediate
+publish
+_*
+node_modules
View
744 build/cake/Cakefile
@@ -0,0 +1,744 @@
+# `cake` is a simplified version of [Make](http://www.gnu.org/software/make/)
+# ([Rake](http://rake.rubyforge.org/), [Jake](http://github.com/280north/jake))
+# for CoffeeScript. You define tasks with names and descriptions in a Cakefile,
+# and can call them from the command line, or invoke them from other tasks.
+#
+# Running `cake` with no arguments will print out a list of all the tasks in the
+# current directory's Cakefile.
+#
+# TODO:
+# * break the script in smaller parts
+#
+# * js
+# * css
+# * html
+# * img
+# * lint
+#
+
+# External dependencies.
+fs = require 'fs'
+path = require 'path'
+{spawn, exec} = require 'child_process'
+{EventEmitter} = require 'events'
+colors = require 'colors'
+prompt = require 'prompt'
+mkdirp = require 'mkdirp'
+config = require './conf/default'
+helper = require './helper'
+uglify = require 'uglify-js'
+cssmin = require 'clean-css'
+htmlmin = require 'html-minifier'
+
+base = process.cwd()
+
+# Extend a source object with the properties of another object (shallow copy).
+extend = exports.extend = (object, properties) ->
+ for key, val of properties
+ object[key] = val
+ object
+
+# ## configuration
+repo =
+ h5bp: 'git://github.com/paulirish/html5-boilerplate.git'
+
+# merge the local config with global object for this module,
+# so that interpolation works as expected (of course, config should not redefine global variable)
+extend global, config
+
+# the event emitter used along tasks to handle some asynchronous stuff, gem for global EventEmitter
+gem = new EventEmitter
+
+# ### task monkey-patch
+#
+# to provide a tasks-scopped EventEmitter to enable some async stuff and task ordering
+#
+_task = global.task
+
+# internal _tasks cache, stored as `taskname: status` where status turns false
+# once the end event is emitted
+_tasks = {}
+
+task = (name, description, action) ->
+
+ description = description.grey
+ _task name, description, (options) ->
+ em = new EventEmitter()
+ .on('error', (err) -> console.log 'error occured'.red.bold; error err)
+ .on('log', console.log.bind console, " #{name} » ".magenta)
+ .on('end', (results) ->
+ console.log "✔ end:#{name}".green
+ gem.emit "end:#{name}", results
+ _tasks[name] = 'done'
+ )
+
+ state = _tasks[name]
+
+ # emit the end event and prevent action call if the task is
+ # already done
+ return gem.emit "end:#{name}" if state is 'done'
+
+ # set the task state to pending, will turn done once the task emiter
+ # emit the end event
+ _tasks[name] = 'pending'
+
+ # invoke the task if the task is unknown yet
+ action.call @, options, em unless state
+
+
+# error handler, usage:
+#
+# return error err if err
+#
+# return error new Error(':((') if err
+error = (err) ->
+ console.error ''.red + err.message.red
+ process.exit 1
+
+
+
+# ### docs
+# Generates the source documentation of this cake script
+#
+# added as a conveniency
+task 'docs', 'Generates the source documentation of this cake script', (options, em) ->
+
+ commands = [
+ "rm -rf documentation"
+ "cp Cakefile Cakefile.coffee"
+ "docco conf/*.coffee *.coffee"
+ "cp -r docs documentation"
+ "rm -rf docs Cakefile.coffee"
+ ].join(' && ')
+
+ exec commands, (err, stdout) ->
+ return error err if err
+
+ em.emit 'log', '\n » ' + stdout.trim().split('\n').join('\n » ').grey
+ em.emit 'end'
+
+# ## Main tasks
+#
+# * combining and minifying CSS
+# * combining and minifying JS
+# * image optimization
+
+task 'build', 'Build with defaults configuration the main tasks: js, css and img optimiaztion', (option, em) ->
+
+ start = +new Date
+
+ invoke 'js'
+ invoke 'css'
+ invoke 'img'
+ invoke 'usemin'
+
+ remaining = 0
+ handle = (name) ->
+ remaining++
+ return ->
+ return unless --remaining is 0
+
+ em.emit 'log', "Build done, now copy the #{dir.intermediate} folder over to #{dir.publish}"
+
+ em.emit 'log', 'minify html before copy...'
+ invoke 'htmlclean'
+ # gem.emit 'end:htmlclean'
+
+
+ gem.on 'end:js', handle()
+ gem.on 'end:css', handle()
+ gem.on 'end:img', handle()
+ gem.on 'end:usemin', handle()
+ gem.on 'end:htmlclean', ->
+ commands = [
+ "cp -r #{base}/#{dir.intermediate} #{base}/#{dir.publish}",
+ "rm -rf #{base}/#{dir.intermediate}"
+ ].join(' && ')
+
+ exec commands, (options, err) ->
+ return error err if err
+ elapsed = (+new Date - start) / 1000
+ console.log "✔ Build Script successful (#{elapsed}s). Check your #{dir.publish}/ folder".green.bold
+ em.emit 'end'
+
+
+
+
+task 'js', 'Combines and minifies JS', (options, em) ->
+
+ invoke 'js.scripts.concat'
+
+ gem.on 'end:js.scripts.concat', em.emit.bind(em, 'end')
+
+
+task 'css', 'Combines and minifies CSS', (options, em) ->
+
+ invoke 'css.concat'
+
+ gem.on 'end:css.concat', em.emit.bind(em, 'end')
+
+task 'img', 'Performs img optimization', (options, em) ->
+
+ # will most likely be done by using spawning optipng on
+ # dir.img folder
+ invoke 'img.optimize'
+
+ gem.on 'end:img.optimize', em.emit.bind(em, 'end')
+
+# ### createproject
+#
+# Generate a new project from your HTML5 Boilerplate repo clone
+#
+# - by: Rick Waldron & Michael Cetrulo
+# - cake edition by: Mickael Daniel
+#
+# The terminal will prompt with this message:
+#
+# To create a new html5-boilerplate project,
+# enter a new directory name:
+#
+# Type the name of the new project you are creating, ideally in lowercase letters,
+# with no spaces - this will be the directory name that your new project lives in.
+# Press Enter to continue.
+#
+# If you attempt to create a directory that already exists, createproject.sh will warn you and stop running.
+#
+# If all goes smoothly, you will see the following messages:
+#
+# Created Directory: [name]
+#
+# [ A list of all the html5-boilerplate files being copied ]
+#
+# Created Project: [name]
+# Success! You now have a clean project to begin making the next HTML5 wunderkind demo!
+#
+
+option '-o', '--output [DIR]', 'directory for the creteproject task'
+task 'createproject', 'a simple create project task', (options) ->
+
+ src = path.join __dirname, repo.h5bp.split('/').reverse()[0].replace(/\.git/, '')
+
+ exists = path.existsSync src
+
+ commands = [
+ if exists then "git pull" else "git clone #{repo.h5bp}"
+ ].join(' && ')
+ gitcmd = if exists then 'pull' else 'clon'
+
+ console.log 'createproject'.magenta, "#{gitcmd}ing #{repo.h5bp}...".grey
+
+ args = (if exists then 'pull' else "clone #{repo.h5bp}").split(' ')
+
+ helper.spawn 'git', args, (code, stdout, stderr) ->
+ return error err if code > 0
+ stdout = stdout.replace(/\n/gm, '')
+ console.log "#{stdout}".grey.bold
+
+ console.log ' - To create a new html5-boilerplate project, enter a new directory name:'.blue
+
+ prompt.message = "".bold
+ prompt.delimiter = ''
+ prompt.override = {directory: options.output}
+
+ prompt.start() unless prompt.override.directory
+ prompt.get ['directory'], (err, result) ->
+ return error err if err
+ return error new Error("please provide a directory name") unless result.directory
+ dest = path.join(__dirname, result.directory)
+ mkdirp dest, 0755, (err) ->
+ return error err if err
+ console.log " ✔ Created Directory: #{dest}".grey.bold
+ exec ["cd #{src}", "cp -vr css js img build test *.html *.xml *.txt *.png *.ico .htaccess #{dest}"].join(' && '), (err, stdout, stderr) ->
+ return error stderr if err
+ output = stdout.split(/\n/)
+
+ output.forEach (line) -> console.log " » #{line}".grey
+ console.log " ✔ Created Project: #{dest}".grey.bold
+
+
+
+# ## Support tasks
+
+# ### intro
+#
+# Output the intro message
+#
+task 'intro', 'Kindly inform the developer about the impending magic', (options, em) ->
+ message = """
+
+ ====================================================================
+ Welcome to the ★ HTML5 Boilerplate Build Script! ★
+
+ We're going to get your site all ship-shape and ready for prime time.
+
+ This should take somewhere between 15 seconds and a few minutes,
+ mostly depending on how many images we're going to compress.
+
+ Feel free to come back or stay here and follow along.
+ =====================================================================
+ """
+
+ em.emit 'log', message.split('\n').join('\n ').grey
+ em.emit 'end', message.grey
+
+
+task 'check', 'Performs few validations upon the current repo, outputing errors if any', (options, em) ->
+ # check few configuration values, namely dir.source
+ # Test whether or not the dir.source path exists.
+ exists = path.existsSync "#{base}/#{dir.source}"
+
+ return error new Error("#{base}/#{dir.source} does not exist, change the dir.source config or run cake createproject and enter #{dir.source} when prompted") unless exists
+
+
+task 'clean', 'Wipe the previous build', (options, em) ->
+ invoke 'intro'
+ invoke 'check'
+
+ em.emit 'log', 'Cleaning up previous build directory...'.grey
+
+
+ exec "rm -rf #{dir.intermediate} #{dir.publish}", (err, stdout, stderr) ->
+ return error err if err
+
+ em.emit 'end', stdout
+
+# ### mkdirs
+# Create the directory structure
+#
+# Copy the whole `dir.source` to `dir.intermediate`
+#
+task 'mkdirs', 'Create the directory intermediate structure', (options, em) ->
+
+ invoke 'clean'
+
+ gem.on 'end:clean', ->
+ failmsg = "Your dir.publish folder is set to #{dir.publish} which could delete your entire site or worse. Change it in project.properties"
+ dangerousPath = !!~['..', '.', '/', './', '../'].indexOf(dir.publish)
+ return error new Error(failmsg) if dangerousPath
+
+ process.chdir path.join(__dirname, "#{dir.source}")
+ helper.fileset(".", [file.default.exclude, file.exclude].join(' '))
+ .on('error', console.error.bind(console))
+ .on('end', (files) ->
+ em.emit 'log', "Copying #{files.length} files over to #{dir.intermediate} and #{dir.publish} from #{dir.source}".grey
+ destinations = [dir.intermediate]
+ remaining = files.length * destinations.length
+ for to in destinations then do (to) ->
+ for file in files then do(file) ->
+ fragment = file.split '/'
+ dirname = '/' + file.split('/')[1..-2].join('/')
+ dirname = dirname.replace(dirname.split(__dirname)[1].split('/')[1..][0], to)
+ filename = file.split('/')[-1..][0]
+
+ mkdirp dirname, 0755, (err) ->
+ return error err if err
+ to = path.join(dirname, filename)
+ exec "cp -v #{file} #{to}", (err, stdout, stderr) ->
+ return error err if err
+ em.emit 'end', 'done' if --remaining is 0
+ )
+
+# ## JS tasks
+
+# ### js.main.concat
+#
+# Concatenates the JS files in dir.js. depends on mkdirs
+task 'js.main.concat', 'Concatenates the JS files in dir.js', (options, em) ->
+
+ invoke 'mkdirs'
+
+ concat = (output) ->
+ output = new Buffer output.join('\n\n')
+ fs.writeFile path.join(__dirname, "#{dir.intermediate}", "#{dir.js}", 'script-concat.js'), output, (err) ->
+ return error err if err
+ em.emit 'log', 'script-concat.js just concat...'.grey
+ em.emit 'end', true
+
+ handle = (files) ->
+ em.emit 'log', 'Concatenating Main JS scripts...'
+
+ helper.fileset "#{dir.js.main}/plugins.js #{dir.js.main}/#{file.root.script}", '', (err, files) ->
+ output = []
+ remaining = files.length
+ for file in files then do (file) ->
+ fs.readFile file, (err, body) ->
+ return error err if err
+ output.push body
+
+ concat(output) if --remaining is 0
+
+
+ gem.once 'end:mkdirs', handle
+
+# ### js.mylibs.concat
+#
+# Concatenates the JS files in dir.js.mylibs. depends on mkdirs
+#
+task 'js.mylibs.concat', 'Concatenates the JS files in dir.js.mylibs', (options, em) ->
+
+ invoke 'mkdirs'
+
+ concat = (output) ->
+ output = new Buffer output.join('\n\n')
+ fs.writeFile path.join(__dirname, "#{dir.intermediate}", "#{dir.js.mylibs}", 'mylibs-concat.js'), output, (err) ->
+ return error err if err
+ em.emit 'log', 'mylibs-concat.js just concat...'.grey
+ em.emit 'end', true
+
+ gem.once 'end:mkdirs', (files) ->
+ em.emit 'log', "Concatenating JS libraries in #{dir.js.mylibs}".grey
+
+ process.chdir path.join(__dirname, "#{dir.intermediate}")
+ helper.fileset "#{dir.js.mylibs}/**/*.js", "#{file.default.js.bypass}", (err, files) ->
+ return error err if err
+
+ output = []
+
+ return concat(output) unless files.length
+
+ remaining = files.length
+ for file in files then do(file) ->
+ fs.readFile file, (err, body) ->
+ return error err if err
+ output.push body
+ concat(output) if --remaining is 0
+
+
+# ### js.scripts.concat
+#
+# Concatenating library file with main script file
+#
+# Calculates an md5 checksum, prefix the script name, and copy over to `#{dir.publish}/#{dir.js}/`
+#
+# publish/js/e816baa.scripts-concat.min.js
+#
+task 'js.scripts.concat', 'Concatenating library file with main script file', (options, em) ->
+
+ invoke 'js.main.concat'
+ invoke 'js.mylibs.concat'
+
+ concat = (source) ->
+ concat.remaining = concat.remaining or= 0
+ concat.remaining++
+
+ return ->
+ return if --concat.remaining
+ helper.fileset "#{base}/#{dir.intermediate}/#{dir.js}/**-concat.js", (err, files) ->
+ return error err if err
+
+ em.emit 'log', 'Concatenating library file with main script file'.grey
+ helper.concat files, (err, buffers) ->
+ return error err if err
+
+ em.emit 'log', "Writing to #{dir.intermediate}/#{dir.js}/scripts-concat.min.js".grey
+
+ filename = "scripts-concat.min.js"
+ from = "#{dir.intermediate}/#{dir.js}/#{filename}"
+ output = buffers.map (buffer) ->
+ return buffer.toString()
+
+ fs.writeFile path.join(base, from), output.join('\n\n'), (err) ->
+ return error err if err
+ em.emit 'log', "File ✔ #{from}".grey
+
+ em.emit 'log', 'Calculating checksum...'.grey
+
+
+ helper.checksum path.join(base,from), (err, md5) ->
+ return error err if err
+
+ em.emit 'log', "✔ md5 is #{md5} for file #{from}"
+ # now copy over the file to #{dir.js}/#{script.sha}.js
+ md5 = md5.substring 0, hash.length
+ to = "#{dir.intermediate}/#{dir.js}/#{md5}.#{filename}"
+
+ # set the global script.js for future reference in usemin
+ global.scripts = { js: "#{md5}.#{filename}" }
+
+ em.emit 'log', "now copy over the file to #{to}"
+ return helper.copy path.join(base, from), path.join(base, to), (err) ->
+ return error err if err
+ em.emit 'log', "✔ Copy done » #{to}"
+
+ em.emit 'log', 'Minify the output now...'
+ invoke 'js.all.minify'
+
+
+ gem.on 'end:js.main.concat', concat('main')
+ gem.on 'end:js.mylibs.concat', concat('mylibs')
+
+ gem.on 'end:js.all.minify', em.emit.bind(em, 'end')
+
+
+# ### js.mylibs.concat
+#
+# Minifies the scripts.js files in #{dir.intermediate}/#{dir.js}. depends on mkdirs
+#
+task 'js.all.minify', "Minifies the *-concat.js files in #{dir.intermediate}/#{dir.js}", (options, em) ->
+
+ gem.once 'end:mkdirs', (result) ->
+ em.emit 'log', 'Minifying scripts'.grey
+
+ dirname = path.join dir.intermediate, dir.js
+ process.chdir path.join(__dirname, dirname)
+ helper.fileset "**-concat.min.js", (err, files) ->
+ return error err if err
+
+ remaining = files.length
+ for file in files then do (file) ->
+ fs.readFile file, (err, body) ->
+ jsp = uglify.parser
+ pro = uglify.uglify
+
+ ast = jsp.parse body.toString()
+ ast = pro.ast_mangle ast
+ ast = pro.ast_squeeze ast
+ code = new Buffer pro.gen_code(ast)
+
+ fs.writeFile file, new Buffer(code), (err) ->
+ return error err if err
+ em.emit 'log', "Uglified #{file}".grey
+ em.emit 'end', files if --remaining is 0
+
+ invoke 'mkdirs'
+
+# ## jshint
+# run the `dir.js` folder through jshint with default options. Exits and reports in case of lint errors.
+#
+# Run separately
+task 'jshint', 'jshint task, run jshint on any non min.js file in dir.js', (options, em) ->
+ helper.fileset "#{dir.source}/#{dir.js}/", "**/*.min.js #{dir.source}/#{dir.js.libs}", (err, files) ->
+ return error err if err
+ exec 'jshint ' + files.join(' '), (err, stdout, stderr) ->
+ return em.emit 'log', ' ✔ Congrats! Lint Free!'.green unless err
+
+ if err.message is 'Command failed: '
+ em.emit 'log', [
+ "jshint returns the following errors \n".grey
+ stdout.split('\n').map((line) -> return "#{line}").join('\n').grey
+ ].join('\n')
+
+ return error err if err
+
+# ## csslint
+# run the `dir.css` folder through csslint with default options. Reports in case of lint errors.
+#
+# Run separately
+task 'csslint', 'csslint task, run csslint on dir.css and ommit *.min.css one', (options, em) ->
+ helper.fileset "#{dir.source}/#{dir.css}/", "**/*.min.css", (err, files) ->
+ return error err if err
+ for file in files then do (file) ->
+ exec 'csslint ' + file, (err, stdout, stderr) ->
+ return em.emit 'log', " ✔ Congrats! Lint Free! --> #{file}".green if stdout.match(/no\serrors/i)
+
+ em.emit 'log', [
+ " ✗ csslint returns the following errors".red
+ stdout.split('\n').map((line) -> return " #{line}").join('\n').grey
+ ].join('\n')
+
+
+# ## CSS tasks
+
+# ### css.concat
+#
+# Concat the CSS files depending on the @imports in your file.root.stylesheet
+#
+# `@import` should be fortmated like so
+#
+# @import url('style.bar.css')
+# @import url("style.foo.css")
+#
+task 'css.concat', 'Concat the CSS files depending on the @imports in your file.root.stylesheet', (options, em) ->
+
+ invoke 'mkdirs'
+
+ gem.once 'end:mkdirs', ->
+ em.emit 'log', 'Copy source file to intermediate directory'
+ from = path.join dir.source, dir.css, file.root.stylesheet
+ to = path.join dir.intermediate, dir.css, file.root.stylesheet
+ return em.emit 'copy', from, to
+
+ # called once the source to intermediate copy is done
+ em.on 'copy', (from, to) ->
+ em.emit 'log', 'Concatenating any @imports'
+
+ # replace imports with h5bp-import tags (part 1) this one wraps @media types
+ file = fs.readFile path.join(base, to), 'utf8', (err, body) ->
+
+ # go sync during the process of replace to ease the process
+ body = body.replace /@import url\([^\)]+\)/gi, (match) ->
+ file = match.match(/@import url\(([^\)]+)\)/)?[1].replace(/['|"]/g, '')
+ filepath = path.join base, dir.intermediate, dir.css, file
+
+ # test if the url property is valid and match an actual file in the repo
+ ok = path.existsSync filepath
+ return error new Error("@import-ed #{filepath} does not exist") unless ok
+
+ em.emit 'log', "replacing #{match} with #{file} content"
+ return "/* h5bp-import --> #{file} */\n" + fs.readFileSync filepath, 'utf8'
+
+ # now minify the whole css file, done using clean-css
+ # may just opt to use yuicompressor instead by spawning a java process
+ # (make it configurable)
+ em.emit 'log', "Minify #{to} file..."
+ body = cssmin.process body
+ em.emit 'log', 'Done'.green
+ # write the file to the intermediate dir
+ to = path.join base, to
+
+ checksum = helper.checksum(body).substring 0, hash.length
+
+ em.emit 'log', "Cheskum for this file is #{checksum}"
+ to = to.replace(/style\.css/, "#{checksum}.style.min.css")
+
+ # set the global style.css for future reference in usemin
+ global.style = { css: to.split('/').reverse()[0] }
+
+ em.emit 'log', "Write the min css file to #{to}"
+ fs.writeFile to, body, (err) ->
+ return error err if err
+ em.emit 'end', body
+
+# ## img optimization tasks
+
+# ### img.optimize
+# mainly done using optipng and by spawning child process, so you'll need
+# optipng in yout PATH for this to work
+#
+# no jpg optimization for now
+task 'img.optimize', 'Run optipng', (options, em) ->
+
+ invoke 'mkdirs'
+
+ gem.once 'end:mkdirs', ->
+ em.emit 'log', 'Optimizing images...'
+ em.emit 'log', 'This part might take a while. But everything else is most likely already done.'
+
+ em.emit 'log', 'Run optipng on the .png files'
+
+ # check that we have actual png files before runing optipng
+ helper.fileset "optipng #{base}/#{dir.intermediate}/#{dir.images}/*.png", (err, files) ->
+ return erorr err if err
+
+ # prevent optipng from returning with error if no files to process
+ return em.emit 'end' unless files.length
+
+ # run optipng with default options (make this configurable)
+ exec "optipng #{base}/#{dir.intermediate}/#{dir.images}/*.png", (err, stdout, stderr) ->
+ return error new Error( (stderr || stdout).trim().split('\n').join('\n » ') ) if err
+
+ em.emit 'log', '\n » ' + stdout.trim().split('\n').join('\n » ').grey, '\n\n'
+ em.emit 'end'
+
+# ## Html tasks
+#
+# tasks related to html manipulation such as cleaning
+# and updating script/css references, html minification and so on
+#
+task 'usemin', 'Replaces references to non-minified scripts/styles', (options, em) ->
+ remaining = 0
+ handle = ->
+ remaining++
+ return ->
+ return unless --remaining is 0
+ em.emit 'log', 'Switching to minified js files...'
+
+ process.chdir path.join(base, dir.intermediate)
+ helper.fileset "#{file.pages.default.include}", (err, pages) ->
+ return error err if err
+
+ pagesCount = pages.length
+ for page in pages then do (page) ->
+
+ # again go sync during the replace process
+ fs.readFile page, 'utf8', (err, body) ->
+ return error err if err
+
+ em.emit 'log', 'switch from a regular jquery to minified'
+ body = body.replace /jquery-(\d|\d(\.\d)+)\.js/g, (file, version) ->
+ return "jquery-#{version}.min.js"
+
+ em.emit 'log', 'switch any google CDN reference to minified'
+ body = body.replace /(\d|\d(\.\d)+)\/jquery\.js/g, (match, version) ->
+ return "#{version}/jquery.min.js"
+
+ em.emit 'log', 'Kill off those versioning flags: ?v=2'
+ body = body.replace /\?v=\d+/g, (match) ->
+ return ""
+
+ em.emit 'log', 'Remove favicon.ico reference if it is pointing to the root'
+ body = body.replace /<link rel=["']shortcut icon["'] href=["']\/favicon\.ico["']>/g, (match) ->
+ return ""
+
+ em.emit 'log', "Update the HTML to reference our concatenated script file: #{scripts.js}"
+ body = body.replace /<!-- scripts concatenated[\d\w\s\W]*<!-- end scripts -->/gm, ->
+ return "<script defer src=\"#{dir.js}/#{scripts.js}\"></script>"
+
+ em.emit 'log', "Update the HTML with the new css filename: #{style.css}"
+ body = body.replace /<link rel=["']?stylesheet["']?\shref=["']?(.*)\/style.css["']?\s*>/gm, (match, prefix) ->
+ return "<link rel=\"stylesheet\" href=\"#{prefix}/#{style.css}\">"
+
+ fs.writeFile page, body, (err) ->
+ return error err if err
+ em.emit 'log', "#{page} now referencing minified files"
+ em.emit 'end' if --pagesCount is 0
+
+
+ gem.on 'end:js', handle()
+ gem.on 'end:css', handle()
+
+ invoke 'js'
+ invoke 'css'
+
+# ## html tasks
+
+# ### manifest
+# stumb method for now.
+
+task 'manifest', 'Manifest stuff', (options, em) ->
+
+# ### htmlclean
+#
+# using the fantastic html minifier tool by kangax:: https://github.com/kangax/html-minifier
+task 'htmlclean', 'Peforms basic to aggresive minification', (options, em) ->
+ em.emit 'log', 'Run html-minifier on the HTML with standard options'
+
+ gem.once 'end:mkdirs', ->
+ # get the file to min
+ em.emit 'log', "Run on #{file.pages.default.include}"
+
+ process.chdir path.join(base, dir.intermediate)
+ helper.fileset "#{file.pages.default.include}", (err, pages) ->
+ return error err if err
+
+ remaining = pages.length
+ for page in pages then do (page) ->
+ fs.readFile page, 'utf8', (err, body) ->
+ return error err if err
+
+ # todo: move the configuration over to config files
+ # going with most aggresive minification for now
+
+ output = htmlmin.minify body, {
+ removeComments: true
+ removeCommentsFromCDATA: true
+ removeEmptyAttributes: true
+ cleanAttributes: true
+ removeAttributeQuotes: true
+ removeRedundantAttributes: true
+ removeScriptTypeAttributes: true
+ removeStyleLinkTypeAttributes: true
+ collapseWhitespace: true
+
+ # tests no longer pass when set to true, stripping some scripts
+ # removeEmptyElements: true
+ collapseBooleanAttributes: true
+ removeOptionalTags: true
+ }
+
+ fs.writeFile page, output, (err) ->
+ return error err if err
+ em.emit 'end' if --remaining is 0
+
+ invoke 'mkdirs'
+
View
79 build/cake/README.md
@@ -0,0 +1,79 @@
+## install
+
+In the root of this folder (build/cake), run
+
+ npm install
+
+It'll get the dependencies as defined in the `package.json` file (they're quite a few, this may take a while)
+
+ mkdirp@0.0.6 ./node_modules/mkdirp
+ colors@0.5.0 ./node_modules/colors
+ html-minifier@0.4.5 ./node_modules/html-minifier
+ coffee-script@1.1.2 ./node_modules/coffee-script
+ uglify-js@1.0.7 ./node_modules/uglify-js
+ clean-css@0.2.4 ./node_modules/clean-css
+ └── optimist@0.1.9
+ vows@0.5.11 ./node_modules/vows
+ └── eyes@0.1.6
+ connect@1.7.1 ./node_modules/connect
+ ├── mime@1.2.3
+ └── qs@0.3.1
+ zombie@0.10.1 ./node_modules/zombie
+ ├── mime@1.2.3
+ ├── websocket-client@1.0.0
+ ├── contextify@0.0.5
+ └── jsdom@0.2.4
+ prompt@0.1.8 ./node_modules/prompt
+ ├── pkginfo@0.2.2
+ ├── async@0.1.9
+ └── winston@0.5.0
+ fileset@0.0.1 ./node_modules/fileset
+ ├── glob@2.0.8
+ └── findit@0.1.1
+
+
+### h5bp
+
+cd to `build/cake` and run `cake` to get the following output
+
+ cake docs # Generates the source documentation of this cake script
+ cake build # Build with defaults configuration the main tasks: js, css and img optimiaztion
+ cake js # Combines and minifies JS
+ cake css # Combines and minifies CSS
+ cake img # Performs img optimization
+ cake createproject # a simple create project task
+ cake intro # Kindly inform the developer about the impending magic
+ cake check # Performs few validations upon the current repo, outputing errors if any
+ cake clean # Wipe the previous build
+ cake mkdirs # Create the directory intermediate structure
+ cake js.main.concat # Concatenates the JS files in dir.js
+ cake js.mylibs.concat # Concatenates the JS files in dir.js.mylibs
+ cake js.scripts.concat # Concatenating library file with main script file
+ cake js.all.minify # Minifies the *-concat.js files in intermediate/js
+ cake jshint # jshint task, run jshint on any non min.js file in dir.js
+ cake csslint # csslint task, run csslint on dir.css and ommit *.min.css one
+ cake css.concat # Concat the CSS files depending on the @imports in your file.root.stylesheet
+ cake img.optimize # Run optipng
+ cake usemin # Replaces references to non-minified scripts/styles
+ cake htmlclean # Peforms basic to aggresive minification
+
+ -o, --output directory for the createproject task
+
+This is a quick and dirty implementation, but the following tasks may (or may not) work
+
+* js: concat libs/mylibs files, calculates a new checksum and uglify the file
+* css: a basic @import inline is made, calculates a new checksum then minify
+* img: execute optipng over the .png files in dir.images
+* lint: jshint/csslint
+* usemin: replaces references to non-minified scripts/styles
+* htmlclean: html minification
+
+#### test
+
+ npm test
+
+it'll trigger a first clone/pull/build if needed, and run the vows/zombie test suite.
+
+Check the [tests/build.js](https://github.com/mklabs/cakes/blob/master/h5bp/tests/build.js) file to see the basic asserts
+
+
View
124 build/cake/conf/default.coffee
@@ -0,0 +1,124 @@
+#
+# Default Build Settings
+# you can override these settings on a project basis in a project.properties file
+# so probably best not to touch these as they could be overwritten in later versions!
+#
+
+
+#
+# Directory Paths
+#
+dir = exports.dir = {}; file = exports.file = {}; build = exports.build = {}; tool = exports.tool = {}; scripts = exports.script = {}; images = exports.images = {}; hash = exports.hash = {}
+file.pages = {}; file.pages.default = {}; file.default = {}; file.root = {}; file.default.js = {}
+build.concat = {}; build.version = {}; build.scripts = {}
+scripts.compilation = {}
+images.strip = {}
+
+
+dir.source = "_test"
+dir.intermediate = "intermediate"
+dir.publish = "publish"
+dir.build = "build"
+dir.build.tools = "#{dir.build}/tools"
+dir.test = "test"
+dir.demo = "demo"
+dir.js = { main: 'js', toString: -> 'js'}
+# scripts in the lib directory will only be minified, not concatenated together
+dir.js.libs = "#{dir.js}/libs"
+dir.js.mylibs = "#{dir.js}/mylibs"
+dir.css = "css"
+dir.images = "img"
+
+
+#
+# HTML, PHP, etc files to clean and update script/css references
+#
+file.pages.default.include = "index.html, 404.html"
+
+# You will need to include the property file.pages.include in your project.properties file
+# and add any extra pages you want to be updated by the scripts in a comma separated list
+
+
+# the server configuration you're going with. If you don't use apache,
+# get a different one here: github.com/paulirish/html5-boilerplate-server-configs
+
+file.serverconfig = ".htaccess"
+
+#
+# Files not to be copied over by the script to the publish directory
+
+#file.default.exclude = "node_modules"
+file.default.exclude = ".gitignore .project .settings README.markdown, README.md, **/.git/**, **/.svn/**, #{dir.test}/**, #{dir.demo}/**, #{dir.intermediate}/**, #{dir.publish}/**, #{dir.build}/**, **/nbproject/**, *.komodoproject, **/.komodotools/**, **/dwsync.xml, **_notes, **/.hg/**, **/.idea/**"
+# Declare the file.exclude property in your project.properties file if you want to exclude files / folders you have added
+# Note: you cannot declare an empty file.exclude property
+
+#
+# Bypass Optimization for these files
+#
+file.default.js.bypass = ""
+# If set, these files will not be optimized (minifications, concatinations, image optimizations will not be applied)
+# Note: you cannot declare an empty file.default.bypass property
+
+#
+# Root Script file
+# this is the file that will be swapped for the concatenated and minified javascript.
+#
+file.root.script = "script.js"
+
+#
+# Root Stylesheet
+# this is the file that contains the @import directives
+#
+file.root.stylesheet = "style.css"
+
+#
+# Default Stylesheet
+#
+file.default.stylesheets = ""
+
+#
+# Script Optimisation
+#
+# If set, concat libraries with main scripts file, producing single script file
+build.concat.scripts = true
+
+# default options for closure compiler.
+scripts.compilation.level = "SIMPLE_OPTIMIZATIONS"
+scripts.compilation.warninglevel = "QUIET"
+
+#
+# Image Optimisation
+#
+images.strip.metadata = true
+# Seting this to true will strip the metadata from all jpeg files.
+# YOU SHOULD ONLY DO THIS IF YOU OWN THE COPYRIGHT TO ALL THE IMAGES IN THE BUILD
+
+#
+# Bypass Optimization for these image files or folders
+#
+# images.default.bypass
+# If set, these images will not be optimized
+# Note: you cannot declare an empty images.default.bypass property
+
+
+# Build Info
+build.version.info = "buildinfo.properties"
+build.scripts.dir = "#{dir.build}/build-scripts"
+
+# Tools
+tool.yuicompressor = "yuicompressor-2.4.5.jar"
+tool.htmlcompressor = "htmlcompressor-1.4.3.jar"
+tool.csscompressor = "css-compressor/cli.php"
+tool.rhino = "rhino.jar"
+tool.jslint = "fulljslint.js"
+tool.jshint = "fulljshint.js"
+tool.csslint = "csslint-rhino.js"
+
+# Default Lint Utils Options
+tool.jshint.opts = "maxerr=25,eqeqeq=true"
+tool.jslint.opts = "maxerr=25,evil=true,browser=true,eqeqeq=true,immed=true,newcap=true,nomen=true,es5=true,rhino=true,undef=true,white=false,devel=true"
+tool.csslint.opts = ""
+
+# Default hash length
+hash.length = 7
+
View
596 build/cake/documentation/Cakefile.html
596 additions, 0 deletions not shown
View
45 build/cake/documentation/default.html
@@ -0,0 +1,45 @@
+<!DOCTYPE html> <html> <head> <title>default.coffee</title> <meta http-equiv="content-type" content="text/html; charset=UTF-8"> <link rel="stylesheet" media="all" href="docco.css" /> </head> <body> <div id="container"> <div id="background"></div> <div id="jump_to"> Jump To &hellip; <div id="jump_wrapper"> <div id="jump_page"> <a class="source" href="Cakefile.html"> Cakefile.coffee </a> <a class="source" href="default.html"> default.coffee </a> <a class="source" href="helper.html"> helper.coffee </a> </div> </div> </div> <table cellpadding="0" cellspacing="0"> <thead> <tr> <th class="docs"> <h1> default.coffee </h1> </th> <th class="code"> </th> </tr> </thead> <tbody> <tr id="section-1"> <td class="docs"> <div class="pilwrap"> <a class="pilcrow" href="#section-1">&#182;</a> </div> <p>Default Build Settings
+you can override these settings on a project basis in a project.properties file
+so probably best not to touch these as they could be overwritten in later versions!</p> </td> <td class="code"> <div class="highlight"><pre></pre></div> </td> </tr> <tr id="section-2"> <td class="docs"> <div class="pilwrap"> <a class="pilcrow" href="#section-2">&#182;</a> </div> <p>Directory Paths</p> </td> <td class="code"> <div class="highlight"><pre><span class="nv">dir = exports.dir = </span><span class="p">{};</span> <span class="nv">file = exports.file = </span><span class="p">{};</span> <span class="nv">build = exports.build = </span><span class="p">{};</span> <span class="nv">tool = exports.tool = </span><span class="p">{};</span> <span class="nv">scripts = exports.script = </span><span class="p">{};</span> <span class="nv">images = exports.images = </span><span class="p">{};</span> <span class="nv">hash = exports.hash = </span><span class="p">{}</span>
+<span class="nv">file.pages = </span><span class="p">{};</span> <span class="nv">file.pages.default = </span><span class="p">{};</span> <span class="nv">file.default = </span><span class="p">{};</span> <span class="nv">file.root = </span><span class="p">{};</span> <span class="nv">file.default.js = </span><span class="p">{}</span>
+<span class="nv">build.concat = </span><span class="p">{};</span> <span class="nv">build.version = </span><span class="p">{};</span> <span class="nv">build.scripts = </span><span class="p">{}</span>
+<span class="nv">scripts.compilation = </span><span class="p">{}</span>
+<span class="nv">images.strip = </span><span class="p">{}</span>
+
+
+<span class="nv">dir.source = </span><span class="s2">&quot;_test&quot;</span>
+<span class="nv">dir.intermediate = </span><span class="s2">&quot;intermediate&quot;</span>
+<span class="nv">dir.publish = </span><span class="s2">&quot;publish&quot;</span>
+<span class="nv">dir.build = </span><span class="s2">&quot;build&quot;</span>
+<span class="nv">dir.build.tools = </span><span class="s2">&quot;#{dir.build}/tools&quot;</span>
+<span class="nv">dir.test = </span><span class="s2">&quot;test&quot;</span>
+<span class="nv">dir.demo = </span><span class="s2">&quot;demo&quot;</span>
+<span class="nv">dir.js = </span><span class="p">{</span> <span class="nv">main: </span><span class="s1">&#39;js&#39;</span><span class="p">,</span> <span class="nv">toString: </span><span class="o">-&gt;</span> <span class="s1">&#39;js&#39;</span><span class="p">}</span></pre></div> </td> </tr> <tr id="section-3"> <td class="docs"> <div class="pilwrap"> <a class="pilcrow" href="#section-3">&#182;</a> </div> <p>scripts in the lib directory will only be minified, not concatenated together</p> </td> <td class="code"> <div class="highlight"><pre><span class="nv">dir.js.libs = </span><span class="s2">&quot;#{dir.js}/libs&quot;</span>
+<span class="nv">dir.js.mylibs = </span><span class="s2">&quot;#{dir.js}/mylibs&quot;</span>
+<span class="nv">dir.css = </span><span class="s2">&quot;css&quot;</span>
+<span class="nv">dir.images = </span><span class="s2">&quot;img&quot;</span></pre></div> </td> </tr> <tr id="section-4"> <td class="docs"> <div class="pilwrap"> <a class="pilcrow" href="#section-4">&#182;</a> </div> <p>HTML, PHP, etc files to clean and update script/css references</p> </td> <td class="code"> <div class="highlight"><pre><span class="nv">file.pages.default.include = </span><span class="s2">&quot;index.html, 404.html&quot;</span></pre></div> </td> </tr> <tr id="section-5"> <td class="docs"> <div class="pilwrap"> <a class="pilcrow" href="#section-5">&#182;</a> </div> <p>You will need to include the property file.pages.include in your project.properties file
+and add any extra pages you want to be updated by the scripts in a comma separated list</p> </td> <td class="code"> <div class="highlight"><pre></pre></div> </td> </tr> <tr id="section-6"> <td class="docs"> <div class="pilwrap"> <a class="pilcrow" href="#section-6">&#182;</a> </div> <p>the server configuration you're going with. If you don't use apache,
+ get a different one here: github.com/paulirish/html5-boilerplate-server-configs</p> </td> <td class="code"> <div class="highlight"><pre><span class="nv">file.serverconfig = </span><span class="s2">&quot;.htaccess&quot;</span></pre></div> </td> </tr> <tr id="section-7"> <td class="docs"> <div class="pilwrap"> <a class="pilcrow" href="#section-7">&#182;</a> </div> <p>Files not to be copied over by the script to the publish directory</p> </td> <td class="code"> <div class="highlight"><pre></pre></div> </td> </tr> <tr id="section-8"> <td class="docs"> <div class="pilwrap"> <a class="pilcrow" href="#section-8">&#182;</a> </div> <p>file.default.exclude = "node_modules"</p> </td> <td class="code"> <div class="highlight"><pre><span class="nv">file.default.exclude = </span><span class="s2">&quot;.gitignore .project .settings README.markdown, README.md, **/.git/**, **/.svn/**, #{dir.test}/**, #{dir.demo}/**, #{dir.intermediate}/**, #{dir.publish}/**, #{dir.build}/**, **/nbproject/**, *.komodoproject, **/.komodotools/**, **/dwsync.xml, **_notes, **/.hg/**, **/.idea/**&quot;</span></pre></div> </td> </tr> <tr id="section-9"> <td class="docs"> <div class="pilwrap"> <a class="pilcrow" href="#section-9">&#182;</a> </div> <p>Declare the file.exclude property in your project.properties file if you want to exclude files / folders you have added
+Note: you cannot declare an empty file.exclude property</p> </td> <td class="code"> <div class="highlight"><pre></pre></div> </td> </tr> <tr id="section-10"> <td class="docs"> <div class="pilwrap"> <a class="pilcrow" href="#section-10">&#182;</a> </div> <p>Bypass Optimization for these files</p> </td> <td class="code"> <div class="highlight"><pre><span class="nv">file.default.js.bypass = </span><span class="s2">&quot;&quot;</span></pre></div> </td> </tr> <tr id="section-11"> <td class="docs"> <div class="pilwrap"> <a class="pilcrow" href="#section-11">&#182;</a> </div> <p>If set, these files will not be optimized (minifications, concatinations, image optimizations will not be applied)
+Note: you cannot declare an empty file.default.bypass property</p> </td> <td class="code"> <div class="highlight"><pre></pre></div> </td> </tr> <tr id="section-12"> <td class="docs"> <div class="pilwrap"> <a class="pilcrow" href="#section-12">&#182;</a> </div> <p>Root Script file
+this is the file that will be swapped for the concatenated and minified javascript.</p> </td> <td class="code"> <div class="highlight"><pre><span class="nv">file.root.script = </span><span class="s2">&quot;script.js&quot;</span></pre></div> </td> </tr> <tr id="section-13"> <td class="docs"> <div class="pilwrap"> <a class="pilcrow" href="#section-13">&#182;</a> </div> <p>Root Stylesheet
+this is the file that contains the @import directives</p> </td> <td class="code"> <div class="highlight"><pre><span class="nv">file.root.stylesheet = </span><span class="s2">&quot;style.css&quot;</span></pre></div> </td> </tr> <tr id="section-14"> <td class="docs"> <div class="pilwrap"> <a class="pilcrow" href="#section-14">&#182;</a> </div> <p>Default Stylesheet</p> </td> <td class="code"> <div class="highlight"><pre><span class="nv">file.default.stylesheets = </span><span class="s2">&quot;&quot;</span></pre></div> </td> </tr> <tr id="section-15"> <td class="docs"> <div class="pilwrap"> <a class="pilcrow" href="#section-15">&#182;</a> </div> <p>Script Optimisation</p>
+
+<p>If set, concat libraries with main scripts file, producing single script file</p> </td> <td class="code"> <div class="highlight"><pre><span class="nv">build.concat.scripts = </span><span class="kc">true</span></pre></div> </td> </tr> <tr id="section-16"> <td class="docs"> <div class="pilwrap"> <a class="pilcrow" href="#section-16">&#182;</a> </div> <p>default options for closure compiler.</p> </td> <td class="code"> <div class="highlight"><pre><span class="nv">scripts.compilation.level = </span><span class="s2">&quot;SIMPLE_OPTIMIZATIONS&quot;</span>
+<span class="nv">scripts.compilation.warninglevel = </span><span class="s2">&quot;QUIET&quot;</span></pre></div> </td> </tr> <tr id="section-17"> <td class="docs"> <div class="pilwrap"> <a class="pilcrow" href="#section-17">&#182;</a> </div> <p>Image Optimisation</p> </td> <td class="code"> <div class="highlight"><pre><span class="nv">images.strip.metadata = </span><span class="kc">true</span></pre></div> </td> </tr> <tr id="section-18"> <td class="docs"> <div class="pilwrap"> <a class="pilcrow" href="#section-18">&#182;</a> </div> <p>Seting this to true will strip the metadata from all jpeg files.
+YOU SHOULD ONLY DO THIS IF YOU OWN THE COPYRIGHT TO ALL THE IMAGES IN THE BUILD</p> </td> <td class="code"> <div class="highlight"><pre></pre></div> </td> </tr> <tr id="section-19"> <td class="docs"> <div class="pilwrap"> <a class="pilcrow" href="#section-19">&#182;</a> </div> <p>Bypass Optimization for these image files or folders</p>
+
+<p>images.default.bypass
+If set, these images will not be optimized
+Note: you cannot declare an empty images.default.bypass property</p> </td> <td class="code"> <div class="highlight"><pre></pre></div> </td> </tr> <tr id="section-20"> <td class="docs"> <div class="pilwrap"> <a class="pilcrow" href="#section-20">&#182;</a> </div> <p>Build Info</p> </td> <td class="code"> <div class="highlight"><pre><span class="nv">build.version.info = </span><span class="s2">&quot;buildinfo.properties&quot;</span>
+<span class="nv">build.scripts.dir = </span><span class="s2">&quot;#{dir.build}/build-scripts&quot;</span></pre></div> </td> </tr> <tr id="section-21"> <td class="docs"> <div class="pilwrap"> <a class="pilcrow" href="#section-21">&#182;</a> </div> <p>Tools</p> </td> <td class="code"> <div class="highlight"><pre><span class="nv">tool.yuicompressor = </span><span class="s2">&quot;yuicompressor-2.4.5.jar&quot;</span>
+<span class="nv">tool.htmlcompressor = </span><span class="s2">&quot;htmlcompressor-1.4.3.jar&quot;</span>
+<span class="nv">tool.csscompressor = </span><span class="s2">&quot;css-compressor/cli.php&quot;</span>
+<span class="nv">tool.rhino = </span><span class="s2">&quot;rhino.jar&quot;</span>
+<span class="nv">tool.jslint = </span><span class="s2">&quot;fulljslint.js&quot;</span>
+<span class="nv">tool.jshint = </span><span class="s2">&quot;fulljshint.js&quot;</span>
+<span class="nv">tool.csslint = </span><span class="s2">&quot;csslint-rhino.js&quot;</span></pre></div> </td> </tr> <tr id="section-22"> <td class="docs"> <div class="pilwrap"> <a class="pilcrow" href="#section-22">&#182;</a> </div> <p>Default Lint Utils Options</p> </td> <td class="code"> <div class="highlight"><pre><span class="nv">tool.jshint.opts = </span><span class="s2">&quot;maxerr=25,eqeqeq=true&quot;</span>
+<span class="nv">tool.jslint.opts = </span><span class="s2">&quot;maxerr=25,evil=true,browser=true,eqeqeq=true,immed=true,newcap=true,nomen=true,es5=true,rhino=true,undef=true,white=false,devel=true&quot;</span>
+<span class="nv">tool.csslint.opts = </span><span class="s2">&quot;&quot;</span></pre></div> </td> </tr> <tr id="section-23"> <td class="docs"> <div class="pilwrap"> <a class="pilcrow" href="#section-23">&#182;</a> </div> <p>Default hash length</p> </td> <td class="code"> <div class="highlight"><pre><span class="nv">hash.length = </span><span class="mi">7</span>
+
+</pre></div> </td> </tr> </tbody> </table> </div> </body> </html>
View
186 build/cake/documentation/docco.css
@@ -0,0 +1,186 @@
+/*--------------------- Layout and Typography ----------------------------*/
+body {
+ font-family: 'Palatino Linotype', 'Book Antiqua', Palatino, FreeSerif, serif;
+ font-size: 15px;
+ line-height: 22px;
+ color: #252519;
+ margin: 0; padding: 0;
+}
+a {
+ color: #261a3b;
+}
+ a:visited {
+ color: #261a3b;
+ }
+p {
+ margin: 0 0 15px 0;
+}
+h1, h2, h3, h4, h5, h6 {
+ margin: 0px 0 15px 0;
+}
+ h1 {
+ margin-top: 40px;
+ }
+#container {
+ position: relative;
+}
+#background {
+ position: fixed;
+ top: 0; left: 525px; right: 0; bottom: 0;
+ background: #f5f5ff;
+ border-left: 1px solid #e5e5ee;
+ z-index: -1;
+}
+#jump_to, #jump_page {
+ background: white;
+ -webkit-box-shadow: 0 0 25px #777; -moz-box-shadow: 0 0 25px #777;
+ -webkit-border-bottom-left-radius: 5px; -moz-border-radius-bottomleft: 5px;
+ font: 10px Arial;
+ text-transform: uppercase;
+ cursor: pointer;
+ text-align: right;
+}
+#jump_to, #jump_wrapper {
+ position: fixed;
+ right: 0; top: 0;
+ padding: 5px 10px;
+}
+ #jump_wrapper {
+ padding: 0;
+ display: none;
+ }
+ #jump_to:hover #jump_wrapper {
+ display: block;
+ }
+ #jump_page {
+ padding: 5px 0 3px;
+ margin: 0 0 25px 25px;
+ }
+ #jump_page .source {
+ display: block;
+ padding: 5px 10px;
+ text-decoration: none;
+ border-top: 1px solid #eee;
+ }
+ #jump_page .source:hover {
+ background: #f5f5ff;
+ }
+ #jump_page .source:first-child {
+ }
+table td {
+ border: 0;
+ outline: 0;
+}
+ td.docs, th.docs {
+ max-width: 450px;
+ min-width: 450px;
+ min-height: 5px;
+ padding: 10px 25px 1px 50px;
+ overflow-x: hidden;
+ vertical-align: top;
+ text-align: left;
+ }
+ .docs pre {
+ margin: 15px 0 15px;
+ padding-left: 15px;
+ }
+ .docs p tt, .docs p code {
+ background: #f8f8ff;
+ border: 1px solid #dedede;
+ font-size: 12px;
+ padding: 0 0.2em;
+ }
+ .pilwrap {
+ position: relative;
+ }
+ .pilcrow {
+ font: 12px Arial;
+ text-decoration: none;
+ color: #454545;
+ position: absolute;
+ top: 3px; left: -20px;
+ padding: 1px 2px;
+ opacity: 0;
+ -webkit-transition: opacity 0.2s linear;
+ }
+ td.docs:hover .pilcrow {
+ opacity: 1;
+ }
+ td.code, th.code {
+ padding: 14px 15px 16px 25px;
+ width: 100%;
+ vertical-align: top;
+ background: #f5f5ff;
+ border-left: 1px solid #e5e5ee;
+ }
+ pre, tt, code {
+ font-size: 12px; line-height: 18px;
+ font-family: Monaco, Consolas, "Lucida Console", monospace;
+ margin: 0; padding: 0;
+ }
+
+
+/*---------------------- Syntax Highlighting -----------------------------*/
+td.linenos { background-color: #f0f0f0; padding-right: 10px; }
+span.lineno { background-color: #f0f0f0; padding: 0 5px 0 5px; }
+body .hll { background-color: #ffffcc }
+body .c { color: #408080; font-style: italic } /* Comment */
+body .err { border: 1px solid #FF0000 } /* Error */
+body .k { color: #954121 } /* Keyword */
+body .o { color: #666666 } /* Operator */
+body .cm { color: #408080; font-style: italic } /* Comment.Multiline */
+body .cp { color: #BC7A00 } /* Comment.Preproc */
+body .c1 { color: #408080; font-style: italic } /* Comment.Single */
+body .cs { color: #408080; font-style: italic } /* Comment.Special */
+body .gd { color: #A00000 } /* Generic.Deleted */
+body .ge { font-style: italic } /* Generic.Emph */
+body .gr { color: #FF0000 } /* Generic.Error */
+body .gh { color: #000080; font-weight: bold } /* Generic.Heading */
+body .gi { color: #00A000 } /* Generic.Inserted */
+body .go { color: #808080 } /* Generic.Output */
+body .gp { color: #000080; font-weight: bold } /* Generic.Prompt */
+body .gs { font-weight: bold } /* Generic.Strong */
+body .gu { color: #800080; font-weight: bold } /* Generic.Subheading */
+body .gt { color: #0040D0 } /* Generic.Traceback */
+body .kc { color: #954121 } /* Keyword.Constant */
+body .kd { color: #954121; font-weight: bold } /* Keyword.Declaration */
+body .kn { color: #954121; font-weight: bold } /* Keyword.Namespace */
+body .kp { color: #954121 } /* Keyword.Pseudo */
+body .kr { color: #954121; font-weight: bold } /* Keyword.Reserved */
+body .kt { color: #B00040 } /* Keyword.Type */
+body .m { color: #666666 } /* Literal.Number */
+body .s { color: #219161 } /* Literal.String */
+body .na { color: #7D9029 } /* Name.Attribute */
+body .nb { color: #954121 } /* Name.Builtin */
+body .nc { color: #0000FF; font-weight: bold } /* Name.Class */
+body .no { color: #880000 } /* Name.Constant */
+body .nd { color: #AA22FF } /* Name.Decorator */
+body .ni { color: #999999; font-weight: bold } /* Name.Entity */
+body .ne { color: #D2413A; font-weight: bold } /* Name.Exception */
+body .nf { color: #0000FF } /* Name.Function */
+body .nl { color: #A0A000 } /* Name.Label */
+body .nn { color: #0000FF; font-weight: bold } /* Name.Namespace */
+body .nt { color: #954121; font-weight: bold } /* Name.Tag */
+body .nv { color: #19469D } /* Name.Variable */
+body .ow { color: #AA22FF; font-weight: bold } /* Operator.Word */
+body .w { color: #bbbbbb } /* Text.Whitespace */
+body .mf { color: #666666 } /* Literal.Number.Float */
+body .mh { color: #666666 } /* Literal.Number.Hex */
+body .mi { color: #666666 } /* Literal.Number.Integer */
+body .mo { color: #666666 } /* Literal.Number.Oct */
+body .sb { color: #219161 } /* Literal.String.Backtick */
+body .sc { color: #219161 } /* Literal.String.Char */
+body .sd { color: #219161; font-style: italic } /* Literal.String.Doc */
+body .s2 { color: #219161 } /* Literal.String.Double */
+body .se { color: #BB6622; font-weight: bold } /* Literal.String.Escape */
+body .sh { color: #219161 } /* Literal.String.Heredoc */
+body .si { color: #BB6688; font-weight: bold } /* Literal.String.Interpol */
+body .sx { color: #954121 } /* Literal.String.Other */
+body .sr { color: #BB6688 } /* Literal.String.Regex */
+body .s1 { color: #219161 } /* Literal.String.Single */
+body .ss { color: #19469D } /* Literal.String.Symbol */
+body .bp { color: #954121 } /* Name.Builtin.Pseudo */
+body .vc { color: #19469D } /* Name.Variable.Class */
+body .vg { color: #19469D } /* Name.Variable.Global */
+body .vi { color: #19469D } /* Name.Variable.Instance */
+body .il { color: #666666 } /* Literal.Number.Integer.Long */
View
65 build/cake/documentation/helper.html
@@ -0,0 +1,65 @@
+<!DOCTYPE html> <html> <head> <title>helper.coffee</title> <meta http-equiv="content-type" content="text/html; charset=UTF-8"> <link rel="stylesheet" media="all" href="docco.css" /> </head> <body> <div id="container"> <div id="background"></div> <div id="jump_to"> Jump To &hellip; <div id="jump_wrapper"> <div id="jump_page"> <a class="source" href="Cakefile.html"> Cakefile.coffee </a> <a class="source" href="default.html"> default.coffee </a> <a class="source" href="helper.html"> helper.coffee </a> </div> </div> </div> <table cellpadding="0" cellspacing="0"> <thead> <tr> <th class="docs"> <h1> helper.coffee </h1> </th> <th class="code"> </th> </tr> </thead> <tbody> <tr id="section-1"> <td class="docs"> <div class="pilwrap"> <a class="pilcrow" href="#section-1">&#182;</a> </div> </td> <td class="code"> <div class="highlight"><pre></pre></div> </td> </tr> <tr id="section-2"> <td class="docs"> <div class="pilwrap"> <a class="pilcrow" href="#section-2">&#182;</a> </div> <p><code>helpers</code> expose a basic wrapper around node-glob by isaacs and
+findit by substack. Enable multiple pattern matching, and include
+exlude ability.</p> </td> <td class="code"> <div class="highlight"><pre></pre></div> </td> </tr> <tr id="section-3"> <td class="docs"> <div class="pilwrap"> <a class="pilcrow" href="#section-3">&#182;</a> </div> <p>External dependency</p> </td> <td class="code"> <div class="highlight"><pre><span class="p">{</span><span class="nx">EventEmitter</span><span class="p">}</span> <span class="o">=</span> <span class="nx">require</span> <span class="s1">&#39;events&#39;</span>
+<span class="nv">path = </span><span class="nx">require</span> <span class="s1">&#39;path&#39;</span>
+<span class="nv">fs = </span><span class="nx">require</span> <span class="s1">&#39;fs&#39;</span>
+<span class="nv">crypto = </span><span class="nx">require</span> <span class="s1">&#39;crypto&#39;</span>
+<span class="nv">child = </span><span class="nx">require</span> <span class="s1">&#39;child_process&#39;</span></pre></div> </td> </tr> <tr id="section-4"> <td class="docs"> <div class="pilwrap"> <a class="pilcrow" href="#section-4">&#182;</a> </div> <h2>fileset</h2>
+
+<p>expose filset module as helper method</p> </td> <td class="code"> <div class="highlight"><pre><span class="nv">exports.fileset = </span><span class="nx">require</span> <span class="s1">&#39;fileset&#39;</span></pre></div> </td> </tr> <tr id="section-5"> <td class="docs"> <div class="pilwrap"> <a class="pilcrow" href="#section-5">&#182;</a> </div> <h2>concat</h2>
+
+<p>ease the concatenation process by taking a list of absolute path, and returning
+an array of Buffers. There is no order management yet.</p> </td> <td class="code"> <div class="highlight"><pre><span class="nv">exports.concat = </span><span class="nf">(files, callback) -&gt;</span>
+ <span class="k">return</span> <span class="nx">callback</span> <span class="k">new</span> <span class="nb">Error</span><span class="p">(</span><span class="s1">&#39;concat: Files array is empty&#39;</span><span class="p">)</span> <span class="nx">unless</span> <span class="nx">files</span><span class="p">.</span><span class="nx">length</span>
+
+ <span class="nv">output = </span><span class="p">[]</span>
+ <span class="nv">remaining = </span><span class="nx">files</span><span class="p">.</span><span class="nx">length</span>
+ <span class="nv">onFile = </span><span class="nf">(err, body) -&gt;</span>
+ <span class="k">return</span> <span class="nx">callback</span> <span class="nx">err</span> <span class="k">if</span> <span class="nx">err</span>
+ <span class="nx">output</span><span class="p">.</span><span class="nx">push</span> <span class="nx">body</span>
+ <span class="nx">callback</span> <span class="kc">null</span><span class="p">,</span> <span class="nx">output</span> <span class="k">if</span> <span class="o">--</span><span class="nx">remaining</span> <span class="o">is</span> <span class="mi">0</span>
+
+ <span class="nx">fs</span><span class="p">.</span><span class="nx">readFile</span> <span class="nx">file</span><span class="p">,</span> <span class="nx">onFile</span> <span class="k">for</span> <span class="nx">file</span> <span class="k">in</span> <span class="nx">files</span></pre></div> </td> </tr> <tr id="section-6"> <td class="docs"> <div class="pilwrap"> <a class="pilcrow" href="#section-6">&#182;</a> </div> <h2>checksum</h2>
+
+<p>Simple checksum helper, takes an absolute path, open a fileStream,
+and generates back the according <code>md5</code> hash, <code>hex</code> encoded.</p> </td> <td class="code"> <div class="highlight"><pre><span class="nv">exports.checksum = </span><span class="nf">(file, callback) -&gt;</span>
+ <span class="nv">md5 = </span><span class="nx">crypto</span><span class="p">.</span><span class="nx">createHash</span> <span class="s1">&#39;md5&#39;</span></pre></div> </td> </tr> <tr id="section-7"> <td class="docs"> <div class="pilwrap"> <a class="pilcrow" href="#section-7">&#182;</a> </div> <p>assume file is the actual content if no callback is provided</p> </td> <td class="code"> <div class="highlight"><pre> <span class="nx">unless</span> <span class="nx">callback</span>
+ <span class="nx">md5</span><span class="p">.</span><span class="nx">update</span> <span class="nx">file</span>
+ <span class="k">return</span> <span class="nx">md5</span><span class="p">.</span><span class="nx">digest</span> <span class="s1">&#39;hex&#39;</span>
+
+ <span class="nx">fs</span><span class="p">.</span><span class="nx">createReadStream</span><span class="p">(</span><span class="nx">file</span><span class="p">)</span>
+ <span class="p">.</span><span class="kc">on</span><span class="p">(</span><span class="s1">&#39;error&#39;</span><span class="p">,</span> <span class="nf">(err) -&gt;</span> <span class="nx">callback</span> <span class="nx">err</span><span class="p">)</span>
+ <span class="p">.</span><span class="kc">on</span><span class="p">(</span><span class="s1">&#39;data&#39;</span><span class="p">,</span> <span class="nf">(data) -&gt;</span> <span class="nx">md5</span><span class="p">.</span><span class="nx">update</span> <span class="nx">data</span><span class="p">)</span>
+ <span class="p">.</span><span class="kc">on</span><span class="p">(</span><span class="s1">&#39;end&#39;</span><span class="p">,</span> <span class="nf">() -&gt;</span> <span class="nx">callback</span> <span class="kc">null</span><span class="p">,</span> <span class="nx">md5</span><span class="p">.</span><span class="nx">digest</span><span class="p">(</span><span class="s1">&#39;hex&#39;</span><span class="p">))</span></pre></div> </td> </tr> <tr id="section-8"> <td class="docs"> <div class="pilwrap"> <a class="pilcrow" href="#section-8">&#182;</a> </div> <h2>copy</h2>
+
+<p>Takes two path parameters: from and to, opens a ReadStream on from, a WriteStream on to,
+and pipes the ReadStream to the WriteStream. <strong>files must exists</strong></p> </td> <td class="code"> <div class="highlight"><pre><span class="nv">exports.copy = </span><span class="nf">(from, to, callback) -&gt;</span>
+ <span class="nv">fstream = </span><span class="nx">fs</span><span class="p">.</span><span class="nx">createReadStream</span><span class="p">(</span><span class="nx">from</span><span class="p">)</span>
+ <span class="nv">wstream = </span><span class="nx">fs</span><span class="p">.</span><span class="nx">createWriteStream</span><span class="p">(</span><span class="nx">to</span><span class="p">)</span>
+
+ <span class="nx">fstream</span><span class="p">.</span><span class="nx">pipe</span> <span class="nx">wstream</span>
+ <span class="nx">fstream</span>
+ <span class="p">.</span><span class="kc">on</span><span class="p">(</span><span class="s1">&#39;end&#39;</span><span class="p">,</span> <span class="o">-&gt;</span> <span class="nx">callback</span> <span class="kc">null</span> <span class="p">)</span>
+ <span class="p">.</span><span class="kc">on</span><span class="p">(</span><span class="s1">&#39;error&#39;</span><span class="p">,</span> <span class="nf">(err) -&gt;</span> <span class="nx">callback</span> <span class="nx">err</span><span class="p">)</span>
+
+
+<span class="nv">exports.spawn = </span><span class="nf">(cmd, args, callback) -&gt;</span>
+ <span class="nv">stderr = </span><span class="p">[]</span>
+ <span class="nv">stdout = </span><span class="p">[]</span>
+ <span class="nv">ch = </span><span class="nx">child</span><span class="p">.</span><span class="nx">spawn</span><span class="p">(</span><span class="nx">cmd</span><span class="p">,</span> <span class="nx">args</span><span class="p">)</span>
+
+ <span class="nx">ch</span><span class="p">.</span><span class="nx">stdout</span><span class="p">.</span><span class="nx">pipe</span> <span class="nx">process</span><span class="p">.</span><span class="nx">stdout</span><span class="p">,</span> <span class="p">{</span><span class="nv">end: </span><span class="kc">false</span><span class="p">}</span>
+ <span class="nx">ch</span><span class="p">.</span><span class="nx">stderr</span><span class="p">.</span><span class="nx">pipe</span> <span class="nx">process</span><span class="p">.</span><span class="nx">stderr</span>
+ <span class="nx">ch</span><span class="p">.</span><span class="nx">stdout</span><span class="p">.</span><span class="kc">on</span> <span class="s1">&#39;data&#39;</span><span class="p">,</span> <span class="nf">(data) -&gt;</span> <span class="nx">stdout</span><span class="p">[</span><span class="nx">stdout</span><span class="p">.</span><span class="nx">length</span><span class="p">]</span> <span class="o">=</span> <span class="nx">data</span>
+ <span class="nx">ch</span><span class="p">.</span><span class="nx">stderr</span><span class="p">.</span><span class="kc">on</span> <span class="s1">&#39;data&#39;</span><span class="p">,</span> <span class="nf">(data) -&gt;</span> <span class="nx">stderr</span><span class="p">[</span><span class="nx">stderr</span><span class="p">.</span><span class="nx">length</span><span class="p">]</span> <span class="o">=</span> <span class="nx">data</span>
+
+ <span class="nx">ch</span><span class="p">.</span><span class="kc">on</span> <span class="s1">&#39;exit&#39;</span><span class="p">,</span> <span class="nf">(code) -&gt;</span>
+ <span class="nv">stdout = </span><span class="nx">stdout</span><span class="p">.</span><span class="nx">join</span> <span class="s1">&#39;\n&#39;</span>
+ <span class="nv">stderr = </span><span class="nx">stderr</span><span class="p">.</span><span class="nx">join</span> <span class="s1">&#39;\n&#39;</span>
+
+ <span class="nx">callback</span> <span class="nx">code</span><span class="p">,</span> <span class="nx">stdout</span><span class="p">,</span> <span class="nx">stderr</span> <span class="k">if</span> <span class="nx">callback</span>
+ <span class="nx">ch</span><span class="p">.</span><span class="nx">emit</span> <span class="s1">&#39;end&#39;</span><span class="p">,</span> <span class="nx">code</span><span class="p">,</span> <span class="nx">stdout</span><span class="p">,</span> <span class="nx">stderr</span>
+
+ <span class="k">return</span> <span class="nx">ch</span>
+
+</pre></div> </td> </tr> </tbody> </table> </div> </body> </html>
View
84 build/cake/helper.coffee
@@ -0,0 +1,84 @@
+
+# `helpers` expose a basic wrapper around node-glob by isaacs and
+# findit by substack. Enable multiple pattern matching, and include
+# exlude ability.
+
+
+# External dependency
+{EventEmitter} = require 'events'
+path = require 'path'
+fs = require 'fs'
+crypto = require 'crypto'
+child = require 'child_process'
+
+# ## fileset
+# expose filset module as helper method
+exports.fileset = require 'fileset'
+
+
+# ## concat
+# ease the concatenation process by taking a list of absolute path, and returning
+# an array of Buffers. There is no order management yet.
+exports.concat = (files, callback) ->
+ return callback new Error('concat: Files array is empty') unless files.length
+
+ output = []
+ remaining = files.length
+ onFile = (err, body) ->
+ return callback err if err
+ output.push body
+ callback null, output if --remaining is 0
+
+ fs.readFile file, onFile for file in files
+
+# ## checksum
+# Simple checksum helper, takes an absolute path, open a fileStream,
+# and generates back the according `md5` hash, `hex` encoded.
+exports.checksum = (file, callback) ->
+ md5 = crypto.createHash 'md5'
+
+ # assume file is the actual content if no callback is provided
+ unless callback
+ md5.update file
+ return md5.digest 'hex'
+
+ fs.createReadStream(file)
+ .on('error', (err) -> callback err)
+ .on('data', (data) -> md5.update data)
+ .on('end', () -> callback null, md5.digest('hex'))
+
+
+
+# ## copy
+#
+# Takes two path parameters: from and to, opens a ReadStream on from, a WriteStream on to,
+# and pipes the ReadStream to the WriteStream. **files must exists**
+#
+exports.copy = (from, to, callback) ->
+ fstream = fs.createReadStream(from)
+ wstream = fs.createWriteStream(to)
+
+ fstream.pipe wstream
+ fstream
+ .on('end', -> callback null )
+ .on('error', (err) -> callback err)
+
+
+exports.spawn = (cmd, args, callback) ->
+ stderr = []
+ stdout = []
+ ch = child.spawn(cmd, args)
+
+ ch.stdout.pipe process.stdout, {end: false}
+ ch.stderr.pipe process.stderr
+ ch.stdout.on 'data', (data) -> stdout[stdout.length] = data
+ ch.stderr.on 'data', (data) -> stderr[stderr.length] = data
+
+ ch.on 'exit', (code) ->
+ stdout = stdout.join '\n'
+ stderr = stderr.join '\n'
+
+ callback code, stdout, stderr if callback
+ ch.emit 'end', code, stdout, stderr
+
+ return ch
View
33 build/cake/package.json
@@ -0,0 +1,33 @@
+{
+ "author": "",
+ "name": "cakes",
+ "description": "collection of cake files, mainly trying to learn this cool stuff",
+ "version": "0.0.0",
+ "repository": {
+ "url": ""
+ },
+ "engines": {
+ "node": "> 0.4"
+ },
+ "scripts": { "test": "vows tests/*.js --spec" },
+ "config": {
+ "port": 8080,
+ "host": "localhost",
+ "root": "publish"
+ },
+ "dependencies": {
+ "fileset": "0.0.x",
+ "colors": "0.5.x",
+ "prompt": "0.1.x",
+ "mkdirp": "0.0.6",
+ "uglify-js": "1.0.x",
+ "clean-css": "0.2.4",
+ "html-minifier": "0.4.x"
+ },
+ "devDependencies": {
+ "coffee-script": "1.1.2",
+ "connect": "1.7.x",
+ "zombie": "0.10.1",
+ "vows": "0.5.x"
+ }
+}
View
45 build/cake/tests/build.js
@@ -0,0 +1,45 @@
+var zombie = require('zombie'),
+ colors = require('colors'),
+ assert = require('assert'),
+ vows = require('vows'),
+ macros = require('./helpers/macros');
+
+// basic configuration value
+var port = process.env.npm_package_config_port || 8080,
+ host = process.env.npm_package_config_host || 'localhost',
+ root = process.env.npm_package_config_root || 'publish';
+
+// error helper, assert fails, output err.message to the console
+// and exists process with exit code > 0
+var error = function(err) {
+ console.error(err.message);
+ process.exit(1);
+};
+
+vows.describe("build/basics").addBatch({
+ "when hitting localhost:8080": macros.assertListen(port, root, {
+ "and using zombie.js": macros.assertZombie(host, port, {
+ "to so some tests on these scripts/links": function(err, browser, status) {
+ // Load the page from localhost
+ if(err) return error(err);
+
+ var $ = function qsa(selector) {
+ return Array.prototype.slice.call(browser.querySelectorAll(selector));
+ };
+
+ var scripts = $('script[src]');
+
+ assert.ok($('script[src*=modernizr]').length, 'modernizr');
+ assert.ok(scripts.length, 'should have some script with src attribute'.red.bold);
+ assert.ok($('link[href]').length, 'should have some links with href attribute'.red.bold);
+
+ scripts.forEach(function(item) {
+ var text = item.outerHTML;
+ // prevent assert on ga.js
+ assert.ok(text.match(/\.min.js/), text.red.bold + ' does not reference min file'.red.bold);
+ });
+ }
+ })
+ })
+}).export(module);
+
View
124 build/cake/tests/helpers/macros.js
@@ -0,0 +1,124 @@
+var zombie = require('zombie'),
+ colors = require('colors'),
+ connect = require('connect'),
+ path = require('path'),
+ child = require('child_process'),
+ exec = require('child_process').exec,
+ EventEmitter = require('events').EventEmitter;
+
+// ## macros
+//
+// and vows helpers. defines a few macros to help in tests
+var macros = module.exports = {};
+
+// **extendContext**: private module scope helper. copy over the properties from vows to context.
+//
+// borrowed to [hook.io macros](https://github.com/hookio/hook.io/blob/master/test/helpers/macros.js#170)
+var extendContext = function extendContext (context, vows) {
+ if (vows && vows.topic) {
+ console.error('Cannot include topic at top-level of nested vows:'.red);
+ process.exit(1);
+ }
+
+ Object.keys(vows).forEach(function (key) {
+ context[key] = vows[key];
+ });
+
+ return context;
+};
+
+var spawn = function spawn(cmd, args, callback) {
+ var stderr = [], stdout = [],
+ ch = child.spawn(cmd, args);
+
+ ch.stdout.pipe(process.stdout, {end: false});
+ ch.stderr.pipe(process.stderr);
+ ch.stdout.on('data', function(data) { stdout[stdout.length] = data; });
+ ch.stderr.on('data', function(data) { stderr[stderr.length] = data; });
+ ch.on('exit', (callback || function (code) {
+ ch.emit('end', code, stdout.join('\n'), stderr.join('\n'));
+ }));
+
+ return ch;
+};
+
+
+// **assertListen**: macro to start and listen to the port provided.
+// Starts up a new connect server with very basic middleware setup,
+// logger and static configured to serve the publish dir
+//
+// Now, does the entire integration with build script,
+// cloning the repo if not done yet, making the build
+// and runing the test
+//
+// todo: break this up in another macro
+macros.assertListen = function (port, root, vows) {
+ var context = {
+ topic: function () {
+ var em = new EventEmitter,
+ build, server, stderr = [];
+
+ root = path.join(__dirname, '../..', root);
+
+ server = connect.createServer()
+ .use(connect.static(root));
+
+ // prior to the whole test suite to run, ensure the root dir is there
+ // otherwise, call the main build task and continue
+ var ok = path.existsSync(root);
+ if(ok) {
+ server.listen(port, em.emit.bind(em, 'success'));
+ return em;
+ }
+
+ console.log((' » ' + root + ' needs to be there, going to execute cake build...').grey);
+
+ process.chdir(path.resolve(__dirname, '../../'));
+
+ var build = spawn('cake', ['build']);
+
+ build.on('end', function (code, stdout, stderr) {
+ var createproject = /createproject/.test(stderr);
+ if(code > 0 && !createproject) throw new Error(('cake build exited with code ' + code + '\n ' + stderr).red);
+
+ // if there's no error, continue to vows at this point
+ if(!createproject) return server.listen(port, em.emit.bind(em, 'success'));
+
+ // otherwise, run the createproject task without prompt and rerun the build
+
+ // grab the dir.source value from the error output
+ var output = stderr.match(/\/[^\s]+/)[0].split('/').reverse()[0];
+
+ // end run the cake project task
+ spawn('cake', ['-o', output, 'createproject'])
+ .on('end', function(code, stdout, stderr) {
+ // finally, we can go on with build and test
+
+ // rerun the build, this time should be all good
+ spawn('cake', ['build'])
+ .on('end', function(code, stdout, stderr) {
+ if (code > 0) throw new Error(stderr || stderr);
+ server.listen(port, em.emit.bind(em, 'success'));
+ });
+ });
+ });
+
+ return em;
+ }
+ };
+
+ return extendContext(context, vows);
+};
+
+
+// **assertZombie**: tell zombie to visit the page host:8080.
+macros.assertZombie = function(host, port, vows) {
+ var context = {
+ topic: function() {
+ zombie.visit('http://' + host + ':' + port, {runScripts: false}, this.callback.bind(this))
+ }
+ };
+
+ return extendContext(context, vows);
+};
+
Please sign in to comment.
Something went wrong with that request. Please try again.