feat(parser): more friendly error for duplicate import attribute key (#28262)

The message felt a bit confusing for me.

**Before**
```
  × Identifier `type` has already been declared
   ╭─[babel/packages/babel-parser/test/fixtures/es2025/import-attributes/invalid-syntax-export-with-repeated-type/input.js:1:50]
 1 │ export { default as foo } from "foo.json" with { type: "json", type: "html" };
   ·                                                  ──┬─          ──┬─
   ·                                                    │             ╰── It can not be redeclared here
   ·                                                    ╰── `type` has already been declared here
   ╰────
```

**After**
```
  × Duplicate import attribute key `type`
   ╭─[babel/packages/babel-parser/test/fixtures/es2025/import-attributes/invalid-syntax-export-with-repeated-type/input.js:1:50]
 1 │ export { default as foo } from "foo.json" with { type: "json", type: "html" };
   ·                                                  ──┬─          ──┬─
   ·                                                    │             ╰── This key is repeated
   ·                                                    ╰── `type` was first specified here
   ╰────
  help: Each import attribute key must be unique
```
This commit is contained in:
sapphi-red committed 2026-10-28 16:48:58 +00:00
1 parent 31cbe378d4
commit deed7dcf29
4 files changed
+38 -29

No files matched your search

+9 -7
View File
@@ -87,13 +87,6 @@ macro_rules! parser_diagnostics {
}
parser_diagnostics! {
redeclaration(name: &'a str, declare_span: Span, redeclare_span: Span) => {
OxcDiagnostic::error(format!("Identifier `{name}` has already been declared")).with_labels([
declare_span.label(format!("`{name}` has already been declared here")),
redeclare_span.label("It can not be redeclared here"),
])
};
overlong_source() => {
OxcDiagnostic::error("Source length exceeds 4 GiB limit")
};
@@ -1359,6 +1352,15 @@ parser_diagnostics! {
.with_help("Wrap this with quotes")
};
duplicate_import_attribute_key(name: &'a str, previous_span: Span, duplicate_span: Span) => {
OxcDiagnostic::error(format!("Duplicate import attribute key `{name}`"))
.with_labels([
previous_span.label(format!("`{name}` was already specified here")),
duplicate_span.label("This key is repeated"),
])
.with_help("Each import attribute key must be unique")
};
// TS18058
default_import_not_allowed_in_defer(span: Span) => {
ts_error("18058", "Default imports are not allowed in a deferred import.").with_label(span)
+1 -1
View File
@@ -392,7 +392,7 @@ impl<'a, C: Config> ParserImpl<'a, C> {
let span = e.key.span();
if let Some(old_span) = keys.insert(key, span) {
let name = key.as_str().unwrap_or_else(|| span.source_text(self.source_text));
self.error(diagnostics::redeclaration(name, old_span, span));
self.error(diagnostics::duplicate_import_attribute_key(name, old_span, span));
}
}
+16 -12
View File
@@ -9493,13 +9493,14 @@ Expect to Parse: tasks/coverage/babel/packages/babel-parser/test/fixtures/typesc
· ───
╰────
× Identifier `type` has already been declared
× Duplicate import attribute key `type`
╭─[babel/packages/babel-parser/test/fixtures/es2025/import-attributes/invalid-syntax-export-with-repeated-type/input.js:1:50]
1 │ export { default as foo } from "foo.json" with { type: "json", type: "html" };
· ──┬─ ──┬─
· │ ╰── It can not be redeclared here
· ╰── `type` has already been declared here
· │ ╰── This key is repeated
· ╰── `type` was already specified here
╰────
help: Each import attribute key must be unique
× Expected `{` but found `;`
╭─[babel/packages/babel-parser/test/fixtures/es2025/import-attributes/invalid-syntax-export-without-attributes-identifier/input.js:1:29]
@@ -9508,29 +9509,32 @@ Expect to Parse: tasks/coverage/babel/packages/babel-parser/test/fixtures/typesc
· ╰── `{` expected
╰────
× Identifier `type` has already been declared
× Duplicate import attribute key `type`
╭─[babel/packages/babel-parser/test/fixtures/es2025/import-attributes/invalid-syntax-with-repeated-type/input.js:1:35]
1 │ import foo from "foo.json" with { type: "json", type: "html" };
· ──┬─ ──┬─
· │ ╰── It can not be redeclared here
· ╰── `type` has already been declared here
· │ ╰── This key is repeated
· ╰── `type` was already specified here
╰────
help: Each import attribute key must be unique
× Identifier `type` has already been declared
× Duplicate import attribute key `type`
╭─[babel/packages/babel-parser/test/fixtures/es2025/import-attributes/invalid-syntax-with-repeated-type-string/input.js:1:35]
1 │ import foo from "foo.json" with { "type": "json", type: "html", "type": "js" };
· ───┬── ──┬─
· │ ╰── It can not be redeclared here
· ╰── `type` has already been declared here
· │ ╰── This key is repeated
· ╰── `type` was already specified here
╰────
help: Each import attribute key must be unique
× Identifier `type` has already been declared
× Duplicate import attribute key `type`
╭─[babel/packages/babel-parser/test/fixtures/es2025/import-attributes/invalid-syntax-with-repeated-type-string/input.js:1:51]
1 │ import foo from "foo.json" with { "type": "json", type: "html", "type": "js" };
· ──┬─ ───┬──
· │ ╰── It can not be redeclared here
· ╰── `type` has already been declared here
· │ ╰── This key is repeated
· ╰── `type` was already specified here
╰────
help: Each import attribute key must be unique
× Expected `{` but found `;`
╭─[babel/packages/babel-parser/test/fixtures/es2025/import-attributes/invalid-syntax-without-attributes-identifier/input.js:1:16]
+12 -9
View File
@@ -25811,41 +25811,44 @@ Negative Passed: 4653/4653 (100.00%)
22 │
╰────
× Identifier `type` has already been declared
× Duplicate import attribute key `type`
╭─[test262/test/language/module-code/import-attributes/early-dup-attribute-key-export.js:21:3]
20 │ export * from './import-attribute-3_FIXTURE.js' with {
21 │ type: 'json',
· ──┬─
· ╰── `type` has already been declared here
· ╰── `type` was already specified here
22 │ 'typ\u0065': ''
· ─────┬─────
· ╰── It can not be redeclared here
· ╰── This key is repeated
23 │ };
╰────
help: Each import attribute key must be unique
× Identifier `type` has already been declared
× Duplicate import attribute key `type`
╭─[test262/test/language/module-code/import-attributes/early-dup-attribute-key-import-nobinding.js:22:3]
21 │ import './import-attribute-2_FIXTURE.js' with {
22 │ type: 'json',
· ──┬─
· ╰── `type` has already been declared here
· ╰── `type` was already specified here
23 │ 'typ\u0065': ''
· ─────┬─────
· ╰── It can not be redeclared here
· ╰── This key is repeated
24 │ };
╰────
help: Each import attribute key must be unique
× Identifier `type` has already been declared
× Duplicate import attribute key `type`
╭─[test262/test/language/module-code/import-attributes/early-dup-attribute-key-import-withbinding.js:22:3]
21 │ import x from './import-attribute-1_FIXTURE.js' with {
22 │ type: 'json',
· ──┬─
· ╰── `type` has already been declared here
· ╰── `type` was already specified here
23 │ 'typ\u0065': ''
· ─────┬─────
· ╰── It can not be redeclared here
· ╰── This key is repeated
24 │ };
╰────
help: Each import attribute key must be unique
× Private identifier '#x' is not allowed outside class bodies
╭─[test262/test/language/module-code/invalid-private-names-call-expression-bad-reference.js:40:14]