🗂

clasp で import もできるローカル GAS 開発環境

2023/02/21に公開

はじめに

GAS である程度の規模のプロジェクトを開発しようとすると git によるバージョン管理をしたり、typescript を使いたくなったりで clasp を導入することになるかと思います。
しかし clasp 単体では typescript のコンパイルとかはやってくれるもののモジュールの import をしてくれずあまり便利な環境とは言い難いです。
今回、既存の GAS プロジェクトを clasp で管理できるように import のできる clasp 環境の構築を試してみましたので、その方法を書き残します。

構成

前述の通り clasp 単体では import をしてくれず、import を使ったコードを書きたい場合は import を解決してバンドルしたファイルを GAS にデプロイする必要があります。
そのため、今回は webpack で import を含む typescript をバンドルする構成にしました。
ビルド時間などの観点から esbuild にしました。

ファイル構成

全体のファイル構成は以下のようになりました。(必要なとこのみ記載)

.
├── public/
│   ├── appsscript.json
│   └── app.js
├── src/
│   ├── app.ts
│   └── その他 ts ファイル
├── .clasp.json
├── package.json
├── tsconfig.json
└── build.js
webpack
.
├── public/
│   ├── appsscript.json
│   └── app.js
├── src/
│   ├── app.ts
│   └── その他 ts ファイル
├── .clasp.json
├── package.json
├── tsconfig.json
└── webpack.config.js

public

このディレクトリ配下のファイルが GAS にデプロイする対象のファイルになります。
設定方法はあとで記載しますが、webpack の書き出し先と clasp のルートディレクトリでここを指定しています。

src

開発用のディレクトリです。
通常の typescript プロジェクトと同じような感覚で使えます。
app.ts のみ、GAS 上でグローバルな変数(GAS から直接参照可能な変数)を設定するために GAS 特有の書き方をしています。

設定内容

esbuild の設定

yarn add -D esbuild esbuild-gas-plugin

build.js

一番ベーシックな esbuild の利用であればビルドファイルは不要なのですが、今回は GAS のビルドプラグインを利用するためにビルドファイルを用意しています。

const { GasPlugin } = require('esbuild-gas-plugin');

require('esbuild').build({
  entryPoints: ['src/app.ts'],
  bundle: true,
  outfile: 'public/app.js',
  plugins: [GasPlugin]
}).catch(() => process.exit(1))

clasp の設定

.clasp.json

{
  "scriptId": /* GAS ID */,
  "rootDir": "./public",
  "fileExtension": [
    "js",
    "ts"
  ]
}

.clasp.json でデプロイ対象のディレクトリを webpack の書き出し先ディレクトリに強いしています。
fileExtensionts は、webpack が js ファイルとして書き出してくれるので不要かもしれません。

GAS のグローバル変数

src/app.ts

webpack でバンドルしたファイルでは定義したメソッドを含む変数は即時関数の中に入れられてしまい GAS から直接参照することができません。
そのため、GAS から参照したい変数は GAS で暗黙的に適宜されている(?)global 変数のフィールドとして持たせることで GAS から参照可能にしています。

import { hoge, fuga } from '@/hoge';

declare let global: any;
global.onEdit = hoge;  // スプレッドシートの編集トリガーとしても使える
global.fuga = fuga;
webpack の設定

webpack でのバンドルを行うにあたり以下のパッケージを導入しました。

yarn add -D webpack webpack-cli gas-webpack-plugin ts-loader tsconfig-paths-webpack-plugin

webpack.config.js

const GasPlugin = require("gas-webpack-plugin");
const TsconfigPathsPlugin = require('tsconfig-paths-webpack-plugin');

module.exports = {
  mode: 'development',
  target: ["web", "es5"],  // アロー関数を function に置き換えるため
  context: __dirname,
  entry: "./src/app.ts",
  output: {
    path: `${__dirname}/public`,  // 書き出し先に GAS へのデプロイディレクトリを指定
    filename: 'app.js'
  },
  module: {
    rules: [
      {
        test: /\.ts$/,
        use: 'ts-loader'  // typescript をトランスパイルするため
      }
    ]
  },
  resolve: {
    extensions: [".ts"],
    plugins: [
      new TsconfigPathsPlugin({ configFile: './tsconfig.json' })  // import でエイリアスを使うため
    ]
  },
  plugins: [
    new GasPlugin({ autoGlobalExportsFiles: ['**/*.ts'] })  // GAS 用のトランスパイルのため
  ],
  devtool: 'cheap-module-source-map'
}

tsconfig.json

{
  "extends": "./node_modules/gts/tsconfig-google.json",
  "compilerOptions": {
    "target": "es5",  // アロー関数を function に置き換えるため
    "baseUrl": ".",
    "paths": {
      "@/*": [
        "./src/*"
      ]
    }
  },
  "include": [
    "**/*.ts",
    "webpack.config.js",
  ]
}

古いランタイム GAS ではアロー関数が使えない

今回は既存の GAS プロジェクトの clasp 管理への移行ということで、GAS のランタイムが古くアロー関数などの ES5 以降の機能が使えない状態でした。
GAS の設定画面から ES5 に対応している V8 エンジンへの移行も可能なのですが、今回は全てのコードを typescript に移行することは考えていなかったため、念の為古いランタイムのまま clasp への移行を行いました。
古いランタイムを使う場合 webpack, typescript の両方で target に es5 を指定する必要があります。

参考

clasp の導入について:

webpack でのバンドルについて:

Discussion