Skip to content

Commit

Permalink
feat: add spans and patch flags
Browse files Browse the repository at this point in the history
  • Loading branch information
phoenix-ru committed Sep 26, 2023
1 parent b6c88d1 commit cf0d09a
Show file tree
Hide file tree
Showing 20 changed files with 581 additions and 152 deletions.
6 changes: 3 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ All-In-One Vue compiler written in Rust.

Currently in early development, and the closest goal is to reach feature-parity with the current [Vue SFC compiler](https://sfc.vuejs.org).

## Progress till MVP ![](https://geps.dev/progress/68)
## Progress till MVP ![](https://geps.dev/progress/70)
A minimal target of this project includes:
- Vue 3 code generation;
- [unplugin](https://github.com/unjs/unplugin) integration;
Expand All @@ -20,7 +20,7 @@ A minimal target of this project includes:
This project uses [Vue SFC playground](https://sfc.vuejs.org) as its reference to compare the output. As of April 2023, fervid is capable of producing the DEV code almost identical to the official compiler, except for:
- [WIP] Context variables. This includes usages like `{{ foo + bar.buzz }}` or `<div v-if="isShown">`.
These usages require a JavaScript parser and transformer like SWC and support for them in fervid is currently ongoing.
- Patch flags. These are used to help Vue runtime when diffing the VNodes. If a VNode only has one prop which is dynamic, and all the other props and text are static, this needs to be conveyed to Vue for fast updates. I am currently researching how they are originally implemented.
- [WIP] Patch flags. These are used to help Vue runtime when diffing the VNodes. If a VNode only has one prop which is dynamic, and all the other props and text are static, this needs to be conveyed to Vue for fast updates. I am currently researching how they are originally implemented.

To check correctness of fervid, you can compare the [run log](run.log) to the output of playground. For doing so, go to https://sfc.vuejs.org and paste in the contents of [crates/fervid/benches/fixtures/input.vue](crates/fervid/benches/fixtures/input.vue).

Expand Down Expand Up @@ -133,7 +133,7 @@ Code generator
- [ ] Eager pre-compilation of Vue imports (avoid unneccessary bundler->compiler calls)

Integrations
- [ ] WASM binary (with/without WASI)
- [x] WASM binary (unpublished)
- [x] NAPI binary (unpublished)
- [ ] [unplugin](https://github.com/unjs/unplugin)
- [ ] [Turbopack](https://github.com/vercel/turbo) plugin (when plugin system is defined)
2 changes: 2 additions & 0 deletions crates/fervid/src/parser/attributes.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ use nom::{
sequence::{delimited, preceded},
Err, IResult,
};
use swc_core::common::DUMMY_SP;

use crate::parser::{
ecma::{parse_js, parse_js_pat},
Expand Down Expand Up @@ -359,6 +360,7 @@ fn parse_directive<'i>(
argument,
value: *model_binding,
modifiers,
span: DUMMY_SP, // TODO
});
}
Result::Err(_) => {}
Expand Down
6 changes: 6 additions & 0 deletions crates/fervid/src/parser/core.rs
Original file line number Diff line number Diff line change
Expand Up @@ -122,6 +122,7 @@ fn parse_root_block<'a>(
out.template = Some(SfcTemplateBlock {
lang,
roots: Vec::new(),
span: DUMMY_SP, // TODO
});

return Ok(input);
Expand All @@ -137,6 +138,7 @@ fn parse_root_block<'a>(
out.template = Some(SfcTemplateBlock {
lang,
roots: children,
span: DUMMY_SP, // TODO
});

return Ok(input);
Expand Down Expand Up @@ -301,6 +303,8 @@ pub fn parse_element_node(input: &str) -> IResult<&str, Node> {
children: vec![],
template_scope: 0,
kind: ElementKind::Element,
patch_hints: Default::default(),
span: DUMMY_SP, // TODO
}),
));
}
Expand All @@ -326,6 +330,8 @@ pub fn parse_element_node(input: &str) -> IResult<&str, Node> {
children,
template_scope: 0,
kind: ElementKind::Element,
patch_hints: Default::default(),
span: DUMMY_SP, // TODO
}),
))
}
Expand Down
34 changes: 24 additions & 10 deletions crates/fervid_codegen/src/builtins/component.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,20 +2,17 @@
//! Please do not confuse with the user components.
use fervid_core::{AttributeOrBinding, ElementNode, StrOrExpr, VBindDirective, VueImports};
use swc_core::{
common::DUMMY_SP,
ecma::{
ast::{CallExpr, Callee, Expr, ExprOrSpread, Ident, Lit, ObjectLit, PropOrSpread, Str},
atoms::JsWord,
},
use swc_core::ecma::{
ast::{CallExpr, Callee, Expr, ExprOrSpread, Ident, Lit, ObjectLit, PropOrSpread, Str},
atoms::JsWord,
};

use crate::CodegenContext;

impl CodegenContext {
/// Generates the `<component>` builtin
pub fn generate_component_builtin(&mut self, element_node: &ElementNode) -> Expr {
let span = DUMMY_SP; // TODO
let span = element_node.span;

// Shortcut
let attributes = &element_node.starting_tag.attributes;
Expand Down Expand Up @@ -94,13 +91,11 @@ impl CodegenContext {

let component_builtin_slots = self.generate_builtin_slots(element_node);

let patch_flag = 0; // TODO This comes from the attributes

self.generate_componentlike(
identifier,
component_builtin_attrs,
component_builtin_slots,
patch_flag,
&element_node.patch_hints,
true,
span,
)
Expand All @@ -115,6 +110,7 @@ mod tests {
AttributeOrBinding, BuiltinType, ElementKind, Node, StartingTag, VSlotDirective,
VueDirectives,
};
use swc_core::common::DUMMY_SP;

use crate::test_utils::js;

Expand All @@ -135,6 +131,8 @@ mod tests {
},
children: vec![],
template_scope: 0,
patch_hints: Default::default(),
span: DUMMY_SP,
},
r#""#,
);
Expand All @@ -159,6 +157,8 @@ mod tests {
},
children: vec![],
template_scope: 0,
patch_hints: Default::default(),
span: DUMMY_SP,
},
r#"(_openBlock(),_createBlock(_resolveDynamicComponent("div")))"#,
);
Expand All @@ -183,6 +183,8 @@ mod tests {
},
children: vec![],
template_scope: 0,
patch_hints: Default::default(),
span: DUMMY_SP,
},
r#"(_openBlock(),_createBlock(_resolveDynamicComponent(foo)))"#,
);
Expand Down Expand Up @@ -217,6 +219,8 @@ mod tests {
},
children: vec![],
template_scope: 0,
patch_hints: Default::default(),
span: DUMMY_SP,
},
r#"(_openBlock(),_createBlock(_resolveDynamicComponent("div"),{foo:"bar",baz:qux}))"#,
)
Expand All @@ -238,6 +242,8 @@ mod tests {
},
children: vec![Node::Text("foobar")],
template_scope: 0,
patch_hints: Default::default(),
span: DUMMY_SP,
},
r#"(_openBlock(),_createBlock(_resolveDynamicComponent("div"),null,{"default":_withCtx(()=>[_createTextVNode("foobar")]),_:1}))"#,
)
Expand Down Expand Up @@ -275,8 +281,12 @@ mod tests {
},
children: vec![Node::Text("foobar")],
template_scope: 0,
patch_hints: Default::default(),
span: DUMMY_SP,
})],
template_scope: 0,
patch_hints: Default::default(),
span: DUMMY_SP,
},
r#"(_openBlock(),_createBlock(_resolveDynamicComponent("div"),null,{named:_withCtx(()=>[_createTextVNode("foobar")]),_:1}))"#,
)
Expand Down Expand Up @@ -332,9 +342,13 @@ mod tests {
},
children: vec![Node::Text("bazqux")],
template_scope: 0,
patch_hints: Default::default(),
span: DUMMY_SP,
}),
],
template_scope: 0,
patch_hints: Default::default(),
span: DUMMY_SP,
},
r#"(_openBlock(),_createBlock(_resolveDynamicComponent("div"),{foo:"bar",baz:qux},{named:_withCtx(()=>[_createTextVNode("bazqux")]),"default":_withCtx(()=>[_createTextVNode("foobar")]),_:1}))"#,
)
Expand Down
30 changes: 22 additions & 8 deletions crates/fervid_codegen/src/builtins/keepalive.rs
Original file line number Diff line number Diff line change
@@ -1,15 +1,12 @@
use fervid_core::{ElementNode, VueImports};
use swc_core::{
common::DUMMY_SP,
ecma::ast::{ArrayLit, Expr, ExprOrSpread, Ident},
};
use fervid_core::{ElementNode, PatchFlags, PatchFlagsSet, PatchHints, VueImports};
use swc_core::ecma::ast::{ArrayLit, Expr, ExprOrSpread, Ident};

