source: node_modules/undici/lib/fetch/file.js@ 65b6638

main
Last change on this file since 65b6638 was d24f17c, checked in by Aleksandar Panovski <apano77@…>, 15 months ago

Initial commit

  • Property mode set to 100644
File size: 9.3 KB
RevLine 
[d24f17c]1'use strict'
2
3const { Blob, File: NativeFile } = require('buffer')
4const { types } = require('util')
5const { kState } = require('./symbols')
6const { isBlobLike } = require('./util')
7const { webidl } = require('./webidl')
8const { parseMIMEType, serializeAMimeType } = require('./dataURL')
9const { kEnumerableProperty } = require('../core/util')
10const encoder = new TextEncoder()
11
12class File extends Blob {
13 constructor (fileBits, fileName, options = {}) {
14 // The File constructor is invoked with two or three parameters, depending
15 // on whether the optional dictionary parameter is used. When the File()
16 // constructor is invoked, user agents must run the following steps:
17 webidl.argumentLengthCheck(arguments, 2, { header: 'File constructor' })
18
19 fileBits = webidl.converters['sequence<BlobPart>'](fileBits)
20 fileName = webidl.converters.USVString(fileName)
21 options = webidl.converters.FilePropertyBag(options)
22
23 // 1. Let bytes be the result of processing blob parts given fileBits and
24 // options.
25 // Note: Blob handles this for us
26
27 // 2. Let n be the fileName argument to the constructor.
28 const n = fileName
29
30 // 3. Process FilePropertyBag dictionary argument by running the following
31 // substeps:
32
33 // 1. If the type member is provided and is not the empty string, let t
34 // be set to the type dictionary member. If t contains any characters
35 // outside the range U+0020 to U+007E, then set t to the empty string
36 // and return from these substeps.
37 // 2. Convert every character in t to ASCII lowercase.
38 let t = options.type
39 let d
40
41 // eslint-disable-next-line no-labels
42 substep: {
43 if (t) {
44 t = parseMIMEType(t)
45
46 if (t === 'failure') {
47 t = ''
48 // eslint-disable-next-line no-labels
49 break substep
50 }
51
52 t = serializeAMimeType(t).toLowerCase()
53 }
54
55 // 3. If the lastModified member is provided, let d be set to the
56 // lastModified dictionary member. If it is not provided, set d to the
57 // current date and time represented as the number of milliseconds since
58 // the Unix Epoch (which is the equivalent of Date.now() [ECMA-262]).
59 d = options.lastModified
60 }
61
62 // 4. Return a new File object F such that:
63 // F refers to the bytes byte sequence.
64 // F.size is set to the number of total bytes in bytes.
65 // F.name is set to n.
66 // F.type is set to t.
67 // F.lastModified is set to d.
68
69 super(processBlobParts(fileBits, options), { type: t })
70 this[kState] = {
71 name: n,
72 lastModified: d,
73 type: t
74 }
75 }
76
77 get name () {
78 webidl.brandCheck(this, File)
79
80 return this[kState].name
81 }
82
83 get lastModified () {
84 webidl.brandCheck(this, File)
85
86 return this[kState].lastModified
87 }
88
89 get type () {
90 webidl.brandCheck(this, File)
91
92 return this[kState].type
93 }
94}
95
96class FileLike {
97 constructor (blobLike, fileName, options = {}) {
98 // TODO: argument idl type check
99
100 // The File constructor is invoked with two or three parameters, depending
101 // on whether the optional dictionary parameter is used. When the File()
102 // constructor is invoked, user agents must run the following steps:
103
104 // 1. Let bytes be the result of processing blob parts given fileBits and
105 // options.
106
107 // 2. Let n be the fileName argument to the constructor.
108 const n = fileName
109
110 // 3. Process FilePropertyBag dictionary argument by running the following
111 // substeps:
112
113 // 1. If the type member is provided and is not the empty string, let t
114 // be set to the type dictionary member. If t contains any characters
115 // outside the range U+0020 to U+007E, then set t to the empty string
116 // and return from these substeps.
117 // TODO
118 const t = options.type
119
120 // 2. Convert every character in t to ASCII lowercase.
121 // TODO
122
123 // 3. If the lastModified member is provided, let d be set to the
124 // lastModified dictionary member. If it is not provided, set d to the
125 // current date and time represented as the number of milliseconds since
126 // the Unix Epoch (which is the equivalent of Date.now() [ECMA-262]).
127 const d = options.lastModified ?? Date.now()
128
129 // 4. Return a new File object F such that:
130 // F refers to the bytes byte sequence.
131 // F.size is set to the number of total bytes in bytes.
132 // F.name is set to n.
133 // F.type is set to t.
134 // F.lastModified is set to d.
135
136 this[kState] = {
137 blobLike,
138 name: n,
139 type: t,
140 lastModified: d
141 }
142 }
143
144 stream (...args) {
145 webidl.brandCheck(this, FileLike)
146
147 return this[kState].blobLike.stream(...args)
148 }
149
150 arrayBuffer (...args) {
151 webidl.brandCheck(this, FileLike)
152
153 return this[kState].blobLike.arrayBuffer(...args)
154 }
155
156 slice (...args) {
157 webidl.brandCheck(this, FileLike)
158
159 return this[kState].blobLike.slice(...args)
160 }
161
162 text (...args) {
163 webidl.brandCheck(this, FileLike)
164
165 return this[kState].blobLike.text(...args)
166 }
167
168 get size () {
169 webidl.brandCheck(this, FileLike)
170
171 return this[kState].blobLike.size
172 }
173
174 get type () {
175 webidl.brandCheck(this, FileLike)
176
177 return this[kState].blobLike.type
178 }
179
180 get name () {
181 webidl.brandCheck(this, FileLike)
182
183 return this[kState].name
184 }
185
186 get lastModified () {
187 webidl.brandCheck(this, FileLike)
188
189 return this[kState].lastModified
190 }
191
192 get [Symbol.toStringTag] () {
193 return 'File'
194 }
195}
196
197Object.defineProperties(File.prototype, {
198 [Symbol.toStringTag]: {
199 value: 'File',
200 configurable: true
201 },
202 name: kEnumerableProperty,
203 lastModified: kEnumerableProperty
204})
205
206webidl.converters.Blob = webidl.interfaceConverter(Blob)
207
208webidl.converters.BlobPart = function (V, opts) {
209 if (webidl.util.Type(V) === 'Object') {
210 if (isBlobLike(V)) {
211 return webidl.converters.Blob(V, { strict: false })
212 }
213
214 if (
215 ArrayBuffer.isView(V) ||
216 types.isAnyArrayBuffer(V)
217 ) {
218 return webidl.converters.BufferSource(V, opts)
219 }
220 }
221
222 return webidl.converters.USVString(V, opts)
223}
224
225webidl.converters['sequence<BlobPart>'] = webidl.sequenceConverter(
226 webidl.converters.BlobPart
227)
228
229// https://www.w3.org/TR/FileAPI/#dfn-FilePropertyBag
230webidl.converters.FilePropertyBag = webidl.dictionaryConverter([
231 {
232 key: 'lastModified',
233 converter: webidl.converters['long long'],
234 get defaultValue () {
235 return Date.now()
236 }
237 },
238 {
239 key: 'type',
240 converter: webidl.converters.DOMString,
241 defaultValue: ''
242 },
243 {
244 key: 'endings',
245 converter: (value) => {
246 value = webidl.converters.DOMString(value)
247 value = value.toLowerCase()
248
249 if (value !== 'native') {
250 value = 'transparent'
251 }
252
253 return value
254 },
255 defaultValue: 'transparent'
256 }
257])
258
259/**
260 * @see https://www.w3.org/TR/FileAPI/#process-blob-parts
261 * @param {(NodeJS.TypedArray|Blob|string)[]} parts
262 * @param {{ type: string, endings: string }} options
263 */
264function processBlobParts (parts, options) {
265 // 1. Let bytes be an empty sequence of bytes.
266 /** @type {NodeJS.TypedArray[]} */
267 const bytes = []
268
269 // 2. For each element in parts:
270 for (const element of parts) {
271 // 1. If element is a USVString, run the following substeps:
272 if (typeof element === 'string') {
273 // 1. Let s be element.
274 let s = element
275
276 // 2. If the endings member of options is "native", set s
277 // to the result of converting line endings to native
278 // of element.
279 if (options.endings === 'native') {
280 s = convertLineEndingsNative(s)
281 }
282
283 // 3. Append the result of UTF-8 encoding s to bytes.
284 bytes.push(encoder.encode(s))
285 } else if (
286 types.isAnyArrayBuffer(element) ||
287 types.isTypedArray(element)
288 ) {
289 // 2. If element is a BufferSource, get a copy of the
290 // bytes held by the buffer source, and append those
291 // bytes to bytes.
292 if (!element.buffer) { // ArrayBuffer
293 bytes.push(new Uint8Array(element))
294 } else {
295 bytes.push(
296 new Uint8Array(element.buffer, element.byteOffset, element.byteLength)
297 )
298 }
299 } else if (isBlobLike(element)) {
300 // 3. If element is a Blob, append the bytes it represents
301 // to bytes.
302 bytes.push(element)
303 }
304 }
305
306 // 3. Return bytes.
307 return bytes
308}
309
310/**
311 * @see https://www.w3.org/TR/FileAPI/#convert-line-endings-to-native
312 * @param {string} s
313 */
314function convertLineEndingsNative (s) {
315 // 1. Let native line ending be be the code point U+000A LF.
316 let nativeLineEnding = '\n'
317
318 // 2. If the underlying platform’s conventions are to
319 // represent newlines as a carriage return and line feed
320 // sequence, set native line ending to the code point
321 // U+000D CR followed by the code point U+000A LF.
322 if (process.platform === 'win32') {
323 nativeLineEnding = '\r\n'
324 }
325
326 return s.replace(/\r?\n/g, nativeLineEnding)
327}
328
329// If this function is moved to ./util.js, some tools (such as
330// rollup) will warn about circular dependencies. See:
331// https://github.com/nodejs/undici/issues/1629
332function isFileLike (object) {
333 return (
334 (NativeFile && object instanceof NativeFile) ||
335 object instanceof File || (
336 object &&
337 (typeof object.stream === 'function' ||
338 typeof object.arrayBuffer === 'function') &&
339 object[Symbol.toStringTag] === 'File'
340 )
341 )
342}
343
344module.exports = { File, FileLike, isFileLike }
Note: See TracBrowser for help on using the repository browser.