Repository: swc-project/swc (rust)

Issue:
jsc.paths regression in 1.3.79 with symlinks
### Describe the bug

This issue still exists with a more accurate bazel use case: https://github.com/swc-project/swc/issues/8265. See the [latest change](https://github.com/alexeagle/swc_8265_repro/commit/16bfa92286bdafe5dc48ead53c9b4177ce603fbb) to reproduce it.

See https://github.com/jbedard/swc/commit/08db61738cec601fa873734757f1eee71ed5225a for a new test that should reproduce it, but I haven't finished the rust setup to actually compile+run this test yet.

### Input code

```typescript
import { moduleA } from "@modules/moduleA";

moduleA();
```


### Config

```json
{
  "jsc": {
    "parser": {
      "syntax": "typescript"
    },
    "target": "es2021",
    "baseUrl": "./src",
    "paths": {
      "@modules/*": [
        "./modules/*"
      ]
    }
  },
  "module": {
    "type": "commonjs"
  },
  "sourceMaps": true
}
```


### Playground link (or link to the minimal reproduction)

https://github.com/alexeagle/swc_8265_repro/

### SWC Info output

swc-darwin-arm64 --version
SWC 0.91.64

### Expected behavior

SWC 1.3.78 produces

```
const _moduleA = require("./modules/moduleA/index");
```

In https://github.com/swc-project/swc/issues/8258 you suggested that we compare with what tsc produces. As I understand it, typescript never attempts to re-write import/require statements. So the result is here

https://github.com/aspect-build/rules_swc/pull/216/files#diff-e3a9eafb3b943b9863d6cea0b348d4ef40bc1b7bb5867a38ac889fcacea0cbf4R3

"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
var moduleA_1 = require("@modules/moduleA");
(0, moduleA_1.moduleA)();

### Actual behavior

SWC 1.3.79 - 1.4.3+ produces:

```
const _moduleA = require("../../../../../../../shared/cache/bazel/user_base/09a27d1eeea3b44d1580773090924b4d/sandbox/linux-sandbox/629/execroot/aspect_rules_swc/examples/paths/src/modules/moduleA");
```

### Version

1.3.79

### Additional context

See https://github.com/swc-project/swc/issues/8265

https://github.com/swc-project/swc/commit/7dfdc1221890d373d2e6caf52bc8dee8c20765ca fixed the original example in the [original repo](https://github.com/alexeagle/swc_8265_repro/tree/f1b6331f8fee3ad85d418fb1d5a5e20408b7ab3a).

However another example that is closer aligned with what actually occurs in bazel reproduces the issue, essentially just another layer of symlinks: https://github.com/alexeagle/swc_8265_repro/commit/16bfa92286bdafe5dc48ead53c9b4177ce603fbb

Pull request:
fix(es/module): Fix interop of `jsc.paths` with symlinks
**Description:**


**Related issue:**

 - Closes #8667

Patch:
diff --git a/crates/swc_ecma_transforms_module/src/path.rs b/crates/swc_ecma_transforms_module/src/path.rs
index 057f09f730..06ee744f46 100644
--- a/crates/swc_ecma_transforms_module/src/path.rs
+++ b/crates/swc_ecma_transforms_module/src/path.rs
@@ -1,8 +1,8 @@
 use std::{
     borrow::Cow,
     env::current_dir,
-    fs::read_link,
-    io::{self},
+    fs::canonicalize,
+    io,
     path::{Component, Path, PathBuf},
     sync::Arc,
 };
