quasar: From @quasar/app v3.0.0-beta.16, Show compile error Can't resolve imported dependency "crypto"

Describe the bug

My electron app use crypto lib UI, it works @quasar/app v3.0.0-beta.15

BUT, it don’t work from @quasar/app v3.0.0-beta.16 ~ 20 shows some compile error

[19:58:17] Starting 'desktop'...

 Dev mode.......... electron
 Pkg quasar........ v2.0.0-beta.12
 Pkg @quasar/app... v3.0.0-beta.16
 Pkg webpack....... v5
 Debugging......... enabled

 Configured browser support (at least 87.68% of global marketshare):
 · Chrome for Android >= 90
 · Firefox for Android >= 87
 · Android >= 90
 · Chrome >= 80
 · Edge >= 87
 · Firefox >= 79
 · iOS >= 11.0-11.2
 · Opera >= 71
 · Safari >= 11.1

 App • Chaining "Renderer" Webpack config
 App •  WAIT  • Compiling of "Renderer" in progress...
 App •  DONE  • "Renderer" compiled with errors • 22614ms



 App •  ERROR  •  Renderer  in ./node_modules/bsv/lib/crypto/hash.node.js

Module not found: Can't resolve imported dependency "crypto"
Did you forget to install it? You can run: yarn add crypto

 App •  ERROR  •  Renderer  in ./node_modules/bsv/lib/mnemonic/pbkdf2.js

Module not found: Can't resolve imported dependency "crypto"
Did you forget to install it? You can run: yarn add crypto

 App •  ERROR  •  Renderer  in ./node_modules/dotwallet/src/utils.js

Module not found: Can't resolve imported dependency "crypto"
Did you forget to install it? You can run: yarn add crypto

 App •  ERROR  •  Renderer  in ./node_modules/abort-controller/polyfill.mjs

Module not found: Can't resolve imported dependency "./dist/abort-controller"


 App •  COMPILATION FAILED  • Please check the log above for details.

Platform (please complete the following information): Quasar Version: v2.0.0-beta.12 @quasar/app Version: @quasar/app v3.0.0-beta.16 ~ 20 Quasar mode:

  • SPA
  • SSR
  • PWA
  • Electron
  • Cordova
  • Capacitor
  • BEX

Tested on:

  • SPA
  • SSR
  • PWA
  • Electron
  • Cordova
  • Capacitor
  • BEX

OS: Mac OS Node: 12 NPM: Yarn: Browsers: iOS: Android: Electron: 12

About this issue

  • Original URL
  • State: closed
  • Created 3 years ago
  • Comments: 15 (9 by maintainers)

Most upvoted comments

It is not important here but I am not using electron mode (SPA/PWA instead). I am using dropbox sdk (yarn add dropbox) and just installing it might be enough to reproduce the error (which is related to webpack 5). I had below error for two packages - crypto and util:

App •  ERROR  •  UI  in ./node_modules/dropbox/dist/Dropbox-sdk.min.js

Module not found: Can't resolve imported dependency "crypto"

To solve the issue I had to run yarn add -D node-polyfill-webpack-plugin and configure the plugin in quasar.conf.js:

const NodePolyfillPlugin = require('node-polyfill-webpack-plugin');
module.exports = configure(function (ctx) {
  return {
    build: {
      extendWebpack(cfg) {
        cfg.plugins.push(new NodePolyfillPlugin({}));
      }
    }
  }
}

The solution is based on https://github.com/dropbox/dropbox-sdk-js/issues/614.

crypto (as well any electron/node module) would be exposed/handled by your preload script: Read more on: https://next.quasar.dev/quasar-cli/developing-electron-apps/electron-preload-script

here a example, where I’m handling electron and node stuff: src-electron/electron-preload.ts

import { ipcRenderer, contextBridge, App } from 'electron';
import fs from 'fs';
import crypto from 'crypto';

type GetPath = App['getPath'];
const getPath: GetPath = (name) => {
  // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
  const result = ipcRenderer.sendSync('remote-get-path', name);
  return result as string;
};

const preload = {
  // probably u don't wanna to expose the whole fs module here
  // but if some of your dependencies relly on it, u'll be forced to do that.
  node: {
    fs() {
      return fs;
    },
    crypto() {
      return crypto;
    },
  },
  electron: {
    getPath,
  },
};

contextBridge.exposeInMainWorld('preload', preload);

src-electron/electron-main.ts

import { app, BrowserWindow, ipcMain } from 'electron';
import path from 'path';

function createWindow() {
  mainWindow = new BrowserWindow({ /*....*/ })
  
  ipcMain.on('remote-get-path', (event, path) => {
    event.returnValue = app.getPath(path)
  });
}

if u’re using typescript, don’t forget to declare the types:

src/global.d.ts

export {};

declare global {
  interface Window {
    preload: {
      node: {
        fs: () => typeof import('fs');
        crypto: () => typeof import('crypto');
      };
      electron: {
        getPath: import('electron').App['getPath'];
      };
    };
  }
}

so, u can access them through the window.preload.node path/to/somewhere.ts

export default {
  setup () {
    // even if now u're supporting only the electron mode, I highly recommend you to write universal code.
    // in this case, interfaces and dependency injection are u friend, just declare the interface (and/or an abstract class)
    // so implement and register the service based on that interface in a boot what are registered only at the electron mode.
    // so if u need add support to other module, all what u need to do, is add a new boot with the desired implementation
    if (process.env.MODE === 'electron') {
      const { getPath } = window.preload.electron
      const { fs, crypto } = window.preload.node
    }
  }
}

in the case of that packages are required by 3th party libs, like serialport, aws-polly, etc, u’ll need to declare the needed libs as a external (so the packages will make use of the preloaded packages):

quasar.config.js

module.exports = configure(function (ctx) {
  return {
    build: {
      chainWebpack (chain) {
        if (ctx.mode.electron) {
          chain.merge({
            externals: {
              fs: 'window.preload.node.fs()',
              crypto: 'window.preload.node.crypto()'
            }
          })
        }
      }
    }
  }
}

registering that externals, u’ll be able to import that packages directly in your code, but I think that is a little hacky:

import fs from 'fs'
import crypto from 'crypto'

This worked for me for directly including crypto in a component (e.g. import md5 from 'crypto-js/md5':

  1. yarn add crypto-js
  2. Edit quasar.conf.js:
      extendWebpack (cfg) {
        cfg.resolve.fallback = { crypto: false }
      },

@baryon Have you tried removing node_modules and yarn.lock and re-installing?