Gruntfileでcoffeescript コンパイル & wsh 利用 !
仕事ではWindowsなので、何か作る場合には wsh を利用すると簡単に作れていい。 でも素の JScript を書くのはめんどくさいし、どうせなら今流行り(?)の coffeescript で 書きたいとか思っちゃう。
そしてさらにそのコンパイルは自動化したくて、なんかいいのないかと探していたら、ナウい方法は Grunt を使用することみたいじゃあないですか。
ナウい Grunt を使用して、フルい wsh を書くってのもなんか変だけど、とりあえずやってみようと いうことでやってみた。
node.js のインストール
まずは、 nodebrew をインストールする。 nodebrew は、node.js のいろんなバージョンをインストール出来るやつ。
$ curl https://raw.github.com/hokaccha/nodebrew/master/nodebrew | perl - setup
.zshenv にパス追加。
path=(
# homebrew
/usr/local/bin(N-/)
# coreutils
/usr/local/opt/coreutils/libexec/gnubin(N-/)
# nodebrew
$HOME/.nodebrew/current/bin(N-/)
)
こんな感じ。(実際にはもっとたくさん書いてあるけど。)
(N-/) で、存在する場合だけ追加してくれる。
以下のコマンドでインストール出来るバージョンが確認出来る。
$ nodebrew ls-remote
最新をバイナリでインストール。
$ nodebrew install-binary latest
$ nodebrew use v0.11.2
coffeescript のインストール
npm で簡単にインストール出来る。
$ npm install -g coffeescript
Grunt のインストール
これも npm で。 Grunt には grunt-cli と grunt がある。 グローバルに grunt-cli をインストールして、 grunt はプロジェクト固有の ディレクトリにインストールのがいいみたい。
$ npm install -g grunt-cli
package.json の作成
とりあえずプロジェクトの説明として package.json ってのが必要みたい。 以下のコマンドで作成。いろいろ聞かれるので適当に答える。
$ npm init
出来上がった package.json はこんな感じ。
{
"name": "wsh",
"version": "0.0.0",
"description": "windows script hosting",
"main": "index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"repository": "",
"author": "yuyunko",
"license": "BSD",
"readmeFilename": "README.md",
}
なんかいろいろダメっぽいところあるけどとりあえずまぁこれでよし。
依存するパッケージのインストール
Grunt で必要なパッケージをインストールする。
$ npm install grunt --save-dev
何はさておき、 grunt をインストールする。その際、 –save-dev オプション をつけると、 package.json に自動で記述を追加してくれる。
他にも必要なパッケージをインストール。
$ npm install grunt-contrib-concat --save-dev
$ npm install grunt-contrib-coffee --save-dev
$ npm install grunt-contrib-watch --save-dev
$ npm install grunt-contrib-uglify --save-dev
$ npm install grunt-contrib-clean --save-dev
$ npm install glob --save-dev
上記コマンドで package.json はこんなのに変わってるはず。
{
"name": "wsh",
"version": "0.0.0",
"description": "windows script hosting",
"main": "index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"repository": "",
"author": "yuyunko",
"license": "BSD",
"readmeFilename": "README.md",
"devDependencies": {
"grunt": "~0.4.1",
"grunt-contrib-concat": "~0.3.0",
"grunt-contrib-coffee": "~0.7.0",
"grunt-contrib-watch": "~0.4.4",
"grunt-contrib-uglify": "~0.2.1",
"grunt-contrib-clean": "~0.4.1",
"glob": "~3.2.1"
}
}
Gruntfile の作成
Grunt の実行には Gruntfile が必要。 Gruntfile.js か、 Gruntfile.coffee 。 coffeescript で書くのがいいらしいので、そっちで書く。
全部書くとこんな感じ。
module.exports = (grunt) ->
pkg = grunt.file.readJSON 'package.json'
grunt.initConfig
pkg : grunt.file.readJSON 'package.json'
# config
watch:
default:
files: ['coffee/**/*.coffee']
tasks: ['coffee', 'concat', 'nkf:concat:Shift_JIS:dos']
build:
files: ['coffee/**/*.coffee']
tasks: ['coffee', 'concat', 'uglify', 'nkf:uglify:Shift_JIS:dos']
coffee:
compile:
options:
sourceMap: true
bare: true
files: [
expand: true
cwd: 'coffee/'
src: ['**/*.coffee']
dest: 'js/'
ext: '.js'
]
uglify:
uglify_target:
options:
beautify: false
files: [
expand: true
cwd: 'js/'
src: ['**/*.js']
dest: 'uglify/'
ext: '.js'
]
concat:
options:
banner: '''
@if(0)==(0) ECHO OFF
pushd "%~dp0" > nul
CScript.exe //NoLogo //E:JScript "%~f0" %*
popd > nul
pause
GOTO :EOF
@end
// <%= grunt.template.today("yyyy-mm-dd") %> compiled !
'''+"\n\n"
default:
files:
'concat/helloworld.cmd': ['js/lib/Lib.js', 'js/lib/File.js', 'js/lib/Shell.js', 'js/helloworld.js']
'concat/hellogrunt.cmd': ['js/lib/Lib.js', 'js/lib/File.js', 'js/hellogrunt.js']
build:
files:
'concat/helloworld.cmd': ['uglify/lib/Lib.js', 'uglify/lib/File.js', 'uglify/lib/Shell.js', 'uglify/helloworld.js']
'concat/hellogrunt.cmd': ['uglify/lib/Lib.js', 'uglify/lib/File.js', 'uglify/hellogrunt.js']
clean: [
'js'
'concat'
'uglify'
]
# nkf task
grunt.registerTask 'nkf', 'Change encoding and newline character', (dir, encoding, nl) ->
{exec} = require 'child_process'
glob = require 'glob'
dir ?= '.'
switch encoding
when 'ISO-2022-JP'
encOpt = 'j'
when 'Shift_JIS'
encOpt = 's'
when 'EUC-JP'
encOpt = 'e'
else
encOpt = 'w'
switch nl
when 'dos'
nlOpt = 'w'
when 'mac'
nlOpt = 'm'
when 'unix'
nlOpt = 'u'
else
nlOpt = null
cmdSfx = if nlOpt? then "nkf -O#{encOpt}L#{nlOpt} " else "nkf -O#{encOpt} "
done = @async()
glob "#{dir}/**/*.cmd", (err, files) ->
if err?
console.log err
done false
else
for file in files
cmd = cmdSfx + "#{file} cmd/#{file.split("#{dir}/").join('')}"
console.log cmd
# exec nkf
options = {timeout: 5000}
exec cmd, options, (error, stdout, stderr) ->
if error?
console.log 'ERR', error, stderr
done false
else
console.log stdout
done()
# loadNpmTasks
# read from package.json
for taskName of pkg.devDependencies when taskName.substring(0, 6) is 'grunt-'
grunt.loadNpmTasks taskName
grunt.registerTask 'default', ['coffee', 'concat:default', 'nkf:concat:Shift_JIS:dos', 'watch:default']
grunt.registerTask 'build', ['coffee', 'uglify', 'concat:build', 'nkf:concat:Shift_JIS:dos', 'clean']
上記の設定で、
$ grunt
と実行すると、以下が行われる。
- coffeescript のコンパイル
- ファイルの結合 ( wsh として実行出来るようにヘッダも追加)
- nkf で UTF-8 から Shift_JIS への変換 (改行コードも LF から CRLF に変換)
- ファイルが更新されるのを監視して、更新されたら、1. から再実行
さらに、
$ grunt build
とやると、以下が行われる。
- coffeescript のコンパイル
- js の圧縮
- ファイルの結合 ( wsh として実行出来るようにヘッダも追加)
- nkf で UTF-8 から Shift_JIS への変換 (改行コードも LF から CRLF に変換)
- js 、 concat 、 uglify ディレクトリの削除
ちなみにディレクトリ構成はこんな感じ。
.
├── Gruntfile.coffee
├── README.md
├── cmd
│ ├── helloworld.cmd
│ └── hellogrunt.cmd
├── coffee
│ ├── helloworld.coffee
│ ├── hellogrunt.coffee
│ ├── lib
│ │ ├── Excel.coffee
│ │ ├── File.coffee
│ │ ├── Lib.coffee
│ │ ├── Network.coffee
│ │ └── Shell.coffee
├── concat
│ ├── helloworld.cmd
│ └── hellogrunt.cmd
├── js
│ ├── helloworld.js
│ ├── helloworld.js.map
│ ├── hellogrunt.js
│ ├── hellogrunt.js.map
│ ├── lib
│ │ ├── Excel.js
│ │ ├── Excel.js.map
│ │ ├── File.js
│ │ ├── File.js.map
│ │ ├── Lib.js
│ │ ├── Lib.js.map
│ │ ├── Network.js
│ │ ├── Network.js.map
│ │ ├── Shell.js
│ │ └── Shell.js.map
├── node_modules (配下は略)
│
└── package.json
便利。