diff --git a/CHANGELOG.md b/CHANGELOG.md index 25e9793b..f91db1e8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] +### Fixed + +- Patching: removing one `[[table]]` entry no longer deletes the sub-tables of the other entries. ([#321]) + ## [3.3.0] - 2026-09-27 ### Added @@ -501,5 +505,6 @@ This first forked version from [timhall/toml-patch](https://github.com/timhall/t [#313]: https://github.com/DecimalTurn/toml-patch/pull/313 [#314]: https://github.com/DecimalTurn/toml-patch/pull/314 [#316]: https://github.com/DecimalTurn/toml-patch/pull/316 +[#321]: https://github.com/DecimalTurn/toml-patch/pull/321 [0e66e68]: https://github.com/DecimalTurn/toml-patch/commit/0e66e68cbf42a07bc44445e46c3ea7bea97f95c1 [f97d571]: https://github.com/DecimalTurn/toml-patch/commit/f97d57194952b6603f0fcd686c32d3ae52a2c903 diff --git a/src/__tests__/patch.test.ts b/src/__tests__/patch.test.ts index 6a374f67..ecd7a79f 100644 --- a/src/__tests__/patch.test.ts +++ b/src/__tests__/patch.test.ts @@ -2098,6 +2098,104 @@ test('should remove table array and preserve leading KV', () => { expect(patched).not.toContain('name'); }); +test('should remove one table array entry and keep the sub-tables of the others', () => { + const existing = dedent` + [[route]] + id = "a" + [route.args] + x = 1 + + [[route]] + id = "b" + [route.args] + y = 2 + + [[route]] + id = "c" + [route.args] + z = 3 + ` + '\n'; + + for (const removed of [0, 1, 2]) { + const updated = parse(existing) as { route: object[] }; + updated.route.splice(removed, 1); + + const patched = patch(existing, updated); + const expected = [ + dedent` + [[route]] + id = "b" + [route.args] + y = 2 + + [[route]] + id = "c" + [route.args] + z = 3 + ` + '\n', + dedent` + [[route]] + id = "a" + [route.args] + x = 1 + + [[route]] + id = "c" + [route.args] + z = 3 + ` + '\n', + dedent` + [[route]] + id = "a" + [route.args] + x = 1 + + [[route]] + id = "b" + [route.args] + y = 2 + ` + '\n', + ]; + + expect(patched).toBe(expected[removed]); + expect(parse(patched)).toEqual(updated); + } +}); + +test('should remove a nested table array entry and keep the next parent entry\'s sub-tables', () => { + const existing = dedent` + [[group]] + id = "g0" + [[group.route]] + id = "a" + [[group.route]] + id = "b" + + [[group]] + id = "g1" + [group.route.args] + x = 1 + ` + '\n'; + + const updated = parse(existing) as { group: { route: object[] }[] }; + updated.group[0].route.splice(1, 1); + const patched = patch(existing, updated); + const expected = dedent` + [[group]] + id = "g0" + [[group.route]] + id = "a" + + [[group]] + id = "g1" + [group.route.args] + x = 1 + ` + '\n'; + + expect(patched).toBe(expected); + expect(parse(patched)).toEqual(updated); +}); + test('should remove leading KV and preserve table array', () => { const existing = dedent` title = "Project" diff --git a/src/patch.ts b/src/patch.ts index 2faf1dfb..08ef6812 100644 --- a/src/patch.ts +++ b/src/patch.ts @@ -3200,8 +3200,26 @@ function applyChanges( const key = (node as Table | TableArray).key.item.value; const siblings = (parent as { items: TreeNode[] }).items; const extending: TreeNode[] = []; - for (const sibling of siblings) { + // A sub-table belongs to the nearest [[x]] entry above it, so an + // entry owns only the siblings up to the next entry of its own + // array or of an ancestor array ([[x]] ends an [[x.y]] entry too). + const aotEntry = isTableArray(node); + const ownStart = siblings.indexOf(node); + let ownEnd = siblings.length; + if (aotEntry) { + for (let i = ownStart + 1; i < siblings.length; i++) { + const s = siblings[i]; + if (!isTableArray(s)) continue; + const sKey = s.key.item.value; + if (sKey.length <= key.length && arraysEqual(sKey, key.slice(0, sKey.length))) { + ownEnd = i; + break; + } + } + } + for (const [index, sibling] of siblings.entries()) { if (sibling === node) continue; + if (aotEntry && (index < ownStart || index >= ownEnd)) continue; const siblingKey = isKeyValue(sibling) ? sibling.key.value : isTable(sibling) || isTableArray(sibling)