I have a library that has an optional dependency and handles it using a dynamic import in a try catch block, like so:
let someModule = null;
try {
someModule = await import ("may-not-exist"); // Webpack and Vite erroring
} catch (e) {
// pass
}
// ... check if someModule is null
Webpack and Vite (and possibly others?) both try to statically analyze imports and so when this is used inside a project that relies on Webpack or Vite, they fail to build if the module is not found.
The standard fix for Webpack would be to add /* webpackIgnore: true */
comment in the import:
someModule = await import (/* webpackIgnore: true */"may-not-exist"); // Webpack happy, not Vite
That works for Webpack, but not Vite . I couldn't find a similar workaround for Vite. The only solution I found was to force the module name to be dynamic, for example:
const tryImport = async (path) => {
try {
return await import(path); // Vite happy, not Webpack
} catch (e__ignored) {
// module doesn't exist
return null;
}
};
const someModule = await tryImport("may-not-exist");
Now Vite is not showing any errors or warnings, but although Webpack is not throwing fatal errors, it is showing very persistent warnings:
Critical dependency: the request of a dependency is an expression
It seems like Webpack requires dynamic imports to use statically known paths and not even /* webpackIgnore: true */
silences it (I tried adding it both inside the import
and inside tryImport
in front of the path).
QUESTION:
How can I implement optional dependencies in a library that is to work in various environments including Webpack and Vite? I obviously don't want to require users to modify their configuration to suppress or ignore warnings like that.
SOLUTION:
Based on @Andrei Gătej 's answer, the solution was just to assign the module name to a variable (which keeps Vite happy as it seems enough to disable its static check) and then to add the usual webpack ignore comment inside the import statement:
let socket;
const moduleName = "may-not-exist"; // suppress Vite static analysis
try {
socket = await import(/* webpackIgnore: true */ moduleName);
} catch (e) {
// pass
}
// ... check if someModule is null
I think you can still use an expression for the import()
's argument:
const fileName = "a.js";
import("./modules/" + fileName).then(console.log);
Notice how I give webpack a hint where these modules might reside - the modules
folder.
I have drawn the idea from this article I wrote a while ago - Demystifying webpack's 'import' function: using dynamic arguments
The caveat is that webpack will create a chunk for that dynamic import regardless of whether the module does exist or not.
As far as I remember, webpack handles dynamic expressions provided to import()
a bit differently - for this, I would recommend checking out the article mentioned above.