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+ */
610const wm = new WeakMap ( ) ;
711
812class 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-
121127module . exports = Blob ;
0 commit comments