Memakai Grunt Untuk Mengelola Proyek JavaScript


Saya sudah biasa memakai Gradle atau Maven untuk mengelola proyek Java. Lalu bagaimana dengan proyek JavaScript? Tentu saja tool serupa tidak begitu berguna bila dipakai pada web yang hanya sekedar memakai JavaScript di dalam HTML. Tapi untuk sebuah proyek library JavaScript (seperti jQuery), penggunaan tool otomatis untuk mengelola proyek akan sangat berguna. Grunt adalah salah satu tool yang dirancang untuk keperluan tersebut. Contoh proyek open-source JavaScript yang memakai Grunt adalah jQuery. Pada artikel ini, saya akan mencoba memakai Grunt untuk mengelola proyek JavaScript sederhana.

Untuk men-install Grunt, saya perlu memakai npm. npm adalah package manager yang berjalan pada platform Node.js. Ini adalah sesuatu yang memiliki fungsi mirip seperti Composer di PHP. Pada platform Linux, saya memberikan perintah berikut ini untuk men-install npm:

$ sudo apt-get install nodejs
$ sudo apt-get install npm

Setelah npm ter-install, saya dapat men-install Grunt CLI dengan menggunakan perintah berikut ini:

$ sudo npm install -g grunt-cli

Perintah di atas akan men-install perintah grunt-cli pada lokasi /usr/local (dapat diatur dengan mengubah nilai konfigurasi prefix). Dengan demikian, saya dapat memanggil perintah grunt dari mana saja.

Untuk menghasilkan template proyek JavaScript secara cepat, saya akan menggunakan grunt-init. Tapi sebelumnya, saya perlu men-install-nya terlebih dahulu dengan memberikan perintah berikut ini:

$ sudo npm install -g grunt-init

Untuk memakai grunt-init, saya perlu men-download minimal sebuah template yang akan dijadikan sebagai patokan struktur direktori awal. Lokasi template secara default terletak di folder ~/.grunt-init/. Sebagai latihan, saya akan memakai template grunt-init-commonjs dengan memberikan perintah berikut ini:

$ git clone https://github.com/gruntjs/grunt-init-commonjs.git ~/.grunt-init/commonjs

Bila git belum ter-install, saya perlu memberikan perintah sudo apt-get install git. Perintah di atas akan menyalin sebuah template untuk proyek JavaScript yang bersifat umum.

Saya siap untuk membuat proyek baru. Tapi sebelumnya, saya perlu mengatasi sebuah permasalahan kecil terlebih dahulu. Pada distro Linux yang saya pakai, perintah seperti grunt atau grunt-init tidak akan bisa dijalankan, malah muncul kesalahan seperti berikut ini:

$ grunt --version
/usr/bin/env: node: No such file or directory

Hal ini terjadi karena konflik nama antara package untuk Node.js dengan package node (Amateur Packet Radio Node Program) sehingga binary Node.js yang seharusnya adalah node terpaksa mengalah dan diganti nama menjadi nodejs. Karena banyak script yang mengharapkan nama binary Node.js berupa node, saya perlu me-rename nodejs menjadi node, atau agar aman, saya dapat membuat symbolic link seperti berikut ini:

$ sudo ln -s /usr/bin/nodejs /usr/bin/node

Sekarang, saya siap untuk membuat sebuah proyek JavaScript baru dengan memberikan perintah berikut ini:

$ mkdir myUtils
$ cd myUtils
$ grunt-init commonjs
Running "init:commonjs" (init) task
This task will create one or more files in the current directory, based on the
environment and the answers to a few questions. Note that answering "?" to any
question will show question-specific help and answering "none" to most questions
will leave its value blank.

