From 69706847f753dd22a64d5c7803823095a8aa9f89 Mon Sep 17 00:00:00 2001 From: Moustapha HappyDev Date: Fri, 1 Mar 2024 22:52:03 +0000 Subject: [PATCH 01/13] chore: `only` helper for json test --- internal/printer/printer_test.go | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/internal/printer/printer_test.go b/internal/printer/printer_test.go index 4a43ce71e..2c02c2278 100644 --- a/internal/printer/printer_test.go +++ b/internal/printer/printer_test.go @@ -91,6 +91,7 @@ type jsonTestcase struct { name string source string want []ASTNode + only bool } func TestPrinter(t *testing.T) { @@ -3770,6 +3771,14 @@ const c = '\'' }, } + for _, tt := range tests { + if tt.only { + tests = make([]jsonTestcase, 0) + tests = append(tests, tt) + break + } + } + for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { // transform output from source From 045f4c0017c18273cb516c5e36ec5381d4aa62be Mon Sep 17 00:00:00 2001 From: Moustapha HappyDev Date: Fri, 1 Mar 2024 22:52:35 +0000 Subject: [PATCH 02/13] fix: remove divergence from html spec --- internal/parser.go | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/internal/parser.go b/internal/parser.go index 18275683a..53cf1dab3 100644 --- a/internal/parser.go +++ b/internal/parser.go @@ -902,8 +902,10 @@ func inHeadIM(p *parser) bool { p.im = afterHeadIM return true case a.Body, a.Html, a.Br: - p.parseImpliedToken(EndTagToken, a.Head, a.Head.String()) + p.oe.pop() p.addLoc() + p.originalIM = nil + p.im = afterHeadIM return false case a.Template: if !p.oe.contains(a.Template) { From 1d077f8332c4ce3764522b3567b49c154c4c491d Mon Sep 17 00:00:00 2001 From: Moustapha HappyDev Date: Fri, 1 Mar 2024 22:53:24 +0000 Subject: [PATCH 03/13] test: add test for #712 --- internal/printer/printer_test.go | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/internal/printer/printer_test.go b/internal/printer/printer_test.go index 2c02c2278..191268949 100644 --- a/internal/printer/printer_test.go +++ b/internal/printer/printer_test.go @@ -3744,6 +3744,11 @@ const c = '\'' source: `

Hello world!

`, want: []ASTNode{{Type: "element", Name: "html", Children: []ASTNode{{Type: "element", Name: "body", Children: []ASTNode{{Type: "element", Name: "h1", Children: []ASTNode{{Type: "text", Value: "Hello world!"}}}}}}}, {Type: "element", Name: "style"}}, }, + { + name: "style after html with component in head", + source: ``, + want: []ASTNode{{Type: "element", Name: "html", Attributes: []ASTNode{{Type: "attribute", Kind: "quoted", Name: "lang", Value: "en", Raw: "\"en\""}}, Children: []ASTNode{{Type: "element", Name: "head", Children: []ASTNode{{Type: "component", Name: "BaseHead"}}}}}, {Type: "element", Name: "style", Children: []ASTNode{{Type: "text", Value: "@use \"../styles/global.scss\";"}}}}, + }, { name: "style in html", source: `

Hello world!

`, From 69e4d4ae5a8a64fc7fbc2504c2f0e64cf8a3f5a9 Mon Sep 17 00:00:00 2001 From: Moustapha HappyDev Date: Sat, 2 Mar 2024 00:44:13 +0000 Subject: [PATCH 04/13] `addLoc` before popping the stack of oe --- internal/parser.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/internal/parser.go b/internal/parser.go index 53cf1dab3..ecc8c6d98 100644 --- a/internal/parser.go +++ b/internal/parser.go @@ -902,8 +902,8 @@ func inHeadIM(p *parser) bool { p.im = afterHeadIM return true case a.Body, a.Html, a.Br: - p.oe.pop() p.addLoc() + p.oe.pop() p.originalIM = nil p.im = afterHeadIM return false From 851a1282e313f31b108edf125d256fc9521f30ec Mon Sep 17 00:00:00 2001 From: Moustapha HappyDev Date: Tue, 5 Mar 2024 19:38:04 +0000 Subject: [PATCH 05/13] test: add test --- internal/printer/printer_test.go | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/internal/printer/printer_test.go b/internal/printer/printer_test.go index 191268949..8a1e8dcfc 100644 --- a/internal/printer/printer_test.go +++ b/internal/printer/printer_test.go @@ -3749,6 +3749,11 @@ const c = '\'' source: ``, want: []ASTNode{{Type: "element", Name: "html", Attributes: []ASTNode{{Type: "attribute", Kind: "quoted", Name: "lang", Value: "en", Raw: "\"en\""}}, Children: []ASTNode{{Type: "element", Name: "head", Children: []ASTNode{{Type: "component", Name: "BaseHead"}}}}}, {Type: "element", Name: "style", Children: []ASTNode{{Type: "text", Value: "@use \"../styles/global.scss\";"}}}}, }, + { + name: "style after html with component in head and body", + source: `
`, + want: []ASTNode{{Type: "element", Name: "html", Attributes: []ASTNode{{Type: "attribute", Kind: "quoted", Name: "lang", Value: "en", Raw: "\"en\""}}, Children: []ASTNode{{Type: "element", Name: "head", Children: []ASTNode{{Type: "component", Name: "BaseHead"}}}, {Type: "element", Name: "body", Children: []ASTNode{{Type: "component", Name: "Header"}}}}}, {Type: "element", Name: "style", Children: []ASTNode{{Type: "text", Value: "@use \"../styles/global.scss\";"}}}}, + }, { name: "style in html", source: `

Hello world!

`, From 261d7d3c77333f0679e363718a1793cca6ebb27c Mon Sep 17 00:00:00 2001 From: Moustapha HappyDev Date: Wed, 6 Mar 2024 14:52:25 +0000 Subject: [PATCH 06/13] literal parsing after `body` and `html` --- internal/parser.go | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/internal/parser.go b/internal/parser.go index ecc8c6d98..d33aa8405 100644 --- a/internal/parser.go +++ b/internal/parser.go @@ -1439,12 +1439,18 @@ func inBodyIM(p *parser) bool { if p.elementInScope(defaultScope, a.Body) { p.im = afterBodyIM } + if p.literal { + p.oe.pop() + } case a.Html: p.addLoc() if p.elementInScope(defaultScope, a.Body) { p.parseImpliedToken(EndTagToken, a.Body, a.Body.String()) return false } + if p.literal { + p.oe.pop() + } return true case a.Address, a.Article, a.Aside, a.Blockquote, a.Button, a.Center, a.Details, a.Dialog, a.Dir, a.Div, a.Dl, a.Fieldset, a.Figcaption, a.Figure, a.Footer, a.Header, a.Hgroup, a.Listing, a.Main, a.Menu, a.Nav, a.Ol, a.Pre, a.Section, a.Summary, a.Ul: p.addLoc() @@ -2702,9 +2708,10 @@ func inLiteralIM(p *parser) bool { p.addLoc() p.oe.pop() p.acknowledgeSelfClosingTag() + } else { + // always continue `inLiteralIM` + return true } - // always continue `inLiteralIM` - return true case StartExpressionToken: p.addExpression() // always continue `inLiteralIM` From b951379d32bf7ce5409fa8051813c69b0dd46331 Mon Sep 17 00:00:00 2001 From: Moustapha HappyDev Date: Wed, 6 Mar 2024 15:00:00 +0000 Subject: [PATCH 07/13] test: add test for style tag after body --- internal/printer/printer_test.go | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/internal/printer/printer_test.go b/internal/printer/printer_test.go index 8a1e8dcfc..9d783a8c1 100644 --- a/internal/printer/printer_test.go +++ b/internal/printer/printer_test.go @@ -3754,6 +3754,11 @@ const c = '\'' source: `
`, want: []ASTNode{{Type: "element", Name: "html", Attributes: []ASTNode{{Type: "attribute", Kind: "quoted", Name: "lang", Value: "en", Raw: "\"en\""}}, Children: []ASTNode{{Type: "element", Name: "head", Children: []ASTNode{{Type: "component", Name: "BaseHead"}}}, {Type: "element", Name: "body", Children: []ASTNode{{Type: "component", Name: "Header"}}}}}, {Type: "element", Name: "style", Children: []ASTNode{{Type: "text", Value: "@use \"../styles/global.scss\";"}}}}, }, + { + name: "style after body with component in head and body", + source: `
`, + want: []ASTNode{{Type: "element", Name: "html", Attributes: []ASTNode{{Type: "attribute", Kind: "quoted", Name: "lang", Value: "en", Raw: "\"en\""}}, Children: []ASTNode{{Type: "element", Name: "head", Children: []ASTNode{{Type: "component", Name: "BaseHead"}}}, {Type: "element", Name: "body", Children: []ASTNode{{Type: "component", Name: "Header"}}}, {Type: "element", Name: "style", Children: []ASTNode{{Type: "text", Value: "@use \"../styles/global.scss\";"}}}}}}, + }, { name: "style in html", source: `

Hello world!

`, From 4f8f51a015e628cf81a6eaaa79172b524c108fcf Mon Sep 17 00:00:00 2001 From: Moustapha HappyDev Date: Wed, 6 Mar 2024 15:22:17 +0000 Subject: [PATCH 08/13] test: add tsx tests --- .../compiler/test/tsx/literal-style-tag.ts | 68 +++++++++++++++++++ 1 file changed, 68 insertions(+) create mode 100644 packages/compiler/test/tsx/literal-style-tag.ts diff --git a/packages/compiler/test/tsx/literal-style-tag.ts b/packages/compiler/test/tsx/literal-style-tag.ts new file mode 100644 index 000000000..69b878d39 --- /dev/null +++ b/packages/compiler/test/tsx/literal-style-tag.ts @@ -0,0 +1,68 @@ +import { convertToTSX } from '@astrojs/compiler'; +import { test } from 'uvu'; +import * as assert from 'uvu/assert'; +import { TSXPrefix } from '../utils'; + +test('preserve style tag position I', async () => { + const input = ` + +

Hello world!

+ + +`; + const output = `${TSXPrefix} + + +

Hello world!

+ + + +
+export default function __AstroComponent_(_props: Record): any {}\n`; + const { code } = await convertToTSX(input, { sourcemap: 'external' }); + assert.snapshot(code, output, `expected code to match snapshot`); +}); + +test('preserve style tag position II', async () => { + const input = ` + + + + +`; + const output = `${TSXPrefix} + + + + + + + +export default function __AstroComponent_(_props: Record): any {}\n`; + const { code } = await convertToTSX(input, { sourcemap: 'external' }); + assert.snapshot(code, output, `expected code to match snapshot`); +}); + +test('preserve style tag position III', async () => { + const input = `
+`; + const output = `${TSXPrefix} +
+ + +export default function __AstroComponent_(_props: Record): any {}\n`; + const { code } = await convertToTSX(input, { sourcemap: 'external' }); + assert.snapshot(code, output, `expected code to match snapshot`); +}); + +test('preserve style tag position IV', async () => { + const input = `
`; + const output = `${TSXPrefix} +
+ +export default function __AstroComponent_(_props: Record): any {}\n`; + const { code } = await convertToTSX(input, { sourcemap: 'external' }); + assert.snapshot(code, output, `expected code to match snapshot`); +}); + +test.run(); From 45195560ee6ec6aac2d793819970af047441f44e Mon Sep 17 00:00:00 2001 From: Moustapha HappyDev Date: Wed, 6 Mar 2024 15:36:22 +0000 Subject: [PATCH 09/13] test: add missing semicolon in test --- packages/compiler/test/tsx/literal-style-tag.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/compiler/test/tsx/literal-style-tag.ts b/packages/compiler/test/tsx/literal-style-tag.ts index 69b878d39..5490fe725 100644 --- a/packages/compiler/test/tsx/literal-style-tag.ts +++ b/packages/compiler/test/tsx/literal-style-tag.ts @@ -45,7 +45,7 @@ export default function __AstroComponent_(_props: Record): any {}\n test('preserve style tag position III', async () => { const input = `
-`; +`; const output = `${TSXPrefix}
From e7d39311a4a0a2ac739312ffe8f81246d9ff1685 Mon Sep 17 00:00:00 2001 From: Moustapha HappyDev Date: Wed, 6 Mar 2024 17:42:41 +0000 Subject: [PATCH 10/13] test: add more tests from duplicates --- internal/printer/printer_test.go | 5 +++ .../compiler/test/tsx/literal-style-tag.ts | 40 +++++++++---------- 2 files changed, 23 insertions(+), 22 deletions(-) diff --git a/internal/printer/printer_test.go b/internal/printer/printer_test.go index c19757fec..f4eb683a4 100644 --- a/internal/printer/printer_test.go +++ b/internal/printer/printer_test.go @@ -3752,6 +3752,11 @@ const c = '\'' source: `

Hello world!

`, want: []ASTNode{{Type: "element", Name: "html", Children: []ASTNode{{Type: "element", Name: "body", Children: []ASTNode{{Type: "element", Name: "h1", Children: []ASTNode{{Type: "text", Value: "Hello world!"}}}}}}}, {Type: "element", Name: "style"}}, }, + { + name: "style after empty html", + source: ``, + want: []ASTNode{{Type: "element", Name: "html"}, {Type: "element", Name: "style"}}, + }, { name: "style after html with component in head", source: ``, diff --git a/packages/compiler/test/tsx/literal-style-tag.ts b/packages/compiler/test/tsx/literal-style-tag.ts index 5490fe725..235be1e8f 100644 --- a/packages/compiler/test/tsx/literal-style-tag.ts +++ b/packages/compiler/test/tsx/literal-style-tag.ts @@ -4,18 +4,10 @@ import * as assert from 'uvu/assert'; import { TSXPrefix } from '../utils'; test('preserve style tag position I', async () => { - const input = ` - -

Hello world!

- - + const input = `

Hello world!

`; const output = `${TSXPrefix} - - -

Hello world!

- - +

Hello world!

export default function __AstroComponent_(_props: Record): any {}\n`; @@ -24,18 +16,22 @@ export default function __AstroComponent_(_props: Record): any {}\n }); test('preserve style tag position II', async () => { - const input = ` - - - - + const input = ` +`; + const output = `${TSXPrefix} + + + +export default function __AstroComponent_(_props: Record): any {}\n`; + const { code } = await convertToTSX(input, { sourcemap: 'external' }); + assert.snapshot(code, output, `expected code to match snapshot`); +}); + +test('preserve style tag position III', async () => { + const input = ` `; const output = `${TSXPrefix} - - - - - + export default function __AstroComponent_(_props: Record): any {}\n`; @@ -43,7 +39,7 @@ export default function __AstroComponent_(_props: Record): any {}\n assert.snapshot(code, output, `expected code to match snapshot`); }); -test('preserve style tag position III', async () => { +test('preserve style tag position IV', async () => { const input = `
`; const output = `${TSXPrefix} @@ -55,7 +51,7 @@ export default function __AstroComponent_(_props: Record): any {}\n assert.snapshot(code, output, `expected code to match snapshot`); }); -test('preserve style tag position IV', async () => { +test('preserve style tag position V', async () => { const input = `
`; const output = `${TSXPrefix}
From 551f81c2c980bd681cccac2ebc0be8267902e52d Mon Sep 17 00:00:00 2001 From: Princesseuh <3019731+Princesseuh@users.noreply.github.com> Date: Tue, 16 Jul 2024 11:22:29 +0200 Subject: [PATCH 11/13] nit: formatting --- .../compiler/test/tsx/literal-style-tag.ts | 40 +++++++++---------- 1 file changed, 20 insertions(+), 20 deletions(-) diff --git a/packages/compiler/test/tsx/literal-style-tag.ts b/packages/compiler/test/tsx/literal-style-tag.ts index 235be1e8f..0b2eb0323 100644 --- a/packages/compiler/test/tsx/literal-style-tag.ts +++ b/packages/compiler/test/tsx/literal-style-tag.ts @@ -4,61 +4,61 @@ import * as assert from 'uvu/assert'; import { TSXPrefix } from '../utils'; test('preserve style tag position I', async () => { - const input = `

Hello world!

+ const input = `

Hello world!

`; - const output = `${TSXPrefix} + const output = `${TSXPrefix}

Hello world!

export default function __AstroComponent_(_props: Record): any {}\n`; - const { code } = await convertToTSX(input, { sourcemap: 'external' }); - assert.snapshot(code, output, `expected code to match snapshot`); + const { code } = await convertToTSX(input, { sourcemap: 'external' }); + assert.snapshot(code, output, 'expected code to match snapshot'); }); test('preserve style tag position II', async () => { - const input = ` + const input = ` `; - const output = `${TSXPrefix} + const output = `${TSXPrefix} export default function __AstroComponent_(_props: Record): any {}\n`; - const { code } = await convertToTSX(input, { sourcemap: 'external' }); - assert.snapshot(code, output, `expected code to match snapshot`); + const { code } = await convertToTSX(input, { sourcemap: 'external' }); + assert.snapshot(code, output, 'expected code to match snapshot'); }); test('preserve style tag position III', async () => { - const input = ` + const input = ` `; - const output = `${TSXPrefix} + const output = `${TSXPrefix} export default function __AstroComponent_(_props: Record): any {}\n`; - const { code } = await convertToTSX(input, { sourcemap: 'external' }); - assert.snapshot(code, output, `expected code to match snapshot`); + const { code } = await convertToTSX(input, { sourcemap: 'external' }); + assert.snapshot(code, output, 'expected code to match snapshot'); }); test('preserve style tag position IV', async () => { - const input = `
+ const input = `
`; - const output = `${TSXPrefix} + const output = `${TSXPrefix}
export default function __AstroComponent_(_props: Record): any {}\n`; - const { code } = await convertToTSX(input, { sourcemap: 'external' }); - assert.snapshot(code, output, `expected code to match snapshot`); + const { code } = await convertToTSX(input, { sourcemap: 'external' }); + assert.snapshot(code, output, 'expected code to match snapshot'); }); test('preserve style tag position V', async () => { - const input = `
`; - const output = `${TSXPrefix} + const input = `
`; + const output = `${TSXPrefix}
export default function __AstroComponent_(_props: Record): any {}\n`; - const { code } = await convertToTSX(input, { sourcemap: 'external' }); - assert.snapshot(code, output, `expected code to match snapshot`); + const { code } = await convertToTSX(input, { sourcemap: 'external' }); + assert.snapshot(code, output, 'expected code to match snapshot'); }); test.run(); From 06f77851147f22e9446113d69b20ebe938418bd4 Mon Sep 17 00:00:00 2001 From: Princesseuh <3019731+Princesseuh@users.noreply.github.com> Date: Tue, 16 Jul 2024 11:24:01 +0200 Subject: [PATCH 12/13] chore: changeset --- .changeset/ninety-kings-tease.md | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 .changeset/ninety-kings-tease.md diff --git a/.changeset/ninety-kings-tease.md b/.changeset/ninety-kings-tease.md new file mode 100644 index 000000000..ba3995f0d --- /dev/null +++ b/.changeset/ninety-kings-tease.md @@ -0,0 +1,5 @@ +--- +"@astrojs/compiler": patch +--- + +Fixes style and script tags sometimes being forcefully put into the body / head tags in the AST From 858e178d245c57b46f415bbe8dfd546a3ab1b920 Mon Sep 17 00:00:00 2001 From: Princesseuh <3019731+Princesseuh@users.noreply.github.com> Date: Tue, 16 Jul 2024 11:38:50 +0200 Subject: [PATCH 13/13] test: add AST tests --- packages/compiler/test/parse/literal.ts | 70 +++++++++++++++++++ .../compiler/test/tsx/literal-style-tag.ts | 2 +- 2 files changed, 71 insertions(+), 1 deletion(-) create mode 100644 packages/compiler/test/parse/literal.ts diff --git a/packages/compiler/test/parse/literal.ts b/packages/compiler/test/parse/literal.ts new file mode 100644 index 000000000..6602e2307 --- /dev/null +++ b/packages/compiler/test/parse/literal.ts @@ -0,0 +1,70 @@ +import { parse } from '@astrojs/compiler'; +import { test } from 'uvu'; +import * as assert from 'uvu/assert'; +import type { ElementNode } from '../../types.js'; + +test('preserve style tag position I', async () => { + const input = `

Hello world!

+`; + const { ast } = await parse(input); + + const lastChildren = ast.children.at(-1) as ElementNode; + + assert.equal(lastChildren.type, 'element', 'Expected last child node to be of type "element"'); + assert.equal(lastChildren.name, 'style', 'Expected last child node to be of type "style"'); +}); + +test('preserve style tag position II', async () => { + const input = ` +`; + const { ast } = await parse(input); + + const lastChildren = ast.children.at(-1) as ElementNode; + + assert.equal(lastChildren.type, 'element', 'Expected last child node to be of type "element"'); + assert.equal(lastChildren.name, 'style', 'Expected last child node to be of type "style"'); +}); + +test('preserve style tag position III', async () => { + const input = ` +`; + const { ast } = await parse(input); + + const lastChildren = ast.children.at(-1) as ElementNode; + + assert.equal(lastChildren.type, 'element', 'Expected last child node to be of type "element"'); + assert.equal(lastChildren.name, 'style', 'Expected last child node to be of type "style"'); + assert.equal( + lastChildren.children[0].type, + 'text', + 'Expected last child node to be of type "text"' + ); +}); + +test('preserve style tag position IV', async () => { + const input = `
+`; + const { ast } = await parse(input); + + const lastChildren = ast.children.at(-1) as ElementNode; + + assert.equal(lastChildren.type, 'element', 'Expected last child node to be of type "element"'); + assert.equal(lastChildren.name, 'style', 'Expected last child node to be of type "style"'); + assert.equal( + lastChildren.children[0].type, + 'text', + 'Expected last child node to be of type "text"' + ); +}); + +test('preserve style tag position V', async () => { + const input = `
`; + const { ast } = await parse(input); + + const firstChild = ast.children.at(0) as ElementNode; + const lastChild = firstChild.children.at(-1) as ElementNode; + + assert.equal(lastChild.type, 'element', 'Expected last child node to be of type "element"'); + assert.equal(lastChild.name, 'style', 'Expected last child node to be of type "style"'); + assert.equal(lastChild.children[0].type, 'text', 'Expected last child node to be of type "text"'); +}); diff --git a/packages/compiler/test/tsx/literal-style-tag.ts b/packages/compiler/test/tsx/literal-style-tag.ts index 0b2eb0323..e945f053a 100644 --- a/packages/compiler/test/tsx/literal-style-tag.ts +++ b/packages/compiler/test/tsx/literal-style-tag.ts @@ -1,7 +1,7 @@ import { convertToTSX } from '@astrojs/compiler'; import { test } from 'uvu'; import * as assert from 'uvu/assert'; -import { TSXPrefix } from '../utils'; +import { TSXPrefix } from '../utils.js'; test('preserve style tag position I', async () => { const input = `

Hello world!