'SvelteKit could not resolve dependency even though it's external

I'm making a user authentication page and in that matter, I'm using bcrypt for hashing. Everything was nice and well until I actually use the bcrypt module in the code. Running it in development mode works totally fine and it when previewing the build it runs totally fine, but in the build process an error occurs that when deployed causes it to stop building and errors out.

Here's the error:

> Using @sveltejs/adapter-vercel
 > node_modules/@mapbox/node-pre-gyp/lib/util/s3_setup.js:43:28: error: Could not resolve "mock-aws-s3" (mark it as external to exclude it from the bundle, or surround it with try/catch to handle the failure at run-time)
    43 │     const AWSMock = require('mock-aws-s3');
       ╵                             ~~~~~~~~~~~~~

 > node_modules/@mapbox/node-pre-gyp/lib/util/s3_setup.js:76:22: error: Could not resolve "aws-sdk" (mark it as external to exclude it from the bundle, or surround it with try/catch to handle the failure at run-time)
    76 │   const AWS = require('aws-sdk');
       ╵                       ~~~~~~~~~

 > node_modules/@mapbox/node-pre-gyp/lib/util/s3_setup.js:112:23: error: Could not resolve "nock" (mark it as external to exclude it from the bundle, or surround it with try/catch to handle the failure at run-time)
    112 │   const nock = require('nock');
        ╵                        ~~~~~~

> Build failed with 3 errors:
node_modules/@mapbox/node-pre-gyp/lib/util/s3_setup.js:43:28: error: Could not resolve "mock-aws-s3" (mark it as external to exclude it from the bundle, or surround it with try/catch to handle the failure at run-time)
node_modules/@mapbox/node-pre-gyp/lib/util/s3_setup.js:76:22: error: Could not resolve "aws-sdk" (mark it as external to exclude it from the bundle, or surround it with try/catch to handle the failure at run-time)
node_modules/@mapbox/node-pre-gyp/lib/util/s3_setup.js:112:23: error: Could not resolve "nock" (mark it as external to exclude it from the bundle, or surround it with try/catch to handle the failure at run-time)
Error: Build failed with 3 errors:
node_modules/@mapbox/node-pre-gyp/lib/util/s3_setup.js:43:28: error: Could not resolve "mock-aws-s3" (mark it as external to exclude it from the bundle, or surround it with try/catch to handle the failure at run-time)
node_modules/@mapbox/node-pre-gyp/lib/util/s3_setup.js:76:22: error: Could not resolve "aws-sdk" (mark it as external to exclude it from the bundle, or surround it with try/catch to handle the failure at run-time)
node_modules/@mapbox/node-pre-gyp/lib/util/s3_setup.js:112:23: error: Could not resolve "nock" (mark it as external to exclude it from the bundle, or surround it with try/catch to handle the failure at run-time)
    at failureErrorWithLog (/home/joshgil/Codes/TouchOfClassEvents/node_modules/esbuild/lib/main.js:1493:15)
    at /home/joshgil/Codes/TouchOfClassEvents/node_modules/esbuild/lib/main.js:1151:28
    at runOnEndCallbacks (/home/joshgil/Codes/TouchOfClassEvents/node_modules/esbuild/lib/main.js:1069:65)
    at buildResponseToResult (/home/joshgil/Codes/TouchOfClassEvents/node_modules/esbuild/lib/main.js:1149:7)
    at /home/joshgil/Codes/TouchOfClassEvents/node_modules/esbuild/lib/main.js:1258:14
    at /home/joshgil/Codes/TouchOfClassEvents/node_modules/esbuild/lib/main.js:629:9
    at handleIncomingPacket (/home/joshgil/Codes/TouchOfClassEvents/node_modules/esbuild/lib/main.js:726:9)
    at Socket.readFromStdout (/home/joshgil/Codes/TouchOfClassEvents/node_modules/esbuild/lib/main.js:596:7)
    at Socket.emit (node:events:390:28)
    at Socket.emit (node:domain:475:12)

I followed what the error said, and marked those packages a external, but it still wouldn't work, Here's my svelte.config.js

// svelte.config.js
import sveltePreprocess from 'svelte-preprocess';
import makeAttractionsImporter from 'attractions/importer.js';
import vercel from '@sveltejs/adapter-vercel';
import path, { dirname, resolve } from 'path';
import { fileURLToPath } from 'url';

const __dirname = dirname(fileURLToPath(import.meta.url));

/** @type {import('@sveltejs/kit').Config} */
const config = {
    // Consult https://github.com/sveltejs/svelte-preprocess
    // for more information about preprocessors
    preprocess: sveltePreprocess({
        postcss: true,
        scss: {
            importer: makeAttractionsImporter({
                themeFile: path.join(__dirname, 'static/css/theme.scss')
            }),
            includePaths: [path.join(__dirname, './static/css')]
        }
    }),

    kit: {
        // hydrate the <div id="svelte"> element in src/app.html
        target: '#svelte',
        adapter: vercel(),
        vite: {
            optimizeDeps: {
                exclude: ['bcrypt']
            },
            ssr: {
                external: ['mock-aws-s3', 'aws-sdk', 'nock', 'node-pre-gyp']
            },
            resolve: {
                alias: {
                    $lib: resolve('src/lib'),
                    $components: resolve('src/components'),
                    $utils: resolve('src/utils')
                }
            }
        }
    }
};

export default config;

I started looking into it, and it seems like the problem is caused by node-pre-gyp and so I also marked it as external in the config, but it still yields the same exact error.

It seems like the problem is when node-pre-gyp is trying to resolve the C++ binary for bcrypt and it only errors on build, when it is forcefully run, it runs perfectly fine.



Solution 1:[1]

Vercel have published an article about some libraries (and especially bcrypt) working locally but not on the serverless functions, due to the lack of native dependencies. They advise to either choose an alternative package or to install the necessary shared libraries in the Build Image.

bcryptjs is working perfectly fine for me. The downside is indeed the slower speed (30% slower).

Solution 2:[2]

I had same issue but with @googlemaps/js-api-loader package. I found discussion about this issues in SvelteKit repo https://github.com/sveltejs/kit/issues/928

I fixed it by updating svelte.config.js like this:

vite: {
  ssr: {
    noExternal: ['@googlemaps/js-api-loader']
  }
}

Solution 3:[3]

I was having the same issue with bcrypt and argon2 as they both rely on node-pre-gyp and did some digging and found an implementation of bcrypt in pure javascript.

Caveat it will most likely be slower than the C implementations but it does work in production Sveltekit environment, tested on Vercel.

Here's the bcryptjs package: https://www.npmjs.com/package/bcryptjs

Solution 4:[4]

One solution that I've found is to just include these dependencies "nock", "mock-aws-s3", "aws-sdk" in the package.json and this will resolve the issue but not the intended way, because this means the bundle is going to bigger because there is 3 extra dependencies that is not needed in the client.

I hope there is a better solution!

The caveat of this solution is that the bundle size will be larger of course, and I'm hoping that it's not that much larger.

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1 Ennoriel
Solution 2 Jardulino
Solution 3 ouflak
Solution 4