モダンなAtomパッケージのテンプレート（2017年版）

この記事は[Atomアドベントカレンダー](https://qiita.com/advent-calendar/2017/atom)の25日目の記事です。

2017年のAtomアドベントカレンダーもいよいよラストになってしまいました。
トリということで、2017年におけるパッケージのテンプレート（自分調べ）を紹介いたします。

完成版のリポジトリはこちらです
https://github.com/Kesin11/MyAtomPackageTemplate/tree/v2

# ES2015
AtomはかつてCoffeeScriptで作られたことにより話題になったエディタです。当然、拡張機能であるパッケージもCoffeeScriptで書かれていました。
しかし現在ではCoffeeScriptはその役目を果たし終えました。現在ではjsでもパッケージを書くことが可能であり、Atom本体周辺も積極的に過去のCoffeeScriptベースのコードを書き直しています。
[リリースノート](https://atom.io/releases)を見ていただくとコアパッケージでもデカフェイン（カフェインを抜く=js化）やconvert to jsという文字を新しいバージョンが出るたびに見かけます。

自分のパッケージをjs化するにはjsでコードを書き、ファイルの先頭に'use babel'と付けるだけです。
これでclassやPromiseといったES2015の機能を使うことができ、async/awaitも使うことが可能です。

# GUI
AtomパッケージのGUIはHTMLとCSSで作ることができます。jsからDOMを操作することさらにリッチなGUIを作ることも可能であり、[etch](https://github.com/atom/etch)というライブラリを使うことでReactと同じようにJSXでDOMを構築することも可能です。

etchについての詳しい内容はこちらの記事を参照してください。
[AtomパッケージでGUIを作る（etchの紹介）](https://qiita.com/Kesin11/items/fc84a094df0bd6a94a0e)

# テスト
Atomパッケージは最初にGenerate Packageでひな形を作成したときからテストを実行できる環境が用意されています。
しかし最初から用意されているテストランナーは古いJasmineにAtomが独自に拡張を加えたものであり、現代では古い環境となってしまいました。

現在では[atom-mocha-test-runner](https://github.com/BinaryMuse/atom-mocha-test-runner)を使うことでこの古いテストランナーをmochaに差し替えることが可能です。
詳しい内容は今年のアドベントカレンダーに投稿した以下の記事を参照してください。

- [最近のライブラリはatom-mocha-test-runnerでJasmineの呪縛から逃れている](https://qiita.com/Kesin11/items/f45ef70bda95a4f20418)
- [atom-mocha-test-runnnerの使い方（実践編）](https://qiita.com/Kesin11/items/2dbdff4368d82a546185)

# Lint
Atomが提供しているわけではありませんが、自分でESLintをインストールすればlintをかけることも可能です。
自分の場合はこのような.eslintrcを使用しています。

```js
{
    "parserOptions": {
      "ecmaVersion": 2017,
      "sourceType": "module",
      "ecmaFeatures": {
        "jsx": true
      }
    },
    "plugins": [
      "mocha"
    ],
    "env": {
      "node": true,
      "es6": true,
      "mocha": true,
    },
    "extends": [
      # Select your base rules
      "eslint:recommended",
    ],
    "rules": {
      # Add your rule config
      semi: ["error", "never"],
    },
    "globals": {
        "atom": false,
    }
}

```

ベース部分はasync/await + import + JSXを有効にする設定で、テストコード用にpluginsでmochaを追加しています。
自分はrulesに最低限のチェックとしてeslint:recommendedを使用していますが、好みでairbnbなどメジャーなものに差し替えてもいいでしょう。
少し注意が必要なのは最後のglobalsです。パッケージを作成するにあたって当然`atom`を使う必要があるのですが、これはグローバル変数として定義されているのでglobalsに追加してlintを無視させる必要があります。

# CI
多くのAtomパッケージはCIサービスを使用してテストを実行させています。
CIに乗せる方法はそれほど難しくなく、[atom/ci](https://github.com/atom/ci)に著名なCIの設定ファイルが置かれているので、それをコピーして各CIでリポジトリの登録をするだけです。個人的にはTravisCIがオススメです。

オススメの理由は、[atom/ci](https://github.com/atom/ci)の.tarvis.ymlを使うだけでLinux/Mac × stable/betaの4パターンでテストを実行してくれるためです。
CIでのテストではAtomのバージョンは固定されておらず、その時点でのstable, betaバージョンを自動的にダウンロードしてその上でパッケージのテストを実行してくれます。そのためAtomの次のバージョンでAPIに変更が入ってパッケージが壊れてしまう場合には事前に検知することが可能というわけです。当然テストがちゃんと書かれている必要はありますが。

さらにTravisCIではコミットやプルリクのトリガー以外に、定期的にビルドを実行してくれる機能（Cron Jobs）があります。
これは前述のstable, betaをダウンロードしてテストする手法と相性が良く、自分でAtomの更新を逐一チェックしなくても自動的に最新バージョンでパッケージが壊れないかテストし続けてくれます。
Atomはだいたい1ヶ月に1回ペースでバージョンが上がっているので、自分が公開しているパッケージではmasterブランチをRuns weeklyでビルドが走るように設定しています。

↓こんな感じです
<img width="948" alt="travisci_cron_job.png" src="https://qiita-image-store.s3.amazonaws.com/0/10384/c70d288d-7081-3d36-239b-efe83ab0e443.png">


# クラス化
前述のES2015の項で紹介したようにclass構文が使用可能ですが、package.jsonのmainで指定しているパッケージのエントリポイントはオブジェクトがexportされている前提となっているため、パッケージ本体をクラスで書くことはできません。
という制約はあるものの、ようはAtom本体から自分のパッケージが読み込まれたときにクラスではなくてオブジェクトを返せればOKなのでこういう構成に変更してしまいます。

```package.json
{
  "name": "my-atom-package-template",
  "main": "./lib/main",
  // 関係ない部分は省略
}
```

```main.js
'use babel'

import MyAtomPackageTemplate from './my-atom-package-template'

export default new MyAtomPackageTemplate()
```

```my-atom-package-template.js
'use babel'

export default class MyAtomPackageTemplate {

  constructor () {
  }

  activate() {
  }

  deactivate() {
  }
}
```

エントリポイントとしてダミーのmain.jsを設定して、その中でインスタンスを作成してexportしてやります。
これでactivate()やdeactivate()を置くパッケージ本体もクラスとして書くことができます :tada: 

# まとめ
以上のテクニックを全て詰め込んだリポジトリがこちらです
https://github.com/Kesin11/MyAtomPackageTemplate/tree/v2

今現在パッケージをメンテしている方、これからパッケージを作る方の参考になれば幸いです。
それでは2018年のAtomの発展を期待しつつよいお年を！