TypeScript+nodemon+BrowserSyncをgulpでまともに動かすために

# お品書き

1. TypeScriptをサーバー側で差分コンパイル。
2. nodemonでNodeの自動再起動
3. BrowserSyncでフロントエンドも自動リロード

# 概要

* TypeScriptとnodemonが絡んだサンプルの作成。
* とりあえず使えるもの

# 実際の設定

## devDependencies

### package.json(一部抜粋)

```json
"devDependencies" {
    "browser-sync": "^2.13.0",
    "gulp": "^3.9.1",
    "gulp-nodemon": "^2.1.0",
    "gulp-typescript": "^2.13.6"
}
```

### インストール

```ps1
npm i -D browser-sync gulp gulp-nodemon gulp-typescript
```

## TypeScriptの差分コンパイル

参考→ [gulp-tscとgulp-typescriptの利用方法の違いについて](http://qiita.com/yuichiroharai/items/42e559e2c15e1e5ffa39)  

残念ながらgulp-tscは当の昔にdeprecatedされてしまったようです。なので今後はgulpに組み込むときはgulp-typescriptのみを使っていくようですが、こっちのほうがtsconfig.jsonから設定を取り込めたり、差分コンパイルできたり何かと便利です。  

### tsconfig.json

```json
{
    "compilerOptions": {
        "diagnostics": true,
        "emitDecoratorMetadata": true,
        "experimentalDecorators": true,
        "module": "commonjs",
        "moduleResolution": "node",
        "target": "es2015"
    },
    "exclude": [
        "node_modules",
        "bower_components",
        "jspm_packages"
    ]
}
```

`target`はとりあえずes2015で最新の機能をフル活用するようにしていますがここら辺は様々な事情で変えていく必要があるかもしれません。例えばどうしてもIE向けに作る必要があるときとか、古いNode向けとか。

### gulpfile.js(TypeScript部)

```js
const gulpts = require("gulp-typescript");
const tsProject = gulpts.createProject(
    "tsconfig.json", {
        sortOutput: true,
        typescript: require('typescript')
    });

gulp.task("serverCompile", () => {
    gulp.src(['server/ts/**/*.ts'])
        .pipe(gulpts(tsProject))
        .js
        .pipe(gulp.dest('server/js/'));
});

gulp.task("clientCompile", () => {
    gulp.src(['public/**/*.ts'])
        .pipe(gulpts(tsProject))
        .js
        .pipe(gulp.dest('public/'));
});
```

ここら辺は好みでしょうが自分は`public`と`server`の二つのディレクトリでサーバー処理部分とクライアント処理部分を分けさらにそれぞれの中に`ts`、`js`とディレクトリを分けています。

## BrowserSync

厄介者その1  

### gulpfile.js(BrowserSync部)

```js
const browserSync = require("browser-sync").create();
const reload = browserSync.reload;
gulp.task("browser-sync", ['nodemon'], () => {
    browserSync.init(null, {
        proxy:"localhost:3000",
        open:false,
        port:"7000",
        files: [
            "public/**/*.*"
        ],
        serveStatic:['public/**/*.*'],
        minify:true
    });
});
```

何が厄介ってときどき`port`が暴れだしたり`proxy`が暴れだしたりするところでしょう。具体的には番号が一つずれてたり。とりあえずそんな時はCtrl+Cで様子見。このBrowserSyncをnodemonで実際に動作させてNodeの再起動時にもブラウザを自動リロードできるようになります。

## nodemon

厄介者その2

### gulpfile.js(nodemon部)

```js
const gulpts = require("gulp-nodemon");
const path = require("path");
gulp.task('nodemon', (cb) => {
    let called = false;

    return nodemon({
        script: 'server/js/',
        watch:'server/',
        ext: 'js html css ts pug',
        tasks: (changedFiles) => {
            let tasks = [];
            changedFiles.forEach(file => {
                if (path.extname(file) === '.ts' && !~tasks.indexOf("serverCompile"))
                    tasks.push("serverCompile");
            });
            return tasks;
        }
    }).on('start', () => {
        if (!called) {
            called = true;
            cb();
        }
    }).on('restart', () => {
        setTimeout(() => {
            reload();
        }, 500);
    });
});
```

なんとなく`tasks`使わなくてもできそうなものなんだけどそれだとコンパイルに連動して動作しません。それに`watch`や`script`の指定ミスると動作が遅くなったり、こけたり。あと最初pathの存在に気づかなくて延々と悩んだり。いよいよ締めの`default`です。

## default

```js
gulp.task("default", ['clientCompile','browser-sync'], () => {
    console.log("gulp start");
    // client tasks
    gulp.watch('public/**/*.ts',['clientCompile']);
});
```

実はここもひと悶着ありました。`gulp.watch('public/**/*.ts',['clientCompile'])`　これをどこに持ってくるかで。nodmonのところに持って行ってみたり。browser-syncのところに持って行ってみたり。

# 感想

JavaScriptは神秘的。