use std::sync::Arc;
use arcstr::ArcStr;
use oxc::ast_visit::VisitMut;
use oxc::span::SourceType;
use oxc_index::IndexVec;
use rolldown_common::{
EcmaView, ExportsKind, FlatOptions, ModuleDefFormat, ModuleIdx, ModuleType, NormalModule,
SideEffectDetail, StableModuleId, side_effects::DeterminedSideEffects,
side_effects::HookSideEffects,
};
use rolldown_common::{
ModuleLoaderMsg, RUNTIME_MODULE_ID, RUNTIME_MODULE_KEY, ResolvedId, RuntimeModuleBrief,
RuntimeModuleTaskResult,
};
use rolldown_ecmascript::{EcmaAst, EcmaCompiler};
use rolldown_error::BuildResult;
use rolldown_utils::concat_string;
use rolldown_utils::indexmap::FxIndexSet;
use rolldown_utils::stabilize_id::stabilize_id;
use rustc_hash::{FxHashMap, FxHashSet};
use crate::{
ast_scanner::{AstScanner, ScanResult},
utils::tweak_ast_for_scanning::PreProcessor,
};
use rolldown_fs::FileSystem;
use super::resolve_utils::resolve_dependencies;
use super::task_context::TaskContext;
const RUNTIME_BASE_JS: &str = include_str!("../runtime/runtime-base.js");
const RUNTIME_TAIL_JS: &str = include_str!("../runtime/runtime-tail.js");
const RUNTIME_HEAD_NODE_JS: &str = include_str!("../runtime/runtime-head-node.js");
const RUNTIME_TAIL_NODE_JS: &str = include_str!("../runtime/runtime-tail-node.js");
fn get_runtime_js() -> String {
concat_string!(RUNTIME_BASE_JS, RUNTIME_TAIL_JS)
}
fn get_runtime_js_with_node_platform() -> String {
concat_string!(RUNTIME_HEAD_NODE_JS, RUNTIME_BASE_JS, RUNTIME_TAIL_NODE_JS)
}
pub struct RuntimeModuleTask<Fs: FileSystem + Clone + 'static> {
module_idx: ModuleIdx,
ctx: Arc<TaskContext<Fs>>,
flat_options: FlatOptions,
}
impl<Fs: FileSystem + Clone + 'static> RuntimeModuleTask<Fs> {
pub fn new(
module_idx: ModuleIdx,
shared_context: Arc<TaskContext<Fs>>,
flat_options: FlatOptions,
) -> Self {
Self { module_idx, ctx: shared_context, flat_options }
}
#[tracing::instrument(name = "RuntimeNormalModuleTaskResult::run", level = "debug", skip_all)]
pub async fn run(self) {
if let Err(errs) = self.run_inner().await {
let _ = self.ctx.tx.send(ModuleLoaderMsg::BuildErrors(errs.into_vec().into_boxed_slice()));
}
}
async fn run_inner(&self) -> BuildResult<()> {
let source: String = if self.ctx.options.is_esm_format_with_node_platform() {
get_runtime_js_with_node_platform()
} else {
get_runtime_js()
};
let original_source = source.clone();
let mut sourcemap_chain = vec![];
let mut side_effects: Option<HookSideEffects> = None;
let mut module_type = ModuleType::Js;
let mut code_changed_by_plugins: Option<Vec<String>> = Some(vec![]);
let source: ArcStr = self
.ctx
.plugin_driver
.transform(
RUNTIME_MODULE_KEY,
self.module_idx,
source,
&mut sourcemap_chain,
&mut side_effects,
&mut module_type,
None,
&mut code_changed_by_plugins,
)
.await?
.into();
let mut modified_by_plugins: Vec<String> = vec![];
if let Some(plugin_names) = code_changed_by_plugins {
if !plugin_names.is_empty() && source.as_str() != original_source {
modified_by_plugins = plugin_names;
}
}
let (ast, scan_result) = self.make_ecma_ast(RUNTIME_MODULE_KEY, &source)?;
let ScanResult {
named_imports,
named_exports,
stmt_infos,
default_export_ref,
namespace_object_ref,
imports,
import_records: raw_import_records,
ast_usage,
symbol_ref_db,
new_url_references,
dummy_record_set,
ecma_view_meta,
..
} = scan_result;
let determined_side_effects = match side_effects {
Some(HookSideEffects::False) => DeterminedSideEffects::UserDefined(false),
Some(HookSideEffects::NoTreeshake) => DeterminedSideEffects::NoTreeshake,
Some(HookSideEffects::True) | None => {
let has_side_effects = stmt_infos
.iter()
.any(|stmt_info| stmt_info.side_effect.contains(SideEffectDetail::Unknown));
DeterminedSideEffects::Analyzed(has_side_effects)
}
};
let mut resolved_id = ResolvedId::make_dummy();
resolved_id.id = RUNTIME_MODULE_ID;
let module_type = ModuleType::Js;
let resolved_deps = resolve_dependencies(
&resolved_id,
&self.ctx.options,
&self.ctx.resolver,
&self.ctx.plugin_driver,
&raw_import_records,
source.clone(),
&mut vec![],
&module_type,
)
.await?;
let module = NormalModule {
idx: self.module_idx,
repr_name: "rolldown_runtime".to_string(),
stable_id: StableModuleId::new(&RUNTIME_MODULE_ID, &self.ctx.options.cwd),
id: RUNTIME_MODULE_ID,
debug_id: stabilize_id(RUNTIME_MODULE_KEY, &self.ctx.options.cwd),
exec_order: u32::MAX,
module_type,
ecma_view: EcmaView {
source,
import_records: IndexVec::default(),
sourcemap_chain,
importers: FxIndexSet::default(),
importers_idx: FxIndexSet::default(),
dynamic_importers: FxIndexSet::default(),
imported_ids: FxIndexSet::default(),
dynamically_imported_ids: FxIndexSet::default(),
side_effects: determined_side_effects,
named_imports,
named_exports,
imports,
default_export_ref,
exports_kind: ExportsKind::Esm,
namespace_object_ref,
def_format: ModuleDefFormat::EsmMjs,
ast_usage,
self_referenced_class_decl_symbol_ids: FxHashSet::default(),
hashbang_range: None,
meta: ecma_view_meta,
mutations: vec![],
new_url_references,
this_expr_replace_map: FxHashMap::default(),
hmr_info: scan_result.hmr_info,
hmr_hot_ref: None,
directive_range: vec![],
dummy_record_set,
constant_export_map: FxHashMap::default(),
enum_member_value_map: FxHashMap::default(),
import_attribute_map: FxHashMap::default(),
json_module_none_self_reference_included_symbol: None,
cjs_reexport_import_record_ids: Vec::new(),
},
originative_resolved_id: resolved_id,
};
let mut runtime = RuntimeModuleBrief::new(self.module_idx, &symbol_ref_db.ast_scopes);
runtime.set_modified_by_plugins(modified_by_plugins);
let result = ModuleLoaderMsg::RuntimeNormalModuleDone(Box::new(RuntimeModuleTaskResult {
ast,
module,
stmt_infos,
runtime,
resolved_deps,
raw_import_records,
local_symbol_ref_db: symbol_ref_db,
}));
let _ = self.ctx.tx.send(result);
Ok(())
}
fn make_ecma_ast(&self, filename: &str, source: &ArcStr) -> BuildResult<(EcmaAst, ScanResult)> {
let source_type = SourceType::default();
let mut ast = EcmaCompiler::parse(filename, source.clone(), source_type)?;
ast.program.with_mut(|fields| {
let mut pre_processor = PreProcessor::new(fields.allocator, false, None);
pre_processor.visit_program(fields.program);
});
let scoping = ast.make_scoping();
let facade_path = RUNTIME_MODULE_ID;
let runtime_flat_options = self.flat_options - FlatOptions::IgnoreAnnotations;
let scan_result = ast.program.with_mut(|fields| {
let program = &*fields.program;
let scanner = AstScanner::new(
self.module_idx,
scoping,
"rolldown_runtime",
ModuleDefFormat::EsmMjs,
source,
&facade_path,
&program.comments,
&self.ctx.options,
fields.allocator,
runtime_flat_options,
);
scanner.scan(program)
})?;
Ok((ast, scan_result))
}
}