mime-model
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.
Usage
$ npm install mime-model
const { MimeNode } = require('mime-model');
Parse from EML
const eml = fs.readFileSync('path/to/email.eml');
const node = MimeNode.from(eml);
Create from scratch
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
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!
multipart
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) {
// ...
}
}
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
Add a child node to a multipart node
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);
Remove a child node
Removes a child node from the parent node and returns the removed node.
parentNode.removeChild(childNode);
or
childNode.delete();
Check Content-Type
MIME-type of the node or null
if not set.
node.contentType -> String
Example
if (node.contentType === 'image/png') {
console.log('Image attachment');
}
Node editing
All the following properties can be used as content options when creating new nodes using MimeNode.create()
.
subject
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 ✅';
date
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
encoding
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';
}
charset
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';
}
content
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';
filename
Read or set the file name for the node.
imageNode.filename = 'Image ✅.png';
disposition
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';
contentId
Read or set Content-Id value.
imageNode.contentId = '<unique-id@example.com>';
addresses
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>
messageId
Read or set Message-Id value.
console.log(rootNode.messageId);
// " <28b35c92-852a-4862-b654-72e25f091223@example.com>"
headers
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' ]
]
Get specific header value
Returns a list of parsed and encoded values for a header key. Each header key has a specific format.
node.getHeader(key) -> Array
The response is always an array as any header key can be defined multiple times. If you only need the oldest/bottom value, use the last entry from the array.
Example
Content-Disposition: attachment;
filename*0*=utf-8''nyan%20cat%20%E2%9C%94.gif
let headerValueArray = node.getHeader('content-disposition');
console.log(headerValueArray[0].params.filename);
// "nyan cat ✔.gif"
resetContent()
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
removeHeaders()
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);
setHeaders()
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"
removeBody()
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.
setBody()
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);
License
MIT