Skip to content

💡 RFC: Less opinionated, more pluggable MD parsing #1349

@FredKSchott

Description

@FredKSchott

Background & Motivation

  • We have a few options right now for customizing markdown parsing in Astro:
    • markdownOptions.remarkPlugins
    • markdownOptions.rehypePlugins
  • It's unclear how these options behave, specifically:
    • what is the difference between remark and rehype?
    • Do these completely overwrite Astro's defaults, when I supply my own?
    • What even are the defaults, if I need to add those back?
    • If they get merged, how do they get merged? can I control order?
    • What about stuff like Astro-required markdown processing, does my config conflict with that?
    • what if I want to use something other than remark, rehype?
  • It's less flexible to couple our configuration to a single named implementation/tool
    • remark and rehype mean a certain tool, what happens if we switch tools internally?

Proposal

markdown: {
  parse: (content: string) => string; // returns parsed .html/.astro
},
  1. Add a new markdown.parse config options
  2. Remove markdownOptions.remarkPlugins, markdownOptions.rehypePlugins

Details

  • When these config options are not defined, we use our own-builtin parser (no change from current behavior)
  • When you define your own, it becomes clear that you are responsible for the entire parsing
  • You are free to use whatever parser you want, or even combine in some way
  • You are free to share common parser configs, like markdown.parse = require('astro-markdown-parser-remark')({...})
  • Astro-specific behaviors would be run after the parser, so that this is clearly not the users responsibility.

Example

By default, we'd have a fully-featured Markdown parser inside of Astro. If you'd like to extend it, you can provide your own parser like this:

let parser = unified()
    .use('remark-parse')
    // add your plugins that you want (gfm, slugs, etc)
    // .use('remark-astro') (needed? I don't think so if we implement this as `.astro` output)
    .use('remark-rehype')
    .use('rehype-stringify');

export default ({
  markdown: {
    parse: (content) => parser.process(content).toString()
  }
});

Alternative Designs

// easier to extend with more parsers in the future
parsers: {
  markdown: ...
}
// less risk of organizing in the wrong way, before we know the full config object
markdownParser: ...,
styleParser: ...,

Related RFCs

  • Not yet filed, but @natemoo-re and I have talked a bit about moving from a model of "configure everything markdown via the parser" to "configure markdown via custom Astro components". Similar to how MDX lets you define your own H1, H2, Code, etc. components that the parser can then use.

Open Questions

  1. Are there any common use-cases that this feels like overkill for? Ex: adding a single remark plugin? This is a problem with our current API as well, and this proposal offers a solution via markdown.parse = require('astro-parser-remark')(...) that could support defaults out of the box.

Help make it happen!

  • I am willing to submit a PR to implement this change.
  • I am willing to submit a PR to implement this change, but would need some guidance.
  • I am not willing to submit a PR to implement this change.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions