├── .gitignore ├── .hound.yml ├── .rubocop.yml ├── .travis.yml ├── Gemfile ├── LICENSE.txt ├── README.md ├── Rakefile ├── bin ├── console └── setup ├── jekyll-tidy.gemspec ├── lib ├── jekyll-tidy.rb └── jekyll │ ├── tidy.rb │ └── tidy │ └── version.rb └── test ├── fixtures ├── clean.html ├── compressed.html └── dirty.html ├── helper.rb └── test_tidy.rb /.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 | *.bundle 19 | *.so 20 | *.o 21 | *.a 22 | mkmf.log 23 | -------------------------------------------------------------------------------- /.hound.yml: -------------------------------------------------------------------------------- 1 | rubocop: 2 | config_file: .rubocop.yml 3 | # version: 0.54.0 (default) 4 | # http://help.houndci.com/configuration/rubocop 5 | -------------------------------------------------------------------------------- /.rubocop.yml: -------------------------------------------------------------------------------- 1 | Documentation: 2 | Enabled: false 3 | Gemspec/OrderedDependencies: 4 | Enabled: false 5 | Layout/EmptyLineAfterMagicComment: 6 | Enabled: false 7 | Layout/IndentArray: 8 | EnforcedStyle: consistent 9 | Layout/IndentHash: 10 | EnforcedStyle: consistent 11 | Metrics/LineLength: 12 | Max: 90 13 | Metrics/MethodLength: 14 | Max: 24 15 | Naming/FileName: 16 | Enabled: false 17 | Style/BracesAroundHashParameters: 18 | Enabled: false 19 | Style/Encoding: 20 | Enabled: false 21 | Style/FrozenStringLiteralComment: 22 | Enabled: false 23 | Style/GuardClause: 24 | Enabled: false 25 | Style/HashSyntax: 26 | EnforcedStyle: hash_rockets 27 | Style/MutableConstant: 28 | Enabled: false 29 | Style/PercentLiteralDelimiters: 30 | PreferredDelimiters: 31 | "%q": "{}" 32 | "%Q": "{}" 33 | "%w": "()" 34 | "%W": "()" 35 | Style/RedundantReturn: 36 | Enabled: false 37 | Style/StringLiterals: 38 | EnforcedStyle: double_quotes 39 | Style/TrailingCommaInArrayLiteral: 40 | EnforcedStyleForMultiline: consistent_comma 41 | Style/TrailingCommaInHashLiteral: 42 | EnforcedStyleForMultiline: consistent_comma 43 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | sudo: false 2 | language: ruby 3 | rvm: 4 | - 2.4.0 5 | - 2.3.0 6 | - 2.2.0 7 | - 2.1.0 8 | - 2.0.0 9 | before_install: gem install bundler -v 1.14.3 10 | -------------------------------------------------------------------------------- /Gemfile: -------------------------------------------------------------------------------- 1 | source "https://rubygems.org" 2 | 3 | # Specify your gem's dependencies in jekyll-tidy.gemspec 4 | gemspec 5 | -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2017 Wyatt Kirby 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in 13 | all 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, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | THE SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Jekyll::Tidy 2 | 3 | [![Build Status](https://travis-ci.org/apsislabs/jekyll-tidy.svg?branch=master)](https://travis-ci.org/apsislabs/jekyll-tidy) [![Gem Version](https://badge.fury.io/rb/jekyll-tidy.svg)](https://badge.fury.io/rb/jekyll-tidy) 4 | 5 | `jekyll-tidy` is a plugin for tidying the HTML output of your Jekyll website, using either [`HtmlBeautifier`](https://github.com/threedaymonk/htmlbeautifier) or [`HtmlCompressor`](https://github.com/paolochiodi/htmlcompressor). 6 | 7 | ## Usage 8 | 9 | Usage is straightforward. 10 | 11 | ### via Bundler 12 | 13 | If you have the gem Bundler installed, simply include the gem in your `Gemfile` under the `:jekyll_plugins` group: 14 | 15 | ```ruby 16 | group :jekyll_plugins do 17 | gem "jekyll-tidy" 18 | end 19 | ``` 20 | Run `bundle install` to install the plugin and its dependencies.
21 | The plugin will be automatically loaded the next time you run `bundle exec jekyll build` or `bundle exec jekyll serve` 22 | 23 | ### via config file 24 | 25 | The plugin-gem can also be loaded via the `_config.yml`
26 | Simply add this gem to the `gems:` list (or `plugins:` list, if you're on Jekyll v3.5 and above.) 27 | 28 | ```yaml 29 | gems: 30 | - jekyll-tidy 31 | ``` 32 | 33 | ## Configuration 34 | 35 | `jekyll-tidy` takes three configuration settings: 36 | 37 | * `exclude` — an array of files to exclude from tidying. 38 | * `ignore_env` — a `JEKYLL_ENV` string on which to skip tidying entirely. 39 | * `compress_html` — a flag for whether or not to compress the HTML output 40 | 41 | ```yaml 42 | jekyll_tidy: 43 | exclude: ["index.html"] 44 | ignore_env: development 45 | compress_html: true # or false 46 | ``` 47 | 48 | ### exclude (default: []) 49 | 50 | `exclude` is an array of relative file paths that will be ignored by `jekyll-tidy`. Exclude must be set as an array, or it will cause errors. 51 | 52 | ```yaml 53 | jekyll_tidy: 54 | exclude: ["index.html"] # excludes only index.html 55 | ``` 56 | 57 | `exclude` can also take a glob of file paths.
58 | **Note:** File globs need to be wrapped with `""` when defining the array with square brackets. 59 | 60 | ```yaml 61 | jekyll_tidy: 62 | exclude: ["_posts/*.md"] # excludes all markdown files directly within the _posts directory. 63 | ``` 64 | 65 | ```yaml 66 | jekyll_tidy: 67 | exclude: 68 | - _posts/*.md # excludes all markdown files directly within the _posts directory. 69 | - _posts/**/*.md # excludes all markdown files anywhere within the _posts directory 70 | ``` 71 | 72 | ### compress_html (default: false) 73 | 74 | If `compress_html` is set to `true` then `HtmlCompressor` will be used to tidy the markup. 75 | When set to `false`, `HtmlBeautifier` will be used to tidy the markup. 76 | 77 | **Note**: if you set the `compress_html` option to `true` and your templates have inline CSS or javascript, it will not be minified. 78 | 79 | ### ignore_env (default: nil) 80 | 81 | If `ignore_env` is set to a string, we will check the `JEKYLL_ENV` environment variable and skip tidying if it matches. 82 | 83 | Setting `_config.yml` with: 84 | 85 | ```yaml 86 | ignore_env: development 87 | ``` 88 | 89 | and then running jekyll server with: 90 | 91 | ```sh 92 | $ JEKYLL_ENV=development jekyll serve 93 | ``` 94 | 95 | will skip all tidying. 96 | 97 | ## Development 98 | 99 | After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake test` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment. 100 | 101 | To install this gem onto your local machine, run `bundle exec rake install`. 102 | 103 | ## Contributing 104 | 105 | Bug reports and pull requests are welcome on GitHub at https://github.com/apsislabs/jekyll-tidy. 106 | 107 | ## License 108 | 109 | The gem is available as open source under the terms of the [MIT License](http://opensource.org/licenses/MIT). 110 | 111 | --- 112 | 113 | # Built by Apsis 114 | 115 | [![apsis](https://s3-us-west-2.amazonaws.com/apsiscdn/apsis.png)](https://www.apsis.io) 116 | 117 | `jekyll-tidy` was built by Apsis Labs. We love sharing what we build! Check out our [other libraries on Github](https://github.com/apsislabs), and if you like our work you can [hire us](https://www.apsis.io/work-with-us/) to build your vision. 118 | -------------------------------------------------------------------------------- /Rakefile: -------------------------------------------------------------------------------- 1 | require "bundler/gem_tasks" 2 | require "rake/testtask" 3 | 4 | Rake::TestTask.new(:test) do |t| 5 | t.libs << "test" 6 | t.libs << "lib" 7 | t.test_files = FileList["test/**/test_*.rb"] 8 | end 9 | 10 | task :default => :test 11 | -------------------------------------------------------------------------------- /bin/console: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | 3 | require "bundler/setup" 4 | require "jekyll/tidy" 5 | 6 | # You can add fixtures and/or initialization code here to make experimenting 7 | # with your gem easier. You can also use a different console, if you like. 8 | 9 | # (If you use this, don't forget to add pry to your Gemfile!) 10 | # require "pry" 11 | # Pry.start 12 | 13 | require "irb" 14 | IRB.start(__FILE__) 15 | -------------------------------------------------------------------------------- /bin/setup: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | set -euo pipefail 3 | IFS=$'\n\t' 4 | set -vx 5 | 6 | bundle install 7 | 8 | # Do any other automated setup that you need to do here 9 | -------------------------------------------------------------------------------- /jekyll-tidy.gemspec: -------------------------------------------------------------------------------- 1 | # coding: utf-8 2 | lib = File.expand_path("lib", __dir__) 3 | $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib) 4 | require "jekyll/tidy/version" 5 | 6 | Gem::Specification.new do |spec| 7 | spec.name = "jekyll-tidy" 8 | spec.version = Jekyll::Tidy::VERSION 9 | spec.authors = ["Wyatt Kirby"] 10 | spec.email = ["wyatt@apsis.io"] 11 | 12 | spec.summary = "Sanitize and Tidy HTML Output for Jekyll" 13 | spec.homepage = "http://www.apsis.io" 14 | spec.license = "MIT" 15 | 16 | spec.files = Dir.glob("lib/**/*").concat(%w(LICENSE.txt README.md)) 17 | spec.bindir = "exe" 18 | spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) } 19 | spec.require_paths = ["lib"] 20 | 21 | spec.add_runtime_dependency "jekyll" 22 | spec.add_runtime_dependency "htmlbeautifier" 23 | spec.add_runtime_dependency "htmlcompressor" 24 | 25 | spec.add_development_dependency "bundler", "~> 1.14" 26 | spec.add_development_dependency "rake", "~> 10.0" 27 | spec.add_development_dependency "minitest", "~> 5.0" 28 | end 29 | -------------------------------------------------------------------------------- /lib/jekyll-tidy.rb: -------------------------------------------------------------------------------- 1 | # ---------------------------------------------------------------------------- 2 | # Frozen-string-literal: true 3 | # Copyright: 2016 - MIT License 4 | # Encoding: utf-8 5 | # ---------------------------------------------------------------------------- 6 | 7 | require "jekyll/tidy" 8 | -------------------------------------------------------------------------------- /lib/jekyll/tidy.rb: -------------------------------------------------------------------------------- 1 | require "jekyll/tidy/version" 2 | 3 | require "jekyll" 4 | require "htmlbeautifier" 5 | require "htmlcompressor" 6 | 7 | module Jekyll 8 | module Tidy 9 | class << self 10 | def init(config) 11 | @jekyll_config = config 12 | end 13 | 14 | def jekyll_tidy_config 15 | @jekyll_config["jekyll_tidy"] || {} 16 | end 17 | 18 | def output_clean(output) 19 | if compress_output? 20 | return HtmlCompressor::Compressor.new.compress output 21 | else 22 | return HtmlBeautifier.beautify output 23 | end 24 | end 25 | 26 | def up(path, output) 27 | return output if ignore_env? 28 | output_clean(output) unless exclude?(path) 29 | end 30 | 31 | def exclude?(file_path) 32 | jekyll_tidy_config["exclude"].to_a.any? do |exclude_path| 33 | File.fnmatch? exclude_path, file_path, File::FNM_PATHNAME 34 | end 35 | end 36 | 37 | def compress_output? 38 | jekyll_tidy_config["compress_html"] == true 39 | end 40 | 41 | def ignore_env? 42 | Jekyll.env == jekyll_tidy_config["ignore_env"] 43 | end 44 | end 45 | end 46 | 47 | # Jekyll Hooks 48 | # ------------------------------------- 49 | 50 | Hooks.register :site, :after_init do |site| 51 | Tidy.init(site.config) 52 | end 53 | 54 | Hooks.register :documents, :post_render do |doc| 55 | doc.output = Tidy.up(doc.relative_path, doc.output) 56 | end 57 | 58 | Hooks.register :pages, :post_render do |page| 59 | page.output = Tidy.up(page.relative_path, page.output) 60 | end 61 | end 62 | -------------------------------------------------------------------------------- /lib/jekyll/tidy/version.rb: -------------------------------------------------------------------------------- 1 | module Jekyll 2 | module Tidy 3 | VERSION = "0.2.2" 4 | end 5 | end 6 | -------------------------------------------------------------------------------- /test/fixtures/clean.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | Dirty Html 4 | 5 | 6 |

This is a dirty file

7 |

It has indentation all over the place

8 |

With a mix of tabs and spaces

9 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /test/fixtures/compressed.html: -------------------------------------------------------------------------------- 1 | Dirty Html

This is a dirty file

It has indentation all over the place

With a mix of tabs and spaces

-------------------------------------------------------------------------------- /test/fixtures/dirty.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | Dirty Html 4 | 5 | 6 |

This is a dirty file

7 |

It has indentation all over the place

8 |

With a mix of tabs and spaces

9 | 10 | 11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /test/helper.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | $LOAD_PATH.unshift File.expand_path("../lib", __dir__) 4 | require "jekyll/tidy" 5 | require "minitest/autorun" 6 | 7 | def load_fixture(file) 8 | File.read("#{__dir__}/fixtures/#{file}") 9 | end 10 | -------------------------------------------------------------------------------- /test/test_tidy.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | require "helper" 4 | 5 | module Jekyll 6 | class TidyTest < Minitest::Test 7 | def setup_fixtures(overrides = {}) 8 | Tidy.init(Utils.deep_merge_hashes(Configuration::DEFAULTS, overrides)) 9 | end 10 | 11 | def test_that_it_has_a_version_number 12 | refute_nil Tidy::VERSION 13 | end 14 | 15 | def test_outputs_clean_html 16 | setup_fixtures 17 | 18 | dirty_html = load_fixture("dirty.html") 19 | expected = load_fixture("clean.html") 20 | actual = Tidy.output_clean(dirty_html) 21 | 22 | assert_equal expected, actual 23 | end 24 | 25 | def test_outputs_compressed_html 26 | setup_fixtures({ 27 | "jekyll_tidy" => { 28 | "compress_html" => true, 29 | }, 30 | }) 31 | 32 | dirty_html = load_fixture("dirty.html") 33 | expected = load_fixture("compressed.html") 34 | actual = Tidy.output_clean(dirty_html) 35 | 36 | assert_equal expected, actual 37 | end 38 | 39 | def test_matches_file_globs_correctly 40 | setup_fixtures({ 41 | "jekyll_tidy" => { 42 | "exclude" => ["_posts/*", "_test/**/*", "**/fail.html", "_only_md/*.md"], 43 | }, 44 | }) 45 | 46 | excluded_samples = %w( 47 | _posts/index.md _posts/index.html 48 | _test/foo.md _test/pages/foo.html 49 | _pages/fail.html _pages/test/fail.html fail.html 50 | _only_md/test.md 51 | ) 52 | 53 | non_excluded_samples = %w( 54 | _posts/test/index.html 55 | _pages/index.md 56 | _only_md/test.html 57 | ) 58 | 59 | excluded_samples.each do |file| 60 | assert Tidy.exclude?(file) 61 | end 62 | 63 | non_excluded_samples.each do |file| 64 | refute Tidy.exclude?(file) 65 | end 66 | end 67 | end 68 | end 69 | --------------------------------------------------------------------------------