Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions crates/oxc_semantic/src/checker/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ pub fn check<'a>(kind: AstKind<'a>, ctx: &SemanticBuilder<'a>) {
AstKind::Program(program) => {
js::check_duplicate_class_elements(ctx);
js::check_unresolved_exports(program, ctx);
ts::check_ts_export_assignment_in_program(program, ctx);
}
AstKind::BindingIdentifier(ident) => {
js::check_identifier(&ident.name, ident.span, ident.symbol_id.get(), ctx);
Expand Down
64 changes: 64 additions & 0 deletions crates/oxc_semantic/src/checker/typescript.rs
Original file line number Diff line number Diff line change
Expand Up @@ -157,6 +157,7 @@ fn not_allowed_namespace_declaration(span: Span) -> OxcDiagnostic {

pub fn check_ts_module_declaration<'a>(decl: &TSModuleDeclaration<'a>, ctx: &SemanticBuilder<'a>) {
check_ts_module_or_global_declaration(decl.span, ctx);
check_ts_export_assignment_in_module_decl(decl, ctx);
}

pub fn check_ts_global_declaration<'a>(decl: &TSGlobalDeclaration<'a>, ctx: &SemanticBuilder<'a>) {
Expand Down Expand Up @@ -449,3 +450,66 @@ pub fn check_jsx_expression_container(
ctx.error(jsx_expressions_may_not_use_the_comma_operator(container.expression.span()));
}
}

fn ts_export_assignment_cannot_be_used_with_other_exports(span: Span) -> OxcDiagnostic {
ts_error("2309", "An export assignment cannot be used in a module with other exported elements")
.with_label(span)
.with_help("If you want to use `export =`, remove other `export`s and put all of them to the right hand value of `export =`. If you want to use `export`s, remove `export =` statement.")
}

pub fn check_ts_export_assignment_in_program<'a>(program: &Program<'a>, ctx: &SemanticBuilder<'a>) {
if !ctx.source_type.is_typescript() {
return;
}
check_ts_export_assignment_in_statements(&program.body, ctx);
}

fn check_ts_export_assignment_in_module_decl<'a>(
module_decl: &TSModuleDeclaration<'a>,
ctx: &SemanticBuilder<'a>,
) {
let Some(body) = &module_decl.body else {
return;
};
match body {
TSModuleDeclarationBody::TSModuleDeclaration(nested) => {
check_ts_export_assignment_in_module_decl(nested, ctx);
}
TSModuleDeclarationBody::TSModuleBlock(block) => {
check_ts_export_assignment_in_statements(&block.body, ctx);
}
}
}

fn check_ts_export_assignment_in_statements<'a>(
statements: &[Statement<'a>],
ctx: &SemanticBuilder<'a>,
) {
let mut export_assignment_spans = vec![];
let mut has_other_exports = false;

for stmt in statements {
match stmt {
Statement::TSExportAssignment(export_assignment) => {
export_assignment_spans.push(export_assignment.span);
}
Statement::ExportNamedDeclaration(export_decl) => {
// ignore `export {}`
if export_decl.declaration.is_none() && export_decl.specifiers.is_empty() {
continue;
}
has_other_exports = true;
}
Statement::ExportDefaultDeclaration(_) | Statement::ExportAllDeclaration(_) => {
has_other_exports = true;
}
_ => {}
}
}

if has_other_exports {
for span in export_assignment_spans {
ctx.error(ts_export_assignment_cannot_be_used_with_other_exports(span));
}
}
}
5 changes: 5 additions & 0 deletions tasks/coverage/misc/fail/export-equal-with-normal-export.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
export const foo = 1;
export const bar = 2;

export = 3;
export = 4;
19 changes: 18 additions & 1 deletion tasks/coverage/snapshots/parser_misc.snap
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
parser_misc Summary:
AST Parsed : 52/52 (100.00%)
Positive Passed: 52/52 (100.00%)
Negative Passed: 120/120 (100.00%)
Negative Passed: 121/121 (100.00%)

× Cannot assign to 'arguments' in strict mode
╭─[misc/fail/arguments-eval.ts:1:10]
Expand Down Expand Up @@ -79,6 +79,23 @@ Negative Passed: 120/120 (100.00%)
╰────
help: for octal literals use the '0o' prefix instead

× TS(2309): An export assignment cannot be used in a module with other exported elements
╭─[misc/fail/export-equal-with-normal-export.ts:4:1]
3 │
4 │ export = 3;
· ───────────
5 │ export = 4;
╰────
help: If you want to use `export =`, remove other `export`s and put all of them to the right hand value of `export =`. If you want to use `export`s, remove `export =` statement.

× TS(2309): An export assignment cannot be used in a module with other exported elements
╭─[misc/fail/export-equal-with-normal-export.ts:5:1]
4 │ export = 3;
5 │ export = 4;
· ───────────
╰────
help: If you want to use `export =`, remove other `export`s and put all of them to the right hand value of `export =`. If you want to use `export`s, remove `export =` statement.

× Expected `,` or `]` but found `const`
╭─[misc/fail/imbalanced-array-expr.js:2:1]
1 │ const foo = [0, 1
Expand Down
Loading
Loading