Fixing the Unresolved Dynamic Import in Astro

I was working on adding a search feature to my site using Pagefind, and I ran into a weird issue with Astro. I was trying to load the pagefind.js script inside a component, but Vite, the bundler Astro uses, kept throwing an error.

My first attempt was a simple dynamic import inside my search component:

TypeScript
const pagefind = await import("/pagefind/pagefind.js");

This seemed right, but Vite’s dev server immediately complained with this error:

Shell
06:30:09 [ERROR] [vite] Internal server error: Failed to resolve import "/pagefind/pagefind.js" from "src/components/Search.astro?astro&type=script&index=0&lang.ts". Does the file exist?
  Plugin: vite:import-analysis
  File: /Users/pyk/github/pyk/sh/src/components/Search.astro?astro&type=script&index=0&lang.ts:15:31
  3  |    const pagefind = await import(
  4  |      "/pagefind/pagefind.js"
     |      ^
  6  |    );
  7  |    pagefind.init();
      at TransformPluginContext._formatLog (file:///Users/pyk/github/pyk/sh/node_modules/.pnpm/[email protected]_@[email protected][email protected][email protected][email protected][email protected]/node_modules/vite/dist/node/chunks/dep-N2lvmi1C.js:42527:41)
      at TransformPluginContext.error (file:///Users/pyk/github/pyk/sh/node_modules/.pnpm/[email protected]_@[email protected][email protected][email protected][email protected][email protected]/node_modules/vite/dist/node/chunks/dep-N2lvmi1C.js:42524:16)
      at normalizeUrl (file:///Users/pyk/github/pyk/sh/node_modules/.pnpm/[email protected]_@[email protected][email protected][email protected][email protected][email protected]/node_modules/vite/dist/node/chunks/dep-N2lvmi1C.js:40503:23)
      at process.processTicksAndRejections (node:internal/process/task_queues:105:5)
      at async file:///Users/pyk/github/pyk/sh/node_modules/.pnpm/[email protected]_@[email protected][email protected][email protected][email protected][email protected]/node_modules/vite/dist/node/chunks/dep-N2lvmi1C.js:40622:37
      at async Promise.all (index 0)
      at async TransformPluginContext.transform (file:///Users/pyk/github/pyk/sh/node_modules/.pnpm/[email protected]_@[email protected][email protected][email protected][email protected][email protected]/node_modules/vite/dist/node/chunks/dep-N2lvmi1C.js:40549:7)
      at async EnvironmentPluginContainer.transform (file:///Users/pyk/github/pyk/sh/node_modules/.pnpm/[email protected]_@[email protected][email protected][email protected][email protected][email protected]/node_modules/vite/dist/node/chunks/dep-N2lvmi1C.js:42322:18)
      at async loadAndTransform (file:///Users/pyk/github/pyk/sh/node_modules/.pnpm/[email protected]_@[email protected][email protected][email protected][email protected][email protected]/node_modules/vite/dist/node/chunks/dep-N2lvmi1C.js:35738:27)
06:31:25 [watch] src/components/Search.astro
06:31:25 [watch] src/components/Search.astro

The pagefind.js file is served by the astro-pagefind integration, so it doesn’t exist as a physical file in my project’s source. Vite was trying to be smart by analyzing my imports, but it couldn’t find the file.

I tried a few things that didn’t work:

  1. Relative Path: I changed the path to ../pagefind/pagefind.js. This didn’t work because Vite was looking for the file relative to my component’s location.

  2. @vite-ignore: I found a Vite feature that’s supposed to stop it from trying to resolve an import.

    TypeScript
    const pagefind = await import(/* @vite-ignore */ "/pagefind/pagefind.js");

    For some reason, this didn’t work either. Vite still tried to be smart and failed.

  3. Mark as external: I tried to mark pagefind/pagefind.js as external in my astro config but did not work.

  4. Module declaration: I tried to declare the pagefind/pagefind.js module in src/env.d.ts and it did not work.

The solution was to hide the import path from Vite’s static analysis. By putting the path in a variable, Vite doesn’t try to resolve it at build time, and the import is handled correctly in the browser.

Here’s the code that finally worked:

TypeScript
const pagefindPath = "/pagefind/pagefind.js";
const pagefind = await import(pagefindPath);

It’s a simple trick, but it got me unstuck. It seems like Vite’s static analysis can sometimes be a bit too aggressive, and this is a good way to work around it when you’re dealing with dynamically served scripts.