Web・IT うんたらら

業務系とWeb系の狭間でIT業界を彷徨いながら備忘録と足跡を残していきます

gruntをWindows XP+shift_jis環境で使う

OSは未だにWindows XPだし、文字コードShift_Jisのがあったりするし
そんなレガシーな環境でも少しづつモダンなツールを導入して効率化を計っていきたい!

ということでgruntを導入してみました。
node.js上で動く、主にHTML/CSS/JavaScript を対象としたタスクランナーで、
ファイル更新を感知してミニファイ、結合、ライブリロードなどの処理を自動でやってくれるツールです。


公式
http://gruntjs.com/
このあたりの記事が詳しいです。
http://kojika17.com/2013/03/grunt.js-memo.html
shift_jis対応はこの記事を参考にさせていただきました。
http://www.mamoida.com/2012/10/gruntconvert/


以下、gruntについての基礎知識があることを前提で、標記の環境に導入するにあたって
つまづいたところと対応をメモします。


  • grunt-contribのインストール

grunt-cli、grunt本体の次にインストールされるであろう、gruntの基本タスクパッケージである
grunt-contribですが、インストール時に早速こけます。
理由は、XPのファイルパス長制限。255バイトまでしか認識しません。
grunt-contribにはそれだけで240バイトを超えるパスがあるので、親ディレクトリに許されるパスはせいぜい数バイトという…。
xpにはもちろんシンボリックリンクのような仕組みはないので、ネットワークドライブを作ることで対処しました。
親ディレクトリを右クリックから共有し、コマンドプロンプトからネットワークドライブとして認識させます。

net use z:  \\自分のホスト名\親ディレクトリの共有名


以降、このz:ドライブ(任意で変更してください)にてインストールおよびその他の作業を行います。



gruntは基本的にUTF-8しか対応していません。さすが今時のツール……。
とはいえ、やっぱりShift_Jis環境で開発する場合もあるわけで、いったんUTF-8に変更後、ミニファイや結合等の処理を行い、Shift_Jisに戻す、という処理を行おうとしたのですが、今度は文字コード変換に一苦労させられました。

冒頭の参考記事にもあるとおり、nodeでの文字コード変換は、node-iconvというモジュールで行うのが主流らしいのですが、Windowsにはそのままではインストールできません。
インストール時、内部でnode-gypというモジュールを使っているのですが、なんとこれ、動作にVisualStudioや、.net frameworkが必要なんですね。
おいおい、なんのためのnpm…と思いつつ、Visual C++ Expressをインストールするも、またもやエラー。
"C:¥Documents"に何か作ろうとしているけど…あーはいはい、出ました

Documents And Settings 問題

WindowsXPはあらゆる標準パスにスペース使いまくっているせいで、オープンソースソフトがこけまくります。
これの対処はちょっと大変なので、諦めて普通のlibiconvをインストールし、外部プロセスとしてキックすることにしました。

libiconvのダウンロードはこちらから
http://gnuwin32.sourceforge.net/packages/libiconv.htm


このlibiconvを使って、文字コードを変換するgrantタスクを作ります。

フォルダ構造は、以下のような形を想定。

.
├── Gruntfile.js
├── build                             //実行結果の出力先
│   ├── *.htm
│   ├── css
│   ├── image
│   ├── js
├── src                                //実際に編集するファイル(入力元)
│   ├── css
│   ├── image
│   ├── js
├── tasks                             //今回作成するタスク
│   ├── convertsjis.js
│   ├── convertutf8.js
│   └── lib
│       └── grunt-iconv-convertchar.js
└── tmp                               //文字コード変換に使う一時ファイル
    ├── js


上図内におけるtaskフォルダを作成し、文字コード変換処理を実装していきます。

lib/grunt-iconv-convertchar.js

/* Usage:
 *     convert(grunt, this, 'SHIFT_JIS', 'UTF_8'); //Convert charcode from Shift_jis to UTF-8.
 */

