This document shows a possible migration path from Cu.import()
to ES modules for the code of Firefox.
Use standard JavaScript, to simplify the use of third-party tools (e.g. static analysis, code rewriting, smart IDEs) and onboarding of new developers.
- 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.
- Add-ons and Thunderbird should not be affected by the migration.
- Memory usage should not be adversly affected.
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.
- (c/o @jonco) Implement an internal API to synchronously load an ES module into a specified global.
- (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
. - (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 useCu.import()
to import jsm code. - (c/o everyone) Reject patches that introduce new jsm modules.
- (c/o @Yoric) Optionally, patch mozReview to do this automatically.
- (c/o @Yoric) Start porting JSM modules to
"use strict"
. - (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 rewriteEXPORTED_SYMBOLS
/this.EXPORTED_SYMBOLS
intoexport
. - (c/o everyone) Start rejecting patches to ES6 modules that use
Cu.import('foo.jsm')
instead ofimport 'foo.mod.js'
when the latter is available. Optionally, make this part of mozReview. - (c/o everyone) Start migrating uses of
Cu.import
toimport
wherever possible. Optionally, make mozReview detect instances automatically. - (c/o everyone) Start rewriting modules with conditional exports to make them compatible with the migration script.
- (c/o everyone) Start rewriting stuff that use the backstage passes to make them compatible with the migration script.
At this stage, we will still have the following cases to contend with:
- conditional imports;
- scoped imports and imports from within a function;
- uses of
XPCOM.defineLazyModuleGetter
; - 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.
TBD.
I believe that the plan can be simplified now that legacy extensions are no longer supported.
Simplified plan:
import
keyword (and dynamicimport(…)
) to import legacyjsm
s, which would have a singledefault
export containing the exported globals at the time they finished evaluating (like what Node.js does)import(…)
to be used in legacyjsm
s to import ES6 modules (depends on bug 1342012)