【TypeScript + Webpack】今すぐ誰でも、ローカルで GoogleAppsScript の開発を始められるテンプレート (初心者向け)

# 🏰 作ったもの
ローカルで TypeScript を使った GoogleAppsScript の開発を
**今すぐ・誰でも**はじめられるテンプレートです。

▶︎▶︎▶︎ <a href="https://github.com/matcher-inc/gas-template" target="_blank" rel="noopener noreferrer">**TypeScript で GoogleAppsScript の開発を始めるテンプレート**</a> ◀︎◀︎◀︎

できるだけプロジェクトの内容をシンプルにするために
『TypeScript で GoogleAppsScript の開発をする』
ことに関係のないものはプロジェクト内にほとんど含めていません。

過去の僕がファイル数が多くなると
どのファイルが何に効いているのか分からなくなって思考停止してしまうマンだったため、
過去の僕向けに作った結果、シンプルになりました。

# 🙋‍♀️ 対象者

下記のような願望を持っている、
何度か GAS 開発をやったことがある人を想定しています。

- GAS で開発したプロジェクトを git で管理したい人
- いつもの Web エディタではなく、VSCode 等使い慣れているエディタで開発したい人
- プロジェクトが大きくなって、ディレクトリを分けて開発したくなってきた人
- npm パッケージを使って開発したい人
- TypeScript で開発することで心の安寧を得たい人
- 特に何か目的があるわけではないけど、ワンランク上の GAS 開発に挑戦してみたい人


<details>
<summary>「 GAS 開発自体まったく初めてです🔰」という人へ</summary>

　
GAS の開発経験がない状態からいきなり
「TypeScript + Webpack + Clasp でローカルで GAS 開発をしよう！」
だと新しいことが多すぎてハードルが高すぎるかもしれません。

『ノンプログラマ向けGASレクチャー』という
超絶神な資料を公開してくださっている方がいるので、
そちらで勉強してある程度 GAS に慣れたタイミングでこちらに戻ってきてもらえたらと思います。

→ <a href="https://qiita.com/sakaimo/items/4eee96ed254f42ba88c1" target="_blank" rel="noopener noreferrer">ノンプログラマ向けのGASレクチャ資料を公開します。(V8エンジン対応)</a>
</details>

# 🧞‍♂️ 実現できること
ざっくりと
・Clasp
・Webpack
・TypeScript
の 3つ によって、

いつもの Web エディタ上で GAS 開発するのと比べて
以下の恩恵を受けることができます。

### - Clasp による恩恵
⭕️ **git でバージョン管理ができる**
⭕️ **自分の好きなエディタが使える ( VSCode 等)**

Clasp は Google さんが作ってくれたツールです。
これによっていつもの Web エディタではなく、ローカルで GAS 開発ができるようになります。

前述 2つ の恩恵は、
どちらもローカルで開発できることによって得られるものです。

### - Webpack による恩恵
⭕️ **ディレクトリを分けて開発できる**
⭕️ **npm パッケージを使える**

Webpack はいろんなところに散らばっているファイル (モジュール) を
いい感じにひとまとめにしてくれるもの (モジュールバンドラー) です。

npm パッケージとは「世界中の技術者が作ってくれた、誰でも使える便利なもの」のようなイメージです。
Webpack を使うことで、GAS 開発の中でも npm パッケージを使うことができるようになります。

<details>
<summary>「Webpack とか npm って何ですか？🔰」という人へ</summary>

　
Webpack, npm の説明を始めるといくら行数があっても足りないほどなので
こちらでは偉大な先輩方のとてもわかりやすい記事のリンクを貼るに留めます。

このあたりの内容はとても奥が深く、
僕自身も 理解しきれたか/使いこなせるか と言われたら全然まだまだです。

一朝一夕で理解しきれるものではないため、
少しずつ関連する 記事/書籍 を読みながら、手を動かしながら身につけていきましょう。

