Repository: swc-project/swc (rust)

Issue:
Using `emit_leading_comments_of_span` for `emit_module` and `emit_script`
### Describe the feature

```diff
#[emitter]
    pub fn emit_module(&mut self, node: &Module) -> Result {
+      self.emit_leading_comments_of_span(node.span(), false)?;
        if let Some(ref shebang) = node.shebang {
            punct!("#!");
            self.wr.write_str_lit(DUMMY_SP, &*shebang)?;
            self.wr.write_line()?;
        }
        for stmt in &node.body {
            emit!(stmt);
        }
+      self.emit_trailing_comments_of_pos(node.span().hi, true, true)?;
    }
```

I'm doing something like. Because some AST nodes in `module#body` were deleted in manipulating, I couldn't get the correct comment output with the following codes.

```rs
let comments = SingleThreadedComments::default();

self.swc_modules.iter().for_each(|module| {
  comments.add_leading(
      module.span.lo,
      Comment {
          kind: CommentKind::Line,
          span: DUMMY_SP,
          text: format!(" {}", SOME_DEBUG_INFO),
      },
  );
});

let mut emitter = swc_ecma_codegen::Emitter {
  cfg: swc_ecma_codegen::Config { minify: false },
  cm: crate::graph::SOURCE_MAP.clone(),
  comments: Some(&comments),
  wr: Box::new(JsWriter::with_target(
      crate::graph::SOURCE_MAP.clone(),
      "\n",
      &mut output,
      None,
      EsVersion::latest(),
  )),
};
```

This is my hack version now.
```diff

self.swc_modules.iter().for_each(|module| {
+ module.ast.body.insert(
+   0,
+   ModuleItem::Stmt(Stmt::Empty(EmptyStmt {
+       span: swc_common::Span {
+           lo: module.ast.span.lo,
+           hi: module.ast.span.hi,
+           ..Default::default()
+       },
+   })),
+ );
  comments.add_leading(
      module.span.lo,
      Comment {
          kind: CommentKind::Line,
          span: DUMMY_SP,
          text: format!(" {}", SOME_DEBUG_INFO),
      },
  );
});


```

I would like to send a PR to do this if there's no downside to this.

### Babel plugin or link to the feature description

_No response_

### Additional context

_No response_

Pull request description:
feat(es/codegen): emit comments in method emit_module and emit_script
<!-- Thanks for submitting a pull request! Please provide enough information so that others can review your pull request. -->

**Description:**

<!-- Explain the **motivation** for making this change. What existing problem does the pull request solve? -->

**BREAKING CHANGE:**
<!--
If this PR introduces a breaking change, it must contain a notice for it to be included in the CHANGELOG. Add description or remove entirely if not breaking.

You may need to update `MIGRATION.md` for the breaking changes.
-->

**Related issue (if exists):**

Close #3354.



