Repository: swc-project/swc (rust)

Issue:
Import assertions are blanket converted to import attributes
### Describe the bug

When using the `jsc.experiment.keepImportAssertions` flag the SWC compiler simply takes all assertions and outputs the new `with { type: "some-type" }` syntax regardless of what the input was. 

This causes issues with Node being able to run the code and generates a syntax error when trying to run the generated code. As it has been accepted that the `assert` syntax will not be getting removed there is no need to blanket change the syntax as this causes more issues than it solves.

### Input code

```typescript
import packageJSON from "./package.json" assert { type: "json" };
```


### Config

```json
{
  "$schema": "https://json.schemastore.org/swcrc",
  "jsc": {
    "parser": {
      "syntax": "typescript"
    },
    "target": "es2022",
    "experimental": { "keepImportAssertions": true }
  },
  "module": {
    "type": "es6"
  },
  "isModule": true
}
```


### Playground link

https://play.swc.rs/?version=1.3.82&code=H4sIAAAAAAAAA8vMLcgvKlEoSEzOTkxP9Qr291NIK8rPVVDS04eK6WUV5%2BcpKSQWF6cCFVaXVBakWikogQVrrbmS8%2FOK83NS9XLy0zWQDNG0BgDW5p%2FHWQAAAA%3D%3D&config=H4sIAAAAAAAAA0VOOw7CMAzdc4rIMwPKwMDGyMAhomKhQNNYtiu1qnL3OrSUze%2FvxXkPb%2Bng6hc7DVBkQT6wMTIPGidjQGdC6TiRwlespy2jkV%2BozYESziHAzuNEyCmj5fvW6OGDSPdMhfUmNqOpDGKK8oi%2Bur0RcnmOPf5%2Fartb%2BwUOU5LHz9birroVVF6is80AAAA%3D

### SWC Info output

_No response_

### Expected behavior

The type of assertion is preserved.

### Actual behavior

The assertion is transformed into the newer `with` syntax.

### Version

1.3.82

### Additional context

I have solved this issue for me by downgrading to swc version `1.3.80`. However, I think a more flexible solution is required.

Pull request:
feat(es/codegen): Add an option to print `assert` for import attributes
**Description:**

 - `jsc.experimental.keepImportAssertions` is renamed to `jsc.experimental.keepImportAttributes`.
 - `jsc.experimental.emitAssertForImportAttributes` is added.

**Related issue:**

 - Closes #7908

Patch:
diff --git a/bindings/binding_core_node/Cargo.toml b/bindings/binding_core_node/Cargo.toml
index d07a3a1d76..20024c9698 100644
--- a/bindings/binding_core_node/Cargo.toml
+++ b/bindings/binding_core_node/Cargo.toml
@@ -54,6 +54,7 @@ tracing-subscriber = { version = "0.3.9", features = ["env-filter"] }
 swc_core = { version = "0.82.10", features = [
   "allocator_node",
   "ecma_ast",
+  "ecma_codegen",
   "ecma_ast_serde",
   "common_concurrent",
   "bundler",
diff --git a/bindings/binding_core_node/src/bundle.rs b/bindings/binding_core_node/src/bundle.rs
index c2378b1698..2fe29702c9 100644
--- a/bindings/binding_core_node/src/bundle.rs
+++ b/bindings/binding_core_node/src/bundle.rs
@@ -129,16 +129,16 @@ impl Task for BundleTask {
                                 None,
                                 None,
                                 true,
-                                codegen_target,
                                 SourceMapsConfig::Bool(true),
                                 // TODO
                                 &Default::default(),
                                 None,
-                                minify,
                                 None,
                                 true,
-                                false,
                                 Default::default(),
+                                swc_core::ecma::codegen::Config::default()
+                                    .with_target(codegen_target)
+                                    .with_minify(minify),
                             )?;
 
                             Ok((k, output))
diff --git a/bindings/binding_core_node/src/print.rs b/bindings/binding_core_node/src/print.rs
index 983cad30e1..720c23e57f 100644
--- a/bindings/binding_core_node/src/print.rs
+++ b/bindings/binding_core_node/src/print.rs
@@ -40,18 +40,18 @@ impl Task for PrintTask {
                     None,
                     options.output_path.clone(),
                     true,
-                    options.config.jsc.target.unwrap_or(EsVersion::Es2020),
                     options
                         .source_maps
                         .clone()
                         .unwrap_or(SourceMapsConfig::Bool(false)),
                     &Default::default(),
                     None,
-                    options.config.minify.into_bool(),
                     None,
                     options.config.emit_source_map_columns.into_bool(),
-                    false,
                     Default::default(),
+                    swc_core::ecma::codegen::Config::default()
+                        .with_target(options.config.jsc.target.unwrap_or(EsVersion::Es2020))
+                        .with_minify(options.config.minify.into_bool()),
                 )
                 .convert_err()
         })
@@ -102,18 +102,18 @@ pub fn print_sync(program: String, options: Buffer) -> napi::Result<TransformOut
             None,
             options.output_path,
             true,
-            codegen_target,
             options
                 .source_maps
                 .clone()
                 .unwrap_or(SourceMapsConfig::Bool(false)),
             &Default::default(),
             None,
-            options.config.minify.into_bool(),
             None,
             options.config.emit_source_map_columns.into_bool(),
-            false,
             Default::default(),
+            swc_core::ecma::codegen::Config::default()
+                .with_target(codegen_target)
+                .with_minify(options.config.minify.into_bool()),
         )
         .convert_err()
     })