Please answer the following:
[?] Project name (myUtils) 
[?] Description (The best project ever.) My reusable JS APIs.
[?] Version (0.1.0) 
[?] Project git repository (git://github.com/snake/myUtils.git) 
[?] Project homepage (https://github.com/snake/myUtils) 
[?] Project issues tracker (https://github.com/snake/myUtils/issues) 
[?] Licenses (MIT) Apache
[?] Author name (none) Solid Snake
[?] Author email (none) solid@snake.com
[?] Author url (none) https://thesolidsnake.wordpress.com
[?] What versions of node does it run on? (>= 0.10.0) 
[?] Main module/entry point (lib/myUtils) 
[?] Npm test command (grunt nodeunit) 
[?] Do you need to make any changes to the above before continuing? (y/N) N

Writing .gitignore...OK
Writing .jshintrc...OK
Writing Gruntfile.js...OK
Writing README.md...OK
Writing lib/.jshintrc...OK
Writing lib/myUtils.js...OK
Writing test/myUtils_test.js...OK
Writing package.json...OK

Initialized from template "commonjs".
You should now install project dependencies with npm install. After that, you
may execute project tasks with grunt. For more information about installing
and configuring Grunt, please see the Getting Started guide:

http://gruntjs.com/getting-started

Done, without errors.

grunt-init akan menanyakan beberapa pertanyaan. Saya bisa mengisinya atau menerima nilai default dengan menekan tombol Enter. Setelah pertanyaan selesai dijawab, grunt-init akan membuat sebuah proyek baru dengan struktur seperti berikut ini:

$ tree -a
.
|--- .gitignore
|--- Gruntfile.js
|--- .jshintrc
|--- lib
|    |--- .jshintrc
|    |--- myUtils.js
|--- package.json
|--- README.md
|--- test
     |--- myUtils_test.js

2 directories, 8 files

File Gruntfile.js adalah file wajib yang dibutuhkan untuk bekerja dengan Grunt. File package.json dipakai oleh npm untuk men-download modul lain yang dibutuhkan. grunt-init juga sudah membuat file lib\myUtils.js yang nantinya akan berisi kode program saya. Selain itu, juga ada file lib\myUtils_test.js yang berisi unit test untuk menguji program JavaScript yang ada.

Bila saya membuka file package.json, saya akan memperoleh isi "devDependencies" yang terlihat seperti berikut ini:

...
"devDependencies": {
    "grunt-contrib-concat": "~0.3.0",
    "grunt-contrib-uglify": "~0.2.0",
    "grunt-contrib-jshint": "~0.6.0",
    "grunt-contrib-nodeunit": "~0.2.0",
    "grunt-contrib-watch": "~0.4.0",
    "grunt": "~0.4.5"
},
...

Konfigurasi di atas menunjukkan ketergantungan pada beberapa plugin Grunt. Salah satu kelebihan Grunt adalah ia memiliki banyak plugin seperti yang terdaftar di http://gruntjs.com/plugins. Sebagai contoh, grunt-contrib-concat adalah plugin Grunt untuk menggabungkan beberapa file JavaScript yang berbeda menjadi satu. Developer selalu lebih nyaman memakai beberapa file berbeda (misalnya satu file untuk sebuah object JavaScript) sementara kinerja download akan lebih baik bila <script> merujuk pada satu file tunggal. Itu sebabnya file perlu digabungkan menjadi satu pada saat distribusi. Plugin grunt-contrib-uglify akan memakai UglifyJS untuk menghasilkan versi minified dari kode program JavaScript yang memiliki ukuran lebih kecil. Plugin grunt-contrib-jshint akan memakai JSHint untuk melakukan analisa kode program kode program JavaScript (JSHint adalah fork dari JSLint). Plugin grunt-contrib-nodeunit dibutuhkan untuk melakukan unit test dengan menggunakan nodeunit (ini adalah sesuatu yang memiliki fungsi mirip seperti JUnit di Java). Dan terakhir, plugin grunt-contrib-watch memiliki kemampuan untuk mengerjakan task Grunt tertentu secara otomatis bila ada file yang berubah.

Saat ini, semua plugin yang dibutuhkan belum ter-install pada lokasi proyek. Oleh sebab itu, saya perlu meminta npm untuk men-download semua yang ada di package.json tersebut dengan memberikan perintah:

$ npm install

Perintah di atas akan menyebabkan npm men-download dan meletakkan file yang dibutuhkan pada folder node_modules.

Berikutnya, saya akan melihat is file Gruntfile.js. Secara garis besar, saya dapat melihat konfigurasi untuk masing-masing task yang ada seperti berikut ini:

'use strict';

module.exports = function(grunt) {


  grunt.initConfig({

    pkg: ...,

    banner: ...,

    concat: ...,

    uglify: ...,

    nodeunit: ...,

    jshint: ...,

    watch: ...,

  });

  ...

};

Selain konfigurasi, saya juga menjumpai pemanggilan grunt.loadNpmTasks() yang akan me-load plugin. Setiap plugin menawarkan task masing-masing. Bila menginginkan task yang tidak disediakan oleh plugin Grunt, saya dapat membuat kode programnya sendiri dengan memanggil grunt.registerTask() yang melewatkan function yang berisi apa yang akan dikerjakan oleh task baru tersebut.

Apa itu task? Task adalah sebuah proses yang dikerjakan secara otomatis. Pada era konvensional, developer membuat batchfile (*.bat) atau shell script (*.sh) untuk mengerjakan proses pengelolaan proyek secara otomatis. Tapi file-file tersebut sering kali menjadi bertambah banyak, tidak terorganisir, dan sulit dipakai oleh developer lain (perbedaan platform, bahasa, dsb). Apache Ant mempopulerkan cara baru yang lebih seragam dan lebih standar dimana task yang umum dijumpai pada pengelolaan proyek Java didefinisikan dalam bentuk XML. Kesuksesan Ant pun dilanjutkan oleh tool lain seperti Maven dan Gradle (yang kini sedang ‘naik daun’ dan dipakai oleh Android Studio). Grunt adalah salah satu tool serupa tetapi ditujukan untuk mengelola proyek JavaScript.

Untuk melihat task apa saja yang bisa dikerjakan, saya dapat memberikan perintah berikut ini:

$ grunt --help
...
Available tasks
        concat  Concatenate files. *                                           
        uglify  Minify files with UglifyJS. *                                  
      nodeunit  Run Nodeunit unit tests. *                                     
        jshint  Validate files with JSHint. *                                  
         watch  Run predefined tasks whenever watched files change.            
       default  Alias for "jshint", "nodeunit", "concat", "uglify" tasks. 
...

Pada bagian Available tasks, saya dapat menjumpai task apa saja yang dapat saya panggil.

Sebagai contoh, bila saya ingin menjalankan unit test, saya dapat memberikan perintah seperti berikut ini:

$ grunt nodeunit
Running "nodeunit:files" (nodeunit) task
Testing myUtils_test.js.OK
>> 1 assertions passed (10ms)

Done, without errors.

Terlihat bahwa hasil pengujian sukses tanpa kesalahan.

Untuk menghasilkan file distribusi dimana seluruh file JavaScript terpisah akan digabungkan menjadi satu, saya dapat memberikan perintah:

$ grunt concat
Running "concat:dist" (concat) task
File "dist/myUtils.js" created.

Done, without errors.

Task di atas akan membuat sebuah file baru di folder dist dengan nama myUtils.js. Ini adalah file yang dapat didistribusikan kepada pengguna. Saya bisa membuat versi minified dari file yang dihasilkan oleh task concat tersebut dengan memberikan perintah:

$ grunt uglify
Running "uglify:dist" (uglify) task
File "dist/myUtils.min.js" created.

Done, without errors.

File myUtils.min.js adalah file berukuran kecil dari file myUtils.js yang dihasilkan oleh UglifyJS.

Penggunaan template grunt-init membuat struktur proyek JavaScript menjadi jelas dan rapi. Sebagai contoh, pada template yang saya pakai, kode program JavaScript yang dibuat developer terletak di direktori lib. Hasil akhir yang merupakan penggabungan file di lib dan versi minified-nya dapat dijumpai di folder dist. Kedua file tersebut merupakan output yang dapat didistribusikan langsung ke pengguna.

Task watch adalah sebuah task yang agak unik yang tidak saya jumpai di dunia Java. Bila saya menjalankan task ini, saya akan memperoleh hasil seperti:

$ grunt watch
Running "watch" task
Waiting...

Saya harus tetap membiarkan console ini tetap aktif. Lalu, saya membuka console lain dan melakukan perubahan pada file lib/myUtils.js. Begitu saya selesai menyimpan perubahan pada file tersebut, console yang menjalankan task watch akan menampilkan informasi seperti berikut ini:

Running "watch" task
Waiting...OK
>> File "lib/myUtils.js" changed.

Running "jshint:lib" (jshint) task
>> 1 file lint free.

Running "nodeunit:files" (nodeunit) task
Testing myUtils_test.jsF
>> awesome - no args
>> Message: should be awesome.
>> Error: 'not awesome?' == 'awesome'
>> at Object.exports.awesome.no args (test/myUtils_test.js:33:10)
>> at Object.exports.awesome.setUp (test/myUtils_test.js:28:5)

Warning: 1/1 assertions failed (17ms) Use --force to continue.

Waiting...

Terlihat bahwa begitu ada file JavaScript yang dimodifikasi oleh developer, maka task jshint dan nodeunit akan dikerjakan secara otomatis dan saya bisa langsung melihat laporannya. Bila proses seperti ini dilakukan di server tempat dimana seluruh developer men-commit perubahan kode program mereka, maka saya sudah memiliki sebuah sistem continous integration sederhana yang menguji kode program dari developer secara otomatis setiap kali ada perubahan.

Perihal Solid Snake
I'm nothing...

One Response to Memakai Grunt Untuk Mengelola Proyek JavaScript

Apa komentar Anda?

Please log in using one of these methods to post your comment:

Logo WordPress.com

You are commenting using your WordPress.com account. Logout / Ubah )

Gambar Twitter

You are commenting using your Twitter account. Logout / Ubah )

Foto Facebook

You are commenting using your Facebook account. Logout / Ubah )

Foto Google+

You are commenting using your Google+ account. Logout / Ubah )

Connecting to %s

%d blogger menyukai ini: