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

Path imported by `[<ReactComponent(import="...", from="...")>] should not be relative to the caller side #624

Open
MangelMaxime opened this issue Sep 22, 2024 · 3 comments

Comments

@MangelMaxime
Copy link
Contributor

I have a Code.tsx file with a Components.fs file where I am binding the component for usage in F#:

[<ReactComponent(import="default", from="./${outDir}/Code.tsx")>]
let Code (code: string) (language : string) = React.imported()

Then in another file from another directory, I am calling the F# Code function to use the component. The generated code is:

import Code from "./Code.tsx";

The issue is that ./Code.tsx is invalid because the caller file is not inside of the same directory as Components.fs:

.
├── Components
│   ├── Code.tsx
│   ├── Components.fs
│   └── Components.fs.js
├── Utils
│   ├── Html.fs

The import path should be relative to where the API is defined.

IHMO this should be the default behaviour otherwise, it means that if you need to use a JSX component then you need to re-declare it several times 😅

A workaround, is to create a standard Feliz binding using Interop.reactApi.createElement to we can use Fable special macro${outDir}:

type Components =

    static member Code (code: string) (language: string) =
        Interop.reactApi.createElement (
            import "default" "${outDir}/Code.tsx",
            {|
                code = code
                language = language
            |}
        )

It is important to not mark this member as inline otherwise it will not work because the call site is not stable.

@Zaid-Ajaj
Copy link
Owner

Hi there @MangelMaxime so the problem here is that we need access to the value of outDir from the compiler. Here is where use it:

let makeImport (selector: string) (path: string) =
    Fable.Import({ Selector = selector.Trim()
                   Path = path.Trim()
                   Kind = Fable.UserImport(false) }, Fable.Any, None)

Maybe Fable compiler should automatically replace ${outDir} with its value when encountering Fable.Import AST node? 🤔

OR Fable could expose the outDir as an environment variable such that we get access to it in compile-time:

let path = 
  match env "FABLE_OUT_DIR" with
  | Some outDir -> path.Replace("${outDir}", outDir).Trim()
  | None -> path.Trim()

@MangelMaxime
Copy link
Contributor Author

Replacing ${outDir} automatically was my idea but I need to check when it happens in Fable.

One reason to do it this way, is like that plugins don't know to worry about path and we don't have to duplicate the logic once per plugin.

@Zaid-Ajaj
Copy link
Owner

One reason to do it this way, is like that plugins don't know to worry about path and we don't have to duplicate the logic once per plugin.

Yeah it makes more sense to do that in the compiler ✅ you probably need to traverse the resulting AST from compiler plugins, check for Fable.Import nodes and update their Path if it contains the${outDir} macro

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