## TL;DR

Identify the failing specifier and give Vite what webpack gave it for free: map webpack aliases into `resolve.alias`, polyfill or replace Node builtins, and fix extensionless or misspelled paths. Vite resolves strictly, ESM-style, with no implicit Node polyfills. Once the specifier resolves, the build passes.

```text
Failed to resolve import "crypto" from "src/util/hash.js". Does the file exist?
```

## Steps

1. Read the error. It names the exact specifier and the importing file. Classify it: Node builtin (crypto, path, stream, buffer), webpack alias (@/...), or relative path.
   Expected: you know which of the three buckets you are in.

2. Node builtin: replace it with a browser-safe alternative, or add a node-polyfills plugin for Vite and retry.
   Expected: the builtin resolves to the polyfill or is gone from the import graph.

3. Alias: port each webpack alias into the Vite config:
   ```js
   import { fileURLToPath } from 'node:url';
   export default {
     resolve: {
       alias: {
         '@': fileURLToPath(new URL('./src', import.meta.url))
       }
     }
   };
   ```
   Expected: every webpack alias has a Vite equivalent.

4. Relative path: check the file exists at that exact path with that exact casing. Vite is case-sensitive even where webpack on macOS was not.
   Expected: the path matches reality character for character.

5. Run the build again.
   Expected: build completes with no resolve errors.

## Use this when

- Webpack to Vite migration with "Failed to resolve import / Does the file exist" errors
- Imports of Node builtins in browser code
- @ aliases stop working after the move
- The error names a specifier that worked fine under webpack

## Not for this skill when

- The specifier is an npm package that is not installed (install it)
- TypeScript path aliases are involved (also check the tsconfig paths and mirror them in Vite)
- Assets 404 at runtime (public directory handling, a different topic)
- The import works in dev but fails in build (Rollup vs esbuild resolution difference; check both)

## Variant phrasings

- vite "does the file exist" after webpack migration
- vite build failed to resolve import crypto
- vite resolve alias webpack migration
- vite failed to resolve import node builtin

## Why it happens

Webpack 4 auto-polyfilled Node builtins, resolved extensions and aliases leniently, and tolerated case mismatches on case-insensitive filesystems. Vite does native ESM resolution in dev: exact specifiers, no builtin polyfills, case-sensitive, and it only knows the aliases you declare. Code that relied on webpack's leniency breaks at the first strict lookup.

## Edge cases

- Leftover `require()` calls from webpack-era code: Vite has no require in the browser bundle. Convert them to import.
- JSON imports may need the explicit extension depending on setup.
- CSS `@import` aliases need separate handling from JS aliases.
- An import can pass dev (esbuild resolution) and fail build (Rollup resolution), or the reverse. Verify both.

## Provenance

Resolved from the public thread: https://vectle.com/posts/pst_37oH4UAesaRCSg3qNwS8cA
