refactor(syntax): remove ModuleRecord::export_default (#7578)

This value can be derived.
This commit is contained in:
Boshen 2024-12-02 09:17:57 +00:00
parent d476660b0b
commit 18519dea33
8 changed files with 61 additions and 60 deletions

View file

@ -464,7 +464,17 @@ impl ModuleRecord {
.iter() .iter()
.map(|(name, span)| (CompactStr::from(name.as_str()), *span)) .map(|(name, span)| (CompactStr::from(name.as_str()), *span))
.collect(), .collect(),
export_default: other.export_default, export_default: other
.local_export_entries
.iter()
.filter_map(|export_entry| export_entry.export_name.default_export_span())
.chain(
other
.indirect_export_entries
.iter()
.filter_map(|export_entry| export_entry.export_name.default_export_span()),
)
.next(),
..ModuleRecord::default() ..ModuleRecord::default()
} }
} }

View file

@ -49,9 +49,6 @@ impl Rule for NoDefaultExport {
if let Some(span) = module_record.export_default { if let Some(span) = module_record.export_default {
ctx.diagnostic(no_default_export_diagnostic(span)); ctx.diagnostic(no_default_export_diagnostic(span));
} }
if let Some(span) = module_record.exported_bindings.get("default") {
ctx.diagnostic(no_default_export_diagnostic(*span));
}
} }
} }

View file

