From f8c329c8e57c85cc4a394a74802af1f37dcedefd Mon Sep 17 00:00:00 2001 From: Florian Loch Date: Wed, 13 May 2020 23:57:22 +0200 Subject: [PATCH] feat: support nested objects in 'fromJSON()' --- src/__tests__/volume.test.ts | 29 +++++++++++++++++++++++++++++ src/volume.ts | 25 ++++++++++++++++++++++++- 2 files changed, 53 insertions(+), 1 deletion(-) diff --git a/src/__tests__/volume.test.ts b/src/__tests__/volume.test.ts index 4572fe8b4..bea1f6a90 100644 --- a/src/__tests__/volume.test.ts +++ b/src/__tests__/volume.test.ts @@ -222,6 +222,35 @@ describe('volume', () => { expect(vol.toJSON()).toEqual(json); }); + it('Accept a nested dict as input because its nicer to read', () => { + const vol1 = new Volume(); + const vol2 = new Volume(); + + const jsonFlat = { + '/dir/file': '...', + '/dir/dir/dir2/hello.sh': 'world', + '/hello.js': 'console.log(123)', + '/dir/dir/test.txt': 'Windows', + }; + const jsonNested = { + '/dir/': { + file: '...', + dir: { + dir2: { + 'hello.sh': 'world', + }, + 'test.txt': 'Windows', + }, + }, + '/hello.js': 'console.log(123)', + }; + + vol1.fromJSON(jsonFlat); + vol2.fromJSON(jsonNested); + + expect(vol1.toJSON()).toEqual(vol2.toJSON()); + }); + it('Invalid JSON throws error', () => { try { const vol = new Volume(); diff --git a/src/volume.ts b/src/volume.ts index 8849d71c0..f0773e7b5 100644 --- a/src/volume.ts +++ b/src/volume.ts @@ -509,6 +509,10 @@ function validateGid(gid: number) { // ---------------------------------------- Volume export type DirectoryJSON = Record; +// tslint:disable-next-line:interface-over-type-literal +export type NestedDirectoryJSON = { + [key: string]: string | NestedDirectoryJSON | null; +}; /** * `Volume` represents a file system. @@ -864,7 +868,7 @@ export class Volume { } // fromJSON(json: {[filename: string]: string}, cwd: string = '/') { - fromJSON(json: DirectoryJSON, cwd: string = process.cwd()) { + fromJSON(json: NestedDirectoryJSON, cwd: string = process.cwd()) { for (let filename in json) { const data = json[filename]; @@ -878,6 +882,25 @@ export class Volume { this.writeFileSync(filename, data); } else { this.mkdirpBase(filename, MODE.DIR); + + if (data !== null && typeof data === 'object') { + const child: NestedDirectoryJSON = {}; + + for (const childFilename in data) { + let combinedFilename; + + // the path interpreted as directory could end with the separator symbol... + if (filename.endsWith(sep)) { + combinedFilename = filename + childFilename; + } else { + combinedFilename = filename + sep + childFilename; + } + + child[combinedFilename] = data[childFilename]; + } + + this.fromJSON(child, ''); + } } } }