benvを使ってangular.jsのコードをnode上で動作させる

はじめに

テストなどを書きたい時にブラウザを立ち上げずに動作チェックなどをしたいことがある。 しかしangular.jsを直接nodeからrequireすると怒られる

$ npm install --save angular
$ node -p 'require("angular")'
ReferenceError: window is not defined
    at Object.<anonymous> (/home/podhmo/tmp/angular-api-test/node_modules/angular/angular.js:29016:4)
    at Module._compile (module.js:435:26)
    at Object.Module._extensions..js (module.js:442:10)
    at Module.load (module.js:356:32)
    at Function.Module._load (module.js:311:12)
    at Module.require (module.js:366:17)
    at require (module.js:385:17)
    at Object.<anonymous> (/home/podhmo/tmp/angular-api-test/node_modules/angular/index.js:1:63)
    at Module._compile (module.js:435:26)
    at Object.Module._extensions..js (module.js:442:10)

benvを使う

artsy/benv を使うとjsdomでwrapした環境が手に入るらしい。 以下の記事を参考にした。

about benv

artsy/benv はjsdomのwrapperの模様。

Stub a browser environment and test your client-side code in node.js.

以下の様なコードを書く

// loadSample.js
'use strict';
var benv = require('benv');

function setup(cb){
  benv.setup(function(){
    global.Node = window.Node;
    benv.expose({
      angular: benv.require('./node_modules/angular/angular.js', 'angular')
    });
    cb();
  });
}

setup(function(){
  console.log('window is', typeof window);
  console.log('document is', typeof document);
  console.log('angular is', typeof angular);
  console.log('window.angular === angular', window.angular === angular);
});

以下の様に実行できる。

$ node loadSample.js
window is object
document is object
angular is object
window.angular === angular true

global.Node = window.Node について

参考にした記事に従ってコードを書いた時以下の様なエラーが発生した。これを防ぐために追加している。

ReferenceError: Node is not defined
    at /home/podhmo/tmp/angular-api-test/node_modules/angular/angular.js:2789:22
    at /home/podhmo/tmp/angular-api-test/node_modules/angular/angular.js:29016:3
    at Object.<anonymous> (/home/podhmo/tmp/angular-api-test/node_modules/angular/angular.js:29116:4)
    at Module._compile (module.js:435:26)
    at Object.Module._extensions..js (module.js:442:10)
    at Module.load (module.js:356:32)
    at Object.load (/home/podhmo/tmp/angular-api-test/node_modules/rewire/lib/moduleEnv.js:20:18)
    at internalRewire (/home/podhmo/tmp/angular-api-test/node_modules/rewire/lib/rewire.js:57:15)
    at rewire (/home/podhmo/tmp/angular-api-test/node_modules/rewire/lib/index.js:11:12)
    at Object.module.exports.require (/home/podhmo/tmp/angular-api-test/node_modules/benv/index.js:69:13)

gist

gistにしてみた。

https://gist.github.com/podhmo/c17f6e7b720019787729

追記

このままだとangularをどうやって使うかはわからないのでsampleも書いてみた。

// appSample.js
'use strict';
var benv = require('benv');

function setup(cb){
  benv.setup(function(){
    global.Node = window.Node;
    benv.expose({
      angular: benv.require('./node_modules/angular/angular.js', 'angular')
    });
    cb();
  });
}

setup(function(){
  angular.module('myApp', [])
    .controller('helloController', ['$scope', function ($scope) {
      $scope.title = 'Node!';
    }]);

  document.body.innerHTML = '<div ng-controller="helloController">{{ title }}</div>';
  angular.bootstrap(document, ['myApp']);
  console.log(angular.element(document.body).html());
});

以下の様な出力になる

$ node appSample.js
<div ng-controller="helloController" class="ng-scope ng-binding">Node!</div>

参考