diff --git a/bindings/binding_core_wasm/Cargo.toml b/bindings/binding_core_wasm/Cargo.toml
index cf4db2630f..a5d0717cca 100644
--- a/bindings/binding_core_wasm/Cargo.toml
+++ b/bindings/binding_core_wasm/Cargo.toml
@@ -37,6 +37,7 @@ serde = { version = "1", features = ["derive"] }
 serde-wasm-bindgen = "0.4.5"
 swc_core = { version = "0.82.10", features = [
   "ecma_ast_serde",
+  "ecma_codegen",
   "binding_macro_wasm",
   "ecma_transforms",
   "ecma_visit",
diff --git a/crates/binding_macros/src/wasm.rs b/crates/binding_macros/src/wasm.rs
index cfb25ab747..8aef150de0 100644
--- a/crates/binding_macros/src/wasm.rs
+++ b/crates/binding_macros/src/wasm.rs
@@ -248,17 +248,17 @@ macro_rules! build_print_sync {
                         None,
                         None,
                         true,
-                        opts.codegen_target().unwrap_or($crate::wasm::EsVersion::Es2020),
                         opts.source_maps
                             .clone()
                             .unwrap_or($crate::wasm::SourceMapsConfig::Bool(false)),
                         &Default::default(),
                         None,
-                        opts.config.minify.into(),
                         None,
                         opts.config.emit_source_map_columns.into_bool(),
-                        false,
                         Default::default(),
+                        swc_core::ecma::codegen::Config::default()
+                            .with_target(opts.codegen_target().unwrap_or($crate::wasm::EsVersion::Es2020))
+                            .with_minify(opts.config.minify.into())
                     ),"failed to print code")?;
 
                     serde_wasm_bindgen::to_value(&s)
diff --git a/crates/dbg-swc/src/util/mod.rs b/crates/dbg-swc/src/util/mod.rs
index 8acfd2d05a..00f0bb3033 100644
--- a/crates/dbg-swc/src/util/mod.rs
+++ b/crates/dbg-swc/src/util/mod.rs
@@ -89,10 +89,7 @@ pub fn print_js(cm: Arc<SourceMap>, m: &Module, minify: bool) -> Result<String>
         }
 
         let mut e = swc_ecma_codegen::Emitter {
-            cfg: swc_ecma_codegen::Config {
-                minify,
-                ..Default::default()
-            },
+            cfg: swc_ecma_codegen::Config::default().with_minify(true),
             cm,
             comments: None,
             wr,
diff --git a/crates/swc/benches/typescript.rs b/crates/swc/benches/typescript.rs
index cde28c9da5..b87bfc8ccb 100644
--- a/crates/swc/benches/typescript.rs
+++ b/crates/swc/benches/typescript.rs
@@ -115,15 +115,13 @@ fn bench_codegen(b: &mut Bencher, _target: EsVersion) {
                     None,
                     None,
                     false,
-                    EsVersion::Es2020,
                     SourceMapsConfig::Bool(false),
                     &Default::default(),
                     None,
-                    false,
                     None,
                     false,
-                    false,
                     Default::default(),
+                    swc_ecma_codegen::Config::default().with_target(EsVersion::Es2020),
                 )
                 .unwrap()
             }));
diff --git a/crates/swc/src/config/mod.rs b/crates/swc/src/config/mod.rs
index 48086b9e0f..f1c2bf934a 100644
--- a/crates/swc/src/config/mod.rs
+++ b/crates/swc/src/config/mod.rs
@@ -667,7 +667,7 @@ impl Options {
             comments.map(|v| v as _),
         );
 
-        let keep_import_assertions = experimental.keep_import_assertions.into_bool();
+        let keep_import_attributes = experimental.keep_import_attributes.into_bool();
 
         #[cfg(feature = "plugin")]
         let plugin_transforms = {
@@ -801,7 +801,7 @@ impl Options {
                 ),
                 // The transform strips import assertions, so it's only enabled if
                 // keep_import_assertions is false.
-                Optional::new(import_assertions(), !keep_import_assertions),
+                Optional::new(import_assertions(), !keep_import_attributes),
                 Optional::new(
                     typescript::strip_with_jsx::<Option<&dyn Comments>>(
                         cm.clone(),
@@ -873,6 +873,9 @@ impl Options {
             preserve_comments,
             emit_source_map_columns: cfg.emit_source_map_columns.into_bool(),
             output: JscOutputConfig { charset, preamble },
+            emit_assert_for_import_attributes: experimental
+                .emit_assert_for_import_attributes
+                .into_bool(),
         })
     }
 }
@@ -1354,6 +1357,7 @@ impl Config {
 }
 
 /// One `BuiltConfig` per a directory with swcrc
