JavaScript
TypeScript
gulp
webpack

TypeScriptでasync/await (webpackビルド)

More than 1 year has passed since last update.

やりたいこと

  • TypeScriptでasync/awaitを使う
  • ライブラリの型定義も取得したい
  • webpackでビルド
  • ついでにgulpタスクとして動かしたい

TypeScript の async/await をいますぐ使う - Qiita でやってる方法のwebpack版。

方針

  • awesome-typescript-loaderで一気にできそうだけどやり方がよくわからなかった
  • Browserify+tsify+babelifyでもできそうだけどやり方がry)

結局、ts-loaderbabel-loaderを使った。ES6を経由しつつBabelでES5にする。

ts -> (ts-loader) -> es6 -> (babel-loader) -> es5

必要環境インストール

npmでごっそり取る。

$ npm init
$ npm i -D gulp typescript typings webpack-stream ts-loader babel-loader
$ npm i -D babel-core babel-preset-es2015 babel-preset-stage-0
$ npm i -S babel-polyfill

typingsで型定義取得

tsdが非推奨になったことに注意。typingsを入れる。

$ npm i -g typings
$ typings init
$ typings i lodash -S -A
$ npm i -S lodash

型定義だけ取得するので--save (-S)--ambient (-A)を指定しとけばOK。
そのうちtypingsでライブラリ本体も取れるようになるのかな。
関係ないけどlodashは型定義だけで17000行以上あってビビる。

エントリファイル作成

起点となるスクリプトとしてとりあえずsrc/entry.tsとして書く。
今回はCommonJSライブラリを作成したかったので、exportで外部に出すライブラリを指定している。

src/entry.ts
import {Foo} from './Foo';
import {Bar} from './Bar';
export = {Foo:Foo, Bar:Bar};

ちなみに三行目をexport = {Foo, Bar};にしたらライブラリ利用時にReferenceErrorになる。なんでだ。

TypeScriptコンパイル設定

tsconfig.jsonを書く。

tsconfig.json
{
  "compilerOptions": {
    "module": "commonjs",
    "target": "es6"
  },
  "files": [
    "typings/browser.d.ts",
    "src/entry.ts",
    "src/Foo.ts",
    "src/Bar.ts"
  ]
}

BabelでES5にするので、TypeScriptのtargetはes6でいい。
typings/(browser|main).d.tsもfilesに追加。

webpackの設定

webpack.config.jsを書く。

追記: ライブラリを作る場合はexternalsを追加しておこう。

webpack.config.js
module.exports = {
  entry: ['babel-polyfill', './src/entry.ts'], // gulp.srcで指定する場合不要
  resolve: {
    extensions: ['', '.ts', '.js']
  },
  output: {
    path: __dirname + '/lib',
    filename: 'bundle.js',
    libraryTarget: 'commonjs' // CommonJSとして出力
  },
  module: {
    loaders: [
      {
        test: /\.ts(x?)$/,
        loader: 'babel-loader?presets[]=es2015,presets[]=stage-0!ts-loader'
      }
    ]
  },
  externals: {
    "babel-polyfill": "babel-polyfill",
    "lodash": "lodash"
  }
};

babelでasync/awaitを使うにはbabel-polyfillの同梱とstage-2以上のpresets指定が必要。
loaderは"!"で繋げる必要がある。この場合query使えないので、presetsはloader記述に埋める。
loaderを単に並べたりpreLoader使うのではbabel-loaderがうまく走らなかった。
ここでだいぶハマった...

追記: さらに結構すっきりさせられる事がわかった。

  • loaderの代わりにloadersプロパティを使えば"!"の代わりに配列で指定できる
  • .babelrcを書いておくとbabelが設定を読みにいってくれる
  • babel-loaderの代わりにbabelと書ける

これらを使えば、

webpack.config.js(一部)
  // ...
    loaders: [
      {
        test: /\.ts(x?)$/,
        loaders: ['babel', 'ts']
      }
    ]
  // ...
.babelrc
{
  "presets": ["es2015", "stage-0"]
}

こんなにすっきり!

webpackでビルド

この時点でwebpackを直接叩いてビルドできる。簡単にやるならこれで。

$ npm i -g webpack
$ webpack

gulpでビルド

gulpタスクから叩きたいとき。

gulpfile.js
var gulp = require('gulp');
var webpack = require('webpack-stream');

gulp.task('webpack', () => {
  return gulp.src(['babel-polyfill', './src/entry.ts'])
    .pipe(webpack(require('./webpack.config.js')))
    .pipe(gulp.dest('lib'));
});
ビルド
$ gulp webpack

webpack-streamでgulp.src指定するときはwebpack.config.jsのentryは消しておくこと。
残しておいたらファイルサイズでかくなったので、多分どっかでダブってる。

そのまんまwebpack使うなら普通に呼べばOK。

gulpfile.js
var gulp = require('gulp');
var gutil = require('gulp-util');
var webpack = require('webpack');

gulp.task('webpack', (callback) => {
  webpack(require('./webpack.config.js'), (err, stats) => {
    if(err) throw new gutil.PluginError('webpack', err);
    callback();
  });
});
ビルド
$ npm i -D webpack
$ gulp webpack

以上。いやー疲れた。
いろんな設定をいろんなところに書けるから、何がいい方法かさっぱりわからんね。
もっといい方法あったら教えて下さい。