'nodemailer.createTransport is not a function, possible webpack v5 polyfill issue
I am trying to get nodemailer to work in my Quasar/Electron project. Before Webpack V5, all was working fine. However, in my new project using Webpack V5, which is a breaking change, I'm really struggling to get it to work.
I have modified my quasar.conf.js file as is recommended: https://quasar.dev/quasar-cli/handling-webpack#webpack-v5-compatibility-issues
So my quasar.config.js now includes the following code:
const { configure } = require('quasar/wrappers');
const NodePolyfillPlugin = require('node-polyfill-webpack-plugin');
extendWebpack: cfg => {
cfg.plugins.push(new NodePolyfillPlugin({}));
},
and my package.json has the code:
"browser": {
"nodemailer": false
}
Before these changes, I was getting the following error messages (which are now gone):
BREAKING CHANGE: webpack < 5 used to include polyfills for node.js core modules by default.
This is no longer the case. Verify if you need this module and configure a polyfill for it.
If you want to include a polyfill, you need to:
- add a fallback 'resolve.fallback: { "path": require.resolve("path-browserify") }'
- install 'path-browserify'
If you don't want to include a polyfill, you can use an empty module like this:
resolve.fallback: { "path": false }
So now I can pull in nodemailer into my Quasar/Electron app. My .vue page has this:
var nodemailer = require('nodemailer')
So far so good.
My next step is to put in the nodemailer code, and I've copied/pasted from the nodemailer site and I'm using a test email I created from ethereal email (I eventually want to use gmail):
let transporter = nodemailer.createTransport({
host: "smtp.ethereal.email",
port: 587,
secure: false, // true for 465, false for other ports
auth: {
user: '[email protected]',
pass: 'testpass'
},
});
// send mail with defined transport object
let info = await transporter.sendMail({
from: '"Fred Foo 👻" <[email protected]>', // sender address
to: "[email protected], [email protected]", // list of receivers
subject: "Hello ✔", // Subject line
text: "Hello world?", // plain text body
html: "<b>Hello world?</b>", // html body
});
However, I am now getting the error message:
peopleDetails.vue?70bb:287 Uncaught (in promise) TypeError: nodemailer.createTransport is not a function
So... really, I am not sure if my error is because of the whole Webpack v5 polyfill thing, or if it's a nodemailer problem. I need to send emails from this application. I do have an emailjs account, but this is going to be a desktop application and users will need to be able to send their own emails from their own email addresses. I want users to be able to send formatted emails both through SMTP and through their own desktop email client. The email client can work by itself (I have not tackled how to incorporate this, yet), but some users will have to send 50-100 emails at a time, so sending them in the background would be beneficial.
I would love help. I've been fighting this for two days. Thanks!
Solution 1:[1]
I figured this out, and it's not a polyfill issue. I hope my answer can help someone else:
If anyone is using Quasar/Electron and wants to use nodemailer. This works for me. Nodemailer must be run as a node process and can't be run on the renderer process.
I put the nodemailer code inside the electron-preload.js file (under the src-electron directory).
Electron uses Context Isolation. Therefore, the nodemailer code needs to be put into the electron-preload.js file. Just putting it in any old .vue file will cause an error. The error I was getting was: nodemailer.createTransport is not a function
In order to overcome the Context Isolation, I had to use the Context Bridge as explained in this electron documentation: https://www.electronjs.org/docs/latest/tutorial/context-isolation
For testing purposes, I'm using ethereal.email as suggested in the nodemailer docs.
electron-preload.js:
import { contextBridge } from 'electron'
contextBridge.exposeInMainWorld('myAPI', {
doAThing: (emailFrom, emailTo, emailSubject, emailHTML) => {
var nodemailer = require("nodemailer")
let transporter = nodemailer.createTransport({
host: "smtp.ethereal.email",
port: 587,
secure: false, // true for 465, false for other ports
auth: {
user: '[email protected]',
pass: 'password'
},
})
const mailOptions = {
from: emailFrom,
to: emailTo,
subject: emailSubject,
html: emailHTML,
}
transporter.sendMail(mailOptions, function (err, info) {
if (err) console.log(err);
else console.log(info);
})
console.log("in the PreLoad Script")
}
}) // end contextBridge.exposeInMainWorld
Then, in my .vue file (I'm calling mine personDetails.vue), I have a button, and when clicks it fires off this method:
async sendEmailMethodSMTP() {
myAPI.doAThing(this.emailFrom, this.emailTo, this.emailSubject, this.emailHTML )
}, // end sendEmailMethodSMTP
I'm collecting the variable information in the information I'm passing from the .vue file (this.emailFrom, this.emailTo, this.emailSubject, this.emailHTML), to the doAThing method in the electron-proload.js file.
When the button is pressed, the information is passed to the preloader, nodemailer is fired off, and the email is sent.
I have not compiled my Quasar/Electron app to make sure this works in a live environment, but running quasar in dev mode this is working great.
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 | Cynthia |