+#[non_exhaustive]
 pub struct BuiltInput<P: swc_ecma_visit::Fold> {
     pub program: Program,
     pub pass: P,
@@ -1377,6 +1381,37 @@ pub struct BuiltInput<P: swc_ecma_visit::Fold> {
     pub emit_source_map_columns: bool,
 
     pub output: JscOutputConfig,
+    pub emit_assert_for_import_attributes: bool,
+}
+
+impl<P> BuiltInput<P>
+where
+    P: swc_ecma_visit::Fold,
+{
+    pub fn with_pass<N>(self, map: impl FnOnce(P) -> N) -> BuiltInput<N>
+    where
+        N: swc_ecma_visit::Fold,
+    {
+        BuiltInput {
+            program: self.program,
+            pass: map(self.pass),
+            syntax: self.syntax,
+            target: self.target,
+            minify: self.minify,
+            external_helpers: self.external_helpers,
+            source_maps: self.source_maps,
+            input_source_map: self.input_source_map,
+            is_module: self.is_module,
+            output_path: self.output_path,
+            source_file_name: self.source_file_name,
+            preserve_comments: self.preserve_comments,
+            inline_sources_content: self.inline_sources_content,
+            comments: self.comments,
+            emit_source_map_columns: self.emit_source_map_columns,
+            output: self.output,
+            emit_assert_for_import_attributes: self.emit_assert_for_import_attributes,
+        }
+    }
 }
 
 /// `jsc` in  `.swcrc`.
@@ -1460,7 +1495,10 @@ pub struct JscExperimental {
     pub plugins: Option<Vec<PluginConfig>>,
     /// If true, keeps import assertions in the output.
     #[serde(default)]
-    pub keep_import_assertions: BoolConfig<false>,
+    pub keep_import_attributes: BoolConfig<false>,
+
+    #[serde(default)]
+    pub emit_assert_for_import_attributes: BoolConfig<false>,
     /// Location where swc may stores its intermediate cache.
     /// Currently this is only being used for wasm plugin's bytecache.
     /// Path should be absolute directory, which will be created if not exist.
diff --git a/crates/swc/src/lib.rs b/crates/swc/src/lib.rs
index 6ef1ade4d1..398447dc90 100644
--- a/crates/swc/src/lib.rs
+++ b/crates/swc/src/lib.rs
@@ -501,15 +501,13 @@ impl Compiler {
         source_file_name: Option<&str>,
         output_path: Option<PathBuf>,
         inline_sources_content: bool,
-        target: EsVersion,
         source_map: SourceMapsConfig,
         source_map_names: &AHashMap<BytePos, JsWord>,
         orig: Option<&sourcemap::SourceMap>,
-        minify: bool,
         comments: Option<&dyn Comments>,
         emit_source_map_columns: bool,
-        ascii_only: bool,
         preamble: &str,
+        codegen_config: swc_ecma_codegen::Config,
     ) -> Result<TransformOutput, Error>
     where
         T: Node + VisitWith<IdentCollector>,
@@ -535,17 +533,12 @@ impl Compiler {
                     w.preamble(preamble).unwrap();
                     let mut wr = Box::new(w) as Box<dyn WriteJs>;
 
-                    if minify {
+                    if codegen_config.minify {
                         wr = Box::new(swc_ecma_codegen::text_writer::omit_trailing_semi(wr));
                     }
 
                     let mut emitter = Emitter {
-                        cfg: swc_ecma_codegen::Config {
-                            minify,
-                            target,
-                            ascii_only,
-                            ..Default::default()
-                        },
+                        cfg: codegen_config,
                         comments,
                         cm: self.cm.clone(),
                         wr,
@@ -970,26 +963,9 @@ impl Compiler {
                 }
             };
 
-            let pass = chain!(config.pass, custom_after_pass(&config.program));
-
-            let config = BuiltInput {
-                program: config.program,
-                pass,
-                syntax: config.syntax,
-                target: config.target,
-                minify: config.minify,
-                external_helpers: config.external_helpers,
-                source_maps: config.source_maps,
-                input_source_map: config.input_source_map,
-                is_module: config.is_module,
-                output_path: config.output_path,
-                source_file_name: config.source_file_name,
-                preserve_comments: config.preserve_comments,
-                inline_sources_content: config.inline_sources_content,
-                comments: config.comments,
-                emit_source_map_columns: config.emit_source_map_columns,
-                output: config.output,
-            };
+            let after_pass = custom_after_pass(&config.program);
+
+            let config = config.with_pass(|pass| chain!(pass, after_pass));
 
             let orig = if config.source_maps.enabled() {
                 self.get_orig_src_map(&fm, &config.input_source_map, false)?
@@ -1170,15 +1146,16 @@ impl Compiler {
                 Some(&fm.name.to_string()),
                 opts.output_path.clone().map(From::from),
                 opts.inline_sources_content,
-                target,
                 source_map,
                 &source_map_names,
                 orig.as_ref(),
-                true,
                 Some(&comments),
                 opts.emit_source_map_columns,
-                opts.format.ascii_only,
                 &opts.format.preamble,
+                swc_ecma_codegen::Config::default()
+                    .with_target(target)
+                    .with_minify(true)
+                    .with_ascii_only(opts.format.ascii_only),
             )
         })
     }
@@ -1245,19 +1222,22 @@ impl Compiler {
                 config.source_file_name.as_deref(),
                 config.output_path,
                 config.inline_sources_content,
-                config.target,
                 config.source_maps,
                 &source_map_names,
                 orig,
-                config.minify,
                 config.comments.as_ref().map(|v| v as _),
                 config.emit_source_map_columns,
-                config
-                    .output
-                    .charset
-                    .map(|v| matches!(v, OutputCharset::Ascii))
-                    .unwrap_or(false),
                 &config.output.preamble,
+                swc_ecma_codegen::Config::default()
+                    .with_target(config.target)
+                    .with_minify(config.minify)
+                    .with_ascii_only(
+                        config
+                            .output
+                            .charset
+                            .map(|v| matches!(v, OutputCharset::Ascii))
+                            .unwrap_or(false),
+                    ),
             )
         })
     }
