Skip to content

gcanossa/gas-app

Repository files navigation

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.

About

No description, website, or topics provided.

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published