こちらの続きです。
- claspは便利。だが自由度が高すぎて環境整えるのが大変
前回、まとめにこんなことを書きましたが、このデメリットを改善するための Google aside を使ってみます。
Google aside
Google aside (Apps Script in IDE)は Google 謹製の clasp 用テンプレートフレームワークです。実行後、いくつかの質問に回答することで、claspにちょうどいい開発環境を構築してくれます。
https://github.com/google/aside
asideの利用
aside はこのように使います。
$ asdf local nodejs 22.2.0
$ npx @google/aside init
実行すると、いくつか質問が出てきます。
この回答結果で clasp の開発に便利なコードが生成されます。
✔ Project Title: … calculate_sample
✔ Create an Angular UI? … No
✔ Generate package.json? … Yes
✔ Adding scripts...
✔ Saving package.json...
✔ Installing dependencies...
✔ Installing src template...
✔ Installing test template...
✔ Script ID (optional): …
✔ Script ID for production environment (optional): …
✔ Creating calculate_sample...
-> Google Sheets Link: https://drive.google.com/open?id=1rROKUn8XinG6puFg4wRT7CZ0FMEgOuoSo4U-CMtGX7Y
-> Apps Script Link: https://script.google.com/d/1iV_olJEdt5vMJ3UWdG_y-COeP5dFf6lPQjvYgN627Pfcd-vxtgsrR87G/edit
ただ、aside経由だとスタンドアローンスクリプトは作れず、必ず関連するスプレッドシートも作られます。
asideの実行により生成されたファイル・フォルダは以下の通りです。
yusuke@suzuki-m2-Mac-mini calculate_sample_aside % ls -al
total 832
drwxr-xr-x 25 yusuke staff 800 7 1 21:50 .
drwxr-xr-x 12 yusuke staff 384 7 1 21:47 ..
-rw------- 1 yusuke staff 152 7 1 21:50 .clasp-dev.json
-rw------- 1 yusuke staff 152 7 1 21:50 .clasp-prod.json
-rw-r--r-- 1 yusuke staff 1 7 1 21:50 .claspignore
-rw-r--r-- 1 yusuke staff 114 7 1 21:50 .editorconfig
-rw-r--r-- 1 yusuke staff 44 7 1 21:50 .eslintignore
-rw-r--r-- 1 yusuke staff 398 7 1 21:50 .eslintrc.json
-rw-r--r-- 1 yusuke staff 64 7 1 21:50 .gitignore
-rw-r--r-- 1 yusuke staff 19 7 1 21:50 .prettierignore
-rw-r--r-- 1 yusuke staff 125 7 1 21:50 .prettierrc.json
-rw-r--r-- 1 yusuke staff 14 7 1 21:47 .tool-versions
-rw-r--r-- 1 yusuke staff 11358 7 1 21:50 LICENSE
-rw-r--r-- 1 yusuke staff 124 7 1 21:50 appsscript.json
drwxr-xr-x 2 yusuke staff 64 7 1 21:50 dist
-rw-r--r-- 1 yusuke staff 246 7 1 21:50 jest.config.json
-rw-r--r-- 1 yusuke staff 570 7 1 21:50 license-config.json
-rw-r--r-- 1 yusuke staff 553 7 1 21:50 license-header.txt
drwxr-xr-x 483 yusuke staff 15456 7 1 21:50 node_modules
-rw-r--r-- 1 yusuke staff 342627 7 1 21:50 package-lock.json
-rw-r--r-- 1 yusuke staff 1528 7 1 21:50 package.json
-rw-r--r-- 1 yusuke staff 1229 7 1 21:50 rollup.config.mjs
drwxr-xr-x 4 yusuke staff 128 7 1 21:50 src
drwxr-xr-x 3 yusuke staff 96 7 1 21:50 test
-rw-r--r-- 1 yusuke staff 455 7 1 21:50 tsconfig.json
src以下にコードのサンプルが置いてあります。
- src/index.ts
/**
* Copyright 2023 Google LLC
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/* eslint-disable @typescript-eslint/no-unused-vars */
import { hello } from './example-module';
console.log(hello());
- src/example-modules.ts
/**
* Copyright 2023 Google LLC
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
export function hello() {
return 'Hello Apps Script!';
}
claspコマンドは使わず、npmコマンドを介して build を行います。npm run に指定できるコマンド、どんな処理を指定してるかは package.json の内容を確認してください。
% npm run build
> calculate-sample@0.0.0 build
> npm run clean && npm run bundle && ncp appsscript.json dist/appsscript.json
> calculate-sample@0.0.0 clean
> rimraf build dist
> calculate-sample@0.0.0 bundle
> rollup --no-treeshake -c rollup.config.mjs
src/index.ts → dist...
created dist in 612ms
ビルドすることでトランスコンパイルされ生成された dist/index.js が生成されます。このファイルをGASのランタイム環境に転送します。
- dist/index.js
/**
* Copyright 2023 Google LLC
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
function hello() {
return "Hello Apps Script!";
}
console.log(hello());
dist以下のこれをGASに転送するため、deployを行います。
lint, testを実行し、dist/index.js, dist/appsscript.json を転送。
% npm run deploy
> calculate-sample@0.0.0 deploy
> npm run lint && npm run test && npm run build && ncp .clasp-dev.json .clasp.json && clasp push -f
> calculate-sample@0.0.0 lint
> npm run license && eslint --fix --no-error-on-unmatched-pattern src/ test/
> calculate-sample@0.0.0 license
> license-check-and-add add -f license-config.json
No default format specified. Using {"prepend":"/*","append":"*/"} as backup
! Inserted license into 1 file(s)
Command succeeded
=============
WARNING: You are currently running a version of TypeScript which is not officially supported by @typescript-eslint/typescript-estree.
You may find that it works just fine, or you may not.
SUPPORTED TYPESCRIPT VERSIONS: >=3.3.1 <5.2.0
YOUR TYPESCRIPT VERSION: 5.5.2
Please only submit bug reports when using the officially supported version.
=============
(node:44029) [DEP0180] DeprecationWarning: fs.Stats constructor is deprecated.
(Use `node --trace-deprecation ...` to show where the warning was created)
> calculate-sample@0.0.0 test
> jest test/ --passWithNoTests --detectOpenHandles
PASS test/example-module.test.ts
example-module
hello
✓ Returns a hello message (1 ms)
Test Suites: 1 passed, 1 total
Tests: 1 passed, 1 total
Snapshots: 0 total
Time: 0.815 s
Ran all test suites matching /test\//i.
> calculate-sample@0.0.0 build
> npm run clean && npm run bundle && ncp appsscript.json dist/appsscript.json
> calculate-sample@0.0.0 clean
> rimraf build dist
> calculate-sample@0.0.0 bundle
> rollup --no-treeshake -c rollup.config.mjs
src/index.ts → dist...
created dist in 491ms
(node:44296) [DEP0040] DeprecationWarning: The `punycode` module is deprecated. Please use a userland alternative instead.
(Use `node --trace-deprecation ...` to show where the warning was created)
└─ dist/appsscript.json
└─ dist/index.js
Pushed 2 files.
前回claspで作ったTypeScriptのコードをasideに移植
ということで、前回 clasp で作った計算プログラムを aside のプロジェクトに移植してみます。
- src/Logger.ts
// Logger.ts
export function logMessage(message: string): void {
Logger.log(message);
}
- src/Utilitiles.ts
// Utilities.ts
export function add(a: number, b: number): number {
return a + b;
}
export function subtract(a: number, b: number): number {
return a - b;
}
- src/index.ts
/* eslint-disable @typescript-eslint/no-unused-vars */
import { logMessage } from './Logger';
import { add, subtract } from './Utilities';
function main() {
const resultAdd = add(5, 3);
const resultSubtract = subtract(5, 3);
//const resultSubtract2 = add("aaa", "bbb");
logMessage(`Addition Result: ${resultAdd}`);
logMessage(`Subtraction Result: ${resultSubtract}`);
//logMessage(`${resultSubtract2}`);
}
ついでにテストも書いちゃう
test/utilities.test.ts ファイルを作って、src/Utilities.ts のユニットテストを書いてみる。
テストツールは Jest が使われます。
ユニットテスト。GASでスプレッドシートを直に操作する等のUI処理は、テストが書きづらいので書きません。今回は計算や配列操作、データ処理を行うロジックのコードについて、ユニットテストが書いています。
- test/utilities.test.ts
import { add, subtract } from '../src/Utilities';
describe('utilities-test', () => {
// Path: test/utilities.test.ts
// add function test
describe('add', () => {
it('add 2 number 1 (integer number)', () => {
expect(add(2, 3)).toBe(5);
expect(add(-2, 3)).toBe(1);
expect(add(2, -3)).toBe(-1);
expect(add(-2, -3)).toBe(-5);
});
it('add 2 number 2 (decimal number)', () => {
expect(add(2.4, 3.5)).toBe(5.9);
expect(add(-2.4, 3.5)).toBe(1.1);
expect(add(2.4, -3.5)).toBe(-1.1);
expect(add(-2.4, -3.5)).toBe(-5.9);
});
it('add 2 number 3 (NaN)', () => {
expect(add(NaN, 3.5)).toBe(NaN);
expect(add(1, NaN)).toBe(NaN);
expect(add(NaN, NaN)).toBe(NaN);
});
});
// Path: test/utilities.test.ts
// subtract function test
describe('subtract', () => {
it('subtract 2 number 1 (integer number)', () => {
expect(subtract(2, 3)).toBe(-1);
expect(subtract(-2, 3)).toBe(-5);
expect(subtract(2, -3)).toBe(5);
expect(subtract(-2, -3)).toBe(1);
});
it('subtract 2 number 2 (decimal number)', () => {
expect(subtract(2.4, 3.5)).toBe(-1.1);
expect(subtract(-2.4, 3.5)).toBe(-5.9);
expect(subtract(2.4, -3.5)).toBe(5.9);
expect(subtract(-2.4, -3.5)).toBe(1.1);
});
it('subtract 2 number 3 (NaN)', () => {
expect(subtract(NaN, 3.5)).toBe(NaN);
expect(subtract(1, NaN)).toBe(NaN);
expect(subtract(NaN, NaN)).toBe(NaN);
});
});
});
テストの実行。こんな感じで通りました。
% npm run test
> calculate-sample@0.0.0 test
> jest test/ --passWithNoTests --detectOpenHandles
PASS test/utilities.test.ts
PASS test/example-module.test.ts
Test Suites: 2 passed, 2 total
Tests: 7 passed, 7 total
Snapshots: 0 total
Time: 0.546 s, estimated 1 s
Ran all test suites matching /test\//i.
ビルド
テストも通ったので、npm run build
でコードをビルド。
こんなコードが生成されます。
- dist/index.js
/**
* Copyright 2023 Google LLC
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
function logMessage(message) {
Logger.log(message);
}
function add(a, b) {
return a + b;
}
function subtract(a, b) {
return a - b;
}
function main() {
const resultAdd = add(5, 3);
const resultSubtract = subtract(5, 3);
logMessage(`Addition Result: ${resultAdd}`);
logMessage(`Subtraction Result: ${resultSubtract}`);
}
あえてバグを作ってみると…
ちなみに、文字列の足し算を行えるようにしてclasp版でGASにpushされてしまった件。
function main() {
const resultAdd = add(5, 3);
const resultSubtract = subtract(5, 3);
const resultSubtract2 = add("aaa", "bbb");
logMessage(`Addition Result: ${resultAdd}`);
logMessage(`Subtraction Result: ${resultSubtract}`);
logMessage(`${resultSubtract2}`);
}
これを、aside版で行ってみると、
% npm run build
> calculate-sample@0.0.0 build
> npm run clean && npm run bundle && ncp appsscript.json dist/appsscript.json
> calculate-sample@0.0.0 clean
> rimraf build dist
> calculate-sample@0.0.0 bundle
> rollup --no-treeshake -c rollup.config.mjs
src/index.ts → dist...
[!] (plugin rpt2) RollupError: [plugin rpt2] src/index.ts:6:31 - error TS2345: Argument of type 'string' is not assignable to parameter of type 'number'.
6 const resultSubtract2 = add("aaa", "bbb");
~~~~~
src/index.ts
at getRollupError (/Users/yusuke/projects/MIERUNE/gas/calculate_sample_aside/node_modules/rollup/dist/shared/parseAst.js:282:41)
at Object.error (/Users/yusuke/projects/MIERUNE/gas/calculate_sample_aside/node_modules/rollup/dist/shared/parseAst.js:278:42)
at Object.error (/Users/yusuke/projects/MIERUNE/gas/calculate_sample_aside/node_modules/rollup/dist/shared/rollup.js:804:32)
at RollupContext.error (/Users/yusuke/projects/MIERUNE/gas/calculate_sample_aside/node_modules/rollup-plugin-typescript2/src/context.ts:35:17)
at /Users/yusuke/projects/MIERUNE/gas/calculate_sample_aside/node_modules/rollup-plugin-typescript2/src/diagnostics.ts:70:17
at Array.forEach (<anonymous>)
at printDiagnostics (/Users/yusuke/projects/MIERUNE/gas/calculate_sample_aside/node_modules/rollup-plugin-typescript2/src/diagnostics.ts:42:14)
at typecheckFile (/Users/yusuke/projects/MIERUNE/gas/calculate_sample_aside/node_modules/rollup-plugin-typescript2/src/index.ts:67:3)
at Object.<anonymous> (/Users/yusuke/projects/MIERUNE/gas/calculate_sample_aside/node_modules/rollup-plugin-typescript2/src/index.ts:269:5)
at Generator.next (<anonymous>)
このように、npm のビルド時点で怒られ、デプロイができなくなります。ということで、claspのように、コードが間違っていても push されることはありません。
まとめ
- 素のGASを書くのは色々ツライが、claspを使うとメリットが色々享受できる
- claspは便利だが自由度が高すぎ
- Google謹製のasideはclaspを制御するのに都合がよい
以上