Skip to content

Commit a320b45

Browse files
committed
refactor in sync with node-fetch
1 parent 870be1e commit a320b45

File tree

3 files changed

+87
-82
lines changed

3 files changed

+87
-82
lines changed

index.d.ts

-4
This file was deleted.

index.js

+79-73
Original file line numberDiff line numberDiff line change
@@ -1,107 +1,120 @@
11
// Based on https://github.com/tmpvar/jsdom/blob/aa85b2abf07766ff7bf5c1f6daafb3726f2f2db5/lib/jsdom/living/blob.js
22
// (MIT licensed)
33

4-
const {Readable: ReadableStream} = require('stream');
4+
const {Readable} = require('stream');
5+
const {types} = require('util');
56

7+
/**
8+
* @type {WeakMap<Blob, { type: string, buffer: Buffer }}
9+
*/
610
const wm = new WeakMap();
711

812
class Blob {
13+
/**
14+
* The Blob() constructor returns a new Blob object. The content of the blob consists of the concatenation of the values given in the parameter array.
15+
* @param {(ArrayBufferLike | ArrayBufferView | Blob | Buffer | string)[]} blobParts
16+
* @param {{ type?: string }} [options]
17+
*/
918
constructor(blobParts = [], options = {type: ''}) {
10-
const buffers = [];
11-
let size = 0;
12-
13-
blobParts.forEach(element => {
14-
let buffer;
15-
if (element instanceof Buffer) {
16-
buffer = element;
17-
} else if (ArrayBuffer.isView(element)) {
18-
buffer = Buffer.from(element.buffer, element.byteOffset, element.byteLength);
19-
} else if (element instanceof ArrayBuffer) {
20-
buffer = Buffer.from(element);
21-
} else if (element instanceof Blob) {
22-
buffer = wm.get(element).buffer;
23-
} else {
24-
buffer = Buffer.from(typeof element === 'string' ? element : String(element));
19+
const buffers = blobParts.map(element => {
20+
if (Buffer.isBuffer(element)) {
21+
return element;
2522
}
2623

27-
size += buffer.length;
28-
buffers.push(buffer);
24+
if (ArrayBuffer.isView(element)) {
25+
return Buffer.from(
26+
element.buffer,
27+
element.byteOffset,
28+
element.byteLength
29+
);
30+
}
31+
32+
if (types.isAnyArrayBuffer(element)) {
33+
return Buffer.from(element);
34+
}
35+
36+
if (wm.has(element)) {
37+
return wm.get(element).buffer;
38+
}
39+
40+
return Buffer.from(
41+
typeof element === 'string' ? element : String(element)
42+
);
2943
});
3044

31-
const buffer = Buffer.concat(buffers, size);
45+
const buffer = Buffer.concat(buffers);
3246

33-
const type = options.type === undefined ? '' : String(options.type).toLowerCase();
47+
const type =
48+
options.type === undefined ? '' : String(options.type).toLowerCase();
3449

3550
wm.set(this, {
3651
type: /[^\u0020-\u007E]/.test(type) ? '' : type,
37-
size,
3852
buffer
3953
});
4054
}
4155

56+
/**
57+
* The Blob interface's size property returns the size of the Blob in bytes.
58+
*/
4259
get size() {
43-
return wm.get(this).size;
60+
return wm.get(this).buffer.byteLength;
4461
}
4562

63+
/**
64+
* The type property of a Blob object returns the MIME type of the file.
65+
*/
4666
get type() {
4767
return wm.get(this).type;
4868
}
4969

50-
text() {
51-
return Promise.resolve(wm.get(this).buffer.toString());
70+
/**
71+
* The text() method in the Blob interface returns a Promise that resolves with a string containing the contents of the blob, interpreted as UTF-8.
72+
*/
73+
async text() {
74+
return wm.get(this).buffer.toString();
5275
}
5376

54-
arrayBuffer() {
77+
/**
78+
* The arrayBuffer() method in the Blob interface returns a Promise that resolves with the contents of the blob as binary data contained in an ArrayBuffer.
79+
*/
80+
async arrayBuffer() {
5581
const buf = wm.get(this).buffer;
56-
const ab = buf.buffer.slice(buf.byteOffset, buf.byteOffset + buf.byteLength);
57-
return Promise.resolve(ab);
82+
const ab = buf.buffer.slice(
83+
buf.byteOffset,
84+
buf.byteOffset + buf.byteLength
85+
);
86+
return ab;
5887
}
5988

89+
/**
90+
* The Blob interface's stream() method returns a ReadableStream which upon reading returns the data contained within the Blob.
91+
*/
6092
stream() {
61-
const readable = new ReadableStream();
62-
readable._read = () => { };
63-
readable.push(wm.get(this).buffer);
64-
readable.push(null);
65-
return readable;
93+
return Readable.from(wm.get(this).buffer);
94+
}
95+
96+
get [Symbol.toStringTag]() {
97+
return 'Blob';
6698
}
6799

100+
/**
101+
* @returns {string}
102+
*/
68103
toString() {
69-
return '[object Blob]';
104+
return Object.prototype.toString.call(this);
70105
}
71106

72-
slice(...args) {
73-
const {size} = this;
74-
75-
const start = args[0];
76-
const end = args[1];
77-
let relativeStart;
78-
let relativeEnd;
79-
80-
if (start === undefined) {
81-
relativeStart = 0; //
82-
} else if (start < 0) {
83-
relativeStart = Math.max(size + start, 0); //
84-
} else {
85-
relativeStart = Math.min(start, size);
86-
}
87-
88-
if (end === undefined) {
89-
relativeEnd = size; //
90-
} else if (end < 0) {
91-
relativeEnd = Math.max(size + end, 0); //
92-
} else {
93-
relativeEnd = Math.min(end, size);
94-
}
95-
96-
const span = Math.max(relativeEnd - relativeStart, 0);
97-
const slicedBuffer = wm.get(this).buffer.slice(
98-
relativeStart,
99-
relativeStart + span
100-
);
101-
const blob = new Blob([], {type: args[2]});
102-
const _ = wm.get(blob);
103-
_.buffer = slicedBuffer;
104-
return blob;
107+
/**
108+
* The Blob interface's slice() method creates and returns a new Blob object which contains data from a subset of the blob on which it's called.
109+
*
110+
* @param {number} [start]
111+
* @param {number} [end]
112+
* @param {string} [contentType]
113+
*/
114+
slice(start, end, contentType) {
115+
return new Blob([wm
116+
.get(this)
117+
.buffer.subarray(start, end)], {type: contentType});
105118
}
106119
}
107120

@@ -111,11 +124,4 @@ Object.defineProperties(Blob.prototype, {
111124
slice: {enumerable: true}
112125
});
113126

114-
Object.defineProperty(Blob.prototype, Symbol.toStringTag, {
115-
value: 'Blob',
116-
writable: false,
117-
enumerable: false,
118-
configurable: true
119-
});
120-
121127
module.exports = Blob;

package.json

+8-5
Original file line numberDiff line numberDiff line change
@@ -11,15 +11,16 @@
1111
"lint": "xo",
1212
"test": "xo && ava",
1313
"report": "nyc ava",
14-
"coverage": "nyc --reporter json --reporter text ava && codecov -f coverage/coverage-final.json"
14+
"coverage": "nyc --reporter json --reporter text ava && codecov -f coverage/coverage-final.json",
15+
"prepublishOnly": "npx typescript --declaration --emitDeclarationOnly --allowJs index.js"
1516
},
1617
"repository": "https://github.com/node-fetch/fetch-blob.git",
1718
"keywords": [
1819
"blob",
1920
"node-fetch"
2021
],
2122
"engines": {
22-
"node": ">=6"
23+
"node": ">=10.17"
2324
},
2425
"author": "David Frank",
2526
"license": "MIT",
@@ -36,14 +37,16 @@
3637
"xo": "^0.30.0"
3738
},
3839
"xo": {
40+
"rules": {
41+
"node/no-unsupported-features/node-builtins": 0
42+
},
3943
"overrides": [
4044
{
4145
"files": "test.js",
4246
"rules": {
43-
"node/no-unsupported-features/es-syntax": 0,
44-
"node/no-unsupported-features/node-builtins": 0
47+
"node/no-unsupported-features/es-syntax": 0
4548
}
4649
}
4750
]
4851
}
49-
}
52+
}

0 commit comments

Comments
 (0)