mod builder;
mod resolution;
use core::fmt;
use vize_carton::{smallvec, CompactString, FxHashMap, SmallVec, String, ToCompactString};
use vize_relief::BindingType;
use super::types::{
BlockScopeData, CallbackScopeData, ClientOnlyScopeData, ClosureScopeData,
EventHandlerScopeData, ExternalModuleScopeData, JsGlobalScopeData, NonScriptSetupScopeData,
ParentScopes, ScopeBinding, ScopeData, ScopeId, ScopeKind, ScriptSetupScopeData, Span,
UniversalScopeData, VForScopeData, VSlotScopeData, VueGlobalScopeData,
};
pub struct Scope {
pub id: ScopeId,
pub parents: ParentScopes,
pub kind: ScopeKind,
bindings: FxHashMap<CompactString, ScopeBinding>,
data: ScopeData,
pub span: Span,
}
struct SortedBindings<'a>(&'a FxHashMap<CompactString, ScopeBinding>);
impl fmt::Debug for SortedBindings<'_> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let mut entries: SmallVec<[(&CompactString, &ScopeBinding); 64]> = self.0.iter().collect();
entries.sort_unstable_by(|(left, _), (right, _)| left.as_str().cmp(right.as_str()));
f.debug_map()
.entries(
entries
.iter()
.map(|(name, binding)| (name.as_str(), *binding)),
)
.finish()
}
}
impl fmt::Debug for Scope {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("Scope")
.field("id", &self.id)
.field("parents", &self.parents)
.field("kind", &self.kind)
.field("bindings", &SortedBindings(&self.bindings))
.field("data", &self.data)
.field("span", &self.span)
.finish()
}
}
impl Scope {
#[inline]
pub fn new(id: ScopeId, parent: Option<ScopeId>, kind: ScopeKind) -> Self {
Self {
id,
parents: parent.map(|p| smallvec![p]).unwrap_or_default(),
kind,
bindings: FxHashMap::default(),
data: ScopeData::None,
span: Span::default(),
}
}
#[inline]
pub fn with_parents(id: ScopeId, parents: ParentScopes, kind: ScopeKind) -> Self {
Self {
id,
parents,
kind,
bindings: FxHashMap::default(),
data: ScopeData::None,
span: Span::default(),
}
}
#[inline]
pub fn with_span(
id: ScopeId,
parent: Option<ScopeId>,
kind: ScopeKind,
start: u32,
end: u32,
) -> Self {
Self {
id,
parents: parent.map(|p| smallvec![p]).unwrap_or_default(),
kind,
bindings: FxHashMap::default(),
data: ScopeData::None,
span: Span::new(start, end),
}
}
#[inline]
pub fn with_span_parents(
id: ScopeId,
parents: ParentScopes,
kind: ScopeKind,
start: u32,
end: u32,
) -> Self {
Self {
id,
parents,
kind,
bindings: FxHashMap::default(),
data: ScopeData::None,
span: Span::new(start, end),
}
}
#[inline]
pub fn parent(&self) -> Option<ScopeId> {
self.parents.first().copied()
}
#[inline]
pub fn add_parent(&mut self, parent: ScopeId) {
if !self.parents.contains(&parent) {
self.parents.push(parent);
}
}
#[inline]
pub fn set_data(&mut self, data: ScopeData) {
self.data = data;
}
#[inline]
pub fn data(&self) -> &ScopeData {
&self.data
}
#[inline]
pub fn add_binding(&mut self, name: CompactString, binding: ScopeBinding) {
self.bindings.insert(name, binding);
}
#[inline]
pub fn get_binding(&self, name: &str) -> Option<&ScopeBinding> {
self.bindings.get(name)
}
#[inline]
pub fn get_binding_mut(&mut self, name: &str) -> Option<&mut ScopeBinding> {
self.bindings.get_mut(name)
}
#[inline]
pub fn has_binding(&self, name: &str) -> bool {
self.bindings.contains_key(name)
}
#[inline]
pub fn bindings(&self) -> impl Iterator<Item = (&str, &ScopeBinding)> {
self.bindings.iter().map(|(k, v)| (k.as_str(), v))
}
#[inline]
pub fn binding_count(&self) -> usize {
self.bindings.len()
}
pub fn display_name(&self) -> String {
match (&self.kind, &self.data) {
(ScopeKind::ClientOnly, ScopeData::ClientOnly(data)) => {
data.hook_name
.strip_prefix("on")
.map(|s| String::from(s.to_ascii_lowercase().as_str()))
.unwrap_or_else(|| data.hook_name.clone())
}
_ => self.kind.to_display().to_compact_string(),
}
}
}
#[derive(Debug)]
pub struct ScopeChain {
pub(crate) scopes: Vec<Scope>,
pub(crate) current: ScopeId,
}
impl Default for ScopeChain {
fn default() -> Self {
Self::new()
}
}
const JS_UNIVERSAL_GLOBALS: &[&str] = &[
"AggregateError",
"arguments", "Array",
"ArrayBuffer",
"AsyncFunction",
"AsyncGenerator",
"AsyncGeneratorFunction",
"AsyncIterator",
"Atomics",
"BigInt",
"BigInt64Array",
"BigUint64Array",
"Boolean",
"console", "DataView",
"Date",
"decodeURI",
"decodeURIComponent",
"encodeURI",
"encodeURIComponent",
"Error",
"eval",
"EvalError",
"Float32Array",
"Float64Array",
"Function",
"Generator",
"GeneratorFunction",
"globalThis",
"Infinity",
"Int16Array",
"Int32Array",
"Int8Array",
"Intl",
"isFinite",
"isNaN",
"Iterator",
"JSON",
"Map",
"Math",
"NaN",
"Number",
"Object",
"parseFloat",
"parseInt",
"Promise",
"Proxy",
"RangeError",
"ReferenceError",
"Reflect",
"RegExp",
"Set",
"SharedArrayBuffer",
"String",
"Symbol",
"SyntaxError",
"this", "TypeError",
"Uint16Array",
"Uint32Array",
"Uint8Array",
"Uint8ClampedArray",
"undefined",
"URIError",
"WeakMap",
"WeakSet",
];
impl ScopeChain {
#[inline]
pub fn new() -> Self {
let mut root = Scope::new(ScopeId::ROOT, None, ScopeKind::JsGlobalUniversal);
for name in JS_UNIVERSAL_GLOBALS {
root.add_binding(
CompactString::new(name),
ScopeBinding::new(BindingType::JsGlobalUniversal, 0),
);
}
Self {
scopes: vec![root],
current: ScopeId::ROOT,
}
}
#[inline]
pub fn with_capacity(capacity: usize) -> Self {
let mut root = Scope::new(ScopeId::ROOT, None, ScopeKind::JsGlobalUniversal);
for name in JS_UNIVERSAL_GLOBALS {
root.add_binding(
CompactString::new(name),
ScopeBinding::new(BindingType::JsGlobalUniversal, 0),
);
}
let mut scopes = Vec::with_capacity(capacity);
scopes.push(root);
Self {
scopes,
current: ScopeId::ROOT,
}
}
#[inline]
pub fn current_scope(&self) -> &Scope {
unsafe { self.scopes.get_unchecked(self.current.as_u32() as usize) }
}
#[inline]
pub fn current_scope_mut(&mut self) -> &mut Scope {
let idx = self.current.as_u32() as usize;
unsafe { self.scopes.get_unchecked_mut(idx) }
}
#[inline]
pub fn get_scope(&self, id: ScopeId) -> Option<&Scope> {
self.scopes.get(id.as_u32() as usize)
}
#[inline]
pub unsafe fn get_scope_unchecked(&self, id: ScopeId) -> &Scope {
self.scopes.get_unchecked(id.as_u32() as usize)
}
#[inline]
pub const fn current_id(&self) -> ScopeId {
self.current
}
#[inline]
pub fn len(&self) -> usize {
self.scopes.len()
}
#[inline]
pub fn is_empty(&self) -> bool {
self.scopes.len() == 1
}
#[inline]
pub fn iter(&self) -> impl Iterator<Item = &Scope> {
self.scopes.iter()
}
#[inline]
pub fn find_scope_by_kind(&self, kind: ScopeKind) -> Option<ScopeId> {
self.scopes.iter().find(|s| s.kind == kind).map(|s| s.id)
}
#[inline]
pub fn get_scope_mut(&mut self, id: ScopeId) -> Option<&mut Scope> {
self.scopes.get_mut(id.as_u32() as usize)
}
#[inline]
pub fn set_current(&mut self, id: ScopeId) {
self.current = id;
}
pub(crate) fn build_template_parents(&self) -> ParentScopes {
let mut parents: ParentScopes = smallvec![self.current];
if let Some(vue_id) = self.find_scope_by_kind(ScopeKind::VueGlobal) {
if !parents.contains(&vue_id) {
parents.push(vue_id);
}
}
parents
}
}
#[cfg(test)]
#[path = "chain_tests.rs"]
mod tests;