Stimulus アプリケーションの起動時に何をしているのか？

# はじめに

私は、StimulusをRailsで使うとき、[stimulus-rails](https://github.com/hotwired/stimulus-rails) gemをインストールします。
そして、以下のコマンドを実行してStimulusを使う環境を整えます。
```sh
./bin/rails stimulus:install
```
このコマンドを実行すると、複数のファイルが作成されます。
このファイルがあるおかげで、Stimulusが気軽に使える状態になります。

作成されるファイルの中に、Stimulusアプリケーションを起動している以下のファイルがあります。
```js:app/javascript/controllers/application.js
import { Application } from "@hotwired/stimulus"

const application = Application.start()

// Configure Stimulus development experience
application.debug = false
window.Stimulus   = application

export { application }
```
`Application.start()`で、Stimulusアプリケーションを起動しています。
このおかげで、Stimulus controllerがいい感じに使えるようになってるのだろうと推測できます。
実際のところ、`Application.start()`は何をしているのでしょうか？

# `Application.start()`は何をしているのか？

## `static start` メソッド

`Application.start()`で呼び出されているのは、`static start`メソッドです。
`static start`メソッドは、`Application` classのインスタンス`application`を作り、`start`メソッドを実行しています。

```ts
static start(element?: Element, schema?: Schema): Application {
    const application = new this(element, schema)
    application.start()
    return application
}
```

## `constructor` メソッド

`Application` classの`constructor`は以下のように定義されています。

```ts
constructor(element: Element = document.documentElement, schema: Schema = defaultSchema) {
    this.element = element
    this.schema = schema
    this.dispatcher = new Dispatcher(this)
    this.router = new Router(this)
    this.actionDescriptorFilters = { ...defaultActionDescriptorFilters }
}
```

仮引数`element`に何も渡さなければ、`<html>`要素が`element`プロパティに代入されます。Stimulusアプリケーションを適用する範囲を指定したい場合は、仮引数`element`に何らかのDOMを渡せば良いということです。

仮引数`schema`に何も渡さなければ、`defaultSchema`が`schema`プロパティに代入されます。`defaultSchema`は、Stimulusで使われるキーワードが定義されているオブジェクトです。例えば、HTMLタグに`hello`というStimulus controllerを適用する場合、HTML要素の属性に`data-controller="hello"`をセットします。このときに使うキーワードが、`defaultSchema`で定義されています。

適用するStimulus controllerを指定するキーワードを変えるようなことはあまりないと思います。最も使いそうなユースケースは、action descriptorのカスタマイズです。action descriptorとは、下記`<div>`要素の`keydown.esc`の部分です。これは、「Escキーを押したとき」という意味です。デフォルトでは、「@キーを押したとき」という意味のaction descriptorはありません。これを追加したい場合は、`defaultSchema`をカスタマイズして、`constructor` メソッドの仮引数`schema`に渡します。
```html
<div data-controller="modal"
     data-action="keydown.esc->modal#close" tabindex="0">
</div>
```

`constructor` メソッドでは、以下のプロパティの初期化もしています。

- `router`
- `dispatcher`
- `actionDescriptorFilters`

`router`、`dispatcher`がどういうものなのかは、まだ読み解けてないのですが、大雑把に以下のような役割があるようです。
- `router`: DOMの監視
- `dispatcher`: DOMイベントとstimulus controllerのメソッドの紐付け

`actionDescriptorFilters`では、[DOM event listener options](https://developer.mozilla.org/en-US/docs/Web/API/EventTarget/addEventListener#options)にないStimulus独自のoptionを定義しています。


## `async start`メソッド

`async start`メソッドは、`static start`メソッドで実行されています。`domReady`関数を使って、DOMが読み込まれるまで待ちます。その後、`dispather`と`router`を起動しています。

```ts
async start() {
    await domReady()
    this.logDebugActivity("application", "starting")
    this.dispatcher.start()
    this.router.start()
    this.logDebugActivity("application", "start")
}
```

# おわりに


Stimulus アプリケーションの起動時に、以下のことをしていることがわかりました。
- Stimulusで使われるキーワードを定義
- `Router`を起動
- `Dispatcher`を起動

`Router`、`Dispatcher`については、それぞれどのような目的を持つものなのかはっきりわかりませんでした。
なので、今後は、これらがどのように動いているのか調べてみたいと思います。