Patch:
diff --git a/crates/swc_ecma_codegen/src/lib.rs b/crates/swc_ecma_codegen/src/lib.rs
index a44b968a73..d182ca9506 100644
--- a/crates/swc_ecma_codegen/src/lib.rs
+++ b/crates/swc_ecma_codegen/src/lib.rs
@@ -93,6 +93,7 @@ where
 
     #[emitter]
     pub fn emit_script(&mut self, node: &Script) -> Result {
+        self.emit_leading_comments_of_span(node.span(), false)?;
         if let Some(ref shebang) = node.shebang {
             punct!("#!");
             self.wr.write_str_lit(DUMMY_SP, &*shebang)?;
@@ -101,14 +102,17 @@ where
         for stmt in &node.body {
             emit!(stmt);
         }
+        self.emit_trailing_comments_of_pos(node.span().hi, true, true)?;
     }
 
     #[emitter]
     pub fn emit_module_item(&mut self, node: &ModuleItem) -> Result {
+        self.emit_leading_comments_of_span(node.span(), false)?;
         match *node {
             ModuleItem::Stmt(ref stmt) => emit!(stmt),
             ModuleItem::ModuleDecl(ref decl) => emit!(decl),
         }
+        self.emit_trailing_comments_of_pos(node.span().hi, true, true)?;
     }
 
     #[emitter]


Test patch:
diff --git a/crates/swc/tests/tsc-references/asOperator4_es2015.1.normal.js b/crates/swc/tests/tsc-references/asOperator4_es2015.1.normal.js
index 3b53c29f83..baa8c6d855 100644
--- a/crates/swc/tests/tsc-references/asOperator4_es2015.1.normal.js
+++ b/crates/swc/tests/tsc-references/asOperator4_es2015.1.normal.js
@@ -1,5 +1,6 @@
 //@module: commonjs
 //@filename: foo.ts
 export function foo() {}
+// These should emit identically
 foo;
 foo;
diff --git a/crates/swc/tests/tsc-references/asOperator4_es5.1.normal.js b/crates/swc/tests/tsc-references/asOperator4_es5.1.normal.js
index 3b53c29f83..baa8c6d855 100644
--- a/crates/swc/tests/tsc-references/asOperator4_es5.1.normal.js
+++ b/crates/swc/tests/tsc-references/asOperator4_es5.1.normal.js
@@ -1,5 +1,6 @@
 //@module: commonjs
 //@filename: foo.ts
 export function foo() {}
+// These should emit identically
 foo;
 foo;
diff --git a/crates/swc/tests/tsc-references/generatorAssignability_es5.1.normal.js b/crates/swc/tests/tsc-references/generatorAssignability_es5.1.normal.js
index e0c610d48a..7ac1f41255 100644
--- a/crates/swc/tests/tsc-references/generatorAssignability_es5.1.normal.js
+++ b/crates/swc/tests/tsc-references/generatorAssignability_es5.1.normal.js
@@ -206,6 +206,7 @@ function _wrapAsyncGenerator(fn) {
     };
 }
 var _marked = regeneratorRuntime.mark(f1);
+// spread iterable
 _toConsumableArray(g1); // error
 _toConsumableArray(g2); // ok
 // binding pattern over iterable
@@ -215,10 +216,12 @@ var _g2 = _slicedToArray(g2, 1), x2 = _g2[0]; // ok
 var _g11 = _toArray(g1), y1 = _g11.slice(0); // error
 var _g21 = _toArray(g2), y2 = _g21.slice(0); // ok
 var ref;
+// assignment pattern over iterable
 ref = _slicedToArray(g1, 1), _ = ref[0], ref; // error
 var ref1;
 ref1 = _slicedToArray(g2, 1), _ = ref1[0], ref1; // ok
 var ref2;
+// assignment rest pattern over iterable
 ref2 = _toArray(g1), _ = ref2.slice(0), ref2; // error
 var ref3;
 ref3 = _toArray(g2), _ = ref3.slice(0), ref3; // ok
diff --git a/crates/swc/tests/tsc-references/noUncheckedIndexedAccessDestructuring_es5.1.normal.js b/crates/swc/tests/tsc-references/noUncheckedIndexedAccessDestructuring_es5.1.normal.js
index 316b935f76..8a8d5ef558 100644
--- a/crates/swc/tests/tsc-references/noUncheckedIndexedAccessDestructuring_es5.1.normal.js
+++ b/crates/swc/tests/tsc-references/noUncheckedIndexedAccessDestructuring_es5.1.normal.js
@@ -118,6 +118,7 @@ t2.z.toString(); // Should error
     q.y.toFixed(); // Should OK
     q.z.toFixed(); // Should error
 }var ref;
+// Assignment forms
 ref = _slicedToArray(strArray, 1), target_string = ref[0], ref; // Should error
 var ref1;
 ref1 = _slicedToArray(strArray, 1), target_string_undef = ref1[0], ref1; // Should OK
diff --git a/crates/swc/tests/tsc-references/templateStringInTaggedTemplateES6_es5.1.normal.js b/crates/swc/tests/tsc-references/templateStringInTaggedTemplateES6_es5.1.normal.js
index 9abbe4ce8b..f76e625ccb 100644
--- a/crates/swc/tests/tsc-references/templateStringInTaggedTemplateES6_es5.1.normal.js
+++ b/crates/swc/tests/tsc-references/templateStringInTaggedTemplateES6_es5.1.normal.js
@@ -18,4 +18,5 @@ function _templateObject() {
     };
     return data;
 }
+// @target: ES6
 ("I AM THE TAG  PORTION")(_templateObject(), "AM");
