Repository: swc-project/swc (rust)

Issue:
`.swcrc` loaded via `--config-file` does not resolve `jsc.baseUrl`
### Describe the bug

I recently upgraded from `"@swc/core": "^1.3.70"` to `"@swc/core": "^1.3.76"`, and my configuration does not compile anymore.

### Input code

Running the compile command: 

```typescript
npx swc package --config-file .build.swcrc --copy-files -d dist
```


### Config

In my `.build.swcrc` file.

```json
{
  "jsc": {
    "baseUrl": ".",
    "transform": {
      "react": {
        "runtime": "automatic",
        "importSource": "react"
      }
    },
    "parser": {
      "syntax": "typescript",
      "tsx": true
    },
    "target": "es2022",
    "loose": false,
    "minify": {
      "compress": false,
      "mangle": false
    },
    "paths": {
      "@components/stateless": [
        "package/components/stateless"
      ],
      "@components/stateful": [
        "package/components/stateful"
      ],
      "@internal": [
        "internal-components"
      ],
      "@contexts": [
        "package/contexts"
      ],
      "@hooks": [
        "package/hooks"
      ],
      "@lib/backend": [
        "package/lib/backend"
      ],
      "@lib/api": [
        "package/lib/api"
      ],
      "@lib": [
        "package/lib"
      ],
      "@public/*": [
        "package/public/*"
      ],
      "@styles/*": [
        "package/styles/*"
      ]
    }
  },
  "module": {
    "type": "es6",
    "strict": false,
    "lazy": true,
    "noInterop": false
  },
  "minify": false
}
```


### Playground link

_No response_

### Expected behavior

I expect a compilation success (worked well up until at least `"@swc/core": "^1.3.70"`)

### Actual behavior

I get this error during compilation:

```
thread 'thread '<unnamed><unnamed>' panicked at '' panicked at 'base_dir(.) must be absolute. Please ensure that `jsc.baseUrl` is specified correctly.base_dir(.) must be absolute. Please ensure that `jsc.baseUrl` is specified correctly.', ', /Users/runner/.cargo/registry/src/index.crates.io-6f17d22bba15001f/swc_ecma_transforms_module-0.173.28/src/path.rs/Users/runner/.cargo/registry/src/index.crates.io-6f17d22bba15001f/swc_ecma_transforms_module-0.173.28/src/path.rs::104104::1313
```

### Version

`"@swc/cli": "^0.1.62"` `"@swc/core": "^1.3.76"`

### Additional context

_No response_

Pull request:
fix(es): Resolve `jsc.baseUrl` for `.swcrc` specified by `--config-file`
**Description:**


**Related issue:**

 - Closes #7800

Patch:
diff --git a/crates/swc/src/config/mod.rs b/crates/swc/src/config/mod.rs
index 43a87f999d..b493b28772 100644
--- a/crates/swc/src/config/mod.rs
+++ b/crates/swc/src/config/mod.rs
@@ -863,7 +863,7 @@ impl Options {
     }
 }
 
