@samitouri / QOS-React-2 / commits / 0e4656ac1c

[rust][wip] swc->estree for jsx

Joe Savona committed Jul 28, 2023 at 16:12 UTC 0e4656ac1ce6c37e2292025fa7bc52db02c0327b
7 files changed +409 -41
compiler/forget/crates/forget_build_hir/src/build.rs
+56 -3
@@ -1,6 +1,6 @@
1 use std::collections::HashSet;
2
3 -use bumpalo::collections::String;
3 +use bumpalo::collections::{CollectIn, String};
4 use forget_diagnostics::Diagnostic;
5 use forget_estree::{
6 AssignmentTarget, BinaryExpression, BlockStatement, Expression, ExpressionOrSpread,
@@ -9,8 +9,8 @@ use forget_estree::{
9 };
10 use forget_hir::{
11 BlockKind, BranchTerminal, Environment, ForTerminal, GotoKind, IdentifierOperand, InstrIx,
12 - InstructionKind, InstructionValue, LValue, LoadGlobal, LoadLocal, Operand, PlaceOrSpread,
13 - PrimitiveValue, TerminalValue,
12 + InstructionKind, InstructionValue, JSXAttribute, JSXElement, LValue, LoadGlobal, LoadLocal,
13 + Operand, PlaceOrSpread, PrimitiveValue, TerminalValue,
14 };
15
16 use crate::builder::{Binding, Builder, LoopScope};
@@ -468,6 +468,10 @@ fn lower_expression<'a>(
468 })
469 }
470
471 + Expression::JSXElement(expr) => {
472 + InstructionValue::JSXElement(lower_jsx_element(env, builder, *expr)?)
473 + }
474 +
475 _ => todo!("Lower expr {expr:#?}"),
476 };
477 Ok(builder.push(value))
@@ -530,6 +534,55 @@ fn lower_function<'a>(
534 })
535 }
536
537 +fn lower_jsx_element<'a>(
538 + env: &'a Environment<'a>,
539 + builder: &mut Builder<'a>,
540 + expr: forget_estree::JSXElement,
541 +) -> Result<JSXElement<'a>, Diagnostic> {
542 + let props: Result<Vec<JSXAttribute<'_>>, Diagnostic> = expr
543 + .opening_element
544 + .attributes
545 + .into_iter()
546 + .map(|attr| lower_jsx_attribute(env, builder, attr))
547 + .collect();
548 + let props = props?;
549 + let children: Result<Vec<Operand>, Diagnostic> = expr
550 + .children
551 + .into_iter()
552 + .map(|child| {
553 + let ix = lower_jsx_child(env, builder, child)?;
554 + Ok(Operand { effect: None, ix })
555 + })
556 + .collect();
557 + let children = children?;
558 + todo!("lower jsx element");
559 + // Ok(JSXElement {
560 + // tag: todo!(),
561 + // props,
562 + // children: if children.is_empty() {
563 + // None
564 + // } else {
565 + // Some(children)
566 + // },
567 + // })
568 +}
569 +
570 +fn lower_jsx_attribute<'a>(
571 + env: &'a Environment<'a>,
572 + builder: &mut Builder<'a>,
573 + attr: forget_estree::JSXAttributeOrSpread,
574 +) -> Result<JSXAttribute<'a>, Diagnostic> {
575 + todo!("lower jsx attribute")
576 +}
577 +
578 +fn lower_jsx_child<'a>(
579 + env: &'a Environment<'a>,
580 + builder: &mut Builder<'a>,
581 + child: forget_estree::JSXChildItem,
582 +) -> Result<InstrIx, Diagnostic> {
583 + todo!("lower jsx child")
584 +}
585 +
586 fn lower_assignment<'a>(
587 env: &'a Environment<'a>,
588 builder: &mut Builder<'a>,
compiler/forget/crates/forget_estree/src/generated.rs
+3
@@ -1429,6 +1429,9 @@ impl Serialize for ExportAllDeclaration {
1429 #[derive(Deserialize, Clone, Debug)]
1430 pub struct JSXIdentifier {
1431 pub name: String,
1432 + #[serde(skip)]
1433 + #[serde(default)]
1434 + pub binding: Option<Binding>,
1435 #[serde(default)]
1436 pub loc: Option<SourceLocation>,
1437 #[serde(default)]
compiler/forget/crates/forget_estree/src/snapshots/forget_estree__tests__fixtures@test.json.snap
+15
@@ -395,7 +395,10 @@ Output:
395 "type": "JSXMemberExpression",
396 "object": {
397 "type": "JSXIdentifier",
398 + "type": "Identifier",
399 "name": "Foo",
400 + "loc": null,
401 + "range": null,
402 "loc": {
403 "source": null,
404 "start": {
@@ -414,7 +417,10 @@ Output:
417 },
418 "property": {
419 "type": "JSXIdentifier",
420 + "type": "Identifier",
421 "name": "Bar",
422 + "loc": null,
423 + "range": null,
424 "loc": {
425 "source": null,
426 "start": {
@@ -452,7 +458,10 @@ Output:
458 "type": "JSXAttribute",
459 "name": {
460 "type": "JSXIdentifier",
461 + "type": "Identifier",
462 "name": "a",
463 + "loc": null,
464 + "range": null,
465 "loc": {
466 "source": null,
467 "start": {
@@ -552,7 +561,10 @@ Output:
561 "type": "JSXMemberExpression",
562 "object": {
563 "type": "JSXIdentifier",
564 + "type": "Identifier",
565 "name": "Foo",
566 + "loc": null,
567 + "range": null,
568 "loc": {
569 "source": null,
570 "start": {
@@ -571,7 +583,10 @@ Output:
583 },
584 "property": {
585 "type": "JSXIdentifier",
586 + "type": "Identifier",
587 "name": "Bar",
588 + "loc": null,
589 + "range": null,
590 "loc": {
591 "source": null,
592 "start": {
compiler/forget/crates/forget_estree_codegen/src/ecmascript.json
+5
@@ -681,6 +681,11 @@
681 "fields": {
682 "name": {
683 "type": "String"
684 + },
685 + "binding": {
686 + "type": "Option<Binding>",
687 + "optional": true,
688 + "skip": true
689 }
690 }
691 },
compiler/forget/crates/forget_estree_swc/src/lib.rs
+290 -38
@@ -8,11 +8,13 @@ use swc_core::common::errors::Handler;
8 use swc_core::common::source_map::Pos;
9 use swc_core::common::{FileName, FilePathMapping, Mark, SourceMap, Span, SyntaxContext, GLOBALS};
10 use swc_core::ecma::ast::{
11 - AssignOp, BinaryOp, BlockStmt, BlockStmtOrExpr, CallExpr, Callee, Decl, EsVersion, Expr,
12 - ExprOrSpread, Function, Ident, Lit, MemberExpr, MemberProp, ModuleItem, OptChainBase, Pat,
13 - PatOrExpr, Program, Stmt, UnaryOp, VarDecl, VarDeclKind, VarDeclOrExpr,
11 + AssignOp, BinaryOp, BlockStmt, BlockStmtOrExpr, Callee, Decl, EsVersion, Expr, ExprOrSpread,
12 + Function, Ident, JSXAttr, JSXAttrName, JSXAttrOrSpread, JSXAttrValue, JSXElement,
13 + JSXElementChild, JSXElementName, JSXExpr, JSXMemberExpr, JSXObject, Lit, MemberExpr,
14 + MemberProp, ModuleItem, OptChainBase, Pat, PatOrExpr, Program, Stmt, UnaryOp, VarDecl,
15 + VarDeclKind, VarDeclOrExpr,
16 };
15 -use swc_core::ecma::parser::Syntax;
17 +use swc_core::ecma::parser::{Syntax, TsConfig};
18 use swc_core::ecma::transforms::base::resolver;
19 use swc_core::ecma::visit::FoldWith;
20
@@ -35,7 +37,10 @@ pub fn parse(
37 fm.clone(),
38 &handler,
39 EsVersion::Es5,
38 - Syntax::Typescript(Default::default()),
40 + Syntax::Typescript(TsConfig {
41 + tsx: true,
42 + ..Default::default()
43 + }),
44 swc::config::IsModule::Bool(true),
45 Some(&comments),
46 )?;
@@ -340,13 +345,20 @@ fn convert_expression(cx: &Context, expr: &Expr) -> forget_estree::Expression {
345 range: convert_span(&expr.span),
346 }))
347 }
343 - Expr::Await(_expr) => {
344 - // forget_estree::Expression::AwaitExpression(Box::new(forget_estree::AwaitExpression {
345 - // argument: convert_expression(cx, &expr.arg),
346 - // loc: None,
347 - // range: convert_span(&expr.span),
348 - // }))
349 - todo!("await expression")
348 + Expr::Await(expr) => {
349 + forget_estree::Expression::AwaitExpression(Box::new(forget_estree::AwaitExpression {
350 + argument: convert_expression(cx, &expr.arg),
351 + loc: None,
352 + range: convert_span(&expr.span),
353 + }))
354 + }
355 + Expr::Yield(expr) => {
356 + forget_estree::Expression::YieldExpression(Box::new(forget_estree::YieldExpression {
357 + argument: expr.arg.as_ref().map(|arg| convert_expression(cx, arg)),
358 + is_delegate: expr.delegate,
359 + loc: None,
360 + range: convert_span(&expr.span),
361 + }))
362 }
363 Expr::Unary(expr) => {
364 forget_estree::Expression::UnaryExpression(Box::new(forget_estree::UnaryExpression {
@@ -377,32 +389,7 @@ fn convert_expression(cx: &Context, expr: &Expr) -> forget_estree::Expression {
389 },
390 )),
391 },
380 - Expr::Lit(expr) => {
381 - let (value, range) = match expr {
382 - Lit::Bool(expr) => (
383 - forget_estree::JsValue::Bool(expr.value),
384 - convert_span(&expr.span),
385 - ),
386 - Lit::Num(expr) => (
387 - forget_estree::JsValue::Number(expr.value.into()),
388 - convert_span(&expr.span),
389 - ),
390 - Lit::Str(expr) => (
391 - forget_estree::JsValue::String(expr.value.to_string()),
392 - convert_span(&expr.span),
393 - ),
394 - Lit::Null(expr) => (forget_estree::JsValue::Null, convert_span(&expr.span)),
395 - _ => todo!(),
396 - };
397 - forget_estree::Expression::Literal(Box::new(forget_estree::Literal {
398 - value,
399 - raw: None,
400 - loc: None,
401 - regex: None,
402 - range,
403 - bigint: None,
404 - }))
405 - }
392 + Expr::Lit(expr) => forget_estree::Expression::Literal(Box::new(convert_literal(cx, expr))),
393 Expr::Assign(expr) => forget_estree::Expression::AssignmentExpression(Box::new(
394 forget_estree::AssignmentExpression {
395 operator: convert_assignment_operator(expr.op),
@@ -470,10 +457,265 @@ fn convert_expression(cx: &Context, expr: &Expr) -> forget_estree::Expression {
457 forget_estree::Expression::MemberExpression(Box::new(member))
458 }
459 },
460 + Expr::JSXElement(expr) => {
461 + forget_estree::Expression::JSXElement(Box::new(convert_jsx_element(cx, expr)))
462 + }
463 + Expr::Paren(expr) => convert_expression(cx, &expr.expr),
464 _ => todo!("translate expression {:#?}", expr),
465 }
466 }
467
468 +fn convert_jsx_element(cx: &Context, expr: &JSXElement) -> forget_estree::JSXElement {
469 + let attributes = expr
470 + .opening
471 + .attrs
472 + .iter()
473 + .map(|attr| match attr {
474 + JSXAttrOrSpread::JSXAttr(attr) => forget_estree::JSXAttributeOrSpread::JSXAttribute(
475 + Box::new(convert_jsx_attribute(cx, attr)),
476 + ),
477 + JSXAttrOrSpread::SpreadElement(attr) => {
478 + forget_estree::JSXAttributeOrSpread::JSXSpreadAttribute(Box::new(
479 + forget_estree::JSXSpreadAttribute {
480 + argument: convert_expression(cx, &attr.expr),
481 + loc: None,
482 + range: None, // sigh, no span
483 + },
484 + ))
485 + }
486 + })
487 + .collect();
488 + let opening_element = forget_estree::JSXOpeningElement {
489 + attributes,
490 + name: convert_jsx_name(cx, &expr.opening.name),
491 + self_closing: expr.opening.self_closing,
492 + loc: None,
493 + range: convert_span(&expr.opening.span),
494 + };
495 + let children = expr
496 + .children
497 + .iter()
498 + .map(|child| convert_jsx_child(cx, child))
499 + .collect();
500 + let closing_element = expr
501 + .closing
502 + .as_ref()
503 + .map(|closing| forget_estree::JSXClosingElement {
504 + name: convert_jsx_name(cx, &closing.name),
505 + loc: None,
506 + range: convert_span(&closing.span),
507 + });
508 + forget_estree::JSXElement {
509 + opening_element,
510 + children,
511 + closing_element,
512 + loc: None,
513 + range: convert_span(&expr.span),
514 + }
515 +}
516 +
517 +fn convert_jsx_name(cx: &Context, name: &JSXElementName) -> forget_estree::JSXElementName {
518 + match name {
519 + JSXElementName::Ident(name) => {
520 + forget_estree::JSXElementName::JSXIdentifier(Box::new(convert_jsx_identifier(cx, name)))
521 + }
522 + JSXElementName::JSXMemberExpr(name) => forget_estree::JSXElementName::JSXMemberExpression(
523 + Box::new(convert_jsx_member_expression(cx, name)),
524 + ),
525 + JSXElementName::JSXNamespacedName(name) => {
526 + forget_estree::JSXElementName::JSXNamespacedName(Box::new(
527 + forget_estree::JSXNamespacedName {
528 + namespace: convert_jsx_identifier(cx, &name.ns),
529 + name: convert_jsx_identifier(cx, &name.name),
530 + loc: None,
531 + range: None, // sigh, swc doesn't have a span for the entire name
532 + },
533 + ))
534 + }
535 + }
536 +}
537 +
538 +fn convert_jsx_member_expression(
539 + cx: &Context,
540 + expr: &JSXMemberExpr,
541 +) -> forget_estree::JSXMemberExpression {
542 + forget_estree::JSXMemberExpression {
543 + object: match &expr.obj {
544 + JSXObject::JSXMemberExpr(obj) => {
545 + forget_estree::JSXMemberExpressionOrIdentifier::JSXMemberExpression(Box::new(
546 + convert_jsx_member_expression(cx, obj),
547 + ))
548 + }
549 + JSXObject::Ident(obj) => forget_estree::JSXMemberExpressionOrIdentifier::JSXIdentifier(
550 + Box::new(convert_jsx_identifier(cx, obj)),
551 + ),
552 + },
553 + property: convert_jsx_identifier(cx, &expr.prop),
554 + loc: None,
555 + range: None, // sigh, swc doesn't have a span for the member expr
556 + }
557 +}
558 +
559 +fn convert_jsx_child(cx: &Context, child: &JSXElementChild) -> forget_estree::JSXChildItem {
560 + match child {
561 + JSXElementChild::JSXText(child) => {
562 + forget_estree::JSXChildItem::JSXText(Box::new(forget_estree::JSXText {
563 + value: child.value.to_string(),
564 + raw: child.raw.to_string(),
565 + loc: None,
566 + range: convert_span(&child.span),
567 + }))
568 + }
569 + JSXElementChild::JSXElement(child) => {
570 + forget_estree::JSXChildItem::JSXElement(Box::new(convert_jsx_element(cx, child)))
571 + }
572 + JSXElementChild::JSXExprContainer(child) => {
573 + forget_estree::JSXChildItem::JSXExpressionContainer(Box::new(
574 + forget_estree::JSXExpressionContainer {
575 + expression: match &child.expr {
576 + JSXExpr::Expr(expr) => forget_estree::JSXExpressionOrEmpty::Expression(
577 + convert_expression(cx, expr),
578 + ),
579 + JSXExpr::JSXEmptyExpr(expr) => {
580 + forget_estree::JSXExpressionOrEmpty::JSXEmptyExpression(Box::new(
581 + forget_estree::JSXEmptyExpression {
582 + loc: None,
583 + range: convert_span(&expr.span),
584 + },
585 + ))
586 + }
587 + },
588 + loc: None,
589 + range: convert_span(&child.span),
590 + },
591 + ))
592 + }
593 + _ => todo!("handle other jsx child types"),
594 + }
595 +}
596 +
597 +fn convert_jsx_attribute(cx: &Context, attr: &JSXAttr) -> forget_estree::JSXAttribute {
598 + let name = match &attr.name {
599 + JSXAttrName::Ident(name) => {
600 + forget_estree::JSXIdentifierOrNamespacedName::JSXIdentifier(Box::new({
601 + let ident = convert_identifier(cx, name);
602 + forget_estree::JSXIdentifier {
603 + name: ident.name,
604 + binding: ident.binding,
605 + loc: ident.loc,
606 + range: ident.range,
607 + }
608 + }))
609 + }
610 + JSXAttrName::JSXNamespacedName(name) => {
611 + forget_estree::JSXIdentifierOrNamespacedName::JSXNamespacedName(Box::new(
612 + forget_estree::JSXNamespacedName {
613 + namespace: convert_jsx_identifier(cx, &name.ns),
614 + name: convert_jsx_identifier(cx, &name.name),
615 + loc: None,
616 + range: None, // sigh, swc doesn't have a span for the entire name
617 + },
618 + ))
619 + }
620 + };
621 + let value = attr.value.as_ref().map(|attr| match attr {
622 + JSXAttrValue::Lit(value) => {
623 + forget_estree::JSXAttributeValue::Literal(Box::new(convert_literal(cx, value)))
624 + }
625 + JSXAttrValue::JSXElement(value) => {
626 + forget_estree::JSXAttributeValue::JSXElement(Box::new(convert_jsx_element(cx, value)))
627 + }
628 + JSXAttrValue::JSXExprContainer(value) => {
629 + forget_estree::JSXAttributeValue::JSXExpressionContainer(Box::new(
630 + forget_estree::JSXExpressionContainer {
631 + expression: match &value.expr {
632 + JSXExpr::Expr(expr) => forget_estree::JSXExpressionOrEmpty::Expression(
633 + convert_expression(cx, expr),
634 + ),
635 + JSXExpr::JSXEmptyExpr(expr) => {
636 + forget_estree::JSXExpressionOrEmpty::JSXEmptyExpression(Box::new(
637 + forget_estree::JSXEmptyExpression {
638 + loc: None,
639 + range: convert_span(&expr.span),
640 + },
641 + ))
642 + }
643 + },
644 + loc: None,
645 + range: convert_span(&value.span),
646 + },
647 + ))
648 + }
649 + _ => todo!("handle other attribute value types"),
650 + });
651 + forget_estree::JSXAttribute {
652 + name,
653 + value,
654 + loc: None,
655 + range: convert_span(&attr.span),
656 + }
657 +}
658 +
659 +fn convert_literal(_cx: &Context, expr: &Lit) -> forget_estree::Literal {
660 + let (value, range, regex, bigint) = match expr {
661 + Lit::Bool(expr) => (
662 + forget_estree::JsValue::Bool(expr.value),
663 + convert_span(&expr.span),
664 + None,
665 + None,
666 + ),
667 + Lit::Num(expr) => (
668 + forget_estree::JsValue::Number(expr.value.into()),
669 + convert_span(&expr.span),
670 + None,
671 + None,
672 + ),
673 + Lit::Str(expr) => (
674 + forget_estree::JsValue::String(expr.value.to_string()),
675 + convert_span(&expr.span),
676 + None,
677 + None,
678 + ),
679 + Lit::Null(expr) => (
680 + forget_estree::JsValue::Null,
681 + convert_span(&expr.span),
682 + None,
683 + None,
684 + ),
685 + Lit::Regex(expr) => (
686 + forget_estree::JsValue::Undefined,
687 + convert_span(&expr.span),
688 + Some(forget_estree::RegExpValue {
689 + pattern: expr.exp.to_string(),
690 + flags: expr.flags.to_string(),
691 + }),
692 + None,
693 + ),
694 + Lit::BigInt(expr) => (
695 + forget_estree::JsValue::Undefined,
696 + convert_span(&expr.span),
697 + None,
698 + Some(
699 + expr.raw
700 + .as_ref()
701 + .expect("Expected bigint to have a raw value")
702 + .to_string(),
703 + ),
704 + ),
705 + Lit::JSXText(_) => {
706 + panic!("Unexpected expression: JSXText is not an expression")
707 + }
708 + };
709 + forget_estree::Literal {
710 + value,
711 + raw: None,
712 + loc: None,
713 + regex,
714 + range,
715 + bigint,
716 + }
717 +}
718 +
719 fn convert_arguments(
720 cx: &Context,
721 arguments: &[ExprOrSpread],
@@ -638,6 +880,16 @@ fn convert_identifier(cx: &Context, identifier: &Ident) -> forget_estree::Identi
880 }
881 }
882
883 +fn convert_jsx_identifier(cx: &Context, identifier: &Ident) -> forget_estree::JSXIdentifier {
884 + let name = identifier.sym.as_ref().to_string();
885 + forget_estree::JSXIdentifier {
886 + name,
887 + binding: convert_binding(cx, identifier.span.ctxt),
888 + loc: None,
889 + range: convert_span(&identifier.span),
890 + }
891 +}
892 +
893 #[cfg(test)]
894 mod tests {
895 #[test]
compiler/forget/crates/forget_fixtures/tests/fixtures/jsx.js new
+9
@@ -0,0 +1,9 @@
1 +function Component(props) {
2 + return (
3 + <Foo bool={true} str="string" number={3.14} var={props.variable}>
4 + Hello
5 + {props.foo}
6 + <div>{props.bar}</div>
7 + </Foo>
8 + );
9 +}
compiler/forget/crates/forget_hir/src/instruction.rs
+31
@@ -36,6 +36,7 @@ impl<'a> Instruction<'a> {
36 | InstructionValue::LoadLocal(_)
37 | InstructionValue::Primitive(_)
38 | InstructionValue::Function(_)
39 + | InstructionValue::JSXElement(_)
40 | InstructionValue::Tombstone => {}
41 }
42 }
@@ -62,6 +63,7 @@ impl<'a> Instruction<'a> {
63 | InstructionValue::LoadLocal(_)
64 | InstructionValue::Primitive(_)
65 | InstructionValue::Function(_)
66 + | InstructionValue::JSXElement(_)
67 | InstructionValue::Tombstone => {}
68 }
69 Ok(())
@@ -83,6 +85,7 @@ impl<'a> Instruction<'a> {
85 | InstructionValue::Primitive(_)
86 | InstructionValue::StoreLocal(_)
87 | InstructionValue::Function(_)
88 + | InstructionValue::JSXElement(_)
89 | InstructionValue::Tombstone => {}
90 }
91 }
@@ -122,6 +125,20 @@ impl<'a> Instruction<'a> {
125 f(dep)
126 }
127 }
128 + InstructionValue::JSXElement(value) => {
129 + f(&mut value.tag);
130 + for attr in &mut value.props {
131 + match attr {
132 + JSXAttribute::Spread { argument } => f(argument),
133 + JSXAttribute::Attribute { name: _, value } => f(value),
134 + }
135 + }
136 + if let Some(children) = &mut value.children {
137 + for child in children {
138 + f(child)
139 + }
140 + }
141 + }
142 InstructionValue::DeclareContext(_)
143 | InstructionValue::LoadContext(_)
144 | InstructionValue::LoadGlobal(_)
@@ -147,6 +164,7 @@ pub enum InstructionValue<'a> {
164 DeclareLocal(DeclareLocal<'a>),
165 // Destructure(Destructure<'a>),
166 Function(FunctionExpression<'a>),
167 + JSXElement(JSXElement<'a>),
168 // JsxFragment(JsxFragment<'a>),
169 // JsxText(JsxText<'a>),
170 LoadContext(LoadContext),
@@ -382,6 +400,19 @@ pub struct StoreLocal<'a> {
400 pub value: Operand,
401 }
402
403 +#[derive(Debug)]
404 +pub struct JSXElement<'a> {
405 + pub tag: Operand,
406 + pub props: Vec<'a, JSXAttribute<'a>>,
407 + pub children: Option<Vec<'a, Operand>>,
408 +}
409 +
410 +#[derive(Debug)]
411 +pub enum JSXAttribute<'a> {
412 + Spread { argument: Operand },
413 + Attribute { name: String<'a>, value: Operand },
414 +}
415 +
416 #[derive(Clone, Debug)]
417 pub struct Operand {
418 pub ix: InstrIx,