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
As of today, January 2020, CoffeeScript is completely dead on the market (though the GitHub repository is still kind of alive).
One crucial difference between the two languages is that TypeScript is the superset of JavaScript while CoffeeScript is a language which is an enhanced version of JavaScript. Not just these two languages but there are other languages such as Dart, Kotlin, etc. which can be compiled into JavaScript.
To run CoffeeScripts on node, you can either: Type coffee -c example. coffee to compile, followed by node example. js to run the compiled JS.
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With