Building WebExtensions in Typescript

I spent yesterday evening doing something I haven’t done in a while: tinkering. You may have seen the news that there’s a big change coming in Firefox. The short version is that later this year, the old extension model is going to be retired permanently, and extensions using it will no longer work. As someone with an extension on, I’ve received more than a few emails warning me that they’re about to go dark. This isn’t the first time Mozilla has tried to entice folks to move on from XUL Overlays: Jetpack was a similar effort to allow extensions to play better within a sandbox. This time I think it’s going to stick: the performance benefits seems undeniable, and as a developer the prospect of writing a single extension to support multiple browsers is pretty appealing.

Over a year ago I took a stab at porting OpenAttribute to Browser (Web)Extensions. I read the Firefox code and basically understood it, but only because it was the third iteration of something I’d built. The Chrome code — which should be close to a proper WebExtension — was almost inscrutable to me. So naturally I wanted to start with tests. But a year ago I couldn’t quite make the connection for some reason. WebExtensions split your code between the page (content scripts) and the background process. Long running things belong in the background, and the two communicate via message passing. After reading about the coming XUL-pocalypse, I decided to take another run at it.

Last night, though, I focused on something far smaller: just understanding how to put together a WebExtension using the technologies I’m familiar with — react, redux — and the ones I’m interested in — TypeScript. The result is an extension that doesn’t do much, but it is written in TypeScript, and it does work in both Firefox and Chrome from a single code base.

The attribution extensions I’ve written have always had a data flow problem. There’s the question of what triggers parsing, where the extracted data is stored, and how you update the display. Not to mention how do you do that without slowing down overall browser performance. I’ve had good luck with React in other projects: it feels like it forces me to think of things more functionally, making it easier to write tests: does this component do the right thing with the right data? does this other thing send the right signals with the right input? Cool. But how to do that across the process boundary between background and content scripts?

webext-redux is a package that makes it easy to manage a Redux store in both processes and keep it in sync. The only real wrinkle is that the actions you fire on the content side have to be mapped to actions on the background process, which is where the mutations all take place.

So why TypeScript? I’ve been enjoying ES6 and the changes it brings to JavaScript. But I’ve still missed the types you get in Go with MyPy. TypeScript is interesting: it’s duck typed, but the ducks seem to quack louder than they do in Python.

I was particularly intrigued by ambient modules, which is how TypeScript provides type information for third party JavaScript libraries you may want to integrate. Luckily type definitions already exit for the web extension API, and it’s easy to write a (useless) one to quell the compiler warnings.

I think the biggest shift I’ve been trying to make is understanding imports. import * as actions from './actions' feels weird to write, and to be honest I’m not sure how it differs from import actions from './actions' when there’s not a default export.

I like TypeScript enough to try another experiment in the future. The compiler already pointed out a couple of errors that would have been hard to track down.

Up next: figuring out how to test web extensions and build a single code base that runs under Chrome, Firefox, Edge, and Opera.

Remembering with org-mode and Ubiquity

Yesterday evening I published my second set of Ubiquity commands which provide a Ubiquity interface between Firefox and Emacs — specifically org-mode — using org-protocol. Ubiquity is an experimental extension from Mozilla Labs that lets you interact with the browser by giving it short, plain text commands. For example, “share” to post a bookmark to Delicious, or “map” to open a map of the selected address.

Org-Mode is an Emacs mode that can be used to keep track of notes, agendas and task lists. I use it to maintain my task list for various projects and take notes when I’m in a meeting. I really like that while it’s an outline editor at heart, it lets me write lots of text and go back later and figure out what’s actually actionable, as opposed to maintaining separate notes and task lists. org-protocol is included in recent releases and lets you launch an instance of emacsclient with some additional information (i.e., the URL and title of a web page, etc) and take some action on it. One of the built in “protocols” is sending that information to remember mode, which org-mode augments.

The main command is simply remember. Invoking it will send the current URL and document title to org-mode’s Remember buffer. You can optionally type a note or select text in the page to be captured along with the link.

Once you’re in the buffer you can make any changes needed and then simply C-c C-c to save the note, or C-1 C-c C-c to interactively file the note someplace else. I’m using this command to quickly store links with some notes to project files. I hope this will be particularly useful when I run across something for a project I’m not actually able to spend time on at the moment.

Note that before using the commands you need to configure Firefox to understand org-protocol:// links, and need to configure a remember template. The template I use looks like:

(?w "* %?\n\n  Source: %u, %c\n\n  %i" nil "Notes")

This store the information in the Notes section of my org-default-notes-file and positions the cursor ready to type a heading.

To install, visit the command page and click “Subscribe”in the upper right hand corner when prompted (this assumes you have Ubiquity already installed). You can find the Javascript source on gitorious; I’ll be adding my RDFa commands to that repository as well.

date:2009-10-07 12:52:04
category:development, geek
tags:emacs, firefox, mozilla, orgmode, ubiquity