Create a gist now

Instantly share code, notes, and snippets.

@sh19910711 /20141204.md Secret
Created Dec 3, 2014

What would you like to do?

RequireJSとか

JavaScript Advent Calendar 2014

1. phantomochajs

Mocha などでクライアントサイドの JavaScript をテストするときに、 忌まわしき test_runner.html を自動生成できるツールをつくった。

URL: https://www.npmjs.org/package/phantomochajs

gulp.src() などを使ってテストコードを指定すると、 テストフレームワークとかテストコード自身を読み込む HTML ファイルを自動生成し、 テストを実行できる状態のウェブサーバーを立ち上げて PhantomJS 上でテストを実行する。

gulp = require "gulp"
phantomochajs = require "phantomochajs"

gulp.task "test/client", ->
  gulp.src ["spec/spec_helper.coffee", "spec/**/*_spec.coffee", "spec/**/*_spec.js"]
    .pipe phantomochajs()

ほかには、以下のような感じで依存ライブラリを指定できるようにした。

gulp.task "test", ->
  gulp.src ["spec/spec_config.coffee", "spec/spec_helper.coffee", "spec/**/*_spec.coffee"]
    .pipe phantomochajs(
      dependencies: [
        "./bower_components/requirejs/require.js"
        "./tmp/js/app/utils.js"
      ]
      test_dependencies: [
        "./bower_components/mocha/mocha.js"
        "./bower_components/chai/chai.js"
        "./bower_components/sinon/pkg/sinon.js"
      ]
    )

2. RequireJS で glob

話は変わって、RequireJS で glob ができると個人的に都合が良い感じがあり、 RequireJS のプラグイン指定みたいに HTTP リクエストで演算的な操作ができると良さそうだなと思っていた。 例えば、以下のような構成で AMD なモジュールを置いているときに、

(root)
  |-my_module_a.js
  \-sub_modules/
     |-my_module_1.js
     \-my_module_2.js

GET /sub_modules/*.js という感じの HTTP リクエストを受け取ったら、 ファイル名などをベースにモジュールの名前を決めて、 下記のような JavaScript コードを生成する。

define(
  ["sub_modules/my_module_1", "sub_modules/my_module_2"],
  function(MyModule1, MyModule2) {
    function SubModules() {}
    SubModules.prototype.MyModule1 = MyModule1;
    SubModules.prototype.MyModule2 = MyModule2;
    return SubModules;
  }
)

これで以下のような形で名前空間的なものを導入できるはず。

# coffeescript
requirejs ["sub_modules/*"], (SubModules)->
  mod_1 = new SubModules::MyModule1()
  mod_2 = new SubModules::MyModule2()

ディレクトリがネストしているときも、さらに glob をするように依存関係を指定することで対応できる。 (define(["{dirname}/*", ...]) みたいな感じで)

実際に phantomochajs の隠し機能として追加してみたところ(amd_glob: true で有効にできる)、 少なくとも普通の単純なケースに関しては動作が確認できて、 これでテスト時については名前空間的なものを利用することができるようになった。

あとはビルドができると良いが、こちらはいまひとつ手が進まなかった。 AMD の最適化系ツールで scalableminds/amd-optimize という、 gulp.src などから流れてくるファイルを依存関係順で並べ替え、適切な ID を割り振った状態で書き出してくれるものがあって、 この出力を gulp-concat などを使って結合すると良い感じに固まる。

これを使って、

gulp = require "gulp"
concat      = require "gulp-concat"
amdSource   = require "amd-source" # TODO?: create
amdOptimize = require "amd-optimize"

http = require "http"
connect        = require "connect"
connectMincer  = require "connect-mincer"
connectAmdGlob = require "connect-amd-glob" # TODO?: create

gulp.task "build", ->
  app = connect()
  app.use connectMincer(...) # ビルド対象
  app.use connectAmdGlob(...) # glob なリクエストに対して AMD かつ名前空間的なモジュールを返す
  server = http.createServer(app).listen(...)

  # entry_point.js から辿れる全ての依存先モジュールを取得し、
  # vinyl-fs に落とし込んで適切な ID を割り振りストリームに流しこむ
  amdSource ["entry_point"], {baseUrl: "http://url/to/glob-able"}
    .pipe amdOptimize("entry_point")
    .pipe concat("main.js")
    .pipe gulp.dest("dist/")
    .on "end", -> server.close()

みたいな形でビルドできると良さそう。

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment