Is your feature request related to a problem? Please describe.
I'm in the process of my v5 migration and ran into an API surface snag while building a page-type plugin that renders .mdx files.
Core only parses .md files (build.ts#L84), so, like the canvas and bases page types, the plugin creates its pages in generate. For each .mdx file it:
- Runs the
.mdx body through the site's own transformer chain
- Bundles the widgets the page imports with esbuild.
- Renders each widget to HTML at build time as an island that a small runtime hydrates in the browser.
The snag: steps 1 and 2 are both async:
- Four of the default transformers are async (CreatedModifiedDate, SyntaxHighlighting, ObsidianFlavoredMarkdown, Description), and
unified().runSync() throws on them.
- esbuild plugins only work with its async API.
However, the emit and partial emit generate are both sync, so a generate that returns a promise crashes the build with virtualPages is not iterable.
Describe the solution you'd like
My copy of the Quartz source has a working solution that the live site uses: widen the return type to allow a promise, and await it at both call sites:
export type PageGenerator = (args: {
...
-}) => VirtualPage[]
+}) => VirtualPage[] | Promise<VirtualPage[]>
// PageTypePluginEntry
- generate?: (...args: never[]) => VirtualPage[]
+ generate?: (...args: never[]) => VirtualPage[] | Promise<VirtualPage[]>
plugins/pageTypes/dispatcher.ts
- const virtualPages = pt.generate({ content, cfg, ctx })
+ const virtualPages = await pt.generate({ content, cfg, ctx })
This looks like a capability that was lost from v4 to v5. In v4, the plugins that created pages (tag and folder pages) were emitters, and emitters could be async (types.ts#L51, tagPage.tsx#L124). I couldn't gauge whether the move to sync was an intentional design choice or a migration oversight, and didn't want to push a PR without understanding the call.
If this gets resolved, I should be able to publish the MDX plugin as a community plugin.
Describe alternatives you've considered
- Other hooks: none of them gets the work done before the dispatcher needs the pages.
- Transformers only see
.md files, and they run in worker threads.
- Filters are synchronous.
- Other emitters run after the dispatcher (emit.ts#L67-L74). That's too late, because virtual pages have to exist before rendering so they're visible to
allFiles and transclusion.
- Naming files
.mdx.md so core parses them, and re-parsing flagged files with MDX syntax inside a transformer. It works around the limit, but it's a strange convention to force on authors.
- Letting page types register file extensions with the parse phase, so non-
.md formats go through the worker pipeline. This is arguably the more principled design and I'd welcome it. The change above is small and doesn't rule it out.
Is your feature request related to a problem? Please describe.
I'm in the process of my v5 migration and ran into an API surface snag while building a page-type plugin that renders
.mdxfiles.Core only parses
.mdfiles (build.ts#L84), so, like the canvas and bases page types, the plugin creates its pages ingenerate. For each.mdxfile it:.mdxbody through the site's own transformer chainThe snag: steps 1 and 2 are both async:
unified().runSync()throws on them.However, the emit and partial emit
generateare both sync, so ageneratethat returns a promise crashes the build withvirtualPages is not iterable.Describe the solution you'd like
My copy of the Quartz source has a working solution that the live site uses: widen the return type to allow a promise, and await it at both call sites:
export type PageGenerator = (args: { ... -}) => VirtualPage[] +}) => VirtualPage[] | Promise<VirtualPage[]>plugins/pageTypes/dispatcher.tsThis looks like a capability that was lost from v4 to v5. In v4, the plugins that created pages (tag and folder pages) were emitters, and emitters could be async (types.ts#L51, tagPage.tsx#L124). I couldn't gauge whether the move to sync was an intentional design choice or a migration oversight, and didn't want to push a PR without understanding the call.
If this gets resolved, I should be able to publish the MDX plugin as a community plugin.
Describe alternatives you've considered
.mdfiles, and they run in worker threads.allFilesand transclusion..mdx.mdso core parses them, and re-parsing flagged files with MDX syntax inside a transformer. It works around the limit, but it's a strange convention to force on authors..mdformats go through the worker pipeline. This is arguably the more principled design and I'd welcome it. The change above is small and doesn't rule it out.