mirror of
https://github.com/keanuplayz/dotfiles.git
synced 2024-08-15 02:33:12 +00:00
rewrite markdown2htmldoc to JS
This commit is contained in:
parent
9f1d9f506e
commit
2114cfc5c7
7 changed files with 1252 additions and 188 deletions
1
script-resources/markdown2htmldoc/.gitignore
vendored
Normal file
1
script-resources/markdown2htmldoc/.gitignore
vendored
Normal file
|
@ -0,0 +1 @@
|
||||||
|
node_modules
|
86
script-resources/markdown2htmldoc/main.js
Executable file
86
script-resources/markdown2htmldoc/main.js
Executable file
|
@ -0,0 +1,86 @@
|
||||||
|
#!/usr/bin/env node
|
||||||
|
|
||||||
|
const fs = require('fs');
|
||||||
|
const argparse = require('argparse');
|
||||||
|
const markdownIt = require('markdown-it');
|
||||||
|
const markdownItTaskCheckbox = require('markdown-it-task-checkbox');
|
||||||
|
const markdownItEmoji = require('markdown-it-emoji');
|
||||||
|
const markdownItHeaderAnchors = require('./markdown-it-header-anchors');
|
||||||
|
const Prism = require('prismjs');
|
||||||
|
const loadPrismLanguages = require('prismjs/components/');
|
||||||
|
|
||||||
|
let parser = new argparse.ArgumentParser();
|
||||||
|
parser.addArgument('inputFile', {
|
||||||
|
nargs: argparse.Const.OPTIONAL,
|
||||||
|
metavar: 'INPUT_FILE',
|
||||||
|
help: '(stdin by default)',
|
||||||
|
});
|
||||||
|
parser.addArgument('outputFile', {
|
||||||
|
nargs: argparse.Const.OPTIONAL,
|
||||||
|
metavar: 'OUTPUT_FILE',
|
||||||
|
help: '(stdout by default)',
|
||||||
|
});
|
||||||
|
let args = parser.parseArgs();
|
||||||
|
|
||||||
|
let md = markdownIt({
|
||||||
|
html: true,
|
||||||
|
linkify: true,
|
||||||
|
highlight: (str, lang) => {
|
||||||
|
if (lang.length > 0) {
|
||||||
|
loadPrismLanguages([lang]);
|
||||||
|
let h = Prism.highlight(str, Prism.languages[lang], lang);
|
||||||
|
return h;
|
||||||
|
}
|
||||||
|
return str;
|
||||||
|
},
|
||||||
|
});
|
||||||
|
md.use(markdownItTaskCheckbox);
|
||||||
|
md.use(markdownItEmoji);
|
||||||
|
md.use(markdownItHeaderAnchors);
|
||||||
|
|
||||||
|
let markdownDocument = fs.readFileSync(args.get('inputFile', 0), 'utf-8');
|
||||||
|
let renderedMarkdown = md.render(markdownDocument);
|
||||||
|
|
||||||
|
let renderedHtmlDocument = `
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
|
<meta http-equiv="X-UA-Compatible" content="ie=edge">
|
||||||
|
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/github-markdown-css/3.0.1/github-markdown.min.css" integrity="sha256-HbgiGHMLxHZ3kkAiixyvnaaZFNjNWLYKD/QG6PWaQPc=" crossorigin="anonymous" />
|
||||||
|
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/prism/1.17.1/themes/prism.min.css" integrity="sha256-77qGXu2p8NpfcBpTjw4jsMeQnz0vyh74f5do0cWjQ/Q=" crossorigin="anonymous" />
|
||||||
|
<style>
|
||||||
|
html, body {
|
||||||
|
padding: 0;
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
.markdown-body {
|
||||||
|
max-width: 882px;
|
||||||
|
margin: 0 auto;
|
||||||
|
padding: 32px;
|
||||||
|
}
|
||||||
|
.octicon-link {
|
||||||
|
font: normal normal 16px 'octicons-link';
|
||||||
|
line-height: 1;
|
||||||
|
-webkit-font-smoothing: antialiased;
|
||||||
|
-moz-osx-font-smoothing: grayscale;
|
||||||
|
-webkit-user-select: none;
|
||||||
|
-moz-user-select: none;
|
||||||
|
-ms-user-select: none;
|
||||||
|
user-select: none;
|
||||||
|
}
|
||||||
|
.octicon-link::before {
|
||||||
|
content: '\\f05c';
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<article class="markdown-body">
|
||||||
|
${renderedMarkdown}
|
||||||
|
</article>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
|
`;
|
||||||
|
|
||||||
|
fs.writeFileSync(args.get('outputFile', 1), renderedHtmlDocument, 'utf-8');
|
|
@ -0,0 +1,39 @@
|
||||||
|
const GithubSlugger = require('github-slugger');
|
||||||
|
|
||||||
|
function markdownItHeaderAnchors(md) {
|
||||||
|
let slugger = new GithubSlugger();
|
||||||
|
|
||||||
|
let defaultRender =
|
||||||
|
md.renderer.rules.heading_open ||
|
||||||
|
((tokens, idx, options, _env, self) =>
|
||||||
|
self.renderToken(tokens, idx, options));
|
||||||
|
|
||||||
|
// eslint-disable-next-line camelcase
|
||||||
|
md.renderer.rules.heading_open = (tokens, idx, opts, env, self) => {
|
||||||
|
let renderedHeadingOpen = defaultRender(tokens, idx, opts, env, self);
|
||||||
|
|
||||||
|
let innerText = '';
|
||||||
|
let headingContentToken = tokens[idx + 1];
|
||||||
|
headingContentToken.children.forEach(child => {
|
||||||
|
switch (child.type) {
|
||||||
|
case 'html_block':
|
||||||
|
case 'html_inline':
|
||||||
|
break;
|
||||||
|
case 'emoji':
|
||||||
|
innerText += child.markup;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
innerText += child.content;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
if (innerText.length > 0) {
|
||||||
|
let id = slugger.slug(innerText);
|
||||||
|
renderedHeadingOpen += `<a id="${id}" class="anchor" href="#${id}" aria-hidden="true"><span class="octicon octicon-link"></span></a>`;
|
||||||
|
}
|
||||||
|
|
||||||
|
return renderedHeadingOpen;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = markdownItHeaderAnchors;
|
21
script-resources/markdown2htmldoc/package.json
Normal file
21
script-resources/markdown2htmldoc/package.json
Normal file
|
@ -0,0 +1,21 @@
|
||||||
|
{
|
||||||
|
"private": true,
|
||||||
|
"dependencies": {
|
||||||
|
"argparse": "^1.0.10",
|
||||||
|
"github-slugger": "^1.2.1",
|
||||||
|
"markdown-it": "*",
|
||||||
|
"markdown-it-emoji": "*",
|
||||||
|
"markdown-it-task-checkbox": "*",
|
||||||
|
"prismjs": "^1.17.1"
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"eslint": "*",
|
||||||
|
"eslint-config-dmitmel": "dmitmel/eslint-config-dmitmel"
|
||||||
|
},
|
||||||
|
"eslintConfig": {
|
||||||
|
"extends": "eslint-config-dmitmel/presets/node",
|
||||||
|
"rules": {
|
||||||
|
"node/shebang": "off"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
3
script-resources/markdown2htmldoc/setup.sh
Executable file
3
script-resources/markdown2htmldoc/setup.sh
Executable file
|
@ -0,0 +1,3 @@
|
||||||
|
#!/usr/bin/env sh
|
||||||
|
|
||||||
|
yarn install --production
|
1101
script-resources/markdown2htmldoc/yarn.lock
Normal file
1101
script-resources/markdown2htmldoc/yarn.lock
Normal file
File diff suppressed because it is too large
Load diff
|
@ -1,188 +0,0 @@
|
||||||
#!/usr/bin/env ruby
|
|
||||||
|
|
||||||
require "commonmarker"
|
|
||||||
require "rouge"
|
|
||||||
|
|
||||||
ROUGE_THEME = Rouge::Themes::Pastie
|
|
||||||
COMMONMARKER_PARSE_OPTIONS = [:DEFAULT, :FOOTNOTES]
|
|
||||||
COMMONMARKER_RENDER_OPTIONS = [:UNSAFE]
|
|
||||||
COMMONMARKER_EXTENSIONS = [:tagfilter, :autolink, :table, :strikethrough, :tasklist]
|
|
||||||
|
|
||||||
# parse arguments {{{
|
|
||||||
|
|
||||||
def get_program_name()
|
|
||||||
return File.basename($PROGRAM_NAME)
|
|
||||||
end
|
|
||||||
|
|
||||||
def print_help()
|
|
||||||
$stderr.print <<EOF
|
|
||||||
usage: #{get_program_name()} [<input_file>] [<output_file>]
|
|
||||||
|
|
||||||
if <input_file> is not specified, input will be read from stdin
|
|
||||||
if <output_file> is not specified, output will be printed to stdout
|
|
||||||
EOF
|
|
||||||
exit(true)
|
|
||||||
end
|
|
||||||
|
|
||||||
def print_error(*args)
|
|
||||||
$stderr.puts("#{get_program_name()}: error:", *args)
|
|
||||||
exit(false)
|
|
||||||
end
|
|
||||||
|
|
||||||
input_file_path = nil
|
|
||||||
output_file_path = nil
|
|
||||||
|
|
||||||
reading_only_positional_args = false
|
|
||||||
positional_arg_index = 0
|
|
||||||
ARGV.each do |arg|
|
|
||||||
is_positional = reading_only_positional_args
|
|
||||||
|
|
||||||
if not is_positional
|
|
||||||
case arg
|
|
||||||
when "-h", "--help"
|
|
||||||
print_help()
|
|
||||||
when "--"
|
|
||||||
reading_only_positional_args = true
|
|
||||||
else
|
|
||||||
if arg.start_with?("-")
|
|
||||||
print_error("unknown option: #{arg}")
|
|
||||||
else
|
|
||||||
is_positional = true
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
if is_positional
|
|
||||||
case positional_arg_index
|
|
||||||
when 0 then input_file_path = arg
|
|
||||||
when 1 then output_file_path = arg
|
|
||||||
else print_error("unexpected argument: #{arg}")
|
|
||||||
end
|
|
||||||
positional_arg_index += 1
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
# }}}
|
|
||||||
|
|
||||||
# parse markdown document {{{
|
|
||||||
|
|
||||||
if input_file_path.nil?()
|
|
||||||
markdown = $stdin.read()
|
|
||||||
else
|
|
||||||
markdown = IO.read(input_file_path)
|
|
||||||
end
|
|
||||||
doc = CommonMarker.render_doc(markdown, COMMONMARKER_PARSE_OPTIONS, COMMONMARKER_EXTENSIONS)
|
|
||||||
|
|
||||||
# }}}
|
|
||||||
|
|
||||||
# add header anchors {{{
|
|
||||||
|
|
||||||
header_slug_occurences = {}
|
|
||||||
doc.walk do |node|
|
|
||||||
if node.type == :header
|
|
||||||
header_slug = ""
|
|
||||||
node.walk do |child_node|
|
|
||||||
if [:text, :html, :html_inline, :code, :code_block, :footnote_reference].include?(child_node.type)
|
|
||||||
header_slug += child_node.string_content
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
header_slug.downcase!()
|
|
||||||
header_slug.strip!()
|
|
||||||
# remove special characters
|
|
||||||
header_slug.delete!("\u2000-\u206F\u2E00-\u2E7F\\\\'!\"#$%&()*+,./:;<=>?@[]\\^`{|}~’")
|
|
||||||
# remove emoji
|
|
||||||
header_slug.delete!("\u{1F600}-\u{1F6FF}")
|
|
||||||
# remove whitespace
|
|
||||||
header_slug.gsub!(/\s/, "-")
|
|
||||||
|
|
||||||
# make this slug unique
|
|
||||||
while header_slug_occurences.key?(header_slug)
|
|
||||||
occurences = header_slug_occurences[header_slug]
|
|
||||||
occurences += 1
|
|
||||||
header_slug_occurences[header_slug] = occurences
|
|
||||||
header_slug += "-" + occurences.to_s()
|
|
||||||
end
|
|
||||||
header_slug_occurences[header_slug] = 0
|
|
||||||
|
|
||||||
anchor_node = CommonMarker::Node.new(:inline_html)
|
|
||||||
anchor_node.string_content = "<a class=\"anchor\" name=\"#{header_slug}\" href=\"\##{header_slug}\"><span class=\"octicon octicon-link\"></span></a>"
|
|
||||||
node.prepend_child(anchor_node)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
# }}}
|
|
||||||
|
|
||||||
# highlight code blocks {{{
|
|
||||||
|
|
||||||
rouge_formatter = Rouge::Formatters::HTML.new()
|
|
||||||
|
|
||||||
doc.walk do |node|
|
|
||||||
if node.type == :code_block
|
|
||||||
language = node.fence_info
|
|
||||||
if not language.empty?()
|
|
||||||
source = node.string_content
|
|
||||||
lexer = Rouge::Lexer.find_fancy(language) || Rouge::Lexers::PlainText.new()
|
|
||||||
highlighted_code = rouge_formatter.format(lexer.lex(source))
|
|
||||||
|
|
||||||
new_node = CommonMarker::Node.new(:html)
|
|
||||||
new_node.string_content = "<pre><code class=\"highlight highlight-source-#{new_node.html_escape_html(language)}\">#{highlighted_code}</code></pre>"
|
|
||||||
node.insert_after(new_node)
|
|
||||||
node.delete
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
# }}}
|
|
||||||
|
|
||||||
# render HTML {{{
|
|
||||||
|
|
||||||
rendered_html = <<EOF
|
|
||||||
<!DOCTYPE html>
|
|
||||||
<html>
|
|
||||||
<head>
|
|
||||||
<meta charset="UTF-8">
|
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
||||||
<meta http-equiv="X-UA-Compatible" content="ie=edge">
|
|
||||||
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/github-markdown-css/3.0.1/github-markdown.min.css" integrity="sha256-HbgiGHMLxHZ3kkAiixyvnaaZFNjNWLYKD/QG6PWaQPc=" crossorigin="anonymous" />
|
|
||||||
<style>
|
|
||||||
html, body {
|
|
||||||
padding: 0;
|
|
||||||
margin: 0;
|
|
||||||
}
|
|
||||||
.markdown-body {
|
|
||||||
max-width: 882px;
|
|
||||||
margin: 0 auto;
|
|
||||||
padding: 32px;
|
|
||||||
}
|
|
||||||
.octicon-link {
|
|
||||||
font: normal normal 16px 'octicons-link';
|
|
||||||
line-height: 1;
|
|
||||||
-webkit-font-smoothing: antialiased;
|
|
||||||
-moz-osx-font-smoothing: grayscale;
|
|
||||||
-webkit-user-select: none;
|
|
||||||
-moz-user-select: none;
|
|
||||||
-ms-user-select: none;
|
|
||||||
user-select: none;
|
|
||||||
}
|
|
||||||
.octicon-link::before {
|
|
||||||
content: '\\f05c';
|
|
||||||
}
|
|
||||||
#{ROUGE_THEME.render()}
|
|
||||||
</style>
|
|
||||||
</head>
|
|
||||||
<body>
|
|
||||||
<article class="markdown-body">
|
|
||||||
#{doc.to_html(COMMONMARKER_RENDER_OPTIONS)}
|
|
||||||
</article>
|
|
||||||
</body>
|
|
||||||
</html>
|
|
||||||
EOF
|
|
||||||
|
|
||||||
if output_file_path.nil?()
|
|
||||||
$stdout.write(rendered_html)
|
|
||||||
else
|
|
||||||
IO.write(output_file_path, rendered_html)
|
|
||||||
end
|
|
||||||
|
|
||||||
# }}}
|
|
1
scripts/markdown2htmldoc
Symbolic link
1
scripts/markdown2htmldoc
Symbolic link
|
@ -0,0 +1 @@
|
||||||
|
../script-resources/markdown2htmldoc/main.js
|
Loading…
Reference in a new issue