From 0e972611d8ffd8bdedb7cfa75fbefc17252ad7b2 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Sun, 19 Apr 2026 01:18:24 +0000 Subject: [PATCH 1/2] Fix JSX props with long string values causing compile error (fixes #3839) When a JSX prop's string value exceeds ~100 characters, Fable wraps it in a Let binding. The transformJsxProps function only matched the direct Value(NewTuple([key; value])) pattern, causing a 'Cannot detect JSX prop key at compile time' error for any string longer than the threshold. Fix: add a match arm for MaybeCasted(Let(_, letValue, Value(NewTuple(...)))) to extract the key from the tuple body and use the Let-bound value as the prop value. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> chore: revert CHANGELOG.md --- src/Fable.Transforms/Fable2Babel.fs | 10 ++++++++++ .../Integration/data/jsxListOptimisation/Components.fs | 8 ++++++++ .../data/jsxListOptimisation/Components.jsx.expected | 2 ++ 3 files changed, 20 insertions(+) diff --git a/src/Fable.Transforms/Fable2Babel.fs b/src/Fable.Transforms/Fable2Babel.fs index 00cbbf0b31..d44fcbcb96 100644 --- a/src/Fable.Transforms/Fable2Babel.fs +++ b/src/Fable.Transforms/Fable2Babel.fs @@ -2078,6 +2078,16 @@ module Util = | value -> Some(props, [ value ]) else Some((key, value) :: props, children) + // When the string value exceeds ~100 chars, Fable wraps it in a Let binding. + // The Let body still contains the tuple with the prop key and a reference to the bound var; + // we use the bound value (letValue) as the actual prop value. + | Some(props, children), MaybeCasted(Fable.Let(_, letValue, MaybeCasted(Fable.Value(Fable.NewTuple([ StringConst key; _ ], _), _)))) -> + if key = "children" then + match letValue with + | Replacements.Util.ArrayOrListLiteral(children, _) -> Some(props, children) + | _ -> Some(props, [ letValue ]) + else + Some((key, letValue) :: props, children) | Some _, e -> addError com [] e.Range "Cannot detect JSX prop key at compile time" diff --git a/tests/Integration/Integration/data/jsxListOptimisation/Components.fs b/tests/Integration/Integration/data/jsxListOptimisation/Components.fs index 938064405f..f9b5881434 100644 --- a/tests/Integration/Integration/data/jsxListOptimisation/Components.fs +++ b/tests/Integration/Integration/data/jsxListOptimisation/Components.fs @@ -162,3 +162,11 @@ let propsCanUseUnbox = Html.div [ unbox ("id", "myid") ] + +// Regression test for https://github.com/fable-compiler/Fable/issues/3839 +// String values longer than ~100 chars were wrapped in a Let binding by Fable, +// causing transformJsxProps to fail with "Cannot detect JSX prop key at compile time". +let divWithLongClassName = + Html.div [ + prop.className "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" + ] diff --git a/tests/Integration/Integration/data/jsxListOptimisation/Components.jsx.expected b/tests/Integration/Integration/data/jsxListOptimisation/Components.jsx.expected index 7371329c93..d46f30308b 100644 --- a/tests/Integration/Integration/data/jsxListOptimisation/Components.jsx.expected +++ b/tests/Integration/Integration/data/jsxListOptimisation/Components.jsx.expected @@ -110,3 +110,5 @@ export function divWithConditionalWithoutElseBranchWorks(show) { } export const propsCanUseUnbox =
; + +export const divWithLongClassName =
; From ff368be9d73ca19912ea9c871ab9665a6d9d4df8 Mon Sep 17 00:00:00 2001 From: Mangel Maxime Date: Tue, 21 Apr 2026 22:40:31 +0200 Subject: [PATCH 2/2] style: apply fantomas --- src/Fable.Transforms/Fable2Babel.fs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/Fable.Transforms/Fable2Babel.fs b/src/Fable.Transforms/Fable2Babel.fs index d44fcbcb96..690136a760 100644 --- a/src/Fable.Transforms/Fable2Babel.fs +++ b/src/Fable.Transforms/Fable2Babel.fs @@ -2081,7 +2081,8 @@ module Util = // When the string value exceeds ~100 chars, Fable wraps it in a Let binding. // The Let body still contains the tuple with the prop key and a reference to the bound var; // we use the bound value (letValue) as the actual prop value. - | Some(props, children), MaybeCasted(Fable.Let(_, letValue, MaybeCasted(Fable.Value(Fable.NewTuple([ StringConst key; _ ], _), _)))) -> + | Some(props, children), + MaybeCasted(Fable.Let(_, letValue, MaybeCasted(Fable.Value(Fable.NewTuple([ StringConst key; _ ], _), _)))) -> if key = "children" then match letValue with | Replacements.Util.ArrayOrListLiteral(children, _) -> Some(props, children)