Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

mock-aws-s3, aws-sdk, nock dependencies vs devDependencies issue #661

Open
johnmanko opened this issue Jul 6, 2022 · 60 comments
Open

mock-aws-s3, aws-sdk, nock dependencies vs devDependencies issue #661

johnmanko opened this issue Jul 6, 2022 · 60 comments

Comments

@johnmanko
Copy link

johnmanko commented Jul 6, 2022

Usage of mock-aws-s3, aws-sdk, and nock are used in the call stack originating from lib/node-pre-gyp.js (which is the package main), but they are only listed in "devDependencies".

    "aws-sdk": "^2.1012.0",
    "mock-aws-s3": "^4.0.2",
    "nock": "^13.1.4",

This causes a problem with webpack:

ERROR in ../project/node_modules/@mapbox/node-pre-gyp/lib/util/s3_setup.js 43:20-42
Module not found: Error: Can't resolve 'mock-aws-s3' in '/Path/to/project/node_modules/@mapbox/node-pre-gyp/lib/util'
 @ ../project/node_modules/@mapbox/node-pre-gyp/lib/node-pre-gyp.js 15:21-62
 @ ../project/node_modules/bcrypt/bcrypt.js 3:17-48
 @ ./src/lib/server/initialize.ts 3:0-69 9:12-39 21:23-56
 @ ./src/index.ts 12:0-53 51:4-19

ERROR in ../project/node_modules/@mapbox/node-pre-gyp/lib/util/s3_setup.js 76:14-32
Module not found: Error: Can't resolve 'aws-sdk' in '/Path/to/project/node_modules/@mapbox/node-pre-gyp/lib/util'
 @ ../project/node_modules/@mapbox/node-pre-gyp/lib/node-pre-gyp.js 15:21-62
 @ ../project/node_modules/bcrypt/bcrypt.js 3:17-48
 @ ./src/lib/server/initialize.ts 3:0-69 9:12-39 21:23-56
 @ ./src/index.ts 12:0-53 51:4-19

ERROR in ../project/node_modules/@mapbox/node-pre-gyp/lib/util/s3_setup.js 112:15-30
Module not found: Error: Can't resolve 'nock' in '/Path/to/project/node_modules/@mapbox/node-pre-gyp/lib/util'
 @ ../project/node_modules/@mapbox/node-pre-gyp/lib/node-pre-gyp.js 15:21-62
 @ ../project/node_modules/bcrypt/bcrypt.js 3:17-48
 @ ./src/lib/server/initialize.ts 3:0-69 9:12-39 21:23-56
 @ ./src/index.ts 12:0-53 51:4-19

Either those modules should be moved to "dependencies", or the main call chain should never hit them.

@bhallstein
Copy link

I've encountered the same issue while trying to compile a back-end app with webpack for limited distribution. @johnmanko's analysis definitely looks correct to me.

@gaberudy
Copy link

I encountered this issue when depending on bcrypt, which depends on @mapbox/node-pre-gyp and using rollup as a bundler. These seem to be unnecessary hard dependencies for this package.

@johnmanko
Copy link
Author

@gaberudy

I ended up having to remove bcrypt and replaced it with the native node crypto:

        const {
            scryptSync,
            randomBytes
        } = await import('crypto');

        if (salted) {
            const hash = scryptSync(password, salted, 64).toString('hex');
            return new HashAndSalt(hash, salted);
        } else {
            const salt = randomBytes(32).toString("hex");
            const hash = await scryptSync(password, salt, 64).toString('hex');
            return new HashAndSalt(hash, salt);
        }

@JustroX
Copy link

JustroX commented Sep 14, 2022

I encountered this issue using nest js. I solved it by removing webpack from compilerOptions

   // I remove this block
  "compilerOptions": {
    "webpack": true
  }

@bthibault
Copy link

+1

@Karhide
Copy link

Karhide commented Sep 28, 2022

+1, encountered depending on node-sqlite3

@eau-de-la-seine
Copy link

I have encountered the same problem and opened an issue at bcrypt yesterday so they improve their README, but my bad I didn't know that was a recent node-pre-gyp issue. Anyway, dev dependencies should not be used in production files

@tintinthong
Copy link

Is there a workaround for this issue? I face this problem

