Skip to content

Instantly share code, notes, and snippets.

@yuuki7
Last active November 14, 2025 13:17
Convert a GitHub Wiki to static HTML files
require 'fileutils'
require 'pathname'
require 'uri'
require 'commonmarker'
require 'github/markup'
require 'gollum-lib'
require 'nokogiri'
def postprocess_html(content_html, toc_html = nil)
dom = Nokogiri::HTML5.fragment(content_html)
dom.css('a.internal[href]').each do |a|
href = URI.parse(a['href'])
path = Pathname.new(href.path)
# Strip extensions from internal links
if path.extname == '.md'
href.path = path.sub_ext('').to_s
a['href'] = href.to_s
end
# Remove rel="nofollow"
a.remove_attribute('rel')
end
# Insert TOC before the first h2 heading
# if toc_html
# first_heading = dom.at_css('h2')
# if first_heading
# first_heading.before(toc_html)
# end
# end
dom.to_html
end
wiki_repo_dir = Pathname.new('./wikinder.wiki')
output_dir = Pathname.new('./wikinder.github.io')
FileUtils.mkdir_p(output_dir)
HTML_TEMPLATE = DATA.read
# https://github.com/gollum/gollum/wiki/Custom-rendering-gems
GitHub::Markup::Markdown::MARKDOWN_GEMS['commonmarker'] = proc do |content|
Commonmarker.to_html(content, options: {
render: { unsafe: true },
extension: {
strikethrough: true,
tagfilter: true,
table: true,
autolink: true,
tasklist: true,
footnotes: true,
},
})
end
GitHub::Markup::Markdown::MARKDOWN_GEMS.delete('kramdown')
wiki = Gollum::Wiki.new(wiki_repo_dir, {
hyphened_tag_lookup: true,
})
wiki.pages.each do |page|
path = Pathname.new(page.path)
is_home = path.to_s == 'Home.md'
is_about = path.to_s == 'Wikinder.md'
if is_home
title = 'Wikinder'
article_title = 'Welcome to Wikinder'
canonical_path = ''
output_filename = 'index.html'
page_nav = wiki.pages
.map { |pg|
{
path: "/#{URI.encode_uri_component(Pathname.new(pg.path).sub_ext('').to_s)}",
title: pg.title.tr('-', ' '),
}
}
.reject { |pg| pg[:title] =~ /^(?:Home|LICENSE|README)$|^Wikinder/ }
.sort_by { |pg| pg[:title].downcase }
.map { |pg| "<a href=\"%{path}\">%{title}</a>" % pg }
.join(' · ')
else
if is_about
article_title = 'About'
else
article_title = page.title.tr('-', ' ')
end
title = "#{article_title} - Wikinder"
canonical_path = URI.encode_uri_component(path.sub_ext('').to_s)
output_filename = path.sub_ext('.html')
page_nav = '<a href="/">Wikinder</a>'
end
content = postprocess_html(page.formatted_data, page.toc_data)
html = HTML_TEMPLATE % {
title: title,
canonical_path: canonical_path,
page_nav: page_nav,
article_title: article_title,
content: content,
footer: page.footer.formatted_data,
wiki_page_path: is_home ? '' : "/#{canonical_path}",
}
output_path = output_dir.join(output_filename)
File.write(output_path, html)
end
__END__
<!DOCTYPE html>
<html lang="mul">
<head>
<meta charset="utf-8">
<meta name="color-scheme" content="light dark">
<meta name="format-detection" content="telephone=no">
<meta name="viewport" content="width=device-width, initial-scale=1">
<meta property="og:title" content="%{title}">
<meta property="og:url" content="https://wikinder.org/%{canonical_path}">
<meta property="og:site_name" content="Wikinder">
<meta property="og:image" content="https://wikinder.org/og-image.jpg">
<meta property="og:type" content="article">
<meta name="twitter:card" content="summary">
<meta name="twitter:image" content="https://wikinder.org/og-image.jpg">
<meta name="twitter:image:alt" content="bear">
<title>%{title}</title>
<link rel="canonical" href="https://wikinder.org/%{canonical_path}">
<link rel="license" href="https://creativecommons.org/licenses/by-sa/4.0/">
<script>
window.MathJax = {
tex: {
inlineMath: {'[+]': [['$', '$']]},
packages: {'[+]': ['ams']},
},
loader: {
dependencies: {
'[mathjax-euler-extension]/chtml': ['output/chtml'],
},
paths: {
font: 'https://cdn.jsdelivr.net/npm/@mathjax',
'mathjax-euler-extension': '[font]/mathjax-euler-font-extension',
},
load: [
'input/tex-base',
'[tex]/ams',
'output/chtml',
'[mathjax-euler-extension]/chtml',
],
},
};
</script>
<script src="https://cdn.jsdelivr.net/npm/mathjax@4/startup.js"></script>
<style>
#article-header {
align-items: baseline;
display: flex;
gap: 1rem;
}
#article-title {
margin: 0;
}
.toc {
margin-top: 1rem;
}
.toc-title {
font-weight: bold;
}
img {
max-width: 100%%;
}
pre {
overflow-wrap: anywhere;
white-space: pre-wrap;
}
</style>
</head>
<body>
<header id="page-header">
<nav id="page-nav">
<p>
%{page_nav}
</p>
</nav>
</header>
<main id="page-main">
<article id="article">
<header id="article-header">
<h1 id="article-title">%{article_title}</h1>
</header>
<section id="article-main">
%{content}
</section>
</article>
</main>
<footer id="page-footer">
%{footer}
<p>
<a href="https://github.com/wikinder/wikinder/wiki%{wiki_page_path}">Edit this page</a>
</p>
</footer>
</body>
</html>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment