#`routeLoader$` can't return unserializable data?

6 messages · Page 1 of 1 (latest)

subtle kettle
#

I'm trying to use Qwik city to build something like a blog using static files. I have a couple of reasons to not use .md/.mdx files in the routes directory and instead want to use a custom collection utility that loads Mdx files and generate dynamic routes from those files' paths which I do as follows:

export const onStaticGenerate: StaticGenerateHandler = async () => {
  const parts = await loadPartsCollection()

  return {
    params: parts.map(part => ({ slug: part.slug }))
  }
}

export const useRequestedPart = routeLoader$(async (requestEvent) => {
  return loadPart(requestEvent.params.slug)
})

export default component$(() => {
  const entry = useRequestedPart()
  // ...
})

I've then written loadPart to return both the Mdx file's metadata/frontmatter, as well as "render" its content (like in this Github issue: https://github.com/QwikDev/qwik/issues/5100#issuecomment-1707498231 using imported.default().children). This works fine in dev mode, however I want to deploy the blog entirely using SSG and that throws a wrench into the generator. Because I return the rendered markdown, which is inherently non-serializable, I'm getting Qwik error code 3 "Only primitive and object literals can be serialized" and I can't seem to find a way around this

livid widget
#

Hmm, I think there's something wrong. JSX nodes can get serialized, so maybe you're serializing something else as well

#

besides, for SSG you have everything rendered so there would be no need to serialize MDX would there?

subtle kettle
#

Yeah that's what confused me as well. I thought if at all then I'd be getting serialization issues in dev mode, not in SSG mode, but it is the other way around.

I tried the following to make sure that it is because of the markdown rendering. The only thing it returns is the JSX nodes and an empty object

export const useRequestedPart = routeLoader$(async (requestEvent) => {
  const modules = await import.meta.glob('/../content/**/*.mdx', { eager: true })

  // @ts-ignore
  const post = modules['../content/parts/panels/elec-pwr.mdx'].default().children
  return { frontmatter: {}, rendered: post }
})

This too works in dev mode, but not in SSG:

#
QWIK ERROR Code(3) https://github.com/QwikDev/qwik/blob/main/packages/qwik/src/core/error/error.ts#L11 qe {
  type: [Function: dr],
  props: {},
  immutableProps: null,
  children: undefined,
  flags: 3,
  key: null
} Error: Code(3) https://github.com/QwikDev/qwik/blob/main/packages/qwik/src/core/error/error.ts#L11
    at createAndLogError (file:///home/christopher/code/own/hogpit/qwik-city/node_modules/@builder.io/qwik/dist/core.prod.mjs:92:54)
    at logErrorAndStop (file:///home/christopher/code/own/hogpit/qwik-city/node_modules/@builder.io/qwik/dist/core.prod.mjs:60:57)
    at qError (file:///home/christopher/code/own/hogpit/qwik-city/node_modules/@builder.io/qwik/dist/core.prod.mjs:177:12)
    ...

Error during SSG
Error: Code(3) https://github.com/QwikDev/qwik/blob/main/packages/qwik/src/core/error/error.ts#L11
  Pathname: /parts/panels/elec-pwr/
  Plugin: qwik-ssg
      at createAndLogError (file:///home/christopher/code/own/hogpit/qwik-city/node_modules/@builder.io/qwik/dist/core.prod.mjs:92:54)
      at logErrorAndStop (file:///home/christopher/code/own/hogpit/qwik-city/node_modules/@builder.io/qwik/dist/core.prod.mjs:60:57)
      at qError (file:///home/christopher/code/own/hogpit/qwik-city/node_modules/@builder.io/qwik/dist/core.prod.mjs:177:12)
      at file:///home/christopher/code/own/hogpit/qwik-city/node_modules/@builder.io/qwik/dist/core.prod.mjs:4484:15
      at Array.map (<anonymous>)
      at serializeObjects (file:///home/christopher/code/own/hogpit/qwik-city/node_modules/@builder.io/qwik/dist/core.prod.mjs:4439:17)
      ...
error during build:
undefined
error: script "build.server" exited with code 1

(left out some parts of the backtrace because of discord character limit)

subtle kettle
#

Okay I still don't understand why I got this error in the first case, but I managed to come up with a workaround. Rather than have loadPartsCollection be an async function that returns the collection I instead use import.meta.glob in the top-level:

// src/collections/parts-collection.ts
import type { DocumentHeadProps } from '@builder.io/qwik-city'

// ...
const content = import.meta.glob('/content/**/*.mdx', { eager: true })

export const partsCollection = Object.keys(content).reduce((collection, path) => {
  const slug = getSlug(entry)
  collection[slug] = mapEntry(entry)

  return collection
}, {})
// (types omitted for clarity)

Then I can just import partsCollection from within my route file and use as is, completely circumventing routeLoader$