Skip to content

Commit

Permalink
Make the package compatible with browser targets natively
Browse files Browse the repository at this point in the history
Remove the need for a polyfill by providing an implementation that uses
web APIs.
  • Loading branch information
vlovich committed Feb 1, 2022
1 parent c372d0b commit 941ae92
Show file tree
Hide file tree
Showing 5 changed files with 410 additions and 341 deletions.
42 changes: 42 additions & 0 deletions lib/browser-buffer-ops.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
const replacementCharacterBuffer = new Uint8Array([0xef, 0xbf, 0xbd]);

const base64js = require('base64-js');

module.exports = {
fromBase64: (b64str) => {
try {
return base64js.toByteArray(b64str);
} catch (e) {
return new Uint8Array();
}
},

toUtf8: TextEncoder.prototype.encode.bind(new TextEncoder()),
fromUtf8: TextDecoder.prototype.decode.bind(new TextDecoder()),
fromAscii: TextDecoder.prototype.decode.bind(new TextDecoder('ascii')),

allocByteBuffer: (length) => {
return new Uint8Array(length);
},

includesReplacementCharacter: (haystack) => {
const needle = replacementCharacterBuffer;
if (haystack.length < needle.length) {
return false;
}
let fromIndex = 0;
while (fromIndex !== haystack.length - 3) {
const foundFirst = haystack[fromIndex] === needle[0];
const foundSecond = haystack[fromIndex + 1] === needle[1];
const foundThird = haystack[fromIndex + 2] === needle[2];

if (foundFirst && foundSecond && foundThird) {
return true;
} else {
fromIndex += 1;
}
}

return false;
},
};
21 changes: 21 additions & 0 deletions lib/node-buffer-ops.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
const replacementCharacterBuffer = new Uint8Array([0xef, 0xbf, 0xbd]);

module.exports = {
fromBase64: (b64str) => {
return Buffer.from(b64str, 'base64');
},

toUtf8: (str) => {
return Buffer.from(str, 'utf-8');
},
fromUtf8: String,
fromAscii: (buffer) => {
return buffer.toString('ascii');
},

allocByteBuffer: Buffer.alloc,

includesReplacementCharacter: (haystack) => {
return haystack.includes(replacementCharacterBuffer);
},
};
21 changes: 11 additions & 10 deletions lib/rfc2047.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

const isUtf8RegExp = /^utf-?8$/i;
const isLatin1RegExp = /^(?:iso-8859-1|latin1|us-ascii)$/i;
const bufferOps = require('./node-buffer-ops');
const rfc2047 = (module.exports = {});

let iconv;
Expand All @@ -22,13 +23,13 @@ function stringify(obj) {
return obj;
} else if (obj === null || typeof obj === 'undefined') {
return '';
} else if (obj instanceof Uint8Array) {
return bufferOps.fromUtf8(obj);
} else {
return String(obj);
}
}

const replacementCharacterBuffer = Buffer.from('�');

function decodeBuffer(encodedText, encoding) {
if (encoding === 'q') {
encodedText = encodedText.replace(/_/g, ' ');
Expand All @@ -42,7 +43,7 @@ function decodeBuffer(encodedText, encoding) {
numValidlyEncodedBytes += 1;
}
}
const buffer = Buffer.alloc(
const buffer = bufferOps.allocByteBuffer(
encodedText.length - numValidlyEncodedBytes * 2
);
let j = 0;
Expand All @@ -62,7 +63,7 @@ function decodeBuffer(encodedText, encoding) {
}
return buffer;
} else {
return Buffer.from(encodedText, 'base64');
return bufferOps.fromBase64(encodedText);
}
}

Expand Down Expand Up @@ -95,23 +96,23 @@ function decodeEncodedWord(encodedText, encoding, charset) {
converter = new iconv.Iconv('iso-8859-1', 'utf-8//TRANSLIT');
}
try {
return converter.convert(buffer).toString('utf-8');
return bufferOps.fromUtf8(converter.convert(buffer));
} catch (e2) {}
} else if (isUtf8RegExp.test(charset)) {
const decoded = buffer.toString('utf-8');
const decoded = bufferOps.fromUtf8(buffer);
if (
!/\ufffd/.test(decoded) ||
buffer.includes(replacementCharacterBuffer)
bufferOps.includesReplacementCharacter(buffer)
) {
return decoded;
}
} else if (isLatin1RegExp.test(charset)) {
return buffer.toString('ascii');
return bufferOps.fromAscii(buffer);
} else if (iconvLite && iconvLite.encodingExists(charset)) {
decoded = iconvLite.decode(buffer, charset);
if (
!/\ufffd/.test(decoded) ||
buffer.includes(replacementCharacterBuffer)
bufferOps.includesReplacementCharacter(buffer)
) {
return decoded;
}
Expand Down Expand Up @@ -264,7 +265,7 @@ rfc2047.encode = (text) => {
const charset = 'utf-8';
// Around 25% faster than encodeURIComponent(token.replace(/ /g, "_")).replace(/%/g, "="):
const encodedWordBody = bufferToQuotedPrintableString(
Buffer.from(token, 'utf-8')
bufferOps.toUtf8(token)
);
if (previousTokenWasEncodedWord) {
result += ' ';
Expand Down
3 changes: 2 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,8 @@
"iconv-lite": "0.4.5"
},
"browser": {
"iconv": false
"iconv": false,
"lib/node-buffer-ops.js": "lib/browser-buffer-ops.js"
},
"nyc": {
"include": [
Expand Down
Loading

0 comments on commit 941ae92

Please sign in to comment.