Repository: denoland/deno
Pull request: fix(ext/node): add the missing path information when the file is not found in `fs.stat` and `fs.statSync`

<!--
Before submitting a PR, please read https://docs.deno.com/runtime/manual/references/contributing

1. Give the PR a descriptive title.

  Examples of good title:
    - fix(std/http): Fix race condition in server
    - docs(console): Update docstrings
    - feat(doc): Handle nested reexports

  Examples of bad title:
    - fix #7123
    - update docs
    - fix bugs

2. Ensure there is a related issue and it is referenced in the PR text.
3. Ensure there are tests that cover the changes.
4. Ensure `cargo test` passes.
5. Ensure `./tools/format.js` passes without changing files.
6. Ensure `./tools/lint.js` passes.
7. Open as a draft PR if your work is still in progress. The CI won't run
   all steps, but you can add '[ci]' to a commit message to force it to.
8. If you would like to run the benchmarks on the CI, add the 'ci-bench' label.
-->
Fix #25921.
See more details in #25921.


Changed files:
filename: ext/node/polyfills/_fs/_fs_stat.ts
patch: @@ -383,7 +383,10 @@ export function stat(
 
   Deno.stat(path).then(
     (stat) => callback(null, CFISBIS(stat, options.bigint)),
-    (err) => callback(denoErrorToNodeError(err, { syscall: "stat" })),
+    (err) =>
+      callback(
+        denoErrorToNodeError(err, { syscall: "stat", path: getPathname(path) }),
+      ),
   );
 }
 
@@ -417,9 +420,16 @@ export function statSync(
       return;
     }
     if (err instanceof Error) {
-      throw denoErrorToNodeError(err, { syscall: "stat" });
+      throw denoErrorToNodeError(err, {
+        syscall: "stat",
+        path: getPathname(path),
+      });
     } else {
       throw err;
     }
   }
 }
+
+function getPathname(path: string | URL) {
+  return typeof path === "string" ? path : path.pathname;
+}

filename: tests/unit_node/fs_test.ts
patch: @@ -26,6 +26,7 @@ import {
   cp,
   FileHandle,
   open,
+  stat,
   writeFile,
 } from "node:fs/promises";
 import process from "node:process";
@@ -123,6 +124,48 @@ Deno.test(
   },
 );
 
+Deno.test(
+  "[node/fs statSync] throw error with path information",
+  () => {
+    const file = "non-exist-file";
+    const fileUrl = new URL(file, import.meta.url);
+
+    assertThrows(() => {
+      statSync(file);
+    }, "Error: ENOENT: no such file or directory, stat 'non-exist-file'");
+
+    assertThrows(() => {
+      statSync(fileUrl);
+    }, `Error: ENOENT: no such file or directory, stat '${fileUrl.pathname}'`);
+  },
+);
+
+Deno.test(
+  "[node/fs/promises stat] throw error with path information",
+  async () => {
+    const file = "non-exist-file";
+    const fileUrl = new URL(file, import.meta.url);
+
+    try {
+      await stat(file);
+    } catch (error: unknown) {
+      assertEquals(
+        `${error}`,
+        "Error: ENOENT: no such file or directory, stat 'non-exist-file'",
+      );
+    }
+
+    try {
+      await stat(fileUrl);
+    } catch (error: unknown) {
+      assertEquals(
+        `${error}`,
+        `Error: ENOENT: no such file or directory, stat '${fileUrl.pathname}'`,
+      );
+    }
+  },
+);
+
 Deno.test(
   "[node/fs/promises cp] copy file",
   async () => {