diff --git a/.changeset/poor-maps-fly.md b/.changeset/poor-maps-fly.md
new file mode 100644
index 000000000..406d59185
--- /dev/null
+++ b/.changeset/poor-maps-fly.md
@@ -0,0 +1,5 @@
+---
+'@astrojs/compiler': patch
+---
+
+Fix edge case with Fragment parsing in head, add `fragment` node to AST output
diff --git a/internal/parser.go b/internal/parser.go
index 9ad7c7118..c34c53d01 100644
--- a/internal/parser.go
+++ b/internal/parser.go
@@ -750,6 +750,9 @@ func inHeadIM(p *parser) bool {
return true
}
p.tok.Data = s
+ } else if p.oe.top() != nil && (isComponent(p.oe.top().Data) || isFragment((p.oe.top().Data))) {
+ p.addText(p.tok.Data)
+ return true
}
case StartTagToken:
// Allow components in Head
diff --git a/internal/printer/print-to-json.go b/internal/printer/print-to-json.go
index 6780aef57..89dd4ffc0 100644
--- a/internal/printer/print-to-json.go
+++ b/internal/printer/print-to-json.go
@@ -25,7 +25,7 @@ type ASTPoint struct {
type ASTNode struct {
Type string `json:"type"`
- Name string `json:"name,omitempty"`
+ Name string `json:"name"`
Value string `json:"value,omitempty"`
Attributes []ASTNode `json:"attributes,omitempty"`
Directives []ASTNode `json:"directives,omitempty"`
@@ -59,6 +59,8 @@ func (n ASTNode) String() string {
}
if n.Name != "" {
str += fmt.Sprintf(`,"name":"%s"`, escapeForJSON(n.Name))
+ } else if n.Type == "fragment" {
+ str += `,"name":""`
}
if n.Value != "" || n.Type == "attribute" {
str += fmt.Sprintf(`,"value":"%s"`, escapeForJSON(n.Value))
@@ -201,6 +203,8 @@ func renderNode(p *printer, parent *ASTNode, n *Node, opts t.ParseOptions) {
node.Type = "component"
} else if n.CustomElement {
node.Type = "custom-element"
+ } else if n.Fragment {
+ node.Type = "fragment"
} else {
node.Type = "element"
}
diff --git a/internal/printer/printer_test.go b/internal/printer/printer_test.go
index 41a709fd6..5f5dc2967 100644
--- a/internal/printer/printer_test.go
+++ b/internal/printer/printer_test.go
@@ -1244,6 +1244,20 @@ import { Container, Col, Row } from 'react-bootstrap';
code: `
${$$renderComponent($$result,'Fragment',Fragment,{},{"default": () => $$render` + BACKTICK + `Default
Named
` + BACKTICK + `,})}`,
},
},
+ {
+ name: "Fragment shorthand only",
+ source: `<>Hello>`,
+ want: want{
+ code: `${$$renderComponent($$result,'Fragment',Fragment,{},{"default": () => $$render` + BACKTICK + `Hello` + BACKTICK + `,})}`,
+ },
+ },
+ {
+ name: "Fragment literal only",
+ source: `world`,
+ want: want{
+ code: `${$$renderComponent($$result,'Fragment',Fragment,{},{"default": () => $$render` + BACKTICK + `world` + BACKTICK + `,})}`,
+ },
+ },
{
name: "Fragment slotted",
source: `<>Default
Named
>`,
@@ -1905,6 +1919,16 @@ func TestPrintToJSON(t *testing.T) {
source: ``,
want: []ASTNode{{Type: "comment", Value: " hello "}},
},
+ {
+ name: "Fragment Shorthand",
+ source: `<>Hello>`,
+ want: []ASTNode{{Type: "fragment", Name: "", Children: []ASTNode{{Type: "text", Value: "Hello"}}}},
+ },
+ {
+ name: "Fragment Literal",
+ source: `World`,
+ want: []ASTNode{{Type: "fragment", Name: "Fragment", Children: []ASTNode{{Type: "text", Value: "World"}}}},
+ },
{
name: "Frontmatter",
source: `---
diff --git a/lib/compiler/deno/astro.wasm b/lib/compiler/deno/astro.wasm
index 307f11984..eb3af38f1 100755
Binary files a/lib/compiler/deno/astro.wasm and b/lib/compiler/deno/astro.wasm differ
diff --git a/lib/compiler/test/parse-fragment.test.mjs b/lib/compiler/test/parse-fragment.test.mjs
new file mode 100644
index 000000000..77eda9837
--- /dev/null
+++ b/lib/compiler/test/parse-fragment.test.mjs
@@ -0,0 +1,25 @@
+/* eslint-disable no-console */
+
+import { parse } from '@astrojs/compiler';
+
+const src = `<>Hello>World`;
+
+async function run() {
+ const result = await parse(src);
+
+ const [first, second] = result.ast.children;
+ if (first.type !== 'fragment') {
+ throw new Error(`Expected first child node to be of type "fragment"`);
+ }
+ if (first.name !== '') {
+ throw new Error(`Expected first child node to have name of ""`);
+ }
+ if (second.type !== 'fragment') {
+ throw new Error(`Expected second child node to be of type "fragment"`);
+ }
+ if (second.name !== 'Fragment') {
+ throw new Error(`Expected second child node to have name of "Fragment"`);
+ }
+}
+
+await run();
diff --git a/lib/compiler/test/test.mjs b/lib/compiler/test/test.mjs
index 7bc0e751f..9e1f0d4d1 100644
--- a/lib/compiler/test/test.mjs
+++ b/lib/compiler/test/test.mjs
@@ -9,5 +9,6 @@ import './script-fragment.test.mjs';
import './top-level-expression.test.mjs';
import './stress.test.mjs';
import './parse.test.mjs';
+import './parse-fragment.test.mjs';
import './parse-ii.test.mjs';
import './render-head.test.mjs';