From 1a8e0f4fce679628b3fa9ae3cca1a5765e4adcad Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jimmy=20Wa=CC=88rting?= Date: Fri, 5 Jun 2020 10:58:35 +0200 Subject: [PATCH 1/7] options.buffer isn't a valid option --- index.js | 3 --- 1 file changed, 3 deletions(-) diff --git a/index.js b/index.js index aaf70f3..fa9e895 100644 --- a/index.js +++ b/index.js @@ -44,9 +44,6 @@ class Blob { this[TYPE] = type; } - if (options && Buffer.isBuffer(options.buffer)) { - this[BUFFER] = options.buffer; - } } get size() { From 3b21cb472f8b91870d7a44623fda6d136b6af90f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jimmy=20Wa=CC=88rting?= Date: Fri, 5 Jun 2020 10:59:55 +0200 Subject: [PATCH 2/7] use default arguments --- index.js | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/index.js b/index.js index fa9e895..d40b482 100644 --- a/index.js +++ b/index.js @@ -7,12 +7,7 @@ const BUFFER = Symbol('buffer'); const TYPE = Symbol('type'); class Blob { - constructor(...args) { - this[TYPE] = ''; - - const blobParts = args[0]; - const options = args[1]; - + constructor(blobParts = [], options = { type: '' }) { const buffers = []; /* eslint-disable-next-line no-unused-vars */ let size = 0; From a386ab7c084525e0a6cb52e2d908f4b9b556be73 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jimmy=20Wa=CC=88rting?= Date: Fri, 5 Jun 2020 11:01:01 +0200 Subject: [PATCH 3/7] concat perf --- index.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/index.js b/index.js index d40b482..9103797 100644 --- a/index.js +++ b/index.js @@ -32,7 +32,7 @@ class Blob { }); } - this[BUFFER] = Buffer.concat(buffers); + const buffer = Buffer.concat(buffers, size); const type = options && options.type !== undefined && String(options.type).toLowerCase(); if (type && !/[^\u0020-\u007E]/.test(type)) { From 802c03b45f96e6e682acff9311713f30bb5cf635 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jimmy=20Wa=CC=88rting?= Date: Fri, 5 Jun 2020 11:02:14 +0200 Subject: [PATCH 4/7] truly private internal properties --- index.js | 33 ++++++++++++++++----------------- 1 file changed, 16 insertions(+), 17 deletions(-) diff --git a/index.js b/index.js index 9103797..23cc83e 100644 --- a/index.js +++ b/index.js @@ -3,13 +3,11 @@ const {Readable: ReadableStream} = require('stream'); -const BUFFER = Symbol('buffer'); -const TYPE = Symbol('type'); +const wm = new WeakMap(); class Blob { constructor(blobParts = [], options = { type: '' }) { const buffers = []; - /* eslint-disable-next-line no-unused-vars */ let size = 0; if (blobParts) { @@ -22,7 +20,7 @@ class Blob { } else if (element instanceof ArrayBuffer) { buffer = Buffer.from(element); } else if (element instanceof Blob) { - buffer = element[BUFFER]; + buffer = wm.get(element).buffer; } else { buffer = Buffer.from(typeof element === 'string' ? element : String(element)); } @@ -34,27 +32,29 @@ class Blob { const buffer = Buffer.concat(buffers, size); - const type = options && options.type !== undefined && String(options.type).toLowerCase(); - if (type && !/[^\u0020-\u007E]/.test(type)) { - this[TYPE] = type; - } + const type = options.type === undefined ? '' : String(options.type).toLowerCase(); + wm.set(this, { + type: /[^\u0020-\u007E]/.test(type) ? '' : type, + size, + buffer + }); } get size() { - return this[BUFFER].length; + return wm.get(this).size; } get type() { - return this[TYPE]; + return wm.get(this).type; } text() { - return Promise.resolve(this[BUFFER].toString()); + return Promise.resolve(wm.get(this).buffer.toString()); } arrayBuffer() { - const buf = this[BUFFER]; + const buf = wm.get(this).buffer; const ab = buf.buffer.slice(buf.byteOffset, buf.byteOffset + buf.byteLength); return Promise.resolve(ab); } @@ -62,7 +62,7 @@ class Blob { stream() { const readable = new ReadableStream(); readable._read = () => { }; - readable.push(this[BUFFER]); + readable.push(wm.get(this).buffer); readable.push(null); return readable; } @@ -96,14 +96,13 @@ class Blob { } const span = Math.max(relativeEnd - relativeStart, 0); - - const buffer = this[BUFFER]; - const slicedBuffer = buffer.slice( + const slicedBuffer = wm.get(this).buffer.slice( relativeStart, relativeStart + span ); const blob = new Blob([], {type: args[2]}); - blob[BUFFER] = slicedBuffer; + const _ = wm.get(blob); + _.buffer = slicedBuffer; return blob; } } From 23fc22c28c7234f52ec5a32fe12b941280f2af01 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jimmy=20Wa=CC=88rting?= Date: Fri, 5 Jun 2020 11:11:06 +0200 Subject: [PATCH 5/7] removed spaces --- index.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/index.js b/index.js index 23cc83e..06e477d 100644 --- a/index.js +++ b/index.js @@ -6,7 +6,7 @@ const {Readable: ReadableStream} = require('stream'); const wm = new WeakMap(); class Blob { - constructor(blobParts = [], options = { type: '' }) { + constructor(blobParts = [], options = {type: ''}) { const buffers = []; let size = 0; From 5c6b31f64b5b02aaac5a159bcfffbccb4979a354 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jimmy=20Wa=CC=88rting?= Date: Fri, 5 Jun 2020 12:08:33 +0200 Subject: [PATCH 6/7] 100% test coverage --- index.js | 44 +++++++++++++++++------------------- test.js | 68 +++++++++++++++++++++++++++++++++++++++++++++++++------- 2 files changed, 81 insertions(+), 31 deletions(-) diff --git a/index.js b/index.js index 06e477d..6dd75a0 100644 --- a/index.js +++ b/index.js @@ -10,25 +10,23 @@ class Blob { const buffers = []; let size = 0; - if (blobParts) { - blobParts.forEach(element => { - let buffer; - if (element instanceof Buffer) { - buffer = element; - } else if (ArrayBuffer.isView(element)) { - buffer = Buffer.from(element.buffer, element.byteOffset, element.byteLength); - } else if (element instanceof ArrayBuffer) { - buffer = Buffer.from(element); - } else if (element instanceof Blob) { - buffer = wm.get(element).buffer; - } else { - buffer = Buffer.from(typeof element === 'string' ? element : String(element)); - } - - size += buffer.length; - buffers.push(buffer); - }); - } + blobParts.forEach(element => { + let buffer; + if (element instanceof Buffer) { + buffer = element; + } else if (ArrayBuffer.isView(element)) { + buffer = Buffer.from(element.buffer, element.byteOffset, element.byteLength); + } else if (element instanceof ArrayBuffer) { + buffer = Buffer.from(element); + } else if (element instanceof Blob) { + buffer = wm.get(element).buffer; + } else { + buffer = Buffer.from(typeof element === 'string' ? element : String(element)); + } + + size += buffer.length; + buffers.push(buffer); + }); const buffer = Buffer.concat(buffers, size); @@ -80,17 +78,17 @@ class Blob { let relativeEnd; if (start === undefined) { - relativeStart = 0; + relativeStart = 0; // } else if (start < 0) { - relativeStart = Math.max(size + start, 0); + relativeStart = Math.max(size + start, 0); // } else { relativeStart = Math.min(start, size); } if (end === undefined) { - relativeEnd = size; + relativeEnd = size; // } else if (end < 0) { - relativeEnd = Math.max(size + end, 0); + relativeEnd = Math.max(size + end, 0); // } else { relativeEnd = Math.min(end, size); } diff --git a/test.js b/test.js index a25be73..320e735 100644 --- a/test.js +++ b/test.js @@ -3,12 +3,33 @@ const Blob = require('.'); const getStream = require('get-stream'); const {Response} = require('node-fetch'); -test('Blob ctor', t => { +test('new Blob()', t => { + const blob = new Blob(); // eslint-disable-line no-unused-vars + t.pass(); +}); + +test('new Blob(parts)', t => { const data = 'a=1'; const blob = new Blob([data]); // eslint-disable-line no-unused-vars t.pass(); }); +test('Blob ctor parts', async t => { + const parts = [ + 'a', + new Uint8Array([98]), // b + new Uint16Array([25699]), // cd + new Uint8Array([101]).buffer, // e + Buffer.from('f'), + new Blob(['g']), + {} + ]; + + const blob = new Blob(parts); + console.log(await blob.text()) + t.is(await blob.text(), 'abcdefg[object Object]'); +}); + test('Blob size', t => { const data = 'a=1'; const blob = new Blob([data]); @@ -16,12 +37,27 @@ test('Blob size', t => { }); test('Blob type', t => { - const data = 'a=1'; const type = 'text/plain'; - const blob = new Blob([data], {type}); + const blob = new Blob([], {type}); + t.is(blob.type, type); +}); + +test('Blob slice type', t => { + const type = 'text/plain'; + const blob = new Blob().slice(0, 0, type); t.is(blob.type, type); }); +test('invalid Blob type', t => { + const blob = new Blob([], {type: '\u001Ftext/plain'}); + t.is(blob.type, ''); +}); + +test('invalid Blob slice type', t => { + const blob = new Blob().slice(0, 0, '\u001Ftext/plain'); + t.is(blob.type, ''); +}); + test('Blob text()', async t => { const data = 'a=1'; const type = 'text/plain'; @@ -55,11 +91,27 @@ test('Blob toString()', t => { }); test('Blob slice()', async t => { - const data = 'a=1'; - const type = 'text/plain'; - const blob = new Blob([data], {type}); - const blob2 = blob.slice(0, 1); - t.is(await blob2.text(), data.slice(0, 1)); + const data = 'abcdefgh'; + const blob = new Blob([data]).slice(); + t.is(await blob.text(), data); +}); + +test('Blob slice(0, 1)', async t => { + const data = 'abcdefgh'; + const blob = new Blob([data]).slice(0, 1); + t.is(await blob.text(), 'a'); +}); + +test('Blob slice(-1)', async t => { + const data = 'abcdefgh'; + const blob = new Blob([data]).slice(-1); + t.is(await blob.text(), 'h'); +}); + +test('Blob slice(0, -1)', async t => { + const data = 'abcdefgh'; + const blob = new Blob([data]).slice(0, -1); + t.is(await blob.text(), 'abcdefg'); }); test('Blob works with node-fetch Response.blob()', async t => { From 86f69c545516d9e4dca02f82cf3025b821f951f3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jimmy=20Wa=CC=88rting?= Date: Fri, 5 Jun 2020 12:15:30 +0200 Subject: [PATCH 7/7] lint fix --- test.js | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/test.js b/test.js index 320e735..115835f 100644 --- a/test.js +++ b/test.js @@ -17,16 +17,15 @@ test('new Blob(parts)', t => { test('Blob ctor parts', async t => { const parts = [ 'a', - new Uint8Array([98]), // b - new Uint16Array([25699]), // cd - new Uint8Array([101]).buffer, // e + new Uint8Array([98]), + new Uint16Array([25699]), + new Uint8Array([101]).buffer, Buffer.from('f'), new Blob(['g']), {} ]; const blob = new Blob(parts); - console.log(await blob.text()) t.is(await blob.text(), 'abcdefg[object Object]'); });