・[【初心者向け】NPMとpackage.jsonを概念的に理解する](https://qiita.com/righteous/items/e5448cb2e7e11ab7d477)
・[やっぱりwebpackがわからない（エピソード1）](https://zenn.dev/antez/articles/58307946cf4f3e)
・[webpackとBabelの基本を理解する(1) ―webpack編―](https://qiita.com/koedamon/items/3e64612d22f3473f36a4)
</details>



### - TypeScript による恩恵
⭕️ **型付きで開発できる**

TypeScript は進化版 JavaScript みたいなもの (AltJS) で
ほぼ JavaScript と同じ文法で静的型付け言語っぽく開発することができます。

GoogleAppsScript はほぼ JavaScript なので、書きやすい反面
実行するそのときまでプログラムが意図した通りに動くか分かりません。
(動的型付け)

ちょっとプログラムが大きくなって込み入ってくると
開発しにくいなあというか、ちょっと怖いなあというか、
つまり静的型付け的な要素が欲しいなあ、となってきます。

まさに ↑ な時のために、
今回の TypeScript で開発できるテンプレートを作りました。

<details>
<summary>「 TypeScript 自体ほとんどやったことありません🔰」という人へ </summary>

　
「そもそも TypeScript って何？」
「TypeScript ってあんまり使ったことないけどどうやって勉強すればいい？」
という方は、

『[**サバイバルTypeScript**](https://typescriptbook.jp/)』という超絶神なサイトがあるので
ぜひこちらを読んでみてください。

↓ 中でも最初に読むとよさそうな箇所
・[TypeScript誕生の背景](https://typescriptbook.jp/overview/before-typescript)
・[なぜTypeScriptを使うべきか](https://typescriptbook.jp/overview/why-you-should-use-typescript)
・[静的型付け](https://typescriptbook.jp/overview/static-type)
</details>


# 🏃‍♂️ テンプレートを使った開発の始め方

### 0. 前提
すでに空の GAS プロジェクトが作られていて、
↑ をローカルで開発できるようにしたい、という場合の手順です。

GAS プロジェクトの作成がまだの方はこちらから
→ https://script.google.com/home/start

また、初めての場合は下記リンクから **Google Apps Script API の有効化**も忘れずに。
→ https://script.google.com/home/usersettings

<details>
<summary>動作確認済みの環境 ①：Ubuntu</summary>

```
Ubuntu: 18.04.6 LTS
node: v16.16.0
npm: 8.18.0
clasp: 2.4.1
webpack: 5.74.0
webpack-cli: 4.10.0
TypeScript: 4.8.3
```
</details>

<details>
<summary>動作確認済みの環境 ②：Mac</summary>

```
MacOS: 11.6
node: v16.17.1
npm: 8.15.0
clasp: 2.4.1
webpack: 5.74.0
webpack-cli: 4.10.0
TypeScript: 4.8.3
```
</details>


### 1. テンプレートにアクセス
▶︎▶︎▶︎ <a href="https://github.com/matcher-inc/gas-template" target="_blank" rel="noopener noreferrer">**TypeScript で GoogleAppsScript の開発を始めるテンプレート**</a> ◀︎◀︎◀︎

### 2. [Use this template] ボタンを押す
<img width="473" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/210717/1da2b2f2-da0f-4c79-7ea9-495a764dfeea.png">

※ github にログインしていないと [Use this template] のボタンが出てきません！

<details>
<summary>「 github のアカウントを持っていません🔰」という人へ</summary>

　
手順の 2,3 を飛ばして
『[4. 下記コマンドを順番に実行する](#4-下記コマンドを順番に実行する)』に移ってください。

また、その際『① テンプレートをクローン』で実行するコマンドは
代わりに以下のコマンドを実行してください。


```
# ①' テンプレートをクローン
git clone https://github.com/matcher-inc/gas-template my-gas-project
```
</details>

<details>
<summary>「テンプレートとかよく分からないけど一旦使ってみたいです🔰」という人へ</summary>

　
手順の 2,3 を飛ばして
『[4. 下記コマンドを順番に実行する](#4-下記コマンドを順番に実行する)』に移ってください。

また、その際『① テンプレートをクローン』で実行するコマンドは
代わりに以下のコマンドを実行してください。


```
# ①' テンプレートをクローン
git clone https://github.com/matcher-inc/gas-template my-gas-project
```
</details>



### 3. [Create repository from template] ボタンを押す
- Owner
- Repository name
- Description
- Public/Private

などは自分に都合の良いように設定してください。

<img width="800" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/210717/e74fb882-e645-3bb6-9868-53279c87e999.png">

### 4. 下記コマンドを順番に実行する
```bash
# ① テンプレートをクローン
git clone <テンプレートから新規作成したリポジトリのURL> my-gas-project

# ② ディレクトリを移動
cd my-gas-project

# ③ 必要なモジュールをインストール
npm install
```
### 5. `.clasp.json` に スクリプトID を貼り付ける
先に作っておいた空の GAS プロジェクトのスクリプト ID を .clasp.json に貼り付けることで
[ローカルの開発環境 ⇄ オンライン上の GAS プロジェクト] の紐付けを行います。

```json:.clasp.json
{
  "scriptId": "ここに スクリプトID を貼り付ける",
  "rootDir": "./dist/"
}
```

<details>
<summary>「スクリプト ID の見つけ方がわかりません🔰」という人へ</summary>

　
スクリプト ID の見つけ方
→ [【GAS】スクリプトIDを確認する方法｜プロジェクトを判断する識別子](https://blog-and-destroy.com/42782)
</details>


### 6. Clasp のユーザー認証を行う ( Clasp の利用がはじめての場合)
Clasp の利用がはじめての場合、
下記コマンドを実行して Clasp のユーザー認証を行う必要があります。
(以前に Clasp を利用したことがあり、この設定を済ませている場合は飛ばして大丈夫です)

```bash
# Clasp のユーザー認証を行う
npx clasp login --no-localhost
```

<details>
<summary>「コマンド実行後の手順をもう少し詳しく知りたい🔰」という人へ</summary>

　
前述のコマンドを実行すると以下のように出力され、
「 Clasp のユーザー認証がしたいならこのサイトにアクセスしてね！」
と URL が発行されます。

指示通り URL をコピー & アクセスして
画面の指示に従ってユーザー認証を進めてください。

```bash
$ npx clasp login --no-localhost
Logging in globally…
🔑 Authorize clasp by visiting this url:
https://accounts.google.com/o/oauth2/v2/auth?very-very-long-query # ←この URL コピーしてアクセス！

Enter the code from that page here:
```

アクセス先の画面で指示通り認証を進めていくと、
最後に認証コードが発行されます。

発行された認証コードをコピーして
`Enter the code from that page here:`
となっているところに貼り付けて、Enter キーを押せば認証完了です！

</details>


### 7. `npm run deploy` が正常に実行できるか確認

ここまで正しく設定できていれば、`npm run deploy` を実行することで
・Webpack によるバンドル
・Clasp によるオンライン上への開発内容反映
の両方ができるようになっているはずです。

`npm run deploy` を実行してこんな感じ ↓ になればOK！
```bash
$ npm run deploy

> gas-template@1.0.0 deploy
> webpack && clasp push

asset bundle.js 3.87 KiB [emitted] (name: main)
runtime modules 891 bytes 4 modules
cacheable modules 234 bytes
  ./src/index.ts 170 bytes [built] [code generated]
  ./src/sample_module/index.ts 64 bytes [built] [code generated]
webpack 5.74.0 compiled successfully in 1853 ms
└─ dist/appsscript.json
└─ dist/bundle.js
Pushed 2 files.
```


--

うまくいかない場合は以下のポイントを確認してみてください。

🚨 **`npm install` で必要なモジュールがインストールできているか**
  → node_modules フォルダが空の場合は `npm install` が実行できていません。



🚨 **.clasp.json に正しく スクリプトID が貼り付けられているか**
  → ここにミスがあると、Webpack でバンドルできても
  Clasp がうまくオンライン上に開発内容を反映できません。

🚨 **Clasp のユーザー認証が行われているか**
  → これを忘れているとローカルで開発した内容を
  Clasp を使ってオンライン上に反映することができません。
  参照：[6. Clasp のユーザー認証を行う](#6-Clasp-のユーザー認証を行う-(-Clasp-の利用がはじめての場合))

🚨 **`npm run deploy` の実行場所が正しいか**
  → 記事に記載の手順通りに行っている場合であれば、
  my-gas-project ディレクトリの中で `npm run deploy` を実行できているか確認しましょう。

# ❗️ テンプレートを使った開発時の注意点
### - src/index.ts での関数宣言方法について

ローカルで開発した内容をオンライン上に反映後、
いつもの Web エディタ上で関数を選択して実行したり
トリガーを設定したりするためには、
少しだけ関数の宣言方法に工夫が必要です。

具体的には、**gloabl オブジェクトのプロパティとして関数を宣言してあげる**必要があります。

```typescript:src/index.ts
// 他ファイルで宣言されている関数を import
import { sampleFunc } from 'sample_module';

// import した関数を golbal オブジェクトに func1 という名前で埋め込み
(global as any).func1 = sampleFunc;

// アロー関数を global オブジェクトに func2 という名前で埋め込み
(global as any).func2 = (): void => {
  const msg: string = 'hello hello !!';
  console.log(msg);
};
```

↑ このような形で関数を宣言することで、
func1, func2 をいつもの Web エディタ上で 選択/実行 できたり、
トリガー設定できたりします。

### - 開発内容の反映は毎回上書きされる
ローカルで開発した内容をオンライン上に反映するために Clasp を使っていますが
**Clasp による開発内容の反映は全て上書き**です。

そのため、
「すでに Web エディタ上である程度開発を進めていて、
それを途中からローカルでの TypeScript 開発に切り替えたい」
という場合は要注意です。

一度でもローカルの内容をオンライン上に反映してしまうと
Web エディタ上で開発してきた内容は全て上書きされて消えてしまいます。

ですので、テンプレートを用いた開発を始める際に特別の事情がない限りは
**開発途中の GAS プロジェクトの紐付けは避けて、**
**新しく空の GAS プロジェクトを作って開発を始める**ことをおすすめします。

# 📚 プロジェクトファイルのざっくり説明

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/210717/63ee1a7f-a18d-ab07-b260-5314b5e492cc.png)

### - dist フォルダ
src フォルダの中にあるファイルが
Webpack によってひとまとめに (バンドル) されて、
完成したファイル bundle.js がここに置かれます。

appsscript.json という設定ファイルも入っていますが、
こちらは基本触らなくて大丈夫です。

### - node_modules フォルダ
npm コマンドでインストールされたパッケージが入っている場所です。

『 npm コマンドで何をインストールするか』は基本的に package.json に記載されており
`npm install` することで package.json の内容をもとにインストールが行われます。
上記の理由から、node_modules フォルダの中身は git での管理対象から外しています。
( .gitignore で設定)

### - src フォルダ
基本的に開発で使うのはこのフォルダです。

Webpack の設定で『 src フォルダ内の index.ts 』
を起点にファイルのまとめ作業 (バンドル) を行うようにしているので
**必ず index.ts を作る**ようにしてください。

( ↑ の設定は webpack.config.js に書いてあります)

### - .clasp.json
Clasp に関する設定ファイルです。

・どの GAS プロジェクトに対して (scriptId)
・どのファイルを push するか (rootDir)
が書かれています。

```json:.clasp.json
{
  "scriptId": "<Script ID>",
  "rootDir": "./dist/"
}
```

`<Script ID>` のところに
自分の スクリプトID を貼り付けて使います。

### - .gitignore
git で管理したくないものを指定するためのファイルです。

今回作ったテンプレートでは以下 3つ を管理対象外として指定しています。
- node_modules：.package.json の内容から復元可能のため
- dist/bundle.js：webpack でバンドルすれば復元可能のため
- .clasp.json：スクリプトID が記載されるため

### - package.json
`npm install` 時にインストールして欲しいパッケージの一覧です。
(誤解を恐れずに言えば、お買い物リストのようなもの)

<details>
<summary>「 package-lock.json ってうのもあるけど何が違うの？🔰」という人へ</summary>

　
package-lock.json というのもありますが、
package.json とは似て非なるものです。

非常にざっくり説明すると、
・package.json → お買い物 **"する"** リスト
・package-lock.json → お買い物 **"した"** リスト
のようなイメージです。

↑ の詳細の説明は以下の記事にわかりやすく書かれているので
気になる方は読んでみてください。
→ [package-lock.json ってなに？](https://qiita.com/sugurutakahashi12345/items/1f6bb7a372b8263500e5)

</details>



### - README.md
ただの説明文です。

[海外版 Qiita 的なところ](https://dev.to/)でも記事を書いて公開するつもりのため、
README.md は英語で書いてあります。

(英語に間違いやわかりにくい点等あればご指摘いただけますと幸いです...！🙏)

### - tsconfig.json
TypeScript に関する設定が書かれているファイルです。

このテンプレートでは基本いじらなくて大丈夫です。

### - webpack.config.js
Webpasck に関する設定が書かれているファイルです。

こちらも、このテンプレートでは基本いじらなくて大丈夫です。



# 🚀 スプレッドシートの値を取得するサンプル 開発手順
最後に、スプレッドシートの値を取得してただコンソールで表示するだけの
簡単なプロジェクトをテンプレートを使用して開発する手順を紹介します。

### 1. 新しいスプレッドシートを作成
<a href="https://docs.google.com/spreadsheets/u/0/" target="_blank" rel="noopener noreferrer">こちら</a>から空白の新しいスプレッドシートを作ります。
↓ のような感じで適当に値も入れておきましょう。
<img width="400" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/210717/c3a78987-b47e-59a6-931b-9a0cdead91a8.png">

### 2. [拡張機能 → Apps Script] を選択
<img width="600" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/210717/d3ef1cfd-1bf1-4a8d-e5b6-00962c9066a1.png">

↓ Web エディタが開けたら スクリプトID をコピーして控えておきましょう。
(あとで .clasp.json に貼り付けるため)
<img width="800" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/210717/75fd9db0-8982-92a3-7df1-93fb4d751b78.png">

### 3. テンプレートを使った開発の準備
手順通りに進めていきましょう！
( `npm run deploy` ができるところまで確認できたらOKです！)

参照：[🏃‍♂️ テンプレートを使った開発の始め方](#%EF%B8%8F-テンプレートを使った開発の始め方)

`npm run deploy` がうまくいっていたら、
Web エディタがこんな感じ ↓ になっていると思いますので確認してみてください。
(もしなっていなかったら、画面を更新してみてください)
<img width="800" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/210717/0ffeb630-097e-5131-b524-98cd0eea2325.png">

### 4. 下記のコードを src/index.ts に追加
```diff_typescript:src/index.ts
import { sampleFunc } from 'sample_module';

// embed imported module function
(global as any).func1 = sampleFunc;

// embed arrow function
(global as any).func2 = (): void => {
  const msg: string = 'hello hello !!';
  console.log(msg);
};

+ (global as any).getSheetData = () => {
+   // スクリプトプロパティから スプレッドシートID を取得 → 無ければ処理中断
+   const ssId = PropertiesService.getScriptProperties().getProperty('SSID');
+   if(!ssId) return;
+ 
+   // スプレッドシートを取得 → シートを名前で取得 → 無ければ処理中断
+   const spreadsheet = SpreadsheetApp.openById(ssId);
+   const sheet = spreadsheet.getSheetByName('シート1');
+   if(!sheet) return;
+ 
+   // データがある範囲を取得 → 範囲内のデータを全て取得
+   const data = sheet.getDataRange().getValues();
+ 
+   // 取得したデータを 1行 ずつ表示
+   data.forEach((row, index) => {
+     console.log(`data[${index}]: ${row}`);
+   });
+ };
+ 
```

### 5. スクリプトプロパティに スプレッドシートID を設定
**① スプレッドシートID をコピー**
<img width="800" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/210717/da172237-73ec-66a0-0632-cd4475aa6314.png">

**② GAS の Web エディタからスクリプトプロパティを設定**
<img width="700" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/210717/7e6d0d4d-5f15-edb9-98c3-d0fac265b777.png">
<img width="700" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/210717/40fe08a8-6b79-e3eb-bd24-6c02227567bd.png">

### 6. `npm run deploy` を実行
開発内容を Webpack でバンドル & Clasp でオンライン上に開発内容を反映します。

↓ 実行してこんな感じになれば OK！
```bash
$ npm run deploy

> gas-template@1.0.0 deploy
> webpack && clasp push

asset bundle.js 4.43 KiB [emitted] (name: main)
runtime modules 891 bytes 4 modules
cacheable modules 704 bytes
  ./src/index.ts 640 bytes [built] [code generated]
  ./src/sample_module/index.ts 64 bytes [built] [code generated]
webpack 5.74.0 compiled successfully in 2053 ms
└─ dist/appsscript.json
└─ dist/bundle.js
Pushed 2 files.
```

### 7. Web エディタで getSheetData を 選択&実行
`npm run deploy` がうまく実行できていれば
getSheetData が選択できるようになっているはずです。
(選択できない場合はページを更新してみてください)
<img width="800" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/210717/70f39ef4-b67f-d8b4-b716-ee6bd49ad3ff.png">

getSheetData を実行して、
スプレッドシートのデータがコンソールに出力されたら成功です！
<img width="800" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/210717/0add0dbe-d560-7516-f373-d49e7f4ef039.png">

# 🍭 おまけ: npm scripts
ここまで `npm run XXX` 系のやつ (npm scripts) は 
`npm run deploy` しか紹介していませんが、
実はそれ以外にもいくつか用意しているので紹介します。

<details>
<summary>「そもそも npm scripts ってなんですか？🔰」という人へ</summary>

　
非常にざっくりと説明すると、
package.json で設定をすることで
自分が実行したいコマンドを自分で良い感じに用意できるものです。

↓ package.json に設定が書かれています。
<img width="600" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/210717/4a080d92-d0ff-6309-d379-e3b58a06beb4.png">
このあたりを見るとわかりやすいと思います。
・<a href="https://www.youtube.com/watch?v=rk-TGZ2Xsw4" target="_blank" rel="noopener noreferrer">【モダンJavaScript #13】npm scriptsで簡単にコマンド実行！GulpやGruntは私はもう使っていません！</a>
・<a href="https://www.twilio.com/blog/npm-scripts-jp" target="_blank" rel="noopener noreferrer">今まで知らずにいたnpmスクリプトでできる3つのこと</a>

</details>

```bash
# Webpack で src 内のファイルをひとまとめに (バンドル) する
npm run build

# src 内の変更を検知して、自動的に Webpack がバンドルするようにする
npm run build:watch

# Clasp で dist 内のファイルをオンライン上にアップロードする
npm run push

# dist 内の変更を検知して、自動的に Clasp がオンライン上にアップロードする
npm run push:watch

# [ Webpack でバンドル → Clasp でオンライン上にアップロード] を一度に行う
npm run deploy
```

僕は開発中は基本的に
・ `npm run build:watch`
・ `npm run push:watch`
を 2つ とも実行しておいて、

**開発内容がすぐに Webpack されて、**
**すぐにそれがオンライン上に反映される状態**にしています。

↑ こうすると、毎回いちいち `npm run deploy` をしなくて済むのでおすすめです！