ES6 "Import()" issues "MODULE_NOT_FOUND" [Webpack - NodeJS] - javascript

I transpile my ES6 code using Webpack along with BabelJS in NodeJS environment. The problem is that, when I try to import a set of specific .js modules, the MODULE_NOT_FOUND exception would be thrown. This is the main chunk of code I've written so far:
export default async () => {
const modulesToBeImportedByNames = ['a.js', 'b.js', 'c.js'];
const modulesToBeImportedByPromises =
modulesToBeImportedByNames.map(moduleFilename =>
import(`./${moduleFilename}`) // exception is thrown here
);
const importedModules = await Promise.all(modulesToBeImportedByPromises);
}
Note: I've used #babel/plugin-syntax-dynamic-import plugin within my BabelJS configuration.

Looks like the filenames should be relative, as you are importing application files.
const modulesToBeImportedByNames = ['./a.js', './b.js', './c.js'];

Related

Nodejs - import stuff from mjs file into js file OR how to use require in mjs file?

I am new to JS and Node. From what I been reading it seems like ES6 import is not supported in Node so I am forced to use experiment mode (renaming my file to mjs)
This works for the most part unless the mjs file needs to use require. My current solution is to break out the chunk of code that depends on require to a seperate .js file and import that js file
foo.mjs
import readFileSync from './util.js'
...
//orignally just do require('fs') and do fs.readFileSync()
const configFile = readFileSync(configFilePath);
util.js
const fs = require('fs');
const path = require('path');
function readFileSync(filePath) {
console.log(__dirname)
console.log(path.resolve(__dirname, filePath))
return fs.readFileSync(path.resolve(__dirname, filePath), 'utf8');
}
module.exports = readFileSync
Is there a way I can avoid doing this? My app.js has lots of require and I don't want to break out all those parts into separate js files.
I tried changing require to import
// const express = require('express');
import * as express from 'express';
but when run node --experimental-modules app.mjs
I get an error
TypeError: express is not a function
I think I need to either
find a way to use require in mjs
or find a way to import stuff in mjs into a js file
You have three decent options:
Use Babel or TypeScript to get interoperability between the two types of modules
Write a little wrapper to redefine require in ESM code
Use import() (dynamic import) in your CJS to import ESM code
The wrapper to use require in ESM code:
import { createRequire } from 'module'
const require = createRequire(import.meta.url)
const cjsFile = require('./test') // test is regular .js file using module.exports
Dynamic imports:
// foo.mjs:
export const foo = () => true
// index.js:
import('./foo.mjs').then(x => console.log(x.foo()))
Large projects with mixed module types frequently use Babel and/or TypeScript, both of which handle the mixed types just fine. Babel would be simpler to learn and get set up. But if you don't have a ton of code to deal with, the createRequire method works just fine.
As for the issue with Express, it failed because the default export is what you want: import express from 'express', not import * as express....

ES6 import of multiple files with key identifiers

I am trying to import a module that could be associated with multiple files depending on conditionals. I originally had that being imported using a require dynamically through a "const" as so:
const question_pack_requires = {};
require.context('../question-packs/section-2', true, /^(.*\.(js$))[^.]*$/im).keys()
.forEach(async (key) => {
const name = path.normalize(key).replace('.js', '');
// THIS IS THE PROMISE-BASED (ASYNC / AWAIT) WEBPACK DYNAMIC IMPORT SYNTAX
const questionPack = await import(`../question-packs/section-2/${name}`);
question_pack_requires[name] = questionPack;
});
export default question_pack_requires;
However, this has broken when upgrading to Webpack 4 and now is throwing a Type Error. So was planning on just importing each of these manually and assigning a key to each one, however, I am unsure the syntax on how to do that.
Basically I need to convert this "require" to an "import" or import these files manually one by one. s there anyone that can help guide me in the best way to do that?

Inline import by ES6 modules in Node.js