use crate::CodegenContext;

impl CodegenContext {
/// Generates `(_openBlock(), _createBlock(_KeepAlive, null, [keepalive_children], 1024))`
pub fn generate_keepalive(&mut self, element_node: &ElementNode) -> Expr {
let span = DUMMY_SP; // TODO
let span = element_node.span;

// _KeepAlive
let keepalive_identifier = Expr::Ident(Ident {
Expand Down Expand Up @@ -41,13 +38,21 @@ impl CodegenContext {
};

let should_wrap_in_block = keepalive_children.is_some();
let patch_flag = if should_wrap_in_block { 1024 } else { 0 };

let patch_hints = PatchHints {
flags: if should_wrap_in_block {
PatchFlagsSet::from(PatchFlags::DynamicSlots)
} else {
PatchFlagsSet::default()
},
props: vec![],
};

self.generate_componentlike(
keepalive_identifier,
keepalive_attrs,
keepalive_children,
patch_flag,
&patch_hints,
should_wrap_in_block,
span,
)
Expand All @@ -57,6 +62,7 @@ impl CodegenContext {
#[cfg(test)]
mod tests {
use fervid_core::{AttributeOrBinding, BuiltinType, ElementKind, Node, StartingTag};
use swc_core::common::DUMMY_SP;

use crate::test_utils::js;

Expand All @@ -75,6 +81,8 @@ mod tests {
},
children: vec![],
template_scope: 0,
patch_hints: Default::default(),
span: DUMMY_SP,
},
r#"_createVNode(_KeepAlive)"#,
)
Expand Down Expand Up @@ -105,6 +113,8 @@ mod tests {
},
children: vec![],
template_scope: 0,
patch_hints: Default::default(),
span: DUMMY_SP,
},
r#"_createVNode(_KeepAlive,{foo:"bar",baz:qux})"#,
)
Expand All @@ -123,6 +133,8 @@ mod tests {
},
children: vec![Node::Text("foobar")],
template_scope: 0,
patch_hints: Default::default(),
span: DUMMY_SP,
},
r#"(_openBlock(),_createBlock(_KeepAlive,null,[_createTextVNode("foobar")],1024))"#,
)
Expand Down Expand Up @@ -153,6 +165,8 @@ mod tests {
},
children: vec![Node::Text("foobar")],
template_scope: 0,
patch_hints: Default::default(),
span: DUMMY_SP,
},
r#"(_openBlock(),_createBlock(_KeepAlive,{foo:"bar",baz:qux},[_createTextVNode("foobar")],1024))"#,
)
Expand Down
34 changes: 21 additions & 13 deletions crates/fervid_codegen/src/builtins/slot.rs
Original file line number Diff line number Diff line change
@@ -1,13 +1,10 @@
use fervid_core::{AttributeOrBinding, ElementNode, StrOrExpr, VBindDirective, VueImports};
use swc_core::{
common::DUMMY_SP,
ecma::{
ast::{
ArrayLit, CallExpr, Callee, Expr, ExprOrSpread, Ident, Lit, MemberExpr, MemberProp,
ObjectLit, Str,
},
atoms::{js_word, JsWord},
use swc_core::ecma::{
ast::{
ArrayLit, CallExpr, Callee, Expr, ExprOrSpread, Ident, Lit, MemberExpr, MemberProp,
ObjectLit, Str,
},
atoms::{js_word, JsWord},
};

use crate::CodegenContext;
Expand All @@ -20,7 +17,7 @@ impl CodegenContext {
/// renderSlot(_ctx.$slots, "slot-name", /*optional*/ { slot: attributes }, /*optional*/ [slot, children])
/// ```
pub fn generate_slot(&mut self, element_node: &ElementNode) -> Expr {
let span = DUMMY_SP; // TODO
let span = element_node.span;

// The `name` attribute should NOT be generated,
// therefore we split attributes generation to two slices, like so:
Expand Down Expand Up @@ -184,6 +181,7 @@ impl CodegenContext {
#[cfg(test)]
mod tests {
use fervid_core::{BuiltinType, ElementKind, Node, StartingTag};
use swc_core::common::DUMMY_SP;

use crate::test_utils::js;

Expand All @@ -200,6 +198,8 @@ mod tests {
},
children: $children,
template_scope: 0,
patch_hints: Default::default(),
span: DUMMY_SP,
}
};
}
Expand Down Expand Up @@ -375,7 +375,9 @@ mod tests {
directives: None
},
children: vec![Node::Text("Placeholder")],
template_scope: 0
template_scope: 0,
patch_hints: Default::default(),
span: DUMMY_SP,
}),
Node::Element(ElementNode {
kind: ElementKind::Component,
Expand All @@ -385,7 +387,9 @@ mod tests {
directives: None
},
children: vec![],
template_scope: 0
template_scope: 0,
patch_hints: Default::default(),
span: DUMMY_SP,
})
]
),
Expand Down Expand Up @@ -427,7 +431,9 @@ mod tests {
directives: None
},
children: vec![Node::Text("Placeholder")],
template_scope: 0
template_scope: 0,
patch_hints: Default::default(),
span: DUMMY_SP,
}),
Node::Element(ElementNode {
kind: ElementKind::Component,
Expand All @@ -437,7 +443,9 @@ mod tests {
directives: None
},
children: vec![],
template_scope: 0
template_scope: 0,
patch_hints: Default::default(),
span: DUMMY_SP,
})
]
),
Expand Down
Loading

0 comments on commit cf0d09a

Please sign in to comment.