1
1
// Based on https://github.com/tmpvar/jsdom/blob/aa85b2abf07766ff7bf5c1f6daafb3726f2f2db5/lib/jsdom/living/blob.js
2
2
// (MIT licensed)
3
3
4
- const { Readable : ReadableStream } = require ( 'stream' ) ;
4
+ const { Readable} = require ( 'stream' ) ;
5
+ const { types} = require ( 'util' ) ;
5
6
7
+ /**
8
+ * @type {WeakMap<Blob, { type: string, buffer: Buffer } }
9
+ */
6
10
const wm = new WeakMap ( ) ;
7
11
8
12
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
+ */
9
18
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 ;
25
22
}
26
23
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
+ ) ;
29
43
} ) ;
30
44
31
- const buffer = Buffer . concat ( buffers , size ) ;
45
+ const buffer = Buffer . concat ( buffers ) ;
32
46
33
- const type = options . type === undefined ? '' : String ( options . type ) . toLowerCase ( ) ;
47
+ const type =
48
+ options . type === undefined ? '' : String ( options . type ) . toLowerCase ( ) ;
34
49
35
50
wm . set ( this , {
36
51
type : / [ ^ \u0020 - \u007E ] / . test ( type ) ? '' : type ,
37
- size,
38
52
buffer
39
53
} ) ;
40
54
}
41
55
56
+ /**
57
+ * The Blob interface's size property returns the size of the Blob in bytes.
58
+ */
42
59
get size ( ) {
43
- return wm . get ( this ) . size ;
60
+ return wm . get ( this ) . buffer . byteLength ;
44
61
}
45
62
63
+ /**
64
+ * The type property of a Blob object returns the MIME type of the file.
65
+ */
46
66
get type ( ) {
47
67
return wm . get ( this ) . type ;
48
68
}
49
69
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 ( ) ;
52
75
}
53
76
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 ( ) {
55
81
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 ;
58
87
}
59
88
89
+ /**
90
+ * The Blob interface's stream() method returns a ReadableStream which upon reading returns the data contained within the Blob.
91
+ */
60
92
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' ;
66
98
}
67
99
100
+ /**
101
+ * @returns {string }
102
+ */
68
103
toString ( ) {
69
- return '[object Blob]' ;
104
+ return Object . prototype . toString . call ( this ) ;
70
105
}
71
106
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 } ) ;
105
118
}
106
119
}
107
120
@@ -111,11 +124,4 @@ Object.defineProperties(Blob.prototype, {
111
124
slice : { enumerable : true }
112
125
} ) ;
113
126
114
- Object . defineProperty ( Blob . prototype , Symbol . toStringTag , {
115
- value : 'Blob' ,
116
- writable : false ,
117
- enumerable : false ,
118
- configurable : true
119
- } ) ;
120
-
121
127
module . exports = Blob ;
0 commit comments