slidev icon indicating copy to clipboard operation
slidev copied to clipboard

Allow (pre)parser extension

Open twitwi opened this issue 3 years ago • 7 comments

Is your feature request related to a problem? Please describe. For some presentation that aggregate subslides, it takes a lot of space to even write the front matter with the two --- (one after the frontmatter, and one after the (empty) content) + the empty line. With a custom syntax for includes, I had in a previous system:

#@chunk: title.md
#@chunk: objectives.md
#@chunk: toc.md

Which now takes a lot of vertical boilerplate:

src: title.md
---

---
src: objectives.md
---

---
src: toc.md
---

---

There are other cases where a custom compact notation would be nice to have (e.g. open a slide at every # bla title, so no --- everywhere) (e.g. alternate syntax to open a slide with a specific layout, @cover(bg.jpg), etc). For some use cases, the solution below might be also simpler than adding rules to the markdownit parser (but the blocking point is really the things that happen before markdown parsing, so the split at ---).

Describe the solution you'd like A flexible, minimal-change solution would be to add an extension point to the parser, that addons and the user can customize. E.g. allow to call a (chain of) custom parsers for instance here https://github.com/slidevjs/slidev/blob/fb8054f376b3b71c594b349fa7339cf2e2344a7e/packages/parser/src/core.ts#L130 Things to consider without too much filtering:

  • pass the list of lines to the extension, pass the current line index,
  • allow the extension to consume/replace/remove lines and to return an index for continuation (which will often be unchanged?) and whether the extensions actually did something,
  • do the default behavior if no extension did anything for a given line
  • loop properly so that an extension can process the content generated/modified by another extension
  • nice to have but maybe too much: maybe have a notion of priority/phases to control the order in the chain.

Describe alternatives you've considered I could write a script (bash, python) that generates the actual md file...

twitwi avatar Sep 02 '22 13:09 twitwi

Have you tried to write an inline Vite plugin to do the transformation? If that can't help, I am fine to expose some capability for such customization

antfu avatar Sep 02 '22 20:09 antfu

I'll have a look at what can be done with vite, it is still some tech I don't know very well.

twitwi avatar Sep 02 '22 20:09 twitwi

@antfu can vite plugins "intercept" a file read with fs as done in this case? https://github.com/slidevjs/slidev/blob/df066d434468160795e5718d22d08d450e2dd1a9/packages/parser/src/fs.ts#L9

I tried a simple plugin but it seems to be called with individual virtual 1.md 2.md ... files (but not the entry point).

twitwi avatar Sep 02 '22 22:09 twitwi

I see. PR welcome to propose your interface in mind (or if you want to start with a more detailed API design first would also be great)

antfu avatar Sep 03 '22 04:09 antfu

I started thinking about it, I imagine a few callbacks from the parser to the plugin, do you prefer a style where there is an api with several methods or rather a single method with an kind of event type (i.e. the plugin implements a single method, that is a if (type == '') .... elif ....)?

For the "chain" aspect, where e.g. several (slidev) addons can each add a parser plugins and the user folder can also add parser plugins, ~~I think the core of the solution is related to the same question with shortcuts https://github.com/slidevjs/slidev/issues/629 ... any suggestions on how you would do that best? I guess it is almost handled by https://github.com/slidevjs/slidev/blob/main/packages/slidev/node/plugins/setupClient.ts#L27 so I would update there.~~ I PR'd a /* chained_injections */ that should be sufficient (no fine control on the order but I don't think it would be used a lot anyways) https://github.com/slidevjs/slidev/pull/702

twitwi avatar Sep 03 '22 16:09 twitwi

NB: The (chained) injection unfortunately runs in the client but the preparser is on the node side so it means I'll have to update the server extension code, i.e., https://github.com/slidevjs/slidev/blob/f0941ce770ff8879e5adff695dc4015cfbd4e5ef/packages/slidev/node/plugins/setupNode.ts#L19

twitwi avatar Sep 05 '22 14:09 twitwi

NB: the situation is even more tricky as:

  • to know which addons are used we need the headmatter (frontmatter of the first slide)
  • so the parser basically should run addon-less until it has read the headmatter,
  • then only it can load the addon, asynchronously, and using config information from another project (the node part, which is not a dependence of the parser part)
  • then only it can go on parsing with the possible addons enabled

I've come up with a somewhat convoluted solution where the node module injects an addon loader function into the parser that calls it when it has found the headmatter. I'll clean it a little and push it for feedback.

twitwi avatar Sep 08 '22 16:09 twitwi

This issue has been automatically marked as stale because it has not had recent activity. It will be closed if no further activity occurs. Thank you for your contributions.

stale[bot] avatar Nov 07 '22 16:11 stale[bot]