-#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
+#[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq, Eq)]
 pub enum RootMode {
     #[serde(rename = "root")]
     Root,
diff --git a/crates/swc/src/lib.rs b/crates/swc/src/lib.rs
index 8514e23853..00782b1b30 100644
--- a/crates/swc/src/lib.rs
+++ b/crates/swc/src/lib.rs
@@ -140,7 +140,6 @@ use swc_common::{
     BytePos, FileName, Mark, SourceFile, SourceMap, Spanned, GLOBALS,
 };
 pub use swc_config::config_types::{BoolConfig, BoolOr, BoolOrDataConfig};
-use swc_config::merge::Merge;
 use swc_ecma_ast::{EsVersion, Ident, Program};
 use swc_ecma_codegen::{self, text_writer::WriteJs, Emitter, Node};
 use swc_ecma_loader::resolvers::{
@@ -755,67 +754,73 @@ impl Compiler {
 
             let root = root.as_ref().unwrap_or(&CUR_DIR);
 
-            let config_file = match config_file {
-                Some(ConfigFile::Str(ref s)) => Some(load_swcrc(Path::new(&s))?),
-                _ => None,
+            let swcrc_path = match config_file {
+                Some(ConfigFile::Str(s)) => Some(PathBuf::from(s.clone())),
+                _ => {
+                    if *swcrc {
+                        if let FileName::Real(ref path) = name {
+                            find_swcrc(path, root, *root_mode)
+                        } else {
+                            None
+                        }
+                    } else {
+                        None
+                    }
+                }
             };
 
-            if let FileName::Real(ref path) = name {
-                if *swcrc {
-                    let mut parent = path.parent();
-                    while let Some(dir) = parent {
-                        let swcrc = dir.join(".swcrc");
-
-                        if swcrc.exists() {
-                            let config = load_swcrc(&swcrc)?;
-
-                            let mut config = config
-                                .into_config(Some(path))
-                                .context("failed to process config file")?;
-
-                            if let Some(config_file) = config_file {
-                                config.merge(config_file.into_config(Some(path))?)
-                            }
-
-                            if let Some(c) = &mut config {
-                                if c.jsc.base_url != PathBuf::new() {
-                                    let joined = dir.join(&c.jsc.base_url);
-                                    c.jsc.base_url = if cfg!(target_os = "windows")
-                                        && c.jsc.base_url.as_os_str() == "."
-                                    {
-                                        dir.canonicalize().with_context(|| {
-                                            format!(
-                                                "failed to canonicalize base url using the path \
-                                                 of .swcrc\nDir: {}\n(Used logic for windows)",
-                                                dir.display(),
-                                            )
-                                        })?
-                                    } else {
-                                        joined.canonicalize().with_context(|| {
-                                            format!(
-                                                "failed to canonicalize base url using the path \
-                                                 of .swcrc\nPath: {}\nDir: {}\nbaseUrl: {}",
-                                                joined.display(),
-                                                dir.display(),
-                                                c.jsc.base_url.display()
-                                            )
-                                        })?
-                                    };
-                                }
-                            }
-
-                            return Ok(config);
-                        }
+            let config_file = match swcrc_path.as_deref() {
+                Some(s) => Some(load_swcrc(s)?),
+                _ => None,
+            };
+            let filename_path = match name {
+                FileName::Real(p) => Some(&**p),
+                _ => None,
+            };
 
-                        if dir == root && *root_mode == RootMode::Root {
-                            break;
+            if let Some(filename_path) = filename_path {
+                if let Some(config) = config_file {
+                    let dir = swcrc_path
+                        .as_deref()
+                        .and_then(|p| p.parent())
+                        .expect(".swcrc path should have parent dir");
+
+                    let mut config = config
+                        .into_config(Some(filename_path))
+                        .context("failed to process config file")?;
+
+                    if let Some(c) = &mut config {
+                        if c.jsc.base_url != PathBuf::new() {
+                            let joined = dir.join(&c.jsc.base_url);
+                            c.jsc.base_url = if cfg!(target_os = "windows")
+                                && c.jsc.base_url.as_os_str() == "."
+                            {
+                                dir.canonicalize().with_context(|| {
+                                    format!(
+                                        "failed to canonicalize base url using the path of \
+                                         .swcrc\nDir: {}\n(Used logic for windows)",
+                                        dir.display(),
+                                    )
+                                })?
+                            } else {
+                                joined.canonicalize().with_context(|| {
+                                    format!(
+                                        "failed to canonicalize base url using the path of \
+                                         .swcrc\nPath: {}\nDir: {}\nbaseUrl: {}",
+                                        joined.display(),
+                                        dir.display(),
+                                        c.jsc.base_url.display()
+                                    )
+                                })?
+                            };
                         }
-                        parent = dir.parent();
                     }
+
+                    return Ok(config);
                 }
 
                 let config_file = config_file.unwrap_or_default();
-                let config = config_file.into_config(Some(path))?;
+                let config = config_file.into_config(Some(filename_path))?;
 
                 return Ok(config);
             }
@@ -832,7 +837,7 @@ impl Compiler {
                 }
             }
         })
-        .with_context(|| format!("failed to read swcrc file ({})", name))
+        .with_context(|| format!("failed to read .swcrc file for input file at `{}`", name))
     }
 
     /// This method returns [None] if a file should be skipped.
@@ -1258,6 +1263,24 @@ impl Compiler {
     }
 }
 
+fn find_swcrc(path: &Path, root: &Path, root_mode: RootMode) -> Option<PathBuf> {
+    let mut parent = path.parent();
+    while let Some(dir) = parent {
+        let swcrc = dir.join(".swcrc");
+
+        if swcrc.exists() {
+            return Some(swcrc);
+        }
+
+        if dir == root && root_mode == RootMode::Root {
+            break;
+        }
+        parent = dir.parent();
+    }
+
+    None
+}
+
 #[tracing::instrument(level = "info", skip_all)]
 fn load_swcrc(path: &Path) -> Result<Rc, Error> {
     let content = read_to_string(path).context("failed to read config (.swcrc) file")?;
diff --git a/package.json b/package.json
index 45b685ded4..e1e43bb57e 100644
--- a/package.json
+++ b/package.json
@@ -96,7 +96,7 @@
         "@swc/plugin-jest": "latest",
         "@taplo/cli": "^0.3.2",
         "@types/jest": "^28.1.4",
-        "@types/node": "^14.14.41",
+        "@types/node": "^20.5.0",
         "@types/terser": "^3.12.0",
         "acorn": "^8.6.0",
         "acorn-jsx": "^5.3.2",
diff --git a/yarn.lock b/yarn.lock
index c263a98c34..dddceb226e 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -2566,7 +2566,7 @@ __metadata:
     "@swc/plugin-jest": latest
     "@taplo/cli": ^0.3.2
     "@types/jest": ^28.1.4
-    "@types/node": ^14.14.41
+    "@types/node": ^20.5.0
     "@types/terser": ^3.12.0
     acorn: ^8.6.0
     acorn-jsx: ^5.3.2
@@ -2785,10 +2785,10 @@ __metadata:
   languageName: node
   linkType: hard
 
-"@types/node@npm:^14.14.41":
-  version: 14.18.48
-  resolution: "@types/node@npm:14.18.48"
-  checksum: bc410153cd12b5f5218c2c2a5ab4a65e8dc99ebffe98261a30e9d4beb8f9a0e4ee48c41b850f8cb80da6ed1024e42bba8dee1592869aa5b1826ddb7c29ad6899
+"@types/node@npm:^20.5.0":
+  version: 20.5.0
+  resolution: "@types/node@npm:20.5.0"
+  checksum: 659bc5fc93b5c02bd88ca4bfae4f6b9dc307d45884d1dd9d69df85819a9943cdc00cd3c87eec3048866df6a67f52297f74d170e47a44f61edb3e8f770d94e85e
   languageName: node
   linkType: hard
 


Test patch:
diff --git a/node-swc/__tests__/error_test.mjs b/node-swc/__tests__/error_test.mjs
index e3214f064a..d39b77c9d8 100644
--- a/node-swc/__tests__/error_test.mjs
+++ b/node-swc/__tests__/error_test.mjs
@@ -13,7 +13,7 @@ it("should work", () => {
 it("should report good error", () => {
   expect(() => {
     swc.transformFileSync(__dirname + "/../tests/error/simple.js");
-  }).toThrow("failed to read swcrc file");
+  }).toThrow("failed to deserialize .swcrc (json) file");
 });
 
 it("should report good error (handler)", () => {