mirror of
https://github.com/nodejs/node.git
synced 2024-11-21 10:59:27 +00:00
buffer: make File cloneable
Fixes: https://github.com/nodejs/node/issues/47612 PR-URL: https://github.com/nodejs/node/pull/47613 Reviewed-By: Antoine du Hamel <duhamelantoine1995@gmail.com> Reviewed-By: Benjamin Gruenbaum <benjamingr@gmail.com> Reviewed-By: Yagiz Nizipli <yagiz@nizipli.com> Reviewed-By: Matteo Collina <matteo.collina@gmail.com>
This commit is contained in:
parent
da5887d8e9
commit
17fd32790a
@ -5094,6 +5094,9 @@ added:
|
||||
- v19.2.0
|
||||
- v18.13.0
|
||||
changes:
|
||||
- version: REPLACEME
|
||||
pr-url: https://github.com/nodejs/node/pull/47613
|
||||
description: Makes File instances cloneable.
|
||||
- version: v20.0.0
|
||||
pr-url: https://github.com/nodejs/node/pull/47153
|
||||
description: No longer experimental.
|
||||
|
@ -504,4 +504,5 @@ module.exports = {
|
||||
isBlob,
|
||||
kHandle,
|
||||
resolveObjectURL,
|
||||
TransferableBlob,
|
||||
};
|
||||
|
@ -2,14 +2,18 @@
|
||||
|
||||
const {
|
||||
DateNow,
|
||||
FunctionPrototypeApply,
|
||||
NumberIsNaN,
|
||||
ObjectDefineProperties,
|
||||
ObjectSetPrototypeOf,
|
||||
StringPrototypeToWellFormed,
|
||||
Symbol,
|
||||
SymbolToStringTag,
|
||||
} = primordials;
|
||||
|
||||
const {
|
||||
Blob,
|
||||
TransferableBlob,
|
||||
} = require('internal/blob');
|
||||
|
||||
const {
|
||||
@ -20,6 +24,7 @@ const {
|
||||
|
||||
const {
|
||||
codes: {
|
||||
ERR_INVALID_THIS,
|
||||
ERR_MISSING_ARGS,
|
||||
},
|
||||
} = require('internal/errors');
|
||||
@ -28,13 +33,32 @@ const {
|
||||
inspect,
|
||||
} = require('internal/util/inspect');
|
||||
|
||||
const {
|
||||
kClone,
|
||||
kDeserialize,
|
||||
} = require('internal/worker/js_transferable');
|
||||
|
||||
const kState = Symbol('state');
|
||||
|
||||
function isFile(object) {
|
||||
return object?.[kState] !== undefined;
|
||||
}
|
||||
|
||||
class FileState {
|
||||
name;
|
||||
lastModified;
|
||||
|
||||
/**
|
||||
* @param {string} name
|
||||
* @param {number} lastModified
|
||||
*/
|
||||
constructor(name, lastModified) {
|
||||
this.name = name;
|
||||
this.lastModified = lastModified;
|
||||
}
|
||||
}
|
||||
|
||||
class File extends Blob {
|
||||
/** @type {string} */
|
||||
#name;
|
||||
|
||||
/** @type {number} */
|
||||
#lastModified;
|
||||
|
||||
constructor(fileBits, fileName, options = kEmptyObject) {
|
||||
if (arguments.length < 2) {
|
||||
throw new ERR_MISSING_ARGS('fileBits', 'fileName');
|
||||
@ -55,16 +79,21 @@ class File extends Blob {
|
||||
lastModified = DateNow();
|
||||
}
|
||||
|
||||
this.#name = StringPrototypeToWellFormed(`${fileName}`);
|
||||
this.#lastModified = lastModified;
|
||||
this[kState] = new FileState(StringPrototypeToWellFormed(`${fileName}`), lastModified);
|
||||
}
|
||||
|
||||
get name() {
|
||||
return this.#name;
|
||||
if (!isFile(this))
|
||||
throw new ERR_INVALID_THIS('File');
|
||||
|
||||
return this[kState].name;
|
||||
}
|
||||
|
||||
get lastModified() {
|
||||
return this.#lastModified;
|
||||
if (!isFile(this))
|
||||
throw new ERR_INVALID_THIS('File');
|
||||
|
||||
return this[kState].lastModified;
|
||||
}
|
||||
|
||||
[kInspect](depth, options) {
|
||||
@ -80,12 +109,32 @@ class File extends Blob {
|
||||
return `File ${inspect({
|
||||
size: this.size,
|
||||
type: this.type,
|
||||
name: this.#name,
|
||||
lastModified: this.#lastModified,
|
||||
name: this[kState].name,
|
||||
lastModified: this[kState].lastModified,
|
||||
}, opts)}`;
|
||||
}
|
||||
|
||||
[kClone]() {
|
||||
return {
|
||||
data: { ...super[kClone]().data, ...this[kState] },
|
||||
deserializeInfo: 'internal/file:TransferableFile',
|
||||
};
|
||||
}
|
||||
|
||||
[kDeserialize](data) {
|
||||
super[kDeserialize](data);
|
||||
|
||||
this[kState] = new FileState(data.name, data.lastModified);
|
||||
}
|
||||
}
|
||||
|
||||
function TransferableFile(handle, length, type = '') {
|
||||
FunctionPrototypeApply(TransferableBlob, this, [handle, length, type]);
|
||||
}
|
||||
|
||||
ObjectSetPrototypeOf(TransferableFile.prototype, File.prototype);
|
||||
ObjectSetPrototypeOf(TransferableFile, File);
|
||||
|
||||
ObjectDefineProperties(File.prototype, {
|
||||
name: kEnumerableProperty,
|
||||
lastModified: kEnumerableProperty,
|
||||
@ -98,4 +147,5 @@ ObjectDefineProperties(File.prototype, {
|
||||
|
||||
module.exports = {
|
||||
File,
|
||||
TransferableFile,
|
||||
};
|
||||
|
@ -158,3 +158,25 @@ const { inspect } = require('util');
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
(async () => {
|
||||
// File should be cloneable via structuredClone.
|
||||
// Refs: https://github.com/nodejs/node/issues/47612
|
||||
|
||||
const body = ['hello, ', 'world'];
|
||||
const lastModified = Date.now() - 10_000;
|
||||
const name = 'hello_world.txt';
|
||||
|
||||
const file = new File(body, name, { lastModified });
|
||||
const clonedFile = structuredClone(file);
|
||||
|
||||
assert.deepStrictEqual(await clonedFile.text(), await file.text());
|
||||
assert.deepStrictEqual(clonedFile.lastModified, file.lastModified);
|
||||
assert.deepStrictEqual(clonedFile.name, file.name);
|
||||
|
||||
const clonedFile2 = structuredClone(clonedFile);
|
||||
|
||||
assert.deepStrictEqual(await clonedFile2.text(), await clonedFile.text());
|
||||
assert.deepStrictEqual(clonedFile2.lastModified, clonedFile.lastModified);
|
||||
assert.deepStrictEqual(clonedFile2.name, clonedFile.name);
|
||||
})().then(common.mustCall());
|
||||
|
@ -1,7 +1 @@
|
||||
{
|
||||
"structured-clone.any.js": {
|
||||
"fail": {
|
||||
"expected": ["File basic"]
|
||||
}
|
||||
}
|
||||
}
|
||||
{}
|
||||
|
Loading…
Reference in New Issue
Block a user