How to Properly Extend Jekyll/Kramdown Markdown Parsing

Hi all,

tldr; How does one properly add individual syntax extensions to jekyll/kramdown?

I posted a gem recently, which is a generator-style plugin, but wanted to update its implementation to adhere more to the expected jekyll/kramdown standards.

I think this means refactoring the plugin to be more converter-like, but I think a converter plugin would then be responsible for rendering the entire markdown file, which I don’t want. I just want to add one new piece of syntax to parse.

I found this implementation, which if I understand correctly, is subclassing Jekyll’s Convertible module and calling its own transforms for [[wikilinks]] just before calling the regular transform method. It sounds like just what I want.

So, I would like to refactor my plugin to match this implementation, but I can’t seem to get the example I found to hook into Jekyll. I run the usual bundle exec jekyll serve --trace and the plugin code is never run. It’s an older example (8 years old), but after perusing the existing transform method, it seems to check out – e.g. it’s overriding modules that still exist. So I don’t understand why the example isn’t running for me.

Any help is appreciated, as well as input about manner of implementation. I would like this to be a robust plugin that adheres to jekyll/kramdown standards and has intuitive usage that meshes with the rest of the framework.

Hi.

Try running with --verbose to see if the file is called at all. Also you can add some puts or error throwing lines to see something appear. Also Jekyll may have moved on a lot that it cannot find that file.

From looking at the Plugins docs, a converter is what you need to process files with custom extensions. A generator will create new files (such as from a YAML file).

I’d recommend first getting a Hello World converter to work, even add a puts line so you can see it run in the console. And inspect the output HTML directly. Use this example from on the Jekyll Converter docs. I expect this is the modern Jekyll way.

module Jekyll
  class UpcaseConverter < Converter
    safe true
    priority :low

    def matches(ext)
      ext =~ /^\.upcase$/i
    end

    def output_ext(ext)
      ".html"
    end

    def convert(content)
      content.upcase
    end
  end
end

I also cover the plugins types in one page here.

Once you have that working on some *.upcase files, then you can add content to it to match the behavior of the linked WikiLinks plugin.

Or use the generic pattern from the WikiLinks one to match all files. Including I guess .md, .html, etc.
pat = /\[\[(.+?)\]\]/

The Markdown options page of Jekyll’s doc has a section on building custom Markdown processors:

That might be a starting point for building a custom Markdown processor that pre-processes it’s input to expand new extensions before passing the modified input to Kramdown.

1 Like

Thanks for the doc-link chuckhoupt!

One sticking point here is that I need access to the site object which, if I am understanding correctly, is not accessible from converters. The jekyll-wikilinks-plugin example preserves access to the site object via sub-moduling(?) the Convertible module. So I find it desirable to use that method of implementation.

So, the mystery to me is: Why does the Convertible.transform() override not get called? Or, is the site object accessible in the example you sent, but I’m not seeing it? :sweat_smile:

[Note: I’ve never written a custom converter for either Jekyll 3 or 4, so I’m at the limit of my knowledge now]

From the look of the example, it appears that only the converter’s configuration is passed to the converter (i.e. site.mycustomconverter). If you need access beyond that, then I’m guessing custom code is needed.

1 Like

Just posting an update:

After looking at jekyll-relative-links, I’m starting to think generators and converters are both valid solution implementations. I am going to stick with the generator for now.

Sidenote: I’m wondering if the fact that jekyll collections generally require a ‘_’ before the collection name seems hints at generator usage…?

PS Thanks to chuckhoupt and MichaelCurrin for taking the time to read this question.

1 Like

A generator takes data and generates HTML. Say a YAML file or API request or frontmatter. You might have one index page and one data file and generate hundreds of output pages.
Not a good fit for mapping one to one from .myext to .html but I suppose it works.

A converter takes a page of .myext and makes exactly one .html page. So good for adding formats besides markdown.