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

typescript definition not honoring JS output directory #9

Open
sebastian opened this issue Apr 5, 2021 · 11 comments
Open

typescript definition not honoring JS output directory #9

sebastian opened this issue Apr 5, 2021 · 11 comments

Comments

@sebastian
Copy link

Firstly, thanks @alfonsogarciacaro for creating this tool!

I just got started playing with the tool, so chances are high this is user error. If so, my apologies ahead of time.

I have created an Elmish Svelte Store and annotated the makeStore function with [<SveltePlugins.GenerateDeclaration>] in the hopes that I would get a typescript d.ts file as well. When compiling with Fable, I get nothing besides a .js version of my F# script.

I am using:

  • Fable.SvelteStore: 1.0.0-beta-006
  • Fable: 3.1.5 (this is slightly higher than in your samples which use 3.1.1, but downgrading to 3.1.1 does not seem to make any difference on my end)
  • dotnet 5.0.103

I'll try to debug a bit more thoroughly tomorrow (when my head is fresher), but wanted to put down some notes while it's fresh.

The F# file I am compiling has a makeStore function that looks like this:

[<SveltePlugins.GenerateDeclaration>]
let makeStore () =
  let dispatchRef: (Msg -> unit) ref = ref (fun _ -> ())
  let hub = SignalR.connect<Shared.SignalRHub.Action, _, _, Shared.SignalRHub.Response, _>(fun hub -> ...)

  let store, dispatch = SvelteStore.makeElmish (fun () -> init hub) update (fun _ -> hub.stopNow()) ()
  store, SvelteStore.makeDispatcher dispatch

the generated JavaScript ends up looking like this:

export function makeStore() {
    // omitted some generated code for brevity ...
    const patternInput = makeElmish(() => init(hub_2), update, (_arg5) => {
        HubConnection$5__stopNow(hub_2);
    }, void 0);
    const store = patternInput[0];
    const dispatch = patternInput[1];
    return [store, {
        serverMsg: (Item) => dispatch(new Msg(0, Item)),
        unexpectedServerResponse: () => dispatch(new Msg(1)),
        setConnected: (Item) => dispatch(new Msg(2, Item)),
        createElection: () => dispatch(new Msg(3)),
        addCandidate: (name) => dispatch(new Msg(4, name)),
    }];
}

The code is mostly as I would expect, but unfortunately the type definition interface file is missing.

The dotnet fable command I am using is modeled after the one you are using in your samples, namely: dotnet fable watch ../Absolutally.Client/ -o src/bin --exclude Fable.SveltePlugins

I have also tried using the built in --typescript flag in Fable (dotnet fable watch ../Absolutally.Client/ -o src/bin --typescript --typedArrays false), but unfortunately it ends up spitting out any types across the board, which unfortunately doesn't help me much further (but that's a fable problem, not a Fable.Store problem, hence a problem for another day):

    ...
    const store = patternInput[0];
    const dispatch = patternInput[1];
    return [store, {
        serverMsg: (Item: any): any => dispatch(new Msg(0, Item)),
        unexpectedServerResponse: (): any => dispatch(new Msg(1)),
        setConnected: (Item: any): any => dispatch(new Msg(2, Item)),
        createElection: (): any => dispatch(new Msg(3)),
        addCandidate: (name: any): any => dispatch(new Msg(4, name)),
    }];
}

Any pointers are of course welcome!

@sebastian
Copy link
Author

sebastian commented Apr 5, 2021

Just as an FYI: I tried creating a very simple reproduction of the bug, but when I did the d.ts file was created... This is slightly mysterious. I'll look into it more carefully tomorrow.

One notable difference I noticed: in my original project my F# file was compiled down to FileName.js, whereas in the repro repository it became FileName.fs.js (i.e. one retained the .fs the other did not). This is slightly odd too, but maybe it rings a bell of sorts.

@sebastian
Copy link
Author

Aha! I found the problem! The d.ts file is generated alongside the original F# file, and does not honour the --outDir flag given to fable! Hurra! One step forward!

@sebastian
Copy link
Author

I uploaded the mini repro. You can find it here: https://github.com/sebastian/fable.store-repro

What I noticed is that the dispatch function isn't typed? Is this intentional? https://github.com/sebastian/fable.store-repro/blob/main/Library.fs.d.ts#L5

