├── .github └── workflows │ └── node.js.yml ├── .gitignore ├── README.md ├── manifest.json ├── package.json ├── renovate.json └── src ├── extension.js ├── extensionInjector.js └── gmailJsLoader.js /.github/workflows/node.js.yml: -------------------------------------------------------------------------------- 1 | # This workflow will do a clean install of node dependencies, build the source code and run tests across different versions of node 2 | # For more information see: https://help.github.com/actions/language-and-framework-guides/using-nodejs-with-github-actions 3 | 4 | name: Node.js CI 5 | 6 | on: 7 | push: 8 | branches: [ master ] 9 | pull_request: 10 | branches: [ master ] 11 | 12 | jobs: 13 | build: 14 | 15 | runs-on: ubuntu-latest 16 | 17 | strategy: 18 | matrix: 19 | node-version: [10.x, 12.x, 14.x, 15.x] 20 | # See supported Node.js release schedule at https://nodejs.org/en/about/releases/ 21 | 22 | steps: 23 | - uses: actions/checkout@v4 24 | - name: Use Node.js ${{ matrix.node-version }} 25 | uses: actions/setup-node@v4 26 | with: 27 | node-version: ${{ matrix.node-version }} 28 | - run: npm install 29 | - run: npm update 30 | - run: npm run build 31 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | 2 | # Created by https://www.gitignore.io/api/emacs,vim,visualstudiocode,node 3 | 4 | ### Emacs ### 5 | # -*- mode: gitignore; -*- 6 | *~ 7 | \#*\# 8 | /.emacs.desktop 9 | /.emacs.desktop.lock 10 | *.elc 11 | auto-save-list 12 | tramp 13 | .\#* 14 | 15 | # Org-mode 16 | .org-id-locations 17 | *_archive 18 | 19 | # flymake-mode 20 | *_flymake.* 21 | 22 | # eshell files 23 | /eshell/history 24 | /eshell/lastdir 25 | 26 | # elpa packages 27 | /elpa/ 28 | 29 | # reftex files 30 | *.rel 31 | 32 | # AUCTeX auto folder 33 | /auto/ 34 | 35 | # cask packages 36 | .cask/ 37 | dist/ 38 | 39 | # Flycheck 40 | flycheck_*.el 41 | 42 | # server auth directory 43 | /server/ 44 | 45 | # projectiles files 46 | .projectile 47 | 48 | # directory configuration 49 | .dir-locals.el 50 | 51 | ### Node ### 52 | # Logs 53 | logs 54 | *.log 55 | npm-debug.log* 56 | 57 | # Runtime data 58 | pids 59 | *.pid 60 | *.seed 61 | *.pid.lock 62 | 63 | # Directory for instrumented libs generated by jscoverage/JSCover 64 | lib-cov 65 | 66 | # Coverage directory used by tools like istanbul 67 | coverage 68 | 69 | # nyc test coverage 70 | .nyc_output 71 | 72 | # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) 73 | .grunt 74 | 75 | # Bower dependency directory (https://bower.io/) 76 | bower_components 77 | 78 | # node-waf configuration 79 | .lock-wscript 80 | 81 | # Compiled binary addons (http://nodejs.org/api/addons.html) 82 | build/Release 83 | 84 | # Dependency directories 85 | node_modules 86 | jspm_packages 87 | 88 | # Optional npm cache directory 89 | .npm 90 | 91 | # Optional eslint cache 92 | .eslintcache 93 | 94 | # Optional REPL history 95 | .node_repl_history 96 | 97 | # Output of 'npm pack' 98 | *.tgz 99 | 100 | # Yarn Integrity file 101 | .yarn-integrity 102 | 103 | # dotenv environment variables file 104 | .env 105 | 106 | 107 | ### Vim ### 108 | # swap 109 | [._]*.s[a-v][a-z] 110 | [._]*.sw[a-p] 111 | [._]s[a-v][a-z] 112 | [._]sw[a-p] 113 | # session 114 | Session.vim 115 | # temporary 116 | .netrwhist 117 | # auto-generated tag files 118 | tags 119 | 120 | ### VisualStudioCode ### 121 | .vscode/* 122 | !.vscode/settings.json 123 | !.vscode/tasks.json 124 | !.vscode/launch.json 125 | !.vscode/extensions.json 126 | 127 | # End of https://www.gitignore.io/api/emacs,vim,visualstudiocode,node -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 2 | # GmailJS Node boilerplate 3 | 4 | ![Node.js CI](https://github.com/josteink/gmailjs-node-boilerplate/workflows/Node.js%20CI/badge.svg) 5 | 6 | This repo contains sample code to get a WebExtensions-based 7 | browser-extension using 8 | the [gmail.js](https://github.com/KartikTalwar/gmail.js/) library. 9 | 10 | ## Usage 11 | 12 | First get the code and build it: 13 | 14 | ```` 15 | # get code 16 | git clone https://github.com/josteink/gmailjs-node-boilerplate/ 17 | 18 | # get deps and build 19 | cd gmailjs-node-boilerplate 20 | npm install 21 | # ensure you're running latest version! 22 | npm update 23 | npm run build 24 | ```` 25 | 26 | Now ensure the code loads and works: 27 | 28 | * Load the folder containing the extension (`manifest.json`) in 29 | your browser. 30 | * Load `mail.google.com` in your browser and open the developer console. 31 | 32 | You should be greeted by a message saying the following 33 | 34 | Hello, . This is your extension talking! 35 | 36 | If that works, you should now be ready to customize the 37 | extension-code. Do this by editing `extension.js` and rerunning `npm run build`. 38 | 39 | You can also interact with a instance of Gmail.js ready for use. `gmail` 40 | should already be exposed in the developer console as a global 41 | variable. 42 | 43 | You can use this to get familiar with the API. 44 | 45 | Cheers! 46 | 47 | ## Disclaimer 48 | 49 | This project first and foremost about learning. It is **NOT** set up or configured as one would typically create a production WebExtension, and there are several things which can be changed or improved in how things are built and packaged. 50 | 51 | *This is an intentional choice.* 52 | 53 | The aim for this project is not to have a ready-to-use production setup with loads of complex dependencies and tool chain specific configurations. 54 | 55 | Instead this project aims to demonstrate the absolutely simplest way possible one can embed Gmail.js in one's own extension, without introducing any other needless or complicating factors. Thus making it easier to adapt into your own projects and toolchains you already know well. 56 | 57 | It's also meant as a simple way to test the gmail.js API and capabilities without having to setup anything of your own first. 58 | 59 | Lastly it's also a simple playground where bug-reports can easily be reproduced or verified. 60 | 61 | None of these goals aligns particularly well with having a pre-made, production-ready, size-optimized, security-hardened setup. And that's perfectly ok, especially for learning. 62 | 63 | ## Regarding WebExtension Manifest versions 64 | 65 | Gmail.JS and this boilerplate repo was originally developed when 66 | WebExtension [Manifest V2](https://developer.chrome.com/docs/extensions/mv2/manifest/) were 67 | in place. Thanks to the help of **@tomer-regev** this boilerplate repo now 68 | uses [Manifest V3](https://developer.chrome.com/docs/extensions/mv3/intro/mv3-overview/) 69 | instead. 70 | 71 | If you for whatever reason still want to use the V2 code, check out the code 72 | back at [ManifestV2-tag](https://github.com/josteink/gmailjs-node-boilerplate/tree/ManifestV2), which has been maintained for historical reasons. 73 | 74 | Either way, Gmail.JS itself should work fine under both versions with 75 | no change required in your extension-code, unless your extension 76 | itself has code which needs to be migrated. 77 | 78 | For more advice about upgrades, see the [Chrome documentation](https://developer.chrome.com/docs/extensions/mv3/intro/mv3-migration/). 79 | -------------------------------------------------------------------------------- /manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "manifest_version": 3, 3 | "name": "GmailJS Node Boilerplate Extension", 4 | "short_name": "gmailjsnode", 5 | "version": "1.1", 6 | "author": "Jostein Kjønigsen ", 7 | 8 | "description": "A minimal extension which demonstrates how to create a extension with Gmail.JS and NodeJS-based bundling.", 9 | 10 | "content_scripts": [ 11 | { 12 | "matches": [ "*://mail.google.com/*" ], 13 | "js": [ 14 | "src/extensionInjector.js" 15 | ], 16 | "run_at": "document_start" 17 | } 18 | ], 19 | 20 | "web_accessible_resources": [{ 21 | "resources": [ 22 | "dist/gmailJsLoader.js", 23 | "dist/extension.js", 24 | "dist/gmailJsLoader.js.map", 25 | "dist/extension.js.map" 26 | ], 27 | "matches": [""] 28 | }], 29 | "host_permissions": [ 30 | "https://*/*" 31 | ] 32 | } 33 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "gmailjs-node-boilerplate", 3 | "version": "1.1.0", 4 | "description": "", 5 | "main": "", 6 | "scripts": { 7 | "deps": "npm install", 8 | "stage": "mkdir dist || echo Dist already exists.", 9 | "bundle": "esbuild src/gmailJsLoader.js src/extension.js --outdir=dist/ --bundle --sourcemap --target=es6", 10 | "devbuild": "cp ../gmail.js/src/gmail.js node_modules/gmail-js/src/ && npm run build", 11 | "build": "npm run stage && npm run bundle", 12 | "dev": "npm run bundle -- --watch" 13 | }, 14 | "author": "Jostein kjønigsen", 15 | "license": "ISC", 16 | "dependencies": { 17 | "gmail-js": "^1.1.14", 18 | "jquery": "^4.0.0-beta" 19 | }, 20 | "devDependencies": { 21 | "esbuild": "^0.25.0" 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /renovate.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": [ 3 | "config:base" 4 | ] 5 | } 6 | -------------------------------------------------------------------------------- /src/extension.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | // loader-code: wait until gmailjs has finished loading, before triggering actual extensiode-code. 4 | const loaderId = setInterval(() => { 5 | if (!window._gmailjs) { 6 | return; 7 | } 8 | 9 | clearInterval(loaderId); 10 | startExtension(window._gmailjs); 11 | }, 100); 12 | 13 | // actual extension-code 14 | function startExtension(gmail) { 15 | console.log("Extension loading..."); 16 | window.gmail = gmail; 17 | 18 | gmail.observe.on("load", () => { 19 | const userEmail = gmail.get.user_email(); 20 | console.log("Hello, " + userEmail + ". This is your extension talking!"); 21 | 22 | gmail.observe.on("view_email", (domEmail) => { 23 | console.log("Looking at email:", domEmail); 24 | const emailData = gmail.new.get.email_data(domEmail); 25 | console.log("Email data:", emailData); 26 | }); 27 | 28 | gmail.observe.on("compose", (compose) => { 29 | console.log("New compose window is opened!", compose); 30 | }); 31 | }); 32 | } 33 | -------------------------------------------------------------------------------- /src/extensionInjector.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | function addScript(src) { 4 | const script = document.createElement("script"); 5 | script.type = "text/javascript"; 6 | script.src = chrome.runtime.getURL(src); 7 | (document.body || document.head || document.documentElement).appendChild(script); 8 | } 9 | 10 | addScript("dist/gmailJsLoader.js"); 11 | addScript("dist/extension.js"); 12 | -------------------------------------------------------------------------------- /src/gmailJsLoader.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | // gmail.js needs to be loaded as early as possible to be able to intercept 4 | // embedded email-data in the gmail HTML! 5 | // 6 | // To do that we: 7 | // - just load it 8 | // - make sure it's initialized 9 | // - and do nothing else! 10 | // 11 | // Let the "big" extension bundle load separately! 12 | 13 | const GmailFactory = require("gmail-js"); 14 | import $ from "jquery"; 15 | 16 | if ("trustedTypes" in window) { 17 | const trustedHTMLpolicy = trustedTypes.createPolicy("default", { 18 | createHTML: (to_escape) => to_escape, 19 | }); 20 | 21 | $.extend({ 22 | htmlPrefilter: trustedHTMLpolicy.createHTML // this is the actual function which jQuery needs 23 | }); 24 | } 25 | 26 | // don't mess up too bad if we have several gmail.js-based 27 | // extensions loaded at the same time! 28 | window._gmailjs = window._gmailjs || new GmailFactory.Gmail($); 29 | --------------------------------------------------------------------------------