use std::sync::LazyLock;
use bitflags::bitflags;
use heck::ToLowerCamelCase;
use rustc_hash::FxHashMap;
use crate::{CompilerOptions, runtime_mode::RuntimeMode};
#[rspack_cacheable::cacheable]
#[derive(Debug, Clone, Copy, Eq, PartialEq, Hash)]
pub struct RuntimeGlobals(u128);
macro_rules! define_runtime_globals {
(@step ($val:expr) ({$($acc:tt)*}) ($(#[$($attr:tt)+])* const $name:ident; $($rest:tt)*)) => {
define_runtime_globals! {
@step ($val << 1) ({
$($acc)*
$(#[$($attr)+])*
const $name = $val;
}) ($($rest)*)
}
};
(@step ($val:expr) ({$($acc:tt)*}) ()) => {
bitflags! {
impl RuntimeGlobals: u128 {
$($acc)*
}
}
};
($($rest:tt)*) => {
define_runtime_globals! {
@step (1u128) ({}) ($($rest)*)
}
};
}
define_runtime_globals! {
const REQUIRE_SCOPE;
const MODULE;
const MODULE_ID;
const REQUIRE;
const MODULE_CACHE;
const ENSURE_CHUNK;
const ENSURE_CHUNK_HANDLERS;
const PUBLIC_PATH;
const GET_CHUNK_SCRIPT_FILENAME;
const GET_CHUNK_CSS_FILENAME;
const LOAD_SCRIPT;
const HAS_OWN_PROPERTY;
const MODULE_FACTORIES_ADD_ONLY;
const ON_CHUNKS_LOADED;
const CHUNK_CALLBACK;
const MODULE_FACTORIES;
const INTERCEPT_MODULE_EXECUTION;
const HMR_DOWNLOAD_MANIFEST;
const HMR_DOWNLOAD_UPDATE_HANDLERS;
const HMR_INVALIDATE_MODULE_HANDLERS;
const GET_UPDATE_MANIFEST_FILENAME;
const GET_CHUNK_UPDATE_SCRIPT_FILENAME;
const GET_CHUNK_UPDATE_CSS_FILENAME;
const HMR_MODULE_DATA;
const HMR_RUNTIME_STATE_PREFIX;
const EXTERNAL_INSTALL_CHUNK;
const GET_FULL_HASH;
const GLOBAL;
const RETURN_EXPORTS_FROM_RUNTIME;
const INSTANTIATE_WASM;
const COMPILE_WASM;
const ASYNC_MODULE;
const BASE_URI;
const MODULE_LOADED;
const STARTUP_ENTRYPOINT;
const STARTUP_CHUNK_DEPENDENCIES;
const CREATE_SCRIPT_URL;
const CREATE_SCRIPT;
const GET_TRUSTED_TYPES_POLICY;
const DEFINE_PROPERTY_GETTERS;
const ENTRY_MODULE_ID;
const STARTUP_NO_DEFAULT;
const ENSURE_CHUNK_INCLUDE_ENTRIES;
const STARTUP;
const MAKE_NAMESPACE_OBJECT;
const EXPORTS;
const COMPAT_GET_DEFAULT_EXPORT;
const CREATE_FAKE_NAMESPACE_OBJECT;
const NODE_MODULE_DECORATOR;
const ESM_MODULE_DECORATOR;
const SYSTEM_CONTEXT;
const THIS_AS_EXPORTS;
const CURRENT_REMOTE_GET_SCOPE;
const SHARE_SCOPE_MAP;
const INITIALIZE_SHARING;
const SCRIPT_NONCE;
const RELATIVE_URL;
const CHUNK_NAME;
const RUNTIME_ID;
const PREFETCH_CHUNK;
const PREFETCH_CHUNK_HANDLERS;
const PRELOAD_CHUNK;
const PRELOAD_CHUNK_HANDLERS;
const UNCAUGHT_ERROR_HANDLER;
const RSPACK_VERSION;
const HAS_CSS_MODULES;
const RSPACK_UNIQUE_ID;
const HAS_FETCH_PRIORITY;
const AMD_DEFINE;
const AMD_OPTIONS;
const TO_BINARY;
const ASYNC_MODULE_EXPORT_SYMBOL;
const MAKE_DEFERRED_NAMESPACE_OBJECT;
const MAKE_OPTIMIZED_DEFERRED_NAMESPACE_OBJECT;
const DEFERRED_MODULES_ASYNC_TRANSITIVE_DEPENDENCIES;
const DEFERRED_MODULES_ASYNC_TRANSITIVE_DEPENDENCIES_SYMBOL;
const ASYNC_STARTUP;
const RSC_MANIFEST;
}
impl Default for RuntimeGlobals {
fn default() -> Self {
Self::empty()
}
}
pub static REQUIRE_SCOPE_GLOBALS: LazyLock<RuntimeGlobals> = LazyLock::new(|| {
let mut runtime_globals = RuntimeGlobals::all();
runtime_globals.remove(
RuntimeGlobals::MODULE
| RuntimeGlobals::MODULE_ID
| RuntimeGlobals::CHUNK_CALLBACK
| RuntimeGlobals::RETURN_EXPORTS_FROM_RUNTIME
| RuntimeGlobals::MODULE_LOADED
| RuntimeGlobals::EXPORTS
| RuntimeGlobals::THIS_AS_EXPORTS
| RuntimeGlobals::HAS_CSS_MODULES
| RuntimeGlobals::HAS_FETCH_PRIORITY,
);
runtime_globals
});
pub static MODULE_GLOBALS: LazyLock<RuntimeGlobals> =
LazyLock::new(|| RuntimeGlobals::MODULE_ID | RuntimeGlobals::MODULE_LOADED);
pub static BOOTSTRAP_RUNTIME_CONTEXT_GLOBALS: LazyLock<RuntimeGlobals> = LazyLock::new(|| {
RuntimeGlobals::REQUIRE
| RuntimeGlobals::INTERCEPT_MODULE_EXECUTION
| RuntimeGlobals::MODULE
| RuntimeGlobals::MODULE_FACTORIES
| RuntimeGlobals::MODULE_FACTORIES_ADD_ONLY
| RuntimeGlobals::MODULE_CACHE
| RuntimeGlobals::ON_CHUNKS_LOADED
| RuntimeGlobals::EXTERNAL_INSTALL_CHUNK
| RuntimeGlobals::STARTUP_ENTRYPOINT
| RuntimeGlobals::STARTUP
});
pub static INITIALIZE_OBJECT_GLOBALS: LazyLock<RuntimeGlobals> = LazyLock::new(|| {
RuntimeGlobals::ENSURE_CHUNK_HANDLERS
| RuntimeGlobals::PREFETCH_CHUNK_HANDLERS
| RuntimeGlobals::PRELOAD_CHUNK_HANDLERS
| RuntimeGlobals::HMR_DOWNLOAD_UPDATE_HANDLERS
| RuntimeGlobals::HMR_INVALIDATE_MODULE_HANDLERS
| RuntimeGlobals::HMR_MODULE_DATA
});
pub static INITIALIZE_ARRAY_GLOBALS: LazyLock<RuntimeGlobals> =
LazyLock::new(|| RuntimeGlobals::INTERCEPT_MODULE_EXECUTION);
pub fn runtime_globals_property_name(runtime_globals: &RuntimeGlobals) -> Option<&'static str> {
Some(match *runtime_globals {
RuntimeGlobals::REQUIRE_SCOPE => "*",
RuntimeGlobals::MODULE_ID => "id",
RuntimeGlobals::MODULE_LOADED => "loaded",
RuntimeGlobals::MODULE_CACHE => "c",
RuntimeGlobals::ENSURE_CHUNK => "e",
RuntimeGlobals::ENSURE_CHUNK_HANDLERS => "f",
RuntimeGlobals::PUBLIC_PATH => "p",
RuntimeGlobals::GET_CHUNK_SCRIPT_FILENAME => "u",
RuntimeGlobals::GET_CHUNK_CSS_FILENAME => "k",
RuntimeGlobals::LOAD_SCRIPT => "l",
RuntimeGlobals::HAS_OWN_PROPERTY => "o",
RuntimeGlobals::MODULE_FACTORIES_ADD_ONLY => "m (add only)",
RuntimeGlobals::ON_CHUNKS_LOADED => "O",
RuntimeGlobals::CHUNK_CALLBACK => "global chunk callback",
RuntimeGlobals::MODULE_FACTORIES => "m",
RuntimeGlobals::INTERCEPT_MODULE_EXECUTION => "i",
RuntimeGlobals::HMR_DOWNLOAD_MANIFEST => "hmrM",
RuntimeGlobals::HMR_DOWNLOAD_UPDATE_HANDLERS => "hmrC",
RuntimeGlobals::HMR_INVALIDATE_MODULE_HANDLERS => "hmrI",
RuntimeGlobals::HMR_MODULE_DATA => "hmrD",
RuntimeGlobals::HMR_RUNTIME_STATE_PREFIX => "hmrS",
RuntimeGlobals::GET_UPDATE_MANIFEST_FILENAME => "hmrF",
RuntimeGlobals::GET_CHUNK_UPDATE_SCRIPT_FILENAME => "hu",
RuntimeGlobals::GET_CHUNK_UPDATE_CSS_FILENAME => "hk",
RuntimeGlobals::AMD_DEFINE => "amdD",
RuntimeGlobals::AMD_OPTIONS => "amdO",
RuntimeGlobals::EXTERNAL_INSTALL_CHUNK => "C",
RuntimeGlobals::GET_FULL_HASH => "h",
RuntimeGlobals::GLOBAL => "g",
RuntimeGlobals::RETURN_EXPORTS_FROM_RUNTIME => "return-exports-from-runtime",
RuntimeGlobals::INSTANTIATE_WASM => "v",
RuntimeGlobals::COMPILE_WASM => "vs",
RuntimeGlobals::ASYNC_MODULE => "a",
RuntimeGlobals::ASYNC_MODULE_EXPORT_SYMBOL => "aE",
RuntimeGlobals::BASE_URI => "b",
RuntimeGlobals::STARTUP_ENTRYPOINT => "X",
RuntimeGlobals::STARTUP_CHUNK_DEPENDENCIES => "x (chunk dependencies)",
RuntimeGlobals::CREATE_SCRIPT_URL => "tu",
RuntimeGlobals::CREATE_SCRIPT => "ts",
RuntimeGlobals::GET_TRUSTED_TYPES_POLICY => "tt",
RuntimeGlobals::DEFINE_PROPERTY_GETTERS => "d",
RuntimeGlobals::ENTRY_MODULE_ID => "s",
RuntimeGlobals::STARTUP_NO_DEFAULT => "x (no default handler)",
RuntimeGlobals::ENSURE_CHUNK_INCLUDE_ENTRIES => "f (include entries)",
RuntimeGlobals::STARTUP => "x",
RuntimeGlobals::MAKE_NAMESPACE_OBJECT => "r",
RuntimeGlobals::MAKE_DEFERRED_NAMESPACE_OBJECT => "z",
RuntimeGlobals::MAKE_OPTIMIZED_DEFERRED_NAMESPACE_OBJECT => "zO",
RuntimeGlobals::DEFERRED_MODULES_ASYNC_TRANSITIVE_DEPENDENCIES => "zT",
RuntimeGlobals::DEFERRED_MODULES_ASYNC_TRANSITIVE_DEPENDENCIES_SYMBOL => "zS",
RuntimeGlobals::COMPAT_GET_DEFAULT_EXPORT => "n",
RuntimeGlobals::CREATE_FAKE_NAMESPACE_OBJECT => "t",
RuntimeGlobals::ESM_MODULE_DECORATOR => "hmd",
RuntimeGlobals::NODE_MODULE_DECORATOR => "nmd",
RuntimeGlobals::SYSTEM_CONTEXT => "y",
RuntimeGlobals::THIS_AS_EXPORTS => "top-level-this-exports",
RuntimeGlobals::CURRENT_REMOTE_GET_SCOPE => "R",
RuntimeGlobals::SHARE_SCOPE_MAP => "S",
RuntimeGlobals::INITIALIZE_SHARING => "I",
RuntimeGlobals::SCRIPT_NONCE => "nc",
RuntimeGlobals::RELATIVE_URL => "U",
RuntimeGlobals::CHUNK_NAME => "cn",
RuntimeGlobals::RUNTIME_ID => "j",
RuntimeGlobals::PREFETCH_CHUNK => "E",
RuntimeGlobals::PREFETCH_CHUNK_HANDLERS => "F",
RuntimeGlobals::PRELOAD_CHUNK => "G",
RuntimeGlobals::PRELOAD_CHUNK_HANDLERS => "H",
RuntimeGlobals::UNCAUGHT_ERROR_HANDLER => "oe",
RuntimeGlobals::RSPACK_VERSION => "rv",
RuntimeGlobals::RSPACK_UNIQUE_ID => "ruid",
RuntimeGlobals::HAS_CSS_MODULES => "has css modules",
RuntimeGlobals::ASYNC_STARTUP => "asyncStartup",
RuntimeGlobals::HAS_FETCH_PRIORITY => "has fetch priority",
RuntimeGlobals::RSC_MANIFEST => "rscM",
RuntimeGlobals::TO_BINARY => "tb",
_ => return None,
})
}
pub fn runtime_globals_to_string(runtime_globals: &RuntimeGlobals) -> String {
if runtime_globals == &RuntimeGlobals::EXPORTS {
return runtime_variable_name(&RuntimeVariable::Exports).to_string();
}
if runtime_globals == &RuntimeGlobals::REQUIRE {
return runtime_variable_name(&RuntimeVariable::Require).to_string();
}
if runtime_globals == &RuntimeGlobals::MODULE {
return "module".to_string();
}
let name = runtime_globals_property_name(runtime_globals)
.expect("runtime global should have a property name");
if REQUIRE_SCOPE_GLOBALS.contains(*runtime_globals) {
let require = runtime_variable_name(&RuntimeVariable::Require);
let mut result = String::with_capacity(require.len() + 1 + name.len());
result.push_str(require);
result.push('.');
result.push_str(name);
return result;
}
if MODULE_GLOBALS.contains(*runtime_globals) {
let mut result = String::with_capacity("module".len() + 1 + name.len());
result.push_str("module");
result.push('.');
result.push_str(name);
return result;
}
name.to_string()
}
#[derive(Debug, Clone, Copy, Eq, PartialEq, Hash)]
pub enum RuntimeVariable {
Require,
Context,
Modules,
ModuleCache,
Module,
Exports,
StartupExec,
}
pub fn runtime_variable_to_string(
runtime_variable: &RuntimeVariable,
compiler_options: &CompilerOptions,
) -> String {
match compiler_options.experiments.runtime_mode {
RuntimeMode::Webpack => runtime_variable_name(runtime_variable).to_string(),
RuntimeMode::Rspack => rspack_runtime_variable_name(runtime_variable).to_string(),
}
}
pub fn rspack_runtime_variable_name(runtime_variable: &RuntimeVariable) -> &'static str {
match *runtime_variable {
RuntimeVariable::Require => "__rspack_require",
RuntimeVariable::Context => "__rspack_context",
RuntimeVariable::Modules => "__rspack_modules",
RuntimeVariable::ModuleCache => "__rspack_module_cache",
RuntimeVariable::Exports => "__rspack_exports",
RuntimeVariable::Module => "__rspack_module",
RuntimeVariable::StartupExec => "__rspack_exec",
}
}
pub fn runtime_variable_name(runtime_variable: &RuntimeVariable) -> &'static str {
match *runtime_variable {
RuntimeVariable::Require => "__webpack_require__",
RuntimeVariable::Context => "__rspack_context",
RuntimeVariable::Modules => "__webpack_modules__",
RuntimeVariable::ModuleCache => "__webpack_module_cache__",
RuntimeVariable::Exports => "__webpack_exports__",
RuntimeVariable::Module => "__webpack_module__",
RuntimeVariable::StartupExec => "__webpack_exec__",
}
}
type RuntimeGlobalMap = (
FxHashMap<RuntimeGlobals, &'static str>,
FxHashMap<&'static str, RuntimeGlobals>,
FxHashMap<&'static str, RuntimeGlobals>,
FxHashMap<RuntimeGlobals, String>,
);
static RUNTIME_GLOBAL_MAP: LazyLock<RuntimeGlobalMap> = LazyLock::new(|| {
let mut to_flag_name_map = FxHashMap::default();
let mut from_flag_name_map = FxHashMap::default();
let mut from_property_name_map = FxHashMap::default();
let mut to_lexical_name_map = FxHashMap::default();
for (name, value) in RuntimeGlobals::all().iter_names() {
to_flag_name_map.insert(value, name);
from_flag_name_map.insert(name, value);
to_lexical_name_map.insert(value, name.to_lower_camel_case());
if let Some(property_name) = runtime_globals_property_name(&value) {
from_property_name_map.insert(property_name, value);
}
}
to_flag_name_map.shrink_to_fit();
from_flag_name_map.shrink_to_fit();
from_property_name_map.shrink_to_fit();
to_lexical_name_map.shrink_to_fit();
(
to_flag_name_map,
from_flag_name_map,
from_property_name_map,
to_lexical_name_map,
)
});
impl RuntimeGlobals {
pub fn property_name(&self) -> Option<&'static str> {
runtime_globals_property_name(self)
}
pub fn rspack_context_property_name(&self) -> Option<&'static str> {
if *self == RuntimeGlobals::MAKE_NAMESPACE_OBJECT {
Some("N")
} else {
self.property_name()
}
}
pub fn name(&self) -> Option<&'static str> {
RUNTIME_GLOBAL_MAP.0.get(self).copied()
}
pub fn from_property_name(property_name: &str) -> Option<Self> {
RUNTIME_GLOBAL_MAP.2.get(property_name).copied()
}
pub fn from_rspack_context_property_name(property_name: &str) -> Option<Self> {
if property_name == "N" {
return Some(RuntimeGlobals::MAKE_NAMESPACE_OBJECT);
}
let runtime_global = Self::from_property_name(property_name)?;
(runtime_global != RuntimeGlobals::MAKE_NAMESPACE_OBJECT).then_some(runtime_global)
}
pub fn renderable_require_scope(self) -> Self {
self.intersection(*REQUIRE_SCOPE_GLOBALS)
}
pub fn with_require_scope(self) -> Self {
if !self.renderable_require_scope().is_empty() {
self | RuntimeGlobals::REQUIRE_SCOPE
} else {
self
}
}
pub fn to_lexical_name(&self) -> Option<&str> {
RUNTIME_GLOBAL_MAP.3.get(self).map(String::as_str)
}
pub fn should_initialize_as_object(&self) -> bool {
!self.intersection(*INITIALIZE_OBJECT_GLOBALS).is_empty()
}
pub fn should_initialize_as_array(&self) -> bool {
!self.intersection(*INITIALIZE_ARRAY_GLOBALS).is_empty()
}
pub fn needs_bootstrap_runtime_context(&self) -> bool {
!self
.intersection(*BOOTSTRAP_RUNTIME_CONTEXT_GLOBALS)
.is_empty()
}
pub fn to_names(&self) -> Vec<&'static str> {
let mut res = vec![];
for (item, js_name) in RUNTIME_GLOBAL_MAP.0.iter() {
if self.contains(*item) {
res.push(*js_name);
}
}
res
}
pub fn from_names(names: &[String]) -> Self {
let mut res = Self::empty();
for name in names {
if let Some(value) = RUNTIME_GLOBAL_MAP.1.get(name.as_str()) {
res.insert(*value);
}
}
res
}
}