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

support for YAML front matter #485

Closed
elliottregan opened this issue Sep 10, 2014 · 22 comments
Closed

support for YAML front matter #485

elliottregan opened this issue Sep 10, 2014 · 22 comments

Comments

@elliottregan
Copy link

Like this: http://jekyllrb.com/docs/frontmatter/

@scottgonzalez
Copy link
Contributor

How exactly do you envision this working? The front matter is metadata for a document, which doesn't fit into the return value for a parsed document, which results in just text. This is really a task that needs to be delegated to whatever software is using marked.

@elliottregan
Copy link
Author

First, set an option to return an object rather than just text. If a front matter section is present, Marked could return an object like this:

{
meta: {},
content: ""
}

If there is no front matter, it would just return

{
content: ""
}

@scottgonzalez
Copy link
Contributor

This really seems out of scope for marked, but I'll wait for @chjj to respond.

@elliottregan
Copy link
Author

I hear that. I just see a few markdown processors supporting meta data, and it's pretty useful to be able to include it.

On Wed, Sep 10, 2014 at 2:29 PM, Scott González notifications@github.com
wrote:

This really seems out of scope for marked, but I'll wait for @chjj to respond.

Reply to this email directly or view it on GitHub:
#485 (comment)

@howardroark
Copy link

I certainly agree that the pattern itself is showing to be very useful and quite popular. It makes writing documentation and building sites with Markdown a very sane process. If you plan to use the meta data as an object with your template engine, why not just ready it in one swoop as you parse the document string. Maybe even support HJSON as well?

Though following the UNIX philosophy it would make more sense of there was another project that took the parsed Markdown and went from there. If you think about how Jekyll uses it.... it does allow HTML as well.

@iansinnott
Copy link

I just ran into a similar problem where I needed to separate YAML front matter from the markdown content. I ended up using the YAML Front Matter module. Seems to cover this use case pretty well.

@elliottregan
Copy link
Author

So, the situ on is to "pre-process" the .md file before sending it to the client? I guess if that is out of the scope of what marked is trying to do, that's fine. I think it would still be great to have support for at least removing meta data.

On Mon, Oct 6, 2014 at 11:48 AM, Ian Sinnott notifications@github.com
wrote:

I just ran into a similar problem where I needed to separate YAML front matter from the markdown content. I ended up using the [YAML Front Matter module][yml]. Seems to cover this use case pretty well.

[yml]: https://github.com/dworthen/js-yaml-front-matter

Reply to this email directly or view it on GitHub:
#485 (comment)

@adam-lynch
Copy link

IMO it's out of scope. I've had gulp pipelines like this:

gulp.src('*.md')
    .pipe(gulpFrontMatter()) // stores the front matter object on the File object and removes it from each file contents
    .pipe(gulpMarkdown()) // convert to HTML, metadata is still accessible
    .pipe(gulp.dest('./ouput')) // store HTML
    .pipe(es.map(function(file){
         console.log(file.frontMatter.hello); // stupid example
    });

It should be removed before hand.

@dvcrn
Copy link

dvcrn commented Oct 23, 2015

It's been a year but github is currently parsing front matter into a table. Now that this is part of the official GFM parsing, I think we should put it into marked (into gfm) as well.

e.g.: https://github.com/dvcrn/dvcrn.github.io/blob/master/_posts/2015-10-13-my_dream_editor.markdown

Front matter:

---
layout: post
title: "My Dream Editor"
date: "2015-10-13 16:05:17 +0900"
---

Output:
screen shot 2015-10-23 at 11 02 40 am

@adam-lynch
Copy link

@dvcrn if GitHub was, you wouldn't need to use an image as an example right? 😜

@elliottregan
Copy link
Author

Nice catch! @dvcrn !

@adam-lynch click the other link. You can see it at the top of that page.

@adam-lynch
Copy link

Elliott, thanks :). Weird how they render markdown differently here hmm

On Fri, 23 Oct 2015, 17:13 Elliott Regan notifications@github.com wrote:

Nice catch! @dvcrn https://github.com/dvcrn !

@adam-lynch https://github.com/adam-lynch click the other link. You can
see it at the top of that page.


Reply to this email directly or view it on GitHub
#485 (comment).

@elliottregan
Copy link
Author

They don't mention it in their docs, but they are definitely showing key-values, even complex ones with arrays, as a table. I still prefer JSON because it's easier to work with, but it makes sense for Github to display it Asa table.

@scottgonzalez
Copy link
Contributor

They don't mention it in their docs

It's probably just left out because it's unrelated to their markdown parser. See https://github.com/blog/1647-viewing-yaml-metadata-in-your-documents

@justinhelmer
Copy link

FYI if people are still looking, @adam-lynch's comment here is conceptually the way to go.

In terms of implementation, metalsmith is really great for this type of thing, and includes a metalsmith-markdown plugin that is built on top of marked. When used in combination with metalsmith-layouts, you can do some really cool things.

For example (in a gulp pipeline):

function build() {
  var files = ['source/**', '!source/layouts/**', '!source/partials/**'];

  return gulp
      .src(files)
      .pipe(metalsmith({
        use: [
          markdown(),                   // from `metalsmith-markdown` (`marked` wrapper)
          layouts({                     // from `metalsmith-layouts`
            engine: 'handlebars',       // any engine from consolidate.js
            default: 'page.html',
            directory: 'source/layouts',
            partials: 'source/partials',
            pattern: '**/*.html'
          })
        ]
      }))
      .pipe(gulp.dest('build'));
}

