更新ボタンも押すのが面倒な人のためのJavaScript 開発環境


最近、気にっている JavaScript の開発環境を紹介したいと思います。
Yeomen をベースラインとしたAnguler JSとNode JSの環境です。Yeomenが生成するGruntベースの環境は、ソースコードWatchされており、ソースを変更すると自動的にブラウザが更新され、ソースの変更がブラウザに反映されます。それにNodeJSも組み込み、
サーバサイドの開発もできるようになったので、重宝しています。

1. Yeomenの Angular JS の環境を作る

Yeomen をインストールして、

 yo angular:app

を実行すると、

├── Gruntfile.js
├── app
├── bower.json
├── karma-e2e.conf.js
├── karma.conf.js
├── node_modules
├── package.json
└── test

という、ディレクトリ構成の Yeomen の Angular JSの環境が構築されます。

そして、

grunt server

と実行すると、Web Server が立ち上がり、Angular JS アプリの雛形となる画面が表示されます。

Yeomen を使って作成した環境が便利なのは、Grunt の設定が予め入っているので、いろいろと便利です。その一つが、livereload機能で、 HTMLやJavaScriptを編集すると、自動的にブラウザーの画面がリフレッシュされ、更新ボタンを押す必要はありません。

2. Yeomenの Angular JS 環境と Node JSを連携させる

Grunt の設定を書き換えると、Livereloadを Node JS でも利用することができます。

Expressを組み込む

はじめに、Expressを組み込むために、package.json

{
 ....
  "dependencies": {
    "karma-script-launcher": "~0.1.0",
    "karma-firefox-launcher": "~0.1.0",
    "karma-chrome-launcher": "~0.1.0",
    "karma-html2js-preprocessor": "~0.1.0",
    "karma-jasmine": "~0.1.3",
    "karma-requirejs": "~0.1.0",
    "karma-coffee-preprocessor": "~0.1.0",
    "karma-phantomjs-launcher": "~0.1.0",
    "karma": "~0.10.2",
    "grunt-karma": "~0.6.1",
    "express": "latest",
    "ejs": "*"
  },
 .....
}

と、dependenciesにexpressとejbを追加します。

次に、下記のようなexpressのプログラムを作成し、app.jsとして保存します。

var express = require('express');
var http = require('http');
var path = require('path');

var app = express();

app.set('port', process.env.PORT || 3000);
app.use(express.bodyParser());

// フォルダを静的コンテンツのフォルダとする	
app.use(express.static(path.join(__dirname, 'dist')));

http.createServer(app).listen(app.get('port'), function(){
  console.log('Express server listening on port ' + app.get('port'));
});

静的コンテンツのフォルダは、"dist"にしてください。

そして、

 grunt build

と、実行し、JavaScriptCSSコンパイルしたら、

 node app

と実行すると、Express経由で、Angular JSの静的コンテンツを参照することができます。

しかし、これでは、Livereloadとは連携できていません。

Grunt 経由で Expressを起動する

package.json

  "devDependencies": {
    "grunt": "~0.4.1",
    "grunt-contrib-copy": "~0.4.1",
    "grunt-contrib-concat": "~0.3.0",
    "grunt-contrib-coffee": "~0.7.0",
    "grunt-contrib-uglify": "~0.2.0",
    "grunt-contrib-compass": "~0.3.0",
    "grunt-contrib-jshint": "~0.6.0",
    "grunt-contrib-cssmin": "~0.6.0",
    "grunt-contrib-connect": "~0.3.0",
    "grunt-contrib-clean": "~0.4.1",
    "grunt-contrib-htmlmin": "~0.1.3",
    "grunt-contrib-imagemin": "~0.1.4",
    "grunt-contrib-watch": "~0.4.0",
    "grunt-usemin": "~0.1.11",
    "grunt-svgmin": "~0.2.0",
    "grunt-rev": "~0.1.0",
    "grunt-karma": "~0.4.3",
    "grunt-open": "~0.2.0",
    "grunt-concurrent": "~0.3.0",
    "matchdep": "~0.1.2",
    "connect-livereload": "~0.2.0",
    "grunt-google-cdn": "~0.2.0",
    "grunt-ngmin": "~0.0.2",
    "grunt-express-server": "latest"
  }

