From 43e82223046362c5e0176c112675c5636baac389 Mon Sep 17 00:00:00 2001 From: jorenbroekema Date: Mon, 13 Dec 2021 14:49:42 +0100 Subject: [PATCH] fix: recursively sync children steps to fix rename --- src/__tests__/volume/renameSync.test.ts | 24 ++++++++++++++++++++++++ src/node.ts | 23 +++++++++++++++++++++-- src/volume.ts | 1 + 3 files changed, 46 insertions(+), 2 deletions(-) diff --git a/src/__tests__/volume/renameSync.test.ts b/src/__tests__/volume/renameSync.test.ts index 16cc4acc9..35ffe654d 100644 --- a/src/__tests__/volume/renameSync.test.ts +++ b/src/__tests__/volume/renameSync.test.ts @@ -9,6 +9,30 @@ describe('renameSync(fromPath, toPath)', () => { expect(tryGetChildNode(vol.root, 'baz').isFile()).toBe(true); expect(vol.readFileSync('/baz', 'utf8')).toBe('bar'); }); + it('Updates deep links properly when renaming a directory', () => { + const vol = create({}); + vol.mkdirpSync('/foo/bar/qux'); + vol.writeFileSync('/foo/bar/qux/a.txt', 'hello'); + vol.renameSync('/foo/', '/faa/'); + expect(vol.toJSON()).toEqual({ + '/faa/bar/qux/a.txt': 'hello', + }); + + vol.renameSync('/faa/bar/qux/a.txt', '/faa/bar/qux/b.txt'); + expect(vol.toJSON()).toEqual({ + '/faa/bar/qux/b.txt': 'hello', + }); + + vol.renameSync('/faa/', '/fuu/'); + expect(vol.toJSON()).toEqual({ + '/fuu/bar/qux/b.txt': 'hello', + }); + + vol.renameSync('/fuu/bar/', '/fuu/bur/'); + expect(vol.toJSON()).toEqual({ + '/fuu/bur/qux/b.txt': 'hello', + }); + }); it('Rename file two levels deep', () => { const vol = create({ '/1/2': 'foobar' }); vol.renameSync('/1/2', '/1/3'); diff --git a/src/node.ts b/src/node.ts index 0a7adeff1..81273ab4c 100644 --- a/src/node.ts +++ b/src/node.ts @@ -239,7 +239,7 @@ export class Link extends EventEmitter { children: { [child: string]: Link | undefined } = {}; // Path to this node as Array: ['usr', 'bin', 'node']. - steps: string[] = []; + private _steps: string[] = []; // "i-node" of this hard link. node: Node; @@ -250,11 +250,26 @@ export class Link extends EventEmitter { // Number of children. length: number = 0; + name: string; + + get steps() { + return this._steps; + } + + // Recursively sync children steps, e.g. in case of dir rename + set steps(val) { + this._steps = val; + for (const child of Object.values(this.children)) { + child?.syncSteps() + } + } + constructor(vol: Volume, parent: Link, name: string) { super(); this.vol = vol; this.parent = parent; - this.steps = parent ? parent.steps.concat([name]) : [name]; + this.name = name; + this.syncSteps(); } setNode(node: Node) { @@ -347,6 +362,10 @@ export class Link extends EventEmitter { children: Object.keys(this.children), }; } + + syncSteps() { + this.steps = this.parent ? this.parent.steps.concat([this.name]) : [this.name]; + } } /** diff --git a/src/volume.ts b/src/volume.ts index d3b5ca55f..07ddb9728 100644 --- a/src/volume.ts +++ b/src/volume.ts @@ -1626,6 +1626,7 @@ export class Volume { // Rename should overwrite the new path, if that exists. const name = newPathSteps[newPathSteps.length - 1]; + link.name = name; link.steps = [...newPathDirLink.steps, name]; newPathDirLink.setChild(link.getName(), link); }