vue_compiler_core/transformer/
mod.rs

1/*!
2Transform IRNode.
3This module contains the canonical transformations from vue-next and
4the original ones for the parity of features not implemented in Convert.
5
6## Canonical
7* hoistStatic
8* transformExpression
9* ~~vOnce (moved to convert)~~
10* ~~vMemo (moved to convert)~~
11* trackScopes
12
13## Original
14* collect_entities:
15track all helpers/components/directives used in AST.
16Vue track it by helper/helperString.
17* optimize_text:
181. merge consecutive text call
192. wrap text in createTextVNode
20* patch_flag:
21seems patch flag can be extracted out
22 */
23
24pub mod collect_entities;
25pub mod mark_patch_flag;
26pub mod mark_slot_flag;
27pub mod optimize_text;
28pub mod pass;
29pub mod process_expression;
30
31use super::converter::{
32    self as C, BaseConvertInfo as BaseInfo, BaseRoot, ConvertInfo, IRNode, IRRoot, JsExpr as Js,
33    RuntimeDir,
34};
35pub use pass::{CorePass, CorePassExt, MergedPass, Scope};
36use std::marker::PhantomData;
37
38pub trait Transformer {
39    type IR;
40    /// transform will change ir node inplace
41    /// usually transform will have multiple passes
42    fn transform(&mut self, root: &mut Self::IR);
43}
44
45#[derive(Default)]
46pub struct TransformOption {
47    is_ts: bool,
48    inline: bool,
49    prefix_identifier: bool,
50    is_dev: bool,
51}
52
53pub type BaseText<'a> = C::TextIR<BaseInfo<'a>>;
54pub type BaseIf<'a> = C::IfNodeIR<BaseInfo<'a>>;
55pub type BaseFor<'a> = C::ForNodeIR<BaseInfo<'a>>;
56pub type BaseVNode<'a> = C::VNodeIR<BaseInfo<'a>>;
57pub type BaseRenderSlot<'a> = C::RenderSlotIR<BaseInfo<'a>>;
58pub type BaseVSlot<'a> = C::VSlotIR<BaseInfo<'a>>;
59pub type BaseSlotFn<'a> = C::Slot<BaseInfo<'a>>;
60
61struct NoopTransformer<T>(PhantomData<T>);
62
63impl<T> Transformer for NoopTransformer<T> {
64    type IR = T;
65    fn transform(&mut self, _root: &mut Self::IR) {
66        // noop
67    }
68}
69
70trait CoreTransformer<T: ConvertInfo, P: CorePass<T>>: Transformer {
71    fn transform_root(root: &mut IRRoot<T>, ps: &mut P);
72    fn transform_js_expr(e: &mut T::JsExpression, ps: &mut P);
73
74    fn transform_ir(ir: &mut IRNode<T>, ps: &mut P) {
75        use IRNode as I;
76        match ir {
77            I::TextCall(t) => Self::transform_text(t, ps),
78            I::If(i) => Self::transform_if(i, ps),
79            I::For(f) => Self::transform_for(f, ps),
80            I::VNodeCall(v) => Self::transform_vnode(v, ps),
81            I::RenderSlotCall(r) => Self::transform_slot_outlet(r, ps),
82            I::CommentCall(c) => Self::transform_comment(c, ps),
83            I::VSlotUse(s) => Self::transform_v_slot(s, ps),
84            I::AlterableSlot(a) => Self::transform_slot_fn(a, ps),
85        }
86    }
87    fn transform_children(children: &mut Vec<IRNode<T>>, ps: &mut P) {
88        for child in children.iter_mut() {
89            Self::transform_ir(child, ps);
90        }
91    }
92    fn transform_text(t: &mut C::TextIR<T>, ps: &mut P) {
93        ps.enter_text(t);
94        for text in t.texts.as_mut().iter_mut() {
95            Self::transform_js_expr(text, ps);
96        }
97        ps.exit_text(t);
98    }
99    fn transform_if(i: &mut C::IfNodeIR<T>, ps: &mut P) {
100        ps.enter_if(i);
101        for branch in i.branches.iter_mut() {
102            if let Some(c) = branch.condition.as_mut() {
103                Self::transform_js_expr(c, ps);
104            }
105            Self::transform_ir(&mut branch.child, ps);
106        }
107        ps.exit_if(i);
108    }
109    fn transform_for(f: &mut C::ForNodeIR<T>, ps: &mut P) {
110        // 1. first transform source in for node
111        Self::transform_js_expr(&mut f.source, ps);
112        use crate::converter::ForParseResult;
113        // 2. process renderList param
114        // val, key, index should counted as param
115        let ForParseResult { value, key, index } = &mut f.parse_result;
116        ps.enter_fn_param(value);
117        if let Some(k) = key {
118            ps.enter_fn_param(k);
119        }
120        if let Some(i) = index {
121            ps.enter_fn_param(i);
122        }
123
124        // 3. the for itsel
125        ps.enter_for(f);
126        Self::transform_ir(&mut f.child, ps);
127        ps.exit_for(f);
128
129        let ForParseResult { value, key, index } = &mut f.parse_result;
130        if let Some(i) = index {
131            Self::transform_js_expr(i, ps);
132            ps.exit_fn_param(i);
133        }
134        if let Some(k) = key {
135            Self::transform_js_expr(k, ps);
136            ps.exit_fn_param(k);
137        }
138        Self::transform_js_expr(value, ps);
139        ps.exit_fn_param(value);
140    }
141    fn transform_vnode(v: &mut C::VNodeIR<T>, ps: &mut P) {
142        ps.enter_vnode(v);
143        Self::transform_js_expr(&mut v.tag, ps);
144        if let Some(props) = v.props.as_mut() {
145            Self::transform_js_expr(props, ps);
146        }
147        Self::transform_children(&mut v.children, ps);
148        for dir in v.directives.iter_mut() {
149            Self::transform_runtime_dir(dir, ps);
150        }
151        ps.exit_vnode(v);
152    }
153    fn transform_runtime_dir(dir: &mut RuntimeDir<T>, ps: &mut P) {
154        Self::transform_js_expr(&mut dir.name, ps);
155        if let Some(expr) = dir.expr.as_mut() {
156            Self::transform_js_expr(expr, ps);
157        }
158        if let Some(arg) = dir.arg.as_mut() {
159            Self::transform_js_expr(arg, ps);
160        }
161        if let Some(mods) = dir.mods.as_mut() {
162            Self::transform_js_expr(mods, ps);
163        }
164    }
165    fn transform_slot_outlet(r: &mut C::RenderSlotIR<T>, ps: &mut P) {
166        ps.enter_slot_outlet(r);
167        Self::transform_js_expr(&mut r.slot_name, ps);
168        if let Some(props) = r.slot_props.as_mut() {
169            Self::transform_js_expr(props, ps);
170        }
171        Self::transform_children(&mut r.fallbacks, ps);
172        ps.exit_slot_outlet(r);
173    }
174    fn transform_v_slot(s: &mut C::VSlotIR<T>, ps: &mut P) {
175        ps.enter_v_slot(s);
176        for slot in s.stable_slots.iter_mut() {
177            Self::transform_slot_fn(slot, ps);
178        }
179        for slot in s.alterable_slots.iter_mut() {
180            Self::transform_ir(slot, ps);
181        }
182        ps.exit_v_slot(s);
183    }
184    fn transform_slot_fn(slot: &mut C::Slot<T>, ps: &mut P) {
185        ps.enter_slot_fn(slot);
186        Self::transform_js_expr(&mut slot.name, ps);
187        // slot param as fn_param, note: visit param after slot_fn
188        // since v-slot has no bind props that depend on slot param
189        if let Some(p) = &mut slot.param {
190            ps.enter_fn_param(p);
191        }
192        Self::transform_children(&mut slot.body, ps);
193        if let Some(p) = &mut slot.param {
194            Self::transform_js_expr(p, ps);
195            ps.exit_fn_param(p);
196        }
197        ps.exit_slot_fn(slot);
198    }
199    fn transform_comment(c: &mut T::CommentType, ps: &mut P) {
200        ps.enter_comment(c);
201        ps.exit_comment(c);
202    }
203}
204
205pub struct BaseTransformer<'a, P: CorePass<BaseInfo<'a>>> {
206    pass: P,
207    pd: PhantomData<&'a ()>,
208}
209impl<'a, P: CorePass<BaseInfo<'a>>> BaseTransformer<'a, P> {
210    pub fn new(pass: P) -> Self {
211        Self {
212            pass,
213            pd: PhantomData,
214        }
215    }
216}
217
218impl<'a, P: CorePass<BaseInfo<'a>>> Transformer for BaseTransformer<'a, P> {
219    type IR = BaseRoot<'a>;
220    fn transform(&mut self, node: &mut Self::IR) {
221        Self::transform_root(node, &mut self.pass);
222    }
223}
224
225impl<'a, P> CoreTransformer<BaseInfo<'a>, P> for BaseTransformer<'a, P>
226where
227    P: CorePass<BaseInfo<'a>>,
228{
229    fn transform_root(r: &mut IRRoot<BaseInfo<'a>>, ps: &mut P) {
230        ps.enter_root(r);
231        Self::transform_children(&mut r.body, ps);
232        ps.exit_root(r);
233    }
234
235    fn transform_js_expr(e: &mut Js<'a>, ps: &mut P) {
236        ps.enter_js_expr(e);
237        match e {
238            Js::Call(_, args) => {
239                for arg in args.iter_mut() {
240                    Self::transform_js_expr(arg, ps);
241                }
242            }
243            Js::Compound(exprs) => {
244                for expr in exprs.iter_mut() {
245                    Self::transform_js_expr(expr, ps);
246                }
247            }
248            Js::Array(arr) => {
249                for item in arr.iter_mut() {
250                    Self::transform_js_expr(item, ps);
251                }
252            }
253            Js::Props(props) => {
254                for (key, val) in props.iter_mut() {
255                    Self::transform_js_expr(key, ps);
256                    Self::transform_js_expr(val, ps);
257                }
258            }
259            Js::Src(_)
260            | Js::Num(_)
261            | Js::Simple(..)
262            | Js::Param(_)
263            | Js::StrLit(_)
264            | Js::Symbol(_) => {
265                // no further recursion.
266            }
267        }
268        ps.exit_js_expr(e);
269    }
270}
271
272#[cfg(test)]
273mod test {
274    use super::pass::{MergedPass, Scope, SharedInfoPasses};
275    use super::*;
276    pub use crate::converter::test::base_convert;
277    use rustc_hash::FxHashMap;
278    pub fn get_transformer<'a, P>(pass: P) -> BaseTransformer<'a, P>
279    where
280        P: CorePass<BaseInfo<'a>> + 'static,
281    {
282        BaseTransformer {
283            pass,
284            pd: PhantomData,
285        }
286    }
287
288    pub fn transformer_ext<'a, 'b>(
289        passes: &'b mut [&'b mut dyn CorePassExt<BaseInfo<'a>, Scope<'a>>],
290    ) -> BaseTransformer<
291        'a,
292        SharedInfoPasses<'b, &'b mut dyn CorePassExt<BaseInfo<'a>, Scope<'a>>, Scope<'a>>,
293    > {
294        let pass = SharedInfoPasses {
295            passes: MergedPass::new(passes),
296            shared_info: Scope {
297                identifiers: FxHashMap::default(),
298            },
299        };
300        BaseTransformer {
301            pass,
302            pd: PhantomData,
303        }
304    }
305}