nodemailer 8.0.2 → 8.0.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -5,7 +5,7 @@
5
5
  const crypto = require('crypto');
6
6
  const fs = require('fs');
7
7
  const punycode = require('../punycode');
8
- const PassThrough = require('stream').PassThrough;
8
+ const { PassThrough } = require('stream');
9
9
  const shared = require('../shared');
10
10
 
11
11
  const mimeFuncs = require('../mime-funcs');
@@ -19,6 +19,8 @@ const LastNewline = require('./last-newline');
19
19
  const LeWindows = require('./le-windows');
20
20
  const LeUnix = require('./le-unix');
21
21
 
22
+ const FORMATTED_HEADERS = ['From', 'Sender', 'To', 'Cc', 'Bcc', 'Reply-To', 'Date', 'References'];
23
+
22
24
  /**
23
25
  * Creates a new mime tree node. Assumes 'multipart/*' as the content type
24
26
  * if it is a branch, anything else counts as leaf. If rootNode is missing from
@@ -54,7 +56,7 @@ class MimeNode {
54
56
  /**
55
57
  * If date headers is missing and current node is the root, this value is used instead
56
58
  */
57
- this.date = new Date();
59
+ this.date = options.parentNode ? null : new Date();
58
60
 
59
61
  /**
60
62
  * Root node for current mime tree
@@ -175,7 +177,7 @@ class MimeNode {
175
177
  options = contentType;
176
178
  contentType = undefined;
177
179
  }
178
- let node = new MimeNode(contentType, options);
180
+ const node = new MimeNode(contentType, options);
179
181
  this.appendChild(node);
180
182
  return node;
181
183
  }
@@ -256,8 +258,7 @@ class MimeNode {
256
258
  * @return {Object} current node
257
259
  */
