Skip to content

Commit db5c7da

Browse files
authored
💥 Merge pull request #4 from chriskyfung/chore/modernize-project-setup
Chore: Modernize Project Setup and Development Experience
2 parents 641c8f4 + 1b645de commit db5c7da

File tree

15 files changed

+7561
-1359
lines changed

15 files changed

+7561
-1359
lines changed

.gitignore

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -191,8 +191,10 @@ $RECYCLE.BIN/
191191

192192
# Custom rules (everything added below won't be overriden by 'Generate .gitignore File' if you use 'Update' option)
193193

194+
/dist
194195
.clasp.json
195196
.clasp.json.bk
196197
appsscript.json
197198
node_modules
198199
private.[gj]s
200+
IMPROVEMENTS.md

.prettierrc

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
{
2+
"semi": true,
3+
"singleQuote": true,
4+
"trailingComma": "es5"
5+
}

CHANGELOG.md

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
# Changelog
2+
3+
All notable changes to this project will be documented in this file.
4+
5+
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
6+
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7+
8+
## [0.2.0] - 2025-08-20
9+
10+
### Added
11+
- ✨ feat: add `mode` option to process emails as plain text or HTML
12+
- ✨ feat: implement testing and refactor core logic
13+
- 🔧 chore: Add build process for examples with exclusion
14+
- 🔧 chore(deps): add and configure ESLint and Prettier
15+
- 💚 ci(github): create dependabot.yml
16+
- Create FUNDING.yml
17+
18+
### Fixed
19+
- 🔒️ fix: sanitize `<script>` tags from HTML using regex in `findMessages`
20+
- 🐛 fix: handle undefined mode in `findMessages`
21+
- 🐛 fix(examples): update queries for `removeAffiliatesOne` and `removeMoneyHeroInfo`
22+
- 🐛 fix(eslint): resolve all linting errors
23+
24+
### Changed
25+
- 💡 refactor(examples): simplify query for `removeNamecheapAffiliateInfo`
26+
- build(deps-dev): Bump @types/google-apps-script from 1.0.78 to 1.0.99
27+
- build(deps-dev): Bump @google/clasp from 2.4.2 to 2.5.0
28+
- build(deps-dev): Bump braces from 3.0.2 to 3.0.3
29+
30+
### Documentation
31+
- 📝 docs: update `README.md` with `mode` option
32+
- 📝 docs: update development guidelines
33+
- 📝 docs: update documentation and project files
34+
35+
## [0.1.0] - 2024-03-12
36+
37+
### Added
38+
- ✨ feat: add example for GitHub Dependabot alerts
39+
- 🎉 Initial commit
40+
41+
### Changed
42+
- 💡 refactor: comment regex patterns in `examples.js`
43+
44+
### Documentation
45+
- 📄 docs: create code of conduct
46+
- 📝 docs: add author's website URL to `package.json`