metalsmith-layouts is automatically looking for the file.frontMatter property set internally by gulp-front-matter, and using it to render handlebars data for a fixed set of layouts.

Example markdown (myfile.md):

---
layout:    documentation.html
title:     Documentation - 4.2.1
permalink: /documentation/4.2.1/
---
- some
- list

some content

Example handlebars template used by metalsmith-layouts (documentation.html):

<!DOCTYPE html>
<html>
  <body>
    <h1>{{title}}</h1>
    <main>{{{contents}}}</main>
  </body>
</html>

Outputs (myfile.html):

<!DOCTYPE html>
<html>
  <body>
    <h1>Documentation - 4.2.1</h1>
    <main>
      <ul>
        <li>some</li>
        <li>list</li>
      </ul>
      <p>some content</p>
    </main>
  </body>
</html>

None of this would be possible without the work by @chjj and team, so thanks for that.

I originally came here looking for answers, so hopefully this helps the next guy/gal.

Cheers.

@joshbruce
Copy link
Member

See #956

Closing as out of scope for the two targeted specifications of Marked.

@elliottregan
Copy link
Author

:( but that makes sense.

@up9cloud
Copy link

Memo for browser 😆

<div id="content"></div>
<script src="https://unpkg.com/marked/marked.min.js"></script>
<script src="https://unpkg.com/js-yaml@3/dist/js-yaml.js"></script>
<script src="https://unpkg.com/yaml-front-matter@4/dist/yamlFront.js"></script>
<script>
  let cached_lex = marked.Lexer.lex
  marked.Lexer.lex = function (text, options) {
    let parsed = yamlFront.loadFront(text)
    let cKey = '__content'
    let table = [
      [],
      [],
      []
    ]
    for (let k in parsed) {
      if (k === cKey) {
        continue
      }
      table[0].push(k)
      table[1].push('---')
      table[2].push(parsed[k])
    }
    let tableMd = table.map(row => row.join('|')).join('\n')
    return cached_lex(tableMd + '\n' + parsed[cKey], options)
  }
</script>
<script src="https://unpkg.com/jquery@3/dist/jquery.min.js"></script>
<script>
$(document).ready(function() {
  $.get('https://raw.githubusercontent.com/vuejs/vuejs.org/master/src/v2/examples/index.md', function(str) {
    $("#content").html(marked(str));
  });
});
</script>

@mcapodici
Copy link

I want to ignore frontmatter for my scenario.

Here is how I do it:

md = md.replace(/^---$.*^---$/ms, '');

@UziTech
Copy link
Member

UziTech commented May 30, 2019

You can also use front-matter to get any edge cases.

md = frontMatter(md).body;

@sernaferna
Copy link

I came across a case where I do want the rendered output from my markdown to be handled differently based on the front matter. My use case is too niche to describe, but suppose we want to handle the case where we want to prefix some text to the beginning of headers, such that:

---
headingPrefix: Chapter:
---
# Some things I was thinking
These are some things I was thinking...

would become

<h1>Chapter: Some things I was thinking</h1>
<p>These are some things I was thinking...</p>

still contrived, but you get the point; the text in the optional front-matter would be used in rendering HTML from the markdown.

First the extension. To make this work, instead of just creating the extension we create a function that returns the extension; the function can take parameter(s) for the information it needs:

import { marked } from 'marked';

export const fixHeadings = (headingPrefix: string): Partial<Omit<marked.Renderer<false>, 'options'>> => {
  return {
    heading(text, level) {
      return `<h${level}>${headingPrefix} ${text}</h${level}>`;
    },
  };
};

Using the front-matter package we can handle attributes in the markdown front-matter similar to this:

import { marked } from 'marked';
import fm from 'front-matter';
import { fixHeadings } from './fixheadings'

interface MarkdownOptions {
  headingPrefix: string;
  // other options that can be set in the front-matter
}

const defaultOptions: MarkdownOptions = {
  headingPrefix: 'Chapter:', // good to have a default, because the front-matter might not exist
};

export const renderedOutputFromMarkdown = (md: string): string => {
  const options = { ...defaultOptions };
  // by default, the markdown we want to parse is the entire input
  let markdownString = md;

  try {
    const fmResult = fm(md);
    // if there is front-matter the markdown to parse is reduced to just the `body` from `front-matter`
    markdownString = fmResult.body;
    if ((fmResult.attributes as any).headingPrefix) {
      options.headingPrefix = (fmResult.attributes as any).headingPrefix;
    }
  } catch {
    // do nothing; can easily throw exceptions, especially when fm being edited
  }
  marked.use({ renderer: fixHeadings(options.headingPrefix) });
  // other marked.use() options go here as well

  return marked.parse(markdownString);
};

@francoisromain
Copy link

francoisromain commented Dec 6, 2023

updated version of the front matter lib : https://www.npmjs.com/package/gray-matter

Converts a string with front-matter, like this:

---
title: Hello
slug: home
---
<h1>Hello world!</h1>

Into an object like this:

{
  content: '<h1>Hello world!</h1>',
  data: { 
    title: 'Hello', 
    slug: 'home' 
  }
}

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests