Fable: Can't resolve 'fable-core/Foo' imports

Created on 24 May 2017  路  11Comments  路  Source: fable-compiler/Fable

Description

Under default template conditions, Webpack is unable to resolve imports from fable-core JS modules.

I have tried using --refs or tweaking webpack.config.js, but I haven't figured out how to point it to the correct file under ..\packages\Fable.Core\fable-core\Foo.js".

Repro code

Install the dotnet template:

dotnet new -i Fable.Template::*
dotnet new fable -n MyProject
yarn install
dotnet restore

Verify it compiles and runs, then add the following lines to App.fs:
(this is a key part of @Zaid-Ajaj 's Fable.Remoting module, but it happens with any fable-core import I've tried):

[<Import("ofJson",  "fable-core/Serialize")>]
let private dynamicOfJson(json: string, typeArg: obj) : obj = jsNative

Run dotnet fable npm-run start

Expected and actual results

Expected: the project compiles and runs.

Actual result:

ERROR in ./src/App.fs
Module not found: Error: Can't resolve 'fable-core/Serialize' in 'C:\fableTempl\fableTempl\src'
 @ ./src/App.fs 2:0-46
 @ ./fableTempl.fsproj
 @ multi (webpack)-dev-server/client?http://localhost:8080 ./fableTempl.fsproj

Related information

  • fable-core version: 1.0.4 and 1.0.6
  • Operating system: Win10 x64

Most helpful comment

Yes, that signature should work for the current version of Fable.Remoting. Though it's @Zaid-Ajaj's code so he would know best if something else is needed, perhaps for future releases.

I'll try sending a PR later today. Thanks!

All 11 comments

I came across this problem just yasterday. Fixed by using the import function instead of the attribute

let private dynamicOfJson(json: string, typeArg: obj) : obj = 
    import "ofJson" "../../../Fable.Core/fable-core/Serialize"

I am actually finalizing the Fable.Remoting.Client library and have been writing a very long blog about it but I am still blocked at #945

@Zaid-Ajaj

Thank you! It's not actually necessary to use the function, inserting the file path in the ImportAttribute also works.

Normally I'd say this would be a perfectly acceptable workaround since importing from Fable.Core isn't something you have to do every day, but I think it's still a showstopper for library writers since you can't guarantee how the consumer will organize his folders.

I totally agree, fable-core should actually be used as an npm dependency instead of embedding it in the nuget package.

fable-core was an npm dependency before, but this makes it very difficult to make sure both packages Fable.Core in Nuget and fable-core are in sync,

There was an intention at the beginning to have fable-core available as a library for JS projects. However, the API is not always designed to be user-friendly, it contains many hacks that assume the methods will only be called by Fable.Compiler, and it's easy that new compiler versions bring _breaking_ changes to fable-core. Because of this, in general I wouldn't recommend to use fable-core methods directly. Everything should be exposed by the F# API of Fable.Core. In the past, when a user needed something (like inflate from fable-core/Serialize), we just added a method to Fable.Core.

What would you need in this case @piaste? A method to call opJson with a System.Type instance instead of a generic argument? (like ofJson2 (json: string) (t: System.Type)) We can add that to Fable.Core :+1:

Yes, that signature should work for the current version of Fable.Remoting. Though it's @Zaid-Ajaj's code so he would know best if something else is needed, perhaps for future releases.

I'll try sending a PR later today. Thanks!

I actually wanted to use such signature from Fable.Core but I was not sure it should be just

ofJson2 (json: string) (t: System.Type)

Because I am passing the second parameter wrapped in { T : typeArg } using this helper function:

[<Emit("$2[$0] = $1")>]
let private setProp (propName: string) (propValue: obj) (any: obj) : unit = jsNative

let private makeTypeArgument (typeArg: System.Type) = 
    let empty = new obj()
    setProp "T" typeArg empty
    empty

Do I have to build an object literal in Babel AST?

Yes, it's not difficult. We should add something like this after this line:

            let args =
                match i.args with
                | [arg] -> [arg]
                | [arg; typ] -> [arg; makeJsObject None ["T", typ]]
                | _ -> failwithf "Unexpected number of arguments for %s" i.methodName

About the name of the function, I'm not sure, maybe: ofJsonWithType (json: string) (typ: System.Type): obj (there's already a ofJsonWithTypeInfo, which may be confusing)

ofJsonAsType?

@alfonsogarciacaro What do you think about still publishing latest fable-core packages on npm (even though they're also distributed through paket and paket is the preferred distribution)? Just to enable some scenarios where perhaps Fable.Core dependency is not used (but fable-core is).

I know you're trying to keep the versions in sync, I'm just asking if that would enable some advanced, unsupported, completely dangerous and irresponsible scenario, but if you have to put your foot down and say no, so be it.

@ncave This is already possible, just clone the project and run FableTools FAKE target to build Fable.Core.dll + dotnet-fable.dll, and FableCoreJs to build the fable-core JS files.

  • For Fable.Core.dll: Add a direct reference to your project instead of using the package (correct the path as necessary).

In .fsproj

<Reference Include="../Fable/build/fable/Fable.Core.dll" />

In .fsx

#r "../Fable/build/fable/Fable.Core.dll"
  • For dotnet-fable.dll: Call it directly instead of using a CLI tool.
dotnet ../Fable/build/fable/Fable.Core.dll npm-run start
  • For fable-core JS files: Pass the path in the fable-loader options in the webpack.config.js.
        use: {
            loader: resolve("../typescript/fable-loader"),
            options: {
                fableCore: resolve("../../build/fable-core"),
                define: "DOTNETCORE",
                plugins: resolve("../../build/nunit/Fable.Plugins.NUnit.dll"),
                babel: babelOptions
            }
        }

This how Fable tests work: https://github.com/fable-compiler/Fable/tree/master/src/tests

@alfonsogarciacaro
Thank you for the detailed response that shows how to reference fable-core from the Fable repo source.
I was wondering if there is any merit in continuing publishing the latest fable-core to npm on every build (if it can be automated). But I guess minimizing the maintenance effort on the fable tooling has priority over the convenience of npm package distribution, so I think I got my answer.

Was this page helpful?
0 / 5 - 0 ratings

Related issues

stkb picture stkb  路  3Comments

ncave picture ncave  路  3Comments

MangelMaxime picture MangelMaxime  路  3Comments

alfonsogarciacaro picture alfonsogarciacaro  路  3Comments

MangelMaxime picture MangelMaxime  路  3Comments