diff --git a/crates/swc_bundler/examples/bundle.rs b/crates/swc_bundler/examples/bundle.rs
index 8e1b606f15..8bbd9d6c02 100644
--- a/crates/swc_bundler/examples/bundle.rs
+++ b/crates/swc_bundler/examples/bundle.rs
@@ -42,10 +42,7 @@ fn print_bundles(cm: Lrc<SourceMap>, modules: Vec<Bundle>, minify: bool) {
             {
                 let wr = JsWriter::new(cm.clone(), "\n", &mut buf, None);
                 let mut emitter = Emitter {
-                    cfg: swc_ecma_codegen::Config {
-                        minify,
-                        ..Default::default()
-                    },
+                    cfg: swc_ecma_codegen::Config::default().with_minify(true),
                     cm: cm.clone(),
                     comments: None,
                     wr: if minify {
diff --git a/crates/swc_bundler/examples/path.rs b/crates/swc_bundler/examples/path.rs
index 48ad850d84..7857b6902b 100644
--- a/crates/swc_bundler/examples/path.rs
+++ b/crates/swc_bundler/examples/path.rs
@@ -46,10 +46,7 @@ fn main() {
 
     let wr = stdout();
     let mut emitter = Emitter {
-        cfg: swc_ecma_codegen::Config {
-            minify: false,
-            ..Default::default()
-        },
+        cfg: swc_ecma_codegen::Config::default(),
         cm: cm.clone(),
         comments: None,
         wr: Box::new(JsWriter::new(cm, "\n", wr.lock(), None)),
diff --git a/crates/swc_bundler/src/debug/mod.rs b/crates/swc_bundler/src/debug/mod.rs
index 372fbcd5a6..eca3c6ae12 100644
--- a/crates/swc_bundler/src/debug/mod.rs
+++ b/crates/swc_bundler/src/debug/mod.rs
@@ -19,10 +19,7 @@ pub(crate) fn print_hygiene(event: &str, cm: &Lrc<SourceMap>, t: &Module) {
 
     writeln!(w, "==================== @ {} ====================", event).unwrap();
     Emitter {
-        cfg: swc_ecma_codegen::Config {
-            minify: false,
-            ..Default::default()
-        },
+        cfg: swc_ecma_codegen::Config::default(),
         cm: cm.clone(),
         comments: None,
         wr: Box::new(JsWriter::new(cm.clone(), "\n", &mut w, None)),
diff --git a/crates/swc_ecma_codegen/src/config.rs b/crates/swc_ecma_codegen/src/config.rs
index 204d77a041..90ff3f84d4 100644
--- a/crates/swc_ecma_codegen/src/config.rs
+++ b/crates/swc_ecma_codegen/src/config.rs
@@ -5,6 +5,7 @@ use swc_ecma_ast::EsVersion;
 #[derive(Debug, Clone, Copy)]
 #[cfg_attr(feature = "serde-impl", derive(Serialize, Deserialize))]
 #[cfg_attr(feature = "serde-impl", serde(rename_all = "camelCase"))]
+#[non_exhaustive]
 pub struct Config {
     /// The target runtime environment.
     ///
@@ -32,6 +33,9 @@ pub struct Config {
     /// Defaults to `false`.
     #[cfg_attr(feature = "serde-impl", serde(default))]
     pub omit_last_semi: bool,
+
+    #[cfg_attr(feature = "serde-impl", serde(default))]
+    pub emit_assert_for_import_attributes: bool,
 }
 
 impl Default for Config {
@@ -41,6 +45,37 @@ impl Default for Config {
             minify: false,
             ascii_only: false,
             omit_last_semi: false,
+            emit_assert_for_import_attributes: false,
         }
     }
 }
+
+impl Config {
+    pub fn with_target(mut self, target: EsVersion) -> Self {
+        self.target = target;
+        self
+    }
+
+    pub fn with_minify(mut self, minify: bool) -> Self {
+        self.minify = minify;
+        self
+    }
+
+    pub fn with_ascii_only(mut self, ascii_only: bool) -> Self {
+        self.ascii_only = ascii_only;
+        self
+    }
+
+    pub fn with_omit_last_semi(mut self, omit_last_semi: bool) -> Self {
+        self.omit_last_semi = omit_last_semi;
+        self
+    }
+
+    pub fn with_emit_assert_for_import_attributes(
+        mut self,
+        emit_assert_for_import_attributes: bool,
+    ) -> Self {
+        self.emit_assert_for_import_attributes = emit_assert_for_import_attributes;
+        self
+    }
+}
diff --git a/crates/swc_ecma_codegen/src/lib.rs b/crates/swc_ecma_codegen/src/lib.rs
index 16dfb9bb50..c30f9d3c86 100644
--- a/crates/swc_ecma_codegen/src/lib.rs
+++ b/crates/swc_ecma_codegen/src/lib.rs
@@ -310,7 +310,11 @@ where
 
         if let Some(with) = &n.with {
             formatting_space!();
-            keyword!("with");
+            if self.cfg.emit_assert_for_import_attributes {
+                keyword!("asserts");
+            } else {
+                keyword!("with")
+            };
             formatting_space!();
             emit!(with);
         }
@@ -453,7 +457,11 @@ where
 
             if let Some(with) = &node.with {
                 formatting_space!();
-                keyword!("with");
+                if self.cfg.emit_assert_for_import_attributes {
+                    keyword!("asserts");
+                } else {
+                    keyword!("with")
+                };
                 formatting_space!();
                 emit!(with);
             }
@@ -479,7 +487,11 @@ where
 
         if let Some(with) = &node.with {
             formatting_space!();
-            keyword!("with");
+            if self.cfg.emit_assert_for_import_attributes {
+                keyword!("asserts");
+            } else {
+                keyword!("with")
+            };
             formatting_space!();
             emit!(with);
         }
diff --git a/crates/swc_ecma_minifier/benches/full.rs b/crates/swc_ecma_minifier/benches/full.rs
index f0c0935256..17eba4f4e7 100644
--- a/crates/swc_ecma_minifier/benches/full.rs
+++ b/crates/swc_ecma_minifier/benches/full.rs
@@ -112,10 +112,7 @@ fn print<N: swc_ecma_codegen::Node>(cm: Lrc<SourceMap>, nodes: &[N], minify: boo
 
     {
         let mut emitter = swc_ecma_codegen::Emitter {
-            cfg: swc_ecma_codegen::Config {
-                minify,
-                ..Default::default()
-            },
+            cfg: swc_ecma_codegen::Config::default().with_minify(minify),
             cm: cm.clone(),
             comments: None,
             wr: Box::new(JsWriter::new(cm, "\n", &mut buf, None)),
diff --git a/crates/swc_ecma_minifier/examples/compress.rs b/crates/swc_ecma_minifier/examples/compress.rs
index f141fc2345..705ac90247 100644
--- a/crates/swc_ecma_minifier/examples/compress.rs
+++ b/crates/swc_ecma_minifier/examples/compress.rs
@@ -75,10 +75,7 @@ fn print<N: swc_ecma_codegen::Node>(cm: Lrc<SourceMap>, nodes: &[N], minify: boo
 
     {
         let mut emitter = swc_ecma_codegen::Emitter {
-            cfg: swc_ecma_codegen::Config {
-                minify,
-                ..Default::default()
-            },
+            cfg: swc_ecma_codegen::Config::default().with_minify(minify),
             cm: cm.clone(),
             comments: None,
             wr: Box::new(JsWriter::new(cm, "\n", &mut buf, None)),
diff --git a/crates/swc_ecma_minifier/examples/minifier.rs b/crates/swc_ecma_minifier/examples/minifier.rs
index 73708705cb..395f6ebdd2 100644
--- a/crates/swc_ecma_minifier/examples/minifier.rs
+++ b/crates/swc_ecma_minifier/examples/minifier.rs
@@ -76,10 +76,7 @@ fn print<N: swc_ecma_codegen::Node>(cm: Lrc<SourceMap>, nodes: &[N], minify: boo
 
     {
         let mut emitter = swc_ecma_codegen::Emitter {
-            cfg: swc_ecma_codegen::Config {
-                minify,
-                ..Default::default()
-            },
+            cfg: swc_ecma_codegen::Config::default().with_minify(minify),
             cm: cm.clone(),
             comments: None,
             wr: omit_trailing_semi(JsWriter::new(cm, "\n", &mut buf, None)),
diff --git a/crates/swc_ecma_minifier/examples/minify-all.rs b/crates/swc_ecma_minifier/examples/minify-all.rs
index a770bc11b0..6a94bbcecc 100644
--- a/crates/swc_ecma_minifier/examples/minify-all.rs
+++ b/crates/swc_ecma_minifier/examples/minify-all.rs
@@ -122,10 +122,7 @@ fn print<N: swc_ecma_codegen::Node>(cm: Lrc<SourceMap>, nodes: &[N], minify: boo
 
     {
         let mut emitter = swc_ecma_codegen::Emitter {
-            cfg: swc_ecma_codegen::Config {
-                minify,
-                ..Default::default()
-            },
+            cfg: swc_ecma_codegen::Config::default().with_minify(minify),
             cm: cm.clone(),
             comments: None,
             wr: Box::new(JsWriter::new(cm, "\n", &mut buf, None)),
diff --git a/crates/swc_ecma_minifier/src/util/base54.rs b/crates/swc_ecma_minifier/src/util/base54.rs
index 7b0820a6f8..d98dfa98b9 100644
--- a/crates/swc_ecma_minifier/src/util/base54.rs
+++ b/crates/swc_ecma_minifier/src/util/base54.rs
@@ -227,12 +227,9 @@ impl CharFreq {
 
         {
             let mut emitter = Emitter {
-                cfg: swc_ecma_codegen::Config {
-                    target: EsVersion::latest(),
-                    ascii_only: false,
-                    minify: true,
-                    ..Default::default()
-                },
+                cfg: swc_ecma_codegen::Config::default()
+                    .with_target(EsVersion::latest())
+                    .with_minify(true),
                 cm,
                 comments: None,
                 wr: &mut freq,
diff --git a/crates/swc_ecma_transforms_proposal/src/import_assertions.rs b/crates/swc_ecma_transforms_proposal/src/import_assertions.rs
index ef6f5de1bc..5612a312f1 100644
--- a/crates/swc_ecma_transforms_proposal/src/import_assertions.rs
+++ b/crates/swc_ecma_transforms_proposal/src/import_assertions.rs
@@ -1,9 +1,13 @@
 use swc_ecma_ast::{ExportAll, ImportDecl, NamedExport};
 use swc_ecma_visit::{as_folder, noop_visit_mut_type, Fold, VisitMut};
 
-pub fn import_assertions() -> impl VisitMut + Fold {
+#[deprecated(note = "Please use `import_assertions` instead")]
+pub use self::import_attributes as import_assertions;
+
+pub fn import_attributes() -> impl VisitMut + Fold {
     as_folder(ImportAssertions)
 }
+
 struct ImportAssertions;
 
 impl VisitMut for ImportAssertions {
diff --git a/crates/swc_ecma_transforms_typescript/examples/ts_to_js.rs b/crates/swc_ecma_transforms_typescript/examples/ts_to_js.rs
index 7edfc18cad..680d3ab0e0 100644
--- a/crates/swc_ecma_transforms_typescript/examples/ts_to_js.rs
+++ b/crates/swc_ecma_transforms_typescript/examples/ts_to_js.rs
@@ -80,10 +80,7 @@ fn main() {
         let mut buf = vec![];
         {
             let mut emitter = Emitter {
-                cfg: swc_ecma_codegen::Config {
-                    minify: false,
-                    ..Default::default()
-                },
+                cfg: swc_ecma_codegen::Config::default(),
                 cm: cm.clone(),
                 comments: Some(&comments),
                 wr: JsWriter::new(cm.clone(), "\n", &mut buf, None),
diff --git a/crates/swc_html_minifier/src/lib.rs b/crates/swc_html_minifier/src/lib.rs
index bca0f876bf..4a83a7d9aa 100644
--- a/crates/swc_html_minifier/src/lib.rs
+++ b/crates/swc_html_minifier/src/lib.rs
@@ -2114,12 +2114,7 @@ impl Minifier<'_> {
             )) as Box<dyn swc_ecma_codegen::text_writer::WriteJs>;
 
             let mut emitter = swc_ecma_codegen::Emitter {
-                cfg: swc_ecma_codegen::Config {
-                    target,
-                    minify: false,
-                    ascii_only: false,
-                    omit_last_semi: false,
-                },
+                cfg: swc_ecma_codegen::Config::default().with_target(target),
                 cm,
                 comments: Some(&comments),
                 wr,
diff --git a/crates/swc_plugin_runner/src/wasix_runtime.rs b/crates/swc_plugin_runner/src/wasix_runtime.rs
index 09e8d0b54e..1fa2fb936a 100644
--- a/crates/swc_plugin_runner/src/wasix_runtime.rs
+++ b/crates/swc_plugin_runner/src/wasix_runtime.rs
@@ -1,3 +1,5 @@
+#![allow(unused)]
+
 use std::{path::PathBuf, sync::Arc};
 
 use parking_lot::Mutex;


Test patch:
diff --git a/crates/swc/tests/fixture/issues-7xxx/7908/input/1/.swcrc b/crates/swc/tests/fixture/issues-7xxx/7908/input/1/.swcrc
new file mode 100644
index 0000000000..96207a1d39
--- /dev/null
+++ b/crates/swc/tests/fixture/issues-7xxx/7908/input/1/.swcrc
@@ -0,0 +1,17 @@
+{
+    "$schema": "https://json.schemastore.org/swcrc",
+    "jsc": {
+        "parser": {
+            "syntax": "typescript"
+        },
+        "target": "es2022",
+        "experimental": {
+            "keepImportAttributes": true,
+            "emitAssertForImportAttributes": true
+        }
+    },
+    "module": {
+        "type": "es6"
+    },
+    "isModule": true
+}
\ No newline at end of file
diff --git a/crates/swc/tests/fixture/issues-7xxx/7908/input/1/1.js b/crates/swc/tests/fixture/issues-7xxx/7908/input/1/1.js
new file mode 100644
index 0000000000..e020128a2d
--- /dev/null
+++ b/crates/swc/tests/fixture/issues-7xxx/7908/input/1/1.js
@@ -0,0 +1,3 @@
+import packageJSON from "./package.json" assert { type: "json" };
+
+console.log(packageJSON)
diff --git a/crates/swc/tests/fixture/issues-7xxx/7908/input/2/.swcrc b/crates/swc/tests/fixture/issues-7xxx/7908/input/2/.swcrc
new file mode 100644
index 0000000000..fae29fdfd5
--- /dev/null
+++ b/crates/swc/tests/fixture/issues-7xxx/7908/input/2/.swcrc
@@ -0,0 +1,16 @@
+{
+    "$schema": "https://json.schemastore.org/swcrc",
+    "jsc": {
+        "parser": {
+            "syntax": "typescript"
+        },
+        "target": "es2022",
+        "experimental": {
+            "keepImportAttributes": true
+        }
+    },
+    "module": {
+        "type": "es6"
+    },
+    "isModule": true
+}
\ No newline at end of file
diff --git a/crates/swc/tests/fixture/issues-7xxx/7908/input/2/1.js b/crates/swc/tests/fixture/issues-7xxx/7908/input/2/1.js
new file mode 100644
index 0000000000..4606122aa6
--- /dev/null
+++ b/crates/swc/tests/fixture/issues-7xxx/7908/input/2/1.js
@@ -0,0 +1,4 @@
+import packageJSON from "./package.json" assert { type: "json" };
+
+
+console.log(packageJSON)
diff --git a/crates/swc/tests/fixture/issues-7xxx/7908/output/1/1.js b/crates/swc/tests/fixture/issues-7xxx/7908/output/1/1.js
new file mode 100644
index 0000000000..8398c5d0ae
--- /dev/null
+++ b/crates/swc/tests/fixture/issues-7xxx/7908/output/1/1.js
@@ -0,0 +1,4 @@
+import packageJSON from "./package.json" with {
+    type: "json"
+};
+console.log(packageJSON);
diff --git a/crates/swc/tests/fixture/issues-7xxx/7908/output/2/1.js b/crates/swc/tests/fixture/issues-7xxx/7908/output/2/1.js
new file mode 100644
index 0000000000..8398c5d0ae
--- /dev/null
+++ b/crates/swc/tests/fixture/issues-7xxx/7908/output/2/1.js
@@ -0,0 +1,4 @@
+import packageJSON from "./package.json" with {
+    type: "json"
+};
+console.log(packageJSON);
diff --git a/crates/swc/tests/projects.rs b/crates/swc/tests/projects.rs
index 2a2594d519..3f3c578d9f 100644
--- a/crates/swc/tests/projects.rs
+++ b/crates/swc/tests/projects.rs
@@ -7,8 +7,8 @@ use anyhow::Context;
 use rayon::prelude::*;
 use swc::{
     config::{
-        BuiltInput, Config, FileMatcher, JsMinifyOptions, JscConfig, ModuleConfig, Options,
-        SourceMapsConfig, TransformConfig,
+        Config, FileMatcher, JsMinifyOptions, JscConfig, ModuleConfig, Options, SourceMapsConfig,
+        TransformConfig,
     },
     try_with_handler, BoolOrDataConfig, Compiler, TransformOutput,
 };
@@ -719,24 +719,7 @@ fn should_visit() {
 
             dbg!(config.syntax);
 
-            let config = BuiltInput {
-                program: config.program,
-                pass: chain!(Panicking, config.pass),
-                syntax: config.syntax,
-                target: config.target,
-                minify: config.minify,
-                external_helpers: config.external_helpers,
-                source_maps: config.source_maps,
-                input_source_map: config.input_source_map,
-                is_module: config.is_module,
-                output_path: config.output_path,
-                source_file_name: config.source_file_name,
-                preserve_comments: config.preserve_comments,
-                inline_sources_content: config.inline_sources_content,
-                comments: config.comments,
-                emit_source_map_columns: config.emit_source_map_columns,
-                output: config.output,
-            };
+            let config = config.with_pass(|pass| chain!(Panicking, pass));
 
             if config.minify {
                 let preserve_excl = |_: &BytePos, vc: &mut Vec<Comment>| -> bool {
@@ -760,16 +743,16 @@ fn should_visit() {
                 None,
                 config.output_path,
                 config.inline_sources_content,
-                config.target,
                 config.source_maps,
                 &Default::default(),
                 None,
                 // TODO: figure out sourcemaps
-                config.minify,
                 Some(&comments),
                 config.emit_source_map_columns,
-                false,
                 Default::default(),
+                swc_ecma_codegen::Config::default()
+                    .with_target(config.target)
+                    .with_minify(config.minify),
             )
             .unwrap()
             .code)
diff --git a/crates/swc_bundler/tests/deno.rs b/crates/swc_bundler/tests/deno.rs
index 4431ad4eed..ad593a30c7 100644
--- a/crates/swc_bundler/tests/deno.rs
+++ b/crates/swc_bundler/tests/deno.rs
@@ -1085,10 +1085,7 @@ fn bundle(url: &str, minify: bool) -> String {
                     }
 
                     Emitter {
-                        cfg: swc_ecma_codegen::Config {
-                            minify,
-                            ..Default::default()
-                        },
+                        cfg: swc_ecma_codegen::Config::default().with_minify(minify),
                         cm: cm.clone(),
                         comments: None,
                         wr,
diff --git a/crates/swc_ecma_codegen/tests/fixture.rs b/crates/swc_ecma_codegen/tests/fixture.rs
index d162af295e..aba025d528 100644
--- a/crates/swc_ecma_codegen/tests/fixture.rs
+++ b/crates/swc_ecma_codegen/tests/fixture.rs
@@ -49,10 +49,7 @@ fn run(input: &Path, minify: bool) {
             }
 
             let mut emitter = Emitter {
-                cfg: swc_ecma_codegen::Config {
-                    minify,
-                    ..Default::default()
-                },
+                cfg: swc_ecma_codegen::Config::default().with_minify(minify),
                 cm,
                 comments: None,
                 wr,
diff --git a/crates/swc_ecma_codegen/tests/sourcemap.rs b/crates/swc_ecma_codegen/tests/sourcemap.rs
index b8d2e7af2d..dd8358b06d 100644
--- a/crates/swc_ecma_codegen/tests/sourcemap.rs
+++ b/crates/swc_ecma_codegen/tests/sourcemap.rs
@@ -322,12 +322,10 @@ fn identity(entry: PathBuf) {
             wr = Box::new(swc_ecma_codegen::text_writer::omit_trailing_semi(wr));
 
             let mut emitter = Emitter {
-                cfg: swc_ecma_codegen::Config {
-                    minify: true,
-                    target: EsVersion::Es5,
-                    ascii_only: true,
-                    ..Default::default()
-                },
+                cfg: swc_ecma_codegen::Config::default()
+                    .with_minify(true)
+                    .with_ascii_only(true)
+                    .with_target(EsVersion::Es5),
                 cm: cm.clone(),
                 wr,
                 comments: None,
diff --git a/crates/swc_ecma_codegen/tests/test262.rs b/crates/swc_ecma_codegen/tests/test262.rs
index 8e0b270283..b8e60b4e4a 100644
--- a/crates/swc_ecma_codegen/tests/test262.rs
+++ b/crates/swc_ecma_codegen/tests/test262.rs
@@ -136,11 +136,9 @@ fn do_test(entry: &Path, minify: bool) {
             }
 
             let mut emitter = Emitter {
-                cfg: swc_ecma_codegen::Config {
-                    minify,
-                    target: EsVersion::Es5,
-                    ..Default::default()
-                },
+                cfg: swc_ecma_codegen::Config::default()
+                    .with_minify(minify)
+                    .with_target(EsVersion::Es5),
                 cm,
                 wr,
                 comments: if minify { None } else { Some(&comments) },
diff --git a/crates/swc_ecma_minifier/tests/compress.rs b/crates/swc_ecma_minifier/tests/compress.rs
index c3ba509b84..1ac887200a 100644
--- a/crates/swc_ecma_minifier/tests/compress.rs
+++ b/crates/swc_ecma_minifier/tests/compress.rs
@@ -630,10 +630,7 @@ fn print<N: swc_ecma_codegen::Node>(
         }
 
         let mut emitter = Emitter {
-            cfg: swc_ecma_codegen::Config {
-                minify,
-                ..Default::default()
-            },
+            cfg: swc_ecma_codegen::Config::default().with_minify(minify),
             cm,
             comments: None,
             wr,
diff --git a/crates/swc_ecma_minifier/tests/exec.rs b/crates/swc_ecma_minifier/tests/exec.rs
index 98fab543f6..a02d73b4c2 100644
--- a/crates/swc_ecma_minifier/tests/exec.rs
+++ b/crates/swc_ecma_minifier/tests/exec.rs
@@ -77,10 +77,7 @@ fn print<N: swc_ecma_codegen::Node>(
         }
 
         let mut emitter = Emitter {
-            cfg: swc_ecma_codegen::Config {
-                minify,
-                ..Default::default()
-            },
+            cfg: swc_ecma_codegen::Config::default().with_minify(minify),
             cm,
             comments: None,
             wr,
diff --git a/crates/swc_ecma_minifier/tests/mangle.rs b/crates/swc_ecma_minifier/tests/mangle.rs
index 480f48248e..140f3c16d3 100644
--- a/crates/swc_ecma_minifier/tests/mangle.rs
+++ b/crates/swc_ecma_minifier/tests/mangle.rs
@@ -33,10 +33,7 @@ fn print(cm: Lrc<SourceMap>, m: &Module, minify: bool) -> String {
         }
 
         let mut emitter = Emitter {
-            cfg: swc_ecma_codegen::Config {
-                minify,
-                ..Default::default()
-            },
+            cfg: swc_ecma_codegen::Config::default().with_minify(minify),
             cm,
             comments: None,
             wr,
diff --git a/crates/swc_ecma_minifier/tests/terser_exec.rs b/crates/swc_ecma_minifier/tests/terser_exec.rs
index dd8d2ed84a..df809bf1af 100644
--- a/crates/swc_ecma_minifier/tests/terser_exec.rs
+++ b/crates/swc_ecma_minifier/tests/terser_exec.rs
@@ -304,10 +304,7 @@ fn print<N: swc_ecma_codegen::Node>(
         }
 
         let mut emitter = Emitter {
-            cfg: swc_ecma_codegen::Config {
-                minify,
-                ..Default::default()
-            },
+            cfg: swc_ecma_codegen::Config::default().with_minify(minify),
             cm,
             comments: None,
             wr,
diff --git a/crates/swc_ecma_preset_env/tests/test.rs b/crates/swc_ecma_preset_env/tests/test.rs
index f4bf64eb8c..1379699a01 100644
--- a/crates/swc_ecma_preset_env/tests/test.rs
+++ b/crates/swc_ecma_preset_env/tests/test.rs
@@ -159,10 +159,7 @@ fn exec(c: PresetConfig, dir: PathBuf) -> Result<(), Error> {
                 let mut buf = vec![];
                 {
                     let mut emitter = Emitter {
-                        cfg: swc_ecma_codegen::Config {
-                            minify: false,
-                            ..Default::default()
-                        },
+                        cfg: swc_ecma_codegen::Config::default(),
                         comments: None,
                         cm: cm.clone(),
                         wr: Box::new(swc_ecma_codegen::text_writer::JsWriter::new(
diff --git a/crates/swc_ecma_transforms_base/tests/fixer_test262.rs b/crates/swc_ecma_transforms_base/tests/fixer_test262.rs
index 60822e4bb2..4afd9b651f 100644
--- a/crates/swc_ecma_transforms_base/tests/fixer_test262.rs
+++ b/crates/swc_ecma_transforms_base/tests/fixer_test262.rs
@@ -175,10 +175,7 @@ fn identity_tests(tests: &mut Vec<TestDescAndFn>) -> Result<(), io::Error> {
 
                     {
                         let mut emitter = Emitter {
-                            cfg: swc_ecma_codegen::Config {
-                                minify: false,
-                                ..Default::default()
-                            },
+                            cfg: swc_ecma_codegen::Config::default(),
                             cm: cm.clone(),
                             wr: Box::new(swc_ecma_codegen::text_writer::JsWriter::new(
                                 cm.clone(),
@@ -189,10 +186,7 @@ fn identity_tests(tests: &mut Vec<TestDescAndFn>) -> Result<(), io::Error> {
                             comments: None,
                         };
                         let mut expected_emitter = Emitter {
-                            cfg: swc_ecma_codegen::Config {
-                                minify: false,
-                                ..Default::default()
-                            },
+                            cfg: swc_ecma_codegen::Config::default(),
                             cm: cm.clone(),
                             wr: Box::new(swc_ecma_codegen::text_writer::JsWriter::new(
                                 cm, "\n", &mut wr2, None,
diff --git a/crates/swc_ecma_transforms_react/src/jsx/tests.rs b/crates/swc_ecma_transforms_react/src/jsx/tests.rs
index 7ab15dd9f0..acf91be34d 100644
--- a/crates/swc_ecma_transforms_react/src/jsx/tests.rs
+++ b/crates/swc_ecma_transforms_react/src/jsx/tests.rs
@@ -1499,12 +1499,9 @@ fn test_script(src: &str, output: &Path, options: Options) {
         let mut buf = vec![];
 
         let mut emitter = Emitter {
-            cfg: Config {
-                target: Default::default(),
-                ascii_only: true,
-                minify: false,
-                omit_last_semi: true,
-            },
+            cfg: Config::default()
+                .with_ascii_only(true)
+                .with_omit_last_semi(true),
             cm: tester.cm.clone(),
             wr: Box::new(swc_ecma_codegen::text_writer::JsWriter::new(
                 tester.cm.clone(),
diff --git a/crates/swc_ecma_transforms_typescript/tests/strip_correctness.rs b/crates/swc_ecma_transforms_typescript/tests/strip_correctness.rs
index 0497aff5bb..21f42d1011 100644
--- a/crates/swc_ecma_transforms_typescript/tests/strip_correctness.rs
+++ b/crates/swc_ecma_transforms_typescript/tests/strip_correctness.rs
@@ -188,10 +188,7 @@ fn identity(entry: PathBuf) {
 
         {
             let mut emitter = Emitter {
-                cfg: swc_ecma_codegen::Config {
-                    minify: false,
-                    ..Default::default()
-                },
+                cfg: swc_ecma_codegen::Config::default(),
                 cm: cm.clone(),
                 wr: Box::new(swc_ecma_codegen::text_writer::JsWriter::new(
                     cm.clone(),
diff --git a/crates/swc_node_bundler/tests/fixture.rs b/crates/swc_node_bundler/tests/fixture.rs
index 9ecac943b3..237f37a622 100644
--- a/crates/swc_node_bundler/tests/fixture.rs
+++ b/crates/swc_node_bundler/tests/fixture.rs
@@ -91,15 +91,13 @@ fn pass(input_dir: PathBuf) {
                             None,
                             None,
                             false,
-                            EsVersion::Es2020,
                             SourceMapsConfig::Bool(false),
                             &Default::default(),
                             None,
-                            false,
                             Some(&comments),
                             false,
-                            false,
                             Default::default(),
+                            swc_ecma_codegen::Config::default().with_target(EsVersion::Es2020),
                         )
                         .expect("failed to print?")
                         .code;