Skip to content

Commit

Permalink
extract dom-related code to separate module
Browse files Browse the repository at this point in the history
  • Loading branch information
XiNiHa committed Oct 17, 2024
1 parent 22cc559 commit 2df107e
Show file tree
Hide file tree
Showing 5 changed files with 140 additions and 86 deletions.
82 changes: 82 additions & 0 deletions crates/core/src/dom/element.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
use oxc::ast::ast;
use oxc_traverse::TraverseCtx;

use crate::shared::transform::{JsxTransform, TransformInfo, TransformResult};

impl<'a> JsxTransform {
pub fn transform_element_dom(
&self,
el: &ast::JSXElement<'a>,
ctx: &mut TraverseCtx<'a>,
) -> TransformResult<'a> {
let tag_name = match &el.opening_element.name {
ast::JSXElementName::Identifier(ident) => ident.name.clone(),
_ => {
// TODO
return TransformResult {
id: None,
template: None,
exprs: ctx.ast.vec(),
text: false,
skip_template: false,
};
}
};

let attributes = self.generate_attributes_dom(&el.opening_element.attributes);
let child_templates = self.generate_child_templates_dom(&el.children, ctx);

// TODO
let template = format!("<{}{}>{}", tag_name, attributes, child_templates);

TransformResult {
id: None,
template: Some(template),
exprs: ctx.ast.vec(),
text: false,
skip_template: false,
}
}

/// generate attributes string without quotes around values
fn generate_attributes_dom(&self, attrs: &[ast::JSXAttributeItem<'a>]) -> String {
let mut attrs_string = String::new();
for attr_item in attrs {
let ast::JSXAttributeItem::Attribute(attr) = attr_item else {
continue;
};
let ast::JSXAttributeName::Identifier(ident) = &attr.name else {
continue;
};
let name = ident.name.as_ref();

match &attr.value {
Some(ast::JSXAttributeValue::StringLiteral(str_lit)) => {
let value = str_lit.value.as_ref();
attrs_string.push_str(&format!(" {}={}", name, value));
}
Some(_) => {
// TODO
}
None => {
// attributes without a value (e.g., <input disabled />)
attrs_string.push_str(&format!(" {}", name));
}
}
}
attrs_string
}

/// Process children and collect their templates
fn generate_child_templates_dom(
&self,
children: &[ast::JSXChild<'a>],
ctx: &mut TraverseCtx<'a>,
) -> String {
let info = TransformInfo::default();
children
.iter()
.filter_map(|child| self.transform_node(child, ctx, &info)?.template)
.collect::<String>()
}
}
2 changes: 2 additions & 0 deletions crates/core/src/dom/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
pub mod element;
pub mod template;
20 changes: 20 additions & 0 deletions crates/core/src/dom/template.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
use oxc::{ast::ast, span::SPAN};
use oxc_traverse::TraverseCtx;

use crate::{
shared::transform::{TemplateCreationCtx, TransformResult},
Config,
};

impl<'a> TransformResult<'a> {
pub fn create_template_dom(
&self,
config: &Config,
traverse_ctx: &mut TraverseCtx<'a>,
creation_ctx: &mut TemplateCreationCtx<'a>,
wrap: bool,
) -> ast::Expression<'a> {
// TODO
traverse_ctx.ast.expression_null_literal(SPAN)
}
}
1 change: 1 addition & 0 deletions crates/core/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ use oxc::{
};

pub mod config;
mod dom;
mod shared;

pub use config::*;
Expand Down
121 changes: 35 additions & 86 deletions crates/core/src/shared/transform.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,18 +20,19 @@ impl JsxTransform {
}

#[derive(Default)]
struct TransformInfo {
pub struct TransformInfo {
top_level: bool,
skip_id: bool,
last_element: bool,
do_not_escape: bool,
}

struct TransformResult<'a> {
id: Option<Atom<'a>>,
template: Option<String>,
exprs: OxcVec<'a, ast::Expression<'a>>,
text: bool,
pub struct TransformResult<'a> {
pub id: Option<Atom<'a>>,
pub template: Option<String>,
pub exprs: OxcVec<'a, ast::Expression<'a>>,
pub text: bool,
pub skip_template: bool,
}

impl<'a> Traverse<'a> for JsxTransform {
Expand All @@ -51,7 +52,7 @@ impl<'a> Traverse<'a> for JsxTransform {
&Default::default(),
);
*node = result
.map(|r| r.create_template(&self.config, ctx))
.map(|r| r.create_template(&self.config, ctx, false))
.unwrap_or_else(|| ctx.ast.expression_null_literal(SPAN));
}
ast::Expression::JSXFragment(_) => {
Expand All @@ -68,7 +69,7 @@ impl<'a> Traverse<'a> for JsxTransform {
},
);
*node = result
.map(|r| r.create_template(&self.config, ctx))
.map(|r| r.create_template(&self.config, ctx, false))
.unwrap_or_else(|| ctx.ast.expression_null_literal(SPAN));
}
_ => {}
Expand All @@ -77,7 +78,7 @@ impl<'a> Traverse<'a> for JsxTransform {
}

