Most projects similar to Extension.js rely on some sort of abstraction or configuration to get started, making the initial development process slow given the extra learning curve and setup guidelines. By using Extension.js, adding the package to your npm scripts is all it takes to get started developing cross-browser extensions with no build configuration. Say goodbye to extensive configurations to create your next cross-browser extension!
Creating a new extension is super easy. This command will create a new extension named "my-extension" in the current working directory. In your terminal:
npx extension@latest create my-extension
You can also create an extension based on any extension hosted on GitHub. Just add the URL of the folder where the manifest is located and run `npx extension@latest dev <github_url>`. For instance, you can try the Chrome Sample "page-redder" (https://github.com/GoogleChrome/chrome-extensions-samples/tr...).
I first created this project as a way to teach others how to develop browser extensions, until I realized that a good amount of my teachings would involve setting up a new project. With Extension.js, the abstractions and configurations needed to create cross-browser extensions are handled by a simple command-line interface, allowing developers to focus on the actual development of their next extension.
Any feedback is appreciated. I've been using it for a while in personal projects but it is now mature enough for others to give it a go. I'm looking forward to hear what you all have to say! :D
You can run your Mozilla Add-On on Chrome or Edge by adding a --polyfill flag, but for now you need to manually add the extension to Firefox. I do plan to support Firefox in the near future, but no browser runner available at the moment.
To me, 'WebExt' represents a specific specification within the broader category of browser extensions. However, since all major browsers now support this specification (and this only), it has become the de facto standard.
I hope someone more familiar with this topic can provide a more precise explanation
[1] https://developer.mozilla.org/en-US/docs/Glossary/WebExtensi...
[2] https://developer.mozilla.org/en-US/docs/Mozilla/Add-ons/Web...
Basically, until ~8 years ago, there were only three browsers of note with browser extension APIs (Chrome, Firefox, and Safari); and they were all proprietary and all different, so developers had to code each extension from scratch for each browser. And developers of major extensions mostly did.
But then Chromium-based browsers like Brave and Opera and Edge became a thing, and they all inherited de-facto support for what they at the time called "Chromium-style extensions." With this, the Chrome extension API effectively "won" the browser-extension-API developer-mindshare war without really meaning to. Developers became less and less interested in porting their extensions, instead opting to just focus on Chromium-based browsers, since not only was Chrome the majority of the market, but Chromium-based browsers comprised a large fraction of the remainder.
Rather than Mozilla and Apple "solving" this by coming up with some artificial new eleventh-hour browser-extension API standard to foist on Google through the WHATWG (that would have required every dev rewriting all their code), Mozilla and Apple both did the pragmatic thing, and "embraced and extended" Chrome's own extensions API, locking in "whatever Chrome was doing at the time" as now being a conventional cross-browser API. That API, going forward, was referred to in the Firefox and Safari docs — and eventually the Chrome docs as well — as the "WebExtensions API" (mostly so that Mozilla and Apple didn't have to say the words "Chrome" or "Chromium" anywhere in their docs.)
A browser saying that it supports the "WebExtensions API", means that it exposes certain browser-extension APIs in JS under the chrome.* namespace, with Chromium-compatible semantics. Yes, Firefox and Safari both present themselves as Chrome to browser extensions, up to and including answering to the name "chrome" rather than "browser" in JS. Wild, isn't it?
Note, however, that these browsers do still also expose their original, incompatible extension APIs under the browser.* namespace. And if you write your JS carefully, you can attempt to make use of these per-browser features in your extension, while gracefully degrading to a WebExtensions baseline.
The main issue I've run into is that I have no idea how to hook into and modify the behavior of fancy modern web sites with all of their React and Angular and Snorfleflox. I was kind of hoping this was for that. Is there some sort of framework that makes that stuff easier, or failing that, a really good tutorial for an experienced but a little out of date web developer to get up to speed?
https://developer.mozilla.org/en-US/docs/Web/API/MutationObs...
You can hack on the behavior via userscripts, my preferred way to alter websites, though I write extensions too (greasemoney is good tho)
Firefox Support Issue: https://github.com/cezaraugusto/extension.js/issues/5
1: https://github.com/dy/spect 2: https://github.com/kubetail-org/sentineljs
web-ext is excellent, but it seems there are no plans for the project to support more browsers than it currently does. On the other hand, Extension.js plans to support all major vendors.
Except for Firefox support, which is in progress, I believe Extension.js offers parity with all core functionalities of web-ext, but it goes further by providing built-in support for React and TypeScript. All you need to do is add the correct dependencies to get up and running.
Additionally, Extension.js provides comprehensive extension reload support, including changes to the manifest.json file and the service_worker background. This feature sets it apart from similar tools, including web-ext, which do not offer this capability.
The biggest difference, in my opinion, is that Plasmo is a framework, which means you have to learn its abstractions and rely on specific samples tailored for these abstractions to create new extensions. There are config files and specific rules to follow that are not necessarily related to browser extensions.
On the other hand, Extension.js allows developers to create extensions using the standard extension APIs and abstracts only the configuration files, without the need to learn the tooling specifics. This way, a sample from Chrome or MDN that works with a manifest file as the source of truth requires no refactoring to work with Extension.js, making it easier to get started and prototype new projects.
npx extension create my-react-extension --template=react
But I guess it doesn't make it any easier to modify the behavior of fancy modern web sites :_(
Every time i make a chrome extension i get massive imposter syndrome. It is so hard to create a new project for some reason. This would be awesome.
I've written extensions (L I N K S I N B I O) which significantly modify Twitter.com (which uses React Native for Web) and YouTube (which uses web components), which are both SPAs - the main tips I'd give are:
1. Which page am I on?
In an MPA extension or userscript, you just check the current URL and go. In an SPA, you could go as far as patching the pushState() method on History (although you can't do this from an extension's content script - you would need to inject a page script) and watching for popstate events to detect URL changes, but I've found using a MutationObserver [1] to observe the contents of <title> to be a simpler proxy for the current page changing.
When the <title> changes, check the current URL to see if it's one you've already handled, and if not, start making your modifications. This also gives you a natural place to put any teardown logic, such as disconnecting active observers and cancelling any async actions which may be in progress for the previous page, such as waiting for elements to appear.
The target app may even have custom events you can hook into. YouTube has these, for example, but I found they were being fired at the same time the <title> was being changed whenever the user was navigating to a different page, so I stuck with the implementation-independent approach.
2. When are the main elements I care about available?
You'll need some utility functions which allow you to wait for specific elements to be available in the current page, either as an indicator the page is ready to modify, or because you need to do something with their contents.
This could be as un-smart as writing a function which returns a Promise wrapping document.querySelector() calls at regular intervals, or uses a MutationObserver to wait for a specific element to appear, then resolves with it, but there should be lots of existing open source utilities like these available out there (some have already been linked to in this thread).
I ended up writing my own version of these so I could specifically control stopping waiting for an element based on a timeout and/or other conditions, e.g. immediately resolving the Promise with `null` if the current URL has changed at the next available interval.
3. When do the elements I care about change?
Use the MutationObserver API [1] to watch for child elements being added/removed, or for specific attributes being changed.
It's kind of clunky, so you may want to write your own convenience wrapper - this is also a natural place to facilitate storing active observers for a later teardown. I've found I usually end up with at least a pageObservers collection, which makes it easy to disconnect everything which is currently being observed when the page changes.
e.g. I use a MutationObserver on the element which contains Twitter timeline contents to watch for the current window of visible tweets being changed so I can do things like hide quotes of specific tweets or hide Retweets from the Following timeline.
4. Hiding things in an SPA-friendly way
Removing elements which UI libraries such as React expect to be under their control when some state changes at a later time can lead to errors, so I've found it best to use CSS where possible to hide things, and even adding your own class names to use as styling hooks. It can also just be more convenient than writing code to manually remove things from the DOM.
e.g. Twitter uses React Native for Web's styling system, which takes CSS-like style objects and generates utility-like style rules from them (it's like Tailwind in reverse) - this means there aren't any developer-friendly class names to use as styling hooks, so on some pages I add my own. If I open my own user profile, <body> will have 'Profile' and 'OwnProfile' classes on it, which I can use to hide the "Articles" and "Highlights" tabs, which are there purely as Premium upsells.
[1] https://developer.mozilla.org/en-US/docs/Web/API/MutationObs...
Occasionally it might even be faster, since you're not iterating through the mutation records and testing for stuff you care about.
I found it useful to create a `waitForElement(query)` helper function that takes a CSS selector path and returns a Promise that resolves when the query starts finding a match in the DOM. (Internally, it's just populating a table that is iterated over in the MutationObserver callback.)
As for SPA "navigation", I had trouble with popstate events. I hadn't considered your <title> trick; I'll bet that would have worked for me! Right now, I'm polling window.location.href every half second, which sucks.
This could also be because this is my first browser extension development. https://chromewebstore.google.com/detail/bettermenu-for-door...
There is still lot more work to do, so I will checkout your project. Thanks for sharing!
What is it with "I made a _______" posts? Those seem new to me and very braggy. I see them on youtube as well. I feel like if I could check all the posts to HN I'd find a trend of instead of just "Show HN: A tool/app/site to do X" there's a tread of adding "I made" in front. Is that a result of social media conditioning, that you must brag that "I" made?
> Show HN is for something you've made that other people can play with.
At most, it's redundant, but not in a way that hurts anyone.
I don't really use the framework stuff but HMR that works really well is just chefs kiss.
I'm trying to understand what your tool does, above and beyond copying a few files. Is it cross-browser support? multi-language support?
After copying a few files from the Chrome extension documentation site, you'll need to manually enable "developer mode" in your browser and add these files. If you make changes to your code, seeing the updates live requires manually reloading the various contexts that a browser extension can influence. Additionally, if you want to test your extensions on multiple browsers, you must do this manually for each one.
If you plan to use package dependencies like TypeScript or a JavaScript framework, you'll either have to rely on an existing abstraction framework for extensions and learn new techniques, or create your own configuration using tools like webpack, Parcel, esbuild, or another code compiler. At this point, developing browser extensions can become complex and frustrating.
Extension.js simplifies this by automating the process of bundling an extension in a browser ready for development. It comes with auto-reload support for every context, including parts of the browser that are not HTML/CSS/JavaScript. It also has built-in support for code dependencies such as TypeScript and React, where a simple installation enables your extension to have support without any setup wizardry. And it runs on multiple browsers at once.
Hope that clarifies things, and I'm happy to answer more questions :)
- Safari https://github.com/cezaraugusto/extension.js/issues/46 - Firefox https://github.com/cezaraugusto/extension.js/issues/5
2023 416
2022 307
2021 232
2020 252
2019 158
2018 87
2017 42
2016 45
2015 39
2014 42
2013 68
2012 40
2011 28
2010 10
That's just "Show HN: I made" and doesn't include all the similar "I built ..." and similar braggy titles.For example, Firefox does not currently support the `optional_host_permissions` top level manifest key. To work around this, developers can declare their optional host permissions in both the `optional_host_permissions` array for Chrome compatibility and in the `optional_permissions` array for Firefox/Safari compatibility.
Another example is that currently only Chrome supports `background.service_worker` in stable releases. To work around this, developers can write their MV3 background scripts in a way that's compatible with both service workers and event pages, then declare both in the manifest like so:
```json { "background": { "scripts": ["background.js"], "service_worker: "background.js" } } ```
if it's something I'm interested in. The difference between "ShowHN: I built a thing", and "ShowHN: thing" seems inconsequential to me.
But you know that one about how naming things is an unsolved problem in computer science? Looking at random search results, I think it's more about it being a more natural way to describe something. "Show HN: I built a thing that does blah", rather than "Show HN: Foobar, a thing that does blah".
If I do "Show hn: George", what's George? vs "Show HN: I made a thing that does foo called George"
Interestingly enough, there are only 805 results for "Show HN: We" vs 5,060 results for "Show HN: I", What a bunch of loners we are.
I see "X, an app that does Y" as promoting X and sayuhg "reader, you might find X useful if you want to do Y"
The purpose of the posts are entirely different.
Your observation on "We" is interesting though I suspect that teams are more likely to write "X, and app that does Y" because "We made X" doesn't achieve the self promotion goals in the same way as "I made X"
“Extension is a plug-and-play, zero-config, cross-browser extension development tool for browser extensions with built-in support for TypeScript, WebAssembly, React, and modern JavaScript.”
“WebExtensions are a way to write browser extensions: that is, programs installed inside a web browser that modify the behavior of the browser or web pages loaded by the browser. WebExtensions are built on a set of cross-browser APIs, so WebExtensions written for Google Chrome, Opera, or Edge will, in most cases, run in Firefox too.”
> WebExtensions are a way to write browser extensions
Even if “technically correct”, the tool still specifically creates WebExtensions, it does not create “non-WebExtension browser extensions”
Human psychology is fascinating.