Skip to content

Commit

Permalink
Enable JSX (interpolates) string templates
Browse files Browse the repository at this point in the history
  • Loading branch information
alfonsogarciacaro committed May 25, 2022
1 parent b7d3bf8 commit 1c761c2
Show file tree
Hide file tree
Showing 6 changed files with 50 additions and 5 deletions.
1 change: 1 addition & 0 deletions src/Fable.Core/Fable.Core.JS.fs
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ module JSX =
class end

let create (componentOrTag: obj) (props: Prop list): Element = nativeOnly
let html (template: string): Element = nativeOnly

module JS =
type [<AllowNullLiteral>] Function =
Expand Down
38 changes: 34 additions & 4 deletions src/Fable.Transforms/BabelPrinter.fs
Original file line number Diff line number Diff line change
Expand Up @@ -256,6 +256,7 @@ module PrinterExtensions =
| SpreadElement(_)
| ArrayExpression(_)
| ObjectExpression(_)
| JsxTemplate(_)
| JsxElement(_) -> printer.Print(expr)
| _ -> printer.WithParens(expr)

Expand Down Expand Up @@ -292,7 +293,26 @@ module PrinterExtensions =
| Node.ObjectTypeCallProperty(_)
| Node.ObjectTypeInternalSlot(_) -> failwith "Not implemented"

member printer.PrintJsx(componentOrTag: Expression, props: (string * Expression) list, children: Expression list) =
member printer.PrintJsxTemplate(parts: string[], values: Expression[]) =
// Do we need to escape backslashes here?
let escape str = str //Regex.Replace(str, @"(?<!\\)\\", @"\\")
if parts.Length = 1 then
printer.Print(escape parts[0])
else
for i = 0 to parts.Length - 2 do
printer.Print(escape parts[i])
match values[i] with
| JsxElement(componentOrTag, props, children) ->
printer.PrintJsxElement(componentOrTag, props, children)
| JsxTemplate(parts, values) ->
printer.PrintJsxTemplate(parts, values)
| value ->
printer.Print("{")
printer.Print(value)
printer.Print("}")
printer.Print(Array.last parts |> escape)

member printer.PrintJsxElement(componentOrTag: Expression, props: (string * Expression) list, children: Expression list) =
let printTag = function
| StringConstant tag -> printer.Print(tag)
| componentRef -> printer.Print(componentRef)
Expand Down Expand Up @@ -336,7 +356,10 @@ module PrinterExtensions =
printer.Print(text)
printer.PrintNewLine()
| JsxElement(componentOrTag, props, children) ->
printer.PrintJsx(componentOrTag, props, children)
printer.PrintJsxElement(componentOrTag, props, children)
printer.PrintNewLine()
| JsxTemplate(parts, values) ->
printer.PrintJsxTemplate(parts, values)
printer.PrintNewLine()
| child ->
printer.Print("{")
Expand All @@ -352,7 +375,8 @@ module PrinterExtensions =

member printer.Print(expr: Expression) =
match expr with
| JsxElement(componentOrTag, props, children) -> printer.PrintJsx(componentOrTag, props, children)
| JsxElement(componentOrTag, props, children) -> printer.PrintJsxElement(componentOrTag, props, children)
| JsxTemplate(parts, values) -> printer.PrintJsxTemplate(parts, values)
| Super(loc) -> printer.Print("super", ?loc = loc)
| Literal(n) -> printer.Print(n)
| Undefined(loc) -> printer.Print("undefined", ?loc=loc)
Expand Down Expand Up @@ -442,7 +466,13 @@ module PrinterExtensions =
| Statement.BlockStatement(s) -> printer.Print(s)
| ReturnStatement(argument, loc) ->
printer.Print("return ", ?loc = loc)
printer.Print(argument)
// If a JSX template starts with a new line, surround it in parens to avoid
// having only return in single line (this causes JS to ignore the rest of the code)
match argument with
| JsxTemplate(parts,_) when Regex.IsMatch(parts[0], @"^\s*\n") ->
printer.WithParens(argument)
| _ ->
printer.Print(argument)
| SwitchStatement(discriminant, cases, loc) -> printer.PrintSwitchStatement(discriminant, cases, loc)
| LabeledStatement(body, label) -> printer.PrintLabeledStatement(body, label)
| DebuggerStatement(loc) -> printer.Print("debugger", ?loc = loc)
Expand Down
9 changes: 9 additions & 0 deletions src/Fable.Transforms/Fable2Babel.fs
Original file line number Diff line number Diff line change
Expand Up @@ -1294,6 +1294,15 @@ module Util =
| Some "jsx", _ ->
"Expecting a static list or array literal (no generator) for JSX props"
|> addErrorAndReturnNull com range |> Some
| Some "jsx-template", args ->
match args with
| StringConst template ::_ -> Expression.jsxTemplate(template) |> Some
| MaybeCasted(Fable.Value(Fable.StringTemplate(_, parts, values), _))::_ ->
let values = values |> List.mapToArray (transformAsExpr com ctx)
Expression.jsxTemplate(List.toArray parts, values) |> Some
| _ ->
$"Expecting an interpolated string literal without formatting, found %A{args}"
|> addErrorAndReturnNull com range |> Some
| _ -> None

match optimized, callInfo.CallMemberInfo with
Expand Down
3 changes: 3 additions & 0 deletions src/Fable.Transforms/Global/Babel.fs
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,7 @@ type Node =
/// Since the left-hand side of an assignment may be any expression in general, an expression can also be a pattern.
type Expression =
| JsxElement of componentOrTag: Expression * props: (string * Expression) list * children: Expression list
| JsxTemplate of parts: string[] * values: Expression[]
| Literal of Literal
| Identifier of Identifier
| ClassExpression of
Expand Down Expand Up @@ -513,6 +514,8 @@ type InterfaceExtends =
module Helpers =
type Expression with
static member jsxElement(componentOrTag, props, children) = JsxElement(componentOrTag, props, children)
static member jsxTemplate(parts, values) = JsxTemplate(parts, values)
static member jsxTemplate(part) = JsxTemplate([|part|], [||])
static member super(?loc) = Super loc
static member emitExpression(value, args, ?loc) = EmitExpression(value, args, loc)
static member nullLiteral(?loc) = NullLiteral loc |> Literal
Expand Down
2 changes: 1 addition & 1 deletion src/Fable.Transforms/Replacements.Util.fs
Original file line number Diff line number Diff line change
Expand Up @@ -440,7 +440,7 @@ let tryFindInScope (ctx: Context) identName =

let (|MaybeInScope|) (ctx: Context) e =
match e with
| IdentExpr ident ->
| MaybeCasted(IdentExpr ident) ->
match tryFindInScope ctx ident.Name with
| Some e -> e
| None -> e
Expand Down
2 changes: 2 additions & 0 deletions src/Fable.Transforms/Replacements.fs
Original file line number Diff line number Diff line change
Expand Up @@ -968,6 +968,8 @@ let fableCoreLib (com: ICompiler) (ctx: Context) r t (i: CallInfo) (thisArg: Exp
| _ -> None
| "Fable.Core.JSX", "create" ->
Helper.LibCall(com, "JSX", "create", t, args, ?loc=r) |> withTag "jsx" |> Some
| "Fable.Core.JSX", "html" ->
Helper.LibCall(com, "JSX", "html", t, args, ?loc=r) |> withTag "jsx-template" |> Some
| _ -> None

let getReference r t expr = getFieldWith r t expr "contents"
Expand Down

0 comments on commit 1c761c2

Please sign in to comment.