diff --git a/.changeset/gold-bananas-drum.md b/.changeset/gold-bananas-drum.md
new file mode 100644
index 000000000..c2fc4a646
--- /dev/null
+++ b/.changeset/gold-bananas-drum.md
@@ -0,0 +1,5 @@
+---
+'@astrojs/compiler': patch
+---
+
+Rollbacks the dynamic slot generation feature to rework it
diff --git a/internal/printer/print-to-js.go b/internal/printer/print-to-js.go
index c37cf2061..c43b0d8b2 100644
--- a/internal/printer/print-to-js.go
+++ b/internal/printer/print-to-js.go
@@ -69,12 +69,6 @@ type ExtractedStatement struct {
Loc loc.Loc
}
-type NestedSlotChild struct {
- SlotProp string
- Children []*Node
- FirstInGroup bool
-}
-
func printToJs(p *printer, n *Node, cssLen int, opts transform.TransformOptions) PrintResult {
printedMaybeHead := false
render1(p, n, RenderOptions{
@@ -432,7 +426,7 @@ func render1(p *printer, n *Node, opts RenderOptions) {
p.printAttributesToObject(n)
} else if isSlot {
if len(n.Attr) == 0 {
- p.print(DEFAULT_SLOT_PROP)
+ p.print(`"default"`)
} else {
slotted := false
for _, a := range n.Attr {
@@ -445,7 +439,6 @@ func render1(p *printer, n *Node, opts RenderOptions) {
p.print(`"` + escapeDoubleQuote(a.Val) + `"`)
slotted = true
default:
- // add ability to use expressions for slot names later
p.handler.AppendError(&loc.ErrorWithRange{
Code: loc.ERROR_UNSUPPORTED_SLOT_ATTRIBUTE,
Text: "slot[name] must be a static string",
@@ -454,7 +447,7 @@ func render1(p *printer, n *Node, opts RenderOptions) {
}
}
if !slotted {
- p.print(DEFAULT_SLOT_PROP)
+ p.print(`"default"`)
}
}
p.print(`]`)
@@ -558,8 +551,8 @@ func render1(p *printer, n *Node, opts RenderOptions) {
if !isAllWhiteSpace {
switch true {
case n.CustomElement:
- p.print(`,({`)
- p.print(fmt.Sprintf(`%s: () => `, DEFAULT_SLOT_PROP))
+ p.print(`,{`)
+ p.print(fmt.Sprintf(`"%s": () => `, "default"))
p.printTemplateLiteralOpen()
for c := n.FirstChild; c != nil; c = c.NextSibling {
render1(p, c, RenderOptions{
@@ -572,9 +565,166 @@ func render1(p *printer, n *Node, opts RenderOptions) {
})
}
p.printTemplateLiteralClose()
- p.print(`,})`)
+ p.print(`,}`)
case isComponent:
- handleSlots(p, n, opts, depth)
+ p.print(`,`)
+ slottedChildren := make(map[string][]*Node)
+ conditionalSlottedChildren := make([][]*Node, 0)
+ for c := n.FirstChild; c != nil; c = c.NextSibling {
+ slotProp := `"default"`
+ for _, a := range c.Attr {
+ if a.Key == "slot" {
+ if a.Type == QuotedAttribute {
+ slotProp = fmt.Sprintf(`"%s"`, escapeDoubleQuote(a.Val))
+ } else if a.Type == ExpressionAttribute {
+ slotProp = fmt.Sprintf(`[%s]`, a.Val)
+ } else {
+ p.handler.AppendError(&loc.ErrorWithRange{
+ Code: loc.ERROR_UNSUPPORTED_SLOT_ATTRIBUTE,
+ Text: "slot[name] must be a static string",
+ Range: loc.Range{Loc: a.ValLoc, Len: len(a.Val)},
+ })
+ }
+ }
+ }
+ if c.Expression {
+ nestedSlots := make([]string, 0)
+ for c1 := c.FirstChild; c1 != nil; c1 = c1.NextSibling {
+ for _, a := range c1.Attr {
+ if a.Key == "slot" {
+ if a.Type == QuotedAttribute {
+ nestedSlotProp := fmt.Sprintf(`"%s"`, escapeDoubleQuote(a.Val))
+ nestedSlots = append(nestedSlots, nestedSlotProp)
+ } else if a.Type == ExpressionAttribute {
+ nestedSlotProp := fmt.Sprintf(`[%s]`, a.Val)
+ nestedSlots = append(nestedSlots, nestedSlotProp)
+ } else {
+ panic(`unknown slot attribute type`)
+ }
+ }
+ }
+ }
+
+ if len(nestedSlots) == 1 {
+ slotProp = nestedSlots[0]
+ slottedChildren[slotProp] = append(slottedChildren[slotProp], c)
+ continue
+ } else if len(nestedSlots) > 1 {
+ conditionalChildren := make([]*Node, 0)
+ child_loop:
+ for c1 := c.FirstChild; c1 != nil; c1 = c1.NextSibling {
+ for _, a := range c1.Attr {
+ if a.Key == "slot" {
+ if a.Type == QuotedAttribute {
+ nestedSlotProp := fmt.Sprintf(`"%s"`, escapeDoubleQuote(a.Val))
+ nestedSlots = append(nestedSlots, nestedSlotProp)
+ conditionalChildren = append(conditionalChildren, &Node{Type: TextNode, Data: fmt.Sprintf("{%s: () => ", nestedSlotProp), Loc: make([]loc.Loc, 1)})
+ conditionalChildren = append(conditionalChildren, c1)
+ conditionalChildren = append(conditionalChildren, &Node{Type: TextNode, Data: "}", Loc: make([]loc.Loc, 1)})
+ continue child_loop
+ } else if a.Type == ExpressionAttribute {
+ nestedSlotProp := fmt.Sprintf(`[%s]`, a.Val)
+ nestedSlots = append(nestedSlots, nestedSlotProp)
+ conditionalChildren = append(conditionalChildren, &Node{Type: TextNode, Data: fmt.Sprintf("{%s: () => ", nestedSlotProp), Loc: make([]loc.Loc, 1)})
+ conditionalChildren = append(conditionalChildren, c1)
+ conditionalChildren = append(conditionalChildren, &Node{Type: TextNode, Data: "}", Loc: make([]loc.Loc, 1)})
+ continue child_loop
+ } else {
+ panic(`unknown slot attribute type`)
+ }
+ }
+ }
+ conditionalChildren = append(conditionalChildren, c1)
+ }
+ conditionalSlottedChildren = append(conditionalSlottedChildren, conditionalChildren)
+ continue
+ }
+ }
+
+ // Only slot ElementNodes (except expressions containing only comments) or non-empty TextNodes!
+ // CommentNode, JSX comments and others should not be slotted
+ if expressionOnlyHasComment(c) {
+ continue
+ }
+ if c.Type == ElementNode || c.Type == TextNode && !emptyTextNodeWithoutSiblings(c) {
+ slottedChildren[slotProp] = append(slottedChildren[slotProp], c)
+ }
+ }
+ // fix: sort keys for stable output
+ slottedKeys := make([]string, 0, len(slottedChildren))
+ for k := range slottedChildren {
+ slottedKeys = append(slottedKeys, k)
+ }
+ sort.Strings(slottedKeys)
+ if len(conditionalSlottedChildren) > 0 {
+ p.print(`$$mergeSlots(`)
+ }
+ p.print(`{`)
+ numberOfSlots := len(slottedKeys)
+ if numberOfSlots > 0 {
+ childrenLoop:
+ for _, slotProp := range slottedKeys {
+ children := slottedChildren[slotProp]
+
+ // If there are named slots, the default slot cannot be only whitespace
+ if numberOfSlots > 1 && slotProp == "\"default\"" {
+ // Loop over the children and verify that at least one non-whitespace node exists.
+ foundNonWhitespace := false
+ for _, child := range children {
+ if child.Type != TextNode || strings.TrimSpace(child.Data) != "" {
+ foundNonWhitespace = true
+ }
+ }
+ if !foundNonWhitespace {
+ continue childrenLoop
+ }
+ }
+
+ // If selected, pass through result object on the Astro side
+ if opts.opts.ResultScopedSlot {
+ p.print(fmt.Sprintf(`%s: ($$result) => `, slotProp))
+ } else {
+ p.print(fmt.Sprintf(`%s: () => `, slotProp))
+ }
+
+ p.printTemplateLiteralOpen()
+ for _, child := range children {
+ render1(p, child, RenderOptions{
+ isRoot: false,
+ isExpression: opts.isExpression,
+ depth: depth + 1,
+ opts: opts.opts,
+ cssLen: opts.cssLen,
+ printedMaybeHead: opts.printedMaybeHead,
+ })
+ }
+ p.printTemplateLiteralClose()
+ p.print(`,`)
+ }
+ }
+ p.print(`}`)
+ if len(conditionalSlottedChildren) > 0 {
+ for _, children := range conditionalSlottedChildren {
+ p.print(",")
+ for _, child := range children {
+ if child.Type == ElementNode {
+ p.printTemplateLiteralOpen()
+ }
+ render1(p, child, RenderOptions{
+ isRoot: false,
+ isExpression: opts.isExpression,
+ depth: depth + 1,
+ opts: opts.opts,
+ cssLen: opts.cssLen,
+ printedMaybeHead: opts.printedMaybeHead,
+ })
+ if child.Type == ElementNode {
+ p.printTemplateLiteralClose()
+ }
+ }
+ }
+ p.print(`)`)
+ }
case isSlot:
p.print(`,`)
p.printTemplateLiteralOpen()
@@ -638,8 +788,6 @@ func render1(p *printer, n *Node, opts RenderOptions) {
}
}
-const DEFAULT_SLOT_PROP = `"default"`
-
// Section 12.1.2, "Elements", gives this list of void elements. Void elements
// are those that can't have any contents.
// nolint
@@ -660,313 +808,3 @@ var voidElements = map[string]bool{
"track": true,
"wbr": true,
}
-
-func handleSlots(p *printer, n *Node, opts RenderOptions, depth int) {
- p.print(`,`)
- slottedChildren := make(map[string][]*Node)
- hasAnyNestedDynamicSlot := false
- nestedSlotChildren := make([]*NestedSlotChild, 0)
-
- // the highest number of nested slots in an expression
- maxNestedSlotsCount := 0
-
- for c := n.FirstChild; c != nil; c = c.NextSibling {
- slotProp := DEFAULT_SLOT_PROP
- for _, a := range c.Attr {
- if a.Key == "slot" {
- if a.Type == QuotedAttribute {
- slotProp = fmt.Sprintf(`"%s"`, escapeDoubleQuote(a.Val))
- } else if a.Type == ExpressionAttribute {
- slotProp = fmt.Sprintf(`[%s]`, a.Val)
- } else if a.Type == TemplateLiteralAttribute {
- slotProp = fmt.Sprintf(`[%s%s%s]`, BACKTICK, a.Val, BACKTICK)
- } else {
- p.handler.AppendError(&loc.ErrorWithRange{
- Code: loc.ERROR_UNSUPPORTED_SLOT_ATTRIBUTE,
- Text: "Unsupported slot attribute type",
- Range: loc.Range{Loc: a.ValLoc, Len: len(a.Val)},
- })
- }
- }
- }
- if c.Expression {
- // Only slot ElementNodes (except expressions containing only comments) or non-empty TextNodes!
- // CommentNode, JSX comments and others should not be slotted
- if expressionOnlyHasComment(c) {
- continue
- }
- nestedSlotsInExprCount := 0
- hasAnyDynamicSlotsInExpr := false
- var slotProp = DEFAULT_SLOT_PROP
- for c1 := c.FirstChild; c1 != nil; c1 = c1.NextSibling {
- for _, a := range c1.Attr {
- if a.Key == "slot" {
- if a.Type == QuotedAttribute {
- slotProp = fmt.Sprintf(`"%s"`, escapeDoubleQuote(a.Val))
- } else if a.Type == ExpressionAttribute {
- slotProp = fmt.Sprintf(`[%s]`, a.Val)
- hasAnyNestedDynamicSlot, hasAnyDynamicSlotsInExpr = true, true
- } else if a.Type == TemplateLiteralAttribute {
- slotProp = fmt.Sprintf(`[%s%s%s]`, BACKTICK, a.Val, BACKTICK)
- hasAnyNestedDynamicSlot, hasAnyDynamicSlotsInExpr = true, true
- } else {
- panic(`unknown slot attribute type`)
- }
- }
- }
- if c1.Type == ElementNode {
- nestedSlotsInExprCount++
- }
- }
-
- if nestedSlotsInExprCount == 1 && !hasAnyDynamicSlotsInExpr {
- slottedChildren[slotProp] = append(slottedChildren[slotProp], c)
- continue
- } else if nestedSlotsInExprCount > 1 || hasAnyDynamicSlotsInExpr {
- if nestedSlotsInExprCount > maxNestedSlotsCount {
- maxNestedSlotsCount = nestedSlotsInExprCount
- }
- child_loop:
- for c1 := c.FirstChild; c1 != nil; c1 = c1.NextSibling {
- foundNamedSlot := false
- isFirstInGroup := c1 == c.FirstChild
- for _, a := range c1.Attr {
- if a.Key == "slot" {
- var nestedSlotProp string
- var nestedSlotEntry *NestedSlotChild
- if a.Type == QuotedAttribute {
- nestedSlotProp = fmt.Sprintf(`"%s"`, escapeDoubleQuote(a.Val))
- } else if a.Type == ExpressionAttribute {
- nestedSlotProp = fmt.Sprintf(`[%s]`, a.Val)
- hasAnyNestedDynamicSlot = true
- } else if a.Type == TemplateLiteralAttribute {
- hasAnyNestedDynamicSlot = true
- nestedSlotProp = fmt.Sprintf(`[%s%s%s]`, BACKTICK, a.Val, BACKTICK)
- } else {
- panic(`unknown slot attribute type`)
- }
- foundNamedSlot = true
- nestedSlotEntry = &NestedSlotChild{nestedSlotProp, []*Node{c1}, isFirstInGroup}
- nestedSlotChildren = append(nestedSlotChildren, nestedSlotEntry)
- continue child_loop
- }
- }
- if !foundNamedSlot && c1.Type == ElementNode {
- pseudoSlotEntry := &NestedSlotChild{DEFAULT_SLOT_PROP, []*Node{c1}, isFirstInGroup}
- nestedSlotChildren = append(nestedSlotChildren, pseudoSlotEntry)
- } else {
- nestedSlotEntry := &NestedSlotChild{`"@@NON_ELEMENT_ENTRY"`, []*Node{c1}, isFirstInGroup}
- nestedSlotChildren = append(nestedSlotChildren, nestedSlotEntry)
- }
- }
- continue
- }
- }
-
- if c.Type == ElementNode || c.Type == TextNode && !emptyTextNodeWithoutSiblings(c) {
- slottedChildren[slotProp] = append(slottedChildren[slotProp], c)
- }
- }
- // fix: sort keys for stable output
- slottedKeys := make([]string, 0, len(slottedChildren))
- for k := range slottedChildren {
- slottedKeys = append(slottedKeys, k)
- }
- sort.Strings(slottedKeys)
-
- // if any slotted expression contains more than one nested slot (e.g. {true ? Bar
: Foo
})
- // OR if any expression contains a dynamic slot (e.g. {items.map((item)=> ({item.name}
)})
- // we need to use $$mergeSlots
- shouldPrintMergeSlots := maxNestedSlotsCount > 1 || hasAnyNestedDynamicSlot
- if shouldPrintMergeSlots {
- p.print(`$$mergeSlots(`)
- }
- p.print(`({`)
- numberOfSlots := len(slottedKeys)
- if numberOfSlots > 0 {
- childrenLoop:
- for _, slotProp := range slottedKeys {
- children := slottedChildren[slotProp]
-
- // If there are named slots, the default slot cannot be only whitespace
- if numberOfSlots > 1 && slotProp == DEFAULT_SLOT_PROP {
- // Loop over the children and verify that at least one non-whitespace node exists.
- foundNonWhitespace := false
- for _, child := range children {
- if child.Type != TextNode || strings.TrimSpace(child.Data) != "" {
- foundNonWhitespace = true
- }
- }
- if !foundNonWhitespace {
- continue childrenLoop
- }
- }
-
- // If selected, pass through result object on the Astro side
- if opts.opts.ResultScopedSlot {
- p.print(fmt.Sprintf(`%s: ($$result) => `, slotProp))
- } else {
- p.print(fmt.Sprintf(`%s: () => `, slotProp))
- }
-
- p.printTemplateLiteralOpen()
- for _, child := range children {
- render1(p, child, RenderOptions{
- isRoot: false,
- isExpression: opts.isExpression,
- depth: depth + 1,
- opts: opts.opts,
- cssLen: opts.cssLen,
- printedMaybeHead: opts.printedMaybeHead,
- })
- }
- p.printTemplateLiteralClose()
- p.print(`,`)
- }
- }
- p.print(`})`)
- // print nested slots
- if len(nestedSlotChildren) > 0 {
- endSlotIndexes := generateEndSlotIndexes(nestedSlotChildren)
- mergeDefaultSlotsAndUpdateIndexes(&nestedSlotChildren, &endSlotIndexes)
- hasFoundFirstElementNode := false
- for j, nestedSlot := range nestedSlotChildren {
- if nestedSlot.FirstInGroup {
- p.print(`,`)
- }
- // whether this is the first element node in the chain
- // (used to determine which slot render function to use)
- var isFirstElementInChain bool
- isLastInChain := endSlotIndexes[j]
- if nestedSlot.Children[0].Type == ElementNode && !hasFoundFirstElementNode {
- isFirstElementInChain = true
- hasFoundFirstElementNode = true
- }
- renderNestedSlotChild(p, nestedSlot, isFirstElementInChain, isLastInChain, depth, opts)
- if isLastInChain {
- // reset hasFoundFirstElementNode for the next chain
- hasFoundFirstElementNode = false
- }
- }
- }
- if shouldPrintMergeSlots {
- // close $$mergeSlots call
- p.print(`)`)
- }
-}
-
-// Helper function to encapsulate nested slot entry rendering
-func renderNestedSlotChild(p *printer, nestedSlotChild *NestedSlotChild, isFirstElementInChain bool, isLastInChain bool, depth int, opts RenderOptions) {
- if nestedSlotChild.SlotProp == `"@@NON_ELEMENT_ENTRY"` {
- for _, child := range nestedSlotChild.Children {
- p.print(child.Data)
- }
- return
- }
- slotRenderFunction := getSlotRenderFunction(isFirstElementInChain)
- slotRenderFunctionNode := &Node{Type: TextNode, Data: fmt.Sprintf(slotRenderFunction, nestedSlotChild.SlotProp), Loc: make([]loc.Loc, 1)}
- // print the slot render function
- render1(p, slotRenderFunctionNode, RenderOptions{
- isRoot: false,
- isExpression: opts.isExpression,
- depth: depth + 1,
- opts: opts.opts,
- cssLen: opts.cssLen,
- printedMaybeHead: opts.printedMaybeHead,
- })
-
- // print the nested slotted children
- p.printTemplateLiteralOpen()
- for _, child := range nestedSlotChild.Children {
- render1(p, child, RenderOptions{
- isRoot: false,
- isExpression: false,
- depth: depth,
- opts: opts.opts,
- cssLen: opts.cssLen,
- printedMaybeHead: opts.printedMaybeHead,
- })
- }
- p.printTemplateLiteralClose()
-
- // when we are at the end of the chain, close the slot render function
- if isLastInChain {
- p.print(`})`)
- }
-}
-
-func generateEndSlotIndexes(nestedSlotChildren []*NestedSlotChild) map[int]bool {
- endSlotIndexes := make(map[int]bool)
- var latestElementNodeIndex int
-
- for i, nestedSlot := range nestedSlotChildren {
- if nestedSlot.Children[0].Type == ElementNode {
- latestElementNodeIndex = i
- } else if isNonWhitespaceTextNode(nestedSlot.Children[0]) {
- endSlotIndexes[latestElementNodeIndex] = true
- }
- }
-
- // Ensure the last element node index is also added to endSlotIndexes
- if latestElementNodeIndex < len(nestedSlotChildren) {
- endSlotIndexes[latestElementNodeIndex] = true
- }
-
- return endSlotIndexes
-}
-
-func mergeDefaultSlotsAndUpdateIndexes(nestedSlotChildren *[]*NestedSlotChild, endSlotIndexes *map[int]bool) {
- bufferedDefaultSlot := &NestedSlotChild{SlotProp: DEFAULT_SLOT_PROP, Children: []*Node{}}
- updatedNestedSlotChildren := make([]*NestedSlotChild, 0)
- updatedEndSlotIndexes := make(map[int]bool)
-
- for i, nestedSlot := range *nestedSlotChildren {
- if isDefaultSlot(nestedSlot) {
- bufferedDefaultSlot.Children = append(bufferedDefaultSlot.Children, nestedSlot.Children...)
- } else {
- updatedNestedSlotChildren = append(updatedNestedSlotChildren, nestedSlot)
- }
-
- // we reached the end of a slot chain
- if (*endSlotIndexes)[i] {
- // free up memory, this information is now outdated
- // the updated information is stored in updatedEndSlotIndexes
- delete(*endSlotIndexes, i)
-
- if len(bufferedDefaultSlot.Children) > 0 {
- // if the buffered default slot contains any children
- // add it to the updated nested slot children
- updatedNestedSlotChildren = append(updatedNestedSlotChildren, bufferedDefaultSlot)
-
- // reset the buffered default slot
- bufferedDefaultSlot = &NestedSlotChild{SlotProp: DEFAULT_SLOT_PROP, Children: []*Node{}}
- }
- // record the index of the last slot in the chain
- updatedEndSlotIndexes[len(updatedNestedSlotChildren)-1] = true
- }
-
- // free up memory, the actual information of nested slot
- // is now stored in updatedNestedSlotChildren
- (*nestedSlotChildren)[i] = nil
- }
- *nestedSlotChildren = updatedNestedSlotChildren
- *endSlotIndexes = updatedEndSlotIndexes
-}
-
-func getSlotRenderFunction(isNewSlotObject bool) string {
- const FIRST_SLOT_CHILD_FUNCTION = "({%s: () => "
- const NEXT_SLOT_CHILD_FUNCTION = ", %s: () => "
-
- if isNewSlotObject {
- return FIRST_SLOT_CHILD_FUNCTION
- }
- return NEXT_SLOT_CHILD_FUNCTION
-}
-
-func isNonWhitespaceTextNode(n *Node) bool {
- return n.Type == TextNode && strings.TrimSpace(n.Data) != ""
-}
-
-func isDefaultSlot(slot *NestedSlotChild) bool {
- return slot.SlotProp == DEFAULT_SLOT_PROP
-}
diff --git a/internal/printer/printer_test.go b/internal/printer/printer_test.go
index 9ed3248df..ed24a6827 100644
--- a/internal/printer/printer_test.go
+++ b/internal/printer/printer_test.go
@@ -165,14 +165,14 @@ func TestPrinter(t *testing.T) {
name: "ternary component",
source: `{special ? Special
:
Not special
}`,
want: want{
- code: `${special ? $$render` + BACKTICK + `${$$renderComponent($$result,'ChildDiv',ChildDiv,{},({"default": () => $$render` + BACKTICK + `${$$maybeRenderHead($$result)}Special
` + BACKTICK + `,}))}` + BACKTICK + ` : $$render` + BACKTICK + `Not special
` + BACKTICK + `}`,
+ code: `${special ? $$render` + BACKTICK + `${$$renderComponent($$result,'ChildDiv',ChildDiv,{},{"default": () => $$render` + BACKTICK + `${$$maybeRenderHead($$result)}Special
` + BACKTICK + `,})}` + BACKTICK + ` : $$render` + BACKTICK + `Not special
` + BACKTICK + `}`,
},
},
{
name: "ternary layout",
source: `{toggleError ? SITE: {Astro.site}
: <>SITE: {Astro.site}
>}`,
want: want{
- code: `${toggleError ? $$render` + BACKTICK + `${$$renderComponent($$result,'BaseLayout',BaseLayout,{},({"default": () => $$render` + BACKTICK + `${$$maybeRenderHead($$result)}SITE: ${Astro.site}
` + BACKTICK + `,}))}` + BACKTICK + ` : $$render` + BACKTICK + `${$$renderComponent($$result,'Fragment',Fragment,{},({"default": () => $$render` + BACKTICK + `SITE: ${Astro.site}
` + BACKTICK + `,}))}` + BACKTICK + `}`,
+ code: `${toggleError ? $$render` + BACKTICK + `${$$renderComponent($$result,'BaseLayout',BaseLayout,{},{"default": () => $$render` + BACKTICK + `${$$maybeRenderHead($$result)}SITE: ${Astro.site}
` + BACKTICK + `,})}` + BACKTICK + ` : $$render` + BACKTICK + `${$$renderComponent($$result,'Fragment',Fragment,{},{"default": () => $$render` + BACKTICK + `SITE: ${Astro.site}
` + BACKTICK + `,})}` + BACKTICK + `}`,
},
},
{
@@ -182,164 +182,63 @@ func TestPrinter(t *testing.T) {
code: `${$$renderSlot($$result,$$slots["default"])}`,
},
},
- {
- name: "nested dynamic slots I",
- source: `{items.map((item)=>)}`,
- want: want{
- code: "${$$renderComponent($$result,'Component',Component,{},$$mergeSlots(({}),items.map((item)=>({[item.id]: () => $$render`${$$maybeRenderHead($$result)}`}))))}",
- },
- },
- {
- name: "nested dynamic slots II",
- source: `{items.map((item)=>)}`,
- want: want{
- code: "${$$renderComponent($$result,'Component',Component,{},$$mergeSlots(({}),items.map((item)=>({[item.id]: () => $$render`${$$maybeRenderHead($$result)}`, [item.id+1]: () => $$render``}))))}",
- },
- },
- {
- name: "nested dynamic slots III",
- source: `{items.map((item)=> hey
There)}`,
- want: want{
- code: "${$$renderComponent($$result,'Component',Component,{},$$mergeSlots(({}),items.map((item)=> ({[item.id]: () => $$render`${$$maybeRenderHead($$result)}`, [item.id+1]: () => $$render``, \"default\": () => $$render`hey
There`}))))}",
- },
- },
- {
- name: "nested dynamic slots IV",
- source: `{items.map((item)=> hey
There )}`,
- want: want{
- code: "${$$renderComponent($$result,'Component',Component,{},$$mergeSlots(({}),items.map((item)=> ({[item.id]: () => $$render`${$$maybeRenderHead($$result)}` , [item.id+1]: () => $$render`` , \"default\": () => $$render`hey
There`}))))}",
- },
- },
- {
- name: "nested dynamic slots V",
- source: `
-
- {items.map((item) => (
- hey
-
- There
-
-
- ))}
-`,
- want: want{
- code: "${$$renderComponent($$result,'Component',Component,{},$$mergeSlots(({}),items.map((item) => (\n\t\t({[item.id]: () => $$render`${$$maybeRenderHead($$result)}`\n\t\t, [item.id + 1]: () => $$render``\n\t\t, \"default\": () => $$render`hey
There`})\n\t))))}",
- },
- },
{
name: "conditional slot",
source: `{value && foo
}`,
want: want{
- code: "${$$renderComponent($$result,'Component',Component,{},({\"test\": () => $$render`${value && $$render`${$$maybeRenderHead($$result)}foo
`}`,}))}",
+ code: "${$$renderComponent($$result,'Component',Component,{},{\"test\": () => $$render`${value && $$render`${$$maybeRenderHead($$result)}foo
`}`,})}",
},
},
{
name: "ternary slot",
source: `{Math.random() > 0.5 ? A
: B
}`,
want: want{
- code: "${$$renderComponent($$result,'Component',Component,{},$$mergeSlots(({}),Math.random() > 0.5 ? ({\"a\": () => $$render`${$$maybeRenderHead($$result)}A
`}) : ({\"b\": () => $$render`B
`})))}",
+ code: "${$$renderComponent($$result,'Component',Component,{},$$mergeSlots({},Math.random() > 0.5 ? {\"a\": () => $$render`${$$maybeRenderHead($$result)}A
`} : {\"b\": () => $$render`B
`}))}",
},
},
{
- name: "ternary slot II",
- source: `
- {
- Astro.request.method === 'GET' ? (
- Contact Form
-
- ) : (
- Got: {formData?.get('name')}
- )
- }
-`,
- want: want{
- code: `${$$renderComponent($$result,'Layout',Layout,{},$$mergeSlots(({}),
- Astro.request.method === 'GET' ? (
-
- ({"default": () => $$render` + BACKTICK + `${$$maybeRenderHead($$result)}Contact Form
` + BACKTICK + `})
- ) : (
- ({"default": () => $$render` + BACKTICK + `Got: ${formData?.get('name')}
` + BACKTICK + `})
- )
- ))}`,
- },
- },
- {
- name: "ternary slot with one implicit default",
- source: `
- {useSlot
- ? Inside slot with red background
- : Outside slot without background
- }
-`,
- want: want{
- code: `${$$renderComponent($$result,'Main',Main,{},$$mergeSlots(({}),useSlot
- ? ({"outside": () => $$render` + BACKTICK + `${$$maybeRenderHead($$result)}Inside slot with red background
` + BACKTICK + `})
- : ({"default": () => $$render` + BACKTICK + `Outside slot without background
` + BACKTICK + `})
- ))}`,
- },
- },
- {
- name: "function expression slots",
+ name: "function expression slots I",
source: "\n{() => { switch (value) {\ncase 'a': return A
\ncase 'b': return B
\ncase 'c': return C
\n}\n}}\n",
want: want{
- code: "${$$renderComponent($$result,'Component',Component,{},$$mergeSlots(({}),() => { switch (value) {\ncase 'a': return ({\"a\": () => $$render`${$$maybeRenderHead($$result)}A
`})\ncase 'b': return ({\"b\": () => $$render`B
`})\ncase 'c': return ({\"c\": () => $$render`C
`})}\n}))}",
+ code: "${$$renderComponent($$result,'Component',Component,{},$$mergeSlots({},() => { switch (value) {\ncase 'a': return {\"a\": () => $$render`${$$maybeRenderHead($$result)}A
`}\ncase 'b': return {\"b\": () => $$render`B
`}\ncase 'c': return {\"c\": () => $$render`C
`}}\n}))}",
},
},
{
- name: "expression slot I",
- source: `{true && A
}{false && B
}`,
+ name: "function expression slots II (#959)",
+ source: `
+
+
+ {components.map((component, i) => {
+ switch(component) {
+ case "Hero":
+ return Hero
+ case "Component2":
+ return Component2
+ }
+ })}
+
+
+`,
want: want{
- code: "${$$renderComponent($$result,'Component',Component,{},({\"a\": () => $$render`${true && $$render`${$$maybeRenderHead($$result)}A
`}`,\"b\": () => $$render`${false && $$render`B
`}`,}))}",
+ code: `${$$renderComponent($$result,'Layout',Layout,{"title":"Welcome to Astro."},{"default": () => $$render` + BACKTICK + `
+ ${$$maybeRenderHead($$result)}
+ ${$$renderComponent($$result,'Layout',Layout,{"title":"switch bug"},{"default": () => $$render` + BACKTICK + `${components.map((component, i) => {
+ switch(component) {
+ case "Hero":
+ return $$render` + BACKTICK + `Hero
` + BACKTICK + `
+ case "Component2":
+ return $$render` + BACKTICK + `Component2
` + BACKTICK + `
+ }
+ })}` + BACKTICK + `,})}
+
+` + BACKTICK + `,})}`,
},
},
{
- name: "expression slot II",
- source: `
- {true && A}
- {true ? B : null}
- {() => C}
- {true && Default}
-`,
+ name: "expression slot",
+ source: `{true && A
}{false && B
}`,
want: want{
- code: "${$$renderComponent($$result,'Slotted',Slotted,{},({\"a\": () => $$render`${true && $$render`${$$maybeRenderHead($$result)}A`}`,\"b\": () => $$render`${true ? $$render`B` : null}`,\"c\": () => $$render`${() => $$render`C`}`,\"default\": () => $$render`${true && $$render`Default`}`,}))}",
- },
- },
- {
- name: "expression slot III",
- source: `
- {true && A}
- {true ? B : null}
- {() => C}
- {() => {
- const value = 0.33;
- if (value > 0.25) {
- return DefaultAnother
- } else if (value > 0.5) {
- return Another
- } else if (value > 0.75) {
- return Other
- }
- return Yet Another
- }}
-`,
- want: want{
- code: `${$$renderComponent($$result,'Slotted',Slotted,{},$$mergeSlots(({"b": () => $$render` + BACKTICK + `${true ? $$render` + BACKTICK + `${$$maybeRenderHead($$result)}B` + BACKTICK + ` : null}` + BACKTICK + `,"c": () => $$render` + BACKTICK + `${() => $$render` + BACKTICK + `C` + BACKTICK + `}` + BACKTICK + `,}),true && ({["a"]: () => $$render` + BACKTICK + `A` + BACKTICK + `}),() => {
- const value = 0.33;
- if (value > 0.25) {
- return ({"hey": () => $$render` + BACKTICK + `Another` + BACKTICK + `, "default": () => $$render` + BACKTICK + `Default` + BACKTICK + `})
- } else if (value > 0.5) {
- return ({"hey": () => $$render` + BACKTICK + `Another` + BACKTICK + `})
- } else if (value > 0.75) {
- return ({"default": () => $$render` + BACKTICK + `Other` + BACKTICK + `})
- }
- return ({"default": () => $$render` + BACKTICK + `Yet Another` + BACKTICK + `})
- }))}`,
+ code: "${$$renderComponent($$result,'Component',Component,{},{\"a\": () => $$render`${true && $$render`${$$maybeRenderHead($$result)}A
`}`,\"b\": () => $$render`${false && $$render`B
`}`,})}",
},
},
{
@@ -384,9 +283,9 @@ func TestPrinter(t *testing.T) {
Paragraph 2
`,
want: want{
- code: `${$$renderComponent($$result,'Component',Component,{},({"default": () => $$render` + BACKTICK + `
+ code: `${$$renderComponent($$result,'Component',Component,{},{"default": () => $$render` + BACKTICK + `
${$$maybeRenderHead($$result)}Paragraph 1
- Paragraph 2
` + BACKTICK + `,}))}`,
+ Paragraph 2
` + BACKTICK + `,})}`,
},
},
{
@@ -612,7 +511,7 @@ import type data from "test"
},
},
{
- name: "nested template literal expression I",
+ name: "nested template literal expression",
source: "",
want: want{
code: "${$$maybeRenderHead($$result)}",
@@ -622,7 +521,7 @@ import type data from "test"
name: "component in expression with its child expression before its child element",
source: "{list.map(() => ({name}))}",
want: want{
- code: "${list.map(() => ($$render`${$$renderComponent($$result,'Component',Component,{},({\"default\": () => $$render`${name}`,}))}`))}",
+ code: "${list.map(() => ($$render`${$$renderComponent($$result,'Component',Component,{},{\"default\": () => $$render`${name}`,})}`))}",
},
},
{
@@ -651,7 +550,7 @@ import type data from "test"
`,
want: want{
- code: `${$$renderComponent($$result,'Layout',Layout,{"title":"Welcome to Astro."},({"default": () => $$render` + BACKTICK + `
+ code: `${$$renderComponent($$result,'Layout',Layout,{"title":"Welcome to Astro."},{"default": () => $$render` + BACKTICK + `
${$$maybeRenderHead($$result)}
Welcome to Astro
${
@@ -673,7 +572,7 @@ import type data from "test"
})
}
-` + BACKTICK + `,}))}`,
+` + BACKTICK + `,})}`,
},
},
{
@@ -765,7 +664,21 @@ import * as ns from '../components';
name: "slot with quoted attributes",
source: ``,
want: want{
- code: `${` + RENDER_COMPONENT + `($$result,'Component',Component,{},({"\"name\"": () => $$render` + BACKTICK + `${$$maybeRenderHead($$result)}` + BACKTICK + `,}))}`,
+ code: `${` + RENDER_COMPONENT + `($$result,'Component',Component,{},{"\"name\"": () => $$render` + BACKTICK + `${$$maybeRenderHead($$result)}` + BACKTICK + `,})}`,
+ },
+ },
+ {
+ name: "#955 ternary slot with text",
+ source: `Hello{isLeaf ? Leaf
: Branch
}world`,
+ want: want{
+ code: `${` + RENDER_COMPONENT + `($$result,'Component',Component,{},{"default": () => $$render` + BACKTICK + `Hello${isLeaf ? $$render` + BACKTICK + `${$$maybeRenderHead($$result)}Leaf
` + BACKTICK + ` : $$render` + BACKTICK + `Branch
` + BACKTICK + `}world` + BACKTICK + `,})}`,
+ },
+ },
+ {
+ name: "#955 ternary slot with elements",
+ source: ``,
+ want: want{
+ code: `${` + RENDER_COMPONENT + `($$result,'Component',Component,{},{"default": () => $$render` + BACKTICK + `${$$maybeRenderHead($$result)}${isLeaf ? $$render` + BACKTICK + `
Leaf
` + BACKTICK + ` : $$render` + BACKTICK + `
Branch
` + BACKTICK + `}
` + BACKTICK + `,})}`,
},
},
{
@@ -1095,14 +1008,14 @@ const groups = [[0, 1, 2], [3, 4, 5]];
name: "HTML comment in component inside expression I",
source: "{(() => )}",
want: want{
- code: "${(() => $$render`${$$renderComponent($$result,'Component',Component,{},({}))}`)}",
+ code: "${(() => $$render`${$$renderComponent($$result,'Component',Component,{},{})}`)}",
},
},
{
name: "HTML comment in component inside expression II",
source: "{list.map(() => )}",
want: want{
- code: "${list.map(() => $$render`${$$renderComponent($$result,'Component',Component,{},({}))}`)}",
+ code: "${list.map(() => $$render`${$$renderComponent($$result,'Component',Component,{},{})}`)}",
},
},
{
@@ -1137,7 +1050,7 @@ const groups = [[0, 1, 2], [3, 4, 5]];
name: "nested expressions V",
source: `title
{list.map(group => {group.label}
{group.items.map(item => {item})})}`,
want: want{
- code: "${$$maybeRenderHead($$result)}title
${list.map(group => $$render`${$$renderComponent($$result,'Fragment',Fragment,{},({\"default\": () => $$render`${group.label}
${group.items.map(item => $$render`${item}`)}`,}))}`)}",
+ code: "${$$maybeRenderHead($$result)}title
${list.map(group => $$render`${$$renderComponent($$result,'Fragment',Fragment,{},{\"default\": () => $$render`${group.label}
${group.items.map(item => $$render`${item}`)}`,})}`)}",
},
},
{
@@ -1228,7 +1141,7 @@ import Component from "test";
want: want{
frontmatter: []string{`import Component from "test";`},
metadata: metadata{modules: []string{`{ module: $$module1, specifier: 'test', assert: {} }`}},
- code: `${$$renderComponent($$result,'Component',Component,{},({"default": () => $$render` + "`" + ` ${$$maybeRenderHead($$result)}Default
` + "`" + `,"named": () => $$render` + "`" + `Named
` + "`" + `,}))}`,
+ code: `${$$renderComponent($$result,'Component',Component,{},{"default": () => $$render` + "`" + ` ${$$maybeRenderHead($$result)}Default
` + "`" + `,"named": () => $$render` + "`" + `Named
` + "`" + `,})}`,
},
},
{
@@ -1244,7 +1157,7 @@ import Component from 'test';
want: want{
frontmatter: []string{`import Component from 'test';`},
metadata: metadata{modules: []string{`{ module: $$module1, specifier: 'test', assert: {} }`}},
- code: `${$$renderComponent($$result,'Component',Component,{},({"default": () => $$render` + "`" + ` ${$$maybeRenderHead($$result)}Default
` + "`" + `,"named": () => $$render` + "`" + `Named
` + "`" + `,}))}`,
+ code: `${$$renderComponent($$result,'Component',Component,{},{"default": () => $$render` + "`" + ` ${$$maybeRenderHead($$result)}Default
` + "`" + `,"named": () => $$render` + "`" + `Named
` + "`" + `,})}`,
},
},
{
@@ -1254,7 +1167,7 @@ import Component from 'test';
{items.map(item => {item}
)}
`,
want: want{
- code: `${$$renderComponent($$result,'Component',Component,{"data":(data)},({"default": () => $$render` + BACKTICK + `${items.map(item => $$render` + BACKTICK + `${$$maybeRenderHead($$result)}${item}
` + BACKTICK + `)}` + BACKTICK + `,}))}`,
+ code: `${$$renderComponent($$result,'Component',Component,{"data":(data)},{"default": () => $$render` + BACKTICK + `${items.map(item => $$render` + BACKTICK + `${$$maybeRenderHead($$result)}${item}
` + BACKTICK + `)}` + BACKTICK + `,})}`,
},
},
{
@@ -1303,7 +1216,7 @@ const testBool = true;
${testBool ? "Hey" : "Bye"}
- ${testBool && ($$render` + BACKTICK + `${$$renderComponent($$result,'Fragment',Fragment,{},({"default": () => $$render` + BACKTICK + `` + BACKTICK + `,}))}` + BACKTICK + `)}
+ ${testBool && ($$render` + BACKTICK + `${$$renderComponent($$result,'Fragment',Fragment,{},{"default": () => $$render` + BACKTICK + `` + BACKTICK + `,})}` + BACKTICK + `)}
` + RENDER_HEAD_RESULT + `
@@ -1324,11 +1237,11 @@ const testBool = true;
want: want{
code: `${
props.title && (
- $$render` + BACKTICK + `${$$renderComponent($$result,'Fragment',Fragment,{},({"default": () => $$render` + BACKTICK + `
+ $$render` + BACKTICK + `${$$renderComponent($$result,'Fragment',Fragment,{},{"default": () => $$render` + BACKTICK + `
${props.title}
- ` + BACKTICK + `,}))}` + BACKTICK + `
+ ` + BACKTICK + `,})}` + BACKTICK + `
)
}`,
},
@@ -1486,9 +1399,9 @@ const someProps = {
` + RENDER_HEAD_RESULT + `
- ${$$renderComponent($$result,'Counter',Counter,{...(someProps),"client:visible":true,"client:component-hydration":"visible","client:component-path":("../components/Counter.jsx"),"client:component-export":("default"),"class":"astro-hmnnhvcq"},({"default": () => $$render` + "`" + `
+ ${$$renderComponent($$result,'Counter',Counter,{...(someProps),"client:visible":true,"client:component-hydration":"visible","client:component-path":("../components/Counter.jsx"),"client:component-export":("default"),"class":"astro-hmnnhvcq"},{"default": () => $$render` + "`" + `
Hello React!
- ` + "`" + `,}))}
+ ` + "`" + `,})}