Ognjen Regoje bio photo

MY NAME IS
Ognjen Regoje
BUT YOU CAN CALL ME OGGY


I make things that run on the web (mostly).
More /ABOUT me.

me@ognjen.io Twitter LinkedIn Github

Publishing a linked folder in Jekyll

I use markdown to take notes. I wanted to publish a part of those notes using Jekyll.

The obvious way to do this is to just copy the files to the _posts directory.

This would mean I’d have to keep the files in sync and copy-paste them back and forth whenever I made any changes. Not ideal.

I figured I could make _posts a symbolic link to the directory with the notes. Then I’d (somehow) use the Jekyll pre_render hook to filter the posts to only the files I wanted to publish.

This is what I came up. It filters to only the documents that I wanted to publish. It also does some housekeeping to automate converting the notes to posts.

# _plugins/filter.rb

module Filter
  def self.process(site, payload)
    site.collections['posts'].docs.select!{|x| x.data['slug'].include? 'files-to-publish'}
    site.collections['posts'].docs.each do |x|
      x.data['slug'] = x.data['slug'].gsub("files-to-publish", "")
      x.data['permalink'] = x.data['slug']
      x.data['layout'] = 'post' if !x.data['layout']
    end
  end
end

Jekyll::Hooks.register :site, :post_read do |site, payload|
  Filter.process(site, payload)
end

What it does

All the notes that I wanted to publish had filenames in the format files-to-publish.actual-note-slug.md. This removes any files that don’t follow that.

site.collections['posts'].docs.select!{|x| x.data['slug'].include? 'files-to-publish'}

Then I loop through all the remaining posts.

site.collections['posts'].docs.each do |x|

And set the slug to be just the final part of the name.

x.data['slug'] = x.data['slug'].gsub("files-to-publish", "")
x.data['permalink'] = x.data['slug']

And then set the notes to use the post layout. Most notes didn’t have any layout set, but for a couple I specifically set them to use a different one.

x.data['layout'] = 'post' if !x.data['layout']

Finally register the hook to run before the rendering starts.

Jekyll::Hooks.register :site, :pre_render do |site, payload|
  Filter.process(site, payload)
end

This way I don’t have to keep the notes in sync and any notes that I add will be automatically added to the site.

Definitely worth it.

Update

I’ve reworked this to so that the external notes are combined into the _posts collection. And I’ve changed the hook to run on post_read rather then pre_render.

Jekyll::Hooks.register :site, :post_read do |site, payload|
  Filter.process(site, payload)
end

Further update

(new_doc is x in the code above)

I’ve also added the ability to fix links between files as well as correctly embed images.

new_doc.content.gsub!("", "")
new_doc.content.gsub!(/\[(.+)\]\(..\/(.+)\/(.+)\.md\)/, '[\1](/\3)')

I do still have to copy the images to the blog. At the moment I think this is a good thing because I make them smaller for the blog but the notes use the full resolution ones.

Yet another update

I’ve also added the ability to have parts of the markdown file excluded from being generated in Jekyll:

new_doc.content = new_doc.content.split("< !-- exclude -->").map{|x| x.split("< !-- include -->")}.select{|x| x.size > 1}.map{|x| x[1]}.join("")

Note the space in <!-- exclude-->. This is due to because the syntaxt highlighter plugin renders each term within a span which the interprets the rest of <!-- exclude--> as a comment.

#jekyll