Lightning Web Components を TypeScriptで記述する

## 目標

- TypeScriptによる型検査を伴った開発体制をLightning Web Components(LWC)の開発に対しても導入できるようにする
- npmなどのライブラリについても通常のWeb開発と同様に利用できるようにする

## 指針

- LWCとしてプッシュするメタデータに含まれるのはJavaScriptコードである必要があるが、ソースコードをTypeScriptで記述するため、プッシュ前にビルドフェーズを設けてトランスパイルを施す。
- ただし、トランスパイルのターゲットは一般的なブラウザ環境ではない。クラス構文やクラスプロパティ、デコレータなどのリリース前のES機能をそのまま残した状態で出力する必要がある
    - トランスパイルにはTypeScriptのコンパイラ(`tsc`)ではなく、プラグインが豊富かつ柔軟なBabelを利用する。
    - `@babel/preset-env`などは利用せず、[`@babel/preset-typescript`に型定義のストリップのみを任せる](https://github.com/stomita/lwc-typescript-dev-example/blob/91dc307c7aded2b8c45432b64b02baa9b8ecca9e/babel.config.js#L2)。
    - `@babel/plugin-proposal-decorators`や`@babel/plugin-proposal-class-properties`などを指定してしまうと同構文はトランスパイルされてしまう。指定しない
    - だがbabelのparserは同構文を理解する必要があるので、[babelの設定内の`parserOpts.plugins`に`decorator-legacy`を指定する](https://github.com/stomita/lwc-typescript-dev-example/blob/91dc307c7aded2b8c45432b64b02baa9b8ecca9e/babel.config.js#L3-L5)。
- npmのライブラリのバンドル方法については[「Lightning Web Components で npmで公開されているライブラリを利用する」](https://qiita.com/stomita/items/2cfa4db77c543f47d33c)で書いた内容と同様にrollupを行う。
    - 前回は参照ライブラリのみビルドしていたが今回はコンポーネント本体もバンドルに含めているところが違い
    - rollup実行時にはlightning platform関連のmoduleをresolveしないように[無視する設定を追加する](https://github.com/stomita/lwc-typescript-dev-example/blob/91dc307c7aded2b8c45432b64b02baa9b8ecca9e/rollup.config.js#L60-L65)(e.g. `lwc`, `lightning/*`, `@salesforce/*`)。
        - これにより[`lightning` npmモジュール](https://www.npmjs.com/package/lightning) （BitcoinのLightning Networkを利用するためのJSライブラリ）は利用できないことになるが、まあLWCでこれを使うケースはほぼないだろう
    - また`wire`デコレータでwireした関数の関数名はminify時にはmangle(名前の短縮)しないように[設定する](https://github.com/stomita/lwc-typescript-dev-example/blob/91dc307c7aded2b8c45432b64b02baa9b8ecca9e/rollup.config.js#L41-L43)（現状、変数名がshadowされないようにする必要がある模様）

- その他、[前処理用のbabelのプラグイン](https://github.com/stomita/lwc-typescript-dev-example/blob/91dc307c7aded2b8c45432b64b02baa9b8ecca9e/rollup.config.js#L29)、[バンドル時の後処理用rollupのプラグイン](https://github.com/stomita/lwc-typescript-dev-example/blob/91dc307c7aded2b8c45432b64b02baa9b8ecca9e/rollup.config.js#L47-L48)をrollup時に含めている。
    - これはrollup.jsが内部で扱う構文木が対応していない新しいES構文（デコレータなど）が含まれるコードはrollup内では扱えないため、そのhatch。
- TypeScriptソースコードは`force-app/main/default/lwc`とは別のディレクトリに管理する
    - サンプルではプロジェクトルート直下に[`src`ディレクトリを配置し](https://github.com/stomita/lwc-typescript-dev-example/tree/91dc307c7aded2b8c45432b64b02baa9b8ecca9e/src)、こちらでコンポーネント開発をすることにしている。もちろん場所は自由である。
- テンプレートHTMLは今回は`lwc`以下の各コンポーネントフォルダに直接保持する。もちろん何らかの前処理が必要だと考えるなら別に管理し、ビルド時にコピーする形でも構わない。
- プロジェクト内で利用するすべてのコンポーネントのコードをまとめたバンドルを作成し、バンドルされたファイルを共通LWCコンポーネントとして参照させるようにする。各コンポーネントではそれらを[単に読みこんでからdefault exportするだけのJSファイル](https://github.com/stomita/lwc-typescript-dev-example/blob/91dc307c7aded2b8c45432b64b02baa9b8ecca9e/force-app/main/default/lwc/myComp1/myComp1.js)を保持しておく。

## サンプルプロジェクト

順次更新中
https://github.com/stomita/lwc-typescript-dev-example

## 注意点

- 参照しているnpmのライブラリサイズが大きくなると、コンポーネントのpush時に `Error: Value too long for field: Source maximum length is:131072` が発生する。適宜[Code Splittingを行う](https://qiita.com/stomita/items/2cfa4db77c543f47d33c#lwc%E3%81%AE%E3%82%BD%E3%83%BC%E3%82%B9%E3%82%B3%E3%83%BC%E3%83%89%E3%83%95%E3%82%A1%E3%82%A4%E3%83%AB%E3%82%B5%E3%82%A4%E3%82%BA%E5%88%B6%E7%B4%84)必要がある。
- AppExchangeパッケージのセキュリティレビューが必要な場合では、バンドル後のファイルだけではなく、別にソースコードの提出を求められるかもしれない。
- 現時点ではプロジェクト内容によって個別に対処が必要になるだろう点が多く（先のCode Splittingなど）、上記の方法がベストプラクティスというところまでにはならないと思われる。npmライブラリの利用をあきらめて単にTS->JSへtranspileするだけなら穴は少ないかもしれないが…
- `wire`などの型定義は`lwc` (OSS版)に同梱されているが、あまりインテリジェントではない（たとえばハンドラ側の引数は自分で型アノテーションしてやる必要がある）。もちろんplatform側のサービス参照の型定義は提供されていないので、[適宜自分で型定義ファイルを作ってやる必要あり](https://github.com/stomita/lwc-typescript-dev-example/blob/91dc307c7aded2b8c45432b64b02baa9b8ecca9e/typings/%40salesforce.d.ts)。