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?
Related
This question already has answers here:
Node.js - SyntaxError: Unexpected token import
(16 answers)
Closed last year.
With Node 16.13.2 I am trying to add the validate module in an existing code base. Reading the 2 year old question I can't make it work with the below PoC. I get
import Schema from 'validate';
^^^^^^
SyntaxError: Cannot use import statement outside a module
Question
Can anyone show me how the below PoC should look like for it to work?
index.js
const mod = require('./mod');
mod.js
import Schema from 'validate';
const test;
module.exports = test;
If you want to use the import syntax of es6+ then you will either need to use .mjs files (instead of regular .js files), or you will need to add in a compilation/transpilation step into your pipeline.
Using .mjs
If you change the file name of your mod.js file to mod.mjs, then this should work:
import Schema form 'validate';
export const test;
Then in index.js you will either have to change index.js to index.mjs and change the contents to:
import { test } from './mod.mjs';
..or you can keep index.js and change the contents to:
(async () {
const { test } = await import('./mod.mjs')
})();
You can read more in this rather comprehensive article i happened across while googling: https://blog.logrocket.com/how-to-use-ecmascript-modules-with-node-js/
Adding a compilation step
There are many different compilers and/or bundlers to pick from, but for regular vanilla javascript I'd recommend sticking to babel.
Freecodecamp has a tutorial for how to set up babel for use with nodejs: https://www.freecodecamp.org/news/setup-babel-in-nodejs/
I support a relatively complex legacy codebase, but am looking to modernise it a little by bringing in Webpack so that we'd have import & export capabilities in JS.
The problem I'm having is that we use a global object called App where we define and add different properties depending on the page. So for example we have the following file where we instantiate App (loaded on all pages):
app.js
const App = (() => {
const obj = {
Lib: {},
Util: {},
// etc
}
return obj;
})();
Then in another file we add to App.Lib just for the specific page that needs it:
lazyload.js
App.Lib.Lazyload = (() => {
// lazyload logic
})();
We simply concatenate the files during the bundling process, but obviously this is not ideal as none of the files have no knowledge of what goes on outside of it.
Exporting only seems to work for the top level object (where the object is defined), so anything I add to it elsewhere cannot be exported again. For example if I add export default App.Lib.Lazyload; at the end of lazyload.js and then try to import it elsewhere it will not import the Lazyload property.
Is there any way to get this to work without major refactor? If not, would you have any suggestions about the best way to handle it?
I don't think you can import Object.properties in JS. If you want to bundle specific packages (say Lazyload) for packages that need them, you might try:
//lazyload.js
export const LazyLoad = {
//lazyload logic
}
then somewhere else...
import {LazyLoad} from 'path/to/lazyload.js';
// assuming App has already been created/instantiated
App.Lib.Lazyload = LazyLoad;
Using Export Default...
//lazyload.js
const LazyLoad = {};
export default LazyLoad;
then...
import LazyLoad from 'path/to/lazyload.js';
App.Lib.LazyLoad = LazyLoad;
You can find help with Imports and Exports at MDN.
Is it possible to import named exports dynamically?
I have a file, banana.js with hundreds of named exports. Id like to import them on demand. Is this possible? And if it is, will it only load that export and not all?
I know its possible to import them dynamically from individual files but I want them in the same file.
Example below..
// banana.js
export const banana_1 = {
..
}
export const banana_2 = {
..
}
// main.js
const currentPage = 1
async getSomething(){
let { `banana_${currentPage}` } = await import('./banana.js');
const foo = `banana_${currentPage}`
}
Fyi im using Vue.js
From what I know, you might have to use require('./banana.js') here. Please note that require is synchronous, so need for await. If you use eslint and it forbids usage of require, just add // eslint-disable-line to the end of that line. BTW, I don't know Vue at all.
About the object you import, you should probably make an array instead of suffixing each export with a number.
Edit: I just realized that dynamic imports are a thing not only possible with require, so ignore the first paragraph.
Based on your response to my question I offer the following solution. I understand not wanting to deploy and use a full database solution for the sake of hot loading some JSON objects. I'm unsure of your use case and other details related to your project.
You can use a self contained database like SQLite. Instead of setting up a NoSQL or SQL server. You can instead just store what you need to the SQLite file. Since Vue requires Node.js you can use the Node.js SQLite3 library https://www.npmjs.com/package/sqlite3.
I hope this helps.
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.
When using dynamic imports, can I define what I want to import like regular imports?
For example:
import Person from '/classes.js'
As dynamic:
await import('Person from /classes.js') //Incorrect obviously
Dynamic imports will hand you everything from within the module. You can use destructuring the extract the pieces you want.
const { Person } = await import('/classes.js');
You can try this when you need to import some specific file.
const moduleSpecifier = '/classes.js';
import(moduleSpecifier)
.then(someModule => someModule.myFucntion());