Créer un environnement de développement pour votre client JS avec Coffeescript, Cake, Stylus et Uglifyjs (partie 2)

Coffeescript et Stylus nous offre un syntaxe alléchante mais cela se gagne au prix d’une compilation. On ne peut se servir directement des fichiers que l’on a codé. Donc maintenant, vous l’avez compris, nous allons créer un fichier Cakefile. C’est le fichier utiliser par Cake pour savoir quoi entreprendre en fonction de l’action qui lui est fourni en paramètre. Ce fichier nous permettra d’exécuter 3 actions : build CSS, build JS, minification JS. A laquelle nous ajouterons une quatrième action “watch” qui permettra de lancer la compilation après la modification d’un fichier source.

Pour commencer définissons l’arborescence de notre projet :

lib/
src/
styles/
Cakefile

lib/ contient les librairies tierces et surtout le rendu final.
src/ contient les sources en Coffeescript du code.
styles/ contient notre fichier Stylus et le fichier CSS résultant.
Cakefile est notre fichier de build.

Maintenant on passe aux choses sérieuses avec le Cakefile ! Nous allons préparer les données nécessaires à la compilation puis nous allons créer les 4 actions qui nous intéressent. Tout ce qui suit est fortement inspiré de cet article de blog.

On configure notre environnement :

# Name of application to build
appName = "activities"

# Dependencies and shortcuts
sys = require 'sys'
print = sys.print
puts = sys.puts
fs = require 'fs'
{exec} = require 'child_process'

Puis on définit la liste de nos fichiers, soit en les positionnant à la main soit en les mettant dans le dossier src qui sera parcouru pour déterminer la liste des fichiers à compiler. Pour effectuer ce parcours nous écrivons une petite fonction walk.

# Files to compile
appFiles  = [
  '../my/path/to_any_file.coffee'
]

walk = (dir) ->
  list = fs.readdirSync(dir)
  if list
    for file in list
      if file
        filename = dir + '/' + file
        stat = fs.statSync(filename)
        if stat and stat.isDirectory()
          walk (filename)
        else
          appFiles.push(filename)

walk("./src")</pre>

Voici la tâche principale qui va permettre de compiler les fichiers coffee en un fichier JS. On charge les fichiers en mémoire, on les concatène puis on les transforme en JS. A la fin est appelé automatiquement la tâche de minification :

task 'build', 'Build single application file from source files', ->
  puts 'Start build'
  appContents = new Array
  remaining = appFiles.length

  puts 'Building JS' puts "Files to Compile"
  for appFile in appFiles
    puts appFile

  # Load files in memory then process compilation
  for file, index in appFiles then do (file, index) ->
    fs.readFile file, 'utf8', (err, fileContents) ->
      throw err if err
      appContents[index] = fileContents process()
      if --remaining is 0 process = ->
        # Concatenate files
        fs.writeFile "lib/#{appName}.coffee", appContents.join('\n\n'), 'utf8', (err) ->
           throw err if err
          # Compile files
          exec "coffee --compile lib/#{appName}.coffee", (err, stdout, stderr) ->
            throw err if err print stdout + stderr fs.unlink "lib/#{appName}.coffee", (err) ->
              throw err if err
              puts "JS Built to lib/#{appName}.js"
              invoke 'minify'

Vient ensuite la tâche de minification qui va réduire le fichier final et qui appelera à la fin la tâche de styles.

task 'minify', 'Minify the resulting application file after build', ->
  puts 'Start minify'
  command = "uglifyjs lib/#{appName}.js > "
  command += lib/#{appName}.production.js"
  exec command, (err, stdout, stderr) ->
    throw err if err
    print stdout + stderr
    puts 'Minify done' invoke 'styles'

On crée la tâche styles qui va compiler le fichier Stylus en fichier CSS :

task 'styles', 'Compile styles to CSS', ->
  puts 'Building CSS'
  exec "stylus styles/#{appName}.styl", (err, stdout, stderr) ->
  throw err if err
  print stdout + stderr
  puts 'CSS built'

On complète notre fichier Cake par la tâche watch qui va agir comme un démon, dès qu’un fichier source est modifié, le tout est recompilé :

task 'watch', 'Watch source files and build changes', ->
  for file in appFiles then do (file) ->
    fs.watchFile "src/#{file}", (curr, prev) ->
      # If a change occurs, it invokes build action.
      if +curr.mtime isnt +prev.mtime
        puts "Saw change in lib/#{file}"
        invoke 'build'

Le fichier Cakefile qui en résulte n’est que la mise bout à bout des morceaux de code de cet article. Pour compiler ses fichiers il ne reste plus qu’à appeler la commande

cake build

Qui provoque l’affichage suivant :

Start build Building JS Files to compile
../my/path/to_any_file.coffee
./src/activities.coffee
./src/views/activityview.coffee
./src/views/activity.coffee
./src/models/activities.coffee
JS Built to lib/activities.js
Start minify
Minify done
Building CSS
compiled styles/activities.css
CSS built

Maintenant que votre environnement est prêt, il ne vous reste plus qu’à coder !

NB : Vous pouvez suivre l’évolution de ce Cakefile ici : https://github.com/gelnior/teatime

2 Responses to Créer un environnement de développement pour votre client JS avec Coffeescript, Cake, Stylus et Uglifyjs (partie 2)

  1. David says:

    Yo Frank. Il y a des problèmes de formatting sur les extraits de Makefile, plusieurs lignes en une. Par ailleurs, je me demande quel langage utilise cake: ça n’a pas l’air d’etre exactement du python ou ruby, ils ont leur langage de script propre?

  2. Gelnior says:

    Merci, c’est corrigé. Pour le langage j’ai oublié de préciser mais c’est du Coffeescript dont la syntaxe est inspiré de Python et de Ruby.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

%d bloggers like this: