Skip to content

fix: parse select correctly when it contains HTML element #1070

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

Open
wants to merge 3 commits into
base: main
Choose a base branch
from
Open
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/large-mangos-look.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@astrojs/compiler": patch
---

Fixes a bug where the compiler couldn't correctly parse `select` elements, preventing elements from being incorrectly nested
2 changes: 2 additions & 0 deletions internal/parser.go
Original file line number Diff line number Diff line change
Expand Up @@ -2319,6 +2319,8 @@ func inSelectIM(p *parser) bool {
p.resetInsertionMode()
case a.Template:
return inHeadIM(p)
default:
return inBodyIM(p)
}
case CommentToken:
p.addChild(&Node{
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@

[TestPrinter/select_with_2_options_containing_element - 1]
## Input

```
<select><option><span>Lemon</span></option><option><span>Lime</span></option></select>
```

## Output

```js
import {
Fragment,
render as $$render,
createAstro as $$createAstro,
createComponent as $$createComponent,
renderComponent as $$renderComponent,
renderHead as $$renderHead,
maybeRenderHead as $$maybeRenderHead,
unescapeHTML as $$unescapeHTML,
renderSlot as $$renderSlot,
mergeSlots as $$mergeSlots,
addAttribute as $$addAttribute,
spreadAttributes as $$spreadAttributes,
defineStyleVars as $$defineStyleVars,
defineScriptVars as $$defineScriptVars,
renderTransition as $$renderTransition,
createTransitionScope as $$createTransitionScope,
renderScript as $$renderScript,
createMetadata as $$createMetadata
} from "http://localhost:3000/";

export const $$metadata = $$createMetadata(import.meta.url, { modules: [], hydratedComponents: [], clientOnlyComponents: [], hydrationDirectives: new Set([]), hoisted: [] });

const $$Component = $$createComponent(($$result, $$props, $$slots) => {

return $$render`${$$maybeRenderHead($$result)}<select><option><span>Lemon</span></option><option><span>Lime</span></option></select>`;
}, undefined, undefined);
export default $$Component;
```
---
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@

[TestPrinter/select_with_2_options_containing_element_with_div_sibling - 1]
## Input

```
<select><option><span>Lemon</span></option><option><span>Lime</span></option></select><div>Orange</div>
```

## Output

```js
import {
Fragment,
render as $$render,
createAstro as $$createAstro,
createComponent as $$createComponent,
renderComponent as $$renderComponent,
renderHead as $$renderHead,
maybeRenderHead as $$maybeRenderHead,
unescapeHTML as $$unescapeHTML,
renderSlot as $$renderSlot,
mergeSlots as $$mergeSlots,
addAttribute as $$addAttribute,
spreadAttributes as $$spreadAttributes,
defineStyleVars as $$defineStyleVars,
defineScriptVars as $$defineScriptVars,
renderTransition as $$renderTransition,
createTransitionScope as $$createTransitionScope,
renderScript as $$renderScript,
createMetadata as $$createMetadata
} from "http://localhost:3000/";

export const $$metadata = $$createMetadata(import.meta.url, { modules: [], hydratedComponents: [], clientOnlyComponents: [], hydrationDirectives: new Set([]), hoisted: [] });

const $$Component = $$createComponent(($$result, $$props, $$slots) => {

return $$render`${$$maybeRenderHead($$result)}<select><option><span>Lemon</span></option><option><span>Lime</span></option></select><div>Orange</div>`;
}, undefined, undefined);
export default $$Component;
```
---
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@

[TestPrinter/select_with_option_containing_element - 1]
## Input

```
<select><option><span>Lemon</span></option></select>
```

## Output

```js
import {
Fragment,
render as $$render,
createAstro as $$createAstro,
createComponent as $$createComponent,
renderComponent as $$renderComponent,
renderHead as $$renderHead,
maybeRenderHead as $$maybeRenderHead,
unescapeHTML as $$unescapeHTML,
renderSlot as $$renderSlot,
mergeSlots as $$mergeSlots,
addAttribute as $$addAttribute,
spreadAttributes as $$spreadAttributes,
defineStyleVars as $$defineStyleVars,
defineScriptVars as $$defineScriptVars,
renderTransition as $$renderTransition,
createTransitionScope as $$createTransitionScope,
renderScript as $$renderScript,
createMetadata as $$createMetadata
} from "http://localhost:3000/";

export const $$metadata = $$createMetadata(import.meta.url, { modules: [], hydratedComponents: [], clientOnlyComponents: [], hydrationDirectives: new Set([]), hoisted: [] });

const $$Component = $$createComponent(($$result, $$props, $$slots) => {

return $$render`${$$maybeRenderHead($$result)}<select><option><span>Lemon</span></option></select>`;
}, undefined, undefined);
export default $$Component;
```
---
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@

[TestPrinter/select_with_option_containing_element_and_button_containing_selected_content - 1]
## Input

```
<select><button><selectedcontent></selectedcontent></button><option><span>Lemon</span></option></select>
```

## Output

```js
import {
Fragment,
render as $$render,
createAstro as $$createAstro,
createComponent as $$createComponent,
renderComponent as $$renderComponent,
renderHead as $$renderHead,
maybeRenderHead as $$maybeRenderHead,
unescapeHTML as $$unescapeHTML,
renderSlot as $$renderSlot,
mergeSlots as $$mergeSlots,
addAttribute as $$addAttribute,
spreadAttributes as $$spreadAttributes,
defineStyleVars as $$defineStyleVars,
defineScriptVars as $$defineScriptVars,
renderTransition as $$renderTransition,
createTransitionScope as $$createTransitionScope,
renderScript as $$renderScript,
createMetadata as $$createMetadata
} from "http://localhost:3000/";

export const $$metadata = $$createMetadata(import.meta.url, { modules: [], hydratedComponents: [], clientOnlyComponents: [], hydrationDirectives: new Set([]), hoisted: [] });

const $$Component = $$createComponent(($$result, $$props, $$slots) => {

return $$render`${$$maybeRenderHead($$result)}<select><button><selectedcontent></selectedcontent></button><option><span>Lemon</span></option></select>`;
}, undefined, undefined);
export default $$Component;
```
---
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@

[TestPrinter/select_with_option_containing_element_with_div_sibling - 1]
## Input

```
<select><option><span>Lemon</span></option></select><div>Orange</div>
```

## Output

```js
import {
Fragment,
render as $$render,
createAstro as $$createAstro,
createComponent as $$createComponent,
renderComponent as $$renderComponent,
renderHead as $$renderHead,
maybeRenderHead as $$maybeRenderHead,
unescapeHTML as $$unescapeHTML,
renderSlot as $$renderSlot,
mergeSlots as $$mergeSlots,
addAttribute as $$addAttribute,
spreadAttributes as $$spreadAttributes,
defineStyleVars as $$defineStyleVars,
defineScriptVars as $$defineScriptVars,
renderTransition as $$renderTransition,
createTransitionScope as $$createTransitionScope,
renderScript as $$renderScript,
createMetadata as $$createMetadata
} from "http://localhost:3000/";

export const $$metadata = $$createMetadata(import.meta.url, { modules: [], hydratedComponents: [], clientOnlyComponents: [], hydrationDirectives: new Set([]), hoisted: [] });

const $$Component = $$createComponent(($$result, $$props, $$slots) => {

return $$render`${$$maybeRenderHead($$result)}<select><option><span>Lemon</span></option></select><div>Orange</div>`;
}, undefined, undefined);
export default $$Component;
```
---
28 changes: 24 additions & 4 deletions internal/printer/printer_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -1234,19 +1234,19 @@ import { Container, Col, Row } from 'react-bootstrap';
source: `<body><Component><Fragment slot=named><div>Default</div><div>Named</div></Fragment></Component></body>`,
},
{
name: "Fragment with await",
name: "Fragment with await",
source: `<body><Fragment> { await Promise.resolve("Awaited") } </Fragment></body>`,
},
{
name: "Fragment shorthand with await",
name: "Fragment shorthand with await",
source: `<body><> { await Promise.resolve("Awaited") } </></body>`,
},
{
name: "Fragment wrapping link with awaited href",
name: "Fragment wrapping link with awaited href",
source: `<head><Fragment><link rel="preload" href={(await import('../fonts/some-font.woff2')).default} as="font" crossorigin /></Fragment></head>`,
},
{
name: "Component with await",
name: "Component with await",
source: `<body><Component> { await Promise.resolve("Awaited") } </Component></body>`,
},
{
Expand Down Expand Up @@ -2092,6 +2092,26 @@ const meta = { title: 'My App' };
name: "namespace is preserved when inside an expression",
source: `<svg>{<image />}</svg>`,
},
{
name: "select with option containing element",
source: `<select><option><span>Lemon</span></option></select>`,
},
{
name: "select with 2 options containing element",
source: `<select><option><span>Lemon</span></option><option><span>Lime</span></option></select>`,
},
{
name: "select with option containing element with div sibling",
source: `<select><option><span>Lemon</span></option></select><div>Orange</div>`,
},
{
name: "select with 2 options containing element with div sibling",
source: `<select><option><span>Lemon</span></option><option><span>Lime</span></option></select><div>Orange</div>`,
},
{
name: "select with option containing element and button containing selected content",
source: `<select><button><selectedcontent></selectedcontent></button><option><span>Lemon</span></option></select>`,
},
}
for _, tt := range tests {
if tt.only {
Expand Down