Git Product home page Git Product logo

browser-extension-template's Introduction

browser-extension-template

Cross-browser extension boilerplate - barebones template with Parcel 2, options handler and auto-publishing.

Screenshot of extension options:

Sample extension options output

Features

Getting started

1️⃣ Create your own copy

  1. Click Use this template to make a copy of your own. πŸ˜‰

Note: When you create a repository from the template, the Template Cleanup workflow will be triggered to delete and edit template-specific resources. Wait a moment until the workflow finishes (you will see a commit pushed with 'Template cleanup' message).

πŸ›  Build locally

  1. Checkout the copied repository to your local machine eg. with git clone https://github.com/my-username/my-awesome-extension/
  2. Run npm install to install all required dependencies
  3. Run npm run build

The build step will create the distribution folder, this folder will contain the generated extension.

πŸƒ Run the extension

Using web-ext is recommended for automatic reloading and running in a dedicated browser instance. Alternatively you can load the extension manually (see below).

  1. Run npm run watch to watch for file changes and build continuously
  2. Run npm install --global web-ext (only only for the first time)
  3. In another terminal, run web-ext run -t chromium
  4. Check that the extension is loaded by opening the extension options (in Firefox or in Chrome).

Manually

You can also load the extension manually in Chrome or Firefox.

✏️ Make the first change

  1. For example, edit source\manifest.json to "name": "My Awesome Extension",
  2. Go back to your browser, reload and see the change take effect

Note: Firefox will automatically reload content scripts when the extension is updated, Chrome requires you to reload the page to reload the content scripts.

πŸ“• Read the documentation

Here are some websites you should refer to:

Configuration

The extension doesn't target any specific ECMAScript environment or provide any transpiling by default. The extensions output will be the same ECMAScript you write. This allows us to always target the latest browser version, which is a good practice you should be following.

Parcel 2

Being based on Parcel 2 and its WebExtension transformer, you get all the good parts:

  • Browserlist-based code transpiling (which defaults to just the latest Chrome and Firefox versions)
  • Automatically picks up any new file specified in manifest.json

Auto-syncing options

Options are managed by fregante/webext-options-sync, which auto-saves and auto-restores the options form, applies defaults and runs migrations.

Publishing

It's possible to automatically publish to both the Chrome Web Store and Mozilla Addons at once by adding these secrets on GitHub Actions:

  1. CLIENT_ID, CLIENT_SECRET, and REFRESH_TOKEN from Google APIs.
  2. WEB_EXT_API_KEY, and WEB_EXT_API_SECRET from AMO.

Also include EXTENSION_ID in the secrets (how to find it) and add Mozilla’s gecko.id to manifest.json.

The GitHub Actions workflow will:

  1. Build the extension
  2. Create a version number based on the current UTC date time, like 19.6.16 and sets it in the manifest.json
  3. Deploy it to both stores

Auto-publishing

Thanks to the included GitHub Action Workflows, if you set up those secrets in the repo's Settings, the deployment will automatically happen:

  • on a schedule, by default every week (but only if there are any new commits in the last tag)
  • manually, by clicking "Run workflow" in the Actions tab.

Credits

Extension icon made by Freepik from www.flaticon.com is licensed by CC 3.0 BY.

Extensions created using this template

License

This browser extension template is released under CC0 and mentioned below. There is no license file included in here, but when you clone this template, you should include your own license file for the specific license you choose to use.

CC0

browser-extension-template's People

Contributors

austince avatar filips123 avatar foosantos avatar fregante avatar humphd avatar irgendwr avatar janmechtel avatar jetersen avatar maskys avatar nathanclevenger avatar notlmn avatar spookyuser avatar tiqwab avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar

browser-extension-template's Issues

Icon greyed out and no functionality

I cloned the repo, made no modifications and loaded the extenstion into my browser, by loading it from the /distribution folder manually.
However the icon is greyed out and no default functionality can be seen (like the console output or rgb panel)

Change repository description to reflect webpack -> parcel change

Currently, the repository description found in the About section on the right sidebar still reads:

πŸ“• Barebones boilerplate with webpack, options handler and auto-publishing

I suggest changing this to reflect that the template uses parcel now. Thanks!

Unsafe construction of an any type value.

I'm getting this linting error in options-storage.ts. I want to use typescript and I just changed the files extensions to .ts like indicated. I also added @sindresorhus/tsconfig and eslint-config-xo-typescript. It's strange because if I hover over the error I get the type information. And if I assign the result of the call to new OptionsSync({...}) to a variable and I hover over it I get the type information:

