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 {
9- 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 ) ) ;
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+ */
18+ constructor ( blobParts = [ ] , options = { type : '' } ) {
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 ( element . buffer , element . byteOffset , element . byteLength ) ;
26+ }
27+
28+ if ( types . isAnyArrayBuffer ( element ) ) {
29+ return Buffer . from ( element ) ;
30+ }
31+
32+ if ( wm . has ( element ) ) {
33+ return wm . get ( element ) . buffer ;
34+ }
35+
36+ return Buffer . from ( typeof element === 'string' ? element : String ( element ) ) ;
2937 } ) ;
3038
31- const buffer = Buffer . concat ( buffers , size ) ;
39+ const buffer = Buffer . concat ( buffers ) ;
3240
3341 const type = options . type === undefined ? '' : String ( options . type ) . toLowerCase ( ) ;
3442
3543 wm . set ( this , {
3644 type : / [ ^ \u0020 - \u007E ] / . test ( type ) ? '' : type ,
37- size,
3845 buffer
3946 } ) ;
4047 }
4148
49+ /**
50+ * The Blob interface's size property returns the size of the Blob in bytes.
51+ */
4252 get size ( ) {
43- return wm . get ( this ) . size ;
53+ return wm . get ( this ) . buffer . byteLength ;
4454 }
4555
56+ /**
57+ * The type property of a Blob object returns the MIME type of the file.
58+ */
4659 get type ( ) {
4760 return wm . get ( this ) . type ;
4861 }
4962
50- text ( ) {
51- return Promise . resolve ( wm . get ( this ) . buffer . toString ( ) ) ;
63+ /**
64+ * 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.
65+ */
66+ async text ( ) {
67+ return wm . get ( this ) . buffer . toString ( ) ;
5268 }
5369
54- arrayBuffer ( ) {
70+ /**
71+ * 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.
72+ */
73+ async arrayBuffer ( ) {
5574 const buf = wm . get ( this ) . buffer ;
5675 const ab = buf . buffer . slice ( buf . byteOffset , buf . byteOffset + buf . byteLength ) ;
57- return Promise . resolve ( ab ) ;
76+ return ab ;
5877 }
5978
79+ /**
80+ * The Blob interface's stream() method returns a ReadableStream which upon reading returns the data contained within the Blob.
81+ */
6082 stream ( ) {
61- const readable = new ReadableStream ( ) ;
62- readable . _read = ( ) => { } ;
63- readable . push ( wm . get ( this ) . buffer ) ;
64- readable . push ( null ) ;
65- return readable ;
83+ return Readable . from ( wm . get ( this ) . buffer ) ;
84+ }
85+
86+ get [ Symbol . toStringTag ] ( ) {
87+ return 'Blob' ;
6688 }
6789
90+ /**
91+ * @returns {string }
92+ */
6893 toString ( ) {
69- return '[object Blob]' ;
94+ return Object . prototype . toString . call ( this ) ;
7095 }
7196
72- slice ( ...args ) {
73- const { size} = this ;
97+ /**
98+ * 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.
99+ *
100+ * @param {number } [start]
101+ * @param {number } [end]
102+ * @param {string } [contentType]
103+ */
104+ slice ( start , end , contentType ) {
105+ const { size } = this ;
74106
75- const start = args [ 0 ] ;
76- const end = args [ 1 ] ;
77107 let relativeStart ;
78108 let relativeEnd ;
79109
@@ -98,24 +128,17 @@ class Blob {
98128 relativeStart ,
99129 relativeStart + span
100130 ) ;
101- const blob = new Blob ( [ ] , { type : args [ 2 ] } ) ;
131+ const blob = new Blob ( [ ] , { type : contentType } ) ;
102132 const _ = wm . get ( blob ) ;
103133 _ . buffer = slicedBuffer ;
104134 return blob ;
105135 }
106136}
107137
108138Object . defineProperties ( Blob . prototype , {
109- size : { enumerable : true } ,
110- type : { enumerable : true } ,
111- slice : { enumerable : true }
112- } ) ;
113-
114- Object . defineProperty ( Blob . prototype , Symbol . toStringTag , {
115- value : 'Blob' ,
116- writable : false ,
117- enumerable : false ,
118- configurable : true
139+ size : { enumerable : true } ,
140+ type : { enumerable : true } ,
141+ slice : { enumerable : true }
119142} ) ;
120143
121144module . exports = Blob ;
0 commit comments