Repository: swc-project/swc (rust)

Issue:
passing `export { default }` to the parser should throw an error
### Describe the bug

Keywords cannot be used in `export` but SWC parser doesn't reject them.


### Input code

```typescript
export { default }
export { if }
export { default as foo }
export { if as bar }
```


### Config

_No response_

### Playground link (or link to the minimal reproduction)

https://play.swc.rs/?version=1.3.97&code=H4sIAAAAAAAAA0utKMgvKlGoVkhJTUsszSlRqOXlSoWJZaahcGFKEosV0vLz0VUCRZMSi0CiACqxTW9UAAAA&config=H4sIAAAAAAAAA1VPOw7DIAzdOQXy3KFi6NA79BCIOhERBGQTqSjK3UsIpM3m9%2FOzVyElTGzgKdcyFhA1MdKJC8N5TvpTGEDjNRuyMcGtqxPv0qAdY6W2Q4GkacRUU6zuSrUEuBAYe6Jx3s52yP%2BdJvhIyHw17lY9jw6vjaK1gg%2FvpYrtl5QjHhc84GfqZedisPzqyUQLiu0LtK2NIRcBAAA%3D

### SWC Info output

_No response_

### Expected behavior

Throws an error like `Unexpected keyword "default"`.

### Actual behavior

No error happens.

### Version

1.3.97

### Additional context

This errors with most parsers on https://astexplorer.net/.

Pull request:
fix(es/parser): Report error for exported reserved name
**Related issue:**

- Closes #8268.


