vapor / leaf-kit

🍃 An expressive, performant, and extensible templating language built for Swift.
https://docs.vapor.codes/4.0/leaf/getting-started
MIT License
49 stars 38 forks source link

`#extend` with two parameters doesn’t pass second parameter as context when nested #126

Closed benblakely closed 11 months ago

benblakely commented 11 months ago

Description

When nesting an #extend with two parameters, the second parameter seems to be ignored instead of being provided as context to the template.

To Reproduce

Here’s a test (based on the existing testExtendWithSugar) that shows the issue and fails:

func testNestedExtendWithSugar() throws {
    let layout = """
    <body>#import("main")</body>
    """

    let header = """
    <h1>#(child)</h1>
    """

    let base = """
    #extend("layout"):#export("main"):#extend("header", parent)#endexport#endextend
    """

    let expected = """
    <body><h1>Elizabeth</h1></body>
    """

    let layoutAST = try LeafAST(name: "layout", ast: parse(layout))
    let headerAST = try LeafAST(name: "header", ast: parse(header))
    let baseAST = try LeafAST(name: "base", ast: parse(base))

    let baseResolved = LeafAST(from: baseAST, referencing: ["layout": layoutAST, "header": headerAST])

    var serializer = LeafSerializer(
        ast: baseResolved.ast,
        ignoreUnfoundImports: false
    )
    let view = try serializer.serialize(context: ["parent": ["child": "Elizabeth"]])
    let str = view.getString(at: view.readerIndex, length: view.readableBytes) ?? ""

    XCTAssertEqual(str, expected)
}

The test fails with:

XCTAssertEqual failed: ("<body><h1></h1></body>") is not equal to ("<body><h1>Elizabeth</h1></body>")

Expected behavior

My understanding from #111 is that when #extend is given two parameters, the second parameter should be passed as context to the template (which sounds like a very useful feature). That behavior seems to happen correctly when the #extend isn’t nested, but doesn’t work when nested.

My hope is to use this feature to pass data around like the example below:

items/list.leaf:

#extend("layouts/main"):
  #export("content"):
    <h1>Items</h1>
    #for(item in items):
      #extend("items/item", item)
    #endfor
  #endexport
#endextend

items/item.leaf:

<div>
  <h2>#(name)</h2>
  …
</div>

Environment

Additional context

I’ve tried to figure out how to fix the issue but haven’t been able to yet.

I also tried to workaround the issue by wrapping my nested #extend in a #with. From the example above, it would look like this:

items/list.leaf:

#extend("layouts/main"):
  #export("content"):
    <h1>Items</h1>
    #for(item in items):
      #with(item):
        #extend("items/item")
      #endwith
    #endfor
  #endexport
#endextend

But that seems to cause the entire items/item template to not be rendered. I’m not sure if that’s intentional or not. If not, I can file a separate issue for that. Thanks!