Skip to content

Instantly share code, notes, and snippets.

@Yoric
Forked from jonco3/migration.md
Last active July 13, 2018 23:22
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save Yoric/d271066aa4a4484602e817412957a1ff to your computer and use it in GitHub Desktop.
Save Yoric/d271066aa4a4484602e817412957a1ff to your computer and use it in GitHub Desktop.
Migrating from JSM to ES6 modules

This document shows a possible migration path from Cu.import() to ES modules for the code of Firefox.

Objectives

Use standard JavaScript, to simplify the use of third-party tools (e.g. static analysis, code rewriting, smart IDEs) and onboarding of new developers.

Requirements

  1. Preserve current JSM semantics where multiple imports of a single module from different globals results in a single module instance that lives in a separate global to the importer.
  2. Add-ons and Thunderbird should not be affected by the migration.
  3. Memory usage should not be adversly affected.

1. Allow Cu.import() to load ES6 modules

Objectives: At the end of this stage, any new JS module added to the codebase of Firefox will be an ES6 module. However, existing modules are unaffected.

ES6 modules will need their own file extension. As both .jsm and .js are already used by some JSM, let's use .mod.js, which is both unambiguous and IDE-friendly.

  1. (c/o @jonco) Implement an internal API to synchronously load an ES module into a specified global.
  2. (c/o who?) Modify Cu.import() to use the above to load an ES module. Assume that a file is an ES module iff its extension is .mod.js.
  3. (c/o @Yoric) Inform developers that they should now use ES6 modules instead of jsm for their new modules. ES6 modules MUST have extension .mod.js but MAY use Cu.import() to import jsm code.
  4. (c/o everyone) Reject patches that introduce new jsm modules.
  5. (c/o @Yoric) Optionally, patch mozReview to do this automatically.

2. Migrate simple JSM modules to ES6 modules

  1. (c/o @Yoric) Start porting JSM modules to "use strict".
  2. (c/o @Yoric) Develop a migration script that will rewrite "use strict" JSM modules (foo.jsm) to an ES6 module (foo.mod.js) and a shim (foo.jsm) for backwards compatibility. This script should work when there is no conditional exports. At this stage, the script needs to be called one-module-at-a-time to make it easier to find regressions and handle dependents that require a backstage pass. The script will rewrite EXPORTED_SYMBOLS/this.EXPORTED_SYMBOLS into export.
  3. (c/o everyone) Start rejecting patches to ES6 modules that use Cu.import('foo.jsm') instead of import 'foo.mod.js' when the latter is available. Optionally, make this part of mozReview.
  4. (c/o everyone) Start migrating uses of Cu.import to import wherever possible. Optionally, make mozReview detect instances automatically.
  5. (c/o everyone) Start rewriting modules with conditional exports to make them compatible with the migration script.
  6. (c/o everyone) Start rewriting stuff that use the backstage passes to make them compatible with the migration script.

3. Remaining cases

At this stage, we will still have the following cases to contend with:

  1. conditional imports;
  2. scoped imports and imports from within a function;
  3. uses of XPCOM.defineLazyModuleGetter;
  4. backstage passes.

We probably want to wait until there is a programmatic way of loading ES modules added to the spec and then implement that.

5. Static analysis

TBD.

@ExE-Boss
Copy link

ExE-Boss commented Jul 13, 2018

I believe that the plan can be simplified now that legacy extensions are no longer supported.

Simplified plan:

  1. Allow the import keyword (and dynamic import(…)) to import legacy jsms, which would have a single default export containing the exported globals at the time they finished evaluating (like what Node.js does)
  2. (optional) Allow the dynamic import(…) to be used in legacy jsms to import ES6 modules (depends on bug 1342012)
  3. Build a directional graph of dependencies and do conversion starting from the leaf nodes until we reach the root.
    • Once a node would have all its child leafs converted, it could now be converted to an ES6 module and the child leafs would now be able to use named imports.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment