From f8940e9a81a0af8bfb099266ef9786defed843f0 Mon Sep 17 00:00:00 2001 From: Khafra <42794878+KhafraDev@users.noreply.github.com> Date: Sun, 23 Oct 2022 22:41:20 -0400 Subject: [PATCH 01/15] buffer: introduce File --- doc/api/buffer.md | 46 ++++++++++++++ lib/buffer.js | 5 ++ lib/internal/file.js | 117 +++++++++++++++++++++++++++++++++++ test/parallel/test-file.js | 122 +++++++++++++++++++++++++++++++++++++ 4 files changed, 290 insertions(+) create mode 100644 lib/internal/file.js create mode 100644 test/parallel/test-file.js diff --git a/doc/api/buffer.md b/doc/api/buffer.md index 8b46553ec4941a..cb40904947bb86 100644 --- a/doc/api/buffer.md +++ b/doc/api/buffer.md @@ -5013,6 +5013,51 @@ changes: See [`Buffer.from(string[, encoding])`][`Buffer.from(string)`]. +## Class: `File` + + +> Stability: 1 - Experimental + +* Extends: {Blob} + +A [`File`][] provides information about files. + +### `new buffer.File(sources, fileName[, options])` + + +* `sources` {string\[]|ArrayBuffer\[]|TypedArray\[]|DataView\[]|Blob\[]|File\[]} + An array of string, {ArrayBuffer}, {TypedArray}, {DataView}, {File}, or {Blob} + objects, or any mix of such objects, that will be stored within the `File`. +* `fileName` {string} The name of the file. +* `options` {Object} + * `endings` {string} One of either `'transparent'` or `'native'`. When set + to `'native'`, line endings in string source parts will be converted to + the platform native line-ending as specified by `require('node:os').EOL`. + * `type` {string} The File content-type. + * `lastModified` {number} The last modified date of the file. + +### `file.name` + + +* Type: {string} + +The name of the `File`. + +### `file.lastModified` + + +* Type: {number} + +The last modified date of the `File`. + ## `node:buffer` module APIs While, the `Buffer` object is available as a global, there are additional @@ -5359,6 +5404,7 @@ introducing security vulnerabilities into an application. [`ERR_INVALID_ARG_VALUE`]: errors.md#err_invalid_arg_value [`ERR_INVALID_BUFFER_SIZE`]: errors.md#err_invalid_buffer_size [`ERR_OUT_OF_RANGE`]: errors.md#err_out_of_range +[`File`]: https://developer.mozilla.org/en-US/docs/Web/API/File [`JSON.stringify()`]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/JSON/stringify [`SharedArrayBuffer`]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/SharedArrayBuffer [`String.prototype.indexOf()`]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/indexOf diff --git a/lib/buffer.js b/lib/buffer.js index 7c0bbbc81c6398..898bc5032e5f8d 100644 --- a/lib/buffer.js +++ b/lib/buffer.js @@ -126,6 +126,10 @@ const { resolveObjectURL, } = require('internal/blob'); +const { + File, +} = require('internal/file'); + FastBuffer.prototype.constructor = Buffer; Buffer.prototype = FastBuffer.prototype; addBufferPrototypeMethods(Buffer.prototype); @@ -1320,6 +1324,7 @@ function atob(input) { module.exports = { Blob, + File, resolveObjectURL, Buffer, SlowBuffer, diff --git a/lib/internal/file.js b/lib/internal/file.js new file mode 100644 index 00000000000000..a7fec74261ac09 --- /dev/null +++ b/lib/internal/file.js @@ -0,0 +1,117 @@ +'use strict'; + +const { + DateNow, + NumberIsNaN, + ObjectDefineProperties, + ObjectPrototypeHasOwnProperty, + Symbol, + SymbolToStringTag, +} = primordials; + +const { + Blob, +} = require('internal/blob'); + +const { + customInspectSymbol: kInspect, + emitExperimentalWarning, + kEnumerableProperty, + kEmptyObject, + toUSVString, +} = require('internal/util'); + +const { + codes: { + ERR_INVALID_THIS, + ERR_MISSING_ARGS, + }, +} = require('internal/errors'); + +const { + inspect, +} = require('internal/util/inspect'); + +const kState = Symbol('kState'); + +function isFile(object) { + return object?.[kState] !== undefined || object instanceof Blob; +} + +class File extends Blob { + constructor(fileBits, fileName, options = kEmptyObject) { + emitExperimentalWarning('buffer.File'); + + if (arguments.length < 2) { + throw new ERR_MISSING_ARGS('fileBits', 'fileName'); + } + + super(fileBits, options); + + let lastModified; + + if (ObjectPrototypeHasOwnProperty(options, 'lastModified')) { + // Using Number(...) will not throw an error for bigints. + lastModified = +options.lastModified; + + if (NumberIsNaN(lastModified)) { + lastModified = 0; + } + } else { + lastModified = DateNow(); + } + + this[kState] = { + name: toUSVString(fileName), + lastModified: lastModified, + }; + } + + get name() { + if (!isFile(this)) { + throw new ERR_INVALID_THIS('File'); + } + + return this[kState].name; + } + + get lastModified() { + if (!isFile(this)) { + throw new ERR_INVALID_THIS('File'); + } + + return this[kState].lastModified; + } + + [kInspect](depth, options) { + if (depth < 0) { + return this; + } + + const opts = { + ...options, + depth: options.depth == null ? null : options.depth - 1, + }; + + return `File ${inspect({ + size: this.size, + type: this.type, + name: this.name, + lastModified: this.lastModified, + }, opts)}`; + } +} + +ObjectDefineProperties(File.prototype, { + name: kEnumerableProperty, + lastModified: kEnumerableProperty, + [SymbolToStringTag]: { + __proto__: null, + configurable: true, + value: 'File', + } +}); + +module.exports = { + File, +}; diff --git a/test/parallel/test-file.js b/test/parallel/test-file.js new file mode 100644 index 00000000000000..90e7ae4fcc70bf --- /dev/null +++ b/test/parallel/test-file.js @@ -0,0 +1,122 @@ +'use strict'; + +const common = require('../common'); +const assert = require('assert'); +const { Blob, File } = require('buffer'); +const { inspect } = require('util'); + +{ + // ensure File extends Blob + assert.deepStrictEqual(Object.getPrototypeOf(File.prototype), Blob.prototype); +} + +{ + assert.throws(() => new File(), TypeError); + assert.throws(() => new File([]), TypeError); +} + +{ + const properties = ['name', 'lastModified']; + + for (const prop of properties) { + const desc = Object.getOwnPropertyDescriptor(File.prototype, prop); + assert.notStrictEqual(desc, undefined); + // Ensure these properties are getters. + assert.strictEqual(desc.get?.name, `get ${prop}`); + assert.strictEqual(desc.set, undefined); + assert.strictEqual(desc.enumerable, true); + assert.strictEqual(desc.configurable, true); + } +} + +{ + const file = new File([], ''); + assert.strictEqual(file[Symbol.toStringTag], 'File'); + assert.strictEqual(File.prototype[Symbol.toStringTag], 'File'); +} + +{ + assert.throws(() => File.prototype.name, TypeError); + assert.throws(() => File.prototype.lastModified, TypeError); +} + +{ + const keys = Object.keys(File.prototype).sort(); + assert.deepStrictEqual(keys, ['lastModified', 'name']); +} + +{ + const file = new File([], 'dummy.txt.exe'); + assert.strictEqual(file.name, 'dummy.txt.exe'); + assert.strictEqual(file.size, 0); + assert.strictEqual(typeof file.lastModified, 'number'); + assert(file.lastModified <= Date.now()); +} + +{ + const emptyFile = new File([], 'empty.txt'); + const blob = new Blob(['hello world']); + + emptyFile.text.call(blob).then(common.mustCall((text) => { + assert.strictEqual(text, 'hello world'); + })); +} + +{ + const toPrimitive = { + [Symbol.toPrimitive]() { + return 'NaN'; + } + }; + + const invalidLastModified = [ + null, + undefined, + 'string', + false, + toPrimitive, + ]; + + for (const lastModified of invalidLastModified) { + const file = new File([], '', { lastModified }); + assert.strictEqual(file.lastModified, 0); + } +} + +{ + const toPrimitive = { + [Symbol.toPrimitive]() { + throw new TypeError('boom'); + } + }; + + const throwValues = [ + BigInt(3n), + toPrimitive, + ]; + + for (const lastModified of throwValues) { + assert.throws(() => new File([], '', { lastModified }), TypeError); + } +} + +{ + const valid = [ + { + [Symbol.toPrimitive]() { + return 10; + } + }, + new Number(10), + 10, + ]; + + for (const lastModified of valid) { + assert.strictEqual(new File([], '', { lastModified }).lastModified, 10); + } +} + +{ + const file = new File([], ''); + assert(inspect(file).startsWith('File { size: 0, type: \'\', name: \'\', lastModified:')); +} From 5cd047dbbd2fd91cc7d4ae0facc89f0a6aaf8db3 Mon Sep 17 00:00:00 2001 From: Khafra <42794878+KhafraDev@users.noreply.github.com> Date: Sun, 23 Oct 2022 23:09:24 -0400 Subject: [PATCH 02/15] fix: update type-parser --- tools/doc/type-parser.mjs | 1 + 1 file changed, 1 insertion(+) diff --git a/tools/doc/type-parser.mjs b/tools/doc/type-parser.mjs index b660949ce3c5c7..99503f4924bebf 100644 --- a/tools/doc/type-parser.mjs +++ b/tools/doc/type-parser.mjs @@ -43,6 +43,7 @@ const customTypesMap = { `${jsDocPrefix}Reference/Global_Objects/WebAssembly/Instance`, 'Blob': 'buffer.html#class-blob', + 'File': 'buffer.html#class-file', 'BroadcastChannel': 'worker_threads.html#class-broadcastchannel-' + From 991927dba154ee926125ab80f0c1e36a83bc9734 Mon Sep 17 00:00:00 2001 From: Khafra <42794878+KhafraDev@users.noreply.github.com> Date: Sun, 23 Oct 2022 23:16:26 -0400 Subject: [PATCH 03/15] fix: change linebreak to lf --- test/parallel/test-file.js | 244 ++++++++++++++++++------------------- 1 file changed, 122 insertions(+), 122 deletions(-) diff --git a/test/parallel/test-file.js b/test/parallel/test-file.js index 90e7ae4fcc70bf..0dfbc75991d52e 100644 --- a/test/parallel/test-file.js +++ b/test/parallel/test-file.js @@ -1,122 +1,122 @@ -'use strict'; - -const common = require('../common'); -const assert = require('assert'); -const { Blob, File } = require('buffer'); -const { inspect } = require('util'); - -{ - // ensure File extends Blob - assert.deepStrictEqual(Object.getPrototypeOf(File.prototype), Blob.prototype); -} - -{ - assert.throws(() => new File(), TypeError); - assert.throws(() => new File([]), TypeError); -} - -{ - const properties = ['name', 'lastModified']; - - for (const prop of properties) { - const desc = Object.getOwnPropertyDescriptor(File.prototype, prop); - assert.notStrictEqual(desc, undefined); - // Ensure these properties are getters. - assert.strictEqual(desc.get?.name, `get ${prop}`); - assert.strictEqual(desc.set, undefined); - assert.strictEqual(desc.enumerable, true); - assert.strictEqual(desc.configurable, true); - } -} - -{ - const file = new File([], ''); - assert.strictEqual(file[Symbol.toStringTag], 'File'); - assert.strictEqual(File.prototype[Symbol.toStringTag], 'File'); -} - -{ - assert.throws(() => File.prototype.name, TypeError); - assert.throws(() => File.prototype.lastModified, TypeError); -} - -{ - const keys = Object.keys(File.prototype).sort(); - assert.deepStrictEqual(keys, ['lastModified', 'name']); -} - -{ - const file = new File([], 'dummy.txt.exe'); - assert.strictEqual(file.name, 'dummy.txt.exe'); - assert.strictEqual(file.size, 0); - assert.strictEqual(typeof file.lastModified, 'number'); - assert(file.lastModified <= Date.now()); -} - -{ - const emptyFile = new File([], 'empty.txt'); - const blob = new Blob(['hello world']); - - emptyFile.text.call(blob).then(common.mustCall((text) => { - assert.strictEqual(text, 'hello world'); - })); -} - -{ - const toPrimitive = { - [Symbol.toPrimitive]() { - return 'NaN'; - } - }; - - const invalidLastModified = [ - null, - undefined, - 'string', - false, - toPrimitive, - ]; - - for (const lastModified of invalidLastModified) { - const file = new File([], '', { lastModified }); - assert.strictEqual(file.lastModified, 0); - } -} - -{ - const toPrimitive = { - [Symbol.toPrimitive]() { - throw new TypeError('boom'); - } - }; - - const throwValues = [ - BigInt(3n), - toPrimitive, - ]; - - for (const lastModified of throwValues) { - assert.throws(() => new File([], '', { lastModified }), TypeError); - } -} - -{ - const valid = [ - { - [Symbol.toPrimitive]() { - return 10; - } - }, - new Number(10), - 10, - ]; - - for (const lastModified of valid) { - assert.strictEqual(new File([], '', { lastModified }).lastModified, 10); - } -} - -{ - const file = new File([], ''); - assert(inspect(file).startsWith('File { size: 0, type: \'\', name: \'\', lastModified:')); -} +'use strict'; + +const common = require('../common'); +const assert = require('assert'); +const { Blob, File } = require('buffer'); +const { inspect } = require('util'); + +{ + // ensure File extends Blob + assert.deepStrictEqual(Object.getPrototypeOf(File.prototype), Blob.prototype); +} + +{ + assert.throws(() => new File(), TypeError); + assert.throws(() => new File([]), TypeError); +} + +{ + const properties = ['name', 'lastModified']; + + for (const prop of properties) { + const desc = Object.getOwnPropertyDescriptor(File.prototype, prop); + assert.notStrictEqual(desc, undefined); + // Ensure these properties are getters. + assert.strictEqual(desc.get?.name, `get ${prop}`); + assert.strictEqual(desc.set, undefined); + assert.strictEqual(desc.enumerable, true); + assert.strictEqual(desc.configurable, true); + } +} + +{ + const file = new File([], ''); + assert.strictEqual(file[Symbol.toStringTag], 'File'); + assert.strictEqual(File.prototype[Symbol.toStringTag], 'File'); +} + +{ + assert.throws(() => File.prototype.name, TypeError); + assert.throws(() => File.prototype.lastModified, TypeError); +} + +{ + const keys = Object.keys(File.prototype).sort(); + assert.deepStrictEqual(keys, ['lastModified', 'name']); +} + +{ + const file = new File([], 'dummy.txt.exe'); + assert.strictEqual(file.name, 'dummy.txt.exe'); + assert.strictEqual(file.size, 0); + assert.strictEqual(typeof file.lastModified, 'number'); + assert(file.lastModified <= Date.now()); +} + +{ + const emptyFile = new File([], 'empty.txt'); + const blob = new Blob(['hello world']); + + emptyFile.text.call(blob).then(common.mustCall((text) => { + assert.strictEqual(text, 'hello world'); + })); +} + +{ + const toPrimitive = { + [Symbol.toPrimitive]() { + return 'NaN'; + } + }; + + const invalidLastModified = [ + null, + undefined, + 'string', + false, + toPrimitive, + ]; + + for (const lastModified of invalidLastModified) { + const file = new File([], '', { lastModified }); + assert.strictEqual(file.lastModified, 0); + } +} + +{ + const toPrimitive = { + [Symbol.toPrimitive]() { + throw new TypeError('boom'); + } + }; + + const throwValues = [ + BigInt(3n), + toPrimitive, + ]; + + for (const lastModified of throwValues) { + assert.throws(() => new File([], '', { lastModified }), TypeError); + } +} + +{ + const valid = [ + { + [Symbol.toPrimitive]() { + return 10; + } + }, + new Number(10), + 10, + ]; + + for (const lastModified of valid) { + assert.strictEqual(new File([], '', { lastModified }).lastModified, 10); + } +} + +{ + const file = new File([], ''); + assert(inspect(file).startsWith('File { size: 0, type: \'\', name: \'\', lastModified:')); +} From 8325e812241d204e92b73d79c2388a5460752e49 Mon Sep 17 00:00:00 2001 From: Khafra <42794878+KhafraDev@users.noreply.github.com> Date: Sun, 23 Oct 2022 23:26:19 -0400 Subject: [PATCH 04/15] fix: format docs --- doc/api/buffer.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/doc/api/buffer.md b/doc/api/buffer.md index cb40904947bb86..19d277b628eeba 100644 --- a/doc/api/buffer.md +++ b/doc/api/buffer.md @@ -5014,6 +5014,7 @@ changes: See [`Buffer.from(string[, encoding])`][`Buffer.from(string)`]. ## Class: `File` + @@ -5025,6 +5026,7 @@ added: REPLACEME A [`File`][] provides information about files. ### `new buffer.File(sources, fileName[, options])` + @@ -5041,6 +5043,7 @@ added: REPLACEME * `lastModified` {number} The last modified date of the file. ### `file.name` + @@ -5050,6 +5053,7 @@ added: REPLACEME The name of the `File`. ### `file.lastModified` + From 246bb6f811b11f496ce25ea27bf2a23f8a027829 Mon Sep 17 00:00:00 2001 From: Khafra <42794878+KhafraDev@users.noreply.github.com> Date: Mon, 24 Oct 2022 09:33:36 -0400 Subject: [PATCH 05/15] fix: use shorthand property --- lib/internal/file.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/internal/file.js b/lib/internal/file.js index a7fec74261ac09..dff4094a89f167 100644 --- a/lib/internal/file.js +++ b/lib/internal/file.js @@ -63,7 +63,7 @@ class File extends Blob { this[kState] = { name: toUSVString(fileName), - lastModified: lastModified, + lastModified, }; } From 1944749db285ee9213b87ca6893bed84334c7e72 Mon Sep 17 00:00:00 2001 From: Khafra <42794878+KhafraDev@users.noreply.github.com> Date: Mon, 24 Oct 2022 10:10:53 -0400 Subject: [PATCH 06/15] fix: add FileAPI/file WPTs --- .../wpt/FileAPI/file/File-constructor.any.js | 155 ++++++++++++++++++ .../file/send-file-formdata-controls.any.js | 69 ++++++++ .../send-file-formdata-punctuation.any.js | 144 ++++++++++++++++ .../file/send-file-formdata-utf-8.any.js | 33 ++++ .../FileAPI/file/send-file-formdata.any.js | 8 + test/fixtures/wpt/README.md | 1 + test/fixtures/wpt/versions.json | 4 + test/wpt/status/FileAPI/file.json | 25 +++ test/wpt/test-file.js | 13 ++ 9 files changed, 452 insertions(+) create mode 100644 test/fixtures/wpt/FileAPI/file/File-constructor.any.js create mode 100644 test/fixtures/wpt/FileAPI/file/send-file-formdata-controls.any.js create mode 100644 test/fixtures/wpt/FileAPI/file/send-file-formdata-punctuation.any.js create mode 100644 test/fixtures/wpt/FileAPI/file/send-file-formdata-utf-8.any.js create mode 100644 test/fixtures/wpt/FileAPI/file/send-file-formdata.any.js create mode 100644 test/wpt/status/FileAPI/file.json create mode 100644 test/wpt/test-file.js diff --git a/test/fixtures/wpt/FileAPI/file/File-constructor.any.js b/test/fixtures/wpt/FileAPI/file/File-constructor.any.js new file mode 100644 index 00000000000000..0b0185c40bf83c --- /dev/null +++ b/test/fixtures/wpt/FileAPI/file/File-constructor.any.js @@ -0,0 +1,155 @@ +// META: title=File constructor + +const to_string_obj = { toString: () => 'a string' }; +const to_string_throws = { toString: () => { throw new Error('expected'); } }; + +test(function() { + assert_true("File" in globalThis, "globalThis should have a File property."); +}, "File interface object exists"); + +test(t => { + assert_throws_js(TypeError, () => new File(), + 'Bits argument is required'); + assert_throws_js(TypeError, () => new File([]), + 'Name argument is required'); +}, 'Required arguments'); + +function test_first_argument(arg1, expectedSize, testName) { + test(function() { + var file = new File(arg1, "dummy"); + assert_true(file instanceof File); + assert_equals(file.name, "dummy"); + assert_equals(file.size, expectedSize); + assert_equals(file.type, ""); + // assert_false(file.isClosed); XXX: File.isClosed doesn't seem to be implemented + assert_not_equals(file.lastModified, ""); + }, testName); +} + +test_first_argument([], 0, "empty fileBits"); +test_first_argument(["bits"], 4, "DOMString fileBits"); +test_first_argument(["๐“ฝ๐“ฎ๐”๐“ฝ"], 16, "Unicode DOMString fileBits"); +test_first_argument([new String('string object')], 13, "String object fileBits"); +test_first_argument([new Blob()], 0, "Empty Blob fileBits"); +test_first_argument([new Blob(["bits"])], 4, "Blob fileBits"); +test_first_argument([new File([], 'world.txt')], 0, "Empty File fileBits"); +test_first_argument([new File(["bits"], 'world.txt')], 4, "File fileBits"); +test_first_argument([new ArrayBuffer(8)], 8, "ArrayBuffer fileBits"); +test_first_argument([new Uint8Array([0x50, 0x41, 0x53, 0x53])], 4, "Typed array fileBits"); +test_first_argument(["bits", new Blob(["bits"]), new Blob(), new Uint8Array([0x50, 0x41]), + new Uint16Array([0x5353]), new Uint32Array([0x53534150])], 16, "Various fileBits"); +test_first_argument([12], 2, "Number in fileBits"); +test_first_argument([[1,2,3]], 5, "Array in fileBits"); +test_first_argument([{}], 15, "Object in fileBits"); // "[object Object]" +if (globalThis.document !== undefined) { + test_first_argument([document.body], 24, "HTMLBodyElement in fileBits"); // "[object HTMLBodyElement]" +} +test_first_argument([to_string_obj], 8, "Object with toString in fileBits"); +test_first_argument({[Symbol.iterator]() { + let i = 0; + return {next: () => [ + {done:false, value:'ab'}, + {done:false, value:'cde'}, + {done:true} + ][i++]}; +}}, 5, 'Custom @@iterator'); + +[ + 'hello', + 0, + null +].forEach(arg => { + test(t => { + assert_throws_js(TypeError, () => new File(arg, 'world.html'), + 'Constructor should throw for invalid bits argument'); + }, `Invalid bits argument: ${JSON.stringify(arg)}`); +}); + +test(t => { + assert_throws_js(Error, () => new File([to_string_throws], 'name.txt'), + 'Constructor should propagate exceptions'); +}, 'Bits argument: object that throws'); + + +function test_second_argument(arg2, expectedFileName, testName) { + test(function() { + var file = new File(["bits"], arg2); + assert_true(file instanceof File); + assert_equals(file.name, expectedFileName); + }, testName); +} + +test_second_argument("dummy", "dummy", "Using fileName"); +test_second_argument("dummy/foo", "dummy/foo", + "No replacement when using special character in fileName"); +test_second_argument(null, "null", "Using null fileName"); +test_second_argument(1, "1", "Using number fileName"); +test_second_argument('', '', "Using empty string fileName"); +if (globalThis.document !== undefined) { + test_second_argument(document.body, '[object HTMLBodyElement]', "Using object fileName"); +} + +// testing the third argument +[ + {type: 'text/plain', expected: 'text/plain'}, + {type: 'text/plain;charset=UTF-8', expected: 'text/plain;charset=utf-8'}, + {type: 'TEXT/PLAIN', expected: 'text/plain'}, + {type: '๐“ฝ๐“ฎ๐”๐“ฝ/๐”ญ๐”ฉ๐”ž๐”ฆ๐”ซ', expected: ''}, + {type: 'ascii/nonprintable\u001F', expected: ''}, + {type: 'ascii/nonprintable\u007F', expected: ''}, + {type: 'nonascii\u00EE', expected: ''}, + {type: 'nonascii\u1234', expected: ''}, + {type: 'nonparsable', expected: 'nonparsable'} +].forEach(testCase => { + test(t => { + var file = new File(["bits"], "dummy", { type: testCase.type}); + assert_true(file instanceof File); + assert_equals(file.type, testCase.expected); + }, `Using type in File constructor: ${testCase.type}`); +}); +test(function() { + var file = new File(["bits"], "dummy", { lastModified: 42 }); + assert_true(file instanceof File); + assert_equals(file.lastModified, 42); +}, "Using lastModified"); +test(function() { + var file = new File(["bits"], "dummy", { name: "foo" }); + assert_true(file instanceof File); + assert_equals(file.name, "dummy"); +}, "Misusing name"); +test(function() { + var file = new File(["bits"], "dummy", { unknownKey: "value" }); + assert_true(file instanceof File); + assert_equals(file.name, "dummy"); +}, "Unknown properties are ignored"); + +[ + 123, + 123.4, + true, + 'abc' +].forEach(arg => { + test(t => { + assert_throws_js(TypeError, () => new File(['bits'], 'name.txt', arg), + 'Constructor should throw for invalid property bag type'); + }, `Invalid property bag: ${JSON.stringify(arg)}`); +}); + +[ + null, + undefined, + [1,2,3], + /regex/, + function() {} +].forEach(arg => { + test(t => { + assert_equals(new File(['bits'], 'name.txt', arg).size, 4, + 'Constructor should accept object-ish property bag type'); + }, `Unusual but valid property bag: ${arg}`); +}); + +test(t => { + assert_throws_js(Error, + () => new File(['bits'], 'name.txt', {type: to_string_throws}), + 'Constructor should propagate exceptions'); +}, 'Property bag propagates exceptions'); diff --git a/test/fixtures/wpt/FileAPI/file/send-file-formdata-controls.any.js b/test/fixtures/wpt/FileAPI/file/send-file-formdata-controls.any.js new file mode 100644 index 00000000000000..e95d3aada4421f --- /dev/null +++ b/test/fixtures/wpt/FileAPI/file/send-file-formdata-controls.any.js @@ -0,0 +1,69 @@ +// META: title=FormData: FormData: Upload files named using controls +// META: script=../support/send-file-formdata-helper.js + "use strict"; + + formDataPostFileUploadTest({ + fileNameSource: "ASCII", + fileBaseName: "file-for-upload-in-form-NUL-[\0].txt", + }); + + formDataPostFileUploadTest({ + fileNameSource: "ASCII", + fileBaseName: "file-for-upload-in-form-BS-[\b].txt", + }); + + formDataPostFileUploadTest({ + fileNameSource: "ASCII", + fileBaseName: "file-for-upload-in-form-VT-[\v].txt", + }); + + // These have characters that undergo processing in name=, + // filename=, and/or value; formDataPostFileUploadTest postprocesses + // expectedEncodedBaseName for these internally. + + formDataPostFileUploadTest({ + fileNameSource: "ASCII", + fileBaseName: "file-for-upload-in-form-LF-[\n].txt", + }); + + formDataPostFileUploadTest({ + fileNameSource: "ASCII", + fileBaseName: "file-for-upload-in-form-LF-CR-[\n\r].txt", + }); + + formDataPostFileUploadTest({ + fileNameSource: "ASCII", + fileBaseName: "file-for-upload-in-form-CR-[\r].txt", + }); + + formDataPostFileUploadTest({ + fileNameSource: "ASCII", + fileBaseName: "file-for-upload-in-form-CR-LF-[\r\n].txt", + }); + + formDataPostFileUploadTest({ + fileNameSource: "ASCII", + fileBaseName: "file-for-upload-in-form-HT-[\t].txt", + }); + + formDataPostFileUploadTest({ + fileNameSource: "ASCII", + fileBaseName: "file-for-upload-in-form-FF-[\f].txt", + }); + + formDataPostFileUploadTest({ + fileNameSource: "ASCII", + fileBaseName: "file-for-upload-in-form-DEL-[\x7F].txt", + }); + + // The rest should be passed through unmodified: + + formDataPostFileUploadTest({ + fileNameSource: "ASCII", + fileBaseName: "file-for-upload-in-form-ESC-[\x1B].txt", + }); + + formDataPostFileUploadTest({ + fileNameSource: "ASCII", + fileBaseName: "file-for-upload-in-form-SPACE-[ ].txt", + }); diff --git a/test/fixtures/wpt/FileAPI/file/send-file-formdata-punctuation.any.js b/test/fixtures/wpt/FileAPI/file/send-file-formdata-punctuation.any.js new file mode 100644 index 00000000000000..987dba39aff3a1 --- /dev/null +++ b/test/fixtures/wpt/FileAPI/file/send-file-formdata-punctuation.any.js @@ -0,0 +1,144 @@ +// META: title=FormData: FormData: Upload files named using punctuation +// META: script=../support/send-file-formdata-helper.js + "use strict"; + + // These have characters that undergo processing in name=, + // filename=, and/or value; formDataPostFileUploadTest postprocesses + // expectedEncodedBaseName for these internally. + + formDataPostFileUploadTest({ + fileNameSource: "ASCII", + fileBaseName: "file-for-upload-in-form-QUOTATION-MARK-[\x22].txt", + }); + + formDataPostFileUploadTest({ + fileNameSource: "ASCII", + fileBaseName: '"file-for-upload-in-form-double-quoted.txt"', + }); + + formDataPostFileUploadTest({ + fileNameSource: "ASCII", + fileBaseName: "file-for-upload-in-form-REVERSE-SOLIDUS-[\\].txt", + }); + + // The rest should be passed through unmodified: + + formDataPostFileUploadTest({ + fileNameSource: "ASCII", + fileBaseName: "file-for-upload-in-form-EXCLAMATION-MARK-[!].txt", + }); + + formDataPostFileUploadTest({ + fileNameSource: "ASCII", + fileBaseName: "file-for-upload-in-form-DOLLAR-SIGN-[$].txt", + }); + + formDataPostFileUploadTest({ + fileNameSource: "ASCII", + fileBaseName: "file-for-upload-in-form-PERCENT-SIGN-[%].txt", + }); + + formDataPostFileUploadTest({ + fileNameSource: "ASCII", + fileBaseName: "file-for-upload-in-form-AMPERSAND-[&].txt", + }); + + formDataPostFileUploadTest({ + fileNameSource: "ASCII", + fileBaseName: "file-for-upload-in-form-APOSTROPHE-['].txt", + }); + + formDataPostFileUploadTest({ + fileNameSource: "ASCII", + fileBaseName: "file-for-upload-in-form-LEFT-PARENTHESIS-[(].txt", + }); + + formDataPostFileUploadTest({ + fileNameSource: "ASCII", + fileBaseName: "file-for-upload-in-form-RIGHT-PARENTHESIS-[)].txt", + }); + + formDataPostFileUploadTest({ + fileNameSource: "ASCII", + fileBaseName: "file-for-upload-in-form-ASTERISK-[*].txt", + }); + + formDataPostFileUploadTest({ + fileNameSource: "ASCII", + fileBaseName: "file-for-upload-in-form-PLUS-SIGN-[+].txt", + }); + + formDataPostFileUploadTest({ + fileNameSource: "ASCII", + fileBaseName: "file-for-upload-in-form-COMMA-[,].txt", + }); + + formDataPostFileUploadTest({ + fileNameSource: "ASCII", + fileBaseName: "file-for-upload-in-form-FULL-STOP-[.].txt", + }); + + formDataPostFileUploadTest({ + fileNameSource: "ASCII", + fileBaseName: "file-for-upload-in-form-SOLIDUS-[/].txt", + }); + + formDataPostFileUploadTest({ + fileNameSource: "ASCII", + fileBaseName: "file-for-upload-in-form-COLON-[:].txt", + }); + + formDataPostFileUploadTest({ + fileNameSource: "ASCII", + fileBaseName: "file-for-upload-in-form-SEMICOLON-[;].txt", + }); + + formDataPostFileUploadTest({ + fileNameSource: "ASCII", + fileBaseName: "file-for-upload-in-form-EQUALS-SIGN-[=].txt", + }); + + formDataPostFileUploadTest({ + fileNameSource: "ASCII", + fileBaseName: "file-for-upload-in-form-QUESTION-MARK-[?].txt", + }); + + formDataPostFileUploadTest({ + fileNameSource: "ASCII", + fileBaseName: "file-for-upload-in-form-CIRCUMFLEX-ACCENT-[^].txt", + }); + + formDataPostFileUploadTest({ + fileNameSource: "ASCII", + fileBaseName: "file-for-upload-in-form-LEFT-SQUARE-BRACKET-[[].txt", + }); + + formDataPostFileUploadTest({ + fileNameSource: "ASCII", + fileBaseName: "file-for-upload-in-form-RIGHT-SQUARE-BRACKET-[]].txt", + }); + + formDataPostFileUploadTest({ + fileNameSource: "ASCII", + fileBaseName: "file-for-upload-in-form-LEFT-CURLY-BRACKET-[{].txt", + }); + + formDataPostFileUploadTest({ + fileNameSource: "ASCII", + fileBaseName: "file-for-upload-in-form-VERTICAL-LINE-[|].txt", + }); + + formDataPostFileUploadTest({ + fileNameSource: "ASCII", + fileBaseName: "file-for-upload-in-form-RIGHT-CURLY-BRACKET-[}].txt", + }); + + formDataPostFileUploadTest({ + fileNameSource: "ASCII", + fileBaseName: "file-for-upload-in-form-TILDE-[~].txt", + }); + + formDataPostFileUploadTest({ + fileNameSource: "ASCII", + fileBaseName: "'file-for-upload-in-form-single-quoted.txt'", + }); diff --git a/test/fixtures/wpt/FileAPI/file/send-file-formdata-utf-8.any.js b/test/fixtures/wpt/FileAPI/file/send-file-formdata-utf-8.any.js new file mode 100644 index 00000000000000..b8bd74c717a1b3 --- /dev/null +++ b/test/fixtures/wpt/FileAPI/file/send-file-formdata-utf-8.any.js @@ -0,0 +1,33 @@ +// META: title=FormData: FormData: Upload files in UTF-8 fetch() +// META: script=../support/send-file-formdata-helper.js + "use strict"; + + formDataPostFileUploadTest({ + fileNameSource: "ASCII", + fileBaseName: "file-for-upload-in-form.txt", + }); + + formDataPostFileUploadTest({ + fileNameSource: "x-user-defined", + fileBaseName: "file-for-upload-in-form-\uF7F0\uF793\uF783\uF7A0.txt", + }); + + formDataPostFileUploadTest({ + fileNameSource: "windows-1252", + fileBaseName: "file-for-upload-in-form-รขหœยบรฐลธหœโ€š.txt", + }); + + formDataPostFileUploadTest({ + fileNameSource: "JIS X 0201 and JIS X 0208", + fileBaseName: "file-for-upload-in-form-โ˜…ๆ˜Ÿโ˜….txt", + }); + + formDataPostFileUploadTest({ + fileNameSource: "Unicode", + fileBaseName: "file-for-upload-in-form-โ˜บ๐Ÿ˜‚.txt", + }); + + formDataPostFileUploadTest({ + fileNameSource: "Unicode", + fileBaseName: `file-for-upload-in-form-${kTestChars}.txt`, + }); diff --git a/test/fixtures/wpt/FileAPI/file/send-file-formdata.any.js b/test/fixtures/wpt/FileAPI/file/send-file-formdata.any.js new file mode 100644 index 00000000000000..e13a34828a0ebe --- /dev/null +++ b/test/fixtures/wpt/FileAPI/file/send-file-formdata.any.js @@ -0,0 +1,8 @@ +// META: title=FormData: Upload ASCII-named file in UTF-8 form +// META: script=../support/send-file-formdata-helper.js + "use strict"; + + formDataPostFileUploadTest({ + fileNameSource: "ASCII", + fileBaseName: "file-for-upload-in-form.txt", + }); diff --git a/test/fixtures/wpt/README.md b/test/fixtures/wpt/README.md index b4b0e9c151f1a3..6e2ce815a454fd 100644 --- a/test/fixtures/wpt/README.md +++ b/test/fixtures/wpt/README.md @@ -17,6 +17,7 @@ Last update: - encoding: https://github.com/web-platform-tests/wpt/tree/c1b24fce6e/encoding - fetch/data-urls/resources: https://github.com/web-platform-tests/wpt/tree/7c79d998ff/fetch/data-urls/resources - FileAPI: https://github.com/web-platform-tests/wpt/tree/3b279420d4/FileAPI +- FileAPI/file: https://github.com/web-platform-tests/wpt/tree/c01f637cca/FileAPI/file - hr-time: https://github.com/web-platform-tests/wpt/tree/34cafd797e/hr-time - html/webappapis/atob: https://github.com/web-platform-tests/wpt/tree/f267e1dca6/html/webappapis/atob - html/webappapis/microtask-queuing: https://github.com/web-platform-tests/wpt/tree/2c5c3c4c27/html/webappapis/microtask-queuing diff --git a/test/fixtures/wpt/versions.json b/test/fixtures/wpt/versions.json index 374fce9d0dca89..2b6ae3401d3645 100644 --- a/test/fixtures/wpt/versions.json +++ b/test/fixtures/wpt/versions.json @@ -27,6 +27,10 @@ "commit": "3b279420d40afea32506e823f9ac005448f4f3d8", "path": "FileAPI" }, + "FileAPI/file": { + "commit": "c01f637cca43f0e08ce8e4269121dcd89ccbdd82", + "path": "FileAPI/file" + }, "hr-time": { "commit": "34cafd797e58dad280d20040eee012d49ccfa91f", "path": "hr-time" diff --git a/test/wpt/status/FileAPI/file.json b/test/wpt/status/FileAPI/file.json new file mode 100644 index 00000000000000..ad73de30796ab5 --- /dev/null +++ b/test/wpt/status/FileAPI/file.json @@ -0,0 +1,25 @@ +{ + "File-constructor.any.js": { + "fail": { + "note": "File's options is passed to Blob, this is an issue in Blob.", + "expected": [ + "Unusual but valid property bag: null" + ] + } + }, + "Worker-read-file-constructor.worker.js": { + "skip": true + }, + "send-file-formdata-punctuation.any.js": { + "skip": true + }, + "send-file-formdata-utf-8.any.js": { + "skip": true + }, + "send-file-formdata.any.js": { + "skip": true + }, + "send-file-formdata-controls.any.js": { + "skip": true + } +} \ No newline at end of file diff --git a/test/wpt/test-file.js b/test/wpt/test-file.js new file mode 100644 index 00000000000000..71e8c179de95e2 --- /dev/null +++ b/test/wpt/test-file.js @@ -0,0 +1,13 @@ +'use strict'; + +require('../common'); +const { WPTRunner } = require('../common/wpt'); + +const runner = new WPTRunner('FileAPI/file'); + +runner.setInitScript(` + const { File } = require('buffer'); + globalThis.File = File; +`); + +runner.runJsTests(); From 51335874fb4238bb921b4a039e8ffe43488ead28 Mon Sep 17 00:00:00 2001 From: Khafra <42794878+KhafraDev@users.noreply.github.com> Date: Mon, 24 Oct 2022 12:53:55 -0400 Subject: [PATCH 07/15] fix: wpt add expected failures --- test/wpt/status/FileAPI/file.json | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/test/wpt/status/FileAPI/file.json b/test/wpt/status/FileAPI/file.json index ad73de30796ab5..0bd336c9b95fcf 100644 --- a/test/wpt/status/FileAPI/file.json +++ b/test/wpt/status/FileAPI/file.json @@ -3,7 +3,9 @@ "fail": { "note": "File's options is passed to Blob, this is an issue in Blob.", "expected": [ - "Unusual but valid property bag: null" + "Unusual but valid property bag: null", + "Unusual but valid property bag: 1,2,3", + "Unusual but valid property bag: function() {}" ] } }, From d162b8659b7769673044439d35d27427c63832ae Mon Sep 17 00:00:00 2001 From: Khafra <42794878+KhafraDev@users.noreply.github.com> Date: Mon, 24 Oct 2022 16:28:25 -0400 Subject: [PATCH 08/15] fix: bootstrap modules test & add in benchmark --- benchmark/file/file.js | 34 +++++++++++++++++++++++++ test/parallel/test-bootstrap-modules.js | 1 + 2 files changed, 35 insertions(+) create mode 100644 benchmark/file/file.js diff --git a/benchmark/file/file.js b/benchmark/file/file.js new file mode 100644 index 00000000000000..42f866b1ad8ce8 --- /dev/null +++ b/benchmark/file/file.js @@ -0,0 +1,34 @@ +'use strict'; +const common = require('../common.js'); +const { File } = require('buffer'); + +const bench = common.createBenchmark(main, { + bytes: [128, 1024, 1024 ** 2], + n: [1e6], + operation: ['text', 'arrayBuffer'] +}); + +const options = { + lastModified: Date.now() - 1e6, +}; + +async function run(n, bytes, operation) { + const buff = Buffer.allocUnsafe(bytes); + const source = new File(buff, 'dummy.txt', options); + bench.start(); + for (let i = 0; i < n; i++) { + switch (operation) { + case 'text': + await source.text(); + break; + case 'arrayBuffer': + await source.arrayBuffer(); + break; + } + } + bench.end(n); +} + +function main(conf) { + run(conf.n, conf.bytes, conf.operation).catch(console.log); +} diff --git a/test/parallel/test-bootstrap-modules.js b/test/parallel/test-bootstrap-modules.js index c0698fe1a96b5c..53da8b1af1492d 100644 --- a/test/parallel/test-bootstrap-modules.js +++ b/test/parallel/test-bootstrap-modules.js @@ -165,6 +165,7 @@ const expectedModules = new Set([ 'NativeModule internal/worker/js_transferable', 'Internal Binding blob', 'NativeModule internal/blob', + 'NativeModule internal/file', 'NativeModule async_hooks', 'NativeModule net', 'NativeModule path', From 37637533317720a4fe9ddcaa33ddbbe32914931b Mon Sep 17 00:00:00 2001 From: Khafra <42794878+KhafraDev@users.noreply.github.com> Date: Mon, 24 Oct 2022 19:40:24 -0400 Subject: [PATCH 09/15] fix: apply suggestions from review --- doc/api/buffer.md | 1 + lib/internal/file.js | 36 ++++++++++++++++-------------------- test/parallel/test-file.js | 25 ++++++++++++++++++++++++- 3 files changed, 41 insertions(+), 21 deletions(-) diff --git a/doc/api/buffer.md b/doc/api/buffer.md index 19d277b628eeba..0a56a2c033f001 100644 --- a/doc/api/buffer.md +++ b/doc/api/buffer.md @@ -5041,6 +5041,7 @@ added: REPLACEME the platform native line-ending as specified by `require('node:os').EOL`. * `type` {string} The File content-type. * `lastModified` {number} The last modified date of the file. + **Default:** `Date.now()`. ### `file.name` diff --git a/lib/internal/file.js b/lib/internal/file.js index dff4094a89f167..6ee266675650cf 100644 --- a/lib/internal/file.js +++ b/lib/internal/file.js @@ -4,8 +4,6 @@ const { DateNow, NumberIsNaN, ObjectDefineProperties, - ObjectPrototypeHasOwnProperty, - Symbol, SymbolToStringTag, } = primordials; @@ -32,13 +30,13 @@ const { inspect, } = require('internal/util/inspect'); -const kState = Symbol('kState'); +class File extends Blob { + /** @type {string} */ + #name; -function isFile(object) { - return object?.[kState] !== undefined || object instanceof Blob; -} + /** @type {number} */ + #lastModified; -class File extends Blob { constructor(fileBits, fileName, options = kEmptyObject) { emitExperimentalWarning('buffer.File'); @@ -48,11 +46,11 @@ class File extends Blob { super(fileBits, options); - let lastModified; + let { lastModified } = options; - if (ObjectPrototypeHasOwnProperty(options, 'lastModified')) { + if (lastModified !== undefined) { // Using Number(...) will not throw an error for bigints. - lastModified = +options.lastModified; + lastModified = +lastModified; if (NumberIsNaN(lastModified)) { lastModified = 0; @@ -61,26 +59,24 @@ class File extends Blob { lastModified = DateNow(); } - this[kState] = { - name: toUSVString(fileName), - lastModified, - }; + this.#name = toUSVString(fileName); + this.#lastModified = lastModified; } get name() { - if (!isFile(this)) { + if (!(#name in this)) { throw new ERR_INVALID_THIS('File'); } - return this[kState].name; + return this.#name; } get lastModified() { - if (!isFile(this)) { + if (!(#name in this)) { throw new ERR_INVALID_THIS('File'); } - return this[kState].lastModified; + return this.#lastModified; } [kInspect](depth, options) { @@ -96,8 +92,8 @@ class File extends Blob { return `File ${inspect({ size: this.size, type: this.type, - name: this.name, - lastModified: this.lastModified, + name: this.#name, + lastModified: this.#lastModified, }, opts)}`; } } diff --git a/test/parallel/test-file.js b/test/parallel/test-file.js index 0dfbc75991d52e..8661055ccc7aaa 100644 --- a/test/parallel/test-file.js +++ b/test/parallel/test-file.js @@ -71,7 +71,6 @@ const { inspect } = require('util'); const invalidLastModified = [ null, - undefined, 'string', false, toPrimitive, @@ -83,6 +82,11 @@ const { inspect } = require('util'); } } +{ + const file = new File([], '', { lastModified: undefined }); + assert.notStrictEqual(file.lastModified, 0); +} + { const toPrimitive = { [Symbol.toPrimitive]() { @@ -120,3 +124,22 @@ const { inspect } = require('util'); const file = new File([], ''); assert(inspect(file).startsWith('File { size: 0, type: \'\', name: \'\', lastModified:')); } + +{ + function MyClass() {} + MyClass.prototype.lastModified = 10; + + const file = new File([], '', new MyClass()); + assert.strictEqual(file.lastModified, 10); +} + +{ + let counter = 0; + new File([], '', { + get lastModified() { + counter++; + return 10; + } + }); + assert.strictEqual(counter, 1); +} From 2b33ee05532e58db41644bfe081cc2a9dbef8992 Mon Sep 17 00:00:00 2001 From: Khafra <42794878+KhafraDev@users.noreply.github.com> Date: Mon, 24 Oct 2022 19:40:43 -0400 Subject: [PATCH 10/15] fix: apply suggestions from review --- doc/api/buffer.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/api/buffer.md b/doc/api/buffer.md index 0a56a2c033f001..b0522f688f6ad7 100644 --- a/doc/api/buffer.md +++ b/doc/api/buffer.md @@ -5041,7 +5041,7 @@ added: REPLACEME the platform native line-ending as specified by `require('node:os').EOL`. * `type` {string} The File content-type. * `lastModified` {number} The last modified date of the file. - **Default:** `Date.now()`. + **Default:** `Date.now()`. ### `file.name` From b0703ee42324c2dabb4fc42c3be655042a2c1b86 Mon Sep 17 00:00:00 2001 From: Khafra <42794878+KhafraDev@users.noreply.github.com> Date: Mon, 24 Oct 2022 19:57:01 -0400 Subject: [PATCH 11/15] fix: handle invalid this --- lib/internal/file.js | 4 ++-- test/parallel/test-file.js | 10 ++++++++++ 2 files changed, 12 insertions(+), 2 deletions(-) diff --git a/lib/internal/file.js b/lib/internal/file.js index 6ee266675650cf..41c72961df2e11 100644 --- a/lib/internal/file.js +++ b/lib/internal/file.js @@ -64,7 +64,7 @@ class File extends Blob { } get name() { - if (!(#name in this)) { + if (!this || !(#name in this)) { throw new ERR_INVALID_THIS('File'); } @@ -72,7 +72,7 @@ class File extends Blob { } get lastModified() { - if (!(#name in this)) { + if (!this || !(#name in this)) { throw new ERR_INVALID_THIS('File'); } diff --git a/test/parallel/test-file.js b/test/parallel/test-file.js index 8661055ccc7aaa..cbcbf49245fc10 100644 --- a/test/parallel/test-file.js +++ b/test/parallel/test-file.js @@ -143,3 +143,13 @@ const { inspect } = require('util'); }); assert.strictEqual(counter, 1); } + +{ + const getter = Object.getOwnPropertyDescriptor(File.prototype, 'name').get; + assert.throws( + () => getter.call(undefined), + { + code: 'ERR_INVALID_THIS', + } + ); +} From 0dcb54b409c93f2b93771cf5eaabe32500181c54 Mon Sep 17 00:00:00 2001 From: Khafra <42794878+KhafraDev@users.noreply.github.com> Date: Mon, 24 Oct 2022 19:58:32 -0400 Subject: [PATCH 12/15] fix: and lint --- test/parallel/test-file.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/parallel/test-file.js b/test/parallel/test-file.js index cbcbf49245fc10..64a83f77ef919d 100644 --- a/test/parallel/test-file.js +++ b/test/parallel/test-file.js @@ -147,7 +147,7 @@ const { inspect } = require('util'); { const getter = Object.getOwnPropertyDescriptor(File.prototype, 'name').get; assert.throws( - () => getter.call(undefined), + () => getter.call(undefined), // eslint-disable-line no-useless-call { code: 'ERR_INVALID_THIS', } From da4de22d99a3690e82d19f8a3e45f7075def7eee Mon Sep 17 00:00:00 2001 From: Khafra <42794878+KhafraDev@users.noreply.github.com> Date: Mon, 24 Oct 2022 20:05:38 -0400 Subject: [PATCH 13/15] fix: add newline to wpt status --- test/wpt/status/FileAPI/file.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/wpt/status/FileAPI/file.json b/test/wpt/status/FileAPI/file.json index 0bd336c9b95fcf..d50d85d2a34c86 100644 --- a/test/wpt/status/FileAPI/file.json +++ b/test/wpt/status/FileAPI/file.json @@ -24,4 +24,4 @@ "send-file-formdata-controls.any.js": { "skip": true } -} \ No newline at end of file +} From a4bb5f18e1551eee6f5c40f1034b3fb621a51bfe Mon Sep 17 00:00:00 2001 From: Khafra <42794878+KhafraDev@users.noreply.github.com> Date: Mon, 24 Oct 2022 21:23:06 -0400 Subject: [PATCH 14/15] fix: move benchmark --- benchmark/{file => blob}/file.js | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename benchmark/{file => blob}/file.js (100%) diff --git a/benchmark/file/file.js b/benchmark/blob/file.js similarity index 100% rename from benchmark/file/file.js rename to benchmark/blob/file.js From 0c607615483b2eb8b18b5738e528389c461adc3f Mon Sep 17 00:00:00 2001 From: Khafra <42794878+KhafraDev@users.noreply.github.com> Date: Mon, 31 Oct 2022 20:32:44 -0400 Subject: [PATCH 15/15] fix: rebase & fix null options --- lib/internal/file.js | 2 +- test/wpt/status/FileAPI/file.json | 10 ---------- 2 files changed, 1 insertion(+), 11 deletions(-) diff --git a/lib/internal/file.js b/lib/internal/file.js index 41c72961df2e11..df9966532cdc24 100644 --- a/lib/internal/file.js +++ b/lib/internal/file.js @@ -46,7 +46,7 @@ class File extends Blob { super(fileBits, options); - let { lastModified } = options; + let { lastModified } = options ?? kEmptyObject; if (lastModified !== undefined) { // Using Number(...) will not throw an error for bigints. diff --git a/test/wpt/status/FileAPI/file.json b/test/wpt/status/FileAPI/file.json index d50d85d2a34c86..6b50bcec1539e3 100644 --- a/test/wpt/status/FileAPI/file.json +++ b/test/wpt/status/FileAPI/file.json @@ -1,14 +1,4 @@ { - "File-constructor.any.js": { - "fail": { - "note": "File's options is passed to Blob, this is an issue in Blob.", - "expected": [ - "Unusual but valid property bag: null", - "Unusual but valid property bag: 1,2,3", - "Unusual but valid property bag: function() {}" - ] - } - }, "Worker-read-file-constructor.worker.js": { "skip": true },