@Glandos
Copy link

Glandos commented Dec 9, 2022

Bad workaround, add this to your webpack.config.js:

const config = {
// Some other config
	  externals: [
    {
      'nock': 'commonjs2 nock',
      'mock-aws-s3': 'commonjs2 mock-aws-s3'
    }
  ],
}

This will tell webpack to let the require call as-is. It can obviously fail at runtime, and the required statement are still here.

@tintinthong
Copy link

tintinthong commented Dec 10, 2022

Bad workaround, add this to your webpack.config.js:

const config = {
// Some other config
	  externals: [
    {
      'nock': 'commonjs2 nock',
      'mock-aws-s3': 'commonjs2 mock-aws-s3'
    }
  ],
}

This will tell webpack to let the require call as-is. It can obviously fail at runtime, and the required statement are still here.

Downside of this is that you need to handle all the runtime errors (like nested runtime dependencies) can take a long long time

@tobilg
Copy link

tobilg commented Dec 12, 2022

I stumbled upon this as well when trying to bundle the DuckDB Node.js package. This additionally attempts to require npm as well :-/ I manually added nock and mock-aws-s3 as dev dependencies, and set npm as external. Then, it still complains that the package's dependencies html and cs files as well.

Using esbuild is seemingly also not an option, as this produces incorrect builds as well, due to node-pre-gyp...

@NowyQuei
Copy link

some error here...

@therealokoro
Copy link

Hmmm. So i got same error while using bcrypt in a nuxt app. And i just kept on thinking what nuxt had to do with aws. It was frustrating. I just kept on searching online and stumbled at this

@martinszeltins
Copy link

I got this error using Vite and sqlite in an Electron.js app

@vtulin
Copy link

vtulin commented Jan 13, 2023

+1 with sqlite3

@federicofazzeri
Copy link

+1 with Vite and sqlite (Sveltekit app)

@bemon
Copy link

bemon commented Feb 10, 2023

+1 electron + sqlite

@esonwong
Copy link

I solved it by adding externals: ['nock', 'mock-aws-s3', 'aws-sdk'], in webpack config

same as here

@tobilg
Copy link

tobilg commented Feb 16, 2023

I solved it by adding externals: ['nock', 'mock-aws-s3', 'aws-sdk'], in webpack config

same as here

I guess that won‘t help you if the code is actually run, because then the dependencies are missing IMO

@gamebox
Copy link

gamebox commented Feb 27, 2023

Having the same issue depending on bcrypt inside of an Astro project.

@johninator
Copy link

johninator commented Feb 28, 2023

+1 with sqlite3

+1 with sqlite3

@Letsmoe
Copy link

Letsmoe commented Mar 2, 2023

+1 depending on sqlite3 inside an Astro project...

@Erchoc
Copy link

Erchoc commented Mar 6, 2023

+1 sqlite3

@sethgw
Copy link

sethgw commented Mar 12, 2023

+1 sqlite3. send help

joe-re added a commit to joe-re/sql-language-server that referenced this issue Mar 12, 2023
@costinsin
Copy link

Can't those requires be wrapped in try-catch blocks in code to stop bundlers from throwing lint errors?

@andreia-oca
Copy link

@springmeyer I can propose a PR to fix this issue.

I am proposing a try/catch block around the require() line. What do you think?

@pacifiquem
Copy link

I got the same issue while trying to install swaggiffy and it was caused by sqlite3
Screenshot from 2023-06-24 16-13-14
anyone who can help

@pacifiquem
Copy link

pacifiquem commented Jun 24, 2023

+1 swaggify

@Shakahs
Copy link

Shakahs commented Jun 24, 2023

I too encountered this when using sqlite3 in NodeJS.

My simple workaround is to just add the unnecessary dependencies to my project, it builds fine now.

yarn add @mapbox/node-pre-gyp@^1.0.10 aws-sdk@^2.1396.0 mock-aws-s3@^4.0.2 nock@^13.3.1

@DanWizard
Copy link

Was there a fix for this?

@pelletier197
Copy link

My simple workaround is to just add the unnecessary dependencies to my project, it builds fine now.

No doubt this is going to work, but I don't want to bundle a bunch of useless things in my app (and you shouldn't either).

