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".
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: 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
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.
In .fsproj
<Reference Include="../Fable/build/fable/Fable.Core.dll" />
In .fsx
#r "../Fable/build/fable/Fable.Core.dll"
dotnet ../Fable/build/fable/Fable.Core.dll npm-run start
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.
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!