diff --git a/src/Fable.Cli/CHANGELOG.md b/src/Fable.Cli/CHANGELOG.md index 6fda4c8a0..c663aaf99 100644 --- a/src/Fable.Cli/CHANGELOG.md +++ b/src/Fable.Cli/CHANGELOG.md @@ -13,6 +13,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 * [Rust] Added String.Replace(char, char) and test (by @ncave) * [Rust] Support type extensions for external types (by @ncave) * [Rust] Support more System.Array methods and tests (by @ncave) +* [JS] Add `System.String.Normalize` support (by @DashieTM) ## 4.22.0 - 2024-10-02 diff --git a/src/Fable.Transforms/Replacements.fs b/src/Fable.Transforms/Replacements.fs index 0360b27c3..e909a8d18 100644 --- a/src/Fable.Transforms/Replacements.fs +++ b/src/Fable.Transforms/Replacements.fs @@ -1616,6 +1616,34 @@ let strings (com: ICompiler) (ctx: Context) r t (i: CallInfo) (thisArg: Expr opt Helper.LibCall(com, "String", "concat", t, args, hasSpread = true, ?loc = r) |> Some | "CompareOrdinal", None, _ -> Helper.LibCall(com, "String", "compareOrdinal", t, args, ?loc = r) |> Some + | "Normalize", Some str, _ -> + match args with + | [] -> + Helper.InstanceCall( + str, + Naming.lowerFirst i.CompiledName, + t, + [ makeStrConst "NFC" ], + i.SignatureArgTypes, + genArgs = i.GenericArgs, + ?loc = r + ) + |> Some + | [ NormalizationFormEnumValue normalizationForm ] -> + Helper.InstanceCall( + str, + Naming.lowerFirst i.CompiledName, + t, + [ makeStrConst normalizationForm ], + i.SignatureArgTypes, + genArgs = i.GenericArgs, + ?loc = r + ) + |> Some + | _ -> + "Normalization expects an optional System.Text.NormalizationForm" + |> addErrorAndReturnNull com ctx.InlinePath r + |> Some | Patterns.SetContains implementedStringFunctions, thisArg, args -> Helper.LibCall( com, diff --git a/src/Fable.Transforms/Transforms.Util.fs b/src/Fable.Transforms/Transforms.Util.fs index ce53caff1..fb7b93d57 100644 --- a/src/Fable.Transforms/Transforms.Util.fs +++ b/src/Fable.Transforms/Transforms.Util.fs @@ -823,6 +823,19 @@ module AST = Some() | _ -> None + let (|NormalizationFormEnumValue|_|) e = + match e with + | Expr.Value( + kind = NumberConstant(NumberValue.Int32 value, + NumberInfo.IsEnum({ FullName = "System.Text.NormalizationForm" }))) -> + match value with + | 1 -> Some "NFC" + | 2 -> Some "NFD" + | 5 -> Some "NFKC" + | 6 -> Some "NFKD" + | _ -> None + | _ -> None + // TODO: Improve this, see https://github.com/fable-compiler/Fable/issues/1659#issuecomment-445071965 // This is mainly used for inlining so a computation or a reference to a mutable value are understood // as a side effects too (because we don't want to duplicate or change the order of execution) diff --git a/tests/Js/Main/StringTests.fs b/tests/Js/Main/StringTests.fs index bda0f73e7..8d420af31 100644 --- a/tests/Js/Main/StringTests.fs +++ b/tests/Js/Main/StringTests.fs @@ -831,12 +831,12 @@ let tests = testList "Strings" [ for arg in args do "abcd".EndsWith(fst arg) |> equal (snd arg) - + testCase "String.EndsWith with OrdinalIgnoreCase works" <| fun () -> let args = [("ab", false); ("CD", true); ("cd", true); ("bc", false); ("xabcd", false); ("abcd", true)] for arg in args do "ABCD".EndsWith(fst arg, StringComparison.OrdinalIgnoreCase) - |> equal (snd arg) + |> equal (snd arg) testCase "String.Trim works" <| fun () -> " abc ".Trim() @@ -966,6 +966,15 @@ let tests = testList "Strings" [ String.Concat(seq { yield "a"; yield "b"; yield "c" }) |> equal "abc" + testCase "System.String.Normalize works" <| fun () -> + let name1 = "\u0041\u006d\u00e9\u006c\u0069\u0065"; + let name2 = "\u0041\u006d\u0065\u0301\u006c\u0069\u0065"; + notEqual name1 name2 + + let normalized1 = name1.Normalize System.Text.NormalizationForm.FormC + let normalized2 = name2.Normalize System.Text.NormalizationForm.FormC + equal normalized1 normalized2 + testCase "System.String.Join with long array works" <| fun () -> let n = 1_000_000 let a = Array.init n (fun _i -> "a")