@ -438,20 +438,21 @@ impl<'a> ParserImpl<'a> {
errors.push(error); errors.push(error);
} }
} }
let (module_record, module_record_errors) = self.module_record_builder.build();
if errors.len() != 1 { if errors.len() != 1 {
errors.reserve(self.lexer.errors.len() + self.errors.len()); errors.reserve(self.lexer.errors.len() + self.errors.len());
errors.extend(self.lexer.errors); errors.extend(self.lexer.errors);
errors.extend(self.errors); errors.extend(self.errors);
// Skip checking for exports in TypeScript { // Skip checking for exports in TypeScript {
if !self.source_type.is_typescript() { if !self.source_type.is_typescript() {
errors.extend(self.module_record_builder.errors()); errors.extend(module_record_errors);
} }
} }
let irregular_whitespaces = let irregular_whitespaces =
self.lexer.trivia_builder.irregular_whitespaces.into_boxed_slice(); self.lexer.trivia_builder.irregular_whitespaces.into_boxed_slice();
ParserReturn { ParserReturn {
program, program,
module_record: self.module_record_builder.build(), module_record,
errors, errors,
irregular_whitespaces, irregular_whitespaces,
panicked, panicked,

View file

@ -11,7 +11,6 @@ pub struct ModuleRecordBuilder<'a> {
allocator: &'a Allocator, allocator: &'a Allocator,
module_record: ModuleRecord<'a>, module_record: ModuleRecord<'a>,
export_entries: Vec<ExportEntry<'a>>, export_entries: Vec<ExportEntry<'a>>,
export_default_duplicated: Vec<Span>,
exported_bindings_duplicated: Vec<NameSpan<'a>>, exported_bindings_duplicated: Vec<NameSpan<'a>>,
} }
@ -21,16 +20,16 @@ impl<'a> ModuleRecordBuilder<'a> {
allocator, allocator,
module_record: ModuleRecord::new(allocator), module_record: ModuleRecord::new(allocator),
export_entries: vec![], export_entries: vec![],
export_default_duplicated: vec![],
exported_bindings_duplicated: vec![], exported_bindings_duplicated: vec![],
} }
} }
pub fn build(mut self) -> ModuleRecord<'a> { pub fn build(mut self) -> (ModuleRecord<'a>, Vec<OxcDiagnostic>) {
// The `ParseModule` algorithm requires `importedBoundNames` (import entries) to be // The `ParseModule` algorithm requires `importedBoundNames` (import entries) to be
// resolved before resolving export entries. // resolved before resolving export entries.
self.resolve_export_entries(); self.resolve_export_entries();
self.module_record let errors = self.errors();
(self.module_record, errors)
} }
pub fn errors(&self) -> Vec<OxcDiagnostic> { pub fn errors(&self) -> Vec<OxcDiagnostic> {
@ -44,19 +43,25 @@ impl<'a> ModuleRecordBuilder<'a> {
errors.push(diagnostics::duplicate_export(&name_span.name, name_span.span, old_span)); errors.push(diagnostics::duplicate_export(&name_span.name, name_span.span, old_span));
} }
for span in &self.export_default_duplicated { // Multiple default exports
let old_span = module_record.export_default.unwrap(); // `export default foo`
errors.push(diagnostics::duplicate_export("default", *span, old_span)); // `export { default }`
let default_exports = module_record
.local_export_entries
.iter()
.filter_map(|export_entry| export_entry.export_name.default_export_span())
.chain(
module_record
.indirect_export_entries
.iter()
.filter_map(|export_entry| export_entry.export_name.default_export_span()),
)
.collect::<Vec<_>>();
if default_exports.len() > 1 {
errors.push(
OxcDiagnostic::error("Duplicated default export").with_labels(default_exports),
);
} }
// `export default x;`
// `export { y as default };`
if let (Some(span), Some(default_span)) =
(module_record.exported_bindings.get("default"), &module_record.export_default)
{
errors.push(diagnostics::duplicate_export("default", *default_span, *span));
}
errors errors
} }
@ -94,12 +99,6 @@ impl<'a> ModuleRecordBuilder<'a> {
} }
} }
fn add_default_export(&mut self, span: Span) {
if let Some(old_node) = self.module_record.export_default.replace(span) {
self.export_default_duplicated.push(old_node);
}
}
/// [ParseModule](https://tc39.es/ecma262/#sec-parsemodule) /// [ParseModule](https://tc39.es/ecma262/#sec-parsemodule)
/// Step 10. /// Step 10.
fn resolve_export_entries(&mut self) { fn resolve_export_entries(&mut self) {
@ -264,8 +263,6 @@ impl<'a> ModuleRecordBuilder<'a> {
return; return;
} }
let exported_name = &decl.exported; let exported_name = &decl.exported;
let exported_name_span = decl.exported.span();
self.add_default_export(exported_name_span);
let local_name = match &decl.declaration { let local_name = match &decl.declaration {
ExportDefaultDeclarationKind::Identifier(ident) => { ExportDefaultDeclarationKind::Identifier(ident) => {

View file

@ -55,10 +55,6 @@ pub struct ModuleRecord<'a> {
/// Local exported bindings /// Local exported bindings
pub exported_bindings: FxHashMap<Atom<'a>, Span>, pub exported_bindings: FxHashMap<Atom<'a>, Span>,
/// `export default name`
/// ^^^^^^^ span
pub export_default: Option<Span>,
} }
impl<'a> ModuleRecord<'a> { impl<'a> ModuleRecord<'a> {
@ -72,7 +68,6 @@ impl<'a> ModuleRecord<'a> {
indirect_export_entries: Vec::new_in(allocator), indirect_export_entries: Vec::new_in(allocator),
star_export_entries: Vec::new_in(allocator), star_export_entries: Vec::new_in(allocator),
exported_bindings: FxHashMap::default(), exported_bindings: FxHashMap::default(),
export_default: None,
} }
} }
} }
@ -287,6 +282,17 @@ impl ExportExportName<'_> {
Self::Null => None, Self::Null => None,
} }
} }
/// Get default export span
/// `export default foo`
/// `export { default }`
pub fn default_export_span(&self) -> Option<Span> {
match self {
Self::Default(span) => Some(*span),
Self::Name(name_span) if name_span.name == "default" => Some(name_span.span),
_ => None,
}
}
} }
/// `LocalName` for `ExportEntry` /// `LocalName` for `ExportEntry`

View file

