9
9
PromiseReject,
10
10
SafePromisePrototypeFinally,
11
11
ReflectConstruct,
12
+ RegExpPrototypeSymbolReplace,
12
13
RegExpPrototypeTest,
13
14
StringPrototypeToLowerCase,
14
15
StringPrototypeSplit,
@@ -24,7 +25,10 @@ const {
24
25
getDataObject,
25
26
} = internalBinding ( 'blob' ) ;
26
27
27
- const { TextDecoder } = require ( 'internal/encoding' ) ;
28
+ const {
29
+ TextDecoder,
30
+ TextEncoder,
31
+ } = require ( 'internal/encoding' ) ;
28
32
29
33
const {
30
34
makeTransferable,
@@ -48,6 +52,7 @@ const {
48
52
AbortError,
49
53
codes : {
50
54
ERR_INVALID_ARG_TYPE ,
55
+ ERR_INVALID_ARG_VALUE ,
51
56
ERR_INVALID_THIS ,
52
57
ERR_BUFFER_TOO_LARGE ,
53
58
}
@@ -68,10 +73,11 @@ const kMaxChunkSize = 65536;
68
73
69
74
const disallowedTypeCharacters = / [ ^ \u{0020} - \u{007E} ] / u;
70
75
71
- let Buffer ;
72
76
let ReadableStream ;
73
77
let URL ;
78
+ let EOL ;
74
79
80
+ const enc = new TextEncoder ( ) ;
75
81
76
82
// Yes, lazy loading is annoying but because of circular
77
83
// references between the url, internal/blob, and buffer
@@ -82,29 +88,35 @@ function lazyURL(id) {
82
88
return new URL ( id ) ;
83
89
}
84
90
85
- function lazyBuffer ( ) {
86
- Buffer ??= require ( 'buffer' ) . Buffer ;
87
- return Buffer ;
88
- }
89
-
90
91
function lazyReadableStream ( options ) {
91
92
ReadableStream ??=
92
93
require ( 'internal/webstreams/readablestream' ) . ReadableStream ;
93
94
return new ReadableStream ( options ) ;
94
95
}
95
96
97
+ // TODO(@jasnell): This is annoying but this has to be lazy because
98
+ // requiring the 'os' module too early causes building Node.js to
99
+ // fail with an unknown reference failure.
100
+ function lazyEOL ( ) {
101
+ EOL ??= require ( 'os' ) . EOL ;
102
+ return EOL ;
103
+ }
104
+
96
105
function isBlob ( object ) {
97
106
return object ?. [ kHandle ] !== undefined ;
98
107
}
99
108
100
- function getSource ( source , encoding ) {
109
+ function getSource ( source , endings ) {
101
110
if ( isBlob ( source ) )
102
111
return [ source . size , source [ kHandle ] ] ;
103
112
104
113
if ( isAnyArrayBuffer ( source ) ) {
105
114
source = new Uint8Array ( source ) ;
106
115
} else if ( ! isArrayBufferView ( source ) ) {
107
- source = lazyBuffer ( ) . from ( `${ source } ` , encoding ) ;
116
+ source = `${ source } ` ;
117
+ if ( endings === 'native' )
118
+ source = RegExpPrototypeSymbolReplace ( / \n | \r \n / g, source , lazyEOL ( ) ) ;
119
+ source = enc . encode ( source ) ;
108
120
}
109
121
110
122
// We copy into a new Uint8Array because the underlying
@@ -116,6 +128,16 @@ function getSource(source, encoding) {
116
128
}
117
129
118
130
class Blob {
131
+ /**
132
+ * @typedef {string|ArrayBuffer|ArrayBufferView|Blob } SourcePart
133
+ *
134
+ * @param {SourcePart[] } [sources]
135
+ * @param {{
136
+ * endings? : string,
137
+ * type? : string,
138
+ * }} [options]
139
+ * @returns
140
+ */
119
141
constructor ( sources = [ ] , options = { } ) {
120
142
emitExperimentalWarning ( 'buffer.Blob' ) ;
121
143
if ( sources === null ||
@@ -124,12 +146,18 @@ class Blob {
124
146
throw new ERR_INVALID_ARG_TYPE ( 'sources' , 'Iterable' , sources ) ;
125
147
}
126
148
validateObject ( options , 'options' ) ;
127
- const { encoding = 'utf8' } = options ;
128
- let { type = '' } = options ;
149
+ let {
150
+ type = '' ,
151
+ endings = 'transparent' ,
152
+ } = options ;
153
+
154
+ endings = `${ endings } ` ;
155
+ if ( endings !== 'transparent' && endings !== 'native' )
156
+ throw new ERR_INVALID_ARG_VALUE ( 'options.endings' , endings ) ;
129
157
130
158
let length = 0 ;
131
159
const sources_ = ArrayFrom ( sources , ( source ) => {
132
- const { 0 : len , 1 : src } = getSource ( source , encoding ) ;
160
+ const { 0 : len , 1 : src } = getSource ( source , endings ) ;
133
161
length += len ;
134
162
return src ;
135
163
} ) ;
0 commit comments