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