exports.convert = function(grunt, self, fromCode, toCode){
    var fs = require('fs');
    var exec = require('child_process').exec;
    function toArray(obj){
        return (obj instanceof Array) ? obj : [obj];
    }

    function renameToUniq(orgName){
        var destName = orgName;
        i = 0;
        while (fs.existsSync(destName)){
            destName = destName + '_' + i;
            i++;
        }
        fs.renameSync(orgName, destName);
        return destName;
    }

    var files = toArray(grunt.config(self.name)[self.target].src);
    var dests = toArray(grunt.config(self.name)[self.target].dest);

    var done = self.async();
    var doneCount = 0;
    for(var i=0, max=files.length; i<max; i++){
        (function(){
            var src  = files[i];
            var dest = dests[i];
            var isRenameSrc = false;
            if (src === dest){
                src = renameToUniq(src);
                isRenameSrc = true;
            }
            var cmd = 'iconv -f ' + fromCode + ' -t ' + toCode + ' ' + src + '>' + dest;
            var child = exec(cmd, function (error, stdout, stderr) {
                if (error !== null) {
                    grunt.log.writeln('exec error: ' + error);
                }
                grunt.log.writeln('File '+ dest + ' created.');

                if (isRenameSrc){
                    fs.unlinkSync(src);
                }
                doneCount++;
                if (doneCount === max){
                    done();
                }
            });
        })();
    }
};


convertsjis.js

module.exports = function(grunt) {
    var iconv = require('./lib/grunt-iconv-convertchar.js');
    grunt.registerMultiTask('convertsjis', 'Convert charcode from UTF-8 to Shift_Jis.', function() {
        iconv.convert(grunt, this, 'UTF-8', 'SHIFT_JIS');
    });
};


convertutf8.js

module.exports = function(grunt) {
    var iconv = require('./lib/grunt-iconv-convertchar.js');
    grunt.registerMultiTask('convertutf8', 'Convert charcode from Shift_Jis to UTF-8.', function() {
        iconv.convert(grunt, this, 'SHIFT_JIS', 'UTF-8');
    });
};


最後に、Gruntfile.jsに上記のタスクを挿入します。
ミニファイ+結合ならこんな感じ

module.exports = function(grunt) {
    var path = require('path');
    var js = {
        src: [
            'src/js/hoge.js',
            'src/js/fuga.js',
            'src/js/piyo.js'
        ],
        temp: [
            'tmp/js/hoge.js',
            'tmp/js/fuga.js',
            'tmp/js/piyo.js'
        ],
        dest: 'build/js/<%= pkg.name %>.js'
    };

    grunt.initConfig({
        pkg: grunt.file.readJSON('package.json'),

        //sjis => utf8変換
        convertutf8 : {
           dist : {
                src : js.src,
                dest : js.temp
           }
        },

        // ファイル結合
        concat: {
            dist: {
                src: js.temp,
                dest: js.dest
            }
        },

        // ミニファイ
        uglify: {
            options: {
                banner: '/*! <%= pkg.name %> <%= grunt.template.today("yyyy-mm-dd") %> */\n'
            },
            build: {
                src: js.dest,
                dest: js.dest
            }
        },

        //utf8 => sjis変換
        convertsjis : {
           dist : {
                src : js.dest,
                dest : js.dest
           }
        }
    });
    grunt.loadNpmTasks('grunt-contrib');
    grunt.loadTasks('tasks');
    grunt.registerTask('default', ['convertutf8', 'concat', 'uglify', 'convertsjis']);
};


実行

z:¥ grunt


以上!長くなりましたが、無事Windows XPで、Shift_Jisのファイルをgruntで管理できました。
自動監視やライブリロードなんかも導入すると、非常にいい感じです。おすすめ。


…書いた後に気づいたのですが、cygwinなんかを使うともっと簡単に導入できたかも。