Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

@react macro annotation for Scala 3.5.0 #717

Open
steinybot opened this issue Aug 4, 2024 · 5 comments · May be fixed by #736
Open

@react macro annotation for Scala 3.5.0 #717

steinybot opened this issue Aug 4, 2024 · 5 comments · May be fixed by #736

Comments

@steinybot
Copy link
Contributor

It looks like macro annotations can now modify their companion objects in Scala 3.5.0: scala/scala3#19677

@steinybot
Copy link
Contributor Author

Wait a minute... why was I thinking that this needs to go on the props class?

@steinybot
Copy link
Contributor Author

The best solution I can think of which has the fewest breaking changes is to:

  1. Add a scalafix rule which rewrites objects with @react annotations to:
    1. Extract the Props to a companion case class.
    2. Add a parent trait to the object for which a @react macro annotation can implement.
  2. The parent trait defines an abstract given Conversion[Props, KeyAddingStage] (and possibly others to avoid having to chain implicits)
  3. The @react macro annotation implements the given Conversion[Props, KeyAddingStage] using the component.

The exact shape of the props companion case class and member to implement in the companion object needs a bit more thought but this is the basic idea.

@threeseed
Copy link
Contributor

threeseed commented Dec 7, 2024

Class Component

@react
class MyComponent extends StatelessComponent {
  case class Props(int: Int, children: List[String])
  def render() = props.children
}

Expanded to:

class MyComponent(jsProps: js.Object) extends MyComponent.Definition(jsProps) {
  import MyComponent.{Props, State, Snapshot};
  override def render(): ReactElement = props.children
};

object MyComponent extends StatelessComponent.Wrapper {
  case class Props(int: Int, children: List[String])
  type Def = MyComponent;

  def apply(int: Int)(children: List[String]): slinky.core.KeyAndRefAddingStage[Def] =
    this.apply(Props(int, children))
}

External Component

@react
class MyComponent extends ExternalComponent {
  case class Props(int: Int)
  val component = MyComponent
}

Expanded to:

object MyComponent extends slinky.core.ExternalComponentWithAttributesWithRefType[Nothing, js.Object] {
  case class Props(int: Int)
  
  def apply(int: Int): BuildingComponent[Nothing, js.Object] = this.apply(Props.apply(int)).asInstanceOf[BuildingComponent[Nothing, js.Object]];
  def apply(mods: Seq[TagMod[Nothing]]): BuildingComponent[Nothing, js.Object] = new BuildingComponent[Nothing, js.Object](js.Array(component.asInstanceOf[js.Any], js.Dictionary.empty)).apply((mods: _*));
     
  def withKey(key: String): BuildingComponent[Nothing, js.Object] = new BuildingComponent[Nothing, js.Object(js.Array(component.asInstanceOf[js.Any], js.Dictionary.empty)).withKey(key);
    
  def withRef(ref: Function1[js.Object, Unit]): BuildingComponent[Nothing, js.Object] = new BuildingComponent[Nothing, js.Object](js.Array(component.asInstanceOf[js.Any], js.Dictionary.empty)).withRef(ref);
  def withRef(ref: ReactRef[js.Object]): BuildingComponent[Nothing, js.Object] = new BuildingComponent[Nothing, js.Object](js.Array(component.asInstanceOf[js.Any], js.Dictionary.empty)).withRef(ref)
}

Functional Component

@react object MyComponent {
  case class Props[T](in: Seq[T])
  val component = FunctionalComponent[Props[_]] { case Props(in) =>
    in.mkString(" ")
  }
}

Expanded to:

object MyComponent {
  case class Props[T](in: Seq[T])
  val component = FunctionalComponent[Props[_]] { case Props(in) =>
    in.mkString(" ")
  }
  def apply[T](in: Seq[T]) = component.apply(Props.apply(in));
  def apply(props: component.Props) = component.apply(props)
}

@threeseed
Copy link
Contributor

threeseed commented Dec 16, 2024

And discussion on the state of Scala 3 macros here.

@threeseed threeseed linked a pull request Jan 8, 2025 that will close this issue
@threeseed
Copy link
Contributor

threeseed commented Jan 8, 2025

@steinybot @shadaj .. Have submitted a PR which adds a new SBT Plugin for transforming the code.

I did try to use Scalafix but I couldn't get it work. But at least the Scala Meta code in the plugin should be easily transferrable.

What is nice though is that because the code is written out to src_managed you can easily debug any further changes you want to make e.g. using Scala 3 givens. Also not sure what the implication is for IntelliJ i.e. do we need that plugin any more ?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging a pull request may close this issue.

2 participants