use std::{
hash::Hash,
sync::{Arc, LazyLock},
};
use atomic_refcell::AtomicRefCell;
use rspack_collections::DatabaseItem;
use rspack_core::{
ChunkLoading, ChunkUkey, Compilation, CompilationId, CompilationParams,
CompilationRuntimeRequirementInModule, CompilationRuntimeRequirementInTree, CompilerCompilation,
ModuleIdentifier, Plugin, PublicPath, RuntimeGlobals, RuntimeModule, RuntimeModuleExt,
SourceType, get_css_chunk_filename_template, get_js_chunk_filename_template,
has_hash_placeholder,
};
use rspack_error::Result;
use rspack_hash::RspackHash;
use rspack_hook::{plugin, plugin_hook};
use rspack_plugin_javascript::{
JavascriptModulesChunkHash, JsPlugin, impl_plugin_for_js_plugin::chunk_has_js,
};
#[cfg(allocative)]
use rspack_util::allocative;
use rspack_util::fx_hash::FxDashMap;
use crate::{
RuntimePluginHooks,
runtime_module::{
AmdDefineRuntimeModule, AmdOptionsRuntimeModule, AsyncRuntimeModule,
AutoPublicPathRuntimeModule, BaseUriRuntimeModule, ChunkNameRuntimeModule,
ChunkPrefetchPreloadFunctionRuntimeModule, CompatGetDefaultExportRuntimeModule,
CreateFakeNamespaceObjectRuntimeModule, CreateScriptRuntimeModule,
CreateScriptUrlRuntimeModule, DefinePropertyGettersRuntimeModule,
ESMModuleDecoratorRuntimeModule, EnsureChunkRuntimeModule, GetChunkFilenameRuntimeModule,
GetChunkUpdateFilenameRuntimeModule, GetFullHashRuntimeModule, GetMainFilenameRuntimeModule,
GetTrustedTypesPolicyRuntimeModule, GlobalRuntimeModule, HasOwnPropertyRuntimeModule,
LoadScriptRuntimeModule, MakeDeferredNamespaceObjectRuntimeModule,
MakeNamespaceObjectRuntimeModule, NodeModuleDecoratorRuntimeModule, NonceRuntimeModule,
OnChunkLoadedRuntimeModule, PublicPathRuntimeModule, RelativeUrlRuntimeModule,
RuntimeIdRuntimeModule, SystemContextRuntimeModule, ToBinaryRuntimeModule, chunk_has_css,
is_enabled_for_chunk,
},
};
type ArcRuntimePluginHooks = Arc<AtomicRefCell<RuntimePluginHooks>>;
#[cfg_attr(allocative, allocative::root)]
static COMPILATION_HOOKS_MAP: LazyLock<FxDashMap<CompilationId, ArcRuntimePluginHooks>> =
LazyLock::new(Default::default);
const GLOBALS_ON_REQUIRE: &[RuntimeGlobals] = &[
RuntimeGlobals::CHUNK_NAME,
RuntimeGlobals::RUNTIME_ID,
RuntimeGlobals::COMPAT_GET_DEFAULT_EXPORT,
RuntimeGlobals::CREATE_FAKE_NAMESPACE_OBJECT,
RuntimeGlobals::CREATE_SCRIPT,
RuntimeGlobals::CREATE_SCRIPT_URL,
RuntimeGlobals::GET_TRUSTED_TYPES_POLICY,
RuntimeGlobals::DEFINE_PROPERTY_GETTERS,
RuntimeGlobals::ENSURE_CHUNK,
RuntimeGlobals::ENTRY_MODULE_ID,
RuntimeGlobals::GET_FULL_HASH,
RuntimeGlobals::GLOBAL,
RuntimeGlobals::MAKE_NAMESPACE_OBJECT,
RuntimeGlobals::MODULE_CACHE,
RuntimeGlobals::MODULE_FACTORIES,
RuntimeGlobals::MODULE_FACTORIES_ADD_ONLY,
RuntimeGlobals::INTERCEPT_MODULE_EXECUTION,
RuntimeGlobals::PUBLIC_PATH,
RuntimeGlobals::BASE_URI,
RuntimeGlobals::RELATIVE_URL,
RuntimeGlobals::SCRIPT_NONCE,
RuntimeGlobals::UNCAUGHT_ERROR_HANDLER,
RuntimeGlobals::ASYNC_MODULE,
RuntimeGlobals::INSTANTIATE_WASM,
RuntimeGlobals::SHARE_SCOPE_MAP,
RuntimeGlobals::INITIALIZE_SHARING,
RuntimeGlobals::LOAD_SCRIPT,
RuntimeGlobals::SYSTEM_CONTEXT,
RuntimeGlobals::ON_CHUNKS_LOADED,
RuntimeGlobals::MAKE_DEFERRED_NAMESPACE_OBJECT,
];
const MODULE_DEPENDENCIES: &[(RuntimeGlobals, RuntimeGlobals)] = &[
(RuntimeGlobals::MODULE_LOADED, RuntimeGlobals::MODULE),
(RuntimeGlobals::MODULE_ID, RuntimeGlobals::MODULE),
(
RuntimeGlobals::ESM_MODULE_DECORATOR,
RuntimeGlobals::MODULE.union(RuntimeGlobals::REQUIRE_SCOPE),
),
(
RuntimeGlobals::NODE_MODULE_DECORATOR,
RuntimeGlobals::MODULE.union(RuntimeGlobals::REQUIRE_SCOPE),
),
(RuntimeGlobals::AMD_DEFINE, RuntimeGlobals::REQUIRE),
(RuntimeGlobals::AMD_OPTIONS, RuntimeGlobals::REQUIRE_SCOPE),
];
const TREE_DEPENDENCIES: &[(RuntimeGlobals, RuntimeGlobals)] = &[
(
RuntimeGlobals::COMPAT_GET_DEFAULT_EXPORT,
RuntimeGlobals::DEFINE_PROPERTY_GETTERS,
),
(
RuntimeGlobals::CREATE_FAKE_NAMESPACE_OBJECT,
RuntimeGlobals::DEFINE_PROPERTY_GETTERS
.union(RuntimeGlobals::MAKE_NAMESPACE_OBJECT)
.union(RuntimeGlobals::REQUIRE),
),
(
RuntimeGlobals::DEFINE_PROPERTY_GETTERS,
RuntimeGlobals::HAS_OWN_PROPERTY,
),
(
RuntimeGlobals::INITIALIZE_SHARING,
RuntimeGlobals::SHARE_SCOPE_MAP,
),
(
RuntimeGlobals::SHARE_SCOPE_MAP,
RuntimeGlobals::HAS_OWN_PROPERTY,
),
(
RuntimeGlobals::ESM_MODULE_DECORATOR,
RuntimeGlobals::MODULE.union(RuntimeGlobals::REQUIRE_SCOPE),
),
(
RuntimeGlobals::NODE_MODULE_DECORATOR,
RuntimeGlobals::MODULE.union(RuntimeGlobals::REQUIRE_SCOPE),
),
(
RuntimeGlobals::MAKE_DEFERRED_NAMESPACE_OBJECT,
RuntimeGlobals::DEFINE_PROPERTY_GETTERS
.union(RuntimeGlobals::MAKE_NAMESPACE_OBJECT)
.union(RuntimeGlobals::CREATE_FAKE_NAMESPACE_OBJECT)
.union(RuntimeGlobals::HAS_OWN_PROPERTY)
.union(RuntimeGlobals::REQUIRE),
),
];
fn handle_require_scope_globals(
runtime_requirements: &RuntimeGlobals,
runtime_requirements_mut: &mut RuntimeGlobals,
) {
if GLOBALS_ON_REQUIRE
.iter()
.any(|requirement| runtime_requirements.contains(*requirement))
{
runtime_requirements_mut.insert(RuntimeGlobals::REQUIRE_SCOPE);
}
}
fn handle_dependency_globals(
runtime_requirements: &RuntimeGlobals,
runtime_requirements_mut: &mut RuntimeGlobals,
dependencies: &[(RuntimeGlobals, RuntimeGlobals)],
) {
for &(requirement, dependencies) in dependencies.iter() {
if runtime_requirements.contains(requirement) {
*runtime_requirements_mut |= dependencies;
}
}
}
#[plugin]
#[derive(Debug, Default)]
pub struct RuntimePlugin;
impl RuntimePlugin {
pub fn get_compilation_hooks(id: CompilationId) -> ArcRuntimePluginHooks {
if !COMPILATION_HOOKS_MAP.contains_key(&id) {
COMPILATION_HOOKS_MAP.insert(id, Default::default());
}
COMPILATION_HOOKS_MAP
.get(&id)
.expect("should have js plugin drive")
.clone()
}
pub fn get_compilation_hooks_mut(id: CompilationId) -> ArcRuntimePluginHooks {
COMPILATION_HOOKS_MAP.entry(id).or_default().clone()
}
}
#[plugin_hook(CompilerCompilation for RuntimePlugin)]
async fn compilation(
&self,
compilation: &mut Compilation,
_params: &mut CompilationParams,
) -> Result<()> {
let hooks = JsPlugin::get_compilation_hooks_mut(compilation.id());
let mut hooks = hooks.write().await;
hooks.chunk_hash.tap(js_chunk_hash::new(self));
Ok(())
}
#[plugin_hook(JavascriptModulesChunkHash for RuntimePlugin)]
async fn js_chunk_hash(
&self,
compilation: &Compilation,
chunk_ukey: &ChunkUkey,
hasher: &mut RspackHash,
) -> Result<()> {
for identifier in compilation
.chunk_graph
.get_chunk_runtime_modules_iterable(chunk_ukey)
{
if let Some(hash) = compilation.runtime_modules_hash.get(identifier) {
hash.hash(hasher);
}
}
Ok(())
}
#[plugin_hook(CompilationRuntimeRequirementInModule for RuntimePlugin,tracing=false)]
async fn runtime_requirements_in_module(
&self,
_compilation: &Compilation,
_module: &ModuleIdentifier,
_all_runtime_requirements: &RuntimeGlobals,
runtime_requirements: &RuntimeGlobals,
runtime_requirements_mut: &mut RuntimeGlobals,
) -> Result<Option<()>> {
handle_require_scope_globals(runtime_requirements, runtime_requirements_mut);
handle_dependency_globals(
runtime_requirements,
runtime_requirements_mut,
MODULE_DEPENDENCIES,
);
Ok(None)
}
#[plugin_hook(CompilationRuntimeRequirementInTree for RuntimePlugin)]
async fn runtime_requirements_in_tree(
&self,
compilation: &Compilation,
chunk_ukey: &ChunkUkey,
_all_runtime_requirements: &RuntimeGlobals,
runtime_requirements: &RuntimeGlobals,
runtime_requirements_mut: &mut RuntimeGlobals,
runtime_modules_to_add: &mut Vec<(ChunkUkey, Box<dyn RuntimeModule>)>,
) -> Result<Option<()>> {
if compilation.options.output.trusted_types.is_some() {
if runtime_requirements.contains(RuntimeGlobals::LOAD_SCRIPT) {
runtime_requirements_mut.insert(RuntimeGlobals::CREATE_SCRIPT_URL);
}
if runtime_requirements.contains(RuntimeGlobals::CREATE_SCRIPT)
|| runtime_requirements.contains(RuntimeGlobals::CREATE_SCRIPT_URL)
{
runtime_requirements_mut.insert(RuntimeGlobals::GET_TRUSTED_TYPES_POLICY);
}
}
if (runtime_requirements.contains(RuntimeGlobals::GET_CHUNK_UPDATE_SCRIPT_FILENAME)
&& has_hash_placeholder(
compilation
.options
.output
.hot_update_chunk_filename
.as_str(),
))
|| (runtime_requirements.contains(RuntimeGlobals::GET_UPDATE_MANIFEST_FILENAME)
&& has_hash_placeholder(compilation.options.output.hot_update_main_filename.as_str()))
{
runtime_requirements_mut.insert(RuntimeGlobals::GET_FULL_HASH);
}
handle_require_scope_globals(runtime_requirements, runtime_requirements_mut);
handle_dependency_globals(
runtime_requirements,
runtime_requirements_mut,
TREE_DEPENDENCIES,
);
let public_path = compilation
.chunk_by_ukey
.expect_get(chunk_ukey)
.get_entry_options(&compilation.chunk_group_by_ukey)
.and_then(|options| options.public_path.clone())
.unwrap_or_else(|| compilation.options.output.public_path.clone());
if matches!(public_path, PublicPath::Auto)
&& runtime_requirements.contains(RuntimeGlobals::PUBLIC_PATH)
{
runtime_requirements_mut.insert(RuntimeGlobals::GLOBAL);
}
if runtime_requirements.contains(RuntimeGlobals::GET_CHUNK_SCRIPT_FILENAME)
&& matches!(compilation.options.output.chunk_filename.template(), Some(template) if has_hash_placeholder(template))
{
runtime_requirements_mut.insert(RuntimeGlobals::GET_FULL_HASH);
}
if runtime_requirements.contains(RuntimeGlobals::GET_CHUNK_CSS_FILENAME)
&& matches!(compilation.options.output.css_chunk_filename.template(), Some(template) if has_hash_placeholder(template))
{
runtime_requirements_mut.insert(RuntimeGlobals::GET_FULL_HASH);
}
if runtime_requirements.contains(RuntimeGlobals::PREFETCH_CHUNK) {
runtime_requirements_mut.insert(RuntimeGlobals::PREFETCH_CHUNK_HANDLERS);
}
if runtime_requirements.contains(RuntimeGlobals::PRELOAD_CHUNK) {
runtime_requirements_mut.insert(RuntimeGlobals::PRELOAD_CHUNK_HANDLERS);
}
if runtime_requirements.contains(RuntimeGlobals::ENSURE_CHUNK) {
let c = compilation.chunk_by_ukey.expect_get(chunk_ukey);
let has_async_chunks = c.has_async_chunks(&compilation.chunk_group_by_ukey);
if has_async_chunks {
runtime_requirements_mut.insert(RuntimeGlobals::ENSURE_CHUNK_HANDLERS);
}
runtime_modules_to_add.push((
*chunk_ukey,
EnsureChunkRuntimeModule::new(&compilation.runtime_template).boxed(),
));
}
if runtime_requirements.contains(RuntimeGlobals::ENSURE_CHUNK_INCLUDE_ENTRIES) {
runtime_requirements_mut.insert(RuntimeGlobals::ENSURE_CHUNK_HANDLERS);
}
let library_type = {
let chunk = compilation.chunk_by_ukey.expect_get(chunk_ukey);
chunk
.get_entry_options(&compilation.chunk_group_by_ukey)
.and_then(|options| options.library.as_ref())
.or(compilation.options.output.library.as_ref())
.map(|library| library.library_type.clone())
};
for runtime_requirement in runtime_requirements.iter() {
match runtime_requirement {
RuntimeGlobals::ASYNC_MODULE => {
runtime_modules_to_add.push((
*chunk_ukey,
AsyncRuntimeModule::new(&compilation.runtime_template).boxed(),
));
}
RuntimeGlobals::BASE_URI
if is_enabled_for_chunk(chunk_ukey, &ChunkLoading::Disable, compilation) =>
{
runtime_modules_to_add.push((
*chunk_ukey,
BaseUriRuntimeModule::new(&compilation.runtime_template).boxed(),
));
}
RuntimeGlobals::PUBLIC_PATH => match &public_path {
PublicPath::Filename(filename) => {
runtime_modules_to_add.push((
*chunk_ukey,
PublicPathRuntimeModule::new(&compilation.runtime_template, Box::new(filename.clone()))
.boxed(),
));
}
PublicPath::Auto => {
runtime_modules_to_add.push((
*chunk_ukey,
AutoPublicPathRuntimeModule::new(&compilation.runtime_template).boxed(),
));
}
},
RuntimeGlobals::GET_CHUNK_SCRIPT_FILENAME => {
runtime_modules_to_add.push((
*chunk_ukey,
GetChunkFilenameRuntimeModule::new(
&compilation.runtime_template,
"javascript",
"javascript",
SourceType::JavaScript,
compilation
.runtime_template
.render_runtime_globals(&RuntimeGlobals::GET_CHUNK_SCRIPT_FILENAME),
|_| false,
|chunk, compilation| {
chunk_has_js(&chunk.ukey(), compilation).then(|| {
get_js_chunk_filename_template(
chunk,
&compilation.options.output,
&compilation.chunk_group_by_ukey,
)
.clone()
})
},
)
.boxed(),
));
}
RuntimeGlobals::GET_CHUNK_CSS_FILENAME => {
runtime_modules_to_add.push((
*chunk_ukey,
GetChunkFilenameRuntimeModule::new(
&compilation.runtime_template,
"css",
"css",
SourceType::Css,
compilation
.runtime_template
.render_runtime_globals(&RuntimeGlobals::GET_CHUNK_CSS_FILENAME),
|runtime_requirements| {
runtime_requirements.contains(RuntimeGlobals::HMR_DOWNLOAD_UPDATE_HANDLERS)
},
|chunk, compilation| {
chunk_has_css(&chunk.ukey(), compilation).then(|| {
get_css_chunk_filename_template(
chunk,
&compilation.options.output,
&compilation.chunk_group_by_ukey,
)
.clone()
})
},
)
.boxed(),
));
}
RuntimeGlobals::GET_CHUNK_UPDATE_SCRIPT_FILENAME => {
runtime_modules_to_add.push((
*chunk_ukey,
GetChunkUpdateFilenameRuntimeModule::new(&compilation.runtime_template).boxed(),
));
}
RuntimeGlobals::GET_UPDATE_MANIFEST_FILENAME => {
runtime_modules_to_add.push((
*chunk_ukey,
GetMainFilenameRuntimeModule::new(
&compilation.runtime_template,
"update manifest",
RuntimeGlobals::GET_UPDATE_MANIFEST_FILENAME,
compilation.options.output.hot_update_main_filename.clone(),
)
.boxed(),
));
}
RuntimeGlobals::LOAD_SCRIPT => {
runtime_modules_to_add.push((
*chunk_ukey,
LoadScriptRuntimeModule::new(
&compilation.runtime_template,
compilation.options.output.unique_name.clone(),
compilation.options.output.trusted_types.is_some(),
*chunk_ukey,
)
.boxed(),
));
}
RuntimeGlobals::HAS_OWN_PROPERTY => {
runtime_modules_to_add.push((
*chunk_ukey,
HasOwnPropertyRuntimeModule::new(&compilation.runtime_template).boxed(),
));
}
RuntimeGlobals::GET_FULL_HASH => {
runtime_modules_to_add.push((
*chunk_ukey,
GetFullHashRuntimeModule::new(&compilation.runtime_template).boxed(),
));
}
RuntimeGlobals::GLOBAL => {
runtime_modules_to_add.push((
*chunk_ukey,
GlobalRuntimeModule::new(&compilation.runtime_template).boxed(),
));
}
RuntimeGlobals::CREATE_SCRIPT_URL => {
runtime_modules_to_add.push((
*chunk_ukey,
CreateScriptUrlRuntimeModule::new(&compilation.runtime_template).boxed(),
));
}
RuntimeGlobals::CREATE_SCRIPT => {
runtime_modules_to_add.push((
*chunk_ukey,
CreateScriptRuntimeModule::new(&compilation.runtime_template).boxed(),
));
}
RuntimeGlobals::ON_CHUNKS_LOADED => {
runtime_modules_to_add.push((
*chunk_ukey,
OnChunkLoadedRuntimeModule::new(&compilation.runtime_template).boxed(),
));
}
RuntimeGlobals::DEFINE_PROPERTY_GETTERS => {
runtime_modules_to_add.push((
*chunk_ukey,
DefinePropertyGettersRuntimeModule::new(&compilation.runtime_template).boxed(),
));
}
RuntimeGlobals::GET_TRUSTED_TYPES_POLICY => {
runtime_modules_to_add.push((
*chunk_ukey,
GetTrustedTypesPolicyRuntimeModule::new(&compilation.runtime_template).boxed(),
));
}
RuntimeGlobals::CREATE_FAKE_NAMESPACE_OBJECT => {
runtime_modules_to_add.push((
*chunk_ukey,
CreateFakeNamespaceObjectRuntimeModule::new(&compilation.runtime_template).boxed(),
));
}
RuntimeGlobals::MAKE_NAMESPACE_OBJECT => {
runtime_modules_to_add.push((
*chunk_ukey,
MakeNamespaceObjectRuntimeModule::new(&compilation.runtime_template).boxed(),
));
}
RuntimeGlobals::COMPAT_GET_DEFAULT_EXPORT => {
runtime_modules_to_add.push((
*chunk_ukey,
CompatGetDefaultExportRuntimeModule::new(&compilation.runtime_template).boxed(),
));
}
RuntimeGlobals::ESM_MODULE_DECORATOR => {
runtime_modules_to_add.push((
*chunk_ukey,
ESMModuleDecoratorRuntimeModule::new(&compilation.runtime_template).boxed(),
));
}
RuntimeGlobals::NODE_MODULE_DECORATOR => {
runtime_modules_to_add.push((
*chunk_ukey,
NodeModuleDecoratorRuntimeModule::new(&compilation.runtime_template).boxed(),
));
}
RuntimeGlobals::SYSTEM_CONTEXT if matches!(&library_type, Some(t) if t == "system") => {
runtime_modules_to_add.push((
*chunk_ukey,
SystemContextRuntimeModule::new(&compilation.runtime_template).boxed(),
));
}
RuntimeGlobals::SCRIPT_NONCE => {
runtime_modules_to_add.push((
*chunk_ukey,
NonceRuntimeModule::new(&compilation.runtime_template).boxed(),
));
}
RuntimeGlobals::RELATIVE_URL => {
runtime_modules_to_add.push((
*chunk_ukey,
RelativeUrlRuntimeModule::new(&compilation.runtime_template).boxed(),
));
}
RuntimeGlobals::CHUNK_NAME => {
runtime_modules_to_add.push((
*chunk_ukey,
ChunkNameRuntimeModule::new(&compilation.runtime_template).boxed(),
));
}
RuntimeGlobals::RUNTIME_ID => {
runtime_modules_to_add.push((
*chunk_ukey,
RuntimeIdRuntimeModule::new(&compilation.runtime_template).boxed(),
));
}
RuntimeGlobals::PREFETCH_CHUNK => {
runtime_modules_to_add.push((
*chunk_ukey,
ChunkPrefetchPreloadFunctionRuntimeModule::new(
&compilation.runtime_template,
"prefetch",
RuntimeGlobals::PREFETCH_CHUNK,
RuntimeGlobals::PREFETCH_CHUNK_HANDLERS,
)
.boxed(),
));
}
RuntimeGlobals::PRELOAD_CHUNK => {
runtime_modules_to_add.push((
*chunk_ukey,
ChunkPrefetchPreloadFunctionRuntimeModule::new(
&compilation.runtime_template,
"preload",
RuntimeGlobals::PRELOAD_CHUNK,
RuntimeGlobals::PRELOAD_CHUNK_HANDLERS,
)
.boxed(),
));
}
RuntimeGlobals::AMD_DEFINE => {
if compilation.options.amd.is_some() {
runtime_modules_to_add.push((
*chunk_ukey,
AmdDefineRuntimeModule::new(&compilation.runtime_template).boxed(),
));
}
}
RuntimeGlobals::AMD_OPTIONS => {
if let Some(options) = &compilation.options.amd {
runtime_modules_to_add.push((
*chunk_ukey,
AmdOptionsRuntimeModule::new(&compilation.runtime_template, options.clone()).boxed(),
));
}
}
RuntimeGlobals::MAKE_DEFERRED_NAMESPACE_OBJECT => {
runtime_modules_to_add.push((
*chunk_ukey,
MakeDeferredNamespaceObjectRuntimeModule::new(&compilation.runtime_template, *chunk_ukey)
.boxed(),
));
}
RuntimeGlobals::TO_BINARY => {
runtime_modules_to_add.push((
*chunk_ukey,
ToBinaryRuntimeModule::new(&compilation.runtime_template).boxed(),
));
}
_ => {}
}
}
Ok(None)
}
impl Plugin for RuntimePlugin {
fn name(&self) -> &'static str {
"rspack.RuntimePlugin"
}
fn apply(&self, ctx: &mut rspack_core::ApplyContext<'_>) -> Result<()> {
ctx.compiler_hooks.compilation.tap(compilation::new(self));
ctx
.compilation_hooks
.runtime_requirement_in_module
.tap(runtime_requirements_in_module::new(self));
ctx
.compilation_hooks
.runtime_requirement_in_tree
.tap(runtime_requirements_in_tree::new(self));
Ok(())
}
fn clear_cache(&self, id: CompilationId) {
COMPILATION_HOOKS_MAP.remove(&id);
}
}