Closed baryon closed 3 years ago
@baryon Have you tried removing node_modules and yarn.lock and re-installing?
I had kind of the same "issue"... Maybe the following does help:
Since the most recent quasar version uses webpack 5 :fireworks:
There are no polyfills for node.js (core) modules any more.
I solved those kind of problems by providing crypto (and other core) accesses via an sanitized api and electrons context bridge!
@baryon Have you tried removing node_modules and yarn.lock and re-installing?
Tried it many times. Same issue.
@baryon Can you supply a minimal reproduction repo pls? So we can understand what the problem is.
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'
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.
Want a solution that doesn't require additional setup
There will not be a solution without "additional setup"... The webpack5 change is real! And what @TobyMosque recommended is the way to go. crypto
, path
, etc should not be available to the renderer.
@baryon Can you supply a minimal reproduction repo pls? So we can understand what the problem is.
wrote a demo project to reproduce this issue
@baryon
Took a look at your repo (thanks for supplying one!). A few things, due to how newer Electron versions require you to handle the app (for security reasons):
node-polyfill-webpack-plugin
package (as suggested above already).So, how to adapt your repo to the latest Electron specifications (AND not requiring any additional webpack setup):
// src-electron/electron-preload.js
import { contextBridge } from 'electron'
import bsv from 'bsv'
contextBridge.exposeInMainWorld('myAPI', {
getBsvAddress: () => {
const privkey = bsv.PrivateKey.fromRandom()
return privkey.toAddress().toString()
}
})
// src/pages/Index.vue
<script>
import { defineComponent } from 'vue';
import { ref } from 'vue'
export default defineComponent({
name: 'PageIndex',
setup() {
return {
address: ref(window.myAPI.getBsvAddress()) // <<<<<<<<<<<<<<
}
}
})
</script>
As a general rule of thumb, if you care about security and not working around (and against) Electron best practices then do not directly expose nodejs packages through the preload script. Instead, expose methods that themselves use these nodejs packages (do NOT expose nodejs packages methods directly).
Thanks for your reply. The app will be release with capacitor for mobile. It have same issue too. Welcome Any suggestions
This worked for me for directly including crypto in a component (e.g. import md5 from 'crypto-js/md5'
:
yarn add crypto-js
extendWebpack (cfg) {
cfg.resolve.fallback = { crypto: false }
},
Thanks @dsl101, I wasn't satisfied with adding polyfills in my setup but just setting crypto fallback to false (without adding crypto-js/md5) as presented above seems to work fine for my SPA/PWA setup. @rstoenescu I still need polyfills for Cypress config. I guess they might be needed for SSR and other modes. Seems like a feature request but adding polyfills/setting fallbacks based on mode might solve few problems for quite a lot of people. I didn't have time to go deeper into Webpack configuration but items like this one allowed me to experiment with different options and reach the state in which I am satisfied with Quasar v2.
I don't think it would be done on the Quasar side, I think the polyfills got removed for a good reason.
Even if we add a optional flag, like quasar.config.js > build > node > polyfill: true
, that can pass the message the polyfills got removed only to same some bytes, but in the true, you're getting a lot of risks by doing that.
But if u know u risks, u can install it.: https://www.npmjs.com/package/node-polyfill-webpack-plugin
Hi @TobyMosque, thank you for the reply. I am aware that there are a lot of risks related to each package and those even include cases like using Quasar. It is not easy to balance DX vs security but I trust you make better decision than I would since you know the ecosystem a lot better than me. In the end, I wanted to share what problems and possible solutions I have encountered so far using Quasar v2 (and Webpack 5 indirectly) and let you know where is the potential to improve DX. This is still the only item I've found related to webpack no longer polyfilling node packages so I guess others might find some useful information here.
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
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:
Tested on:
OS: Mac OS Node: 12 NPM: Yarn: Browsers: iOS: Android: Electron: 12