I'm trying to dynamically import from my dependencies using a for loop like so
const packageNames = [
'#packageA/address',
'#packageB/app',
'#packageC/auth',
]
for await (const packageName of packageNames) {
try {
const importedPackage = await import(`${packageName}/custom-components`);
console.log('importedPackage :', importedPackage);
// Do something with the imported package
} catch (error) {
console.error(`Error importing ${packageName}: ${error}`);
}
}
I want to import custom-components from those packageNames and if it fails just to catch the error and log it.
But instead, it breaks the app if the custom-components file doesn't exist in the package I'm trying to import from.
One solution I was thinking of would be to use the fs package to check if custom-components file exists in the package but that's not possible to do in a React app.
Is there any other way to get around this, to just catch the error instead of breaking the app with an error like Can't resolve '#packageA/address/custom-components'
I'm using Next.js to develop a website.
I want to use dynamic import import() to dynamically load a module. It might not exist.
And if it does not exist, I'm OK with supressing it:
const Blog = async () => {
let Layout = <div>Fallback layout</div>
try {
const { ModuleLayout } = await import('path-to-module').catch(reason => {})
if (ModuleLayout) {
Layout = ModuleLayout
}
}
catch (error) {
// intentionally swallawed
}
return <Layout />
}
I would expect the try-catch statement to suppress the error for me.
But I get this error:
Module not found: Can't resolve 'path-to-module'
Why the catch block does not work? How can I catch the error of dynamic import?
The try-catch works with normal JavaScript in both the browser and Node:
try {
const { ModuleLayout } = await import('path-to-module').catch(reason => { })
if (ModuleLayout) {
console.log(ModuleLayout);
}
}
catch (error) {
console.log('error');
}
BTW the .catch there is redundant.
I think the problem here is that Next does extra analysis on imports through WebPack:
https://github.com/webpack/webpack/issues/6680#issuecomment-370800037
The suggestion here is to constrain the path to a known directory, but that might not be applicable in your case. Maybe your usecase would make for a good issue on the Next repository so that Vercel is motivated to add a flag to disable the static analysis for certain imports.
Documentation about dynamic import in Next:
https://nextjs.org/docs/advanced-features/dynamic-import#example
Note: In import('path/to/component'), the path must be explicitly written. It can't be a template string nor a variable. Furthermore the import() has to be inside the dynamic() call for Next.js to be able to match webpack bundles / module ids to the specific dynamic() call and preload them before rendering. dynamic() can't be used inside of React rendering as it needs to be marked in the top level of the module for preloading to work, similar to React.lazy.
No hint on how to work around this and get a truly dynamic export at runtime.
Someone here claims warapping the import in dynamic did the trick for them.
For some reason, I have to write code like this:
import { something } from '/Users/my-user-name/my-working-dir/my-package/src/somefile.ts';
Rollup sees /Users thinks that's a node_modules, but not.
I can't find any rollup plugin related to this.
Now, I've written a rollup plugin to fix this, but I didn't write any plugin before, I don't know if I'm doing it right or wrong, but the output is exactly what I want:
function fixLocalImport() {
return {
name: 'fix-local-import', // this name will show up in warnings and errors
resolveId(source, importer) {
if (source.startsWith('/Users')) {
return source;
}
return null; // other ids should be handled as usually
},
load(id) {
return null; // other ids should be handled as usually
}
};
}
Am I doing anything wrong?
Rollup doesn't automatically handle absolute URLs, since they refer to different things depending on the context (either the root of your website server, or the root of your file system, or the root of your project). Writing a plugin is the best solution here, although you don't need to override the "load" hook.
function fixLocalImport() {
return {
name: 'fix-local-import',
resolveId(source, importer) {
if (source.startsWith('/Users')) {
return source;
}
return null;
},
};
}
I have recently rewritten a bunch of old JS to ES2015 making use of module import/exports. I'm using Rollup and Babel to transpile this back.
The libraries are integrated into a number of other sites I don't have control of so I need to be cautious with code to make sure I don't pollute global, doesn't throw errors, etc.
gulpfile.js
var rollupBabel = rollupPluginBabel({
babelrc: false,
presets: [
"babel-preset-es2015-rollup"
]
});
merged.add(rollup({
entry: './js/bnr.js',
format: "es",
plugins: [
rollupBabel
]
})
.pipe(source('bnr.js'))
.pipe(gulp.dest('./compiled/js/')));
bnr.js
import * as helpers from "../lib/helpers";
import moment from "../../node_modules/moment/src/moment";
class Connect {
constructor(window, document) {
this.init();
}
init()
{
// Stuff happens here
}
}
Output
// Helpers and what not here
var hookCallback;
function hooks() {
return hookCallback.apply(null, arguments);
}
// This is done to register the method called with moment()
// without creating circular dependencies.
function setHookCallback(callback) {
hookCallback = callback;
}
function isArray(input) {
return input instanceof Array || Object.prototype.toString.call(input) === '[object Array]';
}
// The rest of moment.js
As you can see all the moment.js related code is being output without an closure/wrapper to keep it out of global. As a result I'm getting various errors on consuming sites.
How can I import moment.js or reconfigure the gulp task to import moment without polluting the global namespace?
Thanks
As suggested by #Bergi the format was the issue, switching to iife wraps the whole thing in a closure to solve the problem.
Using angular 2 with lazy loaded modules, I can receive(for example) 401 HTTP code from server
bootstrap 0b40fee…:101 GET http://localhost:8082/2.chunk.js
Error: Loading chunk 2 failed.
at HTMLScriptElement.onScriptComplete (bootstrap 0b40fee…:91)
at HTMLScriptElement.wrapFn (zone.js:1032)
at ZoneDelegate.invokeTask (zone.js:414)
at Object.onInvokeTask (core.es5.js:4119)
at ZoneDelegate.invokeTask (zone.js:413)
at Zone.runTask (zone.js:181)
at HTMLScriptElement.ZoneTask.invoke (zone.js:476)
How to handle this error?
Check my answer for details
Workaround to bypass this chunk fails error => Programmatically force app to reload if chunks failed error occurs using global error handler.
import { ErrorHandler } from '#angular/core';
#Injectable()
export class GlobalErrorHandler implements ErrorHandler {
handleError(error: any): void {
const chunkFailedMessage = /Loading chunk [\d]+ failed/;
if (chunkFailedMessage.test(err.message)) {
window.location.reload();
}
}
}
Provide it in our root module to change default behavior in our app, so instead of using default ErrorHandler class we are using our custom GlobalErrorHandler class.
#NgModule({
providers: [{provide: ErrorHandler, useClass: GlobalErrorHandler}]
})
I was having the same problem so I investigated. I found the solution. This happened to me when I redeployed to another server and the chunk had a [hash].
You can catch the error either in a catch all like this:
ngOnInit() {
if (!this.previousRouterErrorHandler) {
this.previousRouterErrorHandler = this.router.errorHandler;
let that = this;
this.router.errorHandler = function (err: any) {
// Handle here. err.message == "Loading chunk chunk-name failed."
return that.previousRouterErrorHandler.apply(that.previousRouterErrorHandler, arguments);
};
}
}
Or directly at the link which navigated
click() {
this.router.navigate(['/lazy-route'])
.catch(err => {
// Handle here
});
}
Here is my solution for this. I inject this service as a singleton in my app / core module.
It waits for instances of NavigationError from the router, checks if they are ChunkLoadError's and then does a redirect to the place the user wanted to go to anyway.
// Angular
import { Injectable, OnDestroy } from '#angular/core';
import { Router, NavigationError } from '#angular/router';
// Rxjs
import { Subscription } from 'rxjs';
import { filter } from 'rxjs/operators';
#Injectable()
export class ChunkErrorHandler implements OnDestroy {
private subscription: Subscription;
constructor(router: Router) {
this.subscription = router.events
.pipe(filter(event => event instanceof NavigationError))
.subscribe(event => {
this.handleRouterErrors(event as NavigationError);
});
}
ngOnDestroy() {
this.subscription.unsubscribe();
}
private handleRouterErrors(event: NavigationError) {
if (event.error.name === 'ChunkLoadError') {
window.location.href = `${window.location.origin}${event.url}`;
}
}
}
It happen when when deploy new code.The manifest.js which holds the files and hashes doesn't update without refreshing and when it loads a chunk it obviously uses the old hash from manifest.js.
So while catching error we can do force reload with given url :-
click() {
this.router.navigate(['/lazy-route'])
.catch(err => {
// Handle here
// reload with given route
// window.location.pathname('/lazy-route');
// OR
// reset existing route(containing query params) with given route and force reload
window.history.pushState({}, document.title, '/lazy-route' );
window.location.reload();
});
}
chunk related errors can be raised by any environment or routing related issues making them hard to debunk.
In my case, the amount of data moving in my PWA was too much to handle by the angular router. It was flooding the headers of the js chunks getters and therefore raising bad_request errors.
I suggest you to check out those network calls (getters of chunks.js like http://localhost:xxxx/158.js) for anything unusual in headers and refactor sketchy stuff in your current dev environment, since it's a real black hole time to investigate the source of the error by yourself.
Hope that'll help
check out Catch Storage, i guess service worker save some thing in catch storage
console.log(Object.entries(error));
this help me to understand what's inside the error is
rejection,
promise,
zone,
task
and below is my solution:
handleError(error) {
switch (error?.rejection?.name) {
case 'ChunkLoadError':
window.location.href = window.location.href;
break;
default:
break;
}
}
In my case, I was putting my files in an S3 bucket. I kept getting this error because it was calling the wrong filenames all together and returning an html error response.
At some point I let the IT team know what was happening. They were like, let's invalidate the cache on CloudFront... What?! Yeah! Let's do that...
Moral of the story, if you've been searching the web for answers to this error and can't find any, check with the IT team or any place that the index.html file might be getting cached.
this probably means unhandled exception. you have to handle error responses (4xx, 5xx status codes) from server in whatever way you want: show error message somewhere, redirect to some page, do anything but not leave it unhandled.
for example:
return this.http.get(requestDetails)
.map(res => res.json())
.catch(err => {
console.log('server error:', err)
Observable.throw(err);
});