use farmfe_macro_cache_item::cache_item;
use swc_common::DUMMY_SP;
use swc_ecma_ast::{Id, Ident, ImportSpecifier, ModuleExportName};
use crate::HashSet;
pub type StatementId = usize;
#[derive(Debug, Clone)]
#[cache_item]
pub struct Statement {
pub id: StatementId,
pub import_info: Option<ImportInfo>,
pub export_info: Option<ExportInfo>,
pub defined_idents: HashSet<SwcId>,
pub top_level_await: bool,
pub used_defined_idents: HashSet<SwcId>,
pub side_effects: StatementSideEffects,
}
impl Statement {
pub fn new(
id: StatementId,
export_info: Option<ExportInfo>,
import_info: Option<ImportInfo>,
defined_idents: HashSet<SwcId>,
top_level_await: bool,
) -> Self {
Self {
id,
import_info,
export_info,
defined_idents,
top_level_await,
used_defined_idents: HashSet::default(), side_effects: StatementSideEffects::NoSideEffects,
}
}
}
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
#[cache_item]
#[rkyv(derive(Hash, Eq, PartialEq))]
pub struct WriteTopLevelVar {
pub ident: SwcId,
pub fields: Option<Vec<UsedImportAllFields>>,
}
impl From<Id> for WriteTopLevelVar {
fn from(value: Id) -> Self {
Self {
ident: value.into(),
fields: None,
}
}
}
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
#[cache_item]
#[rkyv(derive(Hash, Eq, PartialEq))]
pub struct ReadTopLevelVar {
pub ident: SwcId,
pub is_global_var: bool,
}
impl From<Id> for ReadTopLevelVar {
fn from(value: Id) -> Self {
Self {
ident: value.into(),
is_global_var: false,
}
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
#[cache_item]
pub enum StatementSideEffects {
WriteTopLevelVar(HashSet<WriteTopLevelVar>),
ReadTopLevelVar(HashSet<ReadTopLevelVar>),
WriteOrCallGlobalVar,
UnclassifiedSelfExecuted,
NoSideEffects,
}
impl StatementSideEffects {
pub fn is_preserved(&self) -> bool {
matches!(
self,
Self::WriteOrCallGlobalVar | Self::UnclassifiedSelfExecuted
)
}
pub fn merge_side_effects(&mut self, other: Self) {
let mut original_self_value = std::mem::replace(self, Self::NoSideEffects);
match (&mut original_self_value, &other) {
(StatementSideEffects::WriteTopLevelVar(a), StatementSideEffects::WriteTopLevelVar(b)) => {
a.extend(b.iter().cloned())
}
(StatementSideEffects::WriteTopLevelVar(_), StatementSideEffects::ReadTopLevelVar(_)) => {}
(StatementSideEffects::WriteTopLevelVar(_), StatementSideEffects::WriteOrCallGlobalVar) => {
original_self_value = other;
}
(
StatementSideEffects::WriteTopLevelVar(_),
StatementSideEffects::UnclassifiedSelfExecuted,
) => {
original_self_value = other;
}
(StatementSideEffects::WriteTopLevelVar(_), StatementSideEffects::NoSideEffects) => {}
(StatementSideEffects::ReadTopLevelVar(_), StatementSideEffects::WriteTopLevelVar(_)) => {
original_self_value = other;
}
(StatementSideEffects::ReadTopLevelVar(a), StatementSideEffects::ReadTopLevelVar(b)) => {
a.extend(b.iter().cloned());
}
(StatementSideEffects::ReadTopLevelVar(_), StatementSideEffects::WriteOrCallGlobalVar) => {
original_self_value = other;
}
(
StatementSideEffects::ReadTopLevelVar(_),
StatementSideEffects::UnclassifiedSelfExecuted,
) => {
original_self_value = other;
}
(StatementSideEffects::ReadTopLevelVar(_), StatementSideEffects::NoSideEffects) => {}
(
StatementSideEffects::WriteOrCallGlobalVar | StatementSideEffects::UnclassifiedSelfExecuted,
_,
) => {}
(StatementSideEffects::NoSideEffects, _) => original_self_value = other,
}
*self = original_self_value;
}
}
#[cache_item]
#[derive(
Debug, Clone, Hash, PartialEq, Eq, PartialOrd, Ord, serde::Serialize, serde::Deserialize,
)]
#[rkyv(derive(Hash, Eq, PartialEq))]
pub struct SwcId {
pub sym: swc_atoms::Atom,
ctxt: u32,
}
impl From<Id> for SwcId {
fn from(value: Id) -> Self {
Self {
sym: value.0,
ctxt: value.1.as_u32(),
}
}
}
impl From<Ident> for SwcId {
fn from(value: Ident) -> Self {
Self {
sym: value.sym,
ctxt: value.ctxt.as_u32(),
}
}
}
impl From<&str> for SwcId {
fn from(value: &str) -> Self {
Self {
sym: value.into(),
ctxt: swc_common::SyntaxContext::empty().as_u32(),
}
}
}
impl From<SwcId> for Ident {
fn from(value: SwcId) -> Self {
let ctxt = value.ctxt();
Ident::new(value.sym, DUMMY_SP, ctxt)
}
}
impl From<SwcId> for ModuleExportName {
fn from(value: SwcId) -> Self {
ModuleExportName::Ident(value.into())
}
}
impl SwcId {
pub fn new(sym: swc_atoms::Atom, ctxt: u32) -> Self {
Self { sym, ctxt }
}
pub fn ctxt(&self) -> swc_common::SyntaxContext {
swc_common::SyntaxContext::from_u32(self.ctxt)
}
pub fn clear_ctxt(&mut self) {
self.ctxt = swc_common::SyntaxContext::empty().as_u32();
}
}
#[derive(Debug, Clone, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
#[cache_item]
#[serde(rename_all = "camelCase")]
pub enum ImportSpecifierInfo {
Namespace(SwcId),
Named {
local: SwcId,
imported: Option<SwcId>,
},
Default(SwcId),
}
impl From<&ImportSpecifier> for ImportSpecifierInfo {
fn from(value: &ImportSpecifier) -> Self {
match value {
ImportSpecifier::Named(named) => ImportSpecifierInfo::Named {
local: named.local.to_id().into(),
imported: named.imported.as_ref().map(|i| match i {
ModuleExportName::Ident(i) => i.to_id().into(),
_ => panic!("non-ident imported is not supported when tree shaking"),
}),
},
ImportSpecifier::Default(default) => {
ImportSpecifierInfo::Default(default.local.to_id().into())
}
ImportSpecifier::Namespace(ns) => ImportSpecifierInfo::Namespace(ns.local.to_id().into()),
}
}
}
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
#[cache_item]
#[serde(rename_all = "camelCase")]
pub struct ImportInfo {
pub source: String,
pub specifiers: Vec<ImportSpecifierInfo>,
pub stmt_id: StatementId,
}
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
#[cache_item]
#[serde(rename_all = "camelCase")]
pub enum ExportSpecifierInfo {
All,
Named {
local: SwcId,
exported: Option<SwcId>,
},
Default,
Namespace(SwcId),
}
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
#[cache_item]
#[serde(rename_all = "camelCase")]
pub struct ExportInfo {
pub source: Option<String>,
pub specifiers: Vec<ExportSpecifierInfo>,
pub stmt_id: StatementId,
}
impl ExportInfo {
pub fn contains_default_export(&self) -> bool {
self
.specifiers
.iter()
.any(|s| matches!(s, ExportSpecifierInfo::Default))
}
}
#[derive(Debug, Default, Hash, PartialEq, Eq, Clone)]
#[cache_item]
#[rkyv(derive(Hash, Eq, PartialEq))]
pub enum UsedImportAllFields {
#[default]
All,
Ident(String),
LiteralComputed(String),
}