Repository: oxc-project/oxc-resolver
Pull request #124 (2024-04-10 14:05:05)

fix: canonicalize is not supported on wasi target

codspeed-hq[bot]: ## [CodSpeed Performance Report](https://codspeed.io/oxc-project/oxc-resolver/branches/Brooooooklyn:fix/realpath-unsupport-on-wasi)

### Merging #124 will **not alter performance**

<sub>Comparing <code>Brooooooklyn:fix/realpath-unsupport-on-wasi</code> (cf0190f) with <code>main</code> (42fc05f)</sub>



### Summary

`✅ 2` untouched benchmarks






Patch:
diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
index b905684..5ac3230 100644
--- a/.github/workflows/ci.yml
+++ b/.github/workflows/ci.yml
@@ -84,6 +84,10 @@ jobs:
       - name: Install dependencies
         run: pnpm install --frozen-lockfile
 
+      - name: Install fixtures dependencies
+        working-directory: fixtures/pnpm8
+        run: pnpm install --frozen-lockfile
+
       - name: Build
         run: |
           rustup target add wasm32-wasi-preview1-threads
diff --git a/src/file_system.rs b/src/file_system.rs
index 95c504e..2b9c15e 100644
--- a/src/file_system.rs
+++ b/src/file_system.rs
@@ -91,6 +91,33 @@ impl FileSystem for FileSystemOs {
     }
 
     fn canonicalize(&self, path: &Path) -> io::Result<PathBuf> {
-        dunce::canonicalize(path)
+        #[cfg(not(target_os = "wasi"))]
+        {
+            dunce::canonicalize(path)
+        }
+        #[cfg(target_os = "wasi")]
+        {
+            let meta = fs::symlink_metadata(path)?;
+            if meta.file_type().is_symlink() {
+                let link = fs::read_link(path)?;
+                let mut path_buf = path.to_path_buf();
+                path_buf.pop();
+                for segment in link.iter() {
+                    match segment.to_str() {
+                        Some("..") => {
+                            path_buf.pop();
+                        }
+                        Some(".") | None => {}
+                        Some(seg) => {
+                            // Need to trim the extra \0 introduces by rust std rust-lang/rust#123727
+                            path_buf.push(seg.trim_end_matches('\0'));
+                        }
+                    }
+                }
+                Ok(path_buf)
+            } else {
+                Ok(path.to_path_buf())
+            }
+        }
     }
 }


Test patch:
diff --git a/napi/__test__/resolver.spec.mjs b/napi/__test__/resolver.spec.mjs
index f17dd29..42bafba 100644
--- a/napi/__test__/resolver.spec.mjs
+++ b/napi/__test__/resolver.spec.mjs
@@ -1,4 +1,4 @@
-import { join, sep } from 'node:path'
+import { join } from 'node:path'
 import { fileURLToPath } from 'node:url'
 
 import test from 'ava'
@@ -217,7 +217,7 @@ for (const [title, context, request, expected] of [
     'handle fragment escaping',
     enhancedResolveRoot,
     './no\0#fragment/\0#/\0##fragment',
-    join(enhancedResolveRoot, 'no#fragment','#', '#.js#fragment'),
+    join(enhancedResolveRoot, 'no#fragment', '#', '#.js#fragment'),
   ],
 ]) {
   test(title, (t) => {
@@ -229,3 +229,31 @@ for (const [title, context, request, expected] of [
     t.is(resolver.sync(context, request).path, expected)
   })
 }
+
+test('resolve pnpm package', (t) => {
+  const pnpmProjectPath = join(currentDir, '..', '..', 'fixtures', 'pnpm8')
+  const resolver = new ResolverFactory({
+    aliasFields: ['browser'],
+  })
+  t.deepEqual(resolver.sync(pnpmProjectPath, 'styled-components'), {
+    path: join(
+      pnpmProjectPath,
+      'node_modules/.pnpm/styled-components@6.1.1_react-dom@18.2.0_react@18.2.0/node_modules/styled-components/dist/styled-components.browser.cjs.js'
+    ),
+  })
+  t.deepEqual(
+    resolver.sync(
+      join(
+        pnpmProjectPath,
+        'node_modules/.pnpm/styled-components@6.1.1_react-dom@18.2.0_react@18.2.0/node_modules/styled-components'
+      ),
+      'react'
+    ),
+    {
+      path: join(
+        pnpmProjectPath,
+        'node_modules/.pnpm/react@18.2.0/node_modules/react/index.js'
+      ),
+    }
+  )
+})