Skip to content

fix: uneval grows sparse arrays by one slot when first hole is past index 0#160

Open
spokodev wants to merge 2 commits into
sveltejs:mainfrom
spokodev:fix/uneval-sparse-array-extra-comma
Open

fix: uneval grows sparse arrays by one slot when first hole is past index 0#160
spokodev wants to merge 2 commits into
sveltejs:mainfrom
spokodev:fix/uneval-sparse-array-extra-comma

Conversation

@spokodev

Copy link
Copy Markdown

What

uneval grows a sparse array by one slot when its first hole is not at index 0:

import { uneval } from 'devalue';

uneval([1, , 3]);   // '[1,,,3]'  → evaluates to [1, <2 empty>, 3], length 4
uneval([1, , ]);    // '[1,,,]'   → length 3
uneval([1, , , 4]); // '[1,,,,4]' → length 5

stringify/parse round-trips these correctly — only uneval is affected. ([, 'a'], where the first hole is at index 0, was already correct, which is why this slipped through.)

Why

In the array-literal branch, a comma is written at the top of every iteration (if (i > 0) result += ','). When the first hole is reached, the code set has_holes = true and then did i -= 1 to "re-process this index as a hole" — but the re-processed iteration writes a second comma for the same slot, so each such array gains one element.

Fix

The comma written at the top of the iteration already represents the hole, so the i -= 1 re-processing is unnecessary (and is what caused the extra comma). Removing it makes every case round-trip:

input before after
[1, , 3] [1,,,3] (len 4) [1,,3] (len 3)
[1, , ] [1,,,] (len 3) [1,,] (len 2)
[1, , , 4] [1,,,,4] (len 5) [1,,,4] (len 4)
[, 'a', , 'b'] [,"a",,"b"] (unchanged) [,"a",,"b"]

The Object.assign path for very sparse arrays returns earlier and is unaffected.

Tests

Added a round-trip test for sparse arrays whose first hole is not at index 0 (red before the change, green after). Full npm test passes (659).

When the first hole in an array literal was at an index greater than 0,
uneval emitted two commas for it: one from `if (i > 0) result += ','` at
the top of the iteration, and a second after `i -= 1` re-processed the
same index. So `uneval([1, , 3])` produced `[1,,,3]` (length 4) instead
of `[1,,3]` (length 3), and the round-tripped array gained a slot.

The comma at the top of the iteration already represents the hole, so
drop the `i -= 1` re-processing.
@changeset-bot

changeset-bot Bot commented Jun 17, 2026

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: fcddf82

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 1 package
Name Type
devalue Patch

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant