LoginSignup
24
24

More than 5 years have passed since last update.

AngularJSのコード書いたらDgeniでAPIページ作って公開したれ

Last updated at Posted at 2014-12-14

このエントリは(Qiitaじゃない方の) Angular JS Advent Calendar 14日目の投稿です.

やりたかったこと

AngularJSでDirectiveやService等の部品をガリガリと作成したら, 「みんな、俺のイカしたAPI見てくれよ!」とかやりたくなる筈。やりたくなれ。
丁度、AngularJS本家APIリファレンスのイメージ.

しかも、.htmlと.js, .cssさえあれば、ブラウザで作成した部品達の動作確認自体まで行えるのがフロント系のフレームワークのよいところ.

下記のようなAngularJSのソース + ngdoc書式のコメントを書いたら, こんな感じのAPIページ を表示しましょう、というお話.

'use strict';

/**
 *
 * @ngdoc directive
 * @module exampleOfGeneratorNgdoc
 * @name sampleElem
 * @restrict E
 * @description 
 * This is a sample directive.
 *
 * @example
    <example module="sampleElemExample" deps="" animate="false">
      <file name="index.html">
        <sample-elem></sample-elem>
      </file>
      <file name="main.js">
        angular.module('sampleElemExample', ['exampleOfGeneratorNgdoc']);
      </file>
    </example>
 *
 **/
angular.module('exampleOfGeneratorNgdoc').directive('sampleElem', function () {
  return {
    restrict: 'E',
    template: '<div class="sample-awesome">Hello, AngularJS directive!</div>'
  };
});

Dgeni

上記のサンプルコードのように, ngdocコメントからHTMLを生成する手段としてはgrunt-ngdocs, gulp-ngdocs 等がある.
今回は, ngdocコメントの処理にAngularJS本家が利用しているという理由で, Dgeni を用いてみることにした.

Dgeniは、拡張性が高く、ServiceやFactory, Filterと言った概念から構成されているため、AngularJS使いにとって取っつきやすそう、と思ったのがきっかけ.

なお, Dgeniについては、@kinzai 氏がとても分かり易いエントリを記載されているので、是非一読されたし.

さて、Dgeniをngdoc生成器として利用するためには、Dgeni本体とdgeni-packagesというnode.jsのパッケージを取り込む必要がある.

npm install dgeni dgeni-packages --save-dev

Dgeni本体は、processorと呼ばれるタスク実行器を順次実行するランナーのようなものと考えるのがよい. 各processorには下記のようなものがある:

  • readFileProcessor: ソースファイルを読み込むprocessor
  • extractJSDocCommentsProcessor: アノテーションを解析するprocessor
  • writeFilesProcessor: ドキュメントを吐き出すprocessor

Dgeni単体では、ngdocやjsdocのコメントを解釈するprocessorを持っていないため、これらのprocessorが登録されているdgeni-packageを併用することが多い.
(gulp本体と各種gulpプラグインの関係を思い浮かべてもらえばよいと思う)

当初、dgeni-packagesに含まれるngdocパッケージを読み込み、Grunt or Gulpから呼び出すだけでいい感じにHTMLが生成されると思い込んでいたのだが、これは結構な間違いであった.

  1. dgeni-packagesに含まれているngdoc生成器は、API用HTMLの一部しか吐いてくれない
  2. 部品の動作サンプルを作るために、bowerの依存関係やら自作モジュールのビルドを考慮する必要があり、ドキュメント生成以外にもgulp(またはgrunt)のタスクを色々用意しないといけない

Dgeniが出力するのは、下記のような断片的なHTMLのみである.

partials/api/exampleOfGeneratorNgdoc/directive/sampleElem.html
<header class="api-profile-header">
  <h1 class="api-profile-header-heading">sampleElem</h1>
  <ol class="api-profile-header-structure naked-list step-list">
    <li>
      - directive in module <a href="api/exampleOfGeneratorNgdoc">exampleOfGeneratorNgdoc</a>
    </li>
  </ol>
