Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fs.walk() should return relative paths, not absolute paths. #3456

Closed
joehillen opened this issue Jun 17, 2023 · 2 comments
Closed

fs.walk() should return relative paths, not absolute paths. #3456

joehillen opened this issue Jun 17, 2023 · 2 comments
Labels
bug Something isn't working needs triage

Comments

@joehillen
Copy link
Contributor

joehillen commented Jun 17, 2023

Describe the bug

The current implementation of walk() with followSymlinks = true seems to
aggressively use realPath(). This is clearly the intended behavior based on
the unit tests.
However, I think this is not the correct behavior. This is unusual behavior because it defeats
the purpose of symlinks. That is, links to directories and files should appear
to be children of the parent, even if their real location is not. I've never
seen a Unix application implicitly resolve and return a symlink'd absolute path.

This is exacerbated by the fact that realPath() throws an exception if the
the destination does not exist #3452 .

@Liamolucko Can you explain the reasoning for using Deno.realPath()?
07be715#diff-820780ce193d0f4f9a9fd1f227979578c2d5607a43f40d3fb5c6eddd629f6af2R114

Steps to Reproduce

An example use case for when the relative path would be desired would be:

import { walk } from "https://deno.land/std@0.192.0/fs/walk.ts";
import { join } from "https://deno.land/std@0.192.0/path/mod.ts";

Deno.mkdirSync("/tmp/test/x/y/z", { recursive: true });
Deno.createSync("/tmp/test/x/b.txt");
Deno.mkdirSync("/tmp/dest", { recursive: true });
Deno.createSync("/tmp/dest/a.txt");
Deno.symlinkSync("../../../../dest/a.txt", "/tmp/test/x/y/z/a.txt");

/**
 * Flatten a directory by copying all files to the root of the directory with
 * '/' replaced by '-'
 */
async function flat(src: string, dest: string) {
  await Deno.mkdir(dest, { recursive: true });

  for await (
    const entry of walk(src, {
      includeFiles: true,
      includeDirs: false,
      followSymlinks: true,
    })
  ) {
    let path = entry.path;
    if (path.startsWith(src)) {
      path = path.slice(src.length);
    }
    const fn = path
      .replace(/^\//, "") // remove leading `/`, if any
      .replace(/\//g, "-"); // replace all `/` with `-`
    await Deno.copyFile(entry.path, join(dest, fn));
  }
}

await flat("/tmp/test", "/tmp/flattened");

Result:

$ find /tmp/flattened
/tmp/flattened/
/tmp/flattened/tmp-dest-a.txt
/tmp/flattened/x-b.txt

Expected behavior

The resulting file is /tmp/flattened/tmp-dest-a.txt instead of /tmp/flattened/x-y-z-a.txt.

In the above example, I have no way of knowing that /tmp/test/x/y/z/a.txt exists
or where tmp-dest-a.txt came from because it was already resolved. I would
expect the entry.path to be relative to the parent directory and to be able
to call Deno.realPath() on my own if I wanted to resolve the symlink.

It would also be nice if { path: "/tmp/test/x/y/z/a.txt", isFile: true, isSymlink: true, ... }
included target: "../../../../dest/a.txt".

Environment

  • OS: Arch Linux
  • deno version: 1.34.1
  • std version: 0.192.0
@joehillen joehillen added bug Something isn't working needs triage labels Jun 17, 2023
@Liamolucko
Copy link
Contributor

@Liamolucko Can you explain the reasoning for using Deno.realPath()?

It was 2 years ago so I don't really remember. There were already tests for that behaviour before my PR, so I wouldn't be surprised if I just added it when those tests failed.

@joehillen
Copy link
Contributor Author

Resolved by #3679

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug Something isn't working needs triage
Projects
None yet
Development

No branches or pull requests

2 participants