{"id":20850036,"url":"https://github.com/cristobalgvera/ez-clasp","last_synced_at":"2025-05-12T04:31:03.993Z","repository":{"id":41112191,"uuid":"368968259","full_name":"cristobalgvera/ez-clasp","owner":"cristobalgvera","description":"Template repository to start a Google Apps Script project using Typescript and some other helper dev stuff","archived":false,"fork":false,"pushed_at":"2023-09-29T03:06:58.000Z","size":2547,"stargazers_count":27,"open_issues_count":0,"forks_count":1,"subscribers_count":4,"default_branch":"main","last_synced_at":"2025-04-01T00:22:13.426Z","etag":null,"topics":["babel","clasp","eslint","gas","google","google-apps-script","husky","javascript","jest","lint-staged","nodejs","prettier","rollup","ts-jest","typescript"],"latest_commit_sha":null,"homepage":"","language":"TypeScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/cristobalgvera.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null}},"created_at":"2021-05-19T18:38:28.000Z","updated_at":"2024-12-31T17:14:37.000Z","dependencies_parsed_at":"2022-08-28T23:31:49.342Z","dependency_job_id":"6ef3517b-1f6e-48be-805b-f575da01f17e","html_url":"https://github.com/cristobalgvera/ez-clasp","commit_stats":null,"previous_names":[],"tags_count":0,"template":true,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/cristobalgvera%2Fez-clasp","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/cristobalgvera%2Fez-clasp/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/cristobalgvera%2Fez-clasp/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/cristobalgvera%2Fez-clasp/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/cristobalgvera","download_url":"https://codeload.github.com/cristobalgvera/ez-clasp/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":253675170,"owners_count":21945909,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["babel","clasp","eslint","gas","google","google-apps-script","husky","javascript","jest","lint-staged","nodejs","prettier","rollup","ts-jest","typescript"],"created_at":"2024-11-18T03:07:47.301Z","updated_at":"2025-05-12T04:31:03.606Z","avatar_url":"https://github.com/cristobalgvera.png","language":"TypeScript","funding_links":[],"categories":["Development","2. Development"],"sub_categories":["Starter Kits","2.1. Starter Kits"],"readme":"# 💻 EZ CLASP\n\nThis project works as a bootstrap to start a new [Google Apps Script](https://developers.google.com/apps-script)\nproject or attach it to an existing one.\n\nIt will give you, out of the box, a well defined develop flow for your GAS project.\n\nThis workflow will let you to:\n\n- Use the latest TypeScript features.\n- Use the latest EcmaScript features.\n- Easily unit test your project, even if your are using the Google-related\n  interfaces (SpreadsheetApp, DriveApp, DataStudioApp, etc.).\n- Maintain your code always formatted and linted in your workspace.\n- Manage your code using versioning tools.\n- Easily make usage of environment variables.\n- Treeshake your code to only deploy the one that is being used.\n\nWelcome to your new **EZ CLASP** life 🚀\n\n## ❓ How to\n\n### 🥇 First usage\n\n1. Use [this template](https://github.com/cristobalgvera/ez-clasp) by clicking\n   the **_\"Use this template\"_** button over repository files.\n\n   You can use `npx degit` way instead:\n\n   ```bash\n   npx degit cristobalgvera/ez-clasp YOUR_REPOSITORY_NAME\n   ```\n\n1. Using the new created repository URL, clone it following next steps\n\n   If you used the `template` way:\n\n   ```bash\n   git clone https://github.com/YOUR_USER_NAME/YOUR_REPOSITORY_NAME.git\n   cd YOUR_REPOSITORY_NAME\n\n   npm install\n   npm run clasp:login # And access to your Google account\n   ```\n\n   If you used `npx degit` way:\n\n   ```bash\n   cd YOUR_REPOSITORY_NAME\n\n   git init\n   npm install\n   npm run clasp:login # And access to your Google account\n   ```\n\n1. **If you DO NOT have an existing project**, run `npm run clasp:create`\n   to create a new project. CLASP CLI will prompt some project types, you\n   should select one of them.\n\n1. **If you have an existing project**, add your Apps Script ID inside\n   [`.clasp.json`](./.clasp.json) in the **_scriptId_** key.\n\n   ![Project configuration](assets/images/project-configuration.png)\n\n1. Create a `.env` file copying the [.env.example](./.env.example) file.\n\n   ```bash\n   cp .env.example .env\n   ```\n\n   Then your can make proper usage of environemnt variables in case you want.\n   If you don't, simply remove all environment variable related stuff.\n\n1. Push your project to Google Apps Script using\n\n   ```bash\n   npm run deploy\n   ```\n\n   The **first time** you execute this command, CLASP CLI will ask you to\n   overwrite manifest file [`appsscript.json`](./appsscript.json), insert `y`\n   key and press `enter`. This will let you to fully control your project\n   from your local environment.\n\n   The `appsscript.json` file contains configuration required by Google to\n   manage permissions used by your users related to your project.\n\n1. **Test the code opening your\n   [Google Apps Script projects dashboard](https://script.google.com/home/my)!**\n\n   There you can change your project name to the name you want (by default will\n   be called `CHANGE_MY_NAME`, in order to remember you to change it).\n\n### 🤔 How to push HTML or non TypeScript files?\n\nIf you need to push some other files that will not be included in transpilation\nprocess, you can put them into [`app`](./app) folder _(or whatever location you\nwant if you change [`.claspignore`](./.claspignore) configuration)_.\n\nYou can put your assets in there, e.g. some HTML or any JavaScript file you need\nto be pushed to Google Apps Script.\n\n⚠️ **Google Apps Script only allows you to push files with these extensions:\n`.html`, `.js`**. If you need files like `.css`, see Google Apps Script\n[HTML best practices page](https://developers.google.com/apps-script/guides/html/best-practices).\n\n### 🤔 How to use environment variables?\n\nGoogle provide us a way to handle environment variables using the\n[PropertiesService](https://developers.google.com/apps-script/reference/properties/properties-service?hl=es-419).\nThis way of handling environment variables can easily transform in a mess\nif you are working in a developers team. You can check this way in order to\nfollow Google's approach.\n\nThis project gives you a way to handle environment variables directly\ninside your local development using Rollup's plugins. You just need to create\na `.env` file located in the root directory of your project and then\nuse the secret values simply using the `process.env.YOUR_SECRET_KEY` approach.\nYou are free to create wrapper services to avoid repeating this pattern and\ngive plenty type safety to your environment variables.\n\nIn order to test the code that make usage of environment variables, you just need\nto add all your required environment variables to the [env.setup.js](./test/env.setup.js)\nfile. This will make your entire process of testing to use those variables.\n\nIf you want to have detailed control over environment variables, you will\nneed to control it from your tests directly, e.g. modifying the `process.env`\nobject to include an specific value. Take care about how to make this\nmocking process, remember the isolation of unit tests. Check\n[this article](https://razinj.dev/how-to-mock-process-env-in-jest/).\n\n### 🗂 How to add Google services, advanced Google services or external libraries?\n\nWhen you add a Google service _(Gmail, Google Sheets, etc.)_ which require some\nkind of permissions, e.g. permissions to read your email or write in a\nspreadsheet, you will need to add those specific permissions\n(a.k.a. OAuthScopes) in the file called [`appsscript.json`](./appsscript.json),\nin the `oauthScope` array as a string. The list of all OAuthScopes can be\nfound in [here](https://developers.google.com/identity/protocols/oauth2/scopes).\n\nSimilarly, when you need to use an advanced service, like Drive\n_(the old version)_, or a third party library, you will need to add those,\nusing the required format, in the [`appsscript.json`](./appsscript.json) file,\ninside the dependencies object, in one of the arrays. See the required structure\nin this [link](http://json.schemastore.org/appsscript)\n\n#### 🧪 How to test Google libraries\n\nGoogle libraries use namespaces as a resource to be imported, meaning there will\nno be imported through `import` syntax. This adds a complexity when testing it.\n\nIn order to easily tests those kind of libraries, you will have to mock the global\nobject, like so:\n\n```typescript\n// my-class.service.ts\n\n// In this example `GoogleService` is the service in use provided by Google\nexport class MyClassService {\n  someMethodThatUseAnyGoogleService(body: BodyType) {\n    const childGoogleService = GoogleService.anyMethod();\n\n    return childGoogleService.childMethod(body);\n  }\n}\n```\n\n```typescript\n// my-class.service.spec.ts\n\nimport { createMock } from '@golevelup/ts-jest';\nimport { MyClassService } from './my-class.service.ts';\n\ndescribe('MyClassService', () =\u003e {\n  let underTest: MyClassService;\n\n  beforeEach(() =\u003e {\n    underTest = new MyClassService();\n  });\n\n  describe('someMethodThatUseAnyGoogleService', () =\u003e {\n    const originalService = global.GoogleService;\n\n    let googleService: typeof GoogleService;\n    let childGoogleService: ReturnType\u003c(typeof googleService)['anyMethod']\u003e;\n\n    beforeEach(() =\u003e {\n      childGoogleService = createMock\u003ctypeof childGoogleService\u003e();\n      googleService = createMock\u003ctypeof googleService\u003e({\n        anyMethod: () =\u003e childGoogleService,\n      });\n\n      global.GoogleService = googleService;\n    });\n\n    afterEach(() =\u003e {\n      global.GoogleService = originalService;\n    });\n\n    it('should call ChildGoogleService with correct parameters', () =\u003e {\n      const expected = { any: 'parameter' };\n\n      const childGoogleServiceSpy = jest.spyOn(\n        childGoogleService,\n        'childMethod',\n      );\n\n      underTest.someMethodThatUseAnyGoogleService(expected as any);\n\n      expect(childGoogleServiceSpy).toHaveBeenCalledWith(expected);\n    });\n\n    it('should return the value', () =\u003e {\n      const expected = { any: 'parameter' };\n\n      jest\n        .spyOn(childGoogleService, 'childMethod')\n        .mockReturnValueOnce(expected);\n\n      const actual = underTest.someMethodThatUseAnyGoogleService({} as any);\n\n      expect(actual).toEqual(expected);\n    });\n  });\n});\n```\n\n## 🍕 Extras\n\n### 📚 Libs\n\nThis project contain these libraries that will help you to have a better\ndevelopment experience:\n\n- **TypeScript** _(Development)_.\n- **ESLint** + **Prettier** _(Format and linting)_.\n- **Babel** + **Rollup** _(Building)_.\n- **Husky** + **Lint-Staged** _(Commit)_.\n- **Jest** _(Testing)_.\n\n#### 🐾 Husky + Lint-Staged\n\nIn order to improve commitment flow when working with teams, template has a pre\nconfigured Husky implementation. The pre-commit flow is:\n\n- Fix **ESLint** issues if possible.\n- Fix **Prettier** issues if possible.\n- **Build** the code.\n\nAll tasks but build run only for the staged changes.\n\n### ❗ Ignored files\n\nIn case you want ignore certain files to be pushed, you can add them to the\n[`.claspignore`](./.claspignore) file. You can see in it some already ignored\nbase directories.\n\n_P.D.: Of course you can delete this **README** file and the [`assets`](./assets)\nfolder._\n\n## 💼 Example projects\n\nYou can found here some practical example usages of this template in order to\nhelp you to understand better how to link it to a Google Apps Script project:\n\n- **[Automatic FUP](https://github.com/cristobalgvera/automatic-fup)**\n  _(include connection to Firebase)_\n- **[Open Orders Update](https://github.com/cristobalgvera/open-orders-update)**\n- **[CMIC Credentials](https://github.com/cristobalgvera/cmic-credentials)**\n- **[Expeditures Projection](https://github.com/cristobalgvera/expeditures-projection)**\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fcristobalgvera%2Fez-clasp","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fcristobalgvera%2Fez-clasp","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fcristobalgvera%2Fez-clasp/lists"}