Home > Software engineering >  How to extend a dynamically imported module?
How to extend a dynamically imported module?

Time:12-12

I'm trying to update a library to dynamically import d3.js after the upgrade to v7, and this syntax handles the import:

import("d3").then((library) => {
    var d3 = library;
    console.log("d3:", d3);
})
.catch((err) => {
    console.error(err);
});

The logged object is in this format (albeit much longer):

Logged "Module" type

Whenever I try to simply extend by adding a property with d3.contextMenu = factory(d3);, I get this TypeError:

TypeError: Cannot add property contextMenu, object is not extensible

Doing a shallow clone like var d3 = {...library, contextMenu: factory(library)}; removes a lot of the module's context, e.x. the get and set methods are not copied at all.

So I tried to create a structured clone of the library before extending it with var d3 = structuredClone(library);, but that gives a DOMExteption:

DOMException: Failed to execute 'structuredClone' on 'Window': [object Module] could not be cloned.

When using the non-ESM syntax, this code covers all my bases and allows for extension of the module:

if (typeof module === "object" && module.exports) {
    var d3 = require("d3");
    module.exports = factory(d3);
} else if (typeof define === "function" && define.amd) {
    try {
        var d3 = require("d3");
    } catch (e) {
        d3 = root.d3;
    }

    d3.contextMenu = factory(d3);
    define([], function () {
        return d3.contextMenu;
    });
} else if (root?.d3) {
    root.d3.contextMenu = factory(root.d3);
}

But I need to add in ESM compatibility for the library, hence my code. What method can I use to extend the module with my function?

CodePudding user response:

As it is not possible to extend an inextensible object, you can either wrap the library or use it as the prototype of a new object. For example:

import('d3').then((library) => {
  const d3 = Object.create(library);
  d3.contextMenu = factory(library);
  for (const prop in d3) console.log(prop);
})
.catch((err) => {
    console.error(err);
});

To export:

module.exports = (async () => {
  const library = await import('d3');
  const d3 = Object.create(library);
  d3.contextMenu = factory(library);
  return d3;
})();

To then import into CommonJS module using require:

(async () => {
  const d3 = await require('./filename.js');
  for (const prop in d3) console.log(prop);
})();
  • Related