Skip to content

fix: render nullish meta children as empty instead of "undefined" - #71

Open
birkskyum wants to merge 1 commit into
solidjs:mainfrom
birkskyum:fix/nullish-meta-children
Open

fix: render nullish meta children as empty instead of "undefined"#71
birkskyum wants to merge 1 commit into
solidjs:mainfrom
birkskyum:fix/nullish-meta-children

Conversation

@birkskyum

Copy link
Copy Markdown
Member

Problem

During SSR, a nullish child is interpolated into the tag as the literal string "undefined":

<Title>{pill()?.title}</Title>

If pill() has not resolved when the shell flushes, the emitted HTML is:

<title>undefined</title>

escape early-returns for non-string values, so escape(undefined) gives back undefined, which the template literal then stringifies. The unescaped branch already guarded against this with children || ""; the escaped branch (used by Title) did not.

Fix

Guard once at the flattenChildren call so both branches behave the same:

-      const children = flattenChildren(tag.props.children);
+      const children = flattenChildren(tag.props.children) ?? "";

       if (tag.setting?.close) {
         return `<${tag.tag} data-sm="${tag.id}"${props}>${
           // @ts-expect-error
-          tag.setting?.escape ? escape(children) : children || ""
+          tag.setting?.escape ? escape(children) : children
         }</${tag.tag}>`;
       }

?? "" rather than || "" so that 0 and false still render, which the previous unescaped branch dropped.

Verification

pnpm test:unit (15 passed), pnpm build and pnpm test:types all clean.

Also verified end to end in a SolidStart 2.0 app, on a route whose <Title> reads an unresolved createAsync. Before the change the SSR HTML contained <title>undefined</title>; after it contains <title></title>.

Context: this is the mechanism behind solidjs/solid-start#1881, where several users saw undefined as their page title in crawlers. This change does not make async titles work under streaming SSR (that needs deferStream on the createAsync), but an empty title is a much better failure mode than a wrong one.

Notes

No test added: the repo's vitest setup is jsdom with generate: 'dom', so renderTags has no SSR coverage to extend, and the most recent change to this function (3cbd12c) was likewise source-only. Happy to add an SSR test config if you would like one.

`escape` returns non-string values untouched, so a nullish child was
interpolated into the tag as the literal string "undefined". Guard once
at the `flattenChildren` call so both the escaped and unescaped branches
behave the same.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant