├── Gemfile
├── .rubocop.yml
├── .gitignore
├── .travis.yml
├── Rakefile
├── test
├── support
│ └── new_pipeline.rb
├── test_helper.rb
└── test_jekyll_html_pipeline.rb
├── LICENSE.txt
├── jekyll-html-pipeline.gemspec
├── README.md
└── lib
└── jekyll-html-pipeline.rb
/Gemfile:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | source 'https://rubygems.org'
4 |
5 | # Specify your gem's dependencies in jekyll-html-pipeline.gemspec
6 | gemspec
7 |
--------------------------------------------------------------------------------
/.rubocop.yml:
--------------------------------------------------------------------------------
1 | inherit_gem:
2 | rubocop-standard:
3 | - config/default.yml
4 |
5 | Style/StringLiterals:
6 | Enabled: true
7 | EnforcedStyle: single_quotes
8 |
9 | RequireParentheses:
10 | Enabled: true
11 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | *.gem
2 | *.rbc
3 | .bundle
4 | .config
5 | .yardoc
6 | Gemfile.lock
7 | InstalledFiles
8 | _yardoc
9 | coverage
10 | doc/
11 | lib/bundler/man
12 | pkg
13 | rdoc
14 | spec/reports
15 | test/tmp
16 | test/version_tmp
17 | tmp
18 |
--------------------------------------------------------------------------------
/.travis.yml:
--------------------------------------------------------------------------------
1 | language: ruby
2 | rvm:
3 | - 2.3.6
4 | - 2.4.3
5 | - 2.5.0
6 |
7 | git:
8 | depth: 10
9 |
10 | sudo: false
11 | cache: bundler
12 |
13 | matrix:
14 | include:
15 | - script: bundle exec rake rubocop
16 | rvm: 2.5.0
17 |
--------------------------------------------------------------------------------
/Rakefile:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | require 'bundler/gem_tasks'
4 |
5 | task default: [:test]
6 |
7 | require 'rake/testtask'
8 | Rake::TestTask.new(:test) do |test|
9 | test.libs << 'lib' << 'test'
10 | test.pattern = 'test/**/test_*.rb'
11 | test.verbose = true
12 | end
13 |
14 | require 'rubocop/rake_task'
15 |
16 | RuboCop::RakeTask.new(:rubocop)
17 |
--------------------------------------------------------------------------------
/test/support/new_pipeline.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | require 'html/pipeline'
4 |
5 | class HelpMarkdownFilter < HTML::Pipeline::MarkdownFilter
6 | def call
7 | html = super
8 |
9 | format_callout!(html)
10 | end
11 |
12 | def format_callout!(html)
13 | html.gsub!(%r{(?:
)?{{#(tip|warning|error)}}(?:
)?}, '')
14 | html.gsub!(%r{(?:
)?{{/(tip|warning|error)}}(?:
)?}, '
')
15 | end
16 | end
17 |
--------------------------------------------------------------------------------
/LICENSE.txt:
--------------------------------------------------------------------------------
1 | The MIT License (MIT)
2 |
3 | Copyright (c) 2013 Garen Torikian
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy of
6 | this software and associated documentation files (the "Software"), to deal in
7 | the Software without restriction, including without limitation the rights to
8 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
9 | the Software, and to permit persons to whom the Software is furnished to do so,
10 | subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
17 | FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
18 | COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
19 | IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
20 | CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
21 |
--------------------------------------------------------------------------------
/test/test_helper.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | require 'rubygems'
4 |
5 | require 'jekyll'
6 | require 'liquid'
7 |
8 | require 'minitest/autorun'
9 |
10 | require 'jekyll-html-pipeline'
11 |
12 | # Send STDERR into the void to suppress program output messages
13 | # STDERR.reopen(test(?e, '/dev/null') ? '/dev/null' : 'NUL:')
14 |
15 | module Converter
16 | class HTMLPipelineTestCase < MiniTest::Test
17 | end
18 | end
19 |
20 | # module
21 | # class Test::Unit::TestCase
22 | # def dest_dir(*subdirs)
23 | # test_dir('dest', *subdirs)
24 | # end
25 |
26 | # def source_dir(*subdirs)
27 | # test_dir('source', *subdirs)
28 | # end
29 |
30 | # def clear_dest
31 | # FileUtils.rm_rf(dest_dir)
32 | # end
33 |
34 | # def test_dir(*subdirs)
35 | # File.join(File.dirname(__FILE__), *subdirs)
36 | # end
37 |
38 | # def directory_with_contents(path)
39 | # FileUtils.rm_rf(path)
40 | # FileUtils.mkdir(path)
41 | # File.open("#{path}/index.html", "w"){ |f| f.write("I was previously generated.") }
42 | # end
43 |
44 | # def capture_stdout
45 | # $old_stdout = $stdout
46 | # $stdout = StringIO.new
47 | # yield
48 | # $stdout.rewind
49 | # return $stdout.string
50 | # ensure
51 | # $stdout = $old_stdout
52 | # end
53 | # end
54 |
--------------------------------------------------------------------------------
/jekyll-html-pipeline.gemspec:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | Gem::Specification.new do |spec|
4 | spec.name = 'jekyll-html-pipeline'
5 | spec.version = '1.1.1'
6 | spec.authors = ['Garen Torikian']
7 | spec.email = ['gjtorikian@gmail.com']
8 | spec.summary = "Use GitHub's HTML::Pipeline, in Jekyll!"
9 | spec.description = "This is a custom Markdown processor for Jekyll 2.0 and above. It allows you to use GitHub's HTML::Pipeline in your Jekyll projects. "
10 | spec.homepage = 'https://github.com/gjtorikian/jekyll-html-pipeline'
11 | spec.license = 'MIT'
12 |
13 | spec.files = `git ls-files`.split($INPUT_RECORD_SEPARATOR)
14 | spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
15 | spec.test_files = spec.files.grep(%r{^(test)/})
16 | spec.require_paths = ['lib']
17 |
18 | spec.add_runtime_dependency 'jekyll', '~> 3.0'
19 | spec.add_dependency 'html-pipeline', '~> 2.8'
20 |
21 | spec.add_development_dependency 'commonmarker', '~> 0.17'
22 | spec.add_development_dependency 'gemoji', '~> 2.0'
23 | spec.add_development_dependency 'minitest', '~> 5.0'
24 | spec.add_development_dependency 'rake'
25 | spec.add_development_dependency 'rubocop'
26 | spec.add_development_dependency 'rubocop-standard'
27 | spec.add_development_dependency 'sanitize', '~> 2.0.6'
28 | end
29 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | [](https://travis-ci.org/gjtorikian/jekyll-html-pipeline)
2 |
3 | # jekyll-html-pipeline
4 |
5 | An [HTML::Pipeline](https://github.com/jch/html-pipeline), for Jekyll.
6 |
7 | ## Installation
8 |
9 | In your *_config.yml* file, add this gem:
10 |
11 | ``` yaml
12 | gems:
13 | - jekyll-html-pipeline
14 | ```
15 |
16 | ## Configuration
17 |
18 | You'll need to be running a Jekyll version after 2.0.0, which is when custom
19 | Markdown filters were introduced. In your *_config.yml* file, indicate that you
20 | want to use `html_pipeline`:
21 |
22 | ``` yaml
23 | markdown: HTMLPipeline
24 | ```
25 |
26 | Next, create an `html_pipeline` key, and indicate which filters you want to include:
27 |
28 | ``` yaml
29 | markdown: HTMLPipeline
30 | html_pipeline:
31 | filters:
32 | - "markdownfilter"
33 | - "sanitizationfilter"
34 | - "emojifilter"
35 | - "mentionfilter"
36 | ```
37 |
38 | Finally, some filters require a context object. You can define these next:
39 |
40 | ``` yaml
41 | markdown: HTMLPipeline
42 | html_pipeline:
43 | filters:
44 | - "markdownfilter"
45 | - "sanitizationfilter"
46 | - "emojifilter"
47 | - "mentionfilter"
48 | context:
49 | asset_root: "http://foo.com/icons"
50 | base_url: "https://github.com/"
51 | ```
52 |
53 | Keep in mind that [filter dependencies are not bundled](https://github.com/jch/html-pipeline#dependencies),
54 | so you'll need to add these in yourself.
55 |
56 | ## Custom filters
57 |
58 | Custom filters can be designed [the same as in HTML::Pipeline](https://github.com/jch/html-pipeline#extending).
59 |
60 | Check out [the test filter](./test/support/new_pipeline.rb) for an example. Because computers are stupid, remember that case-sensitivity matters when adding the custom filter to `filters`.
61 |
--------------------------------------------------------------------------------
/test/test_jekyll_html_pipeline.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | require 'test_helper'
4 |
5 | class HTMLPipelineTest < Converter::HTMLPipelineTestCase
6 | def setup
7 | @config = {
8 | 'html_pipeline' => {
9 | 'filters' => %w[markdownfilter sanitizationfilter emojifilter mentionfilter],
10 | 'context' => {
11 | 'asset_root' => 'http://foo.com/icons',
12 | 'base_url' => 'https://github.com/',
13 | 'commonmarker_extensions' => %w[table strikethrough tagfilter autolink]
14 | }
15 | },
16 | 'markdown' => 'HTMLPipeline'
17 | }
18 | @markdown = Jekyll::Converters::Markdown.new @config
19 | end
20 |
21 | def test_passes_regular_options
22 | assert_equal 'Some Header
', @markdown.convert('# Some Header #').strip
23 | end
24 |
25 | def test_pass_rendering_emoji
26 | assert_equal '
', @markdown.convert(':trollface:').strip
27 | end
28 |
29 | def test_pass_rendering_mentions
30 | assert_equal 'Hey, @mojombo!
', @markdown.convert('**Hey, @mojombo**!').strip
31 | end
32 |
33 | def test_fail_when_a_library_dependency_is_not_met
34 | override = @config.dup
35 | override['html_pipeline']['filters'] << 'AutolinkFilter'
36 | markdown = Jekyll::Converters::Markdown.new override
37 | assert_raises(LoadError) { markdown.convert('http://www.github.com') }
38 | end
39 |
40 | def test_fail_when_a_context_dependency_is_not_met
41 | override = @config.dup
42 | override['html_pipeline'].delete 'context'
43 | markdown = Jekyll::Converters::Markdown.new override
44 | assert_raises(ArgumentError) { markdown.convert(':trollface:') }
45 | end
46 |
47 | def test_work_for_custom_filters
48 | require 'support/new_pipeline'
49 | override = @config.dup
50 | override['html_pipeline']['filters'] = ['HelpMarkdownFilter']
51 | markdown = Jekyll::Converters::Markdown.new override
52 | text = "\n {{#tip}}\n **Tip**: Wow! \n {{/tip}}"
53 | assert_equal "
\nTip: Wow!
\n
", markdown.convert(text)
54 | end
55 | end
56 |
--------------------------------------------------------------------------------
/lib/jekyll-html-pipeline.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | module Jekyll
4 | module Converters
5 | class Markdown::HTMLPipeline
6 | def initialize(config)
7 | require 'html/pipeline'
8 | @config = config
9 | @errors = []
10 | @setup = false
11 | end
12 |
13 | def filter_key(key)
14 | key.to_s.downcase.to_sym
15 | end
16 |
17 | def filter?(filter)
18 | filter < HTML::Pipeline::Filter
19 | rescue LoadError, ArgumentError
20 | false
21 | end
22 |
23 | def symbolize_keys(hash)
24 | hash.each_with_object({}) do |(key, value), result|
25 | new_key = case key
26 | when String then key.to_sym
27 | else key
28 | end
29 | new_value = case value
30 | when Hash then symbolize_keys(value)
31 | when Array then value.map(&:to_sym)
32 | else value
33 | end
34 | result[new_key] = new_value
35 | end
36 | end
37 |
38 | def ensure_default_opts
39 | @config['html_pipeline']['filters'] ||= ['markdownfilter']
40 | @config['html_pipeline']['context'] ||= { 'gfm' => true }
41 | # symbolize strings as keys, which is what HTML::Pipeline wants
42 | @config['html_pipeline']['context'] = symbolize_keys(@config['html_pipeline']['context'])
43 | end
44 |
45 | def setup
46 | return if @setup
47 |
48 | ensure_default_opts
49 |
50 | filters = @config['html_pipeline']['filters'].map do |filter|
51 | if filter?(filter)
52 | filter
53 | else
54 | key = filter_key(filter)
55 | begin
56 | const_filter = HTML::Pipeline.constants.find { |c| c.downcase == key }
57 | # probably a custom filter
58 | if const_filter.nil?
59 | Jekyll::Converters.const_get(filter)
60 | else
61 | HTML::Pipeline.const_get(const_filter)
62 | end
63 | rescue StandardError => e
64 | raise LoadError, e
65 | end
66 | end
67 | end
68 |
69 | @parser = HTML::Pipeline.new(filters, @config['html_pipeline']['context'])
70 | @setup = true
71 | end
72 |
73 | def convert(content)
74 | setup
75 | @parser.to_html(content)
76 | end
77 | end
78 | end
79 | end
80 |
--------------------------------------------------------------------------------