この記事は Google Apps Script Advent Calendar 2021 の 11 日目の記事です
はじめに
GAS に頼るのは最後の手段にしたいというのが個人的な考えですが、
google/claspにより TypeScript で記述できるようになったことで
自分の中で GAS に対するハードルがだいぶ下がりました。(TypeScriptの書き味が一番好きです)
今回はそんな clasp を使って、npm install
した package を利用する方法を記載してみます。
前提
- Node.js および npm を利用できること(本記事では npm を使いますが、yarn に置き換えられる方はそれでも大丈夫かと思います)
- google/clasp がインストール済であること
- (参考リンク / コマンド:
npm install -g @google/clasp
)
- (参考リンク / コマンド:
注意点
- 最後に GAS 上でプロジェクトを開くとわかりますが、1 つの JavaScript ファイルに bundle されるため、目当ての関数を探すのが面倒にはなります。
- また、GAS 上コードを直接編集することはほぼできない感じになります。
- clasp push する前には必ず、
npm run build
(rollup 実行)が必要です。 - 後から気づいたのですが、ドキュメントからのリンクのこちらにも完成形があります。本記事はそれを 0 からやっていく手順です。
手順
clasp プロジェクトのセットアップ
この辺りは初歩的なところなので読み飛ばしても OK です。また、clasp のスタート手順については、README を読むと情報が正確です。
# (未だの場合のみ) clasp の認証を通しておく
clasp login
# 適当にプロジェクトディレクトリを作成してnpm init
cd projectdir
npm init
# GASのプロジェクト構成を作成
clasp create --type standalone
TypeScript 化
これは本題とは少し関係がないところですが、TypeScript で書きたいのでドキュメントに従ってセットアップします。
次のコマンドで形定義を install して
npm i -D @types/google-apps-script
直下に tsconfig.json
を作成すれば OK です。
tsconfig.json は clasp のドキュメントによると、変更するなという項目もあるので注意。
一例を載せておきます。
{
"compilerOptions": {
"target": "ES2019",
"module": "None",
"moduleResolution": "node",
"esModuleInterop": true,
"strict": true,
"lib": ["esnext"],
"experimentalDecorators": true
},
"include": ["src"]
}
スクリプトを記述
ここから本筋の、npm install
した package を利用したコードを書いていきます。
とりあえず moment
を使ってみるので install してコードを作成します。
moment を install して
npm i moment
src ディレクトリに index.ts
を記述
import moment from "moment";
const main = () => {
const now = moment();
Logger.log(now.format());
};
この時点で、 clasp push
コマンドを打つと GAS をアップロードできますが、 clasp open
で GAS プロジェクトを開いて実行しても、エラーになるはずです。GAS 上でも動かせる JavaScript ファイルになるよう、bundler を設定します。
module bundler (rollup.js) を設定
ローカルで書いたソースコードから、GAS 上でも実行できるファイルを生成するために rollup.js を使います。
ちょっと難しそうですが、clasp のドキュメントに書いてあることをやるだけです。
まず、rollup.js を install します。
npm i -D rollup
次に、rollup の config ファイル rollup.config.js
を作成します。一旦ドキュメントからコピペするだけです。
import { babel } from "@rollup/plugin-babel";
import { nodeResolve } from "@rollup/plugin-node-resolve";
const extensions = [".ts", ".js"];
const preventThreeShakingPlugin = () => {
return {
name: 'no-threeshaking',
resolveId(id, importer) {
if (!importer) {
// let's not theeshake entry points, as we're not exporting anything in Apps Script files
return {id, moduleSideEffects: "no-treeshake" }
}
return null;
}
}
}
export default {
input: "./src/index.ts",
output: {
dir: "build",
format: "esm",
},
plugins: [
preventThreeShakingPlugin(),
nodeResolve({
extensions,
}),
babel({ extensions, babelHelpers: "runtime" }),
],
};
あわせて、babel の config ファイルを作成します。
module.exports = {
presets: [
[
// ES features necessary for user's Node version
require("@babel/preset-env").default,
{
targets: {
node: "current",
},
},
],
[require("@babel/preset-typescript").default],
],
plugins: ["@babel/plugin-transform-runtime"]
};
plugins
はドキュメントの例にはありませんが、必要なので追加しておきます。
この状態で rollup を実行すると色々とエラーが出ることがわかるので対応します。(実行前にエラーがわかるようになりたい人生だった)
# 必要な依存関係を解決する (上記のjs内で使ってるplugin等々)
npm i -D @rollup/plugin-babel @rollup/plugin-node-resolve @babel/preset-env @babel/preset-typescript @babel/plugin-transform-runtime
babel.config.js
に必要な plugin がないよと怒られるので編集追加します。
+ plugins: ["@babel/plugin-transform-runtime"],
あと少しです。rollup.config.js
に一部追加します。
node_modules 以下の package.json から main ファイルを解決してくれるのですが、 jsnext:main
という様式で書かれている package があり、それに対応するための追記です。(jsnext:main
以外は ドキュメント記載のデフォルト値を入れてます。)
nodeResolve({
extensions,
+ mainFields: ["jsnext:main", "module", "main"],
}),
ここまでできたら、package.json
に build 用のコマンドを追加して実行してみましょう。
"scripts": {
+ "build": "rollup -c"
},
npm run build
成功すると created build
みたいな出力になるはずです。
clasp のデプロイ
ここまできたらあと少しです。
bundle された js ファイル以外がアップロードされないように、.claspignore
を作成します。
# ignore all files…
**/**
# except the extensions…
!appsscript.json
# and our transpiled code
!build/*.js
# ignore even valid files if in…
.git/**
node_modules/**
そして以下のコマンドを実行するとアップロードできます。
# スクリプトのアップロード
clasp push
clasp open
などで GAS プロジェクトを開き、 main
関数を実行すると、npm install した moment を使ったコードが実行できることが確認できるかと思います。
お疲れ様でした!
終わりに
今回のソースコード完成形はこちらにあります。 (https://github.com/suzukenz/sandbox/tree/master/advent-gas-2021)
記事にすると結構長くなりましたが、一度わかれば構築はすぐで、便利な package を使えたり
import / export が使えたりとメリットが多いので、自分は GAS を作るときはしばらくこれでやってみるつもりです。
また、clasp を使ってローカルに開発環境を作ることで、eslint などの便利なツールの恩恵も受けられます。その辺もいつか触れられたらと思います。
最後まで読んでいただきありがとうございました。