use oxc::{
ast::{NONE, ast},
span::SPAN,
};
use rolldown_common::NormalModule;
use rolldown_ecmascript::CJS_MODULE_REF;
#[cfg(feature = "experimental")]
use rolldown_ecmascript::CJS_ROLLDOWN_MODULE_REF;
#[cfg(feature = "experimental")]
use crate::hmr::hmr_ast_finalizer::HmrAstFinalizer;
use crate::module_finalizers::ScopeHoistingFinalizer;
#[cfg(feature = "experimental")]
pub static MODULE_EXPORTS_NAME_FOR_ESM: &str = "__rolldown_exports__";
#[cfg(feature = "experimental")]
pub static MODULE_ID_PARAM_FOR_HMR: &str = "__rolldown_module_id__";
pub trait HmrAstBuilder<'any, 'ast> {
fn builder(&self) -> &oxc::ast::AstBuilder<'ast>;
fn module(&self) -> &NormalModule;
fn binding_name_for_namespace_object_ref_atom(&self) -> ast::Str<'ast>;
fn alias_name_for_import_meta_hot(&self) -> ast::Str<'ast>;
fn cjs_module_name() -> &'static str;
fn module_id_argument(&self) -> ast::Argument<'ast> {
ast::Argument::StringLiteral(self.builder().alloc_string_literal(
SPAN,
self.builder().str(&self.module().stable_id),
None,
))
}
fn create_register_module_stmt(&self) -> ast::Statement<'ast> {
let module_exports = match self.module().exports_kind {
rolldown_common::ExportsKind::Esm => {
let binding_name_for_namespace_object_ref_atom =
self.binding_name_for_namespace_object_ref_atom();
let namespace_object_ref_expr = ast::Expression::Identifier(
self
.builder()
.alloc_identifier_reference(SPAN, binding_name_for_namespace_object_ref_atom),
);
ast::Argument::ObjectExpression(self.builder().alloc_object_expression(
SPAN,
self.builder().vec1(self.builder().object_property_kind_object_property(
SPAN,
ast::PropertyKind::Init,
self.builder().property_key_static_identifier(SPAN, "exports"),
namespace_object_ref_expr,
true,
false,
false,
)),
))
}
rolldown_common::ExportsKind::CommonJs => {
ast::Argument::from(ast::Expression::Identifier(
self.builder().alloc_identifier_reference(SPAN, Self::cjs_module_name()),
))
}
rolldown_common::ExportsKind::None => {
ast::Argument::from(ast::Expression::ObjectExpression(
self.builder().alloc_object_expression(SPAN, self.builder().vec()),
))
}
};
let arguments = self.builder().vec_from_array([self.module_id_argument(), module_exports]);
let register_call = self.builder().alloc_call_expression(
SPAN,
ast::Expression::Identifier(
self.builder().alloc_identifier_reference(SPAN, "__rolldown_runtime__.registerModule"),
),
NONE,
arguments,
false,
);
ast::Statement::ExpressionStatement(
self
.builder()
.alloc_expression_statement(SPAN, ast::Expression::CallExpression(register_call)),
)
}
fn create_module_hot_context_initializer_stmt(&self) -> ast::Statement<'ast> {
ast::Statement::VariableDeclaration(
self.builder().alloc_variable_declaration(
SPAN,
ast::VariableDeclarationKind::Const,
self.builder().vec1(
self.builder().variable_declarator(
SPAN,
ast::VariableDeclarationKind::Const,
self
.builder()
.binding_pattern_binding_identifier(SPAN, self.alias_name_for_import_meta_hot()),
NONE,
Some(ast::Expression::CallExpression(
self.builder().alloc_call_expression(
SPAN,
ast::Expression::Identifier(
self.builder().alloc_identifier_reference(
SPAN,
"__rolldown_runtime__.createModuleHotContext",
),
),
NONE,
self.builder().vec1(self.module_id_argument()),
false,
),
)),
false,
),
),
false,
),
)
}
}
#[cfg(feature = "experimental")]
impl<'any, 'ast> HmrAstBuilder<'any, 'ast> for HmrAstFinalizer<'any, 'ast> {
fn builder(&self) -> &oxc::ast::AstBuilder<'ast> {
self.builder
}
fn module(&self) -> &NormalModule {
self.module
}
fn binding_name_for_namespace_object_ref_atom(&self) -> ast::Str<'ast> {
self.builder().str(MODULE_EXPORTS_NAME_FOR_ESM)
}
fn alias_name_for_import_meta_hot(&self) -> ast::Str<'ast> {
self.builder().str(&format!("hot_{}", self.module.repr_name))
}
fn cjs_module_name() -> &'static str {
CJS_ROLLDOWN_MODULE_REF
}
fn module_id_argument(&self) -> ast::Argument<'ast> {
ast::Argument::Identifier(
self.builder().alloc_identifier_reference(SPAN, MODULE_ID_PARAM_FOR_HMR),
)
}
}
impl<'any, 'ast> HmrAstBuilder<'any, 'ast> for ScopeHoistingFinalizer<'any, 'ast> {
fn builder(&self) -> &oxc::ast::AstBuilder<'ast> {
&self.snippet.builder
}
fn module(&self) -> &NormalModule {
self.ctx.module
}
fn binding_name_for_namespace_object_ref_atom(&self) -> ast::Str<'ast> {
let name = self.canonical_name_for(self.ctx.module.namespace_object_ref);
self.builder().str(name)
}
fn alias_name_for_import_meta_hot(&self) -> ast::Str<'ast> {
let name =
self.canonical_name_for(self.ctx.module.hmr_hot_ref.expect("HMR hot ref should be set"));
self.builder().str(name)
}
fn cjs_module_name() -> &'static str {
CJS_MODULE_REF
}
}