Skip to content
uupaa edited this page Jan 7, 2015 · 35 revisions

このエントリでは、WebModule version 0.3.x 以上におけるモジュール開発のワークフローについて説明します。 ( English Version )

WebModule version 0.3.x 未満のフォーマットでモジュールを作成している場合は更新が必要です。マイグレーションを参照してください。

WebModule を使ったモジュール開発のワークフローについて

ここでは、例として ~/workflow ディレクトリ以下に GitHub 上で作成した MyExample.js リポジトリをクローンし、MyExample.js モジュールを肉付けし、テストし、公開(npm publish)するまでの流れについて説明します。

作業の途中で問題が発生した場合は、トラブルシュート を参考にしてください。

概要

  1. 必要なソフトウェアをインストールし設定を行う

    # Install JDK and add JAVA_HOME to .zshrc
    export JAVA_HOME=`/usr/libexec/java_home -v 1.x.y_zz`
    
    # Install Homebrew
    $ ruby -e "$(curl -fsSL https://raw.github.com/Homebrew/homebrew/go/install)"
    $ brew -v
    > Homebrew 0.9.5
    
    # Install ndoe.js and npm
    $ brew install node
    $ npm -v
    > 2.0.2
    
    # add NODE_PATH to .zshrc
    export NODE_PATH="/usr/local/lib/node_modules"
    
    # Install dependency modules
    $ npm install -g plato
    $ npm install -g jshint
    $ npm install -g uupaa.compile.js
  2. GitHubで MyExample.js を作成し、~/workspace 以下に WebModule, MyExample.js, node-webkit を設置する

    ~/workflow
        ├── MyExample.js
        ├── node-webkit
        └── WebModule
    
  3. npm run setup で必要なファイルをコピーし npm run sync でセットアップを行う

    $ npm run setup
    $ npm run sync
  4. lib/MyExample.js を修正し, test/testcase.js にテストを記述し、npm run test でテストを行う

    $ npm start
    $ npm run test
    $ npm stop
  5. README.md を修正し、package.json に検索用のキーワードを埋める

    {
      "keywords": ["Example", "WebModule", "Unstable"],
    }
  6. npm run lint, npm run score で品質を高めた後に git push, npm publish で公開する

    $ npm run lint
    $ npm run score
    $ git add .
    $ git commit -m "first commit"
    $ git push
    $ npm publish
  7. https://github.com/ユーザ名/MyExample.js/wiki にドキュメントを記述する

  8. お疲れ様でした! 😆 🍻

詳細

以下詳細です。

  1. WebModule が必要とするソフトウェアとインストールについて:

  2. WebModule を ~/workspace にクローンします:

    $ mkdir ~/workspace
    $ cd workspace
    $ git clone git@github.com:uupaa/WebModule.git
  3. MyExample.js リポジトリを GitHub に作成します:

    • Creating a new repository を参考にしてください

      • (1) ユーザアカウントを選択します
      • (2) WebModule 名を入力します。 命名規則を参考にしてください
      • (3) Description を入力します(省略可能です)
      • (4) Initialize this repository with a README をチェックし、ライセンスを選択します。お勧めは MIT ライセンスです
      • (5) ボタンをクリックしてリポジトリを作成します
      Owner                    Repository name
      +-------------------+   +-------------------+
      | your account  (1) | / | MyExample.js  (2) |
      +-------------------+   +-------------------+
      
      Description (optional)
      +------------------------------------------------+
      | my first webmodule  (3)                        |
      +------------------------------------------------+
      
      [x] Public
      
      [x] Initialize this repository with a README  (4)
      +----------------------+  |  +---------------------------------+
      |                      |  |  | Add a license: MIT License  (4) |
      +----------------------+  |  +---------------------------------+
      
      +------------------------+
      | Create repository  (5) |
      +------------------------+
      
  4. 作成した WebModule (MyExample.js) リポジトリを ~/workspace にクローンします:

    • クローン後に、MyExample.js ディレクトリに移動してください
    > cd ~/workspace
    
    $ git clone git@github.com:YOUR-ACCOUNT/MyExample.js.git
    $ cd MyExample.js
  5. npm run setup コマンドで MyExample.js に必要なファイルをコピーします

    $ cd ~/workspace/MyExample.js
    
    $ node ../WebModule/run/setup
    >   - repositoryFullName: MyExample.js
    >   - repositoryName:     MyExample
    >   - copy source dir:    ~/workspace/WebModule/
    >   - copy target dir:    ~/workspace/MyExample.js/
    >
    >   clone:     ~/workspace/MyExample.js/lint/plato/README.md
    >   clone:     ~/workspace/MyExample.js/release/README.md
    >   clone:     ~/workspace/MyExample.js/.gitignore
    >   clone:     ~/workspace/MyExample.js/.jshintrc
    >   clone:     ~/workspace/MyExample.js/.npmignore
    >   clone:     ~/workspace/MyExample.js/.travis.yml
    >   clone:     ~/workspace/MyExample.js/index.js
    >   clone:     ~/workspace/MyExample.js/lib/MyExample.js
    >   clone:     ~/workspace/MyExample.js/test/testcase.js
    >   clone:     ~/workspace/MyExample.js/test/template/
    >                                       :
    >                                       :
    >   clone:     ~/workspace/MyExample.js/package.json
    >   exists:    ~/workspace/MyExample.js/README.md - overwrite it? (y/n): y
    >   overwrite: ~/workspace/MyExample.js/README.md
    >
    >   done.
    >
    >   Available next actions,
    >   `$ npm run`        # list up npm run-script
    >   `$ npm start`      # start local httpd server
    >   `$ npm run sync`   # sync scripts, install/update node modules, create test pages and minify
  6. num run sync コマンドを実行します

    • package.json のフォーマットのチェックとアップグレードが行われます
    • npm update が行われます
    • WebModule/MODULE_package.json と MyExample.js/package.json の同期が行われます
    • DEPRECATED なコードのチェックが行われます
    • package.json を手動で更新した場合は、その都度 $ npm run sync を実行してください
    $ npm run sync
  7. MyExample.js が必要とする外部モジュールがある場合は、package.json の dependencies や devDependencies に追加します:

    • $npm run build のコンパイル対象とする WebModule は dependencies に追加します
    • 動作テストで利用するが、コンパイルする必要がないモジュールは devDependencies に追加します
    • package.json 変更後は $ npm run sync コマンドを実行し、変更内容を適用してください
  8. Edit tags

    • package.json の keywords に検索用のタグを追加できます
    • ユーザ名 と WebModule は検索で使います。消さないでください
    {
      "keywords": ["Example", "WebModule", "Unstable"],
    }

