Giter Site home page Giter Site logo

fable-compiler / fable Goto Github PK

View Code? Open in Web Editor NEW
2.8K 69.0 291.0 385.36 MB

F# to JavaScript, TypeScript, Python, Rust and Dart Compiler

Home Page: http://fable.io/

License: MIT License

F# 95.93% JavaScript 0.15% TypeScript 1.10% Shell 0.03% Dart 0.37% PHP 0.25% Python 1.13% Rust 1.01% Dockerfile 0.02% Batchfile 0.01%
fsharp javascript typescript dart python rust

fable's Introduction

Fable: F# |> JS

Nuget Build Join the chat at https://gitter.im/fable-compiler/Fable

Follow us on Twitter!

Fable is an F# to JavaScript compiler powered FSharp Compiler Services, designed to make F# a first-class citizen of the JavaScript ecosystem. Check the website for more information and if you find the project useful, don't forget to give us a star!

Fable actually uses a fork of FCS with a few tweaks. Binaries are in lib/fcs folder. See this PR for more info.

Getting started

Check this page.

Building

Requirements

Use VSCode Dev Container

You can use VSCode Dev Container to get a preconfigured environment both with requirements and VSCode extensions.

  1. You need to have docker installed and running.
  2. Install the Dev Container extension in VSCode
  3. Open the project in VSCode and click on the green button in the bottom left corner.

Use your machine

Make sure the following requirements are installed in your system:

Build

Run ./builsh.sh or ./build.cmd to see the build options.

When using VSCode, you can also run the build tasks from the command palette (Ctrl+Shift+P) by typing Run Task and selecting the task you want to run.

We also configured several debug configurations that you can use from the debug panel (Ctrl+Shift+D). This is useful as you can attach the debugger to the Fable compiler process to check what's going on.

Contributing

Just by using Fable you're already contributing! You can help the community a lot by sharing examples and experiences in your personal (or Fable's) blog and/or by editing the Fable Resources page.

Send bug reports (ideally with minimal code to reproduce the problem) and feature requests to this GitHub repository. To interact with the community you can use the Gitter chat but please note maintainers are not checking the chat regularly.

If you are up to contribute a fix or a feature yourself, you're more than welcome! Please send first an issue or a minimal Work In Progess PR so we can discuss the implementation details in advance.

List of changelogs

fable's People

Contributors

alexswan10k avatar alfonsogarciacaro avatar alxandr avatar booksbaum avatar chkn avatar coolya avatar davidpodhola avatar davidtme avatar dbrattli avatar dependabot[bot] avatar entropitor avatar fdcastel avatar forki avatar inosik avatar jgrund avatar kerams avatar krzysztof-cieslak avatar mangelmaxime avatar markek avatar mastoj avatar mlaily avatar ncave avatar nojaf avatar shmew avatar thautwarm avatar thinkbeforecoding avatar tpetricek avatar valery-vitko avatar zaaack avatar zaid-ajaj avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

fable's Issues

Type and record property with the same name

Again this could be a problem with FSharp.Compiler.Service

Consider the following code:

type StyleBuilderHelper =
    { TopOffset : int
      BottomOffset : int }

type DomBuilder =
    { ElementType : string
      StyleBuilderHelper : StyleBuilderHelper }

let offsetBottom size builder =
    { builder with StyleBuilderHelper =  { builder.StyleBuilderHelper with BottomOffset = size } }

The JavaScript generated has a problem in that StyleBuilderHelper is being used as the type name and a record property name:

var offsetBottom = Program.offsetBottom = function (size, builder) {
      var StyleBuilderHelper, inputRecord;
      return StyleBuilderHelper = (inputRecord = builder.StyleBuilderHelper, new StyleBuilderHelper(inputRecord.TopOffset, size)), new DomBuilder(builder.ElementType, StyleBuilderHelper);
    };

A undefined variable is created with the name StyleBuilderHelper then the type StyleBuilderHelper tries to get constructed resulting in
StyleBuilderHelper is not a function

Tuple arguments not always interpreted correctly

The following code displays a bit of odd behaviour:

module Test =
    let start name =
        name, "1"

    let middle next (name, values) =
        printf "Middle: Name='%s', Values='%s'" name values
        (name, values + ", " + next)

    let last (name, values) =
        printf "Last: Name='%s', Values='%s'" name values


module Program =
    [<EntryPoint>]
    let main argv =
        Test.start "Test"
        |> Test.middle "2"
        |> Test.middle "3"
        |> Test.last

        0

