Skip to content

Commit 18519de

Browse files
committed
refactor(syntax): remove ModuleRecord::export_default (#7578)
This value can be derived.
1 parent d476660 commit 18519de

8 files changed

Lines changed: 61 additions & 60 deletions

File tree

crates/oxc_linter/src/module_record.rs

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -464,7 +464,17 @@ impl ModuleRecord {
464464
.iter()
465465
.map(|(name, span)| (CompactStr::from(name.as_str()), *span))
466466
.collect(),
467-
export_default: other.export_default,
467+
export_default: other
468+
.local_export_entries
469+
.iter()
470+
.filter_map(|export_entry| export_entry.export_name.default_export_span())
471+
.chain(
472+
other
473+
.indirect_export_entries
474+
.iter()
475+
.filter_map(|export_entry| export_entry.export_name.default_export_span()),
476+
)
477+
.next(),
468478
..ModuleRecord::default()
469479
}
470480
}

crates/oxc_linter/src/rules/import/no_default_export.rs

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -49,9 +49,6 @@ impl Rule for NoDefaultExport {
4949
if let Some(span) = module_record.export_default {
5050
ctx.diagnostic(no_default_export_diagnostic(span));
5151
}
52-
if let Some(span) = module_record.exported_bindings.get("default") {
53-
ctx.diagnostic(no_default_export_diagnostic(*span));
54-
}
5552
}
5653
}
5754

crates/oxc_parser/src/lib.rs

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -438,20 +438,21 @@ impl<'a> ParserImpl<'a> {
438438
errors.push(error);
439439
}
440440
}
441+
let (module_record, module_record_errors) = self.module_record_builder.build();
441442
if errors.len() != 1 {
442443
errors.reserve(self.lexer.errors.len() + self.errors.len());
443444
errors.extend(self.lexer.errors);
444445
errors.extend(self.errors);
445446
// Skip checking for exports in TypeScript {
446447
if !self.source_type.is_typescript() {
447-
errors.extend(self.module_record_builder.errors());
448+
errors.extend(module_record_errors);
448449
}
449450
}
450451
let irregular_whitespaces =
451452
self.lexer.trivia_builder.irregular_whitespaces.into_boxed_slice();
452453
ParserReturn {
453454
program,
454-
module_record: self.module_record_builder.build(),
455+
module_record,
455456
errors,
456457
irregular_whitespaces,
457458
panicked,

crates/oxc_parser/src/module_record.rs

Lines changed: 21 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,6 @@ pub struct ModuleRecordBuilder<'a> {
1111
allocator: &'a Allocator,
1212
module_record: ModuleRecord<'a>,
1313
export_entries: Vec<ExportEntry<'a>>,
14-
export_default_duplicated: Vec<Span>,
1514
exported_bindings_duplicated: Vec<NameSpan<'a>>,
1615
}
1716

@@ -21,16 +20,16 @@ impl<'a> ModuleRecordBuilder<'a> {
2120
allocator,
2221
module_record: ModuleRecord::new(allocator),
2322
export_entries: vec![],
24-
export_default_duplicated: vec![],
2523
exported_bindings_duplicated: vec![],
2624
}
2725
}
2826

29-
pub fn build(mut self) -> ModuleRecord<'a> {
27+
pub fn build(mut self) -> (ModuleRecord<'a>, Vec<OxcDiagnostic>) {
3028
// The `ParseModule` algorithm requires `importedBoundNames` (import entries) to be
3129
// resolved before resolving export entries.
3230
self.resolve_export_entries();
33-
self.module_record
31+
let errors = self.errors();
32+
(self.module_record, errors)
3433
}
3534

3635
pub fn errors(&self) -> Vec<OxcDiagnostic> {
@@ -44,19 +43,25 @@ impl<'a> ModuleRecordBuilder<'a> {
4443
errors.push(diagnostics::duplicate_export(&name_span.name, name_span.span, old_span));
4544
}
4645

47-
for span in &self.export_default_duplicated {
48-
let old_span = module_record.export_default.unwrap();
49-
errors.push(diagnostics::duplicate_export("default", *span, old_span));
50-
}
51-
52-
// `export default x;`
53-
// `export { y as default };`
54-
if let (Some(span), Some(default_span)) =
55-
(module_record.exported_bindings.get("default"), &module_record.export_default)
56-
{
57-
errors.push(diagnostics::duplicate_export("default", *default_span, *span));
46+
// Multiple default exports
47+
// `export default foo`
48+
// `export { default }`
49+
let default_exports = module_record
50+
.local_export_entries
51+
.iter()
52+
.filter_map(|export_entry| export_entry.export_name.default_export_span())
53+
.chain(
54+
module_record
55+
.indirect_export_entries
56+
.iter()
57+
.filter_map(|export_entry| export_entry.export_name.default_export_span()),
58+
)
59+
.collect::<Vec<_>>();
60+
if default_exports.len() > 1 {
61+
errors.push(
62+
OxcDiagnostic::error("Duplicated default export").with_labels(default_exports),
63+
);
5864
}
59-
6065
errors
6166
}
6267

@@ -94,12 +99,6 @@ impl<'a> ModuleRecordBuilder<'a> {
9499
}
95100
}
96101

