Create and parse MIME nodes.
See the examples folder for usage examples.
This project is supported by Opensense - The beautiful email signature management company for Office 365 and Google Workspace.
$ npm install mime-model
const { MimeNode } = require('mime-model');
const eml = fs.readFileSync('path/to/email.eml');
const node = MimeNode.from(eml);
const node = MimeNode.create(contentType, {
// content options
});
See possible content options below.
In addition to normal content options, you can use the following special options:
defaultHeaders
- iftrue
then sets default headers likeDate
,Message-ID
andMIME-Version
Serialize a mime node back to an EML file.
node.serialize([opts]) -> Buffer
Where opts
is an object that limits ouput:
- ops.headers if
true
will only return headers - ops.body if
true
will only return body of the node
If both are true, an empty value is returned.
Example
const node = MimeNode.create('text/plain', {
encoding: 'quoted-printable',
content: 'Hello world ๐!',
defaultHeaders: true
});
process.stdout.write(node.serialize());
Output
Content-Type: text/plain; charset=utf-8
Content-Transfer-Encoding: quoted-printable
Message-ID: <64300d0d-022d-4ad3-9212-ad84116ef922@mim>
Date: Sun, 06 Nov 2022 15:51:38 +0000
MIME-Version: 1.0
Hello world =F0=9F=94=86!
The multipart
property contains the multipart type for the node, like "mixed" or "alternative", etc. The value is null
if this node is not a multipart node.
node.multipart -> String
Example
if (node.multipart) {
for (let childNode of node.childNodes) {
// ...
}
}
The chldNodes
property contains an array of child nodes for a multipart node.
node.childNodes -> Array
Example
Prints out a tree structure of Content-Type values by traversing all child nodes in the MIME tree.
let walkNodes = (node, level) => {
console.log(' '.repeat(level) + node.contentType);
if (node.multipart) {
for (let childNode of node.childNodes) {
walkNodes(childNode, level + 1);
}
}
};
walkNodes(rootNode, 0);
Example output:
multipart/mixed
multipart/alternative
text/plain
text/html
image/png
node.appendChild(newNode);
or
node.insertBefore(newNode, referenceNode);
Example
const node = MimeNode.create('multipart/mixed', {
// set headers like Message-ID, Date, etc
defaultHeaders: true
});
const textNode = MimeNode.create('text/plain', {
encoding: 'quoted-printable',
content: 'Hello world ๐!'
});
node.appendChild(textNode);
Removes a child node from the parent node and returns the removed node.
parentNode.removeChild(childNode);
or
childNode.delete();
MIME-type of the node or null
if not set.
node.contentType -> String
Example
if (node.contentType === 'image/png') {
console.log('Image attachment');
}
All the following properties can be used as content options when creating new nodes using MimeNode.create()
.
Read or set the subject line. Unicode strings are used by default, so there is no need to encode or decode anything. Value is null
if the subject is not set.
console.log(node.subject); // "Subject line ๐"
node.subject = 'Another subject โ
';
Read or set date objects. Value is null
if the date is not set.
console.log(node.date); // 2022-11-05T19:51:24.992Z (Date object)
node.date = node.date.getTime() + 1000; // number is coerced to a date object
Read or set Content-Transfer-Encoding for a node.
// change content transfer encoding of a node from base64 to quoted-printable
if (node.encoding === 'base64') {
node.encoding = 'quoted-printable';
}
Read or set the character set for a text content node.
// enforce UTF-8 for ISO-2022-JP content
if (node.charset === 'iso-2022-jp') {
node.charset = 'utf-8';
}
Read or set data content. When reading, the value is a buffer in the character set of the node. Transfer encoding is handled silently, so the value is the actual content, not a base64 or quoted-printable string.
When writing a string, it is encoded to the charset of the node automatically. Buffer values are not modified.
If you need to read an unicode string, not a buffer value, use node.contentText
.
// content is binary buffer without encoding
let imageFile = imageNode.content;
fs.writeFileSync('image.png', imageFile);
// charsets for strings are handled silently in the background
textNode.content = textNode.contentText + ' suffix value';
Read or set the file name for the node.
imageNode.filename = 'Image โ
.png';
Read or set Content-Disposition value.
NB! This is not full header, but only the disposition identifier like "attachment" or "inline" or null
if not set.
imageNode.disposition = 'inline';
Read or set Content-Id value.
imageNode.contentId = '<unique-id@example.com>';
Read and set address field values. The returned value is a structured object with unicode strings. When setting an address value, you can use structured objects or a full header string without encoding.
- node.from
- node.to
- node.cc
- node.bcc
- node.sender
- node.replyTo
Example
node.from = 'Juulius ๐ญ <juulius@example.com>';
node.to = [{ name: 'Mรตdu ๐ฏ', address: 'modu@example.com' }];
console.log(node.from);
console.log(node.to);
// [ { address: 'juulius@example.com', name: 'Juulius ๐ญ' } ]
// [ { name: 'Mรตdu ๐ฏ', address: 'modu@example.com' } ]
process.stdout.write(node.serialize({ headers: true }));
// From: =?UTF-8?Q?Juulius_=F0=9F=93=AD?= <juulius@example.com>
// To: =?UTF-8?B?TcO1ZHUg8J+Nrw==?= <modu@example.com>
Read or set Message-Id value.
console.log(rootNode.messageId);
// " <28b35c92-852a-4862-b654-72e25f091223@example.com>"
Read node headers. The value is a list of tuples with header keys and values. The ordering is the same as in the serialized header.
node.headers -> Array
Example
console.log(node.headers);
[
[
'Subject', '=?UTF-8?Q?Nodemailer_is_unicode_friendly_=E2=9C=94?= =?UTF-8?Q?1656663957583?='
],
[ 'Content-Type', 'text/plain; charset=utf-8' ],
[ 'Content-Transfer-Encoding', 'quoted-printable' ],
[ 'Content-Disposition', 'inline' ],
[ 'Message-ID', '<3660bb9f-d645-4945-a492-2c291de3e6fb@mim>' ],
[ 'Date', 'Sun, 06 Nov 2022 13:44:10 +0000' ],
[ 'MIME-Version', '1.0' ]
]
Clears node content and sets a new content type value. Primarily useful if you want to convert a content node to a multipart node. See the setBody() example for usage.
node.resetContent(contentType);
Example
rootNode.resetContent('multipart/signed; protocol="application/pgp-signature"; micalg=pgp-sha512;');
NB MIME boundary is generated automatically for multipart nodes
Removes and returns headers with a specific key value. Useful when you want to move headers from one node to another.
node.removeHeaders(key) -> Array
Example
Move Content-Type headers from one node to another
const contentTypeHeaders = rootNode.removeHeaders('Content-Type');
bodyMimeNode.setHeaders(contentTypeHeaders);
Adds headers to a node. The input array either includes header entry objects returned by removeHeaders()
or full header strings.
node.setHeaders(headersArray);
Example
node.setHeaders([`Subject: =?UTF-8?Q?Nodemailer_is_unicode_friendly_=E2=9C=94?= =?UTF-8?Q?1656663957583?=`]);
console.log(node.subject); // "Nodemailer is unicode friendly โ1656663957583"
Removes and returns body structure from a node. Useful when you want to move body contents from one node to another.
node.removeBody() -> Object
This method works both for content nodes and multipart nodes.
Attach a body object from one node to another. You can get this object from the removeBody()
call.
node.setBody(bodyObj) -> Object
This method works both for content nodes and multipart nodes.
Example
Convert a regular mime node into a multipart node.
// remove body and relevant headers from the root node
const contentTypeHeaders = rootNode.removeHeaders('Content-Type');
const contentTransferEncodingHeaders = rootNode.removeHeaders('Content-Transfer-Encoding');
const mimeBody = rootNode.removeBody();
// create a new empty node and attach headers and body
const childNode = MimeNode.create(null);
childNode.setHeaders(contentTypeHeaders);
childNode.setHeaders(contentTransferEncodingHeaders);
childNode.setBody(mimeBody);
// force the root node into a multipart node and attach the child node to it
rootNode.resetContent('multipart/mixed');
rootNode.appendChild(childNode);
MIT