と、grunt-express-serverを追加し、Gruntfile.js に express-serverの設定を、

  .... 
  // Expressの設定を追加
  grunt.initConfig({
    express: {
      options: {
        // Override defaults here
      },
      dev: {
        options: {
          script: 'app.js'
        }
      },
    },
    yeoman: yeomanConfig,
  .....
    // expressのリロードの設定を組み込む
    watch: {
      express: {
        files:  ['routes/{,*/}*.js', 'app-client/{,*/}*.js','app.js' ],
        tasks:  [ 'express:dev'],
        options: {
          nospawn: true
        }
      },
  .....
  // Serverコマンドにexpressを組み込む
    grunt.registerTask('server', function (target) {
    if (target === 'dist') {
      return grunt.task.run(['build', 'open', 'connect:dist:keepalive']);
    }

    grunt.task.run([
      'clean:server',
      'concurrent:server',
      'connect:livereload',
      'express:dev',
      'open',
      'watch'
    ]);
  });

と設定を追加し、grunt server コマンドで express を起動するようにします。
これで、app.jsを変更すると、自動的にexpressは再起動され、変更が反映されます。

しかし、expressが別のポートで立ち上がっているので、grunt serverのコンテンツとは、クロスドメインとなってしまっています。

Proxy 経由でExpressにアクセスする

同じポートでアクセスするためには、Grunt に proxy の設定を追加します。
まず、package.json

  "devDependencies": {
    "grunt": "~0.4.1",
    "grunt-contrib-copy": "~0.4.1",
    "grunt-contrib-concat": "~0.3.0",
    "grunt-contrib-coffee": "~0.7.0",
    "grunt-contrib-uglify": "~0.2.0",
    "grunt-contrib-compass": "~0.3.0",
    "grunt-contrib-jshint": "~0.6.0",
    "grunt-contrib-cssmin": "~0.6.0",
    "grunt-contrib-connect": "~0.3.0",
    "grunt-contrib-clean": "~0.4.1",
    "grunt-contrib-htmlmin": "~0.1.3",
    "grunt-contrib-imagemin": "~0.1.4",
    "grunt-contrib-watch": "~0.4.0",
    "grunt-usemin": "~0.1.11",
    "grunt-svgmin": "~0.2.0",
    "grunt-rev": "~0.1.0",
    "grunt-karma": "~0.4.3",
    "grunt-open": "~0.2.0",
    "grunt-concurrent": "~0.3.0",
    "matchdep": "~0.1.2",
    "connect-livereload": "~0.2.0",
    "grunt-google-cdn": "~0.2.0",
    "grunt-ngmin": "~0.0.2",
    "grunt-express-server": "latest",
    "grunt-connect-proxy": "latest"
 },
||< 
と、 "grunt-connect-proxy"を追加します。
次に、Gruntfile.jsを開き、connect-proxyの設定を,
>|javascript|
// 
var proxySnippet = require('grunt-connect-proxy/lib/utils').proxyRequest;

 .....

   connect: {
      options: {
        port: 9000,
        // Change this to '0.0.0.0' to access the server from outside.
        hostname: 'localhost'
      },
      // /api 以下のリクエストを 3000 ポートにする
      proxies: [{
        context: '/api',
        host: 'localhost',
        port: '3000',
        https: false,
        changeOrigin: false
      },],
      // proxySnippetを組み込む
      livereload: {
        options: {
          middleware: function (connect) {
            return [
              lrSnippet,
              proxySnippet,
              mountFolder(connect, '.tmp'),
              mountFolder(connect, yeomanConfig.app)
            ];
          }
        }
      },

 ....
 // proxy を grunt server コマンドに組み込む
    grunt.task.run([
      'clean:server',
      'configureProxies',
      'concurrent:server',
      'connect:livereload',
      'express:dev',
      'open',
      'watch'
    ]);
  });

と、組み込みます。

そして、

 grunt server

と、実行し、app.js を

var express = require('express');
var http = require('http');
var path = require('path');

var app = express();

app.set('port', process.env.PORT || 3000);
app.use(express.bodyParser());

app.use(express.static(path.join(__dirname, 'dist')));

// Expressの実装を追加する
app.get("/api/test",function(req,res){
	res.send("test");
})

http.createServer(app).listen(app.get('port'), function(){
  console.log('Express server listening on port ' + app.get('port'));
});

と、/api/test で test と返すような実装を追加し、ファイルを保存すると、Expressは自動的に再起動され、http://localhost:9000/api/testにアクセスすると、Proxy経由でtestと返されます。

作成したプロジェクトは、https://github.com/takeshi/yeoman-angular-express-sample にアップしました。

Yeomenの使い方は下記の本で紹介されています。

AngularJS

AngularJS