const options: OptionsSync<{
    colorRed: number;
    colorGreen: number;
    colorBlue: number;
    text: string;
}>

I don't know why it keeps saying that it is of type any

Source maps don't work by default

If I add console.log('foo'); to my content.ts, I see:

image

but clicking on the content.js:159 reveals a transpiled file rather than the original content.js. This is presumably related to the warning immediately below it, which states that the content.js.map source map could not be loaded. But why not? What's the cause of the ERR_BLOCKED_BY_CLIENT, and can we fix it in the extension config?

I trawled the internet for answers, but it's full of useless suggestions, such as disabling source maps or removing the browser extension which causes the error. Belatedly I found https://parceljs.org/recipes/web-extension/#hmr which says

Source maps don't work unless inlined in web extensions

It doesn't explain why they don't work unless inlined, so I'm guessing it's a fundamental browser thing, e.g. a security restriction on extensions.

But anyway, I added this to package.json:

    "targets": {
        "webext-dev": {
            "sourceMap": {
                "inline": true,
                "inlineSources": true
            }
        },
        "webext-prod": {}
    },

and also modified the build and watch scripts to include --target webext-prod and --target webext-dev respectively.

However this didn't seem to work until I eventually noticed that this actually changes the location of the generated content.js from distribution/ to distribution/webext-dev, which means that the web-ext run command needs an extra option -s distribution/webext-dev. And indeed distribution/webext-dev/content.js does have a source map inlined at the end of the file, and this not only gets rid of the ERR_BLOCKED_BY_CLIENT message, but gives correct mappings back to the true original source files.

If the above enhancement makes sense to you, I would propose including it so that source maps work by default. It works for Typescript too.

BTW, even after all that, the results do not seem perfect. For example, here's a stack trace from my extension:

image

You can see that there are still references to content.js as well as the references to .ts files. But perhaps that's because there are extra stack frames generated by the transpilation whose source code simply doesn't exist in the original Typescript. So I guess I can't complain about those too much.

Deployment cronjob creates tag but it doesn't trigger `deploy-tag`

There is a limitation of workflow: An action in a workflow run can't trigger a new workflow run.

