Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Parse markdown in frontmatter #242

Open
Tracked by #585
caesar opened this issue May 5, 2021 · 8 comments
Open
Tracked by #585

Parse markdown in frontmatter #242

caesar opened this issue May 5, 2021 · 8 comments
Labels
assigned Whether or not this bug has been assigned some to some other issues as a subtask or pre-req enhancement New feature or request

Comments

@caesar
Copy link

caesar commented May 5, 2021

Hi there, and thanks for this cool project.

It would be really useful (to me anyway!) to be able to use markdown syntax in frontmatter values. For example, I have a subtitle key in the frontmatter for my posts, where it would be great to be able to use markdown syntax (at present I use HTML).

What do you think about adding an option to parse frontmatter values as markdown? Or maybe an array of frontmatter keys to be parsed as markdown, in case you don't want it for all of them.

@pngwn
Copy link
Owner

pngwn commented May 7, 2021

Do you have an example I can take a look at?

@caesar
Copy link
Author

caesar commented May 7, 2021

Sure: https://gist.github.com/caesar/7edf5e6cf5670f0866c3fad1a72ece34
See the description key which has markdown content. (In production I am currently using HTML tags instead.)

@pngwn
Copy link
Owner

pngwn commented May 9, 2021

That is helpful, thanks.

I actually came across this problem myself a little while ago and didn't really have a good solution for it. The issue with a flag and some optional keys to transform (or something similar) is that is gets a little unwieldy when a user wants to transform deep keys, I also don't really know what a user might want to do with it. Do they want a simple markdown to html transformation? Or do they want it to be parsed with all of the plugins and configuration that they have applied?

What if we added another option to the markdown configuration option. This is currently an object of:

interface FrontmatterOptions {
  parse: Function; 
  marker: string ;
} 

We could add another option, process or postprocess, a function that would receive the frontmatter, as an object transformed by whatever parser the user has provided (or the default YAML parser if none) and additionally a function that takes in a string and returns html. This would be the users markdown transform instance, so it will run with all of the options and plugins provided by the user. The return value would be the process object and would replace the existing markdown.

Something like this:

function myProcessor(frontmatter, processor) {
  return { 
    // keep other keys the same
    ...frontmatter, 
    
    // parse this to an HTML string, all config and plugins are as below
    description: processor(description) 
  }
} 

mdsvex({
  remarkPlugins: [somePlugin, anotherPlugin],
  frontmatter: {
    postprocess: myProcessor
  }
})

This way if some wanted to treat this markdown differently with another markdown parser, for example, they could easily do:

import { markdownParser } from 'some-lib';

function myProcessor(frontmatter, _) {
  return { 
    // keep other keys the same
    ...frontmatter, 
    
    // doing the date formatting here ensures there is no runtime cost
    // but also keeps the authoring experience good:  'date: 23 Feb 2021'
    date: {
      pretty: frontmatter.date,
      unix: new Date(frontmatter.date).getTime()
    },
    
    // use a different parser for this
    description: markdownParser(description) 
  }
} 

This would also allow arbitrary transforms on the frontmatter, keeping the authoring experience inside .svx files clean but allowing for various transforms (for example turning the date into a unix timestamp etc. for easy sorting as above). Thisis basically a really nice 'static' transform hook. You don't have to import the frontmatter into a component and do this at runtime, we do it at compile time instead. This also makes it easier to transform deep keys frontmatter.key.key without having a weird 'string property accessor' api.

This is technically already possible with a custom plugin but I think it is a pretty nice feature to add out of the box.

What do you think of this approach?

@pngwn
Copy link
Owner

pngwn commented May 9, 2021

I forgot to show this part (may be obvious), you could use this like this since it is an html string. Just import it or use the props that get passed:

{@html frontmatter.description}

@pngwn pngwn added the enhancement New feature or request label May 9, 2021
@pngwn
Copy link
Owner

pngwn commented May 9, 2021

In relation to this, I have been thinking of ways to reduce the API surface area and reduce breaking changes. One way to achieve this is to make some of the existing options functions that can be imported and passed to mdsvex. So instead of adding a configuration flag you could import the 'official plugin' and 'apply' it. An api that looks something like this:

import { mdsvex, highlight, frontmatter, slugify } from 'mdsvex';

mdsvex(opts, [
  frontmatter({
    postProcess: myProcessor
  }),
  highlight(),
  slugify(),
])

I would also provide some mdsvex 'presets' with a bunch of plugins already applied for thins like blogs etc without forcing that config on everyone. This would reduce the actual configuration to a small set of important options and give the user more control (over the order in which plugins run, as well as any custom ones). It would also reduce the need to significant breaking changes.

This will form part of a bigger API rethink in the future but this is actually a decent candidate for this option. The official plugins would be high quality and officially supported but not add to the weight or complexity of the core library.

@caesar
Copy link
Author

caesar commented May 9, 2021

That does seem like a good approach – in particular I like the ability to apply other transformations like the date parsing in your example.
But I wouldn't want to have to use a different markdown parser / to manage the markdown parser dependency myself. I wasn't quite sure from your example, but (in your first example) is the processor parameter a reference to mdsvex's markdown parser instance?

Another option would be to be able to pass a processing function for each key, something like:

import { mdsvex, processor } from 'mdsvex';

mdsvex({
  remarkPlugins: [somePlugin, anotherPlugin],
  frontmatterProcessors: {
    description: processor,
    date: value => new Date(value).getTime(),
  }
})

I'm not sure what you'd do with nested keys.

@pngwn
Copy link
Owner

pngwn commented May 9, 2021

Yes, in the first example the second argument processor is the exact same markdown processor as is used by mdsvex to process your files, so would do exactly as you wanted. The important thing is by making this a function you have the ability to do whatever you want in that transform.

For example a user may want a simple md -> html transform but their mdsvex transform may do a load of funky stuff. This provides and escape hatch for that case.

I want avoid lots of additional API, so the alternative you provided wouldn't really achieve that goal and has no solution for nested keys.

The signature for the suggested API would be:

function(frontmatter_object, mdsvex_markdown_parser_function) {
  return the_processed_frontmatter_object;
}

@caesar
Copy link
Author

caesar commented May 9, 2021

Sure, your solution does seem to be the more flexible way to achieve this. My alternative at first glance seems simpler to use (at least for simple use cases), but I can see that it would be harder to implement in a flexible way.

@pngwn pngwn added this to mdsvex Oct 16, 2021
@pngwn pngwn moved this to Refine in mdsvex Oct 16, 2021
@pngwn pngwn mentioned this issue Feb 23, 2024
8 tasks
@pngwn pngwn added the assigned Whether or not this bug has been assigned some to some other issues as a subtask or pre-req label Feb 23, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
assigned Whether or not this bug has been assigned some to some other issues as a subtask or pre-req enhancement New feature or request
Projects
No open projects
Status: Refine
Development

No branches or pull requests

2 participants