レバレジーズ データAIブログ

インハウスデータ組織のあたまのなか

GASをTypeScriptでローカル開発する冴えたやり方

はじめに

こんにちは、テクノロジー戦略室の室長をしています竹下です。マネージャー業務をやっていると、スプレッドシートをGASで自動化することも多く、JavaScriptはなんだかなーと思っていました。TSKaigiの代表理事としてもTypeScriptで書きたい!と思いたちやってみました。

Googleスプレッドシートやドキュメントなどを操作出来るGoogle Apps Script(以降GAS)は、通常はブラウザエディタで開発することが多いとは思いますが、

  • JavaScriptのため、コード補完が貧弱
  • 近年のAIのコードアシストが使えない
  • バージョン管理が出来ない(gitほど便利じゃない)

などの問題があり、GASで少し複雑な処理を行おうとすると一気に辛くなってきます。

この記事ではclaspを使って、GASをTypeScriptでローカル開発する方法と、実際に開発する際のTipsを紹介します。

claspとは

claspはGoogleが公式に開発している、GASをCLIから実行することが出来るツールです。claspを利用することで、GASのソースをgit管理に入れてローカルで任意のIDEを利用して開発出来るようになります。

導入方法

「clasp typescript」などでGoogle検索して出てくる記事を参考にしたり、IDEからAIエージェントに「TypeScriptでclaspのプロジェクトを初期化して」と指示すると準備を整えてくれます。

ただ、esbuildなどを入れ、実行ファイルを一つのファイルに結合するソースコード結合も行える方が後々楽なので、claspを利用した開発環境構築の記事を参考にするのがおすすめです。

開発手順

詳しくは参考の記事に譲りますが、わかっている人向けに簡単な初期設定の手順と、開発手順を紹介しておきます

初期設定

1: claspのCLIコマンド追加

npm install -g @google/clasp

でglobalにclaspのコマンドを使えるようにしておきます。

2: claspのAPI有効化とログイン

npx clasp login

でログインを行います。 また、まだGoogle Apps Script APIが有効になっていない場合は、AppScriptの設定から、APIを有効にしてください。

3: TypeScriptのプロジェクトの作成

Node.jsによるTypeScrptの開発プロジェクトとして初期化してください。

4: claspの追加とesbuildの設定

npm install clasp
npm install -D esbuild esbuild-gas-plugin

で依存にclaspとesbuildを入れ、esbuildによるトランスパイルを設定してください。

参考: claspを利用した開発環境構築

ソースコードのpush

1: ビルドとソースのpush

ビルドコマンドは、各人の環境に合わせて修正してください。

clasp pull
node esbuild.js # esbuild使わない場合は、tsc compileなど
clasp push

このコマンドを毎回叩くのは面倒なので、package.jsonに

{
  "scripts:": {
    "build": "node esbuild.js && cp appsscript.json dist/",
    "deploy": "clasp pull && npm run build && clasp push"
  }
}

を設定しておくと、

npm run deploy

だけでソースコードをpush出来るのでおすすめです。

Tips

esbuildなどによるソースコードの結合

GASは、pushしたファイルが全てglobalに読み込まれるため、importを書かなくても関数名を書くだけで呼び出せます。(むしろimportを書いてしまうと、App Scriptエディター上で実行するとエラーになってしまいます。)
必ずしもソースコード結合を行う必要は無いのですが、

  • IDEの設定をちゃんとしないとコンパイルエラーが出てしまう
  • 使っていない関数やファイルもpushされてしまう
  • minifyされていないので、ソースコードの容量が大きくなる

などの問題があります。そのため、esbuildなどでソースコード結合を行うようにしておくことで、minifyしたりhoistingしたり出来ます。

生成されたコードの先頭に、コメントを入れておく

デプロイされたソースコードは、ブラウザ上からも変更出来てしまいます。また、minifyすると、ソースコード内のコメントも失われてしまいます。

そのため、以下の様にesbuild.jsのビルド時にソースコードの冒頭にコメントを入れておき、管理しているソースコードを指示したり利用方法を書いておくと親切です。

import esbuild from "esbuild";
import { GasPlugin } from "esbuild-gas-plugin";
import fs from "node:fs";

const attention = `/*
##############################################################################
このGASは、https://github.com/XXXXXX で管理されています。
修正を行う場合は、上記のソースを修正し、デプロイしてください。
デプロイ方法は、README.mdを参照してください。

実行は、YYYYの関数を実行してください。

##############################################################################
*/
`;

esbuild
  .build({
    entryPoints: ["./src/main.ts"],
    bundle: true,
    minify: false, // minifyしたい場合はtrueに変更
    outfile: "./dist/main.js", // エントリーポイントになるファイルを指定
    plugins: [GasPlugin],
  })
  .then(() => {
    console.log("ビルドに成功しました");
    const mainContent = fs.readFileSync("./dist/main.js", "utf8"); 
    // ここで、ビルドされたファイルの頭にコメントを挿入している
    fs.writeFileSync("./dist/main.js", attention + mainContent);
  })
  .catch((error) => {
    console.log("ビルドに失敗しました");
    console.error(error);
    process.exit(1);
  });

push前にpullを行う理由

clasp pushを実行すると、対象のディレクトリが完全に同期されるため、もし手動でブラウザのApp Scriptエディター上でファイルを追加してしまっていると、勝手に削除されてしまいます。
そのため、clasp pullでファイルを同期しておくことで、既存のファイルの削除を防ぐことが出来ます。

GAS実行時に、設定を切り替えたり認証情報を渡す方法

前述のpush前にpullを応用すると、App Scriptエディター側で実行時に設定を変更したりApp Scriptエディター内だけで認証情報を保持することが可能になります。

GASの実行を行う場合に引数を渡せ無いため、ソースコードを変更してから実行しないと設定を切り替えたりすることが出来ません。しかし、ソースコードを書き換えても再度 clasp pushしてしまうと、ソースコードが上書きされてしまい、設定や認証情報が消えてしまいます。

そこで、config.gsのようなコンフィグ情報を記載したファイルをApp Scriptエディター上で作成し設定や認証情報を記載しておき、ローカル開発ではダミーのファイルを用意しておいてコンパイル対象にならないようにすることで、pushしても上書きされないファイルを作ることが出来ます。

App Scriptエディターで実行できる関数の定義

エントリーポイントになるファイル(main.tsなど)には、以下のように関数の設定だけ行うようにし、実装はファイルに分けることを推奨します。

import {
  func1,
  func2
} from "./funcs";
import {
  tool1
} from "./tools";

interface Global {
  func1: typeof func1;
  func2: typeof func2;
  tool1: typeof tool1
}
declare const global: Global;

// entryPoints
// globalに設定した関数が、App Scriptエディター上で見えるようになる
global.func1 = func1;
global.func2 = func2;
global.tool1 = tool1;

App Scriptエディター上での実行時は最新ソースが反映されている

App Scriptエディターを開きっぱなしにした状態で、clasp pushでソースコードを更新した場合、App Scriptエディター上ではページのリロードをするまで最新のソースコードに更新はされていないのですが、ページリロードをせずに実行したとしてもpushされた最新ソースコードがちゃんと実行されます。

おわりに

GASは便利なのですが、少し複雑なことをしようとすると管理が大変になっていました。 claspを利用すると、GASの開発も、通常の開発フローに乗せられるようになるため、複数人で管理するのも楽になりました。 ぜひみなさんもclaspを利用して快適なGAS開発をエンジョイしてください。