97-
fn add_default_export(&mut self, span: Span) {
98-
if let Some(old_node) = self.module_record.export_default.replace(span) {
99-
self.export_default_duplicated.push(old_node);
100-
}
101-
}
102-
103102
/// [ParseModule](https://tc39.es/ecma262/#sec-parsemodule)
104103
/// Step 10.
105104
fn resolve_export_entries(&mut self) {
@@ -264,8 +263,6 @@ impl<'a> ModuleRecordBuilder<'a> {
264263
return;
265264
}
266265
let exported_name = &decl.exported;
267-
let exported_name_span = decl.exported.span();
268-
self.add_default_export(exported_name_span);
269266

270267
let local_name = match &decl.declaration {
271268
ExportDefaultDeclarationKind::Identifier(ident) => {

crates/oxc_syntax/src/module_record.rs

Lines changed: 11 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -55,10 +55,6 @@ pub struct ModuleRecord<'a> {
5555

5656
/// Local exported bindings
5757
pub exported_bindings: FxHashMap<Atom<'a>, Span>,
58-
59-
/// `export default name`
60-
/// ^^^^^^^ span
61-
pub export_default: Option<Span>,
6258
}
6359

6460
impl<'a> ModuleRecord<'a> {
@@ -72,7 +68,6 @@ impl<'a> ModuleRecord<'a> {
7268
indirect_export_entries: Vec::new_in(allocator),
7369
star_export_entries: Vec::new_in(allocator),
7470
exported_bindings: FxHashMap::default(),
75-
export_default: None,
7671
}
7772
}
7873
}
@@ -287,6 +282,17 @@ impl ExportExportName<'_> {
287282
Self::Null => None,
288283
}
289284
}
285+
286+
/// Get default export span
287+
/// `export default foo`
288+
/// `export { default }`
289+
pub fn default_export_span(&self) -> Option<Span> {
290+
match self {
291+
Self::Default(span) => Some(*span),
292+
Self::Name(name_span) if name_span.name == "default" => Some(name_span.span),
293+
_ => None,
294+
}
295+
}
290296
}
291297

292298
/// `LocalName` for `ExportEntry`

tasks/coverage/snapshots/parser_babel.snap

Lines changed: 6 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -3031,24 +3031,20 @@ Expect to Parse: tasks/coverage/babel/packages/babel-parser/test/fixtures/typesc
30313031
· ───────────
30323032
╰────
30333033

3034-
× Duplicated export 'default'
3034+
× Duplicated default export
30353035
╭─[babel/packages/babel-parser/test/fixtures/es2015/modules/duplicate-export-default/input.js:1:8]
30363036
1 │ export default {};
3037-
· ───┬───
3038-
· ╰── Export has already been declared here
3037+
· ───────
30393038
2 │ export default function() {};
3040-
· ───┬───
3041-
· ╰── It cannot be redeclared here
3039+
· ───────
30423040
╰────
30433041

3044-
× Duplicated export 'default'
3042+
× Duplicated default export
30453043
╭─[babel/packages/babel-parser/test/fixtures/es2015/modules/duplicate-export-default-and-export-as-default/input.js:1:8]
30463044
1 │ export default function() {};
3047-
· ───┬───
3048-
· ╰── Export has already been declared here
3045+
· ───────
30493046
2 │ export { foo as default };
3050-
· ───┬───
3051-
· ╰── It cannot be redeclared here
3047+
· ───────
30523048
╰────
30533049

30543050
× Export 'foo' is not defined

tasks/coverage/snapshots/parser_test262.snap

Lines changed: 6 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -22158,15 +22158,13 @@ Expect Syntax Error: tasks/coverage/test262/test/language/import/import-attribut
2215822158
· ╰── It can not be redeclared here
2215922159
╰────
2216022160

22161-
× Duplicated export 'default'
22161+
× Duplicated default export
2216222162
╭─[test262/test/language/module-code/early-dup-export-dflt-id.js:18:8]
2216322163
17 │ var x, y;
2216422164
18 │ export default x;
22165-
· ───┬───
22166-
· ╰── Export has already been declared here
22165+
· ───────
2216722166
19 │ export { y as default };
22168-
· ───┬───
22169-
· ╰── It cannot be redeclared here
22167+
· ───────
2217022168
╰────
2217122169

2217222170
× Unexpected token
@@ -22199,15 +22197,13 @@ Expect Syntax Error: tasks/coverage/test262/test/language/import/import-attribut
2219922197
· ╰── It cannot be redeclared here
2220022198
╰────
2220122199

22202-
× Duplicated export 'default'
22200+
× Duplicated default export
2220322201
╭─[test262/test/language/module-code/early-dup-export-star-as-dflt.js:18:8]
2220422202
17 │ var x;
2220522203
18 │ export default x;
22206-
· ───┬───
22207-
· ╰── Export has already been declared here
22204+
· ───────
2220822205
19 │ export * as default from './early-dup-export-start-as-dflt.js';
22209-
· ───┬───
22210-
· ╰── It cannot be redeclared here
22206+
· ───────
2221122207
╰────
2221222208

2221322209
× Label `label` has already been declared

tasks/coverage/snapshots/parser_typescript.snap

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -22418,14 +22418,12 @@ Expect to Parse: tasks/coverage/typescript/tests/cases/conformance/salsa/private
2241822418
13 │ deleted() {
2241922419
╰────
2242022420

22421-
× Duplicated export 'default'
22421+
× Duplicated default export
2242222422
╭─[typescript/tests/cases/conformance/salsa/plainJSBinderErrors.ts:1:8]
2242322423
1 │ export default 12
22424-
· ───┬───
22425-
· ╰── Export has already been declared here
22424+
· ───────
2242622425
2 │ export default 13
22427-
· ───┬───
22428-
· ╰── It cannot be redeclared here
22426+
· ───────
2242922427
3 │ const await = 1
2243022428
╰────
2243122429

0 commit comments

Comments
 (0)