QwikDev / qwik

Instant-loading web apps, without effort
https://qwik.dev
MIT License
20.76k stars 1.29k forks source link

[🐞] How to load mdx content dynamically? #2705

Closed axelthat closed 1 year ago

axelthat commented 1 year ago

Which component is affected?

Qwik City (routing)

Describe the bug

I want to load markdown content based on the parameter from the url. If the url is /blogs/blog1, I would like to load the content from /src/content/blogs/blog1.mdx. But it is giving me this error,

[plugin:vite-plugin-eslint] Data returned from the onGet() endpoint must be serializable so it can also be transferred over the network in an HTTP response. Please ensure that the data returned from onGet() is limited to only strings, numbers, booleans, arrays or objects, and does not have any circular references. Error: Unable to serialize value.

What is the ideal way to load the dynamic markdown content? I could do the following on Astro and it would work flawlessly,

await import(
    `/src/content/blogs/${params.blog}.mdx`
  )

Reproduction

https://github.com/axelthat/qwik-bug-2

Steps to reproduce

npm install npm run dev

And then on browser, goto /blogs/blog1 or /blogs/blog2.

System Info

System:
  OS: macOS 13.1
  CPU: (10) arm64 Apple M1 Pro
  Memory: 239.75 MB / 16.00 GB
  Shell: 5.8.1 - /bin/zsh
Binaries:
  Node: 16.18.0 - /opt/homebrew/opt/node@16/bin/node
  Yarn: 1.22.19 - /opt/homebrew/bin/yarn
  npm: 8.19.2 - /opt/homebrew/opt/node@16/bin/npm
Browsers:
  Chrome: 109.0.5414.87
  Firefox: 104.0.2
  Safari: 16.2
npmPackages:
  @builder.io/qwik: 0.16.2 => 0.16.2 
  @builder.io/qwik-city: 0.0.128 => 0.0.128 
  undici: 5.14.0 => 5.14.0 
  vite: 4.0.3 => 4.0.3

Additional Information

No response

zanettin commented 1 year ago

Hi @axelthat I guess you can't just add the mdx files into the route directory and let quick load and resolve them as described here, right? that would be pretty easy to achieve.

The error just states, that you can not import something dynamically within a part of the code which gets separated by the qwik optimizer. So actually all functions ending with a dollar sign ($) will be splitted into chunks at build time which then can be streamed on demand. Therefore the optimizer has no chance to guess what you want to load at runtime.

I played around a bit with it and the only solution i can think of atm looks like this (uses qwik-city@0.1.0-beta9):

import { component$, Fragment } from "@builder.io/qwik";
import { loader$ } from "@builder.io/qwik-city";

export const getBlogPost = loader$(async ({ params }) => {
  const { slug } = params;

  // NEEDS TO BE UPDATED MANUALLY WHICH IS KINDA CUMBERSOME
  const registry = {
    foo: () => import("../../blog/foo.mdx"),
    bar: () => import("../../blog/bar.mdx"),
    baz: () => import("../../blog/baz.mdx"),
  };

  const data =
    slug in registry && typeof registry[slug] === "function"
      ? await registry[slug]()
      : null;

  return {
    slug,
    frontmatter: data?.frontmatter,
    default: JSON.stringify(data?.default()?.props?.children),
  };
});

export default component$(() => {
  const blogPost = getBlogPost.use();
  const blogData = blogPost.value;

  return (
    <Fragment>
      <h1>blog {blogPost.value.slug}</h1>
      <h2>{blogData.frontmatter.title}</h2>
      {JSON.parse(blogData.default)?.map((item) => (
        <p>{item.props.children}</p>
      ))}
    </Fragment>
  );
});

Very ugly and quick'n'dirty implemented but maybe it helps you to find a solution that works for you. i would recommend to use the built-in solution mentioned on top 👼

zanettin commented 1 year ago

Hi @axelthat just wanted to check about the state of this issue. have you been successful? 🤞

axelthat commented 1 year ago

@zanettin Sorry for late reply. Yes, that works.

shairez commented 1 year ago

thanks @axelthat and @zanettin

closing

samyosm commented 1 year ago

I don't think this solution works anymore: #5100. Using import() on something other that a route throws an error.