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

Set transform to literal mode #939

Draft
wants to merge 25 commits into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
25 commits
Select commit Hold shift + click to select a range
9c918ad
fix(#935): set transform to literal mode
natemoo-re Jan 11, 2024
cff221c
fix: whitespace handling
natemoo-re Jan 11, 2024
eaa580a
chore: remove only
natemoo-re Jan 11, 2024
4abbf3c
chore: fix test typos
natemoo-re Jan 11, 2024
2b06c85
chore: add regression test for tokenizer
natemoo-re Jan 11, 2024
838be7e
Merge branch 'main' of https://github.com/withastro/compiler into fix…
MoustaphaDev Feb 29, 2024
58888df
test: fix incorrect tests
MoustaphaDev Feb 29, 2024
1c00621
test: add test for #958
MoustaphaDev Feb 29, 2024
b827dbb
refactor `addFrontmatter`
MoustaphaDev Mar 1, 2024
85fa4dc
test: add json test case
MoustaphaDev Mar 1, 2024
2b93f83
update transition scopes
MoustaphaDev Mar 1, 2024
34dddcd
test: fix incorrect tests
MoustaphaDev Mar 1, 2024
3d2f263
test: add test for #958
MoustaphaDev Mar 1, 2024
9b1def4
test: add json test case
MoustaphaDev Mar 1, 2024
51d7a6d
test: update transition scopes
MoustaphaDev Mar 1, 2024
e4178e2
refactor `addFrontmatter`
MoustaphaDev Mar 1, 2024
0784dd7
test: add test for #971
MoustaphaDev Mar 1, 2024
abf1c11
chore: proper name for added tests
MoustaphaDev Mar 1, 2024
54cf6d8
Merge branch 'fix/transform-literal-html-head-body-progress' into fix…
MoustaphaDev Mar 6, 2024
d437012
test: update test to reflect `addFrontmatter` refactor
MoustaphaDev Mar 6, 2024
968b6c4
test: remove code from bad merge commit
MoustaphaDev Mar 6, 2024
8bdf5d5
Merge branch 'main' into fix/transform-literal-html-head-body
MoustaphaDev Mar 6, 2024
c5a3f94
chore: update transition scope
MoustaphaDev Mar 6, 2024
5b3c8b7
Merge branch 'main' into fix/transform-literal-html-head-body
matthewp Mar 29, 2024
2974e55
Add nested head content test
matthewp Mar 29, 2024
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/shiny-plums-smile.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@astrojs/compiler': minor
---

Fixes an edge case that caused `html` and `body` tags with attributes to be ignored when they were wrapped in a component.
6 changes: 3 additions & 3 deletions cmd/astro-wasm/astro-wasm.go
Original file line number Diff line number Diff line change
Expand Up @@ -232,7 +232,7 @@ func Parse() any {
h := handler.NewHandler(source, parseOptions.Filename)

var doc *astro.Node
doc, err := astro.ParseWithOptions(strings.NewReader(source), astro.ParseOptionWithHandler(h), astro.ParseOptionEnableLiteral(true))
doc, err := astro.ParseWithOptions(strings.NewReader(source), astro.ParseOptionEnableLiteral(true), astro.ParseOptionWithHandler(h))
if err != nil {
h.AppendError(err)
}
Expand All @@ -256,7 +256,7 @@ func ConvertToTSX() any {
h := handler.NewHandler(source, transformOptions.Filename)

var doc *astro.Node
doc, err := astro.ParseWithOptions(strings.NewReader(source), astro.ParseOptionWithHandler(h), astro.ParseOptionEnableLiteral(true))
doc, err := astro.ParseWithOptions(strings.NewReader(source), astro.ParseOptionEnableLiteral(true), astro.ParseOptionWithHandler(h))
if err != nil {
h.AppendError(err)
}
Expand Down Expand Up @@ -307,7 +307,7 @@ func Transform() any {
}
}()

doc, err := astro.ParseWithOptions(strings.NewReader(source), astro.ParseOptionWithHandler(h))
doc, err := astro.ParseWithOptions(strings.NewReader(source), astro.ParseOptionEnableLiteral(true), astro.ParseOptionWithHandler(h))
if err != nil {
reject.Invoke(wasm_utils.ErrorToJSError(h, err))
return
Expand Down
19 changes: 4 additions & 15 deletions internal/parser.go
Original file line number Diff line number Diff line change
Expand Up @@ -354,7 +354,7 @@ func (p *parser) addText(text string) {
})
}

func (p *parser) addFrontmatter(empty bool) {
func (p *parser) addFrontmatter() {
if p.frontmatterState == FrontmatterInitial {
if p.doc.FirstChild != nil {
p.fm = &Node{
Expand All @@ -369,13 +369,8 @@ func (p *parser) addFrontmatter(empty bool) {
}
p.doc.AppendChild(p.fm)
}
if empty {
p.frontmatterState = FrontmatterClosed
p.fm.Attr = append(p.fm.Attr, Attribute{Key: ImplicitNodeMarker, Type: EmptyAttribute})
} else {
p.frontmatterState = FrontmatterOpen
p.oe = append(p.oe, p.fm)
}
p.frontmatterState = FrontmatterOpen
p.oe = append(p.oe, p.fm)
}
}

Expand Down Expand Up @@ -646,9 +641,6 @@ func initialIM(p *parser) bool {
p.im = beforeHTMLIM
return true
}
if p.frontmatterState == FrontmatterInitial {
p.addFrontmatter(true)
}
p.quirks = true
p.im = beforeHTMLIM
return false
Expand Down Expand Up @@ -1534,9 +1526,6 @@ func inBodyIM(p *parser) bool {
}
}
}
if p.frontmatterState == FrontmatterInitial {
p.addFrontmatter(true)
}
return true
}

