HomeBlogProjects

Use GSuite Like a PRO

sorcererxw

最近有一个基于 Google 办公套件的开发需求,目的是通过业务后端来读写 Google Suit 文档。一般来说,直接使用 Google Suit SDK 就好了,但是有很多能力限制,甚至也不能创建 Google Form。而 Google 将其大量的能力打包在了 Google App Script 当中(Google 太坏了),所以需要在开发当中引入 GAS。

GAS 介绍

为什么使用 GAS

不过既然 Google 已经提供了多套 SDK 供开发者调用,为什么还要使用 Google App Script:

GAS 的局限性

填坑

解决不能跨文件调用

多个代码文件分开管理是工程化的基础,否则维护成本会非常高,所以如果希望基于 AppsScript 开发正式项目,就需要解决跨文件调用的问题。

在 Typescript 我们一般使用 ES6 的 import module 的方式来进行跨文件调用,那么只需要将在每一个文件内把所用 import 都在顶部声明,并且保证文件之间没有循环引用,就可以计算出 module 调用拓扑图,通过复制 module 代码取代 import 的方式,就可以将这个拓扑图压缩到文件内。

// ------------
// 原始文件
// ------------

// index.ts
import * as A from './A'

function sayHello(){
	A.helloWorld()
}

// A.ts
import {foo} from './B'

export function helloWorld(){
	foo()
	console.log('Hello World!')
}

// B.ts

export function foo(){
}
// ------------
// 处理之后
// ------------

const B = (function () {
	function foo(){}
	B.foo = foo
})()

const foo = B.foo

const A = (function () {
  function helloWorld() {
		foo()
    console.log('Hello World!')
  }
  A.helloWorld = helloWorld
})();

function sayHello() {
  A.helloWorld()
}

以上是一个简单的目标效果展示,通过这种方式可以将多个文件的代码合并。不过现实情况当中还有需要情况需要考虑,罗列几个主要点:

现在 Google AppsScript 搭配了一个工具 clasp,可以快速进行鉴权和上传部署代码。通过这个工具进行上传代码,会自动将 JavaScript 和 TypeScript 转成 gs 文件。对 Typescript 文件,可以在转换出来的文件头部看到 Compiled using ts2gas,可以 clasp 是使用 ts2gas 进行编译 TypeScript。

去看一下 ts2gas 的实现,看到头部注释里面:

/**
 * Transpiles a TypeScript file into a valid Apps Script file.
 * @param {string} source The TypeScript source code as a string.
 * @param {ts.TranspileOptions} transpileOptions custom transpile options.
 * @see https://github.com/Microsoft/TypeScript/wiki/Using-the-Compiler-API
 */

可见 ts2gas 其实并没有什么神奇,只是使用 typescirpt 提供的 Compiler-API 来实现 ts 的编译。使用编译出的结果加上稍许定制的代码,就能够生成 gs 文件(前面说了,gs 其实就是 js)。

那么我们是不是也可以在 ts2gas 的基础上使编译出来的 gs 符合我们预期的效果呢?可以先看看 ts2gas 具体是如何实现的。

const ts2gas = (source: string, transpileOptions: ts.TranspileOptions = {}) => {
	transpileOptions = deepAssign({},  // safe to mutate
    defaults,  // default (overridable)
    transpileOptions,  // user override
    statics,  // statics
  );
	
	// Transpile (cf. https://www.typescriptlang.org/docs/handbook/compiler-options.html)
  const result = ts.transpileModule(source, transpileOptions);

  // # Clean up output (multiline string)
  let output = result.outputText;
  
	const pjson = require('../package.json');  // ugly hack

  // Include an exports object in all files.
  output = `// Compiled using ${pjson.name} ${pjson.version} (TypeScript ${ts.version})
var exports = exports || {};
var module = module || { exports: exports };
${output}`;

	return output;
}

上面就是 ts2gas 的核心代码,就是通过 ts 的 transpileModule 来将原来代码编译。函数允许调用方传入自己的 transpileOptions 来覆盖 defaultOption,不过还是有一个 staticsOption 来强制保证某些特性的准确性,通过调整 staticsOption 就能对如阻止使用 require 的要求进行限制,在编译时抛出错误。