Repository: swc-project/swc (rust)

Issue:
Support shims for built in modules
<!--
If you are using swc at work, please considering adding your company to https://swc.rs/users/
If then, your issue will be fixed more quickly.
-->

**Describe the bug**

Compiling built in modules when the target is the browser and `paths` are configured does not use the packages from `paths` but compiles down to `import` statements. Possibly a duplicate of #1943.

**Input code**

An MVP reproducing and describing the issue is here:

https://github.com/tmpfs/swc-builtin-shim 

**Config**

```json
{
  "jsc": {
    "parser": {
      "syntax": "ecmascript",
      "jsx": true,
      "dynamicImport": false,
      "privateMethod": false,
      "functionBind": false,
      "exportDefaultFrom": false,
      "exportNamespaceFrom": false,
      "decorators": false,
      "decoratorsBeforeExport": false,
      "topLevelAwait": false,
      "importMeta": false
    },
    "paths": {
      "assert": ["node_modules/assert"],
      "events": ["node_modules/events"]
    },
    "transform": null,
    "target": "es2016",
    "loose": false,
    "externalHelpers": false,
    "keepClassNames": false
  }
}
```

**Expected behavior**

We expect the packages configured in `paths` to be used instead of the default behavior for handling builtin modules.

**Version**
The version of @swc/core: 1.2.66

**Additional context**

N/A


Pull request description:
Support target runtime environment.
Update NodeResolver so it only special cases built in modules when
`TargetEnv::Node`.

This is the preliminary work to fix #1955. All we need to do now is thread the target environment (`TargetEnv`) from `spack` command line arguments into `NodeResolver::new` for proper support.

This is a breaking change as it now expects the browser to be the default target so builtin modules are no longer special-cased by default. I think it is reasonable to assume that the browser should be the default target for compiling/bundling.

