アイコンリサイズ用のnpmパッケージを作ってみた その2

ChainZ(クリエイター)
いろいろやってます。

前回の記事ではiconcというパッケージの基本機能を実装しましたが、今回の記事はそれをコマンドツールにしてみます。

前回と同じようにテストを書いてみます:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
// test/index.test.js

// spawnを使ってコマンドを発行する
const spawn = require('child_process').spawn;

...

it('iconc command', (done) => {
    // サンドボックスパス ./test/sandbox
    const sandbox = path.join(__dirname, './sandbox');

    // サンドボックスは毎回リセット
    fs.removeSync(sandbox);
    fs.ensureDirSync(sandbox);

    // ../bin/iconc -f ../file/_icon_.png -d ./gen -s icon1:w100, icon2:w120, icon:p150というコマンドになります。
    // -f 対象ファイル
    // -d 生成したファイルの保存先
    // -s リサイズ設定
    // 前回の実装だと、-s引数ファイルのパス、JSON内容、yaml内容やJSコードのいずれかに対応していますが、さすがにコマンドラインでJSONとyaml書くのが大変なので、
    // <ファイル名>:<リサイズタイプ(pかw)><サイズ>, ...というフォーマットにします
    const child = spawn(path.join(__dirname, '../bin/iconc'), ['-f', '../file/_icon_.png', '-d', './gen', '-s', 'icon1:w100, icon2:w120, icon:p150'], {
        cwd: sandbox,
        stdio: 'inherit'
    });

    // 終了時に実行結果を検証。
    child.on('close', () => {
        Object.keys({
            icon1: { w: 100 },
            icon2: { w: 120 },
            icon: { p: 150 }
        }).forEach(name => {
            fs.existsSync(path.join(sandbox, `gen/${name}.png`)).should.be.true();
        });
        done();
    });
});

...

そして、./bin/iconcを実装します:

引数を取得するyargsを使用しています:npm install yargs --saveで追加します

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
#!/usr/bin/env node

const path = require('path');
const argv = require('yargs').argv;
const Iconc = require('../');
const async = require('async');
const fs = require('fs');
const colors = require('colors');
const assert = require('assert');

const SPACE = 8;
const NODE_ENV = process.env.NODE_ENV;

if (NODE_ENV == 'testing') {
    console.log = function() {};
}

async.waterfall([
    (done) => {
        var opt = opt || {};

        // コマンドラインから渡された引数をアサインする
        opt.file = argv.f || argv.file || opt.file;
        opt.dest = argv.d || argv.dest || opt.dest;
        opt.schema = argv.s || argv.schema || opt.schema;

        // メッセージ表示
        console.log(`${'-'.repeat(SPACE-2)}> Source:`.blue, path.join(process.cwd(), opt.file));
        console.log(`${'-'.repeat(SPACE-2)}> Target:`.green, path.join(process.cwd(), opt.dest));
        console.log('');

        try {
            const iconc = new Iconc(opt);
            iconc.run(done);
        } catch (e) {
            return done(e);
        }
    }
], err => {
    if (err) return console.error(err.message.red);
});

npm testコマンドを叩くと:

もし実行権限のエラーが表示されたら、chmod +x ./bin/iconcで実行権限を与えてください。

やはりエラーになります。schemaはコマンドライン専用のフォーマットにまだ対応していないので、実装します:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
// ./index.js

...

            // schemaがstringタイプの場合はファイルパスと認識し、読み込みの試しをする
            if (typeof(self.schema) == 'string') {
                // コマンドライン専用フォーマットを優先
                if (/([^\:]+)\:(w|p)\d+\,?\s?/g.test(self.schema)){
                    const found = self.schema.split(',').map(i => i.trim());

                    // schemaを既存フォーマットに変換
                    self.schema = found.reduce((a, b) => {
                        const f = b.match(/([^\:]+)\:(w|p)(\d+)/);
                        a[f[1]] = {};
                        a[f[1]][f[2]] = parseInt(f[3]);
                        return a;
                    }, {});
                    return done();
                }
                return fs.exists(self.schema, exists => {
                    if (!exists) {
                        return done(new Error(`schema: ${self.schema} not found`));
                    }

...

もう一度npm testすると:

通りました!次は、今のコマンドをnpmのグローバルbinに入れてみます。package.jsonを開いて、キーを追加:

1
2
3
...,
"bin": "bin/iconc",
...

で、cdでプロジェクトのルートに移動し、npm install . -gを実行すると、iconcというツールはグロバールにインストールされ、iconcだけで呼び出せるようになりました。

例えば:

1
iconc -f ./src_icon.png -d . -s some_img:w100

実行結果は:

あれ?何のメッセージも表示されていません。実際ファイルは作成されています。確かに、ファイル処理の進捗の情報はどこにも出してないので、ちょっと怖いんですね。 今度はEventEmitterというクラスを継承してメッセージを出してみます。いよいよツールとして成立する形になりつつあるので、次回頑張ります〜

次の記事:http://befool.co.jp/blog/chainzhang/creating-npm-package-3/