Expand Down Expand Up @@ -2642,7 +2631,7 @@ func frontmatterIM(p *parser) bool {
switch p.tok.Type {
case FrontmatterFenceToken:
if p.frontmatterState == FrontmatterInitial {
p.addFrontmatter(false)
p.addFrontmatter()
return true
} else {
p.frontmatterState = FrontmatterClosed
Expand Down
113 changes: 96 additions & 17 deletions internal/printer/printer_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -259,7 +259,7 @@ func TestPrinter(t *testing.T) {
},
{
name: "slot with fallback",
source: `<body><slot><p>Hello world!</p></slot><body>`,
source: `<body><slot><p>Hello world!</p></slot></body>`,
want: want{
code: `${$$maybeRenderHead($$result)}<body>${$$renderSlot($$result,$$slots["default"],$$render` + BACKTICK + `<p>Hello world!</p>` + BACKTICK + `)}</body>`,
},
Expand Down Expand Up @@ -568,9 +568,9 @@ import type data from "test"
<h2>div+h2 ${dummyKey}</h2>
</div>
<p>
</p><h2>p+h2 ${dummyKey}</h2>
` + BACKTICK + `
);
<h2>p+h2 ${dummyKey}</h2>
</p>` + BACKTICK + `
);
})
}
</main>
Expand All @@ -588,8 +588,8 @@ import type data from "test"
want: want{
code: `<html lang="en">
${$$maybeRenderHead($$result)}<body>
${Object.keys(importedAuthors).map(author => $$render` + BACKTICK + `<p></p><div>hello</div>` + BACKTICK + `)}
${Object.keys(importedAuthors).map(author => $$render` + BACKTICK + `<p></p><div>${author}</div>` + BACKTICK + `)}
${Object.keys(importedAuthors).map(author => $$render` + BACKTICK + `<p><div>hello</div></p>` + BACKTICK + `)}
${Object.keys(importedAuthors).map(author => $$render` + BACKTICK + `<p><div>${author}</div></p>` + BACKTICK + `)}
</body>
</html>`,
},
Expand Down Expand Up @@ -1602,6 +1602,13 @@ const name = 'named';
code: "${cond && $$render`<meta charset=\"utf8\">`}${cond && $$render`<title>My title</title>`}",
},
},
{
name: "top-level component does not drop body attributes",
source: `<Base><body class="foobar"><slot /></body></Base>`,
want: want{
code: "${$$renderComponent($$result,'Base',Base,{},{\"default\": () => $$render`${$$maybeRenderHead($$result)}<body class=\"foobar\">${$$renderSlot($$result,$$slots[\"default\"])}</body>`,})}",
},
},
{
name: "custom elements",
source: `---
Expand Down Expand Up @@ -1661,7 +1668,7 @@ ${$$renderComponent($$result,'my-element','my-element',{"client:load":true,"clie
},
{
name: "Self-closing script in head works",
source: `<html><head><script is:inline /></head><html>`,
source: `<html><head><script is:inline /></head></html>`,
want: want{
code: `<html><head><script></script>` + RENDER_HEAD_RESULT + `</head></html>`,
},
Expand All @@ -1682,7 +1689,7 @@ ${$$renderComponent($$result,'my-element','my-element',{"client:load":true,"clie
},
{
name: "Self-closing components in head can have siblings",
source: `<html><head><BaseHead /><link href="test"></head><html>`,
source: `<html><head><BaseHead /><link href="test"></head></html>`,
want: want{
code: `<html><head>${$$renderComponent($$result,'BaseHead',BaseHead,{})}<link href="test">` + RENDER_HEAD_RESULT + `</head></html>`,
},
Expand Down Expand Up @@ -2082,7 +2089,7 @@ import { Container, Col, Row } from 'react-bootstrap';
name: "Preserve namespaces in expressions",
source: `<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"><rect xlink:href={` + BACKTICK + `#${iconId}` + BACKTICK + `}></svg>`,
want: want{
code: `${$$maybeRenderHead($$result)}<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"><rect ${$$addAttribute(` + BACKTICK + `#${iconId}` + BACKTICK + `, "xlink:href")}></rect></svg>`,
code: `${$$maybeRenderHead($$result)}<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"><rect${$$addAttribute(` + BACKTICK + `#${iconId}` + BACKTICK + `, "xlink:href")}></rect></svg>`,
},
},
{
Expand Down Expand Up @@ -2297,6 +2304,42 @@ const content = "lol";
</html>`,
},
},
{
// ensurethere are no duplicate elements matching the ones in the link below (`<a>` in this test)
// https://github.com/withastro/compiler/blob/a90d99ee8cc3ad92d1b39d73df1f7301011ee970/internal/parser.go#L1490
name: "<a> tag with expression in table ",
source: `<main>
<table>
<tr>
<td><a href={linkURL}>{linkURL}</a></td>
</tr>
</table>
</main>`,
want: want{
code: `${$$maybeRenderHead($$result)}<main>
<table>
<tr>
<td><a${$$addAttribute(linkURL, "href")}>${linkURL}</a></td>
</tr>
</table>
</main>`,
},
},
{
// makes sure that there are no duplicate elements matching the ones in the link below (`<a>` in this test)
// https://github.com/withastro/compiler/blob/a90d99ee8cc3ad92d1b39d73df1f7301011ee970/internal/parser.go#L1490
name: "<a> tag with expression in template",
source: `<template>
<a href="https://example.com">{text}</a>.
</template>
<p>This should not be a link</p>`,
want: want{
code: `<template>
${$$maybeRenderHead($$result)}<a href="https://example.com">${text}</a>.
</template>
<p>This should not be a link</p>`,
},
},
{
name: "complex table",
source: `<html lang="en">
Expand Down Expand Up @@ -3444,7 +3487,7 @@ const items = ["Dog", "Cat", "Platipus"];
filename: "/projects/app/src/pages/page.astro",
transitions: true,
want: want{
code: `${$$maybeRenderHead($$result)}<div${$$addAttribute($$renderTransition($$result, "daiq24ry", "", (one + '-' + 'two')), "data-astro-transition-scope")}></div>`,
code: `${$$maybeRenderHead($$result)}<div${$$addAttribute($$renderTransition($$result, "eh4gbiwl", "", (one + '-' + 'two')), "data-astro-transition-scope")}></div>`,
},
},
{
Expand All @@ -3453,7 +3496,7 @@ const items = ["Dog", "Cat", "Platipus"];
filename: "/projects/app/src/pages/page.astro",
transitions: true,
want: want{
code: `${$$maybeRenderHead($$result)}<div${$$addAttribute($$renderTransition($$result, "vvov4lyr", "", ` + BACKTICK + `${one}-two` + BACKTICK + `), "data-astro-transition-scope")}></div>`,
code: `${$$maybeRenderHead($$result)}<div${$$addAttribute($$renderTransition($$result, "2oo357hq", "", ` + BACKTICK + `${one}-two` + BACKTICK + `), "data-astro-transition-scope")}></div>`,
},
},
{
Expand All @@ -3462,7 +3505,7 @@ const items = ["Dog", "Cat", "Platipus"];
filename: "/projects/app/src/pages/page.astro",
transitions: true,
want: want{
code: `${$$maybeRenderHead($$result)}<div${$$addAttribute($$renderTransition($$result, "ih7yuffh", (slide({duration:15})), ""), "data-astro-transition-scope")}></div>`,
code: `${$$maybeRenderHead($$result)}<div${$$addAttribute($$renderTransition($$result, "x2dt3p4g", (slide({duration:15})), ""), "data-astro-transition-scope")}></div>`,
},
},
{
Expand All @@ -3471,31 +3514,31 @@ const items = ["Dog", "Cat", "Platipus"];
filename: "/projects/app/src/pages/page.astro",
transitions: true,
want: want{
code: `${$$renderComponent($$result,'Component',Component,{"class":"bar","data-astro-transition-scope":($$renderTransition($$result, "wkm5vset", "morph", ""))})}`,
code: `${$$renderComponent($$result,'Component',Component,{"class":"bar","data-astro-transition-scope":($$renderTransition($$result, "byigm4lx", "morph", ""))})}`,
},
},
{
name: "transition:persist converted to a data attribute",
source: `<div transition:persist></div>`,
transitions: true,
want: want{
code: `${$$maybeRenderHead($$result)}<div${$$addAttribute($$createTransitionScope($$result, "pflz5ime"), "data-astro-transition-persist")}></div>`,
code: `${$$maybeRenderHead($$result)}<div${$$addAttribute($$createTransitionScope($$result, "z45b6una"), "data-astro-transition-persist")}></div>`,
},
},
{
name: "transition:persist uses transition:name if defined",
source: `<div transition:persist transition:name="foo"></div>`,
transitions: true,
want: want{
code: `${$$maybeRenderHead($$result)}<div data-astro-transition-persist="foo"${$$addAttribute($$renderTransition($$result, "peuy4xf7", "", "foo"), "data-astro-transition-scope")}></div>`,
code: `${$$maybeRenderHead($$result)}<div data-astro-transition-persist="foo"${$$addAttribute($$renderTransition($$result, "nqtd2ecx", "", "foo"), "data-astro-transition-scope")}></div>`,
},
},
{
name: "transition:persist-props converted to a data attribute",
source: `<my-island transition:persist transition:persist-props="false"></my-island>`,
transitions: true,
want: want{
code: `${$$renderComponent($$result,'my-island','my-island',{"data-astro-transition-persist-props":"false","data-astro-transition-persist":($$createTransitionScope($$result, "otghnj5u"))})}`,
code: `${$$renderComponent($$result,'my-island','my-island',{"data-astro-transition-persist-props":"false","data-astro-transition-persist":($$createTransitionScope($$result, "rho3aldc"))})}`,
},
},
{
Expand All @@ -3505,6 +3548,31 @@ const items = ["Dog", "Cat", "Platipus"];
code: `${$$renderComponent($$result,'Component',Component,{})}${(void 0)}`,
},
},
{
name: "nested head content stays in the head",
source: `---
const meta = { title: 'My App' };
---

<html>
<head>
<meta charset="utf-8" />

{
meta && <title>{meta.title}</title>
}

<meta name="after">
</head>
<body>
<h1>My App</h1>
</body>
</html>`,
want: want{
frontmatter: []string{"", `const meta = { title: 'My App' };`},
code: `<html> <head> <meta charset="utf-8"> ${ meta && $$render` + BACKTICK + `<title>${meta.title}</title>` + BACKTICK + ` } <meta name="after"> ${$$renderHead($$result)}</head> <body> <h1>My App</h1> </body></html>`,
},
},
}