@ -3031,24 +3031,20 @@ Expect to Parse: tasks/coverage/babel/packages/babel-parser/test/fixtures/typesc
· ─────────── · ───────────
╰──── ╰────
× Duplicated export 'default' × Duplicated default export
╭─[babel/packages/babel-parser/test/fixtures/es2015/modules/duplicate-export-default/input.js:1:8] ╭─[babel/packages/babel-parser/test/fixtures/es2015/modules/duplicate-export-default/input.js:1:8]
1 │ export default {}; 1 │ export default {};
· ───┬─── · ───────
· ╰── Export has already been declared here
2 │ export default function() {}; 2 │ export default function() {};
· ───┬─── · ───────
· ╰── It cannot be redeclared here
╰──── ╰────
× Duplicated export 'default' × Duplicated default export
╭─[babel/packages/babel-parser/test/fixtures/es2015/modules/duplicate-export-default-and-export-as-default/input.js:1:8] ╭─[babel/packages/babel-parser/test/fixtures/es2015/modules/duplicate-export-default-and-export-as-default/input.js:1:8]
1 │ export default function() {}; 1 │ export default function() {};
· ───┬─── · ───────
· ╰── Export has already been declared here
2 │ export { foo as default }; 2 │ export { foo as default };
· ───┬─── · ───────
· ╰── It cannot be redeclared here
╰──── ╰────
× Export 'foo' is not defined × Export 'foo' is not defined

View file

@ -22158,15 +22158,13 @@ Expect Syntax Error: tasks/coverage/test262/test/language/import/import-attribut
· ╰── It can not be redeclared here · ╰── It can not be redeclared here
╰──── ╰────
× Duplicated export 'default' × Duplicated default export
╭─[test262/test/language/module-code/early-dup-export-dflt-id.js:18:8] ╭─[test262/test/language/module-code/early-dup-export-dflt-id.js:18:8]
17 │ var x, y; 17 │ var x, y;
18 │ export default x; 18 │ export default x;
· ───┬─── · ───────
· ╰── Export has already been declared here
19 │ export { y as default }; 19 │ export { y as default };
· ───┬─── · ───────
· ╰── It cannot be redeclared here
╰──── ╰────
× Unexpected token × Unexpected token
@ -22199,15 +22197,13 @@ Expect Syntax Error: tasks/coverage/test262/test/language/import/import-attribut
· ╰── It cannot be redeclared here · ╰── It cannot be redeclared here
╰──── ╰────
× Duplicated export 'default' × Duplicated default export
╭─[test262/test/language/module-code/early-dup-export-star-as-dflt.js:18:8] ╭─[test262/test/language/module-code/early-dup-export-star-as-dflt.js:18:8]
17 │ var x; 17 │ var x;
18 │ export default x; 18 │ export default x;
· ───┬─── · ───────
· ╰── Export has already been declared here
19 │ export * as default from './early-dup-export-start-as-dflt.js'; 19 │ export * as default from './early-dup-export-start-as-dflt.js';
· ───┬─── · ───────
· ╰── It cannot be redeclared here
╰──── ╰────
× Label `label` has already been declared × Label `label` has already been declared

View file

@ -22418,14 +22418,12 @@ Expect to Parse: tasks/coverage/typescript/tests/cases/conformance/salsa/private
13 │ deleted() { 13 │ deleted() {
╰──── ╰────
× Duplicated export 'default' × Duplicated default export
╭─[typescript/tests/cases/conformance/salsa/plainJSBinderErrors.ts:1:8] ╭─[typescript/tests/cases/conformance/salsa/plainJSBinderErrors.ts:1:8]
1 │ export default 12 1 │ export default 12
· ───┬─── · ───────
· ╰── Export has already been declared here
2 │ export default 13 2 │ export default 13
· ───┬─── · ───────
· ╰── It cannot be redeclared here
3 │ const await = 1 3 │ const await = 1
╰──── ╰────