I found a very ugly but functional work around for Vite. For info, I am using the latest version of vite-plugin-electron. What I did is that I created an alias for all the libraries that were causing errors to an empty file.

// vite.config.ts

export default defineConfig({
  plugins: [
  // ...
      electron([
      {
        entry: resolve('./src/main/electron.ts'),
        vite: {
          build: {
            // Whatever you have here
          },
          resolve: {
            alias: {
               // Your other aliases if you have some
              "sqlite3": resolve(__dirname, 'src/main/empty.ts'),
              "pg": resolve(__dirname, 'src/main/empty.ts'),
              "pg-query-stream": resolve(__dirname, 'src/main/empty.ts'),
              "tedious": resolve(__dirname, 'src/main/empty.ts'),
              "mysql": resolve(__dirname, 'src/main/empty.ts'),
              "mysql2": resolve(__dirname, 'src/main/empty.ts'),
              "oracledb": resolve(__dirname, 'src/main/empty.ts'),
            },
          }
        },
      },
    ]),
 ]
})

And in empty.ts, I left an empty file with a beautiful comment.

/**
 * This file is used to override problematic dependencies when using node-pre-gyp.
 * 
 * These dependencies are imported but are never used and cause the application to crash at runtime.
 *
 * See this issue: https://github.com/mapbox/node-pre-gyp/issues/661
 */

I hope we'll get a better fix than that real soon, cause this is without a doubt one of the worst hack I've had to do in a long time.

@lucasth-dev
Copy link

My simple workaround is to just add the unnecessary dependencies to my project, it builds fine now.

No doubt this is going to work, but I don't want to bundle a bunch of useless things in my app (and you shouldn't either).

I found a very ugly but functional work around for Vite. For info, I am using the latest version of vite-plugin-electron. What I did is that I created an alias for all the libraries that were causing errors to an empty file.

// vite.config.ts

export default defineConfig({
  plugins: [
  // ...
      electron([
      {
        entry: resolve('./src/main/electron.ts'),
        vite: {
          build: {
            // Whatever you have here
          },
          resolve: {
            alias: {
               // Your other aliases if you have some
              "sqlite3": resolve(__dirname, 'src/main/empty.ts'),
              "pg": resolve(__dirname, 'src/main/empty.ts'),
              "pg-query-stream": resolve(__dirname, 'src/main/empty.ts'),
              "tedious": resolve(__dirname, 'src/main/empty.ts'),
              "mysql": resolve(__dirname, 'src/main/empty.ts'),
              "mysql2": resolve(__dirname, 'src/main/empty.ts'),
              "oracledb": resolve(__dirname, 'src/main/empty.ts'),
            },
          }
        },
      },
    ]),
 ]
})

And in empty.ts, I left an empty file with a beautiful comment.

/**
 * This file is used to override problematic dependencies when using node-pre-gyp.
 * 
 * These dependencies are imported but are never used and cause the application to crash at runtime.
 *
 * See this issue: https://github.com/mapbox/node-pre-gyp/issues/661
 */

I hope we'll get a better fix than that real soon, cause this is without a doubt one of the worst hack I've had to do in a long time.

Worked for me temporally! Using electron-forge and sqlite3 here.
Gonna often check this issue since it's 1 week fresh problem for me and I don't know why or how to correctly fix it

@paintedwood
Copy link

paintedwood commented Aug 2, 2023

I made a workaround for this issue that completely removes node-pre-gyp from the packaged electron application.

I am no expert in making native packages for nodejs. I use them, as many people do, and I recently tried to package one of them, node-sqlite3, to be a part of electron app.

I use webpack and electron-forge for packaging. This tools produces a self-contained package, in the form of zip archive, debian, or rpm package. The package is OS and CPU architecture-specific (e.g. linux-x64) and it contains, as a part of electron binary, a specific version of nodejs.

In the usual setup, webpack collects the bundle which contains the node-sqlite3 package. That bundle is collected from the local files. At that point, the node-sqlite3 package already contains platform-specific binary files. As a next step, that webpack bundle is packaged along with the electron runtime to the resulting application package.

When running the resulting electron application on the end user machine, it is not expected to download any additional native binaries, compile them, cross-compile binaries to target other platforms, or upload compiled native binaries to the cloud storage.

