From c07c18bc080dc54d4537f0386448b6aa67cfdcc8 Mon Sep 17 00:00:00 2001 From: cabboose Date: Sat, 8 Feb 2025 13:08:34 +0800 Subject: [PATCH 01/10] Enhancement - special property name for plugins to utilise and spread objects --- src/Fable.Transforms/BabelPrinter.fs | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/Fable.Transforms/BabelPrinter.fs b/src/Fable.Transforms/BabelPrinter.fs index b8503cf4b4..238ed8a0cc 100644 --- a/src/Fable.Transforms/BabelPrinter.fs +++ b/src/Fable.Transforms/BabelPrinter.fs @@ -443,6 +443,12 @@ module PrinterExtensions = |> List.iter ( function | _, NullOrUndefinedOrVoid -> () + | "_SPREAD_PROPERTY_", value -> + printProp (fun () -> + printer.Print("{...") + printer.Print(value) + printer.Print("}") + ) | key, StringConstant value -> printProp (fun () -> printer.Print($"{key}=\"{value}\"")) | key, value -> printProp (fun () -> From e7111a64937764f4f6d674be014808747e177419 Mon Sep 17 00:00:00 2001 From: cabboose Date: Sat, 8 Feb 2025 14:28:40 +0800 Subject: [PATCH 02/10] Magic attribute key `__SPREAD_PROPERTY__` to spread objects in JSX element tags --- src/Fable.Transforms/BabelPrinter.fs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Fable.Transforms/BabelPrinter.fs b/src/Fable.Transforms/BabelPrinter.fs index 238ed8a0cc..1778506ead 100644 --- a/src/Fable.Transforms/BabelPrinter.fs +++ b/src/Fable.Transforms/BabelPrinter.fs @@ -443,7 +443,7 @@ module PrinterExtensions = |> List.iter ( function | _, NullOrUndefinedOrVoid -> () - | "_SPREAD_PROPERTY_", value -> + | "__SPREAD_PROPERTY__", value -> printProp (fun () -> printer.Print("{...") printer.Print(value) From 1633c5d2a426600bef35f68c292e557cb4e0f133 Mon Sep 17 00:00:00 2001 From: cabboose Date: Sun, 9 Feb 2025 08:22:36 +0800 Subject: [PATCH 03/10] Add __BOOL_PROPERTY__ magic attribute for JSX elements --- src/Fable.Transforms/BabelPrinter.fs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/Fable.Transforms/BabelPrinter.fs b/src/Fable.Transforms/BabelPrinter.fs index 1778506ead..4968b60af2 100644 --- a/src/Fable.Transforms/BabelPrinter.fs +++ b/src/Fable.Transforms/BabelPrinter.fs @@ -449,6 +449,9 @@ module PrinterExtensions = printer.Print(value) printer.Print("}") ) + // https://legacy.reactjs.org/docs/jsx-in-depth.html#props-default-to-true + // https://stackoverflow.com/questions/38659884/using-boolean-value-of-attributes-in-jsx + | "__BOOL_PROPERTY__", StringConstant value -> printProp (fun () -> printer.Print(value)) | key, StringConstant value -> printProp (fun () -> printer.Print($"{key}=\"{value}\"")) | key, value -> printProp (fun () -> From f12f3da71adaf96b45a01953a1d7964ea70e9f10 Mon Sep 17 00:00:00 2001 From: cabboose Date: Sun, 9 Feb 2025 19:53:47 +0800 Subject: [PATCH 04/10] Add tests for magic properties --- tests/React/Counter.fs | 4 ++++ tests/React/__tests__/Tests.fs | 8 +++++++- 2 files changed, 11 insertions(+), 1 deletion(-) diff --git a/tests/React/Counter.fs b/tests/React/Counter.fs index d6a41d49ca..4c25ee91c9 100644 --- a/tests/React/Counter.fs +++ b/tests/React/Counter.fs @@ -42,3 +42,7 @@ let CounterJSX(init: int) = """ + +[] +let CounterJSXMagic(``__SPREAD_PROPERTY__``: obj, ``__BOOL_PROPERTY__``: string) = + JSX.html $"
" diff --git a/tests/React/__tests__/Tests.fs b/tests/React/__tests__/Tests.fs index 6e79c0748c..5645be7251 100644 --- a/tests/React/__tests__/Tests.fs +++ b/tests/React/__tests__/Tests.fs @@ -32,6 +32,12 @@ Jest.describe("React tests", (fun () -> Jest.expect(header).toHaveTextContent("6") )) + let object = {| attribute = "value" |} // if within test scope, then fable inlines constructor into spreader + Jest.test("Counter JSX renders special properties", (fun () -> + let elem = Counter.CounterJSXMagic(object, "required") + Jest.expect(elem).toStrictEqual(Fable.Core.JSX.jsx """""") + )) + Jest.test("Counter JSX renders correctly", (fun () -> let elem = RTL.render(Counter.CounterJSX(5) |> unbox) Jest.expect(elem.container).toMatchSnapshot() @@ -74,4 +80,4 @@ Jest.describe("React tests", (fun () -> let text = elem.getByTestId "text" Jest.expect(text).toHaveTextContent("3") )) -)) \ No newline at end of file +)) From 862d2cecd45d274c3397631af5639d3f786fffdf Mon Sep 17 00:00:00 2001 From: cabboose Date: Tue, 11 Feb 2025 00:11:37 +0800 Subject: [PATCH 05/10] Pass attrs of JSX Els further down AST transformations --- src/Fable.Transforms/Fable2Babel.fs | 32 ++++++++++++++++++----------- 1 file changed, 20 insertions(+), 12 deletions(-) diff --git a/src/Fable.Transforms/Fable2Babel.fs b/src/Fable.Transforms/Fable2Babel.fs index fc1bba82c8..f74eb879a3 100644 --- a/src/Fable.Transforms/Fable2Babel.fs +++ b/src/Fable.Transforms/Fable2Babel.fs @@ -2024,25 +2024,32 @@ module Util = |> List.append thisArg |> emitExpression range macro - let transformJsxProps (com: IBabelCompiler) props = + let transformJsxProps (com: IBabelCompiler) (props: ((string * Fable.Attribute seq) * Fable.Expr) list) = (Some([], []), props) ||> List.fold (fun propsAndChildren prop -> match propsAndChildren, prop with | None, _ -> None - | Some(props, children), Fable.Value(Fable.NewTuple([ StringConst key; value ], _), _) -> + | Some(props, children), ((key, attrs), value) -> if key = "children" then match value with | Replacements.Util.ArrayOrListLiteral(children, _) -> Some(props, children) | value -> Some(props, [ value ]) + elif hasAttribute Atts.paramObject attrs then + Some((key, value) :: props, children) else Some((key, value) :: props, children) - | Some _, e -> + | Some _, (_, e) -> addError com [] e.Range "Cannot detect JSX prop key at compile time" None ) - let transformJsxEl (com: IBabelCompiler) ctx componentOrTag props = + let transformJsxEl + (com: IBabelCompiler) + ctx + componentOrTag + (props: ((string * Fable.Attribute seq) * Fable.Expr) list) + = match transformJsxProps com props with | None -> Expression.nullLiteral () | Some(props, children) -> @@ -2062,14 +2069,15 @@ module Util = Expression.jsxElement (componentOrTag, props, children) let transformJsxCall (com: IBabelCompiler) ctx callee (args: Fable.Expr list) (info: Fable.MemberFunctionOrValue) = - let names = - info.CurriedParameterGroups |> List.concat |> List.choose (fun p -> p.Name) - - let props = - List.zipSafe names args - |> List.map (fun (key, value) -> - Fable.Value(Fable.NewTuple([ Fable.Value(Fable.StringConstant key, None); value ], false), None) - ) + let nameAttrTuples = + info.CurriedParameterGroups + |> List.concat + |> List.choose (fun p -> p.Name |> Option.bind (fun name -> Some(name, p.Attributes))) + + let props = List.zipSafe nameAttrTuples args + // |> List.map (fun (key, value) -> + // Fable.Value(Fable.NewTuple([ Fable.Value(Fable.StringConstant key.Name.Value, None); value ], false), None) + // ) transformJsxEl com ctx callee props From 66421cd8d33d59071933335fbabd453466f90ef2 Mon Sep 17 00:00:00 2001 From: cabboose Date: Tue, 11 Feb 2025 00:20:57 +0800 Subject: [PATCH 06/10] Revert "Pass attrs of JSX Els further down AST transformations" This reverts commit 862d2cecd45d274c3397631af5639d3f786fffdf. --- src/Fable.Transforms/Fable2Babel.fs | 32 +++++++++++------------------ 1 file changed, 12 insertions(+), 20 deletions(-) diff --git a/src/Fable.Transforms/Fable2Babel.fs b/src/Fable.Transforms/Fable2Babel.fs index f74eb879a3..fc1bba82c8 100644 --- a/src/Fable.Transforms/Fable2Babel.fs +++ b/src/Fable.Transforms/Fable2Babel.fs @@ -2024,32 +2024,25 @@ module Util = |> List.append thisArg |> emitExpression range macro - let transformJsxProps (com: IBabelCompiler) (props: ((string * Fable.Attribute seq) * Fable.Expr) list) = + let transformJsxProps (com: IBabelCompiler) props = (Some([], []), props) ||> List.fold (fun propsAndChildren prop -> match propsAndChildren, prop with | None, _ -> None - | Some(props, children), ((key, attrs), value) -> + | Some(props, children), Fable.Value(Fable.NewTuple([ StringConst key; value ], _), _) -> if key = "children" then match value with | Replacements.Util.ArrayOrListLiteral(children, _) -> Some(props, children) | value -> Some(props, [ value ]) - elif hasAttribute Atts.paramObject attrs then - Some((key, value) :: props, children) else Some((key, value) :: props, children) - | Some _, (_, e) -> + | Some _, e -> addError com [] e.Range "Cannot detect JSX prop key at compile time" None ) - let transformJsxEl - (com: IBabelCompiler) - ctx - componentOrTag - (props: ((string * Fable.Attribute seq) * Fable.Expr) list) - = + let transformJsxEl (com: IBabelCompiler) ctx componentOrTag props = match transformJsxProps com props with | None -> Expression.nullLiteral () | Some(props, children) -> @@ -2069,15 +2062,14 @@ module Util = Expression.jsxElement (componentOrTag, props, children) let transformJsxCall (com: IBabelCompiler) ctx callee (args: Fable.Expr list) (info: Fable.MemberFunctionOrValue) = - let nameAttrTuples = - info.CurriedParameterGroups - |> List.concat - |> List.choose (fun p -> p.Name |> Option.bind (fun name -> Some(name, p.Attributes))) - - let props = List.zipSafe nameAttrTuples args - // |> List.map (fun (key, value) -> - // Fable.Value(Fable.NewTuple([ Fable.Value(Fable.StringConstant key.Name.Value, None); value ], false), None) - // ) + let names = + info.CurriedParameterGroups |> List.concat |> List.choose (fun p -> p.Name) + + let props = + List.zipSafe names args + |> List.map (fun (key, value) -> + Fable.Value(Fable.NewTuple([ Fable.Value(Fable.StringConstant key, None); value ], false), None) + ) transformJsxEl com ctx callee props From b6da4fa3fd43b75b827ef48f7390102b58a0b9f9 Mon Sep 17 00:00:00 2001 From: cabboose Date: Tue, 11 Feb 2025 00:23:36 +0800 Subject: [PATCH 07/10] Revert "Add __BOOL_PROPERTY__ magic attribute for JSX elements" This reverts commit 1633c5d2a426600bef35f68c292e557cb4e0f133. --- src/Fable.Transforms/BabelPrinter.fs | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/Fable.Transforms/BabelPrinter.fs b/src/Fable.Transforms/BabelPrinter.fs index e071392a02..7b5be1ab42 100644 --- a/src/Fable.Transforms/BabelPrinter.fs +++ b/src/Fable.Transforms/BabelPrinter.fs @@ -449,9 +449,6 @@ module PrinterExtensions = printer.Print(value) printer.Print("}") ) - // https://legacy.reactjs.org/docs/jsx-in-depth.html#props-default-to-true - // https://stackoverflow.com/questions/38659884/using-boolean-value-of-attributes-in-jsx - | "__BOOL_PROPERTY__", StringConstant value -> printProp (fun () -> printer.Print(value)) | key, StringConstant value -> printProp (fun () -> printer.Print($"{key}=\"{value}\"")) | key, value -> printProp (fun () -> From 1846624504bc3c2846098c1fcce6487d7afb72ad Mon Sep 17 00:00:00 2001 From: cabboose Date: Tue, 11 Feb 2025 00:25:00 +0800 Subject: [PATCH 08/10] Remove __BOOL_PROP__ magic --- tests/React/Counter.fs | 2 +- tests/React/__tests__/Tests.fs | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/tests/React/Counter.fs b/tests/React/Counter.fs index 4c25ee91c9..fbf4d88e3d 100644 --- a/tests/React/Counter.fs +++ b/tests/React/Counter.fs @@ -44,5 +44,5 @@ let CounterJSX(init: int) = """ [] -let CounterJSXMagic(``__SPREAD_PROPERTY__``: obj, ``__BOOL_PROPERTY__``: string) = +let CounterJSXMagic(``__SPREAD_PROPERTY__``: obj) = JSX.html $"
" diff --git a/tests/React/__tests__/Tests.fs b/tests/React/__tests__/Tests.fs index 4125384a09..5021366c91 100644 --- a/tests/React/__tests__/Tests.fs +++ b/tests/React/__tests__/Tests.fs @@ -34,8 +34,8 @@ Jest.describe("React tests", (fun () -> let object = {| attribute = "value" |} // if within test scope, then fable inlines constructor into spreader Jest.test("Counter JSX renders special properties", (fun () -> - let elem = Counter.CounterJSXMagic(object, "required") - Jest.expect(elem).toStrictEqual(Fable.Core.JSX.jsx """""") + let elem = Counter.CounterJSXMagic(object) + Jest.expect(elem).toStrictEqual(Fable.Core.JSX.jsx """""") )) Jest.test("Counter JSX renders correctly", (fun () -> From dcb0c8df427a46ae743a88df0230dd849d086a08 Mon Sep 17 00:00:00 2001 From: cabboose Date: Wed, 5 Mar 2025 13:42:48 +0800 Subject: [PATCH 09/10] null prop key will only render value --- src/Fable.Transforms/BabelPrinter.fs | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/src/Fable.Transforms/BabelPrinter.fs b/src/Fable.Transforms/BabelPrinter.fs index 7b5be1ab42..d6773b8f1d 100644 --- a/src/Fable.Transforms/BabelPrinter.fs +++ b/src/Fable.Transforms/BabelPrinter.fs @@ -443,13 +443,8 @@ module PrinterExtensions = |> List.iter ( function | _, NullOrUndefinedOrVoid -> () - | "__SPREAD_PROPERTY__", value -> - printProp (fun () -> - printer.Print("{...") - printer.Print(value) - printer.Print("}") - ) | key, StringConstant value -> printProp (fun () -> printer.Print($"{key}=\"{value}\"")) + | "", value -> printProp (fun () -> printer.Print(value)) | key, value -> printProp (fun () -> printer.Print(key + "={") From 420a53478270f7812bb26e9c0a7bd63e06c6b3c1 Mon Sep 17 00:00:00 2001 From: cabboose Date: Wed, 5 Mar 2025 13:46:39 +0800 Subject: [PATCH 10/10] Revert "Add tests for magic properties" This reverts commit abd0a758 --- tests/React/Counter.fs | 4 ---- tests/React/__tests__/Tests.fs | 6 ------ 2 files changed, 10 deletions(-) diff --git a/tests/React/Counter.fs b/tests/React/Counter.fs index fbf4d88e3d..d6a41d49ca 100644 --- a/tests/React/Counter.fs +++ b/tests/React/Counter.fs @@ -42,7 +42,3 @@ let CounterJSX(init: int) = """ - -[] -let CounterJSXMagic(``__SPREAD_PROPERTY__``: obj) = - JSX.html $"
" diff --git a/tests/React/__tests__/Tests.fs b/tests/React/__tests__/Tests.fs index 5021366c91..e3a01ea061 100644 --- a/tests/React/__tests__/Tests.fs +++ b/tests/React/__tests__/Tests.fs @@ -32,12 +32,6 @@ Jest.describe("React tests", (fun () -> Jest.expect(header).toHaveTextContent("6") )) - let object = {| attribute = "value" |} // if within test scope, then fable inlines constructor into spreader - Jest.test("Counter JSX renders special properties", (fun () -> - let elem = Counter.CounterJSXMagic(object) - Jest.expect(elem).toStrictEqual(Fable.Core.JSX.jsx """""") - )) - Jest.test("Counter JSX renders correctly", (fun () -> let elem = RTL.render(Counter.CounterJSX(5) |> unbox) Jest.expect(elem.container).toMatchSnapshot()