Google App Script Frameowrk
The library exists to facilitate the impleentation of an application on the Google App Script platform.
The library is composed of three parts: server, client and mocking.
Server
On the server side (Google App Script) use the function createGasApp(methods) to build an object with the following type:
export type GasApp = {
invoke(name: string, ...params: any[]): any;
};
The function configures a map of functions scoped by the library that can be invoked indirectly throgh the method invoke
The invoke method of the returned object allows to call a scoped function by name.
On the server side is also avilable the function include that produces an HtmlTemplate from a file and return the evaluated content. Can be used to include/import files in an html template.
In order to use the module functions they must be exposed with a global scope function.
import { createGasApp, include } from "@gcanossa/gas-app/server";
const app = createGasApp({
sum(a: number, b: number) {
return a + b;
},
});
function appinvoke(...params: any[]): any {
return app.invoke(...params);
}
function myInclude(...params: any[]): string {
return include(...params);
}
Client
On the client side (an Html page served from Google App Script) are available:
- createBridge(): implements a Proxy getting type inference for the server side functions through the standard window.google.script interface. Every function returns a Promise<[result, userObject]> and comes in two flavours: <name>(...args) and ctx_<name>(userObject, ...args).
- createGasAppBridge(invokeFunctionName: string): does the same thing ad createBridge, but for the functions scoped with GasApp init and called throgh GasApp invoke. The parameter invokeFunctionName is the name of the functio used to expose the invoke method.
Mocking
On the client side (an Html page served from Google App Script) the function initMocks(mocks, appMocks) initialize a mock window.google.script object applying the mocks typing. The two configuration parameters are maps of functions which mocks the server side one. The parameter appMocks is a map of functions maps which mocks one or multiple server side gas app. Each key is the name of the function exposing invoke.
Usage
On the server side (Google App Script) create the desired functions:
function sum(a, b) {
return a + b;
}
function sub(a, b) {
return a - b;
}
function mul(a, b) {
return a * b;
}
function div(a, b) {
return a / b;
}
If necessary scope the with the library:
export type ScopedServerFn = {
sum: typeof sum;
mul: typeof mul;
};
export type ServerFn = {
sub: typeof sub;
div: typeof div;
};
const app = createGasApp<ScopedServerFn>({ sum, mul };
function myApp(...params:any[]){
return app.invoke(...params);
}
On the client create the bridges using the server side exported type:
import { ScopedServerFn, ServerFn } from "./code.ts";
const bridge = createBridge<typeof ServerFn>();
const coreBridge = createGasAppBridge<typeof ScopedServerFn>();
Init the mocks in development mode:
if(import.meta.env.DEV){ //expample using with vite
const gas = initMocks<typeof ServerFn>({
async sum(a, b) { // NOTE: must be a Promise for every mock to mimic server behaviour
return a + b;
},
...
}, {
myApp:{
...
}
...
});
}
Call the methods:
const [res, _] = await bridge.sum(1, 2);
const [res2, { val }] = await bridge.ctx_sum({ val: "test" }, 1, 2);
const [re3, _] = await appBridge.mul(1, 2);
const [res4, { msg }] = await appBridge.ctx_mul({ msg: "test" }, 1, 2);
Usage in Html template
If you need to include/import a file into another Html template, use the *function exposing the *include** function:
main.html
...
<?!= myInclude('path/to/file', { val: 3 }) ?>
...
path/to/file.html
<h1>Title</h1>
<p>
Value is:
<?= val ?>
</p>
Vite plugin
The library contains also a vite function plugin viteGasApp which allows to create simple html pages to be renderd by Google Apps Script.
The plugin explores the input path and expect a structure like this:
/<input>
/ui
global.css
app.js
utils.js
/page1
view.html
script.js
style.css
/page2
view.html
script.js
style.css
Basically a set of .html, .css and .js inside a ui folder. The plugin reproduce the same hierarchy in the destination folder wrapping .js files in <script></script> tags and .css in <style></style> tags and renaming the files in .js.html and .css.html. The script searches the .html files for <script> and <style> tags with the attribute x-gas-include, if found rewrites the tag using the scriptlet <?!= myInclude(...) ?> (example name) effectively inlining the resource. This allows a comfortable local development inlcuding the .js and .css files from the filesystem and an organized clasp folder keeping the script and style resources separated like in local development.