Skip to content
Merged
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 CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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
98 changes: 98 additions & 0 deletions src/__tests__/patch.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down
20 changes: 19 additions & 1 deletion src/patch.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
Loading