I would suggest you use your own PAT when creating tags. You can store your PAT in secrets and use ${{ secrets.PATNAME } in your actions.

env:
  GITHUB_TOKEN: ${{ secrets.PATNAME }}

From: https://github.community/t5/GitHub-Actions/Github-actions-workflow-not-triggering-with-tag-push/m-p/39772#M3959

You can see this in Refined GitHub’s Actions. "Auto-tagger" ran but "Deployment" didn't follow it. Now I had to re-create the tag

rgh

I think this only needs to be updated with instructions to use a custom personal token instead of the provided one.

Parcel building files outside of those defined in manifest.json

It would be great to be able to config parcel to also compile other files in the "/source"-dir such as other used .html/.js files

image

In this example would be redirect.html and redirect.js but they are not compiled to /distribution, only the main files (options.../background...)

erroneous issue, please delete sorry

Rather than defaulting to <all_urls>, which may cause the extension to get rejected by the Chrome Web Store anyway, if possible we should default to the usual block explorers etc. while ensuring that the user can also apply it to any other site they want.

Add ability to manually trigger deployment

GitHub recently add the ability to manually trigger a workflow. This should allow us to have an action handle this trigger in deployment action. This is similar to what Travis calls "Trigger Build" (that we used before to handle on-demand deployments). This feature request would allow us to do the same with GitHub actions.

Read more about this on https://github.blog/changelog/2020-07-06-github-actions-manual-triggers-with-workflow_dispatch/

Loosely related to request made at RGH: refined-github/refined-github#3113

Browser scripts cannot have imports or exports. Use a <script type="module"> instead

Dear Maintainer(s),

I've got the following error trying to build the extension:

$ npm run build

> build
> parcel build source/manifest.json --no-content-hash --no-source-maps --dist-dir distribution --no-cache --detailed-report 0

🚨 Build failed.
@parcel/transformer-js: Browser scripts cannot have imports or exports. Use a <script type="module"> instead.
/Volumes/Dev/ProBrain/BrowserExtensionTemplate/source/options.js:4:1
  3 | 
> 4 | import optionsStorage from './options-storage.js';
>   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  5 | 
  6 | optionsStorage.syncForm('#options-form');
@parcel/transformer-js: The environment was originally created here:
/Volumes/Dev/ProBrain/BrowserExtensionTemplate/source/options.html:31:1
  30 | 
> 31 | <script src="options.js"></script>
>    | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  32 | 

I thought I probably doing something wrong and made a GitHub clone but clicking "Use this template" button as README.md recommends, but got the same issue.

What am I doing wrong?

Linting errors after converting to TypeScript

If I follow the instructions for converting to TypeScript (including using @sindresorhus/tsconfig), npm run lint results in a load of errors:

  source/content.ts:4:8
  βœ–   4:8   Unsafe assignment of an any value.                                                                                                                                 @typescript-eslint/no-unsafe-assignment
  βœ–   4:24  Unsafe call of an any typed value.                                                                                                                                 @typescript-eslint/no-unsafe-call
  βœ–   5:16  Operands of + operation with any is possible only with string, number, bigint or any                                                                               @typescript-eslint/restrict-plus-operands
  βœ–   5:16  Operands of + operation with any is possible only with string, number, bigint or any                                                                               @typescript-eslint/restrict-plus-operands
  βœ–   5:16  Operands of + operation with any is possible only with string, number, bigint or any                                                                               @typescript-eslint/restrict-plus-operands
  βœ–   6:8   Unsafe assignment of an any value.                                                                                                                                 @typescript-eslint/no-unsafe-assignment
  βœ–   8:2   Unsafe assignment of an any value.                                                                                                                                 @typescript-eslint/no-unsafe-assignment
  βœ–  15:1   Promises must be awaited, end with a call to .catch, end with a call to .then with a rejection handler or be explicitly marked as ignored with the void operator.  @typescript-eslint/no-floating-promises

  source/options.ts:2:8
  βœ–   2:8   browser is defined but never used.                                                                                                                                 @typescript-eslint/no-unused-vars
  βœ–   6:1   Unsafe call of an any typed value.                                                                                                                                 @typescript-eslint/no-unsafe-call
  βœ–  13:40  Invalid type "any" of template literal expression.                                                                                                                 @typescript-eslint/restrict-template-expressions
  βœ–  13:65  Invalid type "any" of template literal expression.                                                                                                                 @typescript-eslint/restrict-template-expressions
  βœ–  13:90  Invalid type "any" of template literal expression.                                                                                                                 @typescript-eslint/restrict-template-expressions
  βœ–  17:2   Unsafe assignment of an any value.                                                                                                                                 @typescript-eslint/no-unsafe-assignment

  source/options-storage.ts:undefined:undefined
  βœ–   0:0   Parsing error: File @sindresorhus/tsconfig/tsconfig.json not found.                                                                                                

  15 errors
ERROR: "lint:js" exited with 1.

Some of these are trivial to fix, but I'm struggling to figure out how to address @typescript-eslint/no-unsafe-assignment and eslint/no-unsafe-call other than disabling those rules. BTW I notice those are similar to the title of issue #69.

One very odd error is the last one (Parsing error: File @sindresorhus/tsconfig/tsconfig.json not found). I have no idea what's going on here. Even weirder - I thought I'd accidentally fixed it by changing xo.envs in package.json from browser to webextensions (which seems to be a more logical choice anyway, right?) ... But then when I changed it back to browser, the parsing error didn't come back. πŸ€·β€β™‚οΈ

Anyway, you should be able to reproduce the exact list of errors above via this branch in my fork:

main...aspiers:typescript

Chrome Web Store rejection for obfuscated code

Hey! Thanks for the sweet boilerplate! Its been working really well so far.

Just letting you know that I was rejected from CWS using this boilerplate yesterday for obfuscated code.

Hi there,

We regret to inform you that the most recent submission of your item was rejected. Please find the details below.

Item name: Snippets

Item ID: eoeooehafnjjbkbnlbgfimnjchmadbap

Violation(s):

Code Readability Requirements

Violation reference ID: Red Titanium
Violation: Having obfuscated code in the package.
Violating content:
Code snippet: options.c1ff580f.js:null:null: function $56fca9218aa06700$var$isCurrentPathname(path) {...
How to rectify: Replace the obfuscated code with human-readable code and resubmit the item.
Relevant section of the program policy:
Developers must not obfuscate code or conceal functionality of their extension. (learn more)

I had seen this issue you submitted parcel-bundler/parcel#6092

I didnt have any luck applying your suggestions. However, Im pending another review with --no-scope-hoist which seems to have removed the hexi from all the functions. Ill see if that works and report back...

Filing an issue incase others run into the same thing from CWS.

Build improvements

Following #44

  • Don't minify the CSS
  • include webext-base-css via @import 'webext-base-css';
  • Flat output parcel-bundler/parcel#5858
  • Somehow extract webext-options-sync to its own "vendor" entry so it's not bundled 3 times separately parcel-bundler/parcel#5859
  • Automatically include the browser global

The last one is feasible via:

https://github.com/pixiebrix/webext-messenger/blob/35961df965d1a75da941187ddcb67acdaeb82aec/test/demo-extension/manifest.json#L7

And

https://github.com/pixiebrix/webext-messenger/blob/35961df965d1a75da941187ddcb67acdaeb82aec/.parcelrc#L4-L9

Include "popup" in template

Seems many (most?) browser extensions include a "popup" i.e. a small panel that appears when the icon is clicked:

image

I don't see mention of this browsing the code of this repo. Have you considered providing this by default or making it an option?

In most extensions I've browsed the code of I see popup.css and popup.html (example).

Document the package.json scripts

Hello,

Would it be a good idea providing a brief description + example of each script in the package.json file. For example in my case, I'm trying to load the unpacked extension in chrome (after doing npm run watch) but doesn't seem to be working ( Could not load background script 'browser-polyfill.min.js'. Could not load manifest.). Maybe it's not the right way of doing it πŸ€·β€β™‚οΈ

Thank you in advance.

Lint css error, fetch tags error, duplicated addon once published

Hi there, I had few bumps using the Actions. I manage to get around them by commenting stuff, but I though I should mention.

image

image

Also, when the addon was published to the Firefox developer hub, it got duplicated, because I had previously uploaded it manually. I imagine there is some instruction missing related to the geck.id?

Disabling HMR in parcel breaks automatic reloading for web-ext with Google Chrome

Same issue as #68, where simply reloading the page doesn't reflect any changes. Removing --no-hmr flag from manifest.json fixes the issue - though I'm not sure why.

Steps to reproduce

Clone repo
npm install, npm run watch
Run web-ext run -t chromium (with Google Chrome as default browser)
Make a change anywhere - the console log content.js or the "Set a text!" string in source/options-storage.js
Reload the page
The text/log will not change
Only doing Ctrl-C and restarting web-ext will produce an update

Environment details

Using commit 28339fce620f5e75ef6ee323e72cba3d90c7a745
MacOS Monterey 12.3 (21E230)
Chrome Version 126.0.6478.127 (Official Build) (x86_64)

"@parcel/config-webextension": "^2.12.1-canary.3290",

npx parcel -V
2.0.0-canary.1665+b4fcf7098

importing Image not working from content script

Don't know if it's an issue in the configuration or in parcel, but doing this doesn't work:

// source/react/OnBoarding/OnBoarding.jsx
import logo from "../../images/logo.png"

export default function OnBoarding() {
  return <img src={logo} className="logo-2" alt="Apollo Logo 2" />
}

it results in this:

image

however this does work:

const logo = browser.runtime.getURL("../../logo.f18a6316.png")

image

interestingly, this does work from an html page of the extension which I open upon installation of the extension:

// background.js
import browser from "webextension-polyfill"

browser.runtime.onInstalled.addListener(({ reason }) => {
	if (reason === browser.runtime.OnInstalledReason.INSTALL) {
		browser.tabs.create({
			url: browser.runtime.getURL("on-install/index.html"),
		})
	}
})

// source/on-install/App/App.jsx
import logo from "../../images/logo.png"

export default function OnBoardingApp() {
  return <img src={logo} />
}

image

my manifest:

{
	"name": "Apollo",
	"version": "0.0.0",
	"description": "An awesome new browser extension",
	"homepage_url": "https://github.com/fregante/browser-extension-template",
	"manifest_version": 2,
	"minimum_chrome_version": "80",
	"applications": {
		"gecko": {
			"id": "[email protected]",
			"strict_min_version": "80.0"
		}
	},
	"icons": {
		"128": "icon.png"
	},
	"permissions": [
		"https://github.com/fregante/browser-extension-template/*",
		"tabs",
		"storage"
	],
	"content_scripts": [
		{
			"matches": [
				"<all_urls>",
				"https://*.zendesk.com/agent*",
				"https://*.zendesk.com/admin*"
			],
			"js": ["content.js"],
			"css": ["content.css"],
			"run_at": "document_end"
		}
	],
	"options_ui": {
		"chrome_style": true,
		"page": "options/options.html"
	},
	"background": {
		"persistent": false,
		"scripts": ["background.js"]
	},
	"web_accessible_resources": [
		"on-install/index.html",
		"logo.*.png",
		"images/logo.png",
		"images/logo.*.png",
		"images/*",
		"images/*.png"
	]
}

any ideas what I'm missing here?

Thanks for the awesome project!! πŸ’œ

Issue with using import in content scripts

The default template is throwing this error SyntaxError: import declarations may only appear at top level of a module when attempting to access a page that should trigger the content.js script. Upon searching a bit, I found that the issue was a limitation of the files that can be included through content_scripts due to needing the type="module" attribute needing to be set on the script https://stackoverflow.com/questions/48104433/how-to-import-es6-modules-in-content-script-for-chrome-extension. Is there a version of this template that has this working properly? Does a transpolar need to be configured in order to maintain syntax when dealing with content?

Release workflow requires undocumented write access to repository

Hello!

Thank you so much for this project, it has been a boon while developing my first web extension. ❀️

While setting up a new repository from the template, I've noticed that the "Release" workflow needs write permissions to the repository for git push, but its need is not documented anywhere. Specifically, the following setting needs toggleing:

image

The default is: "Read repository contents and packages permissions"

Enable travis on this repo

Travis should be enabled so the linting is applied to this template, to ensure that the code works.

You could even set up the extension and publish it "privately" (unlisted) on the stores, to ensure that everything works.

Nested folders in /src

Hey, many thanks for the template, it's a very nice learning resource :)

What we are struggling with is having folders inside the /src directory, e.g. having a /src/options folder for all the options-related files. Is this possible?

Add installation instructions for Safari on macOS 11

Related to refined-github/refined-github#14.

Instructions for converting browser extension on Apple developer documentation mentions that anyone can convert extension and basically sideload it into Safari.

I do not yet know how Apple intends to publish their extensions (maybe through App Store). Also I do not have an Apple developer account, even if I did, I'm sure that Apple would not want this extension on their store, even for testing.

Opening this issue to explore options on automating publishing (to App Store?), document any additional setup instructions on doing the same.

/cc @fregante.

Firefox support discussion (MV2, MV3)

Firefox doesn't yet support Manifest v3 background workers (update March 2023)_ but this build works exclusively on that. You can use this issue to discuss alternative methods of supporting both Firefox and Chrome.

Refer to these links for official MV3 support in Firefox:

How to have parcel include onboarding/* into the build

How can I get the files in the folder onboarding/ to be included in the build?

Background

I'm trying to build a an onboarding according this to:
https://extensionworkshop.com/documentation/develop/onboard-upboard-offboard-users/#onboarding

For that I've included this in the background.ts:

const url = browser.runtime.getURL("onboarding/onboarding.html");
await browser.tabs.create({ url });

This works fine if the file is found. But for some reason, parcel build/ parcel watch are not including the onboarding.html file into the distribution/ folder automatically.

I've tried finding a solution in parcel, but the only way I found is a copy. So for now I've worked around that by copy the files manually before the watch:

"copy-onboarding": "xcopy source\\onboarding\\ distribution\\onboarding\\ /y",
"watch": "npm run copy-onboarding && parcel watch source/manifest.json --dist-dir distribution --no-cache --no-hmr"

However that leads to other problems, because now I can't add a onboarding.ts file as it won't be build to onboarding.js and have it's dependencies bundled.

Do you have an idea how to get parcel to include the files?

Unrecognized manifest key 'applications'.

Hello, I've found this template github repository through the MDN Web Docs :)

When I tried generating a project and viewing it under "Load Unpacked" under the Google Chrome extensions, I see the following error

image

`watch` script error after initial install

I cloned the repo, ran npm i, then npm run watch, and it exits with this error:

CopyPlugin Invalid Options

options.0 should be string
options.0.ignore should be array
options.0 should match some schema in anyOf

It's not clear what it's validating or what the steps to resolve this would be.

Move to Parcel

change project description from webpack to parcel

I guess the title is trivial,
πŸ“• Barebones boilerplate with webpack, options handler, and auto-publishing
to
πŸ“• Barebones boilerplate with parcel, options handler, and auto-publishing
and I posted a question in here I would be glad if sb read that and tell me can this repo help me out with that or not.

Content scripts injected via chrome.scripting API

It looks like @parcel/config-webextension does not cover chrome.scripting API.

What if I need to inject scripts and styles dynamically like in the code below?

chrome.scripting
    .executeScript({
      target : {tabId},
      files : [ "checker/index.js" ],
    })

chrome.scripting
    .insertCSS({
      target : {tabId},
      files : [ "checker/index.css" ],
    })

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    πŸ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. πŸ“ŠπŸ“ˆπŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❀️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.