TypeScript & CoffeeScript & Browserifyによるフロントエンド開発環境

**この記事の内容は古くなりました、新しい記事は[こちら](http://qiita.com/wordijp/items/c28b708330152e8b2a2a)です。**

[Browserify](http://browserify.org/)の記事が増えてきましたが、
自分でもフロントエンド開発環境を構築してみて、納得のいく構築が出来ましたので公開します。

[GitHubへのリンク](https://github.com/wordijp/altjs-browserify-base-project)

**2015/03/26追記**
独自タグによるalias指定をoptionalに変更し、
require時に相対パス指定か、aliasがある時はalias指定のどちらも使えるように修正しました。

# 概要

このプロジェクトは、以下の機能を持っています。

- TypeScript、Browserifyの差分ビルド
- gulp.watch、Watchifyの変更監視による自動ビルド
- TypeScript、CoffeeScriptのソースファイルを混在出来る
	- ファイルごとに型が欲しいか、短く書きたいか、で好きな方を選べばいい
	- CoffeeScriptで書いたクラスをTypeScriptでrequireして使う、等
	- ※当然ですが、型チェックをするならTypeScript同士じゃないとダメ

また、独自拡張として以下の対応をしています。

- require時にaliasを指定出来る
	- require('alias名')
	- **(追記)従来のrequire('相対パス')指定も出来る** 
- TypeScriptにて、ユーザ外部モジュール及び型定義ファイルの自動生成
	- import hoge = require(...);
	- [ソースへのrequire用のalias、ユーザ外部モジュール化について](#alias)
- 多段ソースマップの問題を解決し、Browserify生成のjsファイル実行中でもAltJSのソースにbreakpointを貼れる
	- [多段ソースマップの解決について](#multi_source_map)


また、gulpのbuildやwatch中にエラーが発生するとエラー通知がされるようにしています。

## Usage

1. npm install
2. tsd update -s
3. gulp (watch | build | clean) [--env production]

--env productionオプションを付けると公開用として、無しだと開発用としてbundleファイルを生成します。
生成の際、開発用(オプションなし)だとソースマップ生成を、公開用(オプションあり)だとbundleファイルの圧縮を行います。
		
## ファイル構成

```
root
├── src - ソース置き場
│   ├── index.html
|   └── scripts
├── gulpscripts - gulp用の自作スクリプト
│   ├── ambient-external-module.coffee - ソース内の専用タグ収集(alias、外部モジュール化の補助)
│   ├── error-log.coffee               - エラーログ
|   ├── grep-sync.coffee               - 同期版のgrep
|   ├── gulp-callback.coffee           - stream内の任意の場所でコールバックを発生させる
|   ├── merge-multi-sourcemap.coffee   - 多段ソースマップの合成
|   ├── notify-error.coffee            - エラーのデスクトップ通知
|   └── same-path.coffee               - 複数のパスから一致する部分の取得
├── gulpfile.coffee - gulpメインスクリプト
├── package.json    - nodeパッケージ
└── tsd.json        - TypeScript型定義ファイルパッケージ
```



## <a name="alias"></a> ソースへのrequire用のalias、ユーザ外部モジュール化について

ソース中に独自タグである

```ts:hoge.ts
// TypeScript
/// <ambient-external-module alias="{filename}" />
```

```coffee:foo.coffee
# CoffeeScript
###
<ambient-external-module alias="{filename}" />
###
```

を埋め込むことにより、
gulpscripts/ambient-external-module.coffee
がソース中のタグを収集し、Browserifyにソースを追加する際にrequireメソッドによりaliasが定義されます。

```coffee:gulpfile.coffee
b.require('lib/path/to/hoge.js', expose: 'hoge')
```

また、TypeScriptの場合は[dts-bundle](https://github.com/TypeStrong/dts-bundle)により外部モジュール化されsrc_typingsディレクトリ内にユーザ型定義ファイルが作成されるため、

```ts:foo.ts
/// <reference path="root/src_typings/tsd.d.ts" />

import Hoge = require('hoge');
Hoge.foo();
```

という書き方をする事が出来ます。

## <a name="multi_source_map"></a> 多段ソースマップの解決について

AltJSからBrowserifyによるbundleファイル生成までの流れは、以下のようになっています。

- 1.AltJSのトランスパイル
	- hoge.ts -> tsc -> hoge.js & hoge.js.map
	- foo.coffee -> coffee -c -> foo.js & foo.js.map
- 2.Browserifyによるbundle
	- (hoge.js & hoge.js.map) & (foo.js & foo.js.map) -> Browserify -> bundle.js & bundle.js.map
		
中間ファイルであるhoge.jsやfoo.jsのそれぞれのソースマップファイルはAltJSとの紐づけ、
生成物であるbundle.jsのソースマップファイルは中間ファイルとの紐づけがされた状態であり、
bundle.jsのソースマップファイルから、AltJSへと直接紐づける必要があります。

紐づけ方法ですが、[mozilla/source-map](https://github.com/mozilla/source-map/)によりソースマップ内の対応した位置情報をプロットしてみると、
中間ファイルのソースマップファイルであるhoge.js.mapやfoo.js.mapのgeneratedの位置情報と、
生成物のソースマップファイルであるbundle.js.mapのoriginalの位置情報が対になっていると読み取れます。

![multi_source_map_prot.png](https://qiita-image-store.s3.amazonaws.com/0/51728/b8b64e36-641f-0d93-31d9-15d60043a80d.png)

この対になっている位置情報を基に、AltJSのoriginalの位置情報と、生成物のbundle.jsのgeneratedの位置情報を取り出せれば、多段ソースマップの問題が解決出来る事になります。

この問題を解決するスクリプトgulpscripts/merge-multi-sourcemap.coffeeを作成し、Browserify実行後に走らせることでこの問題を解決しています。
- ※スクリプト内では、さらに細かく紐づけをしています。
- **※browserifyでuglifyによる圧縮後のソースマップに試しましたが、列の位置が微妙にずれる結果となってしまった為、uglifyと併用した場合は上手く動かない可能性があります。**

## TypeScriptの定義ファイルについて

定義ファイルは、DefinitelyTypedにより公開されているモジュール用とsrcディレクトリ用の2種類があり、srcディレクトリ用の定義ファイルはgulpで自動生成され、また、TypeScriptを編集した際にも自動更新されます。
また、DefinitelyTypedと同様にルート用の定義ファイル(tsd.d.ts)を用意している為、
定義ファイルのreferenceパスはDefinitelyTyped用、srcディレクトリ用それぞれのtsd.d.tsファイルを参照するだけで良いです。

- DefinitelyTyped用の定義ファイル
	- typings/tsd.d.ts
		
- srcディレクトリ用の定義ファイル
	- src_typings/tsd.d.ts

```ts:src/scripts/hoge.ts
/// <reference path="../../typings/tsd.d.ts" />
/// <reference path="../../src_typings/tsd.d.ts" />
```

## 参考

- [Source Mapを扱う関連ライブラリのまとめ](http://efcl.info/2014/0622/res3933/)
- https://github.com/azu/multi-stage-sourcemap
- [Watchifyでbrowserifyを差分ビルド](http://qiita.com/mizchi/items/b59a6c27558d238658c6)