Reputation: 71
I need to develop a nodejs application in which it should be possible to dynamically import ESM modules in a commonJS module during the run time.
"type":"module"
in package.json file / use .mjs extension.Here's what I have tried so far:-
Using dynamic imports This approach works as expected for importing commonjs modules dynamically during run time. But, for importing ESM modules, there is a requirement to mention "type":"module" in package.json / naming the files with ".mjs" extension.
Using the esm npm package.
This is an old npm package that I found which is able to import ESM modules in commonJS modules dynamically without the need to specify "type":"module"
in package.json file. However, trying to import an ESM module which imports a pure ESM module(example: node-fetch@3) fails with this.
Upvotes: 6
Views: 1999
Reputation: 59495
I had to dig for it in the node codebase but the --experimental-default-type=module
flag allows you to run esm without it defined in package.json or naming the file .mjs
.
Upvotes: 0
Reputation: 486
TLDR:
$ npm install import-sync
then
const importSync = require('import-sync');
const yourModule = importSync('./path/to/your/module');
There is an open issue (#904) for the esm library about
Error [ERR_INVALID_PROTOCOL]: Protocol 'node:' not supported. Expected 'file:'
If we look into the source code of, for example, index.js in node-fetch version 3, you can see that there are imports such as
import http from 'node:http';
import https from 'node:https';
which isn't compatible with the esm library.
I'm sure you're aware of this too, the default require
also doesn't work with node-fetch as described in their CommonJS documentation:
node-fetch from v3 is an ESM-only module - you are not able to import it with require().
If you cannot switch to ESM, please use v2 which remains compatible with CommonJS. Critical bug fixes will continue to be published for v2.
I had a similar requirement, so I wrote import-sync:
The library wraps around esm and will enable you to dynamically import ESM modules in a CommonJS module (or even an ESM module) at runtime, including submodules that leverage pure-esm libraries such as node-fetch.
You can install the library with:
npm install import-sync
Then use it as follows:
// import importSync from 'import-sync'; // for ESM
const importSync = require('import-sync');
if (someCondition) {
const someEs6Module = importSync('../../someEs6Module');
console.log(someEs6Module);
}
Specific to node-fetch, here is an example:
package.json
{
"dependencies": {
"import-sync": "^2.0.4",
"node-fetch": "^3.3.2"
}
}
index.js
const importSync = require('import-sync');
const nodeFetch = importSync('node-fetch');
console.log(nodeFetch);
const myModule = importSync('./myModule');
console.log(myModule);
myModule.js
import nodeFetch from 'node-fetch';
export const randomVariable = 'helloworld';
Running
node index.js
will give the expected results:
If you decide to add "type": "module" to your package.json
later down the line, the logic still applies. See this answer here for an example:
Hope this helps :).
Upvotes: 3