Skip to content

Commit

Permalink
feat: implement new fragment behavior to support head and body (#…
Browse files Browse the repository at this point in the history
  • Loading branch information
natemoo-re authored Nov 29, 2021
1 parent a3133f1 commit 7e1aded
Show file tree
Hide file tree
Showing 8 changed files with 59 additions and 21 deletions.
5 changes: 5 additions & 0 deletions .changeset/small-beds-perform.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@astrojs/compiler': minor
---

Change behavior of `as: "fragment"` option to support arbitrary `head` and `body` tags
4 changes: 2 additions & 2 deletions cmd/astro-wasm/astro-wasm.go
Original file line number Diff line number Diff line change
Expand Up @@ -126,8 +126,8 @@ func Transform() interface{} {
} else if transformOptions.As == "fragment" {
nodes, err := astro.ParseFragment(strings.NewReader(source), &astro.Node{
Type: astro.ElementNode,
Data: atom.Body.String(),
DataAtom: atom.Body,
Data: atom.Template.String(),
DataAtom: atom.Template,
})
if err != nil {
fmt.Println(err)
Expand Down
24 changes: 21 additions & 3 deletions internal/parser.go
Original file line number Diff line number Diff line change
Expand Up @@ -1082,17 +1082,25 @@ func inBodyIM(p *parser) bool {
}
switch p.tok.DataAtom {
case a.Html:
if p.inTemplateFragmentContext() {
p.addElement()
return true
}
if p.oe.contains(a.Template) {
return true
}
copyAttributes(p.oe[0], p.tok)
case a.Base, a.Basefont, a.Bgsound, a.Link, a.Meta, a.Noframes, a.Script, a.Style, a.Template, a.Title:
return inHeadIM(p)
case a.Body:
if p.inTemplateFragmentContext() {
p.addElement()
return true
}
if p.oe.contains(a.Template) {
return true
}
if len(p.oe) >= 1 {
if len(p.oe) > 1 {
body := p.oe[1]
if body.Type == ElementNode && body.DataAtom == a.Body {
p.framesetOK = false
Expand Down Expand Up @@ -1318,7 +1326,13 @@ func inBodyIM(p *parser) bool {
p.acknowledgeSelfClosingTag()
}
return true
case a.Caption, a.Col, a.Colgroup, a.Frame, a.Head, a.Tbody, a.Td, a.Tfoot, a.Th, a.Thead, a.Tr:
case a.Head:
if p.inTemplateFragmentContext() {
p.addElement()
p.im = inHeadIM
return true
}
case a.Caption, a.Col, a.Colgroup, a.Frame, a.Tbody, a.Td, a.Tfoot, a.Th, a.Thead, a.Tr:
// Ignore the token.
default:
p.reconstructActiveFormattingElements()
Expand Down Expand Up @@ -1446,6 +1460,10 @@ func inBodyIM(p *parser) bool {
return true
}

func (p *parser) inTemplateFragmentContext() bool {
return len(p.oe) == 1 && p.context != nil && p.context.DataAtom == a.Template
}

func (p *parser) inBodyEndTagFormatting(tagAtom a.Atom, tagName string) {
// This is the "adoption agency" algorithm, described at
// https://html.spec.whatwg.org/multipage/syntax.html#adoptionAgency
Expand Down Expand Up @@ -2157,7 +2175,7 @@ func inSelectInTableIM(p *parser) bool {
// Section 12.2.6.4.18.
func inTemplateIM(p *parser) bool {
switch p.tok.Type {
case TextToken, CommentToken, DoctypeToken:
case TextToken, CommentToken, DoctypeToken, FrontmatterFenceToken:
return inBodyIM(p)
case StartTagToken:
switch p.tok.DataAtom {
Expand Down
13 changes: 0 additions & 13 deletions internal/token.go
Original file line number Diff line number Diff line change
Expand Up @@ -1849,16 +1849,3 @@ func NewTokenizerFragment(r io.Reader, contextTag string) *Tokenizer {
}
return z
}

// NewTokenizer returns a new HTML Tokenizer for the given Reader.
// The input is assumed to be UTF-8 encoded.
func NewAttributeTokenizer(r io.Reader) *Tokenizer {
buf := new(bytes.Buffer)
buf.ReadFrom(r)
z := &Tokenizer{
r: r,
buf: buf.Bytes(),
fm: FrontmatterClosed,
}
return z
}
2 changes: 1 addition & 1 deletion internal/transform/transform.go
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ func Transform(doc *tycho.Node, opts TransformOptions) *tycho.Node {
// Sometimes files have leading <script hoist> or <style>...
// Since we can't detect a "component-only" file until after `parse`, we need to handle
// them here. The component will be hoisted to the root of the document, `html` and `head` will be removed.
if opts.As != "Fragment" {
if opts.As != "fragment" {
var onlyComponent *tycho.Node
var rootNode *tycho.Node
walk(doc, func(n *tycho.Node) {
Expand Down
29 changes: 29 additions & 0 deletions lib/compiler/test/body-fragment.test.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
/* eslint-disable no-console */

import { transform } from '@astrojs/compiler';

async function run() {
const result = await transform(
`---
import ThemeToggleButton from './ThemeToggleButton.tsx';
---
<title>Uhhh</title>
<body><div>Hello!</div></body>`,
{
sourcemap: true,
as: 'fragment',
site: undefined,
sourcefile: 'MoreMenu.astro',
sourcemap: 'both',
internalURL: 'astro/internal',
}
);

if (!result.code.includes('<body><div>Hello!</div></body>')) {
throw new Error('Expected output to contain <body><div>Hello!</div></body>');
}
}

await run();
2 changes: 0 additions & 2 deletions lib/compiler/test/component-only.test.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -52,8 +52,6 @@ import Layout from '../layouts/content.astro';
}
);

console.log(result.code);

if (result.code.includes('html')) {
throw new Error('Result did not remove <html>');
}
Expand Down
1 change: 1 addition & 0 deletions lib/compiler/test/test.mjs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import './basic.test.mjs';
import './body-fragment.test.mjs';
import './component-only.test.mjs';
import './empty-style.test.mjs';
import './output.test.mjs';

0 comments on commit 7e1aded

Please sign in to comment.