impl<'a> JsxTransform {
fn transform_node(
pub fn transform_node(
&self,
node: &ast::JSXChild<'a>,
ctx: &mut TraverseCtx<'a>,
Expand All @@ -104,6 +105,7 @@ impl<'a> JsxTransform {
template: Some(str),
text: true,
exprs: ctx.ast.vec(),
skip_template: false,
}),
},
ast::JSXChild::ExpressionContainer(container) => {
Expand All @@ -113,6 +115,7 @@ impl<'a> JsxTransform {
template: None,
exprs: ctx.ast.vec(),
text: false,
skip_template: false,
})
}
ast::JSXChild::Spread(spread) => {
Expand All @@ -122,44 +125,23 @@ impl<'a> JsxTransform {
template: None,
exprs: ctx.ast.vec(),
text: false,
skip_template: false,
})
}
}
}

fn transform_element(
pub fn transform_element(
&self,
el: &ast::JSXElement<'a>,
ctx: &mut TraverseCtx<'a>,
) -> TransformResult<'a> {
let tag_name = match &el.opening_element.name {
ast::JSXElementName::Identifier(ident) => ident.name.clone(),
_ => {
// TODO
return TransformResult {
id: None,
template: None,
exprs: ctx.ast.vec(),
text: false,
};
}
};

let attributes = self.generate_attributes(&el.opening_element.attributes);
let child_templates = self.generate_child_templates(&el.children, ctx);

// TODO
let template = format!("<{}{}>{}", tag_name, attributes, child_templates);

TransformResult {
id: None,
template: Some(template),
exprs: ctx.ast.vec(),
text: false,
match self.config.generate {
OutputType::Dom => self.transform_element_dom(el, ctx),
}
}

fn transform_fragment_children(
pub fn transform_fragment_children(
&self,
children: &OxcVec<'a, ast::JSXChild<'a>>,
ctx: &mut TraverseCtx<'a>,
Expand Down Expand Up @@ -189,7 +171,7 @@ impl<'a> JsxTransform {
let child_result = self.transform_node(child, ctx, info);
child_result
.as_ref()
.map(|r| r.create_template(&self.config, ctx))
.map(|r| r.create_template(&self.config, ctx, true))
}
}));
TransformResult {
Expand All @@ -210,71 +192,38 @@ impl<'a> JsxTransform {
id: None,
template: None,
text: false,
skip_template: false,
}
}

/// generate attributes string without quotes around values
fn generate_attributes(&self, attrs: &[ast::JSXAttributeItem<'a>]) -> String {
let mut attrs_string = String::new();
for attr_item in attrs {
let ast::JSXAttributeItem::Attribute(attr) = attr_item else {
continue;
};
let ast::JSXAttributeName::Identifier(ident) = &attr.name else {
continue;
};
let name = ident.name.as_ref();

match &attr.value {
Some(ast::JSXAttributeValue::StringLiteral(str_lit)) => {
let value = str_lit.value.as_ref();
attrs_string.push_str(&format!(" {}={}", name, value));
}
Some(_) => {
// TODO
}
None => {
// attributes without a value (e.g., <input disabled />)
attrs_string.push_str(&format!(" {}", name));
}
}
}
attrs_string
}

/// Process children and collect their templates
fn generate_child_templates(
&self,
children: &[ast::JSXChild<'a>],
ctx: &mut TraverseCtx<'a>,
) -> String {
let info = TransformInfo::default();
children
.iter()
.filter_map(|child| {
self.transform_node(child, ctx, &info)
.and_then(|child_result| child_result.template)
})
.collect::<String>()
}
}

impl<'a> TransformResult<'a> {
fn create_template(
&self,
config: &Config,
ctx: &mut oxc_traverse::TraverseCtx<'a>,
wrap: bool,
) -> ast::Expression<'a> {
let mut creation_ctx = TemplateCreationCtx {
templates: Vec::new(),
};

match config.generate {
OutputType::Dom => {
// TODO
// if template is not null, create template => import { template as _$template } from "solid-js/web"; ... (client side rendering)
ctx.ast.expression_null_literal(SPAN)
}
OutputType::Dom => self.create_template_dom(config, ctx, &mut creation_ctx, wrap),
}
}
}

pub struct TemplateCreationCtx<'a> {
pub templates: Vec<Template<'a>>,
}

pub struct Template<'a> {
pub id: Atom<'a>,
pub template: String,
pub renderer: OutputType,
}

#[cfg(test)]
mod transform_tests {
use super::*;
Expand Down

0 comments on commit 2df107e

Please sign in to comment.