vue_compiler_core/transformer/
collect_entities.rs1use super::{BaseFor, BaseIf, BaseInfo, BaseRenderSlot, BaseText, BaseVNode, BaseVSlot, CorePass};
5use crate::converter::{BaseRoot, IRNode as IR, JsExpr as Js};
6use crate::flags::{HelperCollector, RuntimeHelper as RH};
7use crate::util::{get_vnode_call_helper, VStr};
8use rustc_hash::FxHashSet;
9use std::mem::swap;
10
11#[derive(Default)]
12pub struct EntityCollector<'a> {
13 helpers: HelperCollector,
14 components: FxHashSet<VStr<'a>>,
15 directives: FxHashSet<VStr<'a>>,
16}
17
18impl<'a> CorePass<BaseInfo<'a>> for EntityCollector<'a> {
19 fn exit_root(&mut self, r: &mut BaseRoot<'a>) {
20 if r.body.len() > 1 {
21 self.helpers.collect(RH::Fragment);
22 }
23 let scope = &mut r.top_scope;
24 swap(&mut scope.helpers, &mut self.helpers);
25 swap(&mut scope.components, &mut self.components);
26 swap(&mut scope.directives, &mut self.directives);
27 }
28 fn exit_js_expr(&mut self, e: &mut Js) {
29 match e {
30 Js::Call(h, ..) | Js::Symbol(h) => {
31 self.helpers.collect(*h);
32 }
33 _ => {}
34 }
35 }
36 fn exit_if(&mut self, i: &mut BaseIf) {
37 if i.branches.iter().all(|b| b.condition.is_some()) {
38 self.helpers.collect(RH::CreateComment);
39 }
40 }
41 fn exit_for(&mut self, f: &mut BaseFor<'a>) {
42 if let IR::AlterableSlot(_) = &*f.child {
43 return self.helpers.collect(RH::RenderList);
45 }
46 self.helpers.collect(RH::OpenBlock);
47 self.helpers.collect(RH::CreateElementBlock);
48 self.helpers.collect(RH::RenderList);
49 self.helpers.collect(RH::Fragment);
50 }
51 fn exit_vnode(&mut self, v: &mut BaseVNode<'a>) {
52 if !v.directives.is_empty() {
53 self.helpers.collect(RH::WithDirectives);
54 for dir in v.directives.iter() {
56 if let Js::StrLit(d) = dir.name {
57 self.directives.insert(d);
58 }
59 }
60 }
61 if v.is_block {
62 self.helpers.collect(RH::OpenBlock);
63 }
64 let h = get_vnode_call_helper(v);
65 self.helpers.collect(h);
66 if !v.is_component {
67 return;
68 }
69 if let Some(tag) = is_hoisted_asset(&v.tag) {
72 self.helpers.collect(RH::ResolveComponent);
73 self.components.insert(*tag);
74 }
75 let mut hoisted_dir_names = v
77 .directives
78 .iter()
79 .map(|dir| &dir.name)
80 .filter_map(is_hoisted_asset)
81 .peekable();
82 if hoisted_dir_names.peek().is_some() {
83 self.helpers.collect(RH::ResolveDirective);
84 }
85 for dir_name in hoisted_dir_names {
86 self.directives.insert(*dir_name);
87 }
88 }
89 fn exit_slot_outlet(&mut self, _: &mut BaseRenderSlot<'a>) {
90 self.helpers.collect(RH::RenderSlot);
91 }
92 fn exit_v_slot(&mut self, s: &mut BaseVSlot<'a>) {
93 if !s.alterable_slots.is_empty() {
94 self.helpers.collect(RH::CreateSlots);
95 }
96 self.helpers.collect(RH::WithCtx);
97 }
98 fn exit_comment(&mut self, _: &mut &str) {
99 self.helpers.collect(RH::CreateComment);
100 }
101
102 fn exit_text(&mut self, t: &mut BaseText<'a>) {
103 if !t.fast_path {
104 self.helpers.collect(RH::CreateText);
105 }
106 }
107}
108
109pub fn is_hoisted_asset<'a, 'b>(expr: &'b Js<'a>) -> Option<&'b VStr<'a>> {
110 match expr {
111 Js::Simple(n, _) if VStr::is_asset(n) => Some(n),
112 _ => None,
113 }
114}
115
116#[cfg(test)]
117mod test {
118 use super::super::test::{base_convert, get_transformer};
119 use super::*;
120 use crate::transformer::Transformer;
121 fn transform(s: &str) -> BaseRoot {
122 let mut transformer = get_transformer(EntityCollector::default());
123 let mut ir = base_convert(s);
124 transformer.transform(&mut ir);
125 ir
126 }
127 #[test]
128 fn test_v_if_helper() {
129 let ir = transform("<p v-if='a'/>");
130 let helpers = ir.top_scope.helpers;
131 assert!(helpers.contains(RH::CreateComment));
132 }
133 #[test]
134 fn test_v_for_helper() {
135 let ir = transform("<p v-for='a in b'/>");
136 let helpers = ir.top_scope.helpers;
137 assert!(helpers.contains(RH::Fragment));
138 assert!(helpers.contains(RH::OpenBlock));
139 assert!(helpers.contains(RH::RenderList));
140 assert!(!helpers.contains(RH::CreateComment));
141 }
142 #[test]
143 fn test_v_for_alterable_helper() {
144 let ir = transform(
145 "
146 <comp>
147 <template #slot v-for='a in b'/>
148 </comp>",
149 );
150 let helpers = ir.top_scope.helpers;
151 assert!(!helpers.contains(RH::Fragment));
152 assert!(!helpers.contains(RH::CreateElementBlock));
153 assert!(helpers.contains(RH::RenderList));
154 assert!(helpers.contains(RH::WithCtx));
155 }
156}