[HN Gopher] Show HN: I made a CLI tool to create web extensions ...
       ___________________________________________________________________
        
       Show HN: I made a CLI tool to create web extensions with no build
       configuration
        
       Hello HN! I'm the creator and solo developer of Extension.js, a
       development tool for browser extensions with built-in support for
       TypeScript, WebAssembly, React, and modern JavaScript. Developers
       use it to spend less time configuring the compilation config or
       learning new frameworks and more time actually writing code.  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
        
       Author : cezaraugustodev
       Score  : 237 points
       Date   : 2024-04-30 13:11 UTC (9 hours ago)
        
 (HTM) web link (github.com)
 (TXT) w3m dump (github.com)
        
       | isodev wrote:
       | Looks interesting but I don't understand why Safari is excluded
       | on both desktop and mobile.
        
         | cezaraugustodev wrote:
         | Thanks! I do plan to support all major browsers in the near
         | future, including Safari :)
        
         | bpev wrote:
         | fwiw, in my experience, Safari is a much bigger annoyance to
         | release extensions on than all the other platforms, and has the
         | more differences is release process. So I'd expect this browser
         | to be supported last.
        
       | herrherrmann wrote:
       | Very cool stuff! I might check this out for my extension
       | (https://github.com/herrherrmann/omnivore-list-popup). So far,
       | I've been creating my own scripts to manage the extension builds,
       | dev mode etc. - but I've been eyeing web-ext already to make my
       | life easier. However, my main browser is Firefox, which doesn't
       | seem fully supported yet by Extension, right?
        
         | cezaraugustodev wrote:
         | Thanks a lot!
         | 
         | 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.
        
       | edwinjm wrote:
       | It's called a browser extension, why call it web extension?
        
         | thrdbndndn wrote:
         | It has often been called "WebExtensions" or "WebExt" for a long
         | time [1]. I wasn't even aware that it's just referred as
         | "browser extensions" on MDN [2] now (..and the URL still uses
         | "WebExtensions" and it's all over the actual article content.
         | So I don't know.)
         | 
         | 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...
        
         | jonathanlydall wrote:
         | 100%, I was also initially confused by the term web extension,
         | it's a browser extension.
        
         | cezaraugustodev wrote:
         | HN has a character limitation for the post title and changing
         | the word made the title fit just fine :)
        
         | PurpleRamen wrote:
         | Because this specific browser extensions are named
         | WebExtensions[1].
         | 
         | [1] https://www.w3.org/community/webextensions/
        
         | derefr wrote:
         | "WebExtensions API" is the genericized term coined by (IIRC)
         | Mozilla, for the (subset of the) originally-proprietary browser
         | extension API surface exposed by Chrome, that non-Chromium
         | browsers (Firefox, Safari) have since added support for in
         | order to be able to support easy porting of originally-Chrome-
         | targeted extensions.
         | 
         | 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.
        
       | blackhaj7 wrote:
       | This looks really helpful - thanks!
        
         | cezaraugustodev wrote:
         | Thanks for the kind words!
        
       | mvkel wrote:
       | This is extremely cool. Even the readme is beautiful!
       | 
       | I'm going to give this a try. I've been holding off on adding an
       | extension to my app because of the absolute cluster that is
       | Google Play.
        
         | cezaraugustodev wrote:
         | Thank you! I'm glad you liked the readme as well :D
        
       | mrozbarry wrote:
       | I totally understand why, but no firefox support is a show-
       | stopper for most extension developers.
        
         | cezaraugustodev wrote:
         | Sure thing! There will be support for Firefox very soon
        
         | thenbe wrote:
         | FWIW there's an existing issue for "firefox support" which you
         | can subscribe to in order to be notified when this feature
         | lands.
         | 
         | https://github.com/cezaraugusto/extension.js/issues/5
        
       | ViktorBash wrote:
       | Looks good! I'll give this a swing on my own extension Vim for
       | Docs. Firefox not being supported is a bummer, but it's not like
       | there is a build tool for extensions that does support both
       | Firefox and chromium based browsers.
        
         | cezaraugustodev wrote:
         | Thanks! Firefox support will come in the next update. Stay
         | tuned! :)
        
       | notum wrote:
       | Apologies for the confusion but what is "cross-browser" about
       | this tool if it only works with chromium?
        
         | cezaraugustodev wrote:
         | No worries :) but Chrome and Edge are indeed different
         | browsers. Support for Firefox and Safari is next.
        
           | notum wrote:
           | Not to take away from the simplicity of use, of course,
           | totally giving it a go. I think this is an awesome foot in
           | the door for people who never considered developing
           | extensions.
           | 
           | Firefox would be a MASSIVE win!
        
         | purple-leafy wrote:
         | Agreed. It would be more fair to call it chromium only. I don't
         | consider that cross browser as any chrome extensions defacto
         | works on any chromium browser
        
       | avtar wrote:
       | Minor feedback: please consider an option to exclude emojis from
       | the cli output. I can appreciate wanting to add some character
       | here and there, but the output of ``npx extension create my-
       | extension`` seems very noisy.
        
         | cezaraugustodev wrote:
         | That's great feedback, thank you. Will consider the removal.
        
       | rmdes wrote:
       | well... you just gave me a reason to rebuild my Bluesky extension
       | from scratch, without a dependency from the original extension I
       | used to get my base opengraph running. Bluesky OGraph Poster
        
         | cezaraugustodev wrote:
         | Haha glad to hear that
        
       | chadrs wrote:
       | I've been pretty happy with web-ext, I'm curious what
       | abstractions and configurations Extensions.js avoids
       | comparatively. I'm assuming you'll still need a manifest.json,
       | and it looks like both use npm/package.json for dependencies.
       | 
       | https://github.com/mozilla/web-ext
        
         | cezaraugustodev wrote:
         | Yes, the manifest.json is a required file for all extensions,
         | and the package.json file provides necessary package metadata.
         | 
         | 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.
        
       | _andrei_ wrote:
       | very cool, also check out https://www.plasmo.com/
        
       | outlore wrote:
       | this is cool! how would you contrast this with Plasmo, a similar
       | framework?
       | 
       | https://www.plasmo.com/
        
         | simple10 wrote:
         | Plasmo looks extremely useful. Have you used it to develop any
         | extensions?
        
           | spxneo wrote:
           | I use it and it does a lot of what I used to do manual
           | deployment
           | 
           | so I was wondering the same thing as GP, could this be a
           | self-hosted plasmo of sorts
        
         | cezaraugustodev wrote:
         | Great question!
         | 
         | 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.
        
       | masto wrote:
       | I've made a couple of (admittedly trivial) Chrome extensions to
       | tweak things on sites I use. I didn't really spend any time
       | configuring the compilation config (not sure what that is) or
       | frameworks. I'm guessing the reason for needing something like
       | this is for handling complicated dependencies and cross-platform
       | stuff?
       | 
       | 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?
        
         | grimgrin wrote:
         | The best luck I've had with SPA apps and a goal of manipulating
         | the DOM is by using mutation observers. Worth exploring if you
         | haven't yet
         | 
         | 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)
        
           | rsoto wrote:
           | IMO MutationObserver's API is a bit difficult to grasp. For
           | simpler cases for getting a callback when an element is
           | created, I use spect[1] or sentinel[2].
           | 
           | 1: https://github.com/dy/spect 2:
           | https://github.com/kubetail-org/sentineljs
        
             | sfink wrote:
             | You can wimp out and just use MutationObserver as a way to
             | get a callback whenever _anything_ changes, ignoring all of
             | the mutation records. Then in the callback, look up all of
             | the elements you care about. The API is pretty simple then.
             | It may be less efficient, but usually when you 're mucking
             | about with existing web pages, you're not all that
             | performance sensitive. (And MO batches up updates
             | reasonably well, so it's not like you're running the
             | callback once per change.)
             | 
             | Occasionally it might even be faster, since you're not
             | iterating through the mutation records and testing for
             | stuff you care about.
        
         | cezaraugustodev wrote:
         | Extension.js can help you creating a new extension using React
         | in no time:
         | 
         | 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 :_(
        
         | insin wrote:
         | > 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?
         | 
         | 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...
        
           | sfink wrote:
           | I haven't done a lot of userscript dev, but based on my
           | experience so far, all of this advice is spot-on.
           | 
           | 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.
        
       | simple10 wrote:
       | This is exactly what I need right now. Thanks for building and
       | sharing it! Looking forward to Firefox support. I hope you can
       | get it working.
       | 
       | Firefox Support Issue:
       | https://github.com/cezaraugusto/extension.js/issues/5
        
         | simple10 wrote:
         | It would be great if you could add more details to the Firefox
         | support issue in github. Maybe the community could help solve
         | it?
        
           | cezaraugustodev wrote:
           | The plugin for Firefox is on the way, but any community
           | support is highly appreciated.
        
         | Vinnl wrote:
         | In particular, navigating the conflicting requirements for
         | `manifest.json` would be useful, e.g. the difference between
         | event pages and background service workers, or permissions that
         | are inconsistently required/forbidden.
        
       | lagniappe wrote:
       | Wow this is cool, thank you :) I look forward to trying this out
       | on a little hobby extension I'm working on as well.
        
         | cezaraugustodev wrote:
         | I'm glad to hear!
        
       | from-nibly wrote:
       | Ive made a few extensions. One was silly and changed the imgur
       | heart color when they changed it and everyone was upset.
       | 
       | 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.
        
         | cezaraugustodev wrote:
         | Totally relate. Extension development should be fun, right?
         | Hope Extension.js can help you with your next project!
        
       | aaronharding wrote:
       | Okay I love it, I feel like you should also sprinkle your magic
       | on extension<>tab communication. Whenever I make an extension,
       | it's always such a pain to read from the DOM or send a message
       | from the extension to the active tab.
        
       ___________________________________________________________________
       (page generated 2024-04-30 23:00 UTC)