@@ -245,7 +245,7 @@ where
         //
         // https://github.com/swc-project/swc/issues/8265
         if let FileName::Real(resolved) = &target.filename {
-            if let Ok(orig) = read_link(resolved) {
+            if let Ok(orig) = canonicalize(resolved) {
                 target.filename = FileName::Real(orig);
             }
         }


Test patch:
diff --git a/crates/swc/tests/fixture/deno/paths/issue-2753/2-cjs/input/src/command/restore.ts b/crates/swc/tests/fixture/deno/paths/issue-2753/2-cjs/input/src/command/restore.ts
index 8e2af4f0ca..c45b7ddd4a 100644
--- a/crates/swc/tests/fixture/deno/paths/issue-2753/2-cjs/input/src/command/restore.ts
+++ b/crates/swc/tests/fixture/deno/paths/issue-2753/2-cjs/input/src/command/restore.ts
@@ -1,1 +1,1 @@
-import { module } from "@src/config";
+import { module } from "@src/conf";
diff --git a/crates/swc/tests/fixture/deno/paths/issue-2753/2-cjs/output/src/command/restore.ts b/crates/swc/tests/fixture/deno/paths/issue-2753/2-cjs/output/src/command/restore.ts
index 7793058058..dc435deacd 100644
--- a/crates/swc/tests/fixture/deno/paths/issue-2753/2-cjs/output/src/command/restore.ts
+++ b/crates/swc/tests/fixture/deno/paths/issue-2753/2-cjs/output/src/command/restore.ts
@@ -2,4 +2,4 @@
 Object.defineProperty(exports, "__esModule", {
     value: true
 });
-const _config = require("../config");
+const _conf = require("../conf");
diff --git a/crates/swc/tests/fixture/deno/paths/issue-2753/2-esm/input/src/command/restore.ts b/crates/swc/tests/fixture/deno/paths/issue-2753/2-esm/input/src/command/restore.ts
index 8e2af4f0ca..c45b7ddd4a 100644
--- a/crates/swc/tests/fixture/deno/paths/issue-2753/2-esm/input/src/command/restore.ts
+++ b/crates/swc/tests/fixture/deno/paths/issue-2753/2-esm/input/src/command/restore.ts
@@ -1,1 +1,1 @@
-import { module } from "@src/config";
+import { module } from "@src/conf";
diff --git a/crates/swc/tests/fixture/deno/paths/issue-2753/2-esm/output/src/command/restore.ts b/crates/swc/tests/fixture/deno/paths/issue-2753/2-esm/output/src/command/restore.ts
index cc2bb795da..adb86c0227 100644
--- a/crates/swc/tests/fixture/deno/paths/issue-2753/2-esm/output/src/command/restore.ts
+++ b/crates/swc/tests/fixture/deno/paths/issue-2753/2-esm/output/src/command/restore.ts
@@ -1,1 +1,1 @@
-import { module } from "../config";
+import { module } from "../conf";
diff --git a/crates/swc_cli_impl/tests/issues.rs b/crates/swc_cli_impl/tests/issues.rs
index 29b7d42202..5d29cd0124 100644
--- a/crates/swc_cli_impl/tests/issues.rs
+++ b/crates/swc_cli_impl/tests/issues.rs
@@ -1,5 +1,5 @@
 use std::{
-    fs::{self, create_dir_all},
+    fs::{self, create_dir_all, hard_link},
     path::Path,
     process::{Command, Stdio},
 };
@@ -81,6 +81,69 @@ fn issue_8495_1() -> Result<()> {
     Ok(())
 }
 
+#[test]
+fn issue_8667_1() -> Result<()> {
+    let output_base = TempDir::new()?;
+    let bin_dir = output_base.path().join("bazel-out/arch/bin");
+    let sandbox = output_base.path().join("sandbox/123");
+
+    let pwd = Path::new("tests/fixture-manual/8265").canonicalize()?;
+
+    create_dir_all(bin_dir.join("src/modules/moduleA"))?;
+    create_dir_all(bin_dir.join("src/modules/moduleB"))?;
+    create_dir_all(sandbox.join("src/modules/moduleA"))?;
+    create_dir_all(sandbox.join("src/modules/moduleB"))?;
+
+    // hard links from BINDIR into src
+    hard_link(pwd.join(".swcrc"), bin_dir.join(".swcrc"))?;
+    hard_link(pwd.join("src/index.ts"), bin_dir.join("src/index.ts"))?;
+    hard_link(
+        pwd.join("src/modules/moduleA/index.ts"),
+        bin_dir.join("src/modules/moduleA/index.ts"),
+    )?;
+    hard_link(
+        pwd.join("src/modules/moduleB/index.ts"),
+        bin_dir.join("src/modules/moduleB/index.ts"),
+    )?;
+
+    // soft links from sandbox into bazel-bin
+    symlink(&bin_dir.join(".swcrc"), &sandbox.join(".swcrc"));
+    symlink(&bin_dir.join("src/index.ts"), &sandbox.join("src/index.ts"));
+    symlink(
+        &bin_dir.join("src/modules/moduleA/index.ts"),
+        &sandbox.join("src/modules/moduleA/index.ts"),
+    );
+    symlink(
+        &bin_dir.join("src/modules/moduleB/index.ts"),
+        &sandbox.join("src/modules/moduleB/index.ts"),
+    );
+
+    //
+    print_ls_alr(&sandbox);
+
+    let mut cmd = cli()?;
+    cmd.current_dir(&sandbox)
+        .arg("compile")
+        .arg("--source-maps")
+        .arg("false")
+        .arg("--config-file")
+        .arg(".swcrc")
+        .arg("--out-file")
+        .arg(bin_dir.join("src/index.js"))
+        .arg("src/index.ts");
+
+    cmd.assert().success();
+
+    let content = fs::read_to_string(bin_dir.join("src/index.js"))?;
+    assert!(
+        content.contains("require(\"./modules/moduleA\")"),
+        "{}",
+        content
+    );
+
+    Ok(())
+}
+
 /// ln -s $a $b
 fn symlink(a: &Path, b: &Path) {
     #[cfg(unix)]