Directory entry

~/workspace/MyExample.js 以下のディレクトリ構成です

$ cd
> ~/workspace/MyExample.js

$ tree
.
└── MyExample.js
    ├── bin/                     <- `$ node ../WebModule/run/setup --bin` コマンドで作成されるディレクトリです
    ├── lib/
    │  └── MyExample.js          <- モジュールのソースコードです
    ├── lint/
    │  └── plato/                <- npm run socre コマンドが生成するファイルを格納するディレクトリです
    ├── release/                 <- npm run min や npm run build コマンドで生成したファイルを格納するディレクトリです
    │  ├── MyExample.b.min.js    <- browser用にMinify したコードです
    │  ├── MyExample.w.min.js    <- worker用にMinify したコードです
    │  ├── MyExample.n.min.js    <- node用にMinify したコードです
    │  └── MyExample.nw.min.js   <- node-webkit用にMinify したコードです
    ├── test/                    <- npm test で実行するファイルを格納するディレクトリです
    │  ├── template/            <- test/*.js や test/*.html を生成する元となるテンプレートを格納するディレクトリです
    │  │   ├── browser.html     <- test/index.html の元となるテンプレートファイルです
    │  │   ├── node.js          <- test/node.js の元となるテンプレートファイルです
    │  │   ├── nw.html          <- test/nw.html の元となるテンプレートファイルです
    │  │   ├── nw.package.json  <- test/package.json の元となるテンプレートファイルです
    │  │   └── worker.js        <- test/worker.js を生成する元となるテンプレートを格納するディレクトリです
    │  ├── index.html            <- npm run page コマンドが生成するテストページです。Browser と WebWorkers のテストで使用します
    │  ├── node.js               <- npm run page コマンドが生成するテスト用のJavaScriptです。Node.js のテストで使用します
    │  ├── worker.js             <- npm run page コマンドが生成するテスト用のJavaScriptです。WebWorkers のテストで使用します
    │  ├── nw.html               <- npm run page コマンドが生成するテストページです。nw test による node-webkit のテストで使用します
    │  ├── package.json          <- npm run page コマンドが生成するテストページです。node-webkit がこのファイルを参照します
    │  ├── wmtools.js            <- テストで必要となる機能を集約したファイルです。
    │  │                              Reflection.js, Console.js, Valid.js, Help.js, Task.js, Test.js が同梱されています。
    │  └── testcase.js           <- テストケースを記述するファイルです
    ├── .gitignore               <- gitコマンド用の設定ファイルです
    ├── .npmignore               <- npmコマンド用の設定ファイルです
    ├── .jshintrc                <- npm run hint コマンドが参照する設定ファイルです
    ├── .travis.yml              <- Travis-Cl の設定ファイルです
    ├── index.js                 <- Node.js 用のエントリポイントです。require("MyExample.js") で読み込まれるファイルになります
    ├── package.json             <- npm の設定ファイルです
    ├── LICENSE
    └── README.md

Implement module

WebModule では、以下のようなモジュールの設計と実装の目安があります。
必ずしもこれらを守る必要はありませんが、善処してください。

  • テストが可能なように実装してください。テストがされていないモジュールは使用しないでください
  • 1 つのモジュールに 1 つのクラスが基本です。
    • Codec.jsが行っているように lib/Class1.js, lib/Class2.js のように複数のクラスを実装し Export することもできます
  • 1 つのクラスは 200〜400行程度の適切なボリュームで実装してください
  • 1 つのクラスにメソッドを10個以上持たせないでください。恐らく複雑すぎます、分割してください
  • repositories にあるモジュールも参考にしてください

Add test code

テストケースは ~/workspace/MyExample.js/test/testcase.js に記述します。

  • テストコードの書き方は、 repositories などを参考にしてください

  • テスト方法は testcase.js の冒頭部分に設定を記述できます、以下の部分を修正してください

    var test = new Test("MyExample", {
            disable:    false,  // テストを無効化します
            browser:    true,   // Browser でテストします
            worker:     true,   // WebWorkers でテストします
            node:       true,   // Node.js でテストします
            nw:         true,   // node-webkit でテストします
            button:     true,   // Browser のページに再テスト用のボタンを追加します
            both:       true,   // MyExample と MyExample_ の両方をテストします
                                // see https://github.com/uupaa/WebModule/wiki/SecondaryModulePattern
        }).add([
            testMyExample_value,
            testMyExample_isNumber,
            testMyExample_isInteger,
        ]);
    
    test.run().clone();
    

Change build target

package.json の webmodule プロパティ以下を修正することで、ビルドするソースコードの追加や設定の変更が可能です。


Improve cycle

コード( lib/MyExample.js )を修正したら、品質の確認とテストを行います。

$ npm start で簡易httpサーバを起動し、その後に npm run test とタイプすることでビルドとテストが実行されます。
テスト用のページは http://localhost:8000/MyExample.js/test/index.html でアクセスできます

$ npm start             # 簡易httpサーバをポート8000で起動します
$ npm stop              # 簡易httpサーバを終了します
$ npm test              # node.js + browser + worker でテストを行います
$ npm run browser       # ブラウザで lib/MyExample.js をテストします
$ npm run node          # node.js で lib/MyExample.js をテストします
$ npm run noded         # --debug-brk オプション付きで node.js で lib/MyExample.js をテストします
$ npm run sim           # iOS Simulator を起動し Simulator 上で browser + worker のテストを行います
$ npm run simx          # iOS Simulator を終了します
$ nw test               # node-webkit で lib/MyExample.js をテストを行います

$ npm run test          # npm run min を実行後に npm run node, npm run browser を実行します
$ npm run min           # lib/MyExample.js を minify し、release/MyExample.*.min.js を作成します。
$ npm run build         # lib/MyExample.js と dependencies に記述された依存モジュールのソースコードを一緒にビルドし release/MyExample.*.min.js を作成します

$ npm run hint          # jshint lib/*.js を実行し、ソースコードを静的に検査します
$ npm run score         # plato によるカバレッジを実施し、スコアをブラウザに表示します。

Build

npm run minnpm run build コマンドを実行すると release/MyExample.*.min.js を生成します。
* の部分には Browser なら b が、Worker なら w が、Node なら n が入ります。

ビルドに失敗した場合は、中間ファイル(release/.Minify.tmp.js) を確認してください

package.json に修正を加える事で、ビルドセッティングを変更できます。
デフォルトの設定は以下のようになっています。minify.js に指定可能な引数を参照してください。

  "scripts": {
    "min":          "node ../WebModule/run/minify.js --verbose --strict --keep --pretty",
    "build":        "node ../WebModule/run/minify.js --verbose --strict --module"
  }

Do test

npm test コマンドでテストが走ります。テスト対象は lib/MyExample.js と release/MyExample.*.min.js です。

最初に test/node.js による Node.js のテストが走り、
次に test/index.htmltest/worker.js によるブラウザ上でのテストが始まります。

  • テストは最初 lib/MyExample.js に対して node → browser → worker の順番で行い、次に release/MyExample.*.min.js に対して行います
  • テスト成功でブラウザの画面が緑に、失敗で赤くなります
  • Closure Compiler による MyExample.*.min.js のコンパイルに失敗している場合も赤くなります
  • Closure Compiler の ADVANCED_OPTIMIZATIONS MODE に対応したコードを記述していない場合もコンパイルに失敗します。
    • function SomeAPI() { return { foo: 123 }; } のように記述している場合は、識別子 foo がコンパイラによりリネームされ解決できなくなっている可能性があります
    • 解決するには、minify されているコードを展開し、コンパイル前のコードと照らし合わせを行います
      • MyExample.*.min.js を DevTools 上で開き、Minify されているコードを展開({ }をクリック)します
    • リネームされたくない名前やプロパティは global.MyExample.hoge = 1; ではなく global["MyExample"]["hoge"] = 1; のように保護してください。

Coverage

モジュールの作成がひと通り終わったら、モジュールの静的解析を行いコードの品質を明確にします。

  • npm run lint コマンドを実行すると、JSHint によるテストが行われます。
  • npm run score コマンドを実行すると、Plato による静的解析が行われ、スコアをブラウザに表示します。

  • lint erros をゼロにし、Average Maintainability は65点以上を目指して下さい。60点以下は赤点です
  • JSHint のチェックを緩和するには、 MyExample.js/.jshintrc を修正してください
  • 問題が発覚した場合は、コードを修正 → npm run hintnpm test を繰り返し、動作をチェックしつつ修正していきます

Update README.md

モジュールの作成と品質向上作業が終わったら、MyExample.js の README.md を修正します。


Commit and publish

コードが公開可能なクオリティに達した事を確認できたら、GitHub への push と npm publish で世界にモジュールを公開しましょう。

  • npm run patch を行うと、package.json に記述されている version: "0.0.x" の x が 1 上がります。

    • このような version x.y.z のような3桁のバージョン表記は Semver と呼ばれています。
    • x がメジャーバージョン, y がマイナーバージョン, z がパッチバージョンです。
    • 大幅で破壊的な変更を行い、互換性がなくなる修正をしてしまった場合は、x を +1 します。
    • それほどインパクトがない機能の追加や修正を行った場合は場合は、y を +1 します。
    • バグフィックスなど軽微な修正を行った場合は、z を +1 します。
    • package.json の version を上げずに npm publish を繰り返すと publish 時にエラーになります。
    $ npm run patch         # update patch version
    
    > update patch version. 0.0.0 -> 0.0.1
  • git へのコミットと npm への publish は以下のようにします

$ git add .
$ git status

> # On branch master
> # Changes to be committed:
> #   (use "git reset HEAD <file>..." to unstage)
> #
> #    new file:   .gitignore
> #    new file:   .jshintrc
> #    new file:   .npmignore
> #    new file:   LICENSE
> #    modified:   README.md
> #    new file:   index.js
> #    new file:   lib/MyExample.js
> #    new file:   package.json
> #    new file:   test/index.html
> #    new file:   test/node.js
> #    new file:   test/worker.js
> #    new file:   test/testcase.js

$ git commit -m "first commit"
$ git push

$ npm publish

Write documents

npm publish が終わったら GitHub/Wiki に Markdown でドキュメントを記述します。

open https://github.com/uupaa/MyExample.js/wiki/MyExample コマンドを実行するか、
open test/index.html でブラウザを開き DevTools のコンソールで MyExample.help ENTER とタイプします。

$ open https://github.com/uupaa/MyExample.js/wiki/MyExample

表示されたリンクから、 Reference: のリンクをクリックすると、GitHub の MyExample.js の wiki ページが生成されます。

新しく生成された wikiページに、API の説明を記述してください。

    # MyExample.js

    MyExample.js は、WebModule の機能を説明するためのダミーライブラリです。

    ## MyExample

    new MyExample(value:Number) は、MyExample クラスのインスタンスを生成します。  
    value には数値を指定します。

    ```js
    function MyExample(value) { // @arg Number: the valud.
        this._value = value;
    }
    ```


    ## MyExample.prototype.value

    MyExample#value():Number は、[MyExample](#MyExample) で渡された value を返します。

    ```js
    function MyExample_value() { // @ret Number:
        return this._value;
    }
    ```

    ## MyExample.prototype.isNumber

    MyExample#isNumber():Boolean は、[MyExample](#MyExample) で渡された value が Number 型なら true を返します。

    ```js
    function MyExample_isNumber() { // @ret Boolean:
        return typeof this._value === "number";
    }
    ```

    ## MyExample.prototype.isInteger

    MyExample#isInteger():Boolean は、[MyExample](#MyExample) で渡された value が Number 型で端数を持たない場合に true を返します。

    ```js
    function MyExample_isInteger() { // @ret Boolean:
        return typeof this._value === "number" &&
               Math.floor(this._value) === this._value;
    }
    ```

ドキュメントの記述が面倒だな… と感じたら、こちらのエントリを御覧ください。

Clone this wiki locally