Problem is, that functionality is a part of node-pre-gyp and it's dependencies, and all that, being the runtime dependency of node-sqlite3, is packed by the webpack to the resulting application bundle.

That packaging process is not without issues: in the current version of node-pre-gyp it may require to have a webpack configuration workaround of externals: ['aws-sdk', 'mock-aws-s3', 'nock'].

The bigger picture is, as I see it, that node-pre-gyp should not be a part of electron application bundle at all. It have it's place in the build pipeline, but carrying it further to the end-users should be avoided.

I may not see some cases when it do required, but as far as I could tell, native modules, as soon as they are installed, could work fine without node-pre-gyp.

At runtime, node-sqlite3 uses node-pre-gyp to resolve native module filename. There is a "./lib/binding/napi-v{napi_build_version}-{platform}-{libc}-{arch}" template string in package.json, it is used to locate a native module file for the specific environment.

The electron application is already packaged for a specific environment, so I believe that template string could be resolved to a static path at the time of application packaging. That will effectively remove the need to include node-pre-gyp in the electron application package.

The following code could be added to a webpack configuraton file to do exactly that.

const path = require('path');
const fs = require('fs');
const nodePreGyp = require('@mapbox/node-pre-gyp');
const webpack = require('webpack');

// The following block assumes that the current file is in the project root, so we could calculate paths as relative to __dirname
const sqliteBindingPath = nodePreGyp.find(path.join(__dirname, 'node_modules', 'sqlite3', 'package.json'));
const sqlitePatchFileName = 'sqlite3-binding-with-binary-find-patch.js';
const sqlitePatchFilePath = path.resolve(path.join(__dirname, 'node_modules', 'sqlite3', 'lib', sqlitePatchFileName));

// Here we might write an absolute path. Webpack will take care to produce a portable bundle, in which this 
// absolute path will be changes to a path, relative to the bundle itself.
fs.writeFileSync(sqlitePatchFilePath, `module.exports = require(${JSON.stringify(sqliteBindingPath)});`);

// Add this to your webpack config
module.exports = {
  plugins: [
    new webpack.NormalModuleReplacementPlugin(
      /sqlite3\/lib\/sqlite3-binding\.js$/,
      `./${sqlitePatchFileName}`
    )
  ]
};

The resulting bundle will not contain node-pre-gyp and it's dependencies. That will solve the current issue with packing (externals: ['aws-sdk' ...]) and it will make bundle lighter by 194 KB.

I hope that this workaround might be of an inspiration to someone to create a more universal solution. It could be a lightweight runtime as a part of node-pre-gyp, or, if that is not possible, as a form of some alternative lightweight project. Maybe it could be solved on a side of packages such as node-sqlite3, and not on the node-pre-gyp side. Maybe package managers such as npm could came up with some abstractions to handle native modules that could help in this regard.

@Maxou44
Copy link

Maxou44 commented Aug 9, 2023

Same problem with the bcrypt package, any chance of this bug being fixed?

@yyassif
Copy link

yyassif commented Aug 17, 2023

Solved for me!
I encountered this in Next 13 app directory in client side component where I am using a useTransition hook to transition the bookmarking of a post either bookmark or unbookmark it by invoking a server action function in a separate folder called actions where I have my bookmarks.ts file where I am invoking prisma transaction for deleting or creating.
The solution was to add "use server" at the top of file so that function can only be executed at the server-side level.

@MuhammadDev-OP
Copy link

Same problem using NextJS.13 with Mongo and Prisma. And I actually found that it's happening due to Bcrypt.

@CostinBagotai
Copy link

Found a fix for my case.
Encountered this when I ran yarn make using Electron-Forge-Webpack-Typescript template to which I added sqlite3 package.
I just installed the packages from the error as dev dependencies:
yarn add -D mock-aws-s3 aws-sdk nock

Now it works, compiles sqlite3 with no additional issues.

@lucasreppewelander
Copy link

Not sure if this is an accepted approach but i did this to get it work, my stack is currently using sqlite3 together with electron and vite so this my vite config:

import { builtinModules } from 'module'
import { dependencies } from './package.json'

