Skip to content
Merged
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: 0 additions & 1 deletion crates/oxc_transformer/src/context.rs
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,6 @@ pub struct TransformCtx<'a> {

pub source_text: &'a str,

#[expect(unused)]
pub module: Module,

// Helpers
Expand Down
15 changes: 11 additions & 4 deletions crates/oxc_transformer/src/typescript/diagnostics.rs
Original file line number Diff line number Diff line change
@@ -1,14 +1,21 @@
use oxc_diagnostics::OxcDiagnostic;
use oxc_span::Span;

pub fn import_equals_require_unsupported(span: Span) -> OxcDiagnostic {
OxcDiagnostic::warn("`import lib = require(...);` is only supported when compiling modules to CommonJS.\nPlease consider using `import lib from '...';` alongside Typescript's --allowSyntheticDefaultImports option, or add @babel/plugin-transform-modules-commonjs to your Babel config.")
pub fn import_equals_cannot_be_used_in_esm(span: Span) -> OxcDiagnostic {
OxcDiagnostic::warn("Import assignment cannot be used when targeting ECMAScript modules.")
.with_help(
"Consider using 'import * as ns from \"mod\"',
'import {a} from \"mod\"', 'import d from \"mod\"', or another module format instead.",
)
.with_label(span)
.with_error_code("TS", "1202")
}

pub fn export_assignment_unsupported(span: Span) -> OxcDiagnostic {
OxcDiagnostic::warn("`export = <value>;` is only supported when compiling modules to CommonJS.\nPlease consider using `export default <value>;`, or add @babel/plugin-transform-modules-commonjs to your Babel config.")
pub fn export_assignment_cannot_bed_used_in_esm(span: Span) -> OxcDiagnostic {
OxcDiagnostic::warn("Export assignment cannot be used when targeting ECMAScript modules.")
.with_help("Consider using 'export default' or another module format instead.")
.with_label(span)
.with_error_code("TS", "1203")
}

pub fn ambient_module_nested(span: Span) -> OxcDiagnostic {
Expand Down
10 changes: 2 additions & 8 deletions crates/oxc_transformer/src/typescript/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,7 @@ impl<'a, 'ctx> Traverse<'a> for TypeScript<'a, 'ctx> {

fn exit_program(&mut self, program: &mut Program<'a>, ctx: &mut TraverseCtx<'a>) {
self.annotations.exit_program(program, ctx);
self.module.exit_program(program, ctx);
}

fn enter_arrow_function_expression(
Expand Down Expand Up @@ -215,6 +216,7 @@ impl<'a, 'ctx> Traverse<'a> for TypeScript<'a, 'ctx> {

fn enter_statement(&mut self, stmt: &mut Statement<'a>, ctx: &mut TraverseCtx<'a>) {
self.r#enum.enter_statement(stmt, ctx);
self.module.enter_statement(stmt, ctx);
}

fn exit_statement(&mut self, stmt: &mut Statement<'a>, ctx: &mut TraverseCtx<'a>) {
Expand Down Expand Up @@ -298,12 +300,4 @@ impl<'a, 'ctx> Traverse<'a> for TypeScript<'a, 'ctx> {
rewrite_extensions.enter_export_named_declaration(node, ctx);
}
}

fn enter_ts_export_assignment(
&mut self,
node: &mut TSExportAssignment<'a>,
ctx: &mut TraverseCtx<'a>,
) {
self.module.enter_ts_export_assignment(node, ctx);
}
}
128 changes: 82 additions & 46 deletions crates/oxc_transformer/src/typescript/module.rs
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
use oxc_ast::{ast::*, NONE};
use oxc_span::SPAN;
use oxc_span::{CompactStr, SPAN};
use oxc_syntax::reference::ReferenceFlags;
use oxc_traverse::{Traverse, TraverseCtx};

use super::diagnostics;
use crate::TransformCtx;

pub struct TypeScriptModule<'a, 'ctx> {
Expand All @@ -16,70 +17,105 @@ impl<'a, 'ctx> TypeScriptModule<'a, 'ctx> {
}

impl<'a, 'ctx> Traverse<'a> for TypeScriptModule<'a, 'ctx> {
/// ```TypeScript
/// import b = babel;
/// import AliasModule = LongNameModule;
///
/// ```JavaScript
/// var b = babel;
/// var AliasModule = LongNameModule;
/// ```
fn exit_program(&mut self, program: &mut Program<'a>, ctx: &mut TraverseCtx<'a>) {
// In Babel, it will insert `use strict` in `@babel/transform-modules-commonjs` plugin.
// Once we have a commonjs plugin, we can consider moving this logic there.
if self.ctx.module.is_commonjs() {
let has_use_strict = program.directives.iter().any(Directive::is_use_strict);
if !has_use_strict {
let use_strict = ctx.ast.string_literal(SPAN, "use strict");
program.directives.insert(0, ctx.ast.directive(SPAN, use_strict, "use strict"));
}
}
}

fn enter_statement(&mut self, stmt: &mut Statement<'a>, ctx: &mut TraverseCtx<'a>) {
if let Statement::TSExportAssignment(export_assignment) = stmt {
*stmt = self.transform_ts_export_assignment(export_assignment, ctx);
}
}

fn enter_declaration(&mut self, decl: &mut Declaration<'a>, ctx: &mut TraverseCtx<'a>) {
match decl {
Declaration::TSImportEqualsDeclaration(ts_import_equals)
if ts_import_equals.import_kind.is_value() =>
{
*decl = self.transform_ts_import_equals(ts_import_equals, ctx);
if let Declaration::TSImportEqualsDeclaration(import_equals) = decl {
if import_equals.import_kind.is_value() {
*decl = self.transform_ts_import_equals(import_equals, ctx);
}
_ => {}
}
}
}

fn enter_ts_export_assignment(
impl<'a, 'ctx> TypeScriptModule<'a, 'ctx> {
/// Transform `export = expression` to `module.exports = expression`.
fn transform_ts_export_assignment(
&mut self,
export_assignment: &mut TSExportAssignment<'a>,
_ctx: &mut TraverseCtx<'a>,
) {
if self.ctx.source_type.is_module() {
self.ctx
.error(super::diagnostics::export_assignment_unsupported(export_assignment.span));
ctx: &mut TraverseCtx<'a>,
) -> Statement<'a> {
if self.ctx.module.is_esm() {
self.ctx.error(diagnostics::export_assignment_cannot_bed_used_in_esm(
export_assignment.span,
));
}

// module.exports
let module_exports = {
let reference_id = ctx
.create_reference_in_current_scope(CompactStr::new("module"), ReferenceFlags::Read);
let reference =
ctx.ast.alloc_identifier_reference_with_reference_id(SPAN, "module", reference_id);
let object = Expression::Identifier(reference);
let property = ctx.ast.identifier_name(SPAN, "exports");
ctx.ast.member_expression_static(SPAN, object, property, false)
};

let left = AssignmentTarget::from(SimpleAssignmentTarget::from(module_exports));
let right = ctx.ast.move_expression(&mut export_assignment.expression);
let assignment_expr =
ctx.ast.expression_assignment(SPAN, AssignmentOperator::Assign, left, right);
ctx.ast.statement_expression(SPAN, assignment_expr)
}
}

impl<'a, 'ctx> TypeScriptModule<'a, 'ctx> {
/// Transform TSImportEqualsDeclaration to a VariableDeclaration.
///
/// ```TypeScript
/// import module = require('module');
/// import AliasModule = LongNameModule;
///
/// ```JavaScript
/// const module = require('module');
/// const AliasModule = LongNameModule;
/// ```
fn transform_ts_import_equals(
&self,
decl: &mut TSImportEqualsDeclaration<'a>,
ctx: &mut TraverseCtx<'a>,
) -> Declaration<'a> {
let kind = VariableDeclarationKind::Var;
let decls = {
let binding_pattern_kind =
ctx.ast.binding_pattern_kind_binding_identifier(SPAN, &decl.id.name);
let binding = ctx.ast.binding_pattern(binding_pattern_kind, NONE, false);
let decl_span = decl.span;
let binding_pattern_kind =
ctx.ast.binding_pattern_kind_binding_identifier(SPAN, &decl.id.name);
let binding = ctx.ast.binding_pattern(binding_pattern_kind, NONE, false);
let decl_span = decl.span;

let init = match &mut decl.module_reference {
type_name @ match_ts_type_name!(TSModuleReference) => {
self.transform_ts_type_name(&mut *type_name.to_ts_type_name_mut(), ctx)
let (kind, init) = match &mut decl.module_reference {
type_name @ match_ts_type_name!(TSModuleReference) => (
VariableDeclarationKind::Var,
self.transform_ts_type_name(&mut *type_name.to_ts_type_name_mut(), ctx),
),
TSModuleReference::ExternalModuleReference(reference) => {
if self.ctx.module.is_esm() {
self.ctx.error(diagnostics::import_equals_cannot_be_used_in_esm(decl_span));
}
TSModuleReference::ExternalModuleReference(reference) => {
if self.ctx.source_type.is_module() {
self.ctx.error(super::diagnostics::import_equals_require_unsupported(
decl_span,
));
}

let callee = ctx.ast.expression_identifier_reference(SPAN, "require");
let arguments = ctx
.ast
.vec1(Argument::StringLiteral(ctx.alloc(reference.expression.clone())));
ctx.ast.expression_call(SPAN, callee, NONE, arguments, false)
}
};
ctx.ast.vec1(ctx.ast.variable_declarator(SPAN, kind, binding, Some(init), false))
let callee = ctx.ast.expression_identifier_reference(SPAN, "require");
let arguments =
ctx.ast.vec1(Argument::StringLiteral(ctx.alloc(reference.expression.clone())));
(
VariableDeclarationKind::Const,
ctx.ast.expression_call(SPAN, callee, NONE, arguments, false),
)
}
};
let decls =
ctx.ast.vec1(ctx.ast.variable_declarator(SPAN, kind, binding, Some(init), false));

ctx.ast.declaration_variable(SPAN, kind, decls, false)
}
Expand Down
33 changes: 9 additions & 24 deletions tasks/coverage/snapshots/semantic_babel.snap
Original file line number Diff line number Diff line change
Expand Up @@ -423,8 +423,7 @@ after transform: ["Y", "foo"]
rebuilt : []

tasks/coverage/babel/packages/babel-parser/test/fixtures/estree/typescript/import-require/input.js
semantic error: `import lib = require(...);` is only supported when compiling modules to CommonJS.
Please consider using `import lib from '...';` alongside Typescript's --allowSyntheticDefaultImports option, or add @babel/plugin-transform-modules-commonjs to your Babel config.
semantic error: Import assignment cannot be used when targeting ECMAScript modules.

tasks/coverage/babel/packages/babel-parser/test/fixtures/estree/typescript/literals/input.js
semantic error: Bindings mismatch:
Expand Down Expand Up @@ -1101,12 +1100,10 @@ after transform: ScopeId(0): [ScopeId(1), ScopeId(2), ScopeId(3), ScopeId(4), Sc
rebuilt : ScopeId(0): []

tasks/coverage/babel/packages/babel-parser/test/fixtures/typescript/export/equals/input.ts
semantic error: `export = <value>;` is only supported when compiling modules to CommonJS.
Please consider using `export default <value>;`, or add @babel/plugin-transform-modules-commonjs to your Babel config.
semantic error: Export assignment cannot be used when targeting ECMAScript modules.

tasks/coverage/babel/packages/babel-parser/test/fixtures/typescript/export/equals-in-unambiguous/input.ts
semantic error: `export = <value>;` is only supported when compiling modules to CommonJS.
Please consider using `export default <value>;`, or add @babel/plugin-transform-modules-commonjs to your Babel config.
semantic error: Export assignment cannot be used when targeting ECMAScript modules.

tasks/coverage/babel/packages/babel-parser/test/fixtures/typescript/export/export-type/input.ts
semantic error: Bindings mismatch:
Expand Down Expand Up @@ -1226,18 +1223,10 @@ after transform: ScopeId(0): [SymbolId(0)]
rebuilt : ScopeId(0): [SymbolId(0)]

tasks/coverage/babel/packages/babel-parser/test/fixtures/typescript/import/equals-require/input.ts
semantic error: `import lib = require(...);` is only supported when compiling modules to CommonJS.
Please consider using `import lib from '...';` alongside Typescript's --allowSyntheticDefaultImports option, or add @babel/plugin-transform-modules-commonjs to your Babel config.
semantic error: Import assignment cannot be used when targeting ECMAScript modules.

tasks/coverage/babel/packages/babel-parser/test/fixtures/typescript/import/equals-require-in-unambiguous/input.ts
semantic error: Missing SymbolId: "a"
Missing ReferenceId: "require"
Binding symbols mismatch:
after transform: ScopeId(0): [SymbolId(0)]
rebuilt : ScopeId(0): [SymbolId(0)]
Unresolved references mismatch:
after transform: []
rebuilt : ["require"]
semantic error: Import assignment cannot be used when targeting ECMAScript modules.

tasks/coverage/babel/packages/babel-parser/test/fixtures/typescript/import/export-import/input.ts
semantic error: Missing SymbolId: "A"
Expand All @@ -1246,21 +1235,18 @@ after transform: ScopeId(0): [SymbolId(0)]
rebuilt : ScopeId(0): [SymbolId(0)]

tasks/coverage/babel/packages/babel-parser/test/fixtures/typescript/import/export-import-require/input.ts
semantic error: `import lib = require(...);` is only supported when compiling modules to CommonJS.
Please consider using `import lib from '...';` alongside Typescript's --allowSyntheticDefaultImports option, or add @babel/plugin-transform-modules-commonjs to your Babel config.
semantic error: Import assignment cannot be used when targeting ECMAScript modules.

tasks/coverage/babel/packages/babel-parser/test/fixtures/typescript/import/export-import-type-as-identifier/input.ts
semantic error: `import lib = require(...);` is only supported when compiling modules to CommonJS.
Please consider using `import lib from '...';` alongside Typescript's --allowSyntheticDefaultImports option, or add @babel/plugin-transform-modules-commonjs to your Babel config.
semantic error: Import assignment cannot be used when targeting ECMAScript modules.

tasks/coverage/babel/packages/babel-parser/test/fixtures/typescript/import/export-import-type-require/input.ts
semantic error: Bindings mismatch:
after transform: ScopeId(0): ["a"]
rebuilt : ScopeId(0): []

tasks/coverage/babel/packages/babel-parser/test/fixtures/typescript/import/export-named-import-require/input.ts
semantic error: `import lib = require(...);` is only supported when compiling modules to CommonJS.
Please consider using `import lib from '...';` alongside Typescript's --allowSyntheticDefaultImports option, or add @babel/plugin-transform-modules-commonjs to your Babel config.
semantic error: Import assignment cannot be used when targeting ECMAScript modules.

tasks/coverage/babel/packages/babel-parser/test/fixtures/typescript/import/import-default-and-named-id-type/input.ts
semantic error: Bindings mismatch:
Expand All @@ -1283,8 +1269,7 @@ after transform: ScopeId(0): ["a"]
rebuilt : ScopeId(0): []

tasks/coverage/babel/packages/babel-parser/test/fixtures/typescript/import/import-type-as-identifier/input.ts
semantic error: `import lib = require(...);` is only supported when compiling modules to CommonJS.
Please consider using `import lib from '...';` alongside Typescript's --allowSyntheticDefaultImports option, or add @babel/plugin-transform-modules-commonjs to your Babel config.
semantic error: Import assignment cannot be used when targeting ECMAScript modules.

tasks/coverage/babel/packages/babel-parser/test/fixtures/typescript/import/internal-comments/input.ts
semantic error: Bindings mismatch:
Expand Down
Loading