esbuild
esbuild copied to clipboard
Dynamic import not resolved during build
I have this simple client-side SPA router that will dynamically import a script based on the url eg. example.com/foo/bar
will fire a request for example.com/js/pages/foo/bar.js
I'm handling 404's in the catch
statement like so:
const router = async () => {
const path = window.location.pathname;
const resourcePath = `/js/pages${path}.js`.replace("/.js", ".js");
try {
(await import(resourcePath)).default({ state: window.state });
} catch {
console.error(`Page not found: ${resourcePath}`);
(await import('/js/pages/404.js')).default({ state: window.state });
}
}
The problem is that the build is trying to resolve the 404 import at build time. And even thought the file exists, it still doesn't find it...
✘ [ERROR] Could not resolve "/js/pages/404.js"
src/js/spa_router.js:10:22:
10 │ (await import('/js/pages/404.js')).default({ state: window.state });
╵ ~~~~~~~~~~~~~~~~~~
Error: Build failed with 1 error:
src/js/spa_router.js:10:22: ERROR: Could not resolve "/js/pages/404.js"
wrapping the 404 import in another try-catch
solves the problem and the builder works just fine, but it shouldn't need to be done this way IMO. And I don't understand why it doesn't find the 404.js
(yes, very ironic)
const router = async () => {
const path = window.location.pathname;
const resourcePath = `/js/pages${path}.js`.replace("/.js", ".js");
try {
(await import(resourcePath)).default({ state: window.state });
} catch {
console.error(`Page not found: ${resourcePath}`);
try {
(await import('/js/pages/404.js')).default({ state: window.state });
} catch {}
}
}
This is the build script
await esbuild.build({
entryPoints: ['src/js/**/*.js'],
bundle: true,
minify: true,
sourcemap: true,
splitting: true,
treeShaking: true,
format: "esm",
target: "esnext",
outdir: 'public/js'
})
This is because "/js/pages/404.js"
is an absolute path. esbuild runs its node-behavior resolver against those paths in import(path)
and require(path)
and includes (bundles) them in the bundle. There's no such file with the absolute path in your file system.
If it is in a single build (i.e. in one esbuild.build()
call), you can reference 404.js with relative path, e.g. "./pages/404.js"
.
On the other hand, you may mark this path as external through a plugin, roughly look like this:
var custom_external_plugin = {
name: "custom-external",
setup({ onResolve }) {
onResolve({ filter: /404\.js$/ }, args => {
return { path: args.path, external: true }
})
}
}