Patch:
diff --git a/crates/swc_ecma_parser/src/error.rs b/crates/swc_ecma_parser/src/error.rs
index eabcf2bd90..293295b77b 100644
--- a/crates/swc_ecma_parser/src/error.rs
+++ b/crates/swc_ecma_parser/src/error.rs
@@ -199,6 +199,7 @@ pub enum SyntaxError {
     TrailingCommaInsideImport,
 
     ExportDefaultWithOutFrom,
+    ExportExpectFrom(JsWord),
 
     DotsWithoutIdentifier,
 
@@ -504,6 +505,10 @@ impl SyntaxError {
                 "export default statements required from '...';".into()
             }
 
+            SyntaxError::ExportExpectFrom(s) => {
+                format!("`{}` cannot be used without `from` clause", s).into()
+            }
+
             SyntaxError::DotsWithoutIdentifier => {
                 "`...` must be followed by an identifier in declaration contexts".into()
             }
diff --git a/crates/swc_ecma_parser/src/parser/stmt/module_item.rs b/crates/swc_ecma_parser/src/parser/stmt/module_item.rs
index 173d9ed095..9bb7adfffc 100644
--- a/crates/swc_ecma_parser/src/parser/stmt/module_item.rs
+++ b/crates/swc_ecma_parser/src/parser/stmt/module_item.rs
@@ -634,12 +634,8 @@ impl<I: Tokens> Parser<I> {
 
             expect!(self, '{');
 
-            let mut string_export_binding_span = None;
             while !eof!(self) && !is!(self, '}') {
                 let specifier = self.parse_named_export_specifier(type_only)?;
-                if let ModuleExportName::Str(str_export) = &specifier.orig {
-                    string_export_binding_span = Some(str_export.span);
-                }
                 specifiers.push(ExportSpecifier::Named(specifier));
 
                 if is!(self, '}') {
@@ -653,17 +649,41 @@ impl<I: Tokens> Parser<I> {
             let opt = if is!(self, "from") {
                 Some(self.parse_from_clause_and_semi()?)
             } else {
-                eat!(self, ';');
-                if has_default || has_ns {
-                    syntax_error!(
-                        self,
-                        span!(self, start),
-                        SyntaxError::ExportDefaultWithOutFrom
-                    );
-                }
-                if let Some(span) = string_export_binding_span {
-                    syntax_error!(self, span, SyntaxError::ExportBindingIsString);
+                for s in &specifiers {
+                    match s {
+                        ExportSpecifier::Default(default) => {
+                            self.emit_err(
+                                default.exported.span,
+                                SyntaxError::ExportExpectFrom(default.exported.sym.clone()),
+                            );
+                        }
+                        ExportSpecifier::Namespace(namespace) => {
+                            let export_name = match &namespace.name {
+                                ModuleExportName::Ident(i) => i.sym.clone(),
+                                ModuleExportName::Str(s) => s.value.clone(),
+                            };
+                            self.emit_err(
+                                namespace.span,
+                                SyntaxError::ExportExpectFrom(export_name),
+                            );
+                        }
+                        ExportSpecifier::Named(named) => match &named.orig {
+                            ModuleExportName::Ident(id) if id.is_reserved() => {
+                                self.emit_err(
+                                    id.span,
+                                    SyntaxError::ExportExpectFrom(id.sym.clone()),
+                                );
+                            }
+                            ModuleExportName::Str(s) => {
+                                self.emit_err(s.span, SyntaxError::ExportBindingIsString);
+                            }
+                            _ => {}
+                        },
+                    }
                 }
+
+                eat!(self, ';');
+
                 None
             };
             let (src, with) = match opt {


Test patch:
diff --git a/crates/swc_ecma_parser/tests/errors/issue-8268/1/input.js b/crates/swc_ecma_parser/tests/errors/issue-8268/1/input.js
new file mode 100644
index 0000000000..94dbc4dade
--- /dev/null
+++ b/crates/swc_ecma_parser/tests/errors/issue-8268/1/input.js
@@ -0,0 +1,4 @@
+export { default }
+export { if }
+export { default as foo }
+export { if as bar }
diff --git a/crates/swc_ecma_parser/tests/errors/issue-8268/1/input.js.swc-stderr b/crates/swc_ecma_parser/tests/errors/issue-8268/1/input.js.swc-stderr
new file mode 100644
index 0000000000..56d22d75a7
--- /dev/null
+++ b/crates/swc_ecma_parser/tests/errors/issue-8268/1/input.js.swc-stderr
@@ -0,0 +1,30 @@
+
+  x `default` cannot be used without `from` clause
+   ,-[$DIR/tests/errors/issue-8268/1/input.js:1:1]
+ 1 | export { default }
+   :          ^^^^^^^
+ 2 | export { if }
+   `----
+
+  x `if` cannot be used without `from` clause
+   ,-[$DIR/tests/errors/issue-8268/1/input.js:1:1]
+ 1 | export { default }
+ 2 | export { if }
+   :          ^^
+ 3 | export { default as foo }
+   `----
+
+  x `default` cannot be used without `from` clause
+   ,-[$DIR/tests/errors/issue-8268/1/input.js:2:1]
+ 2 | export { if }
+ 3 | export { default as foo }
+   :          ^^^^^^^
+ 4 | export { if as bar }
+   `----
+
+  x `if` cannot be used without `from` clause
+   ,-[$DIR/tests/errors/issue-8268/1/input.js:3:1]
+ 3 | export { default as foo }
+ 4 | export { if as bar }
+   :          ^^
+   `----
diff --git a/crates/swc_ecma_parser/tests/errors/issue-8268/2/input.ts b/crates/swc_ecma_parser/tests/errors/issue-8268/2/input.ts
new file mode 100644
index 0000000000..c1fad01072
--- /dev/null
+++ b/crates/swc_ecma_parser/tests/errors/issue-8268/2/input.ts
@@ -0,0 +1,2 @@
+export { type export }
+export { type if }
diff --git a/crates/swc_ecma_parser/tests/errors/issue-8268/2/input.ts.swc-stderr b/crates/swc_ecma_parser/tests/errors/issue-8268/2/input.ts.swc-stderr
new file mode 100644
index 0000000000..f2e03fd821
--- /dev/null
+++ b/crates/swc_ecma_parser/tests/errors/issue-8268/2/input.ts.swc-stderr
@@ -0,0 +1,14 @@
+
+  x `export` cannot be used without `from` clause
+   ,-[$DIR/tests/errors/issue-8268/2/input.ts:1:1]
+ 1 | export { type export }
+   :               ^^^^^^
+ 2 | export { type if }
+   `----
+
+  x `if` cannot be used without `from` clause
+   ,-[$DIR/tests/errors/issue-8268/2/input.ts:1:1]
+ 1 | export { type export }
+ 2 | export { type if }
+   :               ^^
+   `----