Patch:
diff --git a/Cargo.lock b/Cargo.lock
index fb0bda2281..b5a6b99637 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -2076,6 +2076,7 @@ dependencies = [
  "swc_common",
  "swc_ecma_ast",
  "swc_ecma_codegen",
+ "swc_ecma_loader",
  "swc_ecma_parser",
  "swc_ecma_transforms",
  "swc_ecma_utils",
@@ -2165,7 +2166,7 @@ checksum = "6446ced80d6c486436db5c078dde11a9f73d42b57fb273121e160b84f63d894c"
 
 [[package]]
 name = "swc"
-version = "0.37.0"
+version = "0.38.0"
 dependencies = [
  "ahash 0.7.4",
  "anyhow",
@@ -2259,7 +2260,7 @@ dependencies = [
 
 [[package]]
 name = "swc_bundler"
-version = "0.50.0"
+version = "0.51.0"
 dependencies = [
  "ahash 0.7.4",
  "anyhow",
@@ -2324,7 +2325,7 @@ dependencies = [
 
 [[package]]
 name = "swc_ecma_ast"
-version = "0.49.1"
+version = "0.49.2"
 dependencies = [
  "arbitrary",
  "is-macro",
@@ -2389,7 +2390,7 @@ dependencies = [
 
 [[package]]
 name = "swc_ecma_loader"
-version = "0.12.0"
+version = "0.13.0"
 dependencies = [
  "anyhow",
  "dashmap",
@@ -2408,7 +2409,7 @@ dependencies = [
 
 [[package]]
 name = "swc_ecma_minifier"
-version = "0.18.3"
+version = "0.19.0"
 dependencies = [
  "ansi_term 0.12.1",
  "anyhow",
@@ -2464,7 +2465,7 @@ dependencies = [
 
 [[package]]
 name = "swc_ecma_preset_env"
-version = "0.34.0"
+version = "0.35.0"
 dependencies = [
  "dashmap",
  "fxhash",
@@ -2490,7 +2491,7 @@ dependencies = [
 
 [[package]]
 name = "swc_ecma_transforms"
-version = "0.63.0"
+version = "0.64.0"
 dependencies = [
  "pretty_assertions 0.6.1",
  "sourcemap",
@@ -2584,7 +2585,7 @@ dependencies = [
 
 [[package]]
 name = "swc_ecma_transforms_module"
-version = "0.30.0"
+version = "0.31.0"
 dependencies = [
  "Inflector",
  "anyhow",
@@ -2607,7 +2608,7 @@ dependencies = [
 
 [[package]]
 name = "swc_ecma_transforms_optimization"
-version = "0.33.0"
+version = "0.34.0"
 dependencies = [
  "dashmap",
  "fxhash",
@@ -2634,7 +2635,7 @@ dependencies = [
 
 [[package]]
 name = "swc_ecma_transforms_proposal"
-version = "0.30.0"
+version = "0.31.0"
 dependencies = [
  "either",
  "fxhash",
@@ -2656,7 +2657,7 @@ dependencies = [
 
 [[package]]
 name = "swc_ecma_transforms_react"
-version = "0.31.1"
+version = "0.32.0"
 dependencies = [
  "base64 0.13.0",
  "dashmap",
@@ -2700,7 +2701,7 @@ dependencies = [
 
 [[package]]
 name = "swc_ecma_transforms_typescript"
-version = "0.32.0"
+version = "0.33.0"
 dependencies = [
  "fxhash",
  "serde",
@@ -2747,7 +2748,7 @@ dependencies = [
 
 [[package]]
 name = "swc_ecmascript"
-version = "0.53.0"
+version = "0.54.0"
 dependencies = [
  "swc_ecma_ast",
  "swc_ecma_codegen",
diff --git a/Cargo.toml b/Cargo.toml
index f5d5db0fdc..5fd6265eb3 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -9,7 +9,7 @@ edition = "2018"
 license = "Apache-2.0/MIT"
 name = "swc"
 repository = "https://github.com/swc-project/swc.git"
-version = "0.37.0"
+version = "0.38.0"
 
 [lib]
 name = "swc"
@@ -29,16 +29,16 @@ serde = {version = "1", features = ["derive"]}
 serde_json = "1"
 sourcemap = "6"
 swc_atoms = {version = "0.2", path = "./atoms"}
-swc_bundler = {version = "0.50.0", path = "./bundler"}
+swc_bundler = {version = "0.51.0", path = "./bundler"}
 swc_common = {version = "0.11.0", path = "./common", features = ["sourcemap", "concurrent"]}
 swc_ecma_ast = {version = "0.49.0", path = "./ecmascript/ast"}
 swc_ecma_codegen = {version = "0.66.0", path = "./ecmascript/codegen"}
 swc_ecma_ext_transforms = {version = "0.24.0", path = "./ecmascript/ext-transforms"}
-swc_ecma_loader = {version = "0.12.0", path = "./ecmascript/loader", features = ["lru", "node", "tsc"]}
-swc_ecma_minifier = {version = "0.18.0", path = "./ecmascript/minifier"}
+swc_ecma_loader = {version = "0.13.0", path = "./ecmascript/loader", features = ["lru", "node", "tsc"]}
+swc_ecma_minifier = {version = "0.19.0", path = "./ecmascript/minifier"}
 swc_ecma_parser = {version = "0.66.0", path = "./ecmascript/parser"}
-swc_ecma_preset_env = {version = "0.34.0", path = "./ecmascript/preset-env"}
-swc_ecma_transforms = {version = "0.63.0", path = "./ecmascript/transforms", features = [
+swc_ecma_preset_env = {version = "0.35.0", path = "./ecmascript/preset-env"}
+swc_ecma_transforms = {version = "0.64.0", path = "./ecmascript/transforms", features = [
   "compat",
   "module",
   "optimization",
@@ -49,7 +49,7 @@ swc_ecma_transforms = {version = "0.63.0", path = "./ecmascript/transforms", fea
 swc_ecma_transforms_base = {version = "0.26.0", path = "./ecmascript/transforms/base"}
 swc_ecma_utils = {version = "0.41.0", path = "./ecmascript/utils"}
 swc_ecma_visit = {version = "0.35.0", path = "./ecmascript/visit"}
-swc_ecmascript = {version = "0.53.0", path = "./ecmascript"}
+swc_ecmascript = {version = "0.54.0", path = "./ecmascript"}
 swc_node_base = {version = "0.2.0", path = "./node/base"}
 swc_visit = {version = "0.2.3", path = "./visit"}
 
diff --git a/bundler/Cargo.toml b/bundler/Cargo.toml
index 08a422cb78..d8c03741c2 100644
--- a/bundler/Cargo.toml
+++ b/bundler/Cargo.toml
@@ -9,7 +9,7 @@ include = ["Cargo.toml", "build.rs", "src/**/*.rs", "src/**/*.js"]
 license = "Apache-2.0/MIT"
 name = "swc_bundler"
 repository = "https://github.com/swc-project/swc.git"
-version = "0.50.0"
+version = "0.51.0"
 
 # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
 [features]
@@ -35,9 +35,9 @@ swc_atoms = {version = "0.2.4", path = "../atoms"}
 swc_common = {version = "0.11.0", path = "../common"}
 swc_ecma_ast = {version = "0.49.0", path = "../ecmascript/ast"}
 swc_ecma_codegen = {version = "0.66.0", path = "../ecmascript/codegen"}
-swc_ecma_loader = {version = "0.12.0", path = "../ecmascript/loader"}
+swc_ecma_loader = {version = "0.13.0", path = "../ecmascript/loader"}
 swc_ecma_parser = {version = "0.66.0", path = "../ecmascript/parser"}
-swc_ecma_transforms = {version = "0.63.0", path = "../ecmascript/transforms", features = ["optimization"]}
+swc_ecma_transforms = {version = "0.64.0", path = "../ecmascript/transforms", features = ["optimization"]}
 swc_ecma_utils = {version = "0.41.0", path = "../ecmascript/utils"}
 swc_ecma_visit = {version = "0.35.0", path = "../ecmascript/visit"}
 
@@ -46,7 +46,7 @@ hex = "0.4"
 ntest = "0.7.2"
 reqwest = {version = "0.11.4", features = ["blocking"]}
 sha-1 = "0.9"
-swc_ecma_transforms = {version = "0.63.0", path = "../ecmascript/transforms", features = ["react", "typescript"]}
+swc_ecma_transforms = {version = "0.64.0", path = "../ecmascript/transforms", features = ["react", "typescript"]}
 tempfile = "3.1.0"
 testing = {version = "0.12.0", path = "../testing"}
 url = "2.1.1"
diff --git a/ecmascript/Cargo.toml b/ecmascript/Cargo.toml
index c585f647fd..d13cd0c89e 100644
--- a/ecmascript/Cargo.toml
+++ b/ecmascript/Cargo.toml
@@ -6,7 +6,7 @@ edition = "2018"
 license = "Apache-2.0/MIT"
 name = "swc_ecmascript"
 repository = "https://github.com/swc-project/swc.git"
-version = "0.53.0"
+version = "0.54.0"
 
 [package.metadata.docs.rs]
 all-features = true
@@ -35,10 +35,10 @@ typescript = ["typescript-parser", "swc_ecma_transforms/typescript"]
 swc_ecma_ast = {version = "0.49.0", path = "./ast"}
 swc_ecma_codegen = {version = "0.66.0", path = "./codegen", optional = true}
 swc_ecma_dep_graph = {version = "0.34.0", path = "./dep-graph", optional = true}
-swc_ecma_minifier = {version = "0.18.0", path = "./minifier", optional = true}
+swc_ecma_minifier = {version = "0.19.0", path = "./minifier", optional = true}
 swc_ecma_parser = {version = "0.66.0", path = "./parser", optional = true, default-features = false}
-swc_ecma_preset_env = {version = "0.34.0", path = "./preset-env", optional = true}
-swc_ecma_transforms = {version = "0.63.0", path = "./transforms", optional = true}
+swc_ecma_preset_env = {version = "0.35.0", path = "./preset-env", optional = true}
+swc_ecma_transforms = {version = "0.64.0", path = "./transforms", optional = true}
 swc_ecma_utils = {version = "0.41.0", path = "./utils", optional = true}
 swc_ecma_visit = {version = "0.35.0", path = "./visit", optional = true}
 
diff --git a/ecmascript/ast/Cargo.toml b/ecmascript/ast/Cargo.toml
index c331485019..83fe3ba946 100644
--- a/ecmascript/ast/Cargo.toml
+++ b/ecmascript/ast/Cargo.toml
@@ -6,7 +6,7 @@ edition = "2018"
 license = "Apache-2.0/MIT"
 name = "swc_ecma_ast"
 repository = "https://github.com/swc-project/swc.git"
-version = "0.49.1"
+version = "0.49.2"
 
 [features]
 default = []
diff --git a/ecmascript/ast/src/lib.rs b/ecmascript/ast/src/lib.rs
index 07b0b7fa84..2d0ed13210 100644
--- a/ecmascript/ast/src/lib.rs
+++ b/ecmascript/ast/src/lib.rs
@@ -129,3 +129,18 @@ impl Default for EsVersion {
         EsVersion::Es5
     }
 }
+
+/// Target runtime environment.
+#[derive(Debug, Copy, Clone, Serialize, Deserialize)]
+pub enum TargetEnv {
+    #[serde(rename = "browser")]
+    Browser,
+    #[serde(rename = "node")]
+    Node,
+}
+
+impl Default for TargetEnv {
+    fn default() -> Self {
+        TargetEnv::Browser
+    }
+}
diff --git a/ecmascript/loader/Cargo.toml b/ecmascript/loader/Cargo.toml
index 7e8556fd93..5237b11ef2 100644
--- a/ecmascript/loader/Cargo.toml
+++ b/ecmascript/loader/Cargo.toml
@@ -6,7 +6,7 @@ edition = "2018"
 license = "Apache-2.0/MIT"
 name = "swc_ecma_loader"
 repository = "https://github.com/swc-project/swc.git"
-version = "0.12.0"
+version = "0.13.0"
 
 [package.metadata.docs.rs]
 all-features = true
diff --git a/ecmascript/loader/src/resolvers/node.rs b/ecmascript/loader/src/resolvers/node.rs
index abf720fc67..104736ef0e 100644
--- a/ecmascript/loader/src/resolvers/node.rs
+++ b/ecmascript/loader/src/resolvers/node.rs
@@ -13,6 +13,7 @@ use std::{
     path::{Component, Path, PathBuf},
 };
 use swc_common::FileName;
+use swc_ecma_ast::TargetEnv;
 
 // Run `node -p "require('module').builtinModules"`
 pub(crate) fn is_core_module(s: &str) -> bool {
@@ -87,22 +88,25 @@ pub(crate) fn is_core_module(s: &str) -> bool {
 
 #[derive(Deserialize)]
 struct PackageJson {
-    #[serde(rename = "swc-main", default)]
-    swc_main: Option<String>,
-    #[serde(default)]
-    esnext: Option<String>,
     #[serde(default)]
     main: Option<String>,
+    #[serde(default)]
+    browser: Option<String>,
 }
 
 #[derive(Debug, Default)]
-pub struct NodeResolver {
-    _private: (),
+pub struct NodeModulesResolver {
+    target_env: TargetEnv,
 }
 
 static EXTENSIONS: &[&str] = &["ts", "tsx", "js", "jsx", "json", "node"];
 
-impl NodeResolver {
+impl NodeModulesResolver {
+    /// Create a node modules resolver for the target runtime environment.
+    pub fn new(target_env: TargetEnv) -> Self {
+        Self { target_env }
+    }
+
     fn wrap(&self, path: PathBuf) -> Result<FileName, Error> {
         let path = path.canonicalize().context("failed to canonicalize")?;
         Ok(FileName::Real(path))
@@ -154,7 +158,16 @@ impl NodeResolver {
         let pkg: PackageJson =
             serde_json::from_reader(reader).context("failed to deserialize package.json")?;
 
-        for main in &[&pkg.swc_main, &pkg.esnext, &pkg.main] {
+        let main_fields = match self.target_env {
+            TargetEnv::Node => {
+                vec![&pkg.main]
+            }
+            TargetEnv::Browser => {
+                vec![&pkg.browser, &pkg.main]
+            }
+        };
+
+        for main in main_fields {
             if let Some(target) = main {
                 let path = pkg_dir.join(target);
                 return self
@@ -201,10 +214,12 @@ impl NodeResolver {
     }
 }
 
-impl Resolve for NodeResolver {
+impl Resolve for NodeModulesResolver {
     fn resolve(&self, base: &FileName, target: &str) -> Result<FileName, Error> {
-        if is_core_module(target) {
-            return Ok(FileName::Custom(target.to_string()));
+        if let TargetEnv::Node = self.target_env {
+            if is_core_module(target) {
+                return Ok(FileName::Custom(target.to_string()));
+            }
         }
 
         let base = match base {
diff --git a/ecmascript/minifier/Cargo.toml b/ecmascript/minifier/Cargo.toml
index 0ae12e3c8f..21aa994c30 100644
--- a/ecmascript/minifier/Cargo.toml
+++ b/ecmascript/minifier/Cargo.toml
@@ -7,7 +7,7 @@ include = ["Cargo.toml", "src/**/*.rs", "src/lists/*.json"]
 license = "Apache-2.0/MIT"
 name = "swc_ecma_minifier"
 repository = "https://github.com/swc-project/swc.git"
-version = "0.18.3"
+version = "0.19.0"
 
 [features]
 debug = []
@@ -29,7 +29,7 @@ swc_common = {version = "0.11.2", path = "../../common"}
 swc_ecma_ast = {version = "0.49.0", path = "../ast"}
 swc_ecma_codegen = {version = "0.66.0", path = "../codegen"}
 swc_ecma_parser = {version = "0.66.0", path = "../parser"}
-swc_ecma_transforms = {version = "0.63.0", path = "../transforms/", features = ["optimization"]}
+swc_ecma_transforms = {version = "0.64.0", path = "../transforms/", features = ["optimization"]}
 swc_ecma_transforms_base = {version = "0.26.0", path = "../transforms/base"}
 swc_ecma_utils = {version = "0.41.0", path = "../utils"}
 swc_ecma_visit = {version = "0.35.0", path = "../visit"}
diff --git a/ecmascript/preset-env/Cargo.toml b/ecmascript/preset-env/Cargo.toml
index 3c2af1d11e..d82a9156ee 100644
--- a/ecmascript/preset-env/Cargo.toml
+++ b/ecmascript/preset-env/Cargo.toml
@@ -5,7 +5,7 @@ documentation = "https://rustdoc.swc.rs/swc_ecma_preset_env/"
 edition = "2018"
 license = "Apache-2.0/MIT"
 name = "swc_ecma_preset_env"
-version = "0.34.0"
+version = "0.35.0"
 
 # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
 
@@ -22,7 +22,7 @@ string_enum = {version = "0.3.1", path = "../../macros/string_enum"}
 swc_atoms = {version = "0.2", path = "../../atoms"}
 swc_common = {version = "0.11.0", path = "../../common"}
 swc_ecma_ast = {version = "0.49.0", path = "../ast"}
-swc_ecma_transforms = {version = "0.63.0", path = "../transforms", features = ["compat", "proposal"]}
+swc_ecma_transforms = {version = "0.64.0", path = "../transforms", features = ["compat", "proposal"]}
 swc_ecma_utils = {version = "0.41.0", path = "../utils"}
 swc_ecma_visit = {version = "0.35.0", path = "../visit"}
 walkdir = "2"
diff --git a/ecmascript/transforms/Cargo.toml b/ecmascript/transforms/Cargo.toml
index 17b9debe03..2fb49360c9 100644
--- a/ecmascript/transforms/Cargo.toml
+++ b/ecmascript/transforms/Cargo.toml
@@ -6,7 +6,7 @@ edition = "2018"
 license = "Apache-2.0/MIT"
 name = "swc_ecma_transforms"
 repository = "https://github.com/swc-project/swc.git"
-version = "0.63.0"
+version = "0.64.0"
 
 [package.metadata.docs.rs]
 all-features = true
@@ -27,11 +27,11 @@ swc_ecma_ast = {version = "0.49.0", path = "../ast"}
 swc_ecma_parser = {version = "0.66.0", path = "../parser"}
 swc_ecma_transforms_base = {version = "0.26.0", path = "./base"}
 swc_ecma_transforms_compat = {version = "0.29.0", path = "./compat", optional = true}
-swc_ecma_transforms_module = {version = "0.30.0", path = "./module", optional = true}
-swc_ecma_transforms_optimization = {version = "0.33.0", path = "./optimization", optional = true}
-swc_ecma_transforms_proposal = {version = "0.30.0", path = "./proposal", optional = true}
-swc_ecma_transforms_react = {version = "0.31.0", path = "./react", optional = true}
-swc_ecma_transforms_typescript = {version = "0.32.0", path = "./typescript", optional = true}
+swc_ecma_transforms_module = {version = "0.31.0", path = "./module", optional = true}
+swc_ecma_transforms_optimization = {version = "0.34.0", path = "./optimization", optional = true}
+swc_ecma_transforms_proposal = {version = "0.31.0", path = "./proposal", optional = true}
+swc_ecma_transforms_react = {version = "0.32.0", path = "./react", optional = true}
+swc_ecma_transforms_typescript = {version = "0.33.0", path = "./typescript", optional = true}
 swc_ecma_utils = {version = "0.41.0", path = "../utils"}
 swc_ecma_visit = {version = "0.35.0", path = "../visit"}
 unicode-xid = "0.2"
diff --git a/ecmascript/transforms/module/Cargo.toml b/ecmascript/transforms/module/Cargo.toml
index b8c4e287ff..9bcd7e2a71 100644
--- a/ecmascript/transforms/module/Cargo.toml
+++ b/ecmascript/transforms/module/Cargo.toml
@@ -6,7 +6,7 @@ edition = "2018"
 license = "Apache-2.0/MIT"
 name = "swc_ecma_transforms_module"
 repository = "https://github.com/swc-project/swc.git"
-version = "0.30.0"
+version = "0.31.0"
 # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
 
 [dependencies]
@@ -19,7 +19,7 @@ serde = {version = "1.0.118", features = ["derive"]}
 swc_atoms = {version = "0.2", path = "../../../atoms"}
 swc_common = {version = "0.11.0", path = "../../../common"}
 swc_ecma_ast = {version = "0.49.0", path = "../../ast"}
-swc_ecma_loader = {version = "0.12.0", path = "../../loader", features = ["node"]}
+swc_ecma_loader = {version = "0.13.0", path = "../../loader", features = ["node"]}
 swc_ecma_parser = {version = "0.66.0", path = "../../parser"}
 swc_ecma_transforms_base = {version = "0.26.0", path = "../base"}
 swc_ecma_utils = {version = "0.41.0", path = "../../utils"}
diff --git a/ecmascript/transforms/optimization/Cargo.toml b/ecmascript/transforms/optimization/Cargo.toml
index 740afa1743..281c44f8ae 100644
--- a/ecmascript/transforms/optimization/Cargo.toml
+++ b/ecmascript/transforms/optimization/Cargo.toml
@@ -6,7 +6,7 @@ edition = "2018"
 license = "Apache-2.0/MIT"
 name = "swc_ecma_transforms_optimization"
 repository = "https://github.com/swc-project/swc.git"
-version = "0.33.0"
+version = "0.34.0"
 # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
 
 [dependencies]
@@ -27,9 +27,9 @@ swc_ecma_visit = {version = "0.35.0", path = "../../visit"}
 
 [dev-dependencies]
 swc_ecma_transforms_compat = {version = "0.29.0", path = "../compat"}
-swc_ecma_transforms_module = {version = "0.30.0", path = "../module"}
-swc_ecma_transforms_proposal = {version = "0.30.0", path = "../proposal"}
-swc_ecma_transforms_react = {version = "0.31.0", path = "../react"}
+swc_ecma_transforms_module = {version = "0.31.0", path = "../module"}
+swc_ecma_transforms_proposal = {version = "0.31.0", path = "../proposal"}
+swc_ecma_transforms_react = {version = "0.32.0", path = "../react"}
 swc_ecma_transforms_testing = {version = "0.26.0", path = "../testing"}
-swc_ecma_transforms_typescript = {version = "0.32.0", path = "../typescript"}
+swc_ecma_transforms_typescript = {version = "0.33.0", path = "../typescript"}
 testing = {version = "0.12.0", path = "../../../testing"}
diff --git a/ecmascript/transforms/proposal/Cargo.toml b/ecmascript/transforms/proposal/Cargo.toml
index d049db234e..da84732d61 100644
--- a/ecmascript/transforms/proposal/Cargo.toml
+++ b/ecmascript/transforms/proposal/Cargo.toml
@@ -6,7 +6,7 @@ edition = "2018"
 license = "Apache-2.0/MIT"
 name = "swc_ecma_transforms_proposal"
 repository = "https://github.com/swc-project/swc.git"
-version = "0.30.0"
+version = "0.31.0"
 
 # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
 
@@ -22,7 +22,7 @@ smallvec = "1.6.0"
 swc_atoms = {version = "0.2", path = "../../../atoms"}
 swc_common = {version = "0.11.0", path = "../../../common"}
 swc_ecma_ast = {version = "0.49.0", path = "../../ast"}
-swc_ecma_loader = {version = "0.12.0", path = "../../loader", optional = true}
+swc_ecma_loader = {version = "0.13.0", path = "../../loader", optional = true}
 swc_ecma_parser = {version = "0.66.0", path = "../../parser"}
 swc_ecma_transforms_base = {version = "0.26.0", path = "../base"}
 swc_ecma_transforms_classes = {version = "0.12.0", path = "../classes"}
@@ -31,5 +31,5 @@ swc_ecma_visit = {version = "0.35.0", path = "../../visit"}
 
 [dev-dependencies]
 swc_ecma_transforms_compat = {version = "0.29.0", path = "../compat"}
-swc_ecma_transforms_module = {version = "0.30.0", path = "../module"}
+swc_ecma_transforms_module = {version = "0.31.0", path = "../module"}
 swc_ecma_transforms_testing = {version = "0.26.0", path = "../testing"}
diff --git a/ecmascript/transforms/react/Cargo.toml b/ecmascript/transforms/react/Cargo.toml
index 2574b9e224..ca86fbc7fc 100644
--- a/ecmascript/transforms/react/Cargo.toml
+++ b/ecmascript/transforms/react/Cargo.toml
@@ -7,7 +7,7 @@ include = ["Cargo.toml", "src/**/*.rs"]
 license = "Apache-2.0/MIT"
 name = "swc_ecma_transforms_react"
 repository = "https://github.com/swc-project/swc.git"
-version = "0.31.1"
+version = "0.32.0"
 # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
 
 [dependencies]
@@ -30,6 +30,6 @@ swc_ecma_visit = {version = "0.35.0", path = "../../visit"}
 [dev-dependencies]
 swc_ecma_codegen = {version = "0.66.0", path = "../../codegen/"}
 swc_ecma_transforms_compat = {version = "0.29.0", path = "../compat/"}
-swc_ecma_transforms_module = {version = "0.30.0", path = "../module"}
+swc_ecma_transforms_module = {version = "0.31.0", path = "../module"}
 swc_ecma_transforms_testing = {version = "0.26.0", path = "../testing/"}
 testing = {version = "0.12.0", path = "../../../testing"}
diff --git a/ecmascript/transforms/typescript/Cargo.toml b/ecmascript/transforms/typescript/Cargo.toml
index b3e61b92fa..2d2c63fa61 100644
--- a/ecmascript/transforms/typescript/Cargo.toml
+++ b/ecmascript/transforms/typescript/Cargo.toml
@@ -6,7 +6,7 @@ edition = "2018"
 license = "Apache-2.0/MIT"
 name = "swc_ecma_transforms_typescript"
 repository = "https://github.com/swc-project/swc.git"
-version = "0.32.0"
+version = "0.33.0"
 # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
 
 [dependencies]
@@ -23,8 +23,8 @@ swc_ecma_visit = {version = "0.35.0", path = "../../visit"}
 [dev-dependencies]
 swc_ecma_codegen = {version = "0.66.0", path = "../../codegen"}
 swc_ecma_transforms_compat = {version = "0.29.0", path = "../compat"}
-swc_ecma_transforms_module = {version = "0.30.0", path = "../module"}
-swc_ecma_transforms_proposal = {version = "0.30.0", path = "../proposal/"}
+swc_ecma_transforms_module = {version = "0.31.0", path = "../module"}
+swc_ecma_transforms_proposal = {version = "0.31.0", path = "../proposal/"}
 swc_ecma_transforms_testing = {version = "0.26.0", path = "../testing"}
 testing = {version = "0.12.0", path = "../../../testing"}
 walkdir = "2.3.1"
diff --git a/node-swc/src/spack.ts b/node-swc/src/spack.ts
index 30f3f0f218..c57ae6ce30 100644
--- a/node-swc/src/spack.ts
+++ b/node-swc/src/spack.ts
@@ -33,17 +33,17 @@ export async function compileBundleOptions(config: BundleInput | string | undefi
 
 /**
  * Usage: In `spack.config.js` / `spack.config.ts`, you can utilize type annotations (to get autocompletions) like
- * 
+ *
  * ```ts
  * import { config } from '@swc/core/spack';
- * 
+ *
  * export default config({
  *      name: 'web',
  * });
  * ```
- * 
- * 
- * 
+ *
+ *
+ *
  */
 export function config(c: BundleInput): BundleInput {
     return c
@@ -62,6 +62,8 @@ export interface SpackConfig {
      */
     mode?: Mode
 
+    target?: Target
+
     entry: EntryConfig,
 
     output: OutputConfig
@@ -87,6 +89,7 @@ export interface ModuleConfig {
 }
 
 export type Mode = 'production' | 'development' | 'none';
+export type Target = 'browser' | 'node';
 
 export type EntryConfig = string | string[] | {
     [name: string]: string
diff --git a/node/binding/src/bundle.rs b/node/binding/src/bundle.rs
index 2ed59f2c5b..0ead30abbe 100644
--- a/node/binding/src/bundle.rs
+++ b/node/binding/src/bundle.rs
@@ -10,7 +10,7 @@ use std::{
     panic::{catch_unwind, AssertUnwindSafe},
     sync::Arc,
 };
-use swc::{config::SourceMapsConfig, resolver::NodeResolver, Compiler, TransformOutput};
+use swc::{config::SourceMapsConfig, resolver::environment_resolver, Compiler, TransformOutput};
 use swc_atoms::js_word;
 use swc_atoms::JsWord;
 use swc_bundler::{BundleKind, Bundler, Load, ModuleRecord, Resolve};
@@ -195,12 +195,14 @@ pub(crate) fn bundle(cx: CallContext) -> napi::Result<JsObject> {
             }),
     ));
 
+    let target_env = static_items.config.target;
+
     cx.env
         .spawn(BundleTask {
             swc: c.clone(),
             config: ConfigItem {
                 loader,
-                resolver: Box::new(NodeResolver::default()) as Box<_>,
+                resolver: Box::new(environment_resolver(target_env)) as Box<_>,
                 static_items,
             },
         })
diff --git a/spack/Cargo.toml b/spack/Cargo.toml
index aef43dca44..1217fa7aa2 100644
--- a/spack/Cargo.toml
+++ b/spack/Cargo.toml
@@ -28,6 +28,7 @@ swc_bundler = {path = "../bundler"}
 swc_common = {path = "../common", features = ["concurrent"]}
 swc_ecma_ast = {path = "../ecmascript/ast"}
 swc_ecma_codegen = {path = "../ecmascript/codegen"}
+swc_ecma_loader = {path = "../ecmascript/loader"}
 swc_ecma_parser = {path = "../ecmascript/parser"}
 swc_ecma_transforms = {path = "../ecmascript/transforms"}
 swc_ecma_utils = {path = "../ecmascript/utils"}
diff --git a/spack/src/config/mod.rs b/spack/src/config/mod.rs
index 3e7c9661e7..ddcba63b2e 100644
--- a/spack/src/config/mod.rs
+++ b/spack/src/config/mod.rs
@@ -9,6 +9,7 @@ use std::{collections::HashMap, fmt, marker::PhantomData, path::PathBuf};
 use string_enum::StringEnum;
 use swc_atoms::JsWord;
 use swc_common::FileName;
+use swc_ecma_ast::TargetEnv;
 use swc_ecma_parser::JscTarget;
 
 mod module;
@@ -25,6 +26,9 @@ pub struct Config {
     #[serde(default)]
     pub mode: Mode,
 
+    #[serde(default)]
+    pub target: TargetEnv,
+
     pub entry: EntryConfig,
 
     #[serde(default)]
diff --git a/src/config/mod.rs b/src/config/mod.rs
index 0b3790e60f..af1e6b7f7d 100644
--- a/src/config/mod.rs
+++ b/src/config/mod.rs
@@ -21,7 +21,9 @@ pub use swc_common::chain;
 use swc_common::{errors::Handler, FileName, Mark, SourceMap};
 use swc_ecma_ast::{Expr, ExprStmt, ModuleItem, Stmt};
 use swc_ecma_ext_transforms::jest;
-use swc_ecma_loader::resolvers::{lru::CachingResolver, node::NodeResolver, tsc::TsConfigResolver};
+use swc_ecma_loader::resolvers::{
+    lru::CachingResolver, node::NodeModulesResolver, tsc::TsConfigResolver,
+};
 use swc_ecma_minifier::option::terser::{TerserCompressorOptions, TerserEcmaVersion};
 use swc_ecma_minifier::option::{MangleOptions, ManglePropertiesOptions};
 pub use swc_ecma_parser::JscTarget;
@@ -1099,7 +1101,7 @@ fn build_resolver(base_url: String, paths: CompiledPaths) -> SwcImportResolver {
 
     let r = {
         let r = TsConfigResolver::new(
-            NodeResolver::default(),
+            NodeModulesResolver::default(),
             base_url.clone().into(),
             paths.clone(),
         );
diff --git a/src/lib.rs b/src/lib.rs
index 0ffe2f5617..2243e4c932 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -30,7 +30,9 @@ use swc_common::{
 };
 use swc_ecma_ast::Program;
 use swc_ecma_codegen::{self, text_writer::WriteJs, Emitter, Node};
-use swc_ecma_loader::resolvers::{lru::CachingResolver, node::NodeResolver, tsc::TsConfigResolver};
+use swc_ecma_loader::resolvers::{
+    lru::CachingResolver, node::NodeModulesResolver, tsc::TsConfigResolver,
+};
 use swc_ecma_minifier::option::MinifyOptions;
 use swc_ecma_parser::{lexer::Lexer, EsConfig, Parser, Syntax};
 use swc_ecma_transforms::{
@@ -46,12 +48,18 @@ use swc_ecma_visit::FoldWith;
 mod builder;
 pub mod config;
 pub mod resolver {
-    use swc_ecma_loader::resolvers::lru::CachingResolver;
+    use swc_ecma_ast::TargetEnv;
+    use swc_ecma_loader::resolvers::{lru::CachingResolver, node::NodeModulesResolver};
+
+    pub type NodeResolver = CachingResolver<NodeModulesResolver>;
 
-    pub type NodeResolver = CachingResolver<swc_ecma_loader::resolvers::node::NodeResolver>;
+    pub fn environment_resolver(target_env: TargetEnv) -> NodeResolver {
+        CachingResolver::new(40, NodeModulesResolver::new(target_env))
+    }
 }
 
-type SwcImportResolver = Arc<NodeImportResolver<CachingResolver<TsConfigResolver<NodeResolver>>>>;
+type SwcImportResolver =
+    Arc<NodeImportResolver<CachingResolver<TsConfigResolver<NodeModulesResolver>>>>;
 
 pub struct Compiler {
     /// swc uses rustc's span interning.


Test patch:
diff --git a/ecmascript/transforms/module/tests/path_node.rs b/ecmascript/transforms/module/tests/path_node.rs
index 7196da25ec..e41ae65f40 100644
--- a/ecmascript/transforms/module/tests/path_node.rs
+++ b/ecmascript/transforms/module/tests/path_node.rs
@@ -1,8 +1,8 @@
 use swc_common::FileName;
-use swc_ecma_loader::resolvers::node::NodeResolver;
+use swc_ecma_loader::resolvers::node::NodeModulesResolver;
 use swc_ecma_transforms_module::path::{ImportResolver, NodeImportResolver};
 use testing::run_test2;
-type TestProvider = NodeImportResolver<NodeResolver>;
+type TestProvider = NodeImportResolver<NodeModulesResolver>;
 
 #[test]
 fn node_modules() {
diff --git a/spack/tests/fixture.rs b/spack/tests/fixture.rs
index 224600c0b0..373c8c9b23 100644
--- a/spack/tests/fixture.rs
+++ b/spack/tests/fixture.rs
@@ -12,12 +12,13 @@ use std::{
     path::{Path, PathBuf},
     sync::Arc,
 };
-use swc::{config::SourceMapsConfig, resolver::NodeResolver};
+use swc::{config::SourceMapsConfig, resolver::environment_resolver};
 use swc_atoms::js_word;
 use swc_bundler::{BundleKind, Bundler, Config, ModuleRecord};
 use swc_common::{FileName, Span, GLOBALS};
 use swc_ecma_ast::{
     Bool, Expr, ExprOrSuper, Ident, KeyValueProp, Lit, MemberExpr, MetaPropExpr, PropName, Str,
+    TargetEnv,
 };
 use swc_ecma_parser::JscTarget;
 use swc_ecma_transforms::fixer;
@@ -139,7 +140,7 @@ fn reference_tests(tests: &mut Vec<TestDescAndFn>, errors: bool) -> Result<(), i
                         compiler.globals(),
                         cm.clone(),
                         &loader,
-                        NodeResolver::default(),
+                        environment_resolver(TargetEnv::Node),
                         Config {
                             require: true,
                             disable_inliner: true,