@sebastian sebastian changed the title No type script definition created typescript definition not honoring JS output directory Apr 6, 2021
@alfonsogarciacaro
Copy link
Member

Hi @sebastian! Thanks a lot for reporting and investigating! And sorry for the late reply 😅 I've cloned and tested your repro. Unfortunately both problems seem to have roots in the Fable compiler itself so I need to fix them there, I will open issues accordingly:

  • I forgot to take into account when you put files in an output directory 🤦 Thanks for catching that! Unfortunately right now Fable is not passing the output directory info to the plugins, so I need to fix that.
  • The dispatch function not being typed: the problem is in the Fable AST, IDispatcher doesn't get the generic info of the Msg arg so the plugin cannot generate the type info. Again this seems to be an issue of the Fable compiler, after trying several things I realized that it worked when using fable 3.1.1 (as in the samples of this repo) but not with fable 3.1.5 (as in your clone) or later. I need to investigate what happened between those releases.

@sebastian
Copy link
Author

Thank you very much for investigating! One day I'll take a deeper look at Fable itself too so I can become a better citizen of the community and contribute to the core engine, so to speak, too!

Thank you for all your work!

@alfonsogarciacaro
Copy link
Member

Hi @sebastian! I've pushed new versions of Fable and the Svelte plugins that hopefully fix the issues. To update the library and the fable compiler you can do the following (in the .fsproj directory):

dotnet add package Fable.SveltePlugins -v 1.0.0-beta-006
dotnet tool update fable

Then run dotnet fable watch ../Absolutally.Client/ -o src/bin (the --exclude argument shouldn't be necessary).

Could you please give it a try and let me know if it works? Thank you!

@sebastian
Copy link
Author

sebastian commented Apr 17, 2021

This is wonderful!

I am now using:

  • fable 3.1.15
  • Fable.Store 1.0.0-beta-005
  • Fable.SvelteStore 1.0.0-beta-006

What works and what doesn't:

  • ✅ The generated typescript definition now has proper types for the dispatch function - this is great!
  • Unfortunately the output directory still doesn't seem to be respected. The definitions file is put right next to the source file, and not alongside the generated javascript

As an aside, and this likely(?) is a Svelte issue (I am rather new to Svelte). When using lists in my store, rather than arrays, then Svelte doesn't want to iterate over them in templates as it requires something that is array-like. I'd argue a list can be iterated, but maybe that's just me.

I.e. the following is not permitted from my Fable.Store store

  {#each $store.NumbersList as n}
    <div>{n}</div>
  {/each}

This does work however, but is nasty:

  {#each [...($store.NumbersList)] as n}
    <div>{n}</div>
  {/each}

Have you come across this too?

@alfonsogarciacaro
Copy link
Member

Thanks @sebastian! Sorry, it's a bit confusing but the plugins are in a different package (Fable.SveltePlugins) so that's the one that needs to be updated. Anyways, I've updated Fable.SvelteStore to bump the dependency so if you donwload v1.0.0-beta-007 it should work.

About F# lists, yes, they're implemented as linked lists so if Svelte wants an ArrayLike (to use an array index, I assume) it won't work (the JS spread ... operator does work though because the Fable list implementation adds Symbol.iterator). If you want to interop with a JS library using collections, the easiest solution is to use array. It's a bit less idiomatic in F# but if you're careful to use only immutable methods (Array.map, etc) they will work more or less like lists in F# (pattern matching works too) and they can be used as "normal" arrays from JS.

@sebastian
Copy link
Author

Sorry, it's a bit confusing but the plugins are in a different package (Fable.SveltePlugins)

My bad! You made the upgrade instructions super clear, and I should have paid a bit more attention. It works really well now!

Thank you again!

@sebastian
Copy link
Author

Do you have a donation button somewhere, so I can send you a coffee? I couldn't find one on your github page.

@alfonsogarciacaro
Copy link
Member

Thanks a lot for the nice words @sebastian! I haven't set any sponsorship. Just by reporting issues you are already helping a lot :) And if you build something cool with Fable and Svelte please do share! In any case, there are other big contributors to the Fable/F# community with open sponsorships, like Zaid-Ajaj (in his Github page) or the OpenCollective for Ionide.

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

No branches or pull requests

2 participants