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 = `<>HelloWorld`; + +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';