export default defineConfig({
  main: {
    build: {
      rollupOptions: {
        external: [
          'electron',
          ...builtinModules,
          ...Object.keys(dependencies || {}),
        ],
      },
    }
  },
  preload: {},
  renderer: {
    plugins: [react()]
  }
});

and now I can at least run my application in dev mode which I couldn't before, we will see how it goes later when I'm gonna ship my app.

ggwp

@notramo
Copy link

notramo commented Sep 27, 2023

I worked around this by putting the packages that depend on this in the "dependencies" section instead of "devDependencies" in package.json.

Also, if you use "bcrypt", you can try Bun, which has Bcrypt (and Argon2 too) in the standard library without installing any packages (https://bun.sh/guides/util/hash-a-password).

@boylett
Copy link

boylett commented Oct 4, 2023

Experienced the same problem when attempting to build with Remix, happens with both argon2 and bcrypt packages.

For me, the fix was to install bcryptjs rather than bcrypt.

@notramo
Copy link

notramo commented Oct 6, 2023

Also worth trying the @node-rs/argon2 and @node-rs/bcrypt packages which are bindings to Rust implementations, and don't use node-pre-gyp, but napi-rs.

As a plus feature, these are faster than the bcrypt or bcryptjs implementations, according to the benchmarks.

@fcanela
Copy link

fcanela commented Oct 7, 2023

This issue has caused me an incredible amount of lost time trying to work around it. There are some suggested solutions to get sqlite3 (which depends on this package) and vite work, but they didn't work for me.

To continue the development, I am falling back into implementing a custom file storage without SQL until this is fixed and I can revert to sqlite3.

I'm not mentioning this not because I think I am entitled to having this software working, but to provide visibility on the impact it's having.

@notramo
Copy link

notramo commented Oct 9, 2023

@fcanela despite Bun is in a very experimental stage, it's worth keeping an eye on it, as it has SQLite in the standard library, out of the box, without any precompiled dependency.
https://bun.sh/docs/api/sqlite

Its standard library provides solutions to a big part of the problems described in this thread.

@adaboese
Copy link

A snippet for anyone if you rather just migrate from bcrypt to scrypt:

import { randomBytes, scrypt, timingSafeEqual } from 'node:crypto';

/**
 * https://dev.to/advename/comment/24a9e
 */
const keyLength = 32;

/**
 * Has a password or a secret with a password hashing algorithm (scrypt)
 * @param {string} password
 * @returns {string} The salt+hash
 */
export const hash = async (password: string) => {
  return new Promise((resolve, reject) => {
    // generate random 16 bytes long salt - recommended by NodeJS Docs
    const salt = randomBytes(16).toString('hex');

    scrypt(password, salt, keyLength, (error, derivedKey) => {
      if (error) reject(error);
      // derivedKey is of type Buffer
      resolve(`${salt}.${derivedKey.toString('hex')}`);
    });
  });
};

/**
 * Compare a plain text password with a salt+hash password
 * @param {string} password The plain text password
 * @param {string} hash The hash+salt to check against
 * @returns {boolean}
 */
export const compare = async (password: string, hash: string) => {
  return new Promise((resolve, reject) => {
    const [salt, hashKey] = hash.split('.');
    // we need to pass buffer values to timingSafeEqual
    const hashKeyBuff = Buffer.from(hashKey, 'hex');
    scrypt(password, salt, keyLength, (error, derivedKey) => {
      if (error) reject(error);
      // compare the new supplied password with the hashed password using timeSafeEqual
      resolve(timingSafeEqual(hashKeyBuff, derivedKey));
    });
  });
};

@Pan332
Copy link

Pan332 commented Apr 20, 2024

I use bcryptjs instead of bcrypt and it worked
or
If you want to replace bcrypt with another library, you can consider using bcryptjs, argon2, or scrypt

@adrianszablowski
Copy link

adrianszablowski commented Apr 24, 2024

I use bcryptjs instead of bcrypt and it worked or If you want to replace bcrypt with another library, you can consider using bcryptjs, argon2, or scrypt

Exacly! I installed bycryptjs instead of bycrypt and the problem disappeared

@wenjoy
Copy link

wenjoy commented May 6, 2024

This bug looks suffer a lot of people and seems still no PR to resolve it. I think it should be easy to solve it by changing dependencies declaration. Should I try to raise the PR?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests