diff --git a/.vscode/launch.json b/.vscode/launch.json new file mode 100644 index 0000000..91e6bfc --- /dev/null +++ b/.vscode/launch.json @@ -0,0 +1,15 @@ +{ + "version": "0.2.0", + "configurations": [ + { + "type": "node", + "request": "launch", + "name": "File Graph Yarn Jest", + "runtimeExecutable": "yarn", + "args": ["test", "--runInBand"], + "cwd": "${workspaceFolder}/packages/file-graph", + "console": "integratedTerminal", + "internalConsoleOptions": "neverOpen" + } + ] +} diff --git a/packages/file-graph/file.js b/packages/file-graph/file.js index 3b1feed..b7622f5 100644 --- a/packages/file-graph/file.js +++ b/packages/file-graph/file.js @@ -1,7 +1,9 @@ +import { basename } from './utils' + export default class File { - constructor({ path, name, type, discovered }) { + constructor({ path, type, discovered }) { this.path = path - this.name = name + this.name = basename(path) this.type = type this.discovered = discovered } diff --git a/packages/file-graph/fileNode.js b/packages/file-graph/fileNode.js index e537c94..88fa5b3 100644 --- a/packages/file-graph/fileNode.js +++ b/packages/file-graph/fileNode.js @@ -11,11 +11,15 @@ export default class FileNode extends File { addChild(fileInput, { makeParents } = {}) { const file = new FileNode(fileInput) if (file.path === this.path) { - throw new DuplicateItemError('File already exists in the tree') + throw new DuplicateItemError( + `File ${file.path} already exists in the tree: ${this.toString()}` + ) } if (this.children.some((child) => child.path === file.path)) { - throw new DuplicateItemError('File already exists in the tree') + throw new DuplicateItemError( + `File ${file.path} already exists in the tree: ${this.toString()}` + ) } const parentPath = dirname(file.path) @@ -37,11 +41,47 @@ export default class FileNode extends File { }) this.addChild(parent, { makeParents }) } else { - throw new ParentNotExistsError('Parent path not found in the tree') + throw new ParentNotExistsError( + `Parent path not found in the tree, ${file.path}, ${this.path}` + ) } } } + merge(fileNode) { + // Merge the children of the provided file node into this node + // If a child already exists, merge the children of that child + // If a child does not exist, add it to this node + if (fileNode.path === this.path) { + fileNode.children.forEach((child) => { + const existingChild = this.children.find((c) => c.path === child.path) + if (existingChild) { + existingChild.merge(child) + } else { + this.children.push(child) + } + }) + + // If a child exists in this node but not in the provided node, remove it + this.children = this.children.filter((child) => + fileNode.children.some((c) => c.path === child.path) + ) + } else if (this.contains(fileNode)) { + const childNode = this.children.find((child) => child.contains(fileNode)) + if (childNode) { + childNode.merge(fileNode) + } else { + this.addChild(fileNode) + } + } else { + throw new Error( + 'Cannot merge file nodes with different paths', + this.path, + fileNode.path + ) + } + } + toString() { const indent = ' '.repeat(this.path.split('/').length - 1) const suffix = this.isDirectory() ? '/' : '' diff --git a/packages/file-graph/fileNode.test.js b/packages/file-graph/fileNode.test.js index 97d0c06..5c82b5b 100644 --- a/packages/file-graph/fileNode.test.js +++ b/packages/file-graph/fileNode.test.js @@ -93,4 +93,53 @@ describe('FileNode', () => { currentNode = currentNode.children[0] } }) + + test('merge-two-trees', () => { + const root = { + path: '/', + type: 'folder', + } + const files1 = [ + { path: '/a', type: 'file' }, + { path: '/b', type: 'folder' }, + { path: '/b/c', type: 'folder' }, + { path: '/b/d', type: 'file' }, + { path: '/b/c/f', type: 'file' }, + ] + + const files2 = [ + { path: '/b', type: 'folder' }, + { path: '/b/c', type: 'folder' }, + { path: '/b/e', type: 'file' }, + { path: '/b/c/g', type: 'file' }, + ] + + const expected = [ + { path: '/a', type: 'file' }, + { path: '/b', type: 'folder' }, + { path: '/b/c', type: 'folder' }, + { path: '/b/e', type: 'file' }, + { path: '/b/c/g', type: 'file' }, + ] + + const node1 = new FileNode(root) + files1.forEach((file) => node1.addChild(file)) + + const node2 = new FileNode(files2[0]) + files2.slice(1).forEach((file) => node2.addChild(file)) + + console.log('node1', node1.toString()) + console.log('node2', node2.toString()) + + const expectedNode = new FileNode(root) + expected.forEach((file) => expectedNode.addChild(file)) + + console.log('expected', expectedNode.toString()) + + node1.merge(node2) + + console.log('node1-after', node1.toString()) + + expect(node1.toString()).toBe(expectedNode.toString()) + }) }) diff --git a/packages/file-graph/utils.js b/packages/file-graph/utils.js index afc3f45..48fffd9 100644 --- a/packages/file-graph/utils.js +++ b/packages/file-graph/utils.js @@ -1,5 +1,8 @@ export function dirname(path) { const parts = path.split('/') + if (parts.length <= 2) { + return '/' + } parts.pop() return parts.join('/') }