I'm starting a project at work and was wondering what the best build tool to use would be.
The whole thing is written in CoffeeScript, using AngularJS for the client-side and NodeJS for the server.
There are several components to the app:
There is tons of shared code between all these, again all written in CoffeeScript.
I'd like a build tool where I can list which app uses what code (much of it shared) and it would build each app's javascript files into a seperate folder.
For example, I would setup a folder called '/compiled/ipad/' which has index.html, and folders for js, css, img, etc. I would list what compiled coffee files I want thrown into /compiled/ipad/js (some of it from /src/shared/*.coffee, some of it from /src/ipad/*.coffee, etc) and what files I want thrown into /compiled/ipad/css. I would want it to be able to easily concatenate files how I want, too.
It would also compile my tests, from /src/test/ipad into /compiled/test/ipad/*.js.
All my client-side unit tests are written using testacular and I'm not sure what I'll write the server-side unit tests in yet.
What build tool/configuration is the best approach here? A Makefile? Something like Grunt? I'm honestly new to the whole build scene.
edit: Decided to go with Browserify. You can find my solution to make it work with Angular here: https://groups.google.com/forum/#!topic/angular/ytoVaikOcCs
Personally I think the drive to write server side code in javascript or coffeescript extends to your build tool chain as well: so stick with using javascript/coffeescript there too. This will allow you to easily automate your server/client tasks from your build tool - I doubt it'd be meaningfully possible with another tool like make (you'd just be writing wrappers around node.js command calls). Suggestions, ordered by structured-ness:
Of course, you can go with some other build tool you are already familiar with from some other language: rake, maven, ant, gradle, etc etc.
I did almost this exact thing all in a Cakefile using node modules as needed.
Set some global variables that are arrays with the path of each file, concatenate those files into a file in the compiled directory that you specify, then compile that one file into js.
For the styles, same thing with the concatenation without the compiling, obviously.
fs = require 'fs'
path = require 'path'
{spawn, exec} = require 'child_process'
parser = require('uglify-js').parser
uglify = require('uglify-js').uglify
cleanCss = require 'clean-css'
coffees =
[
"/src/shared/file1.coffee"
"/src/shared/file2.coffee"
"/src/ipad/file1.coffee"
]
tests =
[
"/src/ipad/tests.coffee"
]
styles =
[
"/src/ipad/styles1.css"
"/src/shared/styles2.css"
]
concatenate = (destinationFile, files, type) ->
newContents = new Array
remaining = files.length
for file, index in files then do (file, index) ->
fs.readFile file, 'utf8', (err, fileContents) ->
throw err if err
newContents[index] = fileContents
if --remaining is 0
fs.writeFile destinationFile, newContents.join '\n\n', 'utf8', (err) ->
throw err if err
if type is 'styles'
minifyCss fileName
else
compileCoffee fileName
compileCoffee = (file) ->
exec "coffee -c #{file}", (err) ->
throw err if err
# delete coffee file leaving only js
fs.unlink 'path/specifying/compiled_coffee', (err) ->
throw err if err
minifyJs file
minifyJs = (file) ->
fs.readFile f, 'utf8', (err, contents) ->
ast = parser.parse contents
ast = uglify.ast_mangle ast
ast = uglify.ast_squeeze ast
minified = uglify.gen_code ast
writeMinified file, minified
writeMinified = (file, contents) ->
fs.writeFile file, contents, 'utf8', (err) -> throw err if err
minifyCss = (file) ->
fs.readFile file, 'utf8', (err, contents) ->
throw err if err
minimized = cleanCss.process contents
clean = minimized.replace 'app/assets', ''
fs.writeFile file, clean, 'utf8', (err) ->
throw err if err
task 'compile_coffees', 'concat, compile, and minify coffees', ->
concatenate '/compiled/ipad/code.coffee', coffees, 'coffee'
task 'concat_styles', 'concat and minify styles', ->
concatenate '/compiled/ipad/css/styles.css', styles, 'styles'
task 'compile_tests', 'concat, compile, and minify test', ->
concatenate '/compiled/ipad/tests.coffee', tests, 'tests'
Now this is roughly what I think you're asking for.
Could definitely be prettier, especially having a separate function for writing the minified contents, but it works.
Not perfect for the styles either because I was using Sass and had other functions before it hit the minified function, but I think you get the idea.
I would put all the shared code into Node.js modules and create a project that looks something like the following:
Project
|~apps/
| |~cms/
| | `-app.js
| |~ipad/
| | `-app.js
| |~iphone/
| | `-app.js
| `~node/
| `-app.js
|~libs/
| |-module.js
| `-module2.js
|~specs/
| |~cms/
| | `-app.js
| |~ipad/
| | `-app.js
| |~iphone/
| | `-app.js
| `~node/
| `-app.js
| `~libs/
| |-module.js
| `-module2.js
`-Makefile
I would then use something like Browserify (there are others) to make the client-side apps where needed. That way instead of having a build file where you say what you need you actually have real apps importing modules.