diff --git a/crates/swc/tests/tsc-references/templateStringMultiline1_ES6_es5.1.normal.js b/crates/swc/tests/tsc-references/templateStringMultiline1_ES6_es5.1.normal.js
index bea0bc9437..17b9e3e73d 100644
--- a/crates/swc/tests/tsc-references/templateStringMultiline1_ES6_es5.1.normal.js
+++ b/crates/swc/tests/tsc-references/templateStringMultiline1_ES6_es5.1.normal.js
@@ -1,1 +1,3 @@
+//@target: es6
+// newlines are <CR><LF>
 "\n";
diff --git a/crates/swc/tests/tsc-references/templateStringMultiline1_es5.1.normal.js b/crates/swc/tests/tsc-references/templateStringMultiline1_es5.1.normal.js
index bea0bc9437..f691d435f7 100644
--- a/crates/swc/tests/tsc-references/templateStringMultiline1_es5.1.normal.js
+++ b/crates/swc/tests/tsc-references/templateStringMultiline1_es5.1.normal.js
@@ -1,1 +1,2 @@
+// newlines are <CR><LF>
 "\n";
diff --git a/crates/swc/tests/tsc-references/templateStringMultiline2_ES6_es5.1.normal.js b/crates/swc/tests/tsc-references/templateStringMultiline2_ES6_es5.1.normal.js
index bea0bc9437..d689fe3dbb 100644
--- a/crates/swc/tests/tsc-references/templateStringMultiline2_ES6_es5.1.normal.js
+++ b/crates/swc/tests/tsc-references/templateStringMultiline2_ES6_es5.1.normal.js
@@ -1,1 +1,3 @@
+//@target: es6
+// newlines are <LF>
 "\n";
diff --git a/crates/swc/tests/tsc-references/templateStringMultiline2_es5.1.normal.js b/crates/swc/tests/tsc-references/templateStringMultiline2_es5.1.normal.js
index bea0bc9437..416a8e05ac 100644
--- a/crates/swc/tests/tsc-references/templateStringMultiline2_es5.1.normal.js
+++ b/crates/swc/tests/tsc-references/templateStringMultiline2_es5.1.normal.js
@@ -1,1 +1,2 @@
+// newlines are <LF>
 "\n";
diff --git a/crates/swc/tests/tsc-references/templateStringPlainCharactersThatArePartsOfEscapes01_ES6_es5.1.normal.js b/crates/swc/tests/tsc-references/templateStringPlainCharactersThatArePartsOfEscapes01_ES6_es5.1.normal.js
index 495378af43..6b3fcf312e 100644
--- a/crates/swc/tests/tsc-references/templateStringPlainCharactersThatArePartsOfEscapes01_ES6_es5.1.normal.js
+++ b/crates/swc/tests/tsc-references/templateStringPlainCharactersThatArePartsOfEscapes01_ES6_es5.1.normal.js
@@ -1,1 +1,2 @@
+// @target: es6
 "0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 2028 2029 0085 t v f b r n";
diff --git a/crates/swc/tests/tsc-references/templateStringPlainCharactersThatArePartsOfEscapes02_ES6_es5.1.normal.js b/crates/swc/tests/tsc-references/templateStringPlainCharactersThatArePartsOfEscapes02_ES6_es5.1.normal.js
index 495378af43..6b3fcf312e 100644
--- a/crates/swc/tests/tsc-references/templateStringPlainCharactersThatArePartsOfEscapes02_ES6_es5.1.normal.js
+++ b/crates/swc/tests/tsc-references/templateStringPlainCharactersThatArePartsOfEscapes02_ES6_es5.1.normal.js
@@ -1,1 +1,2 @@
+// @target: es6
 "0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 2028 2029 0085 t v f b r n";
diff --git a/crates/swc/tests/tsc-references/templateStringTermination1_ES6_es5.1.normal.js b/crates/swc/tests/tsc-references/templateStringTermination1_ES6_es5.1.normal.js
index d5a4e58e5c..2abf781547 100644
--- a/crates/swc/tests/tsc-references/templateStringTermination1_ES6_es5.1.normal.js
+++ b/crates/swc/tests/tsc-references/templateStringTermination1_ES6_es5.1.normal.js
@@ -1,1 +1,2 @@
+// @target: ES6
 "";
