diff --git a/deno.json b/deno.json index acba4b9..a731f65 100644 --- a/deno.json +++ b/deno.json @@ -1,6 +1,6 @@ { "name": "@andyburke/fsdb", - "version": "0.9.0", + "version": "1.0.0", "license": "MIT", "exports": { ".": "./fsdb.ts", diff --git a/indexers/symlinks.ts b/indexers/symlinks.ts index df97c90..3418140 100644 --- a/indexers/symlinks.ts +++ b/indexers/symlinks.ts @@ -104,7 +104,7 @@ export class FSDB_INDEXER_SYMLINKS implements FSDB_INDEXER { } const resolved_item_path = await Deno.readLink(item_file.path); - results.push(resolved_item_path); + results.push(path.normalize(path.resolve(path.dirname(item_file.path), resolved_item_path))); ++counter; } @@ -116,6 +116,44 @@ export class FSDB_INDEXER_SYMLINKS implements FSDB_INDEXER { return results; } + private async make_symlink(target: string, symlink_path: string) { + let tolerate_existing = false; + try { + const existing_link_stat = await Deno.lstat(symlink_path); + if (!existing_link_stat.isSymlink) { + throw new Deno.errors.AlreadyExists( + `There is an existing filesystem entry (not symlink) at ${symlink_path} while attempting to point to ${target}` + ); + } + + const existing_link_target = await Deno.readLink(symlink_path); + const resolved_link_target = path.resolve(existing_link_target); + const resolved_target = path.resolve(target); + + if (resolved_link_target !== resolved_target) { + throw new Deno.errors.AlreadyExists( + `A symlink (${symlink_path}) already exists pointing to (${resolved_link_target}), failed to create one pointing to: ${resolved_target}` + ); + } + + tolerate_existing = true; + } catch (error) { + if (!(error instanceof Deno.errors.NotFound)) { + throw error; + } + } + + await Deno.mkdir(path.dirname(symlink_path), { recursive: true }); + + try { + await fs.ensureSymlink(target, symlink_path); + } catch (error) { + if (!(error instanceof Deno.errors.AlreadyExists) || !tolerate_existing) { + throw error; + } + } + } + async index(item: T, authoritative_path: string): Promise { if (typeof this.config.root !== 'string') { throw new Error('root should have been set by FSDB instance'); @@ -171,9 +209,8 @@ export class FSDB_INDEXER_SYMLINKS implements FSDB_INDEXER { } // create index symlink and reverse link - await Deno.mkdir(path.dirname(symlink_path), { recursive: true }); - await fs.ensureSymlink(authoritative_path, symlink_path); - await fs.ensureSymlink(symlink_path, reverse_link_path); + await this.make_symlink(path.relative(path.dirname(symlink_path), authoritative_path), symlink_path); + await this.make_symlink(path.relative(path.dirname(reverse_link_path), symlink_path), reverse_link_path); results.push(symlink_path); } @@ -193,13 +230,14 @@ export class FSDB_INDEXER_SYMLINKS implements FSDB_INDEXER { } const index_symlink_path: string = await Deno.readLink(item_dir_reverse_link); - if (fs.existsSync(index_symlink_path)) { - await Deno.remove(index_symlink_path); - results.push(index_symlink_path); + const resolved_index_symlink_path: string = path.resolve(path.join(item_dir, index_symlink_path)); + if (fs.existsSync(resolved_index_symlink_path)) { + await Deno.remove(resolved_index_symlink_path); + results.push(resolved_index_symlink_path); } await Deno.remove(item_dir_reverse_link); - results.push(...await cleanup_empty_directories(index_symlink_path)); + results.push(...await cleanup_empty_directories(resolved_index_symlink_path)); } return results;