Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix(#598, #466): fix ternary expression edge cases #617

Merged
merged 1 commit into from
Nov 10, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .changeset/three-ligers-clap.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@astrojs/compiler': patch
---

Fix expression edge cases, improve literal parsing
97 changes: 47 additions & 50 deletions internal/parser.go
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,8 @@ type parser struct {
im insertionMode
// originalIM is the insertion mode to go back to after completing a text
// or inTableText insertion mode.
originalIM insertionMode
originalIM insertionMode
exitLiteralIM func() bool
// fosterParenting is whether new elements should be inserted according to
// the foster parenting rules (section 12.2.6.1).
fosterParenting bool
Expand Down Expand Up @@ -782,13 +783,12 @@ func inHeadIM(p *parser) bool {
case StartTagToken:
// Allow components in Head
if isComponent(p.tok.Data) || isFragment(p.tok.Data) {
p.addElement()
if p.hasSelfClosingToken {
p.addLoc()
p.oe.pop()
p.acknowledgeSelfClosingTag()
p.im = inLiteralIM
oe := len(p.oe)
p.exitLiteralIM = func() bool {
return len(p.oe) == oe
}
return true
return false
}
switch p.tok.DataAtom {
case a.Html:
Expand Down Expand Up @@ -2670,30 +2670,32 @@ func frontmatterIM(p *parser) bool {

// Handle content very literally
func inLiteralIM(p *parser) bool {
switch p.tok.Type {
case ErrorToken:
p.oe.pop()
case TextToken:
p.addText(p.tok.Data)
return true
case StartTagToken:
p.addElement()
if p.hasSelfClosingToken {
if !p.exitLiteralIM() {
switch p.tok.Type {
case ErrorToken:
p.oe.pop()
p.acknowledgeSelfClosingTag()
case TextToken:
p.addText(p.tok.Data)
return true
case StartTagToken:
p.addElement()
if p.hasSelfClosingToken {
p.oe.pop()
p.acknowledgeSelfClosingTag()
}
return true
case EndTagToken:
p.addLoc()
p.oe.pop()
return true
case StartExpressionToken:
p.addExpression()
return true
case EndExpressionToken:
p.addLoc()
p.oe.pop()
return true
}
return true
case EndTagToken:
p.addLoc()
p.oe.pop()
return true
case StartExpressionToken:
p.addExpression()
return true
case EndExpressionToken:
p.addLoc()
p.oe.pop()
return true
}
p.im = p.originalIM
p.originalIM = nil
Expand All @@ -2711,10 +2713,6 @@ func inExpressionIM(p *parser) bool {
case TextToken:
return textIM(p)
case StartTagToken:
if isComponent(p.tok.Data) {
return inBodyIM(p)
}
n := p.oe.top()
if p.isInsideHead() {
switch p.tok.DataAtom {
case a.Noframes, a.Style, a.Script, a.Title, a.Noscript, a.Base, a.Basefont, a.Bgsound, a.Link, a.Meta:
Expand All @@ -2725,23 +2723,8 @@ func inExpressionIM(p *parser) bool {
p.originalIM = origIm
return ret
default:
for {
if n.Expression {
p.oe.pop()
p.im = inHeadIM
p.parseImpliedToken(EndTagToken, a.Head, a.Head.String())
p.parseImpliedToken(StartTagToken, a.Body, a.Body.String())
n.Parent.RemoveChild(n)
p.addChild(n)
p.im = inExpressionIM
break
}

p.oe.pop()
n = p.oe.top()
}

p.originalIM = inBodyIM
p.im = inLiteralIM
p.exitLiteralIM = getExitLiteralFunc(p)
return false
}
} else {
Expand All @@ -2764,6 +2747,18 @@ func ignoreTheRemainingTokens(p *parser) bool {
return true
}

// Generate a function that will exit `inLiteralIM` when all expressions are closed
func getExitLiteralFunc(p *parser) func() bool {
return func() bool {
for _, n := range p.oe {
if n.Expression {
return false
}
}
return true
}
}

const whitespaceOrNUL = whitespace + "\x00"

// Section 12.2.6.5
Expand Down Expand Up @@ -3019,6 +3014,7 @@ func ParseWithOptions(r io.Reader, opts ...ParseOption) (*Node, error) {
framesetOK: true,
im: initialIM,
frontmatterState: FrontmatterInitial,
exitLiteralIM: func() bool { return false },
}

for _, f := range opts {
Expand Down Expand Up @@ -3054,6 +3050,7 @@ func ParseFragmentWithOptions(r io.Reader, context *Node, opts ...ParseOption) (
fragment: true,
context: context,
frontmatterState: FrontmatterInitial,
exitLiteralIM: func() bool { return false },
}
if context != nil && context.Namespace != "" {
p.tokenizer = NewTokenizer(r)
Expand Down
14 changes: 14 additions & 0 deletions internal/printer/printer_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -138,6 +138,20 @@ func TestPrinter(t *testing.T) {
code: `<html><head>${$$renderSlot($$result,$$slots["baseHeadExtension"],$$render` + BACKTICK + `<meta property="test2" content="test2">` + BACKTICK + `)}` + RENDER_HEAD_RESULT + `</head></html>`,
},
},
{
name: "ternary component",
source: `{special ? <ChildDiv><p>Special</p></ChildDiv> : <p>Not special</p>}`,
want: want{
code: `${special ? $$render` + BACKTICK + `${$$renderComponent($$result,'ChildDiv',ChildDiv,{},{"default": () => $$render` + BACKTICK + `${$$maybeRenderHead($$result)}<p>Special</p>` + BACKTICK + `,})}` + BACKTICK + ` : $$render` + BACKTICK + `<p>Not special</p>` + BACKTICK + `}`,
},
},
{
name: "ternary layout",
source: `{toggleError ? <BaseLayout><h1>SITE: {Astro.site}</h1></BaseLayout> : <><h1>SITE: {Astro.site}</h1></>}`,
want: want{
code: `${toggleError ? $$render` + BACKTICK + `${$$renderComponent($$result,'BaseLayout',BaseLayout,{},{"default": () => $$render` + BACKTICK + `${$$maybeRenderHead($$result)}<h1>SITE: ${Astro.site}</h1>` + BACKTICK + `,})}` + BACKTICK + ` : $$render` + BACKTICK + `${$$renderComponent($$result,'Fragment',Fragment,{},{"default": () => $$render` + BACKTICK + `<h1>SITE: ${Astro.site}</h1>` + BACKTICK + `,})}` + BACKTICK + `}`,
},
},
{
name: "orphan slot",
source: `<slot />`,
Expand Down