258
260
  setHeader(key, value) {
259
- let added = false,
260
- headerValue;
261
+ let added = false;
261
262
 
262
263
  // Allow setting multiple headers at once
263
264
  if (!value && key && typeof key === 'object') {
@@ -280,7 +281,7 @@ class MimeNode {
280
281
 
281
282
  key = this._normalizeHeaderKey(key);
282
283
 
283
- headerValue = {
284
+ const headerValue = {
284
285
  key,
285
286
  value
286
287
  };
@@ -404,8 +405,8 @@ class MimeNode {
404
405
  });
405
406
  }
406
407
 
407
- let stream = this.createReadStream();
408
- let buf = [];
408
+ const stream = this.createReadStream();
409
+ const buf = [];
409
410
  let buflen = 0;
410
411
  let returned = false;
411
412
 
@@ -445,7 +446,7 @@ class MimeNode {
445
446
 
446
447
  getTransferEncoding() {
447
448
  let transferEncoding = false;
448
- let contentType = (this.getHeader('Content-Type') || '').toString().toLowerCase().trim();
449
+ const contentType = (this.getHeader('Content-Type') || '').toString().toLowerCase().trim();
449
450
 
450
451
  if (this.content) {
451
452
  transferEncoding = (this.getHeader('Content-Transfer-Encoding') || '').toString().toLowerCase().trim();
@@ -475,8 +476,8 @@ class MimeNode {
475
476
  * @returns {String} Headers
476
477
  */
477
478
  buildHeaders() {
478
- let transferEncoding = this.getTransferEncoding();
479
- let headers = [];
479
+ const transferEncoding = this.getTransferEncoding();
480
+ const headers = [];
480
481
 
481
482
  if (transferEncoding) {
482
483
  this.setHeader('Content-Transfer-Encoding', transferEncoding);
@@ -501,7 +502,7 @@ class MimeNode {
501
502
 
502
503
  // Ensure that Content-Type is the last header for the root node
503
504
  for (let i = this._headers.length - 2; i >= 0; i--) {
504
- let header = this._headers[i];
505
+ const header = this._headers[i];
505
506
  if (header.key === 'Content-Type') {
506
507
  this._headers.splice(i, 1);
507
508
  this._headers.push(header);
@@ -514,8 +515,8 @@ class MimeNode {
514
515
  let value = header.value;
515
516
  let structured;
516
517
  let param;
517
- let options = {};
518
- let formattedHeaders = ['From', 'Sender', 'To', 'Cc', 'Bcc', 'Reply-To', 'Date', 'References'];
518
+ const options = {};
519
+ const formattedHeaders = FORMATTED_HEADERS;
519
520
 
520
521
  if (value && typeof value === 'object' && !formattedHeaders.includes(key)) {
521
522
  Object.keys(value).forEach(key => {
@@ -593,7 +594,7 @@ class MimeNode {
593
594
  }
594
595
 
595
596
  if (typeof this.normalizeHeaderKey === 'function') {
596
- let normalized = this.normalizeHeaderKey(key, value);
597
+ const normalized = this.normalizeHeaderKey(key, value);
597
598
  if (normalized && typeof normalized === 'string' && normalized.length) {
598
599
  key = normalized;
599
600
  }
@@ -614,7 +615,7 @@ class MimeNode {
614
615
  createReadStream(options) {
615
616
  options = options || {};
616
617
 
617
- let stream = new PassThrough(options);
618
+ const stream = new PassThrough(options);
618
619
  let outputStream = stream;
619
620
  let transform;
620
621
 
@@ -682,13 +683,13 @@ class MimeNode {
682
683
  }
683
684
 
684
685
  stream(outputStream, options, done) {
685
- let transferEncoding = this.getTransferEncoding();
686
+ const transferEncoding = this.getTransferEncoding();
686
687
  let contentStream;
687
688
  let localStream;
688
689
 
689
690
  // protect actual callback against multiple triggering
690
691
  let returned = false;
691
- let callback = err => {
692
+ const callback = err => {
692
693
  if (returned) {
693
694
  return;
694
695
  }
@@ -698,14 +699,14 @@ class MimeNode {
698
699
 
699
700
  // for multipart nodes, push child nodes
700
701
  // for content nodes end the stream
701
- let finalize = () => {
702
+ const finalize = () => {
702
703
  let childId = 0;
703
- let processChildNode = () => {
704
+ const processChildNode = () => {
704
705
  if (childId >= this.childNodes.length) {
705
706
  outputStream.write('\r\n--' + this.boundary + '--\r\n');
706
707
  return callback();
707
708
  }
708
- let child = this.childNodes[childId++];
709
+ const child = this.childNodes[childId++];
709
710
  outputStream.write((childId > 1 ? '\r\n' : '') + '--' + this.boundary + '\r\n');
710
711
  child.stream(outputStream, options, err => {
711
712
  if (err) {
@@ -723,7 +724,7 @@ class MimeNode {
723
724
  };
724
725
 
725
726
  // pushes node content
726
- let sendContent = () => {
727
+ const sendContent = () => {
727
728
  if (this.content) {
728
729
  if (Object.prototype.toString.call(this.content) === '[object Error]') {
729
730
  // content is already errored
@@ -736,7 +737,7 @@ class MimeNode {
736
737
  this.content.once('error', this._contentErrorHandler);
737
738
  }
738
739
 
739
- let createStream = () => {
740
+ const createStream = () => {
740
741
  if (['quoted-printable', 'base64'].includes(transferEncoding)) {
741
742
  contentStream = new (transferEncoding === 'base64' ? base64 : qp).Encoder(options);
742
743
 
@@ -761,10 +762,10 @@ class MimeNode {
761
762
  };
762
763
 
763
764
  if (this.content._resolve) {
764
- let chunks = [];
765
+ const chunks = [];
765
766
  let chunklen = 0;
766
767
  let returned = false;
767
- let sourceStream = this._getStream(this.content);
768
+ const sourceStream = this._getStream(this.content);
768
769
  sourceStream.on('error', err => {
769
770
  if (returned) {
770
771
  return;
@@ -792,9 +793,8 @@ class MimeNode {
792
793
  setImmediate(createStream);
793
794
  }
794
795
  return;
795
- } else {
796
- return setImmediate(finalize);
797
796
  }
797
+ return setImmediate(finalize);
798
798
  };
799
799
 
800
800
  if (this._raw) {
@@ -809,7 +809,7 @@ class MimeNode {
809
809
  this._raw.removeListener('error', this._contentErrorHandler);
810
810
  }
811
811
 
812
- let raw = this._getStream(this._raw);
812
+ const raw = this._getStream(this._raw);
813
813
  raw.pipe(outputStream, {
814
814
  end: false
815
815
  });
@@ -851,7 +851,7 @@ class MimeNode {
851
851
 
852
852
  this._envelope.to = this._envelope.to.map(to => to.address).filter(address => address);
853
853
 
854
- let standardFields = ['to', 'cc', 'bcc', 'from'];
854
+ const standardFields = ['to', 'cc', 'bcc', 'from'];
855
855
  Object.keys(envelope).forEach(key => {
856
856
  if (!standardFields.includes(key)) {
857
857
  this._envelope[key] = envelope[key];
@@ -867,10 +867,10 @@ class MimeNode {
867
867
  * @return {Object} Address object
868
868
  */
869
869
  getAddresses() {
870
- let addresses = {};
870
+ const addresses = {};
871
871
 
872
872
  this._headers.forEach(header => {
873
- let key = header.key.toLowerCase();
873
+ const key = header.key.toLowerCase();
874
874
  if (['from', 'sender', 'reply-to', 'to', 'cc', 'bcc'].includes(key)) {
875
875
  if (!Array.isArray(addresses[key])) {
876
876
  addresses[key] = [];
@@ -893,12 +893,12 @@ class MimeNode {
893
893
  return this._envelope;
894
894
  }
895
895
 
896
- let envelope = {
896
+ const envelope = {
897
897
  from: false,
898
898
  to: []
899
899
  };
900
900
  this._headers.forEach(header => {
901
- let list = [];
901
+ const list = [];
902
902
  if (header.key === 'From' || (!envelope.from && ['Reply-To', 'Sender'].includes(header.key))) {
903
903
  this._convertAddresses(this._parseAddresses(header.value), list);
904
904
  if (list.length && list[0]) {
@@ -974,14 +974,18 @@ class MimeNode {
974
974
  });
975
975
 
976
976
  return contentStream;
977
- } else if (typeof content.pipe === 'function') {
977
+ }
978
+
979
+ if (typeof content.pipe === 'function') {
978
980
  // assume as stream
979
981
  return content;
980
- } else if (content && typeof content.path === 'string' && !content.href) {
982
+ }
983
+
984
+ if (content && typeof content.path === 'string' && !content.href) {
981
985
  if (this.disableFileAccess) {
982
986
  contentStream = new PassThrough();
983
987
  setImmediate(() => {
984
- let err = new Error('File access rejected for ' + content.path);
988
+ const err = new Error('File access rejected for ' + content.path);
985
989
  err.code = errors.EFILEACCESS;
986
990
  contentStream.emit('error', err);
987
991
  });
@@ -989,11 +993,13 @@ class MimeNode {
989
993
  }
990
994
  // read file
991
995
  return fs.createReadStream(content.path);
992
- } else if (content && typeof content.href === 'string') {
996
+ }
997
+
998
+ if (content && typeof content.href === 'string') {
993
999
  if (this.disableUrlAccess) {
994
1000
  contentStream = new PassThrough();
995
1001
  setImmediate(() => {
996
- let err = new Error('Url access rejected for ' + content.href);
1002
+ const err = new Error('Url access rejected for ' + content.href);
997
1003
  err.code = errors.EURLACCESS;
998
1004
  contentStream.emit('error', err);
999
1005
  });
@@ -1001,19 +1007,19 @@ class MimeNode {
1001
1007
  }
1002
1008
  // fetch URL
1003
1009
  return nmfetch(content.href, { headers: content.httpHeaders });
1004
- } else {
1005
- // pass string or buffer content as a stream
1006
- contentStream = new PassThrough();
1007
-
1008
- setImmediate(() => {
1009
- try {
1010
- contentStream.end(content || '');
1011
- } catch (_err) {
1012
- contentStream.emit('error', _err);
1013
- }
1014
- });
1015
- return contentStream;
1016
1010
  }
1011
+
1012
+ // pass string or buffer content as a stream
1013
+ contentStream = new PassThrough();
1014
+
1015
+ setImmediate(() => {
1016
+ try {
1017
+ contentStream.end(content || '');
1018
+ } catch (_err) {
1019
+ contentStream.emit('error', _err);
1020
+ }
1021
+ });
1022
+ return contentStream;
1017
1023
  }
1018
1024
 
1019
1025
  /**
@@ -1172,7 +1178,7 @@ class MimeNode {
1172
1178
  * @return {String} address string
1173
1179
  */
1174
1180
  _convertAddresses(addresses, uniqueList) {
1175
- let values = [];
1181
+ const values = [];
1176
1182
 
1177
1183
  uniqueList = uniqueList || [];
1178
1184
 
@@ -1182,17 +1188,15 @@ class MimeNode {
1182
1188
 
1183
1189
  if (!address.name) {
1184
1190
  values.push(address.address.indexOf(' ') >= 0 ? `<${address.address}>` : `${address.address}`);
1185
- } else if (address.name) {
1191
+ } else {
1186
1192
  values.push(`${this._encodeAddressName(address.name)} <${address.address}>`);
1187
1193
  }
1188
1194
 
1189
- if (address.address) {
1190
- if (!uniqueList.filter(a => a.address === address.address).length) {
1191
- uniqueList.push(address);
1192
- }
1195
+ if (!uniqueList.some(a => a.address === address.address)) {
1196
+ uniqueList.push(address);
1193
1197
  }
1194
1198
  } else if (address.group) {
1195
- let groupListAddresses = (address.group.length ? this._convertAddresses(address.group, uniqueList) : '').trim();
1199
+ const groupListAddresses = (address.group.length ? this._convertAddresses(address.group, uniqueList) : '').trim();
1196
1200
  values.push(`${this._encodeAddressName(address.name)}:${groupListAddresses};`);
1197
1201
  }
1198
1202
  });
@@ -1212,14 +1216,14 @@ class MimeNode {
1212
1216
  .replace(/[\x00-\x1F<>]+/g, ' ') // remove unallowed characters
1213
1217
  .trim();
1214
1218
 
1215
- let lastAt = address.lastIndexOf('@');
1219
+ const lastAt = address.lastIndexOf('@');
1216
1220
  if (lastAt < 0) {
1217
1221
  // Bare username
1218
1222
  return address;
1219
1223
  }
1220
1224
 
1221
1225
  let user = address.substr(0, lastAt);
1222
- let domain = address.substr(lastAt + 1);
1226
+ const domain = address.substr(lastAt + 1);
1223
1227
 
1224
1228
  // Usernames are not touched and are kept as is even if these include unicode
1225
1229
  // Domains are punycoded by default
@@ -1285,20 +1289,25 @@ class MimeNode {
1285
1289
  _getTextEncoding(value) {
1286
1290
  value = (value || '').toString();
1287
1291
 
1288
- let encoding = this.textEncoding;
1289
- let latinLen;
1290
- let nonLatinLen;
1291
-
1292
- if (!encoding) {
1293
- // count latin alphabet symbols and 8-bit range symbols + control symbols
1294
- // if there are more latin characters, then use quoted-printable
1295
- // encoding, otherwise use base64
1296
- nonLatinLen = (value.match(/[\x00-\x08\x0B\x0C\x0E-\x1F\u0080-\uFFFF]/g) || []).length;
1297
- latinLen = (value.match(/[a-z]/gi) || []).length;
1298
- // if there are more latin symbols than binary/unicode, then prefer Q, otherwise B
1299
- encoding = nonLatinLen < latinLen ? 'Q' : 'B';
1292
+ if (this.textEncoding) {
1293
+ return this.textEncoding;
1294
+ }
1295
+
1296
+ // count latin alphabet symbols and 8-bit range symbols + control symbols
1297
+ // if there are more latin characters, then use quoted-printable
1298
+ // encoding, otherwise use base64
1299
+ let nonLatinLen = 0;
1300
+ let latinLen = 0;
1301
+ for (let i = 0, len = value.length; i < len; i++) {
1302
+ const code = value.charCodeAt(i);
1303
+ if ((code >= 0x00 && code <= 0x08) || code === 0x0b || code === 0x0c || (code >= 0x0e && code <= 0x1f) || code >= 0x80) {
1304
+ nonLatinLen++;
1305
+ } else if ((code >= 0x41 && code <= 0x5a) || (code >= 0x61 && code <= 0x7a)) {
1306
+ latinLen++;
1307
+ }
1300
1308
  }
1301
- return encoding;
1309
+ // if there are more latin symbols than binary/unicode, then prefer Q, otherwise B
1310
+ return nonLatinLen < latinLen ? 'Q' : 'B';
1302
1311
  }
1303
1312
 
1304
1313
  /**
@@ -1,6 +1,6 @@
1
1
  'use strict';
2
2
 
3
- const Transform = require('stream').Transform;
3
+ const { Transform } = require('stream');
4
4
 
5
5
  class LastNewline extends Transform {
6
6
  constructor() {
@@ -1,18 +1,15 @@
1
1
  'use strict';
2
2
 
3
- const stream = require('stream');
4
- const Transform = stream.Transform;
3
+ const { Transform } = require('stream');
5
4
 
6
5
  /**
7
6
  * Ensures that only <LF> is used for linebreaks
8
7
  *
9
8
  * @param {Object} options Stream options
10
9
  */
11
- class LeWindows extends Transform {
10
+ class LeUnix extends Transform {
12
11
  constructor(options) {
13
12
  super(options);
14
- // init Transform
15
- this.options = options || {};
16
13
  }
17
14
 
18
15
  /**
@@ -24,7 +21,7 @@ class LeWindows extends Transform {
24
21
 
25
22
  for (let i = 0, len = chunk.length; i < len; i++) {
26
23
  if (chunk[i] === 0x0d) {
27
- // \n
24
+ // \r
28
25
  buf = chunk.slice(lastPos, i);
29
26
  lastPos = i + 1;
30
27
  this.push(buf);
@@ -40,4 +37,4 @@ class LeWindows extends Transform {
40
37
  }
41
38
  }
42
39
 
43
- module.exports = LeWindows;
40
+ module.exports = LeUnix;
@@ -1,7 +1,6 @@
1
1
  'use strict';
2
2
 
3
- const stream = require('stream');
4
- const Transform = stream.Transform;
3
+ const { Transform } = require('stream');
5
4
 
6
5
  /**
7
6
  * Ensures that only <CR><LF> sequences are used for linebreaks
@@ -11,8 +10,6 @@ const Transform = stream.Transform;
11
10
  class LeWindows extends Transform {
12
11
  constructor(options) {
13
12
  super(options);
14
- // init Transform
15
- this.options = options || {};
16
13
  this.lastByte = false;
17
14
  }
18
15
 
package/lib/nodemailer.js CHANGED
@@ -20,9 +20,7 @@ const ETHEREAL_CACHE = ['true', 'yes', 'y', '1'].includes((process.env.ETHEREAL_
20
20
  let testAccount = false;
21
21
 
22
22
  module.exports.createTransport = function (transporter, defaults) {
23
- let urlConfig;
24
23
  let options;
25
- let mailer;
26
24
 
27
25
  if (
28
26
  // provided transporter is a configuration object, not transporter plugin
@@ -30,7 +28,8 @@ module.exports.createTransport = function (transporter, defaults) {
30
28
  // provided transporter looks like a connection url
31
29
  (typeof transporter === 'string' && /^(smtps?|direct):/i.test(transporter))
32
30
  ) {
33
- if ((urlConfig = typeof transporter === 'string' ? transporter : transporter.url)) {
31
+ const urlConfig = typeof transporter === 'string' ? transporter : transporter.url;
32
+ if (urlConfig) {
34
33
  // parse a configuration URL into configuration options
35
34
  options = shared.parseConnectionUrl(urlConfig);
36
35
  } else {
@@ -47,7 +46,7 @@ module.exports.createTransport = function (transporter, defaults) {
47
46
  transporter = new JSONTransport(options);
48
47
  } else if (options.SES) {
49
48
  if (options.SES.ses && options.SES.aws) {
50
- let error = new Error(
49
+ const error = new Error(
51
50
  'Using legacy SES configuration, expecting @aws-sdk/client-sesv2, see https://nodemailer.com/transports/ses/'
52
51
  );
53
52
  error.code = errors.ECONFIG;
@@ -59,9 +58,7 @@ module.exports.createTransport = function (transporter, defaults) {
59
58
  }
60
59
  }
61
60
 
62
- mailer = new Mailer(transporter, options, defaults);
63
-
64
- return mailer;
61
+ return new Mailer(transporter, options, defaults);
65
62
  };
66
63
 
67
64
  module.exports.createTestAccount = function (apiUrl, callback) {
@@ -85,11 +82,11 @@ module.exports.createTestAccount = function (apiUrl, callback) {
85
82
 
86
83
  apiUrl = apiUrl || ETHEREAL_API;
87
84
 
88
- let chunks = [];
85
+ const chunks = [];
89
86
  let chunklen = 0;
90
87
 
91
- let requestHeaders = {};
92
- let requestBody = {
88
+ const requestHeaders = {};
89
+ const requestBody = {
93
90
  requestor: packageData.name,
94
91
  version: packageData.version
95
92
  };
@@ -98,7 +95,7 @@ module.exports.createTestAccount = function (apiUrl, callback) {
98
95
  requestHeaders.Authorization = 'Bearer ' + ETHEREAL_API_KEY;
99
96
  }
100
97
 
101
- let req = nmfetch(apiUrl + '/user', {
98
+ const req = nmfetch(apiUrl + '/user', {
102
99
  contentType: 'application/json',
103
100
  method: 'POST',
104
101
  headers: requestHeaders,
@@ -116,16 +113,12 @@ module.exports.createTestAccount = function (apiUrl, callback) {
116
113
  req.once('error', err => callback(err));
117
114
 
118
115
  req.once('end', () => {
119
- let res = Buffer.concat(chunks, chunklen);
116
+ const res = Buffer.concat(chunks, chunklen);
120
117
  let data;
121
- let err;
122
118
  try {
123
119
  data = JSON.parse(res.toString());
124
120
  } catch (E) {
125
- err = E;
126
- }
127
- if (err) {
128
- return callback(err);
121
+ return callback(E);
129
122
  }
130
123
  if (data.status !== 'success' || data.error) {
131
124
  return callback(new Error(data.error || 'Request failed'));
@@ -143,7 +136,7 @@ module.exports.getTestMessageUrl = function (info) {
143
136
  return false;
144
137
  }
145
138
 
146
- let infoProps = new Map();
139
+ const infoProps = new Map();
147
140
  info.response.replace(/\[([^\]]+)\]$/, (m, props) => {
148
141
  props.replace(/\b([A-Z0-9]+)=([^\s]+)/g, (m, key, value) => {
149
142
  infoProps.set(key, value);
package/lib/qp/index.js CHANGED
@@ -1,6 +1,6 @@
1
1
  'use strict';
2
2
 
3
- const Transform = require('stream').Transform;
3
+ const { Transform } = require('stream');
4
4
 
5
5
  /**
6
6
  * Encodes a Buffer into a Quoted-Printable encoded string
@@ -8,20 +8,21 @@ const Transform = require('stream').Transform;
8
8
  * @param {Buffer} buffer Buffer to convert
9
9
  * @returns {String} Quoted-Printable encoded string
10
10
  */
11
+ // usable characters that do not need encoding
12
+ // https://tools.ietf.org/html/rfc2045#section-6.7
13
+ const QP_RANGES = [
14
+ [0x09], // <TAB>
15
+ [0x0a], // <LF>
16
+ [0x0d], // <CR>
17
+ [0x20, 0x3c], // <SP>!"#$%&'()*+,-./0123456789:;
18
+ [0x3e, 0x7e] // >?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}
19
+ ];
20
+
11
21
  function encode(buffer) {
12
22
  if (typeof buffer === 'string') {
13
23
  buffer = Buffer.from(buffer, 'utf-8');
14
24
  }
15
25
 
16
- // usable characters that do not need encoding
17
- let ranges = [
18
- // https://tools.ietf.org/html/rfc2045#section-6.7
19
- [0x09], // <TAB>
20
- [0x0a], // <LF>
21
- [0x0d], // <CR>
22
- [0x20, 0x3c], // <SP>!"#$%&'()*+,-./0123456789:;
23
- [0x3e, 0x7e] // >?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}
24
- ];
25
26
  let result = '';
26
27
  let ord;
27
28
 
@@ -29,7 +30,7 @@ function encode(buffer) {
29
30
  ord = buffer[i];
30
31
  // if the char is in allowed range, then keep as is, unless it is a WS in the end of a line
31
32
  if (
32
- checkRanges(ord, ranges) &&
33
+ checkRanges(ord, QP_RANGES) &&
33
34
  !((ord === 0x20 || ord === 0x09) && (i === len - 1 || buffer[i + 1] === 0x0a || buffer[i + 1] === 0x0d))
34
35
  ) {
35
36
  result += String.fromCharCode(ord);
@@ -57,9 +58,9 @@ function wrap(str, lineLength) {
57
58
  }
58
59
 
59
60
  let pos = 0;
60
- let len = str.length;
61
+ const len = str.length;
61
62
  let match, code, line;
62
- let lineMargin = Math.floor(lineLength / 3);
63
+ const lineMargin = Math.floor(lineLength / 3);
63
64
  let result = '';
64
65
 
65
66
  // insert soft linebreaks where needed
@@ -73,17 +74,20 @@ function wrap(str, lineLength) {
73
74
  }
74
75
 
75
76
  if (line.substr(-1) === '\n') {
76
- // nothing to change here
77
77
  result += line;
78
78
  pos += line.length;
79
79
  continue;
80
- } else if ((match = line.substr(-lineMargin).match(/\n.*?$/))) {
80
+ }
81
+
82
+ if ((match = line.substr(-lineMargin).match(/\n.*?$/))) {
81
83
  // truncate to nearest line break
82
84
  line = line.substr(0, line.length - (match[0].length - 1));
83
85
  result += line;
84
86
  pos += line.length;
85
87
  continue;
86
- } else if (line.length > lineLength - lineMargin && (match = line.substr(-lineMargin).match(/[ \t.,!?][^ \t.,!?]*$/))) {
88
+ }
89
+
90
+ if (line.length > lineLength - lineMargin && (match = line.substr(-lineMargin).match(/[ \t.,!?][^ \t.,!?]*$/))) {
87
91
  // truncate to nearest space
88
92
  line = line.substr(0, line.length - (match[0].length - 1));
89
93
  } else if (line.match(/[=][\da-f]{0,2}$/i)) {
@@ -139,13 +143,14 @@ function wrap(str, lineLength) {
139
143
  */
140
144
  function checkRanges(nr, ranges) {
141
145
  for (let i = ranges.length - 1; i >= 0; i--) {
142
- if (!ranges[i].length) {
146
+ const range = ranges[i];
147
+ if (!range.length) {
143
148
  continue;
144
149
  }
145
- if (ranges[i].length === 1 && nr === ranges[i][0]) {
150
+ if (range.length === 1 && nr === range[0]) {
146
151
  return true;
147
152
  }
148
- if (ranges[i].length === 2 && nr >= ranges[i][0] && nr <= ranges[i][1]) {
153
+ if (range.length === 2 && nr >= range[0] && nr <= range[1]) {
149
154
  return true;
150
155
  }
151
156
  }
@@ -163,7 +168,6 @@ class Encoder extends Transform {
163
168
  constructor(options) {
164
169
  super();
165
170
 
166
- // init Transform
167
171
  this.options = options || {};
168
172
 
169
173
  if (this.options.lineLength !== false) {
@@ -219,7 +223,6 @@ class Encoder extends Transform {
219
223
  }
220
224
  }
221
225
 
222
- // expose to the world
223
226
  module.exports = {
224
227
  encode,
225
228
  wrap,