GEMINI.md

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
# Gemini Project Instructions: gmail-regex-cleaner-apps-script
2+
3+
This document provides instructions for Gemini on how to work with this project.
4+
5+
## Project Overview
6+
7+
This is a Google Apps Script project for cleaning old emails in Gmail based on regular expressions.
8+
9+
- **Source Code**: The main logic is in `src/code.js`. Usage examples are in `src/examples.js`.
10+
- **Management**: The project is managed using `@google/clasp`. See `package.json` for commands.
11+
- **Language**: The project is written in JavaScript.
12+
13+
## Development Guidelines
14+
15+
1. **Caution with Deletion Logic**: The script can permanently delete emails. Any changes to the core logic in `src/code.js` must be tested carefully. When running tests or new example functions, always default to `isDryRun = true`.
16+
2. **Testing**: This project uses [Jest](https://jestjs.io/) for automated testing. Run `npm test` to execute the test suite.
17+
3. **Code Style**: The project is configured with ESLint and Prettier for code quality and consistent formatting. Run `npm run lint` to check for any linting issues. Please adhere to the existing code style.
18+
4. **TypeScript Migration**: The project is a good candidate for migration to TypeScript for improved type safety. If significant changes are made, consider proposing this migration.
19+
5. **Commit Messages**: Use the GitMoji convention for commit messages (e.g., `✨ feat: ...`, `🐛 fix: ...`).

README.md

Lines changed: 100 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -12,48 +12,127 @@ This is a Google Apps Script project that helps you delete old emails in Gmail t
1212

1313
## How to use
1414

15-
1. Create a new Google Apps Script project in Google Drive
16-
2. Copy and paste the code from `src/code.js` into the script editor
17-
3. Edit the variables at the top of the script to suit your needs
18-
4. Create a new void function to set the variables and call the `main()` function in the code.js file. For example:
15+
1. Create a new Google Apps Script project in Google Drive.
16+
2. Copy and paste the code from `src/code.js` and `src/examples.js` into the script editor.
17+
3. From the `examples.js` file, choose a function that matches your needs, or create a new one. You can then run this function from the Apps Script editor.
18+
19+
For example, to run one of the pre-made functions, you would select it in the editor's function list and click **Run**.
20+
21+
The `main` function, which does the core work, has been updated to be more flexible. Here is how you would call it inside a custom function:
1922

2023
```js
21-
function removeOldGoogleAlert() {
24+
function removeOldGoogleAlerts() {
2225
const query = 'from:(Google Alerts <googlealerts-noreply@google.com>)';
23-
const regex = /[0-9]{1,2} (Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec)/m
24-
const dryRun = true;
25-
const dateFormatter = (textWithDate, lastMessageDate) => {
26-
/*
27-
You code to extract and format the date part to a date string that can be parsed by the Date constructor
28-
*/
29-
}
30-
main(query, regex, dryRun, dateFormatter);
26+
const regex = /[0-9]{1,2} (Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec)/m;
27+
28+
// Call main with an options object for configuration
29+
main(query, regex, {
30+
tresholdInDays: 90, // Optional: default is 60
31+
isDryRun: true, // Optional: default is true
32+
mode: 'html', // Optional: default is 'plain'
33+
dateFormatter: (textWithDate, lastMessageDate) => {
34+
/*
35+
Your code to extract and format the date part to a date string
36+
that can be parsed by the Date constructor.
37+
*/
38+
// This is just an example, a real implementation is needed here.
39+
return new Date().toISOString();
40+
}
41+
});
3142
}
3243
```
3344

3445
> [!NOTE]
35-
> `query`: A string that specifies the search criteria for the emails you want to delete. It should follow the same syntax as the Gmail search box. For example, `from:(Google Alerts <googlealerts-noreply@google.com>)` will match all emails from Google Alerts. You can find more examples and tips on how to use the Gmail search operators [here](https://developers.google.com/codelabs/apps-script-fundamentals-1).
46+
> **`query`**: A string that specifies the search criteria for the emails you want to delete. It should follow the same syntax as the Gmail search box. For example, `from:(Google Alerts <googlealerts-noreply@google.com>)` will match all emails from Google Alerts. You can find more examples and tips on how to use the Gmail search operators [here](https://developers.google.com/codelabs/apps-script-fundamentals-1).
3647
>
37-
> `regex`: A regular expression that matches the date part of the email subject or body. It should be enclosed in slashes and follow the JavaScript regex syntax. For example, `/[0-9]{1,2} (Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec)/m` will match a date like “8 Feb” or “23 Nov” in the email.
48+
> **`regex`**: A regular expression that matches the date part of the email subject or body. It should be enclosed in slashes and follow the JavaScript regex syntax. For example, `/[0-9]{1,2} (Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec)/m` will match a date like “8 Feb” or “23 Nov” in the email.
3849
>
39-
> `dryRun`: Optional. A boolean value that indicates whether to run the script in test mode or not. If set to `true`, the script will only log the emails that match the query and the regex, but will not delete them. If set to `false`, the script will delete the emails permanently. It is recommended to run the script with dryRun set to true first to make sure it works as expected.
50+
> The third parameter to `main` is now an **options object** with the following optional properties:
4051
>
41-
> `dateFormatter`: Optional. A function that takes two parameters: `textWithDate` and `lastMessageDate`. The `textWithDate` is a string that contains the date part extracted from the email body. The `lastMessageDate` is a date object that represents the latest date of the email thread. The function should return a date string like `yyyy-MM-dd`. For example, if the `textWithDate` is “8 Feb” and the `lastMessageDate` is “2021-02-10”, the function should return a date object for “2021-02-08”.
52+
> - **`tresholdInDays`**: The number of days to keep emails. Defaults to `60`.
53+
> - **`isDryRun`**: A boolean value that indicates whether to run the script in test mode or not. If `true` (the default), the script will only log the emails that match the query and the regex, but will not delete them. If `false`, the script will delete the emails permanently. It is recommended to run the script with `isDryRun` set to `true` first to make sure it works as expected.
54+
> - **`mode`**: A string that specifies whether to process the email body as plain text or HTML. Can be either `'plain'` (default) or `'html'`. If set to `'html'`, the script will strip all HTML tags from the email body before searching for the regex pattern.
55+
> - **`dateFormatter`**: A function that takes two parameters: `textWithDate` and `lastMessageDate`. The `textWithDate` is a string that contains the date part extracted from the email body. The `lastMessageDate` is a date object that represents the latest date of the email thread. The function should return a date string like `yyyy-MM-dd` that can be parsed by `new Date()`.
4256
43-
5. Save and run the script. You can use the Run menu or the Run button in the toolbar.
57+
4. Save and run the desired function from the script editor. You can use the **Run** menu or the **Run** button in the toolbar.
4458

4559
> [!IMPORTANT]
4660
> When running the script for the first time, you may need to authorize it to access your Gmail account.
4761
48-
6. Optionally, set up a trigger to run the script periodically.You can do this by clicking the Triggers icon in the left sidebar, then clicking the Add a trigger button, and choosing the options you want. For example, you can set the script to run every day, week, or month.
62+
5. Optionally, set up a trigger to run a function periodically. You can do this by clicking the **Triggers** icon in the left sidebar, then clicking the **Add a trigger** button, and choosing the options you want. For example, you can set the script to run every day, week, or month.
4963

5064
> [!WARNING]
51-
> This script will delete your emails permanently, without moving them to the trash. Please use it with caution and make sure you have a backup of your important emails. You can run the script with the dryRun option set to true first to see what emails will be deleted.
65+
> This script will delete your emails permanently, without moving them to the trash. Please use it with caution and make sure you have a backup of your important emails. You can run the script with the `isDryRun` option set to `true` first to see what emails will be deleted.
66+
67+
## Development
68+
69+
This project uses [ESLint](https://eslint.org/) for code linting and [Prettier](https://prettier.io/) for code formatting. It is recommended to run the linter before committing any changes.
70+
71+
To run the linter, use the following command:
72+
73+
```bash
74+
npm run lint
75+
```
76+
77+
This project uses [Jest](https://jestjs.io/) for automated testing.
78+
79+
To run the tests, use the following command:
80+
81+
```bash
82+
npm test
83+
```
84+
85+
### Build Process
86+
87+
This project uses [Rollup](https://rollupjs.org/) to bundle the source files from `src/` into the `dist/` directory, which is ready for deployment with `@google/clasp`.
88+
89+
To build the project, run:
90+
91+
```bash
92+
npm run build
93+
```
94+
95+
This command bundles `src/code.js` and processes `src/examples.js` into the `dist/` directory. It also removes a Node.js-specific code block from `code.js` that is used for testing with Jest.
96+
97+
#### Excluding Example Functions
98+
99+
You can prevent specific functions from `src/examples.js` from being included in the final `dist/examples.js` file. This is useful if you only need a subset of the example functions for your deployment.
100+
101+
To exclude functions, add their names to the `config.examples.exclude` array in your `package.json` file:
102+
103+
```json
104+
{
105+
"name": "gmail-regex-cleaner-apps-script",
106+
"version": "1.0.0",
107+
// ...
108+
"config": {
109+
"examples": {
110+
"exclude": [
111+
"removeAffiliatesOne",
112+
"removeCorelAffiliateInfo"
113+
]
114+
}
115+
},
116+
// ...
117+
}
118+
```
119+
120+
When you run `npm run build`, the functions listed in the `exclude` array will be omitted from the output.
121+
122+
#### Timezone Update
123+
124+
Optionally, you can update the timezone in the `dist/appsscript.json` manifest to match your local machine's timezone. This is useful to ensure that scheduled triggers run at the correct time.
125+
126+
To update the timezone, run the following command **after** the build command:
127+
128+
```bash
129+
npm run update-timezone
130+
```
52131

53132
## Disclaimer
54133

55134
This script is provided as is, without any warranty or liability. Use it at your own risk. Make sure to test the script before using it on your Gmail account. The script may delete emails that you want to keep, or fail to delete emails that you want to remove. The script may also exceed the quota limits of Google Apps Script or Gmail API, resulting in errors or partial execution. The author is not responsible for any loss or damage caused by the use of this script.
56135

57136
## License
58137

59-
This project is distributed under the AGPL-3.0 license. You can use, modify, and distribute this project, as long as you comply with the terms and conditions in the [LICENSE](/LICENSE) file.
138+
This project is distributed under the AGPL-3.0 license. You can use, modify, and distribute this project, as long as you comply with the terms and conditions in the [LICENSE](/LICENSE) file.

eslint.config.mjs

Lines changed: 99 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,99 @@
1+
import globals from 'globals';
2+
import js from '@eslint/js';
3+
import gasPlugin from 'eslint-plugin-googleappsscript';
4+
import pluginPrettier from 'eslint-plugin-prettier';
5+
import prettierConfig from 'eslint-config-prettier';
6+
import jest from 'eslint-plugin-jest';
7+
8+
export default [
9+
// Global ignores
10+
{
11+
ignores: ['node_modules/', 'dist/', '.clasp.json', '*.gasrc.js'],
12+
},
13+
14+
// Base JavaScript configuration
15+
js.configs.recommended,
16+
17+
// Google Apps Script configuration for src/code.js
18+
{
19+
files: ['src/code.js'],
20+
plugins: {
21+
gas: gasPlugin,
22+
},
23+
languageOptions: {
24+
ecmaVersion: 2021,
25+
sourceType: 'script',
26+
globals: {
27+
...globals.googleapps,
28+
...gasPlugin.environments.googleappsscript.globals,
29+
module: 'writable', // For CommonJS compatibility in tests
30+
},
31+
},
32+
rules: {
33+
'no-unused-vars': 'warn',
34+
'no-var': 'error',
35+
'prefer-const': 'error',
36+
},
37+
},
38+
39+
// Google Apps Script configuration for src/examples.js
40+
{
41+
files: ['src/examples.js'],
42+
plugins: {
43+
gas: gasPlugin,
44+
},
45+
languageOptions: {
46+
ecmaVersion: 2021,
47+
sourceType: 'script',
48+
globals: {
49+
...globals.googleapps,
50+
...gasPlugin.environments.googleappsscript.globals,
51+
main: 'readonly', // main is defined in code.js
52+
},
53+
},
54+
rules: {
55+
'no-unused-vars': [
56+
'warn',
57+
{ args: 'none', varsIgnorePattern: '^remove' },
58+
],
59+
'no-var': 'error',
60+
'prefer-const': 'error',
61+
},
62+
},
63+
64+
// Jest test configuration
65+
{
66+
files: ['src/**/*.test.js'],
67+
...jest.configs['flat/recommended'],
68+
languageOptions: {
69+
globals: {
70+
...globals.jest,
71+
},
72+
},
73+
rules: {
74+
...jest.configs['flat/recommended'].rules,
75+
'jest/prefer-expect-assertions': 'off',
76+
},
77+
},
78+
79+
// Node.js specific configuration (for test files)
80+
{
81+
files: ['src/**/*.test.js'],
82+
languageOptions: {
83+
globals: {
84+
...globals.node,
85+
},
86+
},
87+
},
88+
89+
// Prettier configuration (must be last)
90+
prettierConfig,
91+
{
92+
plugins: {
93+
prettier: pluginPrettier,
94+
},
95+
rules: {
96+
'prettier/prettier': 'error',
97+
},
98+
},
99+
];

jest.config.js

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
module.exports = {
2+
testEnvironment: 'node',
3+
setupFiles: ['gas-mock-globals'],
4+
};

0 commit comments

Comments
 (0)