Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .changeset/olive-pugs-repeat.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'devalue': patch
---

fix: emit valid JS for Node `Buffer` in `uneval`
18 changes: 12 additions & 6 deletions src/uneval.js
Original file line number Diff line number Diff line change
Expand Up @@ -290,7 +290,7 @@ export function uneval(value, replacer) {
let str = `new ${type}`;

if (!names.has(thing.buffer)) {
str += `([${stringify_typed_array_elements(new thing.constructor(thing.buffer))}])`;
str += `([${stringify_typed_array_elements(type, thing.buffer)}])`;
} else {
str += `(${stringify(thing.buffer)})`;
}
Expand Down Expand Up @@ -451,7 +451,7 @@ export function uneval(value, replacer) {
let str = `new ${type}`;

if (!names.has(thing.buffer)) {
str += `([${stringify_typed_array_elements(new thing.constructor(thing.buffer))}])`;
str += `([${stringify_typed_array_elements(type, thing.buffer)}])`;
} else {
str += `(${stringify(thing.buffer)})`;
}
Expand Down Expand Up @@ -522,13 +522,19 @@ export function uneval(value, replacer) {
}

/**
* Serialize the elements of a typed array as a comma-separated list.
* Serialize the elements of `buffer`, read as `type`, as a comma-separated list.
* The view is created from `type` rather than from the serialized value's own
* constructor, which may be a subclass like Node's `Buffer` whose `toString`
* decodes the bytes instead of listing them.
* `BigInt64Array`/`BigUint64Array` elements are bigints and must be written
* with an `n` suffix, otherwise the emitted `new BigInt64Array([...])` throws.
* @param {import('./types.js').TypedArray} array
* @param {string} type
* @param {ArrayBufferLike} buffer
*/
function stringify_typed_array_elements(array) {
if (array instanceof BigInt64Array || array instanceof BigUint64Array) {
function stringify_typed_array_elements(type, buffer) {
const array = new (/** @type {any} */ (globalThis)[type])(buffer);

if (type === 'BigInt64Array' || type === 'BigUint64Array') {
return Array.from(array, (element) => `${element}n`).join(',');
}

Expand Down
9 changes: 9 additions & 0 deletions test/index.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -299,6 +299,15 @@ const fixtures = {
js: 'new Uint8Array([1,2,3])',
json: '[["Uint8Array",1],["ArrayBuffer","AQID"]]'
},
{
// `Buffer.alloc` does not allocate from Node's shared pool, so the buffer
// backing this view is exactly four bytes and the expectations are stable
name: 'Node Buffer',
value: Buffer.alloc(4, 65),
js: 'new Uint8Array([65,65,65,65])',
json: '[["Uint8Array",1],["ArrayBuffer","QUFBQQ=="]]',
validate: (value) => assert.equal(value, new Uint8Array([65, 65, 65, 65]))
},
{
name: 'BigInt64Array',
value: new BigInt64Array([1n, -2n, 3n]),
Expand Down
Loading