├── .prettierignore
├── src
├── GasProject1
│ ├── .claspignore
│ ├── Code
│ │ ├── more-stuff.ts
│ │ ├── Code.ts
│ │ └── lost
│ │ │ └── found.ts
│ ├── .clasp.json
│ ├── appsscript.json
│ ├── tsconfig.json
│ ├── index.ts
│ └── README.md
├── GasProject2
│ ├── .claspignore
│ ├── Code
│ │ ├── more-stuff.ts
│ │ ├── Code.ts
│ │ └── lost
│ │ │ └── found.ts
│ ├── .clasp.json
│ ├── appsscript.json
│ ├── FileA.ts
│ ├── Code.ts
│ ├── tsconfig.json
│ └── README.md
├── common-components
│ ├── README.md
│ └── lib1
│ │ ├── tsconfig.json
│ │ ├── README.md
│ │ ├── sub
│ │ └── sub.ts
│ │ └── index.ts
├── tsconfig.json
├── README.md
├── tsconfig-node.json
└── tsconfig-gas.json
├── .gitignore
├── .editorconfig
├── TODOs.md
├── .prettierrc.json
├── LICENSE
├── README.md
└── package.json
/.prettierignore:
--------------------------------------------------------------------------------
1 | build
2 |
--------------------------------------------------------------------------------
/src/GasProject1/.claspignore:
--------------------------------------------------------------------------------
1 | **/**
2 | !**/appsscript.json
3 | !**/*.js
4 |
--------------------------------------------------------------------------------
/src/GasProject2/.claspignore:
--------------------------------------------------------------------------------
1 | **/**
2 | !**/appsscript.json
3 | !**/*.js
4 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | #build
2 | node_modules
3 |
4 | *.tsbuildinfo
5 |
6 | \#*\#
7 | .\#*
8 |
--------------------------------------------------------------------------------
/src/GasProject1/Code/more-stuff.ts:
--------------------------------------------------------------------------------
1 | /** BEGIN more-stuff.ts */
2 |
3 | function more() {
4 | return 'stuff';
5 | }
6 |
7 | /** END more-stuff.ts */
8 |
--------------------------------------------------------------------------------
/src/GasProject2/Code/more-stuff.ts:
--------------------------------------------------------------------------------
1 | /** BEGIN Code/more-stuff.ts */
2 |
3 | function more() {
4 | return 'stuff';
5 | }
6 |
7 | /** END Code/more-stuff.ts */
8 |
--------------------------------------------------------------------------------
/src/GasProject1/.clasp.json:
--------------------------------------------------------------------------------
1 | {
2 | "scriptId": "1TLCgakcLgxDMfRqwxwAobr6kg5ae9OgnOLV-MjIji-BhfTZP8vZ9ylDs",
3 | "rootDir": "../../build/gas/project1",
4 | "filePushOrder": []
5 | }
6 |
--------------------------------------------------------------------------------
/src/GasProject2/.clasp.json:
--------------------------------------------------------------------------------
1 | {
2 | "scriptId": "1oN_vpdtZHm3DKhulKTbNxIClqhRuL3J3wXxZtLEBBMkLve9-Ax_tiBjj",
3 | "rootDir": "../../build/gas/GasProject2",
4 | "filePushOrder": []
5 | }
6 |
--------------------------------------------------------------------------------
/src/GasProject1/appsscript.json:
--------------------------------------------------------------------------------
1 | {
2 | "dependencies": {
3 | "libraries": []
4 | },
5 | "exceptionLogging": "STACKDRIVER",
6 | "runtimeVersion": "V8",
7 | "timeZone": "Etc/GMT"
8 | }
9 |
--------------------------------------------------------------------------------
/src/GasProject2/appsscript.json:
--------------------------------------------------------------------------------
1 | {
2 | "dependencies": {
3 | "libraries": []
4 | },
5 | "exceptionLogging": "STACKDRIVER",
6 | "runtimeVersion": "V8",
7 | "timeZone": "Etc/GMT"
8 | }
9 |
--------------------------------------------------------------------------------
/src/common-components/README.md:
--------------------------------------------------------------------------------
1 | # The `common-components` folder
2 |
3 | This is where standalone are, reusable components are defined
4 |
5 | - `lib1` subfolder holds as sample component project
6 |
--------------------------------------------------------------------------------
/src/GasProject1/Code/Code.ts:
--------------------------------------------------------------------------------
1 | /** BEGIN Code.ts */
2 |
3 | function main() {
4 | console.log('Code.ts: main()');
5 | console.log(lib1.publicFunction());
6 | }
7 |
8 | main();
9 |
10 | /** END Code.ts */
11 |
--------------------------------------------------------------------------------
/src/GasProject2/Code/Code.ts:
--------------------------------------------------------------------------------
1 | /** BEGIN Code/Code.ts */
2 |
3 | function main() {
4 | console.log('Code.ts: main()');
5 | console.log(lib1.publicFunction());
6 | }
7 |
8 | main();
9 |
10 | /** END Code/Code.ts */
11 |
--------------------------------------------------------------------------------
/src/GasProject2/FileA.ts:
--------------------------------------------------------------------------------
1 | /** BEGIN FileA.ts */
2 |
3 | ///
4 |
5 | if (lib1) {
6 | more();
7 |
8 | lib1.publicFunction();
9 | } else {
10 | lost();
11 | }
12 |
13 | /** END FileA */
14 |
--------------------------------------------------------------------------------
/src/GasProject1/Code/lost/found.ts:
--------------------------------------------------------------------------------
1 | /** BEGIN lost/found.ts */
2 |
3 | // even though not referenced in index.ts, this file gets added to the output
4 |
5 | function lost() {
6 | return 'found';
7 | }
8 |
9 | /** END lost/found.ts */
10 |
--------------------------------------------------------------------------------
/src/GasProject2/Code/lost/found.ts:
--------------------------------------------------------------------------------
1 | /** BEGIN lost/found.ts */
2 |
3 | // even though not referenced in index.ts, this file gets added to the output
4 |
5 | function lost() {
6 | return 'found';
7 | }
8 |
9 | /** END lost/found.ts */
10 |
--------------------------------------------------------------------------------
/.editorconfig:
--------------------------------------------------------------------------------
1 | root = true
2 |
3 | [*]
4 | indent_style = space
5 | indent_size = 2
6 | insert_final_newline = true
7 | trim_trailing_whitespace = true
8 | charset = utf-8
9 | end_of_line = lf
10 |
11 | [*.md]
12 | trim_trailing_whitespace = false
13 |
--------------------------------------------------------------------------------
/src/common-components/lib1/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "../../tsconfig-gas",
3 | "compilerOptions": {
4 | "outFile": "../../../build/common-components/lib1/lib1.js"
5 | },
6 |
7 | "references": [],
8 |
9 | "include": ["**/*.ts"]
10 | }
11 |
--------------------------------------------------------------------------------
/TODOs.md:
--------------------------------------------------------------------------------
1 | # TODOs
2 |
3 | - better approach to manage configuration files (.clasp.json, .claspignore, appscript.json) from src to build
4 | - requires new Clasp release
5 | - better approach to handle project2 and lib1 dependency in build
6 | - requires new Clasp release
7 |
--------------------------------------------------------------------------------
/.prettierrc.json:
--------------------------------------------------------------------------------
1 | {
2 | "arrowParens": "always",
3 | "bracketSpacing": false,
4 | "endOfLine": "lf",
5 | "printWidth": 120,
6 | "quoteProps": "as-needed",
7 | "semi": true,
8 | "singleQuote": true,
9 | "tabWidth": 2,
10 | "trailingComma": "es5",
11 | "useTabs": false
12 | }
13 |
--------------------------------------------------------------------------------
/src/GasProject2/Code.ts:
--------------------------------------------------------------------------------
1 | /** BEGIN Code.ts */
2 |
3 | /**
4 | * Here the triple slash directives are meaningless since the tsconfig.json does not define an `outFile`
5 | */
6 |
7 | ///
8 | ///
9 |
10 | /** END Code.ts */
11 |
--------------------------------------------------------------------------------
/src/GasProject1/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "../tsconfig-gas",
3 | "compilerOptions": {
4 | "outFile": "../../build/gas/project1/Code.js"
5 | },
6 | "files": ["index.ts"],
7 | "include": ["**/*.ts"],
8 |
9 | "references": [{ "path": "../common-components/lib1", "prepend": true }]
10 | }
11 |
--------------------------------------------------------------------------------
/src/GasProject2/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "../tsconfig-gas",
3 | "compilerOptions": {
4 | "outDir": "../../build/gas"
5 | },
6 | "include": [
7 | "**/*.ts"
8 | ],
9 | "references": [
10 | {
11 | "path": "../common-components/lib1",
12 | "prepend": true
13 | }
14 | ]
15 | }
16 |
--------------------------------------------------------------------------------
/src/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "files": [],
3 | "include": [],
4 | "references": [
5 | // Sample project 1
6 | {
7 | "path": "./GasProject1"
8 | },
9 | // Sample project 2
10 | {
11 | "path": "./GasProject2"
12 | },
13 | // Sample common component
14 | {
15 | "path": "./common-components/lib1"
16 | }
17 | ]
18 | }
19 |
--------------------------------------------------------------------------------
/src/GasProject1/index.ts:
--------------------------------------------------------------------------------
1 | /** BEGIN index.ts */
2 |
3 | /**
4 | * Here the triple slash directives allow to specify order
5 | * in which files get added to the output
6 | */
7 |
8 | ///
9 | ///
10 |
11 | // other files in tsconfig scope (`files` and `include`) will be added past this point
12 |
13 | /** END index.ts */
14 |
--------------------------------------------------------------------------------
/src/README.md:
--------------------------------------------------------------------------------
1 | # The `src` folder
2 |
3 | - default settings for compiling for GAS `tsconfig-gas.json`
4 | - (also makes `google-apps-script` types available)
5 | - default build definition `tsconfig.json` with references to the GAS project and common components
6 | - `GasProject1` subfolder holds our first GAS project
7 | - `GasProject2` subfolder holds our second GAS project
8 | - `common-components` subfolder where reusable components are defined
9 |
--------------------------------------------------------------------------------
/src/common-components/lib1/README.md:
--------------------------------------------------------------------------------
1 | # The `lib1` component
2 |
3 | A reusable component which can be added to any GAS project.
4 |
5 | Code isolation is defined through namespaces. Exposed parts (using `export`) are available with the `lib1.` property accessor:
6 |
7 | ```ts
8 | lib1.publicFunction();
9 | ```
10 |
11 | ## tsconfig.json
12 |
13 | - use the default compile options `src/tsconfig-gas.json`
14 | - includes every *.ts files as source (no specific file ordering)
15 | - output a single `build/common-components/lib1.js`
16 |
--------------------------------------------------------------------------------
/src/GasProject1/README.md:
--------------------------------------------------------------------------------
1 | # The `GasProject1` folder
2 |
3 | A sample project using the `lib1` reusable component. It is setup to output a single *.js (including the `lib1` component)
4 |
5 | ## tsconfig.json
6 |
7 | - use the default compile options `src/tsconfig-gas.json`
8 | - reference `common-components/lib1` as available
9 | - declare that `common-components/lib1` is to be prepended
10 | - includes every *.ts files as source with `index.ts` first (though after any prepended reference)
11 | - output a single `build/gas/project1/Code.js`
12 |
--------------------------------------------------------------------------------
/src/common-components/lib1/sub/sub.ts:
--------------------------------------------------------------------------------
1 | /** BEGIN sub.ts */
2 |
3 | /**
4 | * Content of this component lives in a namescape
5 | *
6 | * Its exported parts are accessed using the `lib1.` property accessor
7 | */
8 | namespace lib1 {
9 | /**
10 | * This function is not exposed to outside the namespace.
11 | * It is visible only within this namespace block (i.e. namespace `lib1` in index.ts does not know about it)
12 | */
13 | const subInnerFunction = () => 'innerFunction';
14 |
15 | /**
16 | * This function is avaialble globally as `lib1.subPublicFunction()`
17 | */
18 | export const subPublicFunction = () => `subPublicFunction called ${subInnerFunction()}`;
19 | }
20 |
21 | /** END sub.ts */
22 |
--------------------------------------------------------------------------------
/src/GasProject2/README.md:
--------------------------------------------------------------------------------
1 | # The `GasProject2` folder
2 |
3 | A sample project using the `lib1` reusable component. It is setup to output the project structure (instead of a single file like GasProject1).
4 |
5 | ## tsconfig.json
6 |
7 | - use the default compile options `src/tsconfig-gas.json`
8 | - reference `common-components/lib1` as available (but it will not be prepended)
9 | - includes every *.ts files as source (no specific file ordering)
10 | - output a single `build/gas/project1/Code.js`
11 |
12 | ## lib1 component dependancy
13 |
14 | The compiled lib1 component copied from build/common-components to build/gas/GasProject2 after a successfull `npm run build`* command (cf. the 'postbuild' script in package.json)
15 |
--------------------------------------------------------------------------------
/src/common-components/lib1/index.ts:
--------------------------------------------------------------------------------
1 | /** BEGIN lib1/index.ts */
2 |
3 | /**
4 | * Content of this component lives in a namescape
5 | *
6 | * Its exported parts are accessed using the `lib1.` property accessor
7 | */
8 | namespace lib1 {
9 | /**
10 | * This function is not exposed to outside the namespace.
11 | * It is visible only within this namespace block (i.e. namespace `lib1` in sub/sub.ts does not know about it)
12 | */
13 | const innerFunction = () => 'innerFunction';
14 |
15 | /**
16 | * This function is avaialble globally as `lib1.publicFunction()`
17 | */
18 | export const publicFunction = () => {
19 | // subInnerFunction is unreachable from here
20 | // console.log(`publicFunction called ${subInnerFunction()}`);
21 |
22 | console.log(`publicFunction called ${subPublicFunction()}`);
23 |
24 | return `publicFunction called ${innerFunction()}`;
25 | };
26 | }
27 |
28 | /** END lib1/index.ts */
29 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2018 Guillaume Contesso a.k.a. PopGoesTheWza
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/src/tsconfig-node.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "composite": true,
4 | "rootDir": ".",
5 |
6 | "lib": ["es2017"],
7 | // "module": "commonjs",
8 | "moduleResolution": "node",
9 | // "noLib": false,
10 | "target": "es2017", // fully supported with node ^8.10.0 (cf. https://node.green/)
11 | "types": [],
12 |
13 | "strict": true,
14 | // "alwaysStrict": true,
15 | // "noImplicitAny": true,
16 | // "noImplicitReturns": true,
17 | // "strictBindCallApply": true,
18 | // "strictNullChecks": true,
19 | // "strictFunctionTypes": true,
20 | // "strictPropertyInitialization": true,
21 |
22 | "noEmitOnError": true,
23 | // "noErrorTruncation": true,
24 | "noFallthroughCasesInSwitch": true,
25 | "noImplicitThis": true,
26 | "noUnusedLocals": true,
27 | "noUnusedParameters": true,
28 | // "preserveConstEnums": true,
29 | // "removeComments": true,
30 | // "skipLibCheck": true,
31 | "strictNullChecks": true,
32 | // "suppressExcessPropertyErrors": true,
33 | // "suppressImplicitAnyIndexErrors": true,
34 |
35 | "declaration": true,
36 | // "declarationDir": "../types",
37 | // "declarationMap": true,
38 | "newLine": "lf",
39 | "pretty": true,
40 | "sourceMap": true
41 | }
42 | }
43 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Typescript - Google Apps Script project starter [](https://github.com/google/clasp)
2 |
3 | This repository aims at defining how one or more Google Apps Script (GAS) projects can be organized while satisfying the goals below.
4 |
5 | - Modern javascript features (Typescript)
6 | - Optional static and infered typing (Typescript)
7 | - Code isolation and reuse (by using Typescript `namespace` statements and project references)
8 | - Incremental builds (Typescript 3.4)
9 | - Fine control on how source `*.ts` files get compiled into target `*.js` files (i.e. overcome the `ts2gas` library limitations)
10 | - Code pretty printing and linting (Prettier and Tslint)
11 |
12 | ## Installation
13 |
14 | 1. clone this repository: `git clone https://github.com/PopGoesTheWza/ts-gas-project-starter.git`
15 | 1. install (globally) the **Clasp** CLI: `npm install --global @google/clasp`
16 | 1. install local dependencies: `npm install`
17 |
18 | ## NPM scripts
19 |
20 | Several commands are available as NPM scripts: `npm run `. The most commonly used are:
21 |
22 | - `build` and `build-clean` to compile all projects
23 | - `format` and `lint` to normalise code and check its correcteness
24 | - `push-all`, `push-project1` and `push-project2` to publish the GAS projects
25 |
--------------------------------------------------------------------------------
/src/tsconfig-gas.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "composite": true,
4 | "incremental": true,
5 |
6 | // Enforce strict coding style
7 | "strict": true,
8 | "alwaysStrict": true,
9 | // "allowUnreachableCode": false,
10 | // "allowUnusedLabels": false,
11 | // "downlevelIteration": false,
12 | "forceConsistentCasingInFileNames": true,
13 | "noFallthroughCasesInSwitch": true,
14 | "noImplicitAny": true,
15 | "noImplicitReturns": true,
16 | "noImplicitThis": true,
17 | "noUnusedLocals": true,
18 | "noUnusedParameters": true,
19 | "strictBindCallApply": true,
20 | "strictNullChecks": true,
21 | "strictFunctionTypes": true,
22 | "strictPropertyInitialization": true,
23 | // "suppressExcessPropertyErrors": false,
24 | // "suppressImplicitAnyIndexErrors": false,
25 |
26 | // Enable latest Javascriptt syntax and target V8 engine
27 | "experimentalDecorators": true,
28 | "lib": ["ESNext"],
29 | "target": "ES2018",
30 |
31 | // Limit use of `export` statements
32 | "module": "None",
33 | "moduleResolution": "node",
34 | // "allowSyntheticDefaultImports": false,
35 | // "esModuleInterop": false,
36 | // "importHelpers": false,
37 |
38 | // Define files produced by the compiler
39 | "newLine": "lf",
40 | "noEmitOnError": true,
41 | // "declaration": false,
42 | // "declarationMap": false,
43 | // "noResolve": false,
44 | // "preserveConstEnums": false,
45 | // "removeComments": false,
46 | "rootDir": ".",
47 | // "sourceMap": false,
48 | // "stripInternal": false,
49 |
50 | // Make Google Apps Script type definitions ambient
51 | // "noLib": false,
52 | // "skipLibCheck": false,
53 | "types": ["google-apps-script"],
54 | // "typeRoots": ["src/types", "node_modules/@types"],
55 | }
56 | }
57 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "private": true,
3 | "name": "ts-gas-project-starter",
4 | "version": "1.1.0",
5 | "description": "Sample repository for Typescript based Google Apps Script projects",
6 | "author": "Guillaume Contesso a.k.a. PopGoesTheWza",
7 | "license": "MIT",
8 | "keywords": [
9 | "gas",
10 | "google-apps-script",
11 | "ts",
12 | "typescript",
13 | "v8"
14 | ],
15 | "scripts": {
16 | "build-clean": "rimraf build && npm run config-all && npm run build && npm run postbuild",
17 | "build": "tsc --build src",
18 | "postbuild": "npm run postbuild-project2",
19 | "build-project1": "tsc --build src/GasProject1",
20 | "build-project2": "tsc --build src/GasProject2",
21 | "postbuild-project2": "copyfiles -u 3 build/common-components/lib1/lib1.js build/gas/GasProject2",
22 | "format": "prettier --write **.{ts,json}",
23 | "lint": "xo --quiet",
24 | "config-project1": "copyfiles -u 2 src/GasProject1/appsscript.json build/gas/project1",
25 | "config-project2": "copyfiles -u 2 src/GasProject2/appsscript.json build/gas/GasProject2",
26 | "config-all": "npm run config-project1 && npm run config-project2",
27 | "push-project1": "cd src/GasProject1 && clasp push",
28 | "push-project2": "cd src/GasProject2 && clasp push",
29 | "push-all": "npm run config-all && npm run push-project1 && npm run push-project2"
30 | },
31 | "repository": "https://github.com/PopGoesTheWza/ts-gas-project-starter.git",
32 | "devDependencies": {
33 | "@types/google-apps-script": "^1.0.33",
34 | "copyfiles": "^2.4.1",
35 | "prettier": "^2.3.0",
36 | "rimraf": "^3.0.2",
37 | "typescript": "^4.2.4",
38 | "xo": "^0.39.1"
39 | },
40 | "xo": {
41 | "ignores": [
42 | "build",
43 | "**.js"
44 | ],
45 | "space": 2,
46 | "rules": {
47 | "capitalized-comments": "warn",
48 | "no-inner-declarations": "warn",
49 | "no-undef": "off",
50 | "@typescript-eslint/no-unsafe-argument": "warn",
51 | "@typescript-eslint/no-unused-vars": "off",
52 | "@typescript-eslint/restrict-template-expressions": [
53 | "warn",
54 | {
55 | "allowNumber": true,
56 | "allowBoolean": false,
57 | "allowAny": false,
58 | "allowNullish": false
59 | }
60 | ],
61 | "@typescript-eslint/triple-slash-reference": "off",
62 | "unicorn/filename-case": "off"
63 | },
64 | "prettier": true
65 | }
66 | }
67 |
--------------------------------------------------------------------------------