ESMification Prep 3: Dive into example migrations

Skip to first unread message

Tooru Fujisawa

Jul 12, 2022, 10:44:37 AMJul 12
to, Firefox Dev
Hi, everyone.

ESMification is a project to switch the Mozilla-specific JSMs in privileged code to standard ECMAScript modules (ESMs). 

On Thursday, July 14th, we will have a Kick-off meeting to start the project with a short demo of ESMification. We have a timeslot reserved for 30 minutes and it is open to the public. 

To get everyone ready and on the same page, we have written a series of three emails to help contextualize the transition, and this is the last one.

Today, I’d like to introduce a new semi-automated migration tool, and an overview of an example migration patch generated by it.

A new semi-automated migration tool

Bug 1776870 patch adds a new tool `./mach esmify`, that semi-automatically migrates JSM files into ESM files, and also rewrite their consumers to use new APIs to import them.

The word “semi-automated” is used here because there will be some edge cases that this tool doesn’t cover, and those parts need to be handled manually by each team during the migration.

Convert JSMs into ESMs

`./mach esmify --convert` takes one required argument, the path of JSM, or the directory where JSMs are stored.  The specified JSM, or all JSMs inside the directory are converted into ESMs.

$ ./mach esmify --convert PATH_TO_FILE_OR_DIRECTORY

Rewrite JSM consumers to use new API

`./mach esmify --imports` takes one required argument: the directory where imports will be rewritten, and rewrite all imports with new APIs.

$ ./mach esmify --imports PATH_TO_FILE_OR_DIRECTORY

It takes an optional `--prefix` parameter, that limits the rewritten imports to only those files under prefix. For example, if you converted both directory A and directory B to ESMs, but wish to only do import rewrites for directory B, then you would use `--prefix=B/`.

$ ./mach esmify --imports PATH_TO_FILE_OR_DIRECTORY --prefix=PREFIX

An example migration

Bug 1777488 patch migrates JSM files in browser/components/pagedata directory, and rewrites their consumers.

This patch is generated by the following commands.

$ ./mach esmify --convert browser/components/pagedata/
$ ./mach esmify --imports . --prefix=browser/components/pagedata/

Let’s look into changes in the patch.

Export symbols

`PageDataService.jsm` is renamed to `PageDataService.sys.mjs`,  and the code that exports symbols is rewritten in the following pattern:

-var EXPORTED_SYMBOLS = ["PageDataService"];
-const PageDataService = new (class PageDataService extends EventEmitter {
+export const PageDataService = new (class PageDataService extends EventEmitter {

`EXPORTED_SYMBOLS` variable is removed, and each declaration listed in `EXPORTED_SYMBOLS` is converted to an `export` declaration.

Also the rename is reflected in the `` file:

 EXTRA_JS_MODULES.pagedata += [
-    "PageDataSchema.jsm",
+    "PageDataSchema.sys.mjs",

Import system ESMs from system ESMs

`SchemaOrgPageData.jsm` is renamed to `SchemaOrgPageData.sys.mjs`, and an import for other system module is rewritten in the following pattern:

-const { PageDataSchema } = ChromeUtils.import(
+import { PageDataSchema } from "resource:///modules/pagedata/PageDataSchema.sys.mjs";

At the top-level of system ESM, static `import` declaration is used to import other system ESMs.

Import system ESMs from other context

`test_schemaorg_parse.js` imports system module that’s converted into an ESM, and the import call is rewritten in the following pattern:

-const { SchemaOrgPageData } = ChromeUtils.import(
+const { SchemaOrgPageData } = ChromeUtils.importESModule(

System ESM can be imported with a new API `ChromeUtils.importESModule`, that works almost the same as `ChromeUtils.import`.

Lazy getters

`PageDataChild.jsm` file defines lazy getter for a module converted into an ESM, and the lazy getter definition is rewritten in the following pattern:

 XPCOMUtils.defineLazyModuleGetters(lazy, {
+ChromeUtils.defineESModuleGetters(lazy, {
+  PageDataSchema: "resource:///modules/pagedata/PageDataSchema.sys.mjs",

Lazy getters can be defined with a new API `ChromeUtils.defineESModuleGetters`, that works almost the same as `XPCOMUtils.defineLazyModuleGetters`.

Window actors

`PageDataService.jsm`  defines a window actor using modules converted to ESMs, and the window actor definition is rewritten in the following pattern:

     ChromeUtils.registerWindowActor("PageData", {
       parent: {
-        moduleURI: "resource:///actors/PageDataParent.jsm",
+        esModuleURI: "resource:///actors/PageDataParent.sys.mjs",
       child: {
-        moduleURI: "resource:///actors/PageDataChild.jsm",
+        esModuleURI: "resource:///actors/PageDataChild.sys.mjs",

There are some more cases that’s covered by the tool, and some other edge cases that’s not covered. We’ll publish a detailed document for the migration shortly.

Thank you for reading.
If you have questions, feel free to ask in the #esmification matrix room!
Reply all
Reply to author
0 new messages