TypeScript+Gulp+jQueryで開発環境を構築

# やること
TypeScript, Gulp, jQueryを組み合わせて、簡単なWebアプリケーションを作ってみます。

前に、[TypeScript+Gulp+WebPack+jQueryで開発環境を構築](http://qiita.com/d-dai/items/af40f61a0d559081e2b5)という記事を書きましたが、今回はWebPackを使わずにやってみました。
それによってTypeScriptの書き方が少し変わっています。

TypeScriptは型が書けたりクラスが書けたりするAltJS（コンパイルしてjsにして使用する物）です。
TypeScriptはコンパイルしてjsにすることで、ブラウザで使用できるようになります。

入門書としては以下がオススメです。
[TypeScriptリファレンス Ver.1.0対応](https://www.amazon.co.jp/dp/B00K1T8Z4S)

少し古い内容になっているので、以下も合わせてご覧ください。
[TypeScript早わかりチートシート【1.5.3対応】](http://www.buildinsider.net/language/quicktypescript/01)


今回使う他のツールです。

- Gulp: タスクランナーです。Gruntよりも最近は人気があります。
- jQuery: 説明不要かと思いますが、楽にjsが書けるライブラリです。

# 開発環境

- Windows 7
- TypeScript 1.8
- コマンドプロンプト（以下コマンドはコマンドプロンプトで入力します）

# Node.jsをインストール
TypeScriptはnpm(Node Package manager)でインストールが出来ます。
まずは[Node.js](https://nodejs.org/en/)をインストールします。

これでnpmが使えるようになります。

# プロジェクトの作成

まずは任意のディレクトリを作成し、その中で以下コマンドでプロジェクトを作成します。
-yオプションをつけないと、各種項目について都度聞かれます。
-yを付けずにそのままEnterを連打してもかまいません。

```
npm init -y
```
実行すると`package.json`が出来上がります。
後述しますが、このファイルに記載されたライブラリ全てを`npm install`で
インストールできるようになりますので、他の人に配布時に環境構築が簡単にできます。


# ディレクトリ構成
今回は以下のようなディレクトリ構成を実現します。
TypeScriptファイルはtsディレクトリに置き、コンパイルしたjsはdist/jsに置きます。

```
dist
└── js
    └── app.js
node_modules
└── gulp-webpack
├── jquery
└── typings など
ts
├── app.ts
├── model.ts
└── config.ts
typings
├── globals
└── index.d.ts
gulpfile.js
index.html
package.json
tsconfig.json
typings.json
```

# 各種モジュールのインストール

npmで必要な各種モジュールのインストールをします。

以下はグローバルにインストールします。
-gがグローバルにインストールするオプションです。


```
npm i -g typescript typings gulp 
```

以下はローカルにインストールします。

```
npm i -D gulp gulp-typescript gulp-sourcemaps gulp-rename gulp-uglify del jquery
```

これでnode_modulesディレクトリが出来ます。
各モジュールの説明です。

- typescript: TypeScriptです。
- typings: TypeScriptの型定義ファイルを管理します。
- gulp: Gulpです。
- gulp-typescript: GulpにおいてTypeScriptを使用するためのものです。
- gulp-sourcemaps: Gulpにおいてソースマップを作成するために使用するものです。
- gulp-rename: ファイルをリネームするためのものです。
- gulp-uglify: jsを圧縮するためのものです。
- del: ファイルの削除を行うためのものです。
- jquery: jQueryです。

--save-devを付けるとpackage.jsonの`devDependencies`に記載されます。
`npm install`を実行すると、devDependenciesに書かれたライブラリがnode_modulesにインストールされます。
そのためgit管理からはnode_modulesディレクトリは外して問題ありません。

`i -D`は`install --save-dev`の省略形ですので、このように書くことも出来ます。

--saveを付けると`dependencies`に記載されます。
`--save-dev`と`--save`の違いですが、はっきりと理解できていないのですが、
プロジェクトを`git clone`して`npm install`して使う場合はどちらでも同じように動くようです。
詳しく知りたい場合は以下をご参照ください。

[msakamoto-sfのはてなブログ](http://hblog.glamenv-septzen.info/entry/2015/03/22/233241)

なお、インストールされたモジュールは`npm ls`で確認が可能です。
ただ、依存モジュールも全て表示されてしまいますので、
`npm ls --depth=0`と実行すると依存モジュールが除外され見やすいと思います。

不要なモジュールは`npm remove モジュール名`でアンインストールが出来ます。

# jQueryの型定義ファイルのインストール

TypeScriptでjQueryを使う場合、型定義ファイルというものを使います。
先ほどインストールしたtypingsを使って、jQueryの型定義ファイルをインストールします。
```
typings install dt~jquery --save --global
```

少し古い記事だと`--ambient`を使うよう書いているものもありますが、今は--globalを使います。

これで`typingsディレクトリ`と`typings.json`が出来ます。
使用する際は、typingsディレクトリの`index.d.ts`を使います。

# tsconfig.jsonの作成

以下のコマンドで、TypeScriptのコンパイル情報を記載する、tsconfig.jsonを作成します。

```
tsc --init
```

以下のように作成されます。

```
{
    "compilerOptions": {
        "module": "commonjs",
        "target": "es5",
        "noImplicitAny": false,
        "sourceMap": false
    },
    "exclude": [
        "node_modules"
    ]
}
```

`outFile`と`files`を追加し、以下のようにします。

```
{
    "compilerOptions": {
        "module": "commonjs",
        "target": "es5",
        "outFile": "app.js",
        "noImplicitAny": false,
        "sourceMap": false
    },
    "files": [
        "ts/app.ts",
        "ts/model.ts",
        "ts/config.ts",
        "typings/index.d.ts"
    ]
}
```

なお、`tsc`でコンパイルすると、以下のようなエラーが出ます。
moduleをamdにすれば解決しますが、以下エラーが出てもファイル出力自体はされているようですので、このまま進めます。

```
error TS6082: Only 'amd' and 'system' modules are supported alongside --outFile.
```

filesはデフォルト通りexcludeで指定する方法もありますが、
今回は`files`にコンパイルするファイルを指定してみました。
なお、これらは組み合わせは出来ません。
atom-typescriptを使うと、`filesGlob`を使ってパターンマッチで書くことが出来るようです。

[filesGlobについて](https://html5experts.jp/vvakame/17004/)



各設定項目の説明です。

- module: 外部モジュールのコンパイル方法を指定します。commonjs,amdなどを指定できます。
- target: jsのバージョンを指定します。
- outFile: jsを1つにまとめて出力するときに使用します。
- noImplicitAny: trueにすると暗黙的なanyの使用を禁止します。
- sourceMap: ソースマップファイルを作成します。

tsconfigの詳細はこちらをご覧ください。
[TypeScript公式](https://www.typescriptlang.org/docs/handbook/tsconfig-json.html)


# tsファイルの作成
tsディレクトリを作り、その中にtsファイルを作っていきます。
config.ts, model.ts, app.tsの3つのファイルを作っていきます。


## config.ts
Configというネームスペースを使用します。
ENVという定数をexportして、ネームスペースの外から参照できるようにします。

```
'use strict';
namespace Config {
    export const ENV = "dev";

}
```


## model.ts
`reference path`を書くことで、config.ts`を取り込み、Configというネームスペースが参照できます。

Modelというネームスペースを使用し、Personクラスをexportしてネームスペースの外から参照できるようにします。

```
/// <reference path="config.ts"/>

'use strict';

namespace Model {
    export class Person {
        constructor(private name:string = 'dummy') {
        }

        public getName():string {
            if (Config.ENV != 'production') {
                return this.name
            } else {
                return this.name + 'さん';
            }
        }
    }
}
export default Model;
```

## app.ts

このファイルにメインの挙動を記載します。
こちらもreference pathを記載します。

```
/// <reference path="model.ts"/>
/// <reference path="../typings/globals/jquery/index.d.ts"/>

'use strict';

var user = new Model.Person("Mike");
$(() => {
    $("#name").html(user.getName());

});
```

# gulpfile.jsの作成
プロジェクトルートに、Gulpの実行ファイルである、gulpfile.jsを作ります。

```
var gulp = require('gulp');
var ts = require('gulp-typescript');
var sourceMaps = require('gulp-sourcemaps');
var tsProject = ts.createProject('tsconfig.json');
var uglify = require('gulp-uglify');
var rename = require('gulp-rename');
var del = require('del');

var TS_SRC = './ts/**/*.ts';
var JS_DEST = './dist/js/';

var env = process.env.NODE_ENV;

gulp.task('clean', function() {
    del([JS_DEST]);
});

gulp.task('ts-compile', function() {
    if (env === 'production') {
        return tsProject.src()
            .pipe(ts(tsProject))
            .pipe(uglify())
            .pipe(rename({
                extname: '.min.js'
            }))
            .pipe(gulp.dest(JS_DEST));
    } else {
        return tsProject.src()
            .pipe(sourceMaps.init())
            .pipe(ts(tsProject))
            .js.pipe(sourceMaps.write('./'))
            .pipe(gulp.dest(JS_DEST));
    }
});

gulp.task('cp-lib', function() {
    gulp.src('node_modules/jquery/dist/jquery.min.js')
        .pipe(gulp.dest(JS_DEST))
});

gulp.task("watch", function(){
    var watcher = gulp.watch(TS_SRC, ['ts-compile']);
    watcher.on('change', function(event) {
        console.log('File ' + event.path + ' was ' + event.type + ', running tasks...');
    });
});

gulp.task('default', ['ts-compile', 'cp-lib']);
```

gulpは実行時に引数を与えることが可能で、上記taskの第一引数に相当します。
`gulp webpack`と実行した場合は、上記の`task('webpack'～`が実行されます。
`gulp`と実行した場合は、defaultと見なされます。

今回は以下のようなタスクを定義しました。

- clean: delを使って、jsファイルを削除します。引数は配列形式で複数のディレクトリの指定が可能です。
- ts-compile: tsファイルをコンパイルします。
- cp-lib: jQueryをdistにコピーします。
- watch: tsファイルを監視し、変更されたら、webpackコマンドを実行します。


NODE_ENVの値がproductionであれば、`gulp-uglify`を使ってjsを圧縮し、
`gulp-rename`を使って、ファイル名をapp.min.jsにします。

それ以外なら、ソースマップを出力します。
Windowsの場合、コマンドプロンプトで `set NODE_ENV=production` と打つことで設定出来ます。
`tsconfig.json`に`"sourceMap": false`と記載しましたが、こちらの方が優先されるようです。

ここでは設定せず進めます。一番最後まで終わった後に試してみます。


# index.htmlの作成

プロジェクトルートに、ブラウザでアクセスするファイルを作成します。

```
<!DOCTYPE html>
<html lang="ja">
<head>
    <meta charset="utf-8">
    <title>TypeScriptのテスト</title>
    <script src="dist/js/jquery.min.js"></script>
    <script src="dist/js/app.js"></script>
</head>
<body>
<div id="name"></div>
</body>
</html>
```

なお、NODE_ENV = productionを設定して実行した場合には、app.min.jsが作成されますので、
読み込みを変更します。

# 動作確認

## Gulpの動作確認

プロジェクトルートで、`gulp`を実行します。
distディレクトリが作成され、その中にjs/app.jsが作成されれば成功です。

## ブラウザで動作確認
Mikeと表示されれば成功です。


## NODE_ENV=productionで試してみる。
以下を実行します。
```
SET NODE_ENV=production
```

index.htmlのapp.jsの参照をapp.min.jsに変更します。

gulpを実行します。

index.htmlを表示し、同様に表示されればOKです。