</header>
<div>
  <h2>Directive Info</h2>
  <ul>
    <li>This directive executes at priority level 0.</li>
  </ul>
  <h2 id="usage">Usage</h2>
  <div class="usage">
    <ul>
      <li>as element:
      <pre><code>&lt;sample-elem&gt;&#10;...&#10;&lt;/sample-elem&gt;</code></pre>
      </li>
  </div>
</div>

出力先のpathに'partial'とあることからも推測がつくと思うが、完全なHTMLではなく、飽くまで部分的な断片でしかないのだ.

上記のHTMLをブラウザできちんと表示するためには、別途、AngularJSのアプリを作り、ng-include や angular-routeモジュールのng-viewrouteProviderを組み合わせて読み込んでやる必要がある.

また、本家のAPIレファレンスのようなサイドバーメニューを表示したければ、自分でメニュー情報を作成するよう、Dgeniのprocessorから用意しなくてはならないのだ。

このエントリを書こうと思った当初は、一通りドキュメントアプリを生成出来るようになるまでに作成した:

  • Dgeniの設定
  • 追加で作成したDgeniのprocessor
  • Gulpのタスク定義
  • ドキュメントアプリのAngularJSコード

を、ここで解説していこうかと思ったのだけれど、ファイル数が30を越えており、とてもじゃないけど解説仕切れないので割愛.

興味がある人は 冒頭サンプルのGitHubレポジトリ を見てくださいな。

面倒なのでYeomanのgenerator化.

とまぁ、Dgeniを使ってみたものの、AngularっぽいAPIページを作るのには随分骨が折れる。正直、ゼロから自分のプロジェクトに組み込むのなんて、何回もやりたい作業ではない. このままでは、grunt-ngdocsやgulp-ngdocsを素直に使っときゃよかった、という状態だ.

そうだ! Yeomanのgeneratorにしちゃえばいいじゃん! ということで、Yeomanの勉強がてら、generator作ってみました。

使い方はとっても簡単(まぁgeneratorは全部使うの簡単だけどさ...). コマンドプロンプトで下記の通りに入力していけばよい。

npm yo generator-ngdoc
mkdir my-angular-module
cd my-angular-module
yo ngdoc
(質問は適当に答えてください)
cd docs
gulp docs:serve

問題なく動作していれば、ブラウザ上でドキュメントアプリが表示されている筈。

'API'→'sampleElem'と辿っていけば、example付きで冒頭に例示した sample-elemディレクティブのドキュメントが表示されます.

注意は、yo ngdoc での雛形生成が終わった後に、カレントディレクトリをdocsに変更しているところ.

generator-gulp-angularやgenerator-angularで既に作成したAngularJSのモジュールプロジェクトでも、後付けで組み込めるよう、generator-ngdocの生成物は、極力、docsディレクトリ配下に留めておきたかったのだ.

gulp tasks

以下は、generator-ngdocが生成するgulpタスクのリファレンス.
いずれも、(プロジェクトルート)/docs ディレクトリにカレントディレクトリを移動してから実行されたし.

cd docs

docs:serve

gulp docs:serve

ブラウザ上でドキュメントアプリを動作させる(browserSync).

src/**/*.jsをwatchしており、ソースコードに修正が加わった場合は、Dgeniを再実行した上でブラウザがライブリロードされるため、その場で編集結果が確認可能.

docs:build

gulp docs:build

docs/dist配下にドキュメントアプリをパッケージングする. Webサーバに配布する際に用いることを想定.

docs:serve:dist

gulp docs:serve:dist

docs:buildタスクの結果をWebサーバ(browserSync)で確認するためのタスク.

docs:clean

gulp docs:clean

お掃除タスク. docs/.tmpdocs/distを削除.

ngdoc記法の例

さて、ようやっとngdocコメントからAPIページを簡単に吐き出すための仕組みが整った.

