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

[WIP] Plugins come back! #2066

Closed
wants to merge 7 commits into from
Closed

[WIP] Plugins come back! #2066

wants to merge 7 commits into from

Conversation

alfonsogarciacaro
Copy link
Member

After all the problems we're having with the declaration of React components (see for example fable-compiler/fable-react#195) I'm trying now to implement again a plugin system for Fable (as it was in the very first version), so we can declare components through an attribute (as it's done in ReasonReact) and use some compiler magic to overcome the issues we're having to declare React function components in F#.

This new plugin system tries to solve two of the main problems we had in Fable 1:

  • Plugins had access to the full Fable AST, so any change in the AST broke all the plugins.
  • Assemblies containing the plugins had to be referenced in Fable options, which made it difficult for a library to depend on a plugin, as there was no way to warn the user.

In this proposal:

  • Plugins only have access to a simplified version of the Fable AST that will live in a different package and (hopefully) should be more stable and easier to maintain than Fable full AST.

The drawback is the plugins won't be able to do complex code transformations, but this may also be a good thing. The main purpose of the plugins will be to "wrap" the generated code.

  • Attributes themselves can become plugins just by implementing an interface and can be declared in the library that needs them.

  • Another advantage is that (I believe) we can make these plugins work with the JSified Fable so they could work in the REPL too.


It's not done yet, but the idea is in Fable.React we can declare an attribute similar to this:

open Fable.SimpleAst

type ReactComponentAttribute(?memoize: bool) =
    inherit System.Attribute()
    interface TransformDeclaration with
        member _.TransformDeclaration(logger, fullName, decl) =
            match decl with
            | ValueDeclaration _ -> logger.LogWarning("Expecting a function"); decl
            | FunctionDeclaration(args, body) ->
                // Do some magic here to turn the function into a proper React component

And use it as follows:

[<ReactComponent>]
let root model dispatch =
   // render code

Thoughts? @MangelMaxime @ncave @Zaid-Ajaj @forki et al

@gitpod-io
Copy link

gitpod-io bot commented Jun 1, 2020

@MangelMaxime
Copy link
Member

If it can help improve Fable interop I think this is good.

I mean using the attribute + plugin combination, we can isolate a specific interop problem and take strong decision to solve as it will not break impact all the code generated by Fable.

@alfonsogarciacaro alfonsogarciacaro changed the base branch from master to nagareyama July 2, 2020 05:49
@CedricDumont
Copy link

Hi,
Would this allow to change some behaviour of Fable's generation ?
example :
let test = ""
currently generates:
export const test = "";
Instead of a const I would like a let
export let test = "";

I would then decorate my declaration with the attribute and it would generate what I am expecting ?

@alfonsogarciacaro
Copy link
Member Author

alfonsogarciacaro commented Aug 7, 2020

@CedricDumont Well, you had to write a plugin for that but I think it should be possible. Though it's something I wouldn't personally recommend, as exporting mutable values is dangerous, see #986

@CedricDumont
Copy link

CedricDumont commented Aug 7, 2020

@alfonsogarciacaro I understand your point about mutable values (and totally agree with you), but...

  • fable does compile F# to javascript
  • after this process, a webpack loader might also be applied to that generated js
  • that particular loader might want to check for export let variable and do something with them.

=> that is my case

so the generated javascript from fable might not always be the last step in the build process, and other frameworks might rely on javascript's strange behavior...
(perhaps better than write a plugin, this could be done by an option during generation.... option = exportLetVariableAsLet, or keepES6StrangeDecision....)

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 this pull request may close these issues.

3 participants