In an "old way" of managing modules in Node.JS (CommonJS modules) you can do something like this:
Example of Express.js route: app.use('/user', require("./user"));
How to do this when I am using ES6 Modules (import, export) and transcribing by Node.JS server by babel?
I can't just do: app.use('/user', import {user} from './user');
Try separating it out into multiple expressions - import (as well as export) are not available at the same lexical level as you are trying to use it the example:
import { user } from './user'
...
app.use('/user', user)
There is a way to do dynamic inline imports in node, detailed here:
https://javascript.info/modules-dynamic-imports
This code has worked for me:
let {default: foo} = await import('./foo.js');
Here is a working example of a function I wrote as part of a db migration tool in node.
const getMigrations = async (path) => {
const migrateDir = await fs.readdirSync(path);
const migrations = await Promise.all(migrateDir.map(async (filename) => {
let {default: migration} = await import(`${path}/${filename}`);
return migration;
}));
migrations.sort((a, b) => {
return a.seq - b.seq;
});
return migrations;
};
Where an example migration is like:
export default {
seq: 1.1,
label: 'create user table',
sql: `
DROP TABLE IF EXISTS user;
CREATE TABLE user
(
...
);
`
};
I am using node v12.18.4 with "type": "module" in my package.json. When I run the script, I get a warning that the ESM module loader is experimental, but it works. However, there is a note on the page linked to above that says:
Dynamic imports work in regular scripts, they don’t require script type="module".
I hope this helps. I believe you should be able to apply this to your problem.

Do require.resolve for ES modules

I need to resolve ES modules, imported via static import or function-like dynamic import, in a way similar to how CJS modules in node.js can be resolved using require.resolve(). Does anything like that exist for ES modules?
For example, if a Vue package have both vue.runtime.common.js and vue.runtime.esm.js. I need to get path to vue.runtime.esm.js. If package doesn't have one, I'd like to know it.
As long as import.meta.resolve stays experimental, you can use createRequire to get back the require.resolve functionality in ES modules, e.g.
import { createRequire } from 'module';
const require = createRequire(import.meta.url);
const pathName = require.resolve('vue.runtime.esm.js');
you can use import.meta.resolve()
here is an example from the node docs
(async () => {
const dependencyAsset = await import.meta.resolve('component-lib/asset.js');
})();
note that you need to pass in --experimental-import-meta-resolve for this to work, as of node 14.3.0

How can i dynamically load CommonJs module?

I'am working on app with React, TS and Webpack stack.
I need to implement feature that allows my app work with client plugins - js files that override existing functionality of some classes. It can be loaded from anywhere - local file system or remote repository and should be fetched in the runtime, because i need to have an option to specify new extension in config and just press F5.
Dynamic import is not my case, because as far as i understand Webpack needs to be able to at least guess roughly what an import() is meant to be referencing. Using simple 'get' request might be an option, but how can i use loaded script as CommonJS module in this case? And am i correct about dynamic import behavior?
You can use #paciolan/remote-module-loader to remotely load a common js module.
import { createLoadRemoteModule } from "#paciolan/remote-module-loader"
const main = async() => {
const loadRemoteModule = createLoadRemoteModule()
const myModule = await loadRemoteModule("http://fake.url/modules/my-module.js")
const value = myModule.default()
console.log({ value })
}
main()
If you need to pass dependencies to the module:
import {
createLoadRemoteModule,
createRequires
} from "#paciolan/remote-module-loader"
const dependencies = {
react: require("react")
}
const main = async() => {
const requires = createRequires(dependencies)
const loadRemoteModule = createLoadRemoteModule({ requires })
const myModule = await loadRemoteModule("http://fake.url/modules/my-module.js")
const value = myModule.default()
console.log({ value })
}
main()
If need to load a React Component, check out #paciolan/remote-component
You may have to take extra steps if you have a Content Security Policy (CSP) set.

Categories

Resources