...ところまではよいものの、ngdoc記法のリファレンスが以外に見当たらない.
本家のWikiに情報が載っているものの、outdatedって書いてあるため、鵜呑みにしてよいものやら...

結局、AngularJS本体のコメントを漁っていくのが一番確実っぽい.
(どなたか、ちゃんとしたngdocリファレンスの在り処を知っていたら教えてください)

網羅性は一切保証できないが、ngdocの記載方法について、備忘を残しておく.

@ngdoc

@ngdocは、一つのコメントブロックに付与する.
続けて、「何を表すか」を記載する(module/directive/filter/service/provider/type/event/function/method/property/overview)

例えば、myFilterという名前で、filterを作成したら、下記のようになる:

/**
 *
 * @ngdoc filter
 * @module testModule01
 * @name myFilter
 * @description
 * A filter sample.
 *
 **/

なお、@ngdoc controllerは存在しないので注意.
一般的に、モジュールを外部公開する際に、Controllerを利用させる、というシーンがあまり無いからだろうか.
「Directiveに紐付くController」(例: ng-modelngModelController)のケースがあるが、これは@ngdoc type で対応できる.
@ngdoc typeは、「メソッド, プロパティを持つオブジェクトの仕様」を記載できるため.

@module

このコンポーネントが属するmodlue名を記載する.

@name

DirectiveやFilter, Serviceの場合は, コンポーネントの名前自身を記載すればよい.
@ngdoc method の場合等、「誰の持ち物か」の情報が必要である場合は、下記のように # で結合する.

/**
 * :
 * @name 持ち主#関数名
 * :
**/ 

@description

説明文. Markdownが使える.

@param, @return