for _, tt := range tests {
Expand All @@ -3520,8 +3588,8 @@ const items = ["Dog", "Cat", "Platipus"];
// transform output from source
code := test_utils.Dedent(tt.source)

doc, err := astro.Parse(strings.NewReader(code))
h := handler.NewHandler(code, "<stdin>")
doc, err := astro.ParseWithOptions(strings.NewReader(code), astro.ParseOptionEnableLiteral(true), astro.ParseOptionWithHandler(h))

if err != nil {
t.Error(err)
Expand All @@ -3535,6 +3603,7 @@ const items = ["Dog", "Cat", "Platipus"];
RenderScript: tt.transformOptions.RenderScript,
}
transform.Transform(doc, transformOptions, h) // note: we want to test Transform in context here, but more advanced cases could be tested separately

result := PrintToJS(code, doc, 0, transform.TransformOptions{
Scope: "XXXX",
InternalURL: "http://localhost:3000/",
Expand Down Expand Up @@ -3746,6 +3815,11 @@ const c = '\''
source: `<style></style><html><body><h1>Hello world!</h1></body></html>`,
want: []ASTNode{{Type: "element", Name: "style"}, {Type: "element", Name: "html", Children: []ASTNode{{Type: "element", Name: "body", Children: []ASTNode{{Type: "element", Name: "h1", Children: []ASTNode{{Type: "text", Value: "Hello world!"}}}}}}}},
},
{
name: "empty style",
source: `<style define:vars={{ color: "Gainsboro" }}></style>`,
want: []ASTNode{{Type: "element", Name: "style", Attributes: []ASTNode{{Type: "attribute", Kind: "expression", Name: "define:vars", Value: "{ color: \"Gainsboro\" }"}}}},
},
{
name: "style after html",
source: `<html><body><h1>Hello world!</h1></body></html><style></style>`,
Expand Down Expand Up @@ -3776,6 +3850,11 @@ const c = '\''
source: `<main id=` + BACKTICK + `gotcha />`,
want: []ASTNode{{Type: "element", Name: "main", Attributes: []ASTNode{{Type: "attribute", Kind: "template-literal", Name: "id", Value: "gotcha", Raw: "`gotcha"}}}},
},
{
name: "top-level component does not drop body attributes",
source: `<Base><body class="foobar"><slot /></body></Base>`,
want: []ASTNode{{Type: "component", Name: "Base", Attributes: []ASTNode{}, Children: []ASTNode{{Type: "element", Name: "body", Attributes: []ASTNode{{Type: "attribute", Kind: "quoted", Name: "class", Value: "foobar", Raw: "\"foobar\""}}, Children: []ASTNode{{Type: "element", Name: "slot"}}}}}},
},
}

for _, tt := range tests {
Expand Down
5 changes: 5 additions & 0 deletions internal/token_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -500,6 +500,11 @@ func TestBasic(t *testing.T) {
`<div></div><MyAstroComponent` + "\n",
[]TokenType{StartTagToken, EndTagToken, TextToken},
},
{
"literal body",
`<body><slot><p>Hello world!</p></slot></body>`,
[]TokenType{StartTagToken, StartTagToken, StartTagToken, TextToken, EndTagToken, EndTagToken, EndTagToken},
},
}

runTokenTypeTest(t, Basic)
Expand Down
9 changes: 6 additions & 3 deletions internal/transform/transform.go
Original file line number Diff line number Diff line change
Expand Up @@ -242,7 +242,7 @@ func TrimTrailingSpace(doc *astro.Node) {
return
}

if doc.LastChild.Type == astro.TextNode {
if doc.LastChild.Type == astro.TextNode && len(doc.LastChild.Data) < len(strings.TrimRightFunc(doc.LastChild.Data, unicode.IsSpace)) {
doc.LastChild.Data = strings.TrimRightFunc(doc.LastChild.Data, unicode.IsSpace)
return
}
Expand All @@ -254,15 +254,18 @@ func TrimTrailingSpace(doc *astro.Node) {
n = n.LastChild
continue
} else {
n = nil
break
}
}

// Collapse all trailing text nodes
for n != nil && n.Type == astro.TextNode {
n.Data = strings.TrimRightFunc(n.Data, unicode.IsSpace)
n = n.PrevSibling
if len(n.Data) > 0 {
break
} else {
n = n.PrevSibling
}
}
}

Expand Down
Loading