diff --git a/crates/swc/tests/tsc-references/templateStringTermination2_ES6_es5.1.normal.js b/crates/swc/tests/tsc-references/templateStringTermination2_ES6_es5.1.normal.js
index 2087256728..5b4e5fc349 100644
--- a/crates/swc/tests/tsc-references/templateStringTermination2_ES6_es5.1.normal.js
+++ b/crates/swc/tests/tsc-references/templateStringTermination2_ES6_es5.1.normal.js
@@ -1,1 +1,2 @@
+// @target: ES6
 "\\";
diff --git a/crates/swc/tests/tsc-references/templateStringTermination3_ES6_es5.1.normal.js b/crates/swc/tests/tsc-references/templateStringTermination3_ES6_es5.1.normal.js
index c7128984d6..753de4e43e 100644
--- a/crates/swc/tests/tsc-references/templateStringTermination3_ES6_es5.1.normal.js
+++ b/crates/swc/tests/tsc-references/templateStringTermination3_ES6_es5.1.normal.js
@@ -1,1 +1,2 @@
+// @target: ES6
 "`";
diff --git a/crates/swc/tests/tsc-references/templateStringTermination4_ES6_es5.1.normal.js b/crates/swc/tests/tsc-references/templateStringTermination4_ES6_es5.1.normal.js
index a442783619..575f1701af 100644
--- a/crates/swc/tests/tsc-references/templateStringTermination4_ES6_es5.1.normal.js
+++ b/crates/swc/tests/tsc-references/templateStringTermination4_ES6_es5.1.normal.js
@@ -1,1 +1,2 @@
+// @target: ES6
 "\\\\";
diff --git a/crates/swc/tests/tsc-references/templateStringTermination5_ES6_es5.1.normal.js b/crates/swc/tests/tsc-references/templateStringTermination5_ES6_es5.1.normal.js
index 4db7f340fe..0a2b476993 100644
--- a/crates/swc/tests/tsc-references/templateStringTermination5_ES6_es5.1.normal.js
+++ b/crates/swc/tests/tsc-references/templateStringTermination5_ES6_es5.1.normal.js
@@ -1,1 +1,2 @@
+// @target: ES6
 "\\\\\\";
diff --git a/crates/swc/tests/tsc-references/templateStringWhitespaceEscapes1_ES6_es5.1.normal.js b/crates/swc/tests/tsc-references/templateStringWhitespaceEscapes1_ES6_es5.1.normal.js
index ce43695559..07a32b0935 100644
--- a/crates/swc/tests/tsc-references/templateStringWhitespaceEscapes1_ES6_es5.1.normal.js
+++ b/crates/swc/tests/tsc-references/templateStringWhitespaceEscapes1_ES6_es5.1.normal.js
@@ -1,1 +1,2 @@
+//@target: es6
 "\t\n\v\f\r";
diff --git a/crates/swc/tests/tsc-references/templateStringWhitespaceEscapes2_ES6_es5.1.normal.js b/crates/swc/tests/tsc-references/templateStringWhitespaceEscapes2_ES6_es5.1.normal.js
index 7a0fc8670e..f45ffb9a6a 100644
--- a/crates/swc/tests/tsc-references/templateStringWhitespaceEscapes2_ES6_es5.1.normal.js
+++ b/crates/swc/tests/tsc-references/templateStringWhitespaceEscapes2_ES6_es5.1.normal.js
@@ -1,1 +1,3 @@
+//@target: es6
+// <TAB>, <VT>, <FF>, <SP>, <NBSP>, <BOM>
 "\t\v\f \xa0﻿";
diff --git a/crates/swc/tests/tsc-references/templateStringWhitespaceEscapes2_es5.1.normal.js b/crates/swc/tests/tsc-references/templateStringWhitespaceEscapes2_es5.1.normal.js
index 7a0fc8670e..89f96afaa8 100644
--- a/crates/swc/tests/tsc-references/templateStringWhitespaceEscapes2_es5.1.normal.js
+++ b/crates/swc/tests/tsc-references/templateStringWhitespaceEscapes2_es5.1.normal.js
@@ -1,1 +1,2 @@
+// <TAB>, <VT>, <FF>, <SP>, <NBSP>, <BOM>
 "\t\v\f \xa0﻿";