関数のパラメータ、戻り値の説明に用いる.

  • {...}に型名を記載(独自定義でも可.
  • 複数の型が利用可能な場合、| で区切る.
  • 省略可能である場合, {...=} とする.
/**
 *
 * @ngdoc method
 * @module testModule01
 * @name myService.myType#foo
 * @param {Object|String|Boolean|Date|Function|Number|CustomType} param A parameter.
 * @param {Array.<String|Object>} list A list.
 * @param {Object=} opt Optional parameter.
 * @return {String} Result
 * @description
 * A method sample.
 *
 **/

{@link }

ngdocの文中で、他のドキュメントへのリンクを作成してくれる.

/**
 *
 * @ngdoc provider
 * @module testModule01
 * @name myServiceProvider
 * @property {String} prop A property of this provider.
 * @description
 * A provider of {@link myService}
 *
 **/

@ngdoc module

モジュール全体の定義に使う.

  • @packageName Bowerでinstallさせる際のパッケージ名.
/**
 *
 * @ngdoc module
 * @name testModule01
 * @module testModule01
 * @packageName test-module01
 * @description
 * This is a sample module.
 *
 **/

@ngdoc type

上述で少し触れたが、Method, propertyを所有したオブジェクトのAPIを記載したい時に便利.
あるServiceを実行すると、オブジェクトが返ってくる時とか.

  • @property で、このオブジェクトがもつPropertyの説明を記載する.
/**
 *
 * @ngdoc type
 * @module testModule01
 * @name myService.myType
 * @property {String} myProp
 * @description
 * A child type.
 *
 **/

@ngdoc directive

Directive専用で使えるアノテーションが幾つかある.

  • @restrict EとかACとか.
  • @scope スコープを作成する場合、true とする.
  • @priority 動作順序のアレ. 多分、あまり使わないけど...
  • @param Directiveで@paramを使った場合, HTMLテンプレートで利用した場合のattributeの説明となる.
/**
 *
 * @ngdoc directive
 * @module testModule01
 * @name myDirective
 * @restrict AC
 * @scope true
 * @priority 100
 * @param {String} myDirective
 * @param {String=} foofoo
 * @description
 * A directive.
 *
 **/

@ngdoc event

Service等が、的等な条件下で Scope.$emit$rootScope.$broadcast を発火させる場合に記載するやつ.

  • @eventType に 種別(emit or broadcast) on 対象scope を記載する.
/**
 *
 * @ngdoc event
 * @module testModule01
 * @name myService#myEvent
 * @eventType broadcast on rootScope
 * @param {Object} angularEvent Synthetic event object.
 * @param {String=} param Optional parameter.
 * @description
 * A event.
 *
 **/

@example

部品の動作サンプルを生成する際に利用する.

ここで記載したアノテーション情報から、APIページから<iframe>で取り込まれるHTMLが生成される.

  • <example> タグ1つに対して、<iframe>で取り込まれるページが一つ生成される.
  • module属性には、サンプルアプリとしてのModule名を記載する. ng-app="..."の中身となる.
  • <file>タグでサンプルで動作させたいファイル(html, JavaScript, CSS)の中身を記載していく.

エントリ冒頭のサンプルと同一だが、@example の例は下記:

/**
 *
 * @ngdoc directive
 * @module testModule01
 * @name sampleElem
 * @restrict E
 * @description
 * This is a sample directive.
 *
 * @example
    <example module="sampleElemExample" deps="" animate="false">
      <file name="index.html">
        <sample-elem></sample-elem>
      </file>
      <file name="main.js">
        angular.module('sampleElemExample', ['testModule01']);
      </file>
    </example>
 *
 **/

作成したドキュメントアプリをTravis CIで公開

ここまで来たら、「ソースコードをgithubへcommit & push→ドキュメントサイトをgithub.ioへdeploy」というCIのフローを作り込んでみよう.

実際に作ってみたtravis.ymlを晒してみる.

.travis.yml
env:
  global:
  - GIT_COMMITTER_NAME=y-kurami
  - GIT_COMMITTER_EMAIL=yosuke.kurami@gmail.com
  - GIT_AUTHOR_NAME=Quramy
  - GIT_AUTHOR_EMAIL=yosuke.kurami@gmail.com
  - secure: MeJhx5HscQf8Ra9LMCafHqBNmO3AegY5ZLRAhhin9V6s1Q+clSNylB8E0TRqw5zkRvAOwynRxHtOLZOU6MJNwWNO3LIvYYS1o08K7YPWUQL7WCrl/DsXPCJ2xrEdV69YkG0aVmBrpvcnMBsXe78ggUAsB6ZaSL8eEos5ckYtzmU=
language: node_js
node_js:
- '0.10'
before_script:
- npm install -g bower
- npm install -g gulp
- bower install
- cd docs
- npm install
- bower install
- git clone --quiet -b gh-pages https://github.com/Quramy/example-of-generator-ngdoc.git
  dist
- cd ..
script:
- cd docs
- gulp docs:build
- cd ..
after_script:
- cd docs/dist
- git add -A
- git commit -m "UPDATE DOCS."
- '[ "$TRAVIS_BRANCH" == "master" ] && [ "$GH_TOKEN" ] && git push --quiet https://$GH_TOKEN@github.com/Quramy/example-of-generator-ngdoc.git
  gh-pages'
notifications:
  emails:
  - yosuke.kurami@gmail.com

上記の.travis.ymlは、ここを参考に作成している.
リンク先に詳細が記載されているが, github.ioへpushするために、下記を実行する必要がある.

  1. gh-pagesブランチをgit branch --orphan gh-pages で作成
  2. Githubのセキュアトークンを取得
  3. gem install travisでtravisコマンドをインストール
  4. レポジトリルートで下記コマンドを実行し、.travis.ymlのsecure:〜の部分を生成する.
travis encrypt -r (Github ID)/(レポジトリ名) "GH_TOKEN=(2.で取得しているトークン)" --add

まとめ

  • みんなngdocでAngularJSモジュールのドキュメント書こうぜ!
  • Dgeni + dgeni-packageだけだと、完品のAPIページは作れないよ!
  • generator-ngdoc使ってね!
  • こまったら、最終的にはAngularJS本家のコード読むといいよ!
24
24
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
24
24