gist

2012年2月12日日曜日

今すぐできる!Cloud9 IDEカスタマイズ! npmインストーラを作ってみよう。

Cloud9 IDEを拡張して、npmのインストーラを作ってみました。npm installとnpm listができるようになりました。

まず、クライアントのViewです。

cloud9/client/ext/npmtools/npmtools.xml

<a:application xmlns:a="http://ajax.org/2005/aml">
    <a:window
      id        = "winDatagrid"
      title     = "NPM Packages"
      icon      = ""
      center    = "true"
      resizable = "true"
      buttons   = "close"
      modal     = "false"
      skin      = "bk-window"
      width     = "660"
      height    = "400"
      kbclose   = "true"
      draggable = "true">
        <a:vbox anchors="0 0 0 0" edge="0 0 0 0">
            <a:hbox>
                <a:textbox id="tbPackage" flex="1">
                
                </a:textbox>
                <a:button id="btnInstall" caption="Install">
                
                </a:button>
            </a:hbox>
            <a:datagrid id="dgList" flex="1">
                <a:each match="[packages]">
                    <a:column caption="Name" value="[@name]" width="50%" />
                    <a:column caption="Version" value="[@version]" width="50%" />
                </a:each>
            </a:datagrid>
        </a:vbox>
    </a:window>
</a:application>

クライアントのロジック。

cloud9/client/ext/npmtools/npmtools.js

define(function(require, exports, module) {
    var ide = require('core/ide');
    var ext = require('core/ext');
    var panels = require("ext/panels/panels");
    var markup = require('text!ext/npmtools/npmtools.xml');
    var util = require("core/util");
    
module.exports = ext.register('ext/npmtools/npmtools', {
    name   : "NPM Tools",
    dev    : "Tomoyuki Inoue",
    alone  : true,
    type   : ext.GENERAL,
    markup : markup,
    command : "npmtools",
    desp   : [panels],

    nodes  : [],
        
    hook : function(){
        var _self = this;

        this.nodes.push(
            mnuWindows.insertBefore(new apf.item({
                caption : "NPM Tools",
                onclick : function(){
                    ext.initExtension(_self);
                    // モデルを作成
                    var model = new apf.model();
                    // datagridのmodel属性にmodelを設定
                    dgList.setModel("model", model);
                    // data要素を作成
                    var data = winDatagrid.ownerDocument.createElement("data");
                    // dataをロード
                    model.load(data);
                    // modelを設定
                    dgList.setModel(model);
                    
                    _self.listPackages();
                    
                    // ボタンクリック時のイベントハンドラ
                    btnInstall.addEventListener("click", function(e){
                        if(tbPackage.value.length > 0){
                            tbPackage.clear();
                            btnInstall.disable();
                            _self.installPackage(tbPackage.value);
                        }
                    });
                    // datagridを表示
                    winDatagrid.show();
                }
            })
        ), mnuWindows.firstChild);
        // サーバから受信した時の処理を記述。
        ide.addEventListener("socketMessage", this.onMessage.bind(this));
    },
    
    onMessage: function(e) {
            var message = e.message;
            // datagrid_test以外なら何もしない。
            if(message.type !== "result" 
            && message.subtype !== "npmtools")
                return;
            
            if(message.body.subcommand === "list"){
                this.addPackageToDatagrid(message.body.name, message.body.version);
            }
            if(message.body.subcommand === "install"){
                m.clear();
                this.listPackages();   
            }
    },
    
    listPackages: function(){
        // npm listの実行結果を取得
        ide.send({
            command: "npmtools",
            subcommand: "list"
        });
    },
    
    installPackage: function(packageName){
        var data = {
            command: "npmtools",
            subcommand: "install",
            packagename :packageName
        };
        // サーバにデータを送信。
        ide.send(data);
    },
    
    addPackageToDatagrid: function(packageName, version){
        // モデルを取得
        var m = dgList.getModel();
        
        // packages要素を作成
        var member = winDatagrid.ownerDocument.createElement('packages');
        // name属性を設定
        member.setAttribute('name', packageName);
        // version属性を設定
        member.setAttribute('version', version);
        // data要素の子要素としてpackages要素を追加
        m.data.appendChild(member); 
        
        btnInstall.enable();
    },
        
    init : function(apfNode){},
        
    enable : function(){
        if (!this.disabled) return;
        
        this.nodes.each(function(item){
            item.enable();
        });
        this.disabled = false;
    },
    
    disable : function(){
        if (this.disabled) return;
        
        this.nodes.each(function(item){
            item.disable();
        });
        this.disabled = true;
    },
    
    destroy : function(){
        this.nodes.each(function(item){
            item.destroy(true, true);
        });
        this.nodes = [];
    }
});

});

サーバ側のプラグインです。

cloud9/server/cloud9/ext/npmtools/npmtools.js

var Plugin = require("cloud9/plugin");
var sys    = require("sys");
var fs     = require("fs");

// 初期設定
var DataGridTestPlugin = module.exports = function(ide) {
    this.ide   = ide;
    // hooksに「command」を追加する
    this.hooks = ["command"];
    // 自分の名前
    this.name  = "npmtools";
};

// Pluginクラスを継承する。
sys.inherits(DataGridTestPlugin, Plugin);

(function() {

    // command を実装。workspaceから実行される。
    this.command = function(user, message, client) {
        // 自分以外のコマンドは受け付けない。
        if(message.command != "npmtools")
            return false;
            
        var _self = this;
        // サブコマンドがlistの場合、作業フォルダのpackage.jsonを探索。
        if(message.subcommand === "list") {
            this.listPackages(this.ide.workspaceDir, function(err, json){
                if(err) throw err;
                console.log(json.name + "," + json.version);
                
                _self.sendResult(0, message.command, {
                    subcommand: message.subcommand,
                    name: json.name,
                    version: json.version
                });
            });
            return true;
        }
        
        if(message.subcommand === "install"){
            var args = [message.subcommand, message.packagename];
            // npm installを実行
            this.spawnCommand("npm", args, this.ide.workspaceDir, 
                function(err) {},
                function(out) {},
                function(code, err, out) {
                    // クライアントに送信
                    _self.sendResult(0, message.command, {
                        subcommand: message.subcommand, 
                        packagename: message.packagename
                    });
                }
            );
            return true;
        }
        
        return false;
    };

    // サーバ終了時に起動。
    this.dispose = function(callback) {
        // TODO kill all running processes!
        callback();
    };
    
    // NPMパッケージのリストを取得する
    this.listPackages = function(path, cb) {
        // パスの読み込み開始
        var path = path + "/node_modules";
        fs.readdir(path, function(err, dirs){
            if(err) cb(err, null);
            // dirsには指定したパスのファイル名・ディレクトリ名の一覧が入っている
            for(var i = 0; i < dirs.length; i++){
     
                var dir = dirs[i];
                // 頭文字が.の場合読み込まない
                if(dir.charAt(0) === '.') continue;
                 
                // package.jsonのパスを指定
                var jsonPath = path + "/" + dir + "/package.json";
                 
                // package.jsonを読み込み開始
                fs.readFile(jsonPath, function(err, data){
                    if(err) cb(err, null);
                    // dataには読み込んだBufferが入っている
                    // toString('utf8')で文字列にする
                    var jsonStr = data.toString('utf8');
                    // JSONをパースしてコールバック
                    cb(null, JSON.parse(jsonStr));
                });
            }
        });        
    };

}).call(DataGridTestPlugin.prototype);

実行結果はこんな感じ。テキストボックスにパッケージ名を入力してInstallボタンで、npmでインストールできます。

関連ページ

0 件のコメント: