I've been trying to use the import() function to import something which is dynamic at runtime. I would think that as long as I create an entry for the file, webpack could be smart enough to import() the correct module, but that doesn't seem to be the case.
Does anyone know of a way to chunk off an entry and use the import() syntax, feeding it a variable, and have it work at runtime?
A simple example of the root problem is as follows:
// works
import( './a.js' ).then(() => console.log('it worked'));
// something is a dynamic variable that changes at runtime
const something = './a.js';
// does not work, even with this simplistic example
import( something ).catch(() => console.log('it did not work'));
It does not work because, although it is called "dynamic import" it is does not follow what the word means. The idea on "dynamic" import is to be able to import something dynamically at runtime, but here it is caveat: the module to be imported has to be known.
Since webpack does static analysis to do the lazy loading on these import() statements, everything has to be known and predictable, otherwise webpack would not be able to create async chunks on the fly. That is why adding a variable to the import does not work.
Yeah, this bit of webpack is weird. Truly dynamic import doesn't work.
We can put string template inside the import statement, but putting variable name raises Dependency warning.
This is the closest I could achieve - put if conditions or switch case or a string template and then write the import in it.
Something like -
const getModule = (filename) => {return import(`directoryPath/${fileName}`);}
OR
const something = 'a.js';
if (something === 'a.js') {
return import(`./${something}`); // promise
} else if (something === 'b.js'){
.........
}
There's this thing with dynamic imports - webpack bundles all possible files in these conditions which is not cool.
Related
A colleague and I have been trying to write a function for dynamically importing components into our Vue app. We're bundling our code with Webpack 5. At the moment our function looks like this:
const dynamicImport = (pathName, request, resolve, canTryAgain) => {
import(/* webpackChunkName: '[request]' */ `${pathName}/${request}`)
.then(component => resolve(component.default))
.catch(() => {
if (canTryAgain) {
dynamicImport(pathName, request, resolve, false);
} else {
location.reload();
}
});
};
Our hope was that the [request] placeholder in the magic comment would take our request parameter, and name the bundle resulting from this function after whatever we feed that parameter. However, Webpack just takes the entire line that we feed into the import statement ${pathName}/${request} and uses it for naming our bundles. This results in really long and not very elegant file names to use in our frontend. Any idea of how to still feed the import statement two variables/parameters, and have the magic comment only use one of them for naming the bundle?
I don't have the exact solution, but I can point you in some sort of directly. This would likely be managed on the babel-plugin side of things.
Use the loadable-components library as an example. There is the main component library (which would include code similar to what you wrote above), then there is the webpack side of things where transformations occur in a babel plugin.
Loadable Components:
https://loadable-components.com/
Loadable Components Repo:
https://github.com/gregberge/loadable-components/
Babel Chunk Name Functions:
https://github.com/gregberge/loadable-components/blob/main/packages/babel-plugin/src/properties/chunkName.js
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.
How to include an entire file into my bundle main.js?
ES6 can import/export functions and classes. But what if i want to include the whole content from another file into my bundle main.js? how to do it?
I came across the query on Stackoverflow: Managing jQuery plugin dependency in webpack.
I'm not sure about this question though. Those options given there seem to target injecting implicit globals, configuring this, disabling AMD, to include large dists. I don't think this is what i want.
Let's say i have two files in src directory
1- rough.js
const rgh = "qwerty"
2- index.js
import './rough.js' //something like this
console.log (rgh)
Now what i expect in bundle.js is
const rgh = "query";
console.log(rgh)
I just want all the content inside one of my file to get all transported to index.js for webpack to bundle them
Those options given there seem to target injecting implicit globals,
configuring this, disabling AMD, to include large dists. I don't think
this is what i want.
To understand this you need to understand what webpack is doing for you. Web pack takes a series of Javascript files (and more importantly their contents) and parses these into one file. That's what it does from a file point of view, but if you ignore the file and think about what it does from a code point of view, it takes each one of the imported objects and makes them available to other objects depending upon the rules you define in your code (using import and export). You can think of this from a closure point of view something like this:
if you have some code like:
import a from 'a.js';
export default b(){
console.log(a.test());
}
This will be turned into something like, in one js file:
var a = (function() {
var testStr = "test";
function test(){
return testStr;
}
return {test:test};
})();
var b = (function(a) {
console.log(a.test());
})(a);
So you can see that the file isn't really important. What's important is the scope. b can use a because it is injected into it's scope (In this instance as a IIFE).
In the above example a and b are in the global scope but testStr isn't.
So when your talking about "importing my file", you need to forget about that and think about what objects in that file you want to import how. Any variables "in that file" declared directly var a = ....; are in the global scope. So it sounds like what you want to do is import the objects in that file into the global scope.
you just need to import that file in main.js
like this way
I'm trying to add dynamic import into my code to have a better performance on the client-side. So I have a webpack config where is bundling js files. On SFCC the bundled files are in the static folder where the path to that files is something like this: /en/v1569517927607/js/app.js)
I have a function where I'm using dynamic import of es6 to call a module when the user clicks on a button. The problem is that when we call for that module, the browser doesn't find it because the path is wrong.
/en/lazyLoad.js net::ERR_ABORTED 404 (Not Found)
This is normal because the file is on /en/v1569517927607/js/lazyLoad.js.
There is a way to get it from the right path? Here is my code.
window.onload = () => {
const lazyAlertBtn = document.querySelector("#lazyLoad");
lazyAlertBtn.addEventListener("click", () => {
import(/* webpackChunkName: "lazyLoad" */ '../modules/lazyLoad').then(module => {
module.lazyLoad();
});
});
};
I had the same problem and solved it using the Merchant Tools > SEO > Dynamic Mapping module in Business Manager.
There you can use a rule like the following to redirect the request to the static folder:
**/*.bundle.js i s,,,,,/js/{0}.bundle.js
All my chunk files are named with the <module>.bundle pattern.
Here you can find more info :
https://documentation.b2c.commercecloud.salesforce.com/DOC1/topic/com.demandware.dochelp/content/b2c_commerce/topics/search_engine_optimization/b2c_dynamic_mappings.html
Hope this helps.
I believe you'll likely need to do some path.resolve() magic in either your import statement or your webpack.config.js file as is shown in the accepted answer to this question: Set correct path to lazy-load component using Webpack - ES6
We did it in a different way. That required two steps
From within the template file add a script tag that creates a global variable for the static path. Something like
// inside .isml template
<script>
// help webpack know about the path of js scripts -> used for lazy loading
window.__staticPath__ = "${URLUtils.httpsStatic('/')}";
</script>
Then you need to instruct webpack to know where to find chunks by changing __webpack_public_path__ at runtime
// somewhere in your main .js file
// eslint-disable-next-line
__webpack_public_path__ = window.__staticPath__ + 'js/';
Optional step:
You might also want to remove code version from your __staticPath__ using replace (at least we had to do that)
__webpack_public_path__ = window.__staticPath__.replace('{YOUR_CODE_VERSION_GOES_HERE}', '') + 'js/';
Is it necessary to have typing for every JS library you use in typescript?
if not, then how to get rid of errors and make use of a library which doesn't have available definition files
import { NotificationContainer, NotificationManager } from 'react-notifications';
can't find typings for react-notifications
It's not necessary. For your example, you create a file react-notifications.d.ts (you can call it anything you like as long as the extension is .d.ts but it makes sense to name it consistently):
declare module "react-notifications" {
const NotificationContainer: any;
const NotificationManager: any;
}
That's a near-minimal starting point. But you could go a little further and improve on those any declarations. They are a temporary cop-out.
An alternative to #Daniel Earwicker answer, as long as you are in a commonJS environment (and I guess you are using webpack) is to simply require the library using node require
const rn = require('react-notifications')
then use rn.NotificationContainer directly or import NotificationContainer = rn.NotificationContainer