extensionSystem: Load extensions as modules

Continue the move to ESM by loading modules dynamically with
the standard import() expression, rather than by installing a
custom (legacy) importer.

This is a breaking change that affects all extensions, as they
now need to explicitly export the expected symbols.

As we are already breaking all extensions, take that opportunity
and remove support for the individual entry points: Using a
class with enable()/disable() methods has been the recommended
pattern for a long time, it is now the only entry point.

Instead of instantiating the class from an `init()` function,
the class must now be exported as default to be recognized.

Additionally, we no longer install an importer on the extension
object, so extensions that consist of more than one file MUST
import those files as modules now.

There will be a second breaking change for extensions when
gnome-shell's own code is ported to ESM, so most extension
developers will likely want to wait until the port is complete
before starting to port their extensions.

Based on a commit from Evan Welsh.

Part-of: <https://gitlab.gnome.org/GNOME/gnome-shell/-/merge_requests/2795>
This commit is contained in:
Florian Müllner 2023-06-02 00:47:22 +02:00 committed by Marge Bot
parent 0c6c45d426
commit e8ee845e41

View File

@ -512,36 +512,25 @@ var ExtensionManager = class extends Signals.EventEmitter {
let extensionModule;
let extensionState = null;
// TODO: This function does not have an async operation but when ESM is
// merged there will be a dynamic import here instead of `installImporter`.
await 0;
ExtensionUtils.installImporter(extension);
// Extensions can only be imported once, so add a property to avoid
// attempting to re-import an extension.
extension.isImported = true;
try {
extensionModule = extension.imports.extension;
extensionModule = await import(extensionJs.get_uri());
// Extensions can only be imported once, so add a property to avoid
// attempting to re-import an extension.
extension.isImported = true;
} catch (e) {
this.logExtensionError(uuid, e);
return false;
}
if (extensionModule.init) {
try {
extensionState = await extensionModule.init(extension);
} catch (e) {
this.logExtensionError(uuid, e);
return false;
}
try {
extensionState = new extensionModule.default(extension.metadata);
} catch (e) {
this.logExtensionError(uuid, e);
return false;
}
if (!extensionState)
extensionState = extensionModule;
extension.stateObj = extensionState;
extension.state = ExtensionState.DISABLED;
this.emit('extension-loaded', uuid);
return true;