Skip to content

fix(website-listing): Avoid type error if item.description is not a string #12548

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 1 commit into
base: main
Choose a base branch
from

Conversation

gadenbuie
Copy link
Collaborator

@gadenbuie gadenbuie commented Apr 15, 2025

Description

When using custom listings with a custom template, e.g.

---
listing:
  id: my-listing
  template: my-listing.ejs
  contents: my-listing.yml
---

template authors control how the contents are processed and may make odd decisions like including an item like this:

- title: Superzip explorer
  description: null
  href: https://gallery.shinyapps.io/superzip/
  code: https://github.com/posit-dev/py-shinywidgets/tree/main/examples/superzip/
  thumbnail: /gallery/superzip.png

Their template code is handling the rendering, so they may process item.description conditionally and the null is an okay value.

However, Quarto's internal isPlaceHolder(item.description) calls .match() on the description, which may not be a string, causing a type error.

TypeError: Cannot read properties of null (reading 'match')

This PR fixes this by checking not just that item.description is defined but also that it's a string before calling isPlaceholder().

(Sidenote: I'm not sure why this code path is triggered in the presence of a custom listing template, but I'm confident this would avoid the type error I encountered.)

Checklist

I have (if applicable):

  • filed a contributor agreement.
  • referenced the GitHub issue this PR closes
  • updated the appropriate changelog in the PR
  • ensured the present test suite passes
  • added new tests
  • created a separate documentation PR in Quarto's website repo and linked it to this PR

@gadenbuie gadenbuie marked this pull request as ready for review April 15, 2025 16:18
@cscheid
Copy link
Collaborator

cscheid commented Apr 15, 2025

Thanks for the PR, but I think this isn't a good idea. We don't to allow description: null without being pretty careful with the rest of the codebase. For example, we use description in Typescript:

https://github.com/quarto-dev/quarto-cli/blob/main/src/project/types/website/listing/website-listing-feed.ts#L123

I'm sure there are other places.

I concede that we should be documenting these and validating the YAML in listings. But I think description ought to always be a string.

@gadenbuie
Copy link
Collaborator Author

We don't to allow description: null without being pretty careful with the rest of the codebase.

For a custom listing with a custom template is there any guarantee that you'll have any fields? I guess if so it'd be good to document the required fields.

Anyway, this change isn't really arguing that you should allow description to be something else; more I'm suggesting that this is a place where the undefined guard and Typescript's type checks don't fully cover the possible inputs. Checking item.description !== undefined just opens the door to a runtime error when isPlaceholder() makes the incorrect assumption that item.description has to be a string.

The error is super opaque, btw

ERROR: TypeError: Cannot read properties of null (reading 'match')

Stack trace:
    at isPlaceHolder (file:///Users/garrick/.local/share/qvm/versions/v1.7.23/bin/quarto.js:98176:17)
    at file:///Users/garrick/.local/share/qvm/versions/v1.7.23/bin/quarto.js:98846:48
    at Array.map (<anonymous>)
    at templateMarkdownHandler (file:///Users/garrick/.local/share/qvm/versions/v1.7.23/bin/quarto.js:98807:33)
    at markdownHandler (file:///Users/garrick/.local/share/qvm/versions/v1.7.23/bin/quarto.js:99832:24)
    at file:///Users/garrick/.local/share/qvm/versions/v1.7.23/bin/quarto.js:99734:31
    at Array.forEach (<anonymous>)
    at listingHtmlDependencies (file:///Users/garrick/.local/share/qvm/versions/v1.7.23/bin/quarto.js:99733:24)
    at eventLoopTick (ext:core/01_core.js:175:7)
    at async Object.formatExtras (file:///Users/garrick/.local/share/qvm/versions/v1.7.23/bin/quarto.js:100263:41)

It'd be better to validate item.description earlier or to have isPlaceholder() throw a more informative error, etc. But these two lines are equivalent in intent and behavior, except the second doesn't throw a runtime error:

if (item.description !== undefined && !isPlaceHolder(item.description)) {
if (typeof item.description === "string" && !isPlaceHolder(item.description)) {

@cscheid
Copy link
Collaborator

cscheid commented Apr 15, 2025

I agree that we should be validating! But the validation should really be "item.description must be string", instead of "item.description might be null".

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.

2 participants