diff --git a/src/Fable.Core/Fable.Core.Types.fs b/src/Fable.Core/Fable.Core.Types.fs index 1641884a0..7c255439c 100644 --- a/src/Fable.Core/Fable.Core.Types.fs +++ b/src/Fable.Core/Fable.Core.Types.fs @@ -96,8 +96,24 @@ type EmitIndexerAttribute() = type EmitPropertyAttribute(propertyName: string) = inherit Attribute() +/// /// Compile union types as string literals. -/// More info: https://fable.io/docs/communicate/js-from-fable.html#stringenum-attribute +/// +/// +/// You can also use [<CompiledName>] and [<CompiledValue>] to +/// specify the name or literal of the union case in the generated code: +/// +/// [<StringEnum>] +/// type EventType = +/// | [<CompiledName("Abracadabra")>] MouseOver +/// | [<CompiledValue(false)>] RealMagic +/// let eventType = EventType.MouseOver // Compiles: "Abracadabra" +/// let magicPower = EventType.RealMagic // Compiles: false +/// +/// +/// +/// Fable Documentation +/// [] type StringEnumAttribute(caseRules: CaseRules) = inherit Attribute() diff --git a/src/Fable.Transforms/FSharp2Fable.Util.fs b/src/Fable.Transforms/FSharp2Fable.Util.fs index ed0e795be..523b3ae5a 100644 --- a/src/Fable.Transforms/FSharp2Fable.Util.fs +++ b/src/Fable.Transforms/FSharp2Fable.Util.fs @@ -937,12 +937,14 @@ module Helpers = with _ -> failwith $"Cannot find case %s{unionCase.Name} in %s{FsEnt.FullName ent}" - /// Apply case rules to case name if there's no explicit compiled name + /// Apply case rules to case name if there's no explicit compiled name or compiled value let transformStringEnum (rule: CaseRules) (unionCase: FSharpUnionCase) = - match FsUnionCase.CompiledName unionCase with - | Some name -> name - | None -> Naming.applyCaseRule rule unionCase.Name - |> makeStrConst + match FsUnionCase.CompiledName unionCase, FsUnionCase.CompiledValue unionCase with + | Some name, _ -> name |> makeStrConst + | _, Some(CompiledValue.Boolean value) -> makeBoolConst value + | _, Some(CompiledValue.Float value) -> makeFloatConst value + | _, Some(CompiledValue.Integer value) -> makeIntConst value + | _ -> Naming.applyCaseRule rule unionCase.Name |> makeStrConst // let isModuleMember (memb: FSharpMemberOrFunctionOrValue) = // match memb.DeclaringEntity with diff --git a/tests/Js/Main/JsInteropTests.fs b/tests/Js/Main/JsInteropTests.fs index b44ad5c69..c0461d7c8 100644 --- a/tests/Js/Main/JsInteropTests.fs +++ b/tests/Js/Main/JsInteropTests.fs @@ -225,6 +225,18 @@ type LowerAllOptions = | ContentBox | BorderBox +type RespectValuesEnum = Foo = 0 | Bar = 1 | Baz = 2 + +[] +type RespectValues = + | ContentBox + | [] None + | [] AnswerToLife + | [] Pi + | [] Foo + | [] Bar +// | [] Undefined // Error: Expected: undefined - Actual: undefined + [] #endif type Field = OldPassword | NewPassword | ConfirmPassword @@ -829,6 +841,20 @@ let tests = let x = LowerAllOptions.ContentBox x |> unbox |> equal "contentbox" + testCase "StringEnum is overwritten by CompiledValue" <| fun () -> + RespectValues.ContentBox |> unbox |> equal "contentbox" + RespectValues.None |> unbox |> equal false + // When running fable-compiler-js we can't make a distinction between int and float at runtime + // See https://github.com/fable-compiler/Fable/pull/4144#issuecomment-3001681838 + #if !NPM_PACKAGE_FABLE_COMPILER_JAVASCRIPT + RespectValues.Pi |> unbox |> equal 3.14159 + #endif + RespectValues.AnswerToLife |> unbox |> equal 42 + RespectValues.Foo |> unbox |> equal RespectValuesEnum.Foo + + testCase "StringEnum CompiledName over CompiledValue" <| fun () -> + RespectValues.Bar |> unbox |> equal "Bar" + // See https://github.com/fable-compiler/fable-import/issues/72 testCase "Can use values and functions from global modules" <| fun () -> GlobalModule.add 3 4 |> equal 7