Skip to content

Commit

Permalink
enh: expose transform_sfc fn and support used_idents
Browse files Browse the repository at this point in the history
  • Loading branch information
phoenix-ru committed Oct 11, 2023
1 parent ff55af8 commit 995c2cd
Show file tree
Hide file tree
Showing 10 changed files with 201 additions and 67 deletions.
1 change: 1 addition & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

22 changes: 8 additions & 14 deletions crates/fervid/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -42,10 +42,7 @@ pub mod parser;

use fervid_codegen::CodegenContext;
pub use fervid_core::*;
use fervid_transform::{
script::transform_and_record_scripts, structs::ScopeHelper,
template::transform_and_record_template,
};
use fervid_transform::transform_sfc;
use swc_core::ecma::ast::Expr;

/// Naive implementation of the SFC compilation, meaning that:
Expand All @@ -60,28 +57,25 @@ pub fn compile_sync_naive(source: &str) -> Result<String, String> {
// })?;

let mut errors = Vec::new();
let mut sfc = fervid_parser::parse_sfc(&source, &mut errors).map_err(|err| {
let sfc = fervid_parser::parse_sfc(&source, &mut errors).map_err(|err| {
return err.to_string();
})?;

let mut scope_helper = ScopeHelper::default();
let transform_result =
transform_and_record_scripts(sfc.script_setup, sfc.script_legacy, &mut scope_helper);
// TODO Return template used variables as a part of transformation result.
// Also `used_imports`? `vue_imports`? User imports?
let transform_result = transform_sfc(sfc);

let mut ctx = CodegenContext::default();
ctx.used_imports = transform_result.used_vue_imports;

let template_expr: Option<Expr> = sfc.template.as_mut().map(|template_block| {
transform_and_record_template(template_block, &mut scope_helper);
let template_expr: Option<Expr> = transform_result.template_block.map(|template_block| {
ctx.generate_sfc_template(&template_block)
});

// Add imports from script transformation (because macros generate new imports)
ctx.used_imports |= transform_result.added_imports;

let sfc_module = ctx.generate_module(
template_expr,
transform_result.module,
transform_result.export_obj,
transform_result.exported_obj,
transform_result.setup_fn,
);

Expand Down
1 change: 1 addition & 0 deletions crates/fervid_transform/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ license = "Apache-2.0"

[dependencies]
fervid_core = { path="../fervid_core", version = "0.0.1" }
fxhash = "0.2.1"
lazy_static = "1.4.0"
phf = { version = "0.11", features = ["macros"] }
swc_core = { version = "0.83.*", features = ["common", "ecma_ast", "ecma_visit"] }
Expand Down
50 changes: 50 additions & 0 deletions crates/fervid_transform/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,10 @@
use fervid_core::{SfcDescriptor, SfcTemplateBlock, VueImportsSet, BindingTypes, FervidAtom};
use fxhash::FxHashMap as HashMap;
use script::transform_and_record_scripts;
use structs::ScopeHelper;
use swc_core::ecma::ast::{Module, ObjectLit, Function};
use template::transform_and_record_template;

#[macro_use]
extern crate lazy_static;

Expand All @@ -8,3 +15,46 @@ pub mod template;

#[cfg(test)]
mod test_utils;

pub struct TransformSfcResult {
/// Object exported from the `Module`, but detached from it
pub exported_obj: ObjectLit,
/// Module obtained by processing `<script>` and `<script setup>`
pub module: Module,
/// Setup function (not linked to default export yet)
pub setup_fn: Option<Box<Function>>,
/// Transformed template block
pub template_block: Option<SfcTemplateBlock>,
/// All imports `from 'vue'`
pub used_vue_imports: VueImportsSet,
/// Identifiers used in the template and their respective binding types
pub used_idents: HashMap<FervidAtom, BindingTypes>
}

/// Applies all the necessary transformations to the SFC.
///
/// The transformations can be fine-tuned by using individual `transform_` functions.
pub fn transform_sfc(sfc_descriptor: SfcDescriptor) -> TransformSfcResult {
let mut template_block = None;

let mut scope_helper = ScopeHelper::default();
let transform_result = transform_and_record_scripts(
sfc_descriptor.script_setup,
sfc_descriptor.script_legacy,
&mut scope_helper,
);

if let Some(mut template) = sfc_descriptor.template {
transform_and_record_template(&mut template, &mut scope_helper);
template_block = Some(template);
}

TransformSfcResult {
exported_obj: transform_result.export_obj,
module: transform_result.module,
setup_fn: transform_result.setup_fn,
template_block,
used_idents: scope_helper.used_idents,
used_vue_imports: transform_result.added_imports,
}
}
3 changes: 3 additions & 0 deletions crates/fervid_transform/src/script.rs
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,9 @@ pub fn transform_and_record_scripts(
script_legacy: Option<SfcScriptBlock>,
scope_helper: &mut ScopeHelper,
) -> TransformScriptsResult {
// Set `is_inline` flag in `ScopeHelper`
scope_helper.is_inline = script_legacy.is_none() && script_setup.is_some();

//
// STEP 1: Transform Options API `<script>`.
//
Expand Down
15 changes: 7 additions & 8 deletions crates/fervid_transform/src/script/setup.rs
Original file line number Diff line number Diff line change
Expand Up @@ -57,9 +57,8 @@ pub fn transform_and_record_script_setup(
}

ModuleItem::Stmt(stmt) => {
// todo actual analysis and transformation as in `fervid_script`
if let Some(transformed_stmt) = transform_and_record_stmt(
&stmt,
stmt,
&mut scope_helper.setup_bindings,
&vue_imports,
&mut sfc_object_helper,
Expand Down Expand Up @@ -208,19 +207,19 @@ mod tests {
setup: Vec<SetupBinding>,
}

fn analyze_mock(module: &Module) -> MockAnalysisResult {
fn analyze_mock(module: Module) -> MockAnalysisResult {
let mut imports = Vec::new();
let mut vue_imports = VueResolvedImports::default();
let mut setup = Vec::new();
let mut sfc_object = Default::default();

for module_item in module.body.iter() {
match *module_item {
for module_item in module.body.into_iter() {
match module_item {
ModuleItem::ModuleDecl(ModuleDecl::Import(ref import_decl)) => {
collect_imports(import_decl, &mut imports, &mut vue_imports)
}

ModuleItem::Stmt(ref stmt) => {
ModuleItem::Stmt(stmt) => {
transform_and_record_stmt(stmt, &mut setup, &mut vue_imports, &mut sfc_object);
}

Expand All @@ -241,15 +240,15 @@ mod tests {
.expect("analyze_js expects the input to be parseable")
.0;

analyze_mock(&parsed)
analyze_mock(parsed)
}

fn analyze_ts(input: &str) -> MockAnalysisResult {
let parsed = parse_typescript_module(input, 0, Default::default())
.expect("analyze_ts expects the input to be parseable")
.0;

analyze_mock(&parsed)
analyze_mock(parsed)
}

macro_rules! test_js_and_ts {
Expand Down
4 changes: 2 additions & 2 deletions crates/fervid_transform/src/script/setup/statements.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ use super::macros::transform_script_setup_macro_expr;
/// 2. The top-level statements in `<script>` when using mixed `<script>` + `<script setup>`;
/// 3. The insides of `setup()` function in `<script>` Options API.
pub fn transform_and_record_stmt(
stmt: &Stmt,
stmt: Stmt,
out: &mut Vec<SetupBinding>,
vue_imports: &VueResolvedImports,
sfc_object_helper: &mut SfcExportedObjectHelper,
Expand All @@ -39,7 +39,7 @@ pub fn transform_and_record_stmt(
}

// By default, just return the copied statement
Some(stmt.to_owned())
Some(stmt)
}

/// Analyzes the declaration in `script setup` context.
Expand Down
7 changes: 5 additions & 2 deletions crates/fervid_transform/src/structs.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
//! Exports data structs used by the crate
use fervid_core::{BindingTypes, VueImportsSet};
use fervid_core::{BindingTypes, VueImportsSet, FervidAtom};
use fxhash::FxHashMap as HashMap;
use swc_core::ecma::{atoms::JsWord, ast::{Id, Expr, PropOrSpread, Module, ObjectLit, Function, ExprOrSpread}};
use smallvec::SmallVec;

Expand Down Expand Up @@ -44,7 +45,9 @@ pub struct ScopeHelper {
pub setup_bindings: Vec<SetupBinding>,
pub options_api_vars: Option<Box<ScriptLegacyVars>>,
pub is_inline: bool,
pub transform_mode: TemplateGenerationMode
pub transform_mode: TemplateGenerationMode,
/// Identifiers used in the template and their respective binding types
pub used_idents: HashMap<FervidAtom, BindingTypes>,
}

/// https://github.com/vuejs/rfcs/discussions/503
Expand Down
2 changes: 1 addition & 1 deletion crates/fervid_transform/src/template/ast_transform.rs
Original file line number Diff line number Diff line change
Expand Up @@ -309,6 +309,7 @@ impl<'a> Visitor for TemplateVisitor<'_> {
self.current_scope = scope_to_use;

// Transform the VBind and VOn attributes
let patch_hints = &mut element_node.patch_hints;
for attr in element_node.starting_tag.attributes.iter_mut() {
match attr {
// The logic for the patch flags:
Expand All @@ -321,7 +322,6 @@ impl<'a> Visitor for TemplateVisitor<'_> {
let has_bindings = self
.scope_helper
.transform_expr(&mut v_bind.value, scope_to_use);
let patch_hints = &mut element_node.patch_hints;

let Some(StrOrExpr::Str(ref argument)) = v_bind.argument else {
// This is dynamic
Expand Down
Loading

0 comments on commit 995c2cd

Please sign in to comment.