Claudia.js Lambda開発でbabel環境を整える

## はじめに

[Claudia.js](https://claudiajs.com/)を利用してAPI(AWS Lambda & API Gateway)の開発を行う際にBabel - ES2015, 2016, 2017 - が利用できるように環境を整理したいと思います。

Claudia.jsはNode.jsに特化したLambdaのデプロイツールです。 概要については以下を参照ください。
[Node.jsでLambdaを開発するならClaudia.jsがオススメ](http://qiita.com/gaishimo/items/5ae6b35ebd4ae9434994)

今回の完成品はこちら。
https://github.com/gaishimo/claudiajs-template


## Claudia.jsの設定

まずClaudia.jsのCLIツールとAPI Builderをインストールします。

```bash
yarn init
yarn add --dev claudia
yarn add claudia-api-builder
```


```bash
# バージョン確認
$ $(yarn bin)/claudia --version
2.9.0
```

## Babel設定

次にBabelの設定をします。ES2015, ES2016, ES2017が利用できるようにします。
※ES2017はAsync Functionsが利用できます

以下babel関連のライブラリをインストールし、


```bash
yarn add --dev \
  babel-core \
  babel-register \
  babel-loader \
  babel-preset-es2015 \
  babel-preset-es2016 \
  babel-preset-es2017
```

.babelrcを作成します。

```bash
cat <<-EOS > .babelrc
{
  "presets": [
    "es2015",
    "es2016",
    "es2017"
  ]
}
EOS
```

babelが有効かどうか、適当なjsを書いてimport構文やasync等が使えるかテストしてみます。


```bash
cat <<-EOS > tmp.js
import path from 'path'
const func1 = async() => 'test'
EOS
babel-node tmp.js
```
エラーがでなければOKです。

テストに使ったファイルは不要なので削除しておきます。

```bash
rm -f tmp.js
```

## コード作成

Claudia.jsでbabelを利用するサンプルが存在するため、こちらを参考にします。
https://github.com/claudiajs/example-projects/tree/master/babel

まずAPIのコードを作成します。

```src/api.js
import APIBuilder from 'claudia-api-builder'

const api = new APIBuilder()

api.get('/', () => 'hello claudia.js with babel')

module.exports = api

```

APIBuilderオブジェクトはmodule.exportsする必要があります。


そしてpackage.jsonのスクリプトに以下を設定します。

```json
"scripts": {
  "transpile": "babel src/ --out-dir bin/",
  "create:api": "yarn run transpile && claudia create --region ap-northeast-1 --api-module bin/api",
  "update:api": "yarn run transpile && claudia update"
},
```

transpileを実行するとBabelでsrcのコードを変換し、binディレクトリに出力するようになっています。createとupdateでは、transpile後にAPIの作成、更新を行います。

試しに実行してみます。

```bash
$ yarn run transpile
yarn run v0.19.1
babel src/ --out-dir bin/
src/api.js -> bin/api.js
✨  Done in 1.23s.
```

```bash
$ yarn run create:api
yarn run v0.19.1
$ npm run transpile && claudia create --region us-east-1 --api-module bin/api

> claudiajs-babel@1.0.0 transpile /Users/gaishimo/src/sandbox/claudiajs-babel
> babel src/ --out-dir bin/

src/api.js -> bin/api.js
saving configuration
{
  "lambda": {
    "role": "claudiajs-babel-executor",
    "name": "claudiajs-babel",
    "region": "us-east-1"
  },
  "api": {
    "id": "zwakgyo72b",
    "module": "bin/api",
    "url": "https://xxxxxxxxxxx.execute-api.us-east-1.amazonaws.com/latest"
  }
}
✨  Done in 33.26s.
```

作成が完了すると、`claudia.json`が作成されますが、これはLambdaやAPI GatewayのID等を格納したファイルになります。

試しにAPIに対してHTTPリクエストを送るとレスポンスが返ってきます。

```bash
$ curl https://xxxxxxxxxxx.execute-api.us-east-1.amazonaws.com/latest
"hello claudia.js with babel"%
```


## ESLint設定

ESLintの設定もしておきます。設定はairbnbの設定を継承します。

```
yarn add --dev \
  eslint \
  eslint-config-airbnb-base \
  eslint-plugin-import
```


```.eslintrc.yaml
env:
  es6: true
  node: true
extends: airbnb-base
parser: babel-eslint
rules:
  semi:
    - error
    - never
```

※Ruleは好みで指定してください


package.jsonのscriptsに以下を追加し、

```json
  "scripts": {
    ...
    "lint": "eslint -c .eslintrc.yml ./src",
    "lint:fix": "eslint -c .eslintrc.yml --fix ./src"
  },
```

試しにlint実行してみます。

```bash
$ yarn run lint
yarn run v0.19.1
$ eslint -c .eslintrc.yml ./src
✨  Done in 0.95s.
$ yarn run lint:fix
yarn run v0.19.1
$ eslint -c .eslintrc.yml --fix ./src
✨  Done in 0.99s.
```


## テストの設定

更にテストを実行できるようにしてみます。[Mocha](https://mochajs.org/) + [power-assert](https://github.com/power-assert-js/power-assert) を利用します。

まず必要なライブラリをインストール。

```bash
yarn add --dev \
  mocha \
  power-assert \
  babel-preset-power-assert
```

Babelの設定ファイルでpresetsに`babel-preset-power-assert`を追加します。

```.babelrc
{
  "presets": [
    ...,
    "babel-preset-power-assert"
  ]
}
```

それとMochaのoptionファイルを用意しておきます。

```test/mocha.opts
--recursive
--compilers js:babel-register
```

※この前DevOpsを意識したせいか、何故かファイル名を`mocha.ops`にしてしまい、設定が効かない..とかなったことがありました。`opts`なので注意しましょう。


また、ESLintでmochaのglobalな変数等がエラーにならないように以下を設定します。

```.eslintrc.yml
env:
  ...
  mocha: true
```

テストしやすいように、APIのhandlerを以下のようにhandlers.jsに分離しておきます。

```src/api.js
import APIBuilder from 'claudia-api-builder'
import echo from './handlers'

const api = new APIBuilder()

api.get('/', echo)

module.exports = api

```

```src/handlers.js
export default () => 'hello claudia.js with babel'

```

テストコードは以下になります。

```test/handlers.js
import assert from 'power-assert'
import echo from '../src/handlers'

describe('echo', () => {
  it('returns text', () => {
    assert(echo() === 'hello claudia.js with babel')
  })
})

```

package.jsonのscriptsにtestを追加しておき、

```json
  "scripts": {
    ...
    "test": "mocha"
  },
```

テスト実行してみます。

```bash
$ yarn test
yarn test v0.19.1
$ mocha


  echo
    ✓ returns text


  1 passing (93ms)

✨  Done in 1.15s.
```

試しにテストが失敗するようにコードを変更してみると...

```src/handlers.js
export default () => 'hello claudia.js with babel xxx'

```

```bash
$ yarn test
yarn test v0.19.1
$ mocha


  echo
    1) returns text


  0 passing (105ms)
  1 failing

  1) echo returns text:

        # test/handlers.js:7

  assert(echo() === 'hello claudia.js with babel')
         |      |
         |      false
         "hello claudia.js with babel xxx"

  --- [string] 'hello claudia.js with babel'
  +++ [string] echo()
  @@ -20,8 +20,12 @@
   th babel
  + xxx


      + expected - actual

      -false
      +true

  AssertionError: false == true
      at Decorator._callFunc (node_modules/empower-core/lib/decorator.js:110:20)
      at Decorator.concreteAssert (node_modules/empower-core/lib/decorator.js:103:17)
      at decoratedAssert (node_modules/empower-core/lib/decorate.js:49:30)
      at powerAssert (node_modules/empower-core/index.js:63:32)
      at Context.<anonymous> (test/handlers.js:7:5)



error Command failed with exit code 1.
```

エラーと同時にpower-assert固有のassertionメッセージが表示されるのが確認できます。


ついでにテストが成功しないとデプロイできないように、デプロイ前にテスト実行を差し込んでおきます。

```package.json
  "scripts": {
    ...
    "create": "yarn run transpile && yarn test && claudia create --region ap-northeast-1 --api-module bin/api",
    "update": "yarn run transpile && yarn test && claudia update",
    ...
  },
```


## 後片付け

Lambda & API Gateway (IAM Roleも)を削除するには`claudia destroy`を実行します。

```bash
$(yarn bin)/claudia destroy
```

※ちょっとさみしいですが、destroy時は特に出力はでません


## 注意点

gitで管理する場合、コンパイル後のJSを出力するフォルダは.gitignoreに以下のように指定することになると思いますが、


```.gitignore
bin
```

.gitignoreに指定されている場合、Claudia.jsにデプロイ対象とみなされなくなってしまうため、package.jsonの`files`に該当ディレクトリを指定しておく必要があります。


```package.json
  "files": [
    "bin"
  ],
```

## 最後に

これでClaudia.jsでのLambda開発(API)する環境が整いました!