Correctly serialize falsey values at top-level of components (#834)

* Correctly serialize falsey values at top-level of components

* Adding a changeset
This commit is contained in:
Matthew Phillips 2021-07-23 13:00:49 -04:00 committed by GitHub
parent 0c729f248d
commit 164489fbb2
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
5 changed files with 61 additions and 16 deletions

View file

@ -0,0 +1,5 @@
---
'astro': patch
---
Fix for `false` being rendered in conditionals

View file

@ -5,6 +5,23 @@ export type HTag = string | AstroComponent;
const voidTags = new Set(['area', 'base', 'br', 'col', 'command', 'embed', 'hr', 'img', 'input', 'keygen', 'link', 'meta', 'param', 'source', 'track', 'wbr']);
function* _children(children: Array<HChild>) {
for (let child of children) {
// Special: If a child is a function, call it automatically.
// This lets you do {() => ...} without the extra boilerplate
// of wrapping it in a function and calling it.
if (typeof child === 'function') {
yield child();
} else if (typeof child === 'string') {
yield child;
} else if (!child && child !== 0) {
// do nothing, safe to ignore falsey values.
} else {
yield child;
}
}
}
/** Generator for primary h() function */
function* _h(tag: string, attrs: HProps, children: Array<HChild>) {
if (tag.toLowerCase() === '!doctype') {
@ -32,20 +49,7 @@ function* _h(tag: string, attrs: HProps, children: Array<HChild>) {
return;
}
for (let child of children) {
// Special: If a child is a function, call it automatically.
// This lets you do {() => ...} without the extra boilerplate
// of wrapping it in a function and calling it.
if (typeof child === 'function') {
yield child();
} else if (typeof child === 'string') {
yield child;
} else if (!child && child !== 0) {
// do nothing, safe to ignore falsey values.
} else {
yield child;
}
}
yield * _children(children);
yield `</${tag}>`;
}
@ -62,6 +66,6 @@ export async function h(tag: HTag, attrs: HProps, ...pChildren: Array<Promise<HC
}
/** Fragment helper, similar to React.Fragment */
export function Fragment(_: HProps, ...children: Array<string>) {
return children.join('');
export function Fragment(_: HProps, ...children: Array<HChild>) {
return Array.from(_children(children)).join('');
}

View file

@ -80,6 +80,12 @@ Expressions('Does not render falsy values using &&', async ({ runtime }) => {
assert.equal($('#false').length, 0, `Expected {false && <span id="false" />} not to render`);
assert.equal($('#null').length, 0, `Expected {null && <span id="null" />} not to render`);
assert.equal($('#undefined').length, 0, `Expected {undefined && <span id="undefined" />} not to render`);
// Inside of a component
assert.equal($('#frag-true').length, 1, `Expected {true && <span id="true" />} to render`);
assert.equal($('#frag-false').length, 0, `Expected {false && <span id="false" />} not to render`);
assert.equal($('#frag-null').length, 0, `Expected {null && <span id="null" />} not to render`);
assert.equal($('#frag-undefined').length, 0, `Expected {undefined && <span id="undefined" />} not to render`);
});
Expressions.run();

View file

@ -0,0 +1,23 @@
---
const { items, emptyItems } = Astro.props;
const internal = [];
---
<!-- False -->
{false && (
<span id="frag-false" />
)}
<!-- Null -->
{null && (
<span id="frag-null" />
)}
<!-- True -->
{true && (
<span id="frag-true" />
)}
<!-- Undefined -->
{false && (<span id="frag-undefined" />)}

View file

@ -1,3 +1,6 @@
---
import Falsey from '../components/falsy.astro';
---
<html lang="en">
<head>
<title>My site</title>
@ -8,5 +11,9 @@
{undefined && <span id="undefined" />}
{true && <span id="true" />}
<span id="zero">{0 && "VALUE"}</span>
<section id="fragment-container">
<Falsey />
</section>
</body>
</html>