From 77852b8838ac1a9299260fbae2add4627545a7ea Mon Sep 17 00:00:00 2001 From: Ashwin Maroli Date: Mon, 13 Nov 2017 22:02:58 +0530 Subject: [PATCH 01/49] add option to configure kramdown warning output --- docs/_docs/configuration.md | 1 + lib/jekyll/configuration.rb | 1 + lib/jekyll/converters/markdown/kramdown_parser.rb | 9 +++++---- lib/jekyll/converters/smartypants.rb | 9 +++++---- test/test_kramdown.rb | 1 + 5 files changed, 13 insertions(+), 8 deletions(-) diff --git a/docs/_docs/configuration.md b/docs/_docs/configuration.md index cd0f2058..d5d6ebd7 100644 --- a/docs/_docs/configuration.md +++ b/docs/_docs/configuration.md @@ -688,6 +688,7 @@ kramdown: input: GFM hard_wrap: false footnote_nr: 1 + log_warnings: false ``` ## Liquid Options diff --git a/lib/jekyll/configuration.rb b/lib/jekyll/configuration.rb index 087fdf78..3258ffab 100644 --- a/lib/jekyll/configuration.rb +++ b/lib/jekyll/configuration.rb @@ -80,6 +80,7 @@ module Jekyll "input" => "GFM", "hard_wrap" => false, "footnote_nr" => 1, + "log_warnings" => false, }, }.map { |k, v| [k, v.freeze] }].freeze diff --git a/lib/jekyll/converters/markdown/kramdown_parser.rb b/lib/jekyll/converters/markdown/kramdown_parser.rb index 9b6d55cc..b1015dcc 100644 --- a/lib/jekyll/converters/markdown/kramdown_parser.rb +++ b/lib/jekyll/converters/markdown/kramdown_parser.rb @@ -38,11 +38,12 @@ module Jekyll def convert(content) document = Kramdown::Document.new(content, @config) - html_output = document.to_html - document.warnings.each do |warning| - Jekyll.logger.warn "Kramdown warning:", warning + if @config["log_warnings"] + document.warnings.each do |warning| + Jekyll.logger.warn "Kramdown warning:", warning + end end - html_output + document.to_html end private diff --git a/lib/jekyll/converters/smartypants.rb b/lib/jekyll/converters/smartypants.rb index 16161e1d..4882c0e4 100644 --- a/lib/jekyll/converters/smartypants.rb +++ b/lib/jekyll/converters/smartypants.rb @@ -30,11 +30,12 @@ module Jekyll def convert(content) document = Kramdown::Document.new(content, @config) - html_output = document.to_html.chomp - document.warnings.each do |warning| - Jekyll.logger.warn "Kramdown warning:", warning + if @config["log_warnings"] + document.warnings.each do |warning| + Jekyll.logger.warn "Kramdown warning:", warning.sub(%r!^Warning:\s+!, "") + end end - html_output + document.to_html.chomp end end end diff --git a/test/test_kramdown.rb b/test/test_kramdown.rb index 80bcced3..d40a27e2 100644 --- a/test/test_kramdown.rb +++ b/test/test_kramdown.rb @@ -13,6 +13,7 @@ class TestKramdown < JekyllUnitTest "toc_levels" => "1..6", "auto_ids" => false, "footnote_nr" => 1, + "log_warnings" => true, "syntax_highlighter" => "rouge", "syntax_highlighter_opts" => { From fcb1b410e3e93f180312b7a14aa5d1e0f2591c1e Mon Sep 17 00:00:00 2001 From: Ashwin Maroli Date: Thu, 7 Dec 2017 23:44:15 +0530 Subject: [PATCH 02/49] rename log_warnings to show_warnings --- docs/_docs/configuration.md | 2 +- lib/jekyll/configuration.rb | 2 +- lib/jekyll/converters/markdown/kramdown_parser.rb | 5 +++-- lib/jekyll/converters/smartypants.rb | 5 +++-- test/test_kramdown.rb | 2 +- 5 files changed, 9 insertions(+), 7 deletions(-) diff --git a/docs/_docs/configuration.md b/docs/_docs/configuration.md index d5d6ebd7..77b74981 100644 --- a/docs/_docs/configuration.md +++ b/docs/_docs/configuration.md @@ -688,7 +688,7 @@ kramdown: input: GFM hard_wrap: false footnote_nr: 1 - log_warnings: false + show_warnings: false ``` ## Liquid Options diff --git a/lib/jekyll/configuration.rb b/lib/jekyll/configuration.rb index 3258ffab..423ab658 100644 --- a/lib/jekyll/configuration.rb +++ b/lib/jekyll/configuration.rb @@ -80,7 +80,7 @@ module Jekyll "input" => "GFM", "hard_wrap" => false, "footnote_nr" => 1, - "log_warnings" => false, + "show_warnings" => false, }, }.map { |k, v| [k, v.freeze] }].freeze diff --git a/lib/jekyll/converters/markdown/kramdown_parser.rb b/lib/jekyll/converters/markdown/kramdown_parser.rb index b1015dcc..bb03eaaa 100644 --- a/lib/jekyll/converters/markdown/kramdown_parser.rb +++ b/lib/jekyll/converters/markdown/kramdown_parser.rb @@ -38,12 +38,13 @@ module Jekyll def convert(content) document = Kramdown::Document.new(content, @config) - if @config["log_warnings"] + html_output = document.to_html + if @config["show_warnings"] document.warnings.each do |warning| Jekyll.logger.warn "Kramdown warning:", warning end end - document.to_html + html_output end private diff --git a/lib/jekyll/converters/smartypants.rb b/lib/jekyll/converters/smartypants.rb index 4882c0e4..40c0aecd 100644 --- a/lib/jekyll/converters/smartypants.rb +++ b/lib/jekyll/converters/smartypants.rb @@ -30,12 +30,13 @@ module Jekyll def convert(content) document = Kramdown::Document.new(content, @config) - if @config["log_warnings"] + html_output = document.to_html.chomp + if @config["show_warnings"] document.warnings.each do |warning| Jekyll.logger.warn "Kramdown warning:", warning.sub(%r!^Warning:\s+!, "") end end - document.to_html.chomp + html_output end end end diff --git a/test/test_kramdown.rb b/test/test_kramdown.rb index d40a27e2..f68bb17c 100644 --- a/test/test_kramdown.rb +++ b/test/test_kramdown.rb @@ -13,7 +13,7 @@ class TestKramdown < JekyllUnitTest "toc_levels" => "1..6", "auto_ids" => false, "footnote_nr" => 1, - "log_warnings" => true, + "show_warnings" => true, "syntax_highlighter" => "rouge", "syntax_highlighter_opts" => { From 2774a1aeb6048094e718ed7addf929f49742b875 Mon Sep 17 00:00:00 2001 From: Frank Taillandier Date: Mon, 13 Nov 2017 17:41:15 +0100 Subject: [PATCH 03/49] Fix Kramdown warnings --- History.markdown | 95 +++++++++---------- .../2013-07-24-jekyll-1-1-1-released.markdown | 31 +++--- 2 files changed, 59 insertions(+), 67 deletions(-) diff --git a/History.markdown b/History.markdown index 9ed4fdc8..023bb418 100644 --- a/History.markdown +++ b/History.markdown @@ -2,50 +2,50 @@ ### Documentation - * Docs: GitHub Pages instructions (#6384) + * Add formester to the list of saas form backend (#6059) + * GitHub Pages instructions (#6384) * Improve documentation for theme-gem installation (#6387) * Fix diff syntax-highlighting (#6388) + * Update instructions (#6396) * Fix code-block highlighting in docs (#6398) - * Docs: Filtering Posts with categories, tags, or other variables (#6399) + * Filtering Posts with categories, tags, or other variables (#6399) * Fixes formatting on pre-formatted text. (#6405) - * Docs: updates (#6407) - * Docs: Fix `collections_dir` example (#6408) - * Docs: Renaming duplicate of "Scenario 6" to "Scenario 7" (#6411) - * Docs: Mark `collection_dir` as unreleased (#6412) - * Docs: Fix link to SUPPORT (#6415) - * Docs: Added new tutorial to tutorials section on docs (#6406) + * Added new tutorial to tutorials section on docs (#6406) + * Updates (#6407) + * Fix `collections_dir` example (#6408) + * Renaming duplicate of "Scenario 6" to "Scenario 7" (#6411) + * Mark `collection_dir` as unreleased (#6412) + * Fix link to SUPPORT (#6415) * Fix list appearance by adding missing `ol` tag (#6421) * Explain how to override output collection index page (#6424) * Added github-cards to the list of plugins (#6425) - * add post about diversity (#6447) - * Docs: Add a note about Liquid and syntax highlighting (#6466) + * CoC violation correspondants (#6429) + * Add a note about Liquid and syntax highlighting (#6466) + * Remove `sudo` from macOS troubleshooting instructions (#6486) * Add a note on `:jekyll_plugins` group in the docs (#6488) * Updated custom-404-page.md (#6489) - * Remove `sudo` from macOS troubleshooting instructions (#6486) - * add formester to the list of saas form backend (#6059) * Fix a few minor issues in the docs (#6494) + * Add jekyll-pwa-plugin (#6533) * Remove Jekyll-Smartify from plugins directory (#6548) ### Development Fixes + * Added direct collection access to future collection item feature test (#6151) + * add failing test for non-utf8 encoding (#6339) * Upgrade to Cucumber 3.0 (#6395) * Provide a better default hash for tracking liquid stats (#6417) - * Docs: CoC violation correspondants (#6429) - * add failing test for non-utf8 encoding (#6339) * Add configuration for first-timers bot (#6431) - * Update first-timers-issue-template.md (#6472) - * Site: Rename method (#6474) * Do not linkify escaped characters as PRs in History (#6468) * Rely on jekyll-mentions for linking usernames (#6469) + * Update first-timers-issue-template.md (#6472) * Enable `Lint/RescueWithoutErrorClass` Cop (#6482) - * Added direct collection access to future collection item feature test (#6151) * Clean up Rubocop config (#6495) - * Fix #6498: Use Gem to discover the location of bundler. (#6499) + * Use Gem to discover the location of bundler (#6499) * Remove unnecessary encoding comment (#6513) * Suggest using Rubocop to automatically fix errors (#6514) * Assert raising Psych::SyntaxError when `"strict_front_matter"=>true` (#6520) - * [RuboCop] Enable `Style/UnneededCapitalW` cop (#6526) * Use Kernel#Array instead of explicit Array check (#6525) + * RuboCop: Enable `Style/UnneededCapitalW` cop (#6526) * Refactor method to reduce ABC Metric size (#6529) * Remove parentheses around arguments to raise (#6532) * Use double-quotes around gem name (#6535) @@ -54,30 +54,29 @@ ### Minor Enhancements - * Disable default layouts for Pages with a `layout: none` declaration (#6182) - * Upgrade to Rouge 3 (#6381) - * Allow the user to set collections_dir to put all collections under one subdirectory (#6331) - * Scope path glob (#6268) - * Allow plugins to modify the obsolete files. (#6502) - * .sass-cache doesn't *always* land in options['source'] (#6500) * Add Utils::Internet.connected? to determine whether host machine has internet connection. (#5870) - * Add latin mode to slugify (#6509) - * filter relative_url should keep absolute urls with scheme/authority (#6490) - * Log kramdown warnings if log level is WARN (#6522) + * Disable default layouts for Pages with a `layout: none` declaration (#6182) + * Scope path glob (#6268) + * Allow the user to set collections_dir to put all collections under one subdirectory (#6331) + * Upgrade to Rouge 3 (#6381) * Allow URL filters to work directly with documents (#6478) + * filter relative_url should keep absolute urls with scheme/authority (#6490) + * .sass-cache doesn't *always* land in `options['source']` (#6500) + * Allow plugins to modify the obsolete files. (#6502) + * Add latin mode to slugify (#6509) + * Log Kramdown warnings if log level is WARN (#6522) ### Site Enhancements - * Docs: Update instructions (#6396) - * Add special styling for code-blocks run in shell (#6389) - * Update list of files excluded from Docs site (#6457) - * Update site History (#6460) - * Site: Add default twitter card image (#6476) - * Update normalize.css to v7.0.0 (#6491) - * add jekyll-pwa-plugin (#6533) - * [ImgBot] optimizes images (#6519) - * Site: Back to original main navigation (#6544) - * Style mobile-docs select element (#6545) + * (#6389) Add special styling for code-blocks run in shell + * (#6447) Add post about diversity + * (#6457) Update list of files excluded from Docs site + * (#6460) Update site History + * (#6476) Add default twitter card image + * (#6491) Update normalize.css to v7.0.0 + * (#6519) Optimize images + * (#6544) Site: Back to original main navigation + * (#6545) Style mobile-docs select element ### Bug Fixes @@ -213,7 +212,7 @@ * added BibSonomy plugin (#6143) * add plugins for multiple page pagination (#6055) * Update minimum Ruby version in installation.md (#6164) - * [docs] Add information about finding a collection in `site.collections` (#6165) + * Add information about finding a collection in `site.collections` (#6165) * Add `{% raw %}` to Liquid example on site (#6179) * Added improved Pug plugin - removed 404 Jade plugin (#6174) * Linking the link (#6210) @@ -318,7 +317,7 @@ ### Development Fixes - * [Rubocop] add missing comma (#5835) + * Rubocop: add missing comma (#5835) * Appease classifier-reborn (#5934) * Allow releases & development on `*-stable` branches (#5926) * Add script/backport-pr (#5925) @@ -355,7 +354,7 @@ ### Bug Fixes * Exclude Gemfile by default (#5860) - * Convertible#validate_permalink!: ensure the return value of data["permalink"] is a string before asking if it is empty (#5878) + * Convertible#validate_permalink!: ensure the return value of `data["permalink"]` is a string before asking if it is empty (#5878) * Allow abbreviated post dates (#5920) * Remove dependency on include from default about.md (#5903) * Allow colons in `uri_escape` filter (#5957) @@ -416,7 +415,7 @@ * Switch to `https` when possible. (#5611) * Update `_font-awesome.scss` to move .woff file before .ttf (#5614) * Update documentation on updating FontAwesome Iconset (#5655) - * [site] Use defaults for docs and news-items (#5744) + * Use defaults for docs and news-items (#5744) * Sort gems in `docs/_config.yml` (#5746) * Add missing class (#5791) * Improve template docs (#5694) @@ -477,7 +476,7 @@ * Update quickstart.md (#5758) * Correct minor typo (#5764) * Fix a markdown link to look properly on the web (#5769) - * [docs] Info about the help command usage (#5312) + * Info about the help command usage (#5312) * Add missing merge labels for jekyllbot (#5753) * Fix broken links in documentation (#5736) * Docs: add `match_regex` and `replace_regex` filters (#5799) @@ -609,10 +608,10 @@ * Site: exclude README.md and .gitignore (#5304) * Add link to Staticman (#5224) * Update url for OpenShift (#5320) - * [docs] add help for missing static_file e.g. on heroku (#5334) + * Add help for missing static_file e.g. on heroku (#5334) * Add a line about updating theme-gems in the docs (#5318) * Explain how to copy a theme's files (#5335) - * [docs] .md as default extension in examples (#5316) + * .md as default extension in examples (#5316) * Fix small typo in docs (#5347) * Add missing period to sentence in first paragraph. (#5372) * added jekyll-spotify plugin (#5369) @@ -621,7 +620,7 @@ * Add documentation for `relative_url` and `absolute_url` (#5405) * Bugfix on logo in JSON-LD (#5421) * Fix Travis.ci documentation (#5413) - * [docs] Update documentation regarding `bundle install` after `jekyll new` (#5428) + * Update documentation regarding `bundle install` after `jekyll new` (#5428) * Replace classic box-sizing reset with inheritance reset (#5411) * Update Wikipedia YAML list link (#5452) * Add Jekyll 3.3 release post (#5442) @@ -925,7 +924,7 @@ * Fix broken links to the Code of Conduct (#4436) * Upgrade notes: mention trailing slash in permalink; fixes #4440 (#4455) * Add hooks to the plugin categories toc (#4463) - * [add note] Jekyll 3 requires newer version of Ruby. (#4461) + * Jekyll 3 requires newer version of Ruby. (#4461) * Fix typo in upgrading docs (#4473) * Add note about upgrading documentation on jekyllrb.com/help/ (#4484) * Update Rake link (#4496) @@ -993,7 +992,7 @@ * Fix deep_merge_hashes! handling of drops and hashes (#4359) * Page should respect output extension of its permalink (#4373) * Disable auto-regeneration when running server detached (#4376) - * Drop#[]: only use public_send for keys in the content_methods array (#4388) + * Drop#: only use public_send for keys in the content_methods array (#4388) * Extract title from filename successfully when no date. (#4195) ### Development Fixes diff --git a/docs/_posts/2013-07-24-jekyll-1-1-1-released.markdown b/docs/_posts/2013-07-24-jekyll-1-1-1-released.markdown index 90b81847..acbd58ad 100644 --- a/docs/_posts/2013-07-24-jekyll-1-1-1-released.markdown +++ b/docs/_posts/2013-07-24-jekyll-1-1-1-released.markdown @@ -6,25 +6,18 @@ version: 1.1.1 categories: [release] --- +Coming just 10 days after the release of v1.1.0, v1.1.1 is out with a patch for +the nasty excerpt inception bug ([#1339]({{ site.repository }}/issues/1339)) and +non-zero exit codes for invalid commands ([#1338]({{ site.repository +}}/issues/1338)). -Coming just 10 days after the release of v1.1.0, v1.1.1 is out with a patch for the nasty -excerpt inception bug ([#1339][]) and non-zero exit codes for invalid commands -([#1338][]). +To all those affected by the [strange excerpt bug in v1.1.0]({{ site.repository +}}/issues/1321), I'm sorry. I think we have it all patched up and it should be +deployed to [GitHub Pages](https://pages.github.com/) in the next couple weeks. +Thank you for your patience! -To all those affected by the [strange excerpt bug in v1.1.0][#1321], I'm sorry. I think we -have it all patched up and it should be deployed to [GitHub Pages][gh_pages] in the next -couple weeks. Thank you for your patience! +If you're checking out v1.1.x for the first time, definitely check out [what +shipped with v1.1.0!]({{ site.repository }}/releases/tag/v1.1.0) -If you're checking out v1.1.x for the first time, definitely check out [what shipped with -v1.1.0!][v1_1_0] - -See the [GitHub Release][] page for more a more detailed changelog for this release. - -{% assign issue_numbers = "1339|1338|1321" | split: "|" %} -{% for issue in issue_numbers %} -[#{{ issue }}]: {{ site.repository }}/issues/{{ issue }} -{% endfor %} - -[GitHub Release]: {{ site.repository }}/releases/tag/v1.1.1 -[gh_pages]: https://pages.github.com/ -[v1_1_0]: {{ site.repository }}/releases/tag/v1.1.0 +See the [GitHub Release]({{ site.repository }}/releases/tag/v1.1.1) page for +more a more detailed changelog for this release. From 57ec30d6ef20dc21b9c162a56314325ec4d3aa20 Mon Sep 17 00:00:00 2001 From: Frank Taillandier Date: Mon, 13 Nov 2017 18:21:04 +0100 Subject: [PATCH 04/49] Site: Kramdown and Rouge are set by default --- docs/_config.yml | 2 -- 1 file changed, 2 deletions(-) diff --git a/docs/_config.yml b/docs/_config.yml index d6e4443c..8ce43989 100644 --- a/docs/_config.yml +++ b/docs/_config.yml @@ -1,5 +1,3 @@ -markdown: kramdown -highlighter: rouge sass: style: compressed From a74853501aed84ba1d7fb2d77d8312790cea28df Mon Sep 17 00:00:00 2001 From: Doug Beney Date: Mon, 13 Nov 2017 14:49:39 -0500 Subject: [PATCH 05/49] Update plugins.md (#6555) Merge pull request 6555 --- docs/_docs/plugins.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/_docs/plugins.md b/docs/_docs/plugins.md index 7564252f..61d48476 100644 --- a/docs/_docs/plugins.md +++ b/docs/_docs/plugins.md @@ -785,7 +785,7 @@ LESS.js files during generation. #### Converters -- [Pug plugin by Doug Beney](https://github.com/DougBeney/jekyll-pug): Pug (previously Jade) converter for Jekyll. +- [Pug plugin by Doug Beney](http://jekyll-pug.dougie.io): Use the popular Pug (previously Jade) templating language in Jekyll. Complete with caching, includes support, and much more. - [Textile converter](https://github.com/jekyll/jekyll-textile-converter): Convert `.textile` files into HTML. Also includes the `textilize` Liquid filter. - [Slim plugin](https://github.com/slim-template/jekyll-slim): Slim converter and includes for Jekyll with support for Liquid tags. - [HAML plugin by Sam Z](https://gist.github.com/517556): HAML converter for Jekyll. From d8d6360107bbe9943709243ae7cd76a66e0f1b20 Mon Sep 17 00:00:00 2001 From: jekyllbot Date: Mon, 13 Nov 2017 14:49:41 -0500 Subject: [PATCH 06/49] Update history to reflect merge of #6555 [ci skip] --- History.markdown | 1 + 1 file changed, 1 insertion(+) diff --git a/History.markdown b/History.markdown index 023bb418..966e9040 100644 --- a/History.markdown +++ b/History.markdown @@ -27,6 +27,7 @@ * Fix a few minor issues in the docs (#6494) * Add jekyll-pwa-plugin (#6533) * Remove Jekyll-Smartify from plugins directory (#6548) + * Updated Jekyll-Pug listing to include official website (#6555) ### Development Fixes From 32d1b43fc030090ff49fa776dc5b20b6e194c20d Mon Sep 17 00:00:00 2001 From: Aaron D Borden Date: Mon, 13 Nov 2017 13:30:23 -0800 Subject: [PATCH 07/49] Add json extension to list of directory indices (#6550) Merge pull request 6550 --- lib/jekyll/commands/serve.rb | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/lib/jekyll/commands/serve.rb b/lib/jekyll/commands/serve.rb index a89d152e..9861d2c6 100644 --- a/lib/jekyll/commands/serve.rb +++ b/lib/jekyll/commands/serve.rb @@ -17,6 +17,15 @@ module Jekyll "Skips the initial site build which occurs before the server is started.",], }.freeze + DIRECTORY_INDEX = %w( + index.htm + index.html + index.rhtml + index.cgi + index.xml + index.json + ).freeze + # def init_with_program(prog) @@ -85,13 +94,7 @@ module Jekyll :StartCallback => start_callback(opts["detach"]), :BindAddress => opts["host"], :Port => opts["port"], - :DirectoryIndex => %w( - index.htm - index.html - index.rhtml - index.cgi - index.xml - ), + :DirectoryIndex => DIRECTORY_INDEX, } opts[:DirectoryIndex] = [] if opts[:JekyllOptions]["show_dir_listing"] From c2ee73ef8df4704028e6419306f766e1eec02007 Mon Sep 17 00:00:00 2001 From: jekyllbot Date: Mon, 13 Nov 2017 16:30:25 -0500 Subject: [PATCH 08/49] Update history to reflect merge of #6550 [ci skip] --- History.markdown | 1 + 1 file changed, 1 insertion(+) diff --git a/History.markdown b/History.markdown index 966e9040..1e421c38 100644 --- a/History.markdown +++ b/History.markdown @@ -66,6 +66,7 @@ * Allow plugins to modify the obsolete files. (#6502) * Add latin mode to slugify (#6509) * Log Kramdown warnings if log level is WARN (#6522) + * Add json extension to list of directory indices (#6550) ### Site Enhancements From 7c72e62552af1a4d1e597f0db60b5caab648d6b0 Mon Sep 17 00:00:00 2001 From: jekyllbot Date: Wed, 15 Nov 2017 10:19:37 -0500 Subject: [PATCH 09/49] Site: Search with @Algolia DocSearch (#6557) Merge pull request 6557 --- docs/_includes/header.html | 1 + docs/_includes/search/input.html | 1 + docs/_includes/search/script.html | 9 ++++++ docs/_includes/top.html | 1 + docs/_layouts/default.html | 2 +- docs/_sass/_docsearch.scss | 50 +++++++++++++++++++++++++++++++ docs/_sass/_style.scss | 1 + docs/css/screen.scss | 1 + 8 files changed, 65 insertions(+), 1 deletion(-) create mode 100644 docs/_includes/search/input.html create mode 100644 docs/_includes/search/script.html create mode 100644 docs/_sass/_docsearch.scss diff --git a/docs/_includes/header.html b/docs/_includes/header.html index 2577a807..cca35e89 100644 --- a/docs/_includes/header.html +++ b/docs/_includes/header.html @@ -13,6 +13,7 @@ diff --git a/docs/_includes/search/input.html b/docs/_includes/search/input.html new file mode 100644 index 00000000..729f5cab --- /dev/null +++ b/docs/_includes/search/input.html @@ -0,0 +1 @@ + diff --git a/docs/_includes/search/script.html b/docs/_includes/search/script.html new file mode 100644 index 00000000..f770c63a --- /dev/null +++ b/docs/_includes/search/script.html @@ -0,0 +1,9 @@ + + diff --git a/docs/_includes/top.html b/docs/_includes/top.html index 155ffbca..b67648d9 100644 --- a/docs/_includes/top.html +++ b/docs/_includes/top.html @@ -7,6 +7,7 @@ {% feed_meta %} + {% seo %} diff --git a/docs/_layouts/default.html b/docs/_layouts/default.html index 77ebd5f7..8e9574ad 100644 --- a/docs/_layouts/default.html +++ b/docs/_layouts/default.html @@ -8,6 +8,6 @@ {% include footer.html %} {% include anchor_links.html %} {% include analytics.html %} - + {% include search/script.html %} diff --git a/docs/_sass/_docsearch.scss b/docs/_sass/_docsearch.scss new file mode 100644 index 00000000..bebe3125 --- /dev/null +++ b/docs/_sass/_docsearch.scss @@ -0,0 +1,50 @@ +.searchbox { + .searchbox__input { + padding: 5px 5px 5px 29px; + border: none; + border-radius: 5px; + color: #444; + + &::-webkit-input-placeholder { + color: #aaa; + } + + &:-ms-input-placeholder { + color: #aaa; + } + + &::placeholder { + color: #aaa; + } + } +} + +.algolia-autocomplete { + .ds-dropdown-menu { + font-size: 1rem; + text-shadow: none; + + .ds-suggestion.ds-cursor .algolia-docsearch-suggestion:not(.suggestion-layout-simple) .algolia-docsearch-suggestion--content { + background-color: rgba(221, 221, 221, 0.5); + } + } + + .algolia-docsearch-suggestion--category-header { + background-color: #444; + color: #ddd; + padding: 0.35em; + } + + .algolia-docsearch-suggestion--subcategory-column { + color: #444; + } + + .algolia-docsearch-suggestion--highlight { + background-color: #fc0; + color: #222; + } + + .algolia-docsearch-suggestion--text .algolia-docsearch-suggestion--highlight { + box-shadow: inset 0 -2px 0 0 #fc0; + } +} diff --git a/docs/_sass/_style.scss b/docs/_sass/_style.scss index 38276c8f..fe84083a 100644 --- a/docs/_sass/_style.scss +++ b/docs/_sass/_style.scss @@ -62,6 +62,7 @@ nav { padding: 0; margin: 0; white-space: nowrap; + display: inline-block; } li { display: inline-block; } diff --git a/docs/css/screen.scss b/docs/css/screen.scss index 2eff7f89..4bc6bec3 100644 --- a/docs/css/screen.scss +++ b/docs/css/screen.scss @@ -7,3 +7,4 @@ @import "pygments"; @import "font-awesome"; @import "style"; +@import "docsearch"; From 977cfb0ac09b2beece3be73015a1a42e53a56481 Mon Sep 17 00:00:00 2001 From: jekyllbot Date: Wed, 15 Nov 2017 10:19:40 -0500 Subject: [PATCH 10/49] Update history to reflect merge of #6557 [ci skip] --- History.markdown | 1 + 1 file changed, 1 insertion(+) diff --git a/History.markdown b/History.markdown index 1e421c38..595e0258 100644 --- a/History.markdown +++ b/History.markdown @@ -79,6 +79,7 @@ * (#6519) Optimize images * (#6544) Site: Back to original main navigation * (#6545) Style mobile-docs select element + * Site: Search with DocSearch by @Algolia (#6557) ### Bug Fixes From 22cba713dcf29e592a70b892e8fa512ad5f8c141 Mon Sep 17 00:00:00 2001 From: ashmaroli Date: Thu, 16 Nov 2017 00:55:16 +0530 Subject: [PATCH 11/49] Bump JRuby version in Travis config (#6561) Merge pull request 6561 --- .travis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index 2cfe51b7..1da8963a 100644 --- a/.travis.yml +++ b/.travis.yml @@ -9,7 +9,7 @@ rvm: - &ruby2 2.3.5 - &ruby3 2.2.8 - &ruby4 2.1.10 - - &jruby jruby-9.1.13.0 + - &jruby jruby-9.1.14.0 matrix: include: From e4888f2e5220b529147273338449feca20341b9f Mon Sep 17 00:00:00 2001 From: jekyllbot Date: Wed, 15 Nov 2017 14:25:18 -0500 Subject: [PATCH 12/49] Update history to reflect merge of #6561 [ci skip] --- History.markdown | 1 + 1 file changed, 1 insertion(+) diff --git a/History.markdown b/History.markdown index 595e0258..ca4f2e6d 100644 --- a/History.markdown +++ b/History.markdown @@ -52,6 +52,7 @@ * Use double-quotes around gem name (#6535) * Dependencies: upgrade to toml 0.2.0 (#6541) * Lock to cucumber 3.0.1 on Ruby 2.1 (#6546) + * Bump JRuby version in Travis config (#6561) ### Minor Enhancements From 3f8cd30d4dec40342f6a1926e962755f349e6baa Mon Sep 17 00:00:00 2001 From: Frank Taillandier Date: Wed, 15 Nov 2017 21:28:25 +0100 Subject: [PATCH 13/49] Site: Display search only on large resolutions --- docs/_sass/_style.scss | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/docs/_sass/_style.scss b/docs/_sass/_style.scss index fe84083a..8ade52e6 100644 --- a/docs/_sass/_style.scss +++ b/docs/_sass/_style.scss @@ -172,6 +172,10 @@ h6:hover .header-link { } } +@media (max-width: 950px) { + .searchbox { display: none;} +} + /* Footer */ footer { From 68935f1bda79875c3da1a62dbdee2a553e6d741f Mon Sep 17 00:00:00 2001 From: Frank Taillandier Date: Thu, 16 Nov 2017 22:20:38 +0100 Subject: [PATCH 14/49] Reformat --- History.markdown | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/History.markdown b/History.markdown index ca4f2e6d..c92be860 100644 --- a/History.markdown +++ b/History.markdown @@ -71,16 +71,16 @@ ### Site Enhancements - * (#6389) Add special styling for code-blocks run in shell - * (#6447) Add post about diversity - * (#6457) Update list of files excluded from Docs site - * (#6460) Update site History - * (#6476) Add default twitter card image - * (#6491) Update normalize.css to v7.0.0 - * (#6519) Optimize images - * (#6544) Site: Back to original main navigation - * (#6545) Style mobile-docs select element - * Site: Search with DocSearch by @Algolia (#6557) + * Add special styling for code-blocks run in shell (#6389) + * Add post about diversity (#6447) + * Update list of files excluded from Docs site (#6457) + * Update site History (#6460) + * Add default twitter card image (#6476) + * Update normalize.css to v7.0.0 (#6491) + * Optimize images (#6519) + * Back to original main navigation (#6544) + * Styles: mobile-docs select element (#6545) + * Search with DocSearch by @Algolia (#6557) ### Bug Fixes From 0fdb39bbd6925d63966c155ed4665c0e2f67886a Mon Sep 17 00:00:00 2001 From: jekyllbot Date: Fri, 17 Nov 2017 16:36:11 -0500 Subject: [PATCH 15/49] Avoid block parser warning in SmartyPants (#6565) Merge pull request 6565 --- lib/jekyll/converters/smartypants.rb | 7 ++++++- test/test_filters.rb | 10 ++++++++++ 2 files changed, 16 insertions(+), 1 deletion(-) diff --git a/lib/jekyll/converters/smartypants.rb b/lib/jekyll/converters/smartypants.rb index 40c0aecd..a3c3c156 100644 --- a/lib/jekyll/converters/smartypants.rb +++ b/lib/jekyll/converters/smartypants.rb @@ -3,9 +3,14 @@ class Kramdown::Parser::SmartyPants < Kramdown::Parser::Kramdown def initialize(source, options) super - @block_parsers = [:block_html] + @block_parsers = [:block_html, :content] @span_parsers = [:smart_quotes, :html_entity, :typographic_syms, :span_html] end + + def parse_content + add_text @src.scan(%r!\A.*\n!) + end + define_parser(:content, %r!\A!) end module Jekyll diff --git a/test/test_filters.rb b/test/test_filters.rb index a56df6da..afedf746 100644 --- a/test/test_filters.rb +++ b/test/test_filters.rb @@ -100,6 +100,10 @@ class TestFilters < JekyllUnitTest assert_equal "“", @filter.smartify("“") end + should "convert multiple lines" do + assert_equal "…\n…", @filter.smartify("...\n...") + end + should "allow raw HTML passthrough" do assert_equal( "Span HTML is not escaped", @@ -123,6 +127,12 @@ class TestFilters < JekyllUnitTest @filter.smartify(404) ) end + + should "not output any warnings" do + assert_empty( + capture_output { @filter.smartify("Test") } + ) + end end should "sassify with simple string" do From 13911961b9b30792ceda92f48a0847ec8dabbce2 Mon Sep 17 00:00:00 2001 From: jekyllbot Date: Fri, 17 Nov 2017 16:36:12 -0500 Subject: [PATCH 16/49] Update history to reflect merge of #6565 [ci skip] --- History.markdown | 1 + 1 file changed, 1 insertion(+) diff --git a/History.markdown b/History.markdown index c92be860..e7f21d83 100644 --- a/History.markdown +++ b/History.markdown @@ -85,6 +85,7 @@ ### Bug Fixes * Raise when theme root directory is not available (#6455) + * Avoid block parser warning in SmartyPants (#6565) ## 3.6.2 / 2017-10-21 From 0f354704804673331138706d8e1db5975733e665 Mon Sep 17 00:00:00 2001 From: ashmaroli Date: Mon, 20 Nov 2017 00:30:38 +0530 Subject: [PATCH 17/49] Site header redesign (#6567) Merge pull request 6567 --- docs/_includes/header.html | 26 +++++++++---- docs/_includes/mobile-nav-items.html | 17 ++++++++ docs/_includes/primary-nav-items.html | 3 -- docs/_sass/_docsearch.scss | 18 +++++++-- docs/_sass/_style.scss | 56 ++++++++++++++++++++------- 5 files changed, 91 insertions(+), 29 deletions(-) create mode 100644 docs/_includes/mobile-nav-items.html diff --git a/docs/_includes/header.html b/docs/_includes/header.html index cca35e89..7cdcb970 100644 --- a/docs/_includes/header.html +++ b/docs/_includes/header.html @@ -1,19 +1,31 @@
-
-
diff --git a/docs/_includes/mobile-nav-items.html b/docs/_includes/mobile-nav-items.html new file mode 100644 index 00000000..12fe4016 --- /dev/null +++ b/docs/_includes/mobile-nav-items.html @@ -0,0 +1,17 @@ + diff --git a/docs/_includes/primary-nav-items.html b/docs/_includes/primary-nav-items.html index 12fe4016..7609bd7e 100644 --- a/docs/_includes/primary-nav-items.html +++ b/docs/_includes/primary-nav-items.html @@ -11,7 +11,4 @@
  • Help
  • -
  • - GitHub -
  • diff --git a/docs/_sass/_docsearch.scss b/docs/_sass/_docsearch.scss index bebe3125..e984edea 100644 --- a/docs/_sass/_docsearch.scss +++ b/docs/_sass/_docsearch.scss @@ -1,24 +1,34 @@ .searchbox { + padding-top: 1px; .searchbox__input { - padding: 5px 5px 5px 29px; + padding: 6px 5px 5px 29px; + font-size: 0.75em; border: none; border-radius: 5px; - color: #444; + color: #555; + background-color: #333 !important; + box-shadow: 0 0 1px 0 #555; &::-webkit-input-placeholder { color: #aaa; } - &:-ms-input-placeholder { color: #aaa; } - &::placeholder { color: #aaa; } + + &:focus, &:active { + color: #eaeaea; + background-color: #252525 !important; + } } } +.searchbox__submit svg { fill: #fc0 } +.searchbox__reset svg { fill: #999 } + .algolia-autocomplete { .ds-dropdown-menu { font-size: 1rem; diff --git a/docs/_sass/_style.scss b/docs/_sass/_style.scss index 8ade52e6..52e6e8e3 100644 --- a/docs/_sass/_style.scss +++ b/docs/_sass/_style.scss @@ -14,7 +14,6 @@ body { font: 300 21px Lato, 'Helvetica Neue', Helvetica, Arial, sans-serif; color: #ddd; background-color: #333; - border-top: 5px solid #fc0; @include box-shadow(inset 0 3px 30px rgba(0,0,0,.3)); text-shadow: 0 1px 3px rgba(0,0,0,.5); -webkit-font-feature-settings: "kern" 1; @@ -50,13 +49,28 @@ footer { /* Header */ header { + padding: 15px; + background: darken(#333, 3%); h1, nav { display: inline-block; } + .flexbox { + display: flex; + height: 50px; + + & > * { margin: auto } + } + + .logo { + display: block; + img { margin-top: -7px } + } + + .search .svg-icons { display: none } } -nav { +nav, .meta { ul { padding: 0; @@ -68,16 +82,20 @@ nav { li { display: inline-block; } } -.main-nav { - margin-top: 52px; +.meta ul { + margin-left: 10px; + + li { vertical-align: middle; } +} + +.main-nav, .meta { li { - margin-right: 10px; a { @include border-radius(5px); font-weight: 900; - font-size: 14px; + font-size: 0.75em; padding: 0.5em 1em; text-shadow: none; text-transform: uppercase; @@ -101,7 +119,6 @@ nav { } } } - .mobile-nav { padding: 0 5px; @@ -118,9 +135,9 @@ nav { color: #fc0; text-align: center; text-transform: uppercase; - font-size: 14px; + font-size: 0.625em; font-weight: 900; - padding: 5px; + padding: 10px 5px; @include border-radius(5px); } @@ -160,20 +177,29 @@ h6:hover .header-link { opacity: 1; } -@media (max-width: 768px) { - .main-nav ul { - text-align: right; +@media (max-width: 568px) { + header { padding-bottom: 0 } +} +@media (max-width: 580px) { + header { + .flexbox { height: auto } + .logo img { margin-top: 0 } } } +@media (max-width: 699px) { + .searchbox { display: none } +} +@media (max-width: 768px) { + .main-nav ul { text-align: right } +} @media (max-width: 830px) { .main-nav { .show-on-mobiles { display: inline; } .hide-on-mobiles { display: none; } } } - -@media (max-width: 950px) { - .searchbox { display: none;} +@media (max-width: 890px) { + .meta { display: none; } } /* Footer */ From f85a039633628b59baf01aede9940df51e4b1bf0 Mon Sep 17 00:00:00 2001 From: jekyllbot Date: Sun, 19 Nov 2017 14:00:39 -0500 Subject: [PATCH 18/49] Update history to reflect merge of #6567 [ci skip] --- History.markdown | 1 + 1 file changed, 1 insertion(+) diff --git a/History.markdown b/History.markdown index e7f21d83..b71e88bc 100644 --- a/History.markdown +++ b/History.markdown @@ -81,6 +81,7 @@ * Back to original main navigation (#6544) * Styles: mobile-docs select element (#6545) * Search with DocSearch by @Algolia (#6557) + * Site header redesign (#6567) ### Bug Fixes From 0f249eec9fc507db920f2e3b14cb94c707835d9f Mon Sep 17 00:00:00 2001 From: ashmaroli Date: Mon, 20 Nov 2017 18:36:47 +0530 Subject: [PATCH 19/49] move logo above navigation on small screens (#6570) Merge pull request 6570 --- docs/_includes/header.html | 6 +++--- docs/_sass/_style.scss | 3 --- 2 files changed, 3 insertions(+), 6 deletions(-) diff --git a/docs/_includes/header.html b/docs/_includes/header.html index 7cdcb970..b289550d 100644 --- a/docs/_includes/header.html +++ b/docs/_includes/header.html @@ -1,7 +1,4 @@
    -

    @@ -28,4 +25,7 @@

    +
    diff --git a/docs/_sass/_style.scss b/docs/_sass/_style.scss index 52e6e8e3..fb185972 100644 --- a/docs/_sass/_style.scss +++ b/docs/_sass/_style.scss @@ -177,9 +177,6 @@ h6:hover .header-link { opacity: 1; } -@media (max-width: 568px) { - header { padding-bottom: 0 } -} @media (max-width: 580px) { header { .flexbox { height: auto } From c2195b01181fd54874bc2eb5877231af3b437075 Mon Sep 17 00:00:00 2001 From: jekyllbot Date: Mon, 20 Nov 2017 08:06:48 -0500 Subject: [PATCH 20/49] Update history to reflect merge of #6570 [ci skip] --- History.markdown | 1 + 1 file changed, 1 insertion(+) diff --git a/History.markdown b/History.markdown index b71e88bc..94da8dcd 100644 --- a/History.markdown +++ b/History.markdown @@ -82,6 +82,7 @@ * Styles: mobile-docs select element (#6545) * Search with DocSearch by @Algolia (#6557) * Site header redesign (#6567) + * Move logo above site navigation on small screens (#6570) ### Bug Fixes From 6b003a443887771eced77d3d6011f08bf6aad71d Mon Sep 17 00:00:00 2001 From: ashmaroli Date: Wed, 22 Nov 2017 00:58:08 +0530 Subject: [PATCH 21/49] fail gracefully if "sass" gem cannot be loaded (#6573) Merge pull request 6573 --- lib/jekyll/theme.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/jekyll/theme.rb b/lib/jekyll/theme.rb index cf9829ea..edd041ee 100644 --- a/lib/jekyll/theme.rb +++ b/lib/jekyll/theme.rb @@ -38,7 +38,7 @@ module Jekyll def configure_sass return unless sass_path - require "sass" + Jekyll::External.require_with_graceful_fail "sass" Sass.load_paths << sass_path end From 398e7d50894a2a20975342ed27ffc6bbd0a1ec12 Mon Sep 17 00:00:00 2001 From: jekyllbot Date: Tue, 21 Nov 2017 14:28:10 -0500 Subject: [PATCH 22/49] Update history to reflect merge of #6573 [ci skip] --- History.markdown | 1 + 1 file changed, 1 insertion(+) diff --git a/History.markdown b/History.markdown index 94da8dcd..85ca98c6 100644 --- a/History.markdown +++ b/History.markdown @@ -88,6 +88,7 @@ * Raise when theme root directory is not available (#6455) * Avoid block parser warning in SmartyPants (#6565) + * Fail gracefully if "sass" gem cannot be loaded (#6573) ## 3.6.2 / 2017-10-21 From f906a3cb597c049b7febb9767bed2802d47a659d Mon Sep 17 00:00:00 2001 From: Jonathan Hooper Date: Wed, 22 Nov 2017 08:37:18 -0600 Subject: [PATCH 23/49] Rescue from Psych::SyntaxError instead of SyntaxError after parsing YAML (#5828) Merge pull request 5828 --- lib/jekyll/convertible.rb | 2 +- lib/jekyll/document.rb | 4 ++-- test/test_convertible.rb | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/lib/jekyll/convertible.rb b/lib/jekyll/convertible.rb index 1236d9d1..d3749a1a 100644 --- a/lib/jekyll/convertible.rb +++ b/lib/jekyll/convertible.rb @@ -46,7 +46,7 @@ module Jekyll self.content = $POSTMATCH self.data = SafeYAML.load(Regexp.last_match(1)) end - rescue SyntaxError => e + rescue Psych::SyntaxError => e Jekyll.logger.warn "YAML Exception reading #{filename}: #{e.message}" raise e if self.site.config["strict_front_matter"] rescue StandardError => e diff --git a/lib/jekyll/document.rb b/lib/jekyll/document.rb index 5bb25566..1eb84186 100644 --- a/lib/jekyll/document.rb +++ b/lib/jekyll/document.rb @@ -266,7 +266,7 @@ module Jekyll merge_defaults read_content(opts) read_post_data - rescue SyntaxError, StandardError, Errors::FatalException => e + rescue StandardError => e handle_read_error(e) end end @@ -463,7 +463,7 @@ module Jekyll private def handle_read_error(error) - if error.is_a? SyntaxError + if error.is_a? Psych::SyntaxError Jekyll.logger.error "Error:", "YAML Exception reading #{path}: #{error.message}" else Jekyll.logger.error "Error:", "could not read file #{path}: #{error.message}" diff --git a/test/test_convertible.rb b/test/test_convertible.rb index f05cb4fb..5b73e252 100644 --- a/test/test_convertible.rb +++ b/test/test_convertible.rb @@ -31,7 +31,7 @@ class TestConvertible < JekyllUnitTest ret = @convertible.read_yaml(@base, name) assert_equal({}, ret) end - assert_match(%r!YAML Exception|syntax error|Error reading file!, out) + assert_match(%r!YAML Exception!, out) assert_match(%r!#{File.join(@base, name)}!, out) end From a25fd7233435010ea75dd66485a0217548e0663f Mon Sep 17 00:00:00 2001 From: jekyllbot Date: Wed, 22 Nov 2017 09:37:20 -0500 Subject: [PATCH 24/49] Update history to reflect merge of #5828 [ci skip] --- History.markdown | 1 + 1 file changed, 1 insertion(+) diff --git a/History.markdown b/History.markdown index 85ca98c6..ba659ed6 100644 --- a/History.markdown +++ b/History.markdown @@ -53,6 +53,7 @@ * Dependencies: upgrade to toml 0.2.0 (#6541) * Lock to cucumber 3.0.1 on Ruby 2.1 (#6546) * Bump JRuby version in Travis config (#6561) + * Rescue from Psych::SyntaxError instead of SyntaxError after parsing YAML (#5828) ### Minor Enhancements From ed03ca5c16eb45bcda315d63ccdfccc5513fb347 Mon Sep 17 00:00:00 2001 From: jekyllbot Date: Fri, 24 Nov 2017 03:43:37 -0500 Subject: [PATCH 25/49] Docs: Include version badge for latest features (#6574) Merge pull request 6574 --- docs/_docs/collections.md | 6 +++--- docs/_docs/configuration.md | 2 +- docs/_docs/templates.md | 2 +- docs/_includes/docs_version_badge.html | 1 + docs/_sass/_style.scss | 24 ++++++++++++++++++++++++ 5 files changed, 30 insertions(+), 5 deletions(-) create mode 100644 docs/_includes/docs_version_badge.html diff --git a/docs/_docs/collections.md b/docs/_docs/collections.md index 0b59b7db..30ea5592 100644 --- a/docs/_docs/collections.md +++ b/docs/_docs/collections.md @@ -46,10 +46,10 @@ defaults: layout: page ``` -
    -
    Gather your collections
    +
    +
    Gather your collections {%- include docs_version_badge.html version="3.7.0" -%}
    -

    From v3.7.0 you can optionally specify a directory to store all your collections in the same place with collections_dir: my_collections

    +

    You can optionally specify a directory to store all your collections in the same place with collections_dir: my_collections

    Then Jekyll will look in my_collections/_books for the books collection, and in my_collections/_recipes for the recipes collection.

    diff --git a/docs/_docs/configuration.md b/docs/_docs/configuration.md index 77b74981..362d33c5 100644 --- a/docs/_docs/configuration.md +++ b/docs/_docs/configuration.md @@ -549,7 +549,7 @@ defaults: In this example, the `layout` is set to `default` inside the [collection](../collections/) with the name `my_collection`. -It is also possible to use glob patterns when matching defaults. For example, it is possible to set specific layout for each `special-page.html` in any subfolder of `section` folder. +It is also possible to use glob patterns when matching defaults. For example, it is possible to set specific layout for each `special-page.html` in any subfolder of `section` folder. {%- include docs_version_badge.html version="3.7.0" -%} ```yaml collections: diff --git a/docs/_docs/templates.md b/docs/_docs/templates.md index 81eb5b2a..390410aa 100644 --- a/docs/_docs/templates.md +++ b/docs/_docs/templates.md @@ -429,7 +429,7 @@ The default is `default`. They are as follows (with what they filter): - `default`: spaces and non-alphanumeric characters - `pretty`: spaces and non-alphanumeric characters except for `._~!$&'()+,;=@` - `ascii`: spaces, non-alphanumeric, and non-ASCII characters -- `latin`: like `default`, except Latin characters are first transliterated (e.g. `àèïòü` to `aeiou`) +- `latin`: like `default`, except Latin characters are first transliterated (e.g. `àèïòü` to `aeiou`) {%- include docs_version_badge.html version="3.7.0" -%} ## Tags diff --git a/docs/_includes/docs_version_badge.html b/docs/_includes/docs_version_badge.html new file mode 100644 index 00000000..3c358109 --- /dev/null +++ b/docs/_includes/docs_version_badge.html @@ -0,0 +1 @@ +{{ include.version }} diff --git a/docs/_sass/_style.scss b/docs/_sass/_style.scss index fb185972..11d89adf 100644 --- a/docs/_sass/_style.scss +++ b/docs/_sass/_style.scss @@ -1030,6 +1030,30 @@ code.output { text-shadow: 0 1px 0 rgba(255,255,255,.25); } +/* Version badge */ + +.version-badge { + margin-left: .25em; + padding: 0.2em; + font-size: .75em; + font-weight: 400; + background-color: #fc0; + color: #222; + text-shadow: none; + vertical-align: middle; + border-radius: 3.75px; +} + +.note { + .version-badge { + font-size: 0.9rem; + padding: 0.1em 0.2em; + background-color: rgba(0,0,0,0.2); + color: #fff; + box-shadow: inset 0 1px 10px rgba(0,0,0,0.3),0 1px 0 rgba(255,255,255,0.1),0 -1px 0 rgba(0,0,0,0.5); + } +} + /* Responsive tables */ @media (max-width: 768px) { From f58d5988fa24e2770c38a47c72289101a363e6af Mon Sep 17 00:00:00 2001 From: jekyllbot Date: Fri, 24 Nov 2017 03:43:39 -0500 Subject: [PATCH 26/49] Update history to reflect merge of #6574 [ci skip] --- History.markdown | 1 + 1 file changed, 1 insertion(+) diff --git a/History.markdown b/History.markdown index ba659ed6..b9fb4843 100644 --- a/History.markdown +++ b/History.markdown @@ -84,6 +84,7 @@ * Search with DocSearch by @Algolia (#6557) * Site header redesign (#6567) * Move logo above site navigation on small screens (#6570) + * Docs: Include version badge for latest features (#6574) ### Bug Fixes From 7ef3260327ecaf69540f1828b2a16198dff0dd8c Mon Sep 17 00:00:00 2001 From: Florian Thomas Date: Fri, 24 Nov 2017 08:49:13 +0000 Subject: [PATCH 27/49] return correct file in dir if dir has same name as file (#6569) Merge pull request 6569 --- lib/jekyll/commands/serve/servlet.rb | 6 +++- test/fixtures/webrick/bar.html | 1 + test/fixtures/webrick/bar/baz.html | 1 + test/helper.rb | 54 ++++++++++++++++++++++++++++ test/test_commands_serve_servlet.rb | 38 ++++++++++++++++++++ 5 files changed, 99 insertions(+), 1 deletion(-) create mode 100644 test/fixtures/webrick/bar.html create mode 100644 test/fixtures/webrick/bar/baz.html create mode 100644 test/test_commands_serve_servlet.rb diff --git a/lib/jekyll/commands/serve/servlet.rb b/lib/jekyll/commands/serve/servlet.rb index 2e41b697..70b6d66d 100644 --- a/lib/jekyll/commands/serve/servlet.rb +++ b/lib/jekyll/commands/serve/servlet.rb @@ -18,13 +18,17 @@ module Jekyll super end + def search_index_file(req, res) + super || search_file(req, res, ".html") + end + # Add the ability to tap file.html the same way that Nginx does on our # Docker images (or on GitHub Pages.) The difference is that we might end # up with a different preference on which comes first. def search_file(req, res, basename) # /file.* > /file/index.html > /file.html - super || super(req, res, ".html") || super(req, res, "#{basename}.html") + super || super(req, res, "#{basename}.html") end # rubocop:disable Naming/MethodName diff --git a/test/fixtures/webrick/bar.html b/test/fixtures/webrick/bar.html new file mode 100644 index 00000000..76870bf2 --- /dev/null +++ b/test/fixtures/webrick/bar.html @@ -0,0 +1 @@ +Content of bar.html diff --git a/test/fixtures/webrick/bar/baz.html b/test/fixtures/webrick/bar/baz.html new file mode 100644 index 00000000..794d5f22 --- /dev/null +++ b/test/fixtures/webrick/bar/baz.html @@ -0,0 +1 @@ +Content of baz.html diff --git a/test/helper.rb b/test/helper.rb index 672fedd2..aa94be13 100644 --- a/test/helper.rb +++ b/test/helper.rb @@ -44,6 +44,8 @@ require "shoulda" include Jekyll +require "jekyll/commands/serve/servlet" + # Report with color. Minitest::Reporters.use! [ Minitest::Reporters::DefaultReporter.new( @@ -194,3 +196,55 @@ class JekyllUnitTest < Minitest::Test end end end + +class FakeLogger + def <<(str); end +end + +module TestWEBrick + + module_function + + def mount_server(&block) + server = WEBrick::HTTPServer.new(config) + + begin + server.mount("/", Jekyll::Commands::Serve::Servlet, document_root, + document_root_options) + + server.start + addr = server.listeners[0].addr + block.yield([server, addr[3], addr[1]]) + rescue StandardError => e + raise e + ensure + server.shutdown + sleep 0.1 until server.status == :Stop + end + end + + def config + logger = FakeLogger.new + { + :BindAddress => "127.0.0.1", :Port => 0, + :ShutdownSocketWithoutClose => true, + :ServerType => Thread, + :Logger => WEBrick::Log.new(logger), + :AccessLog => [[logger, ""]], + :JekyllOptions => {}, + } + end + + def document_root + "#{File.dirname(__FILE__)}/fixtures/webrick" + end + + def document_root_options + WEBrick::Config::FileHandler.merge({ + :FancyIndexing => true, + :NondisclosureName => [ + ".ht*", "~*", + ], + }) + end +end diff --git a/test/test_commands_serve_servlet.rb b/test/test_commands_serve_servlet.rb new file mode 100644 index 00000000..1f84cbbb --- /dev/null +++ b/test/test_commands_serve_servlet.rb @@ -0,0 +1,38 @@ +# frozen_string_literal: true + +require "webrick" +require "helper" +require "net/http" + +class TestCommandsServeServlet < JekyllUnitTest + def get(path) + TestWEBrick.mount_server do |_server, addr, port| + http = Net::HTTP.new(addr, port) + req = Net::HTTP::Get.new(path) + + http.request(req) { |response| yield(response) } + end + end + + context "with a directory and file with the same name" do + should "find that file" do + get("/bar/") do |response| + assert_equal("200", response.code) + assert_equal("Content of bar.html", response.body.strip) + end + end + + should "find file in directory" do + get("/bar/baz") do |response| + assert_equal("200", response.code) + assert_equal("Content of baz.html", response.body.strip) + end + end + + should "return 404 for non-existing files" do + get("/bar/missing") do |response| + assert_equal("404", response.code) + end + end + end +end From 65fd99045926ed3331d59356dc765936b9bb0dff Mon Sep 17 00:00:00 2001 From: jekyllbot Date: Fri, 24 Nov 2017 03:49:15 -0500 Subject: [PATCH 28/49] Update history to reflect merge of #6569 [ci skip] --- History.markdown | 1 + 1 file changed, 1 insertion(+) diff --git a/History.markdown b/History.markdown index b9fb4843..94d3c9ec 100644 --- a/History.markdown +++ b/History.markdown @@ -91,6 +91,7 @@ * Raise when theme root directory is not available (#6455) * Avoid block parser warning in SmartyPants (#6565) * Fail gracefully if "sass" gem cannot be loaded (#6573) + * return correct file in dir if dir has same name as file (#6569) ## 3.6.2 / 2017-10-21 From abeee6fdf6849ce24ddb5987a282e2e35302ebeb Mon Sep 17 00:00:00 2001 From: ashmaroli Date: Fri, 24 Nov 2017 17:49:08 +0530 Subject: [PATCH 29/49] use version-badge on an existing feature intro (#6575) Merge pull request 6575 --- docs/_docs/themes.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/_docs/themes.md b/docs/_docs/themes.md index 6907ffb0..b5423a6b 100644 --- a/docs/_docs/themes.md +++ b/docs/_docs/themes.md @@ -224,9 +224,9 @@ Your theme's styles can be included in the user's stylesheet using the `@import` ``` {% endraw %} -### Theme-gem dependencies +### Theme-gem dependencies {%- include docs_version_badge.html version="3.5.0" -%} -From `v3.5`, Jekyll will automatically require all whitelisted `runtime_dependencies` of your theme-gem even if they're not explicitly included under the `plugins` array in the site's config file. (Note: whitelisting is only required when building or serving with the `--safe` option.) +Jekyll will automatically require all whitelisted `runtime_dependencies` of your theme-gem even if they're not explicitly included under the `plugins` array in the site's config file. (Note: whitelisting is only required when building or serving with the `--safe` option.) With this, the end-user need not keep track of the plugins required to be included in their config file for their theme-gem to work as intended. From 8060934f9689d77cc9ee4549057364a9a9d4c165 Mon Sep 17 00:00:00 2001 From: jekyllbot Date: Fri, 24 Nov 2017 07:19:10 -0500 Subject: [PATCH 30/49] Update history to reflect merge of #6575 [ci skip] --- History.markdown | 1 + 1 file changed, 1 insertion(+) diff --git a/History.markdown b/History.markdown index 94d3c9ec..d878b8d1 100644 --- a/History.markdown +++ b/History.markdown @@ -85,6 +85,7 @@ * Site header redesign (#6567) * Move logo above site navigation on small screens (#6570) * Docs: Include version badge for latest features (#6574) + * Use version-badge on an existing feature intro (#6575) ### Bug Fixes From e063ac530c9c8508533128eae59fb3e7f23d4576 Mon Sep 17 00:00:00 2001 From: ashmaroli Date: Mon, 27 Nov 2017 01:06:41 +0530 Subject: [PATCH 31/49] drop forwarding to private methods (#6577) Merge pull request 6577 --- lib/jekyll/theme_builder.rb | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/lib/jekyll/theme_builder.rb b/lib/jekyll/theme_builder.rb index aca89dd6..9b43e0a3 100644 --- a/lib/jekyll/theme_builder.rb +++ b/lib/jekyll/theme_builder.rb @@ -21,6 +21,14 @@ class Jekyll::ThemeBuilder initialize_git_repo end + def user_name + @user_name ||= `git config user.name`.chomp + end + + def user_email + @user_email ||= `git config user.email`.chomp + end + private def root @@ -85,14 +93,6 @@ class Jekyll::ThemeBuilder write_file(".gitignore", template("gitignore")) end - def user_name - @user_name ||= `git config user.name`.chomp - end - - def user_email - @user_email ||= `git config user.email`.chomp - end - class ERBRenderer extend Forwardable From 05bca8128c5069fdac4735afb79e390265be6727 Mon Sep 17 00:00:00 2001 From: jekyllbot Date: Sun, 26 Nov 2017 14:36:43 -0500 Subject: [PATCH 32/49] Update history to reflect merge of #6577 [ci skip] --- History.markdown | 1 + 1 file changed, 1 insertion(+) diff --git a/History.markdown b/History.markdown index d878b8d1..f05f9002 100644 --- a/History.markdown +++ b/History.markdown @@ -54,6 +54,7 @@ * Lock to cucumber 3.0.1 on Ruby 2.1 (#6546) * Bump JRuby version in Travis config (#6561) * Rescue from Psych::SyntaxError instead of SyntaxError after parsing YAML (#5828) + * Drop forwarding to private methods by exposing those methods as public (#6577) ### Minor Enhancements From bd1d44493f559039c877203ffb52add57b47d732 Mon Sep 17 00:00:00 2001 From: Parker Moore Date: Wed, 29 Nov 2017 03:17:08 -0500 Subject: [PATCH 33/49] Upgrade pygments to v1.x (#5937) Merge pull request 5937 --- Gemfile | 2 +- lib/jekyll/tags/highlight.rb | 4 ++-- test/test_redcarpet.rb | 13 +++++++------ test/test_tags.rb | 23 ++++++++++++++--------- 4 files changed, 24 insertions(+), 18 deletions(-) diff --git a/Gemfile b/Gemfile index 2ba32da7..8590544e 100644 --- a/Gemfile +++ b/Gemfile @@ -81,7 +81,7 @@ group :jekyll_optional_dependencies do platform :ruby, :mswin, :mingw, :x64_mingw do gem "classifier-reborn", "~> 2.1.0" gem "liquid-c", "~> 3.0" - gem "pygments.rb", "~> 0.6.0" + gem "pygments.rb", "~> 1.0" gem "rdiscount", "~> 2.0" gem "redcarpet", "~> 3.2", ">= 3.2.3" gem "yajl-ruby", "~> 1.2" diff --git a/lib/jekyll/tags/highlight.rb b/lib/jekyll/tags/highlight.rb index 683e3b2b..256342c3 100644 --- a/lib/jekyll/tags/highlight.rb +++ b/lib/jekyll/tags/highlight.rb @@ -86,7 +86,7 @@ MSG end def render_pygments(code, is_safe) - Jekyll::External.require_with_graceful_fail("pygments") + Jekyll::External.require_with_graceful_fail("pygments") unless defined?(Pygments) highlighted_code = Pygments.highlight( code, @@ -118,7 +118,7 @@ MSG :gutter_class => "gutter", :code_class => "code" ) - lexer = Rouge::Lexer.find_fancy(@lang, code) || Rouge::Lexers::PlainText + lexer = ::Rouge::Lexer.find_fancy(@lang, code) || Rouge::Lexers::PlainText formatter.format(lexer.lex(code)) end diff --git a/test/test_redcarpet.rb b/test/test_redcarpet.rb index f4208570..29d787ae 100644 --- a/test/test_redcarpet.rb +++ b/test/test_redcarpet.rb @@ -46,16 +46,17 @@ class TestRedcarpet < JekyllUnitTest end should "render fenced code blocks with syntax highlighting" do - assert_equal "
    puts "Hello world"\n
    ", - @markdown.convert( - <<-EOS + assert_equal \ + "
    puts "Hello world"\n
    ", + @markdown.convert( + <<-EOS ```ruby puts "Hello world" ``` EOS - ).strip + ).strip end end diff --git a/test/test_tags.rb b/test/test_tags.rb index 43d83464..a76f0df1 100644 --- a/test/test_tags.rb +++ b/test/test_tags.rb @@ -185,7 +185,8 @@ CONTENT should "render markdown with pygments" do assert_match( - %(
    test
    ), + %(
    ) +
    +          %(test
    ), @result ) end @@ -193,7 +194,7 @@ CONTENT should "render markdown with pygments with line numbers" do assert_match( %(
    ) +
    -          %(1 test
    ), + %(1 test), @result ) end @@ -206,7 +207,7 @@ CONTENT should "not embed the file" do assert_match( - %(
    ) +
    +          %(
    ) +
               %(./jekyll.gemspec
    ), @result ) @@ -220,7 +221,8 @@ CONTENT should "render markdown with pygments line handling" do assert_match( - %(
    Æ
    ), + %(
    ) +
    +          %(Æ
    ), @result ) end @@ -240,7 +242,8 @@ EOS should "only strip the preceding newlines" do assert_match( - %(
         [,1] [,2]),
    +          %(
    ) +
    +          %(     [,1] [,2]),
               @result
             )
           end
    @@ -265,8 +268,8 @@ EOS
     
           should "only strip the newlines which precede and succeed the entire block" do
             assert_match(
    -          "
    " \
    -          "     [,1] [,2]\n\n\n[1,] FALSE TRUE\n[2,] FALSE TRUE
    ", + %(
    ) +
    +          %(     [,1] [,2]\n\n\n[1,] FALSE TRUE\n[2,] FALSE TRUE
    ), @result ) end @@ -280,7 +283,8 @@ EOS should "only strip the preceding newlines" do assert_match( - %(
         [,1] [,2]),
    +          %(
    ) +
    +          %(     [,1] [,2]),
               @result
             )
           end
    @@ -298,7 +302,8 @@ EOS
     
           should "only strip the preceding newlines" do
             assert_match(
    -          %(
         [,1] [,2]),
    +          %(
    ) +
    +          %(     [,1] [,2]),
               @result
             )
           end
    
    From ff4718d82438512e297f035a4ccc66c848e75e7a Mon Sep 17 00:00:00 2001
    From: jekyllbot 
    Date: Wed, 29 Nov 2017 03:17:09 -0500
    Subject: [PATCH 34/49] Update history to reflect merge of #5937 [ci skip]
    
    ---
     History.markdown | 1 +
     1 file changed, 1 insertion(+)
    
    diff --git a/History.markdown b/History.markdown
    index f05f9002..06d286eb 100644
    --- a/History.markdown
    +++ b/History.markdown
    @@ -55,6 +55,7 @@
       * Bump JRuby version in Travis config (#6561)
       * Rescue from Psych::SyntaxError instead of SyntaxError after parsing YAML (#5828)
       * Drop forwarding to private methods by exposing those methods as public (#6577)
    +  * Upgrade pygments to v1.x (#5937)
     
     ### Minor Enhancements
     
    
    From 79bf9f286589c8a5991c480ddf53f946cfa36ba7 Mon Sep 17 00:00:00 2001
    From: jekyllbot 
    Date: Wed, 29 Nov 2017 03:37:07 -0500
    Subject: [PATCH 35/49] Bump yajl-ruby (#6582)
    
    Merge pull request 6582
    ---
     Gemfile | 2 +-
     1 file changed, 1 insertion(+), 1 deletion(-)
    
    diff --git a/Gemfile b/Gemfile
    index 8590544e..68bc28c2 100644
    --- a/Gemfile
    +++ b/Gemfile
    @@ -84,7 +84,7 @@ group :jekyll_optional_dependencies do
         gem "pygments.rb", "~> 1.0"
         gem "rdiscount", "~> 2.0"
         gem "redcarpet", "~> 3.2", ">= 3.2.3"
    -    gem "yajl-ruby", "~> 1.2"
    +    gem "yajl-ruby", "~> 1.3.1"
       end
     
       # Windows does not include zoneinfo files, so bundle the tzinfo-data gem
    
    From 664b20d3752a81f5073b238906300203733db3d2 Mon Sep 17 00:00:00 2001
    From: jekyllbot 
    Date: Wed, 29 Nov 2017 03:37:08 -0500
    Subject: [PATCH 36/49] Update history to reflect merge of #6582 [ci skip]
    
    ---
     History.markdown | 1 +
     1 file changed, 1 insertion(+)
    
    diff --git a/History.markdown b/History.markdown
    index 06d286eb..49bfec78 100644
    --- a/History.markdown
    +++ b/History.markdown
    @@ -56,6 +56,7 @@
       * Rescue from Psych::SyntaxError instead of SyntaxError after parsing YAML (#5828)
       * Drop forwarding to private methods by exposing those methods as public (#6577)
       * Upgrade pygments to v1.x (#5937)
    +  * Bump yajl-ruby (#6582)
     
     ### Minor Enhancements
     
    
    From 61e53b6b6199263f6937979c1ed68b1ed888c192 Mon Sep 17 00:00:00 2001
    From: ashmaroli 
    Date: Wed, 29 Nov 2017 19:26:46 +0530
    Subject: [PATCH 37/49] cleanup test_redcarpet.rb (#6584)
    
    Merge pull request 6584
    ---
     test/test_redcarpet.rb | 57 ++++++++++++++++++------------------------
     1 file changed, 25 insertions(+), 32 deletions(-)
    
    diff --git a/test/test_redcarpet.rb b/test/test_redcarpet.rb
    index 29d787ae..51da49c5 100644
    --- a/test/test_redcarpet.rb
    +++ b/test/test_redcarpet.rb
    @@ -19,6 +19,13 @@ class TestRedcarpet < JekyllUnitTest
           }
     
           @markdown = Converters::Markdown.new @config
    +
    +      @sample = Jekyll::Utils.strip_heredoc(<<-EOS
    +        ```ruby
    +        puts "Hello world"
    +        ```
    +      EOS
    +                                           )
         end
     
         should "pass redcarpet options" do
    @@ -35,7 +42,7 @@ class TestRedcarpet < JekyllUnitTest
     
         should "pass redcarpet render options" do
           assert_equal "

    bad code not here: i am bad

    ", - @markdown.convert("**bad code not here**: ").strip + @markdown.convert("**bad code not here**: ").strip end context "with pygments enabled" do @@ -46,17 +53,12 @@ class TestRedcarpet < JekyllUnitTest end should "render fenced code blocks with syntax highlighting" do - assert_equal \ - "
    puts "Hello world"\n
    ", - @markdown.convert( - <<-EOS -```ruby -puts "Hello world" -``` -EOS - ).strip + assert_equal( + %(
    puts "Hello world"\n
    ), + @markdown.convert(@sample).strip + ) end end @@ -66,16 +68,12 @@ EOS end should "render fenced code blocks with syntax highlighting" do - assert_equal "
    puts \"Hello world\"\n
    ", - @markdown.convert( - <<-EOS -```ruby -puts "Hello world" -``` - EOS - ).strip + assert_equal( + %(
    puts "Hello world"\n
    ), + @markdown.convert(@sample).strip + ) end end @@ -85,16 +83,11 @@ puts "Hello world" end should "render fenced code blocks without syntax highlighting" do - assert_equal "
    puts "Hello world"\n
    "\ - "
    ", - @markdown.convert( - <<-EOS -```ruby -puts "Hello world" -``` - EOS - ).strip + assert_equal( + %(
    puts "Hello world"\n
    ), + @markdown.convert(@sample).strip + ) end end end From e3b8ba33da2666dc28a77c3e14945f5c93859a72 Mon Sep 17 00:00:00 2001 From: jekyllbot Date: Wed, 29 Nov 2017 08:56:47 -0500 Subject: [PATCH 38/49] Update history to reflect merge of #6584 [ci skip] --- History.markdown | 1 + 1 file changed, 1 insertion(+) diff --git a/History.markdown b/History.markdown index 49bfec78..75ca663c 100644 --- a/History.markdown +++ b/History.markdown @@ -57,6 +57,7 @@ * Drop forwarding to private methods by exposing those methods as public (#6577) * Upgrade pygments to v1.x (#5937) * Bump yajl-ruby (#6582) + * Cleanup test_redcarpet.rb (#6584) ### Minor Enhancements From 65f7deca98964f93e51af44253d4e5a27c1949b9 Mon Sep 17 00:00:00 2001 From: ashmaroli Date: Thu, 30 Nov 2017 23:46:35 +0530 Subject: [PATCH 39/49] Add PageWithoutAFile class from jekyll plugins (#6556) Merge pull request 6556 --- lib/jekyll.rb | 1 + lib/jekyll/page_without_a_file.rb | 18 ++++ test/fixtures/physical.html | 6 ++ test/test_page_without_a_file.rb | 159 ++++++++++++++++++++++++++++++ 4 files changed, 184 insertions(+) create mode 100644 lib/jekyll/page_without_a_file.rb create mode 100644 test/fixtures/physical.html create mode 100644 test/test_page_without_a_file.rb diff --git a/lib/jekyll.rb b/lib/jekyll.rb index 733796cc..c909c3d1 100644 --- a/lib/jekyll.rb +++ b/lib/jekyll.rb @@ -61,6 +61,7 @@ module Jekyll autoload :ThemeAssetsReader, "jekyll/readers/theme_assets_reader" autoload :LogAdapter, "jekyll/log_adapter" autoload :Page, "jekyll/page" + autoload :PageWithoutAFile, "jekyll/page_without_a_file" autoload :PluginManager, "jekyll/plugin_manager" autoload :Publisher, "jekyll/publisher" autoload :Reader, "jekyll/reader" diff --git a/lib/jekyll/page_without_a_file.rb b/lib/jekyll/page_without_a_file.rb new file mode 100644 index 00000000..2d30af51 --- /dev/null +++ b/lib/jekyll/page_without_a_file.rb @@ -0,0 +1,18 @@ +# frozen_string_literal: true + +module Jekyll + # A Jekyll::Page subclass to handle processing files without reading it to + # determine the page-data and page-content based on Front Matter delimiters. + # + # The class instance is basically just a bare-bones entity with just + # attributes "dir", "name", "path", "url" defined on it. + class PageWithoutAFile < Page + def read_yaml(*) + @data ||= {} + end + + def inspect + "#" + end + end +end diff --git a/test/fixtures/physical.html b/test/fixtures/physical.html new file mode 100644 index 00000000..8913980a --- /dev/null +++ b/test/fixtures/physical.html @@ -0,0 +1,6 @@ +--- +title: Physical file +permalink: /physical/ +--- + +A physical file entity diff --git a/test/test_page_without_a_file.rb b/test/test_page_without_a_file.rb new file mode 100644 index 00000000..b1fa9c04 --- /dev/null +++ b/test/test_page_without_a_file.rb @@ -0,0 +1,159 @@ +# frozen_string_literal: true + +require "helper" + +class TestPageWithoutAFile < JekyllUnitTest + def setup_page(*args, base: source_dir, klass: PageWithoutAFile) + dir, file = args + if file.nil? + file = dir + dir = "" + end + klass.new(@site, base, dir, file) + end + + def render_and_write + @site.render + @site.cleanup + @site.write + end + + context "A PageWithoutAFile" do + setup do + clear_dest + @site = Site.new(Jekyll.configuration({ + "source" => source_dir, + "destination" => dest_dir, + "skip_config_files" => true, + })) + end + + context "with default site configuration" do + setup do + @page = setup_page("properties.html") + end + + should "not have page-content and page-data defined within it" do + assert_equal "pages", @page.type.to_s + assert_nil @page.content + assert_empty @page.data + end + + should "have basic attributes defined in it" do + regular_page = setup_page("properties.html", :klass => Page) + basic_attrs = %w(dir name path url) + attrs = { + "content" => "All the properties.\n", + "dir" => "/", + "excerpt" => nil, + "foo" => "bar", + "layout" => "default", + "name" => "properties.html", + "path" => "properties.html", + "permalink" => "/properties/", + "published" => nil, + "title" => "Properties Page", + "url" => "/properties.html", + } + attrs.each do |prop, value| + # assert the props being accessible in a Jekyll::Page instance + assert_equal "All the properties.\n", regular_page["content"] + assert_equal "properties.html", regular_page["name"] + + # assert differences with Jekyll::PageWithoutAFile instance + if basic_attrs.include?(prop) + assert_equal @page[prop], value, "For :" + else + assert_nil @page[prop] + end + end + end + end + + context "with site-wide permalink configuration" do + setup do + @site.permalink_style = :title + end + + should "generate page url accordingly" do + page = setup_page("properties.html") + assert_equal "/properties", page.url + end + end + + context "with default front matter configuration" do + setup do + @site.config["defaults"] = [ + { + "scope" => { + "path" => "", + "type" => "pages", + }, + "values" => { + "layout" => "default", + "author" => "John Doe", + }, + }, + ] + + @page = setup_page("info.md") + end + + should "respect front matter defaults" do + assert_nil @page.data["title"] + assert_equal "John Doe", @page.data["author"] + assert_equal "default", @page.data["layout"] + end + end + + context "with a path outside site.source" do + should "not access its contents" do + base = "../../../" + page = setup_page("pwd", :base => base) + + assert_equal "pwd", page.path + assert_nil page.content + end + end + + context "while processing" do + setup do + clear_dest + @site.config["title"] = "Test Site" + @page = setup_page("physical.html", :base => test_dir("fixtures")) + end + + should "recieve content provided to it" do + assert_nil @page.content + + @page.content = "{{ site.title }}" + assert_equal "{{ site.title }}", @page.content + end + + should "not be processed and written to disk at destination" do + @page.content = "Lorem ipsum dolor sit amet" + @page.data["permalink"] = "/virtual-about/" + + render_and_write + + refute_exist dest_dir("physical") + refute_exist dest_dir("virtual-about") + refute File.exist?(dest_dir("virtual-about", "index.html")) + end + + should "be processed and written to destination when passed as "\ + "an entry in 'site.pages' array" do + @page.content = "{{ site.title }}" + @page.data["permalink"] = "/virtual-about/" + + @site.pages << @page + render_and_write + + refute_exist dest_dir("physical") + assert_exist dest_dir("virtual-about") + assert File.exist?(dest_dir("virtual-about", "index.html")) + assert_equal "Test Site", File.read(dest_dir("virtual-about", "index.html")) + end + end + end +end From 7826bfe552d2e2209b4d3bbd909170b687fb9704 Mon Sep 17 00:00:00 2001 From: Ashwin Maroli Date: Mon, 13 Nov 2017 16:43:27 +0530 Subject: [PATCH 40/49] last released version is at 3.6.2 --- lib/jekyll/version.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/jekyll/version.rb b/lib/jekyll/version.rb index 6e0c1cf6..8f010606 100644 --- a/lib/jekyll/version.rb +++ b/lib/jekyll/version.rb @@ -1,5 +1,5 @@ # frozen_string_literal: true module Jekyll - VERSION = "3.6.0".freeze + VERSION = "3.6.2".freeze end From 0c4b12e6aaffe3416b0bd066001e7cc3a8e8b804 Mon Sep 17 00:00:00 2001 From: jekyllbot Date: Thu, 30 Nov 2017 12:16:37 -0600 Subject: [PATCH 41/49] Update history to reflect merge of #6556 [ci skip] --- History.markdown | 1 + 1 file changed, 1 insertion(+) diff --git a/History.markdown b/History.markdown index 75ca663c..fcd419fb 100644 --- a/History.markdown +++ b/History.markdown @@ -58,6 +58,7 @@ * Upgrade pygments to v1.x (#5937) * Bump yajl-ruby (#6582) * Cleanup test_redcarpet.rb (#6584) + * Add PageWithoutAFile class from jekyll plugins (#6556) ### Minor Enhancements From 2fe54170b4b12eba92cc85229ea2d2cae6aa5e99 Mon Sep 17 00:00:00 2001 From: Frank Taillandier Date: Sun, 3 Dec 2017 01:51:07 +0100 Subject: [PATCH 42/49] Dependency: Bump jekyll-watch to 2.0 (#6589) Merge pull request 6589 --- jekyll.gemspec | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/jekyll.gemspec b/jekyll.gemspec index 98fd5382..90ad0ceb 100644 --- a/jekyll.gemspec +++ b/jekyll.gemspec @@ -34,7 +34,7 @@ Gem::Specification.new do |s| s.add_runtime_dependency("colorator", "~> 1.0") s.add_runtime_dependency("i18n", "~> 0.7") s.add_runtime_dependency("jekyll-sass-converter", "~> 1.0") - s.add_runtime_dependency("jekyll-watch", "~> 1.1") + s.add_runtime_dependency("jekyll-watch", "~> 2.0") s.add_runtime_dependency("kramdown", "~> 1.14") s.add_runtime_dependency("liquid", "~> 4.0") s.add_runtime_dependency("mercenary", "~> 0.3.3") From 760d586de21982cc7df62e61d259950da9e861e7 Mon Sep 17 00:00:00 2001 From: jekyllbot Date: Sat, 2 Dec 2017 18:51:09 -0600 Subject: [PATCH 43/49] Update history to reflect merge of #6589 [ci skip] --- History.markdown | 1 + 1 file changed, 1 insertion(+) diff --git a/History.markdown b/History.markdown index fcd419fb..54bd43db 100644 --- a/History.markdown +++ b/History.markdown @@ -74,6 +74,7 @@ * Add latin mode to slugify (#6509) * Log Kramdown warnings if log level is WARN (#6522) * Add json extension to list of directory indices (#6550) + * Dependency: Bump jekyll-watch to 2.0 (#6589) ### Site Enhancements From cdb031084c7deddd518b1dfaddccbbf8fd8c12bf Mon Sep 17 00:00:00 2001 From: Frank Taillandier Date: Sun, 3 Dec 2017 19:38:34 +0100 Subject: [PATCH 44/49] Docs: Avoid Kramdown warnings --- docs/_docs/history.md | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/docs/_docs/history.md b/docs/_docs/history.md index 85e49361..a7308287 100644 --- a/docs/_docs/history.md +++ b/docs/_docs/history.md @@ -153,7 +153,7 @@ note: This file is autogenerated. Edit /History.markdown instead. - added BibSonomy plugin ([#6143]({{ site.repository }}/issues/6143)) - add plugins for multiple page pagination ([#6055]({{ site.repository }}/issues/6055)) - Update minimum Ruby version in installation.md ([#6164]({{ site.repository }}/issues/6164)) -- [docs] Add information about finding a collection in `site.collections` ([#6165]({{ site.repository }}/issues/6165)) +- Add information about finding a collection in `site.collections` ([#6165]({{ site.repository }}/issues/6165)) - Add {% raw %}`{% raw %}`{% endraw %} to Liquid example on site ([#6179]({{ site.repository }}/issues/6179)) - Added improved Pug plugin - removed 404 Jade plugin ([#6174]({{ site.repository }}/issues/6174)) - Linking the link ([#6210]({{ site.repository }}/issues/6210)) @@ -263,7 +263,7 @@ note: This file is autogenerated. Edit /History.markdown instead. ### Development Fixes {: #development-fixes-v3-5-0} -- [Rubocop] add missing comma ([#5835]({{ site.repository }}/issues/5835)) +- Rubocop: add missing comma ([#5835]({{ site.repository }}/issues/5835)) - Appease classifier-reborn ([#5934]({{ site.repository }}/issues/5934)) - Allow releases & development on `*-stable` branches ([#5926]({{ site.repository }}/issues/5926)) - Add script/backport-pr ([#5925]({{ site.repository }}/issues/5925)) @@ -302,7 +302,7 @@ note: This file is autogenerated. Edit /History.markdown instead. {: #bug-fixes-v3-5-0} - Exclude Gemfile by default ([#5860]({{ site.repository }}/issues/5860)) -- Convertible#validate_permalink!: ensure the return value of data["permalink"] is a string before asking if it is empty ([#5878]({{ site.repository }}/issues/5878)) +- Convertible#validate_permalink!: ensure the return value of `data["permalink"]` is a string before asking if it is empty ([#5878]({{ site.repository }}/issues/5878)) - Allow abbreviated post dates ([#5920]({{ site.repository }}/issues/5920)) - Remove dependency on include from default about.md ([#5903]({{ site.repository }}/issues/5903)) - Allow colons in `uri_escape` filter ([#5957]({{ site.repository }}/issues/5957)) @@ -378,7 +378,7 @@ note: This file is autogenerated. Edit /History.markdown instead. - Switch to `https` when possible. ([#5611]({{ site.repository }}/issues/5611)) - Update `_font-awesome.scss` to move .woff file before .ttf ([#5614]({{ site.repository }}/issues/5614)) - Update documentation on updating FontAwesome Iconset ([#5655]({{ site.repository }}/issues/5655)) -- [site] Use defaults for docs and news-items ([#5744]({{ site.repository }}/issues/5744)) +- Use defaults for docs and news-items ([#5744]({{ site.repository }}/issues/5744)) - Sort gems in `docs/_config.yml` ([#5746]({{ site.repository }}/issues/5746)) - Add missing class ([#5791]({{ site.repository }}/issues/5791)) - Improve template docs ([#5694]({{ site.repository }}/issues/5694)) @@ -440,7 +440,7 @@ note: This file is autogenerated. Edit /History.markdown instead. - Update quickstart.md ([#5758]({{ site.repository }}/issues/5758)) - Correct minor typo ([#5764]({{ site.repository }}/issues/5764)) - Fix a markdown link to look properly on the web ([#5769]({{ site.repository }}/issues/5769)) -- [docs] Info about the help command usage ([#5312]({{ site.repository }}/issues/5312)) +- Info about the help command usage ([#5312]({{ site.repository }}/issues/5312)) - Add missing merge labels for jekyllbot ([#5753]({{ site.repository }}/issues/5753)) - Fix broken links in documentation ([#5736]({{ site.repository }}/issues/5736)) - Docs: add `match_regex` and `replace_regex` filters ([#5799]({{ site.repository }}/issues/5799)) @@ -583,10 +583,10 @@ note: This file is autogenerated. Edit /History.markdown instead. - Site: exclude README.md and .gitignore ([#5304]({{ site.repository }}/issues/5304)) - Add link to Staticman ([#5224]({{ site.repository }}/issues/5224)) - Update url for OpenShift ([#5320]({{ site.repository }}/issues/5320)) -- [docs] add help for missing static_file e.g. on heroku ([#5334]({{ site.repository }}/issues/5334)) +- Add help for missing static_file e.g. on heroku ([#5334]({{ site.repository }}/issues/5334)) - Add a line about updating theme-gems in the docs ([#5318]({{ site.repository }}/issues/5318)) - Explain how to copy a theme's files ([#5335]({{ site.repository }}/issues/5335)) -- [docs] .md as default extension in examples ([#5316]({{ site.repository }}/issues/5316)) +- .md as default extension in examples ([#5316]({{ site.repository }}/issues/5316)) - Fix small typo in docs ([#5347]({{ site.repository }}/issues/5347)) - Add missing period to sentence in first paragraph. ([#5372]({{ site.repository }}/issues/5372)) - added jekyll-spotify plugin ([#5369]({{ site.repository }}/issues/5369)) @@ -595,7 +595,7 @@ note: This file is autogenerated. Edit /History.markdown instead. - Add documentation for `relative_url` and `absolute_url` ([#5405]({{ site.repository }}/issues/5405)) - Bugfix on logo in JSON-LD ([#5421]({{ site.repository }}/issues/5421)) - Fix Travis.ci documentation ([#5413]({{ site.repository }}/issues/5413)) -- [docs] Update documentation regarding `bundle install` after `jekyll new` ([#5428]({{ site.repository }}/issues/5428)) +- Update documentation regarding `bundle install` after `jekyll new` ([#5428]({{ site.repository }}/issues/5428)) - Replace classic box-sizing reset with inheritance reset ([#5411]({{ site.repository }}/issues/5411)) - Update Wikipedia YAML list link ([#5452]({{ site.repository }}/issues/5452)) - Add Jekyll 3.3 release post ([#5442]({{ site.repository }}/issues/5442)) @@ -929,7 +929,7 @@ note: This file is autogenerated. Edit /History.markdown instead. - Fix broken links to the Code of Conduct ([#4436]({{ site.repository }}/issues/4436)) - Upgrade notes: mention trailing slash in permalink; fixes [#4440]({{ site.repository }}/issues/4440) ([#4455]({{ site.repository }}/issues/4455)) - Add hooks to the plugin categories toc ([#4463]({{ site.repository }}/issues/4463)) -- [add note] Jekyll 3 requires newer version of Ruby. ([#4461]({{ site.repository }}/issues/4461)) +- Jekyll 3 requires newer version of Ruby. ([#4461]({{ site.repository }}/issues/4461)) - Fix typo in upgrading docs ([#4473]({{ site.repository }}/issues/4473)) - Add note about upgrading documentation on jekyllrb.com/help/ ([#4484]({{ site.repository }}/issues/4484)) - Update Rake link ([#4496]({{ site.repository }}/issues/4496)) @@ -1005,7 +1005,7 @@ note: This file is autogenerated. Edit /History.markdown instead. - Fix deep_merge_hashes! handling of drops and hashes ([#4359]({{ site.repository }}/issues/4359)) - Page should respect output extension of its permalink ([#4373]({{ site.repository }}/issues/4373)) - Disable auto-regeneration when running server detached ([#4376]({{ site.repository }}/issues/4376)) -- Drop#[]: only use public_send for keys in the content_methods array ([#4388]({{ site.repository }}/issues/4388)) +- Drop#: only use public_send for keys in the content_methods array ([#4388]({{ site.repository }}/issues/4388)) - Extract title from filename successfully when no date. ([#4195]({{ site.repository }}/issues/4195)) ### Development Fixes From 75ba9366df98d7743334b0ee9969b84b132fc9d3 Mon Sep 17 00:00:00 2001 From: Frank Taillandier Date: Sun, 3 Dec 2017 19:39:02 +0100 Subject: [PATCH 45/49] Docs: Build for production --- docs/_docs/usage.md | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/docs/_docs/usage.md b/docs/_docs/usage.md index dc273b29..d4dffa01 100644 --- a/docs/_docs/usage.md +++ b/docs/_docs/usage.md @@ -21,6 +21,13 @@ jekyll build --watch # watched for changes, and regenerated automatically. ``` +Default URL is set to `http://localhost:4000` in development environment. {% include docs_version_badge.html version="3.3.0" %} + +If you want to build for your production environment: + + - Set your production URL in `_config.yml` e.g. `url: https://example.com`. + - Run `JEKYLL_ENV=production bundle exec jekyll build`. +
    Changes to _config.yml are not included during automatic regeneration.

    From e8c8eacf7bf24fa5cacc4e434aa15774c3a728a0 Mon Sep 17 00:00:00 2001 From: Frank Taillandier Date: Sun, 3 Dec 2017 19:40:10 +0100 Subject: [PATCH 46/49] Dev: Run preview in incremental mode --- rake/site.rake | 2 ++ 1 file changed, 2 insertions(+) diff --git a/rake/site.rake b/rake/site.rake index 10d48c52..d260e447 100644 --- a/rake/site.rake +++ b/rake/site.rake @@ -27,6 +27,8 @@ namespace :site do options = { "source" => File.expand_path(docs_folder), "destination" => File.expand_path("#{docs_folder}/_site"), + "incremental" => true, + "profile" => true, "watch" => true, "serving" => true, } From e3142e4c5a9b3a1001513f5970b482c23a1f80c0 Mon Sep 17 00:00:00 2001 From: Frank Taillandier Date: Sun, 3 Dec 2017 19:52:37 +0100 Subject: [PATCH 47/49] Docs: Add title and anchor --- docs/_docs/usage.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/docs/_docs/usage.md b/docs/_docs/usage.md index d4dffa01..a4201c1e 100644 --- a/docs/_docs/usage.md +++ b/docs/_docs/usage.md @@ -21,7 +21,9 @@ jekyll build --watch # watched for changes, and regenerated automatically. ``` -Default URL is set to `http://localhost:4000` in development environment. {% include docs_version_badge.html version="3.3.0" %} +## Override default development settings + +Default URL is set to `http://localhost:4000` in development environment. {% include docs_version_badge.html version="3.3.0" %} If you want to build for your production environment: From 50ff219ba2aa4c9c8b267d231eed3813997fdd6d Mon Sep 17 00:00:00 2001 From: Alex Wood Date: Wed, 6 Dec 2017 16:33:51 -0500 Subject: [PATCH 48/49] Add LiveReload functionality to Jekyll. (#5142) Merge pull request 5142 --- Gemfile | 1 + jekyll.gemspec | 1 + lib/jekyll/commands/serve.rb | 179 ++- .../commands/serve/live_reload_reactor.rb | 153 +++ .../serve/livereload_assets/livereload.js | 1183 +++++++++++++++++ lib/jekyll/commands/serve/servlet.rb | 137 ++ lib/jekyll/commands/serve/websockets.rb | 80 ++ lib/jekyll/utils.rb | 1 + lib/jekyll/utils/thread_event.rb | 35 + test/test_commands_serve.rb | 125 ++ 10 files changed, 1875 insertions(+), 20 deletions(-) create mode 100644 lib/jekyll/commands/serve/live_reload_reactor.rb create mode 100644 lib/jekyll/commands/serve/livereload_assets/livereload.js create mode 100644 lib/jekyll/commands/serve/websockets.rb create mode 100644 lib/jekyll/utils/thread_event.rb diff --git a/Gemfile b/Gemfile index 68bc28c2..f7000acb 100644 --- a/Gemfile +++ b/Gemfile @@ -24,6 +24,7 @@ end group :test do gem "codeclimate-test-reporter", "~> 1.0.5" gem "cucumber", RUBY_VERSION >= "2.2" ? "~> 3.0" : "3.0.1" + gem "httpclient" gem "jekyll_test_plugin" gem "jekyll_test_plugin_malicious" # nokogiri v1.8 does not work with ruby 2.1 and below diff --git a/jekyll.gemspec b/jekyll.gemspec index 90ad0ceb..b434881c 100644 --- a/jekyll.gemspec +++ b/jekyll.gemspec @@ -32,6 +32,7 @@ Gem::Specification.new do |s| s.add_runtime_dependency("addressable", "~> 2.4") s.add_runtime_dependency("colorator", "~> 1.0") + s.add_runtime_dependency("em-websocket", "~> 0.5") s.add_runtime_dependency("i18n", "~> 0.7") s.add_runtime_dependency("jekyll-sass-converter", "~> 1.0") s.add_runtime_dependency("jekyll-watch", "~> 2.0") diff --git a/lib/jekyll/commands/serve.rb b/lib/jekyll/commands/serve.rb index 9861d2c6..50438b5f 100644 --- a/lib/jekyll/commands/serve.rb +++ b/lib/jekyll/commands/serve.rb @@ -1,20 +1,43 @@ # frozen_string_literal: true +require "thread" + module Jekyll module Commands class Serve < Command + # Similar to the pattern in Utils::ThreadEvent except we are maintaining the + # state of @running instead of just signaling an event. We have to maintain this + # state since Serve is just called via class methods instead of an instance + # being created each time. + @mutex = Mutex.new + @run_cond = ConditionVariable.new + @running = false + class << self COMMAND_OPTIONS = { - "ssl_cert" => ["--ssl-cert [CERT]", "X.509 (SSL) certificate."], - "host" => ["host", "-H", "--host [HOST]", "Host to bind to"], - "open_url" => ["-o", "--open-url", "Launch your site in a browser"], - "detach" => ["-B", "--detach", "Run the server in the background"], - "ssl_key" => ["--ssl-key [KEY]", "X.509 (SSL) Private Key."], - "port" => ["-P", "--port [PORT]", "Port to listen on"], - "show_dir_listing" => ["--show-dir-listing", + "ssl_cert" => ["--ssl-cert [CERT]", "X.509 (SSL) certificate."], + "host" => ["host", "-H", "--host [HOST]", "Host to bind to"], + "open_url" => ["-o", "--open-url", "Launch your site in a browser"], + "detach" => ["-B", "--detach", + "Run the server in the background",], + "ssl_key" => ["--ssl-key [KEY]", "X.509 (SSL) Private Key."], + "port" => ["-P", "--port [PORT]", "Port to listen on"], + "show_dir_listing" => ["--show-dir-listing", "Show a directory listing instead of loading your index file.",], - "skip_initial_build" => ["skip_initial_build", "--skip-initial-build", + "skip_initial_build" => ["skip_initial_build", "--skip-initial-build", "Skips the initial site build which occurs before the server is started.",], + "livereload" => ["-l", "--livereload", + "Use LiveReload to automatically refresh browsers",], + "livereload_ignore" => ["--livereload-ignore ignore GLOB1[,GLOB2[,...]]", + Array, + "Files for LiveReload to ignore. Remember to quote the values so your shell "\ + "won't expand them",], + "livereload_min_delay" => ["--livereload-min-delay [SECONDS]", + "Minimum reload delay",], + "livereload_max_delay" => ["--livereload-max-delay [SECONDS]", + "Maximum reload delay",], + "livereload_port" => ["--livereload-port [PORT]", Integer, + "Port for LiveReload to listen on",], }.freeze DIRECTORY_INDEX = %w( @@ -26,7 +49,11 @@ module Jekyll index.json ).freeze - # + LIVERELOAD_PORT = 35_729 + LIVERELOAD_DIR = File.join(__dir__, "serve", "livereload_assets") + + attr_reader :mutex, :run_cond, :running + alias_method :running?, :running def init_with_program(prog) prog.command(:serve) do |cmd| @@ -41,20 +68,34 @@ module Jekyll end cmd.action do |_, opts| + opts["livereload_port"] ||= LIVERELOAD_PORT opts["serving"] = true opts["watch" ] = true unless opts.key?("watch") - config = configuration_from_options(opts) - if Jekyll.env == "development" - config["url"] = default_url(config) - end - [Build, Serve].each { |klass| klass.process(config) } + start(opts) end end end # + def start(opts) + # Set the reactor to nil so any old reactor will be GCed. + # We can't unregister a hook so in testing when Serve.start is + # called multiple times we don't want to inadvertently keep using + # a reactor created by a previous test when our test might not + @reload_reactor = nil + + register_reload_hooks(opts) if opts["livereload"] + config = configuration_from_options(opts) + if Jekyll.env == "development" + config["url"] = default_url(config) + end + [Build, Serve].each { |klass| klass.process(config) } + end + + # + def process(opts) opts = configuration_from_options(opts) destination = opts["destination"] @@ -63,6 +104,76 @@ module Jekyll start_up_webrick(opts, destination) end + def shutdown + @server.shutdown if running? + end + + # Perform logical validation of CLI options + + private + def validate_options(opts) + if opts["livereload"] + if opts["detach"] + Jekyll.logger.warn "Warning:", + "--detach and --livereload are mutually exclusive. Choosing --livereload" + opts["detach"] = false + end + if opts["ssl_cert"] || opts["ssl_key"] + # This is not technically true. LiveReload works fine over SSL, but + # EventMachine's SSL support in Windows requires building the gem's + # native extensions against OpenSSL and that proved to be a process + # so tedious that expecting users to do it is a non-starter. + Jekyll.logger.abort_with "Error:", "LiveReload does not support SSL" + end + unless opts["watch"] + # Using livereload logically implies you want to watch the files + opts["watch"] = true + end + elsif %w(livereload_min_delay + livereload_max_delay + livereload_ignore + livereload_port).any? { |o| opts[o] } + Jekyll.logger.abort_with "--livereload-min-delay, "\ + "--livereload-max-delay, --livereload-ignore, and "\ + "--livereload-port require the --livereload option." + end + end + + # + + private + # rubocop:disable Metrics/AbcSize + def register_reload_hooks(opts) + require_relative "serve/live_reload_reactor" + @reload_reactor = LiveReloadReactor.new + + Jekyll::Hooks.register(:site, :post_render) do |site| + regenerator = Jekyll::Regenerator.new(site) + @changed_pages = site.pages.select do |p| + regenerator.regenerate?(p) + end + end + + # A note on ignoring files: LiveReload errs on the side of reloading when it + # comes to the message it gets. If, for example, a page is ignored but a CSS + # file linked in the page isn't, the page will still be reloaded if the CSS + # file is contained in the message sent to LiveReload. Additionally, the + # path matching is very loose so that a message to reload "/" will always + # lead the page to reload since every page starts with "/". + Jekyll::Hooks.register(:site, :post_write) do + if @changed_pages && @reload_reactor && @reload_reactor.running? + ignore, @changed_pages = @changed_pages.partition do |p| + Array(opts["livereload_ignore"]).any? do |filter| + File.fnmatch(filter, Jekyll.sanitized_path(p.relative_path)) + end + end + Jekyll.logger.debug "LiveReload:", "Ignoring #{ignore.map(&:relative_path)}" + @reload_reactor.reload(@changed_pages) + end + @changed_pages = nil + end + end + # Do a base pre-setup of WEBRick so that everything is in place # when we get ready to party, checking for an setting up an error page # and making sure our destination exists. @@ -92,6 +203,7 @@ module Jekyll :MimeTypes => mime_types, :DocumentRoot => opts["destination"], :StartCallback => start_callback(opts["detach"]), + :StopCallback => stop_callback(opts["detach"]), :BindAddress => opts["host"], :Port => opts["port"], :DirectoryIndex => DIRECTORY_INDEX, @@ -108,11 +220,16 @@ module Jekyll private def start_up_webrick(opts, destination) - server = WEBrick::HTTPServer.new(webrick_opts(opts)).tap { |o| o.unmount("") } - server.mount(opts["baseurl"].to_s, Servlet, destination, file_handler_opts) - Jekyll.logger.info "Server address:", server_address(server, opts) - launch_browser server, opts if opts["open_url"] - boot_or_detach server, opts + if opts["livereload"] + @reload_reactor.start(opts) + end + + @server = WEBrick::HTTPServer.new(webrick_opts(opts)).tap { |o| o.unmount("") } + @server.mount(opts["baseurl"].to_s, Servlet, destination, file_handler_opts) + + Jekyll.logger.info "Server address:", server_address(@server, opts) + launch_browser @server, opts if opts["open_url"] + boot_or_detach @server, opts end # Recreate NondisclosureName under utf-8 circumstance @@ -227,7 +344,29 @@ module Jekyll def start_callback(detached) unless detached proc do - Jekyll.logger.info("Server running...", "press ctrl-c to stop.") + mutex.synchronize do + # Block until EventMachine reactor starts + @reload_reactor.started_event.wait unless @reload_reactor.nil? + @running = true + Jekyll.logger.info("Server running...", "press ctrl-c to stop.") + @run_cond.broadcast + end + end + end + end + + private + def stop_callback(detached) + unless detached + proc do + mutex.synchronize do + unless @reload_reactor.nil? + @reload_reactor.stop + @reload_reactor.stopped_event.wait + end + @running = false + @run_cond.broadcast + end end end end diff --git a/lib/jekyll/commands/serve/live_reload_reactor.rb b/lib/jekyll/commands/serve/live_reload_reactor.rb new file mode 100644 index 00000000..498e78ae --- /dev/null +++ b/lib/jekyll/commands/serve/live_reload_reactor.rb @@ -0,0 +1,153 @@ +# frozen_string_literal: true + +require "json" +require "em-websocket" + +require_relative "websockets" + +module Jekyll + module Commands + class Serve + class LiveReloadReactor + attr_reader :started_event + attr_reader :stopped_event + attr_reader :thread + + def initialize + @thread = nil + @websockets = [] + @connections_count = 0 + @started_event = Utils::ThreadEvent.new + @stopped_event = Utils::ThreadEvent.new + end + + def stop + # There is only one EventMachine instance per Ruby process so stopping + # it here will stop the reactor thread we have running. + EM.stop if EM.reactor_running? + Jekyll.logger.debug("LiveReload Server:", "halted") + end + + def running? + EM.reactor_running? + end + + def handle_websockets_event(ws) + ws.onopen do |handshake| + connect(ws, handshake) + end + + ws.onclose do + disconnect(ws) + end + + ws.onmessage do |msg| + print_message(msg) + end + + ws.onerror do |error| + log_error(error) + end + end + + # rubocop:disable Metrics/MethodLength + def start(opts) + @thread = Thread.new do + # Use epoll if the kernel supports it + EM.epoll + EM.run do + EM.error_handler do |e| + log_error(e) + end + + EM.start_server( + opts["host"], + opts["livereload_port"], + HttpAwareConnection, + opts + ) do |ws| + handle_websockets_event(ws) + end + + # Notify blocked threads that EventMachine has started or shutdown + EM.schedule do + @started_event.set + end + + EM.add_shutdown_hook do + @stopped_event.set + end + + Jekyll.logger.info( + "LiveReload address:", "#{opts["host"]}:#{opts["livereload_port"]}" + ) + end + end + @thread.abort_on_exception = true + end + + # For a description of the protocol see + # http://feedback.livereload.com/knowledgebase/articles/86174-livereload-protocol + def reload(pages) + pages.each do |p| + msg = { + :command => "reload", + :path => p.url, + :liveCSS => true, + } + + Jekyll.logger.debug("LiveReload:", "Reloading #{p.url}") + Jekyll.logger.debug(JSON.dump(msg)) + @websockets.each do |ws| + ws.send(JSON.dump(msg)) + end + end + end + + private + def connect(ws, handshake) + @connections_count += 1 + if @connections_count == 1 + message = "Browser connected" + message += " over SSL/TLS" if handshake.secure? + Jekyll.logger.info("LiveReload:", message) + end + ws.send( + JSON.dump( + :command => "hello", + :protocols => ["http://livereload.com/protocols/official-7"], + :serverName => "jekyll" + ) + ) + + @websockets << ws + end + + private + def disconnect(ws) + @websockets.delete(ws) + end + + private + def print_message(json_message) + msg = JSON.parse(json_message) + # Not sure what the 'url' command even does in LiveReload. The spec is silent + # on its purpose. + if msg["command"] == "url" + Jekyll.logger.info("LiveReload:", "Browser URL: #{msg["url"]}") + end + end + + private + def log_error(e) + Jekyll.logger.warn( + "LiveReload experienced an error. "\ + "Run with --verbose for more information." + ) + Jekyll.logger.debug("LiveReload Error:", e.message) + Jekyll.logger.debug("LiveReload Error:", e.backtrace.join("\n")) + end + end + end + end +end diff --git a/lib/jekyll/commands/serve/livereload_assets/livereload.js b/lib/jekyll/commands/serve/livereload_assets/livereload.js new file mode 100644 index 00000000..eee60ec8 --- /dev/null +++ b/lib/jekyll/commands/serve/livereload_assets/livereload.js @@ -0,0 +1,1183 @@ +(function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);var f=new Error("Cannot find module '"+o+"'");throw f.code="MODULE_NOT_FOUND",f}var l=n[o]={exports:{}};t[o][0].call(l.exports,function(e){var n=t[o][1][e];return s(n?n:e)},l,l.exports,e,t,n,r)}return n[o].exports}var i=typeof require=="function"&&require;for(var o=0;o tag"); + return; + } + } + this.reloader = new Reloader(this.window, this.console, Timer); + this.connector = new Connector(this.options, this.WebSocket, Timer, { + connecting: (function(_this) { + return function() {}; + })(this), + socketConnected: (function(_this) { + return function() {}; + })(this), + connected: (function(_this) { + return function(protocol) { + var _base; + if (typeof (_base = _this.listeners).connect === "function") { + _base.connect(); + } + _this.log("LiveReload is connected to " + _this.options.host + ":" + _this.options.port + " (protocol v" + protocol + ")."); + return _this.analyze(); + }; + })(this), + error: (function(_this) { + return function(e) { + if (e instanceof ProtocolError) { + if (typeof console !== "undefined" && console !== null) { + return console.log("" + e.message + "."); + } + } else { + if (typeof console !== "undefined" && console !== null) { + return console.log("LiveReload internal error: " + e.message); + } + } + }; + })(this), + disconnected: (function(_this) { + return function(reason, nextDelay) { + var _base; + if (typeof (_base = _this.listeners).disconnect === "function") { + _base.disconnect(); + } + switch (reason) { + case 'cannot-connect': + return _this.log("LiveReload cannot connect to " + _this.options.host + ":" + _this.options.port + ", will retry in " + nextDelay + " sec."); + case 'broken': + return _this.log("LiveReload disconnected from " + _this.options.host + ":" + _this.options.port + ", reconnecting in " + nextDelay + " sec."); + case 'handshake-timeout': + return _this.log("LiveReload cannot connect to " + _this.options.host + ":" + _this.options.port + " (handshake timeout), will retry in " + nextDelay + " sec."); + case 'handshake-failed': + return _this.log("LiveReload cannot connect to " + _this.options.host + ":" + _this.options.port + " (handshake failed), will retry in " + nextDelay + " sec."); + case 'manual': + break; + case 'error': + break; + default: + return _this.log("LiveReload disconnected from " + _this.options.host + ":" + _this.options.port + " (" + reason + "), reconnecting in " + nextDelay + " sec."); + } + }; + })(this), + message: (function(_this) { + return function(message) { + switch (message.command) { + case 'reload': + return _this.performReload(message); + case 'alert': + return _this.performAlert(message); + } + }; + })(this) + }); + this.initialized = true; + } + + LiveReload.prototype.on = function(eventName, handler) { + return this.listeners[eventName] = handler; + }; + + LiveReload.prototype.log = function(message) { + return this.console.log("" + message); + }; + + LiveReload.prototype.performReload = function(message) { + var _ref, _ref1; + this.log("LiveReload received reload request: " + (JSON.stringify(message, null, 2))); + return this.reloader.reload(message.path, { + liveCSS: (_ref = message.liveCSS) != null ? _ref : true, + liveImg: (_ref1 = message.liveImg) != null ? _ref1 : true, + originalPath: message.originalPath || '', + overrideURL: message.overrideURL || '', + serverURL: "http://" + this.options.host + ":" + this.options.port + }); + }; + + LiveReload.prototype.performAlert = function(message) { + return alert(message.message); + }; + + LiveReload.prototype.shutDown = function() { + var _base; + if (!this.initialized) { + return; + } + this.connector.disconnect(); + this.log("LiveReload disconnected."); + return typeof (_base = this.listeners).shutdown === "function" ? _base.shutdown() : void 0; + }; + + LiveReload.prototype.hasPlugin = function(identifier) { + return !!this.pluginIdentifiers[identifier]; + }; + + LiveReload.prototype.addPlugin = function(pluginClass) { + var plugin; + if (!this.initialized) { + return; + } + if (this.hasPlugin(pluginClass.identifier)) { + return; + } + this.pluginIdentifiers[pluginClass.identifier] = true; + plugin = new pluginClass(this.window, { + _livereload: this, + _reloader: this.reloader, + _connector: this.connector, + console: this.console, + Timer: Timer, + generateCacheBustUrl: (function(_this) { + return function(url) { + return _this.reloader.generateCacheBustUrl(url); + }; + })(this) + }); + this.plugins.push(plugin); + this.reloader.addPlugin(plugin); + }; + + LiveReload.prototype.analyze = function() { + var plugin, pluginData, pluginsData, _i, _len, _ref; + if (!this.initialized) { + return; + } + if (!(this.connector.protocol >= 7)) { + return; + } + pluginsData = {}; + _ref = this.plugins; + for (_i = 0, _len = _ref.length; _i < _len; _i++) { + plugin = _ref[_i]; + pluginsData[plugin.constructor.identifier] = pluginData = (typeof plugin.analyze === "function" ? plugin.analyze() : void 0) || {}; + pluginData.version = plugin.constructor.version; + } + this.connector.sendCommand({ + command: 'info', + plugins: pluginsData, + url: this.window.location.href + }); + }; + + return LiveReload; + + })(); + +}).call(this); + +},{"./connector":1,"./options":5,"./reloader":7,"./timer":9}],5:[function(require,module,exports){ +(function() { + var Options; + + exports.Options = Options = (function() { + function Options() { + this.https = false; + this.host = null; + this.port = 35729; + this.snipver = null; + this.ext = null; + this.extver = null; + this.mindelay = 1000; + this.maxdelay = 60000; + this.handshake_timeout = 5000; + } + + Options.prototype.set = function(name, value) { + if (typeof value === 'undefined') { + return; + } + if (!isNaN(+value)) { + value = +value; + } + return this[name] = value; + }; + + return Options; + + })(); + + Options.extract = function(document) { + var element, keyAndValue, m, mm, options, pair, src, _i, _j, _len, _len1, _ref, _ref1; + _ref = document.getElementsByTagName('script'); + for (_i = 0, _len = _ref.length; _i < _len; _i++) { + element = _ref[_i]; + if ((src = element.src) && (m = src.match(/^[^:]+:\/\/(.*)\/z?livereload\.js(?:\?(.*))?$/))) { + options = new Options(); + options.https = src.indexOf("https") === 0; + if (mm = m[1].match(/^([^\/:]+)(?::(\d+))?$/)) { + options.host = mm[1]; + if (mm[2]) { + options.port = parseInt(mm[2], 10); + } + } + if (m[2]) { + _ref1 = m[2].split('&'); + for (_j = 0, _len1 = _ref1.length; _j < _len1; _j++) { + pair = _ref1[_j]; + if ((keyAndValue = pair.split('=')).length > 1) { + options.set(keyAndValue[0].replace(/-/g, '_'), keyAndValue.slice(1).join('=')); + } + } + } + return options; + } + } + return null; + }; + +}).call(this); + +},{}],6:[function(require,module,exports){ +(function() { + var PROTOCOL_6, PROTOCOL_7, Parser, ProtocolError, + __indexOf = [].indexOf || function(item) { for (var i = 0, l = this.length; i < l; i++) { if (i in this && this[i] === item) return i; } return -1; }; + + exports.PROTOCOL_6 = PROTOCOL_6 = 'http://livereload.com/protocols/official-6'; + + exports.PROTOCOL_7 = PROTOCOL_7 = 'http://livereload.com/protocols/official-7'; + + exports.ProtocolError = ProtocolError = (function() { + function ProtocolError(reason, data) { + this.message = "LiveReload protocol error (" + reason + ") after receiving data: \"" + data + "\"."; + } + + return ProtocolError; + + })(); + + exports.Parser = Parser = (function() { + function Parser(handlers) { + this.handlers = handlers; + this.reset(); + } + + Parser.prototype.reset = function() { + return this.protocol = null; + }; + + Parser.prototype.process = function(data) { + var command, e, message, options, _ref; + try { + if (this.protocol == null) { + if (data.match(/^!!ver:([\d.]+)$/)) { + this.protocol = 6; + } else if (message = this._parseMessage(data, ['hello'])) { + if (!message.protocols.length) { + throw new ProtocolError("no protocols specified in handshake message"); + } else if (__indexOf.call(message.protocols, PROTOCOL_7) >= 0) { + this.protocol = 7; + } else if (__indexOf.call(message.protocols, PROTOCOL_6) >= 0) { + this.protocol = 6; + } else { + throw new ProtocolError("no supported protocols found"); + } + } + return this.handlers.connected(this.protocol); + } else if (this.protocol === 6) { + message = JSON.parse(data); + if (!message.length) { + throw new ProtocolError("protocol 6 messages must be arrays"); + } + command = message[0], options = message[1]; + if (command !== 'refresh') { + throw new ProtocolError("unknown protocol 6 command"); + } + return this.handlers.message({ + command: 'reload', + path: options.path, + liveCSS: (_ref = options.apply_css_live) != null ? _ref : true + }); + } else { + message = this._parseMessage(data, ['reload', 'alert']); + return this.handlers.message(message); + } + } catch (_error) { + e = _error; + if (e instanceof ProtocolError) { + return this.handlers.error(e); + } else { + throw e; + } + } + }; + + Parser.prototype._parseMessage = function(data, validCommands) { + var e, message, _ref; + try { + message = JSON.parse(data); + } catch (_error) { + e = _error; + throw new ProtocolError('unparsable JSON', data); + } + if (!message.command) { + throw new ProtocolError('missing "command" key', data); + } + if (_ref = message.command, __indexOf.call(validCommands, _ref) < 0) { + throw new ProtocolError("invalid command '" + message.command + "', only valid commands are: " + (validCommands.join(', ')) + ")", data); + } + return message; + }; + + return Parser; + + })(); + +}).call(this); + +},{}],7:[function(require,module,exports){ +(function() { + var IMAGE_STYLES, Reloader, numberOfMatchingSegments, pathFromUrl, pathsMatch, pickBestMatch, splitUrl; + + splitUrl = function(url) { + var hash, index, params; + if ((index = url.indexOf('#')) >= 0) { + hash = url.slice(index); + url = url.slice(0, index); + } else { + hash = ''; + } + if ((index = url.indexOf('?')) >= 0) { + params = url.slice(index); + url = url.slice(0, index); + } else { + params = ''; + } + return { + url: url, + params: params, + hash: hash + }; + }; + + pathFromUrl = function(url) { + var path; + url = splitUrl(url).url; + if (url.indexOf('file://') === 0) { + path = url.replace(/^file:\/\/(localhost)?/, ''); + } else { + path = url.replace(/^([^:]+:)?\/\/([^:\/]+)(:\d*)?\//, '/'); + } + return decodeURIComponent(path); + }; + + pickBestMatch = function(path, objects, pathFunc) { + var bestMatch, object, score, _i, _len; + bestMatch = { + score: 0 + }; + for (_i = 0, _len = objects.length; _i < _len; _i++) { + object = objects[_i]; + score = numberOfMatchingSegments(path, pathFunc(object)); + if (score > bestMatch.score) { + bestMatch = { + object: object, + score: score + }; + } + } + if (bestMatch.score > 0) { + return bestMatch; + } else { + return null; + } + }; + + numberOfMatchingSegments = function(path1, path2) { + var comps1, comps2, eqCount, len; + path1 = path1.replace(/^\/+/, '').toLowerCase(); + path2 = path2.replace(/^\/+/, '').toLowerCase(); + if (path1 === path2) { + return 10000; + } + comps1 = path1.split('/').reverse(); + comps2 = path2.split('/').reverse(); + len = Math.min(comps1.length, comps2.length); + eqCount = 0; + while (eqCount < len && comps1[eqCount] === comps2[eqCount]) { + ++eqCount; + } + return eqCount; + }; + + pathsMatch = function(path1, path2) { + return numberOfMatchingSegments(path1, path2) > 0; + }; + + IMAGE_STYLES = [ + { + selector: 'background', + styleNames: ['backgroundImage'] + }, { + selector: 'border', + styleNames: ['borderImage', 'webkitBorderImage', 'MozBorderImage'] + } + ]; + + exports.Reloader = Reloader = (function() { + function Reloader(window, console, Timer) { + this.window = window; + this.console = console; + this.Timer = Timer; + this.document = this.window.document; + this.importCacheWaitPeriod = 200; + this.plugins = []; + } + + Reloader.prototype.addPlugin = function(plugin) { + return this.plugins.push(plugin); + }; + + Reloader.prototype.analyze = function(callback) { + return results; + }; + + Reloader.prototype.reload = function(path, options) { + var plugin, _base, _i, _len, _ref; + this.options = options; + if ((_base = this.options).stylesheetReloadTimeout == null) { + _base.stylesheetReloadTimeout = 15000; + } + _ref = this.plugins; + for (_i = 0, _len = _ref.length; _i < _len; _i++) { + plugin = _ref[_i]; + if (plugin.reload && plugin.reload(path, options)) { + return; + } + } + if (options.liveCSS) { + if (path.match(/\.css$/i)) { + if (this.reloadStylesheet(path)) { + return; + } + } + } + if (options.liveImg) { + if (path.match(/\.(jpe?g|png|gif)$/i)) { + this.reloadImages(path); + return; + } + } + return this.reloadPage(); + }; + + Reloader.prototype.reloadPage = function() { + return this.window.document.location.reload(); + }; + + Reloader.prototype.reloadImages = function(path) { + var expando, img, selector, styleNames, styleSheet, _i, _j, _k, _l, _len, _len1, _len2, _len3, _ref, _ref1, _ref2, _ref3, _results; + expando = this.generateUniqueString(); + _ref = this.document.images; + for (_i = 0, _len = _ref.length; _i < _len; _i++) { + img = _ref[_i]; + if (pathsMatch(path, pathFromUrl(img.src))) { + img.src = this.generateCacheBustUrl(img.src, expando); + } + } + if (this.document.querySelectorAll) { + for (_j = 0, _len1 = IMAGE_STYLES.length; _j < _len1; _j++) { + _ref1 = IMAGE_STYLES[_j], selector = _ref1.selector, styleNames = _ref1.styleNames; + _ref2 = this.document.querySelectorAll("[style*=" + selector + "]"); + for (_k = 0, _len2 = _ref2.length; _k < _len2; _k++) { + img = _ref2[_k]; + this.reloadStyleImages(img.style, styleNames, path, expando); + } + } + } + if (this.document.styleSheets) { + _ref3 = this.document.styleSheets; + _results = []; + for (_l = 0, _len3 = _ref3.length; _l < _len3; _l++) { + styleSheet = _ref3[_l]; + _results.push(this.reloadStylesheetImages(styleSheet, path, expando)); + } + return _results; + } + }; + + Reloader.prototype.reloadStylesheetImages = function(styleSheet, path, expando) { + var e, rule, rules, styleNames, _i, _j, _len, _len1; + try { + rules = styleSheet != null ? styleSheet.cssRules : void 0; + } catch (_error) { + e = _error; + } + if (!rules) { + return; + } + for (_i = 0, _len = rules.length; _i < _len; _i++) { + rule = rules[_i]; + switch (rule.type) { + case CSSRule.IMPORT_RULE: + this.reloadStylesheetImages(rule.styleSheet, path, expando); + break; + case CSSRule.STYLE_RULE: + for (_j = 0, _len1 = IMAGE_STYLES.length; _j < _len1; _j++) { + styleNames = IMAGE_STYLES[_j].styleNames; + this.reloadStyleImages(rule.style, styleNames, path, expando); + } + break; + case CSSRule.MEDIA_RULE: + this.reloadStylesheetImages(rule, path, expando); + } + } + }; + + Reloader.prototype.reloadStyleImages = function(style, styleNames, path, expando) { + var newValue, styleName, value, _i, _len; + for (_i = 0, _len = styleNames.length; _i < _len; _i++) { + styleName = styleNames[_i]; + value = style[styleName]; + if (typeof value === 'string') { + newValue = value.replace(/\burl\s*\(([^)]*)\)/, (function(_this) { + return function(match, src) { + if (pathsMatch(path, pathFromUrl(src))) { + return "url(" + (_this.generateCacheBustUrl(src, expando)) + ")"; + } else { + return match; + } + }; + })(this)); + if (newValue !== value) { + style[styleName] = newValue; + } + } + } + }; + + Reloader.prototype.reloadStylesheet = function(path) { + var imported, link, links, match, style, _i, _j, _k, _l, _len, _len1, _len2, _len3, _ref, _ref1; + links = (function() { + var _i, _len, _ref, _results; + _ref = this.document.getElementsByTagName('link'); + _results = []; + for (_i = 0, _len = _ref.length; _i < _len; _i++) { + link = _ref[_i]; + if (link.rel.match(/^stylesheet$/i) && !link.__LiveReload_pendingRemoval) { + _results.push(link); + } + } + return _results; + }).call(this); + imported = []; + _ref = this.document.getElementsByTagName('style'); + for (_i = 0, _len = _ref.length; _i < _len; _i++) { + style = _ref[_i]; + if (style.sheet) { + this.collectImportedStylesheets(style, style.sheet, imported); + } + } + for (_j = 0, _len1 = links.length; _j < _len1; _j++) { + link = links[_j]; + this.collectImportedStylesheets(link, link.sheet, imported); + } + if (this.window.StyleFix && this.document.querySelectorAll) { + _ref1 = this.document.querySelectorAll('style[data-href]'); + for (_k = 0, _len2 = _ref1.length; _k < _len2; _k++) { + style = _ref1[_k]; + links.push(style); + } + } + this.console.log("LiveReload found " + links.length + " LINKed stylesheets, " + imported.length + " @imported stylesheets"); + match = pickBestMatch(path, links.concat(imported), (function(_this) { + return function(l) { + return pathFromUrl(_this.linkHref(l)); + }; + })(this)); + if (match) { + if (match.object.rule) { + this.console.log("LiveReload is reloading imported stylesheet: " + match.object.href); + this.reattachImportedRule(match.object); + } else { + this.console.log("LiveReload is reloading stylesheet: " + (this.linkHref(match.object))); + this.reattachStylesheetLink(match.object); + } + } else { + this.console.log("LiveReload will reload all stylesheets because path '" + path + "' did not match any specific one"); + for (_l = 0, _len3 = links.length; _l < _len3; _l++) { + link = links[_l]; + this.reattachStylesheetLink(link); + } + } + return true; + }; + + Reloader.prototype.collectImportedStylesheets = function(link, styleSheet, result) { + var e, index, rule, rules, _i, _len; + try { + rules = styleSheet != null ? styleSheet.cssRules : void 0; + } catch (_error) { + e = _error; + } + if (rules && rules.length) { + for (index = _i = 0, _len = rules.length; _i < _len; index = ++_i) { + rule = rules[index]; + switch (rule.type) { + case CSSRule.CHARSET_RULE: + continue; + case CSSRule.IMPORT_RULE: + result.push({ + link: link, + rule: rule, + index: index, + href: rule.href + }); + this.collectImportedStylesheets(link, rule.styleSheet, result); + break; + default: + break; + } + } + } + }; + + Reloader.prototype.waitUntilCssLoads = function(clone, func) { + var callbackExecuted, executeCallback, poll; + callbackExecuted = false; + executeCallback = (function(_this) { + return function() { + if (callbackExecuted) { + return; + } + callbackExecuted = true; + return func(); + }; + })(this); + clone.onload = (function(_this) { + return function() { + _this.console.log("LiveReload: the new stylesheet has finished loading"); + _this.knownToSupportCssOnLoad = true; + return executeCallback(); + }; + })(this); + if (!this.knownToSupportCssOnLoad) { + (poll = (function(_this) { + return function() { + if (clone.sheet) { + _this.console.log("LiveReload is polling until the new CSS finishes loading..."); + return executeCallback(); + } else { + return _this.Timer.start(50, poll); + } + }; + })(this))(); + } + return this.Timer.start(this.options.stylesheetReloadTimeout, executeCallback); + }; + + Reloader.prototype.linkHref = function(link) { + return link.href || link.getAttribute('data-href'); + }; + + Reloader.prototype.reattachStylesheetLink = function(link) { + var clone, parent; + if (link.__LiveReload_pendingRemoval) { + return; + } + link.__LiveReload_pendingRemoval = true; + if (link.tagName === 'STYLE') { + clone = this.document.createElement('link'); + clone.rel = 'stylesheet'; + clone.media = link.media; + clone.disabled = link.disabled; + } else { + clone = link.cloneNode(false); + } + clone.href = this.generateCacheBustUrl(this.linkHref(link)); + parent = link.parentNode; + if (parent.lastChild === link) { + parent.appendChild(clone); + } else { + parent.insertBefore(clone, link.nextSibling); + } + return this.waitUntilCssLoads(clone, (function(_this) { + return function() { + var additionalWaitingTime; + if (/AppleWebKit/.test(navigator.userAgent)) { + additionalWaitingTime = 5; + } else { + additionalWaitingTime = 200; + } + return _this.Timer.start(additionalWaitingTime, function() { + var _ref; + if (!link.parentNode) { + return; + } + link.parentNode.removeChild(link); + clone.onreadystatechange = null; + return (_ref = _this.window.StyleFix) != null ? _ref.link(clone) : void 0; + }); + }; + })(this)); + }; + + Reloader.prototype.reattachImportedRule = function(_arg) { + var href, index, link, media, newRule, parent, rule, tempLink; + rule = _arg.rule, index = _arg.index, link = _arg.link; + parent = rule.parentStyleSheet; + href = this.generateCacheBustUrl(rule.href); + media = rule.media.length ? [].join.call(rule.media, ', ') : ''; + newRule = "@import url(\"" + href + "\") " + media + ";"; + rule.__LiveReload_newHref = href; + tempLink = this.document.createElement("link"); + tempLink.rel = 'stylesheet'; + tempLink.href = href; + tempLink.__LiveReload_pendingRemoval = true; + if (link.parentNode) { + link.parentNode.insertBefore(tempLink, link); + } + return this.Timer.start(this.importCacheWaitPeriod, (function(_this) { + return function() { + if (tempLink.parentNode) { + tempLink.parentNode.removeChild(tempLink); + } + if (rule.__LiveReload_newHref !== href) { + return; + } + parent.insertRule(newRule, index); + parent.deleteRule(index + 1); + rule = parent.cssRules[index]; + rule.__LiveReload_newHref = href; + return _this.Timer.start(_this.importCacheWaitPeriod, function() { + if (rule.__LiveReload_newHref !== href) { + return; + } + parent.insertRule(newRule, index); + return parent.deleteRule(index + 1); + }); + }; + })(this)); + }; + + Reloader.prototype.generateUniqueString = function() { + return 'livereload=' + Date.now(); + }; + + Reloader.prototype.generateCacheBustUrl = function(url, expando) { + var hash, oldParams, originalUrl, params, _ref; + if (expando == null) { + expando = this.generateUniqueString(); + } + _ref = splitUrl(url), url = _ref.url, hash = _ref.hash, oldParams = _ref.params; + if (this.options.overrideURL) { + if (url.indexOf(this.options.serverURL) < 0) { + originalUrl = url; + url = this.options.serverURL + this.options.overrideURL + "?url=" + encodeURIComponent(url); + this.console.log("LiveReload is overriding source URL " + originalUrl + " with " + url); + } + } + params = oldParams.replace(/(\?|&)livereload=(\d+)/, function(match, sep) { + return "" + sep + expando; + }); + if (params === oldParams) { + if (oldParams.length === 0) { + params = "?" + expando; + } else { + params = "" + oldParams + "&" + expando; + } + } + return url + params + hash; + }; + + return Reloader; + + })(); + +}).call(this); + +},{}],8:[function(require,module,exports){ +(function() { + var CustomEvents, LiveReload, k; + + CustomEvents = require('./customevents'); + + LiveReload = window.LiveReload = new (require('./livereload').LiveReload)(window); + + for (k in window) { + if (k.match(/^LiveReloadPlugin/)) { + LiveReload.addPlugin(window[k]); + } + } + + LiveReload.addPlugin(require('./less')); + + LiveReload.on('shutdown', function() { + return delete window.LiveReload; + }); + + LiveReload.on('connect', function() { + return CustomEvents.fire(document, 'LiveReloadConnect'); + }); + + LiveReload.on('disconnect', function() { + return CustomEvents.fire(document, 'LiveReloadDisconnect'); + }); + + CustomEvents.bind(document, 'LiveReloadShutDown', function() { + return LiveReload.shutDown(); + }); + +}).call(this); + +},{"./customevents":2,"./less":3,"./livereload":4}],9:[function(require,module,exports){ +(function() { + var Timer; + + exports.Timer = Timer = (function() { + function Timer(func) { + this.func = func; + this.running = false; + this.id = null; + this._handler = (function(_this) { + return function() { + _this.running = false; + _this.id = null; + return _this.func(); + }; + })(this); + } + + Timer.prototype.start = function(timeout) { + if (this.running) { + clearTimeout(this.id); + } + this.id = setTimeout(this._handler, timeout); + return this.running = true; + }; + + Timer.prototype.stop = function() { + if (this.running) { + clearTimeout(this.id); + this.running = false; + return this.id = null; + } + }; + + return Timer; + + })(); + + Timer.start = function(timeout, func) { + return setTimeout(func, timeout); + }; + +}).call(this); + +},{}]},{},[8]); diff --git a/lib/jekyll/commands/serve/servlet.rb b/lib/jekyll/commands/serve/servlet.rb index 70b6d66d..640720ee 100644 --- a/lib/jekyll/commands/serve/servlet.rb +++ b/lib/jekyll/commands/serve/servlet.rb @@ -5,6 +5,128 @@ require "webrick" module Jekyll module Commands class Serve + # This class is used to determine if the Servlet should modify a served file + # to insert the LiveReload script tags + class SkipAnalyzer + BAD_USER_AGENTS = [%r!MSIE!].freeze + + def self.skip_processing?(request, response, options) + new(request, response, options).skip_processing? + end + + def initialize(request, response, options) + @options = options + @request = request + @response = response + end + + def skip_processing? + !html? || chunked? || inline? || bad_browser? + end + + def chunked? + @response["Transfer-Encoding"] == "chunked" + end + + def inline? + @response["Content-Disposition"] =~ %r!^inline! + end + + def bad_browser? + BAD_USER_AGENTS.any? { |pattern| @request["User-Agent"] =~ pattern } + end + + def html? + @response["Content-Type"] =~ %r!text/html! + end + end + + # This class inserts the LiveReload script tags into HTML as it is served + class BodyProcessor + HEAD_TAG_REGEX = %r!|! + + attr_reader :content_length, :new_body, :livereload_added + + def initialize(body, options) + @body = body + @options = options + @processed = false + end + + def processed? + @processed + end + + # rubocop:disable Metrics/MethodLength + def process! + @new_body = [] + # @body will usually be a File object but Strings occur in rare cases + if @body.respond_to?(:each) + begin + @body.each { |line| @new_body << line.to_s } + ensure + @body.close + end + else + @new_body = @body.lines + end + + @content_length = 0 + @livereload_added = false + + @new_body.each do |line| + if !@livereload_added && line[" + document.write( + ' + TEMPLATE + ERB.new(Jekyll::Utils.strip_heredoc(template)) + end + + def livereload_args + # XHTML standard requires ampersands to be encoded as entities when in + # attributes. See http://stackoverflow.com/a/2190292 + src = "" + if @options["livereload_min_delay"] + src += "&mindelay=#{@options["livereload_min_delay"]}" + end + if @options["livereload_max_delay"] + src += "&maxdelay=#{@options["livereload_max_delay"]}" + end + if @options["livereload_port"] + src += "&port=#{@options["livereload_port"]}" + end + src + end + end + class Servlet < WEBrick::HTTPServlet::FileHandler DEFAULTS = { "Cache-Control" => "private, max-age=0, proxy-revalidate, " \ @@ -34,6 +156,21 @@ module Jekyll # rubocop:disable Naming/MethodName def do_GET(req, res) rtn = super + + if @jekyll_opts["livereload"] + return rtn if SkipAnalyzer.skip_processing?(req, res, @jekyll_opts) + + processor = BodyProcessor.new(res.body, @jekyll_opts) + processor.process! + res.body = processor.new_body + res.content_length = processor.content_length.to_s + + if processor.livereload_added + # Add a header to indicate that the page content has been modified + res["X-Rack-LiveReload"] = "1" + end + end + validate_and_ensure_charset(req, res) res.header.merge!(@headers) rtn diff --git a/lib/jekyll/commands/serve/websockets.rb b/lib/jekyll/commands/serve/websockets.rb new file mode 100644 index 00000000..1e5f948e --- /dev/null +++ b/lib/jekyll/commands/serve/websockets.rb @@ -0,0 +1,80 @@ +# frozen_string_literal: true + +require "http/parser" + +module Jekyll + module Commands + class Serve + # The LiveReload protocol requires the server to serve livereload.js over HTTP + # despite the fact that the protocol itself uses WebSockets. This custom connection + # class addresses the dual protocols that the server needs to understand. + class HttpAwareConnection < EventMachine::WebSocket::Connection + attr_reader :reload_body, :reload_size + + def initialize(_opts) + # If EventMachine SSL support on Windows ever gets better, the code below will + # set up the reactor to handle SSL + # + # @ssl_enabled = opts["ssl_cert"] && opts["ssl_key"] + # if @ssl_enabled + # em_opts[:tls_options] = { + # :private_key_file => Jekyll.sanitized_path(opts["source"], opts["ssl_key"]), + # :cert_chain_file => Jekyll.sanitized_path(opts["source"], opts["ssl_cert"]) + # } + # em_opts[:secure] = true + # end + + # This is too noisy even for --verbose, but uncomment if you need it for + # a specific WebSockets issue. Adding ?LR-verbose=true onto the URL will + # enable logging on the client side. + # em_opts[:debug] = true + + em_opts = {} + super(em_opts) + + reload_file = File.join(Serve.singleton_class::LIVERELOAD_DIR, "livereload.js") + + @reload_body = File.read(reload_file) + @reload_size = @reload_body.bytesize + end + + # rubocop:disable Metrics/MethodLength + def dispatch(data) + parser = Http::Parser.new + parser << data + + # WebSockets requests will have a Connection: Upgrade header + if parser.http_method != "GET" || parser.upgrade? + super + elsif parser.request_url =~ %r!^\/livereload.js! + headers = [ + "HTTP/1.1 200 OK", + "Content-Type: application/javascript", + "Content-Length: #{reload_size}", + "", + "", + ].join("\r\n") + send_data(headers) + + # stream_file_data would free us from keeping livereload.js in memory + # but JRuby blocks on that call and never returns + send_data(reload_body) + close_connection_after_writing + else + body = "This port only serves livereload.js over HTTP.\n" + headers = [ + "HTTP/1.1 400 Bad Request", + "Content-Type: text/plain", + "Content-Length: #{body.bytesize}", + "", + "", + ].join("\r\n") + send_data(headers) + send_data(body) + close_connection_after_writing + end + end + end + end + end +end diff --git a/lib/jekyll/utils.rb b/lib/jekyll/utils.rb index 55cf9be1..1d3cb9c4 100644 --- a/lib/jekyll/utils.rb +++ b/lib/jekyll/utils.rb @@ -8,6 +8,7 @@ module Jekyll autoload :Internet, "jekyll/utils/internet" autoload :Platforms, "jekyll/utils/platforms" autoload :Rouge, "jekyll/utils/rouge" + autoload :ThreadEvent, "jekyll/utils/thread_event" autoload :WinTZ, "jekyll/utils/win_tz" # Constants for use in #slugify diff --git a/lib/jekyll/utils/thread_event.rb b/lib/jekyll/utils/thread_event.rb new file mode 100644 index 00000000..5afb50d9 --- /dev/null +++ b/lib/jekyll/utils/thread_event.rb @@ -0,0 +1,35 @@ +# frozen_string_literal: true + +require "thread" + +module Jekyll + module Utils + # Based on the pattern and code from + # https://emptysqua.re/blog/an-event-synchronization-primitive-for-ruby/ + class ThreadEvent + attr_reader :flag + + def initialize + @lock = Mutex.new + @cond = ConditionVariable.new + @flag = false + end + + def set + @lock.synchronize do + yield if block_given? + @flag = true + @cond.broadcast + end + end + + def wait + @lock.synchronize do + unless @flag + @cond.wait(@lock) + end + end + end + end + end +end diff --git a/test/test_commands_serve.rb b/test/test_commands_serve.rb index 135e83e2..fd1751d8 100644 --- a/test/test_commands_serve.rb +++ b/test/test_commands_serve.rb @@ -3,7 +3,10 @@ require "webrick" require "mercenary" require "helper" +require "httpclient" require "openssl" +require "thread" +require "tmpdir" class TestCommandsServe < JekyllUnitTest def custom_opts(what) @@ -12,6 +15,128 @@ class TestCommandsServe < JekyllUnitTest ) end + def start_server(opts) + @thread = Thread.new do + merc = nil + cmd = Jekyll::Commands::Serve + Mercenary.program(:jekyll) do |p| + merc = cmd.init_with_program(p) + end + merc.execute(:serve, opts) + end + @thread.abort_on_exception = true + + Jekyll::Commands::Serve.mutex.synchronize do + unless Jekyll::Commands::Serve.running? + Jekyll::Commands::Serve.run_cond.wait(Jekyll::Commands::Serve.mutex) + end + end + end + + def serve(opts) + allow(Jekyll).to receive(:configuration).and_return(opts) + allow(Jekyll::Commands::Build).to receive(:process) + + start_server(opts) + + opts + end + + context "using LiveReload" do + setup do + @temp_dir = Dir.mktmpdir("jekyll_livereload_test") + @destination = File.join(@temp_dir, "_site") + Dir.mkdir(@destination) || flunk("Could not make directory #{@destination}") + @client = HTTPClient.new + @client.connect_timeout = 5 + @standard_options = { + "port" => 4000, + "host" => "localhost", + "baseurl" => "", + "detach" => false, + "livereload" => true, + "source" => @temp_dir, + "destination" => @destination, + } + + site = instance_double(Jekyll::Site) + simple_page = <<-HTML.gsub(%r!^\s*!, "") + + + + + Hello World + + +

    Hello! I am a simple web page.

    + + + HTML + + File.open(File.join(@destination, "hello.html"), "w") do |f| + f.write(simple_page) + end + allow(Jekyll::Site).to receive(:new).and_return(site) + end + + teardown do + capture_io do + Jekyll::Commands::Serve.shutdown + end + + Jekyll::Commands::Serve.mutex.synchronize do + if Jekyll::Commands::Serve.running? + Jekyll::Commands::Serve.run_cond.wait(Jekyll::Commands::Serve.mutex) + end + end + + FileUtils.remove_entry_secure(@temp_dir, true) + end + + should "serve livereload.js over HTTP on the default LiveReload port" do + skip_if_windows "EventMachine support on Windows is limited" + opts = serve(@standard_options) + content = @client.get_content( + "http://#{opts["host"]}:#{opts["livereload_port"]}/livereload.js" + ) + assert_match(%r!LiveReload.on!, content) + end + + should "serve nothing else over HTTP on the default LiveReload port" do + skip_if_windows "EventMachine support on Windows is limited" + opts = serve(@standard_options) + res = @client.get("http://#{opts["host"]}:#{opts["livereload_port"]}/") + assert_equal(400, res.status_code) + assert_match(%r!only serves livereload.js!, res.content) + end + + should "insert the LiveReload script tags" do + skip_if_windows "EventMachine support on Windows is limited" + opts = serve(@standard_options) + content = @client.get_content( + "http://#{opts["host"]}:#{opts["port"]}/#{opts["baseurl"]}/hello.html" + ) + assert_match( + %r!livereload.js\?snipver=1&port=#{opts["livereload_port"]}!, + content + ) + assert_match(%r!I am a simple web page!, content) + end + + should "apply the max and min delay options" do + skip_if_windows "EventMachine support on Windows is limited" + opts = serve(@standard_options.merge( + "livereload_max_delay" => "1066", + "livereload_min_delay" => "3" + )) + content = @client.get_content( + "http://#{opts["host"]}:#{opts["port"]}/#{opts["baseurl"]}/hello.html" + ) + assert_match(%r!&mindelay=3!, content) + assert_match(%r!&maxdelay=1066!, content) + end + end + context "with a program" do setup do @merc = nil From 28e20b933494edfe147645cd0dbbe3878e1e5659 Mon Sep 17 00:00:00 2001 From: jekyllbot Date: Wed, 6 Dec 2017 16:33:53 -0500 Subject: [PATCH 49/49] Update history to reflect merge of #5142 [ci skip] --- History.markdown | 1 + 1 file changed, 1 insertion(+) diff --git a/History.markdown b/History.markdown index 54bd43db..9e67e778 100644 --- a/History.markdown +++ b/History.markdown @@ -75,6 +75,7 @@ * Log Kramdown warnings if log level is WARN (#6522) * Add json extension to list of directory indices (#6550) * Dependency: Bump jekyll-watch to 2.0 (#6589) + * Add LiveReload functionality to Jekyll. (#5142) ### Site Enhancements