When the code is converted and run the following output is produced

Middle: Name='Test', Values='1'
Middle: Name='Test', Values='1, 2'
Last: Name='Test,1, 2, 3', Values='undefined'

It seems the tuple array is being passed into last->name rather than been split up.

Changing Name='%s' to Name='%A' seems to fix the problem.

I don't know if it's any help but here is the broken JavaScript snippet:

var Program = Program_1.Program = function (Program) {
      (function (argv) {
        return Test.last(function () {
          var next;
          return next = "3", function (tupledArg) {
            var name, values;
            return name = tupledArg[0], values = tupledArg[1], Test.middle(next, name, values);
          };
        }()(function () {

and the working one:

    var Program = Program_1.Program = function (Program) {
      (function (argv) {
        return function (tupledArg) {
          var name = tupledArg[0];
          var values = tupledArg[1];
          Test.last(name, values);
        }(function () {
          var next;
          return next = "3", function (tupledArg) {
            var name, values;
            return name = tupledArg[0], values = tupledArg[1], Test.middle(next, name, values);
          };
        }()(function () {

Name and logo

Though I already got used to it, "Fabel" was just a code name until releasing the project (which is not happening quite yet) and the plan was to decide a new name with the community. So that's what this issue is for, together with a request for someone with better design skills than me (mine are close to zero) to propose a logo... if possible not a square with F# logo on upper left, please ;)

Waiting for your wonderful proposals, in the meanwhile, some pros and cons about current candidates:

  • FABEL: pro, may drag on Babel.js appeal among the (huge) JS community; con (by @cloudRoutine), F# projects should have names that stand on their own that aren't bound to a heritage in projects from other languages, like how Fix just changed its name to Forge. It gives the impression that they're their own entities, unconstrained by the expectations of copying the decisions and designs of their forebearers and frees them to pursue their own distinct form and destiny.
  • FABLE: like above but without the cons.
  • FABELOUS: Maybe? ;)
  • Something with "-SCRIPT": cons, there are already F# scripts and also could give the impression it's a different language, when they're exactly the same (albeit small semantic differences).

Add samples

Again, samples can be "borrowed" (or shamelessly copied) and adapted from FunScript. Issue #4 should help for the bindings. Please inform me of any missing feature (there'll be a lot now) to make the samples work.

Arrays compiled to objects? (Fable 0.1.5)

F#: [| 0; 1; 2; 3 |]

is being compiled to

Js: {"0":0,"1":1,"2":2,"3":3}

Is this correct? It shouldn't be compiled to a native array? ([0, 1, 2, 3])

Using the latest version (0.1.5). I didn't work much with arrays until now, so I can't tell if this was happening in the older versions.

Namespace problem

This code compiles ok:

namespace Test.MyNamespace

module MyModule =

    type MyType =
        | SomeInt of int
        | SomeString of string
        | None

    type MyClass() = 

        let letValue = 3
        member this.memberValue = 4

        member this.SomeMethod1() =
            printfn "%d" letValue

        member this.SomeMethod2() =
            printfn "%d" this.memberValue

and so this one:

namespace Test.MyNamespace

    type MyClass() = 

        let letValue = 3
        member this.memberValue = 4

        member this.SomeMethod1() =
            printfn "%d" letValue

        member this.SomeMethod2() =
            printfn "%d" this.memberValue

However, this one

namespace Test.MyNamespace

//module MyModule =

    type MyType =
        | SomeInt of int
        | SomeString of string
        | None

    type MyClass() = 

        let letValue = 3
        member this.memberValue = 4

        member this.SomeMethod1() =
            printfn "%d" letValue

        member this.SomeMethod2() =
            printfn "%d" this.memberValue

fails to compile with error:

Cannot find namespace Test.MyNamespace.MyClass

The error message is a bit misleading, as the real problem is with MyType. When reaching it, getRootDecls is being called with rootNs = "Test.MyNamespace.MyClass" (while the correct should be rootNs = "Test.MyNamespace").

When using a module (first example) the problem doesn't occurs. In this case getRootDecls is called with the correct namespace (rootNs = "Test.MyNamespace.MyModule") when it reaches MyType.

Designing the React Helper API

I'm currently designing an API to make it easier to interact with React from F# and the language combination is providing several interesting opportunities for static checking. However, as expected there are some rough ends and I have doubts about how to define the properties in a React DOM element. Currently we have:

Option 1: Dynamic properties

module R = Fable.ReactHelper
R.input [
    "type" ==> "text"
    "placeholder" ==> "Your name"
    "value" ==> x.state.author
    "onChange" ==> x.handleAuthorChange
] []

This is the most flexible option as we can pass any property in a plain JS object. Thanks to inlining functions we don't need to use createObj here and we also save some braces. If you think the ==> is too verbose, shortening is also trivial: let inline (=>) a b = a ==> b

The problem is that we don't have any static checking or auto completion for the properties. So I'm considering using a lambda instead. But now we have to decide where to get the signature from. At first I took the one from the IntrinsicElements defined in the React definition file:

Option 2: Mutating HTMLElement properties

R.input (fun p ->
    p.``type`` <- "text"
    p.placeholder <- "Your name"
    p.value <- unbox x.state.author
    //p.onchange <- unbox x.handleAuthorChange
    p?onChange <- x.handleAuthorChange
) []           

Now we have static checking and auto completion for the properties which is very nice. Note however that some frictions with the type definitions start to show up and need to resolved with unbox. The biggest problem with this approach is that, though the TypeScript definition uses the standard DOM elements, some attributes have a different spelling in React. In the example above, setting onchange won't work, so we have to set onChange dynamically. This almost breaks all the benefits of static checking :(

Our next option then is to use React.HTMLAttributes instead to be sure we're using the correct spelling:

Option 3: Mutating React.HTMLAttributes properties

R.input (fun p ->
    p.``type`` <- Some "text"
    p.placeholder <- Some "Your name"
    p.value <- unbox x.state.author
    p.onChange <- unbox x.handleAuthorChange
) []

Ok, all the properties have the spelling as React expects it but we now face other problems: first the frictions with the signature multiply because all properties are defined as optional and other Typescript custom elements are used like erased union types or custom-defined functions; and second, in the previous option we had the specific attributes for input tags but here every HTML element share the same attributes. However, this may still be the best option.

There'd be a fourth option but I'm not considering it very much:

Option 4: Using a custom HTMLAttributes record

R.input (fun p ->
    { p with
        ``type`` = Some "text"
        placeholder = Some "Your name"
        value = unbox x.state.author
        onChange = unbox x.handleAuthorChange
}) []

This one is likely the most idiomatic in F# but the indentation rules get more complicated ({ p with cannot be put in the first line) and we don't have auto completion here to discover the properties of p, as far as I know. Most importantly, adopting this style would require creating a custom record for HTMLAttributes as the ones from TypeScript are parsed as interfaces (there are good reasons for that), so this would add an extra step in the maintenance of the Fable.Import.React file.

Here are the options to make the decision and I would love to hear your say on this. I know it's possible to add several options to the API, but this would probably make it more confusing for newcomers and I'd prefer to make it simple at first. Thanks a lot for your help in advance!

Windows: cmd.exe + quotes = Hell

In Windows 8.1, with Node.js 5.4.0, running

node tools\fable2babel.js --projFile "C:\Temp\MyProj\Core\MyProj.Core.fsproj"

gives me this

cmd /C C:\Temp\Fable\tools/../build/main/Fable.exe {"lib":".","outDir":".","symbols":[],"watch":false,"projFile":"MyProj.Core.fsproj"}
events.js:141
      throw er; // Unhandled 'error' event
      ^

Error: spawn cmd ENOENT
    at exports._errnoException (util.js:856:11)
    at Process.ChildProcess._handle.onexit (internal/child_process.js:178:32)
    at onErrorNT (internal/child_process.js:344:16)
    at nextTickCallbackWith2Args (node.js:478:9)
    at process._tickCallback (node.js:392:17)
    at Function.Module.runMain (module.js:432:11)
    at startup (node.js:141:18)
    at node.js:1003:3

which is not a really useful error message :) -- But then, I tried

cmd /C C:\Temp\Fable\tools/../build/main/Fable.exe {"lib":".","outDir":".","symbols":[],"watch":false,"projFile":"MyProj.Core.fsproj"}

which outputs a somewhat better description of the real problem

Unhandled Exception: Newtonsoft.Json.JsonReaderException: Input string '.' is not a valid number. Path 'lib', line 1, position 6.
   at Newtonsoft.Json.JsonTextReader.ParseNumber(ReadType readType)
   at Newtonsoft.Json.JsonTextReader.ReadStringValue(ReadType readType)
   at Newtonsoft.Json.JsonTextReader.ReadAsString()
   at Newtonsoft.Json.Serialization.JsonSerializerInternalReader.ReadForType(JsonReader reader, JsonContract contract, Boolean hasConverter)
   at Newtonsoft.Json.Serialization.JsonSerializerInternalReader.ResolvePropertyAndCreatorValues(JsonObjectContract contract, JsonProperty containerProperty, JsonReader reader, Type objectType)
   at Newtonsoft.Json.Serialization.JsonSerializerInternalReader.CreateObjectUsingCreatorWithParameters(JsonReader reader, JsonObjectContract contract, JsonProperty containerProperty, ObjectConstructor`1 creator, String id)
   at Newtonsoft.Json.Serialization.JsonSerializerInternalReader.CreateNewObject(JsonReader reader, JsonObjectContract objectContract, JsonProperty containerMember, JsonProperty containerProperty, String id, Boolean& createdFromNonDefaultCreator)
   at Newtonsoft.Json.Serialization.JsonSerializerInternalReader.CreateObject(JsonReader reader, Type objectType, JsonContract contract, JsonProperty member, JsonContainerContract containerContract, JsonProperty containerMember, Object existingValue)
   at Newtonsoft.Json.Serialization.JsonSerializerInternalReader.CreateValueInternal(JsonReader reader, Type objectType, JsonContract contract, JsonProperty member, JsonContainerContract containerContract, JsonProperty containerMember, Object existingValue)
   at Newtonsoft.Json.Serialization.JsonSerializerInternalReader.Deserialize(JsonReader reader, Type objectType, Boolean checkAdditionalContent)
   at Newtonsoft.Json.JsonSerializer.DeserializeInternal(JsonReader reader, Type objectType)
   at Newtonsoft.Json.JsonConvert.DeserializeObject(String value, Type type, JsonSerializerSettings settings)
   at Newtonsoft.Json.JsonConvert.DeserializeObject[T](String value, JsonSerializerSettings settings)
   at Newtonsoft.Json.JsonConvert.DeserializeObject[T](String value)
   at Fable.Main.main(String[] argv) in C:\Temp\Fable\src\Main.fs:line 73

I can't see how that json argument could work at all in Windows. Those double quotes in json would be parsed by the command line processor (cmd.exe). And trying to escape them would be... troublesome (to say the least -- See http://stackoverflow.com/a/15262019 and http://blogs.msdn.com/b/twistylittlepassagesallalike/archive/2011/04/23/everyone-quotes-arguments-the-wrong-way.aspx )

Or I am missing something?

Json

I've found an oddity with the JSON.stringify. I was hoping the following 3 classes would yield the same json:

type DataR =
    { Id : int
      Name : string }

let dataR =
    { Id = 1
      Name = "Test" }

type DataC1(idBackingFeild, nameBackingField) =
    member x.Id = idBackingFeild
    member x.Name = nameBackingField

let dataC1 = DataC1(1, "test")

type DataC2() =
    member x.Id = 1
    member x.Name = "Test"

let dataC2 = DataC2()

printf "DataR: %s" (JS.Globals.JSON.stringify dataR)
printf "DataC1: %s" (JS.Globals.JSON.stringify dataC1)
printf "DataC2: %s" (JS.Globals.JSON.stringify dataC2)

but the result is

DataR: {"Id":1,"Name":"Test"}
DataC1: {"idBackingFeild":1,"nameBackingField":"test"}
DataC2: {}

Record type quotes

It looks like for record types the this.x=$arg0 are being wrapped in a quotes.

The following code:

type ElementBuilder = {
    TypeName: string
    Styles: (string * obj) list
}

emits this js:

  var ElementBuilder = Program.ElementBuilder = function () {
    function _class($arg0, $arg1) {
      _classCallCheck(this, _class);

      "this.TypeName=$arg0;this.Styles=$arg1";
    }

    return _class;
  }();

Creating an organisation or moving to fsprojects

Does the project deserve to have its own organisation (I hope so) or will it attract more contributors if moved to fsprojects? I'd love to hear @dsyme opinion on this (and everybody else, of course).

Only one thing is clear, I don't want to have it in my personal repo for the official release :) This is for the community!

Suggestion: Units of Measure support

One of the reasons I considered writing some code in F# and then transpiling it to Typescript or JS was I thought it would be great to get F#'s support for compile time units of measure checking, especially if it transpiled to also support any of the JS runtime units of measures libraries for some runtime security as well.

Maybe this would be useful functionality for Fable to attempt?

Output to ES6? (Possible? Worth it?)

It would be possible to output "pure" ES6 code? (instead of "ES5 with some ES6 classes"?)

I know Babel is supposed to translate ES6 to ES5. But... just being curious.:)

Why?

I'm including the generated .js files in a project written in ES6. And I already have a build pipeline to convert ES6 to ES5. It would be nice to use only one language for everything.

Currently I'm simply importing the Fable-generated files with

import MyModule from '../imports/MyModule';

and it works nicely.

I don't know if this would have any benefit. Just because. ;)

Typescript2Fable - Function names

In typescript definition file there may exist functions with not valid F# name. For such functions name should be enclosed into double ticks.

Build was not evaluated, expected the results to be ready after 'Eval'

Hello,

I seem to be having little luck using the fable-compiler package from npm (version 0.1.6 to be precise). The message I am getting is pretty vague and is as follows:

"Build was not evaluated, expected the results to be ready after 'Eval'"

I have tried attaching the vscode debugger to node, and it appears to be emitted from the process wrapper (i presume around Fable.exe, line 321 index.js). I wasn't quite sure how this is coupled to the node process in terms of parameters so I haven't dug any further yet, however any guidance would be appreciated if I can help get to the bottom of this.

I have tried running from both some local directory with a very simple .fsproj file, and also copying a .fs file directly into the global node modules folder (not sure if this is supposed to work, but either way the error message is the same). Interestingly .fsx files compile perfectly. I should probably also add that I have tried this on two very different environments (one win7 behind heavy corp proxy - node 4.4, second is win 10 with node 5.5, no restrictions) and both behave the same.

On a side note though, awesome work! I have been waiting for something like this for a long time.

Online REPL

It would be great to have an online REPL to show the capabilities of the compiler. Maybe @tpetricek can help here?

Complete unit tests

The project should include at least as many tests as FunScript. So far I've been copying and adapting unit tests from FunScript repo, basically my removing the quotation brackets <@@ and @@> and adding an assertion with Util.equal.

If someone wants to help with this process, please write which file you want to take care of to make sure we don't step on each other's feet. Maybe @MaxWilson can help here too? Thanks!

Cannot find module 'babel-template'

After successfully build fable in windows (build.cmd), trying to test node sample (node tools/fable2babel.js --projFile sample/node/server/app.fsx) I get the error "Cannot find module 'babel-template'".

Repro:

  • build.cmd
  • cd sample/node/server
  • npm install
  • cd ../../..
  • node tools/fable2babel.js --projFile sample/node/server/app.fsx

Error stack trace:

module.js:327
    throw err;
    ^

Error: Cannot find module 'babel-template'
    at Function.Module._resolveFilename (module.js:325:15)
    at Function.Module._load (module.js:276:25)
    at Module.require (module.js:353:17)
    at require (internal/module.js:12:17)
    at Object.<anonymous> (xxx_path_xxx\Fable-master\tools\fable2babel.js:7:16)
    at Module._compile (module.js:409:26)
    at Object.Module._extensions..js (module.js:416:10)
    at Module.load (module.js:343:32)
    at Function.Module._load (module.js:300:12)
    at Function.Module.runMain (module.js:441:10)

After exec "npm install babel-template" everything works ok.

Is "babel-template" missing from package.json?

Enums comparison

Cannot find replacement for Microsoft.FSharp.Core.Operators.>= when comparing enums.

The expression

1 >= 2

Has the following ApplyInfo:

{methodName = ">=";
 ownerFullName = "Microsoft.FSharp.Core.Operators";
 callee = null;
 args =
  [Value (NumberConst (Case1 1,Int32)); Value (NumberConst (Case1 2,Int32))];
 returnType = PrimitiveType Boolean;
 decorators = [];
 calleeTypeArgs = [];
 methodTypeArgs = [PrimitiveType (Number Int32)];}

Which is fine. However, the same expression using enums

MyEnum.A >= MyEnum.B

where

type MyEnum = 
| A = 0x20uy
| B = 0x50uy

produces

{methodName = ">=";
 ownerFullName = "Microsoft.FSharp.Core.Operators";
 callee = null;
 args =
  [Wrapped
     (Value (NumberConst (Case1 32,UInt8Clamped)),DeclaredType MyEnum Class null);
   Wrapped
     (Value (NumberConst (Case1 80,UInt8Clamped)),DeclaredType MyEnum Class null)];
 returnType = PrimitiveType Boolean;
 decorators = [];
 calleeTypeArgs = [];
 methodTypeArgs = [DeclaredType MyEnum Class null];}

Which fails the 'compare' function of Replacements.fs

Suggestions? ;)

BTW: I'm adding more functions to support enums (fdcastel@b7ab040), but not enough to make a PR, yet.

Add --watch option to CLI tool

F# compiler services take some seconds to warm up, adding a watcher should increase the speed of continuous builds. Maybe @rneatherway can provide some insight on the most efficient way to load the code AST. The current method to do it is here.

Typescript2Fable - Enums

At the moment enums are ignored (no types for them is generated) but names of enums types are used as function parameters.

Option 1. Erase enums to obj ( make alias type ENUM_TYPE = obj)
Option 2. Erase enums to ints ( make alias type ENUM_TYPE = int) - enums in TS are just ints under the hood, we could use that
Option 3. Create F# enum types
Option 4. Create F# class with static members for each enum case.

To be fair, I would be happy enough with Option 2 for start and build better solution later on. I should be able to PR it, just let me know which solution you want to use.

NUnit tests with instance members

What should be changed in NUnit plugin to make this work?

namespace NUnit.Tests

open System
open NUnit.Framework

[<NUnit.Framework.TestFixture>] 
module NUnitTests = 

    [<Test>]
    let ``NUnit static member test``() =
        Assert.AreEqual (3, 1 + 2)

    [<NUnit.Framework.TestFixture>] 
    type TestClass() = 

        [<Test>]
        member this.``NUnit instance member test``() =
            Assert.AreEqual (3, 1 + 2)

Currently, the plugin transforms only the first test. The second one fails with

Unexpected member in module <null>: Constructor (...)

as the plugin is written to only expect F# Modules marked with [<TestFixture>], not classes.

Why I'm asking this?

I'm working in a new plugin to transform Visual Studio Unit Tests, which -- unlike NUnit -- doesn't support static members tests, only instance ones.

But since my noob factor is too damn high for now :), I'm needing a little push here to speed the things up. I'm a bit lost with what Babel transformations should be done at IDeclarePlugin.

Multiple assemblies/assembly reference?

I'm trying to port my tests from F# to Js the same way Fable does.

Problem is: unlike Fable.Tests, my test project have a reference to another project which is also compiled with Fable:

MyProj.Core.fsproj

MyProj.Tests.fsproj
  + References MyProj.Core

The .Core project compiles successfully. However, the .Tests projects fails to compile when Fable encounters a type declared into .Core project:

Cannot find replacement for MyType at ...

where MyType is defined in MyProj.Core.

There's any plans to make this happen in the future?

As a workaround I can add all units from .Core project into .Tests. Cheap. But it will work. :)

This will be needed for larger projects, however.

Static constructors not being called?

Sample code: (Source: https://msdn.microsoft.com/en-us/library/dd483470.aspx)

type PointWithCounter(a: int, b: int) =
    // A variable i.
    let mutable i = 0

    // A let binding that uses a pattern.
    let (x, y) = (a, b)

    // A private function binding.
    let privateFunction x y = x * x + 2*y

    // A static let binding.
    static let mutable count = 0

    // A do binding.
    do
        count <- count + 1

    member this.Prop1 = x
    member this.Prop2 = y
    member this.CreatedCount = count
    member this.FunctionValue = privateFunction x y

let point1 = PointWithCounter(10, 52)
printfn "%d %d %d %d" (point1.Prop1) (point1.Prop2) (point1.CreatedCount) (point1.FunctionValue)
let point2 = PointWithCounter(20, 99)
printfn "%d %d %d %d" (point2.Prop1) (point2.Prop2) (point2.CreatedCount) (point2.FunctionValue)

F# output:

10 52 1 204
20 99 2 598

Fable output:

10 52 NaN 204
20 99 NaN 598

The generated .js file seems fine to me, except that the ".cctor" function is never being called. Shouldn't it be called immediately after the call to _createClass(PointWithCounter, ...)?

Typescript2Fable - Anonymous objects in function parameters

Anonymous objects (both objects and unions) defined in function signature are erased to object:

Input

export class Location {
    uri: Uri;
    range: Range;
    constructor(uri: Uri, rangeOrPosition: Range | Position);
}

Output:

type Location =
        abstract createNew: uri: Uri * rangeOrPosition: obj -> Location
        abstract uri: Uri with get, set
        abstract range: Range with get, set

Expected output option 1:

type RangeOrPosition = 
    | Range of Range
    | Position of Position

and Location =
    abstract createNew: uri: Uri * rangeOrPosition: RangeOrPosition -> Location
    abstract uri: Uri with get, set
    abstract range: Range with get, set

Expected output option 2:

and Location =
    abstract createNew: uri: Uri * rangeOrPosition: Range-> Location
    abstract createNew: uri: Uri * rangeOrPosition: Position-> Location
    abstract uri: Uri with get, set
    abstract range: Range with get, set

Option 1 is probably better as it can be also used for anonymous types not only unions

Typescript2Fable fails

node tools/typescript2fable.js D:\Programowanie\Projekty\Fable-vscode-test\asd\typings\vscode-typings.d.ts > D:\Programowanie\Projekty\Fable-vscode-test\vscode.fs
module.js:340
    throw err;
    ^

Error: Cannot find module 'typescript'
    at Function.Module._resolveFilename (module.js:338:15)
    at Function.Module._load (module.js:289:25)
    at Module.require (module.js:366:17)
    at require (module.js:385:17)
    at Object.<anonymous> (D:\Programowanie\Projekty\Fable\tools\typescript2fable.js:2:10)
    at Module._compile (module.js:425:26)
    at Object.Module._extensions..js (module.js:432:10)
    at Module.load (module.js:356:32)
    at Function.Module._load (module.js:313:12)
    at Function.Module.runMain (module.js:457:10)

Looks like typescript is not defined as dependency in packages.json. Works well when I've added it to dependencies and run npm install again

fable2babel > System.InvalidOperationException

Converting the small code block

module React

open System

type HTMLFactory = obj

[<EntryPoint>]
let main argv =
    0

Results in the following error

C:\Dev\Fable>node tools/fable2babel.js --projFile sample/node/react/react.fsproj
cmd /C C:\Dev\Fable\tools/../build/main/Fable.exe {"lib":"../../../lib","outDir":".","symbols":[],"watch":false,"projFile":"react.fsproj","env":"node"}
FABLE ERROR:
Unhandled Exception: ...
FABLE ERROR: System.InvalidOperationException: the type 'HTMLFactory' does not have a qualified name
   at Microsoft.FSharp.Compiler.SourceCodeServices.FSharpEntity.get_FullName() in C:\Users\robin\dev\FSharp.Compiler.Service\src\fsharp\vs\Symbols.fs:line 261
   at [email protected]...
Finished with code 3762504530

HashSet?

Any ideas on how to map a HashSet (System.Collections.Generic.HashSet) to JavaScript?

Should it be part of fable-core.js?

Cannot read property 'Symbol(Symbol.iterator)' of undefined

The following code:

module Program

open System
open Fable.Import
open Fable.Core

[<EntryPoint>]
let main argv =
    let a = [ "prop" ==> "value" ]
    let b = createObj a
    0

Results in the following error

C:\Dev\Fable\lib\fable-core.js:977
      for (var i = 0, cur = null, iter = xs[Symbol.iterator](); ; i++) {
                                           ^

TypeError: Cannot read property 'Symbol(Symbol.iterator)' of undefined
    at Object.Seq.fold (C:\Dev\Fable\lib\fable-core.js:977:44)
    at Object.Util.createObj (C:\Dev\Fable\lib\fable-core.js:59:16)
    at C:\Dev\Fable\sample\node\react\Program.js:16:68
    at C:\Dev\Fable\sample\node\react\Program.js:17:5
    at Object.<anonymous> (C:\Dev\Fable\sample\node\react\Program.js:20:2)
    at Module._compile (module.js:425:26)
    at Object.Module._extensions..js (module.js:432:10)
    at Module.load (module.js:356:32)
    at Function.Module._load (module.js:313:12)
    at Function.Module.runMain (module.js:457:10)

but the following code does work

module Program

open System
open Fable.Import
open Fable.Core

[<EntryPoint>]
let main argv =
    let b = createObj [ "prop" ==> "value" ]
    0

SyntaxError: unknown: 'this' is not allowed before super()

The following code:

type Class1() =
    member val Prop: string = "" with get, set

type Class2() as x =
    inherit Class1()
    do x.Prop <- "Hello"

Emits the following errro:

{ [SyntaxError: unknown: 'this' is not allowed before super() (This is an error on an internal node. Probably an internal error)] _babel: true }

Removing the as x and do x.Prop <- "Hello" solves the problem.

RFC: How to handle byte/word arithmetic?

Ideas on how to make this work?

[<Test>]
let ``Byte wraps at 255``() =
    let a = 255uy
    let b = a + 1uy
    b |> equal 0uy                // FAIL: Expected 0, Actual: 256

[<Test>]
let ``Byte wraps at 0``() =
    let a = byte 0                // ERROR: Cannot find replacement for Microsoft.FSharp.Core.Operators.byte
    let b = a - 1uy
    b |> equal 255uy

[<Test>]
let ``UInt16 wraps at 65535``() =
    let a = 65535us
    let b = a + 1us
    b |> equal 0us                // FAIL: Expected 0, Actual: 65536

[<Test>]
let ``UInt16 wraps at 0``() =
    let a = uint16 0              // ERROR: Cannot find replacement for Microsoft.FSharp.Core.Operators.uint16
    let b = a - 1us
    b |> equal 65535us

exports.default.xxx

I'm trying to get react hot reloading working and I've encounter a bit of a problem. When the hot loader searches for react components it looks in exports.xxx and everything that Fable produces goes in exports.default.xxx so the hot reload can’t find it.
Would it be posable to not put everything in default (or copied to export.)? I think this would also have the advantage of playing well with other JS applications such as able to use the es6 import statement

import { member } from "module-name";

(Sorry if this is completely wrong es6 import/exports still confuse me)

typescript2fable Inheritance

I have an idea on how to get around inheritance problems especially an interface extending a class:

I've got some work in progress but before I finish I'll like some feedback

// interface FirstInterface {
//      FirstFunction(s: string): void
// }

// Would generate the following code:

// An interface prefixed with I
type IFirstInterface =
    abstract FirstFunction : string -> unit

// An abstract class which implements the interface
// This means any child class can inherit this class and not worry about implementing interfaces
[<AbstractClass>]
type FirstInterface =
    interface IFirstInterface with
        member x.FirstFunction(s): unit = failwith "Not implemented yet"

// class SecondClass implements FirstInterface {
//      SecondFunction(i: number): void
// }

// Would generate the following code:

// The interface in needed so any child interfaces can use this.
type ISecondInterface =
    inherit IFirstInterface
    abstract SecondFunction: int -> unit

// Same as the interface generated code but not an abstract class.
type SecondInterface =
    inherit FirstInterface
    interface ISecondInterface with
        member x.SecondFunction(i: int): unit = failwith "Not implemented yet"


// interface ThirdInterface extends SecondInterface {
//      ThirdFunction(b: bool): void
// }

// This interface can now inherit without any problems

type IThirdInterface =
    inherit ISecondInterface
    abstract ThirdFunction: bool -> unit

[<AbstractClass>]
type ThirdInterface =
    inherit SecondInterface
    interface IThirdInterface with
        member x.ThirdFunction(arg1: bool): unit = failwith "Not implemented yet"

Typescript2Fable - Namespaces

Let's consider following typescript definition:

declare namespace vscode {
    export interface TextEditor {
        something : string
    }

    export namespace commands {
        export function something2(texteditor : TextEditor) : string
    }

}

declare module 'vscode' {
    export = vscode;
}

We get following F# output:

namespace Fable.Import
open System

module commands =
    type private ImportAttribute(path) =
        inherit System.Attribute()





    type Global =
        abstract something2: texteditor: TextEditor -> string

    [<Import("commands")>]
    let Global: Global = failwith "JS only"

module vscode =
    type private ImportAttribute(path) =
        inherit System.Attribute()

    and TextEditor =
        abstract something: string with get, set




module vscode =
    type private ImportAttribute(path) =
        inherit System.Attribute()
  1. We get duplicated vscode module which is not valid F# code
  2. Module commands is not nested into vscode module - it may or may not be right solution but if it's desired behavior TextEditor type is not accessible from commands module - should be used vscode.TextEditor (and right ordering of modules)

Detected non-bound identifier

Looks like the latest change (d17eafc) may have introduced a small bug.

Converting the react sample:

node build/fable/index.js --projFile samples/browser/react/fable-sample-react.fsproj

Now produces the following error:

Compiled react-helper.fs at 11:49:17 AM
Compiled models.fs at 11:49:17 AM
Detected non-bound identifier: b in C:\Dev\Fable\samples\browser\react\public/react-helper.fs (62,14--62,15) IsSynthetic=false (C:\Dev\Fable\samples\browser\react\public/components.fs)

Suggestion: Move --clamp to plugin?

Suggestion: To remove --clamp from Fable and implement it as a plugin?

I'm looking at the significant handling you have to do in build script to test this case. Wouldn't be a better idea to move this to a plugin and to improve the tests for all plugins? It would also simplify the build process.

I don't know how many users of this feature exists out there. It may be a simple change. It may be not.

Just throwing out the idea. If you agree, I can work on something about it.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.