三次元日誌(hugo)

gulpで静的サイト生成その2

引き続き、サイト生成作業を続行中。見た目は置いておいて(bootstrap入れたけど)機能を優先して作ろう。 「次へ」と「前へ」のリンクを作れば内部リンクは揃う。 gulpは自由度が高いのでなんとでもなるな。 強まったmakeのようで非常にポテンシャルを感じる。 中期的なも目標としてはローカルもしくはLAN内のnode.jsがホストとなるGUIをさくっと作れるようになるというものがあるのだが、 関連項目が多すぎてひたすら拡がっていくのが危険だ。 特に、インターネットで調べながらだと収束せずにひたすら拡散する傾向がある。 anglar, backboneは避けようと思ったのだがmithrilは気になっているし、electronも気になっている。 あと、typescriptもはやめに使いこなしたい。

だいぶgulpがわかってきた

「前へ」と「次へ」は全部のファイルをリストに投入してソートして隣通しのパスを得る必要がある。 一度リストに投入してから前へと次へを処理した後で、再度リストの中身を一個ずつ後ろに渡せばいいじゃない。 ということでやってみたらあっさりできた。 野良プラグインの作り方が分かるとgulp面白いな。

make-toc.js

var through = require('through2');
var gutil = require('gulp-util');
var path = require('path');

module.exports = function (outputFileName, options) {

    options = options || {};
    var dest = options.dest;
    var filelist = [];

    function transform(file, encoding, callback) {

        // ファイルを集める
        filelist.push(file);

        // callback()は必ず実行
        callback();
    }

    function flush(callback) {

        if (filelist.length > 0) {

            // sortすることで日付順に並ぶ
            filelist.sort(function compareNumbers(a, b) {
                if (a.path < b.path) {
                    return 1;
                } else {
                    return -1;
                }
            });
            var file = filelist[0];

            // 目次
            var output_map = {
                cwd: file.cwd,
                base: file.base,
                path: file.base + outputFileName,
            };
           // console.log(output_map);
            var output = new gutil.File(output_map);

            var html = '<ul>\n';
            //console.log(filelist.length);
            for (var i = 0; i < filelist.length; ++i) {
                var f = filelist[i];
                var rel = dest + "/" + f.path.substr(f.base.length).replace(/\\/g, '/');
                //console.log(rel);              
                html += '<li><a href="' + rel + '">' + f.frontMatter.title + '</a></li>\n';
                
                // 各アイテムのfrontMatterにnextとprevを付ける
                // 降順に並んでいる
                if (i === 0) {
                    // 先頭
                    f.frontMatter.next = "";
                    f.frontMatter.prev = path.relative(path.dirname(f.path), filelist[i + 1].path).replace(/\\/g, '/');
                }
                else if (i === filelist.length-1) {
                    // 終端
                    f.frontMatter.next = path.relative(path.dirname(f.path), filelist[i - 1].path).replace(/\\/g, '/');
                    f.frontMatter.prev = "";
                }
                else {
                    f.frontMatter.next = path.relative(path.dirname(f.path), filelist[i - 1].path).replace(/\\/g, '/');
                    f.frontMatter.prev = path.relative(path.dirname(f.path), filelist[i + 1].path).replace(/\\/g, '/');
                }
            }
            html += '</ul>\n';
            output.contents = new Buffer(html);

            // filelistをoutputにくっつける
            output.filelist = filelist;

            this.push(output);
        }

        // callback()は必ず実行
        callback();
    }

    return through.obj(transform, flush);
};

resplit.js

var through = require('through2');
var gutil = require('gulp-util');
var path = require('path');

module.exports = function () {

    function transform(file, encoding, callback) {

        // ファイルがnullの場合
        if (file.isNull()) {
            // 次のプラグインに処理を渡すためにthis.push(file)しておく
            this.push(file);
            // callback()は必ず実行
            return callback();
        }

        // ファイルがstreamの場合(このサンプルプラグインはstreamに対応しない)
        if (file.isStream()) {
            // emit('error')を使って、プラグイン呼び出し側に'error'イベントを発生させる
            this.emit('error', new gutil.PluginError('gulp-diff', 'Streaming not supported'));
            // callback()は必ず実行
            return callback();
        }

        // do something
        for(var key in file.filelist){
            var output=file.filelist[key];
            this.push(output);           
        }

        // callback()は必ず実行
        callback();
    }

    return through.obj(transform);
};

gulfile.js

gulp.task('posts', function () {
    gulp.src(config.posts)
    // frontMatter処理してhtml化する
        .pipe($.frontMatter({ remove: true }))
        .pipe(myFrontmatter())
        .pipe($.markdown())
    // まとめてsortしてメタ情報を付与して目次を出力する
        .pipe(makeToc('index.html', { dest: 'posts' }))
        .pipe(gulp.dest('build'))
    // 再び分解する
        .pipe(resplit())
    // 以降通常
        .pipe($.debug({ title: 'files:' }))
    // テンプレートを適用して
        .pipe(ejsApplyer({ filename: 'templates/page.ejs' }, { root_path: '../../../../' }))
